From 7846284a2b33c7b9c5c36ecce1e51c84ecbfa551 Mon Sep 17 00:00:00 2001 From: Dale Haiducek <19750917+dhaiducek@users.noreply.github.com> Date: Tue, 7 Mar 2023 15:31:01 -0500 Subject: [PATCH 1/5] Update go-yaml ref: https://issues.redhat.com/browse/ACM-3042 Signed-off-by: Dale Haiducek <19750917+dhaiducek@users.noreply.github.com> (cherry picked from commit 7208f75f94a81cc3b57b6c3e8aac05e9e1629ac7) --- go.mod | 3 ++- go.sum | 8 -------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index c9fd94a4..8fbfc111 100644 --- a/go.mod +++ b/go.mod @@ -23,6 +23,7 @@ require ( k8s.io/kubectl v0.23.9 open-cluster-management.io/addon-framework v0.3.0 sigs.k8s.io/controller-runtime v0.11.2 + sigs.k8s.io/yaml v1.3.0 ) require ( @@ -96,12 +97,12 @@ require ( open-cluster-management.io/api v0.6.1-0.20220208144021-3297cac74dc5 // indirect sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.1 // indirect - sigs.k8s.io/yaml v1.3.0 // indirect ) replace ( golang.org/x/crypto => golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // CVE-2021-43565 golang.org/x/text => golang.org/x/text v0.3.8 // CVE-2022-32149 + gopkg.in/yaml.v2 => gopkg.in/yaml.v2 v2.4.0 // CVE-2022-3064 k8s.io/client-go => k8s.io/client-go v0.23.9 open-cluster-management.io/governance-policy-propagator => github.com/stolostron/governance-policy-propagator v0.0.0-20220727212642-86a318ab17cf open-cluster-management.io/multicloud-operators-subscription => github.com/stolostron/multicloud-operators-subscription v1.2.4-0-20211122-7277a37.0.20220727170504-931d9002a21f diff --git a/go.sum b/go.sum index 8af9dd1b..ee5d1b5a 100644 --- a/go.sum +++ b/go.sum @@ -1074,14 +1074,6 @@ gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= From 8f5165ec2eb7fec0d0e112d7f768aebd27c12af8 Mon Sep 17 00:00:00 2001 From: Dale Haiducek <19750917+dhaiducek@users.noreply.github.com> Date: Thu, 16 Mar 2023 16:07:37 -0400 Subject: [PATCH 2/5] Sync the common Makefile ref: https://issues.redhat.com/browse/ACM-3321 Signed-off-by: Dale Haiducek <19750917+dhaiducek@users.noreply.github.com> (cherry picked from commit 6e9bc9779ee512b2015e8f1065ed7d5a92192f0e) Signed-off-by: Justin Kulikauskas --- .copyrightignore | 13 -- Makefile | 164 ++------------- build/common/Makefile.common.mk | 195 +++++++++++++----- build/common/config/.golangci.yml | 4 - build/common/scripts/config_docker.sh | 28 --- build/common/scripts/install-operator-sdk.sh | 26 --- build/common/scripts/lint_copyright_banner.sh | 34 --- controllers/configurationpolicy_controller.go | 1 - docs/adoption_guide.md | 53 ----- docs/development.md | 50 ----- 10 files changed, 166 insertions(+), 402 deletions(-) delete mode 100644 .copyrightignore delete mode 100755 build/common/scripts/config_docker.sh delete mode 100755 build/common/scripts/install-operator-sdk.sh delete mode 100755 build/common/scripts/lint_copyright_banner.sh delete mode 100644 docs/adoption_guide.md delete mode 100644 docs/development.md diff --git a/.copyrightignore b/.copyrightignore deleted file mode 100644 index eeab86c6..00000000 --- a/.copyrightignore +++ /dev/null @@ -1,13 +0,0 @@ -#file extensions to ignore -.copyrightignore -.yml -.yaml -.properties -.openapi.go -# directories to ingore -operator-sdk/* -build-harness/* -build-harness-extensions/* -vendor -vbh -build \ No newline at end of file diff --git a/Makefile b/Makefile index 889c11eb..98cdced0 100644 --- a/Makefile +++ b/Makefile @@ -15,19 +15,12 @@ PWD := $(shell pwd) LOCAL_BIN ?= $(PWD)/bin - -# Keep an existing GOPATH, make a private one if it is undefined -GOPATH_DEFAULT := $(PWD)/.go -export GOPATH ?= $(GOPATH_DEFAULT) -GOBIN_DEFAULT := $(GOPATH)/bin -export GOBIN ?= $(GOBIN_DEFAULT) -export PATH := $(LOCAL_BIN):$(GOBIN):$(PATH) +export PATH := $(LOCAL_BIN):$(PATH) GOARCH = $(shell go env GOARCH) GOOS = $(shell go env GOOS) TESTARGS_DEFAULT := -v -export TESTARGS ?= $(TESTARGS_DEFAULT) -VERSION ?= $(shell cat COMPONENT_VERSION 2> /dev/null) -IMAGE_NAME_AND_VERSION ?= $(REGISTRY)/$(IMG) +TESTARGS ?= $(TESTARGS_DEFAULT) +CONTROLLER_NAME = $(shell cat COMPONENT_NAME 2> /dev/null) CONTROLLER_NAMESPACE ?= open-cluster-management-agent-addon # Handle KinD configuration MANAGED_CLUSTER_SUFFIX ?= @@ -36,125 +29,43 @@ WATCH_NAMESPACE ?= $(MANAGED_CLUSTER_NAME) KIND_NAME ?= test-$(MANAGED_CLUSTER_NAME) KIND_CLUSTER_NAME ?= kind-$(KIND_NAME) KIND_NAMESPACE ?= $(CONTROLLER_NAMESPACE) -KIND_VERSION ?= latest -# Set the Kind version tag -ifeq ($(KIND_VERSION), minimum) - KIND_ARGS = --image kindest/node:v1.19.16 -else ifneq ($(KIND_VERSION), latest) - KIND_ARGS = --image kindest/node:$(KIND_VERSION) -else - KIND_ARGS = -endif # Test coverage threshold export COVERAGE_MIN ?= 75 COVERAGE_E2E_OUT ?= coverage_e2e.out # Image URL to use all building/pushing image targets; # Use your own docker registry and image name for dev/test by overridding the IMG and REGISTRY environment variable. -IMG ?= $(shell cat COMPONENT_NAME 2> /dev/null) +IMG ?= $(CONTROLLER_NAME) REGISTRY ?= quay.io/stolostron TAG ?= latest - -# Handle base64 OS differences -OS = $(shell uname -s | tr '[:upper:]' '[:lower:]') -BASE64 = base64 -w 0 -ifeq ($(OS), darwin) - BASE64 = base64 -endif - -# go-get-tool will 'go install' any package $1 and install it to LOCAL_BIN. -define go-get-tool -@set -e ;\ -echo "Checking installation of $(1)" ;\ -GOBIN=$(LOCAL_BIN) go install $(1) -endef +IMAGE_NAME_AND_VERSION ?= $(REGISTRY)/$(IMG) include build/common/Makefile.common.mk ############################################################ -# work section +# Lint ############################################################ -$(GOBIN): - @mkdir -p $(GOBIN) - -$(LOCAL_BIN): - @mkdir -p $(LOCAL_BIN) - -############################################################ -# format section -############################################################ - -.PHONY: fmt-dependencies -fmt-dependencies: - $(call go-get-tool,github.com/daixiang0/gci@v0.2.9) - $(call go-get-tool,mvdan.cc/gofumpt@v0.2.0) +.PHONY: lint +lint: .PHONY: fmt -fmt: fmt-dependencies - find . -not \( -path "./.go" -prune \) -name "*.go" | xargs gofmt -s -w - find . -not \( -path "./.go" -prune \) -name "*.go" | xargs gci -w -local "$(shell cat go.mod | head -1 | cut -d " " -f 2)" - find . -not \( -path "./.go" -prune \) -name "*.go" | xargs gofumpt -l -w - -############################################################ -# check section -############################################################ - -.PHONY: lint-dependencies -lint-dependencies: - $(call go-get-tool,github.com/golangci/golangci-lint/cmd/golangci-lint@v1.46.2) - -.PHONY: check -check: lint - -# All available linters: lint-dockerfiles lint-scripts lint-yaml lint-copyright-banner lint-go lint-python lint-helm lint-markdown lint-sass lint-typescript lint-protos -# Default value will run all linters, override these make target with your requirements: -# eg: lint: lint-go lint-yaml -.PHONY: lint -lint: lint-dependencies lint-all +fmt: ############################################################ # test section ############################################################ -GOSEC = $(LOCAL_BIN)/gosec -KUBEBUILDER = $(LOCAL_BIN)/kubebuilder -KBVERSION = 3.2.0 -K8S_VERSION = 1.21.2 .PHONY: test -test: test-dependencies +test: kubebuilder-dependencies kubebuilder KUBEBUILDER_ASSETS=$(LOCAL_BIN) go test $(TESTARGS) `go list ./... | grep -v test/e2e` .PHONY: test-coverage test-coverage: TESTARGS = -json -cover -covermode=atomic -coverprofile=coverage_unit.out test-coverage: test -.PHONY: test-dependencies -test-dependencies: kubebuilder-dependencies kubebuilder - -.PHONY: kubebuilder -kubebuilder: - @if [ "$$($(KUBEBUILDER) version 2>/dev/null | grep -o KubeBuilderVersion:\"[0-9]*\.[0-9]\.[0-9]*\")" != "KubeBuilderVersion:\"$(KBVERSION)\"" ]; then \ - echo "Installing Kubebuilder"; \ - curl -L https://github.com/kubernetes-sigs/kubebuilder/releases/download/v$(KBVERSION)/kubebuilder_$(GOOS)_$(GOARCH) -o $(KUBEBUILDER); \ - chmod +x $(KUBEBUILDER); \ - fi - -.PHONY: kubebuilder-dependencies -kubebuilder-dependencies: $(LOCAL_BIN) - @if [ ! -f $(LOCAL_BIN)/etcd ] || [ ! -f $(LOCAL_BIN)/kube-apiserver ] || [ ! -f $(LOCAL_BIN)/kubectl ] || \ - [ "$$($(KUBEBUILDER) version 2>/dev/null | grep -o KubeBuilderVersion:\"[0-9]*\.[0-9]\.[0-9]*\")" != "KubeBuilderVersion:\"$(KBVERSION)\"" ]; then \ - echo "Installing envtest Kubebuilder assets"; \ - curl -L "https://go.kubebuilder.io/test-tools/$(K8S_VERSION)/$(GOOS)/$(GOARCH)" | tar xz --strip-components=2 -C $(LOCAL_BIN); \ - fi - -.PHONY: gosec -gosec: - $(call go-get-tool,github.com/securego/gosec/v2/cmd/gosec@v2.9.6) - .PHONY: gosec-scan -gosec-scan: gosec - $(GOSEC) -fmt sonarqube -out gosec.json -no-fail -exclude-dir=.go ./... +gosec-scan: ############################################################ # build section @@ -177,7 +88,6 @@ build-images: @docker build -t ${IMAGE_NAME_AND_VERSION} -f build/Dockerfile . @docker tag ${IMAGE_NAME_AND_VERSION} $(REGISTRY)/$(IMG):$(TAG) - # Deploy controller in the configured Kubernetes cluster in ~/.kube/config .PHONY: deploy deploy: generate-operator-yaml @@ -205,14 +115,12 @@ clean: -rm build/_output/bin/* -rm coverage*.out -rm report*.json - -rm kubeconfig_managed* + -rm kubeconfig_* -rm -r vendor/ ############################################################ # Generate manifests ############################################################ -CONTROLLER_GEN = $(LOCAL_BIN)/controller-gen -KUSTOMIZE = $(LOCAL_BIN)/kustomize CRD_OPTIONS ?= "crd:trivialVersions=true,preserveUnknownFields=false" .PHONY: manifests @@ -231,14 +139,6 @@ generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and generate-operator-yaml: manifests $(KUSTOMIZE) build deploy/manager > deploy/operator.yaml -.PHONY: controller-gen -controller-gen: ## Download controller-gen locally if necessary. - $(call go-get-tool,sigs.k8s.io/controller-tools/cmd/controller-gen@v0.6.1) - -.PHONY: kustomize -kustomize: ## Download kustomize locally if necessary. - $(call go-get-tool,sigs.k8s.io/kustomize/kustomize/v4@v4.5.4) - ############################################################ # e2e test section ############################################################ @@ -248,6 +148,7 @@ GINKGO = $(LOCAL_BIN)/ginkgo kind-bootstrap-cluster: kind-bootstrap-cluster-dev kind-deploy-controller .PHONY: kind-bootstrap-cluster-dev +kind-bootstrap-cluster-dev: CLUSTER_NAME = $(MANAGED_CLUSTER_NAME) kind-bootstrap-cluster-dev: kind-create-cluster install-crds kind-controller-kubeconfig .PHONY: kind-deploy-controller @@ -257,7 +158,7 @@ kind-deploy-controller: generate-operator-yaml install-resources deploy deploy-controller: kind-deploy-controller .PHONY: kind-deploy-controller-dev -kind-deploy-controller-dev: kind-deploy-controller +kind-deploy-controller-dev: kind-deploy-controller build-images @echo Pushing image to KinD cluster kind load docker-image $(REGISTRY)/$(IMG):$(TAG) --name $(KIND_NAME) @echo "Patch deployment image" @@ -268,26 +169,10 @@ kind-deploy-controller-dev: kind-deploy-controller # Specify KIND_VERSION to indicate the version tag of the KinD image .PHONY: kind-create-cluster kind-create-cluster: - # ensuring cluster $(KIND_NAME) - -kind create cluster --name $(KIND_NAME) $(KIND_ARGS) - kubectl config use-context $(KIND_CLUSTER_NAME) - kind get kubeconfig --name $(KIND_NAME) > kubeconfig_$(MANAGED_CLUSTER_NAME)_e2e - -.PHONY: kind-controller-kubeconfig -kind-controller-kubeconfig: install-resources - kubectl -n $(KIND_NAMESPACE) apply -f test/resources/e2e_controller_secret.yaml - -rm kubeconfig_$(MANAGED_CLUSTER_NAME) - @kubectl config set-cluster $(KIND_CLUSTER_NAME) --kubeconfig=$(PWD)/kubeconfig_$(MANAGED_CLUSTER_NAME) \ - --server=$(shell kubectl config view --minify -o jsonpath='{.clusters[].cluster.server}' --kubeconfig=kubeconfig_$(MANAGED_CLUSTER_NAME)_e2e) \ - --insecure-skip-tls-verify=true - @kubectl config set-credentials $(KIND_CLUSTER_NAME) --kubeconfig=$(PWD)/kubeconfig_$(MANAGED_CLUSTER_NAME) \ - --token=$$(kubectl get secret -n $(KIND_NAMESPACE) config-policy-controller -o jsonpath='{.data.token}' --kubeconfig=$(PWD)/kubeconfig_$(MANAGED_CLUSTER_NAME)_e2e | $(BASE64) --decode) - @kubectl config set-context $(KIND_CLUSTER_NAME) --kubeconfig=$(PWD)/kubeconfig_$(MANAGED_CLUSTER_NAME) \ - --user=$(KIND_CLUSTER_NAME) --cluster=$(KIND_CLUSTER_NAME) - @kubectl config use-context $(KIND_CLUSTER_NAME) --kubeconfig=$(PWD)/kubeconfig_$(MANAGED_CLUSTER_NAME) .PHONY: kind-additional-cluster kind-additional-cluster: MANAGED_CLUSTER_SUFFIX = 2 +kind-additional-cluster: CLUSTER_NAME = $(MANAGED_CLUSTER_NAME) kind-additional-cluster: kind-create-cluster kind-controller-kubeconfig .PHONY: kind-delete-cluster @@ -296,7 +181,7 @@ kind-delete-cluster: -kind delete cluster --name $(KIND_NAME)2 .PHONY: kind-tests -kind-tests: kind-delete-cluster kind-bootstrap-cluster-dev build-images kind-deploy-controller-dev e2e-test +kind-tests: kind-delete-cluster kind-bootstrap-cluster-dev kind-deploy-controller-dev e2e-test .PHONY: install-crds install-crds: @@ -318,17 +203,6 @@ install-resources: kubectl apply -k deploy/rbac kubectl apply -f deploy/manager/service-account.yaml -n $(KIND_NAMESPACE) -.PHONY: kind-ensure-sa -kind-ensure-sa: - @KUBECONFIG_TOKEN="$$(kubectl config view --raw -o jsonpath='{.users[].user.token}')"; \ - KUBECONFIG_USER="$$(echo "$${KUBECONFIG_TOKEN}" | jq -rR 'split(".") | .[1] | select(. != null) | @base64d | fromjson | .sub')"; \ - echo "Kubeconfig user detected from token: $${KUBECONFIG_USER}"; \ - [ "$${KUBECONFIG_USER}" = "system:serviceaccount:$(KIND_NAMESPACE):config-policy-controller" ] - -.PHONY: e2e-dependencies -e2e-dependencies: - $(call go-get-tool,github.com/onsi/ginkgo/v2/ginkgo@$(shell awk '/github.com\/onsi\/ginkgo\/v2/ {print $$2}' go.mod)) - .PHONY: e2e-test e2e-test: e2e-dependencies $(GINKGO) -v --fail-fast --slow-spec-threshold=10s $(E2E_TEST_ARGS) test/e2e @@ -369,12 +243,8 @@ e2e-debug: ############################################################ # test coverage ############################################################ -GOCOVMERGE = $(LOCAL_BIN)/gocovmerge -.PHONY: coverage-dependencies -coverage-dependencies: - $(call go-get-tool,github.com/wadey/gocovmerge@v0.0.0-20160331181800-b5bfa59ec0ad) - COVERAGE_FILE = coverage.out + .PHONY: coverage-merge coverage-merge: coverage-dependencies @echo Merging the coverage reports into $(COVERAGE_FILE) diff --git a/build/common/Makefile.common.mk b/build/common/Makefile.common.mk index a6e4c3b1..4e242a8b 100644 --- a/build/common/Makefile.common.mk +++ b/build/common/Makefile.common.mk @@ -1,63 +1,166 @@ -# Copyright 2019 The Kubernetes 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. - -############################################################ -# GKE section -############################################################ -PROJECT ?= oceanic-guard-191815 -ZONE ?= us-west1-a -CLUSTER ?= prow - -activate-serviceaccount: -ifdef GOOGLE_APPLICATION_CREDENTIALS - gcloud auth activate-service-account --key-file="$(GOOGLE_APPLICATION_CREDENTIALS)" +# Copyright (c) 2022 Red Hat, Inc. +# Copyright Contributors to the Open Cluster Management project + +LOCAL_BIN ?= $(error LOCAL_BIN is not set.) +ifneq ($(findstring $(LOCAL_BIN), $(PATH)), $(LOCAL_BIN)) + $(error LOCAL_BIN is not in PATH.) +endif + +# go-get-tool will 'go install' any package $1 and install it to LOCAL_BIN. +define go-get-tool + @set -e ;\ + echo "Checking installation of $(1)" ;\ + GOBIN=$(LOCAL_BIN) go install $(1) +endef + +# Handle base64 OS differences +OS = $(shell uname -s | tr '[:upper:]' '[:lower:]') +BASE64 = base64 -w 0 +ifeq ($(OS), darwin) + BASE64 = base64 endif -get-cluster-credentials: activate-serviceaccount - gcloud container clusters get-credentials "$(CLUSTER)" --project="$(PROJECT)" --zone="$(ZONE)" +############################################################ +# Work +############################################################ + +$(LOCAL_BIN): + @mkdir -p $(LOCAL_BIN) + +############################################################ +# Generate manifests +############################################################ +CONTROLLER_GEN = $(LOCAL_BIN)/controller-gen +KUSTOMIZE = $(LOCAL_BIN)/kustomize -config-docker: get-cluster-credentials - @build/common/scripts/config_docker.sh +.PHONY: controller-gen +controller-gen: ## Download controller-gen locally if necessary. + $(call go-get-tool,sigs.k8s.io/controller-tools/cmd/controller-gen@v0.6.1) +.PHONY: kustomize +kustomize: ## Download kustomize locally if necessary. + $(call go-get-tool,sigs.k8s.io/kustomize/kustomize/v4@v4.5.4) + +############################################################ +# Lint +############################################################ FINDFILES=find . \( -path ./.git -o -path ./.github -o -path ./.go \) -prune -o -type f XARGS = xargs -0 ${XARGS_FLAGS} CLEANXARGS = xargs ${XARGS_FLAGS} +.PHONY: lint +lint: lint-dependencies lint-yaml lint-go -lint-dockerfiles: - @${FINDFILES} -name 'Dockerfile*' -print0 | ${XARGS} hadolint -c ./build/common/config/.hadolint.yml - -lint-scripts: - @${FINDFILES} -name '*.sh' -print0 | ${XARGS} shellcheck -e SC2001 +.PHONY: lint-dependencies +lint-dependencies: + $(call go-get-tool,github.com/golangci/golangci-lint/cmd/golangci-lint@v1.46.2) +.PHONY: lint-yaml lint-yaml: - @${FINDFILES} \( -name '*.yml' -o -name '*.yaml' \) -print0 | ${XARGS} grep -L -e "{{" | ${CLEANXARGS} yamllint -c ./build/common/config/.yamllint.yml - -lint-copyright-banner: - @${FINDFILES} \( -name '*.go' -o -name '*.cc' -o -name '*.h' -o -name '*.proto' -o -name '*.py' -o -name '*.sh' \) \( ! \( -name '*.gen.go' -o -name '*.pb.go' -o -name '*_pb2.py' \) \) -print0 |\ - ${XARGS} build/common/scripts/lint_copyright_banner.sh + # Linting YAML + @$(FINDFILES) \( -name '*.yml' -o -name '*.yaml' \) -print0 | $(XARGS) grep -L -e "{{" | $(CLEANXARGS) yamllint -c ./build/common/config/.yamllint.yml +.PHONY: lint-go lint-go: - @${FINDFILES} -name '*.go' \( ! \( -name '*.gen.go' -o -name '*.pb.go' \) \) -print0 | ${XARGS} build/common/scripts/lint_go.sh + # Linting Golang + @$(FINDFILES) -name '*.go' \( ! \( -name '*.gen.go' -o -name '*.pb.go' \) \) -print0 | $(XARGS) build/common/scripts/lint_go.sh -lint-markdown: - @${FINDFILES} -name '*.md' -print0 | ${XARGS} mdl --ignore-front-matter --style build/common/config/mdl.rb - @${FINDFILES} -name '*.md' -print0 | ${XARGS} awesome_bot --skip-save-results --allow_ssl --allow-timeout --allow-dupe --allow-redirect --white-list ${MARKDOWN_LINT_WHITELIST} +.PHONY: fmt-dependencies +fmt-dependencies: + $(call go-get-tool,github.com/daixiang0/gci@v0.10.1) + $(call go-get-tool,mvdan.cc/gofumpt@v0.4.0) -lint-all: lint-yaml lint-go +.PHONY: fmt +fmt: fmt-dependencies + find . -not \( -path "./.go" -prune \) -name "*.go" | xargs gofmt -s -w + find . -not \( -path "./.go" -prune \) -name "*.go" | xargs gofumpt -l -w -format-go: - @${FINDFILES} -name '*.go' \( ! \( -name '*.gen.go' -o -name '*.pb.go' \) \) -print0 | ${XARGS} goimports -w -local "github.com/stolostron" +############################################################ +# Unit Test +############################################################ +KUBEBUILDER = $(LOCAL_BIN)/kubebuilder +KBVERSION = 3.9.1 +K8S_VERSION = 1.26.1 +GOSEC = $(LOCAL_BIN)/gosec + +.PHONY: kubebuilder +kubebuilder: + @if [ "$$($(KUBEBUILDER) version 2>/dev/null | grep -o KubeBuilderVersion:\"[0-9]*\.[0-9]\.[0-9]*\")" != "KubeBuilderVersion:\"$(KBVERSION)\"" ]; then \ + echo "Installing Kubebuilder"; \ + curl -L https://github.com/kubernetes-sigs/kubebuilder/releases/download/v$(KBVERSION)/kubebuilder_$(GOOS)_$(GOARCH) -o $(KUBEBUILDER); \ + chmod +x $(KUBEBUILDER); \ + fi + +.PHONY: kubebuilder-dependencies +kubebuilder-dependencies: $(LOCAL_BIN) + @if [ ! -f $(LOCAL_BIN)/etcd ] || [ ! -f $(LOCAL_BIN)/kube-apiserver ] || [ ! -f $(LOCAL_BIN)/kubectl ] || \ + [ "$$($(KUBEBUILDER) version 2>/dev/null | grep -o KubeBuilderVersion:\"[0-9]*\.[0-9]\.[0-9]*\")" != "KubeBuilderVersion:\"$(KBVERSION)\"" ]; then \ + echo "Installing envtest Kubebuilder assets"; \ + curl -L "https://go.kubebuilder.io/test-tools/$(K8S_VERSION)/$(GOOS)/$(GOARCH)" | tar xz --strip-components=2 -C $(LOCAL_BIN); \ + fi + +.PHONY: gosec +gosec: + $(call go-get-tool,github.com/securego/gosec/v2/cmd/gosec@v2.15.0) -.PHONY: lint-dockerfiles lint-scripts lint-yaml lint-copyright-banner lint-go lint-markdown lint-all format-go config-docker +.PHONY: gosec-scan +gosec-scan: gosec + $(GOSEC) -fmt sonarqube -out gosec.json -no-fail -exclude-dir=.go ./... + +############################################################ +# E2E Test +############################################################ +GINKGO = $(LOCAL_BIN)/ginkgo +CLUSTER_NAME ?= $(error CLUSTER_NAME is not set.) +CONTROLLER_NAME ?= $(error CONTROLLER_NAME is not set.) +KIND_NAME ?= test-$(CLUSTER_NAME) +KIND_CLUSTER_NAME = kind-$(KIND_NAME) +CONTROLLER_NAMESPACE ?= open-cluster-management-agent-addon +KIND_VERSION ?= latest +# Set the Kind version tag +ifeq ($(KIND_VERSION), minimum) + KIND_ARGS = --image kindest/node:v1.19.16 +else ifneq ($(KIND_VERSION), latest) + KIND_ARGS = --image kindest/node:$(KIND_VERSION) +else + KIND_ARGS = +endif + +.PHONY: kind-create-cluster +kind-create-cluster: + # Ensuring cluster $(KIND_NAME) + -kind create cluster --name $(KIND_NAME) $(KIND_ARGS) + kubectl config use-context $(KIND_CLUSTER_NAME) + kind get kubeconfig --name $(KIND_NAME) > kubeconfig_$(CLUSTER_NAME)_e2e + +.PHONY: kind-ensure-sa +kind-ensure-sa: + @KUBECONFIG_TOKEN="$$(kubectl config view --raw -o jsonpath='{.users[].user.token}')"; \ + KUBECONFIG_USER="$$(echo "$${KUBECONFIG_TOKEN}" | jq -rR 'split(".") | .[1] | select(. != null) | @base64d | fromjson | .sub')"; \ + echo "Kubeconfig user detected from token: $${KUBECONFIG_USER}"; \ + [ "$${KUBECONFIG_USER}" = "system:serviceaccount:$(CONTROLLER_NAMESPACE):$(CONTROLLER_NAME)" ] + +.PHONY: kind-controller-kubeconfig +kind-controller-kubeconfig: install-resources + kubectl -n $(CONTROLLER_NAMESPACE) apply -f test/resources/e2e_controller_secret.yaml + -rm kubeconfig_$(CLUSTER_NAME) + @kubectl config set-cluster $(KIND_CLUSTER_NAME) --kubeconfig=$(PWD)/kubeconfig_$(CLUSTER_NAME) \ + --server=$(shell kubectl config view --minify -o jsonpath='{.clusters[].cluster.server}' --kubeconfig=kubeconfig_$(CLUSTER_NAME)_e2e) \ + --insecure-skip-tls-verify=true + @kubectl config set-credentials $(KIND_CLUSTER_NAME) --kubeconfig=$(PWD)/kubeconfig_$(CLUSTER_NAME) \ + --token=$$(kubectl get secret -n $(CONTROLLER_NAMESPACE) $(CONTROLLER_NAME) -o jsonpath='{.data.token}' --kubeconfig=$(PWD)/kubeconfig_$(CLUSTER_NAME)_e2e | $(BASE64) --decode) + @kubectl config set-context $(KIND_CLUSTER_NAME) --kubeconfig=$(PWD)/kubeconfig_$(CLUSTER_NAME) \ + --user=$(KIND_CLUSTER_NAME) --cluster=$(KIND_CLUSTER_NAME) + @kubectl config use-context $(KIND_CLUSTER_NAME) --kubeconfig=$(PWD)/kubeconfig_$(CLUSTER_NAME) + +.PHONY: e2e-dependencies +e2e-dependencies: + $(call go-get-tool,github.com/onsi/ginkgo/v2/ginkgo@$(shell awk '/github.com\/onsi\/ginkgo\/v2/ {print $$2}' go.mod)) + +############################################################ +# Test coverage +############################################################ +GOCOVMERGE = $(LOCAL_BIN)/gocovmerge +.PHONY: coverage-dependencies +coverage-dependencies: + $(call go-get-tool,github.com/wadey/gocovmerge@v0.0.0-20160331181800-b5bfa59ec0ad) diff --git a/build/common/config/.golangci.yml b/build/common/config/.golangci.yml index 89589b1d..8a346c2e 100644 --- a/build/common/config/.golangci.yml +++ b/build/common/config/.golangci.yml @@ -1,10 +1,6 @@ -service: - # When updating this, also update the version stored in docker/build-tools/Dockerfile in the multicloudlab/tools repo. - golangci-lint-version: 1.19.x # use the fixed version to not introduce new linters unexpectedly run: # timeout for analysis, e.g. 30s, 5m, default is 1m deadline: 20m - go: '1.17' timeout: 20m # which dirs to skip: they won't be analyzed; diff --git a/build/common/scripts/config_docker.sh b/build/common/scripts/config_docker.sh deleted file mode 100755 index fe46156b..00000000 --- a/build/common/scripts/config_docker.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/bash -# -# Copyright 2019 The Kubernetes 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. -# Copyright Contributors to the Open Cluster Management project - - -KUBECTL=$(command -v kubectl) -DOCKER_REGISTRY="quay.io" -DOCKER_USERNAME="multicloudlab" -DOCKER_PASSWORD=$(${KUBECTL} -n default get secret quay-cred -o jsonpath='{.data.password}' | base64 --decode) - -# support other container tools, e.g. podman -CONTAINER_CLI=${CONTAINER_CLI:-docker} - -# login the docker registry -${CONTAINER_CLI} login "${DOCKER_REGISTRY}" -u "${DOCKER_USERNAME}" -p "${DOCKER_PASSWORD}" diff --git a/build/common/scripts/install-operator-sdk.sh b/build/common/scripts/install-operator-sdk.sh deleted file mode 100755 index dfc173af..00000000 --- a/build/common/scripts/install-operator-sdk.sh +++ /dev/null @@ -1,26 +0,0 @@ -#!/bin/bash -# -# Copyright 2019 The Kubernetes 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. -# Copyright Contributors to the Open Cluster Management project - - -echo ">>> Installing Operator SDK" - -# Use version 0.17.0 -RELEASE_VERSION=v0.17.0 -# Download binary -curl -LO https://github.com/operator-framework/operator-sdk/releases/download/${RELEASE_VERSION}/operator-sdk-${RELEASE_VERSION}-x86_64-linux-gnu -# Install binary -chmod +x operator-sdk-${RELEASE_VERSION}-x86_64-linux-gnu && mkdir -p /usr/local/bin/ && cp operator-sdk-${RELEASE_VERSION}-x86_64-linux-gnu /usr/local/bin/operator-sdk && rm operator-sdk-${RELEASE_VERSION}-x86_64-linux-gnu diff --git a/build/common/scripts/lint_copyright_banner.sh b/build/common/scripts/lint_copyright_banner.sh deleted file mode 100755 index 595aeb09..00000000 --- a/build/common/scripts/lint_copyright_banner.sh +++ /dev/null @@ -1,34 +0,0 @@ -#!/bin/bash -# -# Copyright 2019 The Kubernetes 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. -# Copyright Contributors to the Open Cluster Management project - - -set -e - -ec=0 -for fn in "$@"; do - if ! grep -L -q -e "Apache License, Version 2" "${fn}"; then - echo "Missing license: ${fn}" - ec=1 - fi - - if ! grep -L -q -e "Copyright" "${fn}"; then - echo "Missing copyright: ${fn}" - ec=1 - fi -done - -exit $ec diff --git a/controllers/configurationpolicy_controller.go b/controllers/configurationpolicy_controller.go index c684c955..9fd9b8de 100644 --- a/controllers/configurationpolicy_controller.go +++ b/controllers/configurationpolicy_controller.go @@ -92,7 +92,6 @@ type cachedEncryptionKey struct { previousKey []byte } -// nolint: structcheck type discoveryInfo struct { apiResourceList []*metav1.APIResourceList apiGroups []*restmapper.APIGroupResources diff --git a/docs/adoption_guide.md b/docs/adoption_guide.md deleted file mode 100644 index 2392d41c..00000000 --- a/docs/adoption_guide.md +++ /dev/null @@ -1,53 +0,0 @@ -[comment]: # ( Copyright Contributors to the Open Cluster Management project ) - -**Table of Contents** -- [Run configuration policy controller locally](#run-configuration-policy-controller-locally) -- [Build a local container image](#build-a-local-container) -- [Make your own policy controller](#make-your-own-policy-controller) - - [Change kind](#change-kind) - - [Change CRD](#change-crd) - - [Change CR](#change-cr) - - [Test new CRD and CR](#test-new-crd-and-cr) - - [Change the logic of the execution](#change-the-logic-of-the-execution) - - [Change the test files](#change-the-test-files) - - [Do integration testing](#do-integration-testing) - -## Run configuration policy controller locally - -To build and run it locally, install operator SDK CLI from https://github.com/operator-framework/operator-sdk/blob/master/doc/user/install-operator-sdk.md. - -Make sure to export GO111MODULE=on as it uses go mod as dependency manager. - -```bash -export GO111MODULE=on -kubectl apply -f deploy/crds/policy.open-cluster-management.io_configurationpolicies_crd.yaml -operator-sdk run --local --verbose -``` -It takes seconds for the configuration policy controller to fully start. You will get the message `Waiting for policies to be available for processing...` once it's fully started and watching for policies. - -To test a configuration policy, open another command prompt to deploy the configuration policy file -``` -kubectl apply -f deploy/crds/policy.open-cluster-management.io_v1_configurationpolicy_cr.yaml -n default -``` -The local process outputs the following messages -``` -{"level":"info","ts":1572447165.453119,"logger":"controller_configurationpolicy","msg":"Reconciling configurationPolicy","Request.Namespace":"default","Request.Name":"example-configurationpolicy"} -Available policies in namespaces: -namespace = kube-public; policy = example-configurationpolicy -namespace = default; policy = example-configurationpolicy -``` -Check the configuration policy resource using `kubectl describe configurationPolicy example-configurationpolicy -n default`. The policy controller checks the cluster and reports the compliancy status in the policy. The status field in the policy is updated with the compliant status, for example- -``` -status: - compliancyDetails: - - Compliant: NonCompliant - Validity: {} - conditions: - - lastTransitionTime: "2020-05-08T15:53:28Z" - message: roles `pod-reader-thur` does not exist as specified, and should be - created - reason: K8s missing a must have object - status: "True" - type: violation - compliant: NonCompliant -``` \ No newline at end of file diff --git a/docs/development.md b/docs/development.md deleted file mode 100644 index 6881904c..00000000 --- a/docs/development.md +++ /dev/null @@ -1,50 +0,0 @@ -[comment]: # ( Copyright Contributors to the Open Cluster Management project ) - -# Development Guide - -## Prerequisite - -- git -- go version v1.12+ -- Linting Tools - - | linting tool | version | - | ------------ | ------- | - | [hadolint](https://github.com/hadolint/hadolint#install) | [v1.17.2](https://github.com/hadolint/hadolint/releases/tag/v1.17.2) | - | [shellcheck](https://github.com/koalaman/shellcheck#installing) | [v0.7.0](https://github.com/koalaman/shellcheck/releases/tag/v0.7.0) | - | [yamllint](https://github.com/adrienverge/yamllint#installation) | [v1.17.0](https://github.com/adrienverge/yamllint/releases/tag/v1.17.0) - | [helm client](https://helm.sh/docs/using_helm/#install-helm) | [v2.10.0](https://github.com/helm/helm/releases/tag/v2.10.0) | - | [golangci-lint](https://github.com/golangci/golangci-lint#install) | [v1.18.0](https://github.com/golangci/golangci-lint/releases/tag/v1.18.0) | - | [autopep8](https://github.com/hhatto/autopep8#installation) | [v1.4.4](https://github.com/hhatto/autopep8/releases/tag/v1.4.4) | - | [mdl](https://github.com/markdownlint/markdownlint#installation) | [v0.5.0](https://github.com/markdownlint/markdownlint/releases/tag/v0.5.0) | - | [awesome_bot](https://github.com/dkhamsing/awesome_bot#installation) | [1.19.1](https://github.com/dkhamsing/awesome_bot/releases/tag/1.19.1) | - | [sass-lint](https://github.com/sasstools/sass-lint#install) | [v1.13.1](https://github.com/sasstools/sass-lint/releases/tag/v1.13.1) | - | [tslint](https://github.com/palantir/tslint#installation--usage) | [v5.18.0](https://github.com/palantir/tslint/releases/tag/5.18.0) - | [prototool](https://github.com/uber/prototool/blob/dev/docs/install.md) | `7df3b95` | - | [goimports](https://godoc.org/golang.org/x/tools/cmd/goimports) | `3792095` | - -## Developer quick start - -- Setup `GIT_HOST` to override the setting for your custom path. - -```bash -export GIT_HOST=github.com/ -``` - -- Run the `linter` and `test` before building the binary. - -```bash -make check -make test -make build -``` - -- Build and push the docker image for local development. - -```bash -export IMG= -export REGISTRY= -make build-push-images -``` - -> **Note:** You need to login the docker registry before running the command above. From 17e9dcd34092b760e8fb5ea2bcf3e9be7b161a49 Mon Sep 17 00:00:00 2001 From: Will Kutler Date: Mon, 27 Mar 2023 13:36:54 -0400 Subject: [PATCH 3/5] Fix order of events on create or delete Signed-off-by: Will Kutler (cherry picked from commit da14bdd3396dbf0aeb753e213b649891705894e0) --- Makefile | 2 +- controllers/configurationpolicy_controller.go | 2 +- test/e2e/case15_event_format_test.go | 7 ++++++- 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 98cdced0..d39fa21b 100644 --- a/Makefile +++ b/Makefile @@ -103,7 +103,7 @@ create-ns: # Run against the current locally configured Kubernetes cluster .PHONY: run run: - WATCH_NAMESPACE=$(WATCH_NAMESPACE) go run ./main.go --leader-elect=false + WATCH_NAMESPACE=$(WATCH_NAMESPACE) go run ./main.go controller --leader-elect=false ############################################################ # clean section diff --git a/controllers/configurationpolicy_controller.go b/controllers/configurationpolicy_controller.go index 9fd9b8de..e64ddee1 100644 --- a/controllers/configurationpolicy_controller.go +++ b/controllers/configurationpolicy_controller.go @@ -1599,7 +1599,7 @@ func (r *ConfigurationPolicyReconciler) handleSingleObj( // error) _ = createStatus("", obj.gvr.Resource, compliantObject, obj.namespaced, obj.policy, obj.index, false, true) - obj.policy.Status.ComplianceState = policyv1.NonCompliant + statusStr := convertPolicyStatusToString(obj.policy) objLog.Info("Sending a noncompliant status event (object missing)", "policy", obj.policy.Name, "status", statusStr) diff --git a/test/e2e/case15_event_format_test.go b/test/e2e/case15_event_format_test.go index d6e66ad9..b1772632 100644 --- a/test/e2e/case15_event_format_test.go +++ b/test/e2e/case15_event_format_test.go @@ -166,8 +166,13 @@ var _ = Describe("Testing compliance event formatting", func() { compPlcEvents := utils.GetMatchingEvents(clientManaged, testNamespace, case15BecomesCompliantName, "", "Policy status is: Compliant", defaultTimeoutSeconds) Expect(compPlcEvents).NotTo(BeEmpty()) + compParentEventsPreCreation := utils.GetMatchingEvents(clientManaged, testNamespace, + case15BecomesCompliantParentName, "policy: "+testNamespace+"/"+case15BecomesCompliantName, + "^NonCompliant;.*No instances of.*found as specified", defaultTimeoutSeconds) + Expect(compParentEventsPreCreation).NotTo(BeEmpty()) compParentEvents := utils.GetMatchingEvents(clientManaged, testNamespace, case15BecomesCompliantParentName, - "policy: "+testNamespace+"/"+case15BecomesCompliantName, "^Compliant;", defaultTimeoutSeconds) + "policy: "+testNamespace+"/"+case15BecomesCompliantName, + "^Compliant;.*and was created successfully$", defaultTimeoutSeconds) Expect(compParentEvents).NotTo(BeEmpty()) }) It("Records events for a policy that becomes noncompliant", func() { From ea5f8f2ccdd2f24d2e0d64acfb5a2454d9143bb9 Mon Sep 17 00:00:00 2001 From: Will Kutler Date: Mon, 27 Mar 2023 15:47:18 -0400 Subject: [PATCH 4/5] Fix compliance state on missing obj evt Signed-off-by: Will Kutler (cherry picked from commit e868a2ba838fd18f60f5d780fae13642c9f95046) --- controllers/configurationpolicy_controller.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/controllers/configurationpolicy_controller.go b/controllers/configurationpolicy_controller.go index e64ddee1..9fd9b8de 100644 --- a/controllers/configurationpolicy_controller.go +++ b/controllers/configurationpolicy_controller.go @@ -1599,7 +1599,7 @@ func (r *ConfigurationPolicyReconciler) handleSingleObj( // error) _ = createStatus("", obj.gvr.Resource, compliantObject, obj.namespaced, obj.policy, obj.index, false, true) - + obj.policy.Status.ComplianceState = policyv1.NonCompliant statusStr := convertPolicyStatusToString(obj.policy) objLog.Info("Sending a noncompliant status event (object missing)", "policy", obj.policy.Name, "status", statusStr) From 20a4071010a28f3e724c1301de85a3547bb0c672 Mon Sep 17 00:00:00 2001 From: Yi Rae Kim Date: Fri, 31 Mar 2023 02:33:37 -0400 Subject: [PATCH 5/5] Fix status history toggling (#111) Description of problem: When Pod security policy is created and the status is changed from inform to enforce, the status is toggling. Not able to enforce pod security policy. How to fix Set default value when Kube API value omitted ref: https://issues.redhat.com/browse/ACM-3109 Signed-off-by: Yi Rae Kim (cherry picked from commit b70748b1ac975b4573678740c1b2c8bef4eef4d2) --- .../configurationpolicy_controller_test.go | 97 +++++++++++ controllers/configurationpolicy_utils.go | 22 ++- controllers/configurationpolicy_utils_test.go | 31 ++++ policy-security.yml | 0 test/e2e/case12_list_compare_test.go | 22 +-- test/e2e/case20_delete_objects_test.go | 3 +- test/e2e/case25_related_object_metric_test.go | 5 +- test/e2e/case26_user_error_metric_test.go | 35 ++-- test/e2e/case28_evauluation_metric_test.go | 1 - test/e2e/case31_policy_history_test.go | 155 ++++++++++++++++++ test/e2e/case8_status_check_test.go | 1 - .../pod-config-policy-number.yaml | 36 ++++ .../pod-config-policy.yaml | 36 ++++ .../pod-policy-number.yaml | 39 +++++ .../case31_policy_history/pod-policy.yaml | 39 +++++ 15 files changed, 484 insertions(+), 38 deletions(-) create mode 100644 policy-security.yml create mode 100644 test/e2e/case31_policy_history_test.go create mode 100644 test/resources/case31_policy_history/pod-config-policy-number.yaml create mode 100644 test/resources/case31_policy_history/pod-config-policy.yaml create mode 100644 test/resources/case31_policy_history/pod-policy-number.yaml create mode 100644 test/resources/case31_policy_history/pod-policy.yaml diff --git a/controllers/configurationpolicy_controller_test.go b/controllers/configurationpolicy_controller_test.go index d68f9dd4..d58f5bf6 100644 --- a/controllers/configurationpolicy_controller_test.go +++ b/controllers/configurationpolicy_controller_test.go @@ -12,6 +12,7 @@ import ( "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes/scheme" @@ -754,3 +755,99 @@ func TestShouldEvaluatePolicy(t *testing.T) { ) } } + +func TestShouldHandleSingleKeyFalse(t *testing.T) { + t.Parallel() + + var unstruct unstructured.Unstructured + var unstructObj unstructured.Unstructured + + var update, skip bool + + type ExpectResult struct { + key string + expect bool + } + + type TestSingleKey struct { + input map[string]interface{} + fromAPI map[string]interface{} + expectResult ExpectResult + } + + tests := []TestSingleKey{ + { + input: map[string]interface{}{ + "hostIPC": false, + "container": "test", + }, + fromAPI: map[string]interface{}{ + "container": "test", + }, + expectResult: ExpectResult{ + "hostIPC", + false, + }, + }, + { + input: map[string]interface{}{ + "container": map[string]interface{}{ + "image": "nginx1.7.9", + "name": "nginx", + "hostIPC": false, + }, + }, + fromAPI: map[string]interface{}{ + "container": map[string]interface{}{ + "image": "nginx1.7.9", + "name": "nginx", + }, + }, + expectResult: ExpectResult{ + "container", + false, + }, + }, + { + input: map[string]interface{}{ + "hostIPC": true, + "container": "test", + }, + fromAPI: map[string]interface{}{ + "container": "test", + }, + expectResult: ExpectResult{ + "hostIPC", + true, + }, + }, + { + input: map[string]interface{}{ + "container": map[string]interface{}{ + "image": "nginx1.7.9", + "name": "nginx", + "hostIPC": true, + }, + }, + fromAPI: map[string]interface{}{ + "container": map[string]interface{}{ + "image": "nginx1.7.9", + "name": "nginx", + }, + }, + expectResult: ExpectResult{ + "container", + true, + }, + }, + } + + for _, test := range tests { + unstruct.Object = test.input + unstructObj.Object = test.fromAPI + key := test.expectResult.key + _, update, _, skip = handleSingleKey(key, unstruct, &unstructObj, "musthave") + assert.Equal(t, update, test.expectResult.expect) + assert.False(t, skip) + } +} diff --git a/controllers/configurationpolicy_utils.go b/controllers/configurationpolicy_utils.go index da5e4bb6..f4a9179f 100644 --- a/controllers/configurationpolicy_utils.go +++ b/controllers/configurationpolicy_utils.go @@ -119,6 +119,16 @@ func equalObjWithSort(mergedObj interface{}, oldObj interface{}) (areEqual bool) return false } default: + // NOTE: when type is string, int, bool + var oVal interface{} + + if oldObj == nil && mergedObj != nil { + ref := reflect.ValueOf(mergedObj) + oVal = reflect.Zero(ref.Type()).Interface() + + return fmt.Sprint(oVal) == fmt.Sprint(mergedObj) + } + if !reflect.DeepEqual(fmt.Sprint(mergedObj), fmt.Sprint(oldObj)) { return false } @@ -175,8 +185,12 @@ func checkFieldsWithSort(mergedObj map[string]interface{}, oldObj map[string]int // extra check to see if value is a byte value mQty, err := apiRes.ParseQuantity(mVal) if err != nil { + oVal := oldObj[i] + if oVal == nil { + oVal = "" + } // An error indicates the value is a regular string, so check equality normally - if fmt.Sprint(oldObj[i]) != fmt.Sprint(mVal) { + if fmt.Sprint(oVal) != fmt.Sprint(mVal) { return false } } else { @@ -194,6 +208,12 @@ func checkFieldsWithSort(mergedObj map[string]interface{}, oldObj map[string]int default: // if field is not an object, just do a basic compare to check for a match oVal := oldObj[i] + // When oVal value omitted because of omitempty + if oVal == nil && mVal != nil { + ref := reflect.ValueOf(mVal) + oVal = reflect.Zero(ref.Type()).Interface() + } + if fmt.Sprint(oVal) != fmt.Sprint(mVal) { return false } diff --git a/controllers/configurationpolicy_utils_test.go b/controllers/configurationpolicy_utils_test.go index 07b92051..371757fe 100644 --- a/controllers/configurationpolicy_utils_test.go +++ b/controllers/configurationpolicy_utils_test.go @@ -118,3 +118,34 @@ func TestCheckFieldsWithSort(t *testing.T) { assert.True(t, checkFieldsWithSort(mergedObj, oldObj)) } + +func TestEqualObjWithSort(t *testing.T) { + t.Parallel() + + oldObj := map[string]interface{}{ + "nonResourceURLs": []string{"/version", "/healthz"}, + "verbs": []string{"get"}, + } + mergedObj := map[string]interface{}{ + "nonResourceURLs": []string{"/version", "/healthz"}, + "verbs": []string{"get"}, + "apiGroups": []interface{}{}, + "resources": []interface{}{}, + } + + assert.True(t, equalObjWithSort(mergedObj, oldObj)) + assert.False(t, equalObjWithSort(mergedObj, nil)) + + oldObj = map[string]interface{}{ + "nonResourceURLs": []string{"/version", "/healthz"}, + "verbs": []string{"get"}, + } + mergedObj = map[string]interface{}{ + "nonResourceURLs": []string{"/version", "/healthz"}, + "verbs": []string{"post"}, + "apiGroups": []interface{}{}, + "resources": []interface{}{}, + } + + assert.False(t, equalObjWithSort(mergedObj, oldObj)) +} diff --git a/policy-security.yml b/policy-security.yml new file mode 100644 index 00000000..e69de29b diff --git a/test/e2e/case12_list_compare_test.go b/test/e2e/case12_list_compare_test.go index 301e785a..e0bcae5b 100644 --- a/test/e2e/case12_list_compare_test.go +++ b/test/e2e/case12_list_compare_test.go @@ -356,7 +356,17 @@ var _ = Describe("Test list handling for musthave", func() { deleteConfigPolicies(policies) }) }) - Describe("Create a statefulset object with a byte quantity field on managed cluster in ns:"+testNamespace, func() { + Describe("Create a statefulset object with a byte quantity field "+ + "on managed cluster in ns:"+testNamespace, Ordered, func() { + cleanup := func() { + // Delete the policies and ignore any errors (in case it was deleted previously) + policies := []string{ + case12ByteCreate, + case12ByteInform, + } + + deleteConfigPolicies(policies) + } It("should only add the list item with the rounded byte value once", func() { By("Creating " + case12ByteCreate + " and " + case12ByteInform + " on managed") utils.Kubectl("apply", "-f", case12ByteCreateYaml, "-n", testNamespace) @@ -389,14 +399,6 @@ var _ = Describe("Test list handling for musthave", func() { return utils.GetComplianceState(managedPlc) }, defaultTimeoutSeconds, 1).Should(Equal("Compliant")) }) - - It("Cleans up", func() { - policies := []string{ - case12ByteCreate, - case12ByteInform, - } - - deleteConfigPolicies(policies) - }) + AfterAll(cleanup) }) }) diff --git a/test/e2e/case20_delete_objects_test.go b/test/e2e/case20_delete_objects_test.go index 2115a06a..6a01a26c 100644 --- a/test/e2e/case20_delete_objects_test.go +++ b/test/e2e/case20_delete_objects_test.go @@ -527,7 +527,8 @@ var _ = Describe("Test objects that should be deleted are actually being deleted return nil }, defaultTimeoutSeconds, 1).Should(BeNil()) }) - It("deletes the pod after the policy is deleted", func() { + AfterAll(func() { + By("deletes the pod after the policy is deleted") deleteConfigPolicies([]string{case20ConfigPolicyNameCreate}) Eventually(func() interface{} { pod := utils.GetWithTimeout(clientManagedDynamic, gvrPod, diff --git a/test/e2e/case25_related_object_metric_test.go b/test/e2e/case25_related_object_metric_test.go index b455eed1..a263e045 100644 --- a/test/e2e/case25_related_object_metric_test.go +++ b/test/e2e/case25_related_object_metric_test.go @@ -56,7 +56,7 @@ var _ = Describe("Test related object metrics", Ordered, func() { // Delete the policies and ignore any errors (in case it was deleted previously) cmd := exec.Command("kubectl", "delete", "-f", policyYaml, - "-n", testNamespace) + "-n", testNamespace, "--ignore-not-found") _, _ = cmd.CombinedOutput() opt := metav1.ListOptions{} utils.ListWithTimeout( @@ -66,13 +66,10 @@ var _ = Describe("Test related object metrics", Ordered, func() { } It("should clean up", cleanup) - It("should have no common related object metrics after clean up", func() { By("Checking metric endpoint for related object gauges") Eventually(func() interface{} { return utils.GetMetrics("common_related_objects") }, defaultTimeoutSeconds, 1).Should(Equal([]string{})) }) - - AfterAll(cleanup) }) diff --git a/test/e2e/case26_user_error_metric_test.go b/test/e2e/case26_user_error_metric_test.go index 594c3880..344a5567 100644 --- a/test/e2e/case26_user_error_metric_test.go +++ b/test/e2e/case26_user_error_metric_test.go @@ -15,6 +15,21 @@ var _ = Describe("Test related object metrics", Ordered, func() { policy1Name = "case26-test-policy-1" policyYaml = "../resources/case26_user_error_metric/case26-missing-crd.yaml" ) + cleanup := func() { + // Delete the policies and ignore any errors (in case it was deleted previously) + cmd := exec.Command("kubectl", "delete", + "-f", policyYaml, + "-n", testNamespace, "--ignore-not-found") + _, _ = cmd.CombinedOutput() + opt := metav1.ListOptions{} + utils.ListWithTimeout( + clientManagedDynamic, gvrConfigPolicy, opt, 0, false, defaultTimeoutSeconds) + By("Checking metric endpoint for related object gauges") + Eventually(func() interface{} { + return utils.GetMetrics("policy_user_errors") + }, defaultTimeoutSeconds, 1).Should(Equal([]string{})) + } + It("should create policy", func() { By("Creating " + policyYaml) utils.Kubectl("apply", @@ -36,25 +51,5 @@ var _ = Describe("Test related object metrics", Ordered, func() { }, defaultTimeoutSeconds, 1).Should(Equal([]string{"policies", "counter", "1"})) }) - cleanup := func() { - // Delete the policies and ignore any errors (in case it was deleted previously) - cmd := exec.Command("kubectl", "delete", - "-f", policyYaml, - "-n", testNamespace) - _, _ = cmd.CombinedOutput() - opt := metav1.ListOptions{} - utils.ListWithTimeout( - clientManagedDynamic, gvrConfigPolicy, opt, 0, false, defaultTimeoutSeconds) - } - - It("should clean up", cleanup) - - It("should have no common related object metrics after clean up", func() { - By("Checking metric endpoint for related object gauges") - Eventually(func() interface{} { - return utils.GetMetrics("policy_user_errors") - }, defaultTimeoutSeconds, 1).Should(Equal([]string{})) - }) - AfterAll(cleanup) }) diff --git a/test/e2e/case28_evauluation_metric_test.go b/test/e2e/case28_evauluation_metric_test.go index cc32f789..f4ba2d76 100644 --- a/test/e2e/case28_evauluation_metric_test.go +++ b/test/e2e/case28_evauluation_metric_test.go @@ -102,7 +102,6 @@ var _ = Describe("Test config policy evaluation metrics", Ordered, func() { opt := metav1.ListOptions{} utils.ListWithTimeout( clientManagedDynamic, gvrConfigPolicy, opt, 0, false, defaultTimeoutSeconds) - Eventually(func() interface{} { return utils.GetMetrics( "config_policy_evaluation_total", fmt.Sprintf(`name=\"%s\"`, policyName)) diff --git a/test/e2e/case31_policy_history_test.go b/test/e2e/case31_policy_history_test.go new file mode 100644 index 00000000..e83a19f5 --- /dev/null +++ b/test/e2e/case31_policy_history_test.go @@ -0,0 +1,155 @@ +// Copyright (c) 2021 Red Hat, Inc. +// Copyright Contributors to the Open Cluster Management project + +package e2e + +import ( + "context" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "open-cluster-management.io/config-policy-controller/test/utils" +) + +const ( + case31Policy = "../resources/case31_policy_history/pod-policy.yaml" + case31ConfigPolicy = "../resources/case31_policy_history/pod-config-policy.yaml" + case31PolicyName = "test-policy-security" + case31ConfigPolicyName = "config-policy-pod" + case31PolicyNumber = "../resources/case31_policy_history/pod-policy-number.yaml" + case31ConfigPolicyNumber = "../resources/case31_policy_history/pod-config-policy-number.yaml" + case31PolicyNumberName = "test-policy-security-number" + case31ConfigPolicyNumberName = "config-policy-pod-number" +) + +var _ = Describe("Test policy history message when KubeAPI return "+ + "omits values in the returned object", Ordered, func() { + Describe("status toggling should not be generated When Policy include default value,", Ordered, func() { + It("creates the policyconfiguration "+case31Policy, func() { + utils.Kubectl("apply", "-f", case31Policy, "-n", "managed") + }) + + It("verifies the policy "+case31PolicyName+" in "+testNamespace, func() { + By("bind policy and configurationpolicy") + parent := utils.GetWithTimeout(clientManagedDynamic, gvrPolicy, + case31PolicyName, testNamespace, true, defaultTimeoutSeconds) + Expect(parent).NotTo(BeNil()) + + plcDef := utils.ParseYaml(case31ConfigPolicy) + ownerRefs := plcDef.GetOwnerReferences() + ownerRefs[0].UID = parent.GetUID() + plcDef.SetOwnerReferences(ownerRefs) + _, err := clientManagedDynamic.Resource(gvrConfigPolicy).Namespace(testNamespace). + Create(context.TODO(), plcDef, metav1.CreateOptions{}) + Expect(err).To(BeNil()) + + By("check configurationpolicy exist") + plc := utils.GetWithTimeout(clientManagedDynamic, gvrConfigPolicy, + case31ConfigPolicyName, testNamespace, true, defaultTimeoutSeconds) + Expect(plc).NotTo(BeNil()) + }) + + It("check history toggling", func() { + By("wait until pod is up") + Eventually(func() interface{} { + managedPlc := utils.GetWithTimeout(clientManagedDynamic, gvrConfigPolicy, + case31ConfigPolicyName, testNamespace, true, defaultTimeoutSeconds) + + return utils.GetComplianceState(managedPlc) + }, defaultTimeoutSeconds, 1).Should(Equal("Compliant")) + + By("check events") + Consistently(func() int { + eventlen := len(utils.GetMatchingEvents(clientManaged, testNamespace, + case31ConfigPolicyName, case31ConfigPolicyName, "NonCompliant;", defaultTimeoutSeconds)) + + return eventlen + }, 30, 5).Should(BeNumerically("<", 2)) + + Consistently(func() int { + eventlen := len(utils.GetMatchingEvents(clientManaged, testNamespace, + case31PolicyName, case31ConfigPolicyName, "NonCompliant;", defaultTimeoutSeconds)) + + return eventlen + }, 30, 5).Should(BeNumerically("<", 3)) + }) + AfterAll(func() { + utils.Kubectl("delete", "policy", case31PolicyName, "-n", + "managed", "--ignore-not-found") + configlPlc := utils.GetWithTimeout(clientManagedDynamic, gvrConfigPolicy, + case31ConfigPolicyName, "managed", false, defaultTimeoutSeconds, + ) + utils.Kubectl("delete", "event", + "--field-selector=involvedObject.name="+case31PolicyName, "-n", "managed") + utils.Kubectl("delete", "event", + "--field-selector=involvedObject.name="+case31ConfigPolicyName, "-n", "managed") + ExpectWithOffset(1, configlPlc).To(BeNil()) + }) + }) + Describe("status should not toggle When Policy include default value of number", Ordered, func() { + It("creates the policyconfiguration "+case31PolicyNumber, func() { + utils.Kubectl("apply", "-f", case31PolicyNumber, "-n", "managed") + }) + + It("verifies the policy "+case31PolicyNumberName+" in "+testNamespace, func() { + By("bind policy and configurationpolicy") + parent := utils.GetWithTimeout(clientManagedDynamic, gvrPolicy, + case31PolicyNumberName, testNamespace, true, defaultTimeoutSeconds) + Expect(parent).NotTo(BeNil()) + + plcDef := utils.ParseYaml(case31ConfigPolicyNumber) + ownerRefs := plcDef.GetOwnerReferences() + ownerRefs[0].UID = parent.GetUID() + plcDef.SetOwnerReferences(ownerRefs) + _, err := clientManagedDynamic.Resource(gvrConfigPolicy).Namespace(testNamespace). + Create(context.TODO(), plcDef, metav1.CreateOptions{}) + Expect(err).To(BeNil()) + + By("check configurationpolicy exist") + plc := utils.GetWithTimeout(clientManagedDynamic, gvrConfigPolicy, + case31ConfigPolicyNumberName, testNamespace, true, defaultTimeoutSeconds) + Expect(plc).NotTo(BeNil()) + }) + + It("check history toggling", func() { + By("wait until pod is up") + Eventually(func() interface{} { + managedPlc := utils.GetWithTimeout(clientManagedDynamic, gvrConfigPolicy, + case31ConfigPolicyNumberName, testNamespace, true, defaultTimeoutSeconds) + + return utils.GetComplianceState(managedPlc) + }, defaultTimeoutSeconds, 1).Should(Equal("Compliant")) + + By("check events") + Consistently(func() int { + eventLen := len(utils.GetMatchingEvents(clientManaged, testNamespace, case31ConfigPolicyNumberName, + case31ConfigPolicyNumberName, "NonCompliant;", defaultTimeoutSeconds)) + + return eventLen + }, 30, 5).Should(BeNumerically("<", 2)) + + // NOTE: pick policy event, these event's reason include ConfigPolicyName + Consistently(func() int { + eventLen := len(utils.GetMatchingEvents(clientManaged, testNamespace, + case31PolicyNumberName, case31ConfigPolicyNumberName, "NonCompliant;", defaultTimeoutSeconds)) + + return eventLen + }, 30, 5).Should(BeNumerically("<", 3)) + }) + AfterAll(func() { + utils.Kubectl("delete", "policy", case31PolicyNumberName, "-n", + "managed", "--ignore-not-found") + configlPlc := utils.GetWithTimeout(clientManagedDynamic, gvrConfigPolicy, + case31ConfigPolicyName, "managed", false, defaultTimeoutSeconds, + ) + utils.Kubectl("delete", "event", + "--field-selector=involvedObject.name="+case31PolicyNumberName, "-n", "managed") + utils.Kubectl("delete", "event", + "--field-selector=involvedObject.name="+case31ConfigPolicyNumberName, "-n", "managed") + + ExpectWithOffset(1, configlPlc).To(BeNil()) + }) + }) +}) diff --git a/test/e2e/case8_status_check_test.go b/test/e2e/case8_status_check_test.go index 4ac3993e..1e41fd99 100644 --- a/test/e2e/case8_status_check_test.go +++ b/test/e2e/case8_status_check_test.go @@ -144,7 +144,6 @@ var _ = Describe("Test pod obj template handling", func() { policies := []string{ case8ConfigPolicyStatusPod, } - deleteConfigPolicies(policies) }) }) diff --git a/test/resources/case31_policy_history/pod-config-policy-number.yaml b/test/resources/case31_policy_history/pod-config-policy-number.yaml new file mode 100644 index 00000000..b12f1f5c --- /dev/null +++ b/test/resources/case31_policy_history/pod-config-policy-number.yaml @@ -0,0 +1,36 @@ +apiVersion: policy.open-cluster-management.io/v1 +kind: ConfigurationPolicy +metadata: + name: config-policy-pod-number + labels: + policy.open-cluster-management.io/policy: test-policy-security + ownerReferences: + - apiVersion: policy.open-cluster-management.io/v1 + blockOwnerDeletion: false + controller: true + kind: Policy + name: test-policy-security-number + uid: 08bae967-4262-498a-84e9-d1f0e321b41e +spec: + pruneObjectBehavior: DeleteAll + remediationAction: enforce + namespaceSelector: + exclude: + - kube-* + include: + - default + object-templates: + - complianceType: musthave + objectDefinition: + apiVersion: v1 + kind: Pod + metadata: + name: case31-pod-policy-number + spec: + priority: 0 + containers: + - image: nginx:1.7.9 + imagePullPolicy: Never + name: nginx + ports: + - containerPort: 80 \ No newline at end of file diff --git a/test/resources/case31_policy_history/pod-config-policy.yaml b/test/resources/case31_policy_history/pod-config-policy.yaml new file mode 100644 index 00000000..4b51897c --- /dev/null +++ b/test/resources/case31_policy_history/pod-config-policy.yaml @@ -0,0 +1,36 @@ +apiVersion: policy.open-cluster-management.io/v1 +kind: ConfigurationPolicy +metadata: + name: config-policy-pod + labels: + policy.open-cluster-management.io/policy: test-policy-security + ownerReferences: + - apiVersion: policy.open-cluster-management.io/v1 + blockOwnerDeletion: false + controller: true + kind: Policy + name: test-policy-security + uid: 08bae967-4262-498a-84e9-d1f0e321b41e +spec: + pruneObjectBehavior: DeleteAll + remediationAction: enforce + namespaceSelector: + exclude: + - kube-* + include: + - default + object-templates: + - complianceType: musthave + objectDefinition: + apiVersion: v1 + kind: Pod + metadata: + name: case31-pod-policy + spec: + hostIPC: false + containers: + - image: nginx:1.7.9 + imagePullPolicy: Never + name: nginx + ports: + - containerPort: 80 \ No newline at end of file diff --git a/test/resources/case31_policy_history/pod-policy-number.yaml b/test/resources/case31_policy_history/pod-policy-number.yaml new file mode 100644 index 00000000..5e2e4e6a --- /dev/null +++ b/test/resources/case31_policy_history/pod-policy-number.yaml @@ -0,0 +1,39 @@ +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: test-policy-security-number + annotations: + policy.open-cluster.management.io/standards: NIST-CSF + policy.open-cluster.management.io/categories: PR.PT Protective Technology + policy.open-cluster.management.io/controls: PR.PT-3 Least Functionality +spec: + remediationAction: enforce + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: config-policy-pod-number + spec: + remediationAction: enforce + namespaceSelector: + exclude: + - kube-* + include: + - default + object-templates: + - complianceType: musthave + objectDefinition: + apiVersion: v1 + kind: Pod + metadata: + name: case31-pod-policy-number + spec: + priority: 0 + containers: + - image: nginx:1.7.9 + imagePullPolicy: Never + name: nginx + ports: + - containerPort: 80 \ No newline at end of file diff --git a/test/resources/case31_policy_history/pod-policy.yaml b/test/resources/case31_policy_history/pod-policy.yaml new file mode 100644 index 00000000..362c3530 --- /dev/null +++ b/test/resources/case31_policy_history/pod-policy.yaml @@ -0,0 +1,39 @@ +apiVersion: policy.open-cluster-management.io/v1 +kind: Policy +metadata: + name: test-policy-security + annotations: + policy.open-cluster.management.io/standards: NIST-CSF + policy.open-cluster.management.io/categories: PR.PT Protective Technology + policy.open-cluster.management.io/controls: PR.PT-3 Least Functionality +spec: + remediationAction: enforce + disabled: false + policy-templates: + - objectDefinition: + apiVersion: policy.open-cluster-management.io/v1 + kind: ConfigurationPolicy + metadata: + name: config-policy-pod + spec: + remediationAction: enforce + namespaceSelector: + exclude: + - kube-* + include: + - default + object-templates: + - complianceType: musthave + objectDefinition: + apiVersion: v1 + kind: Pod + metadata: + name: case31-pod-policy + spec: + hostIPC: false + containers: + - image: nginx:1.7.9 + imagePullPolicy: Never + name: nginx + ports: + - containerPort: 80 \ No newline at end of file