diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..98fbc1c4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,13 @@ +operator.tgz +cover.out +bin +testbin/* +onpremtest/* +ords/*zip +.gitattributes +.vscode +.gitlab-ci.yml + +# development +.idea +.local diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml deleted file mode 100644 index e4d80bb9..00000000 --- a/.gitlab-ci.yml +++ /dev/null @@ -1,27 +0,0 @@ -build-operator: - stage: build - variables: - IMAGE: "$DOCKER_REPO:$CI_COMMIT_BRANCH" - OP_YAML: oracle-database-operator.yaml - script: - - make docker-build IMG="$IMAGE" - - docker push "$IMAGE" - - newimage=$(docker inspect $IMAGE | python -c 'import json,sys; print json.load(sys.stdin)[0]["RepoDigests"][0]') - - echo $newimage - - docker rmi "$IMAGE" && docker system prune -f - - make operator-yaml IMG=$newimage - - curl -s -n $ARTIFACTORY_REPO/$CI_COMMIT_BRANCH/$OP_YAML -T ./$OP_YAML - only: - variables: - - $CI_COMMIT_MESSAGE =~ /\#run-pipeline/ - - $CI_COMMIT_BRANCH =~ /master/ - - $CI_MERGE_REQUEST_ID != "" - except: - variables: - - $CI_COMMIT_MESSAGE =~ /\#skip-pipeline/ - -cleanup: - stage: .post - script: - - echo "Clean up downloaded binaries" - - rm -rf bin/ diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 722965b9..87d9b60b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,16 +1,16 @@ # Contributing to This Repository -We welcome your contributions! There are multiple ways to contribute. +Oracle welcomes your contributions! There are multiple ways that you can contribute. ## Opening issues -For bugs or enhancement requests, please file a GitHub issue unless the problem is security-related. When filing a bug, remember that the more specific the bug is, the more likely it is to be fixed. If you think you've found a security -vulnerability, then do not raise a GitHub issue. Instead, follow the instructions in our -[security policy](./SECURITY.md). +For bugs or enhancement requests, please file a GitHub issue, unless the problem is security-related. + +When filing a bug, remember that the more specific the bug is, the more likely it is to be fixed. If you think you've found a security vulnerability, then do not raise a GitHub issue. Instead, follow the instructions in our [security policy](./SECURITY.md). ## Contributing code -We welcome your code contributions. Before submitting code by using a pull request, +Oracle welcomes your code contributions. Before submitting code by using a pull request, you must sign the [Oracle Contributor Agreement][OCA] (OCA), and your commits must include the following line, using the name and e-mail address you used to sign the OCA: ```text @@ -29,22 +29,22 @@ can be accepted. ## Pull request process -1. Ensure there is an issue created to track and discuss the fix or enhancement that you intend to submit. +1. Ensure that there is an issue created to track and discuss the fix or enhancement that you intend to submit. 1. Fork this repository. 1. Create a branch in your fork to implement the changes. Oracle recommends using the issue number as part of your branch name. For example: `1234-fixes` 1. Ensure that any documentation is updated with the changes that are required by your change. -1. Ensure that any samples are updated, if the base image has been changed. +1. If the base image has been changed, then ensure that any examples are updated. 1. Submit the pull request. *Do not leave the pull request blank*. Explain exactly what your changes are meant to do, and provide simple steps to indicate how to validate your changes. Ensure that you reference the issue that you created as well. -1. Before the changes are merged, Oracle will assign the pull request to 2 or 3 people for review. +1. Before the changes are merged, Oracle will assign the pull request to two or three people for review. ## Code of conduct Follow the [Golden Rule](https://en.wikipedia.org/wiki/Golden_Rule). If you'd -like more specific guidelines, see the [Contributor Covenant Code of Conduct][COC]. +like more specific guidelines, then see the [Contributor Covenant Code of Conduct][COC]. [OCA]: https://oca.opensource.oracle.com [COC]: https://www.contributor-covenant.org/version/1/4/code-of-conduct/ diff --git a/Dockerfile b/Dockerfile index 0c181a48..11a56962 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,9 +1,20 @@ -# Copyright (c) 2021, 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 https://oss.oracle.com/licenses/upl. # # Build the manager binary -FROM golang:1.16 as builder +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 [ "$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; \ + fi +ENV PATH=${GOLANG_VERSION:+"${PATH}:/usr/local/go/bin"} WORKDIR /workspace # Copy the Go Modules manifests @@ -26,6 +37,10 @@ RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o manager # Use oraclelinux:8-slim as base image to package the manager binary FROM oraclelinux:8-slim +ARG CI_COMMIT_SHA +ARG CI_COMMIT_BRANCH +ENV COMMIT_SHA=${CI_COMMIT_SHA} \ + COMMIT_BRANCH=${CI_COMMIT_BRANCH} WORKDIR / COPY --from=builder /workspace/manager . RUN useradd -u 1002 nonroot diff --git a/LICENSE.txt b/LICENSE.txt index 4ac08f59..56dbf679 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2021 Oracle and/or its affiliates. +Copyright (c) 2022 Oracle and/or its affiliates. The Universal Permissive License (UPL), Version 1.0 diff --git a/Makefile b/Makefile index a0bd6870..88e14843 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # @@ -23,6 +23,8 @@ IMG ?= controller:latest CRD_OPTIONS ?= "crd:trivialVersions=true,preserveUnknownFields=false" # ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. ENVTEST_K8S_VERSION = 1.21 +# 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)) @@ -53,13 +55,13 @@ fmt: ## Run go fmt against code. vet: ## Run go vet against code. go vet ./... -TEST ?= ./apis/... ./commons/... ./controllers/... +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) -v -timeout 40m -ginkgo.v -ginkgo.failFast + KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test $(E2ETEST) -test.timeout 0 -test.v --ginkgo.fail-fast ##@ Build @@ -69,14 +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: test ## Build docker image with the manager. - docker build --no-cache=true --build-arg http_proxy=${HTTP_PROXY} --build-arg https_proxy=${HTTPS_PROXY} . -t ${IMG} - -#docker-build-proxy: test -# docker build --build-arg http_proxy=${http_proxy} --build-arg https_proxy=${https_proxy} build . -t ${IMG} +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) +BUILDER_IMG = oraclelinux:8 +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 +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,42 +98,53 @@ 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 > $$(basename $$(pwd)).yaml + 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_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 -CONTROLLER_GEN = $(shell pwd)/bin/controller-gen -controller-gen: ## Download controller-gen locally if necessary. - $(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.6.1) - -KUSTOMIZE = $(shell pwd)/bin/kustomize -kustomize: ## Download kustomize locally if necessary. - $(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v3@v3.8.7) - -ENVTEST = $(shell pwd)/bin/setup-envtest -envtest: ## Download envtest-setup locally if necessary. - $(call go-get-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest@latest) - -# go-get-tool will 'go get' any package $2 and install it to $1. -PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST)))) -define go-get-tool -@[ -f $(1) ] || { \ -set -e ;\ -TMP_DIR=$$(mktemp -d) ;\ -cd $$TMP_DIR ;\ -go mod init tmp ;\ -echo "Downloading $(2)" ;\ -GOBIN=$(PROJECT_DIR)/bin go get $(2) ;\ -rm -rf $$TMP_DIR ;\ -} -endef .PHONY: bundle bundle: manifests kustomize ## Generate bundle manifests and metadata, then validate generated files. @@ -179,3 +201,4 @@ catalog-build: opm ## Build a catalog image. .PHONY: catalog-push catalog-push: ## Push a catalog image. $(MAKE) docker-push IMG=$(CATALOG_IMG) + diff --git a/PREREQUISITES.md b/PREREQUISITES.md index 7fc3cf21..01bb94b4 100644 --- a/PREREQUISITES.md +++ b/PREREQUISITES.md @@ -2,13 +2,14 @@ ## 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) * Containerized Sharded Oracle Database (SHARDING) ### Setting Up a Kubernetes Cluster and Volumes +Review and complete each step as needed. #### Setting Up an OKE Cluster on Oracle Cloud Infrastructure (OCI) @@ -26,7 +27,7 @@ If you intent to use `OraOperator` to handle Oracle Autonomous Database lifecycl ### 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/SIDB_PREREQUISITES.md) +If you intent 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) diff --git a/PROJECT b/PROJECT index 70e34778..fbf861db 100644 --- a/PROJECT +++ b/PROJECT @@ -1,9 +1,14 @@ +# 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 multigroup: true plugins: - go.sdk.operatorframework.io/v2-alpha: {} + manifests.sdk.operatorframework.io/v2: {} + scorecard.sdk.operatorframework.io/v2: {} projectName: oracle-database-operator repo: github.com/oracle/oracle-database-operator resources: @@ -16,6 +21,34 @@ resources: kind: AutonomousDatabase path: github.com/oracle/oracle-database-operator/apis/database/v1alpha1 version: v1alpha1 + webhooks: + validation: true + webhookVersion: v1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: oracle.com + group: database + kind: AutonomousDatabaseBackup + path: github.com/oracle/oracle-database-operator/apis/database/v1alpha1 + version: v1alpha1 + webhooks: + defaulting: true + validation: true + webhookVersion: v1beta1 +- api: + crdVersion: v1beta1 + namespaced: true + controller: true + domain: oracle.com + group: database + kind: AutonomousDatabaseRestore + path: github.com/oracle/oracle-database-operator/apis/database/v1alpha1 + version: v1alpha1 + webhooks: + validation: true + webhookVersion: v1beta1 - api: crdVersion: v1 namespaced: true @@ -38,4 +71,90 @@ 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 + controller: true + domain: oracle.com + group: database + kind: PDB + path: github.com/oracle/oracle-database-operator/apis/database/v1alpha1 + version: v1alpha1 + webhooks: + defaulting: true + validation: true + webhookVersion: v1 +- api: + crdVersion: v1 + namespaced: true + controller: true + domain: oracle.com + group: database + kind: CDB + path: github.com/oracle/oracle-database-operator/apis/database/v1alpha1 + version: v1alpha1 + webhooks: + defaulting: true + validation: true + webhookVersion: v1 +- api: + crdVersion: v1beta1 + namespaced: true + controller: true + domain: oracle.com + group: database + kind: OracleRestDataService + path: github.com/oracle/oracle-database-operator/apis/database/v1alpha1 + version: v1alpha1 +- api: + crdVersion: v1beta1 + namespaced: true + controller: true + domain: oracle.com + group: database + kind: AutonomousContainerDatabase + path: github.com/oracle/oracle-database-operator/apis/database/v1alpha1 + version: v1alpha1 + webhooks: + validation: true + webhookVersion: v1 +- api: + crdVersion: v1beta1 + namespaced: true + controller: true + domain: oracle.com + group: database + kind: DbcsSystem + path: github.com/oracle/oracle-database-operator/apis/database/v1alpha1 + version: v1alpha1 +- api: + crdVersion: v1beta1 + namespaced: true + controller: true + domain: oracle.com + group: database + kind: DataguardBroker + path: github.com/oracle/oracle-database-operator/apis/database/v1alpha1 + version: v1alpha1 + webhooks: + 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 + webhooks: + defaulting: true + validation: true + webhookVersion: v1beta1 version: "3" diff --git a/README.md b/README.md index 99845ab9..cad25dec 100644 --- a/README.md +++ b/README.md @@ -1,40 +1,66 @@ # Oracle Database Operator for Kubernetes -## 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 is announcing _Oracle Database Operator for Kubernetes_ (`OraOperator`). - -Since Oracle Database 19c, Oracle Database images have been supported in containers (Docker, Podman) for production use and Kubernetes deployment with Helm Charts. This release includes Oracle Database Operator, which is a new open source product that extends the Kubernetes API with custom resources and controllers for automating Oracle Database lifecycle management. - -In this release, `OraOperator` supports the following Oracle Database configurations: - -* Oracle Autonomous Database on shared Oracle Cloud Infrastructure (OCI), also known as ADB-S -* Containerized Single Instance databases (SIDB) deployed in the Oracle Kubernetes Engine (OKE) -* Containerized Sharded databases (SHARDED) deployed in OKE - -Oracle will continue to expand Oracle Database Operator support for additional Oracle Database configurations. +## 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. + +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. + +## 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 This release of Oracle Database Operator for Kubernetes (the operator) supports the following lifecycle operations: -* ADB-S: provision, bind, start, stop, terminate (soft/hard), scale (down/up) -* 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) -* SHARDED: provision/deploy sharded databases and the shard topology, add a new shard, delete an existing shard +* 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 +* 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 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 -Upcoming releases will support new configurations, operations and capabilities. +The upcoming releases will support new configurations, operations, and capabilities. ## Release Status -**CAUTION:** The current release of `OraOperator` (v0.1.0) is for development and test only. DO NOT USE IN PRODUCTION. +This production release has been installed and tested on the following Kubernetes platforms: -This release can be deployed on the following platforms: - -* [Oracle Container Engine for Kubernetes (OKE)](https://www.oracle.com/cloud-native/container-engine-kubernetes/) with Kubernetes 1.17 or later -* In an on-premises [Oracle Linux Cloud Native Environment(OLCNE)](https://docs.oracle.com/en/operating-systems/olcne/) 1.3 or later -* [Minikube](https://minikube.sigs.k8s.io/docs/) with version v1.21.0 or later - -In upcoming releases, the operator will be certified against third-party Kubernetes clusters. +* [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/) +* [Amazon Elastic Kubernetes Service](https://aws.amazon.com/eks/) +* [Red Hat OKD](https://www.okd.io/) +* [Red Hat OpenShift](https://www.redhat.com/en/technologies/cloud-computing/openshift/) ## Prerequisites @@ -42,25 +68,76 @@ 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: ```sh - kubectl apply -f https://github.com/jetstack/cert-manager/releases/latest/download/cert-manager.yaml + kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.14.4/cert-manager.yaml ``` -## Quick Install of the Operator +* ### Create Role Bindings for Access Management + + OraOperator supports the following two modes of deployment: + ##### 1. Cluster Scoped Deployment + + This is the default mode, in which OraOperator is deployed to operate in a cluster, and to monitor all the namespaces in the cluster. - 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. Operator pod replicas are set to a default of 3 for High Availability, which can be scaled up and down. + - 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 + ``` - Run the following command + - 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. ```sh - kubectl apply -f oracle-database-operator.yaml + kubectl apply -f rbac/node-rbac.yaml ``` - Ensure that operator pods are up and running +## 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 + ``` + + 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 @@ -76,53 +153,71 @@ 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 Instrunctions](./docs/installation/OPERATOR_INSTALLATION_README.md). +For more details, see [Oracle Database Operator Installation Instructions](./docs/installation/OPERATOR_INSTALLATION_README.md). ## Getting Started with the Operator (Quickstart) -The quickstarts are designed for specific database configurations, including: +The following quickstarts are designed for specific database configurations: * [Oracle Autonomous Database](./docs/adb/README.md) -* [Oracle Database Single Instance configuration](./docs/sidb/README.md) -* [Oracle Database configured with Oracle Sharding](./docs/sharding/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) +* [Oracle Multitenant Database](./docs/multitenant/README.md) +* [Oracle Base Database Cloud Service (BDBCS)](./docs/dbcs/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. +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. ## Uninstall the Operator - To uninstall the operator, the final step consists of deciding whether or not you want to delete the CRDs and APIServices that were introduced to 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: -* ### 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: ```sh + kubectl delete oraclerestdataservice.database.oracle.com --all -n kubectl delete singleinstancedatabase.database.oracle.com --all -n kubectl delete shardingdatabase.database.oracle.com --all -n + kubectl delete dbcssystem.database.oracle.com --all -n kubectl delete autonomousdatabase.database.oracle.com --all -n + kubectl delete autonomousdatabasebackup.database.oracle.com --all -n + kubectl delete autonomousdatabaserestore.database.oracle.com --all -n + kubectl delete autonomouscontainerdatabase.database.oracle.com --all -n + 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, APISerivces and operator deployment. +* ### Delete the RBACs ```sh - kubectl delete -f oracle-database-operator.yaml --ignore-not-found=true + cat rbac/* | kubectl delete -f - ``` - 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, the CRD deletion stops responding, because the CRD instances have finalizers that can only be removed by the operator pod, which is deleted when the APIServices are deleted. +* ### Delete the Deployment -* ### Retaining the CRDs and APIservices - - To delete the operator deployment and retain the CRDs, run the following commands: + 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 - kubectl delete deployment.apps/oracle-database-operator-controller-manager -n oracle-database-operator-system + kubectl delete -f oracle-database-operator.yaml --ignore-not-found=true ``` -## Documentation + 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. + +## 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) * [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 Database Cloud Service](https://docs.oracle.com/en/database/database-cloud-services.html) ## Contributing @@ -130,29 +225,30 @@ See [Contributing to this Repository](./CONTRIBUTING.md) ## Support -You can submit a GitHub issue, or you can also file an [Oracle Support service](https://support.oracle.com/portal/) request, using the 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 Secure platforms are an important basis for general system security. Ensure that your deployment is in compliance with common security practices. ### Managing Sensitive Data + Kubernetes secrets are the usual means for storing credentials or passwords input for access. The operator reads the Secrets programmatically, which limits exposure of sensitive data. However, to protect your sensitive data, Oracle strongly recommends that you set and get sensitive data from Oracle Cloud Infrastructure Vault, or from third-party Vaults. The following is an example of a YAML file fragment for specifying Oracle Cloud Infrastructure Vault as the repository for the admin password. - ``` + +```yaml adminPassword: ociSecretOCID: ocid1.vaultsecret.oc1... ``` + 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) 2021 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/) diff --git a/SECURITY.md b/SECURITY.md index 5acefa28..fb238413 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,32 +1,36 @@ -# Reporting Security Vulnerabilities +# Reporting security vulnerabilities -Oracle values the independent security research community, and believes that -responsible disclosure of security vulnerabilities helps us to ensure the security -and privacy of all of our users. +Oracle values the independent security research community and believes that +responsible disclosure of security vulnerabilities helps us ensure the security +and privacy of all our users. Please do NOT raise a GitHub Issue to report a security vulnerability. If you -believe you have found a security vulnerability, then please submit a report to +believe you have found a security vulnerability, please submit a report to [secalert_us@oracle.com][1] preferably with a proof of concept. Please review some additional information on [how to report security vulnerabilities to Oracle][2]. -Oracle encourages anyone who contacts Oracle Security to use email encryption, using +We encourage people who contact Oracle Security to use email encryption using [our encryption key][3]. -Please do not use other channels, or contact the project maintainers +We ask that you do not use other channels or contact the project maintainers directly. -For non-vulnerability related security issues, including ideas for new or improved -security features, you are welcome to post these as GitHub Issues. +Non-vulnerability related security issues including ideas for new or improved +security features are welcome on GitHub Issues. -## Security Updates, Alerts and Bulletins +## Security updates, alerts and bulletins -Oracle issues security updates on a regular cadence. Many of our projects typically include release security fixes in conjunction with the [Oracle Critical Patch Update][3] program. Security updates are released on the -Tuesday closest to the 17th day of January, April, July and October. A pre-release announcement will be published on the Thursday preceding each release. Additional information, including past advisories, is available on our [security alerts][4] +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 +information, including past advisories, is available on our [security alerts][4] page. -## Security-Related Information +## Security-related information -Oracle will provide security-related information in our documentation. The information can be a threat model, best practices for secure use, or any known security issues. Please note -that labs and example code are intended to demonstrate a concept. These examples should not be used for production use without ensuring that the code is hardened, and in compliance with common security practices. +We will provide security related information such as a threat model, considerations +for secure use, or any known security issues in our documentation. Please note +that labs and sample code are intended to demonstrate a concept and may not be +sufficiently hardened for production use. [1]: mailto:secalert_us@oracle.com [2]: https://www.oracle.com/corporate/security-practices/assurance/vulnerability/reporting.html diff --git a/THIRD_PARTY_LICENSES.txt b/THIRD_PARTY_LICENSES.txt index 792cde69..d75f3946 100644 --- a/THIRD_PARTY_LICENSES.txt +++ b/THIRD_PARTY_LICENSES.txt @@ -1,7 +1,811 @@ -Operator SDK +------------------------------------- +Operator SDK 1.32.0 https://github.com/operator-framework/operator-sdk +Apache 2.0 + +------------------------------------- + +Apache License: + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + 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 + https://github.com/golang + + + 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 +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------- +apimachinery 0.28.4 +https://github.com/kubernetes/apimachinery/tr +Apache 2.0 + +------------------------- +controller-runtime 0.16.3 +https://github.com/kubernetes-sigs/controller-runtime/releases/tag/v0.16.3 +Apache 2.0 + +------------------------- +golang 1.21.4 +https://github.com/golang/go/releases/tag/go1.21.4 +BSD 2-clause or 3-clause + + +BSD 2-clause or 3-clause License: + +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +------------------------------- Copyright notices -------------------------- +-- various source files +Copyright 2021 The Go Authors. All rights reserved. +-- various go source files under src/cmd/compile/internal/ and src/cmd/link/internal/ +// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved. +// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net) +// Portions Copyright © 1997-1999 Vita Nuova Limited +// Portions Copyright © 2000-2007 Vita Nuova Holdings Limited (www.vitanuova.c\om) +// Portions Copyright © 2004,2006 Bruce Ellis +// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net) +// Revisions Copyright © 2000-2007 Lucent Technologies Inc. and others +// Portions Copyright © 2009 The Go Authors. All rights reserved. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to dea\l +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall 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. +---------------------------- Fourth-party information ---------------------- +== NAME OF DEPENDENCY 1 +github.com/google/pprof +== License Type +Apache 2.0 +== Copyright Notices +--------------------------------(separator)--------------------------------- +== NAME OF DEPENDENCY 2 +github.com/chzyer/readline +== License Type +MIT +== Copyright Notices +The MIT License (MIT) + +Copyright (c) 2015 Chzyer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall 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. +--------------------------------(separator)--------------------------------- +== NAME OF DEPENDENCY 3 +github.com/chzyer/test +== License Type +MIT +== Copyright Notices +The MIT License (MIT) + +Copyright (c) 2016 chzyer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall 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. +--------------------------------(separator)--------------------------------- +== NAME OF DEPENDENCY 4 +github.com/chzyer/logex +== License Type +MIT +== Copyright Notices +The MIT License (MIT) + +Copyright (c) 2015 Chzyer + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall 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. +--------------------------------(separator)--------------------------------- +== NAME OF DEPENDENCY 5 +golang.org/x/sys +== License Type +BSD 3-clause +== Copyright Notices +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------(separator)--------------------------------- +== NAME OF DEPENDENCY 6 +github.com/ianlancetaylor/demangle +== License Type +BSD 3-clause +== Copyright Notices +Copyright (c) 2015 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------(separator)--------------------------------- +== NAME OF DEPENDENCY 7 +golang.org/x/arch +== License Type +BSD 3-clause +== Copyright Notices +Copyright (c) 2015 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------(separator)--------------------------------- +== NAME OF DEPENDENCY 8 +golang.org/x/mod +== License Type +BSD 3-clause +== Copyright Notices +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------(separator)--------------------------------- +== NAME OF DEPENDENCY 9 +golang.org/x/sync +== License Type +BSD 3-clause +== Copyright Notices +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------(separator)--------------------------------- +== NAME OF DEPENDENCY 10 +golang.org/x/sys +== License Type +BSD 3-clause +== Copyright Notices +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------(separator)--------------------------------- +== NAME OF DEPENDENCY 11 +golang.org/x/term +== License Type +BSD 3-clause +== Copyright Notices +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------(separator)--------------------------------- +== NAME OF DEPENDENCY 12 +golang.org/x/tools +== License Type +BSD 3-clause +== Copyright Notices +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------(separator)--------------------------------- +== NAME OF DEPENDENCY 13 +golang.org/x/crypto +== License Type +BSD 3-clause +== Copyright Notices +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------(separator)--------------------------------- +== NAME OF DEPENDENCY 14 +github.com/mmcloughlin/avo +== License Type +BSD 3-clause +== Copyright Notices +BSD 3-Clause License + +Copyright (c) 2018, Michael McLoughlin +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +* Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. + +* Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +* Neither the name of the copyright holder nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------(separator)--------------------------------- +== NAME OF DEPENDENCY 15 +golang.org/x/net +== License Type +BSD 3-clause +== Copyright Notices +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------(separator)--------------------------------- +== NAME OF DEPENDENCY 16 +golang.org/x/text +== License Type +BSD 3-clause +== Copyright Notices +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +--------------------------------(separator)--------------------------------- +== LICENSES +. +== Text of license (Apache 2.0) -Apache License + Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ @@ -202,50 +1006,21 @@ 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 - https://github.com/golang - - 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 -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of Google Inc. nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -------------------------- -Logr +Logr 1.3.0 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 -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 +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 as shown at https://oss.oracle.com/licenses/upl @@ -287,27 +1062,69 @@ The Universal Permissive License (UPL), Version 1.0 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. + + +------------------------- +ginkgo 2.13.1 +https://github.com/onsi/ginkgo/releases/tag/v2.13.1 +MIT +------------------------------------ +Gomega +github.com/onsi/gomega +MIT License +Copyright (c) 2013-2014 Onsi Fakhouri + +---------------------------- +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.4.0 +https://github.com/kubernetes-sigs/yaml/tree/v1.3.0 +MIT + +------------------------- +OCI SDK for Go 65.53.0 +https://github.com/oracle/oci-go-sdk +Multiple Licenses: Apache 2.0, UPL + +------------------------------ +Operator Lifecycle Manager (OLM) +github.com/operator-framework/operator-lifecycle-manager +Apache 2.0 + + +------------------------------------ +Prometheus Operator 0.65.2 +https://github.com/prometheus-operator/prometheus-operator +Apache 2.0 + +------------------------------------ +yaml 3.0.1 +https://github.com/go-yaml/yaml/releases/tag/v3.0.1 +Dual license: Apache 2.0, MIT + YAML support for the Go language https://pkg.go.dev/gopkg.in/yaml.v2 Apache 2.0 @@ -315,8 +1132,14 @@ Apache 2.0 ------------------------------------ YAML marshaling and unmarshaling support for Go https://pkg.go.dev/sigs.k8s.io/yaml -DUal license: BSD-3-Clause, MIT +Dual license: BSD-3-Clause, MIT +------------------------------------ +zap 1.26.0 +https://github.com/uber-go/zap/releases/tag/v1.26.0 +MIT + +------------------------------------ The MIT License (MIT) Copyright (c) 2014 Sam Ghods @@ -369,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 @@ -393,13 +1216,4 @@ 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. --------------------------- -Gomega -github.com/onsi/gomega -MIT License -Copyright (c) 2013-2014 Onsi Fakhouri - ------------------------------------- -Operator Lifecycle Manager (OLM) -github.com/operator-framework/operator-lifecycle-manager -Apache 2.0 diff --git a/apis/database/v1alpha1/adbfamily_common_utils.go b/apis/database/v1alpha1/adbfamily_common_utils.go new file mode 100644 index 00000000..d4d3ae9f --- /dev/null +++ b/apis/database/v1alpha1/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 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 new file mode 100644 index 00000000..abf0ee0d --- /dev/null +++ b/apis/database/v1alpha1/autonomouscontainerdatabase_types.go @@ -0,0 +1,211 @@ +/* +** 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 ( + "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" +) + +// 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 + +// 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{}) +} + +// 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/v1alpha1/autonomouscontainerdatabase_webhook.go b/apis/database/v1alpha1/autonomouscontainerdatabase_webhook.go new file mode 100644 index 00000000..37e1d819 --- /dev/null +++ b/apis/database/v1alpha1/autonomouscontainerdatabase_webhook.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 v1alpha1 + +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-v1alpha1-autonomouscontainerdatabase,mutating=false,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomouscontainerdatabases,versions=v1alpha1,name=vautonomouscontainerdatabase.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 +} + +// 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/v1alpha1/autonomouscontainerdatabase_webhook_test.go b/apis/database/v1alpha1/autonomouscontainerdatabase_webhook_test.go new file mode 100644 index 00000000..668a575d --- /dev/null +++ b/apis/database/v1alpha1/autonomouscontainerdatabase_webhook_test.go @@ -0,0 +1,119 @@ +/* +** 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 ( + "context" + "encoding/json" + "time" + + "github.com/oracle/oci-go-sdk/v65/common" + "github.com/oracle/oci-go-sdk/v65/database" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + // +kubebuilder:scaffold:imports +) + +var _ = Describe("test AutonomousContainerDatabase webhook", func() { + Describe("Test ValidateUpdate of the AutonomousContainerDatabase validating webhook", func() { + var ( + resourceName = "testacd" + namespace = "default" + acdLookupKey = types.NamespacedName{Name: resourceName, Namespace: namespace} + + acd *AutonomousContainerDatabase + + timeout = time.Second * 5 + ) + + BeforeEach(func() { + acd = &AutonomousContainerDatabase{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "database.oracle.com/v1alpha1", + Kind: "AutonomousContainerDatabase", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: resourceName, + Namespace: namespace, + }, + Spec: AutonomousContainerDatabaseSpec{ + AutonomousContainerDatabaseOCID: common.String("fake-acd-ocid"), + CompartmentOCID: common.String("fake-compartment-ocid"), + DisplayName: common.String("fake-displayName"), + AutonomousExadataVMClusterOCID: common.String("fake-vmcluster-ocid"), + PatchModel: database.AutonomousContainerDatabasePatchModelUpdates, + }, + } + + specBytes, err := json.Marshal(acd.Spec) + Expect(err).To(BeNil()) + + anns := map[string]string{ + LastSuccessfulSpec: string(specBytes), + } + acd.SetAnnotations(anns) + + Expect(k8sClient.Create(context.TODO(), acd)).To(Succeed()) + + // Change the lifecycleState to AVAILABLE + acd.Status.LifecycleState = database.AutonomousContainerDatabaseLifecycleStateAvailable + Expect(k8sClient.Status().Update(context.TODO(), acd)).To(Succeed()) + + // Make sure the object is created + Eventually(func() error { + createdACD := &AutonomousContainerDatabase{} + return k8sClient.Get(context.TODO(), acdLookupKey, createdACD) + }, timeout).Should(BeNil()) + }) + + AfterEach(func() { + Expect(k8sClient.Delete(context.TODO(), acd)).To(Succeed()) + }) + + It("Cannot change the spec when the lifecycleState is in an intermdeiate state", func() { + var errMsg string = "cannot change the spec when the lifecycleState is in an intermdeiate state" + + acd.Status.LifecycleState = database.AutonomousContainerDatabaseLifecycleStateProvisioning + Expect(k8sClient.Status().Update(context.TODO(), acd)).To(Succeed()) + + acd.Spec.DisplayName = common.String("modified-display-name") + + validateInvalidTest(acd, true, errMsg) + }) + }) +}) diff --git a/apis/database/v1alpha1/autonomousdatabase_types.go b/apis/database/v1alpha1/autonomousdatabase_types.go index b8759348..cd23b3f3 100644 --- a/apis/database/v1alpha1/autonomousdatabase_types.go +++ b/apis/database/v1alpha1/autonomousdatabase_types.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2022 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -40,18 +40,18 @@ package v1alpha1 import ( "encoding/json" - "strconv" + "reflect" - "github.com/oracle/oci-go-sdk/v51/database" - metaV1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "sigs.k8s.io/controller-runtime/pkg/client" - - "github.com/oracle/oracle-database-operator/commons/annotations" + "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 { @@ -61,83 +61,162 @@ type AutonomousDatabaseSpec struct { HardLink *bool `json:"hardLink,omitempty"` } -type OCIConfigSpec struct { - ConfigMapName *string `json:"configMapName,omitempty"` - SecretName *string `json:"secretName,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" +) + +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"` - DisplayName *string `json:"displayName,omitempty"` - DbName *string `json:"dbName,omitempty"` - // +kubebuilder:validation:Enum:=OLTP;DW;AJD;APEX - DbWorkload database.AutonomousDatabaseDbWorkloadEnum `json:"dbWorkload,omitempty"` - IsDedicated *bool `json:"isDedicated,omitempty"` + 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"` - SubnetOCID *string `json:"subnetOCID,omitempty"` - NsgOCIDs []string `json:"nsgOCIDs,omitempty"` - IsAccessControlEnabled *bool `json:"isAccessControlEnabled,omitempty"` - WhitelistedIPs []string `json:"whitelistedIPs,omitempty"` - IsMTLSConnectionRequired *bool `json:"isMTLSConnectionRequired,omitempty"` - PrivateEndpointLabel *string `json:"privateEndpointLabel,omitempty"` + NetworkAccess NetworkAccessSpec `json:"networkAccess,omitempty"` FreeformTags map[string]string `json:"freeformTags,omitempty"` Wallet WalletSpec `json:"wallet,omitempty"` } -type WalletSpec struct { - Name *string `json:"name,omitempty"` - Password PasswordSpec `json:"password,omitempty"` -} - -type PasswordSpec struct { - K8sSecretName *string `json:"k8sSecretName,omitempty"` - OCISecretOCID *string `json:"ociSecretOCID,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 - DisplayName string `json:"displayName,omitempty"` LifecycleState database.AutonomousDatabaseLifecycleStateEnum `json:"lifecycleState,omitempty"` - IsDedicated string `json:"isDedicated,omitempty"` - CPUCoreCount int `json:"cpuCoreCount,omitempty"` - DataStorageSizeInTBs int `json:"dataStorageSizeInTBs,omitempty"` - DbWorkload database.AutonomousDatabaseDbWorkloadEnum `json:"dbWorkload,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" +) + +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=".status.displayName",name="Display Name",type=string +// +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=".status.isDedicated",name="Dedicated",type=string -// +kubebuilder:printcolumn:JSONPath=".status.cpuCoreCount",name="OCPUs",type=integer -// +kubebuilder:printcolumn:JSONPath=".status.dataStorageSizeInTBs",name="Storage (TB)",type=integer -// +kubebuilder:printcolumn:JSONPath=".status.dbWorkload",name="Workload Type",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 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"` } -// LastSuccessfulSpec is an annotation key which maps to the value of last successful spec -const LastSuccessfulSpec string = "lastSuccessfulSpec" +// +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{}) +} // GetLastSuccessfulSpec returns spec from the lass successful reconciliation. // Returns nil, nil if there is no lastSuccessfulSpec. @@ -158,65 +237,154 @@ func (adb *AutonomousDatabase) GetLastSuccessfulSpec() (*AutonomousDatabaseSpec, return &sucSpec, nil } -// UpdateLastSuccessfulSpec updates lastSuccessfulSpec with the current spec. -func (adb *AutonomousDatabase) UpdateLastSuccessfulSpec(kubeClient client.Client) error { +func (adb *AutonomousDatabase) UpdateLastSuccessfulSpec() error { specBytes, err := json.Marshal(adb.Spec) if err != nil { return err } - anns := map[string]string{ - LastSuccessfulSpec: string(specBytes), + anns := adb.GetAnnotations() + + if anns == nil { + anns = map[string]string{ + LastSuccessfulSpec: string(specBytes), + } + } else { + anns[LastSuccessfulSpec] = string(specBytes) } - return annotations.SetAnnotations(kubeClient, adb, anns) + adb.SetAnnotations(anns) + + return nil } -// UpdateAttrFromOCIAutonomousDatabase updates the attributes from database.AutonomousDatabase object and returns the resource -func (adb *AutonomousDatabase) UpdateAttrFromOCIAutonomousDatabase(ociObj database.AutonomousDatabase) *AutonomousDatabase { +// 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.IsDedicated = ociObj.IsDedicated + 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.LifecycleState = ociObj.LifecycleState - adb.Spec.Details.FreeformTags = ociObj.FreeformTags - - adb.Spec.Details.SubnetOCID = ociObj.SubnetId - adb.Spec.Details.NsgOCIDs = ociObj.NsgIds - adb.Spec.Details.IsAccessControlEnabled = ociObj.IsAccessControlEnabled - adb.Spec.Details.WhitelistedIPs = ociObj.WhitelistedIps - adb.Spec.Details.IsMTLSConnectionRequired = ociObj.IsMtlsConnectionRequired - adb.Spec.Details.PrivateEndpointLabel = ociObj.PrivateEndpointLabel - - // update the subresource as well - adb.Status.DisplayName = *ociObj.DisplayName - adb.Status.LifecycleState = ociObj.LifecycleState - adb.Status.IsDedicated = strconv.FormatBool(*ociObj.IsDedicated) - adb.Status.CPUCoreCount = *ociObj.CpuCoreCount - adb.Status.DataStorageSizeInTBs = *ociObj.DataStorageSizeInTBs - adb.Status.DbWorkload = ociObj.DbWorkload - adb.Status.TimeCreated = ociObj.TimeCreated.String() + 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 + } - return adb -} + // 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 + } + } -// +kubebuilder:object:root=true + 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 + } -// AutonomousDatabaseList contains a list of AutonomousDatabase -type AutonomousDatabaseList struct { - metaV1.TypeMeta `json:",inline"` - metaV1.ListMeta `json:"metadata,omitempty"` - Items []AutonomousDatabase `json:"items"` + /*********************************** + * update the status subresource + ***********************************/ + adb.UpdateStatusFromOCIADB(ociObj) + + return !reflect.DeepEqual(oldADB.Spec, adb.Spec) } -func init() { - SchemeBuilder.Register(&AutonomousDatabase{}, &AutonomousDatabaseList{}) +// 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. diff --git a/apis/database/v1alpha1/autonomousdatabase_webhook.go b/apis/database/v1alpha1/autonomousdatabase_webhook.go new file mode 100644 index 00000000..b25e8104 --- /dev/null +++ b/apis/database/v1alpha1/autonomousdatabase_webhook.go @@ -0,0 +1,293 @@ +/* +** 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 ( + "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-v1alpha1-autonomousdatabase,mutating=true,failurePolicy=fail,sideEffects=None,groups=database.oracle.com,resources=autonomousdatabases,versions=v1alpha1,name=mautonomousdatabase.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=vautonomousdatabase.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) + + // TODO(user): fill in your validation logic upon object deletion. + 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/v1alpha1/autonomousdatabase_webhook_test.go b/apis/database/v1alpha1/autonomousdatabase_webhook_test.go new file mode 100644 index 00000000..ee26021f --- /dev/null +++ b/apis/database/v1alpha1/autonomousdatabase_webhook_test.go @@ -0,0 +1,310 @@ +/* +** 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 ( + "context" + "encoding/json" + "time" + + "github.com/oracle/oci-go-sdk/v65/common" + "github.com/oracle/oci-go-sdk/v65/database" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + // +kubebuilder:scaffold:imports +) + +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" + namespace = "default" + + 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{ + 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"), + }, + }, + DataStorageSizeInTBs: common.Int(1), + }, + }, + } + }) + + // Common validation + It("Should not apply values to adminPassword.k8sSecret and adminPassword.ociSecret at the same time", 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...") + + validateInvalidTest(adb, false, errMsg) + }) + + 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...") + + 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") + }) + + 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"} + + validateInvalidTest(adb, false, errMsg) + }) + + 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) + + 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 + Describe("Test ValidateUpdate of the AutonomousDatabase validating webhook", func() { + var ( + resourceName = "testadb" + namespace = "default" + adbLookupKey = types.NamespacedName{Name: resourceName, Namespace: namespace} + + adb *AutonomousDatabase + + timeout = time.Second * 5 + ) + + 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{ + 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, + }, + }, + } + + specBytes, err := json.Marshal(adb.Spec) + Expect(err).To(BeNil()) + + anns := map[string]string{ + LastSuccessfulSpec: string(specBytes), + } + adb.SetAnnotations(anns) + + Expect(k8sClient.Create(context.TODO(), adb)).To(Succeed()) + + // Change the lifecycleState to AVAILABLE + adb.Status.LifecycleState = database.AutonomousDatabaseLifecycleStateAvailable + Expect(k8sClient.Status().Update(context.TODO(), adb)).To(Succeed()) + + // Make sure the object is created + Eventually(func() error { + createdADB := &AutonomousDatabase{} + return k8sClient.Get(context.TODO(), adbLookupKey, createdADB) + }, timeout).Should(BeNil()) + }) + + AfterEach(func() { + Expect(k8sClient.Delete(context.TODO(), adb)).To(Succeed()) + }) + + It("Cannot change the spec when the lifecycleState is in an intermdeiate state", func() { + var errMsg string = "cannot change the spec when the lifecycleState is in an intermdeiate state" + + adb.Status.LifecycleState = database.AutonomousDatabaseLifecycleStateUpdating + Expect(k8sClient.Status().Update(context.TODO(), adb)).To(Succeed()) + + adb.Spec.Details.DbName = common.String("modified-db-name") + + validateInvalidTest(adb, true, errMsg) + }) + + 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) + + validateInvalidTest(adb, true, errMsg) + }) + }) +}) diff --git a/apis/database/v1alpha1/autonomousdatabasebackup_types.go b/apis/database/v1alpha1/autonomousdatabasebackup_types.go new file mode 100644 index 00000000..95c77560 --- /dev/null +++ b/apis/database/v1alpha1/autonomousdatabasebackup_types.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 v1alpha1 + +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 + +// 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{}) +} + +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/v1alpha1/autonomousdatabasebackup_webhook.go b/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go new file mode 100644 index 00000000..99bf3815 --- /dev/null +++ b/apis/database/v1alpha1/autonomousdatabasebackup_webhook.go @@ -0,0 +1,163 @@ +/* +** 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" +) + +// 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() +} + +// 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} + +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} + +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/v1alpha1/autonomousdatabasebackup_webhook_test.go b/apis/database/v1alpha1/autonomousdatabasebackup_webhook_test.go new file mode 100644 index 00000000..497c3f28 --- /dev/null +++ b/apis/database/v1alpha1/autonomousdatabasebackup_webhook_test.go @@ -0,0 +1,172 @@ +/* +** 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 ( + "context" + "time" + + "github.com/oracle/oci-go-sdk/v65/common" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + // +kubebuilder:scaffold:imports +) + +var _ = Describe("test AutonomousDatabaseBackup webhook", func() { + Describe("Test ValidateCreate of the AutonomousDatabaseBackup validating webhook", func() { + var ( + resourceName = "testadbbackup" + namespace = "default" + + backup *AutonomousDatabaseBackup + ) + + BeforeEach(func() { + backup = &AutonomousDatabaseBackup{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "database.oracle.com/v1alpha1", + Kind: "AutonomousDatabaseBackup", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: resourceName, + Namespace: namespace, + }, + Spec: AutonomousDatabaseBackupSpec{ + Target: TargetSpec{}, + }, + } + }) + + 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 + + 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.OCID = common.String("fake.ocid1.autonomousdatabase.oc1...") + + validateInvalidTest(backup, false, errMsg) + }) + }) + + Describe("Test ValidateUpdate of the AutonomousDatabaseBackup validating webhook", func() { + var ( + resourceName = "testadbbackup" + namespace = "default" + backupLookupKey = types.NamespacedName{Name: resourceName, Namespace: namespace} + + backup *AutonomousDatabaseBackup + + timeout = time.Second * 5 + ) + + BeforeEach(func() { + backup = &AutonomousDatabaseBackup{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "database.oracle.com/v1alpha1", + Kind: "AutonomousDatabaseBackup", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: resourceName, + Namespace: namespace, + }, + Spec: AutonomousDatabaseBackupSpec{ + AutonomousDatabaseBackupOCID: common.String("fake.ocid1.autonomousdatabasebackup.oc1..."), + DisplayName: common.String("fake-displayName"), + }, + } + }) + + JustBeforeEach(func() { + Expect(k8sClient.Create(context.TODO(), backup)).To(Succeed()) + + // Make sure the object is created + Eventually(func() error { + createdBackup := &AutonomousDatabaseBackup{} + return k8sClient.Get(context.TODO(), backupLookupKey, createdBackup) + }, timeout).Should(BeNil()) + }) + + AfterEach(func() { + Expect(k8sClient.Delete(context.TODO(), backup)).To(Succeed()) + }) + + Context("The bakcup is using target.k8sADB.name", func() { + BeforeEach(func() { + 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") + + validateInvalidTest(backup, true, errMsg) + }) + + It("Cannot assign a new displayName to this backup", func() { + var errMsg string = "cannot assign a new displayName to this backup" + + backup.Spec.DisplayName = common.String("modified-displayName") + + validateInvalidTest(backup, true, errMsg) + }) + }) + + Context("The bakcup is using target.ociADB.ocid", func() { + BeforeEach(func() { + 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...") + + validateInvalidTest(backup, true, errMsg) + }) + }) + }) +}) diff --git a/apis/database/v1alpha1/autonomousdatabaserestore_types.go b/apis/database/v1alpha1/autonomousdatabaserestore_types.go new file mode 100644 index 00000000..4bea0043 --- /dev/null +++ b/apis/database/v1alpha1/autonomousdatabaserestore_types.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 v1alpha1 + +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 + +// 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{}) +} + +// 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/v1alpha1/autonomousdatabaserestore_webhook.go b/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go new file mode 100644 index 00000000..4f96bd7b --- /dev/null +++ b/apis/database/v1alpha1/autonomousdatabaserestore_webhook.go @@ -0,0 +1,149 @@ +/* +** 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" +) + +// 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() +} + +// 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} + +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/v1alpha1/autonomousdatabaserestore_webhook_test.go b/apis/database/v1alpha1/autonomousdatabaserestore_webhook_test.go new file mode 100644 index 00000000..aed87d71 --- /dev/null +++ b/apis/database/v1alpha1/autonomousdatabaserestore_webhook_test.go @@ -0,0 +1,115 @@ +/* +** 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 ( + "github.com/oracle/oci-go-sdk/v65/common" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + // +kubebuilder:scaffold:imports +) + +var _ = Describe("test AutonomousDatabaseRestore webhook", func() { + Describe("Test ValidateCreate of the AutonomousDatabaseRestore validating webhook", func() { + var ( + resourceName = "testadbrestore" + namespace = "default" + + restore *AutonomousDatabaseRestore + ) + + BeforeEach(func() { + restore = &AutonomousDatabaseRestore{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "database.oracle.com/v1alpha1", + Kind: "AutonomousDatabaseRestore", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: resourceName, + Namespace: namespace, + }, + Spec: AutonomousDatabaseRestoreSpec{ + Target: TargetSpec{}, + }, + } + }) + + 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 + + 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" + + 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) + }) + + 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.PointInTime.Timestamp = nil + + validateInvalidTest(restore, false, errMsg) + }) + + 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.PointInTime.Timestamp = common.String("2021-12-23 11:03:13 UTC") + + validateInvalidTest(restore, false, errMsg) + }) + + It("Invalid timestamp format", func() { + var errMsg string = "invalid timestamp format" + + restore.Spec.Source.PointInTime.Timestamp = common.String("12/23/2021 11:03:13") + + validateInvalidTest(restore, false, errMsg) + }) + }) +}) diff --git a/apis/database/v1alpha1/cdb_types.go b/apis/database/v1alpha1/cdb_types.go new file mode 100644 index 00000000..206781b2 --- /dev/null +++ b/apis/database/v1alpha1/cdb_types.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 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"` + DBTnsurl string `json:"dbTnsurl,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"` +} + +// 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.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: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/cdb_webhook.go b/apis/database/v1alpha1/cdb_webhook.go new file mode 100644 index 00000000..345b6f75 --- /dev/null +++ b/apis/database/v1alpha1/cdb_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 v1alpha1 + +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-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} + +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-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} + +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 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/v1alpha1/dataguardbroker_types.go b/apis/database/v1alpha1/dataguardbroker_types.go new file mode 100644 index 00000000..37d71b92 --- /dev/null +++ b/apis/database/v1alpha1/dataguardbroker_types.go @@ -0,0 +1,119 @@ +/* +** 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 v1alpha1 + +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 DataguardBrokerFastStartFailOver `json:"fastStartFailOver,omitempty"` +} + +type DataguardBrokerFastStartFailOver struct { + Enable bool `json:"enable,omitempty"` + Strategy []DataguardBrokerStrategy `json:"strategy,omitempty"` +} + +// FSFO strategy +type DataguardBrokerStrategy struct { + SourceDatabaseRef string `json:"sourceDatabaseRef,omitempty"` + TargetDatabaseRefs string `json:"targetDatabaseRefs,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"` +} + +//+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" + +// DataguardBroker is the Schema for the dataguardbrokers API +type DataguardBroker struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec DataguardBrokerSpec `json:"spec,omitempty"` + Status DataguardBrokerStatus `json:"status,omitempty"` +} + +//+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/v1alpha1/dataguardbroker_webhook.go b/apis/database/v1alpha1/dataguardbroker_webhook.go new file mode 100644 index 00000000..a9d59286 --- /dev/null +++ b/apis/database/v1alpha1/dataguardbroker_webhook.go @@ -0,0 +1,173 @@ +/* +** 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 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" + "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 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! + +//+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{} + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (r *DataguardBroker) Default() { + dataguardbrokerlog.Info("default", "name", r.Name) + + if r.Spec.LoadBalancer { + if r.Spec.ServiceAnnotations == nil { + r.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"] + 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 { + 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 { + r.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape-flex-max"] = "100" + } + } +} + +// 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{} + +// 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")) + } + + if len(allErrs) == 0 { + 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 +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() + if err != nil { + return nil, err + } + + // Validate Deletion + if r.GetDeletionTimestamp() != nil { + warnings, err := r.ValidateDelete() + if err != nil { + return warnings, err + } + } + + // Now check for updation errors + oldObj, ok := old.(*DataguardBroker) + if !ok { + return nil, nil + } + + if oldObj.Status.ProtectionMode != "" && !strings.EqualFold(r.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) { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("primaryDatabaseRef"), "cannot be changed")) + } + + if len(allErrs) == 0 { + return nil, nil + } + 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() (admission.Warnings, error) { + dataguardbrokerlog.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/dbcssystem_types.go b/apis/database/v1alpha1/dbcssystem_types.go new file mode 100644 index 00000000..37e80a6b --- /dev/null +++ b/apis/database/v1alpha1/dbcssystem_types.go @@ -0,0 +1,230 @@ +/* +** 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 ( + "encoding/json" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + 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"` + HardLink bool `json:"hardLink,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"` + 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"` + KmsKeyId string `json:"kmsKeyId,omitempty"` + KmsKeyVersionId string `json:"kmsKeyVersionId,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"` +} + +// 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"` +} + +// 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"` +} + +// +kubebuilder:object:root=true +// +kubebuilder:subresource:status +// +kubebuilder:resource:path=DbcsSystem,scope=Namespaced + +// 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 +} + +// 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/v1alpha1/groupversion_info.go b/apis/database/v1alpha1/groupversion_info.go index 60029108..3c4b1804 100644 --- a/apis/database/v1alpha1/groupversion_info.go +++ b/apis/database/v1alpha1/groupversion_info.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2022 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -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/oraclerestdataservice_types.go b/apis/database/v1alpha1/oraclerestdataservice_types.go new file mode 100644 index 00000000..37f61a8a --- /dev/null +++ b/apis/database/v1alpha1/oraclerestdataservice_types.go @@ -0,0 +1,152 @@ +/* +** 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 v1alpha1 + +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"` + 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"` + + // +k8s:openapi-gen=true + // +kubebuilder:validation:Minimum=1 + Replicas int `json:"replicas,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"` +} + +// 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"` + 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"` + + 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" + +// OracleRestDataService is the Schema for the oraclerestdataservices API +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/v1alpha1/oraclerestdataservice_webhook.go b/apis/database/v1alpha1/oraclerestdataservice_webhook.go new file mode 100644 index 00000000..bfe3208c --- /dev/null +++ b/apis/database/v1alpha1/oraclerestdataservice_webhook.go @@ -0,0 +1,184 @@ +/* +** 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 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" +) + +// 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! + +//+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{} + +// Default implements webhook.Defaulter so a webhook will be registered for the type +func (r *OracleRestDataService) Default() { + oraclerestdataservicelog.Info("default", "name", r.Name) + // OracleRestDataService Currently supports single replica + r.Spec.Replicas = 1 + keepSecret := true + 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 + } +} + +// 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{} + +// ValidateCreate implements webhook.Validator so a webhook will be registered for the type +func (r *OracleRestDataService) ValidateCreate() (admission.Warnings, error) { + oraclerestdataservicelog.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.VolumeName != "") { + allErrs = append(allErrs, + field.Invalid(field.NewPath("spec").Child("persistence").Child("size"), r.Spec.Persistence, + "invalid persistence specification, specify required size")) + } + + if r.Spec.Persistence.Size != "" { + if r.Spec.Persistence.AccessMode == "" { + allErrs = append(allErrs, + field.Invalid(field.NewPath("spec").Child("persistence").Child("size"), r.Spec.Persistence, + "invalid persistence specification, specify accessMode")) + } + if r.Spec.Persistence.AccessMode != "ReadWriteMany" && r.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\"")) + } + } + + // Validating databaseRef and ORDS kind name not to be same + if r.Spec.DatabaseRef == r.Name { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("Name"), + "cannot be same as DatabaseRef: "+r.Spec.DatabaseRef)) + + } + + if len(allErrs) == 0 { + return nil, nil + } + 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) (admission.Warnings, error) { + oraclerestdataservicelog.Info("validate update", "name", r.Name) + + var allErrs field.ErrorList + + // check creation validations first + warnings, err := r.ValidateCreate() + if err != nil { + return warnings, err + } + + // Now check for updation errors + old, ok := oldRuntimeObject.(*OracleRestDataService) + if !ok { + return nil, nil + } + + if old.Status.DatabaseRef != "" && old.Status.DatabaseRef != r.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 { + allErrs = append(allErrs, + field.Forbidden(field.NewPath("spec").Child("image"), "cannot be changed")) + } + + if len(allErrs) == 0 { + return nil, nil + } + 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() (admission.Warnings, error) { + oraclerestdataservicelog.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/pdb_types.go b/apis/database/v1alpha1/pdb_types.go new file mode 100644 index 00000000..8de9db52 --- /dev/null +++ b/apis/database/v1alpha1/pdb_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 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"` +} + +// 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=".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: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"` +} + +func init() { + SchemeBuilder.Register(&PDB{}, &PDBList{}) +} diff --git a/apis/database/v1alpha1/pdb_webhook.go b/apis/database/v1alpha1/pdb_webhook.go new file mode 100644 index 00000000..1577198e --- /dev/null +++ b/apis/database/v1alpha1/pdb_webhook.go @@ -0,0 +1,335 @@ +/* +** 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 v1alpha1 + +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-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} + +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-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} + +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)")) + } + + switch action { + 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")) + } + 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) + } + 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")) + } + } +} + +// 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.")) + } + +} diff --git a/apis/database/v1alpha1/shardingdatabase_types.go b/apis/database/v1alpha1/shardingdatabase_types.go index c372b991..ffc17ab0 100644 --- a/apis/database/v1alpha1/shardingdatabase_types.go +++ b/apis/database/v1alpha1/shardingdatabase_types.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2022 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -58,26 +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. - 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 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 @@ -88,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 @@ -106,6 +124,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 +142,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"` @@ -140,17 +168,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"` + 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 @@ -162,7 +195,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"` @@ -174,16 +207,80 @@ 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. 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"` + 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. @@ -266,7 +363,7 @@ const ( // var var KubeConfigOnce sync.Once -const lastSuccessfulSpec = "lastSuccessfulSpec" +// #const lastSuccessfulSpec = "lastSuccessfulSpec" // GetLastSuccessfulSpec returns spec from the lass successful reconciliation. // Returns nil, nil if there is no lastSuccessfulSpec. @@ -298,7 +395,7 @@ func (shardingv1 *ShardingDatabase) UpdateLastSuccessfulSpec(kubeClient client.C lastSuccessfulSpec: string(specBytes), } - return annsv1.SetAnnotations(kubeClient, shardingv1, anns) + return annsv1.PatchAnnotations(kubeClient, shardingv1, anns) } func init() { 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/apis/database/v1alpha1/singleinstancedatabase_types.go b/apis/database/v1alpha1/singleinstancedatabase_types.go index 7afb7ae6..7c6c1ea5 100644 --- a/apis/database/v1alpha1/singleinstancedatabase_types.go +++ b/apis/database/v1alpha1/singleinstancedatabase_types.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2023 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -50,42 +50,65 @@ 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 + // +kubebuilder:validation:Enum=standard;enterprise;express;free Edition string `json:"edition,omitempty"` - // SID can only have a-z , A-Z, 0-9 . It cant have any special characters + // 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]+$` - Sid string `json:"sid,omitempty"` - InstallApex bool `json:"installApex,omitempty"` - Charset string `json:"charset,omitempty"` - Pdbname string `json:"pdbName,omitempty"` - LoadBalancer bool `json:"loadBalancer,omitempty"` - FlashBack bool `json:"flashBack,omitempty"` - ArchiveLog bool `json:"archiveLog,omitempty"` - ForceLogging bool `json:"forceLog,omitempty"` - - CloneFrom string `json:"cloneFrom,omitempty"` + // +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"` + DgBrokerConfigured bool `json:"dgBrokerConfigured,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"` // +k8s:openapi-gen=true - // +kubebuilder:validation:Minimum=1 - Replicas int `json:"replicas"` + Replicas int `json:"replicas,omitempty"` NodeSelector map[string]string `json:"nodeSelector,omitempty"` - AdminPassword SingleInstanceDatabaseAdminPassword `json:"adminPassword"` + AdminPassword SingleInstanceDatabaseAdminPassword `json:"adminPassword,omitempty"` Image SingleInstanceDatabaseImage `json:"image"` - Persistence SingleInstanceDatabasePersistence `json:"persistence"` - InitParams SingleInstanceDatabaseInitParams `json:"initParams,omitempty"` + 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 type SingleInstanceDatabasePersistence struct { - Size string `json:"size"` - StorageClass string `json:"storageClass"` - + Size string `json:"size,omitempty"` + StorageClass string `json:"storageClass,omitempty"` // +kubebuilder:validation:Enum=ReadWriteOnce;ReadWriteMany - AccessMode string `json:"accessMode"` + 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 @@ -101,13 +124,15 @@ 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"` - SecretKey string `json:"secretKey"` - KeepSecret bool `json:"keepSecret,omitempty"` + // +kubebuilder:default:="oracle_pwd" + SecretKey string `json:"secretKey,omitempty"` + KeepSecret *bool `json:"keepSecret,omitempty"` } // SingleInstanceDatabaseStatus defines the observed state of SingleInstanceDatabase @@ -115,30 +140,44 @@ 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"` - ReleaseUpdate string `json:"releaseUpdate,omitempty"` + 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"` + // +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"` - 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"` - CloneFrom string `json:"cloneFrom,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"` - ApexInstalled bool `json:"apexInstalled,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"` + DgBrokerConfigured bool `json:"dgBrokerConfigured,omitempty"` + // +kubebuilder:default:="" + TcpsTlsSecret string `json:"tcpsTlsSecret"` // +patchMergeKey=type // +patchStrategy=merge @@ -154,11 +193,14 @@ type SingleInstanceDatabaseStatus struct { //+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",priority=1 +// +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 diff --git a/apis/database/v1alpha1/singleinstancedatabase_webhook.go b/apis/database/v1alpha1/singleinstancedatabase_webhook.go index 1f7b44bb..1a47207d 100644 --- a/apis/database/v1alpha1/singleinstancedatabase_webhook.go +++ b/apis/database/v1alpha1/singleinstancedatabase_webhook.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2023 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -39,7 +39,9 @@ package v1alpha1 import ( + "strconv" "strings" + "time" dbcommons "github.com/oracle/oracle-database-operator/commons/database" @@ -50,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. @@ -71,10 +74,67 @@ var _ webhook.Defaulter = &SingleInstanceDatabase{} func (r *SingleInstanceDatabase) Default() { singleinstancedatabaselog.Info("default", "name", r.Name) - if r.Spec.Edition == "express" { - r.Spec.Replicas = 1 + if r.Spec.LoadBalancer { + // Annotations required for a flexible load balancer on oci + if r.Spec.ServiceAnnotations == nil { + r.Spec.ServiceAnnotations = make(map[string]string) + } + _, ok := r.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape"] + 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 { + 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 { + r.Spec.ServiceAnnotations["service.beta.kubernetes.io/oci-load-balancer-shape-flex-max"] = "100" + } + } + + if r.Spec.AdminPassword.KeepSecret == nil { + keepSecret := true + r.Spec.AdminPassword.KeepSecret = &keepSecret + } + + if r.Spec.Edition == "" { + 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" + } else if r.Spec.Edition == "free" { + r.Spec.Sid = "FREE" + } else { + r.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" + } else { + r.Spec.Pdbname = "ORCLPDB1" + } + } + + if r.Spec.Edition == "express" || r.Spec.Edition == "free" { + // 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 + } } - // TODO(user): fill in your defaulting logic. } // TODO(user): change verbs to "verbs=create;update;delete" if you want to enable deletion validation. @@ -83,67 +143,347 @@ 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 - if r.Spec.Persistence.AccessMode == "ReadWriteOnce" && r.Spec.Replicas != 1 { + 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("spec").Child("replicas"), r.Spec.Replicas, - "should be 1 for accessMode \"ReadWriteOnce\"")) + field.Invalid(field.NewPath("metadata").Child("namespace"), r.Namespace, + "Oracle database operator doesn't watch over this namespace")) } - if r.Spec.Edition == "express" && r.Spec.CloneFrom != "" { + + // Persistence spec validation + if r.Spec.Persistence.Size == "" && (r.Spec.Persistence.AccessMode != "" || + r.Spec.Persistence.StorageClass != "" || r.Spec.Persistence.DatafilesVolumeName != "") { allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("cloneFrom"), r.Spec.CloneFrom, - "Cloning not supported for Express edition")) + field.Invalid(field.NewPath("spec").Child("persistence").Child("size"), r.Spec.Persistence, + "invalid persistence specification, specify required size")) + } + + if r.Spec.Persistence.Size != "" { + if r.Spec.Persistence.AccessMode == "" { + allErrs = append(allErrs, + field.Invalid(field.NewPath("spec").Child("persistence").Child("size"), r.Spec.Persistence, + "invalid persistence specification, specify accessMode")) + } + if r.Spec.Persistence.AccessMode != "ReadWriteMany" && r.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\"")) + } } - if r.Spec.Edition == "express" && strings.ToUpper(r.Spec.Sid) != "XE" { + + if r.Spec.CreateAs == "standby" { + 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")) + } + 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")) + } + 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 + if r.Spec.Replicas > 1 { + valMsg := "" + if r.Spec.Edition == "express" || r.Spec.Edition == "free" { + valMsg = "should be 1 for " + r.Spec.Edition + " edition" + } + if r.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)) + } + } + + if (r.Spec.CreateAs == "clone" || r.Spec.CreateAs == "standby") && r.Spec.PrimaryDatabaseRef == "" { allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("sid"), r.Spec.Sid, - "Express edition SID must be XE")) + 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.CreateAs == "clone" { + allErrs = append(allErrs, + field.Invalid(field.NewPath("spec").Child("createAs"), r.Spec.CreateAs, + "Cloning not supported for "+r.Spec.Edition+" edition")) + } + if r.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")) + } + if r.Spec.Edition == "express" && strings.ToUpper(r.Spec.Sid) != "XE" { + allErrs = append(allErrs, + field.Invalid(field.NewPath("spec").Child("sid"), r.Spec.Sid, + "Express edition SID must only be XE")) + } + if r.Spec.Edition == "free" && strings.ToUpper(r.Spec.Sid) != "FREE" { + allErrs = append(allErrs, + field.Invalid(field.NewPath("spec").Child("sid"), r.Spec.Sid, + "Free edition SID must only be FREE")) + } + if r.Spec.Edition == "express" && strings.ToUpper(r.Spec.Pdbname) != "XEPDB1" { + allErrs = append(allErrs, + field.Invalid(field.NewPath("spec").Child("pdbName"), r.Spec.Pdbname, + "Express edition PDB must be XEPDB1")) + } + if r.Spec.Edition == "free" && strings.ToUpper(r.Spec.Pdbname) != "FREEPDB1" { + allErrs = append(allErrs, + field.Invalid(field.NewPath("spec").Child("pdbName"), r.Spec.Pdbname, + "Free edition PDB must be FREEPDB1")) + } + if r.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")) + } + } else { + if r.Spec.Sid == "XE" { + allErrs = append(allErrs, + field.Invalid(field.NewPath("spec").Child("sid"), r.Spec.Sid, + "XE is reserved as the SID for Express edition of the database")) + } + if r.Spec.Sid == "FREE" { + allErrs = append(allErrs, + field.Invalid(field.NewPath("spec").Child("sid"), r.Spec.Sid, + "FREE is reserved as the SID for FREE edition of the database")) + } } - if r.Spec.Edition == "express" && strings.ToUpper(r.Spec.Pdbname) != "XEPDB1" { + + if r.Spec.CreateAs == "clone" { + if r.Spec.Image.PrebuiltDB { + allErrs = append(allErrs, + field.Invalid(field.NewPath("spec").Child("createAs"), r.Spec.CreateAs, + "cannot clone to create a prebuilt db")) + } 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.CreateAs, + "Edition must be passed when cloning from a source database other than same k8s cluster")) + } + } + + 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 != 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.")) + } + } + + if r.Spec.Persistence.VolumeClaimAnnotation != "" { + strParts := strings.Split(r.Spec.Persistence.VolumeClaimAnnotation, ":") + if len(strParts) != 2 { + allErrs = append(allErrs, + field.Invalid(field.NewPath("spec").Child("persistence").Child("volumeClaimAnnotation"), r.Spec.Persistence.VolumeClaimAnnotation, + "volumeClaimAnnotation should be in : format.")) + } + } + + // servicePort and tcpServicePort validation + if !r.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) { + allErrs = append(allErrs, + field.Invalid(field.NewPath("spec").Child("listenerPort"), r.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) { + allErrs = append(allErrs, + field.Invalid(field.NewPath("spec").Child("tcpsListenerPort"), r.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) { + allErrs = append(allErrs, + field.Invalid(field.NewPath("spec").Child("listenerPort"), r.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 { allErrs = append(allErrs, - field.Invalid(field.NewPath("spec").Child("pdbName"), r.Spec.Pdbname, - "Express edition PDB must be XEPDB1")) + field.Invalid(field.NewPath("spec").Child("tcpsListenerPort"), r.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 err != nil { + allErrs = append(allErrs, + field.Invalid(field.NewPath("spec").Child("tcpsCertRenewInterval"), r.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, + "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 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 + 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 } - edition := r.Spec.Edition - if r.Spec.Edition == "" { - edition = "Enterprise" + + 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) + 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 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 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 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")) + } + } + } + + // 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) { + 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, 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")) } @@ -159,37 +499,37 @@ 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, 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 != "" { allErrs = append(allErrs, - field.Forbidden(field.NewPath("status").Child("ordsInstalled"), "uninstall ORDS to cleanup this SIDB")) + 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 new file mode 100644 index 00000000..e28925e6 --- /dev/null +++ b/apis/database/v1alpha1/webhook_suite_test.go @@ -0,0 +1,212 @@ +/* +** 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 ( + "context" + "crypto/tls" + "encoding/json" + "errors" + "fmt" + "net" + "path/filepath" + "testing" + "time" + + "github.com/onsi/ginkgo/v2" + "github.com/onsi/gomega" + + admissionv1 "k8s.io/api/admission/v1" + //+kubebuilder:scaffold:imports + k8sErrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + 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" +) + +// To avoid dot import +var ( + BeforeSuite = ginkgo.BeforeSuite + AfterSuite = ginkgo.AfterSuite + Describe = ginkgo.Describe + PDescribe = ginkgo.PDescribe + JustBeforeEach = ginkgo.JustBeforeEach + BeforeEach = ginkgo.BeforeEach + AfterEach = ginkgo.AfterEach + Context = ginkgo.Context + By = ginkgo.By + It = ginkgo.It + FIt = ginkgo.FIt + PIt = ginkgo.PIt + Eventually = gomega.Eventually + Expect = gomega.Expect + Succeed = gomega.Succeed + HaveOccurred = gomega.HaveOccurred + BeNil = gomega.BeNil + Equal = gomega.Equal + BeTrue = gomega.BeTrue + BeFalse = gomega.BeFalse + ContainSubstring = gomega.ContainSubstring +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. + +var k8sClient client.Client +var testEnv *envtest.Environment +var ctx context.Context +var cancel context.CancelFunc + +func TestAPIs(t *testing.T) { + gomega.RegisterFailHandler(ginkgo.Fail) + ginkgo.RunSpecs(t, "Webhook Suite") +} + +var _ = BeforeSuite(func() { + logf.SetLogger(zap.New(zap.WriteTo(ginkgo.GinkgoWriter), zap.UseDevMode(true))) + + ctx, cancel = context.WithCancel(context.TODO()) + + 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")}, + }, + } + + cfg, err := testEnv.Start() + Expect(err).NotTo(HaveOccurred()) + Expect(cfg).NotTo(BeNil()) + + scheme := runtime.NewScheme() + err = AddToScheme(scheme) + Expect(err).NotTo(HaveOccurred()) + + err = admissionv1.AddToScheme(scheme) + Expect(err).NotTo(HaveOccurred()) + + //+kubebuilder:scaffold:scheme + + k8sClient, err = client.New(cfg, client.Options{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, + WebhookServer: webhook.NewServer(webhook.Options{ + Port: webhookInstallOptions.LocalServingPort, + Host: webhookInstallOptions.LocalServingHost, + CertDir: webhookInstallOptions.LocalServingCertDir, + }), + LeaderElection: false, + Metrics: metricsserver.Options{ + BindAddress: "0", + }, + }) + Expect(err).NotTo(HaveOccurred()) + + err = (&AutonomousDatabase{}).SetupWebhookWithManager(mgr) + Expect(err).NotTo(HaveOccurred()) + + err = (&AutonomousDatabaseBackup{}).SetupWebhookWithManager(mgr) + Expect(err).NotTo(HaveOccurred()) + + err = (&AutonomousDatabaseRestore{}).SetupWebhookWithManager(mgr) + Expect(err).NotTo(HaveOccurred()) + + err = (&AutonomousContainerDatabase{}).SetupWebhookWithManager(mgr) + Expect(err).NotTo(HaveOccurred()) + + //+kubebuilder:scaffold:webhook + + go func() { + defer ginkgo.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 + } + conn.Close() + return nil + }).Should(Succeed()) +}) + +var _ = AfterSuite(func() { + cancel() + By("tearing down the test environment") + err := testEnv.Stop() + Expect(err).NotTo(HaveOccurred()) +}) + +func validateInvalidTest(obj client.Object, isUpdate bool, expMsgList ...string) { + var err error + + if !isUpdate { + err = k8sClient.Create(context.TODO(), obj) + } else { + err = k8sClient.Update(context.TODO(), obj) + } + + jsonBytes, jsonErr := json.MarshalIndent(obj, "", " ") + Expect(jsonErr).ToNot(HaveOccurred()) + Expect(err).To(HaveOccurred(), "%s: %v", obj.GetObjectKind().GroupVersionKind().Kind, string(jsonBytes)) + + statusErr := &k8sErrors.StatusError{} + Expect(errors.As(err, &statusErr)).To(BeTrue()) + + for _, msg := range expMsgList { + Expect(statusErr.ErrStatus.Message).To(ContainSubstring(msg)) + } +} diff --git a/apis/database/v1alpha1/zz_generated.deepcopy.go b/apis/database/v1alpha1/zz_generated.deepcopy.go index 4d27bb59..10b34ca7 100644 --- a/apis/database/v1alpha1/zz_generated.deepcopy.go +++ b/apis/database/v1alpha1/zz_generated.deepcopy.go @@ -1,7 +1,8 @@ +//go:build !ignore_autogenerated // +build !ignore_autogenerated /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2022 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -49,7 +50,24 @@ import ( ) // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *AutonomousDatabase) DeepCopyInto(out *AutonomousDatabase) { +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) @@ -57,6 +75,128 @@ func (in *AutonomousDatabase) DeepCopyInto(out *AutonomousDatabase) { 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 { @@ -75,6 +215,117 @@ func (in *AutonomousDatabase) DeepCopyObject() runtime.Object { 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 @@ -88,6 +339,7 @@ func (in *AutonomousDatabaseDetails) DeepCopyInto(out *AutonomousDatabaseDetails *out = new(string) **out = **in } + in.AutonomousContainerDatabase.DeepCopyInto(&out.AutonomousContainerDatabase) if in.DisplayName != nil { in, out := &in.DisplayName, &out.DisplayName *out = new(string) @@ -98,11 +350,6 @@ func (in *AutonomousDatabaseDetails) DeepCopyInto(out *AutonomousDatabaseDetails *out = new(string) **out = **in } - if in.IsDedicated != nil { - in, out := &in.IsDedicated, &out.IsDedicated - *out = new(bool) - **out = **in - } if in.DbVersion != nil { in, out := &in.DbVersion, &out.DbVersion *out = new(string) @@ -124,36 +371,12 @@ func (in *AutonomousDatabaseDetails) DeepCopyInto(out *AutonomousDatabaseDetails *out = new(bool) **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.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.IsMTLSConnectionRequired != nil { - in, out := &in.IsMTLSConnectionRequired, &out.IsMTLSConnectionRequired + if in.IsDedicated != nil { + in, out := &in.IsDedicated, &out.IsDedicated *out = new(bool) **out = **in } - if in.PrivateEndpointLabel != nil { - in, out := &in.PrivateEndpointLabel, &out.PrivateEndpointLabel - *out = new(string) - **out = **in - } + in.NetworkAccess.DeepCopyInto(&out.NetworkAccess) if in.FreeformTags != nil { in, out := &in.FreeformTags, &out.FreeformTags *out = make(map[string]string, len(*in)) @@ -207,217 +430,1623 @@ func (in *AutonomousDatabaseList) DeepCopyObject() runtime.Object { } // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. -func (in *AutonomousDatabaseSpec) DeepCopyInto(out *AutonomousDatabaseSpec) { +func (in *AutonomousDatabaseRestore) DeepCopyInto(out *AutonomousDatabaseRestore) { *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 - } + 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 AutonomousDatabaseSpec. -func (in *AutonomousDatabaseSpec) DeepCopy() *AutonomousDatabaseSpec { +// 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(AutonomousDatabaseSpec) + out := new(AutonomousDatabaseRestore) 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 +// 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 } -// 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) +// 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 + 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 *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 + 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 *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 *DataguardBroker) DeepCopyInto(out *DataguardBroker) { + *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 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 *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 + 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 + 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 + } + } + in.FastStartFailOver.DeepCopyInto(&out.FastStartFailOver) +} + +// 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 *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 + 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) +} + +// 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 + } +} + +// 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]) + } + } +} + +// 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 *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 *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 *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 + 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 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 *CatalogSpec) DeepCopyInto(out *CatalogSpec) { +func (in *OracleRestDataServiceImage) DeepCopyInto(out *OracleRestDataServiceImage) { *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) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new OracleRestDataServiceImage. +func (in *OracleRestDataServiceImage) DeepCopy() *OracleRestDataServiceImage { + if in == nil { + return nil } - 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 := 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 + 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]) } } - if in.PvAnnotations != nil { - in, out := &in.PvAnnotations, &out.PvAnnotations +} + +// 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 *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 +} + +// 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.PvMatchLabels != nil { - in, out := &in.PvMatchLabels, &out.PvMatchLabels + 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.ImagePulllPolicy != nil { - in, out := &in.ImagePulllPolicy, &out.ImagePulllPolicy - *out = new(corev1.PullPolicy) - **out = **in + 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 + *out = make([]OracleRestDataServiceRestEnableSchemas, len(*in)) + copy(*out, *in) } + out.Persistence = in.Persistence } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CatalogSpec. -func (in *CatalogSpec) DeepCopy() *CatalogSpec { +// 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(CatalogSpec) + 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 *EnvironmentVariable) DeepCopyInto(out *EnvironmentVariable) { +func (in *OracleRestDataServiceStatus) DeepCopyInto(out *OracleRestDataServiceStatus) { *out = *in + out.Image = in.Image } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EnvironmentVariable. -func (in *EnvironmentVariable) DeepCopy() *EnvironmentVariable { +// 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(EnvironmentVariable) + 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 *GsmSpec) DeepCopyInto(out *GsmSpec) { +func (in *PDB) DeepCopyInto(out *PDB) { *out = *in - if in.EnvVars != nil { - in, out := &in.EnvVars, &out.EnvVars - *out = make([]EnvironmentVariable, len(*in)) - copy(*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 } - if in.Resources != nil { - in, out := &in.Resources, &out.Resources - *out = new(corev1.ResourceRequirements) - (*in).DeepCopyInto(*out) + 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 } - 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 - } + 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 } - 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 + 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 *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]) } } - if in.ImagePulllPolicy != nil { - in, out := &in.ImagePulllPolicy, &out.ImagePulllPolicy - *out = new(corev1.PullPolicy) +} + +// 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 } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GsmSpec. -func (in *GsmSpec) DeepCopy() *GsmSpec { +// 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(GsmSpec) + 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 *GsmStatus) DeepCopyInto(out *GsmStatus) { +func (in *PDBTLSCRT) DeepCopyInto(out *PDBTLSCRT) { *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 - } - } + out.Secret = in.Secret } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GsmStatus. -func (in *GsmStatus) DeepCopy() *GsmStatus { +// 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(GsmStatus) + 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 *GsmStatusDetails) DeepCopyInto(out *GsmStatusDetails) { +func (in *PDBTLSKEY) DeepCopyInto(out *PDBTLSKEY) { *out = *in + out.Secret = in.Secret } -// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new GsmStatusDetails. -func (in *GsmStatusDetails) DeepCopy() *GsmStatusDetails { +// 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(GsmStatusDetails) + 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 *OCIConfigSpec) DeepCopyInto(out *OCIConfigSpec) { +func (in *PITSpec) DeepCopyInto(out *PITSpec) { *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 + 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 OCIConfigSpec. -func (in *OCIConfigSpec) DeepCopy() *OCIConfigSpec { +// 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(OCIConfigSpec) + out := new(PITSpec) in.DeepCopyInto(out) return out } @@ -425,16 +2054,8 @@ func (in *OCIConfigSpec) DeepCopy() *OCIConfigSpec { // 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 - if in.K8sSecretName != nil { - in, out := &in.K8sSecretName, &out.K8sSecretName - *out = new(string) - **out = **in - } - if in.OCISecretOCID != nil { - in, out := &in.OCISecretOCID, &out.OCISecretOCID - *out = new(string) - **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. @@ -462,6 +2083,51 @@ 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 +} + +// 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 @@ -601,6 +2267,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. @@ -680,6 +2371,11 @@ func (in *SingleInstanceDatabase) DeepCopyObject() runtime.Object { // 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. @@ -757,6 +2453,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. @@ -769,9 +2470,71 @@ 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 + 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.NodeSelector != nil { in, out := &in.NodeSelector, &out.NodeSelector *out = make(map[string]string, len(*in)) @@ -779,10 +2542,15 @@ func (in *SingleInstanceDatabaseSpec) DeepCopyInto(out *SingleInstanceDatabaseSp (*out)[key] = val } } - out.AdminPassword = in.AdminPassword + in.AdminPassword.DeepCopyInto(&out.AdminPassword) out.Image = in.Image - out.Persistence = in.Persistence - out.InitParams = in.InitParams + 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. @@ -818,7 +2586,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. @@ -831,6 +2599,107 @@ func (in *SingleInstanceDatabaseStatus) DeepCopy() *SingleInstanceDatabaseStatus 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 *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 + 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 + 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 +} + // 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 @@ -851,3 +2720,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/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/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/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..39b438eb --- /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" + "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/bundle.Dockerfile b/bundle.Dockerfile index 4ca3c52f..d591c4ef 100644 --- a/bundle.Dockerfile +++ b/bundle.Dockerfile @@ -1,4 +1,4 @@ -# Copyright (c) 2021, 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 https://oss.oracle.com/licenses/upl. # diff --git a/commons/adb_family/utils.go b/commons/adb_family/utils.go new file mode 100644 index 00000000..8218502e --- /dev/null +++ b/commons/adb_family/utils.go @@ -0,0 +1,73 @@ +/* +** 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 adbfamily + +import ( + dbv1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + "github.com/oracle/oracle-database-operator/commons/k8s" + "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. +// 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) { + var err error + var ownerADB *dbv1alpha1.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{} + 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) + if err != nil { + return nil, err + } + + } + + return ownerADB, nil +} diff --git a/commons/annotations/annotations.go b/commons/annotations/annotations.go index aa4c4e15..b0196156 100644 --- a/commons/annotations/annotations.go +++ b/commons/annotations/annotations.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2022 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -53,8 +53,9 @@ type PatchValue struct { Value interface{} `json:"value"` } -// SetAnnotations attaches the given metadata to the target object -func SetAnnotations(kubeClient client.Client, obj client.Object, anns map[string]string) error { +// PatchAnnotations attaches the given metadata to the target object +// The obj will be updated with the content returned by the cluster +func PatchAnnotations(kubeClient client.Client, obj client.Object, anns map[string]string) error { payload := []PatchValue{} if obj.GetAnnotations() == nil { diff --git a/commons/database/constants.go b/commons/database/constants.go index fac4719f..6f27750d 100644 --- a/commons/database/constants.go +++ b/commons/database/constants.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2022 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -38,13 +38,17 @@ package commons +const CONTAINER_LISTENER_PORT int32 = 1521 + +const CONTAINER_TCPS_PORT int32 = 2484 + const ORACLE_UID int64 = 54321 const ORACLE_GUID int64 = 54321 const DBA_GUID int64 = 54322 -const NoCloneRef string = "Unavailable" +const SQLPlusCLI string = "sqlplus -s / as sysdba" const GetVersionSQL string = "SELECT VERSION_FULL FROM V\\$INSTANCE;" @@ -58,9 +62,6 @@ const RemoveChkFileCMD string = "rm -f \"${ORACLE_BASE}/oradata/.${ORACLE_SID}.n const CreateDBRecoveryDestCMD string = "mkdir -p ${ORACLE_BASE}/oradata/fast_recovery_area" -const ConfigureOEMSQL string = "exec DBMS_XDB_CONFIG.SETHTTPSPORT(5500);" + - "\nalter system register;" - const SetDBRecoveryDestSQL string = "SHOW PARAMETER db_recovery_file_dest;" + "\nALTER SYSTEM SET db_recovery_file_dest_size=50G scope=both sid='*';" + "\nALTER SYSTEM SET db_recovery_file_dest='${ORACLE_BASE}/oradata/fast_recovery_area' scope=both sid='*';" + @@ -91,25 +92,287 @@ const ArchiveLogFalseCMD string = CreateChkFileCMD + " && " + "echo -e \"SHUTDOWN IMMEDIATE; \n STARTUP MOUNT; \n ALTER DATABASE NOARCHIVELOG; \n SELECT log_mode FROM v\\$database; \n ALTER DATABASE OPEN;" + " \n ALTER PLUGGABLE DATABASE ALL OPEN; \n ALTER SYSTEM REGISTER;\" | %s && " + RemoveChkFileCMD -const GetDatabaseRoleCMD string = "SELECT DATABASE_ROLE FROM V\\$DATABASE; " +const StandbyDatabasePrerequisitesSQL string = "ALTER SYSTEM SET db_create_file_dest='/opt/oracle/oradata/';" + + "\nALTER SYSTEM SET db_create_online_log_dest_1='/opt/oracle/oradata/';" + + "\nALTER SYSTEM SWITCH LOGFILE;" + + "\nALTER DATABASE ADD STANDBY LOGFILE THREAD 1 SIZE 200M;" + + "\nALTER DATABASE ADD STANDBY LOGFILE THREAD 1 SIZE 200M;" + + "\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;" + +const ModifyStdbyDBOpenMode string = "alter database recover managed standby database disconnect;" + +const StandbyTnsnamesEntry string = ` +##STANDBYDATABASE_SID## = +(DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = ##STANDBYDATABASE_SERVICE_EXPOSED## )(PORT = 1521)) + (CONNECT_DATA = + (SERVER = DEDICATED) + (SERVICE_NAME = ##STANDBYDATABASE_SID##) + ) +) +` +const PDBTnsnamesEntry string = ` +##PDB_NAME## = +(DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = 0.0.0.0 )(PORT = 1521)) + (CONNECT_DATA = + (SERVER = DEDICATED) + (SERVICE_NAME = ##PDB_NAME##) + ) +) +` + +const PrimaryTnsnamesEntry string = ` +${PRIMARY_SID} = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = TCP)(HOST = ${PRIMARY_IP})(PORT = 1521 )) + (CONNECT_DATA = + (SERVER = DEDICATED) + (SERVICE_NAME = ${PRIMARY_SID}) + ) + ) + ` + +const ListenerEntry string = `LISTENER = +(DESCRIPTION_LIST = + (DESCRIPTION = + (ADDRESS = (PROTOCOL = IPC)(KEY = EXTPROC1)) + (ADDRESS = (PROTOCOL = TCP)(HOST = 0.0.0.0)(PORT = 1521)) + ) +) +SID_LIST_LISTENER = + (SID_LIST = + (SID_DESC = + (GLOBAL_DBNAME = ${ORACLE_SID^^}) + (SID_NAME = ${ORACLE_SID^^}) + (ORACLE_HOME = ${ORACLE_HOME}) + ) + (SID_DESC = + (GLOBAL_DBNAME = DATAGUARD) + (SID_NAME = ${ORACLE_SID^^}) + (ORACLE_HOME = ${ORACLE_HOME}) + ) + (SID_DESC = + (GLOBAL_DBNAME = ${ORACLE_SID^^}_DGMGRL) + (SID_NAME = ${ORACLE_SID^^}) + (ORACLE_HOME = ${ORACLE_HOME}) + (ENVS="TNS_ADMIN=/opt/oracle/oradata/dbconfig/${ORACLE_SID^^}") + ) + ) + +DEDICATED_THROUGH_BROKER_LISTENER=ON +` + +const CreateAdminPasswordFile string = "umask 177\n cat > admin.pwd < dgmgrl.cmd\n umask 022" + +const RemoveAdminPasswordFile string = "rm -rf admin.pwd" + +const RemoveDGMGRLScriptFile string = "rm -rf dgmgrl.cmd" + +const DataguardBrokerMaxPerformanceCMD string = "CREATE CONFIGURATION dg_config AS PRIMARY DATABASE IS ${PRIMARY_SID} CONNECT IDENTIFIER IS ${PRIMARY_DB_CONN_STR};" + + "\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;" + +const DataguardBrokerMaxAvailabilityCMD string = "CREATE CONFIGURATION dg_config AS PRIMARY DATABASE IS ${PRIMARY_SID} CONNECT IDENTIFIER IS ${PRIMARY_DB_CONN_STR};" + + "\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;" + +const DataguardBrokerAddDBMaxPerformanceCMD string = "ADD DATABASE ${ORACLE_SID} AS CONNECT IDENTIFIER IS ${SVC_HOST}:1521/${ORACLE_SID} MAINTAINED AS PHYSICAL;" + + "\nEDIT DATABASE ${ORACLE_SID} SET PROPERTY LogXptMode='ASYNC';" + + "\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)))';" + + "\nENABLE CONFIGURATION;" + +const DataguardBrokerAddDBMaxAvailabilityCMD string = "ADD DATABASE ${ORACLE_SID} AS CONNECT IDENTIFIER IS ${SVC_HOST}:1521/${ORACLE_SID} MAINTAINED AS PHYSICAL;" + + "\nEDIT DATABASE ${ORACLE_SID} SET PROPERTY LogXptMode='SYNC';" + + "\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)))';" + + "\nENABLE CONFIGURATION;" + +const RemoveStandbyDBFromDGConfgCMD string = "DISABLE DATABASE ${ORACLE_SID};" + + "\nREMOVE DATABASE ${ORACLE_SID};" + +const DBShowConfigCMD string = "SHOW CONFIGURATION;" const DataguardBrokerGetDatabaseCMD string = "SELECT DATABASE || ':' || DATAGUARD_ROLE AS DATABASE FROM V\\$DG_BROKER_CONFIG;" +const EnableFSFOCMD string = "ENABLE FAST_START FAILOVER;" + +const RemoveDataguardConfiguration string = "DISABLE FAST_START FAILOVER;" + + "\nEDIT CONFIGURATION SET PROTECTION MODE AS MAXPERFORMANCE;" + + "\nREMOVE CONFIGURATION;" + +const GetDatabaseRoleCMD string = "SELECT DATABASE_ROLE FROM V\\$DATABASE; " + const RunDatapatchCMD string = " ( while true; do sleep 60; echo \"Installing patches...\" ; done ) & if ! $ORACLE_HOME/OPatch/datapatch -skip_upgrade_check;" + " then echo \"Datapatch execution has failed.\" ; else echo \"DONE: Datapatch execution.\" ; fi ; kill -9 $!;" -const GetSqlpatchDescriptionSQL string = "select TARGET_VERSION || ' (' || PATCH_ID || ')' as patchinfo from dba_registry_sqlpatch order by action_time desc;" +const GetSqlpatchDescriptionSQL string = "select TARGET_VERSION || ' (' || ACTION || ' of ' || PATCH_ID || ')' as patchinfo from dba_registry_sqlpatch order by action_time desc;" const GetSqlpatchStatusSQL string = "select status from dba_registry_sqlpatch order by action_time desc;" const GetSqlpatchVersionSQL string = "select SOURCE_VERSION || ':' || TARGET_VERSION as versions from dba_registry_sqlpatch order by action_time desc;" -const GetCheckpointFileCMD string = "find ${ORACLE_BASE}/oradata -name .${ORACLE_SID}${CHECKPOINT_FILE_EXTN} " +const GetCheckpointFileCMD string = "find ${ORACLE_BASE}/oradata -maxdepth 1 -name .${ORACLE_SID}${CHECKPOINT_FILE_EXTN}" const GetEnterpriseEditionFileCMD string = "if [ -f ${ORACLE_BASE}/oradata/dbconfig/$ORACLE_SID/.docker_enterprise ]; then ls ${ORACLE_BASE}/oradata/dbconfig/$ORACLE_SID/.docker_enterprise; fi " const GetStandardEditionFileCMD string = "if [ -f ${ORACLE_BASE}/oradata/dbconfig/$ORACLE_SID/.docker_standard ]; then ls ${ORACLE_BASE}/oradata/dbconfig/$ORACLE_SID/.docker_standard; fi " +const CreateSIDlinkCMD string = "cd ${ORACLE_BASE}/oradata && test ! -e $ORACLE_SID && ln -s $(basename $PRIMARY_DB_CONN_STR)/$ORACLE_SID" + +const GetPdbsSQL string = "select name from v\\$pdbs where name not like 'PDB\\$SEED' and open_mode like 'READ WRITE';" + +const OpenPDBSeed = "alter pluggable database pdb\\$seed close;" + + "\nalter pluggable database pdb\\$seed open read only;" + +const SetAdminUsersSQL string = "CREATE USER C##DBAPI_CDB_ADMIN IDENTIFIED BY \\\"%[1]s\\\" ACCOUNT UNLOCK CONTAINER=ALL;" + + "\nalter user C##DBAPI_CDB_ADMIN identified by \\\"%[1]s\\\" account unlock;" + + "\nGRANT DBA TO C##DBAPI_CDB_ADMIN CONTAINER = ALL;" + + "\nGRANT PDB_DBA TO C##DBAPI_CDB_ADMIN CONTAINER = ALL;" + + "\nCREATE USER C##_DBAPI_PDB_ADMIN IDENTIFIED BY \\\"%[1]s\\\" CONTAINER=ALL ACCOUNT UNLOCK;" + + "\nalter user C##_DBAPI_PDB_ADMIN identified by \\\"%[1]s\\\" account unlock;" + + "\nGRANT DBA TO C##_DBAPI_PDB_ADMIN CONTAINER = ALL;" + + "\nalter pluggable database pdb\\$seed close;" + + "\nalter pluggable database pdb\\$seed open read write force;" + +const GetUserORDSSchemaStatusSQL string = "alter session set container=%[2]s;" + + "\nselect 'STATUS:'||status as status from ords_metadata.ords_schemas where upper(parsing_schema) = upper('%[1]s');" + +const CreateORDSSchemaSQL = "\nALTER SESSION SET CONTAINER=%[3]s;" + + "\nCREATE USER %[1]s IDENTIFIED BY \\\"%[2]s\\\";" + + "\nGRANT CONNECT, RESOURCE, DBA, PDB_DBA TO %[1]s;" + +const EnableORDSSchemaSQL string = "\nALTER SESSION SET CONTAINER=%[4]s;" + + "\nGRANT INHERIT PRIVILEGES ON USER SYS TO ORDS_METADATA;" + + "\nexec ORDS.enable_schema(p_enabled => %[2]s ,p_schema => '%[1]s',p_url_mapping_type => 'BASE_PATH',p_url_mapping_pattern => '%[3]s',p_auto_rest_auth => FALSE);" + + // SetupORDSCMD is run only for the FIRST TIME, ORDS is installed. Once ORDS is installed, we delete the pod that ran SetupORDSCMD and create new ones. + // Newly created pod doesn't run this SetupORDSCMD. +const SetupORDSCMD string = "$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-property database.api.enabled true" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-property jdbc.auth.enabled true" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-property database.api.management.services.disabled false" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-property database.api.admin.enabled true" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-property dbc.auth.enabled true" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-property restEnabledSql.active true" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-property db.serviceNameSuffix \"\" " + // Mandatory when ORDS Installing at CDB Level -> Maps PDB's + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-property jdbc.InitialLimit 5" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-property jdbc.MaxLimit 20" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-property jdbc.InactivityTimeout 300" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-property feature.sdw true" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-property security.verifySSL false" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-property jdbc.maxRows 1000" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-property pdb.open.asneeded true" + + "\numask 177" + + "\necho db.cdb.adminUser=C##DBAPI_CDB_ADMIN AS SYSDBA > cdbAdmin.properties" + + "\necho db.cdb.adminUser.password=\"%[4]s\" >> cdbAdmin.properties" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-properties --conf apex_pu cdbAdmin.properties" + + "\nrm -f cdbAdmin.properties" + + "\necho db.username=APEX_LISTENER > apexlistener" + + "\necho db.password=\"%[2]s\" >> apexlistener" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-properties --conf apex_al apexlistener" + + "\nrm -f apexlistener" + + "\necho db.username=APEX_REST_PUBLIC_USER > apexRestPublicUser" + + "\necho db.password=\"%[2]s\" >> apexRestPublicUser" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-properties --conf apex_rt apexRestPublicUser" + + "\nrm -f apexRestPublicUser" + + "\necho db.username=APEX_PUBLIC_USER > apexPublicUser" + + "\necho db.password=\"%[2]s\" >> apexPublicUser" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-properties --conf apex apexPublicUser" + + "\nrm -f apexPublicUser" + + "\necho db.adminUser=C##_DBAPI_PDB_ADMIN > pdbAdmin.properties" + + "\necho db.adminUser.password=\"%[4]s\">> pdbAdmin.properties" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-properties --conf apex_pu pdbAdmin.properties" + + "\nrm -f pdbAdmin.properties" + + "\necho -e \"%[1]s\n%[1]s\" > sqladmin.passwd" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war user ${ORDS_USER} \"SQL Administrator , System Administrator , SQL Developer , oracle.dbtools.autorest.any.schema \" < sqladmin.passwd" + + "\nrm -f sqladmin.passwd" + + "\numask 022" + + "\nsed -i 's,jetty.port=8888,jetty.secure.port=8443\\nssl.cert=\\nssl.cert.key=\\nssl.host=%[3]s,g' /opt/oracle/ords/config/ords/standalone/standalone.properties " + + "\nsed -i 's,standalone.static.path=/opt/oracle/ords/doc_root/i,standalone.static.path=/opt/oracle/ords/config/apex/images,g' /opt/oracle/ords/config/ords/standalone/standalone.properties" + +const InitORDSCMD string = "if [ -f $ORDS_HOME/config/ords/defaults.xml ]; then exit ;fi;" + + "\nexport APEXI=$ORDS_HOME/config/apex/images" + + "\n$ORDS_HOME/runOrds.sh --setuponly" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-property database.api.enabled true" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-property jdbc.auth.enabled true" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-property database.api.management.services.disabled false" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-property database.api.admin.enabled true" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-property dbc.auth.enabled true" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-property restEnabledSql.active true" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-property db.serviceNameSuffix \"\" " + // Mandatory when ORDS Installing at CDB Level -> Maps PDB's + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-property jdbc.InitialLimit 5" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-property jdbc.MaxLimit 20" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-property jdbc.InactivityTimeout 300" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-property feature.sdw true" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-property security.verifySSL false" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-property jdbc.maxRows 1000" + + "\nmkdir -p $ORDS_HOME/config/ords/conf" + + "\numask 177" + + "\necho db.cdb.adminUser=C##DBAPI_CDB_ADMIN AS SYSDBA > cdbAdmin.properties" + + "\necho db.cdb.adminUser.password=\"${ORACLE_PWD}\" >> cdbAdmin.properties" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-properties --conf apex_pu cdbAdmin.properties" + + "\nrm -f cdbAdmin.properties" + + "\necho db.adminUser=C##_DBAPI_PDB_ADMIN > pdbAdmin.properties" + + "\necho db.adminUser.password=\"${ORACLE_PWD}\">> pdbAdmin.properties" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-properties --conf apex_pu pdbAdmin.properties" + + "\nrm -f pdbAdmin.properties" + + "\necho -e \"${ORDS_PWD}\n${ORDS_PWD}\" > sqladmin.passwd" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war user ${ORDS_USER} \"SQL Administrator , System Administrator , SQL Developer , oracle.dbtools.autorest.any.schema \" < sqladmin.passwd" + + "\nrm -f sqladmin.passwd" + + "\numask 022" + +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 " + + "s.username = 'APEX_REST_PUBLIC_USER' or " + + "s.username = 'APEX_LISTENER' or " + + "s.username = 'C##_DBAPI_CDB_ADMIN' or " + + "s.username = 'C##_DBAPI_PDB_ADMIN' ) AND p.addr(+) = s.paddr;" + +const KillSessionSQL string = "alter system kill session '%[1]s';" + +const DropAdminUsersSQL string = "drop user C##DBAPI_CDB_ADMIN cascade;" + + "\ndrop user C##_DBAPI_PDB_ADMIN cascade;" + +const UninstallORDSCMD string = "\numask 177" + + "\necho -e \"1\n${ORACLE_HOST}\n${ORACLE_PORT}\n1\n${ORACLE_SERVICE}\nsys\n%[1]s\n%[1]s\n1\" > ords.cred" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war uninstall advanced < ords.cred" + + "\nrm -f ords.cred" + + "\numask 022" + + "\nrm -f /opt/oracle/ords/config/ords/defaults.xml" + + "\nrm -f /opt/oracle/ords/config/ords/credentials" + + "\nrm -rf /opt/oracle/ords/config/ords/conf" + + "\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 ValidateAdminPassword string = "conn sys/\\\"%s\\\"@${ORACLE_SID} as sysdba\nshow user" + const ReconcileError string = "ReconcileError" const ReconcileErrorReason string = "LastReconcileCycleFailed" @@ -140,7 +403,7 @@ const StatusReady string = "Healthy" const StatusError string = "Error" -const ValueUnavailable string = "Unknown" +const ValueUnavailable string = "Unavailable" const NoExternalIp string = "Node ExternalIP unavailable" @@ -156,22 +419,134 @@ const WalletEntriesCMD string = "umask 177\ncat > wallet.passwd < /dev/null; do sleep 0.5; done; 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 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 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;" -const UnzipApex 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;" +const UnzipApexOnORDSPod string = "if [ -f /opt/oracle/ords/config/ords/apex-latest.zip ]; then cd /opt/oracle/ords/config/ords && jar -xf /opt/oracle/ords/config/ords/apex-latest.zip; else echo \"apex-latest.zip not found\"; fi;" const ChownApex string = " chown oracle:oinstall /opt/oracle/oradata/${ORACLE_SID^^}/apex;" 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 IsApexInstalled string = "select 'APEXVERSION:'||version as version FROM DBA_REGISTRY WHERE COMP_ID='APEX';" - -const UninstallApex string = "if [ -f /opt/oracle/oradata/${ORACLE_SID^^}/apex/apxremov.sql ]; then ( while true; do sleep 60; echo \"Uninstalling Apex...\" ; done ) & " + - " cd /opt/oracle/oradata/${ORACLE_SID^^}/apex && echo -e \"@apxremov.sql\" | %[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" + + "@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;" + +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;" + +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 ConfigureApexRest string = "if [ -f ${ORDS_HOME}/config/apex/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;" + + "\n ALTER USER APEX_PUBLIC_USER IDENTIFIED BY \\\"%[1]s\\\" ACCOUNT UNLOCK; " + + "\n ALTER USER APEX_REST_PUBLIC_USER IDENTIFIED BY \\\"%[1]s\\\" ACCOUNT UNLOCK;" + + "\n ALTER USER APEX_LISTENER IDENTIFIED BY \\\"%[1]s\\\" ACCOUNT UNLOCK;" + + "\nexec APEX_UTIL.set_workspace(p_workspace => 'INTERNAL');" + + "\nexec APEX_UTIL.EDIT_USER(p_user_id => APEX_UTIL.GET_USER_ID('ADMIN'), p_user_name => 'ADMIN', p_web_password => '%[1]s', p_new_password => '%[1]s');\n" + +const CopyApexImages string = " ( while true; do sleep 60; echo \"Copying Apex Images...\" ; done ) & mkdir -p /opt/oracle/oradata/${ORACLE_SID^^}_ORDS/apex/images && " + + " cp -R /opt/oracle/oradata/${ORACLE_SID^^}/apex/images/* /opt/oracle/oradata/${ORACLE_SID^^}_ORDS/apex/images; chown -R oracle:oinstall /opt/oracle/oradata/${ORACLE_SID^^}_ORDS/apex; kill -9 $!;" + +const ApexAdmin string = "BEGIN" + + "\napex_util.set_security_group_id(p_security_group_id => 10); APEX_UTIL.REMOVE_USER(p_user_name => 'ADMIN');" + + "\nCOMMIT;" + + "\nEND;" + + "\n/" + + "\nBEGIN" + + "\nAPEX_UTIL.create_user(p_user_name => 'ADMIN',p_email_address => 'admin@oracle.com',p_web_password => '%[1]s',p_developer_privs => 'ADMIN',p_failed_access_attempts => '5' ," + + " p_allow_app_building_yn => 'Y' ,p_allow_sql_workshop_yn => 'Y' ,p_allow_websheet_dev_yn => 'Y' , p_allow_team_development_yn => 'Y' , p_change_password_on_first_use => 'N' );" + + "apex_util.unlock_account(p_user_name => 'ADMIN'); APEX_UTIL.set_security_group_id( null );" + + "\nCOMMIT;" + + "\nEND;" + + "\n/" + + "\nALTER SESSION SET CONTAINER=%[2]s;" + + "\nBEGIN" + + "\napex_util.set_security_group_id(p_security_group_id => 10); APEX_UTIL.REMOVE_USER(p_user_name => 'ADMIN');" + + "\nCOMMIT;" + + "\nEND;" + + "\n/" + + "\nBEGIN" + + "\nAPEX_UTIL.create_user(p_user_name => 'ADMIN',p_email_address => 'admin@oracle.com',p_web_password => '%[1]s',p_developer_privs => 'ADMIN',p_failed_access_attempts => '5' ," + + " p_allow_app_building_yn => 'Y' ,p_allow_sql_workshop_yn => 'Y' ,p_allow_websheet_dev_yn => 'Y' , p_allow_team_development_yn => 'Y' , p_change_password_on_first_use => 'N' );" + + "apex_util.unlock_account(p_user_name => 'ADMIN'); APEX_UTIL.set_security_group_id( null );" + + "\nCOMMIT;" + + "\nEND;" + + "\n/" + +// SetApexUsers is used to set Apex Users, pod that runs SetApexUsers is deleted and new ones is created. +const SetApexUsers string = "\numask 177" + + "\necho db.username=APEX_LISTENER > apexlistener" + + "\necho db.password=\"%[1]s\" >> apexlistener" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-properties --conf apex_al apexlistener" + + "\nrm -f apexlistener" + + "\necho db.username=APEX_REST_PUBLIC_USER > apexRestPublicUser" + + "\necho db.password=\"%[1]s\" >> apexRestPublicUser" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-properties --conf apex_rt apexRestPublicUser" + + "\nrm -f apexRestPublicUser" + + "\necho db.username=APEX_PUBLIC_USER > apexPublicUser" + + "\necho db.password=\"%[1]s\" >> apexPublicUser" + + "\n$JAVA_HOME/bin/java -jar $ORDS_HOME/ords.war set-properties --conf apex apexPublicUser" + + "\nrm -f apexPublicUser" + + "\numask 022" + +// Get Sid, Pdbname, Edition for prebuilt db +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" + +// Command for TCPS certs renewal to prevent their expiry. It is same as the EnableTcpsCMD +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" + +// 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" + +// TCPS clientWallet location +const ClientWalletLocation string = "/opt/oracle/oradata/clientWallet/%s" + +// Service Patch Payloads +// Three port payload: one OEM express, one TCP and one TCPS port +const ThreePortPayload string = "{\"spec\": { \"type\": \"%s\", \"ports\": [{\"name\": \"xmldb\", \"port\": 5500, \"protocol\": \"TCP\"},{%s},{%s}]}}" + +// Two port payload: one OEM express, one TCP/TCPS port +const TwoPortPayload string = "{\"spec\": { \"type\": \"%s\", \"ports\": [{\"name\": \"xmldb\", \"port\": 5500, \"protocol\": \"TCP\"},{%s}]}}" + +// Payload section for listener port +const LsnrPort string = "\"name\": \"listener\", \"protocol\": \"TCP\", \"port\": %d, \"targetPort\": 1521" + +// Payload section for listener node port +const LsnrNodePort string = "\"name\": \"listener\", \"protocol\": \"TCP\", \"port\": 1521, \"nodePort\": %d" + +// Payload section for TCPS port +const TcpsPort string = "\"name\": \"listener-tcps\", \"protocol\": \"TCP\", \"port\": %d, \"targetPort\": 2484" + +// Payload section for TCPS node port +const TcpsNodePort string = "\"name\": \"listener-tcps\", \"protocol\": \"TCP\", \"port\": 2484, \"nodePort\": %d" diff --git a/commons/database/utils.go b/commons/database/utils.go index 4e1a143d..1723bc90 100644 --- a/commons/database/utils.go +++ b/commons/database/utils.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2022 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -45,10 +45,14 @@ import ( "errors" "fmt" "math/rand" + "os" + "strconv" "strings" "time" + "unicode" corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" apierrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/types" @@ -68,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{ @@ -98,6 +104,7 @@ func ResourceEventHandler() predicate.Predicate { if oldStatus != newStatus { return true } + } // Ignore updates to CR status in which case metadata.Generation does not change // Reconcile if object Deletion Timestamp Set @@ -244,9 +251,7 @@ func ExecCommand(r client.Reader, config *rest.Config, podName string, namespace pod := &corev1.Pod{} err := r.Get(ctx, types.NamespacedName{Name: podName, Namespace: namespace}, pod) if err != nil { - return "", fmt.Errorf("could not get pod info: %v", err) - } else { - log.Info("Pod Found", "Name : ", podName) + return "", fmt.Errorf("could not find pod to execute command: %v", err) } client, err := kubernetes.NewForConfig(config) if err != nil { @@ -273,7 +278,7 @@ func ExecCommand(r client.Reader, config *rest.Config, podName string, namespace Tty: false, }) if err != nil { - return "", fmt.Errorf("could not execute: %v", err) + return "", err } if execErr.Len() > 0 { return "", fmt.Errorf("stderr: %v", execErr.String()) @@ -294,13 +299,14 @@ func GenerateRandomString(n int) string { // retuns Ready Pod,No of replicas ( Only running and Pending Pods) ,available pods , Total No of Pods of a particular CRD func FindPods(r client.Reader, version string, image string, name string, namespace string, ctx context.Context, - req ctrl.Request) (corev1.Pod, int, []corev1.Pod, int, error) { + req ctrl.Request) (corev1.Pod, int, []corev1.Pod, []corev1.Pod, error) { log := ctrllog.FromContext(ctx).WithValues("FindPods", req.NamespacedName) // "available" stores list of pods which can be deleted while scaling down i.e the pods other than one of Ready Pods // There are multiple ready pods possible in OracleRestDataService , while others have atmost one readyPod var available []corev1.Pod + var podsMarkedToBeDeleted []corev1.Pod var readyPod corev1.Pod // To Store the Ready Pod ( Pod that Passed Readiness Probe . Will be shown as 1/1 Running ) podList := &corev1.PodList{} @@ -309,18 +315,17 @@ func FindPods(r client.Reader, version string, image string, name string, namesp // List retrieves list of objects for a given namespace and list options. if err := r.List(ctx, podList, listOpts...); err != nil { log.Error(err, "Failed to list pods of "+name, "Namespace", namespace, "Name", name) - return readyPod, 0, available, 0, err + return readyPod, 0, available, podsMarkedToBeDeleted, err } // r.List() lists all the pods in running, pending,terminating stage matching listOpts . so filter them // Fetch the Running and Pending Pods - podsMarkedToBeDeleted := 0 for _, pod := range podList.Items { // Return pods having Image = image (or) if image = ""(Needed in case when called findpods with "" image) if pod.Spec.Containers[0].Image == image || image == "" { if pod.ObjectMeta.DeletionTimestamp != nil { - podsMarkedToBeDeleted += 1 + podsMarkedToBeDeleted = append(podsMarkedToBeDeleted, pod) continue } if pod.Status.Phase == corev1.PodRunning || pod.Status.Phase == corev1.PodPending { @@ -364,7 +369,7 @@ func CheckDBConfig(readyPod corev1.Pod, r client.Reader, config *rest.Config, } else { out, err := ExecCommand(r, config, readyPod.Name, readyPod.Namespace, "", ctx, req, false, "bash", "-c", - fmt.Sprintf("echo -e \"%s\" | %s", CheckModesSQL, GetSqlClient(edition))) + fmt.Sprintf("echo -e \"%s\" | %s", CheckModesSQL, SQLPlusCLI)) if err != nil { log.Error(err, "Error in ExecCommand()") return false, false, false, requeueY @@ -399,6 +404,74 @@ 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 @@ -457,38 +530,41 @@ 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) (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, GetSqlClient(edition))) + 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", - fmt.Sprintf("echo -e \"%s\" | %s", GetDatabaseRoleCMD, GetSqlClient(edition))) + fmt.Sprintf("echo -e \"%s\" | %s", GetDatabaseRoleCMD, SQLPlusCLI)) if err != nil { return "", err } @@ -496,7 +572,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 @@ -504,6 +580,26 @@ func GetDatabaseRole(readyPod corev1.Pod, r client.Reader, return "", errors.New("database role is nil") } +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) + + 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) { anyPodWithPhase := false @@ -539,38 +635,61 @@ func GetNodeIp(r client.Reader, ctx context.Context, req ctrl.Request) string { log := ctrllog.FromContext(ctx).WithValues("GetNodeIp", req.NamespacedName) - readyPod, _, available, _, err := FindPods(r, "", "", req.Name, req.Namespace, ctx, req) + //new workflow + nl := &corev1.NodeList{} + err := r.List(ctx, nl) + nodeip := "" if err != nil { log.Error(err, err.Error()) - return "" - } - if readyPod.Name != "" { - available = append(available, readyPod) + return nodeip } - nodeip := "" - for _, pod := range available { - if nodeip == "" { - nodeip = pod.Status.HostIP - } - if pod.Status.HostIP < nodeip { - nodeip = pod.Status.HostIP + + for _, address := range nl.Items[0].Status.Addresses { + if address.Type == "ExternalIP" { + nodeip = address.Address + break } } - - node := &corev1.Node{} - err = r.Get(ctx, types.NamespacedName{Name: nodeip, Namespace: req.Namespace}, node) - - if err == nil { - for _, address := range node.Status.Addresses { - if address.Type == "ExternalIP" { + if nodeip == "" { + for _, address := range nl.Items[0].Status.Addresses { + if address.Type == "InternalIP" { nodeip = address.Address + break } } } + log.Info("Node IP obtained ! ", "nodeip: ", nodeip) + return nodeip } +// 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, error) { + + 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 "", "", "", 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 "", "", "", err + } + log.Info("GetSidPdbEditionCMD output \n" + out) + 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, ErrNoReadyPod.Error()) + return "", "", "", ErrNoReadyPod +} + // Get Datapatch Status func GetSqlpatchStatus(r client.Reader, config *rest.Config, readyPod corev1.Pod, ctx context.Context, req ctrl.Request) (string, string, string, error) { log := ctrllog.FromContext(ctx).WithValues("getSqlpatchStatus", req.NamespacedName) @@ -611,9 +730,78 @@ func GetSqlpatchStatus(r client.Reader, config *rest.Config, readyPod corev1.Pod return sqlpatchStatuses[0], splitstr[0], splitstr[1], nil } +// Is Source Database On same Cluster +func IsSourceDatabaseOnCluster(cloneFrom string) bool { + if strings.Contains(cloneFrom, ":") && strings.Contains(cloneFrom, "/") { + return false + } + return true +} + +// Apex password validation function +func ApexPasswordValidator(pwd string) bool { + var ( + hasMinLen = false + hasUpper = false + hasLower = false + hasNumber = false + hasSpecial = false + ) + if len(pwd) > 7 { + hasMinLen = true + } + + for _, c := range pwd { + switch { + case unicode.IsUpper(c): + hasUpper = true + case unicode.IsLower(c): + hasLower = true + case unicode.IsNumber(c): + hasNumber = true + case unicode.IsPunct(c): + hasSpecial = true + } + } + + return hasMinLen && hasUpper && hasLower && hasNumber && hasSpecial +} + func GetSqlClient(edition string) string { if edition == "express" { return "su -p oracle -c \"sqlplus -s / as sysdba\"" } return "sqlplus -s / as sysdba" } + +// Function for patching the K8s service with the payload. +// Patch strategy used: Strategic Merge Patch +func PatchService(config *rest.Config, namespace string, ctx context.Context, req ctrl.Request, svcName string, payload string) error { + log := ctrllog.FromContext(ctx).WithValues("patchService", req.NamespacedName) + client, err := kubernetes.NewForConfig(config) + if err != nil { + log.Error(err, "config error") + } + + // Trying to patch the service resource using Strategic Merge strategy + log.Info("Patching the service", "Service", svcName) + _, 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) + 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 namespacesArr { + namespaces[s] = true + } + return namespaces +} diff --git a/commons/dbcssystem/dbcs_reconciler.go b/commons/dbcssystem/dbcs_reconciler.go new file mode 100644 index 00000000..60905c76 --- /dev/null +++ b/commons/dbcssystem/dbcs_reconciler.go @@ -0,0 +1,771 @@ +/* +** 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 common + +import ( + "context" + "encoding/json" + "errors" + "fmt" + "time" + + "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" + + "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" + "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) { + + //var provisionedDbcsSystemId string + ctx := context.TODO() + // Get DB System Details + dbcsDetails := database.LaunchDbSystemDetails{} + // Get the admin password from OCI key + sshPublicKeys, err := getPublicSSHKey(kubeClient, dbcs) + if err != nil { + return "", err + } + // Get Db SystemOption + 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) + if len(dbcs.Spec.DbSystem.Tags) != 0 { + dbcsDetails.FreeformTags = dbcs.Spec.DbSystem.Tags + } + + req := database.LaunchDbSystemRequest{LaunchDbSystemDetails: dbcsDetails} + + // Send the request using the service client + resp, err := dbClient.LaunchDbSystem(ctx, req) + if err != nil { + return " ", err + } + + dbcs.Spec.Id = resp.DbSystem.Id + // Change the phase to "Provisioning" + if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev1alpha1.Provision, nwClient, wrClient); statusErr != nil { + return "", statusErr + } + // Check the State + _, err = CheckResourceState(logger, dbClient, *resp.DbSystem.Id, string(databasev1alpha1.Provision), string(databasev1alpha1.Available)) + if err != nil { + return "", err + } + + return *resp.DbSystem.Id, nil +} + +// Sync the DbcsSystem Database details + +// Get admin password from Secret then OCI valut secret +func GetAdminPassword(kubeClient client.Client, dbcs *databasev1alpha1.DbcsSystem) (string, error) { + if dbcs.Spec.DbSystem.DbAdminPaswordSecret != "" { + // Get the Admin Secret + adminSecret := &corev1.Secret{} + err := kubeClient.Get(context.TODO(), types.NamespacedName{ + Namespace: dbcs.GetNamespace(), + Name: dbcs.Spec.DbSystem.DbAdminPaswordSecret, + }, adminSecret) + + if err != nil { + return "", err + } + + // Get the admin password + key := "admin-password" + if val, ok := adminSecret.Data[key]; ok { + return 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 GetTdePassword(kubeClient client.Client, dbcs *databasev1alpha1.DbcsSystem) (string, error) { + if dbcs.Spec.DbSystem.TdeWalletPasswordSecret != "" { + // Get the Admin Secret + tdeSecret := &corev1.Secret{} + err := kubeClient.Get(context.TODO(), types.NamespacedName{ + Namespace: dbcs.GetNamespace(), + Name: dbcs.Spec.DbSystem.TdeWalletPasswordSecret, + }, tdeSecret) + + if err != nil { + return "", err + } + + // Get the admin password + key := "tde-password" + if val, ok := tdeSecret.Data[key]; ok { + return 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 getPublicSSHKey(kubeClient client.Client, dbcs *databasev1alpha1.DbcsSystem) (string, error) { + if dbcs.Spec.DbSystem.SshPublicKeys[0] != "" { + // Get the Admin Secret + sshkeysecret := &corev1.Secret{} + err := kubeClient.Get(context.TODO(), types.NamespacedName{ + Namespace: dbcs.GetNamespace(), + Name: dbcs.Spec.DbSystem.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 { + + dbcsId := Id + + dbcsReq := database.TerminateDbSystemRequest{ + DbSystemId: &dbcsId, + } + + _, err := dbClient.TerminateDbSystem(context.TODO(), dbcsReq) + if err != nil { + return err + } + + return nil +} + +// 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 + if statusErr := SetDBCSStatus(dbClient, dbcs, nwClient, wrClient); statusErr != nil { + return statusErr + } + if err := kubeClient.Status().Update(context.TODO(), dbcs); err != nil { + return err + } + + return nil + }) +} + +// 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 { + + dbcsId := *dbcs.Spec.Id + + dbcsReq := database.GetDbSystemRequest{ + DbSystemId: &dbcsId, + } + + resp, err := dbClient.GetDbSystem(context.TODO(), dbcsReq) + if err != nil { + return err + } + + // 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) { + // Change the phase to "Available" + if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev1alpha1.Available, nwClient, wrClient); statusErr != nil { + return statusErr + } + } else if string(resp.LifecycleState) == string(databasev1alpha1.Provision) { + // Change the phase to "Provisioning" + if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev1alpha1.Provision, nwClient, wrClient); statusErr != nil { + return statusErr + } + // Check the State + _, err = CheckResourceState(logger, dbClient, *resp.DbSystem.Id, string(databasev1alpha1.Provision), string(databasev1alpha1.Available)) + if err != nil { + return err + } + } else if string(resp.LifecycleState) == string(databasev1alpha1.Update) { + // Change the phase to "Updating" + if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev1alpha1.Update, nwClient, wrClient); statusErr != nil { + return statusErr + } + // Check the State + _, err = CheckResourceState(logger, dbClient, *resp.DbSystem.Id, string(databasev1alpha1.Update), string(databasev1alpha1.Available)) + if err != nil { + return err + } + } else if string(resp.LifecycleState) == string(databasev1alpha1.Failed) { + // Change the phase to "Updating" + if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev1alpha1.Failed, nwClient, wrClient); statusErr != nil { + return statusErr + } + return fmt.Errorf("DbSystem is in Failed State") + } else if string(resp.LifecycleState) == string(databasev1alpha1.Terminated) { + // Change the phase to "Terminated" + if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev1alpha1.Terminate, nwClient, wrClient); statusErr != nil { + return statusErr + } + } + return nil +} + +func GetDbSystemId(logger logr.Logger, dbClient database.DatabaseClient, dbcs *databasev1alpha1.DbcsSystem) error { + dbcsId := *dbcs.Spec.Id + + dbcsReq := database.GetDbSystemRequest{ + DbSystemId: &dbcsId, + } + + response, err := dbClient.GetDbSystem(context.TODO(), dbcsReq) + if err != nil { + return err + } + + dbcs.Spec.DbSystem.CompartmentId = *response.CompartmentId + if response.DisplayName != nil { + dbcs.Spec.DbSystem.DisplayName = *response.DisplayName + } + + if response.Hostname != nil { + dbcs.Spec.DbSystem.HostName = *response.Hostname + } + if response.CpuCoreCount != nil { + dbcs.Spec.DbSystem.CpuCoreCount = *response.CpuCoreCount + } + dbcs.Spec.DbSystem.NodeCount = response.NodeCount + if response.ClusterName != nil { + dbcs.Spec.DbSystem.ClusterName = *response.ClusterName + } + //dbcs.Spec.DbSystem.DbUniqueName = *response.DbUniqueName + if string(response.DbSystem.DatabaseEdition) != "" { + dbcs.Spec.DbSystem.DbEdition = string(response.DatabaseEdition) + } + if string(response.DiskRedundancy) != "" { + dbcs.Spec.DbSystem.DiskRedundancy = string(response.DiskRedundancy) + } + + //dbcs.Spec.DbSystem.DbVersion = *response. + + if response.BackupSubnetId != nil { + dbcs.Spec.DbSystem.BackupSubnetId = *response.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.SubnetId = *response.SubnetId + dbcs.Spec.DbSystem.AvailabilityDomain = *response.AvailabilityDomain + + err = PopulateDBDetails(logger, dbClient, dbcs) + if err != nil { + logger.Info("Error Occurred while collecting the DB details") + return err + } + return nil +} + +func PopulateDBDetails(logger logr.Logger, dbClient database.DatabaseClient, dbcs *databasev1alpha1.DbcsSystem) error { + + 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 + listDBRsp, err := GetListDatabaseRsp(logger, dbClient, dbcs, *dbHomeId) + if err != nil { + logger.Info("Error Occurred while getting List of Databases") + return err + } + + dbcs.Spec.DbSystem.DbName = *listDBRsp.Items[0].DbName + dbcs.Spec.DbSystem.DbUniqueName = *listDBRsp.Items[0].DbUniqueName + dbcs.Spec.DbSystem.DbVersion = *listDbHomeRsp.Items[0].DbVersion + + return nil +} + +func GetListDbHomeRsp(logger logr.Logger, dbClient database.DatabaseClient, dbcs *databasev1alpha1.DbcsSystem) (database.ListDbHomesResponse, error) { + + dbcsId := *dbcs.Spec.Id + CompartmentId := dbcs.Spec.DbSystem.CompartmentId + + dbHomeReq := database.ListDbHomesRequest{ + DbSystemId: &dbcsId, + CompartmentId: &CompartmentId, + } + + response, err := dbClient.ListDbHomes(context.TODO(), dbHomeReq) + if err != nil { + return database.ListDbHomesResponse{}, err + } + + return response, nil +} + +func GetListDatabaseRsp(logger logr.Logger, dbClient database.DatabaseClient, dbcs *databasev1alpha1.DbcsSystem, dbHomeId string) (database.ListDatabasesResponse, error) { + + CompartmentId := dbcs.Spec.DbSystem.CompartmentId + + dbReq := database.ListDatabasesRequest{ + DbHomeId: &dbHomeId, + CompartmentId: &CompartmentId, + } + + response, err := dbClient.ListDatabases(context.TODO(), dbReq) + if err != nil { + return database.ListDatabasesResponse{}, err + } + + 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") + + updateFlag := false + updateDbcsDetails := database.UpdateDbSystemDetails{} + oldSpec, err := dbcs.GetLastSuccessfulSpec() + if err != nil { + return err + } + + if dbcs.Spec.DbSystem.CpuCoreCount > 0 && dbcs.Spec.DbSystem.CpuCoreCount != 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 { + 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) + updateDbcsDetails.LicenseModel = database.UpdateDbSystemDetailsLicenseModelEnum(licenceModel) + updateFlag = true + } + + if dbcs.Spec.DbSystem.InitialDataStorageSizeInGB != 0 && dbcs.Spec.DbSystem.InitialDataStorageSizeInGB != oldSpec.DbSystem.InitialDataStorageSizeInGB { + updateDbcsDetails.DataStorageSizeInGBs = &dbcs.Spec.DbSystem.InitialDataStorageSizeInGB + updateFlag = true + } + + if updateFlag { + updateDbcsRequest := database.UpdateDbSystemRequest{ + DbSystemId: common.String(*dbcs.Spec.Id), + UpdateDbSystemDetails: updateDbcsDetails, + } + + if _, err := dbClient.UpdateDbSystem(context.TODO(), updateDbcsRequest); err != nil { + return err + } + + // Change the phase to "Provisioning" + if statusErr := SetLifecycleState(kubeClient, dbClient, dbcs, databasev1alpha1.Update, nwClient, wrClient); statusErr != nil { + return statusErr + } + // Check the State + _, err = CheckResourceState(log, dbClient, *dbcs.Spec.Id, "UPDATING", "AVAILABLE") + if err != nil { + return err + } + + } + + return nil +} + +func UpdateDbcsSystemId(kubeClient client.Client, dbcs *databasev1alpha1.DbcsSystem) error { + payload := []annotations.PatchValue{{ + Op: "replace", + Path: "/spec/details", + Value: dbcs.Spec, + }} + payloadBytes, err := json.Marshal(payload) + if err != nil { + return err + } + + patch := client.RawPatch(types.JSONPatchType, payloadBytes) + return kubeClient.Patch(context.TODO(), dbcs, patch) +} + +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 + for { + state, err = GetResourceState(logger, dbClient, Id) + if err != nil { + logger.Info("Error occurred while collecting the resource life cycle state") + 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.") + 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) + } + } + + return "", nil +} + +func GetResourceState(logger logr.Logger, dbClient database.DatabaseClient, Id string) (string, error) { + + dbcsId := Id + dbcsReq := database.GetDbSystemRequest{ + DbSystemId: &dbcsId, + } + + response, err := dbClient.GetDbSystem(context.TODO(), dbcsReq) + if err != nil { + return "", err + } + + state := string(response.LifecycleState) + + return state, nil +} + +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{ + DbSystemId: &dbcsId, + } + + resp, err := dbClient.GetDbSystem(context.TODO(), dbcsReq) + if err != nil { + return err + } + + dbcs.Status.AvailabilityDomain = *resp.AvailabilityDomain + dbcs.Status.CpuCoreCount = *resp.CpuCoreCount + dbcs.Status.DataStoragePercentage = resp.DataStoragePercentage + dbcs.Status.DataStorageSizeInGBs = resp.DataStorageSizeInGBs + dbcs.Status.DbEdition = string(resp.DatabaseEdition) + dbcs.Status.DisplayName = *resp.DisplayName + dbcs.Status.LicenseModel = string(resp.LicenseModel) + dbcs.Status.RecoStorageSizeInGB = resp.RecoStorageSizeInGB + dbcs.Status.NodeCount = *resp.NodeCount + dbcs.Status.StorageManagement = string(resp.DbSystemOptions.StorageManagement) + dbcs.Status.Shape = resp.Shape + dbcs.Status.Id = resp.Id + dbcs.Status.SubnetId = *resp.SubnetId + dbcs.Status.TimeZone = *resp.TimeZone + dbcs.Status.LicenseModel = string(resp.LicenseModel) + dbcs.Status.Network.ScanDnsName = resp.ScanDnsName + dbcs.Status.Network.ListenerPort = resp.ListenerPort + dbcs.Status.Network.HostName = *resp.Hostname + dbcs.Status.Network.DomainName = *resp.Domain + + sname, vcnId, err := getSubnetName(*resp.SubnetId, nwClient) + + if err == nil { + dbcs.Status.Network.SubnetName = sname + vcnName, err := getVcnName(vcnId, nwClient) + + if err == nil { + dbcs.Status.Network.VcnName = vcnName + } + + } + + // Work Request Ststaus + dbWorkRequest := databasev1alpha1.DbWorkrequests{} + + dbWorks, err := getWorkRequest(*resp.OpcRequestId, wrClient, dbcs) + if err == nil { + for _, dbWork := range dbWorks { + //status := checkValue(dbcs, dbWork.Id) + // if status != 0 { + dbWorkRequest.OperationId = dbWork.Id + dbWorkRequest.OperationType = dbWork.OperationType + dbWorkRequest.PercentComplete = fmt.Sprint(*dbWork.PercentComplete) //strconv.FormatFloat(dbWork.PercentComplete, 'E', -1, 32) + if dbWork.TimeAccepted != nil { + dbWorkRequest.TimeAccepted = dbWork.TimeAccepted.String() + } + if dbWork.TimeFinished != nil { + dbWorkRequest.TimeFinished = dbWork.TimeFinished.String() + } + if dbWork.TimeStarted != nil { + dbWorkRequest.TimeStarted = dbWork.TimeStarted.String() + } + + if dbWorkRequest != (databasev1alpha1.DbWorkrequests{}) { + status := checkValue(dbcs, dbWork.Id) + if status == 0 { + dbcs.Status.WorkRequests = append(dbcs.Status.WorkRequests, dbWorkRequest) + dbWorkRequest = databasev1alpha1.DbWorkrequests{} + } else { + setValue(dbcs, dbWorkRequest) + } + } + //} + } + } + + // DB Home Status + dbcs.Status.DbInfo = dbcs.Status.DbInfo[:0] + dbStatus := databasev1alpha1.DbStatus{} + + dbHomes, err := getDbHomeList(dbClient, dbcs) + + if err == nil { + for _, dbHome := range dbHomes { + dbDetails, err := getDList(dbClient, dbcs, dbHome.Id) + for _, dbDetail := range dbDetails { + if err == nil { + dbStatus.Id = dbDetail.Id + dbStatus.DbHomeId = *dbDetail.DbHomeId + dbStatus.DbName = *dbDetail.DbName + dbStatus.DbUniqueName = *dbDetail.DbUniqueName + dbStatus.DbWorkload = *dbDetail.DbWorkload + } + dbcs.Status.DbInfo = append(dbcs.Status.DbInfo, dbStatus) + dbStatus = databasev1alpha1.DbStatus{} + } + } + } + return nil +} + +func getDbHomeList(dbClient database.DatabaseClient, dbcs *databasev1alpha1.DbcsSystem) ([]database.DbHomeSummary, error) { + + var items []database.DbHomeSummary + dbcsId := *dbcs.Spec.Id + + dbcsReq := database.ListDbHomesRequest{ + DbSystemId: &dbcsId, + CompartmentId: &dbcs.Spec.DbSystem.CompartmentId, + } + + resp, err := dbClient.ListDbHomes(context.TODO(), dbcsReq) + if err != nil { + return items, err + } + + return resp.Items, nil +} + +func getDList(dbClient database.DatabaseClient, dbcs *databasev1alpha1.DbcsSystem, dbHomeId *string) ([]database.DatabaseSummary, error) { + + dbcsId := *dbcs.Spec.Id + var items []database.DatabaseSummary + dbcsReq := database.ListDatabasesRequest{ + SystemId: &dbcsId, + CompartmentId: &dbcs.Spec.DbSystem.CompartmentId, + DbHomeId: dbHomeId, + } + + resp, err := dbClient.ListDatabases(context.TODO(), dbcsReq) + if err != nil { + return items, err + } + + return resp.Items, nil +} + +func getSubnetName(subnetId string, nwClient core.VirtualNetworkClient) (*string, *string, error) { + + req := core.GetSubnetRequest{SubnetId: common.String(subnetId)} + + // Send the request using the service client + resp, err := nwClient.GetSubnet(context.Background(), req) + + if err != nil { + return nil, nil, err + } + // Retrieve value from the response. + + return resp.DisplayName, resp.VcnId, nil +} + +func getVcnName(vcnId *string, nwClient core.VirtualNetworkClient) (*string, error) { + + req := core.GetVcnRequest{VcnId: common.String(*vcnId)} + + // Send the request using the service client + resp, err := nwClient.GetVcn(context.Background(), req) + + if err != nil { + return nil, err + } + // Retrieve value from the response. + + return resp.DisplayName, nil +} + +// =========== validate Specs ============ +func ValidateSpex(logger logr.Logger, kubeClient client.Client, dbClient database.DatabaseClient, dbcs *databasev1alpha1.DbcsSystem, nwClient core.VirtualNetworkClient, eRecord record.EventRecorder) error { + + //var str1 string + var eventMsg string + var eventErr string = "Spec Error" + lastSuccSpec, err := dbcs.GetLastSuccessfulSpec() + if err != nil { + return err + } + // Check if last Successful update nil or not + if lastSuccSpec == nil { + if dbcs.Spec.DbSystem.DbVersion != "" { + _, err = GetDbLatestVersion(dbClient, dbcs, "") + if err != nil { + eventMsg = "DBCS CRD resource " + GetFmtStr(dbcs.Name) + " DbVersion " + GetFmtStr(dbcs.Spec.DbSystem.DbVersion) + " is not matching available DB releases." + eRecord.Eventf(dbcs, corev1.EventTypeWarning, eventErr, eventMsg) + return err + } + } else { + eventMsg = "DBCS CRD resource " + "DbVersion " + GetFmtStr(dbcs.Name) + GetFmtStr("dbcs.Spec.DbSystem.DbVersion") + " cannot be a empty string." + eRecord.Eventf(dbcs, corev1.EventTypeWarning, eventErr, eventMsg) + return err + } + if dbcs.Spec.DbSystem.DbWorkload != "" { + _, err = getDbWorkLoadType(dbcs) + if err != nil { + eventMsg = "DBCS CRD resource " + GetFmtStr(dbcs.Name) + " DbWorkload " + GetFmtStr(dbcs.Spec.DbSystem.DbWorkload) + " is not matching the DBworkload type OLTP|DSS." + eRecord.Eventf(dbcs, corev1.EventTypeWarning, eventErr, eventMsg) + return err + } + } else { + eventMsg = "DBCS CRD resource " + "DbWorkload " + GetFmtStr(dbcs.Name) + GetFmtStr("dbcs.Spec.DbSystem.DbWorkload") + " cannot be a empty string." + eRecord.Eventf(dbcs, corev1.EventTypeWarning, eventErr, eventMsg) + return err + } + + if dbcs.Spec.DbSystem.NodeCount != nil { + switch *dbcs.Spec.DbSystem.NodeCount { + case 1: + case 2: + default: + eventMsg = "DBCS CRD resource " + "NodeCount " + GetFmtStr(dbcs.Name) + GetFmtStr("dbcs.Spec.DbSystem.NodeCount") + " can be either 1 or 2." + eRecord.Eventf(dbcs, corev1.EventTypeWarning, eventErr, eventMsg) + return err + } + } + + } else { + if lastSuccSpec.DbSystem.DbVersion != dbcs.Spec.DbSystem.DbVersion { + eventMsg = "DBCS CRD resource " + "DbVersion " + GetFmtStr(dbcs.Name) + GetFmtStr("dbcs.Spec.DbSystem.DbVersion") + " cannot be a empty string." + eRecord.Eventf(dbcs, corev1.EventTypeWarning, eventErr, eventMsg) + return err + } + + } + + return nil + +} diff --git a/commons/dbcssystem/dcommon.go b/commons/dbcssystem/dcommon.go new file mode 100644 index 00000000..3af3a6b4 --- /dev/null +++ b/commons/dbcssystem/dcommon.go @@ -0,0 +1,442 @@ +/* +** 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 common + +import ( + "context" + "fmt" + "strconv" + "strings" + + "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" + + databasev1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" +) + +func GetDbHomeDetails(kubeClient client.Client, dbClient database.DatabaseClient, dbcs *databasev1alpha1.DbcsSystem) (database.CreateDbHomeDetails, error) { + + dbHomeDetails := database.CreateDbHomeDetails{} + + dbHomeReq, err := GetDbLatestVersion(dbClient, dbcs, "") + if err != nil { + return database.CreateDbHomeDetails{}, err + } + dbHomeDetails.DbVersion = &dbHomeReq + + dbDetailsReq, err := GetDBDetails(kubeClient, dbcs) + if err != nil { + return database.CreateDbHomeDetails{}, err + } + + dbHomeDetails.Database = &dbDetailsReq + + return dbHomeDetails, nil +} + +func GetDbLatestVersion(dbClient database.DatabaseClient, dbcs *databasev1alpha1.DbcsSystem, dbSystemId string) (string, error) { + + //var provisionedDbcsSystemId string + ctx := context.TODO() + var version database.DbVersionSummary + var sFlag int = 0 + var val int + + dbVersionReq := database.ListDbVersionsRequest{} + if dbSystemId != "" { + dbVersionReq.DbSystemId = common.String(dbSystemId) + } + + dbVersionReq.IsDatabaseSoftwareImageSupported = common.Bool(true) + dbVersionReq.IsUpgradeSupported = common.Bool(false) + dbVersionReq.CompartmentId = common.String(dbcs.Spec.DbSystem.CompartmentId) + dbVersionReq.DbSystemShape = common.String(dbcs.Spec.DbSystem.Shape) + // Send the request using the service client + req := database.ListDbVersionsRequest(dbVersionReq) + + resp, err := dbClient.ListDbVersions(ctx, req) + + if err != nil { + return "", err + } + + if dbcs.Spec.DbSystem.DbVersion != "" { + for i := len(resp.Items) - 1; i >= 0; i-- { + version = resp.Items[i] + s1 := getStr(*version.Version, 2) + s2 := getStr(dbcs.Spec.DbSystem.DbVersion, 2) + if strings.EqualFold(s1, s2) { + val, _ = strconv.Atoi(s1) + if val >= 18 { + s3 := s1 + "c" + 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 + } + } + + } + } + + if sFlag == 1 { + return *version.Version, nil + } + return *version.Version, fmt.Errorf("no database version matched") +} + +func getStr(str1 string, num int) string { + return str1[0:num] +} + +func GetDBDetails(kubeClient client.Client, dbcs *databasev1alpha1.DbcsSystem) (database.CreateDatabaseDetails, error) { + dbDetails := database.CreateDatabaseDetails{} + var val database.CreateDatabaseDetailsDbWorkloadEnum + + if dbcs.Spec.DbSystem.TdeWalletPasswordSecret != "" { + tdePasswd, err := GetTdePassword(kubeClient, dbcs) + if err != nil { + return database.CreateDatabaseDetails{}, err + } + tdePassword := strings.Trim(strings.TrimSuffix(tdePasswd, "\n"), "\"") + dbDetails.TdeWalletPassword = &tdePassword + //fmt.Print(tdePassword) + + } + + adminPasswd, err := GetAdminPassword(kubeClient, dbcs) + if err != nil { + return database.CreateDatabaseDetails{}, err + } + + adminPassword := strings.Trim(strings.TrimSuffix(adminPasswd, "\n"), "\"") + dbDetails.AdminPassword = &adminPassword + //fmt.Print(adminPassword) + if dbcs.Spec.DbSystem.DbName != "" { + dbDetails.DbName = common.String(dbcs.Spec.DbSystem.DbName) + } + + if dbcs.Spec.DbSystem.DbWorkload != "" { + val, err = getDbWorkLoadType(dbcs) + if err != nil { + return dbDetails, err + } else { + dbDetails.DbWorkload = database.CreateDatabaseDetailsDbWorkloadEnum(val) + } + } + dbDetails.DbName = common.String(dbcs.Spec.DbSystem.DbName) + if dbcs.Spec.DbSystem.PdbName != "" { + 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 + } + } + } + + return dbDetails, nil +} + +func getBackupConfig(kubeClient client.Client, dbcs *databasev1alpha1.DbcsSystem) (database.DbBackupConfig, error) { + backupConfig := database.DbBackupConfig{} + + if dbcs.Spec.DbSystem.DbBackupConfig.AutoBackupEnabled != nil { + if *dbcs.Spec.DbSystem.DbBackupConfig.AutoBackupEnabled { + backupConfig.AutoBackupEnabled = dbcs.Spec.DbSystem.DbBackupConfig.AutoBackupEnabled + val1, err := getBackupWindowEnum(dbcs) + if err != nil { + return backupConfig, err + } else { + backupConfig.AutoBackupWindow = database.DbBackupConfigAutoBackupWindowEnum(val1) + } + } + + if dbcs.Spec.DbSystem.DbBackupConfig.RecoveryWindowsInDays != nil { + val1, err := getRecoveryWindowsInDays(dbcs) + if err != nil { + return backupConfig, err + } else { + backupConfig.RecoveryWindowInDays = common.Int(val1) + } + + } + } + + return backupConfig, nil +} + +func getBackupWindowEnum(dbcs *databasev1alpha1.DbcsSystem) (database.DbBackupConfigAutoBackupWindowEnum, error) { + + if strings.ToUpper(*dbcs.Spec.DbSystem.DbBackupConfig.AutoBackupWindow) == "SLOT_ONE" { + return database.DbBackupConfigAutoBackupWindowOne, nil + } else if strings.ToUpper(*dbcs.Spec.DbSystem.DbBackupConfig.AutoBackupWindow) == "SLOT_TWO" { + return database.DbBackupConfigAutoBackupWindowTwo, nil + } else if strings.ToUpper(*dbcs.Spec.DbSystem.DbBackupConfig.AutoBackupWindow) == "SLOT_THREE" { + return database.DbBackupConfigAutoBackupWindowThree, nil + } else if strings.ToUpper(*dbcs.Spec.DbSystem.DbBackupConfig.AutoBackupWindow) == "SLOT_FOUR" { + return database.DbBackupConfigAutoBackupWindowFour, nil + } else if strings.ToUpper(*dbcs.Spec.DbSystem.DbBackupConfig.AutoBackupWindow) == "SLOT_FOUR" { + return database.DbBackupConfigAutoBackupWindowFour, nil + } else if strings.ToUpper(*dbcs.Spec.DbSystem.DbBackupConfig.AutoBackupWindow) == "SLOT_FIVE" { + return database.DbBackupConfigAutoBackupWindowFive, nil + } else if strings.ToUpper(*dbcs.Spec.DbSystem.DbBackupConfig.AutoBackupWindow) == "SLOT_SIX" { + return database.DbBackupConfigAutoBackupWindowSix, nil + } else if strings.ToUpper(*dbcs.Spec.DbSystem.DbBackupConfig.AutoBackupWindow) == "SLOT_SEVEN" { + return database.DbBackupConfigAutoBackupWindowSeven, nil + } else if strings.ToUpper(*dbcs.Spec.DbSystem.DbBackupConfig.AutoBackupWindow) == "SLOT_EIGHT" { + return database.DbBackupConfigAutoBackupWindowEight, nil + } else if strings.ToUpper(*dbcs.Spec.DbSystem.DbBackupConfig.AutoBackupWindow) == "SLOT_NINE" { + return database.DbBackupConfigAutoBackupWindowNine, nil + } else if strings.ToUpper(*dbcs.Spec.DbSystem.DbBackupConfig.AutoBackupWindow) == "SLOT_TEN" { + return database.DbBackupConfigAutoBackupWindowTen, nil + } else if strings.ToUpper(*dbcs.Spec.DbSystem.DbBackupConfig.AutoBackupWindow) == "SLOT_ELEVEN" { + return database.DbBackupConfigAutoBackupWindowEleven, nil + } else if strings.ToUpper(*dbcs.Spec.DbSystem.DbBackupConfig.AutoBackupWindow) == "SLOT_TWELVE" { + return database.DbBackupConfigAutoBackupWindowTwelve, nil + } else { + return database.DbBackupConfigAutoBackupWindowOne, nil + } + + //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) { + + var days int + + switch *dbcs.Spec.DbSystem.DbBackupConfig.RecoveryWindowsInDays { + case 7: + return *dbcs.Spec.DbSystem.DbBackupConfig.RecoveryWindowsInDays, nil + case 15: + return *dbcs.Spec.DbSystem.DbBackupConfig.RecoveryWindowsInDays, nil + case 30: + return *dbcs.Spec.DbSystem.DbBackupConfig.RecoveryWindowsInDays, nil + case 45: + return *dbcs.Spec.DbSystem.DbBackupConfig.RecoveryWindowsInDays, nil + case 60: + return *dbcs.Spec.DbSystem.DbBackupConfig.RecoveryWindowsInDays, nil + default: + days = 30 + return days, nil + } + //return days, fmt.Errorf("RecoveryWindowsInDays values can be 7|15|30|45|60 Days.") +} + +func GetDBSystemopts( + dbcs *databasev1alpha1.DbcsSystem) database.DbSystemOptions { + + dbSystemOpt := database.DbSystemOptions{} + + if dbcs.Spec.DbSystem.StorageManagement != "" { + switch dbcs.Spec.DbSystem.StorageManagement { + case "LVM": + dbSystemOpt.StorageManagement = database.DbSystemOptionsStorageManagementLvm + case "ASM": + dbSystemOpt.StorageManagement = database.DbSystemOptionsStorageManagementAsm + default: + dbSystemOpt.StorageManagement = database.DbSystemOptionsStorageManagementAsm + } + } else { + dbSystemOpt.StorageManagement = database.DbSystemOptionsStorageManagementAsm + } + + return dbSystemOpt +} + +func getLicenceModel(dbcs *databasev1alpha1.DbcsSystem) database.DbSystemLicenseModelEnum { + if dbcs.Spec.DbSystem.LicenseModel == "BRING_YOUR_OWN_LICENSE" { + return database.DbSystemLicenseModelBringYourOwnLicense + + } + return database.DbSystemLicenseModelLicenseIncluded +} + +func getDbWorkLoadType(dbcs *databasev1alpha1.DbcsSystem) (database.CreateDatabaseDetailsDbWorkloadEnum, error) { + + if strings.ToUpper(dbcs.Spec.DbSystem.DbWorkload) == "OLTP" { + + return database.CreateDatabaseDetailsDbWorkloadOltp, nil + } + if strings.ToUpper(dbcs.Spec.DbSystem.DbWorkload) == "DSS" { + return database.CreateDatabaseDetailsDbWorkloadDss, nil + + } + + return database.CreateDatabaseDetailsDbWorkloadDss, fmt.Errorf("DbWorkload values can be OLTP|DSS. The current value set to " + dbcs.Spec.DbSystem.DbWorkload) +} + +func GetNodeCount( + dbcs *databasev1alpha1.DbcsSystem) int { + + if dbcs.Spec.DbSystem.NodeCount != nil { + return *dbcs.Spec.DbSystem.NodeCount + } else { + return 1 + } +} + +func GetInitialStorage( + dbcs *databasev1alpha1.DbcsSystem) int { + + if dbcs.Spec.DbSystem.InitialDataStorageSizeInGB > 0 { + return dbcs.Spec.DbSystem.InitialDataStorageSizeInGB + } + return 256 +} + +func GetDBEdition(dbcs *databasev1alpha1.DbcsSystem) database.LaunchDbSystemDetailsDatabaseEditionEnum { + + if dbcs.Spec.DbSystem.ClusterName != "" { + return database.LaunchDbSystemDetailsDatabaseEditionEnterpriseEditionExtremePerformance + } + + if dbcs.Spec.DbSystem.DbEdition != "" { + switch dbcs.Spec.DbSystem.DbEdition { + case "STANDARD_EDITION": + return database.LaunchDbSystemDetailsDatabaseEditionStandardEdition + case "ENTERPRISE_EDITION": + return database.LaunchDbSystemDetailsDatabaseEditionEnterpriseEdition + case "ENTERPRISE_EDITION_HIGH_PERFORMANCE": + return database.LaunchDbSystemDetailsDatabaseEditionEnterpriseEditionHighPerformance + case "ENTERPRISE_EDITION_EXTREME_PERFORMANCE": + return database.LaunchDbSystemDetailsDatabaseEditionEnterpriseEditionExtremePerformance + default: + return database.LaunchDbSystemDetailsDatabaseEditionEnterpriseEdition + } + } + + return database.LaunchDbSystemDetailsDatabaseEditionEnterpriseEdition +} + +func GetDBbDiskRedundancy( + dbcs *databasev1alpha1.DbcsSystem) database.LaunchDbSystemDetailsDiskRedundancyEnum { + + if dbcs.Spec.DbSystem.ClusterName != "" { + return database.LaunchDbSystemDetailsDiskRedundancyHigh + } + + switch dbcs.Spec.DbSystem.DiskRedundancy { + case "HIGH": + return database.LaunchDbSystemDetailsDiskRedundancyHigh + case "NORMAL": + return database.LaunchDbSystemDetailsDiskRedundancyNormal + } + + return database.LaunchDbSystemDetailsDiskRedundancyNormal +} + +func getWorkRequest(workId string, wrClient workrequests.WorkRequestClient, dbcs *databasev1alpha1.DbcsSystem) ([]workrequests.WorkRequestSummary, error) { + var workReq []workrequests.WorkRequestSummary + + req := workrequests.ListWorkRequestsRequest{CompartmentId: &dbcs.Spec.DbSystem.CompartmentId, OpcRequestId: &workId, ResourceId: dbcs.Spec.Id} + resp, err := wrClient.ListWorkRequests(context.Background(), req) + if err != nil { + return workReq, err + } + + return resp.Items, nil +} + +func GetKeyValue(str1 string) string { + list1 := strings.Split(str1, " ") + for _, value := range list1 { + val1 := strings.Split(value, "=") + if val1[0] == "version" { + return val1[1] + } + } + + return "noversion" +} + +func GetFmtStr(pstr string) string { + + return "[" + pstr + "]" +} + +func checkValue(dbcs *databasev1alpha1.DbcsSystem, workId *string) int { + + var status int = 0 + //dbWorkRequest := databasev1alpha1.DbWorkrequests{} + + if len(dbcs.Status.WorkRequests) > 0 { + for _, v := range dbcs.Status.WorkRequests { + if *v.OperationId == *workId { + status = 1 + } + } + } + + return status +} +func setValue(dbcs *databasev1alpha1.DbcsSystem, dbWorkRequest databasev1alpha1.DbWorkrequests) { + + //var status int = 1 + //dbWorkRequest := databasev1alpha1.DbWorkrequests{} + var counter int = 0 + if len(dbcs.Status.WorkRequests) > 0 { + for _, v := range dbcs.Status.WorkRequests { + if *v.OperationId == *dbWorkRequest.OperationId { + dbcs.Status.WorkRequests[counter].OperationId = dbWorkRequest.OperationId + dbcs.Status.WorkRequests[counter].OperationType = dbWorkRequest.OperationType + dbcs.Status.WorkRequests[counter].PercentComplete = dbWorkRequest.PercentComplete + dbcs.Status.WorkRequests[counter].TimeAccepted = dbWorkRequest.TimeAccepted + dbcs.Status.WorkRequests[counter].TimeFinished = dbWorkRequest.TimeFinished + dbcs.Status.WorkRequests[counter].TimeStarted = dbWorkRequest.TimeStarted + } + counter = counter + 1 + } + } + +} diff --git a/commons/finalizer/finalizer.go b/commons/finalizer/finalizer.go index bafe110e..169cbaef 100644 --- a/commons/finalizer/finalizer.go +++ b/commons/finalizer/finalizer.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2022 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** diff --git a/commons/autonomousdatabase/reconciler_util.go b/commons/k8s/create.go similarity index 55% rename from commons/autonomousdatabase/reconciler_util.go rename to commons/k8s/create.go index 2f820410..5055bc0e 100644 --- a/commons/autonomousdatabase/reconciler_util.go +++ b/commons/k8s/create.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2022 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -36,47 +36,23 @@ ** SOFTWARE. */ -package autonomousdatabase +package k8s import ( "context" - "fmt" + dbv1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + + "github.com/oracle/oci-go-sdk/v65/common" + "github.com/oracle/oci-go-sdk/v65/database" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/util/retry" "sigs.k8s.io/controller-runtime/pkg/client" - - "github.com/go-logr/logr" - "github.com/oracle/oci-go-sdk/v51/common" - "github.com/oracle/oci-go-sdk/v51/database" - "github.com/oracle/oci-go-sdk/v51/secrets" - - dbv1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" - "github.com/oracle/oracle-database-operator/commons/oci" ) -// SetStatus sets the status subresource. -func SetStatus(kubeClient client.Client, adb *dbv1alpha1.AutonomousDatabase) error { - return retry.RetryOnConflict(retry.DefaultRetry, func() error { - curADB := &dbv1alpha1.AutonomousDatabase{} - - namespacedName := types.NamespacedName{ - Namespace: adb.GetNamespace(), - Name: adb.GetName(), - } - - if err := kubeClient.Get(context.TODO(), namespacedName, curADB); err != nil { - return err - } - - curADB.Status = adb.Status - return kubeClient.Status().Update(context.TODO(), curADB) - }) -} +func CreateSecret(kubeClient client.Client, namespace string, name string, data map[string][]byte, owner client.Object, label map[string]string) error { + ownerReference := NewOwnerReference(owner) -func createWalletSecret(kubeClient client.Client, namespacedName types.NamespacedName, data map[string][]byte) error { // Create the secret with the wallet data stringData := map[string]string{} for key, val := range data { @@ -85,8 +61,10 @@ func createWalletSecret(kubeClient client.Client, namespacedName types.Namespace walletSecret := &corev1.Secret{ ObjectMeta: metav1.ObjectMeta{ - Namespace: namespacedName.Namespace, - Name: namespacedName.Name, + Namespace: namespace, + Name: name, + OwnerReferences: ownerReference, + Labels: label, }, StringData: stringData, } @@ -97,31 +75,35 @@ func createWalletSecret(kubeClient client.Client, namespacedName types.Namespace return nil } -func CreateWalletSecret(logger logr.Logger, kubeClient client.Client, dbClient database.DatabaseClient, secretClient secrets.SecretsClient, adb *dbv1alpha1.AutonomousDatabase) error { - // Kube Secret which contains Instance Wallet - walletName := adb.Spec.Details.Wallet.Name - if walletName == nil { - walletName = common.String(adb.GetName() + "-instance-wallet") - } +func CreateAutonomousBackup(kubeClient client.Client, + backupName string, + backupSummary database.AutonomousDatabaseBackupSummary, + ownerADB *dbv1alpha1.AutonomousDatabase) error { - // No-op if Wallet is already downloaded - walletNamespacedName := types.NamespacedName{ - Namespace: adb.GetNamespace(), - Name: *walletName, - } - walletSecret := &corev1.Secret{} - if err := kubeClient.Get(context.TODO(), walletNamespacedName, walletSecret); err == nil { - return nil + backup := &dbv1alpha1.AutonomousDatabaseBackup{ + ObjectMeta: metav1.ObjectMeta{ + Namespace: ownerADB.GetNamespace(), + Name: backupName, + OwnerReferences: NewOwnerReference(ownerADB), + }, + Spec: dbv1alpha1.AutonomousDatabaseBackupSpec{ + Target: dbv1alpha1.TargetSpec{ + K8sADB: dbv1alpha1.K8sADBSpec{ + Name: common.String(ownerADB.Name), + }, + }, + DisplayName: backupSummary.DisplayName, + AutonomousDatabaseBackupOCID: backupSummary.Id, + OCIConfig: dbv1alpha1.OCIConfigSpec{ + ConfigMapName: ownerADB.Spec.OCIConfig.ConfigMapName, + SecretName: ownerADB.Spec.OCIConfig.SecretName, + }, + }, } - data, err := oci.GetWallet(logger, kubeClient, dbClient, secretClient, adb) - if err != nil { + if err := kubeClient.Create(context.TODO(), backup); err != nil { return err } - if err := createWalletSecret(kubeClient, walletNamespacedName, data); err != nil { - return err - } - logger.Info(fmt.Sprintf("Wallet is stored in the Secret %s", *walletName)) return nil } diff --git a/commons/k8s/fetch.go b/commons/k8s/fetch.go new file mode 100644 index 00000000..05792cad --- /dev/null +++ b/commons/k8s/fetch.go @@ -0,0 +1,145 @@ +/* +** 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 k8s + +import ( + "context" + "errors" + + corev1 "k8s.io/api/core/v1" + apiErrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + + dbv1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" +) + +func FetchResource(kubeClient client.Client, namespace string, name string, object client.Object) error { + namespacedName := types.NamespacedName{ + Namespace: namespace, + Name: name, + } + if err := kubeClient.Get(context.TODO(), namespacedName, object); err != nil { + return err + } + + return nil +} + +// 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. +// 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) { + adbList, err := fetchAutonomousDatabases(kubeClient, namespace) + if err != nil { + return nil, err + } + + for _, adb := range adbList.Items { + if adb.Spec.Details.AutonomousDatabaseOCID != nil && *adb.Spec.Details.AutonomousDatabaseOCID == ocid { + return &adb, nil + } + } + + return nil, nil +} + +func fetchAutonomousDatabases(kubeClient client.Client, namespace string) (*dbv1alpha1.AutonomousDatabaseList, error) { + // Get the list of AutonomousDatabaseBackupOCID in the same namespace + adbList := &dbv1alpha1.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. + // No need to change the since we don't know if we obtain the object. + if !apiErrors.IsNotFound(err) { + return adbList, err + } + } + + return adbList, nil +} + +func FetchAutonomousDatabaseBackups(kubeClient client.Client, namespace string) (*dbv1alpha1.AutonomousDatabaseBackupList, error) { + // Get the list of AutonomousDatabaseBackupOCID in the same namespace + backupList := &dbv1alpha1.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. + // No need to change the since we don't know if we obtain the object. + if !apiErrors.IsNotFound(err) { + return backupList, err + } + } + + return backupList, nil +} + +func FetchConfigMap(kubeClient client.Client, namespace string, name string) (*corev1.ConfigMap, error) { + configMap := &corev1.ConfigMap{} + + if err := FetchResource(kubeClient, namespace, name, configMap); err != nil { + return nil, err + } + + return configMap, nil +} + +func FetchSecret(kubeClient client.Client, namespace string, name string) (*corev1.Secret, error) { + secret := &corev1.Secret{} + + if err := FetchResource(kubeClient, namespace, name, secret); err != nil { + return nil, err + } + + return secret, nil +} + +func GetSecretValue(kubeClient client.Client, namespace string, name string, key string) (string, error) { + secret, err := FetchSecret(kubeClient, namespace, name) + if err != nil { + return "", err + } + + val, ok := secret.Data[key] + if !ok { + return "", errors.New("Secret key not found: " + key) + } + return string(val), nil +} diff --git a/commons/k8s/finalizer.go b/commons/k8s/finalizer.go new file mode 100644 index 00000000..015bbc61 --- /dev/null +++ b/commons/k8s/finalizer.go @@ -0,0 +1,92 @@ +/* +** 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 k8s + +import ( + "context" + "encoding/json" + + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" +) + +func patchFinalizer(kubeClient client.Client, obj client.Object) error { + finalizer := obj.GetFinalizers() + + payload := []patchValue{} + + if obj.GetFinalizers() == nil { + payload = append(payload, patchValue{ + Op: "replace", + Path: "/metadata/finalizers", + Value: []string{}, + }) + } + + payload = append(payload, patchValue{ + Op: "replace", + Path: "/metadata/finalizers", + Value: finalizer, + }) + + payloadBytes, err := json.Marshal(payload) + if err != nil { + return err + } + + patch := client.RawPatch(types.JSONPatchType, payloadBytes) + return kubeClient.Patch(context.TODO(), obj, patch) +} + +func AddFinalizerAndPatch(kubeClient client.Client, obj client.Object, finalizer string) error { + controllerutil.AddFinalizer(obj, finalizer) + if err := patchFinalizer(kubeClient, obj); err != nil { + return err + } + return nil +} + +func RemoveFinalizerAndPatch(kubeClient client.Client, obj client.Object, finalizer string) error { + controllerutil.RemoveFinalizer(obj, finalizer) + if err := patchFinalizer(kubeClient, obj); err != nil { + return err + } + return nil +} diff --git a/commons/k8s/utils.go b/commons/k8s/utils.go new file mode 100644 index 00000000..37ec1a3f --- /dev/null +++ b/commons/k8s/utils.go @@ -0,0 +1,86 @@ +/* +** 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 k8s + +import ( + "context" + "encoding/json" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + utilErrors "k8s.io/apimachinery/pkg/util/errors" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func NewOwnerReference(owner client.Object) []metav1.OwnerReference { + ownerRef := []metav1.OwnerReference{ + { + Kind: owner.GetObjectKind().GroupVersionKind().Kind, + APIVersion: owner.GetObjectKind().GroupVersionKind().GroupVersion().String(), + Name: owner.GetName(), + UID: owner.GetUID(), + }, + } + return ownerRef +} + +func CombineErrors(errs ...error) error { + return utilErrors.NewAggregate(errs) +} + +/********************** + Patch resource +**********************/ + +type patchValue struct { + Op string `json:"op"` + Path string `json:"path"` + Value interface{} `json:"value"` +} + +func Patch(kubeClient client.Client, obj client.Object, path string, value interface{}) error { + payload := []patchValue{{ + Op: "replace", + Path: path, + Value: value, + }} + payloadBytes, _ := json.Marshal(payload) + patch := client.RawPatch(types.JSONPatchType, payloadBytes) + return kubeClient.Patch(context.TODO(), obj, patch) +} diff --git a/commons/observability/constants.go b/commons/observability/constants.go new file mode 100644 index 00000000..89ecb946 --- /dev/null +++ b/commons/observability/constants.go @@ -0,0 +1,173 @@ +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 + DefaultServiceTargetPort = 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" + 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" + 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" +) + +// 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/commons/oci/containerdatabase.go b/commons/oci/containerdatabase.go new file mode 100644 index 00000000..a5313d41 --- /dev/null +++ b/commons/oci/containerdatabase.go @@ -0,0 +1,101 @@ +/* +** 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 oci + +import ( + "context" + + "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" +) + +/******************************** + * Autonomous Container Database + *******************************/ +func (d *databaseService) CreateAutonomousContainerDatabase(acd *dbv1alpha1.AutonomousContainerDatabase) (database.CreateAutonomousContainerDatabaseResponse, error) { + createAutonomousContainerDatabaseRequest := database.CreateAutonomousContainerDatabaseRequest{ + CreateAutonomousContainerDatabaseDetails: database.CreateAutonomousContainerDatabaseDetails{ + CompartmentId: acd.Spec.CompartmentOCID, + DisplayName: acd.Spec.DisplayName, + CloudAutonomousVmClusterId: acd.Spec.AutonomousExadataVMClusterOCID, + PatchModel: database.CreateAutonomousContainerDatabaseDetailsPatchModelUpdates, + }, + } + + return d.dbClient.CreateAutonomousContainerDatabase(context.TODO(), createAutonomousContainerDatabaseRequest) +} + +func (d *databaseService) GetAutonomousContainerDatabase(acdOCID string) (database.GetAutonomousContainerDatabaseResponse, error) { + getAutonomousContainerDatabaseRequest := database.GetAutonomousContainerDatabaseRequest{ + AutonomousContainerDatabaseId: common.String(acdOCID), + } + + return d.dbClient.GetAutonomousContainerDatabase(context.TODO(), getAutonomousContainerDatabaseRequest) +} + +func (d *databaseService) UpdateAutonomousContainerDatabase(acdOCID string, difACD *dbv1alpha1.AutonomousContainerDatabase) (database.UpdateAutonomousContainerDatabaseResponse, error) { + updateAutonomousContainerDatabaseRequest := database.UpdateAutonomousContainerDatabaseRequest{ + AutonomousContainerDatabaseId: common.String(acdOCID), + UpdateAutonomousContainerDatabaseDetails: database.UpdateAutonomousContainerDatabaseDetails{ + DisplayName: difACD.Spec.DisplayName, + PatchModel: database.UpdateAutonomousContainerDatabaseDetailsPatchModelEnum(difACD.Spec.PatchModel), + FreeformTags: difACD.Spec.FreeformTags, + }, + } + + return d.dbClient.UpdateAutonomousContainerDatabase(context.TODO(), updateAutonomousContainerDatabaseRequest) +} + +func (d *databaseService) RestartAutonomousContainerDatabase(acdOCID string) (database.RestartAutonomousContainerDatabaseResponse, error) { + restartRequest := database.RestartAutonomousContainerDatabaseRequest{ + AutonomousContainerDatabaseId: common.String(acdOCID), + } + + return d.dbClient.RestartAutonomousContainerDatabase(context.TODO(), restartRequest) +} + +func (d *databaseService) TerminateAutonomousContainerDatabase(acdOCID string) (database.TerminateAutonomousContainerDatabaseResponse, error) { + terminateRequest := database.TerminateAutonomousContainerDatabaseRequest{ + AutonomousContainerDatabaseId: common.String(acdOCID), + } + + return d.dbClient.TerminateAutonomousContainerDatabase(context.TODO(), terminateRequest) +} diff --git a/commons/oci/database.go b/commons/oci/database.go index 3fdf7a00..9c3cd4d8 100644 --- a/commons/oci/database.go +++ b/commons/oci/database.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2022 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -40,553 +40,394 @@ package oci import ( "context" - "errors" "fmt" - "math" - "time" "github.com/go-logr/logr" - "github.com/oracle/oci-go-sdk/v51/common" - "github.com/oracle/oci-go-sdk/v51/database" - "github.com/oracle/oci-go-sdk/v51/secrets" - "github.com/oracle/oci-go-sdk/v51/workrequests" + "github.com/oracle/oci-go-sdk/v65/common" + "github.com/oracle/oci-go-sdk/v65/database" "sigs.k8s.io/controller-runtime/pkg/client" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" - dbv1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + "github.com/oracle/oracle-database-operator/commons/k8s" ) -// CreateAutonomousDatabase sends a request to OCI to provision a database and returns the AutonomousDatabase OCID. -func CreateAutonomousDatabase(logger logr.Logger, kubeClient client.Client, dbClient database.DatabaseClient, secretClient secrets.SecretsClient, adb *dbv1alpha1.AutonomousDatabase) (*database.CreateAutonomousDatabaseResponse, error) { - adminPassword, err := getAdminPassword(logger, kubeClient, secretClient, adb) - if err != nil { - return nil, err - } - - createAutonomousDatabaseDetails := database.CreateAutonomousDatabaseDetails{ - CompartmentId: adb.Spec.Details.CompartmentOCID, - DbName: adb.Spec.Details.DbName, - CpuCoreCount: adb.Spec.Details.CPUCoreCount, - DataStorageSizeInTBs: adb.Spec.Details.DataStorageSizeInTBs, - AdminPassword: common.String(adminPassword), - DisplayName: adb.Spec.Details.DisplayName, - IsAutoScalingEnabled: adb.Spec.Details.IsAutoScalingEnabled, - IsDedicated: adb.Spec.Details.IsDedicated, - DbVersion: adb.Spec.Details.DbVersion, - DbWorkload: database.CreateAutonomousDatabaseBaseDbWorkloadEnum( - adb.Spec.Details.DbWorkload), +type DatabaseService interface { + CreateAutonomousDatabase(adb *dbv1alpha1.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) + 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) + 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) + RestoreAutonomousDatabase(adbOCID string, sdkTime common.SDKTime) (database.RestoreAutonomousDatabaseResponse, error) + ListAutonomousDatabaseBackups(adbOCID string) (database.ListAutonomousDatabaseBackupsResponse, error) + CreateAutonomousDatabaseBackup(adbBackup *dbv1alpha1.AutonomousDatabaseBackup, adbOCID string) (database.CreateAutonomousDatabaseBackupResponse, error) + GetAutonomousDatabaseBackup(backupOCID string) (database.GetAutonomousDatabaseBackupResponse, error) + CreateAutonomousContainerDatabase(acd *dbv1alpha1.AutonomousContainerDatabase) (database.CreateAutonomousContainerDatabaseResponse, error) + GetAutonomousContainerDatabase(acdOCID string) (database.GetAutonomousContainerDatabaseResponse, error) + UpdateAutonomousContainerDatabase(acdOCID string, difACD *dbv1alpha1.AutonomousContainerDatabase) (database.UpdateAutonomousContainerDatabaseResponse, error) + RestartAutonomousContainerDatabase(acdOCID string) (database.RestartAutonomousContainerDatabaseResponse, error) + TerminateAutonomousContainerDatabase(acdOCID string) (database.TerminateAutonomousContainerDatabaseResponse, error) +} - WhitelistedIps: adb.Spec.Details.WhitelistedIPs, - SubnetId: adb.Spec.Details.SubnetOCID, - NsgIds: adb.Spec.Details.NsgOCIDs, - PrivateEndpointLabel: adb.Spec.Details.PrivateEndpointLabel, - IsMtlsConnectionRequired: adb.Spec.Details.IsMTLSConnectionRequired, +type databaseService struct { + logger logr.Logger + kubeClient client.Client + dbClient database.DatabaseClient + vaultService VaultService +} - FreeformTags: adb.Spec.Details.FreeformTags, - } +func NewDatabaseService( + logger logr.Logger, + kubeClient client.Client, + provider common.ConfigurationProvider) (DatabaseService, error) { - createAutonomousDatabaseRequest := database.CreateAutonomousDatabaseRequest{ - CreateAutonomousDatabaseDetails: createAutonomousDatabaseDetails, + dbClient, err := database.NewDatabaseClientWithConfigurationProvider(provider) + if err != nil { + return nil, err } - resp, err := dbClient.CreateAutonomousDatabase(context.TODO(), createAutonomousDatabaseRequest) + vaultService, err := NewVaultService(logger, provider) if err != nil { return nil, err } - return &resp, nil + return &databaseService{ + logger: logger.WithName("dbService"), + kubeClient: kubeClient, + dbClient: dbClient, + vaultService: vaultService, + }, nil } -// Get the desired admin password from either Kubernetes Secret or OCI Vault Secret. -func getAdminPassword(logger logr.Logger, kubeClient client.Client, secretClient secrets.SecretsClient, adb *dbv1alpha1.AutonomousDatabase) (string, error) { - if adb.Spec.Details.AdminPassword.K8sSecretName != nil { - logger.Info(fmt.Sprintf("Getting admin password from Secret %s", *adb.Spec.Details.AdminPassword.K8sSecretName)) +/******************************** + * Autonomous Database + *******************************/ - namespacedName := types.NamespacedName{ - Namespace: adb.GetNamespace(), - Name: *adb.Spec.Details.AdminPassword.K8sSecretName, - } +// 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) { + logger := d.logger.WithName("readPassword") - key := *adb.Spec.Details.AdminPassword.K8sSecretName - adminPassword, err := getValueFromKubeSecret(kubeClient, namespacedName, key) - if err != nil { - return "", err - } - return adminPassword, nil + if passwordSpec.K8sSecret.Name != nil { + logger.Info(fmt.Sprintf("Getting password from Secret %s", *passwordSpec.K8sSecret.Name)) - } else if adb.Spec.Details.AdminPassword.OCISecretOCID != nil { - logger.Info(fmt.Sprintf("Getting admin password from OCI Vault Secret OCID %s", *adb.Spec.Details.AdminPassword.OCISecretOCID)) - - adminPassword, err := getValueFromVaultSecret(secretClient, *adb.Spec.Details.AdminPassword.OCISecretOCID) + key := *passwordSpec.K8sSecret.Name + password, err := k8s.GetSecretValue(d.kubeClient, namespace, *passwordSpec.K8sSecret.Name, key) if err != nil { - return "", err + return nil, err } - return adminPassword, nil - } - return "", errors.New("should provide either AdminPasswordSecret or AdminPasswordOCID") -} -func getValueFromKubeSecret(kubeClient client.Client, namespacedName types.NamespacedName, key string) (string, error) { - secret := &corev1.Secret{} - if err := kubeClient.Get(context.TODO(), namespacedName, secret); err != nil { - return "", err + return common.String(password), nil } - val, ok := secret.Data[key] - if !ok { - return "", errors.New("Secret key not found: " + key) - } - return string(val), nil -} + if passwordSpec.OCISecret.OCID != nil { + logger.Info(fmt.Sprintf("Getting password from OCI Vault Secret OCID %s", *passwordSpec.OCISecret.OCID)) -// GetAutonomousDatabaseResource gets Autonomous Database information from a remote instance -// and return an AutonomousDatabase object -func GetAutonomousDatabaseResource(logger logr.Logger, dbClient database.DatabaseClient, adb *dbv1alpha1.AutonomousDatabase) (*dbv1alpha1.AutonomousDatabase, error) { - getAutonomousDatabaseRequest := database.GetAutonomousDatabaseRequest{ - AutonomousDatabaseId: adb.Spec.Details.AutonomousDatabaseOCID, - } - - response, err := dbClient.GetAutonomousDatabase(context.TODO(), getAutonomousDatabaseRequest) - if err != nil { - return nil, err + password, err := d.vaultService.GetSecretValue(*passwordSpec.OCISecret.OCID) + if err != nil { + return nil, err + } + return common.String(password), nil } - returnedADB := adb.UpdateAttrFromOCIAutonomousDatabase(response.AutonomousDatabase) - - logger.Info("Get information from remote AutonomousDatabase successfully") - return returnedADB, nil + return nil, nil } -// isAttrChanged checks if the values of last successful object and current object are different. -// The function returns false if the types are mismatch or unknown. -// The function returns false if the current object has zero value (not applicable for boolean type). -func isAttrChanged(lastSucObj interface{}, curObj interface{}) bool { - switch curObj.(type) { - case string: // Enum - // type check - lastSucString, ok := lastSucObj.(string) - if !ok { - return false - } - curString := curObj.(string) - - if curString != "" && (lastSucString != curString) { - return true - } - case *int: - // type check - lastSucIntPtr, ok := lastSucObj.(*int) - if !ok { - return false - } - curIntPtr := curObj.(*int) - - if (lastSucIntPtr == nil && curIntPtr != nil) || (lastSucIntPtr != nil && curIntPtr != nil && *lastSucIntPtr != *curIntPtr) { - return true - } - case *string: - // type check - lastSucStringPtr, ok := lastSucObj.(*string) - if !ok { - return false - } - curStringPtr := curObj.(*string) - - if (lastSucStringPtr == nil && curStringPtr != nil) || (lastSucStringPtr != nil && curStringPtr != nil && *lastSucStringPtr != *curStringPtr) { - return true - } - case *bool: - // type check - lastSucBoolPtr, ok := lastSucObj.(*bool) - if !ok { - return false - } - curBoolPtr := curObj.(*bool) - - // For boolean type, we don't have to check zero value - if (lastSucBoolPtr == nil && curBoolPtr != nil) || (lastSucBoolPtr != nil && curBoolPtr != nil && *lastSucBoolPtr != *curBoolPtr) { - return true - } - case []string: - // type check - lastSucSlice, ok := lastSucObj.([]string) - if !ok { - return false - } - - curSlice := curObj.([]string) - if curSlice == nil { - return false - } else if len(lastSucSlice) != len(curSlice) { - return true - } - - for i, v := range lastSucSlice { - if v != curSlice[i] { - return true - } - } - case map[string]string: - // type check - lastSucMap, ok := lastSucObj.(map[string]string) - if !ok { - return false - } +func (d *databaseService) readACD_OCID(acd *dbv1alpha1.ACDSpec, namespace string) (*string, error) { + if acd.OCIACD.OCID != nil { + return acd.OCIACD.OCID, nil + } - curMap := curObj.(map[string]string) - if curMap == nil { - return false - } else if len(lastSucMap) != len(curMap) { - return true + if acd.K8sACD.Name != nil { + fetchedACD := &dbv1alpha1.AutonomousContainerDatabase{} + if err := k8s.FetchResource(d.kubeClient, namespace, *acd.K8sACD.Name, fetchedACD); err != nil { + return nil, err } - for k, v := range lastSucMap { - if w, ok := curMap[k]; !ok || v != w { - return true - } - } + return fetchedACD.Spec.AutonomousContainerDatabaseOCID, nil } - return false + + return nil, nil } -// UpdateGeneralAndPasswordAttributes updates the general and password attributes of the Autonomous Database. -// Based on the responses from OCI calls, we can split the attributes into the following five categories. -// AutonomousDatabaseOCID, CompartmentOCID, IsDedicated, and LifecycleState are excluded since they not applicable in updateAutonomousDatabaseRequest. -// Except for category 1, category 2 and 3 cannot be updated at the same time, i.e.,we can at most update category 1 plus another category 2,or 3. -// 1. General attribute: including DisplayName, DbName, DbWorkload, DbVersion, freeformTags, subnetOCID, nsgOCIDs, and whitelistedIPs. The general attributes can be updated together with one of the other categories in the same request. -// 2. Scale attribute: includining IsAutoScalingEnabled, CpuCoreCount and DataStorageSizeInTBs. -// 3. Password attribute: including AdminPasswordSecret and AdminPasswordOCID -// From the above rules, we group general and password attributes and send the update together in the same request, and then send the scale update in another request. -func UpdateGeneralAndPasswordAttributes(logger logr.Logger, kubeClient client.Client, dbClient database.DatabaseClient, - secretClient secrets.SecretsClient, curADB *dbv1alpha1.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) { - var shouldSendRequest = false - - lastSucSpec, err := curADB.GetLastSuccessfulSpec() +// 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) { + adminPassword, err := d.readPassword(adb.Namespace, adb.Spec.Details.AdminPassword) if err != nil { return resp, err } - // Prepare the update request - updateAutonomousDatabaseDetails := database.UpdateAutonomousDatabaseDetails{} - - if isAttrChanged(lastSucSpec.Details.DisplayName, curADB.Spec.Details.DisplayName) { - updateAutonomousDatabaseDetails.DisplayName = curADB.Spec.Details.DisplayName - shouldSendRequest = true - } - if isAttrChanged(lastSucSpec.Details.DbName, curADB.Spec.Details.DbName) { - updateAutonomousDatabaseDetails.DbName = curADB.Spec.Details.DbName - shouldSendRequest = true - } - if isAttrChanged(lastSucSpec.Details.DbWorkload, curADB.Spec.Details.DbWorkload) { - updateAutonomousDatabaseDetails.DbWorkload = database.UpdateAutonomousDatabaseDetailsDbWorkloadEnum(curADB.Spec.Details.DbWorkload) - shouldSendRequest = true - } - if isAttrChanged(lastSucSpec.Details.DbVersion, curADB.Spec.Details.DbVersion) { - updateAutonomousDatabaseDetails.DbVersion = curADB.Spec.Details.DbVersion - shouldSendRequest = true - } - - if isAttrChanged(lastSucSpec.Details.FreeformTags, curADB.Spec.Details.FreeformTags) { - updateAutonomousDatabaseDetails.FreeformTags = curADB.Spec.Details.FreeformTags - shouldSendRequest = true + acdOCID, err := d.readACD_OCID(&adb.Spec.Details.AutonomousContainerDatabase, adb.Namespace) + if err != nil { + return resp, err } - if isAttrChanged(lastSucSpec.Details.AdminPassword.K8sSecretName, curADB.Spec.Details.AdminPassword.K8sSecretName) || - isAttrChanged(lastSucSpec.Details.AdminPassword.OCISecretOCID, curADB.Spec.Details.AdminPassword.OCISecretOCID) { - // Get the adminPassword - var adminPassword string - - adminPassword, err = getAdminPassword(logger, kubeClient, secretClient, curADB) - if err != nil { - return - } - updateAutonomousDatabaseDetails.AdminPassword = common.String(adminPassword) + createAutonomousDatabaseDetails := database.CreateAutonomousDatabaseDetails{ + CompartmentId: adb.Spec.Details.CompartmentOCID, + DbName: adb.Spec.Details.DbName, + CpuCoreCount: adb.Spec.Details.CPUCoreCount, + DataStorageSizeInTBs: adb.Spec.Details.DataStorageSizeInTBs, + AdminPassword: adminPassword, + DisplayName: adb.Spec.Details.DisplayName, + IsAutoScalingEnabled: adb.Spec.Details.IsAutoScalingEnabled, + 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, - shouldSendRequest = true + FreeformTags: adb.Spec.Details.FreeformTags, } - // Send the request only when something changes - if shouldSendRequest { - - logger.Info("Sending general attributes and ADMIN password update request") - - updateAutonomousDatabaseRequest := database.UpdateAutonomousDatabaseRequest{ - // AutonomousDatabaseId: common.String(curADB.Spec.Details.AutonomousDatabaseOCID), - AutonomousDatabaseId: curADB.Spec.Details.AutonomousDatabaseOCID, - UpdateAutonomousDatabaseDetails: updateAutonomousDatabaseDetails, - } - - resp, err = dbClient.UpdateAutonomousDatabase(context.TODO(), updateAutonomousDatabaseRequest) + createAutonomousDatabaseRequest := database.CreateAutonomousDatabaseRequest{ + CreateAutonomousDatabaseDetails: createAutonomousDatabaseDetails, } - return -} - -// UpdateScaleAttributes updates the scale attributes of the Autonomous Database -// Refer to UpdateGeneralAndPasswordAttributes for more details about how and why we separate the attributes in different calls. -func UpdateScaleAttributes(logger logr.Logger, kubeClient client.Client, dbClient database.DatabaseClient, - curADB *dbv1alpha1.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) { - var shouldSendRequest = false - - lastSucSpec, err := curADB.GetLastSuccessfulSpec() + resp, err = d.dbClient.CreateAutonomousDatabase(context.TODO(), createAutonomousDatabaseRequest) if err != nil { return resp, err } - // Prepare the update request - updateAutonomousDatabaseDetails := database.UpdateAutonomousDatabaseDetails{} - - if isAttrChanged(lastSucSpec.Details.DataStorageSizeInTBs, curADB.Spec.Details.DataStorageSizeInTBs) { - updateAutonomousDatabaseDetails.DataStorageSizeInTBs = curADB.Spec.Details.DataStorageSizeInTBs - shouldSendRequest = true - } - if isAttrChanged(lastSucSpec.Details.CPUCoreCount, curADB.Spec.Details.CPUCoreCount) { - updateAutonomousDatabaseDetails.CpuCoreCount = curADB.Spec.Details.CPUCoreCount - shouldSendRequest = true - } - if isAttrChanged(lastSucSpec.Details.IsAutoScalingEnabled, curADB.Spec.Details.IsAutoScalingEnabled) { - updateAutonomousDatabaseDetails.IsAutoScalingEnabled = curADB.Spec.Details.IsAutoScalingEnabled - shouldSendRequest = true - } - - // Don't send the request if nothing is changed - if shouldSendRequest { - - logger.Info("Sending scale attributes update request") - - updateAutonomousDatabaseRequest := database.UpdateAutonomousDatabaseRequest{ - // AutonomousDatabaseId: common.String(curADB.Spec.Details.AutonomousDatabaseOCID), - AutonomousDatabaseId: curADB.Spec.Details.AutonomousDatabaseOCID, - UpdateAutonomousDatabaseDetails: updateAutonomousDatabaseDetails, - } + return resp, nil +} - resp, err = dbClient.UpdateAutonomousDatabase(context.TODO(), updateAutonomousDatabaseRequest) +func (d *databaseService) GetAutonomousDatabase(adbOCID string) (database.GetAutonomousDatabaseResponse, error) { + getAutonomousDatabaseRequest := database.GetAutonomousDatabaseRequest{ + AutonomousDatabaseId: common.String(adbOCID), } - return + return d.dbClient.GetAutonomousDatabase(context.TODO(), getAutonomousDatabaseRequest) } -func UpdateOneWayTLSAttribute(logger logr.Logger, kubeClient client.Client, dbClient database.DatabaseClient, - curADB *dbv1alpha1.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) { - var shouldSendRequest = false - - lastSucSpec, err := curADB.GetLastSuccessfulSpec() - if err != nil { - return resp, err +func (d *databaseService) UpdateAutonomousDatabaseGeneralFields(adbOCID string, difADB *dbv1alpha1.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) +} - // Prepare the update request - updateAutonomousDatabaseDetails := database.UpdateAutonomousDatabaseDetails{} - - if isAttrChanged(lastSucSpec.Details.IsMTLSConnectionRequired, curADB.Spec.Details.IsMTLSConnectionRequired) { - updateAutonomousDatabaseDetails.IsMtlsConnectionRequired = curADB.Spec.Details.IsMTLSConnectionRequired - shouldSendRequest = true +func (d *databaseService) UpdateAutonomousDatabaseDBWorkload(adbOCID string, difADB *dbv1alpha1.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) { + updateAutonomousDatabaseRequest := database.UpdateAutonomousDatabaseRequest{ + AutonomousDatabaseId: common.String(adbOCID), + UpdateAutonomousDatabaseDetails: database.UpdateAutonomousDatabaseDetails{ + DbWorkload: database.UpdateAutonomousDatabaseDetailsDbWorkloadEnum(difADB.Spec.Details.DbWorkload), + }, } + return d.dbClient.UpdateAutonomousDatabase(context.TODO(), updateAutonomousDatabaseRequest) +} - // Don't send the request if nothing is changed - if shouldSendRequest { - - logger.Info("Sending 1-way TLS attribute update request") - - updateAutonomousDatabaseRequest := database.UpdateAutonomousDatabaseRequest{ - AutonomousDatabaseId: curADB.Spec.Details.AutonomousDatabaseOCID, - UpdateAutonomousDatabaseDetails: updateAutonomousDatabaseDetails, - } - - resp, err = dbClient.UpdateAutonomousDatabase(context.TODO(), updateAutonomousDatabaseRequest) +func (d *databaseService) UpdateAutonomousDatabaseLicenseModel(adbOCID string, difADB *dbv1alpha1.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) { + updateAutonomousDatabaseRequest := database.UpdateAutonomousDatabaseRequest{ + AutonomousDatabaseId: common.String(adbOCID), + UpdateAutonomousDatabaseDetails: database.UpdateAutonomousDatabaseDetails{ + LicenseModel: database.UpdateAutonomousDatabaseDetailsLicenseModelEnum(difADB.Spec.Details.LicenseModel), + }, } - - return + return d.dbClient.UpdateAutonomousDatabase(context.TODO(), updateAutonomousDatabaseRequest) } -func UpdateNetworkAttributes(logger logr.Logger, kubeClient client.Client, dbClient database.DatabaseClient, - curADB *dbv1alpha1.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) { - var shouldSendRequest = false - - lastSucSpec, err := curADB.GetLastSuccessfulSpec() +func (d *databaseService) UpdateAutonomousDatabaseAdminPassword(adbOCID string, difADB *dbv1alpha1.AutonomousDatabase) (resp database.UpdateAutonomousDatabaseResponse, err error) { + adminPassword, err := d.readPassword(difADB.Namespace, difADB.Spec.Details.AdminPassword) if err != nil { return resp, err } - // Prepare the update request - updateAutonomousDatabaseDetails := database.UpdateAutonomousDatabaseDetails{} - - // Network settings - if isAttrChanged(lastSucSpec.Details.SubnetOCID, curADB.Spec.Details.SubnetOCID) { - updateAutonomousDatabaseDetails.SubnetId = curADB.Spec.Details.SubnetOCID - shouldSendRequest = true - } - if isAttrChanged(lastSucSpec.Details.NsgOCIDs, curADB.Spec.Details.NsgOCIDs) { - updateAutonomousDatabaseDetails.NsgIds = curADB.Spec.Details.NsgOCIDs - shouldSendRequest = true - } - if isAttrChanged(lastSucSpec.Details.IsAccessControlEnabled, curADB.Spec.Details.IsAccessControlEnabled) { - updateAutonomousDatabaseDetails.IsAccessControlEnabled = curADB.Spec.Details.IsAccessControlEnabled - shouldSendRequest = true - } - if isAttrChanged(lastSucSpec.Details.WhitelistedIPs, curADB.Spec.Details.WhitelistedIPs) { - updateAutonomousDatabaseDetails.WhitelistedIps = curADB.Spec.Details.WhitelistedIPs - shouldSendRequest = true - } - // PrivateEndpointLabel - if isAttrChanged(lastSucSpec.Details.PrivateEndpointLabel, curADB.Spec.Details.PrivateEndpointLabel) { - updateAutonomousDatabaseDetails.PrivateEndpointLabel = curADB.Spec.Details.PrivateEndpointLabel - shouldSendRequest = true + updateAutonomousDatabaseRequest := database.UpdateAutonomousDatabaseRequest{ + AutonomousDatabaseId: common.String(adbOCID), + UpdateAutonomousDatabaseDetails: database.UpdateAutonomousDatabaseDetails{ + AdminPassword: adminPassword, + }, } + return d.dbClient.UpdateAutonomousDatabase(context.TODO(), updateAutonomousDatabaseRequest) +} - // Don't send the request if nothing is changed - if shouldSendRequest { - - logger.Info("Sending network attributes update request") - - updateAutonomousDatabaseRequest := database.UpdateAutonomousDatabaseRequest{ - AutonomousDatabaseId: curADB.Spec.Details.AutonomousDatabaseOCID, - UpdateAutonomousDatabaseDetails: updateAutonomousDatabaseDetails, - } - - resp, err = dbClient.UpdateAutonomousDatabase(context.TODO(), updateAutonomousDatabaseRequest) +func (d *databaseService) UpdateAutonomousDatabaseScalingFields(adbOCID string, difADB *dbv1alpha1.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, + }, } - - return + return d.dbClient.UpdateAutonomousDatabase(context.TODO(), updateAutonomousDatabaseRequest) } -// SetAutonomousDatabaseLifecycleState starts or stops AutonomousDatabase in OCI based on the LifeCycleState attribute -func SetAutonomousDatabaseLifecycleState(logger logr.Logger, dbClient database.DatabaseClient, adb *dbv1alpha1.AutonomousDatabase) (resp interface{}, err error) { - lastSucSpec, err := adb.GetLastSuccessfulSpec() - if err != nil { - return resp, err +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), + }, } + return d.dbClient.UpdateAutonomousDatabase(context.TODO(), updateAutonomousDatabaseRequest) +} - // Return if the desired lifecycle state is the same as the current lifecycle state - if adb.Spec.Details.LifecycleState == lastSucSpec.Details.LifecycleState { - return nil, nil +func (d *databaseService) UpdateNetworkAccessMTLS(adbOCID string, difADB *dbv1alpha1.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) +} - switch string(adb.Spec.Details.LifecycleState) { - case string(database.AutonomousDatabaseLifecycleStateAvailable): - logger.Info("Sending start request to the Autonomous Database " + *adb.Spec.Details.DbName) +func (d *databaseService) UpdateNetworkAccessPublic( + lastAccessType dbv1alpha1.NetworkAccessTypeEnum, + adbOCID string) (resp database.UpdateAutonomousDatabaseResponse, err error) { - resp, err = startAutonomousDatabase(dbClient, *adb.Spec.Details.AutonomousDatabaseOCID) - if err != nil { - return - } - - case string(database.AutonomousDatabaseLifecycleStateStopped): - logger.Info("Sending stop request to the Autonomous Database " + *adb.Spec.Details.DbName) + updateAutonomousDatabaseDetails := database.UpdateAutonomousDatabaseDetails{} - resp, err = stopAutonomousDatabase(dbClient, *adb.Spec.Details.AutonomousDatabaseOCID) - if err != nil { - return - } + if lastAccessType == dbv1alpha1.NetworkAccessTypeRestricted { + updateAutonomousDatabaseDetails.WhitelistedIps = []string{""} + } else if lastAccessType == dbv1alpha1.NetworkAccessTypePrivate { + updateAutonomousDatabaseDetails.PrivateEndpointLabel = common.String("") + } - case string(database.AutonomousDatabaseLifecycleStateTerminated): - // Special case. - if adb.Spec.Details.LifecycleState == database.AutonomousDatabaseLifecycleStateTerminating { - break - } - logger.Info("Sending teminate request to the Autonomous Database " + *adb.Spec.Details.DbName) + updateAutonomousDatabaseRequest := database.UpdateAutonomousDatabaseRequest{ + AutonomousDatabaseId: common.String(adbOCID), + UpdateAutonomousDatabaseDetails: updateAutonomousDatabaseDetails, + } - resp, err = DeleteAutonomousDatabase(dbClient, *adb.Spec.Details.AutonomousDatabaseOCID) - if err != nil { - return - } + return d.dbClient.UpdateAutonomousDatabase(context.TODO(), updateAutonomousDatabaseRequest) +} - default: - err = fmt.Errorf("invalid lifecycleState value: currently the operator only accept %s, %s and %s as the value of the lifecycleState parameter", - database.AutonomousDatabaseLifecycleStateAvailable, - database.AutonomousDatabaseLifecycleStateStopped, - database.AutonomousDatabaseLifecycleStateTerminated) +func (d *databaseService) UpdateNetworkAccess(adbOCID string, difADB *dbv1alpha1.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 + return d.dbClient.UpdateAutonomousDatabase(context.TODO(), updateAutonomousDatabaseRequest) } -// startAutonomousDatabase starts an Autonomous Database in OCI -func startAutonomousDatabase(dbClient database.DatabaseClient, adbOCID string) (resp database.StartAutonomousDatabaseResponse, err error) { +func (d *databaseService) StartAutonomousDatabase(adbOCID string) (database.StartAutonomousDatabaseResponse, error) { startRequest := database.StartAutonomousDatabaseRequest{ AutonomousDatabaseId: common.String(adbOCID), } - resp, err = dbClient.StartAutonomousDatabase(context.Background(), startRequest) - return + return d.dbClient.StartAutonomousDatabase(context.TODO(), startRequest) } -// stopAutonomousDatabase stops an Autonomous Database in OCI -func stopAutonomousDatabase(dbClient database.DatabaseClient, adbOCID string) (resp database.StopAutonomousDatabaseResponse, err error) { +func (d *databaseService) StopAutonomousDatabase(adbOCID string) (database.StopAutonomousDatabaseResponse, error) { stopRequest := database.StopAutonomousDatabaseRequest{ AutonomousDatabaseId: common.String(adbOCID), } - resp, err = dbClient.StopAutonomousDatabase(context.Background(), stopRequest) - return + return d.dbClient.StopAutonomousDatabase(context.TODO(), stopRequest) } -// DeleteAutonomousDatabase terminates an Autonomous Database in OCI -func DeleteAutonomousDatabase(dbClient database.DatabaseClient, adbOCID string) (resp database.DeleteAutonomousDatabaseResponse, err error) { - +func (d *databaseService) DeleteAutonomousDatabase(adbOCID string) (database.DeleteAutonomousDatabaseResponse, error) { deleteRequest := database.DeleteAutonomousDatabaseRequest{ AutonomousDatabaseId: common.String(adbOCID), } - resp, err = dbClient.DeleteAutonomousDatabase(context.Background(), deleteRequest) - return + return d.dbClient.DeleteAutonomousDatabase(context.TODO(), deleteRequest) } -func WaitUntilWorkCompleted(logger logr.Logger, workClient workrequests.WorkRequestClient, opcWorkRequestID *string) error { - if opcWorkRequestID == nil { - return nil +func (d *databaseService) DownloadWallet(adb *dbv1alpha1.AutonomousDatabase) (resp database.GenerateAutonomousDatabaseWalletResponse, err error) { + // Prepare wallet password + walletPassword, err := d.readPassword(adb.Namespace, adb.Spec.Details.Wallet.Password) + if err != nil { + return resp, err } - logger.Info("Waiting for the work request to finish. opcWorkRequestID = " + *opcWorkRequestID) - - retryPolicy := getCompleteWorkRetryPolicy() - // Apply wait until work complete retryPolicy - workRequest := workrequests.GetWorkRequestRequest{ - WorkRequestId: opcWorkRequestID, - RequestMetadata: common.RequestMetadata{ - RetryPolicy: &retryPolicy, + // Download a Wallet + req := database.GenerateAutonomousDatabaseWalletRequest{ + AutonomousDatabaseId: adb.Spec.Details.AutonomousDatabaseOCID, + GenerateAutonomousDatabaseWalletDetails: database.GenerateAutonomousDatabaseWalletDetails{ + Password: walletPassword, }, } - // GetWorkRequest retries until the work status is SUCCEEDED - if _, err := workClient.GetWorkRequest(context.TODO(), workRequest); err != nil { - return err + // Send the request using the service client + resp, err = d.dbClient.GenerateAutonomousDatabaseWallet(context.TODO(), req) + if err != nil { + return resp, err } - return nil + return resp, nil } -func getCompleteWorkRetryPolicy() common.RetryPolicy { - shouldRetry := func(r common.OCIOperationResponse) bool { - if _, isServiceError := common.IsServiceError(r.Error); isServiceError { - // Don't retry if it's service error. Sometimes it could be network error or other errors which prevents - // request send to server; we do the retry in these cases. - return false - } +/******************************** + * Autonomous Database Restore + *******************************/ - if converted, ok := r.Response.(workrequests.GetWorkRequestResponse); ok { - // do the retry until WorkReqeut Status is Succeeded - ignore case (BMI-2652) - return converted.Status != workrequests.WorkRequestStatusSucceeded - } +func (d *databaseService) RestoreAutonomousDatabase(adbOCID string, sdkTime common.SDKTime) (database.RestoreAutonomousDatabaseResponse, error) { + request := database.RestoreAutonomousDatabaseRequest{ + AutonomousDatabaseId: common.String(adbOCID), + RestoreAutonomousDatabaseDetails: database.RestoreAutonomousDatabaseDetails{ + Timestamp: &sdkTime, + }, + } + return d.dbClient.RestoreAutonomousDatabase(context.TODO(), request) +} + +/******************************** + * Autonomous Database Backup + *******************************/ - return true +func (d *databaseService) ListAutonomousDatabaseBackups(adbOCID string) (database.ListAutonomousDatabaseBackupsResponse, error) { + listBackupRequest := database.ListAutonomousDatabaseBackupsRequest{ + AutonomousDatabaseId: common.String(adbOCID), } - return getRetryPolicy(shouldRetry) + return d.dbClient.ListAutonomousDatabaseBackups(context.TODO(), listBackupRequest) } -func getRetryPolicy(retryOperation func(common.OCIOperationResponse) bool) common.RetryPolicy { - // maximum times of retry - attempts := uint(10) +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, + RetentionPeriodInDays: adbBackup.Spec.RetentionPeriodInDays, + }, + } + + // Use the spec.displayName as the displayName of the backup if is provided, + // otherwise use the resource name as the displayName. + if adbBackup.Spec.DisplayName != nil { + createBackupRequest.DisplayName = adbBackup.Spec.DisplayName + } else { + createBackupRequest.DisplayName = common.String(adbBackup.GetName()) + } + + return d.dbClient.CreateAutonomousDatabaseBackup(context.TODO(), createBackupRequest) +} - nextDuration := func(r common.OCIOperationResponse) time.Duration { - // you might want wait longer for next retry when your previous one failed - // this function will return the duration as: - // 1s, 2s, 4s, 8s, 16s, 32s, 64s etc... - return time.Duration(math.Pow(float64(2), float64(r.AttemptNumber-1))) * time.Second +func (d *databaseService) GetAutonomousDatabaseBackup(backupOCID string) (database.GetAutonomousDatabaseBackupResponse, error) { + getBackupRequest := database.GetAutonomousDatabaseBackupRequest{ + AutonomousDatabaseBackupId: common.String(backupOCID), } - return common.NewRetryPolicy(attempts, retryOperation, nextDuration) + return d.dbClient.GetAutonomousDatabaseBackup(context.TODO(), getBackupRequest) } diff --git a/commons/oci/provider.go b/commons/oci/provider.go index ec653d09..152f1efd 100644 --- a/commons/oci/provider.go +++ b/commons/oci/provider.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2022, 2024 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -39,16 +39,16 @@ package oci import ( - "context" "errors" + "fmt" + "os" - "github.com/oracle/oci-go-sdk/v51/common" - "github.com/oracle/oci-go-sdk/v51/common/auth" + "github.com/oracle/oci-go-sdk/v65/common" + "github.com/oracle/oci-go-sdk/v65/common/auth" "sigs.k8s.io/controller-runtime/pkg/client" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" + "github.com/oracle/oracle-database-operator/commons/k8s" ) const ( @@ -67,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 @@ -82,16 +84,36 @@ 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 - // Read ConfigMap - configMapNamespacedName := types.NamespacedName{ - Namespace: authData.Namespace, - Name: *authData.ConfigMapName, - } - ociConfigMap := &corev1.ConfigMap{} - if err := kubeClient.Get(context.TODO(), configMapNamespacedName, ociConfigMap); err != nil { + // Prepare ConfigMap + ociConfigMap, err := k8s.FetchConfigMap(kubeClient, authData.Namespace, *authData.ConfigMapName) + if err != nil { return nil, err } @@ -111,24 +133,11 @@ func getProviderWithAPIKey(kubeClient client.Client, authData APIKeyAuth) (commo } } - // Read Secret - secretNamespacedName := types.NamespacedName{ - Namespace: authData.Namespace, - Name: *authData.SecretName, - } - - privatekeySecret := &corev1.Secret{} - if err := kubeClient.Get(context.TODO(), secretNamespacedName, privatekeySecret); err != nil { + // Prepare privatekey value + privatekeyValue, err = k8s.GetSecretValue(kubeClient, authData.Namespace, *authData.SecretName, privatekeyKey) + if err != nil { return nil, err } - for key, val := range privatekeySecret.Data { - if key == privatekeyKey { - privatekeyValue = string(val) - } else { - return nil, errors.New("Unable to identify the key: " + key) - } - } - return common.NewRawConfigurationProvider(tenancy, user, region, fingerprint, privatekeyValue, &passphrase), nil } diff --git a/commons/oci/vault.go b/commons/oci/vault.go index 5df1cc04..d7069a88 100644 --- a/commons/oci/vault.go +++ b/commons/oci/vault.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2022 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -42,16 +42,41 @@ import ( "context" "encoding/base64" - "github.com/oracle/oci-go-sdk/v51/common" - "github.com/oracle/oci-go-sdk/v51/secrets" + "github.com/go-logr/logr" + "github.com/oracle/oci-go-sdk/v65/common" + "github.com/oracle/oci-go-sdk/v65/secrets" ) -func getValueFromVaultSecret(secretClient secrets.SecretsClient, vaultSecretOCID string) (string, error) { +type VaultService interface { + GetSecretValue(vaultSecretOCID string) (string, error) +} + +type vaultService struct { + logger logr.Logger + secretClient secrets.SecretsClient +} + +func NewVaultService( + logger logr.Logger, + provider common.ConfigurationProvider) (VaultService, error) { + + secretClient, err := secrets.NewSecretsClientWithConfigurationProvider(provider) + if err != nil { + return nil, err + } + + return &vaultService{ + logger: logger.WithName("vaultService"), + secretClient: secretClient, + }, nil +} + +func (v *vaultService) GetSecretValue(vaultSecretOCID string) (string, error) { request := secrets.GetSecretBundleRequest{ SecretId: common.String(vaultSecretOCID), } - response, err := secretClient.GetSecretBundle(context.TODO(), request) + response, err := v.secretClient.GetSecretBundle(context.TODO(), request) if err != nil { return "", err } diff --git a/commons/oci/wallet.go b/commons/oci/wallet.go index 268e66c5..076460b1 100644 --- a/commons/oci/wallet.go +++ b/commons/oci/wallet.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2022 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -40,142 +40,71 @@ package oci import ( "archive/zip" - "context" - "errors" - "fmt" "io" "io/ioutil" - "math" - "time" - - "github.com/go-logr/logr" - "github.com/oracle/oci-go-sdk/v51/common" - "github.com/oracle/oci-go-sdk/v51/database" - "github.com/oracle/oci-go-sdk/v51/secrets" - "sigs.k8s.io/controller-runtime/pkg/client" - - "k8s.io/apimachinery/pkg/types" - - dbv1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + "strings" ) -// GetWallet downloads the wallet using the given information in the AutonomousDatabase object. -// The function then unzips the wallet and returns a map object which holds the byte values of the unzipped files. -func GetWallet(logger logr.Logger, kubeClient client.Client, dbClient database.DatabaseClient, secretClient secrets.SecretsClient, adb *dbv1alpha1.AutonomousDatabase) (map[string][]byte, error) { - // Get the wallet password from Secret then Vault Secret - walletPassword, err := getWalletPassword(logger, kubeClient, secretClient, adb) - if err != nil { - return nil, err - } - - // Request to download a wallet with the given password - resp, err := generateAutonomousDatabaseWallet(dbClient, *adb.Spec.Details.AutonomousDatabaseOCID, walletPassword) +// ExtractWallet extracts the wallet and returns a map object which holds the byte values of the unzipped files. +func ExtractWallet(content io.ReadCloser) (map[string][]byte, error) { + path, err := saveWalletZip(content) if err != nil { return nil, err } - // Unzip the file - outZip, err := ioutil.TempFile("", "wallet*.zip") - if err != nil { - return nil, err - } - defer outZip.Close() - - if _, err := io.Copy(outZip, resp.Content); err != nil { - return nil, err - } - - data, err := unzipWallet(outZip.Name()) + data, err := unzipWallet(path) if err != nil { return nil, err } return data, nil } -func getWalletPassword(logger logr.Logger, kubeClient client.Client, secretClient secrets.SecretsClient, adb *dbv1alpha1.AutonomousDatabase) (string, error) { - if adb.Spec.Details.Wallet.Password.K8sSecretName != nil { - logger.Info(fmt.Sprintf("Getting wallet password from Secret %s", *adb.Spec.Details.Wallet.Password.K8sSecretName)) - - namespacedName := types.NamespacedName{ - Namespace: adb.GetNamespace(), - Name: *adb.Spec.Details.Wallet.Password.K8sSecretName, - } - - key := *adb.Spec.Details.Wallet.Password.K8sSecretName - walletPassword, err := getValueFromKubeSecret(kubeClient, namespacedName, key) - if err != nil { - return "", err - } - return walletPassword, nil - - } else if adb.Spec.Details.Wallet.Password.OCISecretOCID != nil { - logger.Info(fmt.Sprintf("Getting wallet password from OCI Vault Secret OCID %s", *adb.Spec.Details.Wallet.Password.OCISecretOCID)) - - walletPassword, err := getValueFromVaultSecret(secretClient, *adb.Spec.Details.Wallet.Password.OCISecretOCID) - if err != nil { - return "", err - } - return walletPassword, nil - } - return "", errors.New("should provide either InstancewalletPasswordSecret or a InstancewalletPasswordId") -} - -func generateAutonomousDatabaseWallet(dbClient database.DatabaseClient, adbOCID string, walletPassword string) (database.GenerateAutonomousDatabaseWalletResponse, error) { - - // maximum times of retry - attempts := uint(10) - - // retry for all non-200 status code - retryOnAllNon200ResponseCodes := func(r common.OCIOperationResponse) bool { - return !(r.Error == nil && 199 < r.Response.HTTPResponse().StatusCode && r.Response.HTTPResponse().StatusCode < 300) - } - - nextDuration := func(r common.OCIOperationResponse) time.Duration { - // Wait longer for next retry when your previous one failed - // this function will return the duration as: - // 1s, 2s, 4s, 8s, 16s, 32s, 64s etc... - return time.Duration(math.Pow(float64(2), float64(r.AttemptNumber-1))) * time.Second +func saveWalletZip(content io.ReadCloser) (string, error) { + // Create a temp file wallet*.zip + const walletFileName = "wallet*.zip" + outZip, err := ioutil.TempFile("", walletFileName) + if err != nil { + return "", err } + defer outZip.Close() - walletRetryPolicy := common.NewRetryPolicy(attempts, retryOnAllNon200ResponseCodes, nextDuration) - - // Download a Wallet - req := database.GenerateAutonomousDatabaseWalletRequest{ - AutonomousDatabaseId: common.String(adbOCID), - GenerateAutonomousDatabaseWalletDetails: database.GenerateAutonomousDatabaseWalletDetails{ - Password: common.String(walletPassword), - }, - RequestMetadata: common.RequestMetadata{ - RetryPolicy: &walletRetryPolicy, - }, + // Save the wallet in wallet*.zip + if _, err := io.Copy(outZip, content); err != nil { + return "", err } - // Send the request using the service client - return dbClient.GenerateAutonomousDatabaseWallet(context.TODO(), req) + return outZip.Name(), nil } -func unzipWallet(filename string) (map[string][]byte, error) { - data := map[string][]byte{} +func unzipWallet(path string) (map[string][]byte, error) { + files := map[string][]byte{} - reader, err := zip.OpenReader(filename) + 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")) } diff --git a/commons/oci/workrequest.go b/commons/oci/workrequest.go new file mode 100644 index 00000000..f68d2766 --- /dev/null +++ b/commons/oci/workrequest.go @@ -0,0 +1,102 @@ +/* +** 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 oci + +import ( + "context" + + "github.com/go-logr/logr" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/oracle/oci-go-sdk/v65/common" + "github.com/oracle/oci-go-sdk/v65/workrequests" +) + +type WorkRequestService interface { + Get(opcWorkRequestID string) (workrequests.GetWorkRequestResponse, error) + List(compartmentID string, resourceID string) (workrequests.ListWorkRequestsResponse, error) +} + +type workRequestService struct { + logger logr.Logger + workClient workrequests.WorkRequestClient +} + +func NewWorkRequestService( + logger logr.Logger, + kubeClient client.Client, + provider common.ConfigurationProvider) (WorkRequestService, error) { + + workClient, err := workrequests.NewWorkRequestClientWithConfigurationProvider(provider) + if err != nil { + return nil, err + } + + return &workRequestService{ + logger: logger.WithName("workRequestService"), + workClient: workClient, + }, nil +} + +func (w *workRequestService) Get(opcWorkRequestID string) (workrequests.GetWorkRequestResponse, error) { + workRequest := workrequests.GetWorkRequestRequest{ + WorkRequestId: common.String(opcWorkRequestID), + } + + resp, err := w.workClient.GetWorkRequest(context.TODO(), workRequest) + if err != nil { + return resp, err + } + + return resp, nil +} + +func (w *workRequestService) List(compartmentID string, resourceID string) (workrequests.ListWorkRequestsResponse, error) { + req := workrequests.ListWorkRequestsRequest{ + CompartmentId: common.String(compartmentID), + ResourceId: common.String(resourceID), + } + + resp, err := w.workClient.ListWorkRequests(context.TODO(), req) + if err != nil { + return resp, err + } + + return resp, nil +} diff --git a/commons/sharding/catalog.go b/commons/sharding/catalog.go index a24abeda..58f07490 100644 --- a/commons/sharding/catalog.go +++ b/commons/sharding/catalog.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2022 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -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" @@ -141,10 +140,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 +173,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 +193,16 @@ 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{}}}) + } + + 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 } @@ -208,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{ @@ -217,29 +224,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), - Handler: corev1.Handler{ + 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 +283,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,13 +324,24 @@ 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 { 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 } @@ -328,7 +366,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"), }, @@ -348,6 +386,34 @@ func volumeClaimTemplatesForCatalog(instance *databasev1alpha1.ShardingDatabase, claims[0].Spec.Selector = &metav1.LabelSelector{MatchLabels: OraCatalogSpex.PvMatchLabels} } + 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"), + }, + }, + }, + } + + claims = append(claims, pvcClaim) + } + } + } + return claims } @@ -430,7 +496,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 e368bf74..44f91e51 100644 --- a/commons/sharding/exec.go +++ b/commons/sharding/exec.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2022 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -40,26 +40,56 @@ package commons import ( "bytes" - databasealphav1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + "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 -func ExecCommand(podName string, cmd []string, kubeClient kubernetes.Interface, kubeConfig clientcmd.ClientConfig, instance *databasealphav1.ShardingDatabase, logger logr.Logger) (error, string, string) { +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). @@ -75,7 +105,7 @@ func ExecCommand(podName string, cmd []string, kubeClient kubernetes.Interface, config, err := kubeConfig.ClientConfig() if err != nil { - return err, "Error Occurred", "Error Occurred" + return "Error Occurred", "Error Occurred", err } // Connect to url (constructed from req) using SPDY (HTTP/2) protocol which allows bidirectional streams. @@ -83,7 +113,7 @@ func ExecCommand(podName string, cmd []string, kubeClient kubernetes.Interface, if err != nil { msg = "Error after executing remotecommand.NewSPDYExecutor" LogMessages("Error", msg, err, instance, logger) - return err, "Error Occurred", "Error Occurred" + return "Error Occurred", "Error Occurred", err } err = exec.Stream(remotecommand.StreamOptions{ @@ -100,8 +130,73 @@ func ExecCommand(podName string, cmd []string, kubeClient kubernetes.Interface, if len(execErr.String()) > 0 { LogMessages("INFO", execErr.String(), nil, instance, logger) } - return err, execOut.String(), execErr.String() + return execOut.String(), execErr.String(), err } - return nil, execOut.String(), execErr.String() + 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 720c00df..bcdc8866 100644 --- a/commons/sharding/gsm.go +++ b/commons/sharding/gsm.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2022 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -41,10 +41,11 @@ package commons import ( "context" "fmt" - databasev1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" "reflect" "strconv" + 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" @@ -120,6 +121,7 @@ func buildStatefulSpecForGsm(instance *databasev1alpha1.ShardingDatabase, OraGsm }, VolumeClaimTemplates: volumeClaimTemplatesForGsm(instance, OraGsmSpex), } + /** if OraGsmSpex.Replicas == 0 { OraGsmSpex.Replicas = 1 sfsetspec.Replicas = &OraGsmSpex.Replicas @@ -127,6 +129,7 @@ func buildStatefulSpecForGsm(instance *databasev1alpha1.ShardingDatabase, OraGsm OraGsmSpex.Replicas = 1 sfsetspec.Replicas = &OraGsmSpex.Replicas } + **/ return sfsetspec } @@ -142,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{ { @@ -170,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{ @@ -196,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 } @@ -229,11 +233,16 @@ 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), - Handler: corev1.Handler{ + 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"), }, @@ -262,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 @@ -304,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 { @@ -335,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"), }, @@ -429,8 +440,7 @@ func UpdateProvForGsm(instance *databasev1alpha1.ShardingDatabase, ) (ctrl.Result, error) { var msg string - var size int32 - size = 1 + var size int32 = 1 var isUpdate bool = false var err error var i int @@ -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 } @@ -456,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/provstatus.go b/commons/sharding/provstatus.go index ac7e8c02..87796553 100644 --- a/commons/sharding/provstatus.go +++ b/commons/sharding/provstatus.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2022 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -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) @@ -335,12 +335,11 @@ func GetMetaCondition(instance *databasealphav1.ShardingDatabase, result *ctrl.R func CheckGsmStatus(gname string, instance *databasealphav1.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, ) error { var err error - var msg string + var msg string = "Inside the checkGsmStatus. Checking GSM director in " + GetFmtStr(gname) + " pod." - msg = "Inside the checkGsmStatus. Checking GSM director in " + GetFmtStr(gname) + " pod." LogMessages("DEBUG", msg, nil, instance, logger) - err, _, _ = ExecCommand(gname, getGsmvalidateCmd(), kubeClient, kubeconfig, instance, logger) + _, _, err = ExecCommand(gname, getGsmvalidateCmd(), kubeClient, kubeconfig, instance, logger) if err != nil { return err } @@ -348,14 +347,14 @@ 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 { - err, _, _ := ExecCommand(podName, shardValidationCmd(), kubeClient, kubeconfig, instance, logger) + _, _, err := ExecCommand(podName, shardValidationCmd(), kubeClient, kubeconfig, instance, logger) if err != nil { - return fmt.Errorf("Error ocurred while validating the DB Setup") + 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 5af51390..99987661 100644 --- a/commons/sharding/scommon.go +++ b/commons/sharding/scommon.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2022 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -40,16 +40,22 @@ package commons import ( "context" + "encoding/json" "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/v51/common" - "github.com/oracle/oci-go-sdk/v51/ons" + "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" @@ -76,6 +82,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" @@ -90,6 +97,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 @@ -97,34 +107,28 @@ 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 sidValue string + var pdbValue string 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 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 - } - if variable.Name == "SECRET_VOLUME" { - secretFlag = true - } - if variable.Name == "COMMON_OS_PWD_FILE" { - pwdFileFLag = true - } - if variable.Name == "PWD_KEY" { - pwdKeyFlag = true + //sidValue = variable.Value } if variable.Name == "ORACLE_PDB" { pdbFlag = true + pdbValue = variable.Value } if variable.Name == "SHARD_DIRECTOR_PARAMS" { sDirectParam = true @@ -132,9 +136,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 } @@ -150,8 +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"}) @@ -171,46 +196,112 @@ 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)}) - } - 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"}) + } } } - 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 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.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.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 != "" { + result = append(result, corev1.EnvVar{Name: "INVITED_NODE_SUBNET", Value: instance.Spec.InvitedNodeSubnet}) } } if !catalogParams { @@ -486,15 +577,21 @@ func PodListValidation(podList *corev1.PodList, sfName string, instance *databas func GetPodList(sfsetName string, resType string, instance *databasealphav1.ShardingDatabase, kClient client.Client, ) (*corev1.PodList, error) { podList := &corev1.PodList{} - labelSelector := labels.SelectorFromSet(getlabelsForGsm(instance)) - if resType == "GSM" { + //labelSelector := labels.SelectorFromSet(getlabelsForGsm(instance)) + //labelSelector := map[string]labels.Selector{} + var labelSelector labels.Selector + + //labels.SelectorFromSet() + + switch resType { + case "GSM": labelSelector = labels.SelectorFromSet(getlabelsForGsm(instance)) - } else if resType == "SHARD" { + case "SHARD": labelSelector = labels.SelectorFromSet(getlabelsForShard(instance)) - } else if resType == "CATALOG" { + case "CATALOG": labelSelector = labels.SelectorFromSet(getlabelsForCatalog(instance)) - } else { - err1 := fmt.Errorf("Wrong resources type passed. Supported values are SHARD,GSM and CATALOG") + default: + err1 := fmt.Errorf("wrong resources type passed. Supported values are SHARD,GSM and CATALOG") return nil, err1 } @@ -612,41 +709,91 @@ func getOwnerRef(instance *databasealphav1.ShardingDatabase, } func buildCatalogParams(instance *databasealphav1.ShardingDatabase) string { - var variables []databasealphav1.EnvironmentVariable - variables = instance.Spec.Catalog[0].EnvVars + var variables []databasealphav1.EnvironmentVariable = instance.Spec.Catalog[0].EnvVars var result string var varinfo 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) + } + + 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++ { + 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" { + 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 cport = variable.Value } - if variable.Name == "CATALOG_REGION" { - regionFlag = true - cregion = variable.Value - } if variable.Name == "CATALOG_NAME" { cnameFlag = true cname = variable.Value @@ -655,22 +802,33 @@ func buildCatalogParams(instance *databasealphav1.ShardingDatabase) string { chunksFlag = true catchunks = variable.Value } + } if !sidFlag { 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 { @@ -688,18 +846,15 @@ func buildCatalogParams(instance *databasealphav1.ShardingDatabase) string { varinfo = "catalog_name=" + strings.ToUpper(cname) + ";" result = result + varinfo } + 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 + 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 } @@ -725,64 +880,137 @@ func buildDirectorParams(instance *databasealphav1.ShardingDatabase, oraGsmSpex result = result + varinfo } - if idx == 0 { - varinfo = "director_region=primary;" - result = result + varinfo - } else if idx == 1 { - varinfo = "director_region=standby;" + if oraGsmSpex.Region != "" { + varinfo = "director_region=" + oraGsmSpex.Region + ";" result = result + varinfo } else { - // Do nothing + 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 } if !dportFlag { varinfo = "director_port=1522" result = result + varinfo } - + result = strings.TrimSuffix(result, ";") return result } -func BuildShardParams(sfSet *appsv1.StatefulSet) string { - var variables []corev1.EnvVar - variables = sfSet.Spec.Template.Spec.Containers[0].Env +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 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 + //var isShardRegion bool = false 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 isShardPort = true } - if variable.Name == "SHARD_GROUP" { - varinfo = "shard_group=" + variable.Value + ";" + + } + + 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 - isShardGrp = true } } - if !isShardPort { - varinfo = "shard_port=" + "1521" + ";" + 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 } - if !isShardGrp { - varinfo = "shard_group=" + "shardgroup1" + if OraShardSpex.ShardSpace != "" { + varinfo = "shard_space=" + OraShardSpex.ShardSpace + ";" + result = result + varinfo + } + if OraShardSpex.ShardRegion != "" { + varinfo = "shard_region=" + OraShardSpex.ShardRegion + ";" result = result + varinfo } + 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 } @@ -836,32 +1064,28 @@ func GetShardInviteNodeCmd(shardName string) []string { } func getCancelChunksCmd(sparamStr string) []string { - var cancelChunkCmd []string - cancelChunkCmd = []string{oraScriptMount + "/cmdExec", "/bin/python", oraScriptMount + "/main.py ", "--cancelchunks=" + strconv.Quote(sparamStr), "--optype=gsm"} + var cancelChunkCmd []string = []string{oraScriptMount + "/cmdExec", "/bin/python", oraScriptMount + "/main.py ", "--cancelchunks=" + strconv.Quote(sparamStr), "--optype=gsm"} return cancelChunkCmd } func getMoveChunksCmd(sparamStr string) []string { - var moveChunkCmd []string - moveChunkCmd = []string{oraScriptMount + "/cmdExec", "/bin/python", oraScriptMount + "/main.py ", "--movechunks=" + strconv.Quote(sparamStr), "--optype=gsm"} + var moveChunkCmd []string = []string{oraScriptMount + "/cmdExec", "/bin/python", oraScriptMount + "/main.py ", "--movechunks=" + strconv.Quote(sparamStr), "--optype=gsm"} return moveChunkCmd } func getNoChunksCmd(sparamStr string) []string { - var noChunkCmd []string - noChunkCmd = []string{oraScriptMount + "/cmdExec", "/bin/python", oraScriptMount + "/main.py ", "--validatenochunks=" + strconv.Quote(sparamStr), "--optype=gsm"} + var noChunkCmd []string = []string{oraScriptMount + "/cmdExec", "/bin/python", oraScriptMount + "/main.py ", "--validatenochunks=" + strconv.Quote(sparamStr), "--optype=gsm"} return noChunkCmd } 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 } func getShardCheckCmd(sparamStr string) []string { - var checkShardCmd []string - checkShardCmd = []string{oraScriptMount + "/cmdExec", "/bin/python", oraScriptMount + "/main.py ", "--checkgsmshard=" + strconv.Quote(sparamStr), "--optype=gsm"} + var checkShardCmd []string = []string{oraScriptMount + "/cmdExec", "/bin/python", oraScriptMount + "/main.py ", "--checkgsmshard=" + strconv.Quote(sparamStr), "--optype=gsm"} return checkShardCmd } @@ -882,47 +1106,64 @@ 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 - validateCmd = []string{oraScriptMount + "/cmdExec", "/bin/python", oraScriptMount + "/main.py ", "--validateshard=" + strconv.Quote(shardName), "--optype=gsm"} + 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 - onlineCmd = []string{oraScriptMount + "/cmdExec", "/bin/python", oraScriptMount + "/main.py ", "--checkonlineshard=" + strconv.Quote(sparamStr), "--optype=gsm"} + var onlineCmd []string = []string{oraScriptMount + "/cmdExec", "/bin/python", oraScriptMount + "/main.py ", "--checkonlineshard=" + strconv.Quote(sparamStr), "--optype=gsm"} return onlineCmd } func getGsmAddShardGroupCmd(sparamStr string) []string { - var addSgroupCmd []string - addSgroupCmd = []string{oraScriptMount + "/cmdExec", "/bin/python", oraScriptMount + "/main.py ", sparamStr, "--optype=gsm"} + var addSgroupCmd []string = []string{oraScriptMount + "/cmdExec", "/bin/python", oraScriptMount + "/main.py ", sparamStr, "--optype=gsm"} return addSgroupCmd } func getdeployShardCmd() []string { - var depCmd []string - depCmd = []string{oraScriptMount + "/cmdExec", "/bin/python", oraScriptMount + "/main.py ", "--deployshard=true", "--optype=gsm"} + var depCmd []string = []string{oraScriptMount + "/cmdExec", "/bin/python", oraScriptMount + "/main.py ", "--deployshard=true", "--optype=gsm"} return depCmd } func getGsmvalidateCmd() []string { - var depCmd []string - depCmd = []string{oraScriptMount + "/cmdExec", "/bin/python", oraScriptMount + "/main.py ", "--checkliveness=true", "--optype=gsm"} + var depCmd []string = []string{oraScriptMount + "/cmdExec", "/bin/python", oraScriptMount + "/main.py ", "--checkliveness=true", "--optype=gsm"} return depCmd } @@ -930,9 +1171,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 } @@ -948,6 +1189,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 + "]" @@ -957,8 +1203,9 @@ func ReadConfigMap(cmName string, instance *databasealphav1.ShardingDatabase, kC ) (string, string, string, string, string, string) { var region, fingerprint, user, tenancy, passphrase, str1, topicid, k, value string - cm := &corev1.ConfigMap{} var err error + cm := &corev1.ConfigMap{} + //var err error // Reding a config map err = kClient.Get(context.TODO(), types.NamespacedName{ @@ -986,19 +1233,20 @@ func ReadConfigMap(cmName string, instance *databasealphav1.ShardingDatabase, kC value = line[s+1:] LogMessages("DEBUG", "Key : "+GetFmtStr(k)+" Value : "+GetFmtStr(value), nil, instance, logger) - if k == "region" { + switch k { + case "region": region = value - } else if k == "fingerprint" { + case "fingerprint": fingerprint = value - } else if k == "user" { + case "user": user = value - } else if k == "tenancy" { + case "tenancy": tenancy = value - } else if k == "passpharase" { + case "passpharase": passphrase = value - } else if k == "topicid" { + case "topicid": topicid = value - } else { + default: LogMessages("DEBUG", GetFmtStr(k)+" is not matching with any required value for ONS.", nil, instance, logger) } } @@ -1010,10 +1258,10 @@ func ReadSecret(secName string, instance *databasealphav1.ShardingDatabase, kCli var value string sc := &corev1.Secret{} - var err error + //var err error // Reading a Secret - err = kClient.Get(context.TODO(), types.NamespacedName{ + var err error = kClient.Get(context.TODO(), types.NamespacedName{ Name: secName, Namespace: instance.Spec.Namespace, }, sc) @@ -1067,7 +1315,7 @@ func Contains(list []string, s string) bool { func CheckShardInGsm(gsmPodName string, sparams string, instance *databasealphav1.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, ) error { - err, _, _ := ExecCommand(gsmPodName, getShardCheckCmd(sparams), kubeClient, kubeconfig, instance, logger) + _, _, err := ExecCommand(gsmPodName, getShardCheckCmd(sparams), kubeClient, kubeconfig, instance, logger) if err != nil { msg := "Did not find the shard " + GetFmtStr(sparams) + " in GSM." LogMessages("INFO", msg, nil, instance, logger) @@ -1080,9 +1328,9 @@ func CheckShardInGsm(gsmPodName string, sparams string, instance *databasealphav func CheckOnlineShardInGsm(gsmPodName string, sparams string, instance *databasealphav1.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, ) error { - err, _, _ := ExecCommand(gsmPodName, getOnlineShardCmd(sparams), kubeClient, kubeconfig, instance, logger) + _, _, 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 } @@ -1093,7 +1341,7 @@ func CheckOnlineShardInGsm(gsmPodName string, sparams string, instance *database func MoveChunks(gsmPodName string, sparams string, instance *databasealphav1.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, ) error { - err, _, _ := ExecCommand(gsmPodName, getMoveChunksCmd(sparams), kubeClient, kubeconfig, instance, logger) + _, _, err := ExecCommand(gsmPodName, getMoveChunksCmd(sparams), kubeClient, kubeconfig, instance, logger) if err != nil { msg := "Error occurred in during Chunk movement command submission for shard: " + GetFmtStr(sparams) + " in GSM." LogMessages("INFO", msg, nil, instance, logger) @@ -1105,7 +1353,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, ) error { - err, _, _ := ExecCommand(gsmPodName, getNoChunksCmd(sparams), kubeClient, kubeconfig, instance, logger) + _, _, err := ExecCommand(gsmPodName, getNoChunksCmd(sparams), kubeClient, kubeconfig, instance, logger) if err != nil { msg := "Chunks are not moved completely from the shard: " + GetFmtStr(sparams) + " in GSM." LogMessages("INFO", msg, nil, instance, logger) @@ -1117,7 +1365,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, ) error { - err, _, _ := ExecCommand(gsmPodName, getShardAddCmd(sparams), kubeClient, kubeconfig, instance, logger) + _, _, err := ExecCommand(gsmPodName, getShardAddCmd(sparams), kubeClient, kubeconfig, instance, logger) if err != nil { msg := "Error occurred while adding a shard " + GetFmtStr(sparams) + " in GSM." LogMessages("INFO", msg, nil, instance, logger) @@ -1129,7 +1377,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, ) error { - err, _, _ := ExecCommand(gsmPodName, getdeployShardCmd(), kubeClient, kubeconfig, instance, logger) + _, _, err := ExecCommand(gsmPodName, getdeployShardCmd(), kubeClient, kubeconfig, instance, logger) if err != nil { msg := "Error occurred while deploying the shard in GSM." LogMessages("INFO", msg, nil, instance, logger) @@ -1141,7 +1389,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, ) error { - err, _, _ := ExecCommand(gsmPodName, getCancelChunksCmd(sparams), kubeClient, kubeconfig, instance, logger) + _, _, err := ExecCommand(gsmPodName, getCancelChunksCmd(sparams), kubeClient, kubeconfig, instance, logger) if err != nil { msg := "Error occurred while cancelling the chunks: " + GetFmtStr(sparams) + " in GSM." LogMessages("INFO", msg, nil, instance, logger) @@ -1153,7 +1401,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, ) error { - err, _, _ := ExecCommand(gsmPodName, getShardDelCmd(sparams), kubeClient, kubeconfig, instance, logger) + _, _, err := ExecCommand(gsmPodName, getShardDelCmd(sparams), kubeClient, kubeconfig, instance, logger) if err != nil { msg := "Error occurred while cancelling the chunks: " + GetFmtStr(sparams) + " in GSM." LogMessages("INFO", msg, nil, instance, logger) @@ -1163,19 +1411,19 @@ func RemoveShardFromGsm(gsmPodName string, sparams string, instance *databasealp } func GetSvcIp(PodName string, sparams string, instance *databasealphav1.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, -) (error, string, string) { - err, stdoutput, stderror := ExecCommand(PodName, GetIpCmd(sparams), kubeClient, kubeconfig, instance, logger) +) (string, string, error) { + stdoutput, stderror, err := ExecCommand(PodName, GetIpCmd(sparams), kubeClient, kubeconfig, instance, logger) if err != nil { msg := "Error occurred while getting the IP for k8s service " + GetFmtStr(sparams) LogMessages("INFO", msg, nil, instance, logger) - return err, strings.Replace(stdoutput, "\r\n", "", -1), strings.Replace(stderror, "/r/n", "", -1) + return strings.Replace(stdoutput, "\r\n", "", -1), strings.Replace(stderror, "/r/n", "", -1), err } - return nil, strings.Replace(stdoutput, "\r\n", "", -1), strings.Replace(stderror, "/r/n", "", -1) + 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, ) string { - err, stdoutput, _ := ExecCommand(PodName, getGsmSvcCmd(), kubeClient, kubeconfig, instance, logger) + stdoutput, _, err := ExecCommand(PodName, getGsmSvcCmd(), kubeClient, kubeconfig, instance, logger) if err != nil { msg := "Error occurred while getting the services from the GSM " LogMessages("DEBUG", msg, err, instance, logger) @@ -1186,7 +1434,7 @@ func GetGsmServices(PodName string, instance *databasealphav1.ShardingDatabase, func GetDbRole(PodName string, instance *databasealphav1.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, ) string { - err, stdoutput, _ := ExecCommand(PodName, getDbRoleCmd(), kubeClient, kubeconfig, instance, logger) + stdoutput, _, err := ExecCommand(PodName, getDbRoleCmd(), kubeClient, kubeconfig, instance, logger) if err != nil { msg := "Error occurred while getting the DB role from the database" LogMessages("DEBUG", msg, err, instance, logger) @@ -1197,7 +1445,7 @@ func GetDbRole(PodName string, instance *databasealphav1.ShardingDatabase, kubeC func GetDbOpenMode(PodName string, instance *databasealphav1.ShardingDatabase, kubeClient kubernetes.Interface, kubeconfig clientcmd.ClientConfig, logger logr.Logger, ) string { - err, stdoutput, _ := ExecCommand(PodName, getDbModeCmd(), kubeClient, kubeconfig, instance, logger) + stdoutput, _, err := ExecCommand(PodName, getDbModeCmd(), kubeClient, kubeconfig, instance, logger) if err != nil { msg := "Error occurred while getting the DB mode from the database" LogMessages("DEBUG", msg, err, instance, logger) @@ -1232,6 +1480,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, @@ -1250,3 +1520,31 @@ func SendNotification(title string, body string, instance *databasealphav1.Shard LogMessages("DEBUG", msg, nil, instance, logger) } } + +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 08ba490f..c76fc0e5 100644 --- a/commons/sharding/shard.go +++ b/commons/sharding/shard.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2022 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -40,10 +40,11 @@ package commons import ( "context" - databasev1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" "reflect" "strconv" + 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" @@ -105,8 +106,7 @@ func builObjectMetaForShard(instance *databasev1alpha1.ShardingDatabase, OraShar // Function to build Stateful Specs func buildStatefulSpecForShard(instance *databasev1alpha1.ShardingDatabase, OraShardSpex databasev1alpha1.ShardSpec) *appsv1.StatefulSetSpec { // building Stateful set Specs - var size int32 - size = 1 + var size int32 = 1 sfsetspec := &appsv1.StatefulSetSpec{ ServiceName: OraShardSpex.Name, Selector: &metav1.LabelSelector{ @@ -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,6 +194,15 @@ 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{}}}) + } + + 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{ @@ -218,28 +225,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), - Handler: corev1.Handler{ + 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 +286,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,13 +329,25 @@ 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 { 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 } @@ -331,7 +372,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"), }, @@ -355,8 +396,8 @@ func volumeClaimTemplatesForShard(instance *databasev1alpha1.ShardingDatabase, O } func BuildServiceDefForShard(instance *databasev1alpha1.ShardingDatabase, replicaCount int32, OraShardSpex databasev1alpha1.ShardSpec, svctype string) *corev1.Service { - service := &corev1.Service{} - service = &corev1.Service{ + //service := &corev1.Service{} + service := &corev1.Service{ ObjectMeta: buildSvcObjectMetaForShard(instance, replicaCount, OraShardSpex, svctype), Spec: corev1.ServiceSpec{}, } @@ -402,8 +443,7 @@ func buildSvcObjectMetaForShard(instance *databasev1alpha1.ShardingDatabase, rep func getSvcLabelsForShard(replicaCount int32, OraShardSpex databasev1alpha1.ShardSpec) map[string]string { - var labelStr map[string]string - labelStr = make(map[string]string) + var labelStr map[string]string = make(map[string]string) if replicaCount == -1 { labelStr["statefulset.kubernetes.io/pod-name"] = OraShardSpex.Name + "-0" } else { @@ -418,8 +458,8 @@ func getSvcLabelsForShard(replicaCount int32, OraShardSpex databasev1alpha1.Shar func UpdateProvForShard(instance *databasev1alpha1.ShardingDatabase, OraShardSpex databasev1alpha1.ShardSpec, kClient client.Client, sfSet *appsv1.StatefulSet, shardPod *corev1.Pod, logger logr.Logger, ) (ctrl.Result, error) { var msg string - var size int32 - size = 1 + var size int32 = 1 + //size = 1 var isUpdate bool = false var err error var i int @@ -440,7 +480,7 @@ func UpdateProvForShard(instance *databasev1alpha1.ShardingDatabase, OraShardSpe oraSpexRes := OraShardSpex.Resources if !reflect.DeepEqual(shardContaineRes, oraSpexRes) { - isUpdate = true + isUpdate = false } } } diff --git a/config/certmanager/certificate.yaml b/config/certmanager/certificate.yaml index 3aadc474..6b331894 100644 --- a/config/certmanager/certificate.yaml +++ b/config/certmanager/certificate.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # diff --git a/config/certmanager/kustomization.yaml b/config/certmanager/kustomization.yaml index 8dd6746c..0a7c8089 100644 --- a/config/certmanager/kustomization.yaml +++ b/config/certmanager/kustomization.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # resources: diff --git a/config/certmanager/kustomizeconfig.yaml b/config/certmanager/kustomizeconfig.yaml index f8e232fa..ce54850c 100644 --- a/config/certmanager/kustomizeconfig.yaml +++ b/config/certmanager/kustomizeconfig.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # 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_autonomouscontainerdatabases.yaml b/config/crd/bases/database.oracle.com_autonomouscontainerdatabases.yaml new file mode 100644 index 00000000..bac3a28c --- /dev/null +++ b/config/crd/bases/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/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml b/config/crd/bases/database.oracle.com_autonomousdatabasebackups.yaml new file mode 100644 index 00000000..a5c37507 --- /dev/null +++ b/config/crd/bases/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/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml b/config/crd/bases/database.oracle.com_autonomousdatabaserestores.yaml new file mode 100644 index 00000000..5e9f2c73 --- /dev/null +++ b/config/crd/bases/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/crd/bases/database.oracle.com_autonomousdatabases.yaml b/config/crd/bases/database.oracle.com_autonomousdatabases.yaml index dbce1bfa..f77407f3 100644 --- a/config/crd/bases/database.oracle.com_autonomousdatabases.yaml +++ b/config/crd/bases/database.oracle.com_autonomousdatabases.yaml @@ -20,22 +20,25 @@ spec: scope: Namespaced versions: - additionalPrinterColumns: - - jsonPath: .status.displayName + - 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: .status.isDedicated + - jsonPath: .spec.details.isDedicated name: Dedicated type: string - - jsonPath: .status.cpuCoreCount + - jsonPath: .spec.details.cpuCoreCount name: OCPUs type: integer - - jsonPath: .status.dataStorageSizeInTBs + - jsonPath: .spec.details.dataStorageSizeInTBs name: Storage (TB) type: integer - - jsonPath: .status.dbWorkload + - jsonPath: .spec.details.dbWorkload name: Workload Type type: string - jsonPath: .status.timeCreated @@ -69,10 +72,34 @@ spec: properties: adminPassword: properties: - k8sSecretName: - type: string - ociSecretOCID: - type: string + 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 @@ -101,47 +128,76 @@ spec: additionalProperties: type: string type: object - isAccessControlEnabled: - type: boolean isAutoScalingEnabled: type: boolean isDedicated: type: boolean - isMTLSConnectionRequired: - 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 - nsgOCIDs: - items: - type: string - type: array - privateEndpointLabel: - type: string - subnetOCID: - 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: - k8sSecretName: - type: string - ociSecretOCID: - type: string + k8sSecret: + description: "*********************** *\tSecret specs + ***********************" + properties: + name: + type: string + type: object + ociSecret: + properties: + ocid: + type: string + type: object type: object type: object - whitelistedIPs: - items: - type: string - type: array type: object hardLink: default: false type: boolean ociConfig: + description: "*********************** *\tOCI config ***********************" properties: configMapName: type: string @@ -154,27 +210,106 @@ spec: status: description: AutonomousDatabaseStatus defines the observed state of AutonomousDatabase properties: - cpuCoreCount: - type: integer - dataStorageSizeInTBs: - type: integer - dbWorkload: - description: 'AutonomousDatabaseDbWorkloadEnum Enum with underlying - type: string' - type: string - displayName: + 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 - isDedicated: - type: string - lifecycleState: - description: 'AutonomousDatabaseLifecycleStateEnum Enum with underlying - type: string' - type: string timeCreated: type: string + walletExpiringDate: + type: string type: object type: object served: true 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..6b1c350c --- /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_dataguardbrokers.yaml b/config/crd/bases/database.oracle.com_dataguardbrokers.yaml new file mode 100644 index 00000000..f19a3e22 --- /dev/null +++ b/config/crd/bases/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/crd/bases/database.oracle.com_dbcssystems.yaml b/config/crd/bases/database.oracle.com_dbcssystems.yaml new file mode 100644 index 00000000..3f4b1c46 --- /dev/null +++ b/config/crd/bases/database.oracle.com_dbcssystems.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: 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: [] diff --git a/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml b/config/crd/bases/database.oracle.com_oraclerestdataservices.yaml new file mode 100644 index 00000000..121383fd --- /dev/null +++ b/config/crd/bases/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/crd/bases/database.oracle.com_pdbs.yaml b/config/crd/bases/database.oracle.com_pdbs.yaml new file mode 100644 index 00000000..85af8c1b --- /dev/null +++ b/config/crd/bases/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/crd/bases/database.oracle.com_shardingdatabases.yaml b/config/crd/bases/database.oracle.com_shardingdatabases.yaml index 17dd4e23..641629a0 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 @@ -59,7 +72,7 @@ spec: a container image type: string isDelete: - type: boolean + type: string label: type: string name: @@ -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: @@ -113,15 +148,49 @@ spec: - 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. @@ -140,7 +209,7 @@ spec: a container image type: string isDelete: - type: boolean + type: string label: type: string name: @@ -155,13 +224,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 +272,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 +283,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,13 +394,15 @@ spec: type: boolean isDeleteOraPvc: type: boolean + isDownloadScripts: + type: boolean isExternalSvc: type: boolean - namespace: + isTdeWallet: type: string - nsConfigMap: - type: string - nsSecret: + liveinessCheckPeriod: + type: integer + namespace: type: string portMappings: items: @@ -233,9 +424,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 +437,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 @@ -263,7 +458,12 @@ spec: a container image type: string isDelete: - type: boolean + enum: + - enable + - disable + - failed + - force + type: string label: type: string name: @@ -286,6 +486,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 +528,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,16 +545,29 @@ 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: type: string + tdeWalletPvc: + type: string + tdeWalletPvcMountLocation: + type: string required: - catalog - dbImage - gsm - gsmImage - - secret - shard type: object status: @@ -342,13 +583,14 @@ spec: 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, - 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\"` - \n // other fields }" + \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 diff --git a/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml b/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml index d7abe5e0..1c011e17 100644 --- a/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml +++ b/config/crd/bases/database.oracle.com_singleinstancedatabases.yaml @@ -20,12 +20,15 @@ spec: - 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 - priority: 1 type: string - jsonPath: .status.releaseUpdate name: Version @@ -37,6 +40,13 @@ spec: 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 @@ -68,24 +78,34 @@ spec: keepSecret: type: boolean secretKey: + default: oracle_pwd type: string secretName: type: string required: - - secretKey - secretName type: object archiveLog: type: boolean charset: type: string - cloneFrom: + 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: @@ -94,6 +114,8 @@ spec: description: SingleInstanceDatabaseImage defines the Image source and pullSecrets for POD properties: + prebuiltDB: + type: boolean pullFrom: type: string pullSecrets: @@ -115,8 +137,8 @@ spec: sgaTarget: type: integer type: object - installApex: - type: boolean + listenerPort: + type: integer loadBalancer: type: boolean nodeSelector: @@ -134,30 +156,62 @@ spec: - ReadWriteOnce - ReadWriteMany type: string + datafilesVolumeName: + type: string + scriptsVolumeName: + type: string + setWritePermissions: + type: boolean size: type: string storageClass: type: string - required: - - accessMode - - size - - storageClass + volumeClaimAnnotation: + type: string type: object + primaryDatabaseRef: + type: string readinessCheckPeriod: type: integer replicas: - minimum: 1 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 can only have a-z , A-Z, 0-9 . It cant have any special - characters + 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: - - adminPassword - image - - persistence - - replicas type: object status: description: SingleInstanceDatabaseStatus defines the observed state of @@ -167,9 +221,13 @@ spec: type: boolean archiveLog: type: string + certCreationTimestamp: + type: string + certRenewInterval: + type: string charset: type: string - cloneFrom: + clientWalletLoc: type: string clusterConnectString: type: string @@ -178,13 +236,14 @@ spec: 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, - 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\"` - \n // other fields }" + \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 @@ -247,10 +306,16 @@ spec: 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: @@ -273,6 +338,9 @@ spec: type: integer initSgaSize: type: integer + isTcpsEnabled: + default: false + type: boolean nodes: items: type: string @@ -294,15 +362,23 @@ spec: - ReadWriteOnce - ReadWriteMany type: string + datafilesVolumeName: + type: string + scriptsVolumeName: + type: string + setWritePermissions: + type: boolean size: type: string storageClass: type: string - required: - - accessMode - - size - - storageClass + volumeClaimAnnotation: + type: string type: object + prebuiltDB: + type: boolean + primaryDatabase: + type: string releaseUpdate: type: string replicas: @@ -317,9 +393,17 @@ spec: type: object status: type: string + tcpsConnectString: + type: string + tcpsPdbConnectString: + type: string + tcpsTlsSecret: + default: "" + type: string required: + - isTcpsEnabled - persistence - - replicas + - tcpsTlsSecret type: object type: object served: true 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 6b3d488e..b9f3aa8c 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # # This kustomization.yaml is not intended to be run by itself, @@ -7,8 +7,17 @@ # It should be run by config/default resources: - bases/database.oracle.com_autonomousdatabases.yaml +- bases/database.oracle.com_autonomousdatabasebackups.yaml +- 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 +- bases/database.oracle.com_dataguardbrokers.yaml +- bases/observability.oracle.com_databaseobservers.yaml # +kubebuilder:scaffold:crdkustomizeresource patchesStrategicMerge: @@ -18,6 +27,13 @@ patchesStrategicMerge: #- 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 # +kubebuilder:scaffold:crdkustomizewebhookpatch # [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix. @@ -26,6 +42,13 @@ patchesStrategicMerge: #- 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 # +kubebuilder:scaffold:crdkustomizecainjectionpatch # the following config is for teaching kustomize how to do kustomization for CRDs. diff --git a/config/crd/kustomizeconfig.yaml b/config/crd/kustomizeconfig.yaml index fb9995dc..344a1576 100644 --- a/config/crd/kustomizeconfig.yaml +++ b/config/crd/kustomizeconfig.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # # This file is for teaching kustomize how to substitute name and namespace reference in CRD diff --git a/config/crd/patches/cainjection_in_autonomouscontainerdatabases.yaml b/config/crd/patches/cainjection_in_autonomouscontainerdatabases.yaml new file mode 100644 index 00000000..3985a5ae --- /dev/null +++ b/config/crd/patches/cainjection_in_autonomouscontainerdatabases.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: autonomouscontainerdatabases.database.oracle.com diff --git a/config/crd/patches/cainjection_in_autonomousdatabasebackups.yaml b/config/crd/patches/cainjection_in_autonomousdatabasebackups.yaml new file mode 100644 index 00000000..78280137 --- /dev/null +++ b/config/crd/patches/cainjection_in_autonomousdatabasebackups.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: autonomousdatabasebackups.database.oracle.com diff --git a/config/crd/patches/cainjection_in_autonomousdatabaserestores.yaml b/config/crd/patches/cainjection_in_autonomousdatabaserestores.yaml new file mode 100644 index 00000000..75894cbb --- /dev/null +++ b/config/crd/patches/cainjection_in_autonomousdatabaserestores.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: autonomousdatabaserestores.database.oracle.com diff --git a/config/crd/patches/cainjection_in_autonomousdatabases.yaml b/config/crd/patches/cainjection_in_autonomousdatabases.yaml index 072e3f9e..05842d0b 100644 --- a/config/crd/patches/cainjection_in_autonomousdatabases.yaml +++ b/config/crd/patches/cainjection_in_autonomousdatabases.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # # The following patch adds a directive for certmanager to inject CA into the CRD diff --git a/config/crd/patches/cainjection_in_cdbs.yaml b/config/crd/patches/cainjection_in_cdbs.yaml new file mode 100644 index 00000000..8cb50343 --- /dev/null +++ b/config/crd/patches/cainjection_in_cdbs.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/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: cdbs.database.oracle.com diff --git a/config/crd/patches/cainjection_in_dataguardbrokers.yaml b/config/crd/patches/cainjection_in_dataguardbrokers.yaml new file mode 100644 index 00000000..6409f54c --- /dev/null +++ b/config/crd/patches/cainjection_in_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_dbcssystems.yaml b/config/crd/patches/cainjection_in_dbcssystems.yaml new file mode 100644 index 00000000..9d8521ac --- /dev/null +++ b/config/crd/patches/cainjection_in_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_oraclerestdataservices.yaml b/config/crd/patches/cainjection_in_oraclerestdataservices.yaml new file mode 100644 index 00000000..d2b5d4ee --- /dev/null +++ b/config/crd/patches/cainjection_in_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_pdbs.yaml b/config/crd/patches/cainjection_in_pdbs.yaml new file mode 100644 index 00000000..8c41010a --- /dev/null +++ b/config/crd/patches/cainjection_in_pdbs.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/v1 +kind: CustomResourceDefinition +metadata: + annotations: + cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME) + name: pdbs.database.oracle.com diff --git a/config/crd/patches/cainjection_in_shardingdatabases.yaml b/config/crd/patches/cainjection_in_shardingdatabases.yaml index 6ef22218..45d35376 100644 --- a/config/crd/patches/cainjection_in_shardingdatabases.yaml +++ b/config/crd/patches/cainjection_in_shardingdatabases.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # # The following patch adds a directive for certmanager to inject CA into the CRD diff --git a/config/crd/patches/cainjection_in_singleinstancedatabases.yaml b/config/crd/patches/cainjection_in_singleinstancedatabases.yaml index 4e454c1a..11114339 100644 --- a/config/crd/patches/cainjection_in_singleinstancedatabases.yaml +++ b/config/crd/patches/cainjection_in_singleinstancedatabases.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # # The following patch adds a directive for certmanager to inject CA into the CRD 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_autonomouscontainerdatabases.yaml b/config/crd/patches/webhook_in_autonomouscontainerdatabases.yaml new file mode 100644 index 00000000..03a73384 --- /dev/null +++ b/config/crd/patches/webhook_in_autonomouscontainerdatabases.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: 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 diff --git a/config/crd/patches/webhook_in_autonomousdatabasebackups.yaml b/config/crd/patches/webhook_in_autonomousdatabasebackups.yaml new file mode 100644 index 00000000..1a4eacb6 --- /dev/null +++ b/config/crd/patches/webhook_in_autonomousdatabasebackups.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: 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 diff --git a/config/crd/patches/webhook_in_autonomousdatabaserestores.yaml b/config/crd/patches/webhook_in_autonomousdatabaserestores.yaml new file mode 100644 index 00000000..0a0ed4ad --- /dev/null +++ b/config/crd/patches/webhook_in_autonomousdatabaserestores.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: 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 diff --git a/config/crd/patches/webhook_in_autonomousdatabases.yaml b/config/crd/patches/webhook_in_autonomousdatabases.yaml index 55540503..230f9f68 100644 --- a/config/crd/patches/webhook_in_autonomousdatabases.yaml +++ b/config/crd/patches/webhook_in_autonomousdatabases.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # # The following patch enables conversion webhook for CRD diff --git a/config/crd/patches/webhook_in_cdbs.yaml b/config/crd/patches/webhook_in_cdbs.yaml new file mode 100644 index 00000000..9283c020 --- /dev/null +++ b/config/crd/patches/webhook_in_cdbs.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/v1 +kind: CustomResourceDefinition +metadata: + name: cdbs.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_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/crd/patches/webhook_in_dataguardbrokers.yaml b/config/crd/patches/webhook_in_dataguardbrokers.yaml new file mode 100644 index 00000000..10f62234 --- /dev/null +++ b/config/crd/patches/webhook_in_dataguardbrokers.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: dataguardbrokers.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_dbcssystems.yaml b/config/crd/patches/webhook_in_dbcssystems.yaml new file mode 100644 index 00000000..69e578a3 --- /dev/null +++ b/config/crd/patches/webhook_in_dbcssystems.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: dbcssystems.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_oraclerestdataservices.yaml b/config/crd/patches/webhook_in_oraclerestdataservices.yaml new file mode 100644 index 00000000..c9398c23 --- /dev/null +++ b/config/crd/patches/webhook_in_oraclerestdataservices.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: oraclerestdataservices.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_pdbs.yaml b/config/crd/patches/webhook_in_pdbs.yaml new file mode 100644 index 00000000..2c41e439 --- /dev/null +++ b/config/crd/patches/webhook_in_pdbs.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/v1 +kind: CustomResourceDefinition +metadata: + name: pdbs.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_shardingdatabases.yaml b/config/crd/patches/webhook_in_shardingdatabases.yaml index fccda7d0..b006a011 100644 --- a/config/crd/patches/webhook_in_shardingdatabases.yaml +++ b/config/crd/patches/webhook_in_shardingdatabases.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # # The following patch enables conversion webhook for CRD diff --git a/config/crd/patches/webhook_in_singleinstancedatabases.yaml b/config/crd/patches/webhook_in_singleinstancedatabases.yaml index 66687d8e..aecc7ba9 100644 --- a/config/crd/patches/webhook_in_singleinstancedatabases.yaml +++ b/config/crd/patches/webhook_in_singleinstancedatabases.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # # The following patch enables conversion webhook for CRD 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/default/kustomization.yaml b/config/default/kustomization.yaml index 04f37b2c..d41001b0 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # diff --git a/config/default/manager_auth_proxy_patch.yaml b/config/default/manager_auth_proxy_patch.yaml index c88bb7a7..9bd3bbc9 100644 --- a/config/default/manager_auth_proxy_patch.yaml +++ b/config/default/manager_auth_proxy_patch.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # diff --git a/config/default/manager_webhook_patch.yaml b/config/default/manager_webhook_patch.yaml index cda36c4c..b34405d2 100644 --- a/config/default/manager_webhook_patch.yaml +++ b/config/default/manager_webhook_patch.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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: apps/v1 diff --git a/config/default/webhookcainjection_patch.yaml b/config/default/webhookcainjection_patch.yaml index 51b1069f..c6b7ea34 100644 --- a/config/default/webhookcainjection_patch.yaml +++ b/config/default/webhookcainjection_patch.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 30ed1f75..2aed83d4 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # resources: @@ -9,4 +9,4 @@ kind: Kustomization images: - name: controller newName: container-registry.oracle.com/database/operator - newTag: 0.1.0 + newTag: latest diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 362aac61..54340faf 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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 @@ -26,8 +26,6 @@ spec: labels: control-plane: controller-manager spec: - imagePullSecrets: - - name: container-registry-secret containers: - command: - /manager @@ -43,4 +41,7 @@ spec: requests: cpu: 400m memory: 400Mi + env: + - name : WATCH_NAMESPACE + value : "" terminationGracePeriodSeconds: 10 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/manifests/kustomization.yaml b/config/manifests/kustomization.yaml index 2a0f628b..39275249 100644 --- a/config/manifests/kustomization.yaml +++ b/config/manifests/kustomization.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # resources: 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/config/rbac/auth_proxy_client_clusterrole.yaml b/config/rbac/auth_proxy_client_clusterrole.yaml index b3e55375..1a478f0d 100644 --- a/config/rbac/auth_proxy_client_clusterrole.yaml +++ b/config/rbac/auth_proxy_client_clusterrole.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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: rbac.authorization.k8s.io/v1 diff --git a/config/rbac/auth_proxy_role.yaml b/config/rbac/auth_proxy_role.yaml index fd7483d6..e275bf6d 100644 --- a/config/rbac/auth_proxy_role.yaml +++ b/config/rbac/auth_proxy_role.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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: rbac.authorization.k8s.io/v1 diff --git a/config/rbac/auth_proxy_role_binding.yaml b/config/rbac/auth_proxy_role_binding.yaml index 5632b87b..6a702753 100644 --- a/config/rbac/auth_proxy_role_binding.yaml +++ b/config/rbac/auth_proxy_role_binding.yaml @@ -1,15 +1,15 @@ # -# Copyright (c) 2021, 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: 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 diff --git a/config/rbac/auth_proxy_service.yaml b/config/rbac/auth_proxy_service.yaml index 72b1ecb8..8cca79bf 100644 --- a/config/rbac/auth_proxy_service.yaml +++ b/config/rbac/auth_proxy_service.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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 diff --git a/config/rbac/autonomouscontainerdatabase_editor_role.yaml b/config/rbac/autonomouscontainerdatabase_editor_role.yaml new file mode 100644 index 00000000..8dccd4a5 --- /dev/null +++ b/config/rbac/autonomouscontainerdatabase_editor_role.yaml @@ -0,0 +1,24 @@ +# permissions for end users to edit autonomouscontainerdatabases. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: autonomouscontainerdatabase-editor-role +rules: +- apiGroups: + - database.oracle.com + resources: + - autonomouscontainerdatabases + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - autonomouscontainerdatabases/status + verbs: + - get diff --git a/config/rbac/autonomouscontainerdatabase_viewer_role.yaml b/config/rbac/autonomouscontainerdatabase_viewer_role.yaml new file mode 100644 index 00000000..e9bcec50 --- /dev/null +++ b/config/rbac/autonomouscontainerdatabase_viewer_role.yaml @@ -0,0 +1,20 @@ +# permissions for end users to view autonomouscontainerdatabases. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: autonomouscontainerdatabase-viewer-role +rules: +- apiGroups: + - database.oracle.com + resources: + - autonomouscontainerdatabases + verbs: + - get + - list + - watch +- apiGroups: + - database.oracle.com + resources: + - autonomouscontainerdatabases/status + verbs: + - get diff --git a/config/rbac/autonomousdatabase_editor_role.yaml b/config/rbac/autonomousdatabase_editor_role.yaml index bd6172fd..4cc7959a 100644 --- a/config/rbac/autonomousdatabase_editor_role.yaml +++ b/config/rbac/autonomousdatabase_editor_role.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # diff --git a/config/rbac/autonomousdatabase_viewer_role.yaml b/config/rbac/autonomousdatabase_viewer_role.yaml index af24143f..089bf01e 100644 --- a/config/rbac/autonomousdatabase_viewer_role.yaml +++ b/config/rbac/autonomousdatabase_viewer_role.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # diff --git a/config/rbac/autonomousdatabasebackup_editor_role.yaml b/config/rbac/autonomousdatabasebackup_editor_role.yaml new file mode 100644 index 00000000..1d210196 --- /dev/null +++ b/config/rbac/autonomousdatabasebackup_editor_role.yaml @@ -0,0 +1,24 @@ +# permissions for end users to edit autonomousdatabasebackups. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: autonomousdatabasebackup-editor-role +rules: +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabasebackups + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabasebackups/status + verbs: + - get diff --git a/config/rbac/autonomousdatabasebackup_viewer_role.yaml b/config/rbac/autonomousdatabasebackup_viewer_role.yaml new file mode 100644 index 00000000..3be0c5cb --- /dev/null +++ b/config/rbac/autonomousdatabasebackup_viewer_role.yaml @@ -0,0 +1,20 @@ +# permissions for end users to view autonomousdatabasebackups. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: autonomousdatabasebackup-viewer-role +rules: +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabasebackups + verbs: + - get + - list + - watch +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabasebackups/status + verbs: + - get diff --git a/config/rbac/autonomousdatabaserestore_editor_role.yaml b/config/rbac/autonomousdatabaserestore_editor_role.yaml new file mode 100644 index 00000000..6efd98ae --- /dev/null +++ b/config/rbac/autonomousdatabaserestore_editor_role.yaml @@ -0,0 +1,24 @@ +# permissions for end users to edit autonomousdatabaserestores. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: autonomousdatabaserestore-editor-role +rules: +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabaserestores + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabaserestores/status + verbs: + - get diff --git a/config/rbac/autonomousdatabaserestore_viewer_role.yaml b/config/rbac/autonomousdatabaserestore_viewer_role.yaml new file mode 100644 index 00000000..66cc7f51 --- /dev/null +++ b/config/rbac/autonomousdatabaserestore_viewer_role.yaml @@ -0,0 +1,20 @@ +# permissions for end users to view autonomousdatabaserestores. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: autonomousdatabaserestore-viewer-role +rules: +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabaserestores + verbs: + - get + - list + - watch +- apiGroups: + - database.oracle.com + resources: + - autonomousdatabaserestores/status + verbs: + - get diff --git a/config/rbac/cdb_editor_role.yaml b/config/rbac/cdb_editor_role.yaml new file mode 100644 index 00000000..244ddff2 --- /dev/null +++ b/config/rbac/cdb_editor_role.yaml @@ -0,0 +1,24 @@ +# 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 new file mode 100644 index 00000000..78a84283 --- /dev/null +++ b/config/rbac/cdb_viewer_role.yaml @@ -0,0 +1,20 @@ +# 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/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/dataguardbroker_editor_role.yaml b/config/rbac/dataguardbroker_editor_role.yaml new file mode 100644 index 00000000..d9ad534f --- /dev/null +++ b/config/rbac/dataguardbroker_editor_role.yaml @@ -0,0 +1,24 @@ +# permissions for end users to edit dataguardbrokers. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: dataguardbroker-editor-role +rules: +- apiGroups: + - database.oracle.com + resources: + - dataguardbrokers + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - dataguardbrokers/status + verbs: + - get diff --git a/config/rbac/dataguardbroker_viewer_role.yaml b/config/rbac/dataguardbroker_viewer_role.yaml new file mode 100644 index 00000000..4fe41628 --- /dev/null +++ b/config/rbac/dataguardbroker_viewer_role.yaml @@ -0,0 +1,20 @@ +# permissions for end users to view dataguardbrokers. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: dataguardbroker-viewer-role +rules: +- apiGroups: + - database.oracle.com + resources: + - dataguardbrokers + verbs: + - get + - list + - watch +- apiGroups: + - database.oracle.com + resources: + - dataguardbrokers/status + verbs: + - get diff --git a/config/rbac/dbcssystem_editor_role.yaml b/config/rbac/dbcssystem_editor_role.yaml new file mode 100644 index 00000000..934eea97 --- /dev/null +++ b/config/rbac/dbcssystem_editor_role.yaml @@ -0,0 +1,24 @@ +# permissions for end users to edit dbcssystems. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: dbcssystem-editor-role +rules: +- apiGroups: + - database.oracle.com + resources: + - dbcssystems + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - dbcssystems/status + verbs: + - get diff --git a/config/rbac/dbcssystem_viewer_role.yaml b/config/rbac/dbcssystem_viewer_role.yaml new file mode 100644 index 00000000..8153d112 --- /dev/null +++ b/config/rbac/dbcssystem_viewer_role.yaml @@ -0,0 +1,20 @@ +# permissions for end users to view dbcssystems. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: dbcssystem-viewer-role +rules: +- apiGroups: + - database.oracle.com + resources: + - dbcssystems + verbs: + - get + - list + - watch +- apiGroups: + - database.oracle.com + resources: + - dbcssystems/status + verbs: + - get diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml index 6325cf8c..7a20231c 100644 --- a/config/rbac/kustomization.yaml +++ b/config/rbac/kustomization.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # resources: diff --git a/config/rbac/leader_election_role.yaml b/config/rbac/leader_election_role.yaml index 083cd887..68f2270f 100644 --- a/config/rbac/leader_election_role.yaml +++ b/config/rbac/leader_election_role.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # diff --git a/config/rbac/leader_election_role_binding.yaml b/config/rbac/leader_election_role_binding.yaml index 01f5d630..180bd383 100644 --- a/config/rbac/leader_election_role_binding.yaml +++ b/config/rbac/leader_election_role_binding.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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: rbac.authorization.k8s.io/v1 diff --git a/config/rbac/oraclerestdataservice_editor_role.yaml b/config/rbac/oraclerestdataservice_editor_role.yaml new file mode 100644 index 00000000..bf2b4d02 --- /dev/null +++ b/config/rbac/oraclerestdataservice_editor_role.yaml @@ -0,0 +1,24 @@ +# permissions for end users to edit oraclerestdataservices. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: oraclerestdataservice-editor-role +rules: +- apiGroups: + - database.oracle.com + resources: + - oraclerestdataservices + verbs: + - create + - delete + - get + - list + - patch + - update + - watch +- apiGroups: + - database.oracle.com + resources: + - oraclerestdataservices/status + verbs: + - get diff --git a/config/rbac/oraclerestdataservice_viewer_role.yaml b/config/rbac/oraclerestdataservice_viewer_role.yaml new file mode 100644 index 00000000..a0a39cfd --- /dev/null +++ b/config/rbac/oraclerestdataservice_viewer_role.yaml @@ -0,0 +1,20 @@ +# permissions for end users to view oraclerestdataservices. +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: oraclerestdataservice-viewer-role +rules: +- apiGroups: + - database.oracle.com + resources: + - oraclerestdataservices + verbs: + - get + - list + - watch +- apiGroups: + - database.oracle.com + resources: + - oraclerestdataservices/status + verbs: + - get diff --git a/config/rbac/pdb_editor_role.yaml b/config/rbac/pdb_editor_role.yaml new file mode 100644 index 00000000..7d668e4a --- /dev/null +++ b/config/rbac/pdb_editor_role.yaml @@ -0,0 +1,24 @@ +# 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 new file mode 100644 index 00000000..5fcf68c9 --- /dev/null +++ b/config/rbac/pdb_viewer_role.yaml @@ -0,0 +1,20 @@ +# 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/provshard_editor_role.yaml b/config/rbac/provshard_editor_role.yaml index 473a41c0..1df44f2c 100644 --- a/config/rbac/provshard_editor_role.yaml +++ b/config/rbac/provshard_editor_role.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # diff --git a/config/rbac/provshard_viewer_role.yaml b/config/rbac/provshard_viewer_role.yaml index 36b5bb4b..a4ef06ed 100644 --- a/config/rbac/provshard_viewer_role.yaml +++ b/config/rbac/provshard_viewer_role.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 6d763212..2f33c915 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -6,6 +6,38 @@ metadata: creationTimestamp: null name: 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: @@ -19,11 +51,31 @@ rules: - patch - update - watch +- apiGroups: + - "" + resources: + - deployments + - events + - pods + - services + verbs: + - create + - delete + - get + - list + - patch + - update + - watch - apiGroups: - "" resources: - events - - nodes + verbs: + - create + - patch +- apiGroups: + - "" + resources: - persistentvolumeclaims - pods - pods/exec @@ -37,6 +89,14 @@ rules: - patch - update - watch +- apiGroups: + - "" + resources: + - persistentvolumes + verbs: + - get + - list + - watch - apiGroups: - '''''' resources: @@ -49,6 +109,39 @@ rules: - 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: @@ -74,9 +167,9 @@ rules: - "" resources: - configmaps + - containers - events - namespaces - - nodes - persistentvolumeclaims - pods - pods/exec @@ -91,12 +184,106 @@ 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: + - 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: @@ -116,6 +303,144 @@ 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 + 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: @@ -172,3 +497,49 @@ rules: - 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 diff --git a/config/rbac/role_binding.yaml b/config/rbac/role_binding.yaml index 1ec61dc5..f2ccb566 100644 --- a/config/rbac/role_binding.yaml +++ b/config/rbac/role_binding.yaml @@ -1,9 +1,9 @@ # -# Copyright (c) 2021, 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: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding +kind: RoleBinding metadata: name: oracle-database-operator-manager-rolebinding roleRef: diff --git a/config/rbac/shardingdatabase_editor_role.yaml b/config/rbac/shardingdatabase_editor_role.yaml index f9660cb7..efe6ad75 100644 --- a/config/rbac/shardingdatabase_editor_role.yaml +++ b/config/rbac/shardingdatabase_editor_role.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # diff --git a/config/rbac/shardingdatabase_viewer_role.yaml b/config/rbac/shardingdatabase_viewer_role.yaml index 717c61a4..08b8ca26 100644 --- a/config/rbac/shardingdatabase_viewer_role.yaml +++ b/config/rbac/shardingdatabase_viewer_role.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # diff --git a/config/rbac/singleinstancedatabase_editor_role.yaml b/config/rbac/singleinstancedatabase_editor_role.yaml index 90a19c43..918ef991 100644 --- a/config/rbac/singleinstancedatabase_editor_role.yaml +++ b/config/rbac/singleinstancedatabase_editor_role.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # diff --git a/config/rbac/singleinstancedatabase_viewer_role.yaml b/config/rbac/singleinstancedatabase_viewer_role.yaml index 84bea03d..c1f0f469 100644 --- a/config/rbac/singleinstancedatabase_viewer_role.yaml +++ b/config/rbac/singleinstancedatabase_viewer_role.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # diff --git a/config/samples/acd/autonomouscontainerdatabase_bind.yaml b/config/samples/acd/autonomouscontainerdatabase_bind.yaml new file mode 100644 index 00000000..3d28ba4d --- /dev/null +++ b/config/samples/acd/autonomouscontainerdatabase_bind.yaml @@ -0,0 +1,14 @@ +# +# 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: AutonomousContainerDatabase +metadata: + name: autonomouscontainerdatabase-sample +spec: + autonomousContainerDatabaseOCID: ocid1.autonomouscontainerdatabase... + # 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/acd/autonomouscontainerdatabase_change_displayname.yaml b/config/samples/acd/autonomouscontainerdatabase_change_displayname.yaml new file mode 100644 index 00000000..dd75250d --- /dev/null +++ b/config/samples/acd/autonomouscontainerdatabase_change_displayname.yaml @@ -0,0 +1,16 @@ +# +# 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: AutonomousContainerDatabase +metadata: + name: autonomouscontainerdatabase-sample +spec: + # Update compartmentOCID with your compartment OCID. + compartmentOCID: ocid1.compartment... OR ocid1.tenancy... + displayName: newACD + # 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/acd/autonomouscontainerdatabase_create.yaml b/config/samples/acd/autonomouscontainerdatabase_create.yaml new file mode 100644 index 00000000..5f42a136 --- /dev/null +++ b/config/samples/acd/autonomouscontainerdatabase_create.yaml @@ -0,0 +1,20 @@ +# +# 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: AutonomousContainerDatabase +metadata: + name: autonomouscontainerdatabase-sample +spec: + # Update compartmentOCID with your compartment OCID. + compartmentOCID: ocid1.compartment... OR ocid1.tenancy... + autonomousExadataVMClusterOCID: ocid1.autonomousexainfrastructure... + displayName: newACD + # # An optional field for Database Patch model preference. Should be either RELEASE_UPDATES or RELEASE_UPDATE_REVISIONS + # patchModel: RELEASE_UPDATES + + # 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/acd/autonomouscontainerdatabase_delete_resource.yaml b/config/samples/acd/autonomouscontainerdatabase_delete_resource.yaml new file mode 100644 index 00000000..5be06b5a --- /dev/null +++ b/config/samples/acd/autonomouscontainerdatabase_delete_resource.yaml @@ -0,0 +1,16 @@ +# +# 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: AutonomousContainerDatabase +metadata: + name: autonomouscontainerdatabase-sample +spec: + autonomousContainerDatabaseOCID: ocid1.autonomouscontainerdatabase... + # 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. + ociConfig: + configMapName: oci-cred + secretName: oci-privatekey \ No newline at end of file diff --git a/config/samples/acd/autonomouscontainerdatabase_restart_terminate.yaml b/config/samples/acd/autonomouscontainerdatabase_restart_terminate.yaml new file mode 100644 index 00000000..0e884f6e --- /dev/null +++ b/config/samples/acd/autonomouscontainerdatabase_restart_terminate.yaml @@ -0,0 +1,17 @@ +# +# 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: AutonomousContainerDatabase +metadata: + name: autonomouscontainerdatabase-sample +spec: + autonomousContainerDatabaseOCID: ocid1.autonomouscontainerdatabase... + # Change the action to "TERMINATE" to terminate the database + action: RESTART + + # 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/adb/autonomousdatabase.yaml b/config/samples/adb/autonomousdatabase.yaml deleted file mode 100644 index c1dfc078..00000000 --- a/config/samples/adb/autonomousdatabase.yaml +++ /dev/null @@ -1,11 +0,0 @@ -# -# Copyright (c) 2021, 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: - # Add fields here - foo: bar diff --git a/config/samples/adb/autonomousdatabase_backup.yaml b/config/samples/adb/autonomousdatabase_backup.yaml new file mode 100644 index 00000000..0099a347 --- /dev/null +++ b/config/samples/adb/autonomousdatabase_backup.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: database.oracle.com/v1alpha1 +kind: AutonomousDatabaseBackup +metadata: + name: autonomousdatabasebackup-sample +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: + name: autonomousdatabase-sample + # # Uncomment the below block if you use ADB OCID as the input of the target ADB + # 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: + configMapName: oci-cred + secretName: oci-privatekey \ No newline at end of file diff --git a/config/samples/adb/autonomousdatabase_bind.yaml b/config/samples/adb/autonomousdatabase_bind.yaml index d52b9c18..8d1de0fe 100644 --- a/config/samples/adb/autonomousdatabase_bind.yaml +++ b/config/samples/adb/autonomousdatabase_bind.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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: database.oracle.com/v1alpha1 diff --git a/config/samples/adb/autonomousdatabase_create.yaml b/config/samples/adb/autonomousdatabase_create.yaml index fb58b656..aa77a94c 100644 --- a/config/samples/adb/autonomousdatabase_create.yaml +++ b/config/samples/adb/autonomousdatabase_create.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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 @@ -9,18 +9,57 @@ metadata: spec: details: # Update compartmentOCID with your compartment OCID. - compartmentOCID: ocid1.compartment... + 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: NewADB displayName: NewADB cpuCoreCount: 1 adminPassword: - # The Name of the K8s secret where you want to hold the password of the ADMIN account. Comment out k8sSecretName and uncomment ociSecretOCID if you pass the admin password using OCI Secret. - k8sSecretName: admin-password - # The OCID of the OCI Secret that holds the password of the ADMIN account. It should start with ocid1.vaultsecret... . - # ociSecretOCID: ocid1.vaultsecret... + # 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 + + # 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: + # - 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. + # accessType: PRIVATE + # privateEndpoint: + # subnetOCID: ocid1.subnet... + # nsgOCIDs: + # - 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: + # - 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: 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_delete_resource.yaml b/config/samples/adb/autonomousdatabase_delete_resource.yaml index e075787e..60a8fe5c 100644 --- a/config/samples/adb/autonomousdatabase_delete_resource.yaml +++ b/config/samples/adb/autonomousdatabase_delete_resource.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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: database.oracle.com/v1alpha1 diff --git a/config/samples/adb/autonomousdatabase_rename.yaml b/config/samples/adb/autonomousdatabase_rename.yaml index 8aa2ae8b..d3a29998 100644 --- a/config/samples/adb/autonomousdatabase_rename.yaml +++ b/config/samples/adb/autonomousdatabase_rename.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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: database.oracle.com/v1alpha1 diff --git a/config/samples/adb/autonomousdatabase_restore.yaml b/config/samples/adb/autonomousdatabase_restore.yaml new file mode 100644 index 00000000..3db8a1b6 --- /dev/null +++ b/config/samples/adb/autonomousdatabase_restore.yaml @@ -0,0 +1,31 @@ +# +# 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: AutonomousDatabaseRestore +metadata: + name: autonomousdatabaserestore-sample +spec: + # Restore the database either from a backup or using point-in-time restore + # The name of your AutonomousDatabaseBackup resource + target: + k8sADB: + name: autonomousdatabase-sample + # # Uncomment the below block if you use ADB OCID as the input of the target ADB + # ociADB: + # ocid: ocid1.autonomousdatabase... + source: + k8sADBBackup: + name: autonomousdatabasebackup-sample + # # Uncomment the following field to perform point-in-time restore + # pointInTime: + # # The timestamp must follow this format: YYYY-MM-DD HH:MM:SS GMT + # timestamp: 2022-12-23 11:03:13 UTC + + # 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 + + \ No newline at end of file diff --git a/config/samples/adb/autonomousdatabase_scale.yaml b/config/samples/adb/autonomousdatabase_scale.yaml index 4a7c85e9..cd100675 100644 --- a/config/samples/adb/autonomousdatabase_scale.yaml +++ b/config/samples/adb/autonomousdatabase_scale.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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: database.oracle.com/v1alpha1 diff --git a/config/samples/adb/autonomousdatabase_stop_start_terminate.yaml b/config/samples/adb/autonomousdatabase_stop_start_terminate.yaml index 83d831cd..a41f8d0b 100644 --- a/config/samples/adb/autonomousdatabase_stop_start_terminate.yaml +++ b/config/samples/adb/autonomousdatabase_stop_start_terminate.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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: database.oracle.com/v1alpha1 diff --git a/config/samples/adb/autonomousdatabase_change_admin_password.yaml b/config/samples/adb/autonomousdatabase_update_admin_password.yaml similarity index 52% rename from config/samples/adb/autonomousdatabase_change_admin_password.yaml rename to config/samples/adb/autonomousdatabase_update_admin_password.yaml index 47ad1d65..67fd142d 100644 --- a/config/samples/adb/autonomousdatabase_change_admin_password.yaml +++ b/config/samples/adb/autonomousdatabase_update_admin_password.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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: database.oracle.com/v1alpha1 @@ -10,10 +10,13 @@ spec: details: autonomousDatabaseOCID: ocid1.autonomousdatabase... adminPassword: - # The Name of the secret where you want to hold the password of the ADMIN account. Comment out k8sSecretName and uncomment ociSecretOCID if you pass the admin password using OCI Secret. - k8sSecretName: new-admin-password - # The OCID of the OCI Secret that holds the password of the ADMIN account. It should start with ocid1.vaultsecret... . - # ociSecretOCID: ocid1.vaultsecret... + # 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: 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... # 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 new file mode 100644 index 00000000..93db0d8a --- /dev/null +++ b/config/samples/adb/autonomousdatabase_update_mtls.yaml @@ -0,0 +1,20 @@ +# +# 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: + details: + autonomousDatabaseOCID: 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 + + # 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 \ No newline at end of file diff --git a/config/samples/adb/autonomousdatabase_update_network_access.yaml b/config/samples/adb/autonomousdatabase_update_network_access.yaml new file mode 100644 index 00000000..f0e98806 --- /dev/null +++ b/config/samples/adb/autonomousdatabase_update_network_access.yaml @@ -0,0 +1,47 @@ +# +# 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: + details: + autonomousDatabaseOCID: ocid1.autonomousdatabase... + networkAccess: + # Allow 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: + # - 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 of an dedicated Autonomous Database (ADB-D) with an access control list. + # isAccessControlEnabled: true + # 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: + configMapName: oci-cred + secretName: oci-privatekey \ No newline at end of file diff --git a/config/samples/adb/autonomousdatabase_wallet.yaml b/config/samples/adb/autonomousdatabase_wallet.yaml index 34953403..15fa6ca0 100644 --- a/config/samples/adb/autonomousdatabase_wallet.yaml +++ b/config/samples/adb/autonomousdatabase_wallet.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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: database.oracle.com/v1alpha1 @@ -13,10 +13,13 @@ spec: # Insert a name of the secret where you want the wallet to be stored. The default name is -instance-wallet. name: instance-wallet password: - # The Name of the secret where you want to hold the wallet password. Comment out k8sSecretName and uncomment ociSecretOCID if you pass the wallet password using OCI Secret. - k8sSecretName: instance-wallet-password - # The OCID of the OCI Secret that holds the password of the ADMIN account. It should start with ocid1.vaultsecret... . - # ociSecretOCID: ocid1.vaultsecret... + # 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... # 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/dbcs/database_v1alpha1_dbcssystem.yaml b/config/samples/dbcs/database_v1alpha1_dbcssystem.yaml new file mode 100644 index 00000000..0ca38ddf --- /dev/null +++ b/config/samples/dbcs/database_v1alpha1_dbcssystem.yaml @@ -0,0 +1,7 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: DbcsSystem +metadata: + name: dbcssystem-sample +spec: + # Add fields here + foo: bar diff --git a/config/samples/kustomization.yaml b/config/samples/kustomization.yaml index be6907a3..ac5e158f 100644 --- a/config/samples/kustomization.yaml +++ b/config/samples/kustomization.yaml @@ -1,11 +1,33 @@ # -# Copyright (c) 2021, 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. # ## Append samples you want in your CSV to this file as resources ## resources: - - adb/autonomousdatabase.yaml - - sidb/singleinstancedatabase.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_restart_terminate.yaml # +kubebuilder:scaffold:manifestskustomizesamples diff --git a/config/samples/multitenant/cdb.yaml b/config/samples/multitenant/cdb.yaml new file mode 100644 index 00000000..e3513d12 --- /dev/null +++ b/config/samples/multitenant/cdb.yaml @@ -0,0 +1,43 @@ +# +# 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 new file mode 100644 index 00000000..e270100d --- /dev/null +++ b/config/samples/multitenant/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 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 new file mode 100644 index 00000000..f36e904d --- /dev/null +++ b/config/samples/multitenant/pdb_clone.yaml @@ -0,0 +1,27 @@ +# +# 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 new file mode 100644 index 00000000..2be31acf --- /dev/null +++ b/config/samples/multitenant/pdb_create.yaml @@ -0,0 +1,27 @@ +# +# 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 new file mode 100644 index 00000000..6c5299c0 --- /dev/null +++ b/config/samples/multitenant/pdb_delete.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: 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 new file mode 100644 index 00000000..feac2dbf --- /dev/null +++ b/config/samples/multitenant/pdb_modify.yaml @@ -0,0 +1,22 @@ +# +# 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 new file mode 100644 index 00000000..b48c4ffc --- /dev/null +++ b/config/samples/multitenant/pdb_plug.yaml @@ -0,0 +1,19 @@ +# +# 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 new file mode 100644 index 00000000..8a3202d9 --- /dev/null +++ b/config/samples/multitenant/pdb_secret.yaml @@ -0,0 +1,13 @@ +# +# 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 new file mode 100644 index 00000000..21d7b187 --- /dev/null +++ b/config/samples/multitenant/pdb_unplug.yaml @@ -0,0 +1,27 @@ +# +# 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/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/config/samples/sharding/sharding_v1alpha1_provshard.yaml b/config/samples/sharding/sharding_v1alpha1_provshard.yaml index 60f5f007..a51993e6 100644 --- a/config/samples/sharding/sharding_v1alpha1_provshard.yaml +++ b/config/samples/sharding/sharding_v1alpha1_provshard.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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: sharding.oracle.com/v1alpha1 diff --git a/config/samples/sharding/sharding_v1alpha1_provshard_clonespec.yaml b/config/samples/sharding/sharding_v1alpha1_provshard_clonespec.yaml index 414544bd..acbf5a9c 100644 --- a/config/samples/sharding/sharding_v1alpha1_provshard_clonespec.yaml +++ b/config/samples/sharding/sharding_v1alpha1_provshard_clonespec.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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: sharding.oracle.com/v1alpha1 diff --git a/config/samples/sharding/sharding_v1alpha1_provshard_clonespec1.yaml b/config/samples/sharding/sharding_v1alpha1_provshard_clonespec1.yaml index 67988b2c..5bca5b85 100644 --- a/config/samples/sharding/sharding_v1alpha1_provshard_clonespec1.yaml +++ b/config/samples/sharding/sharding_v1alpha1_provshard_clonespec1.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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: sharding.oracle.com/v1alpha1 diff --git a/config/samples/sharding/sharding_v1alpha1_provshard_orig.yaml b/config/samples/sharding/sharding_v1alpha1_provshard_orig.yaml index fc7ba66d..0300d6ce 100644 --- a/config/samples/sharding/sharding_v1alpha1_provshard_orig.yaml +++ b/config/samples/sharding/sharding_v1alpha1_provshard_orig.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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: sharding.oracle.com/v1alpha1 diff --git a/config/samples/sharding/shardingdatabase.yaml b/config/samples/sharding/shardingdatabase.yaml index 40453b49..b639800f 100644 --- a/config/samples/sharding/shardingdatabase.yaml +++ b/config/samples/sharding/shardingdatabase.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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: database.oracle.com/v1alpha1 diff --git a/config/samples/sidb/dataguardbroker.yaml b/config/samples/sidb/dataguardbroker.yaml new file mode 100644 index 00000000..5425afb7 --- /dev/null +++ b/config/samples/sidb/dataguardbroker.yaml @@ -0,0 +1,29 @@ +# +# 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: DataguardBroker +metadata: + name: dataguardbroker-sample + namespace: default +spec: + + ## Primary DB ref. This is of kind SingleInstanceDatabase + primaryDatabaseRef: "sidb-sample" + + ## Standby DB pod CRD Metadata Name to add this DB to DG config + standbyDatabaseRefs: + # - standbydatabase-sample + # - standbydatabase-sample1 + + ## Type of service . Applicable on cloud enviroments only + ## if loadBalService : false , service type = "NodePort" . else "LoadBalancer" + loadBalancer: false + + ## 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 . + setAsPrimaryDatabase: "" diff --git a/config/samples/sidb/openshift_rbac.yaml b/config/samples/sidb/openshift_rbac.yaml new file mode 100644 index 00000000..6dddb80d --- /dev/null +++ b/config/samples/sidb/openshift_rbac.yaml @@ -0,0 +1,94 @@ +# +# 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: 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: 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: + - apiGroups: + - security.openshift.io + verbs: + - use + resources: + - securitycontextconstraints + resourceNames: + - 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 diff --git a/config/samples/sidb/oraclerestdataservice.yaml b/config/samples/sidb/oraclerestdataservice.yaml new file mode 100644 index 00000000..911a9b1e --- /dev/null +++ b/config/samples/sidb/oraclerestdataservice.yaml @@ -0,0 +1,84 @@ +# +# 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. + databaseRef: "singleinstancedatabase-sample" + + ## Secret containing databaseRef password mapped to secretKey. + ## This secret will be deleted after ORDS Installation unless keepSecret set to true + adminPassword: + secretName: + secretKey: + keepSecret: true + + ## Secret containing ORDS_PUBLIC_USER password mapped to secretKey. secretKey defaults to oracle_pwd + ## This secret will be deleted after ORDS Installation unless keepSecret set to true + ordsPassword: + secretName: + 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 + pullSecrets: + + ## Dedicated persistent storage is optional. If not specified, ORDS will use persistent storage from .spec.databaseRef + ## size is the required minimum size of the persistent volume + ## storageClass is used 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 + # 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" + # volumeName: "" + + ## Type of service Applicable on cloud enviroments only. + ## if loadBalService: false, service type = "NodePort" else "LoadBalancer" + loadBalancer: false + ## Service Annotations (Cloud provider specific), for configuring the service (e.g. private LoadBalancer service) + #serviceAnnotations: + # service.beta.kubernetes.io/oci-load-balancer-internal: "true" + + + ## Deploy only on nodes having required labels. Format label_name: label_value + ## The same lables are applied to the created PVC + ## For instance if the pods need to be restricted to a particular AD + ## Leave commented if there is no such requirement + # nodeSelector: + # topology.kubernetes.io/zone: PHX-AD-1 + + ## Schemas to be ORDS Enabled in PDB of .spec.databaseRef (.spec.pdbName) + ## Schema will be created (if not exists) with password as .spec.ordsPassword + restEnableSchemas: + - schemaName: + enable: true + urlMapping: + + ## If deploying on OpenShift, change service account name to 'sidb-sa' after you run `$ oc apply -f openshift_rbac.yaml` + serviceAccountName: default diff --git a/config/samples/sidb/oraclerestdataservice_apex.yaml b/config/samples/sidb/oraclerestdataservice_apex.yaml new file mode 100644 index 00000000..6bdc9fb5 --- /dev/null +++ b/config/samples/sidb/oraclerestdataservice_apex.yaml @@ -0,0 +1,42 @@ +# +# 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 new file mode 100644 index 00000000..454abf37 --- /dev/null +++ b/config/samples/sidb/oraclerestdataservice_create.yaml @@ -0,0 +1,37 @@ +# +# 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 mapped to secretKey. + adminPassword: + secretName: xedb-admin-secret + + ## Secret containing ORDS_PUBLIC_USER password mapped to secretKey. + ordsPassword: + secretName: ords-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_secrets.yaml b/config/samples/sidb/oraclerestdataservice_secrets.yaml new file mode 100644 index 00000000..9e587960 --- /dev/null +++ b/config/samples/sidb/oraclerestdataservice_secrets.yaml @@ -0,0 +1,33 @@ +# +# 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. +# + +## ORDS password secret +apiVersion: v1 +kind: Secret +metadata: + name: ords-secret + namespace: default +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/config/samples/sidb/singleinstancedatabase.yaml b/config/samples/sidb/singleinstancedatabase.yaml index e39e3208..4425acea 100644 --- a/config/samples/sidb/singleinstancedatabase.yaml +++ b/config/samples/sidb/singleinstancedatabase.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, Oracle and/or its affiliates. +# 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 @@ -9,41 +9,64 @@ metadata: namespace: default spec: - ## Use only alphanumeric characters for sid + ## Use only alphanumeric characters for sid up to a maximum of 8 characters sid: ORCL1 - - ## A source database ref to clone from, leave empty to create a fresh database - cloneFrom: "" - ## NA if cloning from a SourceDB (cloneFrom is set) + ## 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: "" - ## Should refer to SourceDB secret if cloning from a SourceDB (cloneFrom is set) - ## Secret containing SIDB password mapped to secretKey - ## This secret will be deleted after creation of the database unless keepSecret is set to true + ## 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 + ## 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 - ## NA if cloning from a SourceDB (cloneFrom is set) + ## DB character set. N/A for createAs clone or standby charset: AL32UTF8 - ## NA if cloning from a SourceDB (cloneFrom is set) + ## PDB name. N/A for createAs clone or standby pdbName: orclpdb1 ## Enable/Disable Flashback flashBack: false - ## Enable/Disable ArchiveLog + ## Enable/Disable ArchiveLog. Should be true to allow DB cloning archiveLog: false ## Enable/Disable ForceLogging forceLog: false - ## NA if cloning from a SourceDB (cloneFrom is set) - ## Specify both sgaSize and pgaSize (in MB) or dont specify both + ## Enable TCPS + enableTCPS: + + ## User specified TLS-Cert Secret + ## The following specified TLS certs will be used instead of self-signed + tcpsTlsSecret: + + ## 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 + tcpsCertRenewInterval: 8760h + + ## N/A for createAs clone or standby ## Specify Non-Zero value to use + ## 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 processes: 0 @@ -51,30 +74,78 @@ spec: pgaAggregateTarget: 0 ## Database image details - ## Database can be patched by updating the RU version/image - ## Major version changes are not supported + ## Base DB images are available at container-registry.oracle.com or build from https://github.com/oracle/docker-images/tree/main/OracleDatabase/SingleInstance + ## Build patched DB images from https://github.com/oracle/docker-images/tree/main/OracleDatabase/SingleInstance/extensions/patching + ## Prebuilt DB support (https://github.com/oracle/docker-images/tree/main/OracleDatabase/SingleInstance/extensions/prebuiltdb) + ## Specify prebuiltDB as true if the image includes a prebuilt DB + ## If cloning specify an image that is of same major version as the source DB at same or different patch levels image: pullFrom: pullSecrets: + prebuiltDB: false - ## size : Minimum size of pvc | class : PVC storage Class - ## AccessMode can only accept one of ReadWriteOnce, ReadWriteMany - ## Below mentioned storageClass/accessMode applies to OCI block volumes. Update appropriately for other types of persistent volumes. + + ## Database storage details + ## size is the required minimum size of the persistent volume + ## 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 - storageClass: "oci" + ## 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 accessMode: "ReadWriteOnce" + ## 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: "" + + ## 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" + ## if loadBalService : false, service type = "NodePort" else "LoadBalancer" loadBalancer: false + + ## 'listenerPort' and 'tcpsListenerPort' fields customizes port cofigurations for normal and tcps database listeners + ## 'tcpsListenerPort' will come in effect only when 'enableTCPS' field is set + ## If loadBalancer is enabled, the listenerPort, tcpsListenerPort will be the load balancer ports + ## If loadBalancer is disabled, the listenerPort, tcpsListenerPort will be the node ports(should be in range 30000-32767) + ## If enableTCPS is set, and listenerPort is commented/not mentioned in the YAML file, only TCPS endpoint will be exposed + #listenerPort: 30001 + #tcpsListenerPort: 30002 + + ## Service Annotations (Cloud provider specific), for configuring the service (e.g. private LoadBalancer service) + #serviceAnnotations: + # service.beta.kubernetes.io/oci-load-balancer-internal: "true" + + ## Deploy only on nodes having required labels. Format label_name: label_value + ## For instance if the pods need to be restricted to a particular AD + ## Leave commented if there is no such requirement. + # nodeSelector: + # topology.kubernetes.io/zone: PHX-AD-1 - ## Deploy only on nodes having required labels. Format label_name : label_value - ## Leave empty if there is no such requirement. - ## Uncomment to use - # nodeSelector: - # failure-domain.beta.kubernetes.io/zone: bVCG:PHX-AD-1 - # pool: sidb + ## If deploying on OpenShift, change service account name to 'sidb-sa' after you run `$ oc apply -f openshift_rbac.yaml` + serviceAccountName: default - ## Count of Database Pods. Applicable only for "ReadWriteMany" AccessMode + ## Count of Database Pods. Only one pod will have the DB mounted and open. + ## The other replica pods will have instance up and will mount and open the DB if the primary pod dies + ## For "ReadWriteOnce" AccessMode, all the replicas will schedule on the same node that has the storage attached + ## For minimal downtime during patching set the count of replicas > 1 + ## Express edition can only have one replica and does not support patching replicas: 1 diff --git a/config/samples/sidb/singleinstancedatabase_clone.yaml b/config/samples/sidb/singleinstancedatabase_clone.yaml index 2881b4f9..f25484d9 100644 --- a/config/samples/sidb/singleinstancedatabase_clone.yaml +++ b/config/samples/sidb/singleinstancedatabase_clone.yaml @@ -1,7 +1,8 @@ # -# Copyright (c) 2021, Oracle and/or its affiliates. +# 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 kind: SingleInstanceDatabase metadata: @@ -10,33 +11,36 @@ metadata: spec: ## Use only alphanumeric characters for sid - sid: ORCL1 + sid: ORCL2 - ## A source database ref to clone from, leave empty to create a fresh database - cloneFrom: "" + ## 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 + + ## Intended type of database. + createAs: clone ## Should refer to SourceDB secret ## Secret containing SIDB password mapped to secretKey - ## This secret will be deleted after creation of the database unless keepSecret is set to true adminPassword: - secretName: - secretKey: - keepSecret: true + secretName: db-admin-secret ## Database image details - ## Database can be patched out of place by updating the RU version/image - ## Major version changes are not supported + ## This image should be the same as the source DB image being cloned + ## or a patched DB image built from the souce DB image following instructions at + ## https://github.com/oracle/docker-images/tree/main/OracleDatabase/SingleInstance/extensions/patching image: - pullFrom: - pullSecrets: + pullFrom: container-registry.oracle.com/database/enterprise:latest + pullSecrets: oracle-container-registry-secret - ## size : Minimum size of pvc | class : PVC storage Class - ## AccessMode can only accept one of ReadWriteOnce, ReadWriteMany - ## Below mentioned storageClass/accessMode applies to OCI block volumes. Update appropriately for other types of persistent volumes. + ## 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: 100Gi - storageClass: "oci" + ## 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. Applicable only for "ReadWriteMany" AccessMode + ## Count of Database Pods. replicas: 1 diff --git a/config/samples/sidb/singleinstancedatabase_create.yaml b/config/samples/sidb/singleinstancedatabase_create.yaml new file mode 100644 index 00000000..d09d9c26 --- /dev/null +++ b/config/samples/sidb/singleinstancedatabase_create.yaml @@ -0,0 +1,49 @@ +# +# 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 +kind: SingleInstanceDatabase +metadata: + # Creates base sidb-sample. Use singleinstancedatabase_clone.yaml for cloning + # and singleinstancedatabase_patch.yaml for patching + name: sidb-sample + namespace: default +spec: + + ## Use only alphanumeric characters for sid + sid: ORCL1 + + ## DB edition. + edition: enterprise + + ## Secret containing SIDB password mapped to secretKey + adminPassword: + secretName: db-admin-secret + + ## DB character set + charset: AL32UTF8 + + ## PDB name + pdbName: orclpdb1 + + ## Enable/Disable ArchiveLog. Should be true to allow DB cloning + archiveLog: true + + ## Database image details + image: + pullFrom: container-registry.oracle.com/database/enterprise:latest + pullSecrets: oracle-container-registry-secret + + ## 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: 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" + accessMode: "ReadWriteOnce" + + ## Count of Database Pods. + replicas: 1 diff --git a/config/samples/sidb/singleinstancedatabase_express.yaml b/config/samples/sidb/singleinstancedatabase_express.yaml new file mode 100644 index 00000000..64f2e351 --- /dev/null +++ b/config/samples/sidb/singleinstancedatabase_express.yaml @@ -0,0 +1,38 @@ +# +# 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 +kind: SingleInstanceDatabase +metadata: + name: xedb-sample + namespace: default +spec: + + ## Use only alphanumeric characters for sid + sid: XE + + ## DB edition + edition: express + + ## Secret containing SIDB password mapped to secretKey + adminPassword: + secretName: xedb-admin-secret + + ## Database image details + image: + pullFrom: container-registry.oracle.com/database/express:latest + prebuiltDB: true + + ## 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 express edition. + replicas: 1 diff --git a/config/samples/sidb/singleinstancedatabase_free.yaml b/config/samples/sidb/singleinstancedatabase_free.yaml new file mode 100644 index 00000000..6dd0aa39 --- /dev/null +++ b/config/samples/sidb/singleinstancedatabase_free.yaml @@ -0,0 +1,39 @@ +# +# 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 +kind: SingleInstanceDatabase +metadata: + name: freedb-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 is only supported from DB version 23.2 onwards + pullFrom: container-registry.oracle.com/database/free:latest + prebuiltDB: true + + ## 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/config/samples/sidb/singleinstancedatabase_minikube.yaml b/config/samples/sidb/singleinstancedatabase_minikube.yaml deleted file mode 100644 index a3a4824c..00000000 --- a/config/samples/sidb/singleinstancedatabase_minikube.yaml +++ /dev/null @@ -1,94 +0,0 @@ -# -# Copyright (c) 2021, 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: Secret -metadata: - name: db-secret -type: Opaque -stringData: - oracle_pwd: "Change_On_Install_1" - ---- - -apiVersion: v1 -kind: PersistentVolume -metadata: - name: sidb-pv -spec: - accessModes: - - ReadWriteMany - capacity: - storage: 5Gi - storageClassName: sidb - hostPath: - path: /data/oradata - ---- - -apiVersion: database.oracle.com/v1alpha1 -kind: SingleInstanceDatabase -metadata: - name: sidb-sample - namespace: default -spec: - - ## Use only alphanumeric characters for sid - sid: ORCL1 - - ## A source database ref to clone from, leave empty to create a fresh database - cloneFrom: "" - - ## NA if cloning from a SourceDB (cloneFrom is set) - edition: enterprise - - ## Should refer to SourceDB secret if cloning from a SourceDB (cloneFrom is set) - ## Secret containing SIDB password mapped to secretKey - ## This secret will be deleted after creation of the database unless keepSecret is set to true - adminPassword: - secretName: db-secret - secretKey: oracle_pwd - keepSecret: true - - ## NA if cloning from a SourceDB (cloneFrom is set) - charset: AL32UTF8 - - ## NA if cloning from a SourceDB (cloneFrom is set) - pdbName: orclpdb1 - - ## Enable/Disable Flashback - flashBack: false - - ## Enable/Disable ArchiveLog - archiveLog: false - - ## Enable/Disable ForceLogging - forceLog: false - - ## NA if cloning from a SourceDB (cloneFrom is set) - ## Specify both sgaSize and pgaSize (in MB) or dont specify both - ## Specify Non-Zero value to use - initParams: - cpuCount: 0 - processes: 0 - sgaTarget: 0 - pgaAggregateTarget: 0 - - ## Database image details - ## Database can be patched by updating the RU version/image - ## Major version changes are not supported - image: - pullFrom: container-registry.oracle.com/database/enterprise:latest - pullSecrets: oracle-container-registry-secret - - ## size : Minimum size of pvc | class : PVC storage Class - ## AccessMode can only accept one of ReadWriteOnce, ReadWriteMany - persistence: - size: 5Gi - storageClass: "sidb" - accessMode: "ReadWriteMany" - - ## Count of Database Pods. Applicable only for "ReadWriteMany" AccessMode - replicas: 1 diff --git a/config/samples/sidb/singleinstancedatabase_patch.yaml b/config/samples/sidb/singleinstancedatabase_patch.yaml index 8fb0ddb6..9a211cdc 100644 --- a/config/samples/sidb/singleinstancedatabase_patch.yaml +++ b/config/samples/sidb/singleinstancedatabase_patch.yaml @@ -1,37 +1,51 @@ # -# Copyright (c) 2021, Oracle and/or its affiliates. +# 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 kind: SingleInstanceDatabase metadata: - name: sidb-sample-patch + # sidb-sample should have already been created using singleinstancedatabase_create.yaml + name: sidb-sample namespace: default spec: ## Use only alphanumeric characters for sid sid: ORCL1 - + + ## DB edition. + edition: enterprise + ## Secret containing SIDB password mapped to secretKey - ## This secret will be deleted after creation of the database unless keepSecret is set to true adminPassword: - secretName: - secretKey: - keepSecret: true + secretName: db-admin-secret + + ## DB character set + charset: AL32UTF8 + + ## PDB name + pdbName: orclpdb1 + + ## Enable/Disable ArchiveLog. Should be true to allow DB cloning + archiveLog: true - ## Patch the database by updating the RU version/image - ## Major version changes are not supported + ## Patched Database image + ## Using the source base image container-registry.oracle.com/database/enterprise:latest + ## build patched DB images following instructions at + ## https://github.com/oracle/docker-images/tree/main/OracleDatabase/SingleInstance/extensions/patching image: pullFrom: pullSecrets: - ## size : Minimum size of pvc | class : PVC storage Class - ## AccessMode can only accept one of ReadWriteOnce, ReadWriteMany - ## Below mentioned storageClass/accessMode applies to OCI block volumes. Update appropriately for other types of persistent volumes. + ## 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: 100Gi - storageClass: "oci" + ## 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. Applicable only for "ReadWriteMany" AccessMode + ## Count of Database Pods. replicas: 1 diff --git a/config/samples/sidb/singleinstancedatabase_prebuiltdb.yaml b/config/samples/sidb/singleinstancedatabase_prebuiltdb.yaml new file mode 100644 index 00000000..5e4d0a4f --- /dev/null +++ b/config/samples/sidb/singleinstancedatabase_prebuiltdb.yaml @@ -0,0 +1,33 @@ +# +# 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 +kind: SingleInstanceDatabase +metadata: + name: prebuiltdb-sample + namespace: default +spec: + + ## DB edition + edition: free + + ## Secret containing SIDB password mapped to secretKey + adminPassword: + secretName: prebuiltdb-admin-secret + + ## Database Image + image: + pullFrom: container-registry.oracle.com/database/free:latest + prebuiltDB: true + + ## Persistence is optional for prebuilt DB image + ## if specified, the prebuilt DB datafiles are copied over to the persistant volume before DB startup + #persistence: + # size: 50Gi + # storageClass: "oci-bv" + # accessMode: "ReadWriteOnce" + + ## Count of Database Pods. + replicas: 1 diff --git a/config/samples/sidb/singleinstancedatabase_prov.yaml b/config/samples/sidb/singleinstancedatabase_prov.yaml deleted file mode 100644 index dbee637c..00000000 --- a/config/samples/sidb/singleinstancedatabase_prov.yaml +++ /dev/null @@ -1,35 +0,0 @@ -# -# Copyright (c) 2021, 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: sidb-sample - namespace: default -spec: - - ## Use only alphanumeric characters for sid - sid: ORCL1 - - ## Secret containing SIDB password mapped to secretKey - adminPassword: - secretName: - secretKey: - keepSecret: true - - ## Database image details - image: - pullFrom: - pullSecrets: - - ## size : Minimum size of pvc | class : PVC storage Class - ## AccessMode can only accept one of ReadWriteOnce, ReadWriteMany - ## Below mentioned storageClass/accessMode applies to OCI block volumes. Update appropriately for other types of persistent volumes. - persistence: - size: 100Gi - storageClass: "oci" - accessMode: "ReadWriteOnce" - - ## Count of Database Pods. Applicable only for "ReadWriteMany" AccessMode - replicas: 1 diff --git a/config/samples/sidb/singleinstancedatabase_secrets.yaml b/config/samples/sidb/singleinstancedatabase_secrets.yaml new file mode 100644 index 00000000..d98432ee --- /dev/null +++ b/config/samples/sidb/singleinstancedatabase_secrets.yaml @@ -0,0 +1,54 @@ +# +# 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. +# + +## Database Admin Password Secret +apiVersion: v1 +kind: Secret +metadata: + name: db-admin-secret + namespace: default +type: Opaque +stringData: + ## Specify your DB password here + oracle_pwd: + +--- + +## Prebuilt-Database Admin password secret +apiVersion: v1 +kind: Secret +metadata: + name: prebuiltdb-admin-secret + namespace: default +type: Opaque +stringData: + ## Specify your DB password here + oracle_pwd: + +--- + +## Oracle Database XE Admin password secret +apiVersion: v1 +kind: Secret +metadata: + name: xedb-admin-secret + namespace: default +type: Opaque +stringData: + ## Specify your DB password here + oracle_pwd: + +--- + +## Oracle Database Free Admin password secret +apiVersion: v1 +kind: Secret +metadata: + name: freedb-admin-secret + namespace: default +type: Opaque +stringData: + ## Specify your DB password here + oracle_pwd: diff --git a/config/samples/sidb/singleinstancedatabase_standby.yaml b/config/samples/sidb/singleinstancedatabase_standby.yaml new file mode 100644 index 00000000..644438b4 --- /dev/null +++ b/config/samples/sidb/singleinstancedatabase_standby.yaml @@ -0,0 +1,47 @@ +# +# 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 +kind: SingleInstanceDatabase +metadata: + # Creates base standbydatabase-sample. Use singleinstancedatabase_clone.yaml for cloning + # and singleinstancedatabase_patch.yaml for patching + name: standbydatabase-sample + namespace: default +spec: + + ## Use only alphanumeric characters for sid + sid: ORCLS + + ## The name of a source primary database resource from the same namespace + primaryDatabaseRef: "sidb-sample" + + ## Intended type of database. + createAs: standby + + ## Secret containing SIDB password mapped to secretKey + adminPassword: + secretName: db-admin-secret + + ## Database image details + image: + pullFrom: container-registry.oracle.com/database/enterprise:latest + pullSecrets: oracle-container-registry-secret + + ## 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: 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" + accessMode: "ReadWriteOnce" + + ## Type of service . Applicable on cloud enviroments only + ## if loadBalService : false, service type = "NodePort" else "LoadBalancer" + loadBalancer: false + + replicas: 1 + \ No newline at end of file diff --git a/config/samples/sidb/singleinstancedatabase_tcps.yaml b/config/samples/sidb/singleinstancedatabase_tcps.yaml new file mode 100644 index 00000000..06389f96 --- /dev/null +++ b/config/samples/sidb/singleinstancedatabase_tcps.yaml @@ -0,0 +1,63 @@ +# +# 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 +kind: SingleInstanceDatabase +metadata: + # Creates base sidb-sample. Use singleinstancedatabase_clone.yaml for cloning + # and singleinstancedatabase_patch.yaml for patching + name: sidb-sample + namespace: default +spec: + + ## Use only alphanumeric characters for sid + sid: ORCL1 + + ## DB edition. + edition: enterprise + + ## Secret containing SIDB password mapped to secretKey + adminPassword: + secretName: db-admin-secret + + ## DB character set + charset: AL32UTF8 + + ## PDB name + pdbName: orclpdb1 + + ## Enable/Disable ArchiveLog. Should be true to allow DB cloning + archiveLog: true + + ## Enable TCPS + enableTCPS: true + + ## 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 + tcpsCertRenewInterval: 8760h + + ## Database image details + image: + pullFrom: container-registry.oracle.com/database/enterprise:latest + pullSecrets: oracle-container-registry-secret + + ## 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: 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" + accessMode: "ReadWriteOnce" + + ## Count of Database Pods. + replicas: 1 diff --git a/config/scorecard/bases/config.yaml b/config/scorecard/bases/config.yaml index 0650fef2..fbd8c506 100644 --- a/config/scorecard/bases/config.yaml +++ b/config/scorecard/bases/config.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # kind: Configuration diff --git a/config/scorecard/kustomization.yaml b/config/scorecard/kustomization.yaml index d9c19e9b..bf4c1e7c 100644 --- a/config/scorecard/kustomization.yaml +++ b/config/scorecard/kustomization.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # resources: diff --git a/config/scorecard/patches/basic.config.yaml b/config/scorecard/patches/basic.config.yaml index 67fa78c9..516ab755 100644 --- a/config/scorecard/patches/basic.config.yaml +++ b/config/scorecard/patches/basic.config.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # - op: add diff --git a/config/scorecard/patches/olm.config.yaml b/config/scorecard/patches/olm.config.yaml index 521bc8e6..40e4fbe8 100644 --- a/config/scorecard/patches/olm.config.yaml +++ b/config/scorecard/patches/olm.config.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # - op: add diff --git a/config/webhook/kustomization.yaml b/config/webhook/kustomization.yaml index ef3ca64c..f78631f3 100644 --- a/config/webhook/kustomization.yaml +++ b/config/webhook/kustomization.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # resources: diff --git a/config/webhook/kustomizeconfig.yaml b/config/webhook/kustomizeconfig.yaml index afc69aba..972d71bb 100644 --- a/config/webhook/kustomizeconfig.yaml +++ b/config/webhook/kustomizeconfig.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # diff --git a/config/webhook/manifests.yaml b/config/webhook/manifests.yaml index 5cb37b30..c150867f 100644 --- a/config/webhook/manifests.yaml +++ b/config/webhook/manifests.yaml @@ -6,6 +6,150 @@ metadata: creationTimestamp: null name: mutating-webhook-configuration webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: 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: webhook-service + namespace: 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: webhook-service + namespace: 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: webhook-service + namespace: 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: webhook-service + namespace: 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: webhook-service + namespace: 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: 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 @@ -27,6 +171,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 @@ -35,6 +199,191 @@ metadata: creationTimestamp: null name: validating-webhook-configuration webhooks: +- admissionReviewVersions: + - v1 + clientConfig: + service: + name: webhook-service + namespace: 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: webhook-service + namespace: 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: webhook-service + namespace: 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: webhook-service + namespace: 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: webhook-service + namespace: 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: webhook-service + namespace: 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: webhook-service + namespace: 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: webhook-service + namespace: 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: 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 @@ -57,3 +406,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/config/webhook/service.yaml b/config/webhook/service.yaml index 08333c36..f93ad4d2 100644 --- a/config/webhook/service.yaml +++ b/config/webhook/service.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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 diff --git a/controllers/database/autonomouscontainerdatabase_controller.go b/controllers/database/autonomouscontainerdatabase_controller.go new file mode 100644 index 00000000..3845a03f --- /dev/null +++ b/controllers/database/autonomouscontainerdatabase_controller.go @@ -0,0 +1,658 @@ +/* +** 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" + "reflect" + + "github.com/go-logr/logr" + "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/runtime" + "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" + + dbv1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + "github.com/oracle/oracle-database-operator/commons/annotations" + "github.com/oracle/oracle-database-operator/commons/k8s" + "github.com/oracle/oracle-database-operator/commons/oci" +) + +// AutonomousContainerDatabaseReconciler reconciles a AutonomousContainerDatabase object +type AutonomousContainerDatabaseReconciler struct { + KubeClient client.Client + Log logr.Logger + Scheme *runtime.Scheme + Recorder record.EventRecorder + + dbService oci.DatabaseService +} + +// SetupWithManager sets up the controller with the Manager. +func (r *AutonomousContainerDatabaseReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&dbv1alpha1.AutonomousContainerDatabase{}). + WithEventFilter(r.eventFilterPredicate()). + WithOptions(controller.Options{MaxConcurrentReconciles: 5}). + Complete(r) +} + +func (r *AutonomousContainerDatabaseReconciler) eventFilterPredicate() predicate.Predicate { + pred := predicate.Funcs{ + UpdateFunc: func(e event.UpdateEvent) bool { + desiredACD, acdOk := e.ObjectNew.(*dbv1alpha1.AutonomousContainerDatabase) + if acdOk { + oldACD := e.ObjectOld.(*dbv1alpha1.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)) { + // Don't enqueue if the status, lastSucSpec, or the finalizler changes + return false + } + + return true + } + return true + }, + DeleteFunc: func(e event.DeleteEvent) bool { + // Do not trigger reconciliation when the object is deleted from the cluster. + _, acdOk := e.Object.(*dbv1alpha1.AutonomousContainerDatabase) + return !acdOk + }, + } + + return pred +} + +//+kubebuilder:rbac:groups=database.oracle.com,resources=autonomouscontainerdatabases,verbs=get;list;watch;create;update;patch;delete +//+kubebuilder:rbac:groups=database.oracle.com,resources=autonomouscontainerdatabases/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=database.oracle.com,resources=autonomousdatabases,verbs=get;list;watch;create;update;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. +// +// 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 *AutonomousContainerDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := r.Log.WithValues("Namespace/Name", req.NamespacedName) + + var err error + var ociACD *dbv1alpha1.AutonomousContainerDatabase + + // Get the autonomousdatabase instance from the cluster + acd := &dbv1alpha1.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. + if apiErrors.IsNotFound(err) { + return emptyResult, nil + } + // Failed to get ACD, so we don't need to update the status + return emptyResult, err + } + + /****************************************************************** + * Get OCI database client + ******************************************************************/ + if err := r.setupOCIClients(logger, acd); err != nil { + logger.Error(err, "Fail to setup OCI clients") + + return r.manageError(logger, acd, err) + } + + logger.Info("OCI clients configured succesfully") + + /****************************************************************** + * Get OCI AutonomousDatabase + ******************************************************************/ + + if acd.Spec.AutonomousContainerDatabaseOCID != nil { + resp, err := r.dbService.GetAutonomousContainerDatabase(*acd.Spec.AutonomousContainerDatabaseOCID) + if err != nil { + return r.manageError(logger, acd, err) + } + + ociACD = &dbv1alpha1.AutonomousContainerDatabase{} + ociACD.UpdateFromOCIACD(resp.AutonomousContainerDatabase) + } + + /****************************************************************** + * Requeue if the ACD is in an intermediate state + * No-op if the ACD OCID is nil + * To get the latest status, execute before all the reconcile logic + ******************************************************************/ + needsRequeue, err := r.validateLifecycleState(logger, acd, ociACD) + if err != nil { + return r.manageError(logger, acd, err) + } + + if needsRequeue { + return requeueResult, nil + } + + /****************************************************************** + * 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/ + ******************************************************************/ + exitReconcile, err := r.validateCleanup(logger, acd) + if err != nil { + return r.manageError(logger, acd, err) + } + + if exitReconcile { + return emptyResult, nil + } + + /****************************************************************** + * Register/unregister the finalizer + ******************************************************************/ + if err := r.validateFinalizer(acd); err != nil { + return r.manageError(logger, acd, err) + } + + /****************************************************************** + * Validate operations + ******************************************************************/ + exitReconcile, result, err := r.validateOperation(logger, acd, ociACD) + if err != nil { + return r.manageError(logger, acd, err) + } + if exitReconcile { + return result, nil + } + + /****************************************************************** + * Update the status and requeue if it's in an intermediate state + ******************************************************************/ + if err := r.KubeClient.Status().Update(context.TODO(), acd); err != nil { + return r.manageError(logger, acd, err) + } + + if dbv1alpha1.IsACDIntermediateState(acd.Status.LifecycleState) { + logger.WithName("IsIntermediateState").Info("Current lifecycleState is " + string(acd.Status.LifecycleState) + "; reconcile queued") + return requeueResult, nil + } + + if err := r.patchLastSuccessfulSpec(acd); err != nil { + return r.manageError(logger, acd, err) + } + + logger.Info("AutonomousContainerDatabase reconciles successfully") + + return emptyResult, nil +} + +func (r *AutonomousContainerDatabaseReconciler) setupOCIClients(logger logr.Logger, acd *dbv1alpha1.AutonomousContainerDatabase) error { + var err error + + authData := oci.APIKeyAuth{ + ConfigMapName: acd.Spec.OCIConfig.ConfigMapName, + SecretName: acd.Spec.OCIConfig.SecretName, + Namespace: acd.GetNamespace(), + } + + provider, err := oci.GetOCIProvider(r.KubeClient, authData) + if err != nil { + return err + } + + r.dbService, err = oci.NewDatabaseService(logger, r.KubeClient, provider) + if err != nil { + return err + } + + return nil +} + +func (r *AutonomousContainerDatabaseReconciler) manageError(logger logr.Logger, acd *dbv1alpha1.AutonomousContainerDatabase, issue error) (ctrl.Result, error) { + l := logger.WithName("manageError") + + // Has synced at least once + if acd.Status.LifecycleState != "" { + // Send event + r.Recorder.Event(acd, corev1.EventTypeWarning, "UpdateFailed", issue.Error()) + + var finalIssue = issue + + // Roll back + specChanged, err := r.getACD(logger, acd) + if err != nil { + finalIssue = k8s.CombineErrors(finalIssue, err) + } + + // We don't exit the Reconcile if the spec has changed + // becasue it will exit anyway after the manageError is called. + if specChanged { + if err := r.KubeClient.Update(context.TODO(), acd); err != nil { + finalIssue = k8s.CombineErrors(finalIssue, err) + } + } + + l.Error(finalIssue, "UpdateFailed") + + return emptyResult, nil + } else { + // Send event + r.Recorder.Event(acd, corev1.EventTypeWarning, "CreateFailed", issue.Error()) + + return emptyResult, issue + } +} + +// validateLifecycleState gets and validates the current lifecycleState +func (r *AutonomousContainerDatabaseReconciler) validateLifecycleState(logger logr.Logger, acd *dbv1alpha1.AutonomousContainerDatabase, ociACD *dbv1alpha1.AutonomousContainerDatabase) (needsRequeue bool, err error) { + if ociACD == nil { + return false, nil + } + + l := logger.WithName("validateLifecycleState") + + // Special case: Once the status changes to AVAILABLE after the provision operation, the reconcile stops. + // The backup starts right after the provision operation and the controller is not able to track the operation in this case. + // To prevent this issue, requeue the reconcile if the previous status is PROVISIONING and we ignore the status change + // until it becomes BACKUP_IN_PROGRESS. + if acd.Status.LifecycleState == database.AutonomousContainerDatabaseLifecycleStateProvisioning && + ociACD.Status.LifecycleState != database.AutonomousContainerDatabaseLifecycleStateBackupInProgress { + l.Info("Provisioning the ACD and waiting for the backup to start; reconcile queued") + return true, nil + } + + acd.Status = ociACD.Status + + if err := r.KubeClient.Status().Update(context.TODO(), acd); err != nil { + return false, err + } + + if dbv1alpha1.IsACDIntermediateState(ociACD.Status.LifecycleState) { + l.Info("LifecycleState is " + string(acd.Status.LifecycleState) + "; reconcile queued") + return true, nil + } + + return false, nil +} + +func (r *AutonomousContainerDatabaseReconciler) validateCleanup(logger logr.Logger, acd *dbv1alpha1.AutonomousContainerDatabase) (exitReconcile bool, err error) { + l := logger.WithName("validateCleanup") + + isACDToBeDeleted := acd.GetDeletionTimestamp() != nil + + if !isACDToBeDeleted { + return false, nil + } + + if controllerutil.ContainsFinalizer(acd, dbv1alpha1.ACDFinalizer) { + if acd.Status.LifecycleState == database.AutonomousContainerDatabaseLifecycleStateTerminating { + l.Info("Resource is already in TERMINATING state") + // Delete in progress, continue with the reconcile logic + return false, nil + } + + if acd.Status.LifecycleState == database.AutonomousContainerDatabaseLifecycleStateTerminated { + // 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 { + return false, err + } + return true, nil + } + + 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 { + return false, err + } + return true, nil + } + + if acd.Spec.Action != dbv1alpha1.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 + if err := r.KubeClient.Update(context.TODO(), acd); err != nil { + return false, err + } + // Exit the reconcile since we have updated the spec + return true, nil + } + + // 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 *AutonomousContainerDatabaseReconciler) validateFinalizer(acd *dbv1alpha1.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 { + return err + } + } else if !*acd.Spec.HardLink && controllerutil.ContainsFinalizer(acd, dbv1alpha1.ACDFinalizer) { + if err := k8s.RemoveFinalizerAndPatch(r.KubeClient, acd, dbv1alpha1.ACDFinalizer); err != nil { + return err + } + } + } + + return nil +} + +func (r *AutonomousContainerDatabaseReconciler) validateOperation( + logger logr.Logger, + acd *dbv1alpha1.AutonomousContainerDatabase, + ociACD *dbv1alpha1.AutonomousContainerDatabase) (exitReconcile bool, result ctrl.Result, err error) { + + l := logger.WithName("validateOperation") + + lastSpec, err := acd.GetLastSuccessfulSpec() + if err != nil { + return false, emptyResult, err + } + + // If lastSucSpec is nil, then it's CREATE or BIND opertaion + if lastSpec == nil { + if acd.Spec.AutonomousContainerDatabaseOCID == nil { + l.Info("Create operation") + + err := r.createACD(logger, acd) + if err != nil { + return false, emptyResult, err + } + + // Update the ACD OCID + if err := r.updateCR(acd); err != nil { + return false, emptyResult, err + } + + l.Info("AutonomousContainerDatabaseOCID updated; exit reconcile") + return true, emptyResult, nil + } else { + l.Info("Bind operation") + + _, err := r.getACD(logger, acd) + if err != nil { + return false, emptyResult, err + } + + if err := r.updateCR(acd); err != nil { + return false, emptyResult, err + } + + l.Info("spec updated; exit reconcile") + return false, emptyResult, nil + } + } + + // If it's not CREATE or BIND opertaion, then UPDATE or SYNC + // Compare with the lastSucSpec.details. If the details are different, it means that the user updates the spec. + lastDifACD := acd.DeepCopy() + + lastDetailsChanged, err := lastDifACD.RemoveUnchangedSpec(*lastSpec) + if err != nil { + return false, emptyResult, err + } + + if lastDetailsChanged { + l.Info("Update operation") + + // Double check if the user input spec is actually different from the spec in OCI. If so, then update the resource. + + difACD := acd.DeepCopy() + + ociDetailsChanged, err := difACD.RemoveUnchangedSpec(ociACD.Spec) + if err != nil { + return false, emptyResult, err + } + + if ociDetailsChanged { + ociReqSent, specChanged, err := r.updateACD(logger, acd, difACD) + if err != nil { + return false, emptyResult, err + } + + // Requeue the k8s request if an OCI request is sent, since OCI can only process one request at a time. + if ociReqSent { + if specChanged { + if err := r.KubeClient.Update(context.TODO(), acd); err != nil { + return false, emptyResult, err + } + + l.Info("spec updated; exit reconcile") + return false, emptyResult, nil + + } else { + l.Info("reconcile queued") + return true, requeueResult, nil + } + } + } + + // Stop the update and patch the lastSpec when the current ACD matches the oci ACD. + if err := r.patchLastSuccessfulSpec(acd); err != nil { + return false, emptyResult, err + } + + return false, emptyResult, nil + + } else { + l.Info("No operation specified; sync the resource") + + // The user doesn't change the spec and the controller should pull the spec from the OCI. + specChanged, err := r.getACD(logger, acd) + if err != nil { + return false, emptyResult, err + } + + if specChanged { + l.Info("The local spec doesn't match the oci's spec; update the CR") + if err := r.updateCR(acd); err != nil { + return false, emptyResult, err + } + + return true, emptyResult, nil + } + return false, emptyResult, nil + } +} + +func (r *AutonomousContainerDatabaseReconciler) updateCR(acd *dbv1alpha1.AutonomousContainerDatabase) error { + // Update the lastSucSpec + if err := acd.UpdateLastSuccessfulSpec(); err != nil { + return err + } + + if err := r.KubeClient.Update(context.TODO(), acd); err != nil { + return err + } + return nil +} + +func (r *AutonomousContainerDatabaseReconciler) patchLastSuccessfulSpec(acd *dbv1alpha1.AutonomousContainerDatabase) error { + specBytes, err := json.Marshal(acd.Spec) + if err != nil { + return err + } + + anns := map[string]string{ + dbv1alpha1.LastSuccessfulSpec: string(specBytes), + } + + annotations.PatchAnnotations(r.KubeClient, acd, anns) + + return nil +} + +func (r *AutonomousContainerDatabaseReconciler) createACD(logger logr.Logger, acd *dbv1alpha1.AutonomousContainerDatabase) error { + logger.WithName("createACD").Info("Sending CreateAutonomousContainerDatabase request to OCI") + + resp, err := r.dbService.CreateAutonomousContainerDatabase(acd) + if err != nil { + return err + } + + acd.UpdateFromOCIACD(resp.AutonomousContainerDatabase) + + return nil +} + +func (r *AutonomousContainerDatabaseReconciler) getACD(logger logr.Logger, acd *dbv1alpha1.AutonomousContainerDatabase) (bool, error) { + if acd == nil { + return false, errors.New("AutonomousContainerDatabase OCID is missing") + } + + logger.WithName("getACD").Info("Sending GetAutonomousContainerDatabase request to OCI") + + // Get the information from OCI + resp, err := r.dbService.GetAutonomousContainerDatabase(*acd.Spec.AutonomousContainerDatabaseOCID) + if err != nil { + return false, err + } + + specChanged := acd.UpdateFromOCIACD(resp.AutonomousContainerDatabase) + + return specChanged, nil +} + +// updateACD returns true if an OCI request is sent. +// 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) { + + validations := []func(logr.Logger, *dbv1alpha1.AutonomousContainerDatabase, *dbv1alpha1.AutonomousContainerDatabase) (bool, bool, error){ + r.validateGeneralFields, + r.validateDesiredLifecycleState, + } + + for _, op := range validations { + ociReqSent, specChanged, err := op(logger, acd, difACD) + if err != nil { + return false, false, err + } + + if ociReqSent { + return true, specChanged, nil + } + } + + return false, false, nil +} + +func (r *AutonomousContainerDatabaseReconciler) validateGeneralFields( + logger logr.Logger, + acd *dbv1alpha1.AutonomousContainerDatabase, + difACD *dbv1alpha1.AutonomousContainerDatabase) (sent bool, requeue bool, err error) { + + if difACD.Spec.DisplayName == nil && + difACD.Spec.PatchModel == "" && + difACD.Spec.FreeformTags == nil { + return false, false, nil + } + + logger.WithName("validateGeneralFields").Info("Sending UpdateAutonomousDatabase request to OCI") + + resp, err := r.dbService.UpdateAutonomousContainerDatabase(*acd.Spec.AutonomousContainerDatabaseOCID, difACD) + if err != nil { + return false, false, err + } + + acd.UpdateStatusFromOCIACD(resp.AutonomousContainerDatabase) + + return true, false, nil +} + +func (r *AutonomousContainerDatabaseReconciler) validateDesiredLifecycleState( + logger logr.Logger, + acd *dbv1alpha1.AutonomousContainerDatabase, + difACD *dbv1alpha1.AutonomousContainerDatabase) (sent bool, specChanged bool, err error) { + + if difACD.Spec.Action == dbv1alpha1.AcdActionBlank { + return false, false, nil + } + + l := logger.WithName("validateDesiredLifecycleState") + + switch difACD.Spec.Action { + case dbv1alpha1.AcdActionRestart: + l.Info("Sending RestartAutonomousContainerDatabase request to OCI") + + resp, err := r.dbService.RestartAutonomousContainerDatabase(*acd.Spec.AutonomousContainerDatabaseOCID) + if err != nil { + return false, false, err + } + + acd.Status.LifecycleState = resp.LifecycleState + case dbv1alpha1.AcdActionTerminate: + l.Info("Sending TerminateAutonomousContainerDatabase request to OCI") + + _, err := r.dbService.TerminateAutonomousContainerDatabase(*acd.Spec.AutonomousContainerDatabaseOCID) + if err != nil { + return false, false, err + } + + acd.Status.LifecycleState = database.AutonomousContainerDatabaseLifecycleStateTerminating + default: + return false, false, errors.New("unknown lifecycleState") + } + + acd.Spec.Action = dbv1alpha1.AcdActionBlank + + return true, true, nil +} diff --git a/controllers/database/autonomousdatabase_controller.go b/controllers/database/autonomousdatabase_controller.go index 142e78c6..bf56bfe0 100644 --- a/controllers/database/autonomousdatabase_controller.go +++ b/controllers/database/autonomousdatabase_controller.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2022 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -40,572 +40,1354 @@ package controllers import ( "context" + "encoding/json" + "errors" "fmt" "reflect" + "regexp" + "strings" + "time" "github.com/go-logr/logr" - "github.com/oracle/oci-go-sdk/v51/database" - "github.com/oracle/oci-go-sdk/v51/secrets" - "github.com/oracle/oci-go-sdk/v51/workrequests" + "github.com/oracle/oci-go-sdk/v65/common" + "github.com/oracle/oci-go-sdk/v65/database" 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/util/retry" + + "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/handler" "sigs.k8s.io/controller-runtime/pkg/predicate" + "sigs.k8s.io/controller-runtime/pkg/reconcile" dbv1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" - adbutil "github.com/oracle/oracle-database-operator/commons/autonomousdatabase" - "github.com/oracle/oracle-database-operator/commons/finalizer" + "github.com/oracle/oracle-database-operator/commons/annotations" + "github.com/oracle/oracle-database-operator/commons/k8s" "github.com/oracle/oracle-database-operator/commons/oci" ) -// AutonomousDatabaseReconciler reconciles a AutonomousDatabase object +var requeueResult ctrl.Result = ctrl.Result{Requeue: true, RequeueAfter: 15 * time.Second} +var emptyResult ctrl.Result = ctrl.Result{} + +// *AutonomousDatabaseReconciler reconciles a AutonomousDatabase object type AutonomousDatabaseReconciler struct { KubeClient client.Client Log logr.Logger Scheme *runtime.Scheme + Recorder record.EventRecorder - currentLogger logr.Logger + dbService oci.DatabaseService } // SetupWithManager function func (r *AutonomousDatabaseReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&dbv1alpha1.AutonomousDatabase{}). - WithEventFilter(r.eventFilterPredicate()). + Watches( + &dbv1alpha1.AutonomousDatabaseBackup{}, + handler.EnqueueRequestsFromMapFunc(r.enqueueMapFn), + ). + Watches( + &dbv1alpha1.AutonomousDatabaseRestore{}, + 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())) + + 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 { + return predicate.Funcs{ + CreateFunc: func(e event.CreateEvent) bool { + _, backupOk := e.Object.(*dbv1alpha1.AutonomousDatabaseBackup) + _, restoreOk := e.Object.(*dbv1alpha1.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) + if backupOk { + oldBackup := e.ObjectOld.(*dbv1alpha1.AutonomousDatabaseBackup) + return oldBackup.Status.LifecycleState == "" && desiredBackup.Status.LifecycleState != "" + } + + desiredRestore, restoreOk := e.ObjectNew.(*dbv1alpha1.AutonomousDatabaseRestore) + if restoreOk { + oldRestore := e.ObjectOld.(*dbv1alpha1.AutonomousDatabaseRestore) + return oldRestore.Status.Status == "" && desiredRestore.Status.Status != "" + } + + // Enqueue if the event is not from Backup or Restore + return true + }, + } +} func (r *AutonomousDatabaseReconciler) eventFilterPredicate() predicate.Predicate { - pred := predicate.Funcs{ + return predicate.Funcs{ UpdateFunc: func(e event.UpdateEvent) bool { - oldADB := e.ObjectOld.DeepCopyObject().(*dbv1alpha1.AutonomousDatabase) - newADB := e.ObjectNew.DeepCopyObject().(*dbv1alpha1.AutonomousDatabase) - - // Reconciliation should NOT happen if the lastSuccessfulSpec annotation or status.state changes. - oldSucSpec := oldADB.GetAnnotations()[dbv1alpha1.LastSuccessfulSpec] - newSucSpec := newADB.GetAnnotations()[dbv1alpha1.LastSuccessfulSpec] - - lastSucSpecChanged := oldSucSpec != newSucSpec - stateChanged := oldADB.Status.LifecycleState != newADB.Status.LifecycleState - if lastSucSpecChanged || stateChanged { - // Don't enqueue request - return false + // source object can be AutonomousDatabase, AutonomousDatabaseBackup, or AutonomousDatabaseRestore + desiredADB, adbOk := e.ObjectNew.(*dbv1alpha1.AutonomousDatabase) + 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 (!specChanged && statusChanged) || lastSucSpecChanged || + (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. ADB_FINALIZER changes + return false + } + + return true } - // Enqueue request return true }, DeleteFunc: func(e event.DeleteEvent) bool { - // Do not trigger reconciliation when the real object is deleted from the cluster. - return false + // Do not trigger reconciliation when the object is deleted from the cluster. + _, adbOk := e.Object.(*dbv1alpha1.AutonomousDatabase) + return !adbOk }, } - - return pred } // +kubebuilder:rbac:groups=database.oracle.com,resources=autonomousdatabases,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups=database.oracle.com,resources=autonomousdatabases/status,verbs=update;patch +// +kubebuilder:rbac:groups=database.oracle.com,resources=autonomousdatabasebackups,verbs=get;list;watch;create;update;delete +// +kubebuilder:rbac:groups=database.oracle.com,resources=autonomousdatabaserestores,verbs=get;list;watch;create;update;delete +// +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 // 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) { - r.currentLogger = r.Log.WithValues("Namespaced/Name", req.NamespacedName) + logger := r.Log.WithValues("Namespace/Name", req.NamespacedName) + + var err error + var ociADB *dbv1alpha1.AutonomousDatabase // Get the autonomousdatabase instance from the cluster - adb := &dbv1alpha1.AutonomousDatabase{} - if err := r.KubeClient.Get(context.TODO(), req.NamespacedName, adb); err != nil { + desiredADB := &dbv1alpha1.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 ctrl.Result{}, err + 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 and work request client + * Get OCI database client ******************************************************************/ - authData := oci.APIKeyAuth{ - ConfigMapName: adb.Spec.OCIConfig.ConfigMapName, - SecretName: adb.Spec.OCIConfig.SecretName, - Namespace: adb.GetNamespace(), + if err := r.setupOCIClients(logger, desiredADB); err != nil { + logger.Error(err, "Fail to setup OCI clients") + + return r.manageError(logger.WithName("setupOCIClients"), desiredADB, err) } - provider, err := oci.GetOCIProvider(r.KubeClient, authData) + + 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/ + ******************************************************************/ + exitReconcile, err := r.validateCleanup(logger, desiredADB) if err != nil { - r.currentLogger.Error(err, "Fail to get OCI provider") + return r.manageError(logger.WithName("validateCleanup"), desiredADB, err) + } - // Change the status to UNAVAILABLE - adb.Status.LifecycleState = database.AutonomousDatabaseLifecycleStateUnavailable - if statusErr := adbutil.SetStatus(r.KubeClient, adb); statusErr != nil { - return ctrl.Result{}, statusErr - } - return ctrl.Result{}, nil + if exitReconcile { + return emptyResult, nil } - dbClient, err := database.NewDatabaseClientWithConfigurationProvider(provider) + /****************************************************************** + * Register/unregister the finalizer + ******************************************************************/ + exit, err := r.validateFinalizer(logger, desiredADB) if err != nil { - r.currentLogger.Error(err, "Fail to get OCI database client") + return r.manageError(logger.WithName("validateFinalizer"), desiredADB, err) + } - // Change the status to UNAVAILABLE - adb.Status.LifecycleState = database.AutonomousDatabaseLifecycleStateUnavailable - if statusErr := adbutil.SetStatus(r.KubeClient, adb); statusErr != nil { - return ctrl.Result{}, statusErr - } - return ctrl.Result{}, nil + if exit { + return emptyResult, nil } - secretClient, err := secrets.NewSecretsClientWithConfigurationProvider(provider) + /****************************************************************** + * Validate operations + ******************************************************************/ + modifiedADB := desiredADB.DeepCopy() // the ADB which stores the changes + exitReconcile, result, err := r.validateOperation(logger, modifiedADB, ociADB) if err != nil { - r.currentLogger.Error(err, "Fail to get OCI secret client") + return r.manageError(logger.WithName("validateOperation"), modifiedADB, err) + } + if exitReconcile { + return result, nil + } - // Change the status to UNAVAILABLE - adb.Status.LifecycleState = database.AutonomousDatabaseLifecycleStateUnavailable - if statusErr := adbutil.SetStatus(r.KubeClient, adb); statusErr != nil { - return ctrl.Result{}, statusErr - } - return ctrl.Result{}, nil + /***************************************************** + * Sync AutonomousDatabase Backups from OCI + *****************************************************/ + if err := r.syncBackupResources(logger, modifiedADB); err != nil { + return r.manageError(logger.WithName("syncBackupResources"), modifiedADB, err) } - workClient, err := workrequests.NewWorkRequestClientWithConfigurationProvider(provider) - if err != nil { - r.currentLogger.Error(err, "Fail to get OCI work request client") + /***************************************************** + * Validate Wallet + *****************************************************/ + if err := r.validateWallet(logger, modifiedADB); err != nil { + return r.manageError(logger.WithName("validateWallet"), modifiedADB, err) + } - // Change the status to UNAVAILABLE - adb.Status.LifecycleState = database.AutonomousDatabaseLifecycleStateUnavailable - if statusErr := adbutil.SetStatus(r.KubeClient, adb); statusErr != nil { - return ctrl.Result{}, statusErr + /****************************************************************** + * Update the resource if the spec has been changed. + * 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("updateSpec"), modifiedADB, err) } - return ctrl.Result{}, nil + return emptyResult, nil } - r.currentLogger.Info("OCI provider configured succesfully") + /****************************************************************** + * 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 dbv1alpha1.IsADBIntermediateState(modifiedADB.Status.LifecycleState) { + logger.WithName("IsADBIntermediateState").Info("LifecycleState is " + string(modifiedADB.Status.LifecycleState) + "; reconcile queued") + return requeueResult, nil + } /****************************************************************** - * Register/unregister finalizer - * 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/2021/05/14/using-finalizers-to-control-deletion/ + * Update the lastSucSpec, and then finish the reconcile. + * Requeue if the ADB is terminated, but the finalizer is not yet + * removed. ******************************************************************/ - if adb.ObjectMeta.DeletionTimestamp.IsZero() { - // The object is not being deleted - if *adb.Spec.HardLink && !finalizer.HasFinalizer(adb) { - finalizer.Register(r.KubeClient, adb) - r.currentLogger.Info("Finalizer registered successfully.") - } else if !*adb.Spec.HardLink && finalizer.HasFinalizer(adb) { - finalizer.Unregister(r.KubeClient, adb) - r.currentLogger.Info("Finalizer unregistered successfully.") - } + var requeue bool = false + if modifiedADB.GetDeletionTimestamp() != nil && + 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 + } + + if err := r.patchLastSuccessfulSpec(modifiedADB); err != nil { + return r.manageError(logger.WithName("patchLastSuccessfulSpec"), modifiedADB, err) + } + + if requeue { + logger.Info("Reconcile queued") + return requeueResult, nil + } else { - // The object is being deleted - if adb.Spec.Details.AutonomousDatabaseOCID == nil { - r.currentLogger.Info("Autonomous Database OCID is missing. Remove the resource only.") - } else if adb.Status.LifecycleState != database.AutonomousDatabaseLifecycleStateTerminating && - adb.Status.LifecycleState != database.AutonomousDatabaseLifecycleStateTerminated { - // Don't send terminate request if the database is terminating or already terminated - r.currentLogger.Info("Terminate Autonomous Database: " + *adb.Spec.Details.DbName) - if _, err := oci.DeleteAutonomousDatabase(dbClient, *adb.Spec.Details.AutonomousDatabaseOCID); err != nil { - r.currentLogger.Error(err, "Fail to terminate Autonomous Database") - - // Change the status to UNAVAILABLE - adb.Status.LifecycleState = database.AutonomousDatabaseLifecycleStateUnavailable - if statusErr := adbutil.SetStatus(r.KubeClient, adb); statusErr != nil { - return ctrl.Result{}, statusErr - } - } - } + logger.Info("AutonomousDatabase reconciles successfully") + return emptyResult, nil + } +} + +func (r *AutonomousDatabaseReconciler) setupOCIClients(logger logr.Logger, adb *dbv1alpha1.AutonomousDatabase) error { + var err error + + authData := oci.APIKeyAuth{ + ConfigMapName: adb.Spec.OCIConfig.ConfigMapName, + SecretName: adb.Spec.OCIConfig.SecretName, + Namespace: adb.GetNamespace(), + } - finalizer.Unregister(r.KubeClient, adb) - r.currentLogger.Info("Finalizer unregistered successfully.") - // Stop reconciliation as the item is being deleted - return ctrl.Result{}, nil + provider, err := oci.GetOCIProvider(r.KubeClient, authData) + if err != nil { + return err } - /****************************************************************** - * Determine which Database operations need to be executed by checking the changes to spec.details. - * There are three scenario: - * 1. provision operation. The AutonomousDatabaseOCID is missing, and the LastSucSpec annotation is missing. - * 2. bind operation. The AutonomousDatabaseOCID is provided, but the LastSucSpec annotation is missing. - * 3. update operation. Every changes other than the above two cases goes here. - * Afterwards, update the resource from the remote database in OCI. This step will be executed right after - * the above three cases during every reconcile. - /******************************************************************/ - lastSucSpec, err := adb.GetLastSuccessfulSpec() + r.dbService, err = oci.NewDatabaseService(logger, r.KubeClient, provider) if err != nil { - return ctrl.Result{}, err + return err } - if lastSucSpec == nil || !reflect.DeepEqual(lastSucSpec.Details, adb.Spec.Details) { - // spec.details changes - if adb.Spec.Details.AutonomousDatabaseOCID == nil && lastSucSpec == nil { - // If no AutonomousDatabaseOCID specified, create a database - // Update from yaml file might not have an AutonomousDatabaseOCID. Don't create a database if it already has last successful spec. - r.currentLogger.Info("AutonomousDatabase provisioning") + return nil +} - resp, err := oci.CreateAutonomousDatabase(r.currentLogger, r.KubeClient, dbClient, secretClient, adb) +func (r *AutonomousDatabaseReconciler) manageError(logger logr.Logger, adb *dbv1alpha1.AutonomousDatabase, err error) (ctrl.Result, error) { + l := logger.WithName("manageError") + if adb.Status.LifecycleState == "" { + // First time entering reconcile + updateCondition(adb, err) - if err != nil { - r.currentLogger.Error(err, "Fail to provision and get Autonomous Database OCID") + l.Error(err, "CreateFailed") - // Change the status to UNAVAILABLE - adb.Status.LifecycleState = database.AutonomousDatabaseLifecycleStateUnavailable - if statusErr := adbutil.SetStatus(r.KubeClient, adb); 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 + 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.Details.AutonomousDatabaseOCID = resp.AutonomousDatabase.Id + adb.Spec = ociADB.Spec - // Update status.state - adb.Status.LifecycleState = database.AutonomousDatabaseLifecycleStateUnavailable - if statusErr := adbutil.SetStatus(r.KubeClient, adb); statusErr != nil { - return ctrl.Result{}, statusErr + if err := r.KubeClient.Update(context.TODO(), adb); err != nil { + finalError = k8s.CombineErrors(finalError, err) } + } - if err := oci.WaitUntilWorkCompleted(r.currentLogger, workClient, resp.OpcWorkRequestId); err != nil { - r.currentLogger.Error(err, "Fail to watch the status of provision request. opcWorkRequestID = "+*resp.OpcWorkRequestId) + updateCondition(adb, err) - // Change the status to UNAVAILABLE - adb.Status.LifecycleState = database.AutonomousDatabaseLifecycleStateUnavailable - if statusErr := adbutil.SetStatus(r.KubeClient, adb); statusErr != nil { - return ctrl.Result{}, statusErr - } - } + l.Error(finalError, "UpdateFailed") - r.currentLogger.Info("AutonomousDatabase " + *adb.Spec.Details.DbName + " provisioned succesfully") + return emptyResult, nil + } +} - } else if adb.Spec.Details.AutonomousDatabaseOCID != nil && lastSucSpec == nil { - // Binding operation. We have the database ID but hasn't gotten complete infromation from OCI. - // The next step is to get AutonomousDatabse details from a remote instance. +const CONDITION_TYPE_COMPLETE = "Complete" +const CONDITION_REASON_COMPLETE = "ReconcileComplete" - adb, err = oci.GetAutonomousDatabaseResource(r.currentLogger, dbClient, adb) - if err != nil { - r.currentLogger.Error(err, "Fail to get Autonomous Database") +func updateCondition(adb *dbv1alpha1.AutonomousDatabase, err error) { + var condition metav1.Condition - // Change the status to UNAVAILABLE - adb.Status.LifecycleState = database.AutonomousDatabaseLifecycleStateUnavailable - if statusErr := adbutil.SetStatus(r.KubeClient, adb); statusErr != nil { - return ctrl.Result{}, statusErr - } - return ctrl.Result{}, nil + 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 { + condition = metav1.Condition{ + Type: CONDITION_TYPE_COMPLETE, + LastTransitionTime: metav1.Now(), + ObservedGeneration: adb.GetGeneration(), + Reason: CONDITION_REASON_COMPLETE, + Message: errMsg, + Status: metav1.ConditionTrue, + } + } + + if len(adb.Status.Conditions) > 0 { + meta.RemoveStatusCondition(&adb.Status.Conditions, condition.Type) + } + meta.SetStatusCondition(&adb.Status.Conditions, condition) +} + +func (r *AutonomousDatabaseReconciler) validateOperation( + logger logr.Logger, + adb *dbv1alpha1.AutonomousDatabase, + ociADB *dbv1alpha1.AutonomousDatabase) (exit bool, result ctrl.Result, err error) { + + lastSucSpec, err := adb.GetLastSuccessfulSpec() + if err != nil { + return false, emptyResult, err + } + + 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 } - } else { - // The object has successfully synced with the remote database at least once. - // Update the Autonomous Database in OCI. - // Change to the lifecycle state has the highest priority. - - // Get the Autonomous Database OCID from the last successful spec if not presented. - // This happens when a database reference is already created in the cluster. User updates the target CR (specifying metadata.name) but doesn't provide database OCID. - if adb.Spec.Details.AutonomousDatabaseOCID == nil { - adb.Spec.Details.AutonomousDatabaseOCID = lastSucSpec.Details.AutonomousDatabaseOCID + + // Update the ADB OCID + if err := r.updateCR(adb); err != nil { + return false, emptyResult, err } - // Start/Stop/Terminate - setStateResp, err := oci.SetAutonomousDatabaseLifecycleState(r.currentLogger, dbClient, adb) + l.Info("AutonomousDatabaseOCID updated; exit reconcile") + return true, emptyResult, nil + } else { + l.Info("Bind operation") + _, err := r.getADB(logger, adb) if err != nil { - r.currentLogger.Error(err, "Fail to set the Autonomous Database lifecycle state") + return false, emptyResult, err + } - // Change the status to UNAVAILABLE - adb.Status.LifecycleState = database.AutonomousDatabaseLifecycleStateUnavailable - if statusErr := adbutil.SetStatus(r.KubeClient, adb); statusErr != nil { - return ctrl.Result{}, statusErr - } - return ctrl.Result{}, nil + if err := r.updateCR(adb); err != nil { + return false, emptyResult, err } - if setStateResp != nil { - var lifecycleState database.AutonomousDatabaseLifecycleStateEnum - var opcWorkRequestID *string + l.Info("spec updated; exit reconcile") + return true, emptyResult, nil + } + } + + // 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() - if startResp, isStartResponse := setStateResp.(database.StartAutonomousDatabaseResponse); isStartResponse { - lifecycleState = startResp.AutonomousDatabase.LifecycleState - opcWorkRequestID = startResp.OpcWorkRequestId + lastDetailsChanged, err := lastDifADB.RemoveUnchangedDetails(*lastSucSpec) + if err != nil { + return false, emptyResult, err + } - } else if stopResp, isStopResponse := setStateResp.(database.StopAutonomousDatabaseResponse); isStopResponse { - lifecycleState = stopResp.AutonomousDatabase.LifecycleState - opcWorkRequestID = stopResp.OpcWorkRequestId + 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. - } else if deleteResp, isDeleteResponse := setStateResp.(database.DeleteAutonomousDatabaseResponse); isDeleteResponse { - // Special case. Delete response doen't contain lifecycle State - lifecycleState = database.AutonomousDatabaseLifecycleStateTerminating - opcWorkRequestID = deleteResp.OpcWorkRequestId + l.Info("Update operation") - } else { - r.currentLogger.Error(err, "Unknown response type") + exit, err := r.updateADB(logger, adb) + if err != nil { + return false, emptyResult, err + } - // Change the status to UNAVAILABLE - adb.Status.LifecycleState = database.AutonomousDatabaseLifecycleStateUnavailable - if statusErr := adbutil.SetStatus(r.KubeClient, adb); statusErr != nil { - return ctrl.Result{}, statusErr - } - return ctrl.Result{}, nil - } + return exit, emptyResult, nil - // Update status.state - adb.Status.LifecycleState = lifecycleState - if statusErr := adbutil.SetStatus(r.KubeClient, adb); statusErr != nil { - return ctrl.Result{}, statusErr - } + } else { + l.Info("No operation specified; sync the resource") - if err := oci.WaitUntilWorkCompleted(r.currentLogger, workClient, opcWorkRequestID); err != nil { - r.currentLogger.Error(err, "Fail to watch the status of work request. opcWorkRequestID = "+*opcWorkRequestID) + // 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 { + return false, emptyResult, err + } - // Change the status to UNAVAILABLE - adb.Status.LifecycleState = database.AutonomousDatabaseLifecycleStateUnavailable - if statusErr := adbutil.SetStatus(r.KubeClient, adb); statusErr != nil { - return ctrl.Result{}, statusErr - } - } - r.currentLogger.Info(fmt.Sprintf("Set AutonomousDatabase %s lifecycle state to %s successfully\n", - *adb.Spec.Details.DbName, - adb.Spec.Details.LifecycleState)) + 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 - // Update the database in OCI from the local resource. - // The local resource will be synchronized again later. - updateGenPassResp, err := oci.UpdateGeneralAndPasswordAttributes(r.currentLogger, r.KubeClient, dbClient, secretClient, adb) - if err != nil { - r.currentLogger.Error(err, "Fail to update Autonomous Database") + if err := r.updateCR(adb); err != nil { + return false, emptyResult, err + } - // Change the status to UNAVAILABLE - adb.Status.LifecycleState = database.AutonomousDatabaseLifecycleStateUnavailable - if statusErr := adbutil.SetStatus(r.KubeClient, adb); 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 + return true, emptyResult, nil + } + return false, emptyResult, nil + } +} + +func (r *AutonomousDatabaseReconciler) validateCleanup(logger logr.Logger, adb *dbv1alpha1.AutonomousDatabase) (exitReconcile bool, err error) { + l := logger.WithName("validateCleanup") + + isADBToBeDeleted := adb.GetDeletionTimestamp() != nil + + if !isADBToBeDeleted { + return false, nil + } + + if controllerutil.ContainsFinalizer(adb, dbv1alpha1.ADB_FINALIZER) { + if adb.Status.LifecycleState == database.AutonomousDatabaseLifecycleStateTerminating { + // Delete in progress, continue with the reconcile logic + return false, nil + } + + 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, dbv1alpha1.ADB_FINALIZER); err != nil { + return false, err } + return true, nil + } - if updateGenPassResp.OpcWorkRequestId != nil { - // Update status.state - adb.Status.LifecycleState = updateGenPassResp.AutonomousDatabase.LifecycleState - if statusErr := adbutil.SetStatus(r.KubeClient, adb); statusErr != nil { - return ctrl.Result{}, statusErr - } + 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 { + return false, err + } + return true, nil + } - if err := oci.WaitUntilWorkCompleted(r.currentLogger, workClient, updateGenPassResp.OpcWorkRequestId); err != nil { - r.currentLogger.Error(err, "Fail to watch the status of work request. opcWorkRequestID = "+*updateGenPassResp.OpcWorkRequestId) + 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 + } - // Change the status to UNAVAILABLE - adb.Status.LifecycleState = database.AutonomousDatabaseLifecycleStateUnavailable - if statusErr := adbutil.SetStatus(r.KubeClient, adb); statusErr != nil { - return ctrl.Result{}, statusErr - } - } - r.currentLogger.Info("Update AutonomousDatabase " + *adb.Spec.Details.DbName + " succesfully") + // 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 *dbv1alpha1.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) { + l.Info("Finalizer added") + if err := k8s.AddFinalizerAndPatch(r.KubeClient, adb, dbv1alpha1.ADB_FINALIZER); err != nil { + return false, err } - scaleResp, err := oci.UpdateScaleAttributes(r.currentLogger, r.KubeClient, dbClient, adb) - if err != nil { - r.currentLogger.Error(err, "Fail to update Autonomous Database") + finalizerChanged = true - // Change the status to UNAVAILABLE - adb.Status.LifecycleState = database.AutonomousDatabaseLifecycleStateUnavailable - if statusErr := adbutil.SetStatus(r.KubeClient, adb); 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 + } else if !*adb.Spec.HardLink && controllerutil.ContainsFinalizer(adb, dbv1alpha1.ADB_FINALIZER) { + l.Info("Finalizer removed") + + if err := k8s.RemoveFinalizerAndPatch(r.KubeClient, adb, dbv1alpha1.ADB_FINALIZER); err != nil { + return false, err } - if scaleResp.OpcWorkRequestId != nil { - // Update status.state - adb.Status.LifecycleState = scaleResp.AutonomousDatabase.LifecycleState - if statusErr := adbutil.SetStatus(r.KubeClient, adb); statusErr != nil { - return ctrl.Result{}, statusErr - } + finalizerChanged = true + } + } - if err := oci.WaitUntilWorkCompleted(r.currentLogger, workClient, scaleResp.OpcWorkRequestId); err != nil { - r.currentLogger.Error(err, "Fail to watch the status of work request. opcWorkRequestID = "+*scaleResp.OpcWorkRequestId) + // 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) { + l.Info("Finalizer changed during an intermediate state, exit the reconcile") + return true, nil + } - // Change the status to UNAVAILABLE - adb.Status.LifecycleState = database.AutonomousDatabaseLifecycleStateUnavailable - if statusErr := adbutil.SetStatus(r.KubeClient, adb); statusErr != nil { - return ctrl.Result{}, statusErr - } - } - r.currentLogger.Info("Scale AutonomousDatabase " + *adb.Spec.Details.DbName + " succesfully") + return false, nil +} + +// updateCR updates the lastSucSpec and the CR +func (r *AutonomousDatabaseReconciler) updateCR(adb *dbv1alpha1.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 *dbv1alpha1.AutonomousDatabase) error { + copyADB := adb.DeepCopy() + + specBytes, err := json.Marshal(adb.Spec) + if err != nil { + return err + } + + anns := map[string]string{ + dbv1alpha1.LastSuccessfulSpec: string(specBytes), + } + + annotations.PatchAnnotations(r.KubeClient, adb, anns) + + adb.Spec = copyADB.Spec + adb.Status = copyADB.Status + + return nil +} + +func (r *AutonomousDatabaseReconciler) createADB(logger logr.Logger, adb *dbv1alpha1.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 + + 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 *dbv1alpha1.AutonomousDatabase) (bool, error) { + if adb == nil { + return false, errors.New("AutonomousDatabase OCID is missing") + } + + l := logger.WithName("getADB") + + // Get the information from OCI + l.Info("Sending GetAutonomousDatabase request to OCI") + resp, err := r.dbService.GetAutonomousDatabase(*adb.Spec.Details.AutonomousDatabaseOCID) + if err != nil { + return false, err + } + + specChanged := adb.UpdateFromOCIADB(resp.AutonomousDatabase) + + return specChanged, nil +} + +// updateADB returns true if an OCI request is sent. +// 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) { + + l := logger.WithName("updateADB") + + // 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) + if err != nil { + return false, err + } + + adb.Status.LifecycleState = ociADB.Status.LifecycleState + + // Start update + difADB := adb.DeepCopy() + + ociDetailsChanged, 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 } - oneWayTLSResp, err := oci.UpdateOneWayTLSAttribute(r.currentLogger, r.KubeClient, dbClient, adb) + 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 err != nil { + return false, err + } + if sent { + return exit, nil + } + + validations := []func(logr.Logger, *dbv1alpha1.AutonomousDatabase, *dbv1alpha1.AutonomousDatabase, *dbv1alpha1.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 { - r.currentLogger.Error(err, "Fail to update Autonomous Database") + return false, err + } - // Change the status to UNAVAILABLE - adb.Status.LifecycleState = database.AutonomousDatabaseLifecycleStateUnavailable - if statusErr := adbutil.SetStatus(r.KubeClient, adb); 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 + if sent { + return false, nil } + } + } - if oneWayTLSResp.OpcWorkRequestId != nil { - // Update status.state - adb.Status.LifecycleState = oneWayTLSResp.AutonomousDatabase.LifecycleState - if statusErr := adbutil.SetStatus(r.KubeClient, adb); statusErr != nil { - return ctrl.Result{}, statusErr - } + return false, nil +} + +func (r *AutonomousDatabaseReconciler) validateGeneralFields( + logger logr.Logger, + adb *dbv1alpha1.AutonomousDatabase, + difADB *dbv1alpha1.AutonomousDatabase, + ociADB *dbv1alpha1.AutonomousDatabase) (sent 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 + } + + if ociADB.Status.LifecycleState != database.AutonomousDatabaseLifecycleStateAvailable { + 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 + } + + adb.UpdateFromOCIADB(resp.AutonomousDatabase) + + return true, nil +} + +// 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) { + + 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") + resp, err := r.dbService.UpdateAutonomousDatabaseAdminPassword(*adb.Spec.Details.AutonomousDatabaseOCID, difADB) + if err != nil { + 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 + + return true, nil +} + +func (r *AutonomousDatabaseReconciler) validateDbWorkload( + logger logr.Logger, + adb *dbv1alpha1.AutonomousDatabase, + difADB *dbv1alpha1.AutonomousDatabase, + ociADB *dbv1alpha1.AutonomousDatabase) (sent 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") + resp, err := r.dbService.UpdateAutonomousDatabaseDBWorkload(*adb.Spec.Details.AutonomousDatabaseOCID, difADB) + if err != nil { + return false, err + } + + adb.UpdateFromOCIADB(resp.AutonomousDatabase) + + return true, nil +} + +func (r *AutonomousDatabaseReconciler) validateLicenseModel( + logger logr.Logger, + adb *dbv1alpha1.AutonomousDatabase, + difADB *dbv1alpha1.AutonomousDatabase, + ociADB *dbv1alpha1.AutonomousDatabase) (sent 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") + resp, err := r.dbService.UpdateAutonomousDatabaseLicenseModel(*adb.Spec.Details.AutonomousDatabaseOCID, difADB) + if err != nil { + return false, err + } + + adb.UpdateFromOCIADB(resp.AutonomousDatabase) + + return true, nil +} + +func (r *AutonomousDatabaseReconciler) validateScalingFields( + logger logr.Logger, + adb *dbv1alpha1.AutonomousDatabase, + difADB *dbv1alpha1.AutonomousDatabase, + ociADB *dbv1alpha1.AutonomousDatabase) (sent bool, err error) { + + if difADB.Spec.Details.DataStorageSizeInTBs == nil && + difADB.Spec.Details.CPUCoreCount == nil && + difADB.Spec.Details.IsAutoScalingEnabled == nil { + return false, nil + } + + if ociADB.Status.LifecycleState != database.AutonomousDatabaseLifecycleStateAvailable { + 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 + } + + adb.UpdateFromOCIADB(resp.AutonomousDatabase) + + return true, nil +} + +func (r *AutonomousDatabaseReconciler) validateDesiredLifecycleState( + logger logr.Logger, + adb *dbv1alpha1.AutonomousDatabase, + difADB *dbv1alpha1.AutonomousDatabase, + ociADB *dbv1alpha1.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 !dbv1alpha1.ValidADBTerminateState(adb.Status.LifecycleState) { + return false, false, nil + } + } else if dbv1alpha1.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") - if err := oci.WaitUntilWorkCompleted(r.currentLogger, workClient, oneWayTLSResp.OpcWorkRequestId); err != nil { - r.currentLogger.Error(err, "Fail to watch the status of work request. opcWorkRequestID = "+*oneWayTLSResp.OpcWorkRequestId) + resp, err := r.dbService.StopAutonomousDatabase(*adb.Spec.Details.AutonomousDatabaseOCID) + if err != nil { + return false, false, err + } + + 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 dbv1alpha1.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 +} + +// 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 *dbv1alpha1.AutonomousDatabase, + difADB *dbv1alpha1.AutonomousDatabase, + ociADB *dbv1alpha1.AutonomousDatabase) (sent 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 + } + + if ociADB.Status.LifecycleState != database.AutonomousDatabaseLifecycleStateAvailable { + return false, nil + } - // Change the status to UNAVAILABLE - adb.Status.LifecycleState = database.AutonomousDatabaseLifecycleStateUnavailable - if statusErr := adbutil.SetStatus(r.KubeClient, adb); statusErr != nil { - return ctrl.Result{}, statusErr + l := logger.WithName("validateGeneralNetworkAccess") + + if !*adb.Spec.Details.IsDedicated { + var lastAccessType = ociADB.Spec.Details.NetworkAccess.AccessType + var difAccessType = difADB.Spec.Details.NetworkAccess.AccessType + + if difAccessType != "" { + switch difAccessType { + case dbv1alpha1.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 err := r.setMTLSRequired(logger, adb); err != nil { + return false, err } + return true, nil } - r.currentLogger.Info("Update AutonomousDatabase " + *adb.Spec.Details.DbName + " 1-way TLS setting succesfully") - } - networkResp, err := oci.UpdateNetworkAttributes(r.currentLogger, r.KubeClient, dbClient, adb) - if err != nil { - r.currentLogger.Error(err, "Fail to update Autonomous Database") + if err := r.setNetworkAccessPublic(logger, ociADB.Spec.Details.NetworkAccess.AccessType, adb); err != nil { + return false, err + } + return true, nil + case dbv1alpha1.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 !*ociADB.Spec.Details.NetworkAccess.IsMTLSConnectionRequired { + if err := r.setMTLSRequired(logger, adb); err != nil { + return false, err + } + return true, nil + } - // Change the status to UNAVAILABLE - adb.Status.LifecycleState = database.AutonomousDatabaseLifecycleStateUnavailable - if statusErr := adbutil.SetStatus(r.KubeClient, adb); statusErr != nil { - return ctrl.Result{}, statusErr + if err := r.setNetworkAccessPublic(logger, ociADB.Spec.Details.NetworkAccess.AccessType, adb); err != nil { + return false, err + } + return true, nil } - // The reconciler should not requeue since the error returned from OCI during update will not be solved by requeue - } - if networkResp.OpcWorkRequestId != nil { - // Update status.state - adb.Status.LifecycleState = networkResp.AutonomousDatabase.LifecycleState - if statusErr := adbutil.SetStatus(r.KubeClient, adb); statusErr != nil { - return ctrl.Result{}, statusErr + sent, err := r.validateNetworkAccess(logger, adb, difADB, ociADB) + if err != nil { + return false, err + } + if sent { + return true, nil } - if err := oci.WaitUntilWorkCompleted(r.currentLogger, workClient, networkResp.OpcWorkRequestId); err != nil { - r.currentLogger.Error(err, "Fail to watch the status of work request. opcWorkRequestID = "+*networkResp.OpcWorkRequestId) + sent, err = r.validateMTLS(logger, adb, difADB, ociADB) + if err != nil { + return false, err + } + if sent { + return true, nil + } + case dbv1alpha1.NetworkAccessTypePrivate: + l.Info("Configuring network access type to PRIVATE") - // Change the status to UNAVAILABLE - adb.Status.LifecycleState = database.AutonomousDatabaseLifecycleStateUnavailable - if statusErr := adbutil.SetStatus(r.KubeClient, adb); statusErr != nil { - return ctrl.Result{}, statusErr - } + sent, err := r.validateNetworkAccess(logger, adb, difADB, ociADB) + 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 } - r.currentLogger.Info("Update AutonomousDatabase " + *adb.Spec.Details.DbName + " network settings succesfully") + } + } else { + // Access type doesn't change + sent, err := r.validateNetworkAccess(logger, adb, difADB, ociADB) + 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, ociADB) + if err != nil { + return false, err + } + if sent { + return true, nil + } } - // Get the information from OCI - updatedADB, err := oci.GetAutonomousDatabaseResource(r.currentLogger, dbClient, adb) + return false, nil +} + +// Set the mTLS to true but not changing the spec +func (r *AutonomousDatabaseReconciler) setMTLSRequired(logger logr.Logger, adb *dbv1alpha1.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 { - r.currentLogger.Error(err, "Fail to get Autonomous Database") + return err + } - // Change the status to UNAVAILABLE - adb.Status.LifecycleState = database.AutonomousDatabaseLifecycleStateUnavailable - if statusErr := adbutil.SetStatus(r.KubeClient, adb); statusErr != nil { - return ctrl.Result{}, statusErr - } - return ctrl.Result{}, nil + adb.UpdateFromOCIADB(resp.AutonomousDatabase) + + return nil +} + +func (r *AutonomousDatabaseReconciler) validateMTLS( + logger logr.Logger, + adb *dbv1alpha1.AutonomousDatabase, + difADB *dbv1alpha1.AutonomousDatabase, + ociADB *dbv1alpha1.AutonomousDatabase) (sent bool, err error) { + + if difADB.Spec.Details.NetworkAccess.IsMTLSConnectionRequired == nil { + return false, nil } - adb = updatedADB + l := logger.WithName("validateMTLS") - // Update local object and the status - if err := r.updateAutonomousDatabaseDetails(adb); err != nil { - // Change the status to UNAVAILABLE - adb.Status.LifecycleState = database.AutonomousDatabaseLifecycleStateUnavailable - if statusErr := adbutil.SetStatus(r.KubeClient, adb); statusErr != nil { - return ctrl.Result{}, statusErr - } - return ctrl.Result{}, err + 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 } - /***************************************************** - * Instance Wallet - *****************************************************/ - passwordSecretUpdate := (lastSucSpec == nil && adb.Spec.Details.Wallet.Password.K8sSecretName != nil) || - (lastSucSpec != nil && lastSucSpec.Details.Wallet.Password.K8sSecretName != adb.Spec.Details.Wallet.Password.K8sSecretName) - passwordOCIDUpdate := (lastSucSpec == nil && adb.Spec.Details.Wallet.Password.OCISecretOCID != nil) || - (lastSucSpec != nil && lastSucSpec.Details.Wallet.Password.OCISecretOCID != adb.Spec.Details.Wallet.Password.OCISecretOCID) - - if (passwordSecretUpdate || passwordOCIDUpdate) && adb.Status.LifecycleState == database.AutonomousDatabaseLifecycleStateAvailable { - if err := adbutil.CreateWalletSecret(r.currentLogger, r.KubeClient, dbClient, secretClient, adb); err != nil { - r.currentLogger.Error(err, "Fail to download Instance Wallet") - // Change the status to UNAVAILABLE - adb.Status.LifecycleState = database.AutonomousDatabaseLifecycleStateUnavailable - if statusErr := adbutil.SetStatus(r.KubeClient, adb); statusErr != nil { - return ctrl.Result{}, statusErr - } - return ctrl.Result{}, nil - } + adb.UpdateFromOCIADB(resp.AutonomousDatabase) + + return true, nil +} + +func (r *AutonomousDatabaseReconciler) setNetworkAccessPublic(logger logr.Logger, lastAcessType dbv1alpha1.NetworkAccessTypeEnum, adb *dbv1alpha1.AutonomousDatabase) error { + adb.Spec.Details.NetworkAccess.AccessType = dbv1alpha1.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 } - /***************************************************** - * Update last succesful spec - *****************************************************/ - if err := adb.UpdateLastSuccessfulSpec(r.KubeClient); err != nil { - return ctrl.Result{}, err + adb.UpdateFromOCIADB(resp.AutonomousDatabase) + + return nil +} + +func (r *AutonomousDatabaseReconciler) validateNetworkAccess( + logger logr.Logger, + adb *dbv1alpha1.AutonomousDatabase, + difADB *dbv1alpha1.AutonomousDatabase, + ociADB *dbv1alpha1.AutonomousDatabase) (sent 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 == dbv1alpha1.NetworkAccessTypePrivate && + difADB.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs == nil { + difADB.Spec.Details.NetworkAccess.PrivateEndpoint.NsgOCIDs = []string{} } - r.currentLogger.Info("AutonomousDatabase resoursce reconcile successfully") + resp, err := r.dbService.UpdateNetworkAccess(*adb.Spec.Details.AutonomousDatabaseOCID, difADB) + if err != nil { + return false, err + } + + adb.UpdateFromOCIADB(resp.AutonomousDatabase) - return ctrl.Result{}, nil + return true, nil } -// type patchValue struct { -// Op string `json:"op"` -// Path string `json:"path"` -// Value interface{} `json:"value"` -// } +func (r *AutonomousDatabaseReconciler) validateWallet(logger logr.Logger, adb *dbv1alpha1.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 { + return nil + } + + if adb.Status.LifecycleState == database.AutonomousDatabaseLifecycleStateProvisioning { + return nil + } -func (r *AutonomousDatabaseReconciler) updateAutonomousDatabaseDetails(adb *dbv1alpha1.AutonomousDatabase) error { - // Patch the AutonomousDatabase Kubernetes resource to avoid resource version conflicts. - // payload := []patchValue{{ - // Op: "replace", - // Path: "/spec/details", - // Value: adb.Spec.Details, - // }} - // payloadBytes, err := json.Marshal(payload) - // if err != nil { - // return err - // } + l := logger.WithName("validateWallet") - // patch := client.RawPatch(types.JSONPatchType, payloadBytes) - // if err := r.KubeClient.Patch(context.TODO(), adb, patch); err != nil { - // return err - // } + // lastSucSpec may be nil if this is the first time entering the reconciliation loop + var walletName string - if err := retry.RetryOnConflict(retry.DefaultRetry, func() error { - curADB := &dbv1alpha1.AutonomousDatabase{} + if adb.Spec.Details.Wallet.Name == nil { + walletName = adb.GetName() + "-instance-wallet" + } else { + walletName = *adb.Spec.Details.Wallet.Name + } - namespacedName := types.NamespacedName{ - Namespace: adb.GetNamespace(), - Name: adb.GetName(), + secret, err := k8s.FetchSecret(r.KubeClient, adb.GetNamespace(), walletName) + if err == nil { + val, ok := secret.Labels["app"] + if !ok || val != adb.Name { + // Overwrite if the fetched secret has a different label + l.Info("wallet existed but has a different label; skip the download") } + // No-op if Wallet is already downloaded + return nil + } else if !apiErrors.IsNotFound(err) { + return err + } + + resp, err := r.dbService.DownloadWallet(adb) + if err != nil { + return err + } + + data, err := oci.ExtractWallet(resp.Content) + if err != nil { + 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 { + return err + } + + l.Info(fmt.Sprintf("Wallet is stored in the Secret %s", walletName)) + + return nil +} + +// 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 { + l := logger.WithName("syncBackupResources") + + // Get the list of AutonomousDatabaseBackupOCID in the same namespace + backupList, err := k8s.FetchAutonomousDatabaseBackups(r.KubeClient, adb.Namespace) + if err != nil { + return err + } - if err := r.KubeClient.Get(context.TODO(), namespacedName, curADB); err != nil { - return err + curBackupNames := make(map[string]bool) + curBackupOCIDs := make(map[string]bool) + + for _, backup := range backupList.Items { + // mark the backup name that exists + curBackupNames[backup.Name] = true + + // mark the backup ocid that exists + if backup.Spec.AutonomousDatabaseBackupOCID != nil { + curBackupOCIDs[*backup.Spec.AutonomousDatabaseBackupOCID] = true } + } - curADB.Spec.Details = adb.Spec.Details - return r.KubeClient.Update(context.TODO(), curADB) - }); err != nil { + resp, err := r.dbService.ListAutonomousDatabaseBackups(*adb.Spec.Details.AutonomousDatabaseOCID) + if err != nil { return err } - // Update status - if statusErr := adbutil.SetStatus(r.KubeClient, adb); statusErr != nil { - return statusErr + for _, backupSummary := range resp.Items { + // Create the resource if the backup doesn't exist + if !r.ifBackupExists(backupSummary, curBackupOCIDs, backupList) { + validBackupName, err := r.getValidBackupName(*backupSummary.DisplayName, curBackupNames) + if err != nil { + return err + } + + if err := k8s.CreateAutonomousBackup(r.KubeClient, validBackupName, backupSummary, adb); err != nil { + return err + } + + // Add the used name and ocid + curBackupNames[validBackupName] = true + curBackupOCIDs[*backupSummary.AutonomousDatabaseId] = true + + l.Info("Create AutonomousDatabaseBackup " + validBackupName) + } } - r.currentLogger.Info("Update local resource AutonomousDatabase successfully") return nil } + +func (r *AutonomousDatabaseReconciler) getValidBackupName(displayName string, usedNames map[string]bool) (string, error) { + // Convert the displayName to lowercase, and replace spaces, commas, and colons with hyphens + baseName := strings.ToLower(displayName) + + re, err := regexp.Compile(`[^-a-zA-Z0-9]`) + if err != nil { + return "", err + } + + baseName = re.ReplaceAllString(baseName, "-") + + finalName := baseName + var i = 1 + _, ok := usedNames[finalName] + for ok { + finalName = fmt.Sprintf("%s-%d", baseName, i) + _, ok = usedNames[finalName] + i++ + } + + return finalName, nil +} + +func (r *AutonomousDatabaseReconciler) ifBackupExists(backupSummary database.AutonomousDatabaseBackupSummary, curBackupOCIDs map[string]bool, backupList *dbv1alpha1.AutonomousDatabaseBackupList) bool { + _, ok := curBackupOCIDs[*backupSummary.Id] + if ok { + return true + } + + // Special case: when a Backup is creating and hasn't updated the OCID, a duplicated Backup might be created by mistake. + // To handle this case, skip creating the AutonomousDatabaseBackup resource if the current backupSummary is with CREATING state, + // and there is another AutonomousBackup with the same displayName in the cluster is also at CREATING state. + if backupSummary.LifecycleState == database.AutonomousDatabaseBackupSummaryLifecycleStateCreating { + for _, backup := range backupList.Items { + if (backup.Spec.DisplayName != nil && *backup.Spec.DisplayName == *backupSummary.DisplayName) && + (backup.Status.LifecycleState == "" || + backup.Status.LifecycleState == database.AutonomousDatabaseBackupLifecycleStateCreating) { + return true + } + } + } + + return false +} diff --git a/controllers/database/autonomousdatabasebackup_controller.go b/controllers/database/autonomousdatabasebackup_controller.go new file mode 100644 index 00000000..7fac9e04 --- /dev/null +++ b/controllers/database/autonomousdatabasebackup_controller.go @@ -0,0 +1,267 @@ +/* +** 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" + "fmt" + + "github.com/go-logr/logr" + corev1 "k8s.io/api/core/v1" + apiErrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "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/predicate" + + dbv1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + "github.com/oracle/oracle-database-operator/commons/adb_family" + "github.com/oracle/oracle-database-operator/commons/oci" +) + +// AutonomousDatabaseBackupReconciler reconciles a AutonomousDatabaseBackup object +type AutonomousDatabaseBackupReconciler struct { + KubeClient client.Client + Log logr.Logger + Scheme *runtime.Scheme + Recorder record.EventRecorder + + dbService oci.DatabaseService +} + +// SetupWithManager sets up the controller with the Manager. +func (r *AutonomousDatabaseBackupReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&dbv1alpha1.AutonomousDatabaseBackup{}). + WithEventFilter(predicate.GenerationChangedPredicate{}). + WithOptions(controller.Options{MaxConcurrentReconciles: 100}). // ReconcileHandler is never invoked concurrently with the same object. + Complete(r) +} + +//+kubebuilder:rbac:groups=database.oracle.com,resources=autonomousdatabasebackups,verbs=get;list;watch;create;delete +//+kubebuilder:rbac:groups=database.oracle.com,resources=autonomousdatabasebackups/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=database.oracle.com,resources=autonomousdatabases,verbs=get;list + +func (r *AutonomousDatabaseBackupReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := r.Log.WithValues("Namespace/Name", req.NamespacedName) + + backup := &dbv1alpha1.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. + if apiErrors.IsNotFound(err) { + return emptyResult, nil + } + // Failed to get ADBBackup, so we don't need to update the status + return emptyResult, err + } + + /****************************************************************** + * Look up the owner AutonomousDatabase and set the ownerReference + * if the owner hasn't been set yet. + ******************************************************************/ + adbOCID, err := r.verifyTargetADB(backup) + if err != nil { + return r.manageError(backup, err) + } + + /****************************************************************** + * Get OCI database client + ******************************************************************/ + if err := r.setupOCIClients(backup); err != nil { + return r.manageError(backup, err) + } + + logger.Info("OCI clients configured succesfully") + + /****************************************************************** + * Get status from OCI AutonomousDatabaseBackup + ******************************************************************/ + if backup.Spec.AutonomousDatabaseBackupOCID != nil { + backupResp, err := r.dbService.GetAutonomousDatabaseBackup(*backup.Spec.AutonomousDatabaseBackupOCID) + if err != nil { + return r.manageError(backup, err) + } + + adbResp, err := r.dbService.GetAutonomousDatabase(*backupResp.AutonomousDatabaseId) + if err != nil { + return r.manageError(backup, err) + } + + backup.UpdateStatusFromOCIBackup(backupResp.AutonomousDatabaseBackup, adbResp.AutonomousDatabase) + } + + /****************************************************************** + * Requeue if the Backup is in an intermediate state + * 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) { + logger.WithName("IsIntermediateState").Info("Current lifecycleState is " + string(backup.Status.LifecycleState) + "; reconcile queued") + return requeueResult, nil + } + + /****************************************************************** + * If the spec.autonomousDatabaseBackupOCID is empty. + * Otherwise, bind to an exisiting backup. + ******************************************************************/ + if backup.Spec.AutonomousDatabaseBackupOCID == nil { + // Create a new backup + logger.Info("Sending CreateAutonomousDatabaseBackup request to OCI") + backupResp, err := r.dbService.CreateAutonomousDatabaseBackup(backup, adbOCID) + if err != nil { + return r.manageError(backup, err) + } + + // After the creation, update the status first + adbResp, err := r.dbService.GetAutonomousDatabase(*backupResp.AutonomousDatabaseId) + if err != nil { + return r.manageError(backup, err) + } + + backup.UpdateStatusFromOCIBackup(backupResp.AutonomousDatabaseBackup, adbResp.AutonomousDatabase) + if err := r.KubeClient.Status().Update(context.TODO(), backup); err != nil { + return r.manageError(backup, err) + } + + // Then update the OCID + backup.Spec.AutonomousDatabaseBackupOCID = backupResp.Id + backup.UpdateStatusFromOCIBackup(backupResp.AutonomousDatabaseBackup, adbResp.AutonomousDatabase) + + if err := r.KubeClient.Update(context.TODO(), backup); err != nil { + // Do no requeue otherwise it will create multiple backups + r.Recorder.Event(backup, corev1.EventTypeWarning, "ReconcileFailed", err.Error()) + logger.Error(err, "cannot update AutonomousDatabaseBackupOCID; stop reconcile", "AutonomousDatabaseBackupOCID", *backupResp.Id) + + return emptyResult, nil + } + + logger.Info("AutonomousDatabaseBackupOCID updated") + return emptyResult, nil + } + + /****************************************************************** + * Update the status and requeue if it's in an intermediate state + ******************************************************************/ + if err := r.KubeClient.Status().Update(context.TODO(), backup); err != nil { + return r.manageError(backup, err) + } + + if dbv1alpha1.IsBackupIntermediateState(backup.Status.LifecycleState) { + logger.WithName("IsIntermediateState").Info("Reconcile queued") + return requeueResult, nil + } + + logger.Info("AutonomousDatabaseBackup reconciles successfully") + + return emptyResult, nil +} + +// 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 { + logger := r.Log.WithName("set-owner-reference") + + controllerutil.SetOwnerReference(adb, backup, r.Scheme) + if err := r.KubeClient.Update(context.TODO(), backup); err != nil { + return err + } + logger.Info(fmt.Sprintf("Set the owner of AutonomousDatabaseBackup %s to AutonomousDatabase %s", backup.Name, adb.Name)) + + 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 *dbv1alpha1.AutonomousDatabaseBackup) (string, error) { + // Get the target ADB OCID and the ADB resource + 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 { + return "", err + } + } + + 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 + } + + return "", errors.New("cannot get the OCID of the targetADB") +} + +func (r *AutonomousDatabaseBackupReconciler) setupOCIClients(backup *dbv1alpha1.AutonomousDatabaseBackup) error { + var err error + + authData := oci.APIKeyAuth{ + ConfigMapName: backup.Spec.OCIConfig.ConfigMapName, + SecretName: backup.Spec.OCIConfig.SecretName, + Namespace: backup.GetNamespace(), + } + + provider, err := oci.GetOCIProvider(r.KubeClient, authData) + if err != nil { + return err + } + + r.dbService, err = oci.NewDatabaseService(r.Log, r.KubeClient, provider) + if err != nil { + return err + } + + return nil +} + +func (r *AutonomousDatabaseBackupReconciler) manageError(backup *dbv1alpha1.AutonomousDatabaseBackup, issue error) (ctrl.Result, error) { + // Send event + r.Recorder.Event(backup, corev1.EventTypeWarning, "ReconcileFailed", issue.Error()) + + return emptyResult, issue +} diff --git a/controllers/database/autonomousdatabaserestore_controller.go b/controllers/database/autonomousdatabaserestore_controller.go new file mode 100644 index 00000000..254731bb --- /dev/null +++ b/controllers/database/autonomousdatabaserestore_controller.go @@ -0,0 +1,282 @@ +/* +** 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" + "fmt" + + "github.com/go-logr/logr" + corev1 "k8s.io/api/core/v1" + apiErrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "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/controllerutil" + "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" + "github.com/oracle/oracle-database-operator/commons/k8s" + "github.com/oracle/oracle-database-operator/commons/oci" +) + +// AutonomousDatabaseRestoreReconciler reconciles a AutonomousDatabaseRestore object +type AutonomousDatabaseRestoreReconciler struct { + KubeClient client.Client + Log logr.Logger + Scheme *runtime.Scheme + Recorder record.EventRecorder + + dbService oci.DatabaseService + workService oci.WorkRequestService +} + +// SetupWithManager sets up the controller with the Manager. +func (r *AutonomousDatabaseRestoreReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&dbv1alpha1.AutonomousDatabaseRestore{}). + WithEventFilter(predicate.GenerationChangedPredicate{}). + Complete(r) +} + +//+kubebuilder:rbac:groups=database.oracle.com,resources=autonomousdatabaserestores,verbs=get;list;watch;create;delete +//+kubebuilder:rbac:groups=database.oracle.com,resources=autonomousdatabaserestores/status,verbs=get;update;patch +//+kubebuilder:rbac:groups=database.oracle.com,resources=autonomousdatabases,verbs=get;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. +// TODO(user): Modify the Reconcile function to compare the state specified by +// the AutonomousDatabaseRestore 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 *AutonomousDatabaseRestoreReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + logger := r.Log.WithValues("Namespace/Name", req.NamespacedName) + + restore := &dbv1alpha1.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. + if apiErrors.IsNotFound(err) { + return emptyResult, nil + } + // Failed to get ADBRestore, so we don't need to update the status + return emptyResult, err + } + + /****************************************************************** + * Look up the owner AutonomousDatabase and set the ownerReference + * if the owner hasn't been set yet. + ******************************************************************/ + adbOCID, err := r.verifyTargetADB(restore) + if err != nil { + return r.manageError(restore, err) + } + + /****************************************************************** + * Get OCI database client and work request client + ******************************************************************/ + if err := r.setupOCIClients(restore); err != nil { + return r.manageError(restore, err) + } + + logger.Info("OCI clients configured succesfully") + + /****************************************************************** + * Start the restore or update the status + ******************************************************************/ + if restore.Status.WorkRequestOCID == "" { + logger.Info("Start restoring the database") + // Extract the restoreTime from the spec + restoreTime, err := r.getRestoreSDKTime(restore) + if err != nil { + return r.manageError(restore, err) + } + + logger.Info("Sending RestoreAutonomousDatabase request to OCI") + adbResp, err := r.dbService.RestoreAutonomousDatabase(adbOCID, *restoreTime) + if err != nil { + return r.manageError(restore, err) + } + + // Update the restore status + workResp, err := r.workService.Get(*adbResp.OpcWorkRequestId) + if err != nil { + return r.manageError(restore, err) + } + + restore.UpdateStatus(adbResp.AutonomousDatabase, workResp) + if err := r.KubeClient.Status().Update(context.TODO(), restore); err != nil { + return r.manageError(restore, err) + } + + } else { + // Update the status + logger.Info("Update the status of the restore session") + adbResp, err := r.dbService.GetAutonomousDatabase(adbOCID) + if err != nil { + return r.manageError(restore, err) + } + + workResp, err := r.workService.Get(restore.Status.WorkRequestOCID) + if err != nil { + return r.manageError(restore, err) + } + + restore.UpdateStatus(adbResp.AutonomousDatabase, workResp) + if err := r.KubeClient.Status().Update(context.TODO(), restore); err != nil { + return r.manageError(restore, err) + } + } + + // Requeue if it's in intermediate state + if dbv1alpha1.IsRestoreIntermediateState(restore.Status.Status) { + logger.WithName("IsIntermediateState").Info("Current status is " + string(restore.Status.Status) + "; reconcile queued") + return requeueResult, nil + } + + logger.Info("AutonomousDatabaseRestore reconciles successfully") + + return emptyResult, nil +} + +func (r *AutonomousDatabaseRestoreReconciler) getRestoreSDKTime(restore *dbv1alpha1.AutonomousDatabaseRestore) (*common.SDKTime, error) { + if restore.Spec.Source.K8sADBBackup.Name != nil { // restore using backupName + backup := &dbv1alpha1.AutonomousDatabaseBackup{} + if err := k8s.FetchResource(r.KubeClient, restore.Namespace, *restore.Spec.Source.K8sADBBackup.Name, backup); err != nil { + return nil, err + } + + if backup.Status.TimeEnded == "" { + return nil, errors.New("broken backup: ended time is missing from the AutonomousDatabaseBackup " + backup.GetName()) + } + restoreTime, err := backup.GetTimeEnded() + if err != nil { + return nil, err + } + + return restoreTime, nil + + } else { // PIT restore + // The validation of the pitr.timestamp has been handled by the webhook, so the error return is ignored + restoreTime, _ := restore.GetPIT() + return restoreTime, nil + } +} + +// 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 { + logger := r.Log.WithName("set-owner-reference") + + controllerutil.SetOwnerReference(adb, restore, r.Scheme) + if err := r.KubeClient.Update(context.TODO(), restore); err != nil { + return err + } + logger.Info(fmt.Sprintf("Set the owner of AutonomousDatabaseRestore %s to AutonomousDatabase %s", restore.Name, adb.Name)) + + 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 *AutonomousDatabaseRestoreReconciler) verifyTargetADB(restore *dbv1alpha1.AutonomousDatabaseRestore) (string, error) { + // Get the target ADB OCID and the ADB resource + 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 { + return "", err + } + } + + 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 + } + + return "", errors.New("cannot get the OCID of the targetADB") +} + +func (r *AutonomousDatabaseRestoreReconciler) setupOCIClients(restore *dbv1alpha1.AutonomousDatabaseRestore) error { + var err error + + authData := oci.APIKeyAuth{ + ConfigMapName: restore.Spec.OCIConfig.ConfigMapName, + SecretName: restore.Spec.OCIConfig.SecretName, + Namespace: restore.GetNamespace(), + } + + provider, err := oci.GetOCIProvider(r.KubeClient, authData) + if err != nil { + return err + } + + r.dbService, err = oci.NewDatabaseService(r.Log, r.KubeClient, provider) + if err != nil { + return err + } + + r.workService, err = oci.NewWorkRequestService(r.Log, r.KubeClient, provider) + if err != nil { + return err + } + + return nil +} + +// 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) { + // Send event + r.Recorder.Event(restore, corev1.EventTypeWarning, "ReconcileFailed", issue.Error()) + + return emptyResult, issue +} diff --git a/controllers/database/cdb_controller.go b/controllers/database/cdb_controller.go new file mode 100644 index 00000000..5e6c0aca --- /dev/null +++ b/controllers/database/cdb_controller.go @@ -0,0 +1,991 @@ +/* +** 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" + + //"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/v1alpha1" + 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, + }, + } + }(), + }}, + + 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) + + // 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") + cdb.Status.Phase = cdbPhaseDelete + 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) + if err != nil { + log.Info("Could not remove finalizer", "err", err.Error()) + return err + } + + log.Info("Successfully removed CDB Resource") + return nil + } + } + + // Add finalizer for this CR + 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 +} + +/* +************************************************ + - 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 + } + + 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) + } + } +} + +/* +************************************************************* + - SetupWithManager sets up the controller with the Manager. + /************************************************************ +*/ +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/dataguardbroker_controller.go b/controllers/database/dataguardbroker_controller.go new file mode 100644 index 00000000..9faaefd2 --- /dev/null +++ b/controllers/database/dataguardbroker_controller.go @@ -0,0 +1,1199 @@ +/* +** 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/dbcssystem_controller.go b/controllers/database/dbcssystem_controller.go new file mode 100644 index 00000000..003be62a --- /dev/null +++ b/controllers/database/dbcssystem_controller.go @@ -0,0 +1,314 @@ +/* +** 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" + "reflect" + + databasev1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + 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/core" + "github.com/oracle/oci-go-sdk/v65/database" + "github.com/oracle/oci-go-sdk/v65/workrequests" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime" + "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/event" + "sigs.k8s.io/controller-runtime/pkg/log" + "sigs.k8s.io/controller-runtime/pkg/predicate" +) + +// DbcsSystemReconciler reconciles a DbcsSystem object +type DbcsSystemReconciler struct { + KubeClient client.Client + Scheme *runtime.Scheme + Logv1 logr.Logger + Logger logr.Logger + dbClient database.DatabaseClient + nwClient core.VirtualNetworkClient + wrClient workrequests.WorkRequestClient + 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/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 + +// 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 DbcsSystem 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 *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{} + + if err := r.KubeClient.Get(context.TODO(), req.NamespacedName, dbcsInst); err != nil { + if !errors.IsNotFound(err) { + return ctrl.Result{}, err + } + } + + // Create oci-go-sdk client + authData := oci.APIKeyAuth{ + 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 + } + + r.dbClient, err = database.NewDatabaseClientWithConfigurationProvider(provider) + + if err != nil { + return ctrl.Result{}, err + } + + r.nwClient, err = core.NewVirtualNetworkClientWithConfigurationProvider(provider) + if err != nil { + return ctrl.Result{}, err + } + + r.wrClient, err = workrequests.NewWorkRequestClientWithConfigurationProvider(provider) + r.Logger.Info("OCI provider configured succesfully") + + /* + Using Finalizer for object deletion + */ + + if dbcsInst.ObjectMeta.DeletionTimestamp.IsZero() { + // The object is not being deleted + if dbcsInst.Spec.HardLink && !finalizer.HasFinalizer(dbcsInst) { + finalizer.Register(r.KubeClient, dbcsInst) + r.Logger.Info("Finalizer registered successfully.") + } else if !dbcsInst.Spec.HardLink && finalizer.HasFinalizer(dbcsInst) { + finalizer.Unregister(r.KubeClient, dbcsInst) + r.Logger.Info("Finalizer unregistered successfully.") + } + } 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 { + 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 + } + // The reconciler should not requeue since the error returned from OCI during update will not be solved by requeue + return ctrl.Result{}, nil + } + finalizer.Unregister(r.KubeClient, dbcsInst) + r.Logger.Info("Finalizer unregistered successfully.") + // Stop reconciliation as the item is being deleted + return ctrl.Result{}, nil + } + + /* + Determine whether it's a provision or bind operation + */ + lastSucSpec, err := dbcsInst.GetLastSuccessfulSpec() + 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") + + // 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 + } + // 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 + } + 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 + } + assignDBCSID(dbcsInst, dbcsID) + } 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 { + 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 + } + + dbcsInstId := *dbcsInst.Spec.Id + 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 { + return ctrl.Result{}, statusErr + } + return ctrl.Result{}, err + } + + r.Logger.Info("Sync information from remote DbcsSystem System successfully") + + dbcsInstId = *dbcsInst.Spec.Id + if err := dbcsInst.UpdateLastSuccessfulSpec(r.KubeClient); err != nil { + return ctrl.Result{}, err + } + assignDBCSID(dbcsInst, dbcsInstId) + } else { + if dbcsInst.Spec.Id == nil { + dbcsInst.Spec.Id = lastSucSpec.Id + } + + 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") + + // 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 + } + // 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 the Wallet Secret when the secret name is given + //r.updateWalletSecret(dbcs) + + // Update the last succesful spec + 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, dbcsInstId) + if statusErr := dbcsv1.SetLifecycleState(r.KubeClient, r.dbClient, dbcsInst, databasev1alpha1.Available, r.nwClient, r.wrClient); statusErr != nil { + return ctrl.Result{}, statusErr + } + + return ctrl.Result{}, nil +} + +func assignDBCSID(dbcsInst *databasev1alpha1.DbcsSystem, dbcsID string) { + dbcsInst.Spec.Id = &dbcsID +} + +func (r *DbcsSystemReconciler) eventFilterPredicate() predicate.Predicate { + return predicate.Funcs{ + CreateFunc: func(e event.CreateEvent) bool { + return true + }, + 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) + specObject := !reflect.DeepEqual(oldObject.Spec, newObject.Spec) + + deletionTimeStamp := !reflect.DeepEqual(oldObject.GetDeletionTimestamp(), newObject.GetDeletionTimestamp()) + + if specObject || deletionTimeStamp { + return true + } + + return false + }, + DeleteFunc: func(e event.DeleteEvent) bool { + return false + }, + } +} + +// SetupWithManager sets up the controller with the Manager. +func (r *DbcsSystemReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&databasev1alpha1.DbcsSystem{}). + WithEventFilter(r.eventFilterPredicate()). + WithOptions(controller.Options{MaxConcurrentReconciles: 50}). + Complete(r) +} diff --git a/controllers/database/oraclerestdataservice_controller.go b/controllers/database/oraclerestdataservice_controller.go new file mode 100644 index 00000000..783ae70c --- /dev/null +++ b/controllers/database/oraclerestdataservice_controller.go @@ -0,0 +1,1590 @@ +/* +** 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" + "strconv" + "strings" + "time" + + corev1 "k8s.io/api/core/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/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" + "sigs.k8s.io/controller-runtime/pkg/log" + + dbapi "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" + dbcommons "github.com/oracle/oracle-database-operator/commons/database" + + "github.com/go-logr/logr" +) + +const oracleRestDataServiceFinalizer = "database.oracle.com/oraclerestdataservicefinalizer" + +// OracleRestDataServiceReconciler reconciles a OracleRestDataService object +type OracleRestDataServiceReconciler struct { + client.Client + Log logr.Logger + Scheme *runtime.Scheme + Config *rest.Config + Recorder record.EventRecorder +} + +//+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,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 OracleRestDataService 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 *OracleRestDataServiceReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + _ = log.FromContext(ctx) + + oracleRestDataService := &dbapi.OracleRestDataService{} + // Always refresh status before a reconcile + defer r.Status().Update(ctx, oracleRestDataService) + + err := r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: req.Name}, oracleRestDataService) + if err != nil { + if apierrors.IsNotFound(err) { + r.Log.Info("Resource deleted") + return requeueN, nil + } + r.Log.Error(err, err.Error()) + return requeueY, err + } + + /* Initialize Status */ + if oracleRestDataService.Status.Status == "" { + oracleRestDataService.Status.Status = dbcommons.StatusPending + oracleRestDataService.Status.ApxeUrl = dbcommons.ValueUnavailable + oracleRestDataService.Status.DatabaseApiUrl = dbcommons.ValueUnavailable + oracleRestDataService.Status.DatabaseActionsUrl = dbcommons.ValueUnavailable + r.Status().Update(ctx, oracleRestDataService) + } + oracleRestDataService.Status.LoadBalancer = strconv.FormatBool(oracleRestDataService.Spec.LoadBalancer) + oracleRestDataService.Status.Image = oracleRestDataService.Spec.Image + + // Fetch Primary Database Reference + singleInstanceDatabase := &dbapi.SingleInstanceDatabase{} + // Always refresh status before a reconcile + defer r.Status().Update(ctx, singleInstanceDatabase) + + err = r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: oracleRestDataService.Spec.DatabaseRef}, singleInstanceDatabase) + if err != nil { + if apierrors.IsNotFound(err) { + oracleRestDataService.Status.Status = dbcommons.StatusError + oracleRestDataService.Status.DatabaseRef = "" + eventReason := "Error" + eventMsg := "database reference " + oracleRestDataService.Spec.DatabaseRef + " not found" + r.Recorder.Eventf(oracleRestDataService, corev1.EventTypeWarning, eventReason, eventMsg) + r.Log.Info(eventMsg) + return requeueY, nil + } + r.Log.Error(err, err.Error()) + return requeueY, err + } else { + if oracleRestDataService.Status.DatabaseRef == "" { + oracleRestDataService.Status.Status = dbcommons.StatusPending + oracleRestDataService.Status.DatabaseRef = oracleRestDataService.Spec.DatabaseRef + eventReason := "Database Check" + eventMsg := "database reference " + oracleRestDataService.Spec.DatabaseRef + " found" + r.Recorder.Eventf(oracleRestDataService, corev1.EventTypeNormal, eventReason, eventMsg) + } + } + + // Manage OracleRestDataService Deletion + result := r.manageOracleRestDataServiceDeletion(req, ctx, oracleRestDataService, singleInstanceDatabase) + if result.Requeue { + r.Log.Info("Reconcile queued") + return result, nil + } + + // First validate + result, err = r.validate(oracleRestDataService, singleInstanceDatabase, ctx) + if result.Requeue || err != nil { + r.Log.Info("Spec validation failed") + return result, nil + } + + // Create Service + result = r.createSVC(ctx, req, oracleRestDataService, singleInstanceDatabase) + if result.Requeue { + r.Log.Info("Reconcile queued") + return result, nil + } + + // PVC Creation + result, _ = r.createPVC(ctx, req, oracleRestDataService) + if result.Requeue { + r.Log.Info("Reconcile queued") + return result, nil + } + + // Validate if Primary Database Reference is ready + result, sidbReadyPod := r.validateSIDBReadiness(oracleRestDataService, singleInstanceDatabase, ctx, req) + if result.Requeue { + r.Log.Info("Reconcile queued") + return result, nil + } + + // Create ORDS Pods + result = r.createPods(oracleRestDataService, singleInstanceDatabase, ctx, req) + if result.Requeue { + r.Log.Info("Reconcile queued") + return result, nil + } + + var ordsReadyPod corev1.Pod + result, ordsReadyPod = r.checkHealthStatus(oracleRestDataService, singleInstanceDatabase, sidbReadyPod, ctx, req) + if result.Requeue { + r.Log.Info("Reconcile queued") + return result, nil + } + + result = r.restEnableSchemas(oracleRestDataService, singleInstanceDatabase, sidbReadyPod, ordsReadyPod, ctx, req) + if result.Requeue { + r.Log.Info("Reconcile queued") + return result, nil + } + + // Configure Apex + result = r.configureApex(oracleRestDataService, singleInstanceDatabase, sidbReadyPod, ordsReadyPod, ctx, req) + if result.Requeue { + r.Log.Info("Reconcile queued") + return result, nil + } + + // Delete Secrets + r.deleteSecrets(oracleRestDataService, ctx, req) + + if oracleRestDataService.Status.ServiceIP == "" { + return requeueY, nil + } + + return ctrl.Result{}, nil +} + +// ############################################################################# +// +// Validate the CRD specs +// +// ############################################################################# +func (r *OracleRestDataServiceReconciler) validate(m *dbapi.OracleRestDataService, + n *dbapi.SingleInstanceDatabase, ctx context.Context) (ctrl.Result, error) { + + var err error + eventReason := "Spec Error" + var eventMsgs []string + + //First check image pull secrets + if m.Spec.Image.PullSecrets != "" { + secret := &corev1.Secret{} + err = r.Get(ctx, types.NamespacedName{Name: m.Spec.Image.PullSecrets, Namespace: m.Namespace}, secret) + if err != nil { + if apierrors.IsNotFound(err) { + // Secret not found + r.Recorder.Eventf(m, corev1.EventTypeWarning, eventReason, err.Error()) + r.Log.Info(err.Error()) + m.Status.Status = dbcommons.StatusError + return requeueY, err + } + r.Log.Error(err, err.Error()) + return requeueY, err + } + } + + // If ORDS has no peristence specified, ensure SIDB has persistence configured + if m.Spec.Persistence.Size == "" && n.Spec.Persistence.AccessMode == "" { + eventMsgs = append(eventMsgs, "cannot configure ORDS for database "+m.Spec.DatabaseRef+" that has no attached persistent volume") + } + if !m.Status.OrdsInstalled && n.Status.OrdsReference != "" { + eventMsgs = append(eventMsgs, "database "+m.Spec.DatabaseRef+" is already configured with ORDS "+n.Status.OrdsReference) + } + if m.Status.DatabaseRef != "" && m.Status.DatabaseRef != m.Spec.DatabaseRef { + eventMsgs = append(eventMsgs, "databaseRef cannot be updated") + } + if m.Status.Image.PullFrom != "" && m.Status.Image != m.Spec.Image { + 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, ",")) + r.Log.Info(strings.Join(eventMsgs, "\n")) + err = errors.New(strings.Join(eventMsgs, ",")) + return requeueY, err + } + + return requeueN, err +} + +// ##################################################################################################### +// +// Validate Readiness of the primary DB specified +// +// ##################################################################################################### +func (r *OracleRestDataServiceReconciler) validateSIDBReadiness(m *dbapi.OracleRestDataService, + n *dbapi.SingleInstanceDatabase, ctx context.Context, req ctrl.Request) (ctrl.Result, corev1.Pod) { + + log := r.Log.WithValues("validateSidbReadiness", req.NamespacedName) + + // ## 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 + } + + if m.Status.OrdsInstalled || m.Status.CommonUsersCreated { + return requeueN, sidbReadyPod + } + + m.Status.Status = dbcommons.StatusPending + if sidbReadyPod.Name == "" || n.Status.Status != dbcommons.StatusReady { + eventReason := "Database Check" + eventMsg := "status of database " + n.Name + " is not ready, retrying..." + r.Recorder.Eventf(m, corev1.EventTypeWarning, eventReason, eventMsg) + return requeueY, sidbReadyPod + } else { + eventReason := "Database Check" + eventMsg := "status of database " + n.Name + " is ready" + r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) + } + + // Validate databaseRef Admin Password + adminPasswordSecret := &corev1.Secret{} + err = r.Get(ctx, types.NamespacedName{Name: m.Spec.AdminPassword.SecretName, Namespace: m.Namespace}, adminPasswordSecret) + if err != nil { + if apierrors.IsNotFound(err) { + 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, sidbReadyPod + } + log.Error(err, err.Error()) + return requeueY, sidbReadyPod + } + adminPassword := string(adminPasswordSecret.Data[m.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.SQLPlusCLI)) + if err != nil { + log.Error(err, err.Error()) + return requeueY, sidbReadyPod + } + 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 := "Database Check" + eventMsg := "login denied, invalid database admin password in secret " + m.Spec.AdminPassword.SecretName + r.Recorder.Eventf(m, corev1.EventTypeWarning, eventReason, eventMsg) + log.Info(eventMsg) + return requeueY, sidbReadyPod + } else { + eventMsg := "login attempt failed for database admin password in secret " + m.Spec.AdminPassword.SecretName + log.Info(eventMsg) + return requeueY, sidbReadyPod + } + + // Create PDB , CDB Admin users and grant permissions. ORDS installation on CDB level + 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.SetAdminUsersSQL, adminPassword), dbcommons.SQLPlusCLI)) + if err != nil { + log.Error(err, err.Error()) + return requeueY, sidbReadyPod + } + if !strings.Contains(out, "ERROR") || !strings.Contains(out, "ORA-") || + strings.Contains(out, "ERROR") && strings.Contains(out, "ORA-01920") { + m.Status.CommonUsersCreated = true + } + return requeueN, sidbReadyPod +} + +// ##################################################################################################### +// +// Check ORDS Health Status +// +// ##################################################################################################### +func (r *OracleRestDataServiceReconciler) checkHealthStatus(m *dbapi.OracleRestDataService, n *dbapi.SingleInstanceDatabase, + sidbReadyPod corev1.Pod, ctx context.Context, req ctrl.Request) (ctrl.Result, corev1.Pod) { + log := r.Log.WithValues("checkHealthStatus", req.NamespacedName) + + readyPod, _, _, _, err := dbcommons.FindPods(r, m.Spec.Image.Version, + m.Spec.Image.PullFrom, m.Name, m.Namespace, ctx, req) + if err != nil { + log.Error(err, err.Error()) + return requeueY, readyPod + } + if readyPod.Name == "" { + m.Status.Status = dbcommons.StatusNotReady + return requeueY, readyPod + } + + // Get ORDS Status + out, err := dbcommons.ExecCommand(r, r.Config, readyPod.Name, readyPod.Namespace, "", ctx, req, false, "bash", "-c", + dbcommons.GetORDSStatus) + log.Info("GetORDSStatus Output") + log.Info(out) + if strings.Contains(strings.ToUpper(out), "ERROR") { + return requeueY, readyPod + } + if err != nil { + log.Info(err.Error()) + if strings.Contains(strings.ToUpper(err.Error()), "ERROR") { + return requeueY, readyPod + } + } + + m.Status.Status = dbcommons.StatusNotReady + 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 + } + if !m.Status.OrdsInstalled { + m.Status.OrdsInstalled = true + n.Status.OrdsReference = m.Name + r.Status().Update(ctx, n) + eventReason := "ORDS Installation" + eventMsg := "installation of ORDS completed" + r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) + out, err := dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", + ctx, req, false, "bash", "-c", fmt.Sprintf("echo -e \"%s\" | %s", dbcommons.OpenPDBSeed, dbcommons.SQLPlusCLI)) + if err != nil { + log.Error(err, err.Error()) + } else { + log.Info("Close PDB seed") + log.Info(out) + } + } + } + if m.Status.Status == dbcommons.StatusNotReady { + return requeueY, readyPod + } + return requeueN, readyPod +} + +// ############################################################################# +// +// Instantiate Service spec from OracleRestDataService spec +// +// ############################################################################# +func (r *OracleRestDataServiceReconciler) instantiateSVCSpec(m *dbapi.OracleRestDataService) *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: "client", + Port: 8443, + 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 +} + +// ############################################################################# +// +// Instantiate POD spec from OracleRestDataService spec +// +// ############################################################################# +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, + }, + }, + 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, + }, + }, + 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: "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", + }}, + }, + }, + }, + }, + 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 }(), + }, + 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 }(), + }, + VolumeMounts: []corev1.VolumeMount{ + { + MountPath: "/opt/oracle/ords/config/ords", + 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", + }, + { + 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, + }, + }, + }, + { + Name: "ORACLE_PWD", + ValueFrom: &corev1.EnvVarSource{ + SecretKeyRef: &corev1.SecretKeySelector{ + LocalObjectReference: corev1.LocalObjectReference{ + Name: m.Spec.AdminPassword.SecretName, + }, + Key: m.Spec.AdminPassword.SecretKey, + }, + }, + }, + }, + }, + }, + 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", + }}, + 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 }(), + }, + + 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 +} + +//############################################################################# +// Instantiate POD spec from OracleRestDataService spec +//############################################################################# + +// ############################################################################# +// +// Instantiate Persistent Volume Claim spec from SingleInstanceDatabase spec +// +// ############################################################################# +func (r *OracleRestDataServiceReconciler) instantiatePVCSpec(m *dbapi.OracleRestDataService) *corev1.PersistentVolumeClaim { + + pvc := &corev1.PersistentVolumeClaim{ + TypeMeta: metav1.TypeMeta{ + Kind: "PersistentVolumeClaim", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: m.Name, + 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(m.Spec.Persistence.AccessMode)) + return accessMode + }(), + 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), + }, + }, + StorageClassName: &m.Spec.Persistence.StorageClass, + VolumeName: m.Spec.Persistence.VolumeName, + Selector: func() *metav1.LabelSelector { + if m.Spec.Persistence.StorageClass != "oci" { + return nil + } + return &metav1.LabelSelector{ + MatchLabels: 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 + }(), + } + }(), + }, + } + // Set SingleInstanceDatabase instance as the owner and controller + ctrl.SetControllerReference(m, pvc, r.Scheme) + return pvc +} + +// ############################################################################# +// +// Create a Service for OracleRestDataService +// +// ############################################################################# +func (r *OracleRestDataServiceReconciler) createSVC(ctx context.Context, req ctrl.Request, + m *dbapi.OracleRestDataService, n *dbapi.SingleInstanceDatabase) ctrl.Result { + + log := r.Log.WithValues("createSVC", req.NamespacedName) + // Check if the Service already exists, if not create a new one + svc := &corev1.Service{} + svcDeleted := false + // Check if the Service already exists, if not create a new one + // 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}, svc) + if err == nil { + log.Info("Found Existing Service ", "Service.Name", svc.Name) + svcType := corev1.ServiceType("NodePort") + if m.Spec.LoadBalancer { + svcType = corev1.ServiceType("LoadBalancer") + } + + if svc.Spec.Type != svcType { + log.Info("Deleting SVC", " name ", svc.Name) + err = r.Delete(ctx, svc) + if err != nil { + r.Log.Error(err, "Failed to delete svc", " Name", svc.Name) + return requeueN + } + svcDeleted = true + } + } + + if svcDeleted || (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) + if err != nil { + log.Error(err, "Failed to create new service", "Service.Namespace", svc.Namespace, "Service.Name", svc.Name) + return requeueY + } else { + eventReason := "Service creation" + eventMsg := "successfully created service type " + string(svc.Spec.Type) + r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) + log.Info(eventMsg) + } + + } else if err != nil { + log.Error(err, "Failed to get Service") + return requeueY + } + + m.Status.ServiceIP = "" + if m.Spec.LoadBalancer { + if len(svc.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 := svc.Status.LoadBalancer.Ingress[0].Hostname + 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.ServiceIP = lbAddress + m.Status.DatabaseActionsUrl = "https://" + 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" + } + } + return requeueN + } + 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) + + "/ords/sql-developer" + if m.Status.ApexConfigured { + m.Status.ApxeUrl = "https://" + nodeip + ":" + fmt.Sprint(svc.Spec.Ports[0].NodePort) + "/ords/" + + n.Status.Pdbname + "/apex" + } + } + return requeueN +} + +// ############################################################################# +// +// Stake a claim for Persistent Volume +// +// ############################################################################# +func (r *OracleRestDataServiceReconciler) createPVC(ctx context.Context, req ctrl.Request, + m *dbapi.OracleRestDataService) (ctrl.Result, error) { + + // PV is shared for ORDS and SIDB + if m.Spec.Persistence.AccessMode == "" { + return requeueN, nil + } + log := r.Log.WithValues("createPVC", req.NamespacedName) + + pvc := &corev1.PersistentVolumeClaim{} + err := r.Get(ctx, types.NamespacedName{Name: m.Name, Namespace: m.Namespace}, pvc) + if err != nil && apierrors.IsNotFound(err) { + // Define a new PVC + pvc = r.instantiatePVCSpec(m) + 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 + } else { + log.Info("PVC already exists") + } + + return requeueN, nil +} + +// ############################################################################# +// +// Create the requested POD replicas +// +// ############################################################################# +func (r *OracleRestDataServiceReconciler) createPods(m *dbapi.OracleRestDataService, + n *dbapi.SingleInstanceDatabase, ctx context.Context, req ctrl.Request) ctrl.Result { + + log := r.Log.WithValues("createPods", req.NamespacedName) + + readyPod, replicasFound, available, podsMarkedToBeDeleted, err := dbcommons.FindPods(r, m.Spec.Image.Version, + m.Spec.Image.PullFrom, m.Name, m.Namespace, ctx, req) + if err != nil { + log.Error(err, err.Error()) + return requeueY + } + + // Recreate new pods only after earlier pods are terminated completely + for i := 0; i < len(podsMarkedToBeDeleted); i++ { + r.Log.Info("Force deleting pod ", "name", podsMarkedToBeDeleted[i].Name, "phase", podsMarkedToBeDeleted[i].Status.Phase) + var gracePeriodSeconds int64 = 0 + policy := metav1.DeletePropagationForeground + r.Delete(ctx, &podsMarkedToBeDeleted[i], &client.DeleteOptions{ + GracePeriodSeconds: &gracePeriodSeconds, PropagationPolicy: &policy}) + } + + log.Info(m.Name, " pods other than one of Ready Pods : ", dbcommons.GetPodNames(available)) + log.Info(m.Name, " Ready Pod : ", readyPod.Name) + + replicasReq := m.Spec.Replicas + if replicasFound == 0 { + m.Status.Status = dbcommons.StatusPending + } + + if replicasFound == replicasReq { + log.Info("No of " + m.Name + " replicas Found are same as Required") + } 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) + return requeueY + } + } + log.Info("Creating a new "+m.Name+" POD", "POD.Namespace", pod.Namespace, "POD.Name", pod.Name) + err = r.Create(ctx, pod) + if err != nil { + log.Error(err, "Failed to create new "+m.Name+" POD", "pod.Namespace", pod.Namespace, "POD.Name", pod.Name) + return requeueY + } + log.Info("Succesfully Created new "+m.Name+" POD", "POD.NAME : ", pod.Name) + } + } else { + // Delete extra pods + noDeleted := 0 + if readyPod.Name != "" { + available = append(available, readyPod) + } + for _, pod := range available { + if readyPod.Name == pod.Name { + continue + } + if replicasReq == (len(available) - noDeleted) { + break + } + r.Log.Info("Deleting Pod : ", "POD.NAME", pod.Name) + var gracePeriodSeconds int64 = 0 + policy := metav1.DeletePropagationForeground + err := r.Delete(ctx, &pod, &client.DeleteOptions{ + GracePeriodSeconds: &gracePeriodSeconds, PropagationPolicy: &policy}) + noDeleted += 1 + if err != nil { + r.Log.Error(err, "Failed to delete existing POD", "POD.Name", pod.Name) + // Don't requeue + } + } + } + + m.Status.Replicas = m.Spec.Replicas + + return requeueN +} + +// ############################################################################# +// +// Manage Finalizer to cleanup before deletion of OracleRestDataService +// +// ############################################################################# +func (r *OracleRestDataServiceReconciler) manageOracleRestDataServiceDeletion(req ctrl.Request, ctx context.Context, + m *dbapi.OracleRestDataService, n *dbapi.SingleInstanceDatabase) ctrl.Result { + log := r.Log.WithValues("manageOracleRestDataServiceDeletion", req.NamespacedName) + + // Check if the OracleRestDataService instance is marked to be deleted, which is + // indicated by the deletion timestamp being set. + isOracleRestDataServiceMarkedToBeDeleted := m.GetDeletionTimestamp() != nil + if isOracleRestDataServiceMarkedToBeDeleted { + if controllerutil.ContainsFinalizer(m, oracleRestDataServiceFinalizer) { + // Run finalization logic for oracleRestDataServiceFinalizer. If the + // finalization logic fails, don't remove the finalizer so + // that we can retry during the next reconciliation. + if err := r.cleanupOracleRestDataService(req, ctx, m, n); err != nil { + log.Error(err, err.Error()) + return requeueY + } + + n.Status.OrdsReference = "" + // Make sure n.Status.OrdsInstalled is set to false or else it blocks .spec.databaseRef deletion + for i := 0; i < 10; i++ { + log.Info("Clearing the OrdsReference from DB", "name", n.Name) + err := r.Status().Update(ctx, n) + if err != nil { + log.Error(err, err.Error()) + time.Sleep(1 * time.Second) + continue + } + break + } + + // Remove oracleRestDataServiceFinalizer. Once all finalizers have been + // removed, the object will be deleted. + controllerutil.RemoveFinalizer(m, oracleRestDataServiceFinalizer) + err := r.Update(ctx, m) + if err != nil { + log.Error(err, err.Error()) + return requeueY + } + } + return requeueY + } + + // Add finalizer for this CR + if !controllerutil.ContainsFinalizer(m, oracleRestDataServiceFinalizer) { + controllerutil.AddFinalizer(m, oracleRestDataServiceFinalizer) + err := r.Update(ctx, m) + if err != nil { + log.Error(err, err.Error()) + return requeueY + } + } + return requeueN +} + +// ############################################################################# +// +// Finalization logic for OracleRestDataServiceFinalizer +// +// ############################################################################# +func (r *OracleRestDataServiceReconciler) cleanupOracleRestDataService(req ctrl.Request, ctx context.Context, + m *dbapi.OracleRestDataService, n *dbapi.SingleInstanceDatabase) error { + log := r.Log.WithValues("cleanupOracleRestDataService", req.NamespacedName) + + if m.Status.OrdsInstalled { + // ## 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 err + } + + if sidbReadyPod.Name == "" { + eventReason := "ORDS Uninstallation" + eventMsg := "skipping ORDS uninstallation as no ready pod for " + n.Name + " is available" + r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) + return nil + } + + // Get Session id , serial# for ORDS_PUBLIC_USER to kill the sessions + out, err := dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, false, "bash", "-c", + fmt.Sprintf("echo -e \"%s\" | %s ", dbcommons.GetSessionInfoSQL, dbcommons.SQLPlusCLI)) + if err != nil { + log.Error(err, err.Error()) + return err + } + log.Info("GetSessionInfoSQL Output : " + out) + + sessionInfos, _ := dbcommons.StringToLines(out) + killSessions := "" + for _, sessionInfo := range sessionInfos { + if !strings.Contains(sessionInfo, ",") { + // May be a column name or (-----) + continue + } + killSessions += "\n" + fmt.Sprintf(dbcommons.KillSessionSQL, sessionInfo) + } + + //kill all the sessions with given sid,serial# + out, err = dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, false, "bash", "-c", + fmt.Sprintf("echo -e \"%s\" | %s ", killSessions, dbcommons.SQLPlusCLI)) + + if err != nil { + log.Error(err, err.Error()) + return err + } + log.Info("KillSession Output : " + out) + + // Fetch admin Password of database to uninstall ORDS + adminPasswordSecret := &corev1.Secret{} + adminPasswordSecretFound := false + for i := 0; i < 5; i++ { + err := r.Get(ctx, types.NamespacedName{Name: m.Spec.AdminPassword.SecretName, Namespace: n.Namespace}, adminPasswordSecret) + if err != nil { + if apierrors.IsNotFound(err) { + m.Status.Status = dbcommons.StatusError + eventReason := "Error" + eventMsg := "database admin password secret " + m.Spec.AdminPassword.SecretName + " required for ORDS uninstall not found, retrying..." + r.Recorder.Eventf(m, corev1.EventTypeWarning, eventReason, eventMsg) + r.Log.Info(eventMsg) + if i < 4 { + time.Sleep(15 * time.Second) + continue + } + } else { + log.Error(err, err.Error()) + } + } else { + adminPasswordSecretFound = true + break + } + } + // Find ORDS ready pod + readyPod, _, _, _, err := dbcommons.FindPods(r, m.Spec.Image.Version, + m.Spec.Image.PullFrom, m.Name, m.Namespace, ctx, req) + if err != nil { + log.Error(err, err.Error()) + return err + } + if adminPasswordSecretFound && readyPod.Name != "" { + adminPassword := string(adminPasswordSecret.Data[m.Spec.AdminPassword.SecretKey]) + if n.Status.ApexInstalled { + //Uninstall Apex + eventReason := "Apex Uninstallation" + eventMsg := "Uninstalling Apex..." + r.Recorder.Eventf(m, corev1.EventTypeWarning, eventReason, eventMsg) + log.Info(eventMsg) + out, err = dbcommons.ExecCommand(r, r.Config, readyPod.Name, readyPod.Namespace, "", ctx, req, true, "bash", "-c", + fmt.Sprintf(dbcommons.UninstallApex, adminPassword, n.Status.Pdbname)) + if err != nil { + log.Info(err.Error()) + } + n.Status.ApexInstalled = false // To reinstall Apex when ORDS is reinstalled + log.Info("Apex uninstall output: " + out) + } + //Uninstall ORDS + eventReason := "ORDS Uninstallation" + eventMsg := "Uninstalling ORDS..." + r.Recorder.Eventf(m, corev1.EventTypeWarning, eventReason, eventMsg) + log.Info(eventMsg) + uninstallORDS := fmt.Sprintf(dbcommons.UninstallORDSCMD, adminPassword) + out, err = dbcommons.ExecCommand(r, r.Config, readyPod.Name, readyPod.Namespace, "", ctx, req, true, "bash", "-c", + uninstallORDS) + log.Info("ORDS uninstall output: " + out) + if strings.Contains(strings.ToUpper(out), "ERROR") { + return errors.New(out) + } + if err != nil { + log.Info(err.Error()) + } + } + + // Drop Admin Users + out, err = dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, true, "bash", "-c", + fmt.Sprintf("echo -e \"%s\" | %s ", dbcommons.DropAdminUsersSQL, dbcommons.SQLPlusCLI)) + if err != nil { + log.Info(err.Error()) + } + log.Info("Drop admin users: " + out) + + //Delete ORDS pod + var gracePeriodSeconds int64 = 0 + policy := metav1.DeletePropagationForeground + r.Delete(ctx, &readyPod, &client.DeleteOptions{ + GracePeriodSeconds: &gracePeriodSeconds, PropagationPolicy: &policy}) + + //Delete Database Admin Password Secret + if !*m.Spec.AdminPassword.KeepSecret { + err = r.Delete(ctx, adminPasswordSecret, &client.DeleteOptions{}) + if err == nil { + r.Log.Info("Deleted Admin Password Secret :" + adminPasswordSecret.Name) + } + } + } + + // Cleanup steps that the operator needs to do before the CR can be deleted. + log.Info("Successfully cleaned up OracleRestDataService ") + return nil +} + +// ############################################################################# +// +// Configure APEX +// +// ############################################################################# +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) + + 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 { + 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 + } + 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", + fmt.Sprintf(dbcommons.IsApexInstalled, sidbPassword, n.Status.Pdbname)) + if err != nil { + log.Error(err, err.Error()) + return requeueY + } + log.Info("Is Apex installed: \n" + out) + + apexInstalled := "APEXVERSION:" + if !strings.Contains(out, apexInstalled) { + eventReason = "Apex Installation" + eventMsg = "Unable to determine Apex version, retrying install..." + r.Recorder.Eventf(m, corev1.EventTypeWarning, eventReason, eventMsg) + return requeueY + } + + m.Status.Status = dbcommons.StatusReady + eventReason = "Apex Installation" + outArr := strings.Split(out, apexInstalled) + eventMsg = "installation of Apex " + strings.TrimSpace(outArr[len(outArr)-1]) + " completed" + r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) + n.Status.ApexInstalled = true + r.Status().Update(ctx, n) + return requeueN +} + +// ############################################################################# +// +// Delete Secrets +// +// ############################################################################# +func (r *OracleRestDataServiceReconciler) deleteSecrets(m *dbapi.OracleRestDataService, ctx context.Context, req ctrl.Request) { + log := r.Log.WithValues("deleteSecrets", req.NamespacedName) + + if !*m.Spec.AdminPassword.KeepSecret { + // Fetch adminPassword Secret + adminPasswordSecret := &corev1.Secret{} + err := r.Get(ctx, types.NamespacedName{Name: m.Spec.AdminPassword.SecretName, Namespace: m.Namespace}, adminPasswordSecret) + if err == nil { + //Delete Database Admin Password Secret . + err := r.Delete(ctx, adminPasswordSecret, &client.DeleteOptions{}) + if err == nil { + log.Info("Database admin password secret deleted : " + adminPasswordSecret.Name) + } + } + } + + if !*m.Spec.OrdsPassword.KeepSecret { + // Fetch ordsPassword Secret + ordsPasswordSecret := &corev1.Secret{} + err := r.Get(ctx, types.NamespacedName{Name: m.Spec.OrdsPassword.SecretName, Namespace: m.Namespace}, ordsPasswordSecret) + if err == nil { + //Delete ORDS Password Secret . + err := r.Delete(ctx, ordsPasswordSecret, &client.DeleteOptions{}) + if err == nil { + log.Info("ORDS password secret deleted : " + ordsPasswordSecret.Name) + } + } + } + + 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) + } + } + } + +} + +// ############################################################################# +// +// Rest Enable/Disable Schemas +// +// ############################################################################# +func (r *OracleRestDataServiceReconciler) restEnableSchemas(m *dbapi.OracleRestDataService, n *dbapi.SingleInstanceDatabase, + sidbReadyPod corev1.Pod, ordsReadyPod corev1.Pod, ctx context.Context, req ctrl.Request) ctrl.Result { + + log := r.Log.WithValues("restEnableSchemas", req.NamespacedName) + + if sidbReadyPod.Name == "" || n.Status.Status != dbcommons.StatusReady { + eventReason := "Database Check" + eventMsg := "status of database " + n.Name + " is not ready, retrying..." + r.Recorder.Eventf(m, corev1.EventTypeWarning, eventReason, eventMsg) + m.Status.Status = dbcommons.StatusNotReady + return requeueY + } + + // Get available PDBs + availablePDBS, err := dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", + ctx, req, true, "bash", "-c", fmt.Sprintf("echo -e \"%s\" | %s", dbcommons.GetPdbsSQL, dbcommons.SQLPlusCLI)) + if err != nil { + log.Error(err, err.Error()) + return requeueY + } else { + log.Info("PDBs found:") + log.Info(availablePDBS) + } + + restartORDS := false + + for i := 0; i < len(m.Spec.RestEnableSchemas); i++ { + + pdbName := m.Spec.RestEnableSchemas[i].PdbName + if pdbName == "" { + pdbName = n.Spec.Pdbname + } + + // If the PDB mentioned in yaml doesnt contain in the database , continue + if !strings.Contains(strings.ToUpper(availablePDBS), strings.ToUpper(pdbName)) { + eventReason := "PDB Check" + eventMsg := "PDB " + pdbName + " not found for specified schema " + m.Spec.RestEnableSchemas[i].SchemaName + log.Info(eventMsg) + r.Recorder.Eventf(m, corev1.EventTypeWarning, eventReason, eventMsg) + continue + } + + getOrdsSchemaStatus := fmt.Sprintf(dbcommons.GetUserORDSSchemaStatusSQL, m.Spec.RestEnableSchemas[i].SchemaName, pdbName) + + // Get ORDS Schema status for PDB + out, err := dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, true, "bash", "-c", + fmt.Sprintf("echo -e \"%s\" | %s", getOrdsSchemaStatus, dbcommons.SQLPlusCLI)) + if err != nil { + log.Error(err, err.Error()) + return requeueY + } + + // if ORDS already enabled for given PDB + if strings.Contains(out, "STATUS:ENABLED") { + if m.Spec.RestEnableSchemas[i].Enable { + log.Info("Schema already enabled", "schema", m.Spec.RestEnableSchemas[i].SchemaName) + continue + } + } else if strings.Contains(out, "STATUS:DISABLED") { + if !m.Spec.RestEnableSchemas[i].Enable { + log.Info("Schema already disabled", "schema", m.Spec.RestEnableSchemas[i].SchemaName) + continue + } + } else if m.Spec.RestEnableSchemas[i].Enable { + OrdsPasswordSecret := &corev1.Secret{} + // Fetch the secret to get password for database user . Secret has to be created in the same namespace of OracleRestDataService + err = r.Get(ctx, types.NamespacedName{Name: m.Spec.OrdsPassword.SecretName, Namespace: m.Namespace}, OrdsPasswordSecret) + if err != nil { + if apierrors.IsNotFound(err) { + eventReason := "No Secret" + eventMsg := "secret " + m.Spec.OrdsPassword.SecretName + " Not Found" + r.Recorder.Eventf(m, corev1.EventTypeWarning, eventReason, eventMsg) + r.Log.Info(eventMsg) + return requeueY + } + log.Error(err, err.Error()) + return requeueY + } + password := string(OrdsPasswordSecret.Data[m.Spec.OrdsPassword.SecretKey]) + // Create users,schemas and grant enableORDS for PDB + createSchemaSQL := fmt.Sprintf(dbcommons.CreateORDSSchemaSQL, m.Spec.RestEnableSchemas[i].SchemaName, password, pdbName) + log.Info("Creating schema", "schema", m.Spec.RestEnableSchemas[i].SchemaName) + _, err = dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, true, "bash", "-c", + fmt.Sprintf("echo -e \"%s\" | %s", createSchemaSQL, dbcommons.SQLPlusCLI)) + if err != nil { + log.Error(err, err.Error()) + return requeueY + } + } else { + log.Info("Noop, ignoring", "schema", m.Spec.RestEnableSchemas[i].SchemaName) + continue + } + urlMappingPattern := "" + if m.Spec.RestEnableSchemas[i].UrlMapping == "" { + urlMappingPattern = strings.ToLower(m.Spec.RestEnableSchemas[i].SchemaName) + } else { + urlMappingPattern = strings.ToLower(m.Spec.RestEnableSchemas[i].UrlMapping) + } + enableORDSSchema := fmt.Sprintf(dbcommons.EnableORDSSchemaSQL, m.Spec.RestEnableSchemas[i].SchemaName, + strconv.FormatBool(m.Spec.RestEnableSchemas[i].Enable), urlMappingPattern, pdbName) + + // EnableORDS for Schema + out, err = dbcommons.ExecCommand(r, r.Config, sidbReadyPod.Name, sidbReadyPod.Namespace, "", ctx, req, true, "bash", "-c", + fmt.Sprintf("echo -e \"%s\" | %s", enableORDSSchema, dbcommons.SQLPlusCLI)) + if err != nil { + log.Error(err, err.Error()) + return requeueY + } + log.Info(out) + if m.Spec.RestEnableSchemas[i].Enable { + log.Info("REST Enabled", "schema", m.Spec.RestEnableSchemas[i].SchemaName) + } else { + log.Info("REST Disabled", "schema", m.Spec.RestEnableSchemas[i].SchemaName) + restartORDS = true + } + } + + if restartORDS { + r.Log.Info("Restarting ORDS Pod " + ordsReadyPod.Name + " to clear disabled schemas cache") + 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()) + } + return requeueY + } + return requeueN +} + +// ############################################################################# +// +// SetupWithManager sets up the controller with the Manager. +// +// ############################################################################# +func (r *OracleRestDataServiceReconciler) SetupWithManager(mgr ctrl.Manager) error { + return ctrl.NewControllerManagedBy(mgr). + For(&dbapi.OracleRestDataService{}). + Owns(&corev1.Pod{}). //Watch for deleted pods of OracleRestDataService 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/pdb_controller.go b/controllers/database/pdb_controller.go new file mode 100644 index 00000000..4f12ca31 --- /dev/null +++ b/controllers/database/pdb_controller.go @@ -0,0 +1,1369 @@ +/* +** 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/v1alpha1" + + "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 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 + +<<<<<<< HEAD + +======= +>>>>>>> gitlab-origin/master +// 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] + /* + 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 = 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 + } + + pdbAdminName, err := r.getSecret(ctx, req, pdb, pdb.Spec.AdminName.Secret.SecretName, pdb.Spec.AdminName.Secret.Key) + if err != nil { + return err + } + pdbAdminPwd, err := r.getSecret(ctx, req, pdb, pdb.Spec.AdminPwd.Secret.SecretName, pdb.Spec.AdminPwd.Secret.Key) + if err != nil { + return err + } + + /* 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 + } + + pdbAdminName = strings.TrimSuffix(pdbAdminName, "\n") + pdbAdminPwd = strings.TrimSuffix(pdbAdminPwd, "\n") + + 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 = r.callAPI(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.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" + 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") + 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 = r.callAPI(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 + } + + 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 = r.callAPI(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 = r.callAPI(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 := r.callAPI(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 := r.callAPI(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 + } + + 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) + 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 = r.callAPI(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) + +} diff --git a/controllers/database/shardingdatabase_controller.go b/controllers/database/shardingdatabase_controller.go index f30e714b..7fcaac2b 100644 --- a/controllers/database/shardingdatabase_controller.go +++ b/controllers/database/shardingdatabase_controller.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2022 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -44,11 +44,12 @@ import ( "fmt" "reflect" "strconv" + "strings" "time" "github.com/go-logr/logr" - "github.com/oracle/oci-go-sdk/v51/common" - "github.com/oracle/oci-go-sdk/v51/ons" + "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" @@ -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,17 @@ type ShardingDatabaseReconciler struct { kubeConfig clientcmd.ClientConfig Recorder record.EventRecorder osh []*ShardingTopology + InCluster bool + 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 -// +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 @@ -122,6 +128,7 @@ 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} @@ -152,7 +159,7 @@ func (r *ShardingDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.Req return ctrl.Result{}, err } - idx, instFlag := r.checkProvInstance(instance) + _, instFlag := r.checkProvInstance(instance) // assinging osh instance if !instFlag { // Sharding Topolgy Struct Assignment @@ -162,6 +169,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. @@ -183,21 +191,21 @@ 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 =========== 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" } @@ -282,7 +290,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 @@ -302,7 +310,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 @@ -323,6 +331,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 @@ -376,7 +391,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) @@ -409,18 +424,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 @@ -440,6 +443,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) @@ -453,6 +457,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 { @@ -494,13 +514,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) @@ -564,7 +603,7 @@ func (r *ShardingDatabaseReconciler) finalizerShardingDatabaseInstance(instance } // 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 + return fmt.Errorf("delete of the sharding topology is in progress"), true } // Add finalizer for this CR @@ -830,6 +869,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 { @@ -840,6 +880,27 @@ 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 !shardingv1.CheckIsDeleteFlag(OraShardSpex.IsDelete, instance, r.Log) { + err = r.checkShardSpace(instance, OraShardSpex) + if err != nil { + return err + } + err = r.checkShardGroup(instance, OraShardSpex) + if err != nil { + return err + } + } + } + } + // Once the initial Spec is been validated then update the last Sucessful Spec err = instance.UpdateLastSuccessfulSpec(r.Client) if err != nil { @@ -874,17 +935,76 @@ func (r *ShardingDatabaseReconciler) validateSpex(instance *databasev1alpha1.Sha // Compare Env variables for shard begins here if !r.comapreShardEnvVariables(instance, lastSuccSpec) { - return fmt.Errorf("Change of Shard env variables are not") + return fmt.Errorf("change of Shard env variables are not") } // Compare Env variables for catalog begins here if !r.comapreCatalogEnvVariables(instance, lastSuccSpec) { - return fmt.Errorf("Change of Catalog env variables are not") + return fmt.Errorf("change of Catalog env variables are not") } // Compare env variable for Catalog ends here if !r.comapreGsmEnvVariables(instance, lastSuccSpec) { - return fmt.Errorf("Change of GSM env variables are not") + return fmt.Errorf("change of GSM env variables are not") + } + + } + 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 } @@ -1028,6 +1148,7 @@ func (r *ShardingDatabaseReconciler) validateGsm(instance *databasev1alpha1.Shar if availableFlag != true { gsmSfSet = gsmSfSet1 gsmPod = gsmPod1 + // availableFlag = true availableFlag = true } } @@ -1072,7 +1193,7 @@ func (r *ShardingDatabaseReconciler) validateInvidualGsm(instance *databasev1alp 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)) - return gsmSfSet, gsmPod, fmt.Errorf("Pod doesn't exist") + return gsmSfSet, gsmPod, fmt.Errorf("pod doesn't exist") } err = shardingv1.CheckGsmStatus(gsmPod.Name, instance, r.kubeClient, r.kubeConfig, r.Log) if err != nil { @@ -1101,6 +1222,7 @@ func (r *ShardingDatabaseReconciler) validateCatalog(instance *databasev1alpha1. if availlableFlag != true { catalogSfSet = catalogSfSet1 catalogPod = catalogPod1 + // availlableFlag = true availlableFlag = true } } @@ -1201,24 +1323,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) { @@ -1230,13 +1349,16 @@ 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 OraShardSpex.IsDelete != true { + if !shardingv1.CheckIsDeleteFlag(OraShardSpex.IsDelete, instance, r.Log) { shardSfSet, _, err = r.validateShard(instance, OraShardSpex, int(i)) if err != nil { continue } else { - _ = r.verifyShards(instance, gsmPod, shardSfSet) + _ = r.verifyShards(instance, gsmPod, shardSfSet, OraShardSpex) } } @@ -1368,7 +1490,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 @@ -1382,8 +1504,9 @@ 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") - if OraShardSpex.IsDelete != true { + // strings.Contains(stateStr, "DELETE") + + if !shardingv1.CheckIsDeleteFlag(OraShardSpex.IsDelete, instance, r.Log) { if setLifeCycleFlag != true { setLifeCycleFlag = true stateType := string(databasev1alpha1.CrdReconcileWaitingState) @@ -1394,52 +1517,96 @@ 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 + 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 } } } + 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 @@ -1447,13 +1614,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) @@ -1462,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 } @@ -1489,10 +1664,11 @@ 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] - if OraShardSpex.IsDelete == true { + if shardingv1.CheckIsDeleteFlag(OraShardSpex.IsDelete, instance, r.Log) { if setLifeCycleFlag != true { setLifeCycleFlag = true stateType := string(databasev1alpha1.CrdReconcileWaitingState) @@ -1517,7 +1693,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 @@ -1540,28 +1716,53 @@ 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) + 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 + // 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 { + 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 { + // 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 + } + } } } + // 7th Step remove the shards from the GSM // This steps will delete the shard entry from the GSM // It will delete CDB from catalog @@ -1571,8 +1772,10 @@ 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 } + // 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)) @@ -1644,7 +1847,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, ) { @@ -1672,7 +1875,7 @@ func (r *ShardingDatabaseReconciler) gsmInvitedNodeOp(instance *databasev1alpha1 count = count + 1 continue } - err, _, _ = shardingv1.ExecCommand(gsmPodName.Name, shardingv1.GetShardInviteNodeCmd(objName), r.kubeClient, r.kubeConfig, instance, r.Log) + _, _, err = shardingv1.ExecCommand(gsmPodName.Name, shardingv1.GetShardInviteNodeCmd(objName), r.kubeClient, r.kubeConfig, instance, r.Log) if err != nil { msg = "Invite delete and add node failed " + shardingv1.GetFmtStr(objName) + " details in GSM." shardingv1.LogMessages("DEBUG", msg, err, instance, r.Log) @@ -1745,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{ @@ -1780,3 +1992,58 @@ 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 + 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] + 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.DeletingState) { + 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. 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 + "]" + shardingv1.LogMessages("INFO", eventMsg, nil, instance, r.Log) + err = nil + } + } + r.publishEvents(instance, eventMsg, currState) + } + return err +} diff --git a/controllers/database/singleinstancedatabase_controller.go b/controllers/database/singleinstancedatabase_controller.go index c69ba5b3..a20fa1fd 100644 --- a/controllers/database/singleinstancedatabase_controller.go +++ b/controllers/database/singleinstancedatabase_controller.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2023 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -48,15 +48,19 @@ import ( dbapi "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" dbcommons "github.com/oracle/oracle-database-operator/commons/database" + "golang.org/x/text/cases" + "golang.org/x/text/language" "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" 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/rest" "k8s.io/client-go/tools/record" ctrl "sigs.k8s.io/controller-runtime" @@ -78,12 +82,21 @@ type SingleInstanceDatabaseReconciler struct { var requeueY ctrl.Result = ctrl.Result{Requeue: true, RequeueAfter: 15 * time.Second} var requeueN ctrl.Result = ctrl.Result{} +// For scheduling reconcile to renew certs if TCPS is enabled +// Default value is requeueN (No reconcile) +var futureRequeue ctrl.Result = requeueN + const singleInstanceDatabaseFinalizer = "database.oracle.com/singleinstancedatabasefinalizer" +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,verbs=create;delete;get;list;patch;update;watch +//+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 // Reconcile is part of the main kubernetes reconciliation loop which aims to // move the current state of the cluster closer to the desired state. @@ -104,6 +117,7 @@ func (r *SingleInstanceDatabaseReconciler) Reconcile(ctx context.Context, req ct singleInstanceDatabase := &dbapi.SingleInstanceDatabase{} cloneFromDatabase := &dbapi.SingleInstanceDatabase{} + referredPrimaryDatabase := &dbapi.SingleInstanceDatabase{} // Execute for every reconcile defer r.updateReconcileStatus(singleInstanceDatabase, ctx, &result, &err, &blocked, &completed) @@ -114,18 +128,40 @@ func (r *SingleInstanceDatabaseReconciler) Reconcile(ctx context.Context, req ct r.Log.Info("Resource not found") return requeueN, nil } + r.Log.Error(err, err.Error()) return requeueY, err } + /* Initialize Status */ + if singleInstanceDatabase.Status.Status == "" { + singleInstanceDatabase.Status.Status = dbcommons.StatusPending + if singleInstanceDatabase.Spec.Edition != "" { + singleInstanceDatabase.Status.Edition = cases.Title(language.English).String(singleInstanceDatabase.Spec.Edition) + } else { + singleInstanceDatabase.Status.Edition = dbcommons.ValueUnavailable + } + singleInstanceDatabase.Status.Role = dbcommons.ValueUnavailable + singleInstanceDatabase.Status.ConnectString = dbcommons.ValueUnavailable + singleInstanceDatabase.Status.PdbConnectString = dbcommons.ValueUnavailable + singleInstanceDatabase.Status.TcpsConnectString = dbcommons.ValueUnavailable + singleInstanceDatabase.Status.OemExpressUrl = dbcommons.ValueUnavailable + singleInstanceDatabase.Status.ReleaseUpdate = dbcommons.ValueUnavailable + r.Status().Update(ctx, singleInstanceDatabase) + } + // Manage SingleInstanceDatabase Deletion result, err = r.manageSingleInstanceDatabaseDeletion(req, ctx, singleInstanceDatabase) 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(singleInstanceDatabase, cloneFromDatabase, ctx, req) + result, err = r.validate(singleInstanceDatabase, cloneFromDatabase, referredPrimaryDatabase, ctx, req) if result.Requeue { r.Log.Info("Spec validation failed, Reconcile queued") return result, nil @@ -135,43 +171,36 @@ func (r *SingleInstanceDatabaseReconciler) Reconcile(ctx context.Context, req ct return result, nil } - // Service creation - result, err = r.createOrReplaceSVC(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 - result, err = r.createOrReplacePVC(ctx, req, singleInstanceDatabase) + // 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, ctx, req) + result, err = r.createOrReplacePods(singleInstanceDatabase, cloneFromDatabase, referredPrimaryDatabase, ctx, req) if result.Requeue { r.Log.Info("Reconcile queued") return result, nil } - if singleInstanceDatabase.Status.DatafilesCreated != "true" { - // Creation of Oracle Wallet for Single Instance Database credentials - result, err = r.createWallet(singleInstanceDatabase, ctx, req) - if result.Requeue { - r.Log.Info("Reconcile queued") - return result, nil - } - if err != nil { - r.Log.Info("Spec validation failed") - return result, nil - } + // Service creation + result, err = r.createOrReplaceSVC(ctx, req, singleInstanceDatabase) + if result.Requeue { + r.Log.Info("Reconcile queued") + return result, nil } // 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 @@ -188,22 +217,71 @@ func (r *SingleInstanceDatabaseReconciler) Reconcile(ctx context.Context, req ct } } - // Update DB config - result, err = r.updateDBConfig(singleInstanceDatabase, readyPod, ctx, req) - if result.Requeue { - r.Log.Info("Reconcile queued") - return result, nil - } + 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 { + r.Log.Info("Reconcile queued") + return result, nil + } + + // Update Init Parameters + result, err = r.updateInitParameters(singleInstanceDatabase, readyPod, ctx, req) + if result.Requeue { + r.Log.Info("Reconcile queued") + return result, nil + } + + // Configure TCPS + result, err = r.configTcps(singleInstanceDatabase, readyPod, ctx, req) + if result.Requeue { + r.Log.Info("Reconcile queued") + return result, nil + } + + } else { + // Database is in role of standby + 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) + + if err != nil { + r.Log.Error(err, err.Error()) + return requeueY, err + } + 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()) + return requeueY, err + } + r.Log.Info("Standby DB open mode modified") + r.Log.Info(out) + } + + singleInstanceDatabase.Status.PrimaryDatabase = referredPrimaryDatabase.Name + // Store all standbyDatabase sid:name in a map to use it during manual switchover. + if len(referredPrimaryDatabase.Status.StandbyDatabases) == 0 { + referredPrimaryDatabase.Status.StandbyDatabases = make(map[string]string) + } + referredPrimaryDatabase.Status.StandbyDatabases[strings.ToUpper(singleInstanceDatabase.Spec.Sid)] = singleInstanceDatabase.Name + r.Status().Update(ctx, referredPrimaryDatabase) - // Update Init Parameters - result, err = r.updateInitParameters(singleInstanceDatabase, readyPod, ctx, req) - if result.Requeue { - r.Log.Info("Reconcile queued") - return result, nil } // Run Datapatch - if singleInstanceDatabase.Status.DatafilesPatched != "true" { + if strings.ToUpper(singleInstanceDatabase.Status.Role) == "PRIMARY" && singleInstanceDatabase.Status.DatafilesPatched != "true" { // add a blocking reconcile condition err = errors.New("processing datapatch execution") blocked = true @@ -217,23 +295,42 @@ 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 } - // 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 r.Log.Info("Reconcile completed") + + // Scheduling a reconcile for certificate renewal, if TCPS is enabled + if futureRequeue != requeueN { + r.Log.Info("Scheduling Reconcile for cert renewal", "Duration(Hours)", futureRequeue.RequeueAfter.Hours()) + copyFutureRequeue := futureRequeue + futureRequeue = requeueN + return copyFutureRequeue, nil + } + return requeueN, nil } -//############################################################################# -// Update each reconcile condtion/status -//############################################################################# +// ############################################################################# +// +// Update each reconcile condtion/status +// +// ############################################################################# func (r *SingleInstanceDatabaseReconciler) updateReconcileStatus(m *dbapi.SingleInstanceDatabase, ctx context.Context, result *ctrl.Result, err *error, blocked *bool, completed *bool) { + // Always refresh status before a reconcile + defer r.Status().Update(ctx, m) + errMsg := func() string { if *err != nil { return (*err).Error() @@ -284,54 +381,58 @@ func (r *SingleInstanceDatabaseReconciler) updateReconcileStatus(m *dbapi.Single meta.RemoveStatusCondition(&m.Status.Conditions, condition.Type) } meta.SetStatusCondition(&m.Status.Conditions, condition) - // Always refresh status before a reconcile - r.Status().Update(ctx, m) } -//############################################################################# -// Validate the CRD specs -// m = SingleInstanceDatabase -// n = CloneFromDatabase -//############################################################################# +// ############################################################################# +// +// Validate the CRD specs +// m = SingleInstanceDatabase +// n = CloneFromDatabase +// +// ############################################################################# func (r *SingleInstanceDatabaseReconciler) validate(m *dbapi.SingleInstanceDatabase, - n *dbapi.SingleInstanceDatabase, ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + n *dbapi.SingleInstanceDatabase, rp *dbapi.SingleInstanceDatabase, ctx context.Context, req ctrl.Request) (ctrl.Result, error) { var err error eventReason := "Spec Error" var eventMsgs []string - // If Express Edition , Ensure Replicas=1 - if m.Spec.Edition == "express" && m.Spec.Replicas != 1 { - eventMsgs = append(eventMsgs, "XE supports only one replica") + r.Log.Info("Entering reconcile validation") + + //First check image pull secrets + if m.Spec.Image.PullSecrets != "" { + secret := &corev1.Secret{} + err = r.Get(ctx, types.NamespacedName{Name: m.Spec.Image.PullSecrets, Namespace: m.Namespace}, secret) + if err != nil { + if apierrors.IsNotFound(err) { + // Secret not found + r.Recorder.Eventf(m, corev1.EventTypeWarning, eventReason, err.Error()) + r.Log.Info(err.Error()) + m.Status.Status = dbcommons.StatusError + return requeueY, err + } + r.Log.Error(err, err.Error()) + return requeueY, err + } + } + + // If Express/Free Edition, ensure Replicas=1 + if (m.Spec.Edition == "express" || m.Spec.Edition == "free") && m.Spec.Replicas > 1 { + eventMsgs = append(eventMsgs, m.Spec.Edition+" edition supports only one replica") } - // If Block Volume , Ensure Replicas=1 - if m.Spec.Persistence.AccessMode == "ReadWriteOnce" && m.Spec.Replicas != 1 { - eventMsgs = append(eventMsgs, "accessMode ReadWriteOnce supports only one replica") + // If no persistence, ensure Replicas=1 + if m.Spec.Persistence.Size == "" && m.Spec.Replicas > 1 { + eventMsgs = append(eventMsgs, "replicas should be 1 if no persistence is specified") } if m.Status.Sid != "" && !strings.EqualFold(m.Spec.Sid, m.Status.Sid) { eventMsgs = append(eventMsgs, "sid cannot be updated") } - edition := m.Spec.Edition - if m.Spec.Edition == "" { - edition = "Enterprise" - } - if m.Spec.CloneFrom == "" && m.Status.Edition != "" && !strings.EqualFold(m.Status.Edition, 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.CloneFrom != "" { - eventMsgs = append(eventMsgs, "cloning not supported for express edition") - } - if m.Status.OrdsReference != "" && m.Status.Persistence.AccessMode != "" && m.Status.Persistence != m.Spec.Persistence { + if m.Status.OrdsReference != "" && m.Status.Persistence.Size != "" && m.Status.Persistence != m.Spec.Persistence { eventMsgs = append(eventMsgs, "uninstall ORDS to change Peristence") } if len(eventMsgs) > 0 { @@ -341,8 +442,8 @@ func (r *SingleInstanceDatabaseReconciler) validate(m *dbapi.SingleInstanceDatab return requeueN, err } - // Validating the secret - if m.Status.DatafilesCreated != "true" { + // Validating the secret. Pre-built db doesnt need secret + if !m.Spec.Image.PrebuiltDB && m.Status.DatafilesCreated != "true" { secret := &corev1.Secret{} err = r.Get(ctx, types.NamespacedName{Name: m.Spec.AdminPassword.SecretName, Namespace: m.Namespace}, secret) if err != nil { @@ -361,22 +462,20 @@ func (r *SingleInstanceDatabaseReconciler) validate(m *dbapi.SingleInstanceDatab // update status fields m.Status.Sid = m.Spec.Sid - m.Status.Edition = strings.Title(edition) m.Status.Charset = m.Spec.Charset m.Status.Pdbname = m.Spec.Pdbname m.Status.Persistence = m.Spec.Persistence - if m.Spec.CloneFrom == "" { - m.Status.CloneFrom = dbcommons.NoCloneRef - } else { - m.Status.CloneFrom = m.Spec.CloneFrom - } - if m.Spec.CloneFrom != "" { + m.Status.PrebuiltDB = m.Spec.Image.PrebuiltDB + + if m.Spec.CreateAs == "clone" { // Once a clone database has created , it has no link with its reference - if m.Status.DatafilesCreated == "true" { + if m.Status.DatafilesCreated == "true" || + !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()) @@ -389,16 +488,16 @@ func (r *SingleInstanceDatabaseReconciler) validate(m *dbapi.SingleInstanceDatab if n.Status.Status != dbcommons.StatusReady { m.Status.Status = dbcommons.StatusPending eventReason := "Source Database Pending" - eventMsg := "waiting for source database " + m.Spec.CloneFrom + " to be Ready" + 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 } - if !n.Spec.ArchiveLog { + if !*n.Spec.ArchiveLog { m.Status.Status = dbcommons.StatusPending - eventReason := "Source Database Pending" - eventMsg := "waiting for ArchiveLog to turn ON " + n.Name + eventReason := "Source Database Check" + eventMsg := "enable ArchiveLog for database " + n.Name r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) r.Log.Info(eventMsg) err = errors.New(eventMsg) @@ -406,16 +505,67 @@ func (r *SingleInstanceDatabaseReconciler) validate(m *dbapi.SingleInstanceDatab } m.Status.Edition = n.Status.Edition + m.Status.PrimaryDatabase = n.Name + } + + 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) + 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.Sid == rp.Spec.Sid { + r.Log.Info("Standby database SID can not be same as the Primary database SID") + r.Recorder.Eventf(m, corev1.EventTypeWarning, "Spec Error", "Standby and Primary database SID can not be same") + m.Status.Status = dbcommons.StatusError + return requeueY, err + } + + if rp.Status.IsTcpsEnabled { + r.Recorder.Eventf(m, corev1.EventTypeWarning, "Cannot Create", "Standby for TCPS enabled Primary Database is not supported ") + m.Status.Status = dbcommons.StatusError + return requeueY, nil + } + + if m.Status.DatafilesCreated == "true" || + !dbcommons.IsSourceDatabaseOnCluster(m.Spec.PrimaryDatabaseRef) { + return requeueN, nil + } + m.Status.Edition = rp.Status.Edition + + err = ValidatePrimaryDatabaseForStandbyCreation(r, m, rp, ctx, req) + if err != nil { + return requeueY, err + } + + r.Log.Info("Setting up Primary Database for standby creation...") + err = SetupPrimaryDatabase(r, m, rp, ctx, req) + if err != nil { + return requeueY, err + } } + r.Log.Info("Completed reconcile validation") + return requeueN, nil } -//############################################################################# -// Instantiate POD spec from SingleInstanceDatabase spec -//############################################################################# -func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleInstanceDatabase, n *dbapi.SingleInstanceDatabase) *corev1.Pod { +// ############################################################################# +// +// Instantiate POD spec from SingleInstanceDatabase spec +// +// ############################################################################# +func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleInstanceDatabase, n *dbapi.SingleInstanceDatabase, rp *dbapi.SingleInstanceDatabase, + requiredAffinity bool) *corev1.Pod { + // POD spec pod := &corev1.Pod{ TypeMeta: metav1.TypeMeta{ Kind: "Pod", @@ -429,30 +579,168 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns }, }, Spec: corev1.PodSpec{ + Affinity: func() *corev1.Affinity { + if m.Spec.Persistence.AccessMode == "ReadWriteOnce" { + if requiredAffinity { + return &corev1.Affinity{ + PodAffinity: &corev1.PodAffinity{ + RequiredDuringSchedulingIgnoredDuringExecution: []corev1.PodAffinityTerm{{ + LabelSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{{ + Key: "app", + Operator: metav1.LabelSelectorOpIn, + Values: []string{m.Name}, + }}, + }, + TopologyKey: "kubernetes.io/hostname", + }}, + }, + } + } else { + return &corev1.Affinity{ + PodAffinity: &corev1.PodAffinity{ + PreferredDuringSchedulingIgnoredDuringExecution: []corev1.WeightedPodAffinityTerm{{ + Weight: 100, + PodAffinityTerm: corev1.PodAffinityTerm{ + LabelSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{{ + Key: "app", + Operator: metav1.LabelSelectorOpIn, + Values: []string{m.Name}, + }}, + }, + TopologyKey: "kubernetes.io/hostname", + }, + }}, + }, + } + } + } + // For ReadWriteMany Access, spread out the PODs + return &corev1.Affinity{ + PodAntiAffinity: &corev1.PodAntiAffinity{ + PreferredDuringSchedulingIgnoredDuringExecution: []corev1.WeightedPodAffinityTerm{{ + Weight: 100, + PodAffinityTerm: corev1.PodAffinityTerm{ + LabelSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{{ + Key: "app", + Operator: metav1.LabelSelectorOpIn, + Values: []string{m.Name}, + }}, + }, + TopologyKey: "kubernetes.io/hostname", + }, + }}, + }, + } + }(), Volumes: []corev1.Volume{{ - Name: "datamount", + Name: "datafiles-vol", + VolumeSource: func() corev1.VolumeSource { + if m.Spec.Persistence.Size == "" { + return corev1.VolumeSource{EmptyDir: &corev1.EmptyDirVolumeSource{}} + } + /* Persistence is specified */ + return corev1.VolumeSource{ + PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ + ClaimName: m.Name, + ReadOnly: false, + }, + } + }(), + }, { + Name: "oracle-pwd-vol", VolumeSource: corev1.VolumeSource{ - PersistentVolumeClaim: &corev1.PersistentVolumeClaimVolumeSource{ - ClaimName: m.Name, - ReadOnly: false, + Secret: &corev1.SecretVolumeSource{ + SecretName: m.Spec.AdminPassword.SecretName, + Optional: func() *bool { i := (m.Spec.Edition != "express" && m.Spec.Edition != "free"); return &i }(), + Items: []corev1.KeyToPath{{ + Key: m.Spec.AdminPassword.SecretKey, + Path: "oracle_pwd", + }}, }, }, + }, { + 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 + }, + }, + }, + } + }(), + }, { + 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 { - if m.Spec.Edition != "express" { - return []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 /opt/oracle/oradata", int(dbcommons.ORACLE_UID), int(dbcommons.ORACLE_GUID))}, + Command: []string{"/bin/sh", "-c", fmt.Sprintf("chown %d:%d /opt/oracle/oradata || true", int(dbcommons.ORACLE_UID), int(dbcommons.ORACLE_GUID))}, SecurityContext: &corev1.SecurityContext{ // User ID 0 means, root user RunAsUser: func() *int64 { i := int64(0); return &i }(), }, VolumeMounts: []corev1.VolumeMount{{ MountPath: "/opt/oracle/oradata", - Name: "datamount", + 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), + }, + }, + }) + } + /* Wallet only for edition barring express and free editions, non-prebuiltDB */ + if (m.Spec.Edition != "express" && m.Spec.Edition != "free") && !m.Spec.Image.PrebuiltDB { + initContainers = append(initContainers, corev1.Container{ Name: "init-wallet", Image: m.Spec.Image.PullFrom, Env: []corev1.EnvVar{ @@ -466,21 +754,25 @@ 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 { - edition = n.Spec.Edition - if n.Spec.Edition == "" { - edition = "enterprise" + if !dbcommons.IsSourceDatabaseOnCluster(m.Spec.PrimaryDatabaseRef) { + edition = m.Spec.Edition + } else { + edition = n.Spec.Edition + if n.Spec.Edition == "" { + edition = "enterprise" + } } } return []string{"-c", fmt.Sprintf(dbcommons.InitWalletCMD, edition)} @@ -491,59 +783,129 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns }, VolumeMounts: []corev1.VolumeMount{{ MountPath: "/opt/oracle/oradata", - Name: "datamount", + Name: "datafiles-vol", }}, - }} + }) } - return []corev1.Container{{ - Name: "init-permissions", - Image: m.Spec.Image.PullFrom, - Command: []string{"/bin/sh", "-c", fmt.Sprintf("chown %d:%d /opt/oracle/oradata", int(dbcommons.ORACLE_UID), int(dbcommons.ORACLE_GUID))}, - SecurityContext: &corev1.SecurityContext{ - // User ID 0 means, root user - RunAsUser: func() *int64 { i := int64(0); return &i }(), - }, - VolumeMounts: []corev1.VolumeMount{{ - MountPath: "/opt/oracle/oradata", - Name: "datamount", - }}, - }} + return initContainers }(), Containers: []corev1.Container{{ Name: m.Name, Image: m.Spec.Image.PullFrom, - Lifecycle: &corev1.Lifecycle{ - PreStop: &corev1.Handler{ - Exec: &corev1.ExecAction{ - Command: []string{"/bin/sh", "-c", "/bin/echo -en 'shutdown abort;\n' | env ORACLE_SID=${ORACLE_SID^^} sqlplus -S / as sysdba"}, - }, + SecurityContext: &corev1.SecurityContext{ + Capabilities: &corev1.Capabilities{ + // Allow priority elevation for DB processes + Add: []corev1.Capability{"SYS_NICE"}, }, }, - ImagePullPolicy: corev1.PullAlways, - Ports: []corev1.ContainerPort{{ContainerPort: 1521}, {ContainerPort: 5500}}, - - ReadinessProbe: &corev1.Probe{ - Handler: corev1.Handler{ + Lifecycle: &corev1.Lifecycle{ + PreStop: &corev1.LifecycleHandler{ 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 "}, + 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" + } + return []string{"/bin/sh", "-c", "/bin/echo -en 'shutdown " + shutdown_mode + ";\n' | env ORACLE_SID=${ORACLE_SID^^} sqlplus -S / as sysdba"} + }(), }, }, - InitialDelaySeconds: 20, - TimeoutSeconds: 20, - PeriodSeconds: func() int32 { - if m.Spec.ReadinessCheckPeriod > 0 { - return int32(m.Spec.ReadinessCheckPeriod) - } - return 30 - }(), }, - - VolumeMounts: []corev1.VolumeMount{{ - MountPath: "/opt/oracle/oradata", - Name: "datamount", - }}, + Ports: []corev1.ContainerPort{{ContainerPort: dbcommons.CONTAINER_LISTENER_PORT}, {ContainerPort: 5500}}, + + 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 + }(), + } + } 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 != "" { + mounts = append(mounts, corev1.VolumeMount{ + MountPath: "/opt/oracle/oradata", + Name: "datafiles-vol", + }) + } + if m.Spec.Edition == "express" || m.Spec.Edition == "free" || m.Spec.Image.PrebuiltDB { + // mounts pwd as secrets for express edition or prebuilt db + mounts = append(mounts, corev1.VolumeMount{ + MountPath: "/run/secrets/oracle_pwd", + ReadOnly: true, + Name: "oracle-pwd-vol", + SubPath: "oracle_pwd", + }) + } + if m.Spec.TcpsTlsSecret != "" { + mounts = append(mounts, corev1.VolumeMount{ + MountPath: dbcommons.TlsCertsLocation, + ReadOnly: true, + 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 { - if m.Spec.CloneFrom == "" { + // adding XE support, useful for dev/test/CI-CD + if m.Spec.Edition == "express" || m.Spec.Edition == "free" { return []corev1.EnvVar{ { Name: "SVC_HOST", @@ -551,107 +913,220 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns }, { Name: "SVC_PORT", - Value: "1521", + Value: strconv.Itoa(int(dbcommons.CONTAINER_LISTENER_PORT)), }, { - Name: "CREATE_PDB", - Value: func() string { - if m.Spec.Pdbname != "" { - return "true" - } - return "false" - }(), + Name: "ORACLE_CHARACTERSET", + Value: m.Spec.Charset, }, { - Name: "ORACLE_SID", - Value: strings.ToUpper(m.Spec.Sid), + Name: "ORACLE_EDITION", + Value: m.Spec.Edition, }, + } + } + if m.Spec.CreateAs == "clone" { + // Clone DB use-case + return []corev1.EnvVar{ { - Name: "WALLET_DIR", - Value: "/opt/oracle/oradata/dbconfig/$(ORACLE_SID)/.wallet", + Name: "SVC_HOST", + Value: m.Name, }, { - Name: "ORACLE_PDB", - Value: m.Spec.Pdbname, + Name: "SVC_PORT", + Value: strconv.Itoa(int(dbcommons.CONTAINER_LISTENER_PORT)), }, { - Name: "ORACLE_CHARACTERSET", - Value: m.Spec.Charset, + Name: "ORACLE_SID", + Value: strings.ToUpper(m.Spec.Sid), }, { - Name: "ORACLE_EDITION", - Value: m.Spec.Edition, + Name: "WALLET_DIR", + Value: "/opt/oracle/oradata/dbconfig/${ORACLE_SID}/.wallet", }, { - Name: "INIT_SGA_SIZE", + Name: "PRIMARY_DB_CONN_STR", Value: func() string { - if m.Spec.InitParams.SgaTarget > 0 && m.Spec.InitParams.PgaAggregateTarget > 0 { - return strconv.Itoa(m.Spec.InitParams.SgaTarget) + if dbcommons.IsSourceDatabaseOnCluster(m.Spec.PrimaryDatabaseRef) { + return n.Name + ":" + strconv.Itoa(int(dbcommons.CONTAINER_LISTENER_PORT)) + "/" + n.Spec.Sid } - return "" + return m.Spec.PrimaryDatabaseRef }(), }, + CreateOracleHostnameEnvVarObj(m, n), { - Name: "INIT_PGA_SIZE", - Value: func() string { - if m.Spec.InitParams.SgaTarget > 0 && m.Spec.InitParams.PgaAggregateTarget > 0 { - return strconv.Itoa(m.Spec.InitParams.SgaTarget) - } - return "" - }(), + Name: "CLONE_DB", + Value: "true", }, { Name: "SKIP_DATAPATCH", Value: "true", }, } - } - return []corev1.EnvVar{ - { - Name: "SVC_HOST", - Value: m.Name, - }, - { - Name: "SVC_PORT", - Value: "1521", - }, - { - Name: "ORACLE_SID", - Value: strings.ToUpper(m.Spec.Sid), - }, + + } else if m.Spec.CreateAs == "standby" { + //Standby DB Usecase + return []corev1.EnvVar{ + { + Name: "SVC_HOST", + Value: m.Name, + }, + { + Name: "SVC_PORT", + Value: strconv.Itoa(int(dbcommons.CONTAINER_LISTENER_PORT)), + }, + { + Name: "ORACLE_SID", + Value: strings.ToUpper(m.Spec.Sid), + }, + { + Name: "WALLET_DIR", + Value: "/opt/oracle/oradata/dbconfig/${ORACLE_SID}/.wallet", + }, + { + 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: "PRIMARY_SID", + Value: strings.ToUpper(rp.Spec.Sid), + }, + { + Name: "PRIMARY_IP", + Value: rp.Name, + }, + { + Name: "CREATE_PDB", + Value: func() string { + if rp.Spec.Pdbname != "" { + return "true" + } + return "false" + }(), + }, + { + Name: "ORACLE_HOSTNAME", + ValueFrom: &corev1.EnvVarSource{ + FieldRef: &corev1.ObjectFieldSelector{ + FieldPath: "status.podIP", + }, + }, + }, + { + Name: "STANDBY_DB", + Value: "true", + }, + { + Name: "SKIP_DATAPATCH", + Value: "true", + }, + } + } + + return []corev1.EnvVar{ + { + Name: "SVC_HOST", + Value: m.Name, + }, + { + Name: "SVC_PORT", + Value: strconv.Itoa(int(dbcommons.CONTAINER_LISTENER_PORT)), + }, + { + Name: "CREATE_PDB", + Value: func() string { + if m.Spec.Pdbname != "" { + return "true" + } + return "false" + }(), + }, { - Name: "WALLET_DIR", - Value: "/opt/oracle/oradata/dbconfig/$(ORACLE_SID)/.wallet", + Name: "ORACLE_SID", + Value: strings.ToUpper(m.Spec.Sid), }, { - Name: "PRIMARY_DB_CONN_STR", - Value: n.Name + ":1521/" + n.Spec.Sid, + Name: "WALLET_DIR", + Value: func() string { + if m.Spec.Image.PrebuiltDB { + return "" // No wallets for prebuilt DB + } + return "/opt/oracle/oradata/dbconfig/${ORACLE_SID}/.wallet" + }(), }, { - Name: "PRIMARY_SID", - Value: strings.ToUpper(n.Spec.Sid), + Name: "ORACLE_PDB", + Value: m.Spec.Pdbname, }, { - Name: "PRIMARY_NAME", - Value: n.Name, + Name: "ORACLE_CHARACTERSET", + Value: m.Spec.Charset, }, { - Name: "ORACLE_HOSTNAME", - ValueFrom: &corev1.EnvVarSource{ - FieldRef: &corev1.ObjectFieldSelector{ - FieldPath: "status.podIP", - }, - }, + Name: "ORACLE_EDITION", + Value: m.Spec.Edition, }, { - Name: "CLONE_DB", - Value: "true", + Name: "INIT_SGA_SIZE", + Value: func() string { + if m.Spec.InitParams != nil && m.Spec.InitParams.SgaTarget > 0 && m.Spec.InitParams.PgaAggregateTarget > 0 { + return strconv.Itoa(m.Spec.InitParams.SgaTarget) + } + return "" + }(), + }, + { + Name: "INIT_PGA_SIZE", + Value: func() string { + if m.Spec.InitParams != nil && m.Spec.InitParams.SgaTarget > 0 && m.Spec.InitParams.PgaAggregateTarget > 0 { + return strconv.Itoa(m.Spec.InitParams.SgaTarget) + } + return "" + }(), }, { Name: "SKIP_DATAPATCH", Value: "true", }, } + + }(), + + 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{} + } + }(), }}, @@ -669,17 +1144,15 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns SecurityContext: &corev1.PodSecurityContext{ RunAsUser: func() *int64 { - i := int64(0) - if m.Spec.Edition != "express" { - i = int64(dbcommons.ORACLE_UID) - } + i := int64(dbcommons.ORACLE_UID) return &i }(), RunAsGroup: func() *int64 { - i := int64(0) - if m.Spec.Edition != "express" { - i = int64(dbcommons.ORACLE_GUID) - } + i := int64(dbcommons.ORACLE_GUID) + return &i + }(), + FSGroup: func() *int64 { + i := int64(dbcommons.ORACLE_GUID) return &i }(), }, @@ -688,61 +1161,90 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePodSpec(m *dbapi.SingleIns Name: m.Spec.Image.PullSecrets, }, }, + ServiceAccountName: m.Spec.ServiceAccountName, }, } + // Adding pod anti-affinity for standby cases + if m.Spec.CreateAs == "standby" { + weightedPodAffinityTerm := corev1.WeightedPodAffinityTerm{ + Weight: 100, + PodAffinityTerm: corev1.PodAffinityTerm{ + LabelSelector: &metav1.LabelSelector{ + MatchExpressions: []metav1.LabelSelectorRequirement{{ + Key: "app", + Operator: metav1.LabelSelectorOpIn, + Values: []string{rp.Name}, + }}, + }, + TopologyKey: "kubernetes.io/hostname", + }, + } + if m.Spec.Persistence.AccessMode == "ReadWriteOnce" { + pod.Spec.Affinity.PodAntiAffinity = &corev1.PodAntiAffinity{ + PreferredDuringSchedulingIgnoredDuringExecution: []corev1.WeightedPodAffinityTerm{ + weightedPodAffinityTerm, + }, + } + } else { + pod.Spec.Affinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution = + append(pod.Spec.Affinity.PodAntiAffinity.PreferredDuringSchedulingIgnoredDuringExecution, weightedPodAffinityTerm) + } + + } + // Set SingleInstanceDatabase instance as the owner and controller ctrl.SetControllerReference(m, pod, r.Scheme) return pod + } -//############################################################################# -// Instantiate Service spec from SingleInstanceDatabase spec -//############################################################################# -func (r *SingleInstanceDatabaseReconciler) instantiateSVCSpec(m *dbapi.SingleInstanceDatabase) *corev1.Service { +// ############################################################################# +// +// Instantiate Service spec from SingleInstanceDatabase spec +// +// ############################################################################# +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: m.Name, + Name: svcName, 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, - }, - }, + Ports: []corev1.ServicePort{}, Selector: map[string]string{ "app": m.Name, }, - Type: corev1.ServiceType(func() string { - if m.Spec.LoadBalancer { - return "LoadBalancer" - } - return "NodePort" - }()), + Type: svcType, }, } + svc.Spec.Ports = ports // Set SingleInstanceDatabase instance as the owner and controller ctrl.SetControllerReference(m, svc, r.Scheme) return svc } -//############################################################################# -// Instantiate Persistent Volume Claim spec from SingleInstanceDatabase spec -//############################################################################# +// ############################################################################# +// +// Instantiate Persistent Volume Claim spec from SingleInstanceDatabase spec +// +// ############################################################################# func (r *SingleInstanceDatabaseReconciler) instantiatePVCSpec(m *dbapi.SingleInstanceDatabase) *corev1.PersistentVolumeClaim { pvc := &corev1.PersistentVolumeClaim{ @@ -755,6 +1257,15 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePVCSpec(m *dbapi.SingleIns Labels: map[string]string{ "app": m.Name, }, + Annotations: func() map[string]string { + if m.Spec.Persistence.VolumeClaimAnnotation != "" { + strParts := strings.Split(m.Spec.Persistence.VolumeClaimAnnotation, ":") + annotationMap := make(map[string]string) + annotationMap[strParts[0]] = strParts[1] + return annotationMap + } + return nil + }(), }, Spec: corev1.PersistentVolumeClaimSpec{ AccessModes: func() []corev1.PersistentVolumeAccessMode { @@ -762,13 +1273,30 @@ 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), }, }, StorageClassName: &m.Spec.Persistence.StorageClass, + VolumeName: m.Spec.Persistence.DatafilesVolumeName, + Selector: func() *metav1.LabelSelector { + if m.Spec.Persistence.StorageClass != "oci" { + return nil + } + return &metav1.LabelSelector{ + MatchLabels: 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 + }(), + } + }(), }, } // Set SingleInstanceDatabase instance as the owner and controller @@ -776,23 +1304,31 @@ func (r *SingleInstanceDatabaseReconciler) instantiatePVCSpec(m *dbapi.SingleIns return pvc } -//############################################################################# -// Stake a claim for Persistent Volume -//############################################################################# -func (r *SingleInstanceDatabaseReconciler) createOrReplacePVC(ctx context.Context, req ctrl.Request, +// ############################################################################# +// +// 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", req.NamespacedName) + 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: m.Name, Namespace: m.Namespace}, pvc) + err := r.Get(ctx, types.NamespacedName{Name: pvcName, 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) || - pvc.Spec.AccessModes[0] != corev1.PersistentVolumeAccessMode(m.Spec.Persistence.AccessMode) { + 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 { @@ -813,7 +1349,57 @@ func (r *SingleInstanceDatabaseReconciler) createOrReplacePVC(ctx context.Contex } if pvcDeleted || err != nil && apierrors.IsNotFound(err) { // Define a new PVC - pvc = r.instantiatePVCSpec(m) + + // 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.VolumeResourceRequirements{ + 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 { @@ -829,170 +1415,544 @@ func (r *SingleInstanceDatabaseReconciler) createOrReplacePVC(ctx context.Contex return requeueN, nil } -//############################################################################# -// Create a Service for SingleInstanceDatabase -//############################################################################# -func (r *SingleInstanceDatabaseReconciler) createOrReplaceSVC(ctx context.Context, req ctrl.Request, +// ############################################################################# +// +// Stake a claim for Persistent Volume for Datafiles Volume +// +// ############################################################################# +func (r *SingleInstanceDatabaseReconciler) createOrReplacePVCforDatafilesVol(ctx context.Context, req ctrl.Request, m *dbapi.SingleInstanceDatabase) (ctrl.Result, error) { - log := r.Log.WithValues("createOrReplaceSVC", req.NamespacedName) + 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 + } - svcDeleted := false - // Check if the Service already exists, if not create a new one - svc := &corev1.Service{} + pvcDeleted := false + // 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: m.Name, Namespace: m.Namespace}, svc) + err := r.Get(ctx, types.NamespacedName{Name: m.Name, Namespace: m.Namespace}, pvc) + if err == nil { - svcType := corev1.ServiceType("NodePort") - if m.Spec.LoadBalancer { - svcType = corev1.ServiceType("LoadBalancer") - } + if *pvc.Spec.StorageClassName != m.Spec.Persistence.StorageClass || + (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 + // 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 + } - if svc.Spec.Type != svcType { - log.Info("Deleting SVC", " name ", svc.Name) - err = r.Delete(ctx, svc) + // deleting persistent volume claim + log.Info("Deleting PVC", " name ", pvc.Name) + err = r.Delete(ctx, pvc) if err != nil { - r.Log.Error(err, "Failed to delete svc", " Name", svc.Name) + r.Log.Error(err, "Failed to delete Pvc", "Pvc.Name", pvc.Name) return requeueN, err } - svcDeleted = true + 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 svcDeleted || 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) + + if pvcDeleted || err != nil && apierrors.IsNotFound(err) { + // Define a new PVC + pvc = r.instantiatePVCSpec(m) + 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 Service", "Service.Namespace", svc.Namespace, "Service.Name", svc.Name) + 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 Service") + log.Error(err, "Failed to get PVC") return requeueY, err } - log.Info("Found Existing Service ", "Service Name ", svc.Name) - - m.Status.ConnectString = dbcommons.ValueUnavailable - m.Status.PdbConnectString = dbcommons.ValueUnavailable - m.Status.OemExpressUrl = dbcommons.ValueUnavailable - pdbName := "ORCLPDB1" - if m.Spec.Pdbname != "" { - pdbName = strings.ToUpper(m.Spec.Pdbname) - } - if m.Spec.LoadBalancer { - m.Status.ClusterConnectString = svc.Name + "." + svc.Namespace + ":" + fmt.Sprint(svc.Spec.Ports[0].Port) + "/" + strings.ToUpper(m.Spec.Sid) - if len(svc.Status.LoadBalancer.Ingress) > 0 { - m.Status.ConnectString = svc.Status.LoadBalancer.Ingress[0].IP + ":" + fmt.Sprint(svc.Spec.Ports[0].Port) + "/" + strings.ToUpper(m.Spec.Sid) - m.Status.PdbConnectString = svc.Status.LoadBalancer.Ingress[0].IP + ":" + fmt.Sprint(svc.Spec.Ports[0].Port) + "/" + strings.ToUpper(pdbName) - m.Status.OemExpressUrl = "https://" + svc.Status.LoadBalancer.Ingress[0].IP + ":" + fmt.Sprint(svc.Spec.Ports[1].Port) + "/em" - } - return requeueN, nil - } - - m.Status.ClusterConnectString = svc.Name + "." + svc.Namespace + ":" + fmt.Sprint(svc.Spec.Ports[0].Port) + "/" + strings.ToUpper(m.Spec.Sid) - nodeip := dbcommons.GetNodeIp(r, ctx, req) - if nodeip != "" { - m.Status.ConnectString = nodeip + ":" + fmt.Sprint(svc.Spec.Ports[0].NodePort) + "/" + strings.ToUpper(m.Spec.Sid) - m.Status.PdbConnectString = nodeip + ":" + fmt.Sprint(svc.Spec.Ports[0].NodePort) + "/" + strings.ToUpper(pdbName) - m.Status.OemExpressUrl = "https://" + nodeip + ":" + fmt.Sprint(svc.Spec.Ports[1].NodePort) + "/em" - } return requeueN, nil } -//############################################################################# -// Create new Pods or delete old/extra pods -// m = SingleInstanceDatabase -// n = CloneFromDatabase -//############################################################################# -func (r *SingleInstanceDatabaseReconciler) createOrReplacePods(m *dbapi.SingleInstanceDatabase, n *dbapi.SingleInstanceDatabase, - ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - log := r.Log.WithValues("createOrReplacePods", req.NamespacedName) +// ############################################################################# +// +// Create Services for SingleInstanceDatabase +// +// ############################################################################# +func (r *SingleInstanceDatabaseReconciler) createOrReplaceSVC(ctx context.Context, req ctrl.Request, + m *dbapi.SingleInstanceDatabase) (ctrl.Result, error) { - oldVersion := "" - oldImage := "" + log := r.Log.WithValues("createOrReplaceSVC", req.NamespacedName) - // call FindPods() to fetch pods all version/images of the same SIDB kind - readyPod, replicasFound, available, podsMarkedToBeDeleted, err := dbcommons.FindPods(r, "", "", m.Name, m.Namespace, ctx, req) - if err != nil { - log.Error(err, err.Error()) - return requeueY, err - } - if m.Spec.Edition == "express" && podsMarkedToBeDeleted > 0 { - // Recreate new pods only after earlier pods are terminated completely - return requeueY, err - } - if readyPod.Name != "" { - available = append(available, readyPod) - } + /** Two k8s services gets created: + 1. One service is ClusterIP service for cluster only communications on the listener port 1521, + 2. One service is NodePort/LoadBalancer (according to the YAML specs) for users to connect + **/ - for _, pod := range available { - if pod.Labels["version"] != m.Spec.Image.Version { - oldVersion = pod.Labels["version"] + // clusterSvc is the cluster-wide service and extSvc is the external service for the users to connect + clusterSvc := &corev1.Service{} + extSvc := &corev1.Service{} + + clusterSvcName := m.Name + extSvcName := m.Name + "-ext" + + // svcPort is the intended port for extSvc taken from singleinstancedatabase YAML file for normal database connection + // If loadBalancer is true, it would be the listener port otherwise it would be node port + svcPort := func() int32 { + if m.Spec.ListenerPort != 0 { + return int32(m.Spec.ListenerPort) + } else { + return dbcommons.CONTAINER_LISTENER_PORT } - if pod.Spec.Containers[0].Image != m.Spec.Image.PullFrom { - oldImage = pod.Spec.Containers[0].Image + }() + + // tcpsSvcPort is the intended port for extSvc taken from singleinstancedatabase YAML file for TCPS connection + // If loadBalancer is true, it would be the listener port otherwise it would be node port + tcpsSvcPort := func() int32 { + if m.Spec.TcpsListenerPort != 0 { + return int32(m.Spec.TcpsListenerPort) + } else { + return dbcommons.CONTAINER_TCPS_PORT + } + }() + + // Querying for the K8s service resources + getClusterSvcErr := r.Get(ctx, types.NamespacedName{Name: clusterSvcName, Namespace: m.Namespace}, clusterSvc) + getExtSvcErr := r.Get(ctx, types.NamespacedName{Name: extSvcName, Namespace: m.Namespace}, extSvc) + + 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")) + log.Info("Creating a new service", "Service.Namespace", svc.Namespace, "Service.Name", svc.Name) + err := r.Create(ctx, svc) + if err != nil { + log.Error(err, "Failed to create new service", "Service.Namespace", svc.Namespace, "Service.Name", svc.Name) + return requeueY, err } + } else if getClusterSvcErr != nil { + // Error encountered in obtaining the clusterSvc service resource + log.Error(getClusterSvcErr, "Error encountered in obtaining the service", clusterSvcName) + return requeueY, getClusterSvcErr + } + // extSvcType defines the type of the service (LoadBalancer/NodePort) for extSvc as specified in the singleinstancedatabase.yaml file + extSvcType := corev1.ServiceType("NodePort") + if m.Spec.LoadBalancer { + extSvcType = corev1.ServiceType("LoadBalancer") } - // podVersion, podImage if old version PODs are found - imageChanged := oldVersion != "" || oldImage != "" + isExtSvcFound := true - if !imageChanged { - eventReason := "" - eventMsg := "" - if replicasFound == m.Spec.Replicas { - return requeueN, nil + if getExtSvcErr != nil && apierrors.IsNotFound(getExtSvcErr) { + isExtSvcFound = false + } else if getExtSvcErr != nil { + // Error encountered in obtaining the extSvc service resource + log.Error(getExtSvcErr, "Error encountered in obtaining the service", extSvcName) + return requeueY, getExtSvcErr + } else { + // Counting required number of ports in extSvc + requiredPorts := 2 + if m.Spec.EnableTCPS && m.Spec.ListenerPort != 0 { + requiredPorts = 3 } - if replicasFound < m.Spec.Replicas { - if replicasFound != 0 { - eventReason = "Scaling Out" - eventMsg = "from " + strconv.Itoa(replicasFound) + " pods to " + strconv.Itoa(m.Spec.Replicas) - r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) + + // Obtaining all ports of the extSvc k8s service + var targetPorts []int32 + for _, port := range extSvc.Spec.Ports { + if extSvc.Spec.Type == corev1.ServiceType("LoadBalancer") { + targetPorts = append(targetPorts, port.Port) + } else if extSvc.Spec.Type == corev1.ServiceType("NodePort") { + targetPorts = append(targetPorts, port.NodePort) } - // If version is same , call createPods() with the same version , and no of Replicas required - return r.createPods(m, n, ctx, req, replicasFound) } - eventReason = "Scaling In" - eventMsg = "from " + strconv.Itoa(replicasFound) + " pods to " + strconv.Itoa(m.Spec.Replicas) - r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) - // Delete extra PODs - return r.deletePods(ctx, req, m, available, readyPod, replicasFound, m.Spec.Replicas) + + patchSvc := false + + // Conditions to determine whether to patch or not + if extSvc.Spec.Type != extSvcType || len(extSvc.Spec.Ports) != requiredPorts { + patchSvc = true + } + + if (m.Spec.ListenerPort != 0 && svcPort != targetPorts[1]) || (m.Spec.EnableTCPS && m.Spec.TcpsListenerPort != 0 && tcpsSvcPort != targetPorts[len(targetPorts)-1]) { + patchSvc = true + } + + if m.Spec.LoadBalancer { + if m.Spec.EnableTCPS { + if m.Spec.TcpsListenerPort == 0 && tcpsSvcPort != targetPorts[len(targetPorts)-1] { + patchSvc = true + } + } else { + if m.Spec.ListenerPort == 0 && svcPort != targetPorts[1] { + patchSvc = true + } + } + } else { + if m.Spec.EnableTCPS { + if m.Spec.TcpsListenerPort == 0 && tcpsSvcPort != extSvc.Spec.Ports[len(targetPorts)-1].TargetPort.IntVal { + patchSvc = true + } + } else { + if m.Spec.ListenerPort == 0 && svcPort != extSvc.Spec.Ports[1].TargetPort.IntVal { + patchSvc = true + } + } + } + + if patchSvc { + // Reset connect strings whenever patching happens + m.Status.Status = dbcommons.StatusUpdating + m.Status.ConnectString = dbcommons.ValueUnavailable + m.Status.PdbConnectString = dbcommons.ValueUnavailable + m.Status.OemExpressUrl = dbcommons.ValueUnavailable + m.Status.TcpsConnectString = dbcommons.ValueUnavailable + m.Status.TcpsPdbConnectString = dbcommons.ValueUnavailable + + // Payload formation for patching the service + var payload string + if m.Spec.LoadBalancer { + if m.Spec.EnableTCPS { + if m.Spec.ListenerPort != 0 { + payload = fmt.Sprintf(dbcommons.ThreePortPayload, extSvcType, fmt.Sprintf(dbcommons.LsnrPort, svcPort), fmt.Sprintf(dbcommons.TcpsPort, tcpsSvcPort)) + } else { + payload = fmt.Sprintf(dbcommons.TwoPortPayload, extSvcType, fmt.Sprintf(dbcommons.TcpsPort, tcpsSvcPort)) + } + } else { + payload = fmt.Sprintf(dbcommons.TwoPortPayload, extSvcType, fmt.Sprintf(dbcommons.LsnrPort, svcPort)) + } + } else { + if m.Spec.EnableTCPS { + if m.Spec.ListenerPort != 0 && m.Spec.TcpsListenerPort != 0 { + payload = fmt.Sprintf(dbcommons.ThreePortPayload, extSvcType, fmt.Sprintf(dbcommons.LsnrNodePort, svcPort), fmt.Sprintf(dbcommons.TcpsNodePort, tcpsSvcPort)) + } else if m.Spec.ListenerPort != 0 { + payload = fmt.Sprintf(dbcommons.ThreePortPayload, extSvcType, fmt.Sprintf(dbcommons.LsnrNodePort, svcPort), fmt.Sprintf(dbcommons.TcpsPort, tcpsSvcPort)) + } else if m.Spec.TcpsListenerPort != 0 { + payload = fmt.Sprintf(dbcommons.TwoPortPayload, extSvcType, fmt.Sprintf(dbcommons.TcpsNodePort, tcpsSvcPort)) + } else { + payload = fmt.Sprintf(dbcommons.TwoPortPayload, extSvcType, fmt.Sprintf(dbcommons.TcpsPort, tcpsSvcPort)) + } + } else { + if m.Spec.ListenerPort != 0 { + payload = fmt.Sprintf(dbcommons.TwoPortPayload, extSvcType, fmt.Sprintf(dbcommons.LsnrNodePort, svcPort)) + } else { + payload = fmt.Sprintf(dbcommons.TwoPortPayload, extSvcType, fmt.Sprintf(dbcommons.LsnrPort, svcPort)) + } + } + } + + //Attemp Service Pathcing + log.Info("Patching the service", "Service.Name", extSvc.Name, "payload", payload) + err := dbcommons.PatchService(r.Config, m.Namespace, ctx, req, extSvcName, payload) + if err != nil { + log.Error(err, "Failed to patch Service") + } + //Requeue once after patching + return requeueY, err + } } - // Version/Image changed - // PATCHING START (Only Software Patch) - // call FindPods() to find pods of newer version . if running , delete the older version replicas. - readyPod, replicasFound, available, _, err = dbcommons.FindPods(r, m.Spec.Image.Version, - m.Spec.Image.PullFrom, m.Name, m.Namespace, ctx, req) + if !isExtSvcFound { + // Reset connect strings whenever extSvc is recreated + m.Status.Status = dbcommons.StatusUpdating + m.Status.ConnectString = dbcommons.ValueUnavailable + m.Status.PdbConnectString = dbcommons.ValueUnavailable + m.Status.OemExpressUrl = dbcommons.ValueUnavailable + m.Status.TcpsConnectString = dbcommons.ValueUnavailable + m.Status.TcpsPdbConnectString = dbcommons.ValueUnavailable + + // New service has to be created + ports := []corev1.ServicePort{ + { + Name: "xmldb", + Port: 5500, + Protocol: corev1.ProtocolTCP, + }, + } + + if m.Spec.LoadBalancer { + if m.Spec.EnableTCPS { + if m.Spec.ListenerPort != 0 { + ports = append(ports, corev1.ServicePort{ + Name: "listener", + Protocol: corev1.ProtocolTCP, + Port: svcPort, + TargetPort: intstr.FromInt(int(dbcommons.CONTAINER_LISTENER_PORT)), + }) + } + ports = append(ports, corev1.ServicePort{ + Name: "listener-tcps", + Protocol: corev1.ProtocolTCP, + Port: tcpsSvcPort, + TargetPort: intstr.FromInt(int(dbcommons.CONTAINER_TCPS_PORT)), + }) + } else { + ports = append(ports, corev1.ServicePort{ + Name: "listener", + Protocol: corev1.ProtocolTCP, + Port: svcPort, + TargetPort: intstr.FromInt(int(dbcommons.CONTAINER_LISTENER_PORT)), + }) + } + } else { + if m.Spec.EnableTCPS { + if m.Spec.ListenerPort != 0 { + ports = append(ports, corev1.ServicePort{ + Name: "listener", + Protocol: corev1.ProtocolTCP, + Port: dbcommons.CONTAINER_LISTENER_PORT, + NodePort: svcPort, + }) + } + ports = append(ports, corev1.ServicePort{ + Name: "listener-tcps", + Protocol: corev1.ProtocolTCP, + Port: dbcommons.CONTAINER_TCPS_PORT, + }) + if m.Spec.TcpsListenerPort != 0 { + ports[len(ports)-1].NodePort = tcpsSvcPort + } + } else { + ports = append(ports, corev1.ServicePort{ + Name: "listener", + Protocol: corev1.ProtocolTCP, + Port: dbcommons.CONTAINER_LISTENER_PORT, + }) + if m.Spec.ListenerPort != 0 { + ports[len(ports)-1].NodePort = svcPort + } + } + } + + // Create the service + svc := r.instantiateSVCSpec(m, extSvcName, ports, extSvcType) + log.Info("Creating a new service", "Service.Namespace", svc.Namespace, "Service.Name", svc.Name) + err := r.Create(ctx, svc) + if err != nil { + log.Error(err, "Failed to create new service", "Service.Namespace", svc.Namespace, "Service.Name", svc.Name) + return requeueY, err + } + extSvc = svc + } + + var sid, pdbName string + var getSidPdbEditionErr error + if m.Spec.Image.PrebuiltDB { + 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 + } + 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 { + // '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) + } + } + } + + return requeueN, nil +} + +// ############################################################################# +// +// Create new Pods or delete old/extra pods +// m = SingleInstanceDatabase +// n = CloneFromDatabase +// +// ############################################################################# +func (r *SingleInstanceDatabaseReconciler) createOrReplacePods(m *dbapi.SingleInstanceDatabase, n *dbapi.SingleInstanceDatabase, rp *dbapi.SingleInstanceDatabase, + ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + log := r.Log.WithValues("createOrReplacePods", req.NamespacedName) + + oldVersion := "" + oldImage := "" + + // call FindPods() to fetch pods all version/images of the same SIDB kind + readyPod, replicasFound, allAvailable, podsMarkedToBeDeleted, err := dbcommons.FindPods(r, "", "", m.Name, m.Namespace, ctx, req) if err != nil { log.Error(err, err.Error()) - return requeueY, nil + return requeueY, err } - // create new Pods with the new Version and no.of Replicas required - result, err := r.createPods(m, n, ctx, req, replicasFound) - if result.Requeue { - return result, err + // Recreate new pods only after earlier pods are terminated completely + for i := 0; i < len(podsMarkedToBeDeleted); i++ { + r.Log.Info("Force deleting pod ", "name", podsMarkedToBeDeleted[i].Name, "phase", podsMarkedToBeDeleted[i].Status.Phase) + var gracePeriodSeconds int64 = 0 + policy := metav1.DeletePropagationForeground + r.Delete(ctx, &podsMarkedToBeDeleted[i], &client.DeleteOptions{ + GracePeriodSeconds: &gracePeriodSeconds, PropagationPolicy: &policy}) } - // Findpods() only returns non ready pods if readyPod.Name != "" { - log.Info("New ready pod found", "name", readyPod.Name) - available = append(available, readyPod) + allAvailable = append(allAvailable, readyPod) } - if ok, _ := dbcommons.IsAnyPodWithStatus(available, corev1.PodRunning); !ok { - eventReason := "Database Pending" - eventMsg := "waiting for newer version/image DB pods get to running state" - r.Recorder.Eventf(m, corev1.EventTypeWarning, eventReason, eventMsg) - log.Info(eventMsg) - return requeueY, errors.New(eventMsg) + + for _, pod := range allAvailable { + if pod.Labels["version"] != m.Spec.Image.Version { + oldVersion = pod.Labels["version"] + } + if pod.Spec.Containers[0].Image != m.Spec.Image.PullFrom { + oldImage = pod.Spec.Containers[0].Image + } + + } + + // podVersion, podImage if old version PODs are found + imageChanged := oldVersion != "" || oldImage != "" + + if !imageChanged { + eventReason := "" + eventMsg := "" + if replicasFound > m.Spec.Replicas { + eventReason = "Scaling in pods" + eventMsg = "from " + strconv.Itoa(replicasFound) + " to " + strconv.Itoa(m.Spec.Replicas) + r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) + // Delete extra PODs + return r.deletePods(ctx, req, m, allAvailable, readyPod, replicasFound, m.Spec.Replicas) + } + if replicasFound != 0 { + if replicasFound == 1 { + if m.Status.DatafilesCreated != "true" { + log.Info("No datafiles created, single replica found, creating wallet") + // Creation of Oracle Wallet for Single Instance Database credentials + r.createWallet(m, ctx, req) + } + } + if ok, _ := dbcommons.IsAnyPodWithStatus(allAvailable, corev1.PodRunning); !ok { + eventReason = "Database Pending" + eventMsg = "waiting for a pod to get to running state" + log.Info(eventMsg) + r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) + for i := 0; i < len(allAvailable); i++ { + r.Log.Info("Pod status: ", "name", allAvailable[i].Name, "phase", allAvailable[i].Status.Phase) + waitingReason := "" + var stateWaiting *corev1.ContainerStateWaiting + if len(allAvailable[i].Status.InitContainerStatuses) > 0 { + stateWaiting = allAvailable[i].Status.InitContainerStatuses[0].State.Waiting + } else if len(allAvailable[i].Status.ContainerStatuses) > 0 { + stateWaiting = allAvailable[i].Status.ContainerStatuses[0].State.Waiting + } + if stateWaiting != nil { + waitingReason = stateWaiting.Reason + } + if waitingReason == "" { + continue + } + r.Log.Info("Pod unavailable reason: ", "reason", waitingReason) + if strings.Contains(waitingReason, "ImagePullBackOff") || strings.Contains(waitingReason, "ErrImagePull") { + r.Log.Info("Deleting pod", "name", allAvailable[i].Name) + var gracePeriodSeconds int64 = 0 + policy := metav1.DeletePropagationForeground + r.Delete(ctx, &allAvailable[i], &client.DeleteOptions{ + GracePeriodSeconds: &gracePeriodSeconds, PropagationPolicy: &policy}) + } + } + return requeueY, err + } + } + if replicasFound == m.Spec.Replicas { + return requeueN, nil + } + if replicasFound != 0 && replicasFound < m.Spec.Replicas { + eventReason = "Scaling out pods" + eventMsg = "from " + strconv.Itoa(replicasFound) + " to " + strconv.Itoa(m.Spec.Replicas) + r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) + } + // If version is same , call createPods() with the same version , and no of Replicas required + return r.createPods(m, n, rp, ctx, req, replicasFound, false) } - // call FindPods() to find pods of older version . delete all the Pods - readyPod, replicasFound, available, _, err = dbcommons.FindPods(r, oldVersion, + // Version/Image changed + // PATCHING START (Only Software Patch) + log.Info("Pod image change detected, datapatch to be rerun...") + m.Status.DatafilesPatched = "false" + // call FindPods() to find pods of older version. Delete all the Pods + readyPod, oldReplicasFound, oldAvailable, _, err := dbcommons.FindPods(r, oldVersion, oldImage, m.Name, m.Namespace, ctx, req) if err != nil { log.Error(err, err.Error()) @@ -1000,19 +1960,88 @@ func (r *SingleInstanceDatabaseReconciler) createOrReplacePods(m *dbapi.SingleIn } if readyPod.Name != "" { log.Info("Ready pod marked for deletion", "name", readyPod.Name) - available = append(available, readyPod) + oldAvailable = append(oldAvailable, readyPod) + } + + if m.Status.Replicas == 1 { + r.deletePods(ctx, req, m, oldAvailable, corev1.Pod{}, oldReplicasFound, 0) + } + + // call FindPods() to find pods of newer version . if running , delete the older version replicas. + readyPod, newReplicasFound, newAvailable, _, err := dbcommons.FindPods(r, m.Spec.Image.Version, + m.Spec.Image.PullFrom, m.Name, m.Namespace, ctx, req) + if err != nil { + log.Error(err, err.Error()) + return requeueY, nil + } + // Findpods() only returns non ready pods + if readyPod.Name != "" { + log.Info("New ready pod found", "name", readyPod.Name) + newAvailable = append(newAvailable, readyPod) + } + + if newReplicasFound != 0 { + if ok, _ := dbcommons.IsAnyPodWithStatus(newAvailable, corev1.PodRunning); !ok { + eventReason := "Database Pending" + eventMsg := "waiting for pod with changed image to get to running state" + r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) + log.Info(eventMsg) + + for i := 0; i < len(newAvailable); i++ { + r.Log.Info("Pod status: ", "name", newAvailable[i].Name, "phase", newAvailable[i].Status.Phase) + waitingReason := "" + var stateWaiting *corev1.ContainerStateWaiting + if len(newAvailable[i].Status.InitContainerStatuses) > 0 { + stateWaiting = newAvailable[i].Status.InitContainerStatuses[0].State.Waiting + } else if len(newAvailable[i].Status.ContainerStatuses) > 0 { + stateWaiting = newAvailable[i].Status.ContainerStatuses[0].State.Waiting + } + if stateWaiting != nil { + waitingReason = stateWaiting.Reason + } + if waitingReason == "" { + continue + } + r.Log.Info("Pod unavailable reason: ", "reason", waitingReason) + if strings.Contains(waitingReason, "ImagePullBackOff") || strings.Contains(waitingReason, "ErrImagePull") { + r.Log.Info("Deleting pod", "name", newAvailable[i].Name) + var gracePeriodSeconds int64 = 0 + policy := metav1.DeletePropagationForeground + r.Delete(ctx, &newAvailable[i], &client.DeleteOptions{ + GracePeriodSeconds: &gracePeriodSeconds, PropagationPolicy: &policy}) + } + } + return requeueY, errors.New(eventMsg) + } + } + + // create new Pods with the new Version and no.of Replicas required + // if m.Status.Replicas > 1, then it is replica based patching + result, err := r.createPods(m, n, rp, ctx, req, newReplicasFound, m.Status.Replicas > 1) + if result.Requeue { + return result, err + } + if m.Status.Replicas == 1 { + return requeueN, nil } - return r.deletePods(ctx, req, m, available, corev1.Pod{}, replicasFound, 0) + return r.deletePods(ctx, req, m, oldAvailable, corev1.Pod{}, oldReplicasFound, 0) // PATCHING END } -//############################################################################# -// Function for creating Oracle Wallet -//############################################################################# +// ############################################################################# +// +// Function for creating Oracle Wallet +// +// ############################################################################# func (r *SingleInstanceDatabaseReconciler) createWallet(m *dbapi.SingleInstanceDatabase, ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - // Wallet not supported for XE Database - if m.Spec.Edition == "express" { + // Wallet not supported for Express/Free Database + if m.Spec.Edition == "express" || m.Spec.Edition == "free" { + return requeueN, nil + } + + // No Wallet for Pre-built db + if m.Spec.Image.PrebuiltDB { return requeueN, nil } @@ -1033,12 +2062,12 @@ func (r *SingleInstanceDatabaseReconciler) createWallet(m *dbapi.SingleInstanceD return requeueY, nil } - // Iterate through the avaialableFinal (list of pods) to find out the pod whose status is updated about the init containers + // 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 contianer is updated about init containers + // Check if pod status container is updated about init containers if len(pod.Status.InitContainerStatuses) > 0 { podFound = true break @@ -1063,7 +2092,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" @@ -1077,9 +2106,9 @@ func (r *SingleInstanceDatabaseReconciler) createWallet(m *dbapi.SingleInstanceD if err == nil && out != "" { m.Status.Status = dbcommons.StatusError - eventMsg := "wrong edition" + eventMsg := "incorrect database edition" r.Recorder.Eventf(m, corev1.EventTypeWarning, eventReason, eventMsg) - return requeueN, errors.New("wrong Edition") + return requeueY, errors.New(eventMsg) } } @@ -1117,13 +2146,16 @@ func (r *SingleInstanceDatabaseReconciler) createWallet(m *dbapi.SingleInstanceD return requeueN, nil } -//############################################################################# -// Create the requested POD replicas -// m = SingleInstanceDatabase -// n = CloneFromDatabase -//############################################################################# -func (r *SingleInstanceDatabaseReconciler) createPods(m *dbapi.SingleInstanceDatabase, n *dbapi.SingleInstanceDatabase, - ctx context.Context, req ctrl.Request, replicasFound int) (ctrl.Result, error) { +// ############################################################################## +// +// Create the requested POD replicas +// m = SingleInstanceDatabase +// n = CloneFromDatabase +// patching = Boolean variable to differentiate normal usecase with patching +// +// ############################################################################## +func (r *SingleInstanceDatabaseReconciler) createPods(m *dbapi.SingleInstanceDatabase, n *dbapi.SingleInstanceDatabase, rp *dbapi.SingleInstanceDatabase, + ctx context.Context, req ctrl.Request, replicasFound int, replicaPatching bool) (ctrl.Result, error) { log := r.Log.WithValues("createPods", req.NamespacedName) @@ -1133,25 +2165,29 @@ func (r *SingleInstanceDatabaseReconciler) createPods(m *dbapi.SingleInstanceDat log.Info("No of " + m.Name + " replicas found are same as required") return requeueN, nil } + firstPod := false if replicasFound == 0 { m.Status.Status = dbcommons.StatusPending - m.Status.DatafilesCreated = "false" - m.Status.DatafilesPatched = "false" - m.Status.Role = dbcommons.ValueUnavailable - m.Status.ConnectString = dbcommons.ValueUnavailable - m.Status.PdbConnectString = dbcommons.ValueUnavailable - m.Status.OemExpressUrl = dbcommons.ValueUnavailable - m.Status.ReleaseUpdate = dbcommons.ValueUnavailable + firstPod = true + } + if !replicaPatching { + m.Status.Replicas = replicasFound } - // if Found < Required , Create New Pods , Name of Pods are generated Randomly + // if Found < Required, create new pods, name of pods are generated randomly for i := replicasFound; i < replicasReq; i++ { - pod := r.instantiatePodSpec(m, n) + // mandatory pod affinity if it is replica based patching or not the first pod + pod := r.instantiatePodSpec(m, n, rp, replicaPatching || !firstPod) log.Info("Creating a new "+m.Name+" POD", "POD.Namespace", pod.Namespace, "POD.Name", pod.Name) err := r.Create(ctx, pod) if err != nil { log.Error(err, "Failed to create new "+m.Name+" POD", "pod.Namespace", pod.Namespace, "POD.Name", pod.Name) return requeueY, err } + m.Status.Replicas += 1 + if firstPod { + log.Info("Requeue for first pod to get to running state", "POD.Namespace", pod.Namespace, "POD.Name", pod.Name) + return requeueY, err + } } readyPod, _, availableFinal, _, err := dbcommons.FindPods(r, m.Spec.Image.Version, @@ -1164,8 +2200,6 @@ func (r *SingleInstanceDatabaseReconciler) createPods(m *dbapi.SingleInstanceDat availableFinal = append(availableFinal, readyPod) } - m.Status.Replicas = m.Spec.Replicas - podNamesFinal := dbcommons.GetPodNames(availableFinal) log.Info("Final "+m.Name+" Pods After Deleting (or) Adding Extra Pods ( Including The Ready Pod ) ", "Pod Names", podNamesFinal) log.Info(m.Name+" Replicas Available", "Count", len(podNamesFinal)) @@ -1174,11 +2208,13 @@ func (r *SingleInstanceDatabaseReconciler) createPods(m *dbapi.SingleInstanceDat return requeueN, nil } -//############################################################################# -// Create the requested POD replicas -// m = SingleInstanceDatabase -// n = CloneFromDatabase -//############################################################################# +// ############################################################################# +// +// Create the requested POD replicas +// m = SingleInstanceDatabase +// n = CloneFromDatabase +// +// ############################################################################# func (r *SingleInstanceDatabaseReconciler) deletePods(ctx context.Context, req ctrl.Request, m *dbapi.SingleInstanceDatabase, available []corev1.Pod, readyPod corev1.Pod, replicasFound int, replicasRequired int) (ctrl.Result, error) { log := r.Log.WithValues("deletePods", req.NamespacedName) @@ -1199,7 +2235,7 @@ func (r *SingleInstanceDatabaseReconciler) deletePods(ctx context.Context, req c } } - // For deleting all pods , call with readyPod as nil ( corev1.Pod{} ) and append readyPod to avaiable while calling deletePods() + // For deleting all pods , call with readyPod as nil ( corev1.Pod{} ) and append readyPod to available while calling deletePods() // if Found > Required , Delete Extra Pods if replicasFound > len(available) { // if available does not contain readyPOD, add it @@ -1208,132 +2244,151 @@ func (r *SingleInstanceDatabaseReconciler) deletePods(ctx context.Context, req c noDeleted := 0 for _, availablePod := range available { - if readyPod.Name == availablePod.Name { + if readyPod.Name == availablePod.Name && m.Spec.Replicas != 0 { continue } if replicasRequired == (len(available) - noDeleted) { break } r.Log.Info("Deleting Pod : ", "POD.NAME", availablePod.Name) - err := r.Delete(ctx, &availablePod, &client.DeleteOptions{}) + var delOpts *client.DeleteOptions = &client.DeleteOptions{} + if replicasRequired == 0 { + var gracePeriodSeconds int64 = 0 + policy := metav1.DeletePropagationForeground + delOpts.GracePeriodSeconds = &gracePeriodSeconds + delOpts.PropagationPolicy = &policy + } + err := r.Delete(ctx, &availablePod, delOpts) noDeleted += 1 if err != nil { r.Log.Error(err, "Failed to delete existing POD", "POD.Name", availablePod.Name) // Don't requeue + } else { + m.Status.Replicas -= 1 } } - m.Status.Replicas = m.Spec.Replicas - return requeueN, nil } -//############################################################################# -// ValidateDBReadiness and return the ready POD -//############################################################################# -func (r *SingleInstanceDatabaseReconciler) validateDBReadiness(m *dbapi.SingleInstanceDatabase, +// ############################################################################# +// +// ValidateDBReadiness and return the ready POD +// +// ############################################################################# +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 == "" { - eventReason := "Database Pending" - eventMsg := "waiting for database pod to be ready" - 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" - } else if ok, runningPod := dbcommons.IsAnyPodWithStatus(available, corev1.PodRunning); ok { - eventReason = "Database Creating" - eventMsg = "waiting for database to be ready" - m.Status.Status = dbcommons.StatusCreating - if m.Spec.Edition == "express" { - eventReason = "Database Unhealthy" - m.Status.Status = dbcommons.StatusNotReady - } - out, err := dbcommons.ExecCommand(r, r.Config, runningPod.Name, runningPod.Namespace, "", + eventReason := "Database Failed" + eventMsg := "pod creation failed" + r.Recorder.Eventf(sidb, corev1.EventTypeNormal, eventReason, eventMsg) + } else if ok, _ := dbcommons.IsAnyPodWithStatus(available, corev1.PodRunning); ok { + + out, err := dbcommons.ExecCommand(r, r.Config, available[0].Name, sidb.Namespace, "", ctx, req, false, "bash", "-c", dbcommons.GetCheckpointFileCMD) if err != nil { - r.Log.Error(err, err.Error()) - return requeueY, readyPod, err + r.Log.Info(err.Error()) } - r.Log.Info("GetCheckpointFileCMD Output : \n" + out) if out != "" { - eventReason = "Database Unhealthy" - eventMsg = "datafiles exists" - m.Status.DatafilesCreated = "true" - m.Status.Status = dbcommons.StatusNotReady + 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) } - r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) - r.Log.Info(eventMsg) - // As No pod is ready now , turn on mode when pod is ready . so requeue the request - return requeueY, readyPod, errors.New(eventMsg) - } - if m.Status.DatafilesPatched != "true" { - eventReason := "Datapatch Pending" - eventMsg := "datapatch execution pending" - r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) + log.Info("no pod currently in ready state") + return requeueY, sidbReadyPod, nil } - 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.DatafilesPatched != "true" { - m.Status.ReleaseUpdate = version + 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()) } } - if m.Spec.Edition == "express" { - //Configure OEM Express Listener - out, err = dbcommons.ExecCommand(r, r.Config, readyPod.Name, readyPod.Namespace, "", ctx, req, false, - "bash", "-c", fmt.Sprintf("echo -e \"%s\" | su -p oracle -c \"sqlplus -s / as sysdba\" ", dbcommons.ConfigureOEMSQL)) - if err != nil { - r.Log.Error(err, err.Error()) - return requeueY, readyPod, err - } - r.Log.Info("ConfigureOEMSQL output") - r.Log.Info(out) + version, err := dbcommons.GetDatabaseVersion(sidbReadyPod, r, r.Config, ctx, req) + if err != nil { + return requeueY, sidbReadyPod, err + } + dbMajorVersion, err := strconv.Atoi(strings.Split(version, ".")[0]) + if err != nil { + r.Log.Error(err, err.Error()) + 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 sidb.Spec.Edition == "free" && dbMajorVersion < 23 { + errMsg := "the Oracle Database Free is only available from version 23c onwards" + r.Recorder.Eventf(sidb, corev1.EventTypeWarning, "Spec Error", errMsg) + sidb.Status.Status = dbcommons.StatusError + return requeueY, sidbReadyPod, errors.New(errMsg) } - return requeueN, readyPod, nil + available = append(available, sidbReadyPod) + podNamesFinal := dbcommons.GetPodNames(available) + 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 " + sidbReadyPod.Name + " scheduled on node " + sidbReadyPod.Status.HostIP + r.Recorder.Eventf(sidb, corev1.EventTypeNormal, eventReason, eventMsg) + + sidb.Status.CreatedAs = sidb.Spec.CreateAs + + return requeueN, sidbReadyPod, nil } -//############################################################################# -// Function for deleting the Oracle Wallet -//############################################################################# +// ############################################################################# +// +// Function for deleting the Oracle Wallet +// +// ############################################################################# func (r *SingleInstanceDatabaseReconciler) deleteWallet(m *dbapi.SingleInstanceDatabase, ctx context.Context, req ctrl.Request) (ctrl.Result, error) { - // Wallet not supported for XE Database - if m.Spec.Edition == "express" { + // Wallet not supported for Express/Free Database + if m.Spec.Edition == "express" || m.Spec.Edition == "free" { + return requeueN, nil + } + + // No Wallet for Pre-built db + if m.Spec.Image.PrebuiltDB { return requeueN, nil } // Deleting the secret and then deleting the wallet // If the secret is not found it means that the secret and wallet both are deleted, hence no need to requeue - if !m.Spec.AdminPassword.KeepSecret { + if m.Spec.AdminPassword.KeepSecret != nil && !*m.Spec.AdminPassword.KeepSecret { 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) @@ -1364,26 +2419,242 @@ func (r *SingleInstanceDatabaseReconciler) deleteWallet(m *dbapi.SingleInstanceD return requeueN, nil } -//############################################################################# -// Execute Datapatch -//############################################################################# -func (r *SingleInstanceDatabaseReconciler) runDatapatch(m *dbapi.SingleInstanceDatabase, - readyPod corev1.Pod, ctx context.Context, req ctrl.Request) (ctrl.Result, error) { +// ############################################################################# +// +// Updating clientWallet when TCPS is enabled +// +// ############################################################################# +func (r *SingleInstanceDatabaseReconciler) updateClientWallet(m *dbapi.SingleInstanceDatabase, + readyPod corev1.Pod, ctx context.Context, req ctrl.Request) error { + // Updation of tnsnames.ora in clientWallet for HOST and PORT fields + extSvc := &corev1.Service{} + extSvcName := m.Name + "-ext" + getExtSvcErr := r.Get(ctx, types.NamespacedName{Name: extSvcName, Namespace: m.Namespace}, extSvc) + + if getExtSvcErr == nil { + var host string + var port int32 + if m.Spec.LoadBalancer { + if len(extSvc.Status.LoadBalancer.Ingress) > 0 { + host = extSvc.Status.LoadBalancer.Ingress[0].Hostname + if host == "" { + host = extSvc.Status.LoadBalancer.Ingress[0].IP + } + port = extSvc.Spec.Ports[len(extSvc.Spec.Ports)-1].Port + } + } else { + host = dbcommons.GetNodeIp(r, ctx, req) + if host != "" { + port = extSvc.Spec.Ports[len(extSvc.Spec.Ports)-1].NodePort + } + } - // Datapatch not supported for XE Database - if m.Spec.Edition == "express" { - return requeueN, nil + r.Log.Info("Updating the client wallet...") + _, err := dbcommons.ExecCommand(r, r.Config, readyPod.Name, readyPod.Namespace, "", + ctx, req, false, "bash", "-c", fmt.Sprintf(dbcommons.ClientWalletUpdate, host, port)) + if err != nil { + r.Log.Error(err, err.Error()) + return err + } + + } else { + r.Log.Info("Unable to get the service while updating the clientWallet", "Service.Namespace", extSvc.Namespace, "Service.Name", extSvcName) + return getExtSvcErr } + return nil +} - m.Status.Status = dbcommons.StatusPatching - r.Status().Update(ctx, m) - eventReason := "Datapatch Executing" - eventMsg := "datapatch begin execution" - r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) +// ############################################################################# +// +// Configuring TCPS +// +// ############################################################################# +func (r *SingleInstanceDatabaseReconciler) configTcps(m *dbapi.SingleInstanceDatabase, + readyPod corev1.Pod, ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + eventReason := "Configuring TCPS" - //RUN DATAPATCH - out, err := dbcommons.ExecCommand(r, r.Config, readyPod.Name, readyPod.Namespace, "", - ctx, req, false, "bash", "-c", dbcommons.RunDatapatchCMD) + 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 + + // 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..." + 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 + + // 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") + 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 + } + } + + // 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) + // Updating the Status and publishing the event + 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." + r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) + + requeueDuration, _ := time.ParseDuration(m.Spec.TcpsCertRenewInterval) + requeueDuration += func() time.Duration { requeueDuration, _ := time.ParseDuration("1s"); return requeueDuration }() + futureRequeue = ctrl.Result{Requeue: true, RequeueAfter: requeueDuration} + + // update clientWallet + err = r.updateClientWallet(m, readyPod, ctx, req) + if err != nil { + r.Log.Error(err, "Error in updating tnsnames.ora in clientWallet...") + return requeueY, nil + } + } else if !m.Spec.EnableTCPS && m.Status.IsTcpsEnabled { + // Disable TCPS + m.Status.Status = dbcommons.StatusUpdating + r.Status().Update(ctx, m) + + eventMsg := "Disabling TCPS in the database..." + r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) + + out, err := dbcommons.ExecCommand(r, r.Config, readyPod.Name, readyPod.Namespace, "", + ctx, req, false, "bash", "-c", dbcommons.DisableTcpsCMD) + if err != nil { + r.Log.Error(err, err.Error()) + return requeueY, nil + } + r.Log.Info("disable TCPS Output : \n" + out) + // Updating the Status and publishing the event + m.Status.CertCreationTimestamp = "" + m.Status.IsTcpsEnabled = false + m.Status.ClientWalletLoc = "" + m.Status.TcpsTlsSecret = "" + + r.Status().Update(ctx, m) + + eventMsg = "TCPS Disabled." + r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) + + } else if m.Spec.EnableTCPS && m.Status.IsTcpsEnabled && m.Spec.TcpsCertRenewInterval != "" { + // Cert Renewal Logic + certCreationTimestamp, _ := time.Parse(time.RFC3339, m.Status.CertCreationTimestamp) + duration := time.Since(certCreationTimestamp) + allowdDuration, _ := time.ParseDuration(m.Spec.TcpsCertRenewInterval) + if duration > allowdDuration { + m.Status.Status = dbcommons.StatusUpdating + r.Status().Update(ctx, m) + + out, err := dbcommons.ExecCommand(r, r.Config, readyPod.Name, readyPod.Namespace, "", + ctx, req, false, "bash", "-c", fmt.Sprintf(dbcommons.EnableTcpsCMD)) + if err != nil { + r.Log.Error(err, err.Error()) + return requeueY, nil + } + r.Log.Info("Cert Renewal Output : \n" + out) + // Updating the Status and publishing the event + m.Status.CertCreationTimestamp = time.Now().Format(time.RFC3339) + r.Status().Update(ctx, m) + + eventMsg := "TCPS Certificates Renewed at time %s," + r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg, time.Now().Format(time.RFC3339)) + + requeueDuration, _ := time.ParseDuration(m.Spec.TcpsCertRenewInterval) + requeueDuration += func() time.Duration { requeueDuration, _ := time.ParseDuration("1s"); return requeueDuration }() + futureRequeue = ctrl.Result{Requeue: true, RequeueAfter: requeueDuration} + } + if m.Status.CertRenewInterval != m.Spec.TcpsCertRenewInterval { + requeueDuration, _ := time.ParseDuration(m.Spec.TcpsCertRenewInterval) + requeueDuration += func() time.Duration { requeueDuration, _ := time.ParseDuration("1s"); return requeueDuration }() + futureRequeue = ctrl.Result{Requeue: true, RequeueAfter: requeueDuration} + + m.Status.CertRenewInterval = m.Spec.TcpsCertRenewInterval + } + // update clientWallet + err := r.updateClientWallet(m, readyPod, ctx, req) + if err != nil { + r.Log.Error(err, "Error in updating tnsnames.ora clientWallet...") + return requeueY, nil + } + } else if m.Spec.EnableTCPS && m.Status.IsTcpsEnabled && m.Spec.TcpsCertRenewInterval == "" { + // update clientWallet + err := r.updateClientWallet(m, readyPod, ctx, req) + if err != nil { + r.Log.Error(err, "Error in updating tnsnames.ora clientWallet...") + return requeueY, nil + } + } + return requeueN, nil +} + +// ############################################################################# +// +// Execute Datapatch +// +// ############################################################################# +func (r *SingleInstanceDatabaseReconciler) runDatapatch(m *dbapi.SingleInstanceDatabase, + readyPod corev1.Pod, ctx context.Context, req ctrl.Request) (ctrl.Result, error) { + + // Datapatch not supported for XE Database + if m.Spec.Edition == "express" || m.Spec.Edition == "free" { + eventReason := "Datapatch Check" + eventMsg := "datapatch not supported for " + m.Spec.Edition + " edition" + r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) + r.Log.Info(eventMsg) + return requeueN, nil + } + + m.Status.Status = dbcommons.StatusPatching + eventReason := "Datapatch Executing" + eventMsg := "datapatch begins execution" + r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) + r.Status().Update(ctx, m) + + //RUN DATAPATCH + out, err := dbcommons.ExecCommand(r, r.Config, readyPod.Name, readyPod.Namespace, "", + ctx, req, false, "bash", "-c", dbcommons.RunDatapatchCMD) if err != nil { r.Log.Error(err, err.Error()) return requeueY, err @@ -1394,12 +2665,13 @@ func (r *SingleInstanceDatabaseReconciler) runDatapatch(m *dbapi.SingleInstanceD // Get Sqlpatch Description out, err = dbcommons.ExecCommand(r, r.Config, readyPod.Name, readyPod.Namespace, "", ctx, req, false, "bash", "-c", fmt.Sprintf("echo -e \"%s\" | sqlplus -s / as sysdba ", dbcommons.GetSqlpatchDescriptionSQL)) + releaseUpdate := "" if err == nil { r.Log.Info("GetSqlpatchDescriptionSQL Output") r.Log.Info(out) SqlpatchDescriptions, _ := dbcommons.StringToLines(out) if len(SqlpatchDescriptions) > 0 { - m.Status.ReleaseUpdate = SqlpatchDescriptions[0] + releaseUpdate = SqlpatchDescriptions[0] } } @@ -1412,59 +2684,86 @@ func (r *SingleInstanceDatabaseReconciler) runDatapatch(m *dbapi.SingleInstanceD m.Status.DatafilesPatched = "true" status, versionFrom, versionTo, _ := dbcommons.GetSqlpatchStatus(r, r.Config, readyPod, ctx, req) - eventMsg = "data files patched from " + versionFrom + " to " + versionTo + " : " + status + if versionTo != "" { + eventMsg = "data files patched from release update " + versionFrom + " to " + versionTo + ", " + status + ": " + releaseUpdate + } else { + eventMsg = "datapatch execution completed" + } r.Recorder.Eventf(m, corev1.EventTypeNormal, eventReason, eventMsg) return requeueN, nil } -//############################################################################# -// Update Init Parameters -//############################################################################# +// ############################################################################# +// +// Update Init Parameters +// +// ############################################################################# func (r *SingleInstanceDatabaseReconciler) updateInitParameters(m *dbapi.SingleInstanceDatabase, 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 } - - 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.GetSqlClient(m.Spec.Edition))) - if err != nil { - log.Error(err, err.Error()) - return requeueY, err + if m.Status.InitParams == *m.Spec.InitParams { + return requeueN, nil } - log.Info("AlterSgaPgaCpuCMD Output:" + out) - if m.Status.InitParams.Processes != m.Spec.InitParams.Processes { - // Altering 'Processes' needs database to be restarted + 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.AlterProcessesCMD, m.Spec.InitParams.Processes, dbcommons.GetSqlClient(m.Spec.Edition), - dbcommons.GetSqlClient(m.Spec.Edition))) + 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 } - log.Info("AlterProcessesCMD 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) + } + log.Info("AlterSgaPgaCpuCMD Output:" + out) } - out, err = dbcommons.ExecCommand(r, r.Config, readyPod.Name, readyPod.Namespace, "", - ctx, req, false, "bash", "-c", fmt.Sprintf(dbcommons.GetInitParamsSQL, dbcommons.GetSqlClient(m.Spec.Edition))) - if err != nil { - log.Error(err, err.Error()) - return requeueY, err + 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("GetInitParamsSQL Output:" + out) - m.Status.InitParams = m.Spec.InitParams + 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, + dbcommons.SQLPlusCLI)) + if err != nil { + log.Error(err, err.Error()) + return requeueY, err + } + log.Info("AlterProcessesCMD Output:" + out) + } return requeueN, nil } -//############################################################################# -// Update DB config params like FLASHBACK , FORCELOGGING , ARCHIVELOG -//############################################################################# +// ############################################################################# +// +// Update DB config params like FLASHBACK , FORCELOGGING , ARCHIVELOG +// +// ############################################################################# func (r *SingleInstanceDatabaseReconciler) updateDBConfig(m *dbapi.SingleInstanceDatabase, readyPod corev1.Pod, ctx context.Context, req ctrl.Request) (ctrl.Result, error) { @@ -1483,21 +2782,15 @@ func (r *SingleInstanceDatabaseReconciler) updateDBConfig(m *dbapi.SingleInstanc flashBackStatus, archiveLogStatus, forceLoggingStatus, result := dbcommons.CheckDBConfig(readyPod, r, r.Config, ctx, req, m.Spec.Edition) if result.Requeue { + 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) @@ -1509,7 +2802,7 @@ func (r *SingleInstanceDatabaseReconciler) updateDBConfig(m *dbapi.SingleInstanc log.Info(out) out, err = dbcommons.ExecCommand(r, r.Config, readyPod.Name, readyPod.Namespace, "", ctx, req, false, "bash", "-c", - fmt.Sprintf("echo -e \"%s\" | %s", dbcommons.SetDBRecoveryDestSQL, dbcommons.GetSqlClient(m.Spec.Edition))) + fmt.Sprintf("echo -e \"%s\" | %s", dbcommons.SetDBRecoveryDestSQL, dbcommons.SQLPlusCLI)) if err != nil { log.Error(err, err.Error()) return requeueY, err @@ -1518,7 +2811,7 @@ func (r *SingleInstanceDatabaseReconciler) updateDBConfig(m *dbapi.SingleInstanc log.Info(out) out, err = dbcommons.ExecCommand(r, r.Config, readyPod.Name, readyPod.Namespace, "", ctx, req, false, "bash", "-c", - fmt.Sprintf(dbcommons.ArchiveLogTrueCMD, dbcommons.GetSqlClient(m.Spec.Edition))) + fmt.Sprintf(dbcommons.ArchiveLogTrueCMD, dbcommons.SQLPlusCLI)) if err != nil { log.Error(err, err.Error()) return requeueY, err @@ -1528,9 +2821,9 @@ 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.GetSqlClient(m.Spec.Edition))) + fmt.Sprintf("echo -e \"%s\" | %s", dbcommons.ForceLoggingTrueSQL, dbcommons.SQLPlusCLI)) if err != nil { log.Error(err, err.Error()) return requeueY, err @@ -1539,27 +2832,29 @@ 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 return result, nil } if archiveLogStatus { out, err := dbcommons.ExecCommand(r, r.Config, readyPod.Name, readyPod.Namespace, "", ctx, req, false, "bash", "-c", - fmt.Sprintf("echo -e \"%s\" | %s", dbcommons.FlashBackTrueSQL, dbcommons.GetSqlClient(m.Spec.Edition))) + fmt.Sprintf("echo -e \"%s\" | %s", dbcommons.FlashBackTrueSQL, dbcommons.SQLPlusCLI)) if err != nil { log.Error(err, err.Error()) + m.Status.Status = dbcommons.StatusNotReady return requeueY, err } log.Info("FlashBackTrue Output") log.Info(out) } else { - // Occurs when flashback is attermpted to be turned on without turning on archiving first - eventReason := "Waiting" - eventMsg := "enable ArchiveLog to turn ON Flashback" - log.Info(eventMsg) + // Occurs when flashback is attempted to be turned on without turning on archiving first + eventReason := "Database Check" + eventMsg := "enable ArchiveLog to turn on Flashback" r.Recorder.Eventf(m, corev1.EventTypeWarning, eventReason, eventMsg) + log.Info(eventMsg) changeArchiveLog = true } @@ -1569,9 +2864,9 @@ 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.GetSqlClient(m.Spec.Edition))) + fmt.Sprintf("echo -e \"%s\" | %s", dbcommons.FlashBackFalseSQL, dbcommons.SQLPlusCLI)) if err != nil { log.Error(err, err.Error()) return requeueY, err @@ -1579,35 +2874,37 @@ 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 return result, nil } if !flashBackStatus { out, err := dbcommons.ExecCommand(r, r.Config, readyPod.Name, readyPod.Namespace, "", ctx, req, false, "bash", "-c", - fmt.Sprintf(dbcommons.ArchiveLogFalseCMD, dbcommons.GetSqlClient(m.Spec.Edition))) + fmt.Sprintf(dbcommons.ArchiveLogFalseCMD, dbcommons.SQLPlusCLI)) if err != nil { log.Error(err, err.Error()) + m.Status.Status = dbcommons.StatusNotReady return requeueY, err } log.Info("ArchiveLogFalse Output") log.Info(out) } else { - // Occurs when archiving is attermpted to be turned off without turning off flashback first - eventReason := "Waiting" - eventMsg := "turn OFF Flashback to disable ArchiveLog" - log.Info(eventMsg) + // Occurs when archiving is attempted to be turned off without turning off flashback first + eventReason := "Database Check" + eventMsg := "turn off Flashback to disable ArchiveLog" r.Recorder.Eventf(m, corev1.EventTypeWarning, eventReason, eventMsg) + log.Info(eventMsg) 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.GetSqlClient(m.Spec.Edition))) + fmt.Sprintf("echo -e \"%s\" | %s", dbcommons.ForceLoggingFalseSQL, dbcommons.SQLPlusCLI)) if err != nil { log.Error(err, err.Error()) return requeueY, err @@ -1622,6 +2919,7 @@ func (r *SingleInstanceDatabaseReconciler) updateDBConfig(m *dbapi.SingleInstanc flashBackStatus, archiveLogStatus, forceLoggingStatus, result = dbcommons.CheckDBConfig(readyPod, r, r.Config, ctx, req, m.Spec.Edition) if result.Requeue { + m.Status.Status = dbcommons.StatusNotReady return result, nil } @@ -1636,29 +2934,141 @@ 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) - 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 } -//############################################################################# -// Manage Finalizer to cleanup before deletion of SingleInstanceDatabase -//############################################################################# +// ############################################################################# +// +// # 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) + 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 +// +// ############################################################################# +func (r *SingleInstanceDatabaseReconciler) updateORDSStatus(m *dbapi.SingleInstanceDatabase, ctx context.Context, req ctrl.Request) { + + if m.Status.OrdsReference == "" { + return + } + n := &dbapi.OracleRestDataService{} + err := r.Get(ctx, types.NamespacedName{Namespace: req.Namespace, Name: m.Status.OrdsReference}, n) + if err != nil { + return + } + + if n.Status.OrdsInstalled { + // Update Status to Healthy/Unhealthy when SIDB turns Healthy/Unhealthy after ORDS is Installed + n.Status.Status = m.Status.Status + r.Status().Update(ctx, n) + return + } +} + +// ############################################################################# +// +// Manage Finalizer to cleanup before deletion of SingleInstanceDatabase +// +// ############################################################################# func (r *SingleInstanceDatabaseReconciler) manageSingleInstanceDatabaseDeletion(req ctrl.Request, ctx context.Context, m *dbapi.SingleInstanceDatabase) (ctrl.Result, error) { log := r.Log.WithValues("manageSingleInstanceDatabaseDeletion", req.NamespacedName) @@ -1701,9 +3111,11 @@ func (r *SingleInstanceDatabaseReconciler) manageSingleInstanceDatabaseDeletion( return requeueN, nil } -//############################################################################# -// Finalization logic for singleInstanceDatabaseFinalizer -//############################################################################# +// ############################################################################# +// +// Finalization logic for singleInstanceDatabaseFinalizer +// +// ############################################################################# func (r *SingleInstanceDatabaseReconciler) cleanupSingleInstanceDatabase(req ctrl.Request, ctx context.Context, m *dbapi.SingleInstanceDatabase) (ctrl.Result, error) { log := r.Log.WithValues("cleanupSingleInstanceDatabase", req.NamespacedName) @@ -1717,6 +3129,13 @@ func (r *SingleInstanceDatabaseReconciler) cleanupSingleInstanceDatabase(req ctr return requeueY, nil } + if m.Status.DgBrokerConfigured { + 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) + return requeueY, errors.New(eventMsg) + } + // 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 { @@ -1738,20 +3157,17 @@ func (r *SingleInstanceDatabaseReconciler) cleanupSingleInstanceDatabase(req ctr for _, pod := range podList.Items { podNames += pod.Name + " " } - eventReason := "Waiting" - eventMsg := "waiting for " + req.Name + " database pods ( " + podNames + " ) to terminate" - r.Recorder.Eventf(m, corev1.EventTypeWarning, eventReason, eventMsg) - r.Log.Info(eventMsg) - time.Sleep(15 * time.Second) } log.Info("Successfully cleaned up SingleInstanceDatabase") return requeueN, nil } -//############################################################################# -// SetupWithManager sets up the controller with the Manager -//############################################################################# +// ############################################################################# +// +// SetupWithManager sets up the controller with the Manager +// +// ############################################################################# func (r *SingleInstanceDatabaseReconciler) SetupWithManager(mgr ctrl.Manager) error { return ctrl.NewControllerManagedBy(mgr). For(&dbapi.SingleInstanceDatabase{}). @@ -1760,3 +3176,517 @@ func (r *SingleInstanceDatabaseReconciler) SetupWithManager(mgr ctrl.Manager) er WithOptions(controller.Options{MaxConcurrentReconciles: 100}). //ReconcileHandler is never invoked concurrently with the same object. Complete(r) } + +// ############################################################################# +// +// Check primary database status +// +// ############################################################################# +func CheckPrimaryDatabaseStatus(p *dbapi.SingleInstanceDatabase) error { + + if p.Status.Status != dbcommons.StatusReady { + return fmt.Errorf("referred primary database %v is NOT READY", p.Name) + } + return nil +} + +// ############################################################################# +// +// Check if refered database is the primary database +// +// ############################################################################# +func CheckDatabaseRoleAsPrimary(p *dbapi.SingleInstanceDatabase) error { + + if strings.ToUpper(p.Status.Role) != "PRIMARY" { + return fmt.Errorf("referred database %v is not in PRIMARY role", p.Name) + } + 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, + d.Spec.Image.PullFrom, d.Name, d.Namespace, ctx, req) + + return dbReadyPod, err +} + +// ############################################################################# +// +// Get admin password for singleinstancedatabase +// +// ############################################################################# +func GetDatabaseAdminPassword(r client.Reader, d *dbapi.SingleInstanceDatabase, ctx context.Context) (string, error) { + + adminPasswordSecret := &corev1.Secret{} + adminPassword := "" + err := r.Get(ctx, types.NamespacedName{Name: d.Spec.AdminPassword.SecretName, Namespace: d.Namespace}, adminPasswordSecret) + if err != nil { + return adminPassword, err + } + adminPassword = string(adminPasswordSecret.Data[d.Spec.AdminPassword.SecretKey]) + return adminPassword, nil +} + +// ############################################################################# +// +// Validate primary singleinstancedatabase admin password +// +// ############################################################################# +func ValidatePrimaryDatabaseAdminPassword(r *SingleInstanceDatabaseReconciler, p *dbapi.SingleInstanceDatabase, + adminPassword string, ctx context.Context, req ctrl.Request) error { + + dbReadyPod, err := GetDatabaseReadyPod(r, p, ctx, req) + if err != nil { + return err + } + + out, err := dbcommons.ExecCommand(r, r.Config, dbReadyPod.Name, dbReadyPod.Namespace, "", ctx, req, true, "bash", "-c", + fmt.Sprintf("echo -e \"%s\" | %s", fmt.Sprintf(dbcommons.ValidateAdminPassword, adminPassword), dbcommons.GetSqlClient(p.Spec.Edition))) + if err != nil { + return err + } + + if strings.Contains(out, "USER is \"SYS\"") { + r.Log.Info("validated Admin password successfully") + } else { + if strings.Contains(out, "ORA-01017") { + r.Log.Info("Invalid primary database password, Logon denied") + } + return fmt.Errorf("primary database admin password validation failed") + } + + return nil +} + +// ############################################################################# +// +// Validate refered primary database db params are all enabled +// +// ############################################################################# +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("%v modes are not enabled in the primary database %v", strings.Join(missingModes, ","), p.Name) + } + 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 { + + log := r.Log.WithValues("ValidatePrimaryDatabase", req.NamespacedName) + + if stdby.Status.DatafilesCreated == "true" { + return nil + } + + log.Info(fmt.Sprintf("Checking primary database %s status...", primary.Name)) + err := CheckPrimaryDatabaseStatus(primary) + if err != nil { + stdby.Status.Status = dbcommons.StatusPending + return err + } + + log.Info("Checking for referred database role...") + err = CheckDatabaseRoleAsPrimary(primary) + if err != nil { + stdby.Status.Status = dbcommons.StatusError + return err + } + + r.Recorder.Eventf(stdby, corev1.EventTypeNormal, "Validation", "Primary database is ready") + + adminPassword, err := GetDatabaseAdminPassword(r, stdby, ctx) + if err != nil { + stdby.Status.Status = dbcommons.StatusError + return err + } + + log.Info(fmt.Sprintf("Validating admin password for the primary Database %s...", primary.Name)) + err = ValidatePrimaryDatabaseAdminPassword(r, primary, adminPassword, ctx, req) + if err != nil { + stdby.Status.Status = dbcommons.StatusError + return err + } + + 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", err.Error()) + stdby.Status.Status = dbcommons.StatusError + return err + } + + r.Recorder.Eventf(stdby, corev1.EventTypeNormal, "Validation", "Successfully validated the primary database admin password and configuration") + + return nil +} + +// ############################################################################# +// +// 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) + + 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 { + + out, err := dbcommons.ExecCommand(r, r.Config, primaryReadyPod.Name, primaryReadyPod.Namespace, "", + ctx, req, false, "bash", "-c", fmt.Sprintf("cat /opt/oracle/oradata/dbconfig/%s/tnsnames.ora", strings.ToUpper(p.Spec.Sid))) + if err != nil { + return fmt.Errorf("error obtaining the contents of tnsnames.ora in the primary database %v", p.Name) + } + r.Log.Info("tnsnames.ora content is as follows:") + r.Log.Info(out) + + if strings.Contains(out, "(SERVICE_NAME = "+strings.ToUpper(s.Spec.Sid)+")") { + r.Log.Info("TNS ENTRY OF " + s.Spec.Sid + " ALREADY EXISTS ON PRIMARY Database ") + } else { + tnsnamesEntry := dbcommons.StandbyTnsnamesEntry + tnsnamesEntry = strings.ReplaceAll(tnsnamesEntry, "##STANDBYDATABASE_SID##", s.Spec.Sid) + tnsnamesEntry = strings.ReplaceAll(tnsnamesEntry, "##STANDBYDATABASE_SERVICE_EXPOSED##", s.Name) + + out, err = dbcommons.ExecCommand(r, r.Config, primaryReadyPod.Name, primaryReadyPod.Namespace, "", ctx, req, false, "bash", "-c", + fmt.Sprintf("echo -e \"%s\" | cat >> /opt/oracle/oradata/dbconfig/%s/tnsnames.ora ", tnsnamesEntry, strings.ToUpper(p.Spec.Sid))) + if err != nil { + return fmt.Errorf("unable to set tnsnames.ora in the primary database %v", p.Name) + } + r.Log.Info("Modifying tnsnames.ora Output") + r.Log.Info(out) + + } + 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, "", + ctx, req, false, "bash", "-c", "lsnrctl stop && lsnrctl start") + if err != nil { + return fmt.Errorf("unable to restart listener in the database through pod %v", primaryReadyPod.Name) + } + r.Log.Info("Listener restart output") + r.Log.Info(out) + 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 { + + out, err := dbcommons.ExecCommand(r, r.Config, primaryReadyPod.Name, primaryReadyPod.Namespace, "", + ctx, req, false, "bash", "-c", fmt.Sprintf("cat /opt/oracle/oradata/dbconfig/%s/listener.ora ", strings.ToUpper(p.Spec.Sid))) + if err != nil { + return fmt.Errorf("unable to obtain contents of listener.ora in primary database %v", p.Name) + } + r.Log.Info("listener.ora Output") + r.Log.Info(out) + + if strings.Contains(out, strings.ToUpper(p.Spec.Sid)+"_DGMGRL") { + r.Log.Info("LISTENER.ORA ALREADY HAS " + p.Spec.Sid + "_DGMGRL ENTRY IN SID_LIST_LISTENER ") + } else { + out, err = dbcommons.ExecCommand(r, r.Config, primaryReadyPod.Name, primaryReadyPod.Namespace, "", ctx, req, false, "bash", "-c", + fmt.Sprintf("echo -e \"%s\" | cat > /opt/oracle/oradata/dbconfig/%s/listener.ora ", dbcommons.ListenerEntry, strings.ToUpper(p.Spec.Sid))) + if err != nil { + return fmt.Errorf("unable to modify listener.ora in the primary database %v", p.Name) + } + r.Log.Info("Modifying listener.ora Output") + r.Log.Info(out) + + err = RestartListenerInDatabase(r, primaryReadyPod, ctx, req) + if err != nil { + return err + } + + } + 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", + fmt.Sprintf("echo -e \"%s\" | %s", dbcommons.StandbyDatabasePrerequisitesSQL, dbcommons.SQLPlusCLI)) + if err != nil { + return fmt.Errorf("unable to run StandbyDatabasePrerequisitesSQL in primary database") + } + r.Log.Info("StandbyDatabasePrerequisites Output") + r.Log.Info(out) + 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 { + + log := r.Log.WithValues("SetupPrimaryDatabase", req.NamespacedName) + + totalStandbyPods, err := GetTotalDatabasePods(r, stdby, ctx, req) + if err != nil { + return err + } + // NO need to setup primary database if standby database pods are initialized + if totalStandbyPods > 0 { + return nil + } + + primaryDbReadyPod, err := GetDatabaseReadyPod(r, primary, ctx, req) + if err != nil { + return err + } + + log.Info("Setting up tnsnames.ora in primary database", "primaryDatabase", primary.Name) + err = SetupTnsNamesPrimaryForDG(r, primary, stdby, primaryDbReadyPod, ctx, req) + if err != nil { + return err + } + + log.Info("Setting up listener.ora in primary database", "primaryDatabase", primary.Name) + err = SetupListenerPrimaryForDG(r, primary, stdby, primaryDbReadyPod, ctx, req) + if err != nil { + return err + } + + log.Info("Setting up some InitParams for DG in primary database", "primaryDatabase", primary.Name) + err = SetupInitParamsPrimaryForDG(r, primaryDbReadyPod, ctx, req) + if err != nil { + return err + } + + return nil + +} + +// ############################################################################# +// +// 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, "", + 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") + r.Log.Info(out) + + pdbs, _ = dbcommons.StringToLines(out) + 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 { + if pdb == "" { + continue + } + + // Get the Tnsnames.ora entries + out, err := dbcommons.ExecCommand(r, r.Config, dbReadyPod.Name, dbReadyPod.Namespace, "", + ctx, req, false, "bash", "-c", fmt.Sprintf("cat /opt/oracle/oradata/dbconfig/%s/tnsnames.ora", strings.ToUpper(d.Spec.Sid))) + if err != nil { + return err + } + r.Log.Info("tnsnames.ora Output") + r.Log.Info(out) + + if strings.Contains(out, "(SERVICE_NAME = "+strings.ToUpper(pdb)+")") { + r.Log.Info("TNS ENTRY OF " + strings.ToUpper(pdb) + " ALREADY EXISTS ON SIDB ") + } else { + tnsnamesEntry := dbcommons.PDBTnsnamesEntry + tnsnamesEntry = strings.ReplaceAll(tnsnamesEntry, "##PDB_NAME##", strings.ToUpper(pdb)) + + // Add Tnsnames.ora For pdb on Standby Database + out, err = dbcommons.ExecCommand(r, r.Config, dbReadyPod.Name, dbReadyPod.Namespace, "", ctx, req, false, "bash", "-c", + fmt.Sprintf("echo -e \"%s\" | cat >> /opt/oracle/oradata/dbconfig/%s/tnsnames.ora ", tnsnamesEntry, strings.ToUpper(d.Spec.Sid))) + if err != nil { + return err + } + r.Log.Info("Modifying tnsnames.ora for Pdb Output") + r.Log.Info(out) + + } + } + + 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 { + + out, err := dbcommons.ExecCommand(r, r.Config, dbReadyPod.Name, dbReadyPod.Namespace, "", ctx, req, false, "bash", "-c", + fmt.Sprintf("echo -e \"%s\" | cat >> /opt/oracle/oradata/dbconfig/%s/tnsnames.ora ", dbcommons.PrimaryTnsnamesEntry, strings.ToUpper(s.Spec.Sid))) + if err != nil { + return err + } + r.Log.Info("Modifying tnsnames.ora Output") + r.Log.Info(out) + + 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"))) + if err != nil { + return err + } + r.Log.Info("FlashBackTrue Output") + r.Log.Info(out) + return nil +} + +// ############################################################################# +// +// setup standby database +// +// ############################################################################# +func SetupStandbyDatabase(r *SingleInstanceDatabaseReconciler, stdby *dbapi.SingleInstanceDatabase, + primary *dbapi.SingleInstanceDatabase, ctx context.Context, req ctrl.Request) error { + + primaryReadyPod, err := GetDatabaseReadyPod(r, primary, ctx, req) + if err != nil { + return err + } + r.Log.Info("Primary DB Name: " + primaryReadyPod.Name) + + stdbyReadyPod, err := GetDatabaseReadyPod(r, stdby, ctx, req) + if err != nil { + return err + } + + r.Log.Info("Getting the list of all pdbs in primary database") + pdbListPrimary, err := GetAllPdbInDatabase(r, primaryReadyPod, ctx, req) + if err != nil { + return err + } + + r.Log.Info("Setting up tnsnames in standby database for the pdbs of primary database") + err = SetupTnsNamesForPDBListInDatabase(r, stdby, stdbyReadyPod, ctx, req, pdbListPrimary) + if err != nil { + return err + } + + r.Log.Info("Setting up tnsnames entry for primary database in standby database") + err = SetupPrimaryDBTnsNamesInStandby(r, stdby, stdbyReadyPod, ctx, req) + if err != nil { + return err + } + + r.Log.Info("Setting up listener in the standby database") + err = SetupListenerPrimaryForDG(r, stdby, primary, stdbyReadyPod, ctx, req) + if err != nil { + return err + } + + flashBackStatus, _, _, result := dbcommons.CheckDBConfig(stdbyReadyPod, r, r.Config, ctx, req, stdby.Spec.Edition) + if result.Requeue { + return fmt.Errorf("error in obtaining the Database Config status") + } + if !flashBackStatus { + r.Log.Info("Setting up flashback mode in the standby database") + err = EnableFlashbackInDatabase(r, stdbyReadyPod, ctx, req) + if err != nil { + return err + } + } + + 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/controllers/database/suite_test.go b/controllers/database/suite_test.go index 9b62324a..6c6772b4 100644 --- a/controllers/database/suite_test.go +++ b/controllers/database/suite_test.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2022 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -42,13 +42,12 @@ import ( "path/filepath" "testing" - . "github.com/onsi/ginkgo" + . "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" - "sigs.k8s.io/controller-runtime/pkg/envtest/printer" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" @@ -66,12 +65,10 @@ var testEnv *envtest.Environment func TestAPIs(t *testing.T) { RegisterFailHandler(Fail) - RunSpecsWithDefaultAndCustomReporters(t, - "Controller Suite", - []Reporter{printer.NewlineReporter{}}) + RunSpecs(t, "Controller Suite") } -var _ = BeforeSuite(func(done Done) { +var _ = BeforeSuite(func() { logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true))) By("bootstrapping test environment") @@ -87,14 +84,15 @@ var _ = BeforeSuite(func(done Done) { err = databasev1alpha1.AddToScheme(scheme.Scheme) Expect(err).NotTo(HaveOccurred()) + err = databasev1alpha1.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") diff --git a/controllers/observability/databaseobserver_controller.go b/controllers/observability/databaseobserver_controller.go new file mode 100644 index 00000000..bd58e71e --- /dev/null +++ b/controllers/observability/databaseobserver_controller.go @@ -0,0 +1,547 @@ +/* +** 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,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. +// 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 { + + // 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 + + } + + // 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 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..8c20ebe5 --- /dev/null +++ b/controllers/observability/databaseobserver_resource.go @@ -0,0 +1,183 @@ +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" + "k8s.io/apimachinery/pkg/util/intstr" + "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, + TargetPort: intstr.FromInt32(constants.DefaultServiceTargetPort), + }, + }, + }, + } + + 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/adb/ACD.md b/docs/adb/ACD.md new file mode 100644 index 00000000..81ee1a65 --- /dev/null +++ b/docs/adb/ACD.md @@ -0,0 +1,239 @@ +# 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. + +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). + +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. + +## Required Permissions + +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. + +## Supported Features + +After the operator is deployed, choose one of the following operations to create an `AutonomousContainerDatabase` custom resource for Oracle Autonomous Container Database in your cluster. + +* [Provision](#provision-an-autonomous-container-database) an Autonomous Container Database +* [Bind](#bind-to-an-existing-autonomous-container-database) to an existing Autonomous Container Database + +After you create the resource, you can use the operator to perform the following tasks: + +* [Change the display name](#change-the-display-name) of an Autonomous Container Database +* [Restart/Terminate](#restartterminate) an Autonomous Container Database +* [Delete the resource](#delete-the-resource) from the cluster + +## Provision an Autonomous Container Database + +Follow the steps to provision an Autonomous Database that will map objects in your cluster. + +1. Get the `Compartment OCID`. + + Login Cloud Console and click `Compartment`. + + ![compartment-1](/images/adb/compartment-1.png) + + Click on the compartment name where you want to create your database, and **copy** the `OCID` of the compartment. + + ![compartment-2](/images/adb/compartment-2.png) + +2. Get the `AutonomousExadataVMCluster OCID`. + + Login Cloud Console. Go to `Autonomous Database`, and click the `Autonomous Exadata VM Cluster` under the Dedicated Infrastructure. + + ![aei-1](/images/adb/adb-id-1.png) + + Click on the name of the Autonomous Exadata VM Cluster, and copy the `OCID`. + + ![aei-2](/images/adb/aei-id-1.png) + + ![aei-3](/images/adb/aei-id-2.png) + +3. Add the following fields to the AutonomousContainerDatabase resource definition. An example `.yaml` file is available here: [`config/samples/acd/autonomouscontainerdatabase_create.yaml`](./../../config/samples/acd/autonomouscontainerdatabase_create.yaml) + | Attribute | Type | Description | Required? | + |----|----|----|----| + | `spec.compartmentOCID` | string | The [OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the compartment of the Autonomous Container Database. | Yes | + | `spec.autonomousExadataVMClusterOCID` | string | The [OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the Autonomous Exadata Infrastructure. | Yes | + | `spec.displayName` | string | The user-friendly name for the Autonomous Container Database. The name does not have to be unique. | Yes | + | `spec.patchModel` | string | The Database Patch model preference. The following values are valid: RELEASE_UPDATES and RELEASE_UPDATE_REVISIONS. Currently, the Release Update Revision maintenance type is not a selectable option. | No | + | `spec.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/ADB_PREREQUISITES.md#authorized-with-instance-principal). Otherwise, you will need the values from the [Authorized with API Key Authentication](./../adb/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/v1alpha1 + kind: AutonomousContainerDatabase + metadata: + name: autonomouscontainerdatabase-sample + spec: + compartmentOCID: ocid1.compartment... OR ocid1.tenancy... + autonomousExadataVMClusterOCID: ocid1.autonomousexainfrastructure... + displayName: newACD + ociConfig: + configMapName: oci-cred + secretName: oci-privatekey + ``` + +4. Apply the yaml: + + ```sh + kubectl apply -f config/samples/acd/autonomouscontainerdatabase_create.yaml + autonomouscontainerdatabase.database.oracle.com/autonomouscontainerdatabase-sample created + ``` + +## Bind to an existing Autonomous Container Database + +Other than provisioning a container database, you can bind to an existing Autonomous Container Database in your cluster. + +1. Clean up the resource you created in the earlier provision operation: + + ```sh + kubectl delete adb/autonomouscontainerdatabase-sample + autonomouscontainerdatabase.database.oracle.com/autonomouscontainerdatabase-sample deleted + ``` + +2. Copy the `Autonomous Container Database OCID` from Cloud Console. + + ![acd-id-1](/images/adb/adb-id-1.png) + + ![acd-id-2](/images/adb/acd-id-1.png) + + ![acd-id-3](/images/adb/acd-id-2.png) + +3. Add the following fields to the AutonomousContainerDatabase resource definition. An example `.yaml` file is available here: [`config/samples/acd/autonomouscontainerdatabase_bind.yaml`](./../../config/samples/acd/autonomouscontainerdatabase_bind.yaml) + | Attribute | Type | Description | Required? | + |----|----|----|----| + | `spec.autonomousContainerDatabaseOCID` | string | The [OCID](https://docs.cloud.oracle.com/Content/General/Concepts/identifiers.htm) of the Autonomous Container 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 | + + ```yaml + --- + apiVersion: database.oracle.com/v1alpha1 + kind: AutonomousContainerDatabase + metadata: + name: autonomouscontainerdatabase-sample + spec: + autonomousContainerDatabaseOCID: ocid1.autonomouscontainerdatabase... + ociConfig: + configMapName: oci-cred + secretName: oci-privatekey + ``` + +4. Apply the yaml. + + ```sh + kubectl apply -f config/samples/acd/autonomouscontainerdatabase_bind.yaml + autonomouscontainerdatabase.database.oracle.com/autonomouscontainerdatabase-sample created + ``` + +## Change the display name + +> Note: this operation requires an `AutonomousContainerDatabase` object to be in your cluster. This example assumes the provision operation or the bind operation has been completed, and the operator is authorized with API Key Authentication. + +You can change the display name of the database by modifying the value of the `displayName`, as follows: + +1. An example YAML file is available here: [config/samples/acd/autonomouscontainerdatabase_change_displayname.yaml](./../../config/samples/acd/autonomouscontainerdatabase_change_displayname.yaml) + + ```yaml + --- + apiVersion: database.oracle.com/v1alpha1 + kind: AutonomousContainerDatabase + metadata: + name: autonomouscontainerdatabase-sample + spec: + compartmentOCID: ocid1.compartment... OR ocid1.tenancy... + displayName: RenamedADB + ociConfig: + configMapName: oci-cred + secretName: oci-privatekey + ``` + + * `displayNameName`: User-friendly name of the Autonomous Container Database. The name does not have to be unique. + +2. Apply the change using `kubectl`. + + ```sh + kubectl apply -f config/samples/acd/autonomouscontainerdatabase_change_displayname.yaml + autonomouscontainerdatabase.database.oracle.com/autonomouscontainerdatabase-sample configured + ``` + +## Restart/Terminate + +> Note: this operation requires an `AutonomousContainerDatabase` 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. + +Users can restart/terminate a database using the `action` attribute. The value will be erased after the change is applied. +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 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) + + ```yaml + --- + apiVersion: database.oracle.com/v1alpha1 + kind: AutonomousContainerDatabase + metadata: + name: autonomouscontainerdatabase-sample + spec: + autonomousContainerDatabaseOCID: ocid1.autonomouscontainerdatabase... + # Change the action to "TERMINATE" to terminate the database + action: RESTART + ociConfig: + configMapName: oci-cred + secretName: oci-privatekey + ``` + +2. Apply the change to restart the database. + + ```sh + kubectl apply -f config/samples/acd/autonomouscontainerdatabase_restart_terminate.yaml + autonomouscontainerdatabase.database.oracle.com/autonomouscontainerdatabase-sample configured + ``` + +## Delete the resource + +> Note: this operation requires an `AutonomousContainerDatabase` 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. + +The `hardLink` defines the behavior when the resource is deleted from the cluster. If the `hardLink` is set to true, the Operator terminates the Autonomous Container Database in OCI when the resource is removed; otherwise, the Autonomous Container Database remains unchanged. By default the value is `false` if it is not explicitly specified. + +Follow the steps to delete the resource and terminate the Autonomous Container Database. + +1. Use the example [autonomouscontainerdatabase_delete_resource.yaml](./../../config/samples/acd/autonomouscontainerdatabase_delete_resource.yaml) which sets the attribute `hardLink` to true. + + ```yaml + --- + apiVersion: database.oracle.com/v1alpha1 + kind: AutonomousContainerDatabase + metadata: + name: autonomouscontainerdatabase-sample + spec: + autonomousContainerDatabaseOCID: ocid1.autonomouscontainerdatabase... + hardLink: true + ociConfig: + configMapName: oci-cred + secretName: oci-privatekey + ``` + +2. Apply the yaml + + ```sh + kubectl apply -f config/samples/acd/autonomouscontainerdatabase_delete_resource.yaml + autonomouscontainerdatabase.database.oracle.com/autonomouscontainerdatabase-sample configured + ``` + +3. Delete the resource in your cluster + + ```sh + kubectl delete acd/autonomouscontainerdatabase-sample + autonomouscontainerdatabase.database.oracle.com/autonomouscontainerdatabase-sample deleted + ``` + +Now, you can verify that the Autonomous Container Database is in TERMINATING state. diff --git a/docs/adb/ADB_LONG_TERM_BACKUP.md b/docs/adb/ADB_LONG_TERM_BACKUP.md new file mode 100644 index 00000000..4720697d --- /dev/null +++ b/docs/adb/ADB_LONG_TERM_BACKUP.md @@ -0,0 +1,49 @@ +# Creating Long-Term Backups of an Oracle Autonomous Database + +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). + +## 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.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 | + + ```yaml + --- + apiVersion: database.oracle.com/v1alpha1 + kind: AutonomousDatabaseBackup + metadata: + name: autonomousdatabasebackup-sample + spec: + target: + k8sADB: + name: autonomousdatabase-sample + # # Uncomment the below block if you use ADB OCID as the input of the target ADB + # ociADB: + # ocid: ocid1.autonomousdatabase... + displayName: autonomousdatabasebackup-sample + isLongTermBackup: true + retentionPeriodInDays: 90 + ociConfig: + configMapName: oci-cred + secretName: oci-privatekey + ``` + +2. Apply the yaml: + + ```sh + kubectl apply -f config/samples/adb/autonomousdatabase_backup.yaml + autonomousdatabasebackup.database.oracle.com/autonomousdatabasebackup-sample created + ``` diff --git a/docs/adb/ADB_PREREQUISITES.md b/docs/adb/ADB_PREREQUISITES.md index 057a8687..f8c04c4b 100644 --- a/docs/adb/ADB_PREREQUISITES.md +++ b/docs/adb/ADB_PREREQUISITES.md @@ -2,7 +2,7 @@ ## Oracle Autonomous Database (ADB) Prerequisites -Oracle Database Operator for Kubernetes must have access to OCI services. +Oracle Database Operator for Kubernetes must have access to OCI services. To provide access, choose **one of the following approaches**: @@ -10,12 +10,11 @@ To provide access, choose **one of the following approaches**: * The Kubernetes cluster nodes are [granted with Instance Principal](#authorized-with-instance-principal) -### Authorized with API Key Authentication +## Authorized with API Key Authentication -By default, all pods in the Oracle Container Engine for Kubernetes (OKE) are able to access the instance principal certificates, so that the operator calls OCI REST endpoints without any extra step. If you're using OKE, then please proceed to the installation. -If the operator is deployed in a third-party Kubernetes cluster, then the credentials of the Oracle Cloud Infrastructure (OCI) user are needed. The operator reads these credentials from a ConfigMap and a Secret. +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`. +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`. ```sh ./set_ocicredentials.sh run @@ -45,56 +44,107 @@ kubectl create secret generic oci-privatekey \ After creating the ConfigMap and the Secret, use their names as the values of `ociConfigMap` and `ociSecret` attributes in the yaml files for provisioning, binding, and other operations. -### Authorized with Instance Principal +## 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. +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. -> Note: Instance principal authorization applies only to instances that are running in the Oracle Cloud Infrastructure (OCI). +To set up the instance principals, you will have to: -To set up Instance Principle authorization: +* [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) -1. Get the `compartment OCID`: +### Define Dynamic Group - Log in to the cloud console, and click **Compartment**. +1. Go to the **Dynamic Groups** page, and click **Create Dynamic Group**. - ![compartment-1](/images/adb/compartment-1.png) - - Choose the compartment where the cluster creates instances, and **copy** the OCID in the details page. + ![instance-principal-1](/images/adb/instance-principal-1.png) - ![compartment-2](/images/adb/compartment-2.png) +2. In the **Matching Rules** section, write rules the to include the OKE nodes in the dynamic group. -2. Create a dynamic group and matching rules: + Example 1 : enables **all** the instances, including OKE nodes in the compartment, to be members of the dynamic group. - Go to the **Dynamic Groups** page, and click **Create Dynamic Group**. + ```sh + All {instance.compartment.id = ''} + ``` - ![instance-principal-1](/images/adb/instance-principal-1.png) + ![instance-principal-2](/images/adb/instance-principal-2.png) - In the **Matching Rules** section, write the following rule. Change `compartment-OCID` to the OCID of your compartment. This rule enables all the resources, including **nodes** in the compartment, to be members of the dynamic group. + Example 2 : enables the specific OKE nodes in the compartment, to be members of the dynamic group. ```sh - All {instance.compartment.id = 'compartment-OCID'} + Any {instance.id = '', instance.id = '', instance.id = ''} ``` - ![instance-principal-2](/images/adb/instance-principal-2.png) + ![instance-principal-3](/images/adb/instance-principal-3.png) + +3. To apply the rules, click **Create**. - To apply the rules, click **Create**. +### Define Policies -3. Set up policies for dynamic groups: +1. Get the `compartment name` where the database resides: + + > Note: You may skip this step if the database is in the root compartment. + + Go to **Autonomous Database** in the Cloud Console. + + ![adb-id-1](/images/adb/adb-id-1.png) + + Copy the name of the compartment in the details page. + + ![instance-principal-4](/images/adb/instance-principal-4.png) + +2. Set up policies for dynamic groups to grant access to its OCI interactions. Use the dynamic group name is from the [Define Dynamic Group](#define-dynamic-group) section, and the compartment name from the previous step: Go to **Policies**, and click **Create Policy**. - ![instance-principal-3](/images/adb/instance-principal-3.png) + ![instance-principal-5](/images/adb/instance-principal-5.png) + + Example 1: enable the dynamic group to manage **all** the resources in a compartment + + ```sh + Allow dynamic-group to manage all-resources in compartment + ``` - This example enables the dynamic group to manage all the resources in your tenancy: + Example 2: enable the dynamic group to manage **all** the resources in your tenancy (root compartment). ```sh - Allow dynamic-group to manage all-resources in tenancy + Allow dynamic-group to manage all-resources in tenancy ``` - You can also specify a particular resouce access for the dynamic group. This example enables 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 + Allow dynamic-group to manage autonomous-database-family in compartment ``` -At this stage, the operator has been granted sufficient permissions to call OCI services. You can now proceed to the installation. +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/docs/adb/ADB_RESTORE.md b/docs/adb/ADB_RESTORE.md new file mode 100644 index 00000000..7a80090a --- /dev/null +++ b/docs/adb/ADB_RESTORE.md @@ -0,0 +1,51 @@ +# Restoring an Oracle Autonomous Database Manually + +To restore an Autonomous Database from a backup, use this document. + +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 + +To restore an Autonomous Database from a backup, or by using point-in-time restore, complete this procedure. + +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.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 | + + ```yaml + --- + apiVersion: database.oracle.com/v1alpha1 + kind: AutonomousDatabaseRestore + metadata: + name: autonomousdatabaserestore-sample + spec: + target: + k8sADB: + name: autonomousdatabase-sample + # # Uncomment the below block if you use ADB OCID as the input of the target ADB + # ociADB: + # ocid: ocid1.autonomousdatabase... + source: + k8sADBBackup: + name: autonomousdatabasebackup-sample + # # Uncomment the following field to perform point-in-time restore + # pointInTime: + # timestamp: 2022-12-23 11:03:13 UTC + ociConfig: + configMapName: oci-cred + secretName: oci-privatekey + ``` + +2. Apply the yaml: + + ```sh + kubectl apply -f config/samples/adb/autonomousdatabase_restore.yaml + autonomousdatabaserestore.database.oracle.com/autonomousdatabaserestore-sample created + ``` diff --git a/docs/adb/NETWORK_ACCESS_OPTIONS.md b/docs/adb/NETWORK_ACCESS_OPTIONS.md new file mode 100644 index 00000000..e029b52d --- /dev/null +++ b/docs/adb/NETWORK_ACCESS_OPTIONS.md @@ -0,0 +1,250 @@ +# Configuring Network Access for Oracle Autonomous Database + +To configure network access for Oracle Autonomous Database (Autonomous Database), review and complete the procedures in this document. + +Network access for Autonomous Database includes public access, and configuring secure access, either over public networks using access control rules (ACLs), or by using using private endpoints inside a Virtual Cloud Network (VCN) in your tenancy. This document also describes procedures to configure the Transport Layer Security (TLS) connections, with the option either to require mutual TLS only, or to allow both one-way TLS and mutual TLS. + +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. + +### Types of Network Access + +There are three types of network access supported by Autonomous Database: + +* **PUBLIC** + + 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. + +* **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) + +For Autonomous Databases on dedicated Exadata infrastructure, refiew the following examples: + +* Configure network access [with access control list enabled](#autonomous-database-with-access-control-list-enabled-on-dedicated-exadata-infrastructure) + +> 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. + +### Autonomous Database with PUBLIC access type on shared Exadata infrastructure + +To configure the network with PUBLIC access type, 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 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 | + + ```yaml + --- + apiVersion: database.oracle.com/v1alpha1 + kind: AutonomousDatabase + metadata: + name: autonomousdatabase-sample + spec: + details: + autonomousDatabaseOCID: ocid1.autonomousdatabase... + networkAccess: + # Allow secure access from everywhere. + accessType: PUBLIC + ociConfig: + configMapName: oci-cred + secretName: oci-privatekey + ``` + +2. Apply the yaml: + + ```sh + 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 + +To configure the network with RESTRICTED access type, 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 | + + ```yaml + --- + apiVersion: database.oracle.com/v1alpha1 + kind: AutonomousDatabase + metadata: + name: autonomousdatabase-sample + spec: + details: + autonomousDatabaseOCID: ocid1.autonomousdatabase... + networkAccess: + # Restrict access by defining access control rules in an Access Control List (ACL). + 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 + ociConfig: + configMapName: oci-cred + secretName: oci-privatekey + ``` + +2. Apply the yaml: + + ```sh + 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 + +To configure the network with PRIVATE access type, 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. + +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? | + |----|----|----|----| + | `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 | + + ```yaml + --- + apiVersion: database.oracle.com/v1alpha1 + kind: AutonomousDatabase + metadata: + name: autonomousdatabase-sample + spec: + 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... + ociConfig: + configMapName: oci-cred + secretName: oci-privatekey + ``` + +### Allow both TLS and mutual TLS (mTLS) authentication of Autonomous Database on shared Exadata infrastructure + +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: + +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 | + + ```yaml + --- + apiVersion: database.oracle.com/v1alpha1 + kind: AutonomousDatabase + metadata: + name: autonomousdatabase-sample + spec: + details: + autonomousDatabaseOCID: ocid1.autonomousdatabase... + networkAccess: + 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 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_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 | + + ```yaml + --- + apiVersion: database.oracle.com/v1alpha1 + kind: AutonomousDatabase + metadata: + name: autonomousdatabase-sample + spec: + details: + autonomousDatabaseOCID: ocid1.autonomousdatabase... + networkAccess: + isAccessControlEnabled: true + accessControlList: + - 1.1.1.1 + - 1.1.0.0/16 + ociConfig: + configMapName: oci-cred + secretName: oci-privatekey + ``` + +2. Apply the yaml: + + ```sh + kubectl apply -f config/samples/adb/autonomousdatabase_update_network_access.yaml + autonomousdatabase.database.oracle.com/autonomousdatabase-sample configured diff --git a/docs/adb/README.md b/docs/adb/README.md index 6faaf2a0..1b59c4d6 100644 --- a/docs/adb/README.md +++ b/docs/adb/README.md @@ -2,15 +2,17 @@ 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. + ## Required Permissions -The opeartor 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 sample 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. ## Supported Features -After the operator is deployed, choose either one of the following operations to create an `AutonomousDatabase` custom resource for Oracle Autonomous Database in your cluster. +After the operator is deployed, choose one of the following operations to create an `AutonomousDatabase` custom resource for Oracle Autonomous Database in your cluster. * [Provision](#provision-an-autonomous-database) an Autonomous Database * [Bind](#bind-to-an-existing-autonomous-database) to an existing Autonomous Database @@ -19,20 +21,20 @@ After you create the resource, you can use the operator to perform the following * [Scale the OCPU core count or storage](#scale-the-ocpu-core-count-or-storage) an Autonomous Database * [Rename](#rename) an Autonomous Database -* [Manage ADMIN database user password](#manage-admin-passsword) of an Autonomous Database +* [Manage ADMIN database user password](#manage-admin-password) of an Autonomous Database * [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 -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 -Follow the steps to provision an Autonomous Database that will bind objects in your cluster. +Follow these steps to provision an Autonomous Database that will map objects in your cluster. 1. Get the `Compartment OCID`. - Login cloud console and click `Compartment`. + Log in to the Cloud Console and click `Compartment`. ![compartment-1](/images/adb/compartment-1.png) @@ -40,27 +42,45 @@ Follow the steps to provision an Autonomous Database that will bind objects in y ![compartment-2](/images/adb/compartment-2.png) -2. Create a Kubernetes Secret to hold the password of the ADMIN user. +2. To create an Autonomous Database on Dedicated Exadata Infrastructure (ADB-D), the OCID of the Oracle Autonomous Container Database is required. + + You can skip this step if you want to create a Autonomous Database on Shared Exadata Infrastructure (ADB-S). + + Go to the Cloud Console and click `Autonomous Database`. + + ![acd-id-1](/images/adb/adb-id-1.png) + + Under `Dedicated Infrastructure`, click `Autonomous Container Database`. + + ![acd-id-2](/images/adb/acd-id-1.png) + + Click on the name of Autonomous Container Database and copy the `Autonomous Container Database OCID` from the Cloud Console. + + ![acd-id-3](/images/adb/acd-id-2.png) - You can create this secret with the following command (as an example): +3. Create a Kubernetes Secret to hold the password of the ADMIN user. **The key and the name of the secret must be the same.** + + You can create this secret by using a command similar to the following example: ```sh kubectl create secret generic admin-password --from-literal=admin-password='password_here' ``` -3. 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 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.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 `k8sSecretName` or `ociSecretOCID` must be provided. If both `k8sSecretName` and `ociSecretOCID` appear, the Operator reads the password from the K8s secret that `k8sSecretName` refers to. | Yes | - | `spec.details.adminPassword.k8sSecretName` | string | The **name** of the K8s Secret where you want to hold the password for the ADMIN user. | Conditional | - |`spec.details.adminPassword.ociSecretOCID` | 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` | 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.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.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) | 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.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.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 | @@ -81,14 +101,19 @@ Follow the steps to provision an Autonomous Database that will bind objects in y displayName: NewADB cpuCoreCount: 1 adminPassword: - k8sSecretName: admin-password # use the name of the secret from step 2 + k8sSecret: + name: admin-password # use the name of the secret from step 2 dataStorageSizeInTBs: 1 ociConfig: configMapName: oci-cred secretName: oci-privatekey ``` -4. Apply the yaml: +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. + +6. Apply the YAML: ```sh kubectl apply -f config/samples/adb/autonomousdatabase_create.yaml @@ -97,7 +122,9 @@ Follow the steps to provision an Autonomous Database that will bind objects in y ## Bind to an existing Autonomous Database -Other than provisioning a database, you can bind to an existing database in your cluster. +Other than provisioning a database, you can create the custom resource using an existing Autonomous Database. + +The operator also generates the `AutonomousBackup` custom resources if a database already has backups. The operator syncs the `AutonomousBackups` in every reconciliation loop by getting the list of OCIDs of the AutonomousBackups from OCI, and then creates the `AutonomousDatabaseBackup` object automatically if it cannot find a resource that has the same `AutonomousBackupOCID` in the cluster. 1. Clean up the resource you created in the earlier provision operation: @@ -143,9 +170,9 @@ Other than provisioning a database, you can bind to an existing database in your ## Scale the OCPU core count or storage -> Note: this operation requires an `AutonomousDatabase` object to be in your cluster. This example assumes either the provision operation or the bind operation has been done by the users 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 done, and the operator is authorized with API Key Authentication. -Users 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. Here is an example of scaling the CPU count and storage size (TB) up to 2 and turning off the auto-scaling 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 `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. 1. An example YAML file is available here: [config/samples/adb/autonomousdatabase_scale.yaml](./../../config/samples/adb/autonomousdatabase_scale.yaml) @@ -207,7 +234,7 @@ You can rename the database by changing the values of the `dbName` and `displayN autonomousdatabase.database.oracle.com/autonomousdatabase-sample configured ``` -## Manage Admin Passsword +## Manage Admin Password > Note: this operation requires an `AutonomousDatabase` object to be in your cluster. This example assumes the provision operation or the bind operation has been completed, and the operator is authorized with API Key Authentication. @@ -221,7 +248,7 @@ You can rename the database by changing the values of the `dbName` and `displayN \* 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. -2. Update the example [config/samples/adb/autonomousdatabase_change_admin_password.yaml](./../../config/samples/adb/autonomousdatabase_change_admin_password.yaml) +2. Update the example [config/samples/adb/autonomousdatabase_update_admin_password.yaml](./../../config/samples/adb/autonomousdatabase_update_admin_password.yaml) ```yaml --- @@ -233,18 +260,19 @@ You can rename the database by changing the values of the `dbName` and `displayN details: autonomousDatabaseOCID: ocid1.autonomousdatabase... adminPassword: - k8sSecretName: new-admin-password + k8sSecret: + name: new-admin-password ociConfig: configMapName: oci-cred secretName: oci-privatekey ``` - * `adminPassword.k8sSecretName`: the **name** of the secret that you created in **step1**. + * `adminPassword.k8sSecret.name`: the **name** of the secret that you created in **step1**. 3. Apply the YAML. ```sh - kubectl apply -f config/samples/adb/autonomousdatabase_change_admin_password.yaml + kubectl apply -f config/samples/adb/autonomousdatabase_update_admin_password.yaml autonomousdatabase.database.oracle.com/autonomousdatabase-sample configured ``` @@ -278,14 +306,15 @@ A client Wallet is required to connect to a shared Oracle Autonomous Database. U wallet: name: instance-wallet password: - k8sSecretName: instance-wallet-password + k8sSecret: + name: instance-wallet-password ociConfig: configMapName: oci-cred secretName: oci-privatekey ``` * `wallet.name`: the name of the new Secret where you want the downloaded Wallet to be stored. - * `wallet.password.k8sSecretName`: the **name** of the secret you created in **step1**. + * `wallet.password.k8sSecret.name`: the **name** of the secret you created in **step1**. 3. Apply the YAML @@ -310,14 +339,14 @@ 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. -Users can start/stop/terminate a database using the `lifecycleState` attribute. +To start, stop, or terminate a database, use the `lifecycleState` attribute. Here's a list of the values you can set for `lifecycleState`: * `AVAILABLE`: to start the database * `STOPPED`: to stop the database * `TERMINATED`: to terminate the database -1. A sample .yaml file is available here: [config/samples/adb/autonomousdatabase_stop_start_terminate.yaml](./../../config/samples/adb/autonomousdatabase_stop_start_terminate.yaml) +1. An example .yaml file is available here: [config/samples/adb/autonomousdatabase_stop_start_terminate.yaml](./../../config/samples/adb/autonomousdatabase_stop_start_terminate.yaml) ```yaml --- @@ -347,9 +376,9 @@ Here's a list of the values you can set for `lifecycleState`: The `hardLink` defines the behavior when the resource is deleted from the cluster. If the `hardLink` is set to true, the Operator terminates the Autonomous Database in OCI when the resource is removed; otherwise, the database remains unchanged. By default the value is `false` if it is not explicitly specified. -Follow the steps to delete the resource and terminate the Autonomous Database. +To delete the resource and terminate the Autonomous Database, complete these steps: -1. Use the example [autonomousdatabase_delete_resource.yaml](./../../config/samples/adb/autonomousdatabase_delete_resource.yaml) which sets the attribute `hardLink` to true. +1. Use the example [autonomousdatabase_delete_resource.yaml](./../../config/samples/adb/autonomousdatabase_delete_resource.yaml), which sets the attribute `hardLink` to true. ```yaml --- @@ -382,20 +411,33 @@ Follow the steps to delete the resource and terminate the Autonomous Database. 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 -If you edit and re-apply the `.yaml` file, the Autonomous Database controller will only update the parameters that the file contains. The parameters which are not in the file will not be impacted. To get the verbose output of the current spec, use below command: +If you edit and reapply the `.yaml` file, then the Autonomous Database controller will only update the parameters that the file contains. The parameters that are not in the file will not be updated. To obtain the verbose output of the current spec, use the following command: ```sh kubectl describe adb/autonomousdatabase-sample ``` -### The resource is in UNAVAILABLE state +If any error occurs during the reconciliation loop, then the operator reports the error using the resource's event stream, which shows up in kubectl describe output. + +### Check the logs of the pod where the operator deploys -If an error occurs during the operation, the `lifecycleState` of the resoursce changes to UNAVAILABLE. Follow the steps to check the logs. +To check the logs, use these steps: 1. List the pod replicas @@ -403,7 +445,7 @@ If an error occurs during the operation, the `lifecycleState` of the resoursce c kubectl get pods -n oracle-database-operator-system ``` -2. Use the below command to check the logs of the Pod which has a failure +2. Use the following command to check the logs of the Pod that has a failure ```sh kubectl logs -f pod/oracle-database-operator-controller-manager-78666fdddb-s4xcm -n oracle-database-operator-system diff --git a/docs/dbcs/README.md b/docs/dbcs/README.md new file mode 100644 index 00000000..c8b8d5d9 --- /dev/null +++ b/docs/dbcs/README.md @@ -0,0 +1,189 @@ +# Using the DB Operator DBCS 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. + +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 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. + +Note: Oracle Base Database Cloud Service (BDBCS) was previously known as Database Cloud Service (DBCS). + +# Supported Database Editions and Versions + +All single-node OCI Oracle RAC DB systems support the following Oracle Database editions: + +- Standard Edition +- Enterprise Edition +- Enterprise Edition - High Performance +- Enterprise Edition - Extreme Performance + + +Two-node Oracle RAC DB systems require Oracle Enterprise Edition - Extreme Performance. + +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 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 21c +- Oracle Database 19c +- Oracle Database 18c +- Oracle Database 12c Release 2 (12.2) + + +# 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. + +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: +``` +[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 + + +[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 +autonomousdatabasebackups.database.oracle.com 2022-02-08T18:28:55Z +autonomousdatabaserestores.database.oracle.com 2022-02-08T18:28:55Z +autonomousdatabases.database.oracle.com 2022-02-22T23:23:25Z +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 +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 +singleinstancedatabases.database.oracle.com 2022-02-22T23:23:25Z +``` + + +# Prerequisites to deploy a DBCS system using Oracle DB Operator DBCS Controller + +Before you deploy a DBCS system in OCI using the Oracle DB Operator DBCS Controller, complete the following procedure. + +**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: + +``` +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=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" + +kubectl create secret generic oci-privatekey --from-file=privatekey=/root/.oci/oci_api_key.pem +``` + + +## 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": + +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 BDBCS Service. +For example: + +``` +-- 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 +``` + + +## 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: + +``` +[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): +Your identification has been saved in /root/.ssh/id_rsa. +Your public key has been saved in /root/.ssh/id_rsa.pub. +The key fingerprint is: +SHA256:+SuiES/3m9+iuIVyG/QBQL1x7CfRsxtvswBsaBuW5iE DBCS_System-202203 +The key's randomart image is: ++---[RSA 2048]----+ +| .o. . . | +| .o + o | +| .O . o | +| E X.*.+ | +| .*.=S+ + | +| +oo oo + | +| + * o .o o | +| *.*...o. | +| ..+o==o.. | ++----[SHA256]-----+ + + +[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: + +[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) + +## Connecting to OCI DBCS database deployed using Oracle DB Operator DBCS 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). + +## 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. diff --git a/docs/dbcs/provisioning/bind_to_existing_dbcs_system.md b/docs/dbcs/provisioning/bind_to_existing_dbcs_system.md new file mode 100644 index 00000000..6fcff5de --- /dev/null +++ b/docs/dbcs/provisioning/bind_to_existing_dbcs_system.md @@ -0,0 +1,32 @@ +# Binding to an existing DBCS System already deployed in OCI DBCS 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. + +**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: + +- OCI Configmap as `oci-cred` +- OCI Secret as `oci-privatekey` +- OCID of the existing DBCS System as `ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa` + + +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 +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. + +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](./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. diff --git a/docs/dbcs/provisioning/bind_to_existing_dbcs_system.yaml b/docs/dbcs/provisioning/bind_to_existing_dbcs_system.yaml new file mode 100644 index 00000000..49647229 --- /dev/null +++ b/docs/dbcs/provisioning/bind_to_existing_dbcs_system.yaml @@ -0,0 +1,8 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: DbcsSystem +metadata: + name: dbcssystem-existing +spec: + id: "ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa" + ociConfigMap: "oci-cred" + 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 new file mode 100644 index 00000000..454a4452 --- /dev/null +++ b/docs/dbcs/provisioning/bind_to_existing_dbcs_system_sample_output.log @@ -0,0 +1,177 @@ +[root@docker-test-server test]# cat bind_to_existing_dbcs_system.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" +[root@docker-test-server test]# +[root@docker-test-server test]# +[root@docker-test-server test]# kubectl apply -f bind_to_existing_dbcs_system.yaml +dbcssystem.database.oracle.com/dbcssystem-existing created +[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]# +[root@docker-test-server test]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-dlhls -n oracle-database-operator-system +. +. +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 +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: 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 +Spec: + 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: 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.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 + 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 diff --git a/docs/dbcs/provisioning/database_connection.md b/docs/dbcs/provisioning/database_connection.md new file mode 100644 index 00000000..66ac2b5d --- /dev/null +++ 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> +``` diff --git a/docs/dbcs/provisioning/dbcs_controller_parameters.md b/docs/dbcs/provisioning/dbcs_controller_parameters.md new file mode 100644 index 00000000..82fc3dc2 --- /dev/null +++ b/docs/dbcs/provisioning/dbcs_controller_parameters.md @@ -0,0 +1,33 @@ +# Oracle DB Operator DBCS Controller - Parameters to use in .yaml file + +This page has the details of the parameters to define the specs related to an operation to be performed for an OCI DBCS System to be managed using Oracle DB Operator DBCS Controller. + +| Parameter Name | Description | Mandatory Parameter? (Y/N) | Parameter Value type | Default Value (If Any) | Allowed Values (If Any) | +| -------------- | ---------- | ------- | ------- | ------- | ------- | +| ociConfigMap | Kubernetes Configmap created for OCI account in the prerequisites steps. | Y | String | | | +| 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 -.| +| 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 | +| dbEdition | Oracle Database Software Edition. | N | String | | STANDARD_EDITION or ENTERPRISE_EDITION or ENTERPRISE_EDITION_HIGH_PERFORMANCE or ENTERPRISE_EDITION_EXTREME_PERFORMANCE | +| dbName | The database name. | Y | String | | The database name cannot be longer than 8 characters. It can only contain alphanumeric characters. | +| dbVersion | The Oracle Database software version. | Y | String | | Min lenght: 1 and Max length: 255 | +| dbWorkload | The database workload type. | Y | String | | OLTP or DSS | +| diskRedundancy | The type of redundancy configured for the DB system. NORMAL is 2-way redundancy. HIGH is 3-way redundancy. | N | String | | HIGH or NORMAL | +| displayName | The user-friendly name for the DB system. The name does not have to be unique. | N | String | | Min length: 1 and Max length: 255 | +| hostName | The hostname for the DB system. | Y | String | | Hostname can contain only alphanumeric and hyphen (-) characters. | +| initialDataStorageSizeInGB | Size (in GB) of the initial data volume that will be created and attached to a virtual machine DB system. | N | Integer | | Min Value in GB: 2 | +| licenseModel | The Oracle license model that applies to all the databases on the DB system. | N | String | LICENSE_INCLUDED | LICENSE_INCLUDED or BRING_YOUR_OWN_LICENSE | +| nodeCount | The number of nodes in the DB system. For RAC DB systems, the value is greater than 1. | N | Integer | | Minimum: 1 | +| pdbName | The name of the pluggable database. The name must begin with an alphabetic character and can contain a maximum of thirty alphanumeric characters. Special characters are not permitted. | N | String | | The PDB name can contain only alphanumeric and underscore (_) characters. | +| privateIp | A private IP address of your choice. Must be an available IP address within the subnet's CIDR. If you don't specify a value, Oracle automatically assigns a private IP address from the subnet. | N | String | | Min length: 1 and Max length: 46 | +| shape | The shape of the DB system. The shape determines resources to allocate to the DB system. | Y | String | | Please refer to this link for the available shapes: https://docs.oracle.com/en-us/iaas/Content/Database/Concepts/overview.htm | +| sshPublicKeys | Kubernetes secret created with the Public Key portion of the key pair created to access the DB System. | Y | String | | | +| storageManagement | The storage option used in DB system. ASM - Automatic storage management LVM - Logical Volume management. | N | String | | ASM or LVM | +| subnetId | The OCID of the subnet the DB system is associated with. | Y | String | | | +| tags | Tags for the DB System resource. Each tag is a simple key-value pair with no predefined name, type, or namespace. | N | String | | | +| tdeWalletPasswordSecret | The Kubernetes secret for the TDE Wallet password. | N | String | | | +| timeZone | The time zone of the DB system. | N | String | | Please refer to this link: https://docs.oracle.com/en-us/iaas/Content/Database/References/timezones.htm#Time_Zone_Options | diff --git a/docs/dbcs/provisioning/dbcs_service_with_2_node_rac.md b/docs/dbcs/provisioning/dbcs_service_with_2_node_rac.md new file mode 100644 index 00000000..1cfbe006 --- /dev/null +++ b/docs/dbcs/provisioning/dbcs_service_with_2_node_rac.md @@ -0,0 +1,59 @@ +# Deploy a 2 Node RAC DB System using OCI DBCS 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. + +**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: + +- OCI Configmap as `oci-cred` +- 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` +- 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` +- Database Name as `db0130` +- Oracle Database Software Image Version as `21c` +- 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` +- 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` +- Private IP explicitly assigned to be `10.0.1.99` +- Oracle VMDB Shape as `VM.Standard2.2` +- 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` +- 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` + + +**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_all_parameters_asm.yaml](./dbcs_service_with_2_node_rac.yaml) for this use case as below: + +1. Deploy the .yaml file: +```sh +[root@docker-test-server test]# kubectl apply -f dbcs_service_with_2_node_rac.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. + +``` +[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_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. diff --git a/docs/dbcs/provisioning/dbcs_service_with_2_node_rac.yaml b/docs/dbcs/provisioning/dbcs_service_with_2_node_rac.yaml new file mode 100644 index 00000000..168cb427 --- /dev/null +++ b/docs/dbcs/provisioning/dbcs_service_with_2_node_rac.yaml @@ -0,0 +1,38 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: DbcsSystem +metadata: + name: dbcssystem-create +spec: + ociConfigMap: "oci-cred" + ociSecret: "oci-privatekey" + dbSystem: + availabilityDomain: "OLou:PHX-AD-1" + clusterName: "maa-cluster" + compartmentId: "ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya" + dbAdminPaswordSecret: "admin-password" + dbBackupConfig: + autoBackupEnabled: True + autoBackupWindow: "SLOT_FOUR" + recoveryWindowsInDays: 15 + dbEdition: "ENTERPRISE_EDITION_EXTREME_PERFORMANCE" + dbName: "db0130" + dbVersion: "21c" + dbWorkload: "OLTP" + diskRedundancy: "EXTERNAL" + displayName: "dbsystem0130" + hostName: "host0130" + initialDataStorageSizeInGB: 256 + licenseModel: "BRING_YOUR_OWN_LICENSE" + nodeCount: 2 + pdbName: "PDB0130" + privateIp: "10.0.1.99" + shape: "VM.Standard2.2" + sshPublicKeys: + - "oci-publickey" + storageManagement: "ASM" + subnetId: "ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a" + tags: + "TEST": "test_case_provision" + "CreatedBy": "MAA_TEAM" + tdeWalletPasswordSecret: "tde-password" + timeZone: "Europe/Berlin" diff --git a/docs/dbcs/provisioning/dbcs_service_with_2_node_rac_sample_output.log b/docs/dbcs/provisioning/dbcs_service_with_2_node_rac_sample_output.log new file mode 100644 index 00000000..e33e7a86 --- /dev/null +++ b/docs/dbcs/provisioning/dbcs_service_with_2_node_rac_sample_output.log @@ -0,0 +1,352 @@ +NTERPRISE_EDITION_EXTREME_PERFORMANCE[root@docker-test-server test]# cat dbcs_service_with_2_node_rac.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" + clusterName: "maa-cluster" + compartmentId: "ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya" + dbAdminPaswordSecret: "admin-password" + dbBackupConfig: + autoBackupEnabled: True + autoBackupWindow: "SLOT_FOUR" + recoveryWindowsInDays: 15 + dbEdition: "ENTERPRISE_EDITION_EXTREME_PERFORMANCE" + dbName: "db0130" + dbVersion: "21c" + dbWorkload: "OLTP" + diskRedundancy: "EXTERNAL" + displayName: "dbsystem0130" + hostName: "host0130" + initialDataStorageSizeInGB: 256 + licenseModel: "BRING_YOUR_OWN_LICENSE" + nodeCount: 2 + pdbName: "PDB0130" + privateIp: "10.0.1.99" + shape: "VM.Standard2.2" + sshPublicKeys: + - "oci-publickey" + storageManagement: "ASM" + subnetId: "ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a" + tags: + "TEST": "test_case_provision" + "CreatedBy": "MAA_TEAM" + tdeWalletPasswordSecret: "tde-password" + timeZone: "Europe/Berlin" +[root@docker-test-server test]# +[root@docker-test-server test]# kubectl apply -f dbcs_service_with_2_node_rac.yaml +dbcssystem.database.oracle.com/dbcssystem-create configured + + + + +[root@docker-test-server test]# kubectl get ns + + + +NAME STATUS AGE +cert-manager Active 14d +default Active 139d +kube-node-lease Active 139d +kube-public Active 139d +kube-system Active 139d +oracle-database-operator-system Active 14d +shns Active 88d +[root@docker-test-server test]# +[root@docker-test-server test]# +[root@docker-test-server test]# +[root@docker-test-server test]# +[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 14d +pod/oracle-database-operator-controller-manager-665874bd57-g2cgw 1/1 Running 6 14d +pod/oracle-database-operator-controller-manager-665874bd57-q42f8 1/1 Running 7 14d + +NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE +service/oracle-database-operator-controller-manager-metrics-service ClusterIP 10.96.130.124 8443/TCP 14d +service/oracle-database-operator-webhook-service ClusterIP 10.96.4.104 443/TCP 14d + +NAME READY UP-TO-DATE AVAILABLE AGE +deployment.apps/oracle-database-operator-controller-manager 3/3 3 3 14d + +NAME DESIRED CURRENT READY AGE +replicaset.apps/oracle-database-operator-controller-manager-665874bd57 3 3 3 14d +[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-09T04:56:51.425Z 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-09T04:56:51.912Z INFO controller-runtime.manager.controller.dbcssystem DbcsSystem DBSystem provisioning {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} +2022-03-09T04:56:58.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-09T04:57:58.865Z 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-09T04:58:59.218Z 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-09T04:59:59.440Z 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-09T05:01:00.337Z 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-09T05:02:00.893Z 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-09T05:03:02.191Z 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-09T05:04:02.716Z 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-09T05:05:03.081Z 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-09T05:06:03.311Z 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-09T05:07:03.748Z 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-09T05:08:04.219Z 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-09T05:09:04.561Z 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-09T05:10:05.402Z 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-09T05:11:05.798Z 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-09T05:12:06.382Z 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-09T05:13:06.739Z 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-09T05:14:07.309Z 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-09T05:15:08.005Z 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-09T05:16:08.293Z 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-09T05:17:09.084Z 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-09T05:18:09.600Z 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-09T05:19:09.996Z 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-09T05:20:10.354Z 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-09T05:21:11.059Z 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-09T05:22:11.365Z 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-09T05:23:11.665Z 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-09T05:24:12.008Z 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-09T05:25:12.551Z 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-09T05:26:12.988Z 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-09T05:27:13.371Z 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-09T05:28:13.745Z 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-09T05:29:14.034Z 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-09T05:30:14.407Z 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-09T05:31:14.713Z 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-09T05:32:15.202Z 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-09T05:33:15.451Z 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-09T05:34:15.791Z 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-09T05:35:16.216Z 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-09T05:36:16.526Z 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-09T05:37:17.150Z 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-09T05:38:17.447Z 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-09T05:39:17.790Z 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-09T05:40:18.475Z 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-09T05:41:19.115Z 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-09T05:42:19.717Z 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-09T05:43:20.357Z 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-09T05:44:20.661Z 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-09T05:45:20.888Z 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-09T05:46:21.140Z 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-09T05:47:21.431Z 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-09T05:48:21.902Z 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-09T05:49:22.473Z 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-09T05:50:23.330Z 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-09T05:51:23.947Z 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-09T05:52:24.471Z 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-09T05:53:24.961Z 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-09T05:54:25.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-09T05:55:25.720Z 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-09T05:56:26.148Z 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-09T05:57:26.807Z 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-09T05:58:27.458Z 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-09T05:59:28.274Z 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-09T06:00:28.616Z 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-09T06:01:28.966Z 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-09T06:02:29.594Z 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-09T06:03:29.902Z 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-09T06:04:30.357Z 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-09T06:05:30.791Z 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-09T06:06:31.781Z 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-09T06:07:32.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-09T06:08:32.581Z 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-09T06:09:32.969Z 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-09T06:10:33.868Z 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-09T06:11:34.492Z INFO controller-runtime.manager.controller.dbcssystem DbcsSystem system provisioned 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 +Name: dbcssystem-create +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-09T04:54:51Z + 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:clusterName: + 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:nodeCount: + 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-09T04:54:51Z + 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-09T06:11:37Z + Resource Version: 55318179 + UID: 69389564-7574-4150-b44c-3705ea358800 +Spec: + Db System: + Availability Domain: OLou:PHX-AD-1 + Cluster Name: maa-cluster + Compartment Id: ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya + 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: ENTERPRISE_EDITION_EXTREME_PERFORMANCE + Db Name: db0130 + Db Version: 21c + Db Workload: OLTP + Disk Redundancy: EXTERNAL + Display Name: dbsystem0130 + Host Name: host0130 + Initial Data Storage Size In GB: 256 + License Model: BRING_YOUR_OWN_LICENSE + Node Count: 2 + Pdb Name: PDB0130 + Private Ip: 10.0.1.99 + Shape: VM.Standard2.2 + Ssh Public Keys: + oci-publickey + Storage Management: ASM + Subnet Id: ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a + 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 +Status: + Availability Domain: OLou:PHX-AD-1 + Cpu Core Count: 4 + Data Storage Percentage: 80 + Data Storage Size In G Bs: 256 + Db Edition: ENTERPRISE_EDITION_EXTREME_PERFORMANCE + Db Info: + Db Home Id: ocid1.dbhome.oc1.phx.anyhqljr5gy3jhqanf2nzf4535im4dgfaliqtyeqa24gu3j5cg2u7676wo2q + Db Name: db0130 + Db Unique Name: db0130_phx1td + Db Workload: OLTP + Id: ocid1.database.oc1.phx.anyhqljrabf7htyalxkrhk636ibrjzbji7d4fnfm6xbhpizxybllfqzykaca + Display Name: dbsystem0130 + Id: ocid1.dbsystem.oc1.phx.anyhqljrabf7htyahapx2mkbpvilwvfhvisk3umch23s3nnz4spx3zthw55a + 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: 2 + Reco Storage Size In GB: 256 + Shape: VM.Standard2.2 + State: AVAILABLE + Storage Management: ASM + Subnet Id: ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a + Time Zone: Europe/Berlin + Work Requests: + Operation Id: ocid1.coreservicesworkrequest.oc1.phx.abyhqljrapznsaptxjhk3o2ao5nzq7axpinpekj7lf36qmd6veh4ntg45hxa + Operation Type: Create DB System + Percent Complete: 100 + Time Accepted: 2022-03-09 04:56:56.007 +0000 UTC + Time Finished: 2022-03-09 06:08:59.539 +0000 UTC + Time Started: 2022-03-09 04:57:18.983 +0000 UTC +Events: + + + +[root@docker-test-server DBCS]# ssh -i id_rsa opc@129.146.35.79 +The authenticity of host '129.146.35.79 (129.146.35.79)' can't be established. +ECDSA key fingerprint is SHA256:KeuW7n18XXH8mFWnSvcMIeER7NpKyfG4njRpN9Xq/Mk. +ECDSA key fingerprint is MD5:64:e9:52:4f:18:14:fb:eb:ed:48:34:9d:15:80:04:5c. +Are you sure you want to continue connecting (yes/no)? yes +Warning: Permanently added '129.146.35.79' (ECDSA) to the list of known hosts. +[opc@host01301 ~]$ +[opc@host01301 ~]$ +[opc@host01301 ~]$ +[opc@host01301 ~]$ sudo su - grid +Last login: Wed Mar 9 18:23:10 CET 2022 + +[grid@host01301 ~]$ +[grid@host01301 ~]$ cemutlo -n +dbSys3zthw55a diff --git a/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm.md b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm.md new file mode 100644 index 00000000..5ef94c30 --- /dev/null +++ b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm.md @@ -0,0 +1,58 @@ +# Create DBCS 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. + +**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: + +- OCI Configmap as `oci-cred` +- 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` +- 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` +- 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` +- 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` +- 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 `ASM` +- OCID of the Subnet as `ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a` +- 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` + + +**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_all_parameters_asm.yaml](./dbcs_service_with_all_parameters_asm.yaml) for this use case as below: + +1. Deploy the .yaml file: +```sh +[root@docker-test-server DBCS]# 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. + +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_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. diff --git a/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm.yaml b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm.yaml new file mode 100644 index 00000000..1dcec54c --- /dev/null +++ b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm.yaml @@ -0,0 +1,36 @@ +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" + dbBackupConfig: + autoBackupEnabled: True + autoBackupWindow: "SLOT_FOUR" + recoveryWindowsInDays: 15 + dbEdition: "STANDARD_EDITION" + dbName: "db0130" + dbVersion: "21c" + dbWorkload: "OLTP" + diskRedundancy: "EXTERNAL" + displayName: "dbsystem0130" + hostName: "host0130" + initialDataStorageSizeInGB: 256 + licenseModel: "BRING_YOUR_OWN_LICENSE" + pdbName: "PDB0130" + privateIp: "10.0.1.99" + shape: "VM.Standard2.1" + sshPublicKeys: + - "oci-publickey" + storageManagement: "ASM" + subnetId: "ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a" + tags: + "TEST": "test_case_provision" + "CreatedBy": "MAA_TEAM" + tdeWalletPasswordSecret: "tde-password" + timeZone: "Europe/Berlin" 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 new file mode 100644 index 00000000..a2fc6690 --- /dev/null +++ b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_asm_sample_output.log @@ -0,0 +1,313 @@ +[root@docker-test-server test]# cat dbcs_service_with_all_parameters_asm.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" + dbBackupConfig: + autoBackupEnabled: True + autoBackupWindow: "SLOT_FOUR" + recoveryWindowsInDays: 15 + dbEdition: "STANDARD_EDITION" + dbName: "db0130" + dbVersion: "21c" + dbWorkload: "OLTP" + diskRedundancy: "EXTERNAL" + displayName: "dbsystem0130" + hostName: "host0130" + initialDataStorageSizeInGB: 256 + licenseModel: "BRING_YOUR_OWN_LICENSE" + pdbName: "PDB0130" + privateIp: "10.0.1.99" + shape: "VM.Standard2.1" + sshPublicKeys: + - "oci-publickey" + storageManagement: "ASM" + subnetId: "ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a" + tags: + "TEST": "test_case_provision" + "CreatedBy": "MAA_TEAM" + tdeWalletPasswordSecret: "tde-password" + timeZone: "Europe/Berlin" +[root@docker-test-server test]# +[root@docker-test-server test]# +[root@docker-test-server test]# kubectl apply -f dbcs_service_with_all_parameters_asm.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]# kubectl logs -f pod/oracle-database-operator-controller-manager-665874bd57-dlhls -n oracle-database-operator-system +. +. +2022-03-09T02:59:43.691Z 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-09T02:59:44.410Z INFO controller-runtime.manager.controller.dbcssystem DbcsSystem DBSystem provisioning {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} +2022-03-09T02:59:52.341Z 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-09T03:00:52.845Z 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-09T03:01:53.382Z 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-09T03:02:53.737Z 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-09T03:03:54.188Z 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-09T03:04:54.545Z 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-09T03:05:55.030Z 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-09T03:06:55.429Z 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-09T03:07:55.789Z 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-09T03:08:56.188Z 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-09T03:09:56.905Z 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-09T03:10:57.308Z 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-09T03:11:58.068Z 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-09T03:12:58.444Z 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-09T03:13:58.840Z 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-09T03:14:59.194Z 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-09T03:15:59.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-09T03:17:00.249Z 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-09T03:18:00.599Z 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-09T03:19:00.881Z 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-09T03:20:01.121Z 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-09T03:21:01.488Z 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-09T03:22:01.874Z 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-09T03:23:02.726Z 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-09T03:24:03.634Z 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-09T03:25:03.978Z 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-09T03:26:04.450Z 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-09T03:27:04.763Z 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-09T03:28:05.246Z 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-09T03:29:05.825Z 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-09T03:30:06.398Z 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-09T03:31: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-09T03:32:07.551Z 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-09T03:33:08.057Z 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-09T03:34:08.452Z 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-09T03:35:08.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-09T03:36:09.216Z 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-09T03:37:09.584Z 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-09T03:38:09.881Z 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-09T03:39:10.602Z 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-09T03:40:10.869Z 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-09T03:41:11.301Z 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-09T03:42:12.468Z 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-09T03:43:12.732Z 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-09T03:44:13.243Z 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-09T03:45: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-09T03:46:13.873Z 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-09T03:47:14.440Z 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-09T03:48:14.941Z 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-09T03:49:15.381Z 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-09T03:50:16.038Z 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-09T03:51:16.335Z 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-09T03:52:16.785Z 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-09T03:53:17.374Z 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-09T03:54:17.675Z 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-09T03:55:18.054Z 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-09T03:56:18.623Z 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-09T03:57:19.033Z 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-09T03:58:19.611Z 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-09T03:59:20.320Z INFO controller-runtime.manager.controller.dbcssystem DbcsSystem system provisioned 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 +Name: dbcssystem-create +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-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 +Spec: + Db System: + Availability Domain: OLou:PHX-AD-1 + Compartment Id: ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya + 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 Workload: OLTP + Disk Redundancy: EXTERNAL + Display Name: dbsystem0130 + Host Name: host0130 + 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 + Ssh Public Keys: + oci-publickey + Storage Management: ASM + Subnet Id: ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a + 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 +Status: + Availability Domain: OLou:PHX-AD-1 + Cpu Core Count: 1 + Data Storage Percentage: 80 + Data Storage Size In G Bs: 256 + Db Edition: STANDARD_EDITION + Db Info: + Db Home Id: ocid1.dbhome.oc1.phx.anyhqljr5gy3jhqaubltt77vlwmsx7w5d5dvq6be7isglwbpqijfi5gflh5a + Db Name: db0130 + Db Unique Name: db0130_phx1sw + Db Workload: OLTP + Id: ocid1.database.oc1.phx.anyhqljrabf7htya5bzvoxrrc2qu6yjw6c27hcsx32bp7c76vzy35kesa2nq + Display Name: dbsystem0130 + Id: ocid1.dbsystem.oc1.phx.anyhqljrabf7htyaz42sxinatef6xieeppxmwg3bwlw5chpefc52s4joraxq + 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 + Work Requests: + Operation Id: ocid1.coreservicesworkrequest.oc1.phx.abyhqljrlpxe723pq3z5fkeyfgbu4ewsysjcdrxiyxigponwosy44uhcpcsq + 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 diff --git a/docs/dbcs/provisioning/dbcs_service_with_all_parameters_lvm.md b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_lvm.md new file mode 100644 index 00000000..d6beeb16 --- /dev/null +++ b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_lvm.md @@ -0,0 +1,55 @@ +# Create DBCS 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. + +**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: + +- 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` +- 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` +- Database Workload Type as Transaction Processing i.e. `OLTP` +- Display Name for the DBCS System as `dbsystem0130` +- Database Hostname Prefix as `host0130` +- 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` +- 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` +- 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` + + +**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_all_parameters_lvm.yaml](./dbcs_service_with_all_parameters_lvm.yaml) for this use case as below: + +1. Deploy the .yaml file: +```sh +[root@docker-test-server DBCS]# 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. + +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_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. diff --git a/docs/dbcs/provisioning/dbcs_service_with_all_parameters_lvm.yaml b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_lvm.yaml new file mode 100644 index 00000000..73208317 --- /dev/null +++ b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_lvm.yaml @@ -0,0 +1,34 @@ +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" + dbBackupConfig: + autoBackupEnabled: True + autoBackupWindow: "SLOT_FOUR" + recoveryWindowsInDays: 15 + dbEdition: "STANDARD_EDITION" + dbName: "db0130" + dbVersion: "21c" + dbWorkload: "OLTP" + displayName: "dbsystem0130" + hostName: "host0130" + initialDataStorageSizeInGB: 256 + licenseModel: "BRING_YOUR_OWN_LICENSE" + pdbName: "PDB0130" + shape: "VM.Standard2.1" + sshPublicKeys: + - "oci-publickey" + storageManagement: "LVM" + subnetId: "ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a" + tags: + "TEST": "test_case_provision" + "CreatedBy": "MAA_TEAM" + tdeWalletPasswordSecret: "tde-password" + timeZone: "Europe/Berlin" diff --git a/docs/dbcs/provisioning/dbcs_service_with_all_parameters_lvm_sample_output.log b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_lvm_sample_output.log new file mode 100644 index 00000000..f6946eff --- /dev/null +++ b/docs/dbcs/provisioning/dbcs_service_with_all_parameters_lvm_sample_output.log @@ -0,0 +1,266 @@ +[root@docker-test-server test]# cat dbcs_service_with_all_parameters_lvm.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" + dbBackupConfig: + autoBackupEnabled: True + autoBackupWindow: "SLOT_FOUR" + recoveryWindowsInDays: 15 + dbEdition: "STANDARD_EDITION" + dbName: "db0130" + dbVersion: "21c" + dbWorkload: "OLTP" + displayName: "dbsystem0130" + hostName: "host0130" + initialDataStorageSizeInGB: 256 + licenseModel: "BRING_YOUR_OWN_LICENSE" + pdbName: "PDB0130" + shape: "VM.Standard2.1" + sshPublicKeys: + - "oci-publickey" + storageManagement: "LVM" + subnetId: "ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a" + tags: + "TEST": "test_case_provision" + "CreatedBy": "MAA_TEAM" + tdeWalletPasswordSecret: "tde-password" + timeZone: "Europe/Berlin" +[root@docker-test-server test]# +[root@docker-test-server test]# kubectl apply -f dbcs_service_with_all_parameters_lvm.yaml +dbcssystem.database.oracle.com/dbcssystem-create created +[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-09T01:28:57.125Z INFO controller-runtime.manager.controller.dbcssystem DbcsSystem DBSystem provisioning {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} +2022-03-09T01:29:04.321Z 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-09T01:30:04.972Z 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-09T01:31:05.417Z 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-09T01:32:05.728Z 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-09T01:33:06.284Z 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-09T01:34:06.763Z 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-09T01:35:07.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-09T01:36:07.594Z 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-09T01:37:08.416Z 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-09T01:38:08.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-09T01:39:08.998Z 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-09T01:40:09.408Z 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-09T01:41:10.348Z 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-09T01:42:10.845Z 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-09T01:43:11.152Z 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-09T01:44:11.560Z 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-09T01:45:11.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-09T01:46:12.217Z 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-09T01:47:12.442Z 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-09T01:48:12.826Z INFO controller-runtime.manager.controller.dbcssystem DbcsSystem system provisioned succesfully {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-create", "namespace": "default"} +[root@docker-test-server test]# + + + + + + +[root@docker-test-server test]# kubectl describe dbcssystems.database.oracle.com dbcssystem-create +Name: dbcssystem-create +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-09T01:28:56Z + 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:displayName: + f:hostName: + f:initialDataStorageSizeInGB: + f:licenseModel: + f:pdbName: + 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-09T01:28:56Z + 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:vcnName: + f:nodeCount: + f:recoStorageSizeInGB: + f:shape: + f:state: + f:storageManagement: + f:subnetId: + f:timeZone: + f:workRequests: + Manager: manager + Operation: Update + Time: 2022-03-09T01:48:12Z + Resource Version: 55235730 + UID: 53f67e5d-7725-4c8d-a3c2-53ac82f6ef11 +Spec: + Db System: + Availability Domain: OLou:PHX-AD-1 + Compartment Id: ocid1.compartment.oc1..aaaaaaaa4hecw2shffuuc4fcatpin4x3rdkesmmf4he67osupo7g6f7i6eya + 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 Workload: OLTP + Display Name: dbsystem0130 + Host Name: host0130 + Initial Data Storage Size In GB: 256 + License Model: BRING_YOUR_OWN_LICENSE + Pdb Name: PDB0130 + Shape: VM.Standard2.1 + Ssh Public Keys: + oci-publickey + Storage Management: LVM + Subnet Id: ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a + 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 +Status: + Availability Domain: OLou:PHX-AD-1 + Cpu Core Count: 1 + Data Storage Percentage: 80 + Data Storage Size In G Bs: 256 + Db Edition: STANDARD_EDITION + Db Info: + Db Home Id: ocid1.dbhome.oc1.phx.anyhqljr5gy3jhqahugk47wa6hp36fwamqh24lv7bavbqleyerdjgpoublgq + Db Name: db0130 + Db Unique Name: db0130_phx1t6 + Db Workload: OLTP + Id: ocid1.database.oc1.phx.anyhqljrabf7htya4wwpjsm6bc4jlipqrxl7lpgm5dt7rjpfcwnuynslifra + Display Name: dbsystem0130 + Id: ocid1.dbsystem.oc1.phx.anyhqljrabf7htya3y2uepxpcpy4t2gv5ctnw3r2jkfaevxloydy5uilgpna + License Model: BRING_YOUR_OWN_LICENSE + Network: + Client Subnet: k8test-pubvcn + Domain Name: k8testpubvcn.k8test.oraclevcn.com + Host Name: host0130 + Listener Port: 1521 + Vcn Name: k8test + Node Count: 1 + Reco Storage Size In GB: 256 + Shape: VM.Standard2.1 + State: AVAILABLE + Storage Management: LVM + Subnet Id: ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a + Time Zone: Europe/Berlin + Work Requests: + Operation Id: ocid1.coreservicesworkrequest.oc1.phx.abyhqljrmfvsuabnnapzaxlpzxyipcfbqlquxd4yg7cfw57ectybunbjw4tq + Operation Type: Create DB System + Percent Complete: 100 + Time Accepted: 2022-03-09 01:29:01.315 +0000 UTC + Time Finished: 2022-03-09 01:46:27.292 +0000 UTC + Time Started: 2022-03-09 01:29:13.294 +0000 UTC +Events: +[root@docker-test-server test]# + + diff --git a/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters.md b/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters.md new file mode 100644 index 00000000..f6bc8185 --- /dev/null +++ b/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters.md @@ -0,0 +1,44 @@ +# Deploy a DBCS DB System using OCI DBCS Service 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. + +**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: + +- 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` +- Database Admin Credential as `admin-password` +- Database Name as `dbsystem0130` +- Oracle Database Software Image Version as `19c` +- Database Workload Type as Transaction Processing i.e. `OLTP` +- Database Hostname Prefix as `host1205` +- 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` + + +**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_minimal_parameters.yaml](./dbcs_service_with_minimal_parameters.yaml) for this use case as below: + +1. Deploy the .yaml file: +```sh +[root@docker-test-server DBCS]# kubectl apply -f create_required.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. + +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_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. diff --git a/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters.yaml b/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters.yaml new file mode 100644 index 00000000..dc02cfa3 --- /dev/null +++ b/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters.yaml @@ -0,0 +1,25 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: DbcsSystem +metadata: + name: dbcssystem-create +spec: + ociConfigMap: "oci-cred" + ociSecret: "oci-privatekey" + dbSystem: + availabilityDomain: "OLou:EU-MILAN-1-AD-1" + compartmentId: "ocid1.compartment.oc1..aaaaaaaaks5baeqlvv4kyj2jiwnrbxgzm3gsumcfy4c6ntj2ro5i3a5gzhhq" + dbAdminPaswordSecret: "admin-password" + dbEdition: "ENTERPRISE_EDITION_HIGH_PERFORMANCE" + dbName: "testdb" + displayName: "dbsystem0130" + licenseModel: "BRING_YOUR_OWN_LICENSE" + dbVersion: "19c" + dbWorkload: "OLTP" + hostName: "host1205" + shape: "VM.Standard2.1" + 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 new file mode 100644 index 00000000..133f109c --- /dev/null +++ b/docs/dbcs/provisioning/dbcs_service_with_minimal_parameters_sample_output.log @@ -0,0 +1,135 @@ +/usr/bin/kubectl describe dbcssystems.database.oracle.com 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 +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 +Spec: + Db System: + Availability Domain: OLou:EU-MILAN-1-AD-1 + Compartment Id: ocid1.compartment.oc1..aaaaaaaaks5baeqlvv4kyj2jiwnrbxgzm3gsumcfy4c6ntj2ro5i3a5gzhhq + 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 + Ssh Public Keys: + oci-publickey + Subnet Id: ocid1.subnet.oc1.eu-milan-1.aaaaaaaaeiy3tvcsnyg6upfp3ydtu7jmfnmoyifq2ax6y45b5qpdbpide5xa + Oci Config Map: oci-cred + Oci Secret: oci-privatekey +Status: + 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_HIGH_PERFORMANCE + Db Info: + 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.eu-milan-1.anwgsljrabf7htyasoe7b7mtfecc7tdfkp6w5knvvufxmk3phztxfktf6naq + Display Name: dbsystem0130 + Id: ocid1.dbsystem.oc1.eu-milan-1.anwgsljrabf7htyat3fsgcftfilt45bgbrfgawroa2oasamavsluwqyr5aya + 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 + Work Requests: + Operation Id: ocid1.coreservicesworkrequest.oc1.eu-milan-1.abwgsljrkv6jwqtepnxyhnxgtzolw74bqfh5oqlwskq72dqgjpfs5rxu66wa + 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: + diff --git a/docs/dbcs/provisioning/known_issues.md b/docs/dbcs/provisioning/known_issues.md new file mode 100644 index 00000000..2dc54a48 --- /dev/null +++ b/docs/dbcs/provisioning/known_issues.md @@ -0,0 +1,12 @@ +# Known Issues - Oracle DB Operator DBCS Controller + +Below are the known issues using the Oracle DB Operator DBCS Controller: + +1. There is a known issue related to the DB Version 19c, 12c and 11g when used with the Oracle DB Operator DBCS Controller. DB Version 21c and 18c work with the controller. +2. In order to scale up storage of an existing DBCS system, the steps will be: + * Bind the existing DBCS System to DBCS Controller. + * Apply the change to scale up its storage. + This causes issue. The actual real step sequence that work is + * Bind + * Apply Shape change + * Apply scale storage change diff --git a/docs/dbcs/provisioning/scale_down_dbcs_system_shape.md b/docs/dbcs/provisioning/scale_down_dbcs_system_shape.md new file mode 100644 index 00000000..abe98eea --- /dev/null +++ b/docs/dbcs/provisioning/scale_down_dbcs_system_shape.md @@ -0,0 +1,45 @@ +# Scale Down the shape of an existing DBCS 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 order to scale down an existing DBCS system, the steps will be: + +1. Bind the existing DBCS System to DBCS 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: + +- OCID of existing VMDB as `ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa` +- 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` +- Database Admin Credential as `admin-password` +- Database Hostname Prefix as `host0130` +- 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` + +**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./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: + +1. Deploy the .yaml file: +```sh +[root@docker-test-server DBCS]# 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. + +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](./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. diff --git a/docs/dbcs/provisioning/scale_down_dbcs_system_shape.yaml b/docs/dbcs/provisioning/scale_down_dbcs_system_shape.yaml new file mode 100644 index 00000000..5e2cfb3f --- /dev/null +++ b/docs/dbcs/provisioning/scale_down_dbcs_system_shape.yaml @@ -0,0 +1,17 @@ +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" 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 new file mode 100644 index 00000000..acc45208 --- /dev/null +++ b/docs/dbcs/provisioning/scale_down_dbcs_system_shape_sample_output.log @@ -0,0 +1,378 @@ +[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 +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:24:08.850Z 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:24:12.990Z 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:25:13.409Z 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:26:13.878Z 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:27:14.206Z 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:28:14.465Z 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:29:14.735Z 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:30:15.027Z 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:31:15.331Z 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:32:15.768Z 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:33:16.188Z 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:34:16.476Z 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:35:17.125Z 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:36:17.598Z 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:37:18.000Z 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: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 +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 +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.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: 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.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 + 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 + Operation Id: ocid1.coreservicesworkrequest.oc1.phx.abyhqljr5sveun3f6k3zuz23py7mm7jncmpq5vwyajbo5ezhc765347defwq + 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 +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 new file mode 100644 index 00000000..8efccb5f --- /dev/null +++ b/docs/dbcs/provisioning/scale_up_dbcs_system_shape.md @@ -0,0 +1,45 @@ +# Scale UP the shape of an existing DBCS 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 order to scale up an existing DBCS system, the steps will be: + +1. Bind the existing DBCS System to DBCS 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: + +- OCID of existing VMDB as `ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa` +- 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` +- Database Admin Credential as `admin-password` +- Database Hostname Prefix as `host0130` +- Oracle VMDB 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` + +**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./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: + +1. Deploy the .yaml file: +```sh +[root@docker-test-server DBCS]# 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. + +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](./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. diff --git a/docs/dbcs/provisioning/scale_up_dbcs_system_shape.yaml b/docs/dbcs/provisioning/scale_up_dbcs_system_shape.yaml new file mode 100644 index 00000000..d1c2b95d --- /dev/null +++ b/docs/dbcs/provisioning/scale_up_dbcs_system_shape.yaml @@ -0,0 +1,17 @@ +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.2" + sshPublicKeys: + - "oci-publickey" + subnetId: "ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a" diff --git a/docs/dbcs/provisioning/scale_up_dbcs_system_shape_sample_output.log b/docs/dbcs/provisioning/scale_up_dbcs_system_shape_sample_output.log new file mode 100644 index 00000000..96b52924 --- /dev/null +++ b/docs/dbcs/provisioning/scale_up_dbcs_system_shape_sample_output.log @@ -0,0 +1,351 @@ +[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: 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 +apiVersion: database.oracle.com/v1alpha1 + Operation: Update + Time: 2022-03-08T23:27:52Z + Resource Version: 55191827 + UID: 96d7bc49-33e9-42cc-8dd0-ada9a5a4c7e5 +Spec: + 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: 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.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 + 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]# + + + +[root@docker-test-server test]# cat scale_up_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.2" + sshPublicKeys: + - "oci-publickey" + subnetId: "ocid1.subnet.oc1.phx.aaaaaaaauso243tymnzeh6zbz5vkejgyu4ugujul5okpa5xbaq3275izbc7a" +[root@docker-test-server test]# + + +[root@docker-test-server test]# kubectl apply -f scale_up_dbcs_system_shape.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-08T23:32:12.728Z 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:32:50.935Z 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:32:55.703Z 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-08T23:33:55.990Z 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-08T23:34:56.830Z 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-08T23:35:57.120Z 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-08T23:36:57.675Z 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-08T23:37:58.011Z 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-08T23:38:58.566Z 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-08T23:39:58.929Z 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-08T23:40:59.368Z 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-08T23:41:59.837Z 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-08T23:43:00.298Z 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-08T23:44:00.581Z 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-08T23:45:00.942Z 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-08T23:46:01.332Z 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: 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]# + diff --git a/docs/dbcs/provisioning/scale_up_storage.md b/docs/dbcs/provisioning/scale_up_storage.md new file mode 100644 index 00000000..64514025 --- /dev/null +++ b/docs/dbcs/provisioning/scale_up_storage.md @@ -0,0 +1,45 @@ +# Scale UP the storage of an existing DBCS 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 order to scale up storage of an existing DBCS system, the steps will be: + +1. Bind the existing DBCS System to DBCS 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: + +- OCID of existing VMDB as `ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa` +- 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` +- Database Admin Credential as `admin-password` +- Database Hostname Prefix as `host0130` +- 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` + + +Use the file: [scale_up_storage.yaml](./scale_up_storage.yaml) for this use case as below: + +1. Deploy the .yaml file: +```sh +[root@test-server DBCS]# 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. + +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](./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. diff --git a/docs/dbcs/provisioning/scale_up_storage.yaml b/docs/dbcs/provisioning/scale_up_storage.yaml new file mode 100644 index 00000000..34e64b5e --- /dev/null +++ b/docs/dbcs/provisioning/scale_up_storage.yaml @@ -0,0 +1,18 @@ +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" diff --git a/docs/dbcs/provisioning/scale_up_storage_sample_output.log b/docs/dbcs/provisioning/scale_up_storage_sample_output.log new file mode 100644 index 00000000..667667b8 --- /dev/null +++ b/docs/dbcs/provisioning/scale_up_storage_sample_output.log @@ -0,0 +1,389 @@ +[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: 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 +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.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: 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.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 + 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 + Operation Id: ocid1.coreservicesworkrequest.oc1.phx.abyhqljr5sveun3f6k3zuz23py7mm7jncmpq5vwyajbo5ezhc765347defwq + 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 +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 + 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 + Operation Id: ocid1.coreservicesworkrequest.oc1.phx.abyhqljr5sveun3f6k3zuz23py7mm7jncmpq5vwyajbo5ezhc765347defwq + 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 + 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 diff --git a/docs/dbcs/provisioning/terminate_dbcs_system.md b/docs/dbcs/provisioning/terminate_dbcs_system.md new file mode 100644 index 00000000..071cda30 --- /dev/null +++ b/docs/dbcs/provisioning/terminate_dbcs_system.md @@ -0,0 +1,46 @@ +# Terminate an existing DBCS System + +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 order to terminate an existing DBCS system, the steps will be: + +1. Bind the existing DBCS System to DBCS Controller. +2. Apply the change to terminate this DBCS 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: + +- 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). + +Use the file: [terminate_dbcs_system.yaml](./terminate_dbcs_system.yaml) for this use case as below: + +1. Deploy the .yaml file: +```sh +[root@test-server DBCS]# 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 +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 +``` + +3. Check and confirm if the existing OCI DBCS system is NO longer available after sometime because of termination: + +``` +[root@test-server DBCS]# 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. diff --git a/docs/dbcs/provisioning/terminate_dbcs_system.yaml b/docs/dbcs/provisioning/terminate_dbcs_system.yaml new file mode 100644 index 00000000..075ce54e --- /dev/null +++ b/docs/dbcs/provisioning/terminate_dbcs_system.yaml @@ -0,0 +1,9 @@ +apiVersion: database.oracle.com/v1alpha1 +kind: DbcsSystem +metadata: + name: dbcssystem-terminate +spec: + hardLink: True + id: "ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa" + ociConfigMap: "oci-cred" + ociSecret: "oci-privatekey" diff --git a/docs/dbcs/provisioning/terminate_dbcs_system_sample_output.log b/docs/dbcs/provisioning/terminate_dbcs_system_sample_output.log new file mode 100644 index 00000000..ff8afc96 --- /dev/null +++ b/docs/dbcs/provisioning/terminate_dbcs_system_sample_output.log @@ -0,0 +1,248 @@ +[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: 5 + 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:licenseModel: + f:shape: + f:sshPublicKeys: + f:subnetId: + f:id: + f:ociConfigMap: + f:ociSecret: + Manager: kubectl-client-side-apply + Operation: Update + Time: 2022-03-09T01:15:19Z + Resource Version: 55226409 + 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 + License Model: BRING_YOUR_OWN_LICENSE + 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: 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 + 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 + Operation Id: ocid1.coreservicesworkrequest.oc1.phx.abyhqljr5sveun3f6k3zuz23py7mm7jncmpq5vwyajbo5ezhc765347defwq + 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 + 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 + Operation Id: ocid1.coreservicesworkrequest.oc1.phx.abyhqljrvhcpme5ijlsxup22kuumjuzn367vdxwhblv2nxpwshfwnig5au7a + Operation Type: Update DB System License Type + Percent Complete: 100 + Time Accepted: 2022-03-09 01:16:16.991 +0000 UTC + Time Finished: 2022-03-09 01:17:05.025 +0000 UTC + Time Started: 2022-03-09 01:16:24.716 +0000 UTC +Events: +[root@docker-test-server test]# + + +[root@docker-test-server test]# cat terminate_dbcs_system.yaml +apiVersion: database.oracle.com/v1alpha1 +kind: DbcsSystem +metadata: + name: dbcssystem-terminate +spec: + hardLink: True + id: "ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa" + ociConfigMap: "oci-cred" + ociSecret: "oci-privatekey" +[root@docker-test-server test]# +[root@docker-test-server test]# kubectl apply -f terminate_dbcs_system.yaml +dbcssystem.database.oracle.com/dbcssystem-terminate created +[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-09T01:24:18.773Z INFO controller-runtime.manager.controller.dbcssystem OCI provider configured succesfully {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-terminate", "namespace": "default"} +2022-03-09T01:24:18.793Z INFO controller-runtime.manager.controller.dbcssystem Finalizer registered successfully. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-terminate", "namespace": "default"} +2022-03-09T01:24:22.461Z INFO controller-runtime.manager.controller.dbcssystem Sync information from remote DbcsSystem System successfully {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-terminate", "namespace": "default"} + + + + + + +[root@docker-test-server test]# kubectl delete -f terminate_dbcs_system.yaml +dbcssystem.database.oracle.com "dbcssystem-terminate" deleted +[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-09T01:25:05.199Z INFO controller-runtime.manager.controller.dbcssystem OCI provider configured succesfully {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-terminate", "namespace": "default"} +2022-03-09T01:25:05.199Z INFO controller-runtime.manager.controller.dbcssystem Terminate DbcsSystem Database: {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-terminate", "namespace": "default"} +2022-03-09T01:25:06.920Z INFO controller-runtime.manager.controller.dbcssystem Finalizer unregistered successfully. {"reconciler group": "database.oracle.com", "reconciler kind": "DbcsSystem", "name": "dbcssystem-terminate", "namespace": "default"} + + +[root@docker-test-server test]# kubectl delete dbcssystems.database.oracle.com dbcssystem-existing +dbcssystem.database.oracle.com "dbcssystem-existing" deleted +[root@docker-test-server test]# +[root@docker-test-server test]# kubectl describe dbcssystems.database.oracle.com dbcssystem-existing +Error from server (NotFound): dbcssystems.database.oracle.com "dbcssystem-existing" not found +[root@docker-test-server test]# +[root@docker-test-server test]# kubectl delete dbcssystems.database.oracle.com dbcssystem-create +dbcssystem.database.oracle.com "dbcssystem-create" deleted +[root@docker-test-server test]# +[root@docker-test-server test]# kubectl delete dbcssystems.database.oracle.com dbcssystem-create +Error from server (NotFound): dbcssystems.database.oracle.com "dbcssystem-create" not found +[root@docker-test-server test]# diff --git a/docs/dbcs/provisioning/update_license.md b/docs/dbcs/provisioning/update_license.md new file mode 100644 index 00000000..7c7c43b7 --- /dev/null +++ b/docs/dbcs/provisioning/update_license.md @@ -0,0 +1,46 @@ +# Update License type of an existing DBCS 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 order to update the license type an existing DBCS system, the steps will be: + +1. Bind the existing DBCS System to DBCS 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: + +- OCID of existing VMDB as `ocid1.dbsystem.oc1.phx.anyhqljrabf7htyanr3lnp6wtu5ld7qwszohiteodvwahonr2yymrftarkqa` +- 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` +- Database Admin Credential as `admin-password` +- Database Hostname Prefix as `host0130` +- Target license model as `BRING_YOUR_OWN_LICENSE` +- 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` + +**NOTE:** For the details of the parameters to be used in the .yaml file, please refer [here](./dbcs_controller_parameters.md). + +Use the file: [update_license.yaml](./update_license.yaml) for this use case as below: + +1. Deploy the .yaml file: +```sh +[root@test-server DBCS]# 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. + +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](./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. diff --git a/docs/dbcs/provisioning/update_license.yaml b/docs/dbcs/provisioning/update_license.yaml new file mode 100644 index 00000000..1fb54a64 --- /dev/null +++ b/docs/dbcs/provisioning/update_license.yaml @@ -0,0 +1,18 @@ +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" diff --git a/docs/dbcs/provisioning/update_license_sample_output.log b/docs/dbcs/provisioning/update_license_sample_output.log new file mode 100644 index 00000000..7bed4383 --- /dev/null +++ b/docs/dbcs/provisioning/update_license_sample_output.log @@ -0,0 +1,388 @@ +[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 + 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 + Operation Id: ocid1.coreservicesworkrequest.oc1.phx.abyhqljr5sveun3f6k3zuz23py7mm7jncmpq5vwyajbo5ezhc765347defwq + 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 + 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]# + + + +[root@docker-test-server test]# cat update_license.yaml +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" +[root@docker-test-server test]# +[root@docker-test-server test]# kubectl apply -f update_license.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-09T01:15:19.090Z 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-09T01:15:23.534Z 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:16:23.931Z 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:17:24.701Z 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: 5 + 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:licenseModel: + f:shape: + f:sshPublicKeys: + f:subnetId: + f:id: + f:ociConfigMap: + f:ociSecret: + Manager: kubectl-client-side-apply + Operation: Update + Time: 2022-03-09T01:15:19Z + Resource Version: 55226409 + 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 + License Model: BRING_YOUR_OWN_LICENSE + 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: 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 + 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 + Operation Id: ocid1.coreservicesworkrequest.oc1.phx.abyhqljr5sveun3f6k3zuz23py7mm7jncmpq5vwyajbo5ezhc765347defwq + 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 + 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 + Operation Id: ocid1.coreservicesworkrequest.oc1.phx.abyhqljrvhcpme5ijlsxup22kuumjuzn367vdxwhblv2nxpwshfwnig5au7a + Operation Type: Update DB System License Type + Percent Complete: 100 + Time Accepted: 2022-03-09 01:16:16.991 +0000 UTC + Time Finished: 2022-03-09 01:17:05.025 +0000 UTC + Time Started: 2022-03-09 01:16:24.716 +0000 UTC +Events: +[root@docker-test-server test]# \ No newline at end of file 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 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 new file mode 100644 index 00000000..6c9a6756 --- /dev/null +++ b/docs/multitenant/README.md @@ -0,0 +1,254 @@ + + +# 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** + +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 + + **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 diff --git a/docs/multitenant/images/K8S_NAMESPACE_SEG.png b/docs/multitenant/images/K8S_NAMESPACE_SEG.png new file mode 100644 index 00000000..594471ed Binary files /dev/null and b/docs/multitenant/images/K8S_NAMESPACE_SEG.png differ diff --git a/docs/multitenant/images/K8S_SECURE1.png b/docs/multitenant/images/K8S_SECURE1.png new file mode 100644 index 00000000..292c9335 Binary files /dev/null and b/docs/multitenant/images/K8S_SECURE1.png differ diff --git a/docs/multitenant/images/K8S_SECURE2.png b/docs/multitenant/images/K8S_SECURE2.png new file mode 100644 index 00000000..b9713d7c Binary files /dev/null and b/docs/multitenant/images/K8S_SECURE2.png differ diff --git a/docs/multitenant/images/K8S_SECURE3.png b/docs/multitenant/images/K8S_SECURE3.png new file mode 100644 index 00000000..b70123c1 Binary files /dev/null and b/docs/multitenant/images/K8S_SECURE3.png differ diff --git a/docs/multitenant/images/K8S_SECURE4.png b/docs/multitenant/images/K8S_SECURE4.png new file mode 100644 index 00000000..860144e7 Binary files /dev/null and b/docs/multitenant/images/K8S_SECURE4.png differ diff --git a/docs/multitenant/openssl_schema.jpg b/docs/multitenant/openssl_schema.jpg new file mode 100644 index 00000000..4453d52f Binary files /dev/null and b/docs/multitenant/openssl_schema.jpg differ 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 new file mode 100644 index 00000000..7352257e --- /dev/null +++ b/docs/multitenant/usecase01/README.md @@ -0,0 +1,593 @@ + + + +# 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 | +| 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 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. + + + +```text + + +-----------+ + | openssl | + +-----------+ + | + | + +-----------+ + | tls.key | + | tls.crt +------------+ + | ca.crt | | + +-----------+ | + | + +------------------------|---------------------------+ + |KUBERNETES +------+--------+ | + |CLUSTER +---|kubernet secret|---+ | + | | +---------------+ | | + | | | | + | +----------+---+ https +--+----------+ | + | |ORDS CONTAINER|<-------------->| PDB/POD | | + | +----------+---+ +-------------+ | + | cdb.yaml | pdb.yaml | + +-------------|--------------------------------------+ + | + | + +-----------+ + | DB SERVER | + +-----------+ + +``` + +```bash + +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) + + +---- + +#### 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 cdb_create.yaml -n oracle-database-operator-system +``` +Example: **cdb_create.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: "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" + +``` +> **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...... +``` + +[Example of cdb.yaml](./cdb_create.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) + +----- + +#### 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 +``` + +Example: **pdb_create.yaml** + +```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: "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 +``` + ++ 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/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/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/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/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/ImagePush.log b/docs/multitenant/usecase01/logfiles/ImagePush.log new file mode 100644 index 00000000..9b8df426 --- /dev/null +++ b/docs/multitenant/usecase01/logfiles/ImagePush.log @@ -0,0 +1,11 @@ +/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/usecase01/logfiles/cdb.log b/docs/multitenant/usecase01/logfiles/cdb.log new file mode 100644 index 00000000..c75e9bf8 --- /dev/null +++ b/docs/multitenant/usecase01/logfiles/cdb.log @@ -0,0 +1,372 @@ +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/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 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/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 new file mode 100644 index 00000000..cb42ecc3 --- /dev/null +++ b/docs/multitenant/usecase01/logfiles/testapi.log @@ -0,0 +1,62 @@ +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/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/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..be3581ad --- /dev/null +++ b/docs/multitenant/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/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/pdb_map.yaml b/docs/multitenant/usecase01/pdb_map.yaml new file mode 100644 index 00000000..3300a7fa --- /dev/null +++ b/docs/multitenant/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/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/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/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/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/docs/multitenant/usecase02/README.md b/docs/multitenant/usecase02/README.md new file mode 100644 index 00000000..c434271f --- /dev/null +++ b/docs/multitenant/usecase02/README.md @@ -0,0 +1,649 @@ + + + +# 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)) +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] | + +```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" + 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" + +``` + +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" + 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" +``` + +```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" + pdbTlsKey: + secret: + secretName: "db-tls" + key: "tls.key" + pdbTlsCrt: + secret: + secretName: "db-tls" + key: "tls.crt" + pdbTlsCat: + secret: + secretName: "db-ca" + key: "ca.crt" + +``` +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" + 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" + +``` +```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**. The procedure to unplug and plug database does not change apply the same file. + +```yaml +#tde_secret +apiVersion: v1 +kind: Secret +metadata: + name: tde1-secret + namespace: oracle-database-operator-system +type: Opaque +data: + tdepassword: "d2VsY29tZTEK" + tdesecret: "bW1hbHZlenoK" +``` + +```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: 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 +``` + +```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 + +``` + + + + + + +[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/usecase02/pdb_clone.yaml b/docs/multitenant/usecase02/pdb_clone.yaml new file mode 100644 index 00000000..0ecc3c70 --- /dev/null +++ b/docs/multitenant/usecase02/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/usecase02/pdb_plug.yaml b/docs/multitenant/usecase02/pdb_plug.yaml new file mode 100644 index 00000000..77c00b9c --- /dev/null +++ b/docs/multitenant/usecase02/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/usecase02/pdb_plugtde.yaml b/docs/multitenant/usecase02/pdb_plugtde.yaml new file mode 100644 index 00000000..17d84346 --- /dev/null +++ b/docs/multitenant/usecase02/pdb_plugtde.yaml @@ -0,0 +1,56 @@ +# +# 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: "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/usecase02/pdb_unplug.yaml b/docs/multitenant/usecase02/pdb_unplug.yaml new file mode 100644 index 00000000..085d337e --- /dev/null +++ b/docs/multitenant/usecase02/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/usecase02/pdb_unplugtde.yaml b/docs/multitenant/usecase02/pdb_unplugtde.yaml new file mode 100644 index 00000000..4c26bffe --- /dev/null +++ b/docs/multitenant/usecase02/pdb_unplugtde.yaml @@ -0,0 +1,54 @@ +# +# 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: "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/usecase02/tde_secret.yaml b/docs/multitenant/usecase02/tde_secret.yaml new file mode 100644 index 00000000..d0186ff2 --- /dev/null +++ b/docs/multitenant/usecase02/tde_secret.yaml @@ -0,0 +1,15 @@ +# +# 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/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 00000000..bcb0ae77 Binary files /dev/null and b/docs/multitenant/usecase03/NamespaceSegregation.png differ 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/observability/README.md b/docs/observability/README.md new file mode 100644 index 00000000..986b1885 --- /dev/null +++ b/docs/observability/README.md @@ -0,0 +1,256 @@ +# Managing Observability on Kubernetes for Oracle Databases + +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 +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) +* [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 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 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 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 +[./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 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 +``` + +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 +``` + +## 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 + +### 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/docs/sharding/README.md b/docs/sharding/README.md index beb155d6..0c817467 100644 --- a/docs/sharding/README.md +++ b/docs/sharding/README.md @@ -22,20 +22,23 @@ 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. [Prerequsites for running Oracle Sharding Database Controller](#prerequsites-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. -## 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. +**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: @@ -44,9 +47,24 @@ 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 [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: @@ -70,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: @@ -84,22 +104,80 @@ 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 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 + +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. + +**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 -After you have the above prerequsites completed, you can proceed to the next section for your environment to provision the Oracle Database Sharding Topology. +Deploy Oracle Database Sharding Topology with `System-Managed Sharding with SNR RAFT enabled` on your Cloud based Kubernetes cluster. -## Provisioning Sharding Topology in a Cloud-Based Kubernetes Cluster (OKE in this case) +**NOTE: SNR RAFT Feature is available only for Oracle 23ai RDBMS and Oracle 23ai GSM version.** -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 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 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..99620f04 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,46 @@ # 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: + +- 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. + +Please refer the below example for the above steps: ```sh +# Create a directory for files for the secret: +rm -rf /tmp/.secrets/ mkdir /tmp/.secrets/ -# Generate a random openssl key -openssl rand -hex 64 -out /tmp/.secrets/pwd.key +# Create directories and initialize the variables +RSADIR="/tmp/.secrets" +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 +openssl genrsa -out "${RSADIR}"/key.pem +openssl rsa -in "${RSADIR}"/key.pem -out "${RSADIR}"/key.pub -pubout -# Use a password you want and add it to a text file -echo ORacle_21c > /tmp/.secrets/common_os_pwdfile +# Create a text file with the password +rm -f $PWDFILE_ENC +echo ORacle_23c > ${RSADIR}/pwdfile.txt -# 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 +# Create encrypted file from the text file using the RSA key +openssl pkeyutl -in $PWDFILE -out $PWDFILE_ENC -pubin -inkey $PUBKEY -encrypt -# Remove the password text file -rm -f /tmp/.secrets/common_os_pwdfile +# Remove the initial text file: +rm -f $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 +# Deleting the existing secret if existing +kubectl delete secret $SECRET_NAME -n $NAMESPACE -# Check the secret details -kubectl get secret -n shns -``` +# 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 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/oraclesi.yaml b/docs/sharding/provisioning/oraclesi.yaml index ffc5734b..cac70ffa 100644 --- a/docs/sharding/provisioning/oraclesi.yaml +++ b/docs/sharding/provisioning/oraclesi.yaml @@ -1,14 +1,14 @@ # -# Copyright (c) 2021, 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 a6facda0..43b50a5e 100644 --- a/docs/sharding/provisioning/oraclesi_pvc_commented.yaml +++ b/docs/sharding/provisioning/oraclesi_pvc_commented.yaml @@ -1,46 +1,45 @@ # -# Copyright (c) 2021, 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/provisioning_with_control_on_resources.md b/docs/sharding/provisioning/provisioning_with_control_on_resources.md deleted file mode 100644 index 734cad89..00000000 --- a/docs/sharding/provisioning/provisioning_with_control_on_resources.md +++ /dev/null @@ -1,39 +0,0 @@ -# Provisioning Oracle Database Sharding Topology with Additional Control on Resources 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. - -This example uses `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` -* 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 - -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) - -Use the YAML file [shard_prov_memory_cpu.yaml](./shard_prov_memory_cpu.yaml). - -1. Deploy the `shard_prov_memory_cpu.yaml` file: - - ```sh - kubectl apply -f 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/scale_in_delete_an_existing_shard.md b/docs/sharding/provisioning/scale_in_delete_an_existing_shard.md deleted file mode 100644 index 9334741b..00000000 --- a/docs/sharding/provisioning/scale_in_delete_an_existing_shard.md +++ /dev/null @@ -1,44 +0,0 @@ -# Scale In - Delete an existing Shard From a Working Oracle Database Sharding Topology - -This use case demonstrates how to delete an existing Shard from an existing Oracle Database sharding topology 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` - -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) - -NOTE: Use tag `isDelete: true` to delete the shard you want. - -This use case deletes the shard `shard2` from the above Sharding Topology. - -Use the file: [shard_prov_delshard.yaml](./shard_prov_delshard.yaml) for this use case as below: - -1. Deploy the `shard_prov_delshard.yaml` file: - ```sh - kubectl apply -f 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. - -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/scale_out_add_shards.md b/docs/sharding/provisioning/scale_out_add_shards.md deleted file mode 100644 index 174d8c6c..00000000 --- a/docs/sharding/provisioning/scale_out_add_shards.md +++ /dev/null @@ -1,31 +0,0 @@ -# Scale Out - Add Shards to an existing Oracle Database Sharding Topology - -This use case demonstrates adding a new shard to an existing Oracle Database sharding topology 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` -* 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) - -This use case adds three new shards `shard3`,`shard4`,`shard4` to above Sharding Topology. - -Use the file: [shard_prov_extshard.yaml](./shard_prov_extshard.yaml) for this use case as below: - -1. Deploy the `shard_prov_extshard.yaml` file: - ```sh - kubectl apply -f 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 diff --git a/docs/sharding/provisioning/shard_prov.yaml b/docs/sharding/provisioning/shard_prov.yaml deleted file mode 100644 index 508192d7..00000000 --- a/docs/sharding/provisioning/shard_prov.yaml +++ /dev/null @@ -1,46 +0,0 @@ -# -# Copyright (c) 2021, 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 0acc0ec5..00000000 --- a/docs/sharding/provisioning/shard_prov_clone.yaml +++ /dev/null @@ -1,64 +0,0 @@ -# -# Copyright (c) 2021, 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 98d9ee56..00000000 --- a/docs/sharding/provisioning/shard_prov_clone_across_ads.yaml +++ /dev/null @@ -1,72 +0,0 @@ -# -# Copyright (c) 2021, 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_delshard.yaml b/docs/sharding/provisioning/shard_prov_delshard.yaml deleted file mode 100644 index 91dfdd28..00000000 --- a/docs/sharding/provisioning/shard_prov_delshard.yaml +++ /dev/null @@ -1,52 +0,0 @@ -# -# Copyright (c) 2021, 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 - isDelete: true - - name: shard3 - storageSizeInGb: 50 - - name: shard4 - storageSizeInGb: 50 - - name: shard5 - 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_extshard.yaml b/docs/sharding/provisioning/shard_prov_extshard.yaml deleted file mode 100644 index 953fe23f..00000000 --- a/docs/sharding/provisioning/shard_prov_extshard.yaml +++ /dev/null @@ -1,51 +0,0 @@ -# -# Copyright (c) 2021, 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 - - name: shard3 - storageSizeInGb: 50 - - name: shard4 - storageSizeInGb: 50 - - name: shard5 - 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_send_notification.yaml b/docs/sharding/provisioning/shard_prov_send_notification.yaml deleted file mode 100644 index af723c8e..00000000 --- a/docs/sharding/provisioning/shard_prov_send_notification.yaml +++ /dev/null @@ -1,75 +0,0 @@ -# -# Copyright (c) 2021, 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/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/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 55% 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..64e2f4eb 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-Managed 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-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. @@ -17,25 +19,31 @@ 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. + + +**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 `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 52% 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..f7aef949 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-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. 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,35 @@ 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:** 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/) * 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/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 new file mode 100644 index 00000000..c4f45a48 --- /dev/null +++ b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_control_on_resources.md @@ -0,0 +1,44 @@ +# 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-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: + +* 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 + +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_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). + +1. Deploy the `ssharding_shard_prov_memory_cpu.yaml` file: + + ```sh + 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`: + + ```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/provisioning_with_notification_using_oci_notification.md b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_with_notification_using_oci_notification.md similarity index 65% 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..1a6a1ee3 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-Managed 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,17 @@ 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) + * 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: [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/system_sharding/ssharding_provisioning_without_db_gold_image.md b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_without_db_gold_image.md new file mode 100644 index 00000000..b223d1af --- /dev/null +++ b/docs/sharding/provisioning/system_sharding/ssharding_provisioning_without_db_gold_image.md @@ -0,0 +1,37 @@ +# 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-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 `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` + + +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.yaml](./ssharding_shard_prov.yaml) for this use case as below: + +1. Deploy the `ssharding_shard_prov.yaml` file: + ```sh + kubectl apply -f 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 + ``` \ No newline at end of file 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 new file mode 100644 index 00000000..bca34253 --- /dev/null +++ b/docs/sharding/provisioning/system_sharding/ssharding_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 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-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. + +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` + +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_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: [ssharding_shard_prov_delshard.yaml](./ssharding_shard_prov_delshard.yaml) for this use case as below: + +1. Deploy the `ssharding_shard_prov_delshard.yaml` file: + ```sh + 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 `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/system_sharding/ssharding_scale_out_add_shards.md b/docs/sharding/provisioning/system_sharding/ssharding_scale_out_add_shards.md new file mode 100644 index 00000000..1db8e6c3 --- /dev/null +++ b/docs/sharding/provisioning/system_sharding/ssharding_scale_out_add_shards.md @@ -0,0 +1,34 @@ +# 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-Managed 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` + +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_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: [ssharding_shard_prov_extshard.yaml](./ssharding_shard_prov_extshard.yaml) for this use case as below: + +1. Deploy the `ssharding_shard_prov_extshard.yaml` file: + ```sh + 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 "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..7d4e16ec --- /dev/null +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov.yaml @@ -0,0 +1,57 @@ +# +# 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..b7dd1397 --- /dev/null +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_clone_across_ads.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 + 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 + 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_delshard.yaml b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_delshard.yaml new file mode 100644 index 00000000..75caca31 --- /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: 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 + 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 57% rename from docs/sharding/provisioning/shard_prov_memory_cpu.yaml rename to docs/sharding/provisioning/system_sharding/ssharding_shard_prov_memory_cpu.yaml index a542ebab..793a3e4d 100644 --- a/docs/sharding/provisioning/shard_prov_memory_cpu.yaml +++ b/docs/sharding/provisioning/system_sharding/ssharding_shard_prov_memory_cpu.yaml @@ -1,7 +1,8 @@ # -# Copyright (c) 2021, 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: 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..9b2905e8 --- /dev/null +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_by_cloning_db_from_gold_image_across_ads.md @@ -0,0 +1,55 @@ +# 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) + +**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: + ```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..a4669667 --- /dev/null +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_by_cloning_db_gold_image_in_same_ad.md @@ -0,0 +1,51 @@ +# 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) + +**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: + ```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..b52b8745 --- /dev/null +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_control_on_resources.md @@ -0,0 +1,45 @@ +# 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) + * 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). + +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..640301a2 --- /dev/null +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_with_notification_using_oci_notification.md @@ -0,0 +1,85 @@ +# 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) + * 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: + +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/provisioning_without_db_gold_image.md b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_without_db_gold_image.md similarity index 52% rename from docs/sharding/provisioning/provisioning_without_db_gold_image.md rename to docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_without_db_gold_image.md index 0a908b52..2be5ac9f 100644 --- a/docs/sharding/provisioning/provisioning_without_db_gold_image.md +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_provisioning_without_db_gold_image.md @@ -1,28 +1,31 @@ -# Provisioning Oracle Database Sharding Topology Without Database Gold Image +# Provisioning Oracle Sharded Database with User Defined 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 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 `shard_prov.yaml` to provision an Oracle Database sharding topology using Oracle Sharding controller with: +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` -* Two sharding Pods: `shard1` and `shard2` +* 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 `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 `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: [shard_prov.yaml](./shard_prov.yaml) for this use case as below: +Use the file: [udsharding_shard_prov.yaml](./udsharding_shard_prov.yaml) for this use case as below: -1. Deploy the `shard_prov.yaml` file: +1. Deploy the `udsharding_shard_prov.yaml` file: ```sh - kubectl apply -f shard_prov.yaml + kubectl apply -f udsharding_shard_prov.yaml ``` 1. Check the status of the deployment: ```sh 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..2c4cbfc2 --- /dev/null +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_in_delete_an_existing_shard.md @@ -0,0 +1,65 @@ +# 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. + +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) + * 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: [udsharding_shard_prov_delshard.yaml](./udsharding_shard_prov_delshard.yaml) for this use case as below: + +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 + ``` +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 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 check the status, use the following command: + ```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: + 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..20f50b29 --- /dev/null +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_scale_out_add_shards.md @@ -0,0 +1,35 @@ +# 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) + * 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: [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..9b565b73 --- /dev/null +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_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" + 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..28f36608 --- /dev/null +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_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 + 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 + 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 + 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_delshard.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_delshard.yaml new file mode 100644 index 00000000..2342dc55 --- /dev/null +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_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" + 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: enable + - name: shard5 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardSpace: sspace5 + 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 \ 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 new file mode 100644 index 00000000..f45d421f --- /dev/null +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_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" + 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 + - name: shard5 + storageSizeInGb: 50 + imagePullPolicy: "Always" + shardSpace: sspace5 + 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_memory_cpu.yaml b/docs/sharding/provisioning/user-defined-sharding/udsharding_shard_prov_memory_cpu.yaml new file mode 100644 index 00000000..e663aa65 --- /dev/null +++ b/docs/sharding/provisioning/user-defined-sharding/udsharding_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" + 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 diff --git a/docs/sidb/PREREQUISITES.md b/docs/sidb/PREREQUISITES.md new file mode 100644 index 00000000..b904f5da --- /dev/null +++ b/docs/sidb/PREREQUISITES.md @@ -0,0 +1,31 @@ +## Deployment Prerequisites +To deploy Oracle Single Instance Database in Kubernetes using the OraOperator, complete these steps. + +* ### 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. + + 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 + +* ### 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. + +* ### 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/) + +* ### 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: + + ``` + minikube start --memory=8g --disk-size=100g + ``` + diff --git a/docs/sidb/README.md b/docs/sidb/README.md index 7500e212..ff357195 100644 --- a/docs/sidb/README.md +++ b/docs/sidb/README.md @@ -1,295 +1,1244 @@ # Managing Oracle Single Instance Databases with Oracle Database Operator for Kubernetes -Oracle Database Operator for Kubernetes (the operator) includes the Single Instance Database Controller that enables provisioning, cloning, and patching of Oracle Single Instance Databases on Kubernetes. The following sections explain the setup and functionality of the operator +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 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) + * [Pre-built Database](#pre-built-database) + * [XE Database](#xe-database) + * [Free Database](#free-database) + * [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) + * [Changing Init Parameters](#changing-init-parameters) + * [Clone a Database](#clone-a-database) + * [Patch a Database](#patch-a-database) + * [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) + * [Setup Data Guard Configuration for a Single Instance Database (Preview status)](#setup-data-guard-configuration-for-a-single-instance-database-preview-status) + * [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) + * [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) + * [Database API](#database-api) + * [Advanced Usages](#advanced-usages) + * [Oracle Data Pump](#oracle-data-pump) + * [REST Enabled SQL](#rest-enabled-sql) + * [Database Actions](#database-actions) + * [APEX Installation](#apex-installation) + * [Delete ORDS](#delete-ords) + * [Maintenance Operations](#maintenance-operations) + * [Additional Information](#additional-information) -* [Prerequisites](#prerequisites) -* [Kind SingleInstanceDatabase Resource](#kind-singleinstancedatabase-resource) -* [Provision New Database](#provision-new-database) -* [Clone Existing Database](#clone-existing-database) -* [Patch/Rollback Database](#patchrollback-database) ## Prerequisites -Oracle strongly recommends that you follow the [Prerequisites](./SIDB_PREREQUISITES.md). +Oracle strongly recommends to comply with the [prerequisites](./PREREQUISITES.md) and the following requirements -## Kind SingleInstanceDatabase Resource + ### Mandatory Resource Privileges - The Oracle Database Operator creates the SingleInstanceDatabase kind as a custom resource that enables Oracle Database to be managed as a native Kubernetes object + Single Instance Database(sidb) controller mandatorily requires the following Kubernetes resource privileges: -* ### SingleInstanceDatabase Sample YAML - - For the use cases detailed below a sample .yaml file is available at - * Enterprise, Standard Editions - [config/samples/sidb/singleinstancedatabase.yaml](./../../config/samples/sidb/singleinstancedatabase.yaml) + | Resources | Privileges | + | --- | --- | + | 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 | - **Note:** The `adminPassword` field of the above `singleinstancedatabase.yaml` yaml contains a secret for Single Instance Database creation (Provisioning a new database or cloning an existing database). This secret gets deleted after the database pod becomes ready for security reasons. + For managing the required levels of access, configure [role binding](../../README.md#role-binding-for-access-management) - More info on creating Kubernetes Secret available at [https://kubernetes.io/docs/tasks/configmap-secret/managing-secret-using-kubectl/](https://kubernetes.io/docs/tasks/configmap-secret/managing-secret-using-kubectl/) + ### Optional Resource Privileges -* ### List Databases + Single Instance Database(sidb) controller optionally requires the following Kubernetes resource privileges depending on the functionality being used: - ```sh - $ kubectl get singleinstancedatabases -o name + | Functionality | Resources | Privileges | + | --- | --- | --- | + | NodePort Services | Nodes | list watch | + | Storage Expansion with block volumes | StorageClasses | get list watch | + | Custom Scripts Execution | PersistentVolumes | get list watch | - singleinstancedatabase.database.oracle.com/sidb-sample - singleinstancedatabase.database.oracle.com/sidb-sample-clone + For exposing the database via Nodeport services, apply [RBAC](../../rbac/node-rbac.yaml) + ```sh + kubectl apply -f rbac/node-rbac.yaml ``` - -* ### Quick Status - + For automatic storage expansion of block volumes, apply [RBAC](../../rbac/storage-class-rbac.yaml) ```sh - $ kubectl get singleinstancedatabase sidb-sample - - NAME EDITION STATUS ROLE VERSION CLUSTER CONNECT STR CONNECT STR OEM EXPRESS URL - sidb-sample Enterprise Healthy PRIMARY 19.3.0.0.0 (29517242) sidb-sample.default:1521/ORCL1 144.25.10.119:1521/ORCL https://144.25.10.119:5500/em + 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 ``` + + ### OpenShift Security Context Constraints -* ### Detailed Status + 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 - $ kubectl describe singleinstancedatabase sidb-sample-clone + oc new-project sidb-ns + ``` - Name: sidb-sample-clone - Namespace: default - Labels: - Annotations: - API Version: database.oracle.com/v1alpha1 - Kind: SingleInstanceDatabase - Metadata: .... - Spec: .... - Status: - Cluster Connect String: sidb-sample-clone.default:1521/ORCL1C - Conditions: - Last Transition Time: 2021-06-29T15:45:33Z - Message: Waiting for database to be ready - Observed Generation: 2 - Reason: LastReconcileCycleQueued - Status: True - Type: ReconcileQueued - Last Transition Time: 2021-06-30T11:07:56Z - Message: processing datapatch execution - Observed Generation: 3 - Reason: LastReconcileCycleBlocked - Status: True - Type: ReconcileBlocked - Last Transition Time: 2021-06-30T11:16:58Z - Message: no reconcile errors - Observed Generation: 3 - Reason: LastReconcileCycleCompleted - Status: True - Type: ReconcileComplete - Connect String: 144.25.10.119:1521/ORCL1C - Datafiles Created: true - Datafiles Patched: true - Edition: Enterprise - Flash Back: true - Force Log: false - Oem Express URL: https://144.25.10.119:5500/em - Pdb Name: orclpdb1 - Release Update: 19.11.0.0.0 (32545013) - Replicas: 2 - Role: PRIMARY - Sid: ORCL1C - Status: Healthy - Events: - Type Reason Age From Message - ---- ------ ---- ---- ------- - Normal Database Pending 35m (x2 over 35m) SingleInstanceDatabase Waiting for database pod to be ready - Normal Database Creating 27m (x24 over 34m) SingleInstanceDatabase Waiting for database to be ready - Normal Database Ready 22m SingleInstanceDatabase database open on pod sidb-sample-clone-133ol scheduled on node 10.0.10.6 - Normal Datapatch Pending 21m SingleInstanceDatabase datapatch execution pending - Normal Datapatch Executing 20m SingleInstanceDatabase datapatch begin execution - Normal Datapatch Done 8s SingleInstanceDatabase Datapatch from 19.3.0.0.0 to 19.11.0.0.0 : SUCCESS + **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 ``` -## Provision New Database + 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 + +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. + +### Resource Details + +#### Database List +To list databases, use the following command as an example, where the database names are `sidb-sample` and `sidb-sample-clone`, which are the names we will use as database names in command examples: + +```sh +$ kubectl get singleinstancedatabases -o name + + singleinstancedatabase.database.oracle.com/sidb-sample + singleinstancedatabase.database.oracle.com/sidb-sample-clone + +``` + +#### Quick Status +To obtain a quick database status, use the following command as an example: + +```sh +$ kubectl get singleinstancedatabase sidb-sample + +NAME EDITION STATUS VERSION CONNECT STR TCPS CONNECT STR OEM EXPRESS URL +sidb-sample Enterprise Healthy 19.3.0.0.0 10.0.25.54:1521/ORCL1 Unavailable https://10.0.25.54:5500/em +``` + +#### Detailed Status +To obtain a detailed database status, use the following command as an example: + +```sh +$ kubectl describe singleinstancedatabase sidb-sample-clone + + Name: sidb-sample-clone + Namespace: default + Labels: + Annotations: + API Version: database.oracle.com/v1alpha1 + Kind: SingleInstanceDatabase + Metadata: .... + Spec: .... + Status: + Cluster Connect String: sidb-sample-clone.default:1521/ORCL1C + Conditions: + Last Transition Time: (YYYY-MM-DD)T(HH:MM:SS)Z + Message: Waiting for database to be ready + Observed Generation: 2 + Reason: LastReconcileCycleQueued + Status: True + Type: ReconcileQueued + Last Transition Time: 2021-06-30T11:07:56Z + Message: processing datapatch execution + Observed Generation: 3 + Reason: LastReconcileCycleBlocked + Status: True + Type: ReconcileBlocked + Last Transition Time: (YYYY-MM-DD)T(HH:MM:SS)Z + Message: no reconcile errors + Observed Generation: 3 + Reason: LastReconcileCycleCompleted + Status: True + Type: ReconcileComplete + Connect String: 10.0.25.58:1521/ORCL1C + Datafiles Created: true + Datafiles Patched: true + Edition: Enterprise + Flash Back: true + Force Log: false + Oem Express URL: https://10.0.25.58:5500/em + Pdb Name: orclpdb1 + Release Update: 19.11.0.0.0 + Replicas: 2 + Role: PRIMARY + Sid: ORCL1C + Status: Healthy + Events: + Type Reason Age From Message + ---- ------ ---- ---- ------- + Normal Database Pending 35m (x2 over 35m) SingleInstanceDatabase waiting for database pod to be ready + Normal Database Creating 27m (x24 over 34m) SingleInstanceDatabase waiting for database to be ready + Normal Database Ready 22m SingleInstanceDatabase database open on pod sidb-sample-clone-133ol scheduled on node 10.0.10.6 + Normal Datapatch Pending 21m SingleInstanceDatabase datapatch execution pending + Normal Datapatch Executing 20m SingleInstanceDatabase datapatch begin execution + Normal Datapatch Done 8s SingleInstanceDatabase datafiles patched from 19.3.0.0.0 to 19.11.0.0.0 : SUCCESS + +``` + +### 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)** - - Easily provision a new database instance on **minikube** using [singleinstancedatabase_minikube.yaml](../../config/samples/sidb/singleinstancedatabase_minikube.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. - Sign into [Oracle Container Registry](https://container-registry.oracle.com/ords/f?p=113:4:7154182141811:::4:P4_REPOSITORY,AI_REPOSITORY,AI_REPOSITORY_NAME,P4_REPOSITORY_NAME,P4_EULA_ID,P4_BUSINESS_AREA_ID:9,9,Oracle%20Database%20Enterprise%20Edition,Oracle%20Database%20Enterprise%20Edition,1,0&cs=3Y_90hkCQLfJzrvTLiEipIGgWGUytfrtAPuHFocuWd0NDSacbBPlamohfLuiJA-bAsVL6Z_yKEMsTbb52bm6IRA) and accept the license agreement for the Database image, ignore if you have accepted already. +Create this secret using the following command as an example: - Create an image pull secret for Oracle Container Registry, ignore if you have created already: + kubectl create secret generic db-admin-secret --from-literal=oracle_pwd= - ```sh - $ 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 - ``` +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)**. - Now, Easily provision a new database instance on minikube by using following one command. +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. + +2. If you have not already done so, create an image pull secret for the Oracle Container Registry: ```sh - $ kubectl create -f singleinstancedatabase_minikube.yaml + $ 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 + ``` + Note: Generate the auth token from user profile section on top right of the page after logging into container-registry.oracle.com - singleinstancedatabase.database.oracle.com/sidb-sample created + 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 + kubectl create secret generic oracle-container-registry-secret --from-file=.dockerconfigjson=.docker/config.json --type=kubernetes.io/dockerconfigjson ``` - - - Provision a new database instance on any K8s cluster by specifying appropriate values for the attributes in the [singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml) and running the following command: + or + ```sh + podman login container-registry.oracle.com + 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: ```sh - $ kubectl create -f singleinstancedatabase.yaml - - singleinstancedatabase.database.oracle.com/sidb-sample created + $ kubectl apply -f singleinstancedatabase_create.yaml + + singleinstancedatabase.database.oracle.com/sidb-sample created ``` - **NOTE:** Make sure you have created the required `.spec.adminPassword` [secret](https://kubernetes.io/docs/tasks/configmap-secret/managing-secret-using-kubectl/) and `.spec.persistence` [persistent volume](https://kubernetes.io/docs/concepts/storage/persistent-volumes/) +**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. +- 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. + +#### 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: +```sh +$ kubectl apply -f singleinstancedatabase_prebuiltdb.yaml + + singleinstancedatabase.database.oracle.com/prebuiltdb-sample created +``` + +This pre-built image includes the data files of the database inside the image itself. As a result, the database startup time of the container is reduced, down to a couple of seconds. The pre-built database image can be very useful in continuous integration/continuous delivery (CI/CD) scenarios, in which databases are used for conducting tests or experiments, and the workflow is simple. + +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: + + kubectl apply -f singleinstancedatabase_express.yaml + +This command pulls the XE image uploaded on the [Oracle Container Registry](https://container-registry.oracle.com/). + +**Note:** +- Provisioning Oracle Database express edition is supported for release 21c (21.3.0) only. +- 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`. + +#### 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: + + kubectl apply -f singleinstancedatabase_free.yaml + +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 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. + +#### 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: + +```bash +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. + +```sh +$ kubectl get singleinstancedatabase sidb-sample -o "jsonpath={.status.status}" - * ### Creation Status - - Creating a new database instance takes a while. When the 'status' status returns the response "Healthy", the Database is open for connections. - - ```sh - $ kubectl get singleinstancedatabase sidb-sample -o "jsonpath={.status.status}" - - Healthy - ``` + Healthy +``` + +Clients can get the connect-string to the CDB from `.status.connectString` and PDB from `.status.pdbConnectString`. For example: + +```sh +$ kubectl get singleinstancedatabase sidb-sample -o "jsonpath={.status.connectString}" + + 10.0.25.54:1521/ORCL +``` +```sh +$ kubectl get singleinstancedatabase sidb-sample -o "jsonpath={.status.pdbConnectString}" + + 10.0.25.54:1521/ORCLPDB +``` + +Use any supported client or SQLPlus to connect to the database using the above connect strings as follows +```sh +$ sqlplus sys/<.spec.adminPassword>@10.0.25.54:1521/ORCL as sysdba + +SQL*Plus: Release 19.0.0.0.0 - Production on Wed May 4 16:00:49 2022 +Version 19.14.0.0.0 + +Copyright (c) 1982, 2021, Oracle. All rights reserved. + + +Connected to: +Oracle Database 21c Express Edition Release 21.0.0.0.0 - Production +Version 21.3.0.0.0 + +SQL> +``` +**Note:** The `<.spec.adminPassword>` above refers to the database password for SYS, SYSTEM and PDBADMIN users, which in turn represented by `spec` section's `adminPassword` field of the **[config/samples/sidb/singleinstancedatabase.yaml](../config/samples/sidb/../../../../config/samples/sidb/singleinstancedatabase.yaml)** file. + +The Oracle Database inside the container also has Oracle Enterprise Manager Express (OEM Express) as a basic observability console. To access OEM Express, start the browser, and paste in a URL similar to the following example: + +```sh +$ kubectl get singleinstancedatabase sidb-sample -o "jsonpath={.status.oemExpressUrl}" + + https://10.0.25.54:5500/em +``` +**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: +- Dynamic Persistence Provisioning +- Static Persistence Provisioning + +#### Dynamic Persistence +In **Dynamic Persistence Provisioning**, a persistent volume is provisioned by mentioning a storage class. For example, **oci-bv** storage class is specified in the **[singleinstancedatabase_create.yaml](../../config/samples/sidb/singleinstancedatabase_create.yaml)** file. This storage class facilitates dynamic provisioning of the OCI block volumes. The supported access mode for this class is `ReadWriteOnce`. For other cloud providers, you can similarly use their dynamic provisioning storage classes. + +**Note:** +- 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:** +- 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 +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 +kind: PersistentVolume +metadata: + name: db-vol +spec: + capacity: + storage: 10Gi + accessModes: + - ReadWriteMany + persistentVolumeReclaimPolicy: Retain + 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. + +Static Persistence Provisioning in Oracle Cloud Infrastructure (OCI) is explained in the following subsections: + +##### OCI Block Volume Static Provisioning +With block volume static provisioning, you must manually create a block volume resource from the OCI console, and fetch its `OCID`. To create the persistent volume, you can use the following YAML file: +```yaml +apiVersion: v1 +kind: PersistentVolume +metadata: + name: block-vol +spec: + capacity: + storage: 1024Gi + accessModes: + - ReadWriteOnce + persistentVolumeReclaimPolicy: Retain + csi: + driver: blockvolume.csi.oraclecloud.com + volumeHandle: +``` + +**Note:** OCI block volumes are AD (Availability Domain) specific. Ensure that the database is deployed in the same AD as that of its statically provisioned block volume. In dynamic provisioning, this is done automatically. +To provision the database in a specific AD, uncomment the following line from the **[singleinstancedatabase.yaml](../../config/samples/sidb/singleinstancedatabase.yaml)** file: + +```yaml +nodeSelector: + topology.kubernetes.io/zone: PHX-AD-1 +``` + +##### OCI NFS Volume Static Provisioning +Similar to the block volume static provisioning, you have to manually create a file system resource from the OCI console, and fetch its `OCID, Mount Target IP Address and Export Path`. Mention these values in the following YAML file to create the persistent volume: + +```yaml +apiVersion: v1 +kind: PersistentVolume +metadata: + name: nfs-vol +spec: + capacity: + storage: 1024Gi + volumeMode: Filesystem + accessModes: + - ReadWriteMany + persistentVolumeReclaimPolicy: Retain + csi: + driver: fss.csi.oraclecloud.com + volumeHandle: "::/" +``` + +**Note:** +- Example volumeHandle in the above config file : + + `volumeHandle: "ocid1.filesystem.oc1.eu_frankfurt_1.aaaaaqe3bj...eaaa:10.0.10.156:/FileSystem-20220713-1036-02"` + +- 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. + +### Configuring a Database +The `OraOperator` facilitates you to configure the database. Various database configuration options are explained in the following subsections: + +#### Switching Database Modes +The following database modes can be updated after the database is created: + +- `flashBack` +- `archiveLog` +- `forceLog` + +To change these modes, change their attribute values, and apply the change by using the +`kubectl apply` or `kubectl edit/patch` commands. + +**Caution**: Enable `archiveLog` mode before setting `flashBack` to `ON`, and set `flashBack` to `OFF` before disabling `archiveLog` mode. + +For example: + +```sh +$ kubectl patch singleinstancedatabase sidb-sample --type merge -p '{"spec":{"forceLog": true}}' + + singleinstancedatabase.database.oracle.com/sidb-sample patched +``` +Check the Database Config Status by using the following command: + +```sh +$ kubectl get singleinstancedatabase sidb-sample -o "jsonpath=[{.status.archiveLog}, {.status.flashBack}, {.status.forceLog}]" + + [true, true, true] +``` + +#### Changing Init Parameters + +The following database initialization parameters can be updated after the database is created: + +- sgaTarget +- pgaAggregateTarget +- cpuCount +- processes. + +Change their attribute values and apply using `kubectl apply` or `kubectl edit/patch` commands. + +**Note:** +The value for the initialization parameter `sgaTarget` that you provide should be within the range set by [sga_min_size, sga_max_size]. If the value you provide is not in that range, then `sga_target` is not updated to the value you specify for `sgaTarget`. + +#### Immutable YAML Attributes + +The following attributes cannot be modified after creating the Single Instance Database instance: + +- `sid` +- `edition` +- `charset` +- `pdbName` +- `primaryDatabaseRef` + +If you attempt to changing 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 + + The SingleInstanceDatabase "sidb-sample" is invalid: spec.sid: Forbidden: cannot be changed +``` + +### Clone a Database + +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. + +For example: + +```sh +$ kubectl apply -f singleinstancedatabase_clone.yaml + singleinstancedatabase.database.oracle.com/sidb-sample-clone created +``` - * ### Connection Information +**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. - External and internal (running in Kubernetes pods) clients can connect to the database using .status.connectString and .status.clusterConnectString - respectively in the following command +### Patch a Database - ```sh - $ kubectl get singleinstancedatabase sidb-sample -o "jsonpath={.status.connectString}" +Databases running in your cluster and managed by the Oracle Database operator can be patched or rolled back between release updates of the same major release. To patch databases, specify an image of the higher release update. To roll back databases, specify an image of the lower release update. - 144.25.10.119:1521/ORCL - ``` +Patched Oracle Docker images can be built by using this [patching extension](https://github.com/oracle/docker-images/tree/main/OracleDatabase/SingleInstance/extensions/patching). - The Oracle Database inside the container also has Oracle Enterprise Manager Express configured. To access OEM Express, start the browser and follow the URL: +#### Patch - ```sh - $ kubectl get singleinstancedatabase sidb-sample -o "jsonpath={.status.oemExpressUrl}" +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: - https://144.25.10.119:5500/em - ``` +```sh +kubectl --type=merge -p '{"spec":{"image":{"pullFrom":"patched-image:tag","pullSecrets":"pull-secret"}}}' patch singleinstancedatabase sidb-sample + +singleinstancedatabase.database.oracle.com/sidb-sample patched - * ### Update Database Config +``` + +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 + +To clone and patch the database at the same time, clone your source database by using the [cloning existing database](#clone-existing-database) method, and specify a new release image for the cloned database. Use this method to ensure there are no patching related issues impacting your database performance or functionality. - The following database parameters can be updated post database creation: flashBack, archiveLog, forceLog. Change their attribute values and apply using - kubectl apply or edit/patch commands . Enable archiveLog before turning ON flashBack . Turn OFF flashBack before disabling the archiveLog +#### Datapatch Status - ```sh - $ kubectl patch singleinstancedatabase sidb-sample --type merge -p '{"spec":{"forceLog": true}}' +Patching/Rollback operations are complete when the datapatch tool completes patching or rollback of the data files. Check the data files patching status +and current release update version using the following commands - singleinstancedatabase.database.oracle.com/sidb-sample patched - ``` +```sh +$ kubectl get singleinstancedatabase sidb-sample -o "jsonpath={.status.datafilesPatched}" - * #### Database Config Status + true + +$ kubectl get singleinstancedatabase sidb-sample -o "jsonpath={.status.releaseUpdate}" - Check the Database Config Status using the following command + 19.3.0.0.0 +``` - ```sh - $ kubectl get singleinstancedatabase sidb-sample -o "jsonpath=[{.status.archiveLog}, {.status.flashBack}, {.status.forceLog}]" +#### 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: - [true, true, true] - ``` +```bash +kubectl apply -f singleinstancedatabase_patch.yaml +``` - * ### Update Initialization Parameters +This can also be done using the following command: - The following database initialization parameters can be updated post database creation: `sgaTarget, pgaAggregateTarget, cpuCount, processes`. Change their attribute values and apply using kubectl apply or edit/patch commands. +```sh +kubectl --type=merge -p '{"spec":{"image":{"pullFrom":"old-image:tag","pullSecrets":"pull-secret"}}}' patch singleinstancedatabase sidb-sample - **NOTE** - * `sgaTarget` should be in range [sga_min_size, sga_max_size], else initialization parameter `sga_target` would not be updated to specified `sgaTarget`. +singleinstancedatabase.database.oracle.com/sidb-sample patched - * ### Multiple Replicas - - Multiple database pod replicas can be provisioned when the persistent volume access mode is ReadWriteMany. Database is open and mounted by one of the replicas. Other replicas will have instance started but not mounted and serve to provide quick cold fail-over in case the active pod dies. Update the replica attribute in the .yaml and apply using the kubectl apply command or edit/patch commands +``` - Note: This functionality requires the [K8s extension](https://github.com/oracle/docker-images/tree/main/OracleDatabase/SingleInstance/extensions/k8s) - Pre-built images from container-registry.oracle.com include the K8s extension +### Delete a Database +Please run the following command to delete the database: - * ### Patch Attributes +```bash +kubectl delete singleinstancedatabase.database.oracle.com sidb-sample +``` +The command above will delete the database pods and associated service. - The following attributes cannot be patched post SingleInstanceDatabase instance Creation : sid, edition, charset, pdbName, cloneFrom. +### Advanced Database Configurations +Some advanced database configuration scenarios are as follows: - ```sh - $ kubectl --type=merge -p '{"spec":{"sid":"ORCL1"}}' patch singleinstancedatabase sidb-sample +#### Run Database with Multiple Replicas +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. - The SingleInstanceDatabase "sidb-sample" is invalid: spec.sid: Forbidden: cannot be changed - ``` +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. +- 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. + +For example: + +```sh +$ kubectl --type=merge -p '{"spec":{"loadBalancer": true}}' patch singleinstancedatabase sidb-sample + + singleinstancedatabase.database.oracle.com/sidb-sample patched +``` + +### 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. + +Alternatively, you can use the following command: +```bash +kubectl patch --type=merge singleinstancedatabases.database.oracle.com sidb-sample -p '{"spec": {"enableTCPS": true}}' +``` +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 +``` + +**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 get singleinstancedatabase sidb-sample -o "jsonpath={.status.certCreationTimestamp}" + ``` + +**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 + 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_tcps.yaml](../../config/samples/sidb/singleinstancedatabase_tcps.yaml) file, and apply it. - * #### Patch Persistence Volume Claim +**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 + kubectl cp $(kubectl get pods -l app=sidb-sample -o=jsonpath='{.items[0].metadata.name}'):/opt/oracle/oradata/clientWallet/ORCL1 + ``` +- 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 + 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 +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. + +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. + +**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. + +### Setup Data Guard Configuration for a Single Instance Database (Preview status) - Persistence Volume Claim (PVC) can be patched post SingleInstanceDatabase instance Creation . This will **delete all the database pods, PVC** and new database pods are created using the new PVC . +### Create a Standby Database - ```sh - $ kubectl --type=merge -p '{"spec":{"persistence":{"accessMode":"ReadWriteMany","size":"110Gi","storageClass":""}}}' patch singleinstancedatabase sidb-sample +#### 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. - singleinstancedatabase.database.oracle.com/sidb-sample patched - ``` +#### 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). - * #### Patch Service +**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. - Service can be patched post SingleInstanceDatabase instance Creation . This will **replace the Service with a new type** . - * NodePort - '{"spec":{"loadBalancer": false}}' - * LoadBalancer - '{"spec":{"loadBalancer": true }}' +#### List Standby Databases - ```sh - $ kubectl --type=merge -p '{"spec":{"loadBalancer": false}}' patch singleinstancedatabase sidb-sample +```sh +kubectl get singleinstancedatabase - singleinstancedatabase.database.oracle.com/sidb-sample patched - ``` +NAME EDITION STATUS ROLE VERSION CONNECT STR TCPS CONNECT STR OEM EXPRESS URL +sidb-19 Enterprise Healthy PRIMARY 19.3.0.0.0 10.25.0.26:1521/ORCL1 Unavailable https://10.25.0.26:5500/em +stdby-1 Enterprise Healthy PHYSICAL_STANDBY 19.3.0.0.0 10.25.0.27:32392/ORCLS1 Unavailable https://10.25.0.27:30329/em -## Clone Existing Database +``` - Quickly create copies of your existing database using this cloning functionality. A cloned database is an exact, block-for-block copy of the source database. - This is much faster than creating a fresh new database and copying over the data. +### Query Primary Database Reference +You can query the corresponding primary database for every standby database. + +```sh +kubectl get singleinstancedatabase stdby-1 -o "jsonpath={.status.primaryDatabase}" +sidb-19 +``` + +#### Creation Status - To clone, specify the source database reference as value for the cloneFrom attribute in the sample .yaml. - The source database must have archiveLog mode set to true. + Creating a new standby database instance takes a while. When the 'status' status returns the response "Healthy", the Database is open for connections. ```sh - $ grep 'cloneFrom:' singleinstancedatabase.yaml +$ kubectl get singleinstancedatabase stdby-1 -o "jsonpath={.status.status}" + + Healthy +``` + +### Create a Data Guard Configuration + +#### 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) + +#### 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: + +```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` + +#### DataguardBroker List + +To list the DataguardBroker resources, use the following command: + +```sh + $ kubectl get dataguardbroker -o name + + dataguardbroker.database.oracle.com/dataguardbroker-sample + +``` + +#### Quick Status + +```sh + $ kubectl get dataguardbroker dataguardbroker-sample + + NAME PRIMARY STANDBYS PROTECTION MODE CONNECT STR STATUS + dataguardbroker-sample ORCL ORCLS1,ORCLS2 MaxAvailability 10.0.25.85:31555/DATAGUARD Healthy + +``` + +#### Detailed Status + +```sh + $ kubectl describe dataguardbroker dataguardbroker-sample + + Name: dataguardbroker-sample + Namespace: default + Labels: + Annotations: + API Version: database.oracle.com/v1alpha1 + Kind: DataguardBroker + Metadata: + Creation Timestamp: 2023-01-23T04:29:04Z + Finalizers: + database.oracle.com/dataguardbrokerfinalizer + Generation: 3 + Managed Fields: + API Version: database.oracle.com/v1alpha1 + Fields Type: FieldsV1 + fieldsV1: + ... + Manager: manager + Operation: Update + Time: 2023-01-23T04:30:20Z + API Version: database.oracle.com/v1alpha1 + Fields Type: FieldsV1 + fieldsV1: + ... + Manager: kubectl-client-side-apply + Operation: Update + Time: 2023-01-23T04:44:40Z + Resource Version: 75178376 + UID: c04a3d88-2018-4f7f-b232-b74d6c3d9479 + Spec: + Admin Password: + Keep Secret: true + Secret Key: oracle_pwd + Secret Name: db-secret + Fast Start Fail Over: + Enable: true + Primary Database Ref: sidb-sample + Protection Mode: MaxAvailability + Set As Primary Database: + Standby Database Refs: + standby-sample-1 + standby-sample-2 + Status: + Cluster Connect String: dataguardbroker-sample.default:1521/DATAGUARD + External Connect String: 10.0.25.85:31167/DATAGUARD + Primary Database: OR19E3 + Standby Databases: OR19E3S1,OR19E3S2 + Status: Healthy + Events: + Type Reason Age From Message + ---- ------ ---- ---- ------- + Normal SUCCESS 42m DataguardBroker + Normal DG Configuration up to date 24m (x13 over 56m) DataguardBroker +``` - cloneFrom: "sidb-sample" - - $ kubectl create -f singleinstancedatabase.yaml +### 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. + +The database will be set to primary. Ignored if the database is already primary. + +```sh +$ kubectl apply -f dataguardbroker.yaml + + dataguardbroker.database.oracle.com/dataguardbroker-sample apply + +``` +Or use the patch command - singleinstancedatabase.database.oracle.com/sidb-sample-clone created +```sh +$ kubectl --type=merge -p '{"spec":{"setAsPrimaryDatabase":"ORCLS1"}}' patch dataguardbroker dataguardbroker-sample + + dataguardbroker.database.oracle.com/dataguardbroker-sample patched +``` + +#### Static Primary Database Connection String + + 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 + + ```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 + +### 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 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: + +```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. 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 + +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 + +#### Delete DataguardBroker Resource +```sh +$ 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 + +#### Delete Standby Database +```sh +$ 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 `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 - Note: The clone database can specify a database image different from the source database. In such cases, cloning is supported only between databases of the same major release. +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. + +### Resource Details + +#### ORDS List +To list ORDS services, use the following command: + +```sh +$ kubectl get oraclerestdataservice -o name + + oraclerestdataservice.database.oracle.com/ords-sample + +``` + +#### Quick Status +To obtain a quick status check of the ORDS service, use the following command: + +```sh +$ 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 + +``` + +#### Detailed Status +To obtain a detailed status check of the ORDS service, use the following command: + +```sh +$ kubectl describe oraclerestdataservice ords-sample + + Name: ords-sample + Namespace: default + Labels: + Annotations: + API Version: database.oracle.com/v1alpha1 + Kind: OracleRestDataService + 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 + Database Ref: sidb21c-1 + Image: + Pull From: ... + Pull Secrets: ... + Load Balancer: true + Ords Installed: true + Persistence: + Access Mode: ReadWriteMany + Size: 100Gi + Storage Class: + Service IP: 10.0.25.54 + Status: Healthy + +``` + +### Template YAML -## Patch/Rollback Database +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. +- 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 + +#### Provision ORDS - Databases running in your cluster and managed by this operator can be patched or rolled back between release updates of the same major release. To patch databases, specify an image of the higher release update, and to roll back, specify an image of the lower release update. +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: + +```sh +$ kubectl apply -f oraclerestdataservice_create.yaml + + oraclerestdataservice.database.oracle.com/ords-sample created +``` +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: + +```bash +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: + +```bash +kubectl apply -f singleinstancedatabase_secrets.yaml +``` +The APEX secret created above, will be used while [installing APEX](#apex-installation). + +#### Creation Status - Patched Oracle Docker images can be built using this [patching extension](https://github.com/oracle/docker-images/tree/main/OracleDatabase/SingleInstance/extensions/patching) +Creating a new ORDS instance takes a while. To check the status of the ORDS instance, use the following command: - * ### Patch existing Database +```sh +$ kubectl get oraclerestdataservice/ords-sample -o "jsonpath={.status.status}" - Edit and apply the `singleinstancedatabase.yaml` file of the database resource/object by specifying a new release update for image attributes or run the following command. - - ```sh - kubectl --type=merge -p '{"spec":{"image":{"pullFrom":"patched-image:tag","pullSecrets":"pull-secret"}}}' patch singleinstancedatabase sidb-sample + Healthy +``` +ORDS is open for connections when the `status` column returns `Healthy`. - singleinstancedatabase.database.oracle.com/sidb-sample patched +#### REST Endpoints - ``` +Clients can access the REST Endpoints using `.status.databaseApiUrl` as shown in the following command. - The database pods will be restarted with the new release update image. For minimum downtime, ensure that you have mutiple replicas of the database pods running. +```sh +$ kubectl get oraclerestdataservice/ords-sample -o "jsonpath={.status.databaseApiUrl}" - * ### Clone and Patch Database - - Clone your source database using the method of [cloning existing database](README.md#clone-existing-database) and specify a new release image for the cloned database. Use this method to enusure there are no patching related issues impacting your database performance/functionality - - * ### Datapatch status + https://10.0.25.54:8443/ords/ORCLPDB1/_/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). + +There are two basic approaches for authentication to the REST Endpoints. Certain APIs are specific about which authentication method they will accept. + +#### Database API + +To call certain REST endpoints, you must use the ORDS_PUBLIC_USER with role `SQL Administrator`, and `.spec.ordsPassword` credentials. - Patching/Rollback operations are complete when the datapatch tool completes patching or rollback of the data files. Check the data files patching status - and current release update version using the following commands +The ORDS user also has the following additional roles: `System Administrator, SQL Developer, oracle.dbtools.autorest.any.schema`. +Use this ORDS 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: +- **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 + ``` +- **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 + ``` +- **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 + ``` +- **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 + ``` +- **Get all Feature Usage Statistics** ```sh - $ kubectl get singleinstancedatabase sidb-sample -o "jsonpath={.status.datafilesPatched}" + 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 + ``` +#### Advanced Usages + +##### Oracle Data Pump +The Oracle REST Data Services (ORDS) database API enables you to create Oracle Data Pump export and import jobs by using REST web service calls. + +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 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: + +**Run a Script:** + +Create a file called "/tmp/table.sql" with the following contents. + +```sh + CREATE TABLE DEPT ( + DEPTNO NUMBER(2) CONSTRAINT PK_DEPT PRIMARY KEY, + DNAME VARCHAR2(14), + LOC VARCHAR2(13) + ) ; + + INSERT INTO DEPT VALUES (10,'ACCOUNTING','NEW YORK'); + INSERT INTO DEPT VALUES (20,'RESEARCH','DALLAS'); + INSERT INTO DEPT VALUES (30,'SALES','CHICAGO'); + INSERT INTO DEPT VALUES (40,'OPERATIONS','BOSTON'); + COMMIT; +``` + +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" \ + -H "Content-Type: application/sql" \ + -u '<.spec.restEnableSchemas[].schemaName>:<.spec.ordsPassword>' \ + -d @/tmp/table.sql +``` + +**Basic Call:** + +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" \ + -H "Content-Type: application/sql" \ + -u '<.spec.restEnableSchemas[].schemaName>:<.spec.ordsPassword>' \ + -d $'select * from dept;' | python -m json.tool +``` + +**Note:** `.spec.restEnableSchema[].urlMapping` is optional and is defaulted to `.spec.restEnableSchemas[].schemaName` + +##### Database Actions + +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. +* 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`. + +Database Actions can be accessed with a browser by using `.status.databaseActionsUrl`. For example: + +```sh +$ kubectl get oraclerestdataservice/ords-sample -o "jsonpath={.status.databaseActionsUrl}" + + https://10.0.25.54:8443/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` - true +* Second Page: \ +Username: `.spec.restEnableSchemas[].schemaName` \ +Password: `.spec.ordsPassword` + +![database-actions-home](/images/sidb/database-actions-home.png) + +For more information about Database Actions, see: [Oracle Database Actions](https://docs.oracle.com/en/database/oracle/sql-developer-web/21.2/index.html). + +### APEX Installation + +Oracle APEX is a low-code development platform that enables developers to build scalable, secure enterprise apps, with world-class features that can be deployed anywhere. + +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: + + + ```sh + $ kubectl get oraclerestdataservice ords-sample -o "jsonpath={.status.apexConfigured}" + + [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 +``` + +Sign in to Administration services using +workspace: `INTERNAL` +username: `ADMIN` +password: `.spec.apexPassword` + +![application-express-admin-home](/images/sidb/application-express-admin-home.png) + +**Note:** +- 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: - $ kubectl get singleinstancedatabase sidb-sample -o "jsonpath={.status.releaseUpdate}" + kubectl delete oraclerestdataservice ords-sample - 19.3.0.0.0 (29517242) - ``` - +- You cannot delete the referred Database before deleting its ORDS resource. +- APEX, if installed, also gets uninstalled from the database when ORDS gets deleted. + +## Maintenance Operations +If you need to perform some maintenance operations (Database/ORDS) manually, then the procedure is as follows: +1. Use `kubectl exec` to access the pod where you want to perform the manual operation, a command similar to the following: + + kubectl exec -it /bin/bash + +2. The important locations, such as ORACLE_HOME, ORDS_HOME, and so on, can be found in the environment, by using the `env` command. + +3. Log In to `sqlplus` to perform manual operations by using the following command: + + sqlplus / as sysdba + +## Additional information +Detailed instructions for setting up Single Instance Database by OraOperator using OCI free trial account is available now in the LiveLab format. Please use the following link: + [https://oracle.github.io/cloudtestdrive/AppDev/database-operator/workshops/freetier/?lab=introduction](https://oracle.github.io/cloudtestdrive/AppDev/database-operator/workshops/freetier/?lab=introduction) + +Thanks, [Jan Leemans](https://github.com/janleemans), for this effort!! diff --git a/docs/sidb/SIDB_PREREQUISITES.md b/docs/sidb/SIDB_PREREQUISITES.md deleted file mode 100644 index d56c801c..00000000 --- a/docs/sidb/SIDB_PREREQUISITES.md +++ /dev/null @@ -1,17 +0,0 @@ -## Prerequisites for Oracle Docker Image Deployment -To deploy Oracle Database Operator for Kubernetes on Oracle Docker images, complete these steps. - -* ### Prepare Oracle Docker Images - - Build SingleInstanceDatabase 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 - 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: Oracle Database 19c Enterprise Edition or Standard Edition, and later releases. - -* ### Set Up Kubernetes and Volumes - - Set up an on-premises Kubernetes cluster, or subscribe to a managed Kubernetes service, such as Oracle Cloud Infrastructure Container Engine for Kubernetes, configured with persistent volumes. The persistent volumes are required for 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/) - - More info on creating persistent volumes on OCI is available at [https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengcreatingpersistentvolumeclaim.htm](https://docs.oracle.com/en-us/iaas/Content/ContEng/Tasks/contengcreatingpersistentvolumeclaim.htm) diff --git a/go.mod b/go.mod index 4e19053e..1f30279f 100644 --- a/go.mod +++ b/go.mod @@ -1,16 +1,106 @@ module github.com/oracle/oracle-database-operator -go 1.16 +go 1.21 require ( - github.com/go-logr/logr v0.4.0 - github.com/onsi/ginkgo v1.16.4 - github.com/onsi/gomega v1.13.0 - github.com/oracle/oci-go-sdk/v51 v51.0.0 - gopkg.in/yaml.v2 v2.4.0 - k8s.io/api v0.21.2 - k8s.io/apimachinery v0.21.2 - k8s.io/client-go v0.21.2 - sigs.k8s.io/controller-runtime v0.9.2 - sigs.k8s.io/yaml v1.2.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 + 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 + 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 +) + +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/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/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/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 2b051eef..08185f3e 100644 --- a/go.sum +++ b/go.sum @@ -1,743 +1,362 @@ 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 h1:3ithwDMr7/3vpAMXiH+ZQnYbuIsh+OPhUPMFC9enmn0= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -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/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/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= -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/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= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= -github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= -github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= -github.com/Azure/go-autorest/autorest v0.11.12/go.mod h1:eipySxLmqSyC5s5k1CLupqet0PSENBEDP93LQ9a8QYw= -github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= -github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= -github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= -github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= -github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= +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/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= -github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= -github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= -github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= -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/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= -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/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/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84= -github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= -github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1 h1:6MnRN8NT7+YBpUIWxHtefFZOKTAPgGjpQSxqLNn0+qY= -github.com/cespare/xxhash/v2 v2.1.1/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/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/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= -github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= -github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= -github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= -github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= -github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= -github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= -github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +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.11/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/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= -github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= -github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +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 v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= -github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/evanphx/json-patch v4.11.0+incompatible h1:glyUF9yIYtMHzn8xaKw5rMhdWcwsYV8dZHIq5567/xs= -github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= -github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= -github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -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 v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/logr v0.4.0 h1:K7/B1jt6fIBQVd4Owv2MqGQClcgf0R266+7C/QjRcLc= -github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= -github.com/go-logr/zapr v0.4.0 h1:uc1uML3hRYL9/ZZPdgHS/n8Nzo+eaYL/Efxkkamf7OM= -github.com/go-logr/zapr v0.4.0/go.mod h1:tabnROwaDl0UNxkVeFRbY8bwB37GwRv0P8lg6aAiEnk= -github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= -github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= -github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= -github.com/go-openapi/spec v0.19.5/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk= -github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= -github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= -github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +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/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= +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/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-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -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 h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= -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/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.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.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -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/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.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= +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.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -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/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/google/uuid v1.1.2 h1:EVhdT+1Kseyi1/pUmXKaFxYsDNy9RQYkMWRH68J/W7Y= -github.com/google/uuid v1.1.2/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/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= -github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= -github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= -github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +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/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= -github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q= -github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8= -github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= -github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80= -github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60= -github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM= -github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= -github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU= -github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU= -github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4= -github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90= -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/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= -github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= -github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= -github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ= -github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I= -github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -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.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +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= +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/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= -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.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11 h1:uVUAXhF2To8cbw/3xN3pxj6kk7TYKs98NIrTqPlMWAQ= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -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/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= -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.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= +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/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/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= 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/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= -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.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= -github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= -github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= -github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -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/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg= -github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc= -github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= -github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI= -github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg= -github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY= -github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= -github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +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-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= +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 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1 h1:9f412s+6RmYXLWZSEzVVgPGK7C2PphHj5RJrvfx9AWI= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +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/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/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/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.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= -github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.16.2/go.mod h1:CObGmKUOKaSC0RjmoAK7tKyn4Azo5P2IWuoMnvwxz1E= -github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= -github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.13.0 h1:7lLHu94wT9Ij0o6EWWclhu0aOh32VxhkwEJvzuWPeak= -github.com/onsi/gomega v1.13.0/go.mod h1:lRk9szgn8TxENtWd0Tp4c3wjlRfMTMH27I+3Je41yGY= -github.com/oracle/oci-go-sdk/v51 v51.0.0 h1:eDUVMsAzvf+jfq4xbtpQrxznYNjccwKKyIhmRvZLgwI= -github.com/oracle/oci-go-sdk/v51 v51.0.0/go.mod h1:d9KSNXwE64drofxoor+y/JWofJqLqRF9D1/AtfYIE10= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc= -github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +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.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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= -github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= -github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= -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 h1:HNkLOAEQMIDv/K+04rukrLx6ch7msSRwf3/SASFAGtQ= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -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-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-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/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= -github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -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 h1:iMAkS2TDoNWnKM+Kopnx/8tnEStIfpYA0ur0xQzzhMQ= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -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.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0 h1:mxy4L2jP6qMonqmq+aTtOx1ifVWUgG/TAmntgbh3xv4= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= -github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= -github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= -github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= -github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= -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/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= -github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= -github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= -github.com/sony/gobreaker v0.4.2-0.20210216022020-dd874f9dd33b h1:br+bPNZsJWKicw/5rALEo67QHs5weyD5tf8WST+4sJ0= -github.com/sony/gobreaker v0.4.2-0.20210216022020-dd874f9dd33b/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJOjmxWY= -github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= -github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI= -github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= -github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +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/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/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -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.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +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.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 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= -github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +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/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= -go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= -go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= -go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= -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.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +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.10 h1:z+mqJhf6ss6BSfSM671tgKyZBFPTTJM+HLxnhPC3wu0= -go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= -go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= +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/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= -go.uber.org/zap v1.17.0 h1:MTjgFu6ZLKvY6Pvaqk97GlxNBuMpV4Hy/3P6tRGlI2U= -go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= -golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +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= -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-20190611184440-5c40567a22f8/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/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= 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/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/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-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 h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/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.3.1-0.20200828183125-ce943fd02449/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-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/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-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20181220203305-927f97764cc3/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/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-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/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-20200520004742-59133d7f0dd7/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-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210224082022-3d97a244fca7/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= +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.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 h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= -golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +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-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-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/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +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-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/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-20190616124812-15dcb6c0061f/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-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/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-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-20200831180312-196b9ba8737a/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-20210112080510-489259a85091/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-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40 h1:JWgyZ1qgdTaF3N3oxC+MdTV7qvEEgHo3otj+HB5CM7Q= -golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +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/term v0.0.0-20210220032956-6a3ed077a48d h1:SZxvLBoTP5yHO3Frd4z4vrF+DBX9vMVanchswa69toE= -golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +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.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.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M= -golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -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-20210220033141-f8bda1e9f3ba/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 h1:Vv0JUPWTyeqUq42B2WJ1FeIDjjvGKoA2Ss+Ts0lAVbs= -golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +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-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-20190328211700-ab21143f2384/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-20190614205625-5aca471b1d59/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-20190624222133-a101b041ded4/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-20191112195655-aa38f8e97acc/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-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200505023115-26f46d2f7ef8/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-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY= -golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +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= 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 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= 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= -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.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= +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.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.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-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 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.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/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 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +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-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/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +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= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= -gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= -gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -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.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 h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= -gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= -gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= +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= -k8s.io/api v0.21.2 h1:vz7DqmRsXTCSa6pNxXwQ1IYeAZgdIsua+DZU+o+SX3Y= -k8s.io/api v0.21.2/go.mod h1:Lv6UGJZ1rlMI1qusN8ruAp9PUBFyBwpEHAdG24vIsiU= -k8s.io/apiextensions-apiserver v0.21.2 h1:+exKMRep4pDrphEafRvpEi79wTnCFMqKf8LBtlA3yrE= -k8s.io/apiextensions-apiserver v0.21.2/go.mod h1:+Axoz5/l3AYpGLlhJDfcVQzCerVYq3K3CvDMvw6X1RA= -k8s.io/apimachinery v0.21.2 h1:vezUc/BHqWlQDnZ+XkrpXSmnANSLbpnlpwo0Lhk0gpc= -k8s.io/apimachinery v0.21.2/go.mod h1:CdTY8fU/BlvAbJ2z/8kBwimGki5Zp8/fbVuLY8gJumM= -k8s.io/apiserver v0.21.2/go.mod h1:lN4yBoGyiNT7SC1dmNk0ue6a5Wi6O3SWOIw91TsucQw= -k8s.io/client-go v0.21.2 h1:Q1j4L/iMN4pTw6Y4DWppBoUxgKO8LbffEMVEV00MUp0= -k8s.io/client-go v0.21.2/go.mod h1:HdJ9iknWpbl3vMGtib6T2PyI/VYxiZfq936WNVHBRrA= -k8s.io/code-generator v0.21.2/go.mod h1:8mXJDCB7HcRo1xiEQstcguZkbxZaqeUOrO9SsicWs3U= -k8s.io/component-base v0.21.2 h1:EsnmFFoJ86cEywC0DoIkAUiEV6fjgauNugiw1lmIjs4= -k8s.io/component-base v0.21.2/go.mod h1:9lvmIThzdlrJj5Hp8Z/TOgIkdfsNARQ1pT+3PByuiuc= -k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= -k8s.io/gengo v0.0.0-20201214224949-b6c5ce23f027/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= -k8s.io/klog/v2 v2.8.0 h1:Q3gmuM9hKEjefWFFYF0Mat+YyFJvsUyYuwyNNJ5C9Ts= -k8s.io/klog/v2 v2.8.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec= -k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7 h1:vEx13qjvaZ4yfObSSXW7BrMc/KQBBT/Jyee8XtLf4x0= -k8s.io/kube-openapi v0.0.0-20210305001622-591a79e4bda7/go.mod h1:wXW5VT87nVfh/iLV8FpR2uDvrFyomxbtb1KivDbvPTE= -k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -k8s.io/utils v0.0.0-20210527160623-6fdb442a123b h1:MSqsVQ3pZvPGTqCjptfimO2WjG7A9un2zcpiHkA6M/s= -k8s.io/utils v0.0.0-20210527160623-6fdb442a123b/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -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/apiserver-network-proxy/konnectivity-client v0.0.19/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= -sigs.k8s.io/controller-runtime v0.9.2 h1:MnCAsopQno6+hI9SgJHKddzXpmv2wtouZz6931Eax+Q= -sigs.k8s.io/controller-runtime v0.9.2/go.mod h1:TxzMCHyEUpaeuOiZx/bIdc2T81vfs/aKdvJt9wuu0zk= -sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/structured-merge-diff/v4 v4.1.0 h1:C4r9BgJ98vrKnnVCjwCSXcWjWe0NKcUQkmzDXZXGwH8= -sigs.k8s.io/structured-merge-diff/v4 v4.1.0/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= -sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= -sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +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.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/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/hack/boilerplate.go.txt b/hack/boilerplate.go.txt index dd467e1f..1a8fcae1 100644 --- a/hack/boilerplate.go.txt +++ b/hack/boilerplate.go.txt @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2022 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** diff --git a/image.png b/image.png new file mode 100644 index 00000000..8bd1bbd5 Binary files /dev/null and b/image.png differ diff --git a/images/adb/acd-id-1.png b/images/adb/acd-id-1.png new file mode 100644 index 00000000..c8e422d5 Binary files /dev/null and b/images/adb/acd-id-1.png differ diff --git a/images/adb/acd-id-2.png b/images/adb/acd-id-2.png new file mode 100644 index 00000000..1680d34d Binary files /dev/null and b/images/adb/acd-id-2.png differ diff --git a/images/adb/adb-id-1.png b/images/adb/adb-id-1.png index 4cbf8c5a..7c2ab1f1 100644 Binary files a/images/adb/adb-id-1.png and b/images/adb/adb-id-1.png differ diff --git a/images/adb/aei-id-1.png b/images/adb/aei-id-1.png new file mode 100644 index 00000000..d407a624 Binary files /dev/null and b/images/adb/aei-id-1.png differ diff --git a/images/adb/aei-id-2.png b/images/adb/aei-id-2.png new file mode 100644 index 00000000..534b7d35 Binary files /dev/null and b/images/adb/aei-id-2.png differ diff --git a/images/adb/instance-principal-2.png b/images/adb/instance-principal-2.png index 31afe00f..d036a7e9 100644 Binary files a/images/adb/instance-principal-2.png and b/images/adb/instance-principal-2.png differ diff --git a/images/adb/instance-principal-3.png b/images/adb/instance-principal-3.png index 602d209a..bc1a3160 100644 Binary files a/images/adb/instance-principal-3.png and b/images/adb/instance-principal-3.png differ diff --git a/images/adb/instance-principal-4.png b/images/adb/instance-principal-4.png new file mode 100644 index 00000000..44bd49a9 Binary files /dev/null and b/images/adb/instance-principal-4.png differ diff --git a/images/adb/instance-principal-5.png b/images/adb/instance-principal-5.png new file mode 100644 index 00000000..602d209a Binary files /dev/null and b/images/adb/instance-principal-5.png differ diff --git a/images/sidb/application-express-admin-home.png b/images/sidb/application-express-admin-home.png new file mode 100644 index 00000000..0d581d40 Binary files /dev/null and b/images/sidb/application-express-admin-home.png differ diff --git a/images/sidb/database-actions-home.png b/images/sidb/database-actions-home.png new file mode 100644 index 00000000..90796d36 Binary files /dev/null and b/images/sidb/database-actions-home.png differ diff --git a/main.go b/main.go index 9e68368f..4174e97d 100644 --- a/main.go +++ b/main.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2022 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -39,19 +39,33 @@ 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" 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 ) @@ -62,7 +76,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 } @@ -76,28 +91,79 @@ func main() { "Enabling this will ensure there is only one active controller manager.") flag.Parse() - ctrl.SetLogger(zap.New(zap.UseDevMode(true))) + // Initialize new logger Opts + options := &zap.Options{ + Development: true, + TimeEncoder: zapcore.RFC3339TimeEncoder, + } - mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ - Scheme: scheme, - MetricsBindAddress: metricsAddr, - Port: 9443, - LeaderElection: enableLeaderElection, - LeaderElectionID: "a9d608ea.oracle.com", - }) + 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"), @@ -117,6 +183,26 @@ func main() { 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) + } + // 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) @@ -131,13 +217,139 @@ func main() { 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 = (&databasev1alpha1.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 { + setupLog.Error(err, "unable to create webhook", "webhook", "CDB") + 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 = (&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") + } + if err = (&observabilityv1alpha1.DatabaseObserver{}).SetupWebhookWithManager(mgr); err != nil { + setupLog.Error(err, "unable to create webhook", "webhook", "DatabaseObserver") + 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) + } + + // 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) + } + if err = (&databasecontroller.DataguardBrokerReconciler{ + Client: mgr.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("database").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) + } + + // 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.(*databasev1alpha1.PDB).Spec.PDBName} + } + if err = cache.IndexField(context.TODO(), &databasev1alpha1.PDB{}, "spec.pdbName", indexFunc); err != nil { + setupLog.Error(err, "unable to create index function for ", "controller", "PDB") + 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/oracle-database-operator.yaml b/oracle-database-operator.yaml index ae464a57..504fc7cd 100644 --- a/oracle-database-operator.yaml +++ b/oracle-database-operator.yaml @@ -11,45 +11,33 @@ metadata: annotations: controller-gen.kubebuilder.io/version: v0.6.1 creationTimestamp: null - name: autonomousdatabases.database.oracle.com + name: autonomouscontainerdatabases.database.oracle.com spec: group: database.oracle.com names: - kind: AutonomousDatabase - listKind: AutonomousDatabaseList - plural: autonomousdatabases + kind: AutonomousContainerDatabase + listKind: AutonomousContainerDatabaseList + plural: autonomouscontainerdatabases shortNames: - - adb - - adbs - singular: autonomousdatabase + - acd + - acds + singular: autonomouscontainerdatabase scope: Namespaced versions: - additionalPrinterColumns: - - jsonPath: .status.displayName - name: Display Name + - jsonPath: .spec.displayName + name: DisplayName type: string - jsonPath: .status.lifecycleState name: State type: string - - jsonPath: .status.isDedicated - name: Dedicated - type: string - - jsonPath: .status.cpuCoreCount - name: OCPUs - type: integer - - jsonPath: .status.dataStorageSizeInTBs - name: Storage (TB) - type: integer - - jsonPath: .status.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 + 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' @@ -60,109 +48,182 @@ spec: metadata: type: object spec: - description: 'AutonomousDatabaseSpec defines the desired state of AutonomousDatabase Important: Run "make" to regenerate code after modifying this file' + description: AutonomousContainerDatabaseSpec defines the desired state of AutonomousContainerDatabase properties: - details: - description: AutonomousDatabaseDetails defines the detail information of AutonomousDatabase, corresponding to oci-go-sdk/database/AutonomousDatabase - properties: - adminPassword: - properties: - k8sSecretName: - type: string - ociSecretOCID: - type: string - 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 - lifecycleState: - description: 'AutonomousDatabaseLifecycleStateEnum Enum with underlying type: string' - type: string - nsgOCIDs: - items: - type: string - type: array - privateEndpoint: - type: string - privateEndpointIP: - type: string - privateEndpointLabel: - type: string - subnetOCID: - type: string - wallet: - properties: - name: - type: string - password: - properties: - k8sSecretName: - type: string - ociSecretOCID: - type: string - type: object - type: object + 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 - required: - - details + patchModel: + description: 'AutonomousContainerDatabasePatchModelEnum Enum with underlying type: string' + enum: + - RELEASE_UPDATES + - RELEASE_UPDATE_REVISIONS + type: string type: object status: - description: AutonomousDatabaseStatus defines the observed state of AutonomousDatabase + description: AutonomousContainerDatabaseStatus defines the observed state of AutonomousContainerDatabase properties: - cpuCoreCount: - type: integer - dataStorageSizeInTBs: - type: integer - dbWorkload: - description: 'AutonomousDatabaseDbWorkloadEnum Enum with underlying type: string' + 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: - description: 'INSERT ADDITIONAL STATUS FIELD - define observed state of cluster Important: Run "make" to regenerate code after modifying this file' type: string - isDedicated: + 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: 'AutonomousDatabaseLifecycleStateEnum Enum with underlying type: string' + description: 'AutonomousDatabaseBackupLifecycleStateEnum Enum with underlying type: string' type: string - timeCreated: + 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 @@ -182,20 +243,33 @@ metadata: annotations: controller-gen.kubebuilder.io/version: v0.6.1 creationTimestamp: null - name: shardingdatabases.database.oracle.com + name: autonomousdatabaserestores.database.oracle.com spec: group: database.oracle.com names: - kind: ShardingDatabase - listKind: ShardingDatabaseList - plural: shardingdatabases - singular: shardingdatabase + kind: AutonomousDatabaseRestore + listKind: AutonomousDatabaseRestoreList + plural: autonomousdatabaserestores + shortNames: + - adbr + - adbrs + singular: autonomousdatabaserestore scope: Namespaced versions: - - name: v1alpha1 + - 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: ShardingDatabase is the Schema for the shardingdatabases API + 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' @@ -206,53 +280,1849 @@ spec: metadata: type: object spec: - description: ShardingDatabaseSpec defines the desired state of ShardingDatabase + description: AutonomousDatabaseRestoreSpec defines the desired state of AutonomousDatabaseRestore properties: - 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: + 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 - pvAnnotations: - additionalProperties: + type: object + pointInTime: + properties: + timestamp: + description: 'The timestamp must follow this format: YYYY-MM-DD HH:MM:SS GMT' type: string - type: object - pvMatchLabels: - additionalProperties: + 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 - pvcName: - type: string - resources: - description: ResourceRequirements describes the compute resource requirements. - properties: - limits: - additionalProperties: + 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 + 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 @@ -267,7 +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. 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: @@ -277,15 +2147,47 @@ spec: - 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: @@ -302,7 +2204,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: @@ -317,12 +2219,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: @@ -339,7 +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. 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: @@ -349,10 +2265,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: @@ -361,13 +2376,15 @@ spec: type: boolean isDeleteOraPvc: type: boolean + isDownloadScripts: + type: boolean isExternalSvc: type: boolean - namespace: - type: string - nsConfigMap: + isTdeWallet: type: string - nsSecret: + liveinessCheckPeriod: + type: integer + namespace: type: string portMappings: items: @@ -388,15 +2405,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. @@ -414,7 +2435,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: @@ -436,6 +2462,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: @@ -452,9 +2493,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 @@ -462,16 +2509,29 @@ 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: type: string + tdeWalletPvc: + type: string + tdeWalletPvcMountLocation: + type: string required: - catalog - dbImage - gsm - gsmImage - - secret - shard type: object status: @@ -483,7 +2543,7 @@ 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, 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\"` \n // other fields }" + 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. @@ -583,12 +2643,15 @@ spec: - 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 - priority: 1 type: string - jsonPath: .status.releaseUpdate name: Version @@ -600,6 +2663,13 @@ spec: 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 @@ -625,24 +2695,34 @@ spec: keepSecret: type: boolean secretKey: + default: oracle_pwd type: string secretName: type: string required: - - secretKey - secretName type: object archiveLog: type: boolean charset: type: string - cloneFrom: + 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: @@ -650,6 +2730,8 @@ spec: image: description: SingleInstanceDatabaseImage defines the Image source and pullSecrets for POD properties: + prebuiltDB: + type: boolean pullFrom: type: string pullSecrets: @@ -671,8 +2753,8 @@ spec: sgaTarget: type: integer type: object - installApex: - type: boolean + listenerPort: + type: integer loadBalancer: type: boolean nodeSelector: @@ -689,29 +2771,61 @@ spec: - ReadWriteOnce - ReadWriteMany type: string + datafilesVolumeName: + type: string + scriptsVolumeName: + type: string + setWritePermissions: + type: boolean size: type: string storageClass: type: string - required: - - accessMode - - size - - storageClass + volumeClaimAnnotation: + type: string type: object + primaryDatabaseRef: + type: string readinessCheckPeriod: type: integer replicas: - minimum: 1 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 can only have a-z , A-Z, 0-9 . It cant have any special characters + 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: - - adminPassword - image - - persistence - - replicas type: object status: description: SingleInstanceDatabaseStatus defines the observed state of SingleInstanceDatabase @@ -720,15 +2834,19 @@ spec: type: boolean archiveLog: type: string + certCreationTimestamp: + type: string + certRenewInterval: + type: string charset: type: string - cloneFrom: + 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, 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\"` \n // other fields }" + 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. @@ -774,10 +2892,16 @@ spec: 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: @@ -800,6 +2924,9 @@ spec: type: integer initSgaSize: type: integer + isTcpsEnabled: + default: false + type: boolean nodes: items: type: string @@ -820,15 +2947,23 @@ spec: - ReadWriteOnce - ReadWriteMany type: string + datafilesVolumeName: + type: string + scriptsVolumeName: + type: string + setWritePermissions: + type: boolean size: type: string storageClass: type: string - required: - - accessMode - - size - - storageClass + volumeClaimAnnotation: + type: string type: object + prebuiltDB: + type: boolean + primaryDatabase: + type: string releaseUpdate: type: string replicas: @@ -843,9 +2978,17 @@ spec: type: object status: type: string + tcpsConnectString: + type: string + tcpsPdbConnectString: + type: string + tcpsTlsSecret: + default: "" + type: string required: + - isTcpsEnabled - persistence - - replicas + - tcpsTlsSecret type: object type: object served: true @@ -881,44 +3024,383 @@ rules: - patch - delete - apiGroups: - - coordination.k8s.io + - 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: + - 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: - - leases + - 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: - - configmaps/status + - 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 ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - creationTimestamp: null - name: oracle-database-operator-manager-role -rules: + - 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 @@ -928,27 +3410,23 @@ rules: - update - watch - apiGroups: - - "" + - database.oracle.com resources: - - events - - nodes - - persistentvolumeclaims - - pods - - pods/exec - - pods/log - - services + - dataguardbrokers/finalizers + verbs: + - update +- apiGroups: + - database.oracle.com + resources: + - dataguardbrokers/status verbs: - - create - - delete - get - - list - patch - update - - watch - apiGroups: - - '''''' + - database.oracle.com resources: - - statefulsets/finalizers + - dbcssystems verbs: - create - delete @@ -958,39 +3436,27 @@ rules: - update - watch - apiGroups: - - apps + - database.oracle.com resources: - - statefulsets + - dbcssystems/finalizers verbs: - create - delete - get - - list - patch - update - - watch - apiGroups: - - coordination.k8s.io + - database.oracle.com resources: - - leases + - dbcssystems/status verbs: - - create - get - - list + - patch - update - apiGroups: - - "" + - database.oracle.com resources: - - configmaps - - events - - namespaces - - nodes - - persistentvolumeclaims - - pods - - pods/exec - - pods/log - - secrets - - services + - oraclerestdataservices verbs: - create - delete @@ -1000,15 +3466,23 @@ rules: - update - watch - apiGroups: - - "" + - database.oracle.com resources: - - pods/exec + - oraclerestdataservices/finalizers verbs: - - create + - update - apiGroups: - database.oracle.com resources: - - autonomousdatabases + - oraclerestdataservices/status + verbs: + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - pdbs verbs: - create - delete @@ -1020,8 +3494,19 @@ rules: - apiGroups: - database.oracle.com resources: - - autonomousdatabases/status + - pdbs/finalizers + verbs: + - create + - delete + - get + - patch + - update +- apiGroups: + - database.oracle.com + resources: + - pdbs/status verbs: + - get - patch - update - apiGroups: @@ -1080,6 +3565,52 @@ rules: - 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 @@ -1124,9 +3655,10 @@ 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 @@ -1139,11 +3671,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 @@ -1177,55 +3709,6 @@ spec: selector: control-plane: controller-manager --- -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 - image: container-registry.oracle.com/database/operator:0.1.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 - imagePullSecrets: - - name: container-registry-secret - terminationGracePeriodSeconds: 10 - volumes: - - name: cert - secret: - defaultMode: 420 - secretName: webhook-server-cert ---- apiVersion: cert-manager.io/v1 kind: Certificate metadata: @@ -1255,6 +3738,150 @@ 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-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 @@ -1276,6 +3903,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 @@ -1284,6 +3931,191 @@ metadata: 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 @@ -1306,3 +4138,74 @@ 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 +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: container-registry.oracle.com/database/operator: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/ords/Dockerfile b/ords/Dockerfile new file mode 100644 index 00000000..772a7e6d --- /dev/null +++ b/ords/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/ords/runOrdsSSL.sh b/ords/runOrdsSSL.sh new file mode 100644 index 00000000..35f1b77b --- /dev/null +++ b/ords/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/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/rbac/node-rbac.yaml b/rbac/node-rbac.yaml new file mode 100644 index 00000000..ac474873 --- /dev/null +++ b/rbac/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/rbac/persistent-volume-rbac.yaml b/rbac/persistent-volume-rbac.yaml new file mode 100644 index 00000000..bce9733d --- /dev/null +++ b/rbac/persistent-volume-rbac.yaml @@ -0,0 +1,28 @@ +--- +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/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 +--- diff --git a/set_ocicredentials.sh b/set_ocicredentials.sh index fe2e5a5c..dddf62c4 100755 --- a/set_ocicredentials.sh +++ b/set_ocicredentials.sh @@ -1,5 +1,5 @@ #!/bin/bash -# Copyright (c) 2021, 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. # Parse command line arguments @@ -53,7 +53,7 @@ EOF exit 0 ;; *) # unknown command - echo "Unknow command. Use [set_ocicredentials -h] for help." + echo "Unknown command. Use [set_ocicredentials -h] for help." exit 1 shift # past argument ;; diff --git a/test/e2e/autonomouscontainerdatabase_test.go b/test/e2e/autonomouscontainerdatabase_test.go new file mode 100644 index 00000000..f27d8a6d --- /dev/null +++ b/test/e2e/autonomouscontainerdatabase_test.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 e2etest + +import ( + "context" + "time" + + "github.com/oracle/oci-go-sdk/v65/common" + "github.com/oracle/oci-go-sdk/v65/database" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "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" + // +kubebuilder:scaffold:imports +) + +var _ = Describe("test ACD binding", func() { + var acdLookupKey types.NamespacedName + var acdID string + + AfterEach(func() { + // IMPORTANT: The operator might have to call reconcile multiple times to finish an operation. + // If we do the update immediately, the previous reconciliation will overwrite the changes. + By("Sleeping 20 seconds to wait for reconciliation to finish") + time.Sleep(time.Second * 20) + }) + + Describe("ACD Provisioning", func() { + It("Should create an AutonomousContainerDatabase resource and in OCI", func() { + provisionAcd := &dbv1alpha1.AutonomousContainerDatabase{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "database.oracle.com/v1alpha1", + Kind: "AutonomousContainerDatabase", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "provisionacd", + Namespace: ADBNamespace, + }, + Spec: dbv1alpha1.AutonomousContainerDatabaseSpec{ + DisplayName: common.String(e2eutil.GenerateACDName()), + CompartmentOCID: common.String(SharedCompartmentOCID), + AutonomousExadataVMClusterOCID: common.String(SharedExadataVMClusterOCID), + PatchModel: database.AutonomousContainerDatabasePatchModelUpdates, + OCIConfig: dbv1alpha1.OCIConfigSpec{ + ConfigMapName: common.String(SharedOCIConfigMapName), + SecretName: common.String(SharedOCISecretName), + }, + }, + } + + acdLookupKey = types.NamespacedName{Name: provisionAcd.Name, Namespace: provisionAcd.Namespace} + + Expect(k8sClient.Create(context.TODO(), provisionAcd)).Should(Succeed()) + }) + + It("Should check ACD status is BACKUP IN PROGRESS", e2ebehavior.AssertACDState(&k8sClient, &dbClient, &acdLookupKey, database.AutonomousContainerDatabaseLifecycleStateBackupInProgress, time.Minute*35)) + + It("Should check ACD status is AVAILABLE", e2ebehavior.AssertACDState(&k8sClient, &dbClient, &acdLookupKey, database.AutonomousContainerDatabaseLifecycleStateAvailable, time.Minute*60)) + + It("Should save ACD ocid for next test", func() { + acd := &dbv1alpha1.AutonomousContainerDatabase{} + Expect(k8sClient.Get(context.TODO(), acdLookupKey, acd)).To(Succeed()) + acdID = *acd.Spec.AutonomousContainerDatabaseOCID + }) + + It("Should delete ACD local resource", e2ebehavior.AssertACDLocalDelete(&k8sClient, &dbClient, &acdLookupKey)) + }) + + Describe("ACD Binding", func() { + It("Should create an AutonomousContainerDatabase resource", func() { + acd := &dbv1alpha1.AutonomousContainerDatabase{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "database.oracle.com/v1alpha1", + Kind: "AutonomousContainerDatabase", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "bindacd", + Namespace: ADBNamespace, + }, + Spec: dbv1alpha1.AutonomousContainerDatabaseSpec{ + AutonomousContainerDatabaseOCID: common.String(acdID), + OCIConfig: dbv1alpha1.OCIConfigSpec{ + ConfigMapName: common.String(SharedOCIConfigMapName), + SecretName: common.String(SharedOCISecretName), + }, + }, + } + + acdLookupKey = types.NamespacedName{Name: acd.Name, Namespace: acd.Namespace} + + Expect(k8sClient.Create(context.TODO(), acd)).Should(Succeed()) + }) + + It("Should bind to an ACD", e2ebehavior.AssertACDBind(&k8sClient, &dbClient, &acdLookupKey, database.AutonomousContainerDatabaseLifecycleStateAvailable)) + + It("Should update the ACD", e2ebehavior.UpdateAndAssertACDSpec(&k8sClient, &dbClient, &acdLookupKey)) + + It("Should restart the ACD", e2ebehavior.AssertACDRestart(&k8sClient, &dbClient, &acdLookupKey)) + + It("Should terminate the ACD", e2ebehavior.AssertACDTerminate(&k8sClient, &dbClient, &acdLookupKey)) + }) +}) diff --git a/test/e2e/autonomousdatabase_controller_bind_test.go b/test/e2e/autonomousdatabase_controller_bind_test.go index ebd6c4f0..58de3356 100644 --- a/test/e2e/autonomousdatabase_controller_bind_test.go +++ b/test/e2e/autonomousdatabase_controller_bind_test.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2022 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -42,9 +42,9 @@ import ( "context" "time" - "github.com/oracle/oci-go-sdk/v51/common" - "github.com/oracle/oci-go-sdk/v51/database" - "github.com/oracle/oci-go-sdk/v51/workrequests" + "github.com/oracle/oci-go-sdk/v65/common" + "github.com/oracle/oci-go-sdk/v65/database" + "github.com/oracle/oci-go-sdk/v65/workrequests" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -88,10 +88,6 @@ var _ = Describe("test ADB binding with hardLink=true", func() { err = e2eutil.WaitUntilWorkCompleted(workClient, createResp.OpcWorkRequestId) Expect(err).ShouldNot(HaveOccurred()) - - // listResp, err := e2eutil.ListAutonomousDatabases(dbClient, &SharedCompartmentOCID, &dbName) - // Expect(err).ShouldNot(HaveOccurred()) - // fmt.Printf("List request DB %s is in %s state \n", *listResp.Items[0].DisplayName, listResp.Items[0].LifecycleState) }) Describe("ADB binding with HardLink = false using Wallet Password Secret", func() { @@ -111,7 +107,9 @@ var _ = Describe("test ADB binding with hardLink=true", func() { Wallet: dbv1alpha1.WalletSpec{ Name: common.String(downloadedWallet), Password: dbv1alpha1.PasswordSpec{ - K8sSecretName: common.String(SharedWalletPassSecretName), + K8sSecret: dbv1alpha1.K8sSecretSpec{ + Name: common.String(SharedWalletPassSecretName), + }, }, }, }, @@ -132,11 +130,21 @@ var _ = Describe("test ADB binding with hardLink=true", func() { It("Should download an instance wallet using the password from K8s Secret "+SharedWalletPassSecretName, e2ebehavior.AssertWallet(&k8sClient, &adbLookupKey)) - It("should update ADB", e2ebehavior.UpdateAndAssertDetails(&k8sClient, &dbClient, &adbLookupKey)) + It("should update ADB", e2ebehavior.UpdateAndAssertDetails(&k8sClient, &dbClient, &adbLookupKey, SharedNewAdminPassSecretName, &SharedPlainTextNewAdminPassword, &SharedPlainTextWalletPassword)) + + It("Should stop ADB", e2ebehavior.UpdateAndAssertADBState(&k8sClient, &dbClient, &adbLookupKey, database.AutonomousDatabaseLifecycleStateStopped)) + + 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 isMTLSConnectionRequired to false", e2ebehavior.TestNetworkAccessRestricted(&k8sClient, &dbClient, &adbLookupKey, false)) - It("Should stop ADB", e2ebehavior.UpdateAndAssertState(&k8sClient, &dbClient, &adbLookupKey, database.AutonomousDatabaseLifecycleStateStopped)) + It("Should should change to PRIVATE network access", e2ebehavior.TestNetworkAccessPrivate(&k8sClient, &dbClient, &adbLookupKey, false, &SharedSubnetOCID, &SharedNsgOCID)) - It("Should restart ADB", e2ebehavior.UpdateAndAssertState(&k8sClient, &dbClient, &adbLookupKey, database.AutonomousDatabaseLifecycleStateAvailable)) + It("Should change isMTLSConnectionRequired to true when network access is PRIVATE", e2ebehavior.TestNetworkAccessPrivate(&k8sClient, &dbClient, &adbLookupKey, true, &SharedSubnetOCID, &SharedNsgOCID)) + + It("Should return to PUBLIC access type", e2ebehavior.TestNetworkAccessPublic(&k8sClient, &dbClient, &adbLookupKey)) It("Should delete the resource in cluster but not terminate the database in OCI", e2ebehavior.AssertSoftLinkDelete(&k8sClient, &adbLookupKey)) }) @@ -158,7 +166,9 @@ var _ = Describe("test ADB binding with hardLink=true", func() { Wallet: dbv1alpha1.WalletSpec{ Name: common.String(downloadedWallet), Password: dbv1alpha1.PasswordSpec{ - OCISecretOCID: common.String(SharedInstanceWalletPasswordOCID), + OCISecret: dbv1alpha1.OCISecretSpec{ + OCID: common.String(SharedInstanceWalletPasswordOCID), + }, }, }, }, @@ -179,12 +189,6 @@ var _ = Describe("test ADB binding with hardLink=true", func() { It("Should download an instance wallet using the password from OCI Secret OCID "+SharedInstanceWalletPasswordOCID, e2ebehavior.AssertWallet(&k8sClient, &adbLookupKey)) - It("should update ADB", e2ebehavior.UpdateAndAssertDetails(&k8sClient, &dbClient, &adbLookupKey)) - - It("Should stop ADB", e2ebehavior.UpdateAndAssertState(&k8sClient, &dbClient, &adbLookupKey, database.AutonomousDatabaseLifecycleStateStopped)) - - It("Should restart ADB", e2ebehavior.UpdateAndAssertState(&k8sClient, &dbClient, &adbLookupKey, database.AutonomousDatabaseLifecycleStateAvailable)) - It("Should delete the resource in cluster and terminate the database in OCI", e2ebehavior.AssertHardLinkDelete(&k8sClient, &dbClient, &adbLookupKey)) }) @@ -192,7 +196,7 @@ var _ = Describe("test ADB binding with hardLink=true", func() { Describe("bind to a terminated adb", func() { //Wait until remote state is terminated - It("Should check that OCI adb state is terminated", e2ebehavior.AssertRemoteStateOCID(&k8sClient, &dbClient, &terminatedAdbID, database.AutonomousDatabaseLifecycleStateTerminated)) + It("Should check that OCI adb state is terminated", e2ebehavior.AssertADBRemoteStateOCID(&k8sClient, &dbClient, &terminatedAdbID, database.AutonomousDatabaseLifecycleStateTerminated, time.Second*300)) It("Should create a AutonomousDatabase resource", func() { adb := &dbv1alpha1.AutonomousDatabase{ @@ -221,7 +225,7 @@ var _ = Describe("test ADB binding with hardLink=true", func() { Expect(k8sClient.Create(context.TODO(), adb)).Should(Succeed()) }) - It("Should check for TERMINATED state in local resource", e2ebehavior.AssertLocalState(&k8sClient, &adbLookupKey, database.AutonomousDatabaseLifecycleStateTerminated)) + It("Should check for TERMINATED state in local resource", e2ebehavior.AssertADBLocalState(&k8sClient, &adbLookupKey, database.AutonomousDatabaseLifecycleStateTerminated)) It("Should delete local resource", e2ebehavior.AssertSoftLinkDelete(&k8sClient, &adbLookupKey)) }) diff --git a/test/e2e/autonomousdatabase_controller_create_test.go b/test/e2e/autonomousdatabase_controller_create_test.go index 74b0c09e..9cf0e7e7 100644 --- a/test/e2e/autonomousdatabase_controller_create_test.go +++ b/test/e2e/autonomousdatabase_controller_create_test.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2022 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -42,8 +42,8 @@ import ( "context" "time" - "github.com/oracle/oci-go-sdk/v51/common" - "github.com/oracle/oci-go-sdk/v51/database" + "github.com/oracle/oci-go-sdk/v65/common" + "github.com/oracle/oci-go-sdk/v65/database" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -69,6 +69,8 @@ var _ = Describe("test ADB provisioning", func() { const downloadedWallet = "instance-wallet-secret-1" const resourceName = "createadb1" + const backupName = "adb-backup" + const restoreName = "adb-restore" duplicateAdbResourceName := "duplicateadb" var adbLookupKey = types.NamespacedName{Name: resourceName, Namespace: ADBNamespace} @@ -92,14 +94,18 @@ var _ = Describe("test ADB provisioning", func() { DisplayName: common.String(dbName), CPUCoreCount: common.Int(1), AdminPassword: dbv1alpha1.PasswordSpec{ - K8sSecretName: common.String(SharedAdminPassSecretName), + 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{ - K8sSecretName: common.String(SharedWalletPassSecretName), + K8sSecret: dbv1alpha1.K8sSecretSpec{ + Name: common.String(SharedWalletPassSecretName), + }, }, }, }, @@ -133,7 +139,9 @@ var _ = Describe("test ADB provisioning", func() { DisplayName: common.String(dbName), CPUCoreCount: common.Int(1), AdminPassword: dbv1alpha1.PasswordSpec{ - K8sSecretName: common.String(SharedAdminPassSecretName), + K8sSecret: dbv1alpha1.K8sSecretSpec{ + Name: common.String(SharedAdminPassSecretName), + }, }, DataStorageSizeInTBs: common.Int(1), IsAutoScalingEnabled: common.Bool(true), @@ -149,15 +157,98 @@ var _ = Describe("test ADB provisioning", func() { Expect(k8sClient.Create(context.TODO(), duplicateAdb)).To(Succeed()) }) - It("Should check for local resource state UNAVAILABLE", e2ebehavior.AssertLocalState(&k8sClient, &dupAdbLookupKey, database.AutonomousDatabaseLifecycleStateUnavailable)) + It("Should check for local resource state \"\"", e2ebehavior.AssertADBLocalState(&k8sClient, &dupAdbLookupKey, "")) - It("Should download an instance wallet using the password from K8s Secret "+SharedWalletPassSecretName, e2ebehavior.AssertWallet(&k8sClient, &adbLookupKey)) + It("Should cleanup the resource with duplicated db name", func() { + duplicateAdb := &dbv1alpha1.AutonomousDatabase{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "database.oracle.com/v1alpha1", + Kind: "AutonomousDatabase", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: duplicateAdbResourceName, + Namespace: ADBNamespace, + }, + } + Expect(k8sClient.Delete(context.TODO(), duplicateAdb)).To(Succeed()) + }) + + It("Should create an Autonomous Database Backup", func() { + e2ebehavior.AssertADBState(&k8sClient, &dbClient, &adbLookupKey, database.AutonomousDatabaseLifecycleStateAvailable)() - It("should update ADB", e2ebehavior.UpdateAndAssertDetails(&k8sClient, &dbClient, &adbLookupKey)) + // Get adb ocid + adb := &dbv1alpha1.AutonomousDatabase{} + Expect(k8sClient.Get(context.TODO(), adbLookupKey, adb)).To(Succeed()) + databaseOCID := adb.Spec.Details.AutonomousDatabaseOCID + tnsEntry := dbName + "_high" + err := e2ebehavior.ConfigureADBBackup(&dbClient, databaseOCID, &tnsEntry, &SharedPlainTextAdminPassword, &SharedPlainTextWalletPassword, &SharedBucketUrl, &SharedAuthToken, &SharedOciUser) + Expect(err).ShouldNot(HaveOccurred()) - It("Should stop ADB", e2ebehavior.UpdateAndAssertState(&k8sClient, &dbClient, &adbLookupKey, database.AutonomousDatabaseLifecycleStateStopped)) + adbBackup := &dbv1alpha1.AutonomousDatabaseBackup{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "database.oracle.com/v1alpha1", + Kind: "AutonomousDatabaseBackup", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: backupName, + Namespace: ADBNamespace, + }, + Spec: dbv1alpha1.AutonomousDatabaseBackupSpec{ + Target: dbv1alpha1.TargetSpec{ + OCIADB: dbv1alpha1.OCIADBSpec{ + OCID: common.String(*databaseOCID), + }, + }, + DisplayName: common.String(backupName), + OCIConfig: dbv1alpha1.OCIConfigSpec{ + ConfigMapName: common.String(SharedOCIConfigMapName), + SecretName: common.String(SharedOCISecretName), + }, + }, + } - It("Should restart ADB", e2ebehavior.UpdateAndAssertState(&k8sClient, &dbClient, &adbLookupKey, database.AutonomousDatabaseLifecycleStateAvailable)) + Expect(k8sClient.Create(context.TODO(), adbBackup)).To(Succeed()) + + backupLookupKey := types.NamespacedName{Name: backupName, Namespace: ADBNamespace} + e2ebehavior.AssertBackupRestore(&k8sClient, &dbClient, &backupLookupKey, &adbLookupKey, database.AutonomousDatabaseLifecycleStateBackupInProgress)() + }) + + It("Should restore a database", func() { + e2ebehavior.AssertADBState(&k8sClient, &dbClient, &adbLookupKey, database.AutonomousDatabaseLifecycleStateAvailable)() + + adbRestore := &dbv1alpha1.AutonomousDatabaseRestore{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "database.oracle.com/v1alpha1", + Kind: "AutonomousDatabaseRestore", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: restoreName, + Namespace: ADBNamespace, + }, + Spec: dbv1alpha1.AutonomousDatabaseRestoreSpec{ + Target: dbv1alpha1.TargetSpec{ + K8sADB: dbv1alpha1.K8sADBSpec{ + Name: common.String(resourceName), + }, + }, + Source: dbv1alpha1.SourceSpec{ + K8sADBBackup: dbv1alpha1.K8sADBBackupSpec{ + Name: common.String(backupName), + }, + }, + OCIConfig: dbv1alpha1.OCIConfigSpec{ + ConfigMapName: common.String(SharedOCIConfigMapName), + SecretName: common.String(SharedOCISecretName), + }, + }, + } + + Expect(k8sClient.Create(context.TODO(), adbRestore)).To(Succeed()) + restoreLookupKey := types.NamespacedName{Name: restoreName, Namespace: ADBNamespace} + e2ebehavior.AssertBackupRestore(&k8sClient, &dbClient, &restoreLookupKey, &adbLookupKey, database.AutonomousDatabaseLifecycleStateRestoreInProgress)() + }) + + It("Should download an instance wallet using the password from K8s Secret "+SharedWalletPassSecretName, e2ebehavior.AssertWallet(&k8sClient, &adbLookupKey)) It("Should delete the resource in cluster and terminate the database in OCI", e2ebehavior.AssertHardLinkDelete(&k8sClient, &dbClient, &adbLookupKey)) }) @@ -187,7 +278,9 @@ var _ = Describe("test ADB provisioning", func() { DisplayName: common.String(dbName), CPUCoreCount: common.Int(1), AdminPassword: dbv1alpha1.PasswordSpec{ - OCISecretOCID: common.String(SharedAdminPasswordOCID), + OCISecret: dbv1alpha1.OCISecretSpec{ + OCID: common.String(SharedAdminPasswordOCID), + }, }, DataStorageSizeInTBs: common.Int(1), IsAutoScalingEnabled: common.Bool(true), @@ -195,7 +288,9 @@ var _ = Describe("test ADB provisioning", func() { Wallet: dbv1alpha1.WalletSpec{ Name: common.String(downloadedWallet), Password: dbv1alpha1.PasswordSpec{ - OCISecretOCID: common.String(SharedInstanceWalletPasswordOCID), + OCISecret: dbv1alpha1.OCISecretSpec{ + OCID: common.String(SharedInstanceWalletPasswordOCID), + }, }, }, }, @@ -214,12 +309,6 @@ var _ = Describe("test ADB provisioning", func() { It("Should download an instance wallet using the password from OCI Secret OCID "+SharedInstanceWalletPasswordOCID, e2ebehavior.AssertWallet(&k8sClient, &adbLookupKey)) - It("should update ADB", e2ebehavior.UpdateAndAssertDetails(&k8sClient, &dbClient, &adbLookupKey)) - - It("Should stop ADB", e2ebehavior.UpdateAndAssertState(&k8sClient, &dbClient, &adbLookupKey, database.AutonomousDatabaseLifecycleStateStopped)) - - It("Should restart ADB", e2ebehavior.UpdateAndAssertState(&k8sClient, &dbClient, &adbLookupKey, database.AutonomousDatabaseLifecycleStateAvailable)) - It("Should delete the resource in cluster and terminate the database in OCI", e2ebehavior.AssertHardLinkDelete(&k8sClient, &dbClient, &adbLookupKey)) }) }) diff --git a/test/e2e/backup.sql b/test/e2e/backup.sql new file mode 100644 index 00000000..3663e6ff --- /dev/null +++ b/test/e2e/backup.sql @@ -0,0 +1,20 @@ +set cloudconfig -proxy=&1 &2 +connect ADMIN/&3@&4 +ALTER DATABASE PROPERTY SET default_backup_bucket='&5'; + +BEGIN +DBMS_CLOUD.DROP_CREDENTIAL( credential_name => 'DEF_CRED_NAME' ); +END; +/ + +BEGIN + DBMS_CLOUD.CREATE_CREDENTIAL( + credential_name => 'DEF_CRED_NAME', + username => '&6', + password => '&7' +); +END; +/ + +ALTER DATABASE PROPERTY SET DEFAULT_CREDENTIAL = 'ADMIN.DEF_CRED_NAME'; +exit \ No newline at end of file diff --git a/test/e2e/behavior/shared_behaviors.go b/test/e2e/behavior/shared_behaviors.go index 593bc378..00fc02c9 100644 --- a/test/e2e/behavior/shared_behaviors.go +++ b/test/e2e/behavior/shared_behaviors.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2022 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -45,10 +45,11 @@ import ( "reflect" "time" - "github.com/onsi/ginkgo" + "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" - "github.com/oracle/oci-go-sdk/v51/common" - "github.com/oracle/oci-go-sdk/v51/database" + "github.com/oracle/oci-go-sdk/v65/common" + "github.com/oracle/oci-go-sdk/v65/database" + "github.com/oracle/oci-go-sdk/v65/workrequests" corev1 "k8s.io/api/core/v1" k8sErrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/types" @@ -56,6 +57,9 @@ import ( dbv1alpha1 "github.com/oracle/oracle-database-operator/apis/database/v1alpha1" "github.com/oracle/oracle-database-operator/test/e2e/util" + "os" + "os/exec" + "strings" ) /************************************************************** @@ -66,24 +70,31 @@ import ( **************************************************************/ var ( - Describe = ginkgo.Describe - By = ginkgo.By - GinkgoWriter = ginkgo.GinkgoWriter - Expect = gomega.Expect - BeNil = gomega.BeNil - Eventually = gomega.Eventually - Equal = gomega.Equal - Succeed = gomega.Succeed - BeNumerically = gomega.BeNumerically - BeTrue = gomega.BeTrue + Describe = ginkgo.Describe + By = ginkgo.By + GinkgoWriter = ginkgo.GinkgoWriter + Expect = gomega.Expect + BeNil = gomega.BeNil + Eventually = gomega.Eventually + Equal = gomega.Equal + Succeed = gomega.Succeed + HaveOccurred = gomega.HaveOccurred + BeNumerically = gomega.BeNumerically + BeTrue = gomega.BeTrue + changeTimeout = time.Second * 300 + provisionTimeout = time.Second * 15 + bindTimeout = time.Second * 30 + backupTimeout = time.Minute * 20 + intervalTime = time.Second * 10 + updateADBTimeout = time.Minute * 7 + changeLocalStateTimeout = time.Second * 600 + updateACDTimeout = time.Minute * 3 ) func AssertProvision(k8sClient *client.Client, adbLookupKey *types.NamespacedName) func() { return func() { - // Set the timeout to 15 minutes. The provision operation might take up to 10 minutes + // Set provisionTimeout to 15 minutes. The provision operation might take up to 10 minutes // if we have already send too many requests to OCI. - provisionTimeout := time.Minute * 15 - provisionInterval := time.Second * 10 Expect(k8sClient).NotTo(BeNil()) Expect(adbLookupKey).NotTo(BeNil()) @@ -100,7 +111,7 @@ func AssertProvision(k8sClient *client.Client, adbLookupKey *types.NamespacedNam } return createdADB.Spec.Details.AutonomousDatabaseOCID, nil - }, provisionTimeout, provisionInterval).ShouldNot(BeNil()) + }, provisionTimeout, intervalTime).ShouldNot(BeNil()) fmt.Fprintf(GinkgoWriter, "AutonomousDatabase DbName = %s, and AutonomousDatabaseOCID = %s\n", *createdADB.Spec.Details.DbName, *createdADB.Spec.Details.AutonomousDatabaseOCID) @@ -109,8 +120,6 @@ func AssertProvision(k8sClient *client.Client, adbLookupKey *types.NamespacedNam func AssertBind(k8sClient *client.Client, adbLookupKey *types.NamespacedName) func() { return func() { - bindTimeout := time.Second * 30 - Expect(k8sClient).NotTo(BeNil()) Expect(adbLookupKey).NotTo(BeNil()) @@ -219,16 +228,15 @@ func compareStringMap(obj1 map[string]string, obj2 map[string]string) bool { } // UpdateDetails updates spec.details from local resource and OCI -func UpdateDetails(k8sClient *client.Client, dbClient *database.DatabaseClient, adbLookupKey *types.NamespacedName) func() *dbv1alpha1.AutonomousDatabase { +func UpdateDetails(k8sClient *client.Client, dbClient *database.DatabaseClient, adbLookupKey *types.NamespacedName, newSecretName string, newAdminPassword *string) func() *dbv1alpha1.AutonomousDatabase { return func() *dbv1alpha1.AutonomousDatabase { // Considering that there are at most two update requests will be sent during the update // From the observation per request takes ~3mins to finish - updateTimeout := time.Minute * 7 - updateInterval := time.Second * 20 Expect(k8sClient).NotTo(BeNil()) Expect(dbClient).NotTo(BeNil()) Expect(adbLookupKey).NotTo(BeNil()) + Expect(newAdminPassword).NotTo(BeNil()) derefK8sClient := *k8sClient derefDBClient := *dbClient @@ -251,7 +259,7 @@ func UpdateDetails(k8sClient *client.Client, dbClient *database.DatabaseClient, } return database.AutonomousDatabaseLifecycleStateEnum(listResp.Items[0].LifecycleState), nil - }, updateTimeout, updateInterval).Should(Equal(database.AutonomousDatabaseLifecycleStateAvailable)) + }, updateADBTimeout, intervalTime).Should(Equal(database.AutonomousDatabaseLifecycleStateAvailable)) // Update var newDisplayName = *expectedADB.Spec.Details.DisplayName + "_new" @@ -263,10 +271,16 @@ func UpdateDetails(k8sClient *client.Client, dbClient *database.DatabaseClient, newCPUCoreCount = 1 } - By(fmt.Sprintf("Updating the ADB with newDisplayName = %s and newCPUCoreCount = %d\n", newDisplayName, newCPUCoreCount)) + var newKey = "testKey" + var newVal = "testVal" + + By(fmt.Sprintf("Updating the ADB with newDisplayName = %s, newCPUCoreCount = %d and newFreeformTag = %s:%s\n", + newDisplayName, newCPUCoreCount, newKey, newVal)) expectedADB.Spec.Details.DisplayName = common.String(newDisplayName) 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) Expect(derefK8sClient.Update(context.TODO(), expectedADB)).To(Succeed()) @@ -275,12 +289,13 @@ func UpdateDetails(k8sClient *client.Client, dbClient *database.DatabaseClient, } // AssertADBDetails asserts the changes in spec.details -func AssertADBDetails(k8sClient *client.Client, dbClient *database.DatabaseClient, adbLookupKey *types.NamespacedName, expectedADB *dbv1alpha1.AutonomousDatabase) func() { +func AssertADBDetails(k8sClient *client.Client, + dbClient *database.DatabaseClient, + adbLookupKey *types.NamespacedName, + expectedADB *dbv1alpha1.AutonomousDatabase) func() { return func() { // Considering that there are at most two update requests will be sent during the update // From the observation per request takes ~3mins to finish - updateTimeout := time.Minute * 7 - updateInterval := time.Second * 20 Expect(k8sClient).NotTo(BeNil()) Expect(dbClient).NotTo(BeNil()) @@ -288,17 +303,71 @@ func AssertADBDetails(k8sClient *client.Client, dbClient *database.DatabaseClien derefDBClient := *dbClient + expectedADBDetails := expectedADB.Spec.Details 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.NewLifecycleStateRetryPolicy(database.AutonomousDatabaseLifecycleStateAvailable) + retryPolicy := e2eutil.NewLifecycleStateRetryPolicyADB(database.AutonomousDatabaseLifecycleStateAvailable) resp, err := e2eutil.GetAutonomousDatabase(derefDBClient, expectedADB.Spec.Details.AutonomousDatabaseOCID, &retryPolicy) if err != nil { return false, err } - expectedADBDetails := expectedADB.Spec.Details + 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.CompartmentOCID, resp.AutonomousDatabase.CompartmentId) { + fmt.Fprintf(GinkgoWriter, "Expected CompartmentOCID: %v\nGot: %v\n", expectedADBDetails.CompartmentOCID, resp.CompartmentId) + } + if !compareString(expectedADBDetails.DisplayName, resp.AutonomousDatabase.DisplayName) { + fmt.Fprintf(GinkgoWriter, "Expected DisplayName: %v\nGot: %v\n", expectedADBDetails.DisplayName, resp.AutonomousDatabase.DisplayName) + } + if !compareString(expectedADBDetails.DbName, resp.AutonomousDatabase.DbName) { + fmt.Fprintf(GinkgoWriter, "Expected DbName: %v\nGot:%v\n", expectedADBDetails.DbName, resp.AutonomousDatabase.DbName) + } + if expectedADBDetails.DbWorkload != resp.AutonomousDatabase.DbWorkload { + fmt.Fprintf(GinkgoWriter, "Expected DbWorkload: %v\nGot: %v\n", expectedADBDetails.DbWorkload, resp.AutonomousDatabase.DbWorkload) + } + if !compareBool(expectedADBDetails.IsDedicated, resp.AutonomousDatabase.IsDedicated) { + fmt.Fprintf(GinkgoWriter, "Expected IsDedicated: %v\nGot: %v\n", expectedADBDetails.IsDedicated, resp.AutonomousDatabase.IsDedicated) + } + if !compareString(expectedADBDetails.DbVersion, resp.AutonomousDatabase.DbVersion) { + fmt.Fprintf(GinkgoWriter, "Expected DbVersion: %v\nGot: %v\n", expectedADBDetails.DbVersion, resp.AutonomousDatabase.DbVersion) + } + 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 !compareBool(expectedADBDetails.IsAutoScalingEnabled, resp.AutonomousDatabase.IsAutoScalingEnabled) { + fmt.Fprintf(GinkgoWriter, "Expected IsAutoScalingEnabled: %v\nGot: %v\n", expectedADBDetails.IsAutoScalingEnabled, resp.AutonomousDatabase.IsAutoScalingEnabled) + } + 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 !reflect.DeepEqual(expectedADBDetails.NetworkAccess.AccessControlList, resp.AutonomousDatabase.WhitelistedIps) { + fmt.Fprintf(GinkgoWriter, "Expected AccessControlList: %v\nGot: %v\n", expectedADBDetails.NetworkAccess.AccessControlList, 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 !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 !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 !compareString(expectedADBDetails.NetworkAccess.PrivateEndpoint.HostnamePrefix, resp.AutonomousDatabase.PrivateEndpointLabel) { + fmt.Fprintf(GinkgoWriter, "Expected HostnamePrefix: %v\nGot: %v\n", expectedADBDetails.NetworkAccess.PrivateEndpoint.HostnamePrefix, resp.AutonomousDatabase.PrivateEndpointLabel) + } + } - // Compare the elements one by one rather than doing reflect.DeelEqual(adb1, adb2), since some parameters + // Compare the elements one by one rather than doing reflect.DeelEqual(adb1, adb2), since some parameters // (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. @@ -313,54 +382,156 @@ func AssertADBDetails(k8sClient *client.Client, dbClient *database.DatabaseClien compareInt(expectedADBDetails.CPUCoreCount, resp.AutonomousDatabase.CpuCoreCount) && compareBool(expectedADBDetails.IsAutoScalingEnabled, resp.AutonomousDatabase.IsAutoScalingEnabled) && compareStringMap(expectedADBDetails.FreeformTags, resp.AutonomousDatabase.FreeformTags) && - compareString(expectedADBDetails.SubnetOCID, resp.AutonomousDatabase.SubnetId) && - reflect.DeepEqual(expectedADBDetails.NsgOCIDs, resp.AutonomousDatabase.NsgIds) && - compareBool(expectedADBDetails.IsAccessControlEnabled, resp.AutonomousDatabase.IsAccessControlEnabled) && - reflect.DeepEqual(expectedADBDetails.WhitelistedIPs, resp.AutonomousDatabase.WhitelistedIps) && - compareBool(expectedADBDetails.IsMTLSConnectionRequired, resp.AutonomousDatabase.IsMtlsConnectionRequired) && - compareString(expectedADBDetails.PrivateEndpointLabel, resp.AutonomousDatabase.PrivateEndpointLabel) + 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) return same, nil - }, updateTimeout, updateInterval).Should(BeTrue()) + }, updateADBTimeout, intervalTime).Should(BeTrue()) // IMPORTANT: make sure the local resource has finished reconciling, otherwise the changes will // be conflicted with the next test and cause unknow result. - AssertLocalState(k8sClient, adbLookupKey, database.AutonomousDatabaseLifecycleStateAvailable)() + AssertADBLocalState(k8sClient, adbLookupKey, database.AutonomousDatabaseLifecycleStateAvailable)() + } +} + +func TestNetworkAccessRestricted(k8sClient *client.Client, dbClient *database.DatabaseClient, adbLookupKey *types.NamespacedName, 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)() + } +} + +/* Runs a script that connects to an ADB */ +func AssertAdminPassword(dbClient *database.DatabaseClient, databaseOCID *string, tnsEntry *string, adminPassword *string, walletPassword *string) error { + By("Downloading wallet zip") + walletZip, err := e2eutil.DownloadWalletZip(*dbClient, databaseOCID, walletPassword) + if err != nil { + fmt.Fprint(GinkgoWriter, err) + panic(err) + } + fmt.Fprint(GinkgoWriter, walletZip+" successfully downloaded.\n") + + By("Installing SQLcl") + if _, err := os.Stat("sqlcl-latest.zip"); errors.Is(err, os.ErrNotExist) { + cmd := exec.Command("wget", "https://download.oracle.com/otn_software/java/sqldeveloper/sqlcl-latest.zip") + _, err = cmd.Output() + Expect(err).To(BeNil()) + cmd = exec.Command("unzip", "sqlcl-latest.zip") + _, err = cmd.Output() + Expect(err).To(BeNil()) + } + + proxy := os.Getenv("HTTP_PROXY") + + By("Verify the adb connection") + cmd := exec.Command("./sqlcl/bin/sql", "/nolog", "@verify_connection.sql", proxy, walletZip, *adminPassword, strings.ToLower(*tnsEntry)) + stdout, err := cmd.Output() + + fmt.Fprint(GinkgoWriter, string(stdout)) + + return err +} + +func TestNetworkAccessPrivate(k8sClient *client.Client, dbClient *database.DatabaseClient, adbLookupKey *types.NamespacedName, isMTLSConnectionRequired bool, subnetOCID *string, nsgOCIDs *string) func() { + return func() { + Expect(*subnetOCID).ToNot(Equal("")) + Expect(*nsgOCIDs).ToNot(Equal("")) + + adb := &dbv1alpha1.AutonomousDatabase{} + 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)() } } -// UpdateAndAssertDetails changes the displayName from "foo" to "foo_new", and scale the cpuCoreCount to 2 -func UpdateAndAssertDetails(k8sClient *client.Client, dbClient *database.DatabaseClient, adbLookupKey *types.NamespacedName) func() { +func TestNetworkAccessPublic(k8sClient *client.Client, dbClient *database.DatabaseClient, adbLookupKey *types.NamespacedName) func() { return func() { - expectedADB := UpdateDetails(k8sClient, dbClient, adbLookupKey)() + networkPublicSpec := dbv1alpha1.NetworkAccessSpec{ + AccessType: dbv1alpha1.NetworkAccessTypePublic, + IsMTLSConnectionRequired: common.Bool(true), + } + + TestNetworkAccess(k8sClient, dbClient, adbLookupKey, networkPublicSpec)() + } +} + +func TestNetworkAccess(k8sClient *client.Client, dbClient *database.DatabaseClient, adbLookupKey *types.NamespacedName, networkSpec dbv1alpha1.NetworkAccessSpec) func() { + return func() { + Expect(k8sClient).NotTo(BeNil()) + Expect(dbClient).NotTo(BeNil()) + Expect(adbLookupKey).NotTo(BeNil()) + + derefK8sClient := *k8sClient + + adb := &dbv1alpha1.AutonomousDatabase{} + AssertADBState(k8sClient, dbClient, adbLookupKey, database.AutonomousDatabaseLifecycleStateAvailable)() + Expect(derefK8sClient.Get(context.TODO(), *adbLookupKey, adb)).To(Succeed()) + + adb.Spec.Details.NetworkAccess = networkSpec + Expect(derefK8sClient.Update(context.TODO(), adb)).To(Succeed()) + AssertADBDetails(k8sClient, dbClient, adbLookupKey, adb)() + } +} + +// UpdateAndAssertDetails changes the below fields: +// displayName: "bar" -> "bar_new" +// adminPassword: "foo" -> "foo_new", +// cpuCoreCount: from 1 to 2, or from 2 to 1 +func UpdateAndAssertDetails(k8sClient *client.Client, dbClient *database.DatabaseClient, adbLookupKey *types.NamespacedName, newSecretName string, newAdminPassword *string, walletPassword *string) func() { + return func() { + expectedADB := UpdateDetails(k8sClient, dbClient, adbLookupKey, newSecretName, newAdminPassword)() AssertADBDetails(k8sClient, dbClient, adbLookupKey, expectedADB)() + + ocid := expectedADB.Spec.Details.AutonomousDatabaseOCID + tnsEntry := *expectedADB.Spec.Details.DbName + "_high" + err := AssertAdminPassword(dbClient, ocid, &tnsEntry, newAdminPassword, walletPassword) + Expect(err).ShouldNot(HaveOccurred()) } } -// UpdateAndAssertState updates adb state and then asserts if change is propagated to OCI -func UpdateAndAssertState(k8sClient *client.Client, dbClient *database.DatabaseClient, adbLookupKey *types.NamespacedName, state database.AutonomousDatabaseLifecycleStateEnum) func() { +// UpdateAndAssertADBState updates adb state and then asserts if change is propagated to OCI +func UpdateAndAssertADBState(k8sClient *client.Client, dbClient *database.DatabaseClient, adbLookupKey *types.NamespacedName, state database.AutonomousDatabaseLifecycleStateEnum) func() { return func() { UpdateState(k8sClient, adbLookupKey, state)() - AssertState(k8sClient, dbClient, adbLookupKey, state)() + AssertADBState(k8sClient, dbClient, adbLookupKey, state)() } } -// AssertState asserts local and remote state -func AssertState(k8sClient *client.Client, dbClient *database.DatabaseClient, adbLookupKey *types.NamespacedName, state database.AutonomousDatabaseLifecycleStateEnum) func() { +// AssertADBState asserts local and remote state +func AssertADBState(k8sClient *client.Client, dbClient *database.DatabaseClient, adbLookupKey *types.NamespacedName, state database.AutonomousDatabaseLifecycleStateEnum) func() { return func() { // Waits longer for the local resource to reach the desired state - AssertLocalState(k8sClient, adbLookupKey, state)() + AssertADBLocalState(k8sClient, adbLookupKey, state)() // Double-check the state of the DB in OCI so the timeout can be shorter - AssertRemoteState(k8sClient, dbClient, adbLookupKey, state)() + AssertADBRemoteState(k8sClient, dbClient, adbLookupKey, state)() } } // AssertHardLinkDelete asserts the database is terminated in OCI when hardLink is set to true func AssertHardLinkDelete(k8sClient *client.Client, dbClient *database.DatabaseClient, adbLookupKey *types.NamespacedName) func() { return func() { - changeStateTimeout := time.Second * 300 - Expect(k8sClient).NotTo(BeNil()) Expect(dbClient).NotTo(BeNil()) Expect(adbLookupKey).NotTo(BeNil()) @@ -372,23 +543,20 @@ func AssertHardLinkDelete(k8sClient *client.Client, dbClient *database.DatabaseC Expect(derefK8sClient.Get(context.TODO(), *adbLookupKey, adb)).To(Succeed()) Expect(derefK8sClient.Delete(context.TODO(), adb)).To(Succeed()) - AssertSoftLinkDelete(k8sClient, adbLookupKey)() - By("Checking if the ADB in OCI is in TERMINATING state") // Check every 10 secs for total 60 secs Eventually(func() (database.AutonomousDatabaseLifecycleStateEnum, error) { - retryPolicy := e2eutil.NewLifecycleStateRetryPolicy(database.AutonomousDatabaseLifecycleStateTerminating) - return returnRemoteState(derefK8sClient, derefDBClient, adb.Spec.Details.AutonomousDatabaseOCID, &retryPolicy) - }, changeStateTimeout).Should(Equal(database.AutonomousDatabaseLifecycleStateTerminating)) + retryPolicy := e2eutil.NewLifecycleStateRetryPolicyADB(database.AutonomousDatabaseLifecycleStateTerminating) + return returnADBRemoteState(derefK8sClient, derefDBClient, adb.Spec.Details.AutonomousDatabaseOCID, &retryPolicy) + }, changeTimeout).Should(Equal(database.AutonomousDatabaseLifecycleStateTerminating)) + + AssertSoftLinkDelete(k8sClient, adbLookupKey)() } } // AssertSoftLinkDelete asserts the database remains in OCI when hardLink is set to false func AssertSoftLinkDelete(k8sClient *client.Client, adbLookupKey *types.NamespacedName) func() { return func() { - changeStateTimeout := time.Second * 300 - changeStateInterval := time.Second * 10 - Expect(k8sClient).NotTo(BeNil()) Expect(adbLookupKey).NotTo(BeNil()) @@ -408,15 +576,13 @@ func AssertSoftLinkDelete(k8sClient *client.Client, adbLookupKey *types.Namespac return } return - }, changeStateTimeout, changeStateInterval).Should(Equal(true)) + }, changeTimeout, intervalTime).Should(Equal(true)) } } -// AssertLocalState asserts the lifecycle state of the local resource using adbLookupKey -func AssertLocalState(k8sClient *client.Client, adbLookupKey *types.NamespacedName, state database.AutonomousDatabaseLifecycleStateEnum) func() { +// AssertADBLocalState asserts the lifecycle state of the local resource using adbLookupKey +func AssertADBLocalState(k8sClient *client.Client, adbLookupKey *types.NamespacedName, state database.AutonomousDatabaseLifecycleStateEnum) func() { return func() { - changeLocalStateTimeout := time.Second * 600 - Expect(k8sClient).NotTo(BeNil()) Expect(adbLookupKey).NotTo(BeNil()) @@ -424,15 +590,13 @@ func AssertLocalState(k8sClient *client.Client, adbLookupKey *types.NamespacedNa By("Checking if the lifecycleState of local resource is " + string(state)) Eventually(func() (database.AutonomousDatabaseLifecycleStateEnum, error) { - return returnLocalState(derefK8sClient, *adbLookupKey) + return returnADBLocalState(derefK8sClient, *adbLookupKey) }, changeLocalStateTimeout).Should(Equal(state)) } } -// AssertRemoteState asserts the lifecycle state in OCI using adbLookupKey -func AssertRemoteState(k8sClient *client.Client, dbClient *database.DatabaseClient, adbLookupKey *types.NamespacedName, state database.AutonomousDatabaseLifecycleStateEnum) func() { +func AssertADBRemoteState(k8sClient *client.Client, dbClient *database.DatabaseClient, adbLookupKey *types.NamespacedName, state database.AutonomousDatabaseLifecycleStateEnum) func() { return func() { - Expect(k8sClient).NotTo(BeNil()) Expect(dbClient).NotTo(BeNil()) Expect(adbLookupKey).NotTo(BeNil()) @@ -441,30 +605,42 @@ func AssertRemoteState(k8sClient *client.Client, dbClient *database.DatabaseClie adb := &dbv1alpha1.AutonomousDatabase{} Expect(derefK8sClient.Get(context.TODO(), *adbLookupKey, adb)).To(Succeed()) - - AssertRemoteStateOCID(k8sClient, dbClient, adb.Spec.Details.AutonomousDatabaseOCID, state)() + By("Checking if the lifecycleState of remote resource is " + string(state)) + AssertADBRemoteStateOCID(k8sClient, dbClient, adb.Spec.Details.AutonomousDatabaseOCID, state, changeTimeout)() } } -// AssertRemoteStateOCID asserts the lifecycle state in OCI using autonomousDatabaseOCID -func AssertRemoteStateOCID(k8sClient *client.Client, dbClient *database.DatabaseClient, adbID *string, state database.AutonomousDatabaseLifecycleStateEnum) func() { +// Backup takes ~15 minutes to complete, this function waits 20 minutes until ADB state is AVAILABLE +func AssertADBRemoteStateForBackupRestore(k8sClient *client.Client, dbClient *database.DatabaseClient, adbLookupKey *types.NamespacedName, state database.AutonomousDatabaseLifecycleStateEnum) func() { return func() { - changeRemoteStateTimeout := time.Second * 300 - changeRemoteStateInterval := time.Second * 10 + Expect(k8sClient).NotTo(BeNil()) + Expect(dbClient).NotTo(BeNil()) + Expect(adbLookupKey).NotTo(BeNil()) + + derefK8sClient := *k8sClient + + 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)() + } +} +func AssertADBRemoteStateOCID(k8sClient *client.Client, dbClient *database.DatabaseClient, adbID *string, state database.AutonomousDatabaseLifecycleStateEnum, timeout time.Duration) func() { + return func() { Expect(k8sClient).NotTo(BeNil()) Expect(dbClient).NotTo(BeNil()) Expect(adbID).NotTo(BeNil()) - fmt.Fprintf(GinkgoWriter, "ADB ID is %s", *adbID) + fmt.Fprintf(GinkgoWriter, "ADB ID is %s\n", *adbID) derefK8sClient := *k8sClient derefDBClient := *dbClient By("Checking if the lifecycleState of the ADB in OCI is " + string(state)) Eventually(func() (database.AutonomousDatabaseLifecycleStateEnum, error) { - return returnRemoteState(derefK8sClient, derefDBClient, adbID, nil) - }, changeRemoteStateTimeout, changeRemoteStateInterval).Should(Equal(state)) + return returnADBRemoteState(derefK8sClient, derefDBClient, adbID, nil) + }, timeout, intervalTime).Should(Equal(state)) } } @@ -485,7 +661,7 @@ func UpdateState(k8sClient *client.Client, adbLookupKey *types.NamespacedName, s } } -func returnLocalState(k8sClient client.Client, adbLookupKey types.NamespacedName) (database.AutonomousDatabaseLifecycleStateEnum, error) { +func returnADBLocalState(k8sClient client.Client, adbLookupKey types.NamespacedName) (database.AutonomousDatabaseLifecycleStateEnum, error) { adb := &dbv1alpha1.AutonomousDatabase{} err := k8sClient.Get(context.TODO(), adbLookupKey, adb) if err != nil { @@ -494,10 +670,299 @@ func returnLocalState(k8sClient client.Client, adbLookupKey types.NamespacedName return adb.Status.LifecycleState, nil } -func returnRemoteState(k8sClient client.Client, dbClient database.DatabaseClient, adbID *string, retryPolicy *common.RetryPolicy) (database.AutonomousDatabaseLifecycleStateEnum, error) { +func returnADBRemoteState(k8sClient client.Client, dbClient database.DatabaseClient, adbID *string, retryPolicy *common.RetryPolicy) (database.AutonomousDatabaseLifecycleStateEnum, error) { resp, err := e2eutil.GetAutonomousDatabase(dbClient, adbID, retryPolicy) if err != nil { return "", err } return resp.LifecycleState, nil } + +func returnACDLocalState(k8sClient client.Client, acdLookupKey types.NamespacedName) (database.AutonomousContainerDatabaseLifecycleStateEnum, error) { + acd := &dbv1alpha1.AutonomousContainerDatabase{} + err := k8sClient.Get(context.TODO(), acdLookupKey, acd) + if err != nil { + return "", err + } + return acd.Status.LifecycleState, nil +} + +func returnACDRemoteState(k8sClient client.Client, dbClient database.DatabaseClient, acdID *string, retryPolicy *common.RetryPolicy) (database.AutonomousContainerDatabaseLifecycleStateEnum, error) { + resp, err := e2eutil.GetAutonomousContainerDatabase(dbClient, acdID, retryPolicy) + if err != nil { + return "", err + } + return resp.LifecycleState, nil +} + +/* Runs a script that connects to an ADB and configures the backup bucket */ +func ConfigureADBBackup(dbClient *database.DatabaseClient, databaseOCID *string, tnsEntry *string, adminPassword *string, walletPassword *string, bucket *string, authToken *string, ociUser *string) error { + + By("Downloading wallet zip") + walletZip, err := e2eutil.DownloadWalletZip(*dbClient, databaseOCID, walletPassword) + if err != nil { + fmt.Fprint(GinkgoWriter, err) + panic(err) + } + fmt.Fprint(GinkgoWriter, walletZip+" successfully downloaded.\n") + + By("Installing SQLcl") + if _, err := os.Stat("sqlcl-latest.zip"); errors.Is(err, os.ErrNotExist) { + cmd := exec.Command("wget", "https://download.oracle.com/otn_software/java/sqldeveloper/sqlcl-latest.zip") + _, err = cmd.Output() + Expect(err).To(BeNil()) + cmd = exec.Command("unzip", "sqlcl-latest.zip") + _, err = cmd.Output() + Expect(err).To(BeNil()) + } + + proxy := os.Getenv("HTTP_PROXY") + + By("Configuring adb backup bucket") + cmd := exec.Command("./sqlcl/bin/sql", "/nolog", "@backup.sql", proxy, walletZip, *adminPassword, strings.ToLower(*tnsEntry), *bucket, *ociUser, *authToken) + stdout, err := cmd.Output() + + fmt.Fprint(GinkgoWriter, string(stdout)) + + return err +} + +func AssertBackupRestore(k8sClient *client.Client, dbClient *database.DatabaseClient, backupRestoreLookupKey *types.NamespacedName, adbLookupKey *types.NamespacedName, state database.AutonomousDatabaseLifecycleStateEnum) func() { + return func() { + // After creating a backup, ADB status will change to BACKUP IN PROGRESS + // for ~7 minutes. After that time, the state should return to AVAILBLE + derefK8sClient := *k8sClient + + AssertADBRemoteState(k8sClient, dbClient, adbLookupKey, state)() + + By("Wait until ADB state returns to AVAILABLE") + AssertADBRemoteStateForBackupRestore(k8sClient, dbClient, adbLookupKey, database.AutonomousDatabaseLifecycleStateAvailable)() + + if state == database.AutonomousDatabaseLifecycleStateBackupInProgress { + By("Checking adb backup State is ACTIVE") + createdBackup := &dbv1alpha1.AutonomousDatabaseBackup{} + Eventually(func() (database.AutonomousDatabaseBackupLifecycleStateEnum, error) { + derefK8sClient.Get(context.TODO(), *backupRestoreLookupKey, createdBackup) + return createdBackup.Status.LifecycleState, nil + }, backupTimeout, time.Second*20).Should(Equal(database.AutonomousDatabaseBackupLifecycleStateActive)) + } else { + By("Checking adb restore State is SUCCEEDED") + createdRestore := &dbv1alpha1.AutonomousDatabaseRestore{} + Eventually(func() (workrequests.WorkRequestStatusEnum, error) { + derefK8sClient.Get(context.TODO(), *backupRestoreLookupKey, createdRestore) + return createdRestore.Status.Status, nil + }, backupTimeout, time.Second*20).Should(Equal(workrequests.WorkRequestStatusSucceeded)) + } + } +} + +func AssertACDState(k8sClient *client.Client, dbClient *database.DatabaseClient, acdLookupKey *types.NamespacedName, state database.AutonomousContainerDatabaseLifecycleStateEnum, timeout time.Duration) func() { + return func() { + AssertACDLocalState(k8sClient, acdLookupKey, state, timeout)() + AssertACDRemoteState(k8sClient, dbClient, acdLookupKey, state, timeout)() + } +} + +func AssertACDLocalState(k8sClient *client.Client, acdLookupKey *types.NamespacedName, state database.AutonomousContainerDatabaseLifecycleStateEnum, timeout time.Duration) func() { + return func() { + Expect(k8sClient).NotTo(BeNil()) + Expect(acdLookupKey).NotTo(BeNil()) + + derefK8sClient := *k8sClient + + By("Checking if the lifecycleState of local resource is " + string(state)) + Eventually(func() (database.AutonomousContainerDatabaseLifecycleStateEnum, error) { + return returnACDLocalState(derefK8sClient, *acdLookupKey) + }, timeout).Should(Equal(state)) + } +} + +func AssertACDRemoteState(k8sClient *client.Client, dbClient *database.DatabaseClient, acdLookupKey *types.NamespacedName, state database.AutonomousContainerDatabaseLifecycleStateEnum, timeout time.Duration) func() { + return func() { + derefK8sClient := *k8sClient + + acd := &dbv1alpha1.AutonomousContainerDatabase{} + Expect(derefK8sClient.Get(context.TODO(), *acdLookupKey, acd)).To(Succeed()) + By("Checking if the lifecycleState of remote resource is " + string(state)) + AssertACDRemoteStateOCID(k8sClient, dbClient, acd.Spec.AutonomousContainerDatabaseOCID, state, timeout)() + } +} + +func AssertACDRemoteStateOCID(k8sClient *client.Client, dbClient *database.DatabaseClient, acdID *string, state database.AutonomousContainerDatabaseLifecycleStateEnum, timeout time.Duration) func() { + return func() { + Expect(k8sClient).NotTo(BeNil()) + Expect(dbClient).NotTo(BeNil()) + Expect(acdID).NotTo(BeNil()) + + fmt.Fprintf(GinkgoWriter, "ACD ID is %s\n", *acdID) + + derefK8sClient := *k8sClient + derefDBClient := *dbClient + + By("Checking if the lifecycleState of the ACD in OCI is " + string(state)) + Eventually(func() (database.AutonomousContainerDatabaseLifecycleStateEnum, error) { + return returnACDRemoteState(derefK8sClient, derefDBClient, acdID, nil) + }, timeout, intervalTime).Should(Equal(state)) + } +} + +func AssertACDBind(k8sClient *client.Client, dbClient *database.DatabaseClient, acdLookupKey *types.NamespacedName, state database.AutonomousContainerDatabaseLifecycleStateEnum) func() { + return func() { + + // ACD state should be AVAILABLE + acdBindTimeout := time.Minute * 3 + + By("Wait until ACD is in state AVAILABLE") + AssertACDState(k8sClient, dbClient, acdLookupKey, state, acdBindTimeout)() + } +} + +func UpdateAndAssertACDSpec(k8sClient *client.Client, dbClient *database.DatabaseClient, acdLookupKey *types.NamespacedName) func() { + return func() { + expectedACD := UpdateACDSpec(k8sClient, dbClient, acdLookupKey)() + AssertACDSpec(k8sClient, dbClient, acdLookupKey, expectedACD)() + } +} + +func UpdateACDSpec(k8sClient *client.Client, dbClient *database.DatabaseClient, acdLookupKey *types.NamespacedName) func() *dbv1alpha1.AutonomousContainerDatabase { + return func() *dbv1alpha1.AutonomousContainerDatabase { + Expect(k8sClient).NotTo(BeNil()) + Expect(dbClient).NotTo(BeNil()) + + derefK8sClient := *k8sClient + expectedAcd := &dbv1alpha1.AutonomousContainerDatabase{} + Expect(derefK8sClient.Get(context.TODO(), *acdLookupKey, expectedAcd)).To(Succeed()) + + expectedAcd.Spec.DisplayName = common.String(*expectedAcd.Spec.DisplayName + "_new") + + Expect(derefK8sClient.Update(context.TODO(), expectedAcd)).To(Succeed()) + return expectedAcd + } +} + +func AssertACDSpec(k8sClient *client.Client, dbClient *database.DatabaseClient, acdLookupKey *types.NamespacedName, expectedACD *dbv1alpha1.AutonomousContainerDatabase) func() { + return func() { + Expect(k8sClient).NotTo(BeNil()) + Expect(dbClient).NotTo(BeNil()) + Expect(acdLookupKey).NotTo(BeNil()) + + derefDBClient := *dbClient + + expectedACDSpec := expectedACD.Spec + Eventually(func() (bool, error) { + // Fetch the ACD from OCI when it's in AVAILABLE state, and retry if its attributes doesn't match the new ACD's attributes + retryPolicy := e2eutil.NewLifecycleStateRetryPolicyACD(database.AutonomousContainerDatabaseLifecycleStateAvailable) + resp, err := e2eutil.GetAutonomousContainerDatabase(derefDBClient, expectedACD.Spec.AutonomousContainerDatabaseOCID, &retryPolicy) + if err != nil { + return false, err + } + + debug := true + if debug { + if !compareString(expectedACDSpec.AutonomousContainerDatabaseOCID, resp.AutonomousContainerDatabase.Id) { + fmt.Fprintf(GinkgoWriter, "Expected OCID: %v\nGot: %v\n", expectedACDSpec.AutonomousContainerDatabaseOCID, resp.AutonomousContainerDatabase.Id) + } + if !compareString(expectedACDSpec.CompartmentOCID, resp.AutonomousContainerDatabase.CompartmentId) { + fmt.Fprintf(GinkgoWriter, "Expected CompartmentOCID: %v\nGot: %v\n", expectedACDSpec.CompartmentOCID, resp.CompartmentId) + } + if !compareString(expectedACDSpec.DisplayName, resp.AutonomousContainerDatabase.DisplayName) { + fmt.Fprintf(GinkgoWriter, "Expected DisplayName: %v\nGot: %v\n", expectedACDSpec.DisplayName, resp.AutonomousContainerDatabase.DisplayName) + } + if !compareString(expectedACDSpec.AutonomousExadataVMClusterOCID, resp.AutonomousContainerDatabase.CloudAutonomousVmClusterId) { + fmt.Fprintf(GinkgoWriter, "Expected AutonomousExadataVMClusterOCID: %v\nGot: %v\n", expectedACDSpec.AutonomousExadataVMClusterOCID, resp.AutonomousContainerDatabase.CloudAutonomousVmClusterId) + } + if !compareStringMap(expectedACDSpec.FreeformTags, resp.AutonomousContainerDatabase.FreeformTags) { + fmt.Fprintf(GinkgoWriter, "Expected FreeformTags: %v\nGot: %v\n", expectedACDSpec.FreeformTags, resp.AutonomousContainerDatabase.FreeformTags) + } + if expectedACDSpec.PatchModel != resp.AutonomousContainerDatabase.PatchModel { + fmt.Fprintf(GinkgoWriter, "Expected PatchModel: %v\nGot: %v\n", expectedACDSpec.PatchModel, resp.AutonomousContainerDatabase.PatchModel) + } + } + + // Compare the elements one by one rather than doing reflect.DeelEqual(adb1, adb2), since some parameters + // (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(expectedACDSpec.AutonomousContainerDatabaseOCID, resp.AutonomousContainerDatabase.Id) && + compareString(expectedACDSpec.CompartmentOCID, resp.AutonomousContainerDatabase.CompartmentId) && + compareString(expectedACDSpec.DisplayName, resp.AutonomousContainerDatabase.DisplayName) && + compareString(expectedACDSpec.AutonomousExadataVMClusterOCID, resp.AutonomousContainerDatabase.CloudAutonomousVmClusterId) && + compareStringMap(expectedACDSpec.FreeformTags, resp.AutonomousContainerDatabase.FreeformTags) && + expectedACDSpec.PatchModel == resp.AutonomousContainerDatabase.PatchModel + + return same, nil + }, updateACDTimeout, intervalTime).Should(BeTrue()) + + // IMPORTANT: make sure the local resource has finished reconciling, otherwise the changes will + // be conflicted with the next test and cause unknow result. + AssertACDLocalState(k8sClient, acdLookupKey, database.AutonomousContainerDatabaseLifecycleStateAvailable, time.Minute*2)() + } +} + +func AssertACDRestart(k8sClient *client.Client, dbClient *database.DatabaseClient, acdLookupKey *types.NamespacedName) func() { + return func() { + Expect(k8sClient).NotTo(BeNil()) + Expect(dbClient).NotTo(BeNil()) + Expect(acdLookupKey).NotTo(BeNil()) + + derefK8sClient := *k8sClient + acd := &dbv1alpha1.AutonomousContainerDatabase{} + Expect(derefK8sClient.Get(context.TODO(), *acdLookupKey, acd)).To(Succeed()) + + acd.Spec.Action = dbv1alpha1.AcdActionRestart + + Expect(derefK8sClient.Update(context.TODO(), acd)) + + // Check ACD status is RESTARTING + AssertACDState(k8sClient, dbClient, acdLookupKey, database.AutonomousContainerDatabaseLifecycleStateRestarting, time.Minute*2)() + // Wait until restart is completed + AssertACDState(k8sClient, dbClient, acdLookupKey, database.AutonomousContainerDatabaseLifecycleStateAvailable, time.Minute*7)() + } +} + +func AssertACDTerminate(k8sClient *client.Client, dbClient *database.DatabaseClient, acdLookupKey *types.NamespacedName) func() { + return func() { + Expect(k8sClient).NotTo(BeNil()) + Expect(dbClient).NotTo(BeNil()) + Expect(acdLookupKey).NotTo(BeNil()) + + derefK8sClient := *k8sClient + acd := &dbv1alpha1.AutonomousContainerDatabase{} + Expect(derefK8sClient.Get(context.TODO(), *acdLookupKey, acd)) + + acd.Spec.Action = dbv1alpha1.AcdActionTerminate + Expect(derefK8sClient.Update(context.TODO(), acd)) + + // Check ACD status is TERMINATING + AssertACDState(k8sClient, dbClient, acdLookupKey, database.AutonomousContainerDatabaseLifecycleStateTerminating, time.Minute*2)() + // Wait until status is TERMINATED + AssertACDState(k8sClient, dbClient, acdLookupKey, database.AutonomousContainerDatabaseLifecycleStateTerminated, time.Minute*40)() + } +} + +func AssertACDLocalDelete(k8sClient *client.Client, dbClient *database.DatabaseClient, acdLookupKey *types.NamespacedName) func() { + return func() { + Expect(k8sClient).NotTo(BeNil()) + Expect(dbClient).NotTo(BeNil()) + Expect(acdLookupKey).NotTo(BeNil()) + + derefK8sClient := *k8sClient + existingAcd := &dbv1alpha1.AutonomousContainerDatabase{} + Expect(derefK8sClient.Get(context.TODO(), *acdLookupKey, existingAcd)).To(Succeed()) + Expect(derefK8sClient.Delete(context.TODO(), existingAcd)) + + By("Checking if the AutonomousContainerDatabase resource is deleted") + Eventually(func() (isDeleted bool) { + acd := &dbv1alpha1.AutonomousContainerDatabase{} + isDeleted = false + err := derefK8sClient.Get(context.TODO(), *acdLookupKey, acd) + if err != nil && k8sErrors.IsNotFound(err) { + isDeleted = true + return + } + return + }, changeTimeout, intervalTime).Should(Equal(true)) + + AssertACDRemoteState(k8sClient, dbClient, acdLookupKey, database.AutonomousContainerDatabaseLifecycleStateAvailable, time.Minute*2) + } +} diff --git a/test/e2e/resource/test_config.yaml b/test/e2e/resource/test_config.yaml index 4a5f8027..dc2768e2 100644 --- a/test/e2e/resource/test_config.yaml +++ b/test/e2e/resource/test_config.yaml @@ -1,5 +1,5 @@ # -# Copyright (c) 2021, 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. # @@ -9,8 +9,20 @@ ociConfigFile: ~/.oci/config profile: DEFAULT # Compartment OCID where the database creates -compartmentOCID: ocid1.compartment.. +compartmentOCID: ocid1.compartment... # The OCID of the OCI Vault Secret that holds the password of the ADMIN account (should start with ocid1.vaultsecret...) adminPasswordOCID: ocid1.vaultsecret... # The OCID of the OCI Vault Secret that holds the password of the wallet (should start with ocid1.vaultsecret...) -instanceWalletPasswordOCID: ocid1.vaultsecret... \ No newline at end of file +instanceWalletPasswordOCID: ocid1.vaultsecret... +# The OCID of the subnet used to test the network access settings +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 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 +# The OCI user used to login to OCI Console +ociUser: user +# The Autonomous Exadata VM Cluster used for AutonomousContainerDatabase provision +exadataVMClusterOCID: ocid1.autonomousexainfrastructure... \ No newline at end of file diff --git a/test/e2e/suite_test.go b/test/e2e/suite_test.go index 5f763e58..a9fa06e5 100644 --- a/test/e2e/suite_test.go +++ b/test/e2e/suite_test.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2022 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -41,22 +41,22 @@ package e2etest import ( "context" "fmt" + "os" "path/filepath" "testing" "time" - "github.com/onsi/ginkgo" + "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" "github.com/onsi/gomega/gexec" - "github.com/oracle/oci-go-sdk/v51/common" - "github.com/oracle/oci-go-sdk/v51/database" + "github.com/oracle/oci-go-sdk/v65/common" + "github.com/oracle/oci-go-sdk/v65/database" corev1 "k8s.io/api/core/v1" "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" - "sigs.k8s.io/controller-runtime/pkg/envtest/printer" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" @@ -82,9 +82,13 @@ var ( BeforeSuite = ginkgo.BeforeSuite AfterSuite = ginkgo.AfterSuite Describe = ginkgo.Describe + PDescribe = ginkgo.PDescribe + FDescribe = ginkgo.FDescribe AfterEach = ginkgo.AfterEach By = ginkgo.By It = ginkgo.It + FIt = ginkgo.FIt + PIt = ginkgo.PIt Expect = gomega.Expect Succeed = gomega.Succeed HaveOccurred = gomega.HaveOccurred @@ -104,25 +108,31 @@ const ADBNamespace string = "default" var SharedOCIConfigMapName = "oci-cred" var SharedOCISecretName = "oci-privatekey" var SharedPlainTextAdminPassword = "Welcome_1234" +var SharedPlainTextNewAdminPassword = "Welcome_1234_new" var SharedPlainTextWalletPassword = "Welcome_1234" var SharedCompartmentOCID string var SharedKeyOCID string var SharedAdminPasswordOCID string var SharedInstanceWalletPasswordOCID string +var SharedSubnetOCID string +var SharedNsgOCID string + +var SharedBucketUrl string +var SharedAuthToken string +var SharedOciUser string +var SharedExadataVMClusterOCID string const SharedAdminPassSecretName string = "adb-admin-password" -const SharedWalletPassSecretName = "adb-wallet-password" +const SharedNewAdminPassSecretName string = "new-adb-admin-password" +const SharedWalletPassSecretName string = "adb-wallet-password" func TestAPIs(t *testing.T) { gomega.RegisterFailHandler(ginkgo.Fail) - - ginkgo.RunSpecsWithDefaultAndCustomReporters(t, - "Controller Suite", - []ginkgo.Reporter{printer.NewlineReporter{}}) + ginkgo.RunSpecs(t, "Controller Suite") } -var _ = BeforeSuite(func(done ginkgo.Done) { +var _ = BeforeSuite(func() { logf.SetLogger(zap.New(zap.WriteTo(ginkgo.GinkgoWriter), zap.UseDevMode(true))) By("bootstrapping test environment") @@ -156,6 +166,31 @@ var _ = BeforeSuite(func(done ginkgo.Done) { KubeClient: k8sManager.GetClient(), Log: ctrl.Log.WithName("controllers").WithName("AutonomousDatabase_test"), Scheme: k8sManager.GetScheme(), + Recorder: k8sManager.GetEventRecorderFor("AutonomousDatabase_test"), + }).SetupWithManager(k8sManager) + Expect(err).ToNot(HaveOccurred()) + + err = (&controllers.AutonomousDatabaseBackupReconciler{ + KubeClient: k8sManager.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("AutonomousDatabaseBakcup_test"), + Scheme: k8sManager.GetScheme(), + Recorder: k8sManager.GetEventRecorderFor("AutonomousDatabaseBakcup_test"), + }).SetupWithManager(k8sManager) + Expect(err).ToNot(HaveOccurred()) + + err = (&controllers.AutonomousDatabaseRestoreReconciler{ + KubeClient: k8sManager.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("AutonomousDatabaseRestore_test"), + Scheme: k8sManager.GetScheme(), + Recorder: k8sManager.GetEventRecorderFor("AutonomousDatabaseRestore_test"), + }).SetupWithManager(k8sManager) + Expect(err).ToNot(HaveOccurred()) + + err = (&controllers.AutonomousContainerDatabaseReconciler{ + KubeClient: k8sManager.GetClient(), + Log: ctrl.Log.WithName("controllers").WithName("AutonomousContainerDatabase_test"), + Scheme: k8sManager.GetScheme(), + Recorder: k8sManager.GetEventRecorderFor("AutonomousContainerDatabase_test"), }).SetupWithManager(k8sManager) Expect(err).ToNot(HaveOccurred()) @@ -183,12 +218,24 @@ var _ = BeforeSuite(func(done ginkgo.Done) { SharedCompartmentOCID = testConfig.CompartmentOCID SharedAdminPasswordOCID = testConfig.AdminPasswordOCID SharedInstanceWalletPasswordOCID = testConfig.InstanceWalletPasswordOCID + SharedSubnetOCID = testConfig.SubnetOCID + SharedNsgOCID = testConfig.NsgOCID + SharedBucketUrl = testConfig.BucketURL + SharedAuthToken = testConfig.AuthToken + SharedOciUser = testConfig.OciUser + SharedExadataVMClusterOCID = testConfig.ExadataVMClusterOCID By("checking if the required parameters exist") Expect(testConfig.OCIConfigFile).ToNot(Equal("")) Expect(testConfig.CompartmentOCID).ToNot(Equal("")) Expect(testConfig.AdminPasswordOCID).ToNot(Equal("")) Expect(testConfig.InstanceWalletPasswordOCID).ToNot(Equal("")) + Expect(testConfig.SubnetOCID).ToNot(Equal("")) + Expect(testConfig.NsgOCID).ToNot(Equal("")) + Expect(testConfig.BucketURL).ToNot(Equal("")) + Expect(testConfig.AuthToken).ToNot(Equal("")) + Expect(testConfig.OciUser).ToNot(Equal("")) + Expect(testConfig.ExadataVMClusterOCID).ToNot(Equal("")) By("getting OCI provider") ociConfigUtil, err := e2eutil.GetOCIConfigUtil(testConfig.OCIConfigFile, testConfig.Profile) @@ -219,6 +266,15 @@ var _ = BeforeSuite(func(done ginkgo.Done) { Expect(k8sClient.Create(context.TODO(), adminSecret)).To(Succeed()) }) + By("Creating a k8s secret to hold new admin password", func() { + data := map[string]string{ + SharedNewAdminPassSecretName: SharedPlainTextNewAdminPassword, + } + newAdminSecret, err := e2eutil.CreateKubeSecret(ADBNamespace, SharedNewAdminPassSecretName, data) + Expect(err).ToNot(HaveOccurred()) + Expect(k8sClient.Create(context.TODO(), newAdminSecret)).To(Succeed()) + }) + By("Creating a k8s secret to hold wallet password", func() { data := map[string]string{ SharedWalletPassSecretName: SharedPlainTextWalletPassword, @@ -227,9 +283,7 @@ var _ = BeforeSuite(func(done ginkgo.Done) { Expect(err).ToNot(HaveOccurred()) Expect(k8sClient.Create(context.TODO(), walletSecret)).To(Succeed()) }) - - close(done) -}, 60) +}) var _ = AfterSuite(func() { /* @@ -260,4 +314,8 @@ var _ = AfterSuite(func() { Expect(e2eutil.DeleteAutonomousDatabase(dbClient, adb.Spec.Details.AutonomousDatabaseOCID)).Should(Succeed()) } } + + // Delete sqlcl-latest.zip and sqlcl folder if exists + os.Remove("sqlcl-latest.zip") + os.RemoveAll("sqlcl") }) diff --git a/test/e2e/util/oci_acd_request.go b/test/e2e/util/oci_acd_request.go new file mode 100644 index 00000000..f0a3a841 --- /dev/null +++ b/test/e2e/util/oci_acd_request.go @@ -0,0 +1,78 @@ +/* +** 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 e2eutil + +import ( + "context" + + "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) { + acdDetails := database.CreateAutonomousContainerDatabaseDetails{ + DisplayName: acdName, + CloudAutonomousVmClusterId: exadataVmClusterID, + CompartmentId: compartmentId, + PatchModel: database.CreateAutonomousContainerDatabaseDetailsPatchModelUpdates, + } + + createACDRequest := database.CreateAutonomousContainerDatabaseRequest{ + CreateAutonomousContainerDatabaseDetails: acdDetails, + } + + return dbClient.CreateAutonomousContainerDatabase(context.Background(), createACDRequest) +} + +func GetAutonomousContainerDatabase(dbClient database.DatabaseClient, acdOCID *string, retryPolicy *common.RetryPolicy) (database.GetAutonomousContainerDatabaseResponse, error) { + getRequest := database.GetAutonomousContainerDatabaseRequest{ + AutonomousContainerDatabaseId: acdOCID, + } + + if retryPolicy != nil { + getRequest.RequestMetadata = common.RequestMetadata{ + RetryPolicy: retryPolicy, + } + } + + return dbClient.GetAutonomousContainerDatabase(context.TODO(), getRequest) +} diff --git a/test/e2e/util/oci_config_util.go b/test/e2e/util/oci_config_util.go index b7f33a5b..e66e029b 100644 --- a/test/e2e/util/oci_config_util.go +++ b/test/e2e/util/oci_config_util.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2022 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -48,7 +48,7 @@ import ( "regexp" "strings" - "github.com/oracle/oci-go-sdk/v51/common" + "github.com/oracle/oci-go-sdk/v65/common" corev1 "k8s.io/api/core/v1" ) diff --git a/test/e2e/util/oci_db_request.go b/test/e2e/util/oci_db_request.go index ff73bf77..09cb508a 100644 --- a/test/e2e/util/oci_db_request.go +++ b/test/e2e/util/oci_db_request.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2022 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -41,9 +41,10 @@ package e2eutil import ( "context" - "github.com/oracle/oci-go-sdk/v51/common" - "github.com/oracle/oci-go-sdk/v51/database" - + "github.com/oracle/oci-go-sdk/v65/common" + "github.com/oracle/oci-go-sdk/v65/database" + "io" + "io/ioutil" "time" ) @@ -130,7 +131,7 @@ func generateRetryPolicy(retryFunc func(r common.OCIOperationResponse) bool) com return common.NewRetryPolicy(attempts, retryFunc, nextDuration) } -func NewLifecycleStateRetryPolicy(lifecycleState database.AutonomousDatabaseLifecycleStateEnum) common.RetryPolicy { +func NewLifecycleStateRetryPolicyADB(lifecycleState database.AutonomousDatabaseLifecycleStateEnum) common.RetryPolicy { shouldRetry := func(r common.OCIOperationResponse) bool { if databaseResponse, ok := r.Response.(database.GetAutonomousDatabaseResponse); ok { // do the retry until lifecycle state reaches the passed terminal state @@ -140,3 +141,44 @@ func NewLifecycleStateRetryPolicy(lifecycleState database.AutonomousDatabaseLife } return generateRetryPolicy(shouldRetry) } + +func NewLifecycleStateRetryPolicyACD(lifecycleState database.AutonomousContainerDatabaseLifecycleStateEnum) common.RetryPolicy { + shouldRetry := func(r common.OCIOperationResponse) bool { + if databaseResponse, ok := r.Response.(database.GetAutonomousContainerDatabaseResponse); ok { + // do the retry until lifecycle state reaches the passed terminal state + return databaseResponse.LifecycleState != lifecycleState + } + return true + } + return generateRetryPolicy(shouldRetry) +} + +func DownloadWalletZip(dbClient database.DatabaseClient, databaseOCID *string, walletPassword *string) (string, error) { + + req := database.GenerateAutonomousDatabaseWalletRequest{ + AutonomousDatabaseId: common.String(*databaseOCID), + GenerateAutonomousDatabaseWalletDetails: database.GenerateAutonomousDatabaseWalletDetails{ + Password: common.String(*walletPassword), + }, + } + + resp, err := dbClient.GenerateAutonomousDatabaseWallet(context.TODO(), req) + if err != nil { + return "", err + } + + // Create a temp file wallet*.zip + const walletFileName = "wallet*.zip" + outZip, err := ioutil.TempFile("", walletFileName) + if err != nil { + return "", err + } + defer outZip.Close() + + // Save the wallet in wallet*.zip + if _, err := io.Copy(outZip, resp.Content); err != nil { + return "", err + } + + return outZip.Name(), nil +} diff --git a/test/e2e/util/oci_work_request.go b/test/e2e/util/oci_work_request.go index 450d9c3f..9751ef63 100644 --- a/test/e2e/util/oci_work_request.go +++ b/test/e2e/util/oci_work_request.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2022 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -41,8 +41,8 @@ package e2eutil import ( "context" - "github.com/oracle/oci-go-sdk/v51/common" - "github.com/oracle/oci-go-sdk/v51/workrequests" + "github.com/oracle/oci-go-sdk/v65/common" + "github.com/oracle/oci-go-sdk/v65/workrequests" "time" ) diff --git a/test/e2e/util/util.go b/test/e2e/util/util.go index 885985f1..b9e76aa7 100644 --- a/test/e2e/util/util.go +++ b/test/e2e/util/util.go @@ -1,5 +1,5 @@ /* -** Copyright (c) 2021 Oracle and/or its affiliates. +** Copyright (c) 2022 Oracle and/or its affiliates. ** ** The Universal Permissive License (UPL), Version 1.0 ** @@ -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" @@ -56,12 +56,20 @@ import ( // GenerateDBName returns a string DB concatenate 14 digits of the date time // E.g., DB060102150405 if the curret date-time is 2006.01.02 15:04:05 func GenerateDBName() string { + return "DB" + getDateTimeString() +} + +func GenerateACDName() string { + return "ACD" + getDateTimeString() +} + +func getDateTimeString() string { timeString := time.Now().Format("2006.01.02 15:04:05") trimmed := strings.ReplaceAll(timeString, ":", "") // remove colons trimmed = strings.ReplaceAll(trimmed, ".", "") // remove dots trimmed = strings.ReplaceAll(trimmed, " ", "") // remove spaces trimmed = trimmed[2:] // remove the first two digits of year (2006 -> 06) - return "DB" + trimmed + return trimmed } func unmarshalFromYamlBytes(bytes []byte, obj interface{}) error { @@ -73,7 +81,7 @@ func unmarshalFromYamlBytes(bytes []byte, obj interface{}) error { return json.Unmarshal(jsonBytes, obj) } -// LoadTestFixture create an AutonomousDatabase resoursce from a test fixture +// LoadTestFixture create an AutonomousDatabase resource from a test fixture func LoadTestFixture(adb *dbv1alpha1.AutonomousDatabase, filename string) (*dbv1alpha1.AutonomousDatabase, error) { filePath := "./resource/" + filename yamlBytes, err := ioutil.ReadFile(filePath) @@ -119,6 +127,12 @@ type testConfiguration struct { CompartmentOCID string `yaml:"compartmentOCID"` AdminPasswordOCID string `yaml:"adminPasswordOCID"` InstanceWalletPasswordOCID string `yaml:"instanceWalletPasswordOCID"` + SubnetOCID string `yaml:"subnetOCID"` + NsgOCID string `yaml:"nsgOCID"` + BucketURL string `yaml:"bucketURL"` + AuthToken string `yaml:"authToken"` + OciUser string `yaml:"ociUser"` + ExadataVMClusterOCID string `yaml:"exadataVMClusterOCID"` } func GetTestConfig(filename string) (*testConfiguration, error) { diff --git a/test/e2e/verify_connection.sql b/test/e2e/verify_connection.sql new file mode 100644 index 00000000..85ded8ad --- /dev/null +++ b/test/e2e/verify_connection.sql @@ -0,0 +1,4 @@ +set cloudconfig -proxy=&1 &2 +connect ADMIN/&3@&4 +select 1 from dual; +exit \ No newline at end of file