diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ba4176f..0c4366d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,14 +1,16 @@ -name: Continuos Integration +name: Continuous Integration on: push: branches: - master + # add release branches pull_request: branches: - master + # add release branches schedule: - - cron: '0 0 * * 1' # Every Monday at 00:00 UTC + - cron: '0 0 * * 1' # Every Monday at 00:00 UTC jobs: golang-check: @@ -25,7 +27,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 with: - go-version: ${{ matrix.go-version }} + go-version: ${{ matrix.go-version }} - name: Install Go dependencies run: go mod download @@ -33,8 +35,6 @@ jobs: - name: Go unit and integration tests run: make simulation-test - # TODO coverage report - - name: Go linters if: ${{ matrix.go-version == '1.21' }} uses: golangci/golangci-lint-action@v4 @@ -68,3 +68,29 @@ jobs: - name: Check all files format run: make ec + + - name: Check if images are the same in Makefile and config/manager/kustomization.yaml + run: make check-images + + oadp-compatibility-check: + runs-on: ubuntu-latest + steps: + - name: Checkout OADP operator + uses: actions/checkout@v4 + with: + repository: openshift/oadp-operator + ref: ${{ github.base_ref || github.ref_name }} + + - uses: actions/setup-go@v5 + with: + go-version: "1.20" # OADP Go version + + - name: Checkout Non Admin Controller (NAC) + uses: actions/checkout@v4 + with: + path: oadp-non-admin + + - name: Check Non Admin Controller (NAC) manifests + run: | + NON_ADMIN_CONTROLLER_PATH=./oadp-non-admin make update-non-admin-manifests + test -z "$(git status --short -- ':!oadp-non-admin')" || (echo "run 'make update-non-admin-manifests' in OADP repository to update Non Admin Controller (NAC) manifests" && exit 1) diff --git a/Makefile b/Makefile index bfc7fc2..2cef8ca 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,9 @@ # Image URL to use all building/pushing image targets -IMG ?= controller:latest +IMG ?= quay.io/konveyor/oadp-non-admin:latest +# Kubernetes version from OpenShift 4.15.x https://openshift-release.apps.ci.l2s4.p1.openshiftapps.com/#4-stable # ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. -ENVTEST_K8S_VERSION = 1.29.0 +ENVTEST_K8S_VERSION = 1.28 # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) ifeq (,$(shell go env GOBIN)) @@ -46,7 +47,7 @@ help: ## Display this help. .PHONY: manifests manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects. - $(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases + $(CONTROLLER_GEN) rbac:roleName=non-admin-controller-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases .PHONY: generate generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. @@ -204,7 +205,11 @@ endef ##@ oadp-nac specifics +## Tool Binaries +OC_CLI ?= $(shell which oc) EC ?= $(LOCALBIN)/ec-$(EC_VERSION) + +## Tool Versions EC_VERSION ?= 2.8.0 .PHONY: editorconfig @@ -219,12 +224,28 @@ editorconfig: $(LOCALBIN) ## Download editorconfig locally if necessary. mv $(LOCALBIN)/$${ec_binary} $(EC) ;\ } +# TODO increase!!! +COVERAGE_THRESHOLD=24 + .PHONY: ci -ci: simulation-test lint docker-build hadolint check-generate check-manifests ec ## Run all checks run by the project continuous integration (CI) locally. +ci: simulation-test lint docker-build hadolint check-generate check-manifests ec check-images ## Run all project continuous integration (CI) checks locally. .PHONY: simulation-test simulation-test: envtest ## Run unit and integration tests. KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out + @make check-coverage + +.PHONY: check-coverage +check-coverage: ## Check if test coverage threshold was reached. + @{ \ + set -e ;\ + current_coverage=$(shell go tool cover -func=cover.out | grep total | grep -Eo "[0-9]+\.[0-9]+") ;\ + if [ "$$(echo "$$current_coverage < $(COVERAGE_THRESHOLD)" | bc -l)" -eq 1 ];then \ + echo "Current coverage ($$current_coverage%) is below project threshold of $(COVERAGE_THRESHOLD)%" ;\ + exit 1 ;\ + fi ;\ + echo "Coverage threshold of $(COVERAGE_THRESHOLD)% reached: $$current_coverage%" ;\ + } .PHONY: hadolint hadolint: ## Run container file linter. @@ -241,3 +262,11 @@ check-manifests: manifests ## Check if 'make manifests' was run. .PHONY: ec ec: editorconfig ## Run file formatter checks against all project's files. $(EC) + +.PHONY: check-images +check-images: MANAGER_IMAGE:=$(shell grep -I 'newName: ' ./config/manager/kustomization.yaml | awk -F': ' '{print $$2}') +check-images: MANAGER_TAG:=$(shell grep -I 'newTag: ' ./config/manager/kustomization.yaml | awk -F': ' '{print $$2}') +check-images: ## Check if images are the same in Makefile and config/manager/kustomization.yaml + @if [ "$(MANAGER_IMAGE)" == "" ];then echo "No manager image found" && exit 1;fi + @if [ "$(MANAGER_TAG)" == "" ];then echo "No manager tag found" && exit 1;fi + @grep -Iq "IMG ?= $(MANAGER_IMAGE):$(MANAGER_TAG)" ./Makefile || (echo "Images differ" && exit 1) diff --git a/README.md b/README.md index 6aa28eb..e98f34d 100644 --- a/README.md +++ b/README.md @@ -1,307 +1,62 @@ -# oadp-nac -// TODO(user): Add simple overview of use/purpose +# OADP NAC -## Description -// Non Admin controller - -## Getting Started - -### Prerequisites -- go version v1.21.0+ -- docker version 17.03+. -- kubectl version v1.11.3+. -- Access to a Kubernetes v1.11.3+ cluster. - -### Creating Non Admin User on the OCP cluster -**Create sample Namespace:** -```sh -$ oc create ns nac-testing -``` - -**Create sample identity file:** -```sh -# Using user nacuser with sample pass -$ htpasswd -c -B -b ./users_file.htpasswd nacuser Mypassw0rd -``` - -**Create secret from the previously created htpasswd file in OpenShift** -```sh -$ oc create secret generic htpass-secret --from-file=htpasswd=./users_file.htpasswd -n openshift-config -``` - -**Create OAuth file** -```sh -$ cat > oauth-nacuser.yaml < nacuser-rolebinding.yaml < nacuser-clusterrolebinding.yaml < 8443/TCP 3m8s - -NAME READY UP-TO-DATE AVAILABLE AGE -deployment.apps/oadp-nac-controller-manager 1/1 1 1 3m7s - -NAME DESIRED CURRENT READY AGE -replicaset.apps/oadp-nac-controller-manager-74bbf4577b 1 1 1 3m7s - -``` - -> **NOTE**: If you encounter RBAC errors, you may need to grant yourself cluster-admin -privileges or be logged in as admin. - -**Create instances of your solution** -You can apply the samples (examples) from the config/sample: - -```sh -kubectl apply -k config/samples/ -``` - ->**NOTE**: Ensure that the samples has default values to test it out. +Non Admin Controller -### To Uninstall -**Delete the instances (CRs) from the cluster:** +[![Continuous Integration](https://github.com/migtools/oadp-non-admin/actions/workflows/ci.yml/badge.svg)](https://github.com/migtools/oadp-non-admin/actions/workflows/ci.yml) -```sh -kubectl delete -k config/samples/ -``` + -**Delete the APIs(CRDs) from the cluster:** +Documentation in this repository are considered unofficial and for development purposes only. -```sh -make uninstall -``` - -**UnDeploy the controller from the cluster:** - -```sh -make undeploy -``` - -## Project Distribution - -Following are the steps to build the installer and distribute this project to users. - -1. Build the installer for the image built and published in the registry: - -```sh -make build-installer IMG=/oadp-nac:tag -``` - -NOTE: The makefile target mentioned above generates an 'install.yaml' -file in the dist directory. This file contains all the resources built -with Kustomize, which are necessary to install this project without -its dependencies. +## Description -2. Using the installer +This open source controller adds the non admin feature to [OADP operator](https://github.com/openshift/oadp-operator). With it, cluster admins can configure which namespaces non admin users can backup/restore. -Users can just run kubectl apply -f to install the project, i.e.: +## Getting Started -```sh -kubectl apply -f https://raw.githubusercontent.com//oadp-nac//dist/install.yaml -``` +### Prerequisites +- oc +- Access to a OpenShift cluster +- [OADP operator](https://github.com/openshift/oadp-operator) version `1.4+` installed in the cluster + +> **NOTE:** Before OADP operator version 1.4.0 is released, you need to [install OADP operator from source](docs/CONTRIBUTING.md#install-from-source) to use NAC. + +### Using NAC + +To use NAC functionality: +- **as admin user**: + - create non admin user and its namespace, and apply required permissions to it (to create a non admin user to test NAC, you can check [non admin user documentation](docs/non_admin_user.md)) + - create/update DPA and configure non admin feature as needed, setting it to enabled +- **as non admin user**: + - create sample application + + For example, use one of the sample applications available in `hack/samples/apps/` folder, by running + ```sh + oc process -f ./hack/samples/apps/ \ + -p NAMESPACE= \ + | oc create -f - + ``` + + Check the application was successful deployed by accessing its route. + - create NonAdminBackup + + For example, use one of the sample NonAdminBackup available in `hack/samples/backups/` folder, by running + ```sh + oc process -f ./hack/samples/backups/ \ + -p NAMESPACE= \ + | oc create -f - + ``` + + - TODO NonAdminRestore ## Contributing -// TODO(user): Add detailed information on how you would like others to contribute to this project -**NOTE:** Run `make help` for more information on all potential `make` targets - -More information can be found via the [Kubebuilder Documentation](https://book.kubebuilder.io/introduction.html) +Please check our [contributing documentation](docs/CONTRIBUTING.md) to propose changes to the repository. ## Architecture -The project was generated using kubebuilder version `v3.14.0`, running the following commands -```sh -kubebuilder init \ - --plugins go.kubebuilder.io/v4 \ - --project-version 3 \ - --project-name=oadp-nac \ - --repo=github.com/migtools/oadp-non-admin \ - --domain=oadp.openshift.io -kubebuilder create api \ - --plugins go.kubebuilder.io/v4 \ - --group nac \ - --version v1alpha1 \ - --kind NonAdminBackup \ - --resource --controller -make manifests -``` -> **NOTE:** The information about plugin and project version, as well as project name, repo and domain, is stored in [PROJECT](PROJECT) file - -To upgrade kubebuilder version, create kubebuilder structure using the current kubebuilder version and the upgrade version, using the same commands presented earlier, in two different folders. Then generate a `diff` file from the two folders and apply changes to project code. - -## Quality - -The quality checks of the project are reproduced by the continuos integration (CI) pipeline of the project. CI configuration in [`.github/workflows/ci.yml`](.github/workflows/ci.yml) file. - -To run all checks locally, run `make ci`. - -### Tests - -To run unit and integration tests, run -```sh -make simulation-test -``` - -TODO report, coverage and tests information - -### Linters and code formatters - -To run Go linters and check Go code format, run -```sh -make lint -``` - -To fix Go linters issues and format Go code, run -```sh -make lint-fix -``` - -Go linters and Go code formatters configuration in [`.golangci.yml`](.golangci.yml) file. - -To check all files format, run -```sh -make ec -``` - -Files format configuration in [`.editorconfig`](.editorconfig) file. - -### Container file linter - -To run container file linter, run -```sh -make hadolint -``` - -### Code generation - -To check if project code was generated, run -```sh -make check-generate -make check-manifests -``` +For a better understanding of the project, check our [architecture documentation](docs/architecture.md) and [designs documentation](docs/design/). ## License -Copyright 2024. - -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. - +This repository is licensed under the terms of [Apache License Version 2.0](LICENSE). diff --git a/cmd/main.go b/cmd/main.go index 218bb22..8d6be27 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -20,6 +20,7 @@ package main import ( "crypto/tls" "flag" + "fmt" "os" velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" @@ -36,6 +37,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/webhook" nacv1alpha1 "github.com/migtools/oadp-non-admin/api/v1alpha1" + "github.com/migtools/oadp-non-admin/internal/common/constant" "github.com/migtools/oadp-non-admin/internal/controller" ) @@ -96,6 +98,11 @@ func main() { TLSOpts: tlsOpts, }) + if len(constant.OadpNamespace) == 0 { + setupLog.Error(fmt.Errorf("%v environment variable is empty", constant.NamespaceEnvVar), "environment variable must be set") + os.Exit(1) + } + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ Scheme: scheme, Metrics: metricsserver.Options{ diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index dc2c7d2..c460dde 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -1,12 +1,12 @@ # Adds namespace to all resources. -namespace: oadp-nac-system +namespace: openshift-adp-system # Value of this field is prepended to the # names of all resources, e.g. a deployment named # "wordpress" becomes "alices-wordpress". # Note that it should also match with the prefix (text before '-') of the namespace # field above. -namePrefix: oadp-nac- +namePrefix: # Labels to add to all resources and selectors. #labels: @@ -27,10 +27,12 @@ resources: #- ../prometheus patches: +# Create same deployment as OADP https://github.com/openshift/oadp-operator/blob/master/controllers/nonadmin_controller.go +- path: manager_oadp_patch.yaml # Protect the /metrics endpoint by putting it behind auth. # If you want your controller-manager to expose the /metrics # endpoint w/o any authn/z, please comment the following line. -- path: manager_auth_proxy_patch.yaml +# - path: manager_auth_proxy_patch.yaml # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in # crd/kustomization.yaml diff --git a/config/default/manager_oadp_patch.yaml b/config/default/manager_oadp_patch.yaml new file mode 100644 index 0000000..252465f --- /dev/null +++ b/config/default/manager_oadp_patch.yaml @@ -0,0 +1,27 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: non-admin-controller + namespace: system +spec: + template: + metadata: + annotations: + spec: + containers: + - name: non-admin-controller + imagePullPolicy: Always + args: + command: + livenessProbe: + readinessProbe: + resources: + securityContext: + env: + - name: WATCH_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + restartPolicy: Always + securityContext: + terminationGracePeriodSeconds: diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 8ff4f65..1771d3e 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -4,5 +4,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: quay.io/migi/oadp-nac-operator - newTag: v0.0.37 + newName: quay.io/konveyor/oadp-non-admin + newTag: latest diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 9adf755..0ae1339 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -2,39 +2,39 @@ apiVersion: v1 kind: Namespace metadata: labels: - control-plane: controller-manager + control-plane: non-admin-controller app.kubernetes.io/name: namespace app.kubernetes.io/instance: system app.kubernetes.io/component: manager - app.kubernetes.io/created-by: oadp-nac - app.kubernetes.io/part-of: oadp-nac + app.kubernetes.io/created-by: oadp-operator + app.kubernetes.io/part-of: oadp-operator app.kubernetes.io/managed-by: kustomize name: system --- apiVersion: apps/v1 kind: Deployment metadata: - name: controller-manager + name: non-admin-controller namespace: system labels: - control-plane: controller-manager + control-plane: non-admin-controller app.kubernetes.io/name: deployment - app.kubernetes.io/instance: controller-manager + app.kubernetes.io/instance: non-admin-controller app.kubernetes.io/component: manager - app.kubernetes.io/created-by: oadp-nac - app.kubernetes.io/part-of: oadp-nac + app.kubernetes.io/created-by: oadp-operator + app.kubernetes.io/part-of: oadp-operator app.kubernetes.io/managed-by: kustomize spec: selector: matchLabels: - control-plane: controller-manager + control-plane: non-admin-controller replicas: 1 template: metadata: annotations: kubectl.kubernetes.io/default-container: manager labels: - control-plane: controller-manager + control-plane: non-admin-controller spec: # TODO(user): Uncomment the following code to configure the nodeAffinity expression # according to the platforms which are supported by your solution. @@ -71,7 +71,7 @@ spec: args: - --leader-elect image: controller:latest - name: manager + name: non-admin-controller securityContext: allowPrivilegeEscalation: false capabilities: @@ -98,5 +98,5 @@ spec: requests: cpu: 10m memory: 64Mi - serviceAccountName: controller-manager + serviceAccountName: non-admin-controller terminationGracePeriodSeconds: 10 diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml index 5f1355c..2f853d7 100644 --- a/config/rbac/kustomization.yaml +++ b/config/rbac/kustomization.yaml @@ -7,13 +7,12 @@ resources: - service_account.yaml - role.yaml - role_binding.yaml -- leader_election_role.yaml -- leader_election_role_binding.yaml -- nonadminbackup_editor_role.yaml +# - leader_election_role.yaml +# - leader_election_role_binding.yaml # Comment the following 4 lines if you want to disable # the auth proxy (https://github.com/brancz/kube-rbac-proxy) # which protects your /metrics endpoint. -- auth_proxy_service.yaml -- auth_proxy_role.yaml -- auth_proxy_role_binding.yaml -- auth_proxy_client_clusterrole.yaml +# - auth_proxy_service.yaml +# - auth_proxy_role.yaml +# - auth_proxy_role_binding.yaml +# - auth_proxy_client_clusterrole.yaml diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index 24d8c44..3b3efad 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -2,7 +2,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRole metadata: - name: manager-role + name: non-admin-controller-role rules: - apiGroups: - nac.oadp.openshift.io diff --git a/config/rbac/role_binding.yaml b/config/rbac/role_binding.yaml index 888fd1e..42f638a 100644 --- a/config/rbac/role_binding.yaml +++ b/config/rbac/role_binding.yaml @@ -3,17 +3,17 @@ kind: ClusterRoleBinding metadata: labels: app.kubernetes.io/name: clusterrolebinding - app.kubernetes.io/instance: manager-rolebinding + app.kubernetes.io/instance: non-admin-controller-rolebinding app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: oadp-nac - app.kubernetes.io/part-of: oadp-nac + app.kubernetes.io/created-by: oadp-operator + app.kubernetes.io/part-of: oadp-operator app.kubernetes.io/managed-by: kustomize - name: manager-rolebinding + name: non-admin-controller-rolebinding roleRef: apiGroup: rbac.authorization.k8s.io kind: ClusterRole - name: manager-role + name: non-admin-controller-role subjects: - kind: ServiceAccount - name: controller-manager + name: non-admin-controller namespace: system diff --git a/config/rbac/service_account.yaml b/config/rbac/service_account.yaml index 0b28353..09e0b66 100644 --- a/config/rbac/service_account.yaml +++ b/config/rbac/service_account.yaml @@ -3,10 +3,10 @@ kind: ServiceAccount metadata: labels: app.kubernetes.io/name: serviceaccount - app.kubernetes.io/instance: controller-manager-sa + app.kubernetes.io/instance: non-admin-controller-sa app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: oadp-nac - app.kubernetes.io/part-of: oadp-nac + app.kubernetes.io/created-by: oadp-operator + app.kubernetes.io/part-of: oadp-operator app.kubernetes.io/managed-by: kustomize - name: controller-manager + name: non-admin-controller namespace: system diff --git a/config/samples/nac_v1alpha1_nonadminbackup.yaml b/config/samples/nac_v1alpha1_nonadminbackup.yaml index f33d645..f97101f 100644 --- a/config/samples/nac_v1alpha1_nonadminbackup.yaml +++ b/config/samples/nac_v1alpha1_nonadminbackup.yaml @@ -4,9 +4,9 @@ metadata: labels: app.kubernetes.io/name: nonadminbackup app.kubernetes.io/instance: nonadminbackup-sample - app.kubernetes.io/part-of: oadp-nac + app.kubernetes.io/part-of: oadp-operator app.kubernetes.io/managed-by: kustomize - app.kubernetes.io/created-by: oadp-nac + app.kubernetes.io/created-by: oadp-operator name: nonadminbackup-sample spec: backupSpec: {} diff --git a/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md new file mode 100644 index 0000000..4e7cb89 --- /dev/null +++ b/docs/CONTRIBUTING.md @@ -0,0 +1,113 @@ +# Contributing + +To contribute to the project, please first check if a issue/pull request does not already exist regarding the changes you are suggesting. If a issue exists, please link it to the pull request you are creating. + +If your changes involve controller logic, please test it prior to submitting, by following [install from source section](#install-from-source). + +If your changes involve code, please check [code quality and standardization section](#code-quality-and-standardization). + +If your changes involve Kubernetes objects (`config/` folder), please follow [Kubernetes objects changes section](#kubernetes-objects-changes). + +If you are upgrading project's kubebuilder version, please follow [upgrade kubebuilder version section](#upgrade-kubebuilder-version). + +> **NOTE:** Run `make help` for more information on all potential `make` targets + +## Prerequisites +- go version v1.21.0+ +- docker version 17.03+ +- oc +- Access to a OpenShift cluster + +## Install from source + +To install OADP operator in your cluster, with OADP NAC from current branch, run +```sh +export DEV_IMG=ttl.sh/oadp-non-admin-$(git rev-parse --short HEAD)-$(echo $RANDOM):1h +export NAC_PATH=$PWD +git clone --depth=1 git@github.com:openshift/oadp-operator.git -b master # or appropriate branch +IMG=$DEV_IMG make docker-build docker-push +cd oadp-operator +NON_ADMIN_CONTROLLER_PATH=$NAC_PATH NON_ADMIN_CONTROLLER_IMG=$DEV_IMG make update-non-admin-manifests deploy-olm +``` + +To create a non admin user to test NAC, check [non admin user documentation](non_admin_user.md). + +To uninstall the previously installed OADP operator in your cluster, run +```sh +cd oadp-operator +make undeploy-olm +``` + +> **NOTE:** Make sure there are no running instances of CRDs. Finalizers in those objects can fail uninstall command. + +## Code quality and standardization + +The quality/standardization checks of the project are reproduced by the continuous integration (CI) pipeline of the project. CI configuration in [`.github/workflows/ci.yml`](../.github/workflows/ci.yml) file. + +To run all checks locally, run `make ci`. + +### Tests + +To run unit and integration tests and coverage report, run +```sh +make simulation-test +``` + +To see the html report, run +```sh +go tool cover -html=cover.out +``` + +TODO end to end tests + +### Linters and code formatters + +To run Go linters and check Go code format, run +```sh +make lint +``` + +To fix Go linters issues and format Go code, run +```sh +make lint-fix +``` + +Go linters and Go code formatters configuration in [`.golangci.yml`](../.golangci.yml) file. + +To check all files format, run +```sh +make ec +``` + +Files format configuration in [`.editorconfig`](../.editorconfig) file. + +### Container file linter + +To run container file linter, run +```sh +make hadolint +``` + +### Code generation + +To check if project code was generated, run +```sh +make check-generate +make check-manifests +``` + +## Kubernetes objects changes + +If NAC Kubernetes objects are changed, like CRDs, RBACs, etc, follow this workflow: +- create branch in NAC repository and make the necessary changes +- create branch in OADP operator repository and run `make update-non-admin-manifests` command, pointing to previously created NAC branch. Example: + ```sh + NON_ADMIN_CONTROLLER_PATH=/home/user/oadp-non-admin make update-non-admin-manifests + ``` +- create pull requests both in NAC and OADP operator repositories (OADP operator repository pull request must be merged first) + +[More information](architecture.md#oadp-integration). + +## Upgrade kubebuilder version + +To upgrade kubebuilder version, create kubebuilder structure using the current kubebuilder version and the upgrade version, using the same commands presented in [kubebuilder architecture documentation](architecture.md#kubebuilder), in two different folders. Then generate a `diff` file from the two folders and apply changes to project code. diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..2a8bfc3 --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,40 @@ +# Architecture + +## OADP integration + +Normally, to ship a controller to users, the project would present the file created by `make build-installer` command (which include various Kubernetes objects, like Namespace, ServiceAccount, Deployment, etc), to user to install the controller. But since NAC needs OADP operator to properly work, those Kubernetes objects are shipped within OADP operator (and also Kubernetes objects in `config/samples/` folder). Because of this restriction, generated Kubernetes objects names and labels in `config/` folder, may need to be updated to match OADP operator standards (for example, `oadp-nac` values are changed to `oadp-operator`) and avoid duplications, by changing Kubernetes object names to `non-admin-controller`, or adding it as a prefix. + +> **NOTE:** If needed, you can test NAC alone by running `make build-installer` and `oc apply -f ./dist/install.yaml`. You may want to customize namespace (`openshift-adp-system`) and container image (`quay.io/konveyor/oadp-non-admin:latest`) in that file prior to deploying it to your cluster. + +NAC objects are included in OADP operator through `make update-non-admin-manifests` command, which is run in OADP operator repository. + +> **NOTE:** Manual steps required in OADP operator repository branch prior to implementation of `make update-non-admin-manifests` command: +> - `RELATED_IMAGE_NON_ADMIN_CONTROLLER` must be already set in `config/manager/manager.yaml` file +> - add option to use NAC in OADP CRD +> - write and integrate NAC controller with OADP + +> **NOTE:** `make update-non-admin-manifests` command does not work for deletion, i.e., if a file that was previously managed by the command is deleted (or renamed), it needs to be manually deleted. + +The continuous integration (CI) pipeline of the project verifies if OADP operator repository branches have up to date NAC objects included. + +## Kubebuilder + +The project was generated using kubebuilder version `v3.14.0`, running the following commands +```sh +kubebuilder init \ + --plugins go.kubebuilder.io/v4 \ + --project-version 3 \ + --project-name=oadp-nac \ + --repo=github.com/migtools/oadp-non-admin \ + --domain=oadp.openshift.io +kubebuilder create api \ + --plugins go.kubebuilder.io/v4 \ + --group nac \ + --version v1alpha1 \ + --kind NonAdminBackup \ + --resource --controller +make manifests +``` +> **NOTE:** The information about plugin and project version, as well as project name, repo and domain, is stored in [PROJECT](../PROJECT) file + +> **NOTE:** More information can be found via the [Kubebuilder Documentation](https://book.kubebuilder.io/introduction.html) diff --git a/docs/non_admin_user.md b/docs/non_admin_user.md new file mode 100644 index 0000000..4902541 --- /dev/null +++ b/docs/non_admin_user.md @@ -0,0 +1,70 @@ +# Creating non admin user on a OpenShift cluster + +Check your cloud provider documentation for more detailed information. + +## AWS + +### Authentication + +Choose one of the authentication method sections to follow. + +#### OAuth + +- Create sample identity file + ```sh + htpasswd -c -B -b ./non_admin_user.htpasswd + ``` +- Create secret from the previously created identity file in your cluster + ```sh + oc create secret generic non-admin-user --from-file=htpasswd=./non_admin_user.htpasswd -n openshift-config + ``` +- Add new entry to `spec.identityProviders` field from OAuth cluster (`oc get OAuth cluster`) + ```yaml + ... + spec: + identityProviders: + - name: # non-admin-user + mappingMethod: claim + type: HTPasswd + htpasswd: + fileData: + name: # non-admin-user + ``` +- [Apply permissions to your non admin user](#permissions) + +## Permissions + +- Create non admin user namespace + ```sh + oc create namespace + ``` +- Ensure non admin user have appropriate permissions in its namespace, i.e., non admin user have editor roles for the following objects + - `nonadminbackups.nac.oadp.openshift.io` + + For example + ```yaml + # config/rbac/nonadminbackup_editor_role.yaml + - apiGroups: + - nac.oadp.openshift.io + resources: + - nonadminbackups + verbs: + - create + - delete + - get + - list + - patch + - update + - watch + - apiGroups: + - nac.oadp.openshift.io + resources: + - nonadminbackups/status + verbs: + - get + ``` + For example, make non admin user have `admin` ClusterRole permissions on its namespace + ```sh + oc create rolebinding -namespace-admin --clusterrole=admin --user= --namespace= + ``` + diff --git a/hack/samples/apps/mysql.yaml b/hack/samples/apps/mysql.yaml new file mode 100644 index 0000000..b849811 --- /dev/null +++ b/hack/samples/apps/mysql.yaml @@ -0,0 +1,184 @@ +apiVersion: template.openshift.io/v1 +kind: Template +metadata: + name: mysql-todolist +objects: + # TODO need VolumeSnapshotClass with label velero.io/csi-volumesnapshot-class: 'true'? + - kind: ServiceAccount + apiVersion: v1 + metadata: + name: ${NAMESPACE}-sa + namespace: ${NAMESPACE} + labels: + component: ${NAMESPACE} + - kind: PersistentVolumeClaim + apiVersion: v1 + metadata: + name: mysql + namespace: ${NAMESPACE} + labels: + app: mysql + spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi + - kind: Service + apiVersion: v1 + metadata: + annotations: + template.openshift.io/expose-uri: mariadb://{.spec.clusterIP}:{.spec.ports[?(.name=="mysql")].port} + name: mysql + namespace: ${NAMESPACE} + labels: + app: mysql + service: mysql + spec: + ports: + - protocol: TCP + name: mysql + port: 3306 + selector: + app: mysql + - kind: Deployment + apiVersion: apps/v1 + metadata: + annotations: + template.alpha.openshift.io/wait-for-ready: 'true' + name: mysql + namespace: ${NAMESPACE} + labels: + e2e-app: "true" + spec: + selector: + matchLabels: + app: mysql + strategy: + type: Recreate + template: + metadata: + labels: + e2e-app: "true" + app: mysql + curl-tool: "true" + spec: + serviceAccountName: ${NAMESPACE}-sa + containers: + - image: registry.redhat.io/rhel8/mariadb-105:latest + name: mysql + env: + - name: MYSQL_USER + value: changeme + - name: MYSQL_PASSWORD + value: changeme + - name: MYSQL_ROOT_PASSWORD + value: root + - name: MYSQL_DATABASE + value: todolist + ports: + - containerPort: 3306 + name: mysql + resources: + limits: + memory: 512Mi + volumeMounts: + - name: mysql-data + mountPath: /var/lib/mysql + livenessProbe: + tcpSocket: + port: mysql + initialDelaySeconds: 30 + periodSeconds: 10 + timeoutSeconds: 5 + startupProbe: + exec: + command: + - /usr/bin/timeout + - 1s + - /usr/bin/mysql + - $(MYSQL_DATABASE) + - -h + - 127.0.0.1 + - -u$(MYSQL_USER) + - -p$(MYSQL_PASSWORD) + - -e EXIT + initialDelaySeconds: 5 + periodSeconds: 30 + timeoutSeconds: 2 + successThreshold: 1 + failureThreshold: 40 # 40x30sec before restart pod + - image: docker.io/curlimages/curl:8.5.0 + name: curl-tool + command: ["/bin/sleep", "infinity"] + volumes: + - name: mysql-data + persistentVolumeClaim: + claimName: mysql + - kind: Service + apiVersion: v1 + metadata: + name: todolist + namespace: ${NAMESPACE} + labels: + app: todolist + service: todolist + e2e-app: "true" + spec: + ports: + - name: web + port: 8000 + targetPort: 8000 + selector: + app: todolist + service: todolist + - kind: DeploymentConfig + apiVersion: apps.openshift.io/v1 + metadata: + name: todolist + namespace: ${NAMESPACE} + labels: + app: todolist + service: todolist + e2e-app: "true" + spec: + replicas: 1 + selector: + app: todolist + service: todolist + strategy: + type: Recreate + template: + metadata: + labels: + app: todolist + service: todolist + e2e-app: "true" + spec: + containers: + - name: todolist + image: quay.io/migtools/oadp-ci-todolist-mariadb-go:latest + env: + - name: foo + value: bar + ports: + - containerPort: 8000 + protocol: TCP + initContainers: + - name: init-myservice + image: docker.io/curlimages/curl:8.5.0 + command: ['sh', '-c', 'sleep 10; until /usr/bin/nc -z -w 1 mysql 3306; do echo Trying to connect to mysql DB port; sleep 5; done; echo mysql DB port reachable'] + - kind: Route + apiVersion: route.openshift.io/v1 + metadata: + name: todolist-route + namespace: ${NAMESPACE} + spec: + path: "/" + to: + kind: Service + name: todolist +parameters: + - description: Non admin user namespace + name: NAMESPACE + value: mysql-persistent diff --git a/hack/samples/backups/file-system.yaml b/hack/samples/backups/file-system.yaml new file mode 100644 index 0000000..aa7a086 --- /dev/null +++ b/hack/samples/backups/file-system.yaml @@ -0,0 +1,23 @@ +apiVersion: template.openshift.io/v1 +kind: Template +metadata: + name: fs-nonadminbackup +objects: + - apiVersion: nac.oadp.openshift.io/v1alpha1 + kind: NonAdminBackup + metadata: + name: nonadminbackup-sample-${SUFFIX} + namespace: ${NAMESPACE} + spec: + backupSpec: + includedNamespaces: + - ${NAMESPACE} + defaultVolumesToFsBackup: true +parameters: + - description: NonAdminBackup suffix + from: '[a-z0-9]{8}' + generate: expression + name: SUFFIX + - description: NonAdminBackup namespace + name: NAMESPACE + value: mysql-persistent diff --git a/internal/common/constant/constant.go b/internal/common/constant/constant.go index 6d44ee8..7aa9cce 100644 --- a/internal/common/constant/constant.go +++ b/internal/common/constant/constant.go @@ -17,10 +17,12 @@ limitations under the License. // Package constant contains all common constants used in the project package constant +import "os" + // Common labels for objects manipulated by the Non Admin Controller -// Labels should be used to identify the NAC backup +// Labels should be used to identify the NAC object // Annotations on the other hand should be used to define ownership -// of the specific Object, such as Backup. +// of the specific Object, such as Backup/Restore. const ( OadpLabel = "openshift.io/oadp" // TODO import? ManagedByLabel = "app.kubernetes.io/managed-by" @@ -29,3 +31,11 @@ const ( NabOriginNamespaceAnnotation = "openshift.io/oadp-nab-origin-namespace" NabOriginUUIDAnnotation = "openshift.io/oadp-nab-origin-uuid" ) + +// Common environment variables for the Non Admin Controller +const ( + NamespaceEnvVar = "WATCH_NAMESPACE" +) + +// OadpNamespace is the namespace OADP operator is installed +var OadpNamespace = os.Getenv(NamespaceEnvVar) diff --git a/internal/controller/nonadminbackup_controller.go b/internal/controller/nonadminbackup_controller.go index 5f4281d..5e2eb83 100644 --- a/internal/controller/nonadminbackup_controller.go +++ b/internal/controller/nonadminbackup_controller.go @@ -32,6 +32,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/log" nacv1alpha1 "github.com/migtools/oadp-non-admin/api/v1alpha1" + "github.com/migtools/oadp-non-admin/internal/common/constant" "github.com/migtools/oadp-non-admin/internal/common/function" "github.com/migtools/oadp-non-admin/internal/handler" "github.com/migtools/oadp-non-admin/internal/predicate" @@ -47,8 +48,7 @@ type NonAdminBackupReconciler struct { } const ( - nameField = "Name" - oadpNamespace = "openshift-adp" // TODO user input + nameField = "Name" ) // +kubebuilder:rbac:groups=nac.oadp.openshift.io,resources=nonadminbackups,verbs=get;list;watch;create;update;patch;delete @@ -103,7 +103,7 @@ func (r *NonAdminBackupReconciler) Reconcile(ctx context.Context, req ctrl.Reque veleroBackupName := function.GenerateVeleroBackupName(nab.Namespace, nab.Name) veleroBackup := velerov1api.Backup{} - err = r.Get(ctx, client.ObjectKey{Namespace: oadpNamespace, Name: veleroBackupName}, &veleroBackup) + err = r.Get(ctx, client.ObjectKey{Namespace: constant.OadpNamespace, Name: veleroBackupName}, &veleroBackup) if err != nil && errors.IsNotFound(err) { // Create backup @@ -111,7 +111,7 @@ func (r *NonAdminBackupReconciler) Reconcile(ctx context.Context, req ctrl.Reque veleroBackup = velerov1api.Backup{ ObjectMeta: metav1.ObjectMeta{ Name: veleroBackupName, - Namespace: oadpNamespace, + Namespace: constant.OadpNamespace, }, Spec: *backupSpec, } @@ -159,7 +159,7 @@ func (r *NonAdminBackupReconciler) SetupWithManager(mgr ctrl.Manager) error { WithEventFilter(predicate.CompositePredicate{ NonAdminBackupPredicate: predicate.NonAdminBackupPredicate{}, VeleroBackupPredicate: predicate.VeleroBackupPredicate{ - OadpVeleroNamespace: "openshift-adp", + OadpVeleroNamespace: constant.OadpNamespace, }, Context: r.Context, }).