From f634cdaa176b56f7d4d07911120d744b039f154c Mon Sep 17 00:00:00 2001 From: annastopel Date: Mon, 1 Apr 2019 14:29:17 +0300 Subject: [PATCH] CDI operator OLM integration: - Generate OLM related manifests for CDI in _out/manifests/release/olm OLM bundle: - cdi CSV manifest - cdi crd manifest - cdi package manifest - operatorsource manifest - subscription manifest - operatorgroup manifest - Modify cdi-operator role not to be cluster-admin but more specific - Move all final manifests to _out/manifests directory and update travis with new manifests location - Provide API for vendoring CDI OLM manifests generation code Note: - OLM CSV update to be supported in a separate PR - OLM bundle integration in travis is to be supported together with CSV update --- .travis.yml | 10 +- Makefile | 11 +- assets/cdi_logo.png | Bin 0 -> 11917 bytes cluster/sync.sh | 17 +- doc/cdi-operator-olm.md | 105 ++++ hack/README.md | 67 ++- hack/build/build-manifests.sh | 102 +--- hack/build/config.sh | 9 +- hack/build/docker/builder/Dockerfile | 4 +- hack/build/olm.sh | 73 +++ hack/build/resource-generator.sh | 159 ++++++ manifests/templates/cdi-controller.yaml.in | 14 + manifests/templates/cdi-operator.yaml.in | 103 ---- manifests/templates/registry-host.yaml.in | 1 - .../{ => release}/cdi-operator-cr.yaml.in | 3 + .../templates/release/cdi-operator.yaml.in | 31 + .../release/olm/bundle/cdi-crds.yaml.in | 1 + .../release/olm/bundle/cdi-package.yaml.in | 4 + .../bundle/cdioperator.VERSION.csv.yaml.in | 126 +++++ .../release/olm/cdi-csv-preconditions.yaml.in | 11 + .../release/olm/cdi-operatorsource.yaml.in | 12 + .../release/olm/cdi-subscription.yaml.in | 10 + .../release/olm/operatorgroup.yaml.in | 5 + pkg/operator/controller/controller_test.go | 44 +- pkg/operator/resources/cluster/apiserver.go | 21 +- pkg/operator/resources/cluster/controller.go | 13 +- pkg/operator/resources/cluster/factory.go | 47 +- pkg/operator/resources/cluster/rbac.go | 6 +- .../resources/namespaced/apiserver.go | 22 +- pkg/operator/resources/namespaced/common.go | 159 ------ .../resources/namespaced/controller.go | 10 +- pkg/operator/resources/namespaced/factory.go | 33 +- .../resources/namespaced/uploadproxy.go | 7 +- pkg/operator/resources/operator/components.go | 103 ++++ pkg/operator/resources/operator/factory.go | 116 ++++ pkg/operator/resources/operator/operator.go | 532 ++++++++++++++++++ pkg/operator/resources/utils/common.go | 259 +++++++++ .../manifest-generator/manifest-generator.go | 317 +++++++++-- 38 files changed, 2080 insertions(+), 487 deletions(-) create mode 100644 assets/cdi_logo.png create mode 100644 doc/cdi-operator-olm.md create mode 100755 hack/build/olm.sh create mode 100755 hack/build/resource-generator.sh create mode 100644 manifests/templates/cdi-controller.yaml.in delete mode 100644 manifests/templates/cdi-operator.yaml.in rename manifests/templates/{ => release}/cdi-operator-cr.yaml.in (50%) create mode 100644 manifests/templates/release/cdi-operator.yaml.in create mode 100644 manifests/templates/release/olm/bundle/cdi-crds.yaml.in create mode 100644 manifests/templates/release/olm/bundle/cdi-package.yaml.in create mode 100644 manifests/templates/release/olm/bundle/cdioperator.VERSION.csv.yaml.in create mode 100644 manifests/templates/release/olm/cdi-csv-preconditions.yaml.in create mode 100644 manifests/templates/release/olm/cdi-operatorsource.yaml.in create mode 100644 manifests/templates/release/olm/cdi-subscription.yaml.in create mode 100644 manifests/templates/release/olm/operatorgroup.yaml.in delete mode 100644 pkg/operator/resources/namespaced/common.go create mode 100644 pkg/operator/resources/operator/components.go create mode 100644 pkg/operator/resources/operator/factory.go create mode 100644 pkg/operator/resources/operator/operator.go create mode 100644 pkg/operator/resources/utils/common.go diff --git a/.travis.yml b/.travis.yml index 7958e2d4c..84db1a736 100644 --- a/.travis.yml +++ b/.travis.yml @@ -26,6 +26,7 @@ install: - make generate-verify - make docker - make manifests + - make olm-verify - make apidocs before_script: @@ -75,8 +76,13 @@ deploy: - _out/cmd/cdi-operator/* - _out/cmd/cdi-uploadproxy/* - _out/cmd/cdi-uploadserver/* - - manifests/generated/*.yaml - - manifests/generated/*.yaml.j2 + - _out/manifests/*.yaml + - _out/manifests/release/*.yaml + - _out/manifests/templates/release/*.yaml.j2 + - _out/manifests/release/olm/*.yaml + - _out/manifests/templates/release/olm/*.yaml.j2 + - _out/manifests/release/olm/bundle/*.yaml + - _out/manifests/templates/release/olm/bundle/*.yaml.j2 - manifests/example/* - provider: script skip_cleanup: true diff --git a/Makefile b/Makefile index dbaa39db8..d4faf5110 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,7 @@ .PHONY: build build-controller build-importer build-cloner build-apiserver build-uploadproxy build-uploadserver build-operator build-functest-file-image-init build-functest-registry-image-init build-functest \ docker docker-controller docker-cloner docker-importer docker-apiserver docker-uploadproxy docker-uploadserver docker-operator docker-functest-image-init docker-functest-image-http docker-functest-registry-populate docker-functest-registry docker-functest-registry-init docker-functest-block-device \ cluster-sync cluster-sync-controller cluster-sync-cloner cluster-sync-importer cluster-sync-apiserver cluster-sync-uploadproxy cluster-sync-uploadserver \ + olm-verify olm-push \ test test-functional test-unit test-lint \ publish \ vet \ @@ -48,7 +49,7 @@ apidocs: ${DO} "./hack/update-codegen.sh && ./hack/gen-swagger-doc/gen-swagger-docs.sh v1alpha1 html" build: - ${DO} "./hack/build/build-go.sh clean && ./hack/build/build-go.sh build ${WHAT} && ./hack/build/build-cdi-func-test-file-host.sh && ./hack/build/build-cdi-func-test-registry-host.sh && ./hack/build/build-cdi-func-test-block-device.sh && DOCKER_REPO=${DOCKER_REPO} DOCKER_TAG=${DOCKER_TAG} VERBOSITY=${VERBOSITY} PULL_POLICY=${PULL_POLICY} ./hack/build/build-manifests.sh ${WHAT} && ./hack/build/build-copy-artifacts.sh ${WHAT}" + ${DO} "./hack/build/build-go.sh clean && ./hack/build/build-go.sh build ${WHAT} && ./hack/build/build-cdi-func-test-file-host.sh && ./hack/build/build-cdi-func-test-registry-host.sh && ./hack/build/build-cdi-func-test-block-device.sh && DOCKER_REPO=${DOCKER_REPO} DOCKER_TAG=${DOCKER_TAG} VERBOSITY=${VERBOSITY} PULL_POLICY=${PULL_POLICY} QUAY_NAMESPACE=${QUAY_NAMESPACE} QUAY_REPOSITORY=${QUAY_REPOSITORY} CSV_VERSION=${CSV_VERSION} ./hack/build/build-manifests.sh ${WHAT} && ./hack/build/build-copy-artifacts.sh ${WHAT}" build-controller: WHAT = cmd/cdi-controller build-controller: build @@ -149,6 +150,12 @@ push-operator: push publish: docker ./hack/build/build-docker.sh publish ${WHAT} +olm-verify: + ${DO} "./hack/build/olm.sh verify" + +olm-push: + ${DO} "CSV_VERSION=${CSV_VERSION} QUAY_NAMESPACE=${QUAY_NAMESPACE} QUAY_USERNAME=${QUAY_USERNAME} QUAY_PASSWORD=${QUAY_PASSWORD} QUAY_REPOSITORY=${QUAY_REPOSITORY} ./hack/build/olm.sh push" + vet: ${DO} "./hack/build/build-go.sh vet ${WHAT}" @@ -156,7 +163,7 @@ format: ${DO} "./hack/build/format.sh" manifests: - ${DO} "DOCKER_REPO=${DOCKER_REPO} DOCKER_TAG=${DOCKER_TAG} VERBOSITY=${VERBOSITY} PULL_POLICY=${PULL_POLICY} ./hack/build/build-manifests.sh" + ${DO} "CSV_VERSION=${CSV_VERSION} QUAY_NAMESPACE=${QUAY_NAMESPACE} QUAY_REPOSITORY=${QUAY_REPOSITORY} DOCKER_REPO=${DOCKER_REPO} DOCKER_TAG=${DOCKER_TAG} VERBOSITY=${VERBOSITY} PULL_POLICY=${PULL_POLICY} ./hack/build/build-manifests.sh" goveralls: ${DO} "TRAVIS_JOB_ID=${TRAVIS_JOB_ID} TRAVIS_PULL_REQUEST=${TRAVIS_PULL_REQUEST} TRAVIS_BRANCH=${TRAVIS_BRANCH} ./hack/build/goveralls.sh" diff --git a/assets/cdi_logo.png b/assets/cdi_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..9e15e7ffdd18a6a675332aeb72f17bb557a5e041 GIT binary patch literal 11917 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 DOCKER_TAG= PULL_POLICY= VERBOSITY= CSV_VERSION= QUAY_NAMESPACE= QUAY_REPOSITORY= make manifests +``` +The generated final olm manifests will be located in _out/manifests/release/olm/bundle/_ directory + +Note: there is a structure of operator related manifest +- manifests/release - contains operator manifests that can be deployed without olm +- manifests/olm - contains additional auxilary manifests that are required when deploying with olm and with olm marketplace +- manifests/olm/bundle - contains olm bundle that is to be pushed to quay.io and consumed by marketplace operator +2. Verify generated manifests +```bash +make olm-verify +``` +3. Push the generated verified manifests to quay.io +```bash +CSV_VERSION= QUAY_USERNAME= QUAY_PASSWORD= QUAY_NAMESPACE= QUAY_REPOSITORY= make olm-push +``` + +##CDI OLM installation +### Prerequisites +#### Build OLM manifests and push them to quay +- Build OLM manifests and push to quay. Specify your DOCKER_REPO, DOCKER_TAG, QUAY_NAMESPACE, QUAY_REPOSITORY, CSV_VERSION. +```bash +DOCKER_REPO= DOCKER_TAG= PULL_POLICY= VERBOSITY= CSV_VERSION= QUAY_NAMESPACE= QUAY_REPOSITORY= make manifests +``` +- Push OLM bundle to quay. Provide QUAY_NAMESPACE, QUAY_REPOSITORY, QUAY_USERNAME, QUAY_PASSWORD, CSV_VERSION +```bash +QUAY_NAMESPACE= QUAY_REPOSITORY= QUAY_USERNAME= QUAY_PASSWORD= CSV_VERSION= make olm-push +``` +#### Install OLM and Marketplace operators on cluster +- Install OLM operator from cloned operator-lifecycle-manager repo + +```bash +kubectl apply -f $GOPATH/src/github.com/operator-framework/operator-lifecycle-manager/deploy/upstream/quickstart/olm.yaml +``` +- Install marketplace operator from cloned operator-marketplace repo +```bash +kubectl apply -f $GOPATH/src/github.com/operator-framework/operator-marketplace/deploy/upstream/ --validate=false +``` +###CDI installation by means of OLM and Marketplace operators +- Install CDI operatorsource manifest that specifies the location of CDI OLM bundle in quay +```bash +kubectl apply -f _out/manifests/release/olm/cdi-operatorsource.yaml_ +``` +- Handle marketplace namespace workarouond + + Move _catalogsourceconfig.operators.coreos.com/cdi_ from _markeplace_ namespace to _olm_ namespace by modifying *targetNamespace* field to 'olm' from 'marketplace' +```bash +cluster/kubectl.sh get operatorsource,catalogsourceconfig,catalogsource,subscription,installplan --all-namespaces +cluster/kubectl.sh edit catalogsourceconfig.operators.coreos.com/cdi -n marketplace +``` +- Create CDI namespace +```bash +kubectl create ns cdi +``` +- Configure namespace to be allowed to create operators there +```bash +cluster/kubectl.sh apply -f _out/manifests/release/olm/operatorgroup.yaml +``` +- Install subscription that will point from which channel the app is downloaded +```bash +cluster/kubectl.sh apply -f _out/manifests/release/olm/cdi-subscription.yaml +``` +- Now cdi-operator starts running but in order for it to succeed we need to deploy cdi cr +```bash +cluster/kubectl.sh apply -f _out/manifests/release/cdi-operator-cr.yaml +``` +Now the operator should finish its deployment succefully + +###OKD UI +- Grant cluster-admin permissions to kube-system:default +```bash +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: kube-system-admin + labels: + operator.cdi.kubevirt.io: "" +subjects: +- kind: ServiceAccount + name: default + namespace: kube-system +roleRef: + kind: ClusterRole + name: cluster-admin + apiGroup: "" +``` + +- Start OKD UI +```bash +cd $GOPATH/src/github.com/operator-lifecycle-manager/scripts/ +./run_console_local.sh +``` + + + + + diff --git a/hack/README.md b/hack/README.md index 14e32efae..bea978e5f 100644 --- a/hack/README.md +++ b/hack/README.md @@ -36,7 +36,7 @@ The standard workflow is performed inside a helper container to normalize the bu - `all`: cleans up previous build artifacts, compiles all CDI packages and builds containers - `apidocs`: generate client-go code (same as 'make generate') and swagger docs. -- `build`: compile all CDI binary artifacts and generate controller manifest +- `build`: compile all CDI binary artifacts and generate controller and operator manifests - `build-controller`: compile cdi-controller binary - `build-importer`: compile cdi-importer binary - `build-apiserver`: compile cdi-apiserver binary @@ -82,8 +82,10 @@ The standard workflow is performed inside a helper container to normalize the bu - `generate`: generate client-go deepcopy functions, clientset, listers and informers. - `generate-verify`: generate client-go deepcopy functions, clientset, listers and informers and validate codegen. - `goveralls`: run code coverage tracking system. -- `manifests`: generate a cdi-controller manifest in `manifests/generated/`. Accepts [make variables](#make-variables) DOCKER_TAG, DOCKER_REPO, VERBOSITY, and PULL_POLICY +- `manifests`: generate a cdi-controller and operator manifests in `_out/manifests/`. Accepts [make variables](#make-variables) DOCKER_TAG, DOCKER_REPO, VERBOSITY, PULL_POLICY, CSV_VERSION, QUAY_REPOSITORY, QUAY_NAMESPACE - `publish`: CI ONLY - this recipe is not intended for use by developers +- `olm-verify`: verify generated olm manifests +- `olm-push`: push generated operator bundle to quay.io. Accepts [make_variables](#make-variables) CSV_VERSION, QUAY_USER, QUAY_PASSWORD, QUAY_REPOSITORY - `push`: compiles, builds, and pushes to the repo passed in `DOCKER_REPO=` - `push-controller`: compile, build, and push cdi-controller - `push-importer`: compile, build, and push cdi-importer @@ -112,6 +114,11 @@ These may be passed to a target as `$ make VARIABLE=value target` - `DOCKER_TAG`: (default: latest) Set global version tags for image and manifest creation - `VERBOSITY`: (default: 1) Set global log level verbosity - `PULL_POLICY`: (default: IfNotPresent) Set global CDI pull policy +- `CSV_VERSION`: (default: v0.0.0) Set CSV version of cdi-operator for OLM manifests +- `QUAY_USERNAME`: (default: N/A) username to quay.io +- `QUAY_PASSWORD`: (default: N/A) password for quay.io +- `QUAY_NAMESPACE`: (default: kubevirt) namespace where cdi application is located +- `QUAY_REPOSITORY`: (default: cdi) application name - `TEST_ARGS`: A variable containing a list of additional ginkgo flags to be passed to functional tests. The string "--test-args=" must prefix the variable value. For example: `make TEST_ARGS="--test-args=-ginkgo.noColor=true" test-functional >& foo`. @@ -177,22 +184,58 @@ not supported, then you can use the following example to run Functional Tests. ``` *To customize environment variables see [make targets](#make-targets)* - - Run the generated latest manfifest + - Run the generated latest manifests + There are two options to deploy cdi directly via cdi-controller.yaml or to deploy it via operator + ##### Direct deployment ``` - # kubectl create -f manifests/generated/cdi-controller.yaml - - serviceaccount/cdi-sa created + #kubectl create -f ./_out/manifests/cdi-controller.yaml + + namespace/cdi created + customresourcedefinition.apiextensions.k8s.io/datavolumes.cdi.kubevirt.io created + customresourcedefinition.apiextensions.k8s.io/cdiconfigs.cdi.kubevirt.io created clusterrole.rbac.authorization.k8s.io/cdi created clusterrolebinding.rbac.authorization.k8s.io/cdi-sa created + clusterrole.rbac.authorization.k8s.io/cdi-apiserver created + clusterrolebinding.rbac.authorization.k8s.io/cdi-apiserver created + clusterrolebinding.rbac.authorization.k8s.io/cdi-apiserver-auth-delegator created + serviceaccount/cdi-sa created deployment.apps/cdi-deployment created - customresourcedefinition.apiextensions.k8s.io/datavolumes.cdi.kubevirt.io created + configmap/cdi-insecure-registries created + serviceaccount/cdi-apiserver created + rolebinding.rbac.authorization.k8s.io/cdi-apiserver created + role.rbac.authorization.k8s.io/cdi-apiserver created + rolebinding.rbac.authorization.k8s.io/cdi-extension-apiserver-authentication created + role.rbac.authorization.k8s.io/cdi-extension-apiserver-authentication created + service/cdi-api created + deployment.apps/cdi-apiserver created + service/cdi-uploadproxy created + deployment.apps/cdi-uploadproxy created + + ``` + ##### Deployment via operator + ``` + #./cluster/kubectl.sh apply -f "./_out/manifests/release/cdi-operator.yaml" + namespace/cdi created + customresourcedefinition.apiextensions.k8s.io/cdis.cdi.kubevirt.io created + configmap/cdi-operator-leader-election-helper created + clusterrole.rbac.authorization.k8s.io/cdi.kubevirt.io:operator created + serviceaccount/cdi-operator created + clusterrole.rbac.authorization.k8s.io/cdi-operator-cluster-permissions created + clusterrolebinding.rbac.authorization.k8s.io/cdi-operator created + deployment.apps/cdi-operator created + + #./cluster/kubectl.sh apply -f "./_out/manifests/release/cdi-operator-cr.yaml" + cdi.cdi.kubevirt.io/cdi created + ``` 4. Build and run the func test servers In order to run fucntional tests the below servers have to be run - *host-file-server* is required by the functional tests and provides an endpoint server for image files and s3 buckets - - *registry-server* is required by the functional tests and provides an endpoint server for container images. + - *registry-server* is required by the functional tests and provides an endpoint server for container images. + Note: for this server to run the follwoing setting is required in each cluster node + ``` systemctl -w user.max_user_namespaces=1024 ``` Build and Push to registry @@ -201,12 +244,14 @@ not supported, then you can use the following example to run Functional Tests. ``` Generate manifests ``` - # DOCKER_REPO= DOCKER_TAG= make manifests + # DOCKER_REPO= DOCKER_TAG= PULL_POLICY= VERBOSITY= CSV_VERSION= QUAY_NAMESPACE= QUAY_REPOSITORY= make manifests ``` Run servers ``` - # ./cluster/kubectl.sh apply -f ./manifests/generated/file-host.yaml - # ./cluster/kubectl.sh apply -f ./manifests/generated/registry-host.yaml + # ./cluster/kubectl.sh apply -f ./_out/manifests/file-host.yaml + # ./cluster/kubectl.sh apply -f ./_out/manifests/registry-host.yaml + # ./cluster/kubectl.sh apply -f ./_out/manifests/block-device.yaml + ``` 5. Run the tests diff --git a/hack/build/build-manifests.sh b/hack/build/build-manifests.sh index 4ad26e23c..7393f22e1 100755 --- a/hack/build/build-manifests.sh +++ b/hack/build/build-manifests.sh @@ -18,93 +18,35 @@ script_dir="$(readlink -f $(dirname $0))" source "${script_dir}"/common.sh source "${script_dir}"/config.sh -templates="$(find "${MANIFEST_TEMPLATE_DIR}" -name "*.in" -type f)" generator="${BIN_DIR}/manifest-generator" (cd "${CDI_DIR}/tools/manifest-generator/" && go build -o "${generator}" ./...) + +source "${script_dir}"/resource-generator.sh + + mkdir -p "${MANIFEST_GENERATED_DIR}/" +#generate controller related manifests used to deploy cdi without operator +generateResourceManifest $generator $MANIFEST_GENERATED_DIR "controller-rbac" "cdi-controller.k8s.rbac.yaml" +generateResourceManifest $generator $MANIFEST_GENERATED_DIR "apiserver-rbac" "cdi-apiserver.k8s.rbac.yaml" +generateResourceManifest $generator $MANIFEST_GENERATED_DIR "crd-resources" "cdi-resources.yaml" +generateResourceManifest $generator $MANIFEST_GENERATED_DIR "apiserver" "cdi-apiserver.yaml" +generateResourceManifest $generator $MANIFEST_GENERATED_DIR "controller" "cdi-controller.yaml" +generateResourceManifest $generator $MANIFEST_GENERATED_DIR "uploadproxy" "cdi-uploadproxy.yaml" -for tmpl in ${templates}; do - tmpl=$(readlink -f "${tmpl}") - outFile=$(basename -s .in "${tmpl}") - rm -rf "${MANIFEST_GENERATED_DIR}/${outFile}" - (${generator} -template="${tmpl}" \ - -docker-repo="${DOCKER_REPO}" \ - -docker-tag="${DOCKER_TAG}" \ - -controller-image="${CONTROLLER_IMAGE_NAME}" \ - -importer-image="${IMPORTER_IMAGE_NAME}" \ - -cloner-image="${CLONER_IMAGE_NAME}" \ - -apiserver-image=${APISERVER_IMAGE_NAME} \ - -uploadproxy-image=${UPLOADPROXY_IMAGE_NAME} \ - -uploadserver-image=${UPLOADSERVER_IMAGE_NAME} \ - -operator-image=${OPERATOR_IMAGE_NAME} \ - -verbosity="${VERBOSITY}" \ - -pull-policy="${PULL_POLICY}" \ - -namespace="${NAMESPACE}" - ) 1>"${MANIFEST_GENERATED_DIR}/${outFile}" - (${generator} -template="${tmpl}" \ - -docker-repo="{{ docker_prefix }}" \ - -docker-tag="{{ docker_tag }}" \ - -controller-image="{{ controller_image }}" \ - -importer-image="{{ importer_image }}" \ - -cloner-image="{{ cloner_image }}" \ - -apiserver-image="{{ apiserver_image }}" \ - -uploadproxy-image="{{ uploadproxy_image }}" \ - -uploadserver-image="{{ uploadserver_image }}" \ - -operator-image="{{ operator_image }}" \ - -verbosity="{{ verbosity }}" \ - -pull-policy="{{ pull_policy }}" \ - -namespace="{{ cdi_namespace }}" - ) 1>"${MANIFEST_GENERATED_DIR}/${outFile}.j2" -done +#generate operator related manifests used to deploy cdi with operator-framework +generateResourceManifest $generator $MANIFEST_GENERATED_DIR "operator-rbac" "rbac-operator.authorization.k8s.yaml.in" +generateResourceManifest $generator $MANIFEST_GENERATED_DIR "operator-deployment" "cdi-operator-deployment.yaml" +generateResourceManifest $generator $MANIFEST_GENERATED_DIR "operator-cdi-crd" "cdi-crd.yaml" +generateResourceManifest $generator $MANIFEST_GENERATED_DIR "operator-configmap-cr" "cdi-configmap-cr.yaml" -cat > "${MANIFEST_GENERATED_DIR}/cdi-controller.yaml" << EOF -apiVersion: v1 -kind: Namespace -metadata: - labels: - cdi.kubevirt.io: "" - name: cdi -EOF +#process templated manifests and populate them with generated manifests +tempDir=${MANIFEST_TEMPLATE_DIR} +processDirTemplates ${tempDir} ${OUT_DIR}/manifests ${OUT_DIR}/manifests/templates ${generator} ${MANIFEST_GENERATED_DIR} +processDirTemplates ${tempDir}/release ${OUT_DIR}/manifests/release ${OUT_DIR}/manifests/templates/release ${generator} ${MANIFEST_GENERATED_DIR} +processDirTemplates ${tempDir}/release/olm ${OUT_DIR}/manifests/release/olm ${OUT_DIR}/manifests/templates/release/olm ${generator} ${MANIFEST_GENERATED_DIR} +processDirTemplates ${tempDir}/release/olm/bundle ${OUT_DIR}/manifests/release/olm/bundle ${OUT_DIR}/manifests/templates/release/olm/bundle ${generator} ${MANIFEST_GENERATED_DIR} -(${generator} -code-group=everything \ - -docker-repo="${DOCKER_REPO}" \ - -docker-tag="${DOCKER_TAG}" \ - -controller-image="${CONTROLLER_IMAGE_NAME}" \ - -importer-image="${IMPORTER_IMAGE_NAME}" \ - -cloner-image="${CLONER_IMAGE_NAME}" \ - -apiserver-image=${APISERVER_IMAGE_NAME} \ - -uploadproxy-image=${UPLOADPROXY_IMAGE_NAME} \ - -uploadserver-image=${UPLOADSERVER_IMAGE_NAME} \ - -verbosity="${VERBOSITY}" \ - -pull-policy="${PULL_POLICY}" \ - -namespace="${NAMESPACE}" -) 1>>"${MANIFEST_GENERATED_DIR}/cdi-controller.yaml" -cat > "${MANIFEST_GENERATED_DIR}/cdi-controller.yaml.j2" << EOF -apiVersion: v1 -kind: Namespace -metadata: - labels: - cdi.kubevirt.io: "" - name: {{ cdi_namespace }} -EOF - -(${generator} -code-group=everything \ - -docker-repo="{{ docker_prefix }}" \ - -docker-tag="{{ docker_tag }}" \ - -controller-image="{{ controller_image }}" \ - -importer-image="{{ importer_image }}" \ - -cloner-image="{{ cloner_image }}" \ - -apiserver-image="{{ apiserver_image }}" \ - -uploadproxy-image="{{ uploadproxy_image }}" \ - -uploadserver-image="{{ uploadserver_image }}" \ - -verbosity="{{ verbosity }}" \ - -pull-policy="{{ pull_policy }}" \ - -namespace="{{ cdi_namespace }}" -) 1>>"${MANIFEST_GENERATED_DIR}/cdi-controller.yaml.j2" - -# Remove empty lines at the end of files which are added by go templating -find ${MANIFEST_GENERATED_DIR}/ -type f -exec sed -i {} -e '${/^$/d;}' \; diff --git a/hack/build/config.sh b/hack/build/config.sh index 4310cd52d..27c0ecd68 100755 --- a/hack/build/config.sh +++ b/hack/build/config.sh @@ -26,9 +26,10 @@ FUNC_TEST_REGISTRY_POPULATE="cdi-func-test-registry-populate" FUNC_TEST_REGISTRY_INIT="cdi-func-test-registry-init" FUNC_TEST_BLOCK_DEVICE="cdi-func-test-block-device" -BINARIES="cmd/${CONTROLLER} cmd/${IMPORTER} cmd/${CLONER} cmd/${APISERVER} cmd/${UPLOADPROXY} cmd/${UPLOADSERVER} cmd/${OPERATOR} tools/${FUNC_TEST_INIT} tools/${FUNC_TEST_REGISTRY_INIT}" +BINARIES="cmd/${OPERATOR} cmd/${CONTROLLER} cmd/${IMPORTER} cmd/${CLONER} cmd/${APISERVER} cmd/${UPLOADPROXY} cmd/${UPLOADSERVER} cmd/${OPERATOR} tools/${FUNC_TEST_INIT} tools/${FUNC_TEST_REGISTRY_INIT}" CDI_PKGS="cmd/ pkg/ test/" +OPERATOR_MAIN="cmd/${OPERATOR}" CONTROLLER_MAIN="cmd/${CONTROLLER}" IMPORTER_MAIN="cmd/${IMPORTER}" CLONER_MAIN="cmd/${CLONER}" @@ -36,7 +37,7 @@ APISERVER_MAIN="cmd/${APISERVER}" UPLOADPROXY_MAIN="cmd/${UPLOADPROXY}" UPLOADSERVER_MAIN="cmd/${UPLOADSERVER}" -DOCKER_IMAGES="cmd/${CONTROLLER} cmd/${IMPORTER} cmd/${CLONER} cmd/${APISERVER} cmd/${UPLOADPROXY} cmd/${UPLOADSERVER} cmd/${OPERATOR} tools/${FUNC_TEST_INIT} tools/${FUNC_TEST_HTTP} tools/${FUNC_TEST_REGISTRY} tools/${FUNC_TEST_REGISTRY_POPULATE} tools/${FUNC_TEST_REGISTRY_INIT} tools/${FUNC_TEST_BLOCK_DEVICE}" +DOCKER_IMAGES="cmd/${OPERATOR} cmd/${CONTROLLER} cmd/${IMPORTER} cmd/${CLONER} cmd/${APISERVER} cmd/${UPLOADPROXY} cmd/${UPLOADSERVER} cmd/${OPERATOR} tools/${FUNC_TEST_INIT} tools/${FUNC_TEST_HTTP} tools/${FUNC_TEST_REGISTRY} tools/${FUNC_TEST_REGISTRY_POPULATE} tools/${FUNC_TEST_REGISTRY_INIT} tools/${FUNC_TEST_BLOCK_DEVICE}" DOCKER_REPO=${DOCKER_REPO:-kubevirt} CONTROLLER_IMAGE_NAME=${CONTROLLER_IMAGE_NAME:-cdi-controller} IMPORTER_IMAGE_NAME=${IMPORTER_IMAGE_NAME:-cdi-importer} @@ -49,6 +50,10 @@ DOCKER_TAG=${DOCKER_TAG:-latest} VERBOSITY=${VERBOSITY:-1} PULL_POLICY=${PULL_POLICY:-IfNotPresent} NAMESPACE=${NAMESPACE:-cdi} +CSV_VERSION=${CSV_VERSION:-0.0.0} +QUAY_REPOSITORY=${QUAY_REPOSITORY:-cdi} +QUAY_NAMESPACE=${QUAY_NAMESPACE:-kubevirt} +CDI_LOGO_PATH=${CDI_LOGO_PATH:-"assets/cdi_logo.png"} KUBERNETES_IMAGE="k8s-1.13.3@sha256:bc0f02d6b970650eb16d12f97e5aa1376b3a13b0ffed6227db98675be2ca1184" OPENSHIFT_IMAGE="os-3.11.0-crio@sha256:3f11a6f437fcdf2d70de4fcc31e0383656f994d0d05f9a83face114ea7254bc0" diff --git a/hack/build/docker/builder/Dockerfile b/hack/build/docker/builder/Dockerfile index e45f9cdaa..21ea9e191 100644 --- a/hack/build/docker/builder/Dockerfile +++ b/hack/build/docker/builder/Dockerfile @@ -1,6 +1,8 @@ FROM fedora:29 -RUN dnf install -y qemu xz gzip git gradle gcc && dnf clean all +RUN dnf install -y qemu xz gzip git gradle gcc autoconf automake libtool python jq && dnf clean all + +RUN pip3 install j2cli && pip3 install operator-courier ENV GIMME_GO_VERSION=1.11.5 GOPATH="/go" KUBEBUILDER_VERSION="1.0.7" ARCH="amd64" diff --git a/hack/build/olm.sh b/hack/build/olm.sh new file mode 100755 index 000000000..7957c1dae --- /dev/null +++ b/hack/build/olm.sh @@ -0,0 +1,73 @@ +#!/usr/bin/env bash +# +# This file is part of the KubeVirt project +# +# 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. +# +# Copyright 2019 Red Hat, Inc. +# + +set -e + +source $(dirname "$0")/common.sh +source $(dirname "$0")/config.sh + +if [ $# -ne 1 ]; then + echo "usage: ${0} verify | push" +fi + +BUNDLE_DIR=${OUT_DIR}/manifests/release/olm/bundle +echo "using these manifests:" +ls ${BUNDLE_DIR} + +case ${1} in + +verify) + + IFS= + result=$(operator-courier verify ${BUNDLE_DIR} 2>&1) + echo $result + + if [[ $result =~ "ERROR" ]]; then + echo "olm verify failed!" + exit 1 + fi + + echo "olm verify success!" + exit 0 + + ;; + +push) + + if [[ -z "$QUAY_USERNAME" ]] || [[ -z "$QUAY_USERNAME" ]]; then + echo "please set QUAY_USERNAME, QUAY_PASSWORD" + exit 1 + fi + + echo "getting auth token from Quay" + AUTH_TOKEN=$(curl -sH "Content-Type: application/json" -XPOST https://quay.io/cnr/api/v1/users/login -d ' + { + "user": { + "username": "'"${QUAY_USERNAME}"'", + "password": "'"${QUAY_PASSWORD}"'" + } + }' | jq -r '.token') + + echo "pushing bundle" + + operator-courier push ${BUNDLE_DIR} ${QUAY_NAMESPACE} ${QUAY_REPOSITORY} ${CSV_VERSION} "$AUTH_TOKEN" + + ;; +esac + diff --git a/hack/build/resource-generator.sh b/hack/build/resource-generator.sh new file mode 100755 index 000000000..cbbbf2add --- /dev/null +++ b/hack/build/resource-generator.sh @@ -0,0 +1,159 @@ +#!/bin/bash + +#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. + +set -euo pipefail + +script_dir="$(readlink -f $(dirname $0))" +source "${script_dir}"/common.sh +source "${script_dir}"/config.sh + +#all generated files are placed in manifests/generated +function generateResourceManifest { + codeGroup=$3 + filename=$4 + generator=$1 + targetDir=$2 + + manifestName=$filename + manifestNamej2=$filename".j2" + + rm -rf ${targetDir}/$manifestName + rm -rf ${targetDir}/$manifestNamej2 + (${generator} -code-group=${codeGroup} \ + -docker-repo="${DOCKER_REPO}" \ + -docker-tag="${DOCKER_TAG}" \ + -deploy-cluster-resources="true" \ + -operator-image="${OPERATOR_IMAGE_NAME}" \ + -controller-image="${CONTROLLER_IMAGE_NAME}" \ + -importer-image="${IMPORTER_IMAGE_NAME}" \ + -cloner-image="${CLONER_IMAGE_NAME}" \ + -apiserver-image=${APISERVER_IMAGE_NAME} \ + -uploadproxy-image=${UPLOADPROXY_IMAGE_NAME} \ + -uploadserver-image=${UPLOADSERVER_IMAGE_NAME} \ + -csv-version=${CSV_VERSION} \ + -cdi-logo-path=${CDI_LOGO_PATH} \ + -quay-namespace="${QUAY_NAMESPACE}" \ + -quay-repository="${QUAY_REPOSITORY}" \ + -verbosity="${VERBOSITY}" \ + -pull-policy="${PULL_POLICY}" \ + -namespace="${NAMESPACE}" + ) 1>>"${targetDir}/"$manifestName + + (${generator} -code-group=${codeGroup} \ + -docker-repo="{{ docker_prefix }}" \ + -docker-tag="{{ docker_tag }}" \ + -deploy-cluster-resources="true" \ + -operator-image="{{ operator_image_name }}" \ + -controller-image="{{ controller_image }}" \ + -importer-image="{{ importer_image }}" \ + -cloner-image="{{ cloner_image }}" \ + -apiserver-image="{{ apiserver_image }}" \ + -uploadproxy-image="{{ uploadproxy_image }}" \ + -uploadserver-image="{{ uploadserver_image }}" \ + -csv-version=${CSV_VERSION} \ + -cdi-logo-path=${CDI_LOGO_PATH} \ + -quay-namespace="{{ quay_namespace }}}" \ + -quay-repository="{{ quay_repository }}}" \ + -verbosity="${VERBOSITY}" \ + -pull-policy="{{ pull_policy }}" \ + -namespace="{{ cdi_namespace }}" + ) 1>>"${targetDir}/"$manifestNamej2 + + # Remove empty lines at the end of files which are added by go templating + find ${targetDir}/ -type f -exec sed -i {} -e '${/^$/d;}' \; +} + +function processDirTemplates { + inTmplPath=$1 #Path to directory from which to take manifests templates for processing + outFinalManifestPath=$2 #Path to which to store final manifests version + outTmplPath=$3 #Path to which to store templated manifests version + generator=$4 #generator binary + genManifestsDir=$5 #path where manifests generated from code are stored + + rm -rf $outFinalManifestPath + rm -rf $outTmplPath + mkdir -p $outFinalManifestPath + mkdir -p $outTmplPath + + templates="$(find "${inTmplPath}" -maxdepth 1 -name "*.in" -type f)" + for tmpl in ${templates}; do + tmpl=$(readlink -f "${tmpl}") + populateResourceManifest $generator $outFinalManifestPath $outTmplPath $tmpl $genManifestsDir + done +} + + +# all templated final manifsets are located in _out/manifests/ +# all templated manifsets are located in _out/manifests/templates +function populateResourceManifest { + tmpl=$4 + generator=$1 + targetDir=$2 + tmplTargetDir=$3 + generatedManifests=$5 + + outfile=$(basename -s .in "${tmpl}") + + if [[ $tmpl == *"VERSION"* ]]; then + outfile=${outfile/VERSION/${CSV_VERSION}} + fi + (${generator} -template="${tmpl}" \ + -docker-repo="${DOCKER_REPO}" \ + -docker-tag="${DOCKER_TAG}" \ + -deploy-cluster-resources="true" \ + -operator-image="${OPERATOR_IMAGE_NAME}" \ + -controller-image="${CONTROLLER_IMAGE_NAME}" \ + -importer-image="${IMPORTER_IMAGE_NAME}" \ + -cloner-image="${CLONER_IMAGE_NAME}" \ + -apiserver-image=${APISERVER_IMAGE_NAME} \ + -uploadproxy-image=${UPLOADPROXY_IMAGE_NAME} \ + -uploadserver-image=${UPLOADSERVER_IMAGE_NAME} \ + -csv-version=${CSV_VERSION} \ + -cdi-logo-path=${CDI_LOGO_PATH} \ + -generated-manifests-path=${generatedManifests} \ + -quay-namespace="${QUAY_NAMESPACE}" \ + -quay-repository="${QUAY_REPOSITORY}" \ + -verbosity="${VERBOSITY}" \ + -pull-policy="${PULL_POLICY}" \ + -namespace="${NAMESPACE}" + ) 1>>"${targetDir}/"$outfile + + (${generator} -template="${tmpl}" \ + -docker-repo="{{ docker_prefix }}" \ + -docker-tag="{{ docker_tag }}" \ + -deploy-cluster-resources="true" \ + -operator-image="{{ operator_image_name }}" \ + -controller-image="{{ controller_image }}" \ + -importer-image="{{ importer_image }}" \ + -cloner-image="{{ cloner_image }}" \ + -apiserver-image="{{ apiserver_image }}" \ + -uploadproxy-image="{{ uploadproxy_image }}" \ + -uploadserver-image="{{ uploadserver_image }}" \ + -csv-version=${CSV_VERSION} \ + -cdi-logo-path=${CDI_LOGO_PATH} \ + -generated-manifests-path=${generatedManifests} \ + -quay-namespace="{{ quay_namespace }}" \ + -quay-namespace="{{ quay_repository }}" \ + -verbosity="${VERBOSITY}" \ + -pull-policy="{{ pull_policy }}" \ + -namespace="{{ cdi_namespace }}" + ) 1>>"${tmplTargetDir}/"$outfile".j2" + + # Remove empty lines at the end of files which are added by go templating + find ${targetDir}/ -type f -exec sed -i {} -e '${/^$/d;}' \; + find ${tmplTargetDir}/ -type f -exec sed -i {} -e '${/^$/d;}' \; +} + + + diff --git a/manifests/templates/cdi-controller.yaml.in b/manifests/templates/cdi-controller.yaml.in new file mode 100644 index 000000000..26a37987b --- /dev/null +++ b/manifests/templates/cdi-controller.yaml.in @@ -0,0 +1,14 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + cdi.kubevirt.io: "" + name: {{.Namespace}} +{{index .GeneratedManifests "cdi-resources.yaml"}} +{{index .GeneratedManifests ""}} +{{index .GeneratedManifests "cdi-controller.k8s.rbac.yaml"}} +{{index .GeneratedManifests "cdi-apiserver.k8s.rbac.yaml"}} +{{index .GeneratedManifests "cdi-controller.yaml"}} +{{index .GeneratedManifests "cdi-apiserver.yaml"}} +{{index .GeneratedManifests "cdi-uploadproxy.yaml"}} + diff --git a/manifests/templates/cdi-operator.yaml.in b/manifests/templates/cdi-operator.yaml.in deleted file mode 100644 index 0debde4c4..000000000 --- a/manifests/templates/cdi-operator.yaml.in +++ /dev/null @@ -1,103 +0,0 @@ -apiVersion: v1 -kind: Namespace -metadata: - labels: - cdi.kubevirt.io: "" - name: {{.Namespace}} ---- -apiVersion: v1 -kind: ServiceAccount -metadata: - name: cdi-operator - namespace: {{ .Namespace }} - labels: - operator.cdi.kubevirt.io: "" ---- -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - name: cdi-operator - labels: - operator.cdi.kubevirt.io: "" -subjects: -- kind: ServiceAccount - name: cdi-operator - namespace: {{ .Namespace }} -roleRef: - kind: ClusterRole - name: cluster-admin - apiGroup: "" ---- -apiVersion: v1 -kind: ConfigMap -metadata: - name: "cdi-operator-leader-election-helper" - namespace: {{ .Namespace }} - labels: - operator.cdi.kubevirt.io: "" ---- -apiVersion: apps/v1 -kind: Deployment -metadata: - name: cdi-operator - namespace: {{ .Namespace }} - labels: - operator.cdi.kubevirt.io: "" -spec: - replicas: 1 - selector: - matchLabels: - name: cdi-operator - template: - metadata: - labels: - name: cdi-operator - operator.cdi.kubevirt.io: "" - spec: - serviceAccountName: cdi-operator - containers: - - name: cdi-operator - image: {{ .DockerRepo }}/{{ .OperatorImage }}:{{ .DockerTag }} - ports: - - containerPort: 60000 - name: metrics - imagePullPolicy: {{ .PullPolicy }} - env: - - name: DEPLOY_CLUSTER_RESOURCES - value: 'true' - - name: DOCKER_REPO - value: {{ .DockerRepo }} - - name: DOCKER_TAG - value: {{ .DockerTag }} - - name: CONTROLLER_IMAGE - value: {{ .ControllerImage }} - - name: IMPORTER_IMAGE - value: {{ .ImporterImage }} - - name: CLONER_IMAGE - value: {{ .ClonerImage }} - - name: APISERVER_IMAGE - value: {{ .APIServerImage }} - - name: UPLOAD_PROXY_IMAGE - value: {{ .UploadProxyImage }} - - name: UPLOAD_SERVER_IMAGE - value: {{ .UploadServerImage }} - - name: VERBOSITY - value: '{{ .Verbosity }}' - - name: PULL_POLICY - value: {{ .PullPolicy }} ---- -apiVersion: apiextensions.k8s.io/v1beta1 -kind: CustomResourceDefinition -metadata: - name: cdis.cdi.kubevirt.io - labels: - operator.cdi.kubevirt.io: "" -spec: - group: cdi.kubevirt.io - names: - kind: CDI - listKind: CDIList - plural: cdis - singular: cdi - scope: Cluster - version: v1alpha1 diff --git a/manifests/templates/registry-host.yaml.in b/manifests/templates/registry-host.yaml.in index 09922a7fe..21c55172f 100644 --- a/manifests/templates/registry-host.yaml.in +++ b/manifests/templates/registry-host.yaml.in @@ -90,4 +90,3 @@ spec: - name: sec-docker-reg port: 443 targetPort: 443 - diff --git a/manifests/templates/cdi-operator-cr.yaml.in b/manifests/templates/release/cdi-operator-cr.yaml.in similarity index 50% rename from manifests/templates/cdi-operator-cr.yaml.in rename to manifests/templates/release/cdi-operator-cr.yaml.in index a7c874884..8dbaaeca7 100644 --- a/manifests/templates/cdi-operator-cr.yaml.in +++ b/manifests/templates/release/cdi-operator-cr.yaml.in @@ -2,3 +2,6 @@ apiVersion: cdi.kubevirt.io/v1alpha1 kind: CDI metadata: name: cdi + namespace: {{.Namespace}} +spec: + imagePullPolicy: {{.PullPolicy}} diff --git a/manifests/templates/release/cdi-operator.yaml.in b/manifests/templates/release/cdi-operator.yaml.in new file mode 100644 index 000000000..b63df5c96 --- /dev/null +++ b/manifests/templates/release/cdi-operator.yaml.in @@ -0,0 +1,31 @@ +apiVersion: v1 +kind: Namespace +metadata: + labels: + cdi.kubevirt.io: "" + name: {{.Namespace}} +{{index .GeneratedManifests "cdi-crd.yaml"}} +{{index .GeneratedManifests "cdi-configmap-cr.yaml"}} +--- +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: cdi.kubevirt.io:operator + labels: + operator.cdi.kubevirt.io: "" + rbac.authorization.k8s.io/aggregate-to-admin: "true" +rules: +- apiGroups: + - cdi.kubevirt.io + resources: + - cdis + verbs: + - create + - patch + - get + - list + - delete + - watch + - deletecollection +{{index .GeneratedManifests "rbac-operator.authorization.k8s.yaml.in"}} +{{index .GeneratedManifests "cdi-operator-deployment.yaml"}} diff --git a/manifests/templates/release/olm/bundle/cdi-crds.yaml.in b/manifests/templates/release/olm/bundle/cdi-crds.yaml.in new file mode 100644 index 000000000..57bfe3421 --- /dev/null +++ b/manifests/templates/release/olm/bundle/cdi-crds.yaml.in @@ -0,0 +1 @@ +{{index .GeneratedManifests "cdi-crd.yaml"}} diff --git a/manifests/templates/release/olm/bundle/cdi-package.yaml.in b/manifests/templates/release/olm/bundle/cdi-package.yaml.in new file mode 100644 index 000000000..858a98caa --- /dev/null +++ b/manifests/templates/release/olm/bundle/cdi-package.yaml.in @@ -0,0 +1,4 @@ +packageName: {{.QuayRepository}} +channels: + - name: beta + currentCSV: cdioperator.{{.CsvVersion}} diff --git a/manifests/templates/release/olm/bundle/cdioperator.VERSION.csv.yaml.in b/manifests/templates/release/olm/bundle/cdioperator.VERSION.csv.yaml.in new file mode 100644 index 000000000..56231eb8d --- /dev/null +++ b/manifests/templates/release/olm/bundle/cdioperator.VERSION.csv.yaml.in @@ -0,0 +1,126 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: ClusterServiceVersion +metadata: + name: cdioperator.{{.CsvVersion}} + namespace: placeholder + annotations: + capabilities: "Full Lifecycle" + categories: "Storage,Virtualization" + alm-examples: | + [ + { + "apiVersion":"cdi.kubevirt.io/v1alpha1", + "kind":"CDI", + "metadata": { + "name":"cdi", + "namespace":"cdi" + }, + "spec": { + "imagePullPolicy":"IfNotPresent" + } + } + ] + description: Creates and maintains CDI deployments +spec: + displayName: CDI + description: | + CDI is a kubernetes extension that provides the ability to populate PVCs with VM images upon creation. Multiple image formats and sources are supported + + _The CDI Operator does not support updates yet._ + keywords: + - CDI + - Virtualization + - Storage + version: {{.CsvVersion}} + maturity: alpha +{{.ReplacesCsvVersion}} + maintainers: + - name: KubeVirt project + email: kubevirt-dev@googlegroups.com + provider: + name: KubeVirt/CDI project + links: + - name: CDI + url: https://github.com/kubevirt/containerized-data-importer/blob/master/README.md + - name: Source Code + url: https://github.com/kubevirt/containerized-data-importer + icon: + - base64data: {{.CDILogo}} + mediatype: image/png + labels: + alm-owner-cdi: cdi-operator + operated-by: cdi-operator + selector: + matchLabels: + alm-owner-cdi: cdi-operator + operated-by: cdi-operator + installModes: + - type: OwnNamespace + supported: true + - type: SingleNamespace + supported: true + - type: MultiNamespace + supported: true + - type: AllNamespaces + supported: true + install: + strategy: deployment + spec: + clusterPermissions: + - serviceAccountName: cdi-operator + rules: +{{.OperatorRules}} + deployments: + - name: cdi-operator + spec: +{{.OperatorDeploymentSpec}} + customresourcedefinitions: + owned: + - name: cdis.cdi.kubevirt.io + version: v1alpha1 + kind: CDI + displayName: CDI deployment + description: Represents a CDI deployment. + resources: + - kind: ConfigMap + name: cdi-operator-leader-election-helper + version: v1 + specDescriptors: + - description: The ImageRegistry to use for the CDI components. + displayName: ImageRegistry + path: imageRegistry + x-descriptors: + - 'urn:alm:descriptor:text' + - description: The ImageTag to use for the CDI components. + displayName: ImageTag + path: imageTag + x-descriptors: + - 'urn:alm:descriptor:text' + - description: The ImagePullPolicy to use for the CDI components. + displayName: ImagePullPolicy + path: imagePullPolicy + x-descriptors: + - 'urn:alm:descriptor:io.kubernetes:imagePullPolicy' + statusDescriptors: + - description: The deployment phase. + displayName: Phase + path: phase + x-descriptors: + - 'urn:alm:descriptor:io.kubernetes.phase' + - description: Explanation for the current status of the CDI deployment. + displayName: Condition + path: CDICondition + x-descriptors: + - 'urn:alm:descriptor:io.kubernetes.conditions' + - description: The observed version of the CDI deployment. + displayName: Observed CDI Version + path: observedVersion + - 'urn:alm:descriptor:text' + - description: The targeted version of the CDI deployment. + displayName: Target CDI Version + path: targetVersion + - 'urn:alm:descriptor:text' + - description: The version of the CDI Operator. + displayName: CDI Operator Version + path: operatorVersion + - 'urn:alm:descriptor:text' diff --git a/manifests/templates/release/olm/cdi-csv-preconditions.yaml.in b/manifests/templates/release/olm/cdi-csv-preconditions.yaml.in new file mode 100644 index 000000000..237dbe6a6 --- /dev/null +++ b/manifests/templates/release/olm/cdi-csv-preconditions.yaml.in @@ -0,0 +1,11 @@ +--- +apiVersion: v1 +kind: Namespace +metadata: + labels: + cdi.kubevirt.io: "" + name: {{.Namespace}} +{{index .GeneratedManifests "cdi-crd.yaml"}} +{{index .GeneratedManifests "cdi-configmap-cr.yaml"}} +{{index .GeneratedManifests "rbac-operator.authorization.k8s.yaml.in"}} + diff --git a/manifests/templates/release/olm/cdi-operatorsource.yaml.in b/manifests/templates/release/olm/cdi-operatorsource.yaml.in new file mode 100644 index 000000000..e16d0e844 --- /dev/null +++ b/manifests/templates/release/olm/cdi-operatorsource.yaml.in @@ -0,0 +1,12 @@ +apiVersion: operators.coreos.com/v1 +kind: OperatorSource +metadata: + name: cdi + namespace: marketplace +spec: + type: appregistry + endpoint: https://quay.io/cnr + registryNamespace: {{.QuayNamespace}} + displayName: "CDI" + publisher: "CDI project" + diff --git a/manifests/templates/release/olm/cdi-subscription.yaml.in b/manifests/templates/release/olm/cdi-subscription.yaml.in new file mode 100644 index 000000000..97655ea5f --- /dev/null +++ b/manifests/templates/release/olm/cdi-subscription.yaml.in @@ -0,0 +1,10 @@ +apiVersion: operators.coreos.com/v1alpha1 +kind: Subscription +metadata: + name: cdi + namespace: {{.Namespace}} +spec: + channel: beta + name: cdi + source: cdi + sourceNamespace: olm diff --git a/manifests/templates/release/olm/operatorgroup.yaml.in b/manifests/templates/release/olm/operatorgroup.yaml.in new file mode 100644 index 000000000..489c28fa7 --- /dev/null +++ b/manifests/templates/release/olm/operatorgroup.yaml.in @@ -0,0 +1,5 @@ +apiVersion: operators.coreos.com/v1alpha2 +kind: OperatorGroup +metadata: + name: {{.Namespace}} + namespace: {{.Namespace}} diff --git a/pkg/operator/controller/controller_test.go b/pkg/operator/controller/controller_test.go index 1b9dbb362..3e7b1003a 100644 --- a/pkg/operator/controller/controller_test.go +++ b/pkg/operator/controller/controller_test.go @@ -68,16 +68,17 @@ type args struct { var ( envVars = map[string]string{ - "DOCKER_REPO": "kubevirt", - "DOCKER_TAG": version, - "CONTROLLER_IMAGE": "cdi-controller", - "IMPORTER_IMAGE": "cdi-importer", - "CLONER_IMAGE": "cdi-cloner", - "UPLOAD_PROXY_IMAGE": "cdi-uploadproxy", - "UPLOAD_SERVER_IMAGE": "cdi-uploadserver", - "APISERVER_IMAGE": "cdi-apiserver", - "VERBOSITY": "1", - "PULL_POLICY": "Always", + "DOCKER_REPO": "kubevirt", + "DOCKER_TAG": version, + "DEPLOY_CLUSTER_RESOURCES": "true", + "CONTROLLER_IMAGE": "cdi-controller", + "IMPORTER_IMAGE": "cdi-importer", + "CLONER_IMAGE": "cdi-cloner", + "UPLOAD_PROXY_IMAGE": "cdi-uploadproxy", + "UPLOAD_SERVER_IMAGE": "cdi-uploadserver", + "APISERVER_IMAGE": "cdi-apiserver", + "VERBOSITY": "1", + "PULL_POLICY": "Always", } ) @@ -593,17 +594,18 @@ func createReconciler(client realClient.Client) *ReconcileCDI { namespace := "cdi" clusterArgs := &clusterResources.FactoryArgs{Namespace: namespace} namespacedArgs := &namespaceResources.FactoryArgs{ - DockerRepo: "kubevirt", - DockerTag: version, - ControllerImage: "cdi-controller", - ImporterImage: "cdi-importer", - ClonerImage: "cdi-cloner", - APIServerImage: "cdi-apiserver", - UploadProxyImage: "cdi-uploadproxy", - UploadServerImage: "cdi-uploadserver", - Verbosity: "1", - PullPolicy: "Always", - Namespace: namespace, + DockerRepo: "kubevirt", + DockerTag: version, + DeployClusterResources: "true", + ControllerImage: "cdi-controller", + ImporterImage: "cdi-importer", + ClonerImage: "cdi-cloner", + APIServerImage: "cdi-apiserver", + UploadProxyImage: "cdi-uploadproxy", + UploadServerImage: "cdi-uploadserver", + Verbosity: "1", + PullPolicy: "Always", + Namespace: namespace, } return &ReconcileCDI{ diff --git a/pkg/operator/resources/cluster/apiserver.go b/pkg/operator/resources/cluster/apiserver.go index d62b74371..8cdff62b3 100644 --- a/pkg/operator/resources/cluster/apiserver.go +++ b/pkg/operator/resources/cluster/apiserver.go @@ -33,13 +33,9 @@ func createAPIServerResources(args *FactoryArgs) []runtime.Object { } } -func createAPIServerClusterRoleBinding(namespace string) *rbacv1.ClusterRoleBinding { - return createClusterRoleBinding(apiServerResourceName, apiServerResourceName, apiServerResourceName, namespace) -} - -func createAPIServerClusterRole() *rbacv1.ClusterRole { - clusterRole := createClusterRole(apiServerResourceName) - clusterRole.Rules = []rbacv1.PolicyRule{ +//GetAPIServerClusterRolePermissions generates permissions for operator +func GetAPIServerClusterRolePermissions() []rbacv1.PolicyRule { + return []rbacv1.PolicyRule{ { APIGroups: []string{ "admissionregistration.k8s.io", @@ -91,9 +87,18 @@ func createAPIServerClusterRole() *rbacv1.ClusterRole { }, }, } +} + +func createAPIServerClusterRoleBinding(namespace string) *rbacv1.ClusterRoleBinding { + return CreateClusterRoleBinding(apiServerResourceName, apiServerResourceName, apiServerResourceName, namespace) +} + +func createAPIServerClusterRole() *rbacv1.ClusterRole { + clusterRole := CreateClusterRole(apiServerResourceName) + clusterRole.Rules = GetAPIServerClusterRolePermissions() return clusterRole } func createAPIServerAuthClusterRoleBinding(namespace string) *rbacv1.ClusterRoleBinding { - return createClusterRoleBinding("cdi-apiserver-auth-delegator", "system:auth-delegator", apiServerResourceName, namespace) + return CreateClusterRoleBinding("cdi-apiserver-auth-delegator", "system:auth-delegator", apiServerResourceName, namespace) } diff --git a/pkg/operator/resources/cluster/controller.go b/pkg/operator/resources/cluster/controller.go index 6ef8fc7c3..2aa4eaaae 100644 --- a/pkg/operator/resources/cluster/controller.go +++ b/pkg/operator/resources/cluster/controller.go @@ -34,12 +34,12 @@ func createControllerResources(args *FactoryArgs) []runtime.Object { } func createControllerClusterRoleBinding(namespace string) *rbacv1.ClusterRoleBinding { - return createClusterRoleBinding(controllerServiceAccountName, controlerClusterRoleName, controllerServiceAccountName, namespace) + return CreateClusterRoleBinding(controllerServiceAccountName, controlerClusterRoleName, controllerServiceAccountName, namespace) } -func createControllerClusterRole() *rbacv1.ClusterRole { - clusterRole := createClusterRole(controlerClusterRoleName) - clusterRole.Rules = []rbacv1.PolicyRule{ +//GetControllerPermissions geberates rules for cdi controller +func GetControllerPermissions() []rbacv1.PolicyRule { + return []rbacv1.PolicyRule{ { APIGroups: []string{ "", @@ -189,5 +189,10 @@ func createControllerClusterRole() *rbacv1.ClusterRole { }, }, } +} + +func createControllerClusterRole() *rbacv1.ClusterRole { + clusterRole := CreateClusterRole(controlerClusterRoleName) + clusterRole.Rules = GetControllerPermissions() return clusterRole } diff --git a/pkg/operator/resources/cluster/factory.go b/pkg/operator/resources/cluster/factory.go index 5c4c6984a..54ee3c366 100644 --- a/pkg/operator/resources/cluster/factory.go +++ b/pkg/operator/resources/cluster/factory.go @@ -24,15 +24,48 @@ import ( // FactoryArgs contains the required parameters to generate all cluster-scoped resources type FactoryArgs struct { - Namespace string + DockerRepo string `required:"true" split_words:"true"` + DockerTag string `required:"true" split_words:"true"` + DeployClusterResources string `required:"true" split_words:"true"` + ControllerImage string `required:"true" split_words:"true"` + ImporterImage string `required:"true" split_words:"true"` + ClonerImage string `required:"true" split_words:"true"` + APIServerImage string `required:"true" envconfig:"apiserver_image"` + UploadProxyImage string `required:"true" split_words:"true"` + UploadServerImage string `required:"true" split_words:"true"` + Verbosity string `required:"true"` + PullPolicy string `required:"true" split_words:"true"` + Namespace string } type factoryFunc func(*FactoryArgs) []runtime.Object +const ( + //CdiRBAC - groupCode to generate only operator rbac manifest + CdiRBAC string = "cdi-rbac" + //APIServerRBAC - groupCode to generate only apiserver rbac manifest + APIServerRBAC string = "apiserver-rbac" + //ControllerRBAC - groupCode to generate only controller rbac manifest + ControllerRBAC string = "controller-rbac" + //CRDResources - groupCode to generate only resources' manifest + CRDResources string = "crd-resources" +) + var factoryFunctions = map[string]factoryFunc{ - "crd": createCRDResources, - "controller": createControllerResources, - "apiserver": createAPIServerResources, + CdiRBAC: createCdiRBAC, + APIServerRBAC: createAPIServerResources, + ControllerRBAC: createControllerResources, + CRDResources: createCRDResources, +} + +//IsFactoryResource returns true id codeGroupo belolngs to factory functions +func IsFactoryResource(codeGroup string) bool { + for k := range factoryFunctions { + if codeGroup == k { + return true + } + } + return false } func createCRDResources(args *FactoryArgs) []runtime.Object { @@ -42,6 +75,12 @@ func createCRDResources(args *FactoryArgs) []runtime.Object { } } +func createCdiRBAC(args *FactoryArgs) []runtime.Object { + return append( + createAPIServerResources(args), + createControllerResources(args)...) +} + // CreateAllResources creates all cluster-wide resources func CreateAllResources(args *FactoryArgs) ([]runtime.Object, error) { var resources []runtime.Object diff --git a/pkg/operator/resources/cluster/rbac.go b/pkg/operator/resources/cluster/rbac.go index a64e15742..d607cd512 100644 --- a/pkg/operator/resources/cluster/rbac.go +++ b/pkg/operator/resources/cluster/rbac.go @@ -21,7 +21,8 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func createClusterRoleBinding(name, roleRef, serviceAccount, serviceAccountNamespace string) *rbacv1.ClusterRoleBinding { +//CreateClusterRoleBinding create cluster role bunding +func CreateClusterRoleBinding(name, roleRef, serviceAccount, serviceAccountNamespace string) *rbacv1.ClusterRoleBinding { return &rbacv1.ClusterRoleBinding{ TypeMeta: metav1.TypeMeta{ APIVersion: "rbac.authorization.k8s.io/v1", @@ -48,7 +49,8 @@ func createClusterRoleBinding(name, roleRef, serviceAccount, serviceAccountNames } } -func createClusterRole(name string) *rbacv1.ClusterRole { +//CreateClusterRole create cluster role +func CreateClusterRole(name string) *rbacv1.ClusterRole { return &rbacv1.ClusterRole{ TypeMeta: metav1.TypeMeta{ APIVersion: "rbac.authorization.k8s.io/v1", diff --git a/pkg/operator/resources/namespaced/apiserver.go b/pkg/operator/resources/namespaced/apiserver.go index c986f47e0..c541a47b1 100644 --- a/pkg/operator/resources/namespaced/apiserver.go +++ b/pkg/operator/resources/namespaced/apiserver.go @@ -22,6 +22,8 @@ import ( rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/intstr" + "kubevirt.io/containerized-data-importer/pkg/common" + utils "kubevirt.io/containerized-data-importer/pkg/operator/resources/utils" ) const ( @@ -30,6 +32,10 @@ const ( extensionAPIConfigMapName = "extension-apiserver-authentication" ) +const ( + cdiLabel = common.CDIComponentLabel +) + func createAPIServerResources(args *FactoryArgs) []runtime.Object { return []runtime.Object{ createAPIServerServiceAccount(), @@ -43,15 +49,15 @@ func createAPIServerResources(args *FactoryArgs) []runtime.Object { } func createAPIServerServiceAccount() *corev1.ServiceAccount { - return createServiceAccount(apiServerRessouceName) + return utils.CreateServiceAccount(apiServerRessouceName) } func createAPIServerRoleBinding(serviceAccountNamespace string) *rbacv1.RoleBinding { - return createRoleBinding(apiServerRessouceName, apiServerRessouceName, apiServerRessouceName, serviceAccountNamespace) + return utils.CreateRoleBinding(apiServerRessouceName, apiServerRessouceName, apiServerRessouceName, serviceAccountNamespace) } func createAPIServerRole() *rbacv1.Role { - role := createRole(apiServerRessouceName) + role := utils.CreateRole(apiServerRessouceName) role.Rules = []rbacv1.PolicyRule{ { APIGroups: []string{ @@ -71,7 +77,7 @@ func createAPIServerRole() *rbacv1.Role { } func createExtensionAPIServerRoleBinding(serviceAccountNamespace string) *rbacv1.RoleBinding { - roleBinding := createRoleBinding( + roleBinding := utils.CreateRoleBinding( extensionAPIResourceName, extensionAPIResourceName, apiServerRessouceName, @@ -82,7 +88,7 @@ func createExtensionAPIServerRoleBinding(serviceAccountNamespace string) *rbacv1 } func createExtensionAPIServerRole() *rbacv1.Role { - role := createRole(extensionAPIResourceName) + role := utils.CreateRole(extensionAPIResourceName) role.Rules = []rbacv1.PolicyRule{ { APIGroups: []string{ @@ -104,7 +110,7 @@ func createExtensionAPIServerRole() *rbacv1.Role { } func createAPIServerService() *corev1.Service { - service := createService("cdi-api", cdiLabel, apiServerRessouceName) + service := utils.CreateService("cdi-api", cdiLabel, apiServerRessouceName) service.Spec.Ports = []corev1.ServicePort{ { Port: 443, @@ -119,8 +125,8 @@ func createAPIServerService() *corev1.Service { } func createAPIServerDeployment(repo, image, tag, verbosity, pullPolicy string) *appsv1.Deployment { - deployment := createDeployment(apiServerRessouceName, cdiLabel, apiServerRessouceName, apiServerRessouceName, 1) - container := createContainer(apiServerRessouceName, repo, image, tag, verbosity, corev1.PullPolicy(pullPolicy)) + deployment := utils.CreateDeployment(apiServerRessouceName, cdiLabel, apiServerRessouceName, apiServerRessouceName, 1) + container := utils.CreateContainer(apiServerRessouceName, repo, image, tag, verbosity, corev1.PullPolicy(pullPolicy)) container.ReadinessProbe = &corev1.Probe{ Handler: corev1.Handler{ HTTPGet: &corev1.HTTPGetAction{ diff --git a/pkg/operator/resources/namespaced/common.go b/pkg/operator/resources/namespaced/common.go deleted file mode 100644 index 5073bddfc..000000000 --- a/pkg/operator/resources/namespaced/common.go +++ /dev/null @@ -1,159 +0,0 @@ -/* -Copyright 2018 The CDI Authors. - -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. -*/ - -package namespaced - -import ( - "fmt" - - appsv1 "k8s.io/api/apps/v1" - corev1 "k8s.io/api/core/v1" - rbacv1 "k8s.io/api/rbac/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - - "kubevirt.io/containerized-data-importer/pkg/common" -) - -const ( - cdiLabel = common.CDIComponentLabel -) - -var commonLabels = map[string]string{ - "cdi.kubevirt.io": "", -} - -func withCommonLabels(labels map[string]string) map[string]string { - if labels == nil { - labels = make(map[string]string) - } - - for k, v := range commonLabels { - _, ok := labels[k] - if !ok { - labels[k] = v - } - } - - return labels -} - -func createServiceAccount(name string) *corev1.ServiceAccount { - return &corev1.ServiceAccount{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "v1", - Kind: "ServiceAccount", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Labels: withCommonLabels(nil), - }, - } -} - -func createRoleBinding(name, roleRef, serviceAccount, serviceAccountNamespace string) *rbacv1.RoleBinding { - return &rbacv1.RoleBinding{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "rbac.authorization.k8s.io/v1", - Kind: "RoleBinding", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Labels: withCommonLabels(nil), - }, - RoleRef: rbacv1.RoleRef{ - Kind: "Role", - Name: roleRef, - APIGroup: "rbac.authorization.k8s.io", - }, - Subjects: []rbacv1.Subject{ - { - Kind: "ServiceAccount", - Name: serviceAccount, - Namespace: serviceAccountNamespace, - }, - }, - } -} - -func createRole(name string) *rbacv1.Role { - return &rbacv1.Role{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "rbac.authorization.k8s.io/v1", - Kind: "Role", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Labels: withCommonLabels(nil), - }, - } -} - -func createDeployment(name, matchKey, matchValue, serviceAccount string, numReplicas int32) *appsv1.Deployment { - matchMap := map[string]string{matchKey: matchValue} - deployment := &appsv1.Deployment{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "apps/v1", - Kind: "Deployment", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Labels: withCommonLabels(matchMap), - }, - Spec: appsv1.DeploymentSpec{ - Replicas: &numReplicas, - Selector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - matchKey: matchValue, - }, - }, - Template: corev1.PodTemplateSpec{ - ObjectMeta: metav1.ObjectMeta{ - Labels: withCommonLabels(matchMap), - }, - }, - }, - } - if serviceAccount != "" { - deployment.Spec.Template.Spec.ServiceAccountName = serviceAccount - } - return deployment -} - -func createContainer(name, repo, image, tag, verbosity string, pullPolicy corev1.PullPolicy) corev1.Container { - return corev1.Container{ - Name: name, - Image: fmt.Sprintf("%s/%s:%s", repo, image, tag), - ImagePullPolicy: pullPolicy, - Args: []string{"-v=" + verbosity}, - } -} - -func createService(name, matchKey, matchValue string) *corev1.Service { - matchMap := map[string]string{matchKey: matchValue} - return &corev1.Service{ - TypeMeta: metav1.TypeMeta{ - APIVersion: "v1", - Kind: "Service", - }, - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Labels: withCommonLabels(matchMap), - }, - Spec: corev1.ServiceSpec{ - Selector: matchMap, - }, - } -} diff --git a/pkg/operator/resources/namespaced/controller.go b/pkg/operator/resources/namespaced/controller.go index 22d531a1f..00aa6b878 100644 --- a/pkg/operator/resources/namespaced/controller.go +++ b/pkg/operator/resources/namespaced/controller.go @@ -26,6 +26,8 @@ import ( "k8s.io/apimachinery/pkg/util/intstr" "kubevirt.io/containerized-data-importer/pkg/common" + + utils "kubevirt.io/containerized-data-importer/pkg/operator/resources/utils" ) const ( @@ -56,12 +58,12 @@ func createControllerResources(args *FactoryArgs) []runtime.Object { } func createControllerServiceAccount() *corev1.ServiceAccount { - return createServiceAccount(controllerServiceAccount) + return utils.CreateServiceAccount(controllerServiceAccount) } func createControllerDeployment(repo, controllerImage, importerImage, clonerImage, uploadServerImage, tag, verbosity, pullPolicy string) *appsv1.Deployment { - deployment := createDeployment("cdi-deployment", "app", "containerized-data-importer", controllerServiceAccount, int32(1)) - container := createContainer("cdi-controller", repo, controllerImage, tag, verbosity, corev1.PullPolicy(pullPolicy)) + deployment := utils.CreateDeployment("cdi-deployment", "app", "containerized-data-importer", controllerServiceAccount, int32(1)) + container := utils.CreateContainer("cdi-controller", repo, controllerImage, tag, verbosity, corev1.PullPolicy(pullPolicy)) container.Env = []corev1.EnvVar{ { Name: "IMPORTER_IMAGE", @@ -137,7 +139,7 @@ func createInsecureRegConfigMap() *corev1.ConfigMap { }, ObjectMeta: metav1.ObjectMeta{ Name: common.InsecureRegistryConfigMap, - Labels: withCommonLabels(nil), + Labels: utils.WithCommonLabels(nil), }, } } diff --git a/pkg/operator/resources/namespaced/factory.go b/pkg/operator/resources/namespaced/factory.go index ffeda6423..31436bfc7 100644 --- a/pkg/operator/resources/namespaced/factory.go +++ b/pkg/operator/resources/namespaced/factory.go @@ -24,17 +24,18 @@ import ( // FactoryArgs contains the required parameters to generate all namespaced resources type FactoryArgs struct { - DockerRepo string `required:"true" split_words:"true"` - DockerTag string `required:"true" split_words:"true"` - ControllerImage string `required:"true" split_words:"true"` - ImporterImage string `required:"true" split_words:"true"` - ClonerImage string `required:"true" split_words:"true"` - APIServerImage string `required:"true" envconfig:"apiserver_image"` - UploadProxyImage string `required:"true" split_words:"true"` - UploadServerImage string `required:"true" split_words:"true"` - Verbosity string `required:"true"` - PullPolicy string `required:"true" split_words:"true"` - Namespace string + DockerRepo string `required:"true" split_words:"true"` + DockerTag string `required:"true" split_words:"true"` + ControllerImage string `required:"true" split_words:"true"` + DeployClusterResources string `required:"true" split_words:"true"` + ImporterImage string `required:"true" split_words:"true"` + ClonerImage string `required:"true" split_words:"true"` + APIServerImage string `required:"true" envconfig:"apiserver_image"` + UploadProxyImage string `required:"true" split_words:"true"` + UploadServerImage string `required:"true" split_words:"true"` + Verbosity string `required:"true"` + PullPolicy string `required:"true" split_words:"true"` + Namespace string } type factoryFunc func(*FactoryArgs) []runtime.Object @@ -50,6 +51,16 @@ var factoryFunctions = map[string]factoryFunc{ "uploadproxy": createUploadProxyResources, } +//IsFactoryResource returns true id codeGroupo belolngs to factory functions +func IsFactoryResource(codeGroup string) bool { + for k := range factoryFunctions { + if codeGroup == k { + return true + } + } + return false +} + // GetPrivilegedAccounts return special accounts for OpenShift // TODO should prob break this up into groups like the rest of this stuff func GetPrivilegedAccounts(args *FactoryArgs) []string { diff --git a/pkg/operator/resources/namespaced/uploadproxy.go b/pkg/operator/resources/namespaced/uploadproxy.go index 8c81f3ab7..6a1652192 100644 --- a/pkg/operator/resources/namespaced/uploadproxy.go +++ b/pkg/operator/resources/namespaced/uploadproxy.go @@ -21,6 +21,7 @@ import ( corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/intstr" + utils "kubevirt.io/containerized-data-importer/pkg/operator/resources/utils" ) const ( @@ -35,7 +36,7 @@ func createUploadProxyResources(args *FactoryArgs) []runtime.Object { } func createUploadProxyService() *corev1.Service { - service := createService(uploadProxyResourceName, cdiLabel, uploadProxyResourceName) + service := utils.CreateService(uploadProxyResourceName, cdiLabel, uploadProxyResourceName) service.Spec.Ports = []corev1.ServicePort{ { Port: 443, @@ -50,8 +51,8 @@ func createUploadProxyService() *corev1.Service { } func createUploadProxyDeployment(repo, image, tag, verbosity, pullPolicy string) *appsv1.Deployment { - deployment := createDeployment(uploadProxyResourceName, cdiLabel, uploadProxyResourceName, "", int32(1)) - container := createContainer(uploadProxyResourceName, repo, image, tag, verbosity, corev1.PullPolicy(pullPolicy)) + deployment := utils.CreateDeployment(uploadProxyResourceName, cdiLabel, uploadProxyResourceName, "", int32(1)) + container := utils.CreateContainer(uploadProxyResourceName, repo, image, tag, verbosity, corev1.PullPolicy(pullPolicy)) container.Env = []corev1.EnvVar{ { Name: "APISERVER_PUBLIC_KEY", diff --git a/pkg/operator/resources/operator/components.go b/pkg/operator/resources/operator/components.go new file mode 100644 index 000000000..9405d66e4 --- /dev/null +++ b/pkg/operator/resources/operator/components.go @@ -0,0 +1,103 @@ +/* +Copyright 2018 The CDI Authors. + +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. +*/ + +package operator + +import ( + appsv1 "k8s.io/api/apps/v1" + rbacv1 "k8s.io/api/rbac/v1" + extv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" +) + +const ( + //ControllerImageDefault - defualt value + ControllerImageDefault = "cdi-controller" + //ImporterImageDefault - defualt value + ImporterImageDefault = "cdi-importer" + //ClonerImageDefault - defualt value + ClonerImageDefault = "cdi-cloner" + //APIServerImageDefault - defualt value + APIServerImageDefault = "cdi-apiserver" + //UploadProxyImageDefault - defualt value + UploadProxyImageDefault = "cdi-uploadproxy" + //UploadServerImageDefault - defualt value + UploadServerImageDefault = "cdi-uploadserver" +) + +//CdiImages - images to be provied to cdi operator +type CdiImages struct { + ControllerImage string + ImporterImage string + ClonerImage string + APIServerImage string + UplodaProxyImage string + UplodaServerImage string +} + +//FillDefaults - fill image names with defaults +func (ci *CdiImages) FillDefaults() *CdiImages { + if ci.ControllerImage == "" { + ci.ControllerImage = ControllerImageDefault + } + if ci.ImporterImage == "" { + ci.ImporterImage = ImporterImageDefault + } + if ci.ClonerImage == "" { + ci.ClonerImage = ClonerImageDefault + } + if ci.APIServerImage == "" { + ci.ClonerImage = APIServerImageDefault + } + if ci.UplodaProxyImage == "" { + ci.ClonerImage = UploadProxyImageDefault + } + if ci.UplodaServerImage == "" { + ci.ClonerImage = UploadServerImageDefault + } + + return ci +} + +//NewCdiOperatorDeployment - provides operator deployment spec +func NewCdiOperatorDeployment(namespace string, repository string, tag string, imagePullPolicy string, verbosity string, cdiImages *CdiImages) (*appsv1.Deployment, error) { + name := "cdi-operator" + deployment := createOperatorDeployment( + repository, + namespace, + "true", + name, + cdiImages.ControllerImage, + cdiImages.ImporterImage, + cdiImages.ClonerImage, + cdiImages.APIServerImage, + cdiImages.UplodaProxyImage, + cdiImages.UplodaServerImage, + tag, + verbosity, + imagePullPolicy) + + return deployment, nil +} + +//NewCdiOperatorClusterRole - provides operator clusterRole +func NewCdiOperatorClusterRole() *rbacv1.ClusterRole { + return createOperatorClusterRole(operatorClusterRoleName) +} + +//NewCdiCrd - provides CDI CRD +func NewCdiCrd() *extv1beta1.CustomResourceDefinition { + return createCDIListCRD() +} diff --git a/pkg/operator/resources/operator/factory.go b/pkg/operator/resources/operator/factory.go new file mode 100644 index 000000000..fdfb41084 --- /dev/null +++ b/pkg/operator/resources/operator/factory.go @@ -0,0 +1,116 @@ +/* +Copyright 2018 The CDI Authors. + +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. +*/ + +package operator + +import ( + "fmt" + + appsv1 "k8s.io/api/apps/v1" + rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/runtime" +) + +//FactoryArgs contains the required parameters to generate all cluster-scoped resources +type FactoryArgs struct { + OperatorImage string `required:"true" split_words:"true"` + DockerRepo string `required:"true" split_words:"true"` + DockerTag string `required:"true" split_words:"true"` + DeployClusterResources string `required:"true" split_words:"true"` + ControllerImage string `required:"true" split_words:"true"` + ImporterImage string `required:"true" split_words:"true"` + ClonerImage string `required:"true" split_words:"true"` + APIServerImage string `required:"true" envconfig:"apiserver_image"` + UploadProxyImage string `required:"true" split_words:"true"` + UploadServerImage string `required:"true" split_words:"true"` + Verbosity string `required:"true"` + PullPolicy string `required:"true" split_words:"true"` + Namespace string +} + +type operatorFactoryFunc func(*FactoryArgs) []runtime.Object + +const ( + //OperatorRBAC - operator rbac + OperatorRBAC string = "operator-rbac" + //OperatorDeployment - operator deployment + OperatorDeployment string = "operator-deployment" + //OperatorCdiCRD - operator CRDs + OperatorCdiCRD string = "operator-cdi-crd" + //OperatorConfigMapCR - operartor configmap + OperatorConfigMapCR string = "operator-configmap-cr" +) + +var operatorFactoryFunctions = map[string]operatorFactoryFunc{ + OperatorRBAC: createOperatorClusterRBAC, + OperatorDeployment: createOperatorClusterDeployment, + OperatorCdiCRD: createOperatorCDIClusterResource, + OperatorConfigMapCR: createOperatorConfigMapClusterResource, +} + +//IsFactoryResource returns true id codeGroupo belolngs to factory functions +func IsFactoryResource(codeGroup string) bool { + for k := range operatorFactoryFunctions { + if codeGroup == k { + return true + } + } + return false +} + +//GetOperatorClusterRules returnes operator cluster rules +func GetOperatorClusterRules() *[]rbacv1.PolicyRule { + return getOperatorClusterRules() +} + +//GetOperatorDeploymentSpec returns operator deployment spce +func GetOperatorDeploymentSpec(args *FactoryArgs) *appsv1.DeploymentSpec { + return createOperatorDeploymentSpec(args.DockerRepo, + args.Namespace, + args.DeployClusterResources, + args.OperatorImage, + args.ControllerImage, + args.ImporterImage, + args.ClonerImage, + args.APIServerImage, + args.UploadProxyImage, + args.UploadServerImage, + args.DockerTag, + args.Verbosity, + args.PullPolicy) +} + +// CreateAllOperatorResources creates all cluster-wide resources +func CreateAllOperatorResources(args *FactoryArgs) ([]runtime.Object, error) { + var resources []runtime.Object + for group := range operatorFactoryFunctions { + rs, err := CreateOperatorResourceGroup(group, args) + if err != nil { + return nil, err + } + resources = append(resources, rs...) + } + return resources, nil +} + +// CreateOperatorResourceGroup creates all cluster resources fr a specific group/component +func CreateOperatorResourceGroup(group string, args *FactoryArgs) ([]runtime.Object, error) { + f, ok := operatorFactoryFunctions[group] + if !ok { + return nil, fmt.Errorf("Group %s does not exist", group) + } + return f(args), nil +} diff --git a/pkg/operator/resources/operator/operator.go b/pkg/operator/resources/operator/operator.go new file mode 100644 index 000000000..44ce052b2 --- /dev/null +++ b/pkg/operator/resources/operator/operator.go @@ -0,0 +1,532 @@ +/* +Copyright 2018 The CDI Authors. + +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. +*/ + +package operator + +import ( + "fmt" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + + extv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime" + "kubevirt.io/containerized-data-importer/pkg/common" + cluster "kubevirt.io/containerized-data-importer/pkg/operator/resources/cluster" + utils "kubevirt.io/containerized-data-importer/pkg/operator/resources/utils" +) + +const ( + operatorServiceAccountName = "cdi-operator" + operatorClusterRoleName = "cdi-operator-cluster-role" + operatorNamespacedRoleName = "cdi-operator-role" + privilegedAccountPrefix = "system:serviceaccount" + prometheusLabel = common.PrometheusLabel +) + +func createOperatorResources(args *FactoryArgs) []runtime.Object { + return []runtime.Object{ + createCDIListCRD(), + createOperatorServiceAccount(args.Namespace), + createOperatorClusterRole(operatorClusterRoleName), + createOperatorClusterRoleBinding(args.Namespace), + createOperatorLeaderElectionConfigMap(args.Namespace), + createOperatorDeployment(args.DockerRepo, + args.Namespace, + args.DeployClusterResources, + args.OperatorImage, + args.ControllerImage, + args.ImporterImage, + args.ClonerImage, + args.APIServerImage, + args.UploadProxyImage, + args.UploadServerImage, + args.DockerTag, + args.Verbosity, + args.PullPolicy), + } +} + +func getOperatorClusterRules() *[]rbacv1.PolicyRule { + rules := []rbacv1.PolicyRule{ + { + APIGroups: []string{ + "rbac.authorization.k8s.io", + }, + Resources: []string{ + "roles", + "rolebindings", + "clusterrolebindings", + "clusterroles", + }, + Verbs: []string{ + "*", + }, + }, + { + APIGroups: []string{ + "security.openshift.io", + }, + Resources: []string{ + "securitycontextconstraints", + }, + Verbs: []string{ + "*", + }, + }, + { + APIGroups: []string{ + "", + }, + Resources: []string{ + "serviceaccounts", + "services", + }, + Verbs: []string{ + "*", + }, + }, + { + APIGroups: []string{ + "", + }, + Resources: []string{ + "nodes", + }, + Verbs: []string{ + "get", + "list", + "watch", + "update", + "patch", + }, + }, + { + APIGroups: []string{ + "extensions", + }, + Resources: []string{ + "deployments", + }, + Verbs: []string{ + "*", + }, + }, + { + APIGroups: []string{ + "extensions", + }, + Resources: []string{ + "ingresses", + }, + Verbs: []string{ + "get", + "list", + "watch", + }, + }, + { + APIGroups: []string{ + "", + }, + Resources: []string{ + "configmaps", + }, + Verbs: []string{ + "watch", + "create", + "delete", + "get", + "update", + "patch", + "list", + }, + }, + { + APIGroups: []string{ + "batch", + }, + Resources: []string{ + "jobs", + }, + Verbs: []string{ + "create", + "delete", + "get", + "update", + "patch", + "list", + }, + }, + { + APIGroups: []string{ + "apiextensions.k8s.io", + }, + Resources: []string{ + "customresourcedefinitions", + }, + Verbs: []string{ + "create", + "delete", + "get", + "update", + "patch", + "list", + }, + }, + { + APIGroups: []string{ + "apps", + }, + Resources: []string{ + "deployments", + "daemonstes", + }, + Verbs: []string{ + "create", + "get", + "list", + "delete", + "watch", + "update", + }, + }, + { + APIGroups: []string{ + "admissionregistration.k8s.io", + }, + Resources: []string{ + "validatingwebhookconfigurations", + }, + Verbs: []string{ + "get", + "create", + "update", + }, + }, + { + APIGroups: []string{ + "apiregistration.k8s.io", + }, + Resources: []string{ + "apiservices", + }, + Verbs: []string{ + "get", + "create", + "update", + }, + }, + { + APIGroups: []string{ + "cdi.kubevirt.io", + }, + Resources: []string{ + "*", + }, + Verbs: []string{ + "*", + }, + }, + { + APIGroups: []string{ + "", + }, + Resources: []string{ + "events", + }, + Verbs: []string{ + "create", + "update", + "patch", + }, + }, + { + APIGroups: []string{ + "", + }, + Resources: []string{ + "pods", + "persistentvolumeclaims", + }, + Verbs: []string{ + "get", + "list", + "watch", + "create", + "update", + "patch", + "delete", + }, + }, + { + APIGroups: []string{ + "", + }, + Resources: []string{ + "persistentvolumeclaims/finalizers", + "pods/finalizers", + }, + Verbs: []string{ + "update", + }, + }, + { + APIGroups: []string{ + "", + }, + Resources: []string{ + "services", + }, + Verbs: []string{ + "get", + "list", + "watch", + "create", + "delete", + }, + }, + { + APIGroups: []string{ + "", + }, + Resources: []string{ + "secrets", + }, + Verbs: []string{ + "get", + "list", + "watch", + "create", + }, + }, + { + APIGroups: []string{ + "", + }, + Resources: []string{ + "namespaces", + }, + Verbs: []string{ + "get", + "list", + }, + }, + { + APIGroups: []string{ + "route.openshift.io", + }, + Resources: []string{ + "routes", + }, + Verbs: []string{ + "get", + "list", + "watch", + }, + }, + } + + return &rules +} + +func createOperatorClusterRole(roleName string) *rbacv1.ClusterRole { + clusterRole := cluster.CreateClusterRole(roleName) + clusterRole.Rules = *getOperatorClusterRules() + + return clusterRole +} + +func createOperatorClusterRBAC(args *FactoryArgs) []runtime.Object { + return []runtime.Object{ + createOperatorServiceAccount(args.Namespace), + createOperatorClusterRole(operatorClusterRoleName), + createOperatorClusterRoleBinding(args.Namespace), + } +} + +func createOperatorClusterDeployment(args *FactoryArgs) []runtime.Object { + return []runtime.Object{ + createOperatorDeployment(args.DockerRepo, + args.Namespace, + args.DeployClusterResources, + args.OperatorImage, + args.ControllerImage, + args.ImporterImage, + args.ClonerImage, + args.APIServerImage, + args.UploadProxyImage, + args.UploadServerImage, + args.DockerTag, + args.Verbosity, + args.PullPolicy)} +} + +func createOperatorClusterResources(args *FactoryArgs) []runtime.Object { + return []runtime.Object{ + createCDIListCRD(), + createOperatorLeaderElectionConfigMap(args.Namespace), + } +} + +func createOperatorCDIClusterResource(args *FactoryArgs) []runtime.Object { + return []runtime.Object{ + createCDIListCRD(), + } +} + +func createOperatorConfigMapClusterResource(args *FactoryArgs) []runtime.Object { + return []runtime.Object{ + createOperatorLeaderElectionConfigMap(args.Namespace), + } +} + +func createOperatorClusterRoleBinding(namespace string) *rbacv1.ClusterRoleBinding { + return cluster.CreateClusterRoleBinding(operatorServiceAccountName, operatorClusterRoleName, operatorServiceAccountName, namespace) +} + +func getOperatorPrivilegedAccounts(args *FactoryArgs) []string { + return []string{ + fmt.Sprintf("%s:%s:%s", privilegedAccountPrefix, args.Namespace, operatorServiceAccountName), + } +} + +func createOperatorServiceAccount(namespace string) *corev1.ServiceAccount { + return utils.CreateServiceNamespaceAccount(operatorServiceAccountName, namespace) +} + +func createOperatorLeaderElectionConfigMap(namespace string) *corev1.ConfigMap { + return &corev1.ConfigMap{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "ConfigMap", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "cdi-operator-leader-election-helper", + Namespace: namespace, + Labels: map[string]string{ + "operator.cdi.kubevirt.io": "", + }, + }, + } + +} + +func createCDIListCRD() *extv1beta1.CustomResourceDefinition { + return &extv1beta1.CustomResourceDefinition{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "apiextensions.k8s.io/v1beta1", + Kind: "CustomResourceDefinition", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: "cdis.cdi.kubevirt.io", + Labels: map[string]string{ + "operator.cdi.kubevirt.io": "", + }, + }, + Spec: extv1beta1.CustomResourceDefinitionSpec{ + Group: "cdi.kubevirt.io", + Names: extv1beta1.CustomResourceDefinitionNames{ + Kind: "CDI", + ListKind: "CDIList", + Plural: "cdis", + Singular: "cdi", + }, + Version: "v1alpha1", + Scope: "Cluster", + }, + } +} + +const ( + uploadProxyResourceName = "cdi-uploadproxy" +) + +func createOperatorDeploymentSpec(repo, namespace, deployClusterResources, operatorImage, controllerImage, importerImage, clonerImage, apiServerImage, uploadProxyImage, uploadServerImage, tag, verbosity, pullPolicy string) *appsv1.DeploymentSpec { + spec := utils.CreateOperatorDeploymentSpec("cdi-operator", namespace, "name", "cdi-operator", operatorServiceAccountName, int32(1)) + container := utils.CreatePortsContainer("cdi-operator", repo, operatorImage, tag, verbosity, corev1.PullPolicy(pullPolicy), createPrometheusPorts()) + container.Env = *createOperatorEnvVar(repo, deployClusterResources, operatorImage, controllerImage, importerImage, clonerImage, apiServerImage, uploadProxyImage, uploadServerImage, tag, verbosity, pullPolicy) + spec.Template.Spec.Containers = []corev1.Container{container} + return spec +} + +func createOperatorEnvVar(repo, deployClusterResources, operatorImage, controllerImage, importerImage, clonerImage, apiServerImage, uploadProxyImage, uploadServerImage, tag, verbosity, pullPolicy string) *[]corev1.EnvVar { + return &[]corev1.EnvVar{ + { + Name: "DEPLOY_CLUSTER_RESOURCES", + Value: fmt.Sprintf("%s", deployClusterResources), + }, + { + Name: "DOCKER_REPO", + Value: fmt.Sprintf("%s", repo), + }, + { + Name: "DOCKER_TAG", + Value: fmt.Sprintf("%s", tag), + }, + { + Name: "CONTROLLER_IMAGE", + Value: fmt.Sprintf("%s", controllerImage), + }, + { + Name: "IMPORTER_IMAGE", + Value: fmt.Sprintf("%s", importerImage), + }, + { + Name: "CLONER_IMAGE", + Value: fmt.Sprintf("%s", clonerImage), + }, + { + Name: "APISERVER_IMAGE", + Value: fmt.Sprintf("%s", apiServerImage), + }, + { + Name: "UPLOAD_SERVER_IMAGE", + Value: fmt.Sprintf("%s", uploadServerImage), + }, + { + Name: "UPLOAD_PROXY_IMAGE", + Value: fmt.Sprintf("%s", uploadProxyImage), + }, + { + Name: "VERBOSITY", + Value: verbosity, + }, + { + Name: "PULL_POLICY", + Value: pullPolicy, + }, + } +} + +func createOperatorDeployment(repo, namespace, deployClusterResources, operatorImage, controllerImage, importerImage, clonerImage, apiServerImage, uploadProxyImage, uploadServerImage, tag, verbosity, pullPolicy string) *appsv1.Deployment { + deployment := utils.CreateOperatorDeployment("cdi-operator", namespace, "name", "cdi-operator", operatorServiceAccountName, int32(1)) + container := utils.CreatePortsContainer("cdi-operator", repo, operatorImage, tag, verbosity, corev1.PullPolicy(pullPolicy), createPrometheusPorts()) + container.Env = *createOperatorEnvVar(repo, deployClusterResources, operatorImage, controllerImage, importerImage, clonerImage, apiServerImage, uploadProxyImage, uploadServerImage, tag, verbosity, pullPolicy) + deployment.Spec.Template.Spec.Containers = []corev1.Container{container} + return deployment +} + +func createPrometheusPorts() *[]corev1.ContainerPort { + return &[]corev1.ContainerPort{ + { + Name: "metrics", + ContainerPort: 60000, + }, + } +} diff --git a/pkg/operator/resources/utils/common.go b/pkg/operator/resources/utils/common.go new file mode 100644 index 000000000..618c2e4d1 --- /dev/null +++ b/pkg/operator/resources/utils/common.go @@ -0,0 +1,259 @@ +/* +Copyright 2018 The CDI Authors. + +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. +*/ + +package utils + +import ( + "fmt" + + appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" + rbacv1 "k8s.io/api/rbac/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "kubevirt.io/containerized-data-importer/pkg/common" +) + +const ( + cdiLabel = common.CDIComponentLabel +) + +var commonLabels = map[string]string{ + "cdi.kubevirt.io": "", +} + +var operatorLabels = map[string]string{ + "operator.cdi.kubevirt.io": "", +} + +//WithCommonLabels aggregates common lables +func WithCommonLabels(labels map[string]string) map[string]string { + if labels == nil { + labels = make(map[string]string) + } + + for k, v := range commonLabels { + _, ok := labels[k] + if !ok { + labels[k] = v + } + } + + return labels +} + +//WithOperatorLabels aggregates common lables +func WithOperatorLabels(labels map[string]string) map[string]string { + if labels == nil { + labels = make(map[string]string) + } + + for k, v := range operatorLabels { + _, ok := labels[k] + if !ok { + labels[k] = v + } + } + + return labels +} + +//CreateServiceAccount creates service account +func CreateServiceAccount(name string) *corev1.ServiceAccount { + return &corev1.ServiceAccount{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "ServiceAccount", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Labels: WithCommonLabels(nil), + }, + } +} + +//CreateServiceNamespaceAccount creates service account +func CreateServiceNamespaceAccount(name, namespace string) *corev1.ServiceAccount { + return &corev1.ServiceAccount{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "ServiceAccount", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: namespace, + Labels: map[string]string{ + "operator.cdi.kubevirt.io": "", + }, + }, + } +} + +//CreateRoleBinding creates role binding +func CreateRoleBinding(name, roleRef, serviceAccount, serviceAccountNamespace string) *rbacv1.RoleBinding { + return &rbacv1.RoleBinding{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "rbac.authorization.k8s.io/v1", + Kind: "RoleBinding", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Labels: WithCommonLabels(nil), + }, + RoleRef: rbacv1.RoleRef{ + Kind: "Role", + Name: roleRef, + APIGroup: "rbac.authorization.k8s.io", + }, + Subjects: []rbacv1.Subject{ + { + Kind: "ServiceAccount", + Name: serviceAccount, + Namespace: serviceAccountNamespace, + }, + }, + } +} + +//CreateRole creates role +func CreateRole(name string) *rbacv1.Role { + return &rbacv1.Role{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "rbac.authorization.k8s.io/v1", + Kind: "Role", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Labels: WithCommonLabels(nil), + }, + } +} + +//CreateOperatorDeploymentSpec creates deployment +func CreateOperatorDeploymentSpec(name, namespace, matchKey, matchValue, serviceAccount string, numReplicas int32) *appsv1.DeploymentSpec { + spec := &appsv1.DeploymentSpec{ + Replicas: &numReplicas, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "name": "cdi-operator", + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"name": "cdi-operator"}, + }, + }, + } + + if serviceAccount != "" { + spec.Template.Spec.ServiceAccountName = serviceAccount + } + + return spec +} + +//CreateOperatorDeployment creates deployment +func CreateOperatorDeployment(name, namespace, matchKey, matchValue, serviceAccount string, numReplicas int32) *appsv1.Deployment { + //matchMap := map[string]string{matchKey: matchValue} + deployment := &appsv1.Deployment{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "apps/v1", + Kind: "Deployment", + }, + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{ + "operator.cdi.kubevirt.io": "", + }, + Name: name, + Namespace: namespace, + }, + Spec: *CreateOperatorDeploymentSpec(name, namespace, matchKey, matchValue, serviceAccount, numReplicas), + } + if serviceAccount != "" { + deployment.Spec.Template.Spec.ServiceAccountName = serviceAccount + } + return deployment +} + +//CreateDeployment creates deployment +func CreateDeployment(name, matchKey, matchValue, serviceAccount string, numReplicas int32) *appsv1.Deployment { + matchMap := map[string]string{matchKey: matchValue} + deployment := &appsv1.Deployment{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "apps/v1", + Kind: "Deployment", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Labels: WithCommonLabels(matchMap), + }, + Spec: appsv1.DeploymentSpec{ + Replicas: &numReplicas, + Selector: &metav1.LabelSelector{ + MatchLabels: map[string]string{ + matchKey: matchValue, + }, + }, + Template: corev1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: WithCommonLabels(matchMap), + }, + }, + }, + } + if serviceAccount != "" { + deployment.Spec.Template.Spec.ServiceAccountName = serviceAccount + } + return deployment +} + +//CreatePortsContainer creates container +func CreatePortsContainer(name, repo, image, tag, verbosity string, pullPolicy corev1.PullPolicy, ports *[]corev1.ContainerPort) corev1.Container { + return corev1.Container{ + Name: name, + Image: fmt.Sprintf("%s/%s:%s", repo, image, tag), + Ports: *ports, + ImagePullPolicy: pullPolicy, + } +} + +//CreateContainer creates container +func CreateContainer(name, repo, image, tag, verbosity string, pullPolicy corev1.PullPolicy) corev1.Container { + return corev1.Container{ + Name: name, + Image: fmt.Sprintf("%s/%s:%s", repo, image, tag), + ImagePullPolicy: pullPolicy, + Args: []string{"-v=" + verbosity}, + } +} + +//CreateService creates service +func CreateService(name, matchKey, matchValue string) *corev1.Service { + matchMap := map[string]string{matchKey: matchValue} + return &corev1.Service{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "v1", + Kind: "Service", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Labels: WithCommonLabels(matchMap), + }, + Spec: corev1.ServiceSpec{ + Selector: matchMap, + }, + } +} diff --git a/tools/manifest-generator/manifest-generator.go b/tools/manifest-generator/manifest-generator.go index eed72caaf..c3350b642 100644 --- a/tools/manifest-generator/manifest-generator.go +++ b/tools/manifest-generator/manifest-generator.go @@ -13,50 +13,77 @@ package main import ( + "bufio" + "encoding/base64" "flag" + "io/ioutil" "os" + "path/filepath" + "strings" "text/template" "k8s.io/apimachinery/pkg/runtime" "k8s.io/klog" cdicluster "kubevirt.io/containerized-data-importer/pkg/operator/resources/cluster" + "kubevirt.io/containerized-data-importer/pkg/operator/resources/namespaced" cdinamespaced "kubevirt.io/containerized-data-importer/pkg/operator/resources/namespaced" + cdioperator "kubevirt.io/containerized-data-importer/pkg/operator/resources/operator" +) + +const ( + initialCsvVersion string = "0.0.0" ) type templateData struct { - DockerRepo string - DockerTag string - ControllerImage string - ImporterImage string - ClonerImage string - APIServerImage string - UploadProxyImage string - UploadServerImage string - OperatorImage string - Verbosity string - PullPolicy string - Namespace string + DockerRepo string + DockerTag string + CsvVersion string + ReplacesCsvVersion string + QuayNamespace string + QuayRepository string + OperatorRules string + OperatorDeploymentSpec string + CDILogo string + DeployClusterResources string + OperatorImage string + ControllerImage string + ImporterImage string + ClonerImage string + APIServerImage string + UploadProxyImage string + UploadServerImage string + Verbosity string + PullPolicy string + Namespace string + GeneratedManifests map[string]string } var ( - dockerRepo = flag.String("docker-repo", "", "") - dockertag = flag.String("docker-tag", "", "") - controllerImage = flag.String("controller-image", "", "") - importerImage = flag.String("importer-image", "", "") - clonerImage = flag.String("cloner-image", "", "") - apiServerImage = flag.String("apiserver-image", "", "") - uploadProxyImage = flag.String("uploadproxy-image", "", "") - uploadServerImage = flag.String("uploadserver-image", "", "") - operatorImage = flag.String("operator-image", "", "") - verbosity = flag.String("verbosity", "1", "") - pullPolicy = flag.String("pull-policy", "", "") - namespace = flag.String("namespace", "", "") + dockerRepo = flag.String("docker-repo", "", "") + dockertag = flag.String("docker-tag", "", "") + csvVersion = flag.String("csv-version", "", "") + cdiLogoPath = flag.String("cdi-logo-path", "", "") + genManifestsPath = flag.String("generated-manifests-path", "", "") + quayNamespace = flag.String("quay-namespace", "", "") + quayRepository = flag.String("quay-repository", "", "") + deployClusterResources = flag.String("deploy-cluster-resources", "", "") + operatorImage = flag.String("operator-image", "", "") + controllerImage = flag.String("controller-image", "", "") + importerImage = flag.String("importer-image", "", "") + clonerImage = flag.String("cloner-image", "", "") + apiServerImage = flag.String("apiserver-image", "", "") + uploadProxyImage = flag.String("uploadproxy-image", "", "") + uploadServerImage = flag.String("uploadserver-image", "", "") + verbosity = flag.String("verbosity", "1", "") + pullPolicy = flag.String("pull-policy", "", "") + namespace = flag.String("namespace", "", "") ) func main() { templFile := flag.String("template", "", "") codeGroup := flag.String("code-group", "everything", "") flag.Parse() + klogFlags := flag.NewFlagSet("klog", flag.ExitOnError) klog.InitFlags(klogFlags) flag.CommandLine.VisitAll(func(f1 *flag.Flag) { @@ -75,20 +102,86 @@ func main() { generateFromCode(*codeGroup) } +func getOperatorRules() string { + rules := *cdioperator.GetOperatorClusterRules() + + writer := strings.Builder{} + for _, rule := range rules { + err := MarshallObject(rule, &writer) + if err != nil { + panic(err) + } + } + return fixResourceString(writer.String(), 14) +} + +func getOperatorDeploymentSpec() string { + args := &cdioperator.FactoryArgs{ + Verbosity: *verbosity, + DockerRepo: *dockerRepo, + DockerTag: *dockertag, + DeployClusterResources: *deployClusterResources, + OperatorImage: *operatorImage, + ControllerImage: *controllerImage, + ImporterImage: *importerImage, + ClonerImage: *clonerImage, + APIServerImage: *apiServerImage, + UploadProxyImage: *uploadProxyImage, + UploadServerImage: *uploadServerImage, + PullPolicy: *pullPolicy, + Namespace: *namespace, + } + + spec := cdioperator.GetOperatorDeploymentSpec(args) + + writer := strings.Builder{} + + err := MarshallObject(spec, &writer) + if err != nil { + panic(err) + } + + return fixResourceString(writer.String(), 14) +} + +func fixResourceString(in string, indention int) string { + out := strings.Builder{} + scanner := bufio.NewScanner(strings.NewReader(in)) + for scanner.Scan() { + line := scanner.Text() + // remove separator lines + if !strings.HasPrefix(line, "---") { + // indent so that it fits into the manifest + // spaces is is indention - 2, because we want to have 2 spaces less for being able to start an array + spaces := strings.Repeat(" ", indention-2) + if strings.HasPrefix(line, "apiGroups") { + // spaces + array start + out.WriteString(spaces + "- " + line + "\n") + } else { + // 2 more spaces + out.WriteString(spaces + " " + line + "\n") + } + } + } + return out.String() +} + func generateFromFile(templFile string) { data := &templateData{ - Verbosity: *verbosity, - DockerRepo: *dockerRepo, - DockerTag: *dockertag, - ControllerImage: *controllerImage, - ImporterImage: *importerImage, - ClonerImage: *clonerImage, - APIServerImage: *apiServerImage, - UploadProxyImage: *uploadProxyImage, - UploadServerImage: *uploadServerImage, - OperatorImage: *operatorImage, - PullPolicy: *pullPolicy, - Namespace: *namespace, + Verbosity: *verbosity, + DockerRepo: *dockerRepo, + DockerTag: *dockertag, + CsvVersion: *csvVersion, + DeployClusterResources: *deployClusterResources, + OperatorImage: *operatorImage, + ControllerImage: *controllerImage, + ImporterImage: *importerImage, + ClonerImage: *clonerImage, + APIServerImage: *apiServerImage, + UploadProxyImage: *uploadProxyImage, + UploadServerImage: *uploadServerImage, + PullPolicy: *pullPolicy, + Namespace: *namespace, } file, err := os.OpenFile(templFile, os.O_RDONLY, 0) @@ -97,6 +190,40 @@ func generateFromFile(templFile string) { } defer file.Close() + if strings.Contains(*csvVersion, initialCsvVersion) { + data.ReplacesCsvVersion = "" + } else { + klog.Fatalf("Need to implement CSV upgrade to set ReplacesVersion in CSV when CSVVersion is greater than %s\n", initialCsvVersion) + } + + data.QuayRepository = *quayRepository + data.QuayNamespace = *quayNamespace + data.OperatorRules = getOperatorRules() + data.OperatorDeploymentSpec = getOperatorDeploymentSpec() + data.CDILogo = getCdiLogo(*cdiLogoPath) + + // Read generated manifests in order to populate templated manifest + genDir := *genManifestsPath + + data.GeneratedManifests = make(map[string]string) + + manifests, err := ioutil.ReadDir(genDir) + if err != nil { + klog.Fatalf("Failed to read directory %s: %v\n", templFile, err) + } + + for _, manifest := range manifests { + if manifest.IsDir() { + continue + } + b, err := ioutil.ReadFile(filepath.Join(genDir, manifest.Name())) + if err != nil { + klog.Fatalf("Failed to read file %s: %v\n", templFile, err) + } + + data.GeneratedManifests[manifest.Name()] = string(b) + } + tmpl := template.Must(template.ParseFiles(templFile)) err = tmpl.Execute(os.Stdout, data) if err != nil { @@ -104,37 +231,117 @@ func generateFromFile(templFile string) { } } +func getCdiLogo(path string) string { + file, err := os.Open(path) + if err != nil { + klog.Fatalf("Error retrieving cdi logo file: %v\n", err) + } + + // Read entire file into byte slice. + reader := bufio.NewReader(file) + content, err := ioutil.ReadAll(reader) + if err != nil { + klog.Fatalf("Error reading cdi logo file: %v\n", err) + } + + // Encode as base64. + encoded := base64.StdEncoding.EncodeToString(content) + return encoded +} + +const ( + //ClusterResource - cluster resources + ClusterResource string = "cluster" + //OperatorResource - operator resources + OperatorResource string = "operator" + //NamespaceResource - namespace resources + NamespaceResource string = "namespaces" +) + +type resourceGet func(string) ([]runtime.Object, error) +type resourcetype func(string) bool +type resourceTuple struct { + resourcetype resourcetype + resourceGet resourceGet +} + +var resourcesTable = map[string]resourceTuple{ + ClusterResource: {cdicluster.IsFactoryResource, getClusterResources}, + NamespaceResource: {namespaced.IsFactoryResource, getNamespacedResources}, + OperatorResource: {cdioperator.IsFactoryResource, getOperatorClusterResources}, +} + func generateFromCode(codeGroup string) { var resources []runtime.Object - crs, err := getClusterResources(codeGroup) - if err != nil { - klog.Fatalf("Error getting cluster resources: %v\n", err) - } - - resources = append(resources, crs...) - - nsrs, err := getNamespacedResources(codeGroup) - if err != nil { - klog.Fatalf("Error getting namespaced resources: %v\n", err) - } - - resources = append(resources, nsrs...) + for r, disptach := range resourcesTable { + if disptach.resourcetype(codeGroup) { + crs, err := disptach.resourceGet(codeGroup) + if err != nil { + klog.Fatalf("Error getting %s resources: %v\n", r, err) + } + resources = append(resources, crs...) + } //of codeGroup matches resource then get it + } //iterate through all resources for _, resource := range resources { - err = MarshallObject(resource, os.Stdout) + err := MarshallObject(resource, os.Stdout) if err != nil { klog.Fatalf("Error marshalling resource: %v\n", err) } } } -func getClusterResources(codeGroup string) ([]runtime.Object, error) { - args := &cdicluster.FactoryArgs{ - Namespace: *namespace, +const ( + //ClusterResourcesCodeGroupEverything - generate all cluster resources + ClusterResourcesCodeGroupEverything string = "cluster-everything" + //NamespaceResourcesCodeGroupEverything - generate all namespace resources + NamespaceResourcesCodeGroupEverything string = "namespace-everything" + //ClusterResourcesCodeOperatorGroupEverything - generate all operator resources + ClusterResourcesCodeOperatorGroupEverything string = "operator-everything" +) + +func getOperatorClusterResources(codeGroup string) ([]runtime.Object, error) { + args := &cdioperator.FactoryArgs{ + Verbosity: *verbosity, + DockerRepo: *dockerRepo, + DockerTag: *dockertag, + DeployClusterResources: *deployClusterResources, + OperatorImage: *operatorImage, + ControllerImage: *controllerImage, + ImporterImage: *importerImage, + ClonerImage: *clonerImage, + APIServerImage: *apiServerImage, + UploadProxyImage: *uploadProxyImage, + UploadServerImage: *uploadServerImage, + PullPolicy: *pullPolicy, + Namespace: *namespace, } - if codeGroup == "everything" { + if codeGroup == ClusterResourcesCodeOperatorGroupEverything { + return cdioperator.CreateAllOperatorResources(args) + } + + return cdioperator.CreateOperatorResourceGroup(codeGroup, args) +} + +func getClusterResources(codeGroup string) ([]runtime.Object, error) { + args := &cdicluster.FactoryArgs{ + Verbosity: *verbosity, + DockerRepo: *dockerRepo, + DockerTag: *dockertag, + DeployClusterResources: *deployClusterResources, + ControllerImage: *controllerImage, + ImporterImage: *importerImage, + ClonerImage: *clonerImage, + APIServerImage: *apiServerImage, + UploadProxyImage: *uploadProxyImage, + UploadServerImage: *uploadServerImage, + PullPolicy: *pullPolicy, + Namespace: *namespace, + } + + if codeGroup == ClusterResourcesCodeGroupEverything { return cdicluster.CreateAllResources(args) } @@ -156,7 +363,7 @@ func getNamespacedResources(codeGroup string) ([]runtime.Object, error) { Namespace: *namespace, } - if codeGroup == "everything" { + if codeGroup == NamespaceResourcesCodeGroupEverything { return cdinamespaced.CreateAllResources(args) }