
# Image URL to use all building/pushing image targets
IMG ?= eks-provider:latest
IMG_AUDIT ?= audit:latest
# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary.
ENVTEST_K8S_VERSION = 1.28.3
# Name of the kind cluster for testing
KIND_CLUSTER ?= xaccount
# Name of the pulumi stack
PULUMI_STACK ?= xaccount
NS ?= $(shell whoami)
GO_MOD_NAME = $(shell go list -m)
GOOS ?= "linux"
GOARCH ?= $(shell go env GOARCH)

# Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set)
ifeq (,$(shell go env GOBIN))
GOBIN=$(shell go env GOPATH)/bin
else
GOBIN=$(shell go env GOBIN)
endif

# Setting SHELL to bash allows bash commands to be executed by recipes.
# This is a requirement for 'setup-envtest.sh' in the test target.
# 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

.PHONY: all
all: build

##@ General

# The help target prints out all targets with their descriptions organized
# beneath their categories. The categories are represented by '##@' and the
# target descriptions by '##'. The awk commands is responsible for reading the
# entire set of makefiles included in this invocation, looking for lines of the
# file as xyz: ## something, and then pretty-format the target and help. Then,
# if there's a line with ##@ something, that gets pretty-printed as a category.
# More info on the usage of ANSI control characters for terminal formatting:
# https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters
# More info on the awk command:
# http://linuxcommand.org/lc3_adv_awk.php

