Skip to content

Commit

Permalink
build(operator-sdk): upgrade operator sdk version to 1.22.2 (#430)
Browse files Browse the repository at this point in the history
* fix(prometheus): fix prometheus metrics endpoint

* fix(bundle): build bundle for migration to v1.6.1

* fix(makefile): add fail check for shell

* chore(rbac): move lease to its own proxy-role rule

* build(deps): bump controller-runtime to v0.8.3 and kubernetes dependencies to v0.20.2

* fix(bundle): run make bundle for migration to operator-sdk 1.7.1

* fix(bundle): run make bundle for migration to operator-sdk 1.8.0

* fix(bundle): run make bundle for migration to operator-sdk 1.9.0

* fix(config): add containerPort protocol in manifests

* fix(bundle): remove service account in bundle

* fix(bundle): run make bundle for migration to operator-sdk 1.11.0

* fix(bundle): run make bundle for migration to operator-sdk 1.13.0

* build(makefile): bump controller-gen to 0.7.0

* fix(makefile): remove crd-options

* fix(makefile): revert roleName back to role

* fix(manifests): update manifests after bumping controller-gen to 0.7.0

* fix(makefile): migrate to new env-test tool

* build(deps): bump controller-runtime to v0.10.0 and kubernetes dependencies to v0.22.1

* fix(apis): use marker comments to mark deprecated apis

* fix(makefile): add check if binaries exists and fix kustomize install target error

* test(cryostat): fix cryostat-controller tests after bumping controller-runtime to 0.10.0

* test(cryostat): fix recording-controller tests after bumping controller-runtime to 0.10.0

* fix(bundle): run bundle after migration to operator-sdk v1.14.0

* fix(bundle): run make bundle after migrating to operator-sdk v1.15.0

* feat(manager): add annotation to specify default manager container

* fix(makefile): add PHONY for all targets

* feat(makefile): add ignore-not-found flag to be used in uninstall/undeploy targets

* fix(bundle): run make bundle after migrating to operator-sdk v1.16.0

* build(deps): bump k8s deps to v0.23.0 and controller-gen to v0.8.0

* test(probe-handler): use new struct name ProbeHandler instead of Handler

* test(fakeclient): use NewClientBuilder in place of deprecated NewFakeClientWithScheme

* fix(makefile): add deprecation silencer env var for ginkgo

* test(resource): add metadata for pod template in OtherDeployment func

* fix(manifests): add resource request/limit to kube-rbac-proxy

* fix(manifests): reduce debug log level for the sidecar container kube-rbac-proxy from 10 to 0

* fix(bundle): run make bundle after migration to operator-sdk 1.17.0

* build(deps): remove protobuf replace directive since client-go now pulls 1.3.2

* feat(makefile): support bundle image digests instead of tags

* fix(bundle): run make bundle after migration to operator-sdk 1.18.0

* chore(makefile): clean up env var declaration

* fix(bundle): run make bundle after migration to operator-sdk 1.19.0

* fix(bundle): run make bundle after migration to operator-sdk 1.20.0

* test(suite): fix suite test using global config

* build(deps): bump controller-runtime to 0.11.2 and k8s deps to 0.23.5

* fix(manifests): upgrade kube-rbac-proxy image from v0.8.0 to v0.11.0

* fix(bundle): run make bundle after migration to operator-sdk 1.21.0

* build(deps): bump controller-runtime to 0.12.1 and k8s deps to 0.24.0

* build(deps): bump go to 1.17

* build(deps): bump controller-gen to 0.9.0

* build(deps): bump go version to 1.18

* fix(bundle): run make bundle after migration to operator-sdk 1.22.0

* fix(bundle): run make bundle after migration to operator-sdk 1.22.1

* fix(bundle): run make bundle after migration to operator-sdk 1.22.2

* docs(deps): update deps requirement in README

* fix(gitignore): ignore generated cache directory

* fix(manifests,bundle): generate new manifests and bundle with controller-gen 0.9.0

* fix(makefile): add comments for shell flags

* feat(Makefile): add opm and catalog-build Makefile targets

* fix(manifests): add patch to remove cert-manager volume (commented out)

* fix(reconciler): only update deployment spect.selector if being created

* fix(deploy): merge label/annotations of spec.template when updating deployment

* feat(labels,annotations): labels/annotations map should be checked for nil

* fix(makefile): add comments explaining ENVTEST_K8S_VERSION var

* docs(build): add instructions for catalog-build target

* chore(deployment): correct function names for creating missing labels/annotations map

* chore(Makefile): use binary paths as download targets and rename SETUP_ENVTEST to ENVTEST

* fix(makefile): remove comments about setup-envtest.sh since replaced with setup-envtest binary

* fix(manifests): create patches to add minKubeVersion field in spec

* chore(manifests): clean up csv patches

* fix(ci): upgrade go version to 1.18

* fix(ci): upgrade operator-sdk to 1.22.2

* fix(makefile): use file binary paths as targets

* fix(makefile): remove testbin

* fix(makefile): use env to find bash

* fix(makefile): remove check for opm existence

* fix(manifests): use inline patch instead for removing cert volume

* fix(deployment): shorten field ref

* fix(deps): add back cve comment for gogo/protobuf

* fix(deps): add back replace directive for gogo/protobuf

* fix(deps): add back comments for cve-2020-26160

* fix(go-sum): run go mod tidy

* fix(makefile): add check for existence in binary download targets
  • Loading branch information
Thuan Vo committed Aug 5, 2022
1 parent 4e6f0c7 commit 3588058
Show file tree
Hide file tree
Showing 38 changed files with 2,180 additions and 732 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
- uses: actions/checkout@v2
- uses: actions/setup-go@v2
with:
go-version: '1.17.*'
go-version: '1.18.*'
- uses: actions/cache@v2
with:
path: |
Expand All @@ -35,7 +35,7 @@ jobs:
${{ runner.os }}-go-
- uses: jpkrohling/setup-operator-sdk@v1.1.0
with:
operator-sdk-version: v1.5.0
operator-sdk-version: v1.22.2
- run: make generate manifests manager test-envtest
if: ${{ github.event_name == 'pull_request' }}
- run: make oci-build
Expand Down
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ vendor/
*.so
*.dylib
bin
testbin/*

# Test binary, build with `go test -c`
*.test
Expand All @@ -102,3 +101,6 @@ testbin/*
*.swp
*.swo
*~

# Cache directory
cache/
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# Build the manager binary
FROM docker.io/library/golang:1.17 as builder
FROM docker.io/library/golang:1.18 as builder

WORKDIR /workspace
# Copy the Go Modules manifests
Expand Down
137 changes: 93 additions & 44 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
SHELL := /bin/bash
# Setting SHELL to bash allows bash commands to be executed by recipes.
# Options are set to exit when a recipe line exits non-zero or a piped command fails.
SHELL := /usr/bin/env bash -o pipefail
.SHELLFLAGS = -ec

# OS information
OS = $(shell go env GOOS)
ARCH = $(shell go env GOARCH)

# Current Operator version
IMAGE_VERSION ?= 2.2.0-dev
Expand All @@ -7,9 +14,18 @@ DEFAULT_NAMESPACE ?= quay.io/cryostat
IMAGE_NAMESPACE ?= $(DEFAULT_NAMESPACE)
OPERATOR_NAME ?= cryostat-operator
CLUSTER_CLIENT ?= kubectl
IMAGE_TAG_BASE ?= $(IMAGE_NAMESPACE)/$(OPERATOR_NAME)

# Default bundle image tag
BUNDLE_IMG ?= $(IMAGE_NAMESPACE)/$(OPERATOR_NAME)-bundle:$(BUNDLE_VERSION)
BUNDLE_IMG ?= $(IMAGE_TAG_BASE)-bundle:$(BUNDLE_VERSION)
BUNDLE_IMGS ?= $(BUNDLE_IMG)

# Default catalog image tag
CATALOG_IMG ?= $(IMAGE_TAG_BASE)-catalog:$(BUNDLE_VERSION)
ifneq ($(origin CATALOG_BASE_IMG), undefined)
FROM_INDEX_OPT := --from-index $(CATALOG_BASE_IMG)
endif

# Options for 'bundle-build'
ifneq ($(origin CHANNELS), undefined)
BUNDLE_CHANNELS := --channels=$(CHANNELS)
Expand All @@ -18,12 +34,20 @@ ifneq ($(origin DEFAULT_CHANNEL), undefined)
BUNDLE_DEFAULT_CHANNEL := --default-channel=$(DEFAULT_CHANNEL)
endif
BUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL)
# BUNDLE_GEN_FLAGS are the flags passed to the operator-sdk generate bundle command
BUNDLE_GEN_FLAGS ?= -q --overwrite --version $(BUNDLE_VERSION) $(BUNDLE_METADATA_OPTS)
# USE_IMAGE_DIGESTS defines if images are resolved via tags or digests
# You can enable this value if you would like to use SHA Based Digests
# To enable set flag to true
USE_IMAGE_DIGESTS ?= false
ifeq ($(USE_IMAGE_DIGESTS), true)
BUNDLE_GEN_FLAGS += --use-image-digests
endif

IMAGE_BUILDER ?= podman
# Image URL to use all building/pushing image targets
OPERATOR_IMG ?= $(IMAGE_NAMESPACE)/$(OPERATOR_NAME):$(IMAGE_VERSION)
# Produce CRDs that work back to Kubernetes 1.11 (no version conversion)
CRD_OPTIONS ?= "crd:trivialVersions=true,preserveUnknownFields=false"
OPERATOR_IMG ?= $(IMAGE_TAG_BASE):$(IMAGE_VERSION)


# Images used by the operator
CORE_NAMESPACE ?= $(DEFAULT_NAMESPACE)
Expand All @@ -48,8 +72,11 @@ CERT_MANAGER_MANIFEST ?= \
https://github.com/jetstack/cert-manager/releases/download/v$(CERT_MANAGER_VERSION)/cert-manager.yaml

KUSTOMIZE_VERSION ?= 3.8.7

CONTROLLER_GEN_VERSION ?= 0.9.0
ADDLICENSE_VERSION ?= 1.0.0
OPM_VERSION ?= 1.23.0
# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
ENVTEST_K8S_VERSION ?= 1.24

DEPLOY_NAMESPACE ?= cryostat-operator-system

Expand All @@ -75,19 +102,18 @@ ifneq ("$(wildcard $(GINKGO))","")
GO_TEST="$(GINKGO)" -cover -outputdir=.
endif

.PHONY: all
all: manager

# Run tests
.PHONY: test
test: test-envtest test-scorecard

# FIXME remove ACK_GINKGO_DEPRECATIONS when upgrading to ginkgo 2.0
.PHONY: test-envtest
ENVTEST_ASSETS_DIR=$(shell pwd)/testbin
test-envtest: generate fmt vet manifests
test-envtest: generate manifests fmt vet setup-envtest
ifneq ($(SKIP_TESTS), true)
mkdir -p $(ENVTEST_ASSETS_DIR)
test -f $(ENVTEST_ASSETS_DIR)/setup-envtest.sh || curl -sSLo $(ENVTEST_ASSETS_DIR)/setup-envtest.sh https://raw.githubusercontent.com/kubernetes-sigs/controller-runtime/v0.7.2/hack/setup-envtest.sh
source $(ENVTEST_ASSETS_DIR)/setup-envtest.sh; fetch_envtest_tools $(ENVTEST_ASSETS_DIR); setup_envtest_env $(ENVTEST_ASSETS_DIR); $(GO_TEST) -v -coverprofile cover.out ./...
ACK_GINKGO_DEPRECATIONS=1.16.5 KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" $(GO_TEST) -v -coverprofile cover.out ./...
endif

.PHONY: test-scorecard
Expand All @@ -96,8 +122,6 @@ ifneq ($(SKIP_TESTS), true)
operator-sdk scorecard bundle
endif



# Build manager binary
.PHONY: manager
manager: generate fmt vet
Expand All @@ -108,6 +132,10 @@ manager: generate fmt vet
run: generate fmt vet manifests
go run ./internal/main.go

ifndef ignore-not-found
ignore-not-found = false
endif

# Install CRDs into a cluster
.PHONY: install
install: manifests kustomize
Expand All @@ -116,7 +144,7 @@ install: manifests kustomize
# Uninstall CRDs from a cluster
.PHONY: uninstall
uninstall: manifests kustomize
- $(KUSTOMIZE) build config/crd | $(CLUSTER_CLIENT) delete -f -
- $(KUSTOMIZE) build config/crd | $(CLUSTER_CLIENT) delete --ignore-not-found=$(ignore-not-found) -f -

.PHONY: predeploy
predeploy:
Expand All @@ -139,14 +167,14 @@ endif
# UnDeploy controller from the configured Kubernetes cluster in ~/.kube/config
.PHONY: undeploy
undeploy:
- $(CLUSTER_CLIENT) delete recording --all
- $(CLUSTER_CLIENT) delete -f config/samples/operator_v1beta1_cryostat.yaml
- $(KUSTOMIZE) build config/default | $(CLUSTER_CLIENT) delete -f -
- $(CLUSTER_CLIENT) delete --ignore-not-found=$(ignore-not-found) recording --all
- $(CLUSTER_CLIENT) delete --ignore-not-found=$(ignore-not-found) -f config/samples/operator_v1beta1_cryostat.yaml
- $(KUSTOMIZE) build config/default | $(CLUSTER_CLIENT) delete --ignore-not-found=$(ignore-not-found) -f -

# Generate manifests e.g. CRD, RBAC etc.
.PHONY: manifests
manifests: controller-gen
$(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=role webhook paths="./..." output:crd:artifacts:config=config/crd/bases
$(CONTROLLER_GEN) rbac:roleName=role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases
envsubst < hack/image_tag_patch.yaml.in > config/default/image_tag_patch.yaml
envsubst < hack/image_pull_patch.yaml.in > config/default/image_pull_patch.yaml

Expand Down Expand Up @@ -197,41 +225,62 @@ check_cert_manager:
fi;\
fi

# Location to install dependencies
LOCALBIN ?= $(shell pwd)/bin
$(LOCALBIN):
mkdir -p $(LOCALBIN)

# Download controller-gen locally if necessary
CONTROLLER_GEN = $(shell pwd)/bin/controller-gen
controller-gen:
$(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.4.1)
CONTROLLER_GEN = $(LOCALBIN)/controller-gen
.PHONY: controller-gen
controller-gen: $(CONTROLLER_GEN)
$(CONTROLLER_GEN): $(LOCALBIN)
test -s $(CONTROLLER_GEN) || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@v$(CONTROLLER_GEN_VERSION)

# Download kustomize locally if necessary
KUSTOMIZE = $(shell pwd)/bin/kustomize
kustomize:
$(call go-get-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v3@v$(KUSTOMIZE_VERSION))
KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh"
KUSTOMIZE = $(LOCALBIN)/kustomize
.PHONY: kustomize
kustomize: $(KUSTOMIZE)
$(KUSTOMIZE): $(LOCALBIN)
test -s $(KUSTOMIZE) || curl -s $(KUSTOMIZE_INSTALL_SCRIPT) | bash -s -- $(subst v,,$(KUSTOMIZE_VERSION)) $(LOCALBIN)

# Download addlicense locally if necessary
ADDLICENSE = $(shell pwd)/bin/addlicense
addlicense:
$(call go-get-tool,$(ADDLICENSE),github.com/google/addlicense@v$(ADDLICENSE_VERSION))

# go-get-tool will 'go get' any package $2 and install it to $1.
PROJECT_DIR := $(shell dirname $(abspath $(lastword $(MAKEFILE_LIST))))
define go-get-tool
@[ -f $(1) ] || { \
set -e ;\
TMP_DIR=$$(mktemp -d) ;\
cd $$TMP_DIR ;\
go mod init tmp ;\
echo "Downloading $(2)" ;\
GOBIN=$(PROJECT_DIR)/bin go get $(2) ;\
rm -rf $$TMP_DIR ;\
}
endef
ADDLICENSE = $(LOCALBIN)/addlicense
.PHONY: addlicense
addlicense: $(ADDLICENSE)
$(ADDLICENSE): $(LOCALBIN)
test -s $(ADDLICENSE) || GOBIN=$(LOCALBIN) go install github.com/google/addlicense@v$(ADDLICENSE_VERSION)

# Download setup-envtest locally if necessary
ENVTEST = $(LOCALBIN)/setup-envtest
.PHONY: setup-envtest
setup-envtest: $(ENVTEST)
$(ENVTEST): $(LOCALBIN)
test -s $(ENVTEST) || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest

# Download opm locally if necessary
OPM = $(LOCALBIN)/opm
.PHONY: opm
opm: $(OPM)
$(OPM): $(LOCALBIN)
test -s $(OPM) || \
{ \
set -e ;\
curl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/v$(OPM_VERSION)/$(OS)-$(ARCH)-opm ;\
chmod +x $(OPM) ;\
}

.PHONY: catalog-build
catalog-build: opm
$(OPM) index add --container-tool $(IMAGE_BUILDER) --mode semver --tag $(CATALOG_IMG) --bundles $(BUNDLE_IMGS) $(FROM_INDEX_OPT)

# Generate bundle manifests and metadata, then validate generated files.
.PHONY: bundle
bundle: manifests kustomize
operator-sdk generate kustomize manifests -q
cd config/manager && $(KUSTOMIZE) edit set image controller=$(OPERATOR_IMG)
$(KUSTOMIZE) build config/manifests | operator-sdk generate bundle -q --overwrite --version $(BUNDLE_VERSION) $(BUNDLE_METADATA_OPTS)
$(KUSTOMIZE) build config/manifests | operator-sdk generate bundle $(BUNDLE_GEN_FLAGS)
operator-sdk bundle validate ./bundle

# Build the bundle image.
Expand Down Expand Up @@ -261,16 +310,16 @@ endif
undeploy_bundle:
- operator-sdk cleanup $(OPERATOR_NAME)

# Deploy a Cryostat instance
.PHONY: create_cryostat_cr
create_cryostat_cr: destroy_cryostat_cr
$(CLUSTER_CLIENT) create -f config/samples/operator_v1beta1_cryostat.yaml

# Undeploy a Cryostat instance
.PHONY: destroy_cryostat_cr
destroy_cryostat_cr:
- $(CLUSTER_CLIENT) delete -f config/samples/operator_v1beta1_cryostat.yaml



# Local development/testing helpers

.PHONY: sample_app
Expand Down
15 changes: 9 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,17 +14,17 @@ the core library providing a convenience wrapper and headless stubs for use of
JFR using JDK Mission Control internals.

* [cryostat](https://github.com/cryostatio/cryostat) for the main API
backend to detect JVMs and manage JFR
backend to detect JVMs and manage JFR.

* [cryostat-web](https://github.com/cryostatio/cryostat-web) for the React
graphical frontend included as a submodule in Cryostat and built into
Cryostat's OCI images.

* [jfr-datasource](https://github.com/cryostatio/jfr-datasource) for
the JFR datasource for Grafana
the JFR datasource for Grafana.

* [cryostat-grafana-dashboard](https://github.com/cryostatio/cryostat-grafana-dashboard)
for the Grafana dashboard
for the Grafana dashboard.

# Using
Once deployed, the `cryostat` instance can be accessed via web browser
Expand All @@ -51,8 +51,8 @@ kubectl get secret ${CRYOSTAT_NAME}-jmx-auth -o jsonpath='{$.data.CRYOSTAT_RJMX_

# Building
## Requirements
- `go` v1.16 or v1.17
- [`operator-sdk`](https://github.com/operator-framework/operator-sdk) v1.5.0
- `go` v1.18
- [`operator-sdk`](https://github.com/operator-framework/operator-sdk) v1.22.2
- [`cert-manager`](https://github.com/jetstack/cert-manager) v1.3.0+ (Recommended)
- `podman` or `docker`
- `ginkgo` (Optional)
Expand All @@ -67,12 +67,15 @@ binary to the local registry, tagged as
setting the environment variables `IMAGE_NAMESPACE` and `OPERATOR_NAME`.
`IMAGE_VERSION` can also be set to override the tagged version.

To create an OLM bundle, use `make bundle`. This will generate a CSV, CRDs and
`make bundle` will create an OLM bundle. This will generate a CSV, CRDs and
other manifests, and other required configurations for an OLM bundle versioned
with version `$IMAGE_VERSION` in the `bundle/` directory. `make bundle-build`
will create an OCI image of this bundle, which can then be pushed to an image
repository such as `quay.io`.

`make catalog-build` will build an OCI image of the operator catalog (i.e. index)
with version `$IMAGE_VERSION` that includes the bundle image of the same version.

# Setup / Deployment
## Bundle Deployment

Expand Down
1 change: 1 addition & 0 deletions api/v1beta1/flightrecorder_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ type JMXAuthSecret struct {
// +kubebuilder:storageversion
// +kubebuilder:object:root=true
// +kubebuilder:subresource:status
// +kubebuilder:deprecatedversion:warning="operator.cryostat.io/v1beta1 FlightRecorder is deprecated; please use the Cryostat web application or the Cryostat HTTP API to manage recordings"

// FlightRecorder represents a target Pod that is capable of creating JDK Flight Recordings
// using Cryostat. The Cryostat operator creates FlightRecorder objects when it finds
Expand Down
1 change: 1 addition & 0 deletions api/v1beta1/recording_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ type RecordingStatus struct {
// +kubebuilder:subresource:status
// +kubebuilder:storageversion
// +kubebuilder:resource:path=recordings,scope=Namespaced
// +kubebuilder:deprecatedversion:warning="operator.cryostat.io/v1beta1 Recording is deprecated; please use the Cryostat web application or the Cryostat HTTP API to manage recordings"

// Recording represents a JDK Flight Recording. Create a Recording object to instruct Cryostat to start a new
// recording for a Pod. An alternative to managing recordings with the Cryostat web application.
Expand Down
1 change: 1 addition & 0 deletions api/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion bundle.Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ LABEL operators.operatorframework.io.bundle.manifests.v1=manifests/
LABEL operators.operatorframework.io.bundle.metadata.v1=metadata/
LABEL operators.operatorframework.io.bundle.package.v1=cryostat-operator
LABEL operators.operatorframework.io.bundle.channels.v1=alpha
LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.22.2
LABEL operators.operatorframework.io.metrics.mediatype.v1=metrics+v1
LABEL operators.operatorframework.io.metrics.builder=operator-sdk-v1.4.0+git
LABEL operators.operatorframework.io.metrics.project_layout=go.kubebuilder.io/v3

# Labels for testing.
Expand Down

This file was deleted.

Loading

0 comments on commit 3588058

Please sign in to comment.