.PHONY: help
help: ## Display this help.
	@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n  make \033[36m<target>\033[0m\n"} /^[a-zA-Z_0-9-]+:.*?##/ { printf "  \033[36m%-15s\033[0m %s\n", $$1, $$2 } /^##@/ { printf "\n\033[1m%s\033[0m\n", substr($$0, 5) } ' $(MAKEFILE_LIST)

##@ Development

.PHONY: manifests
manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects.
	$(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="github.com/tidbcloud/eks-provider/api/..." output:crd:artifacts:config=config/crd/bases

.PHONY: generate
generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.
	$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="github.com/tidbcloud/eks-provider/api/..."

.PHONY: lint
lint: golangci-lint #aws-iam-policy-linter## Run linter tests.
	$(GOLANGCI_LINT) run ./...
	#$(AWS_IAM_POLICY_LINTER) ./...

.PHONY: lint-fix
lint-fix: golangci-lint ## Fix linting issues.
	$(GOLANGCI_LINT) run --fix ./...

.PHONY: sync
sync:
	git submodule update --init --recursive

.PHONY: sync-opv2-crd
sync-opv2-crd:
	cd ../ && bash ./.github/scripts/sync-tidb-operator-v2-crd.sh

.PHONY: test
test: sync manifests generate envtest lint ## Run linter, unit and integration tests.
	KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test $$(go list ./... | grep -v -E '/(infra-api|tests)/') -tags all -coverprofile cover.out


.PHONY: unit
unit: ## Run unit tests.
	KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" GOMEMLIMIT=4GiB GOGC=20 go test $$(go list ./... | grep -v -E '/(infra-api|tests)/') -tags unit -coverprofile unit-cover.out

unit-without-coverage: ## Run unit tests without coverage.
	KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" GOMEMLIMIT=4GiB GOGC=20 go test $$(go list ./... | grep -v -E '/(infra-api|tests)/') -tags unit

ENVTEST_ASSETS_DIR=$(shell pwd)/tests/testbin
export PATH := $(PATH):$(PWD)/tests/testbin/bin
.PHONY: integration
integration: envtest sync ginkgo ## Run integration tests.
# it's useless, comment out.
# 	KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" go test $$(go list ./... | grep -v -E '/(infra-api|tests)/') -v -tags integration -coverprofile integration-cover.out
	mkdir -p ./tests/coverage
	# Use Go-based setup-envtest to get KUBEBUILDER_ASSETS and run ginkgo directly.
	KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -p path)" \
		$(GINKGO) -output-dir=./tests/coverage/ --focus="$(or $(E2E_FOCUS), $(focus))" --timeout=10m --poll-progress-after=120s --poll-progress-interval=30s ./tests/integration/...

##@ Build

.PHONY: build
build:
	CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) go build -ldflags "-s -w" -o bin/manager main.go

.PHONY: build-manager
build-manager:
	CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) go build -ldflags "-s -w" -o bin/manager main.go

.PHONY: build-debug
build-debug: generate ## Build manager binary for debug.
	CGO_ENABLED=0 go build -gcflags="all=-N -l" -o bin/manager main.go

.PHONY: build-audit
build-audit:
	CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) go build -a -o bin/audit cmd/audit/main.go

.PHONY: build-%
build-%: BUILD_TARGET=$*
build-%: OUTPUT_DIR ?= bin
build-%:
	CGO_ENABLED=0 GOOS=$(GOOS) GOARCH=$(GOARCH) go build -ldflags "-s -w" -o $(OUTPUT_DIR)/$(BUILD_TARGET) $(GO_MOD_NAME)/$(BUILD_TARGET)

.PHONY: run
run: ## Run a controller from your host.
	go run ./main.go -base-kubeconfig pulumi/apiserver/kubeconfig -root-config=examples/eks-provider/root-config-dev.yaml --kubeconfig=kubeconfig

.PHONY: docker-build
docker-build: sync build ## Build docker image with the manager.
	mkdir -p helm-charts
	cp -r ../helm-charts helm-charts
	docker build -t ${IMG} .
	rm -rf helm-charts

.PHONY: docker-build-%
docker-build-%: BUILD_TARGET=$*
docker-build-%: IMG ?= $(BUILD_TARGET):latest
docker-build-%: sync
	make build-$(BUILD_TARGET) OUTPUT_DIR=$(BUILD_TARGET)/bin && docker build -t $(IMG) -f $(BUILD_TARGET)/Dockerfile $(BUILD_TARGET)

.PHONY: docker-build-audit
docker-build-audit: ## Build the audit docker image.
	docker build --file audit.Dockerfile -t ${IMG_AUDIT} .

local-build-audit: build-audit ## Build the audit docker image locally for arm64.
	GOARCH=${GOARCH} DOCKER_PLATFORMS=${DOCKER_PLATFORMS} docker build --file audit.local.Dockerfile -t ${IMG_AUDIT} .

.PHONY: docker-push
docker-push: ## Push docker image with the manager.
	docker push ${IMG}

.PHONY: docker-push-audit
docker-push-audit: docker-build-audit ## Push the audit docker image.
	docker push ${IMG_AUDIT}

.PHONY: reviewable
reviewable: lint-fix test manifests ## Run through basic checks for the test.
	go mod tidy
	git diff --exit-code

##@ Deployment

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

.PHONY: install
install: manifests kustomize ## Install CRDs into the K8s cluster specified in ~/.kube/config.
	$(KUSTOMIZE) build config/crd | kubectl apply -f -

.PHONY: uninstall
uninstall: manifests kustomize ## Uninstall CRDs from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
	$(KUSTOMIZE) build config/crd | kubectl delete --ignore-not-found=$(ignore-not-found) -f -

.PHONY: deploy
deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in ~/.kube/config.
	cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG}
	$(KUSTOMIZE) build config/default | kubectl apply -f -

.PHONY: undeploy
undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion.
	$(KUSTOMIZE) build config/default | kubectl delete --ignore-not-found=$(ignore-not-found) -f -

CONTROLLER_GEN = $(shell pwd)/bin/controller-gen
.PHONY: controller-gen
controller-gen: ## Download controller-gen locally if necessary.
	$(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.17.2)

GOLANGCI_LINT = $(shell pwd)/bin/golangci-lint
golangci-lint: ## Download golangci-lint locally if necessary
	$(call go-get-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.1.6)

AWS_IAM_POLICY_LINTER = $(shell pwd)/bin/aws-iam-policy-linter
.PHONY: aws-iam-policy-linter
aws-iam-policy-linter:
	CGO_ENABLED=0 GOOS=$(GOOS) go build -C ./cmd/aws-iam-policy-linter -ldflags "-s -w" -o $(AWS_IAM_POLICY_LINTER) ./main.go

LOCALBIN ?= $(shell pwd)/bin
KUSTOMIZE = $(shell pwd)/bin/kustomize
KUSTOMIZE_INSTALL_SCRIPT ?= "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh"
KUSTOMIZE_VERSION ?= v4.5.5
.PHONY: kustomize
kustomize: ## Download kustomize locally if necessary.
	test -s $(LOCALBIN)/kustomize || { curl -s $(KUSTOMIZE_INSTALL_SCRIPT) | bash -s -- $(subst v,,$(KUSTOMIZE_VERSION)) $(LOCALBIN); }

ENVTEST = $(shell pwd)/bin/setup-envtest
.PHONY: envtest
envtest: ## Download envtest-setup locally if necessary.
	$(call go-get-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest@v0.0.0-20221206203637-3da2de04734a)

# 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 install $(2) ;\
rm -rf $$TMP_DIR ;\
}
endef


build-debug-image:
	docker build -f Dockerfile-dev . -t eks-provider-debug:latest
	aws ecr get-login-password --region us-west-2 | docker login --username AWS --password-stdin 385595570414.dkr.ecr.us-west-2.amazonaws.com
	docker tag eks-provider:debug 385595570414.dkr.ecr.us-west-2.amazonaws.com/dbaas/dev/eks-provider-debug:latest
	docker push 385595570414.dkr.ecr.us-west-2.amazonaws.com/dbaas/dev/eks-provider:debug

GINKGO = $(shell pwd)/bin/ginkgo

ginkgo:
	$(call go-get-tool,$(GINKGO),github.com/onsi/ginkgo/v2/ginkgo@v2.22.1)

##@ QA
MOCKGEN = $(shell pwd)/bin/mockgen

.PHONY: mockgen
mockgen:
	$(call go-get-tool,$(MOCKGEN),$(call go-get-tool,$(MOCKGEN),go.uber.org/mock/mockgen@v0.5.2))
	@$(MOCKGEN) --version | grep "v0.5.2" || { echo "Error: Incorrect mockgen binary detected, please remove it and rerun this command"; exit 1; }
	$(MOCKGEN) -source=../pkg/cloud/aws/client.go -destination=../pkg/cloud/aws/client_mock.go -package=aws
	$(MOCKGEN) -source=../pkg/cloud/gcp/client.go -destination=../pkg/cloud/gcp/client_mock.go -package=gcp
	$(MOCKGEN) -source=../pkg/util/time/clock.go -destination=../pkg/util/time/clock_mock.go -package=time
	$(MOCKGEN) -source=../pkg/gitops/config_provider.go -destination=../pkg/gitops/config_provider_mock.go -package=gitops
	$(MOCKGEN) -source=./controllers/configchange/applier_hot_reload.go -destination=./controllers/configchange/applier_hot_reload_mock.go -package=configchange

.PHONY: k8s-upgrade-test
k8s-upgrade-test: mockgen envtest
	ACK_GINKGO_DEPRECATIONS=1.16.5 KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) -i -p path)" go test ./controllers/k8sversion/test/ -v -tags upgrader_integration
