SHELL := /bin/bash GIT_VERSION ?= v0.1.0 GIT_COMMIT ?= $(shell git rev-parse HEAD) BUILD_DATE ?= $(shell date -u +'%Y-%m-%dT%H:%M:%SZ') GIT_MODULE ?= opendev.org/airship/airshipctl/pkg/version LDFLAGS += -X ${GIT_MODULE}.gitVersion=${GIT_VERSION} LDFLAGS += -X ${GIT_MODULE}.gitCommit=${GIT_COMMIT} LDFLAGS += -X ${GIT_MODULE}.buildDate=${BUILD_DATE} GO_FLAGS := -ldflags '-extldflags "-static"' -tags=netgo -trimpath GO_FLAGS += -ldflags '$(LDFLAGS)' # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) ifeq (,$(shell go env GOBIN 2> /dev/null)) GOBIN = $(shell go env GOPATH 2> /dev/null)/bin else GOBIN = $(shell go env GOBIN 2> /dev/null) endif # Produce CRDs that work back to Kubernetes 1.21.2 CRD_OPTIONS ?= crd:crdVersions=v1 TOOLBINDIR := tools/bin # linting LINTER := $(TOOLBINDIR)/golangci-lint LINTER_CONFIG := .golangci.yaml # docker DOCKER_MAKE_TARGET := build DOCKER_CMD_FLAGS := # docker image options DOCKER_REGISTRY ?= quay.io DOCKER_FORCE_CLEAN ?= true DOCKER_IMAGE_NAME ?= airshipctl DOCKER_IMAGE_PREFIX ?= airshipit DOCKER_IMAGE_TAG ?= latest DOCKER_IMAGE ?= $(DOCKER_REGISTRY)/$(DOCKER_IMAGE_PREFIX)/$(DOCKER_IMAGE_NAME):$(DOCKER_IMAGE_TAG) DOCKER_TARGET_STAGE ?= release PUBLISH ?= false # use this variable for image labels added in internal build process COMMIT ?= $(shell git rev-parse HEAD) LABEL ?= org.airshipit.build=community LABEL += --label "org.opencontainers.image.revision=$(COMMIT)" LABEL += --label "org.opencontainers.image.created=$(shell date --rfc-3339=seconds --utc)" LABEL += --label "org.opencontainers.image.title=$(DOCKER_IMAGE_NAME)" # go options PKG ?= ./... TESTS ?= . TEST_FLAGS ?= COVER_FLAGS ?= COVER_PROFILE ?= cover.out COVER_EXCLUDE ?= (zz_generated|errors) # proxy options PROXY ?= http://proxy.foo.com:8000 NO_PROXY ?= localhost,127.0.0.1,.svc.cluster.local USE_PROXY ?= false # docker build flags DOCKER_CMD_FLAGS += --network=host DOCKER_CMD_FLAGS += --force-rm=$(DOCKER_FORCE_CLEAN) ifeq ($(USE_PROXY), true) DOCKER_CMD_FLAGS += --build-arg http_proxy=$(PROXY) DOCKER_CMD_FLAGS += --build-arg https_proxy=$(PROXY) DOCKER_CMD_FLAGS += --build-arg HTTP_PROXY=$(PROXY) DOCKER_CMD_FLAGS += --build-arg HTTPS_PROXY=$(PROXY) DOCKER_CMD_FLAGS += --build-arg no_proxy=$(NO_PROXY) DOCKER_CMD_FLAGS += --build-arg NO_PROXY=$(NO_PROXY) endif ifneq ($(strip $(GOPROXY)),) DOCKER_CMD_FLAGS += --build-arg GOPROXY=$(strip $(GOPROXY)) endif # Godoc server options GD_PORT ?= 8080 # Documentation location DOCS_DIR ?= docs # document validation options UNAME != uname export KIND_URL ?= https://kind.sigs.k8s.io/dl/v0.8.1/kind-$(UNAME)-amd64 KUBECTL_VERSION ?= v1.21.2 export KUBECTL_URL ?= https://storage.googleapis.com/kubernetes-release/release/${KUBECTL_VERSION}/bin/linux/amd64/kubectl .PHONY: depend depend: @go mod download .PHONY: build .PHONY: install install: depend install: @CGO_ENABLED=0 go install . # Core of build logic BIN_DIR := bin BIN_SRC_DIR := krm-functions BINS := airshipctl $(subst $(BIN_SRC_DIR)/,,$(wildcard $(BIN_SRC_DIR)/*)) IMGS := $(BINS) # This section sets the settings for different subcomponents # airshipctl is a special case - we need to override it manually: # its makefile target for image is 'docker-image' - others have # docker-image- targets airshipctl_IMG_TGT_NAME:=docker-image # its main.go is in the root of repo - others have main.go in # $(BIN_SRC_DIR)//main.go airshipctl_FROM_PATH:=. # and its Dockerfile is also in the root of repo - others have Dockerfile in # $(BIN_SRC_DIR)//Dockerfile docker-image_DOCKERFILE:=Dockerfile # kubeval-validator, toolbox and toolbox-virsh don't depend on # airshipctl repo. Their Dockerfiles don't # need to be called from the root of the repo. applier_IS_INDEPENDED:=true kubeval-validator_IS_INDEPENDED:=true clusterctl_IS_INDEPENDED:=true clusterctl-v0.3_IS_INDEPENDED:=true toolbox-virsh_IS_INDEPENDED:=true # in addition toolbox-virsh docker image needs toolbox docker image to be built first docker-image-clusterctl-v0.3_DEPENDENCY:=docker-image-clusterctl docker-image-toolbox-virsh_DEPENDENCY:=docker-image-toolbox # The template that generates targets for creating binaries per component: # Targets will be generated only for components that depend on airshipctl repo (part of that go module) # Note: expressions with ?= won't be executed if the values of that variable was already set to it. # Using that syntax it's possible to build values overrides for components. # Note 2: $$ is needed to instruct make-engine that variable should be used after template rendering. # When template is rendered all $ will be rendered in the template and $$ will be converted to $, e.g. # if we call map_binary_defaults_tmpl for airshipctl $1 will be converted to 'airshipctl' and we'll get # ifneq ($(airshipctl_IS_INDEPENDED),true) # airshipctl_FROM_PATH?=$(BIN_SRC_DIR)/airshipctl/main.go # ... # since we defining airshipctl_FROM_PATH above, and ?= is used in the 2nd line # airshipctl_FROM_PATH will stay the same as it was defined above. define map_binary_defaults_tmpl ifneq ($$($1_IS_INDEPENDED),true) $1_FROM_PATH?=$$(BIN_SRC_DIR)/$1/main.go $$(warning Adding dynamic target $$(BIN_DIR)/$1) $$(BIN_DIR)/$1: $$($1_FROM_PATH) depend @CGO_ENABLED=0 go build -o $$@ $$(GO_FLAGS) $$< $$(warning Adding dynamic target $1) .PHONY: $1 $1: $$(BIN_DIR)/$1 build: $1 endif endef map_binary_defaults = $(eval $(call map_binary_defaults_tmpl,$1)) # Go through all components and generate binary targets for each of them $(foreach bin,$(BINS),$(call map_binary_defaults,$(bin))) .PHONY: images .PHONY: images-publish # The template that generates targets for creating images per components # There is a special logic to handle per-components overrides # 2 targets will be generated per component: docker-image- (possible to override) # and docker-image--publish define map_image_defaults_tmpl $1_IMG_TGT_NAME?=docker-image-$1 $$($1_IMG_TGT_NAME)_IMG_TITLE?=$1 $$($1_IMG_TGT_NAME)_IMG_TAG?=$$(DOCKER_IMAGE_TAG) $$($1_IMG_TGT_NAME)_DOCKERTGT?=$$(DOCKER_TARGET_STAGE) $$($1_IMG_TGT_NAME)_DOCKERFILE?=$$(BIN_SRC_DIR)/$1/Dockerfile $$($1_IMG_TGT_NAME)_MAKETGT?=$$(BIN_DIR)/$1 ifeq ($$($1_IS_INDEPENDED),true) $$($1_IMG_TGT_NAME)_DOCKERROOT?=$$(BIN_SRC_DIR)/$1 else $$($1_IMG_TGT_NAME)_DOCKERROOT?=. endif ifneq ($1,airshipctl) ifneq ($$(origin DOCKER_BASE_PLUGINS_GO_IMAGE), undefined) $$($1_IMG_TGT_NAME)_BASE_GO_IMAGE?=$$(DOCKER_BASE_PLUGINS_GO_IMAGE) endif endif $$($1_IMG_TGT_NAME)_BASE_GO_IMAGE?=$$(DOCKER_BASE_GO_IMAGE) ifneq ($$(strip $$($$($1_IMG_TGT_NAME)_BASE_GO_IMAGE)),) $$($1_IMG_TGT_NAME)_BUILD_ARG += GO_IMAGE=$$($$($1_IMG_TGT_NAME)_BASE_GO_IMAGE) endif ifneq ($1,airshipctl) ifneq ($$(origin DOCKER_BASE_PLUGINS_BUILD_IMAGE), undefined) $$($1_IMG_TGT_NAME)_BASE_BUILD_IMAGE?=$$(DOCKER_BASE_PLUGINS_BUILD_IMAGE) endif endif $$($1_IMG_TGT_NAME)_BASE_BUILD_IMAGE?=$$(DOCKER_BASE_BUILD_IMAGE) ifneq ($$(strip $$($$($1_IMG_TGT_NAME)_BASE_BUILD_IMAGE)),) $$($1_IMG_TGT_NAME)_BUILD_ARG += BUILD_IMAGE=$$($$($1_IMG_TGT_NAME)_BASE_BUILD_IMAGE) endif ifneq ($1,airshipctl) ifneq ($$(origin DOCKER_BASE_PLUGINS_RELEASE_IMAGE), undefined) $$($1_IMG_TGT_NAME)_BASE_RELEASE_IMAGE?=$$(DOCKER_BASE_PLUGINS_RELEASE_IMAGE) endif endif $$($1_IMG_TGT_NAME)_BASE_RELEASE_IMAGE?=$$(DOCKER_BASE_RELEASE_IMAGE) ifneq ($$(strip $$($$($1_IMG_TGT_NAME)_BASE_RELEASE_IMAGE)),) $$($1_IMG_TGT_NAME)_BUILD_ARG += RELEASE_IMAGE=$$($$($1_IMG_TGT_NAME)_BASE_RELEASE_IMAGE) endif ifeq ($1,clusterctl-v0.3) $$($1_IMG_TGT_NAME)_IMG_TAG=v0.3 $$($1_IMG_TGT_NAME)_IMG_TITLE=clusterctl endif $$(warning Adding dynamic target $$($1_IMG_TGT_NAME)) .PHONY: $$($1_IMG_TGT_NAME) $$($1_IMG_TGT_NAME): $$($$($1_IMG_TGT_NAME)_DEPENDENCY) docker build $$($$($1_IMG_TGT_NAME)_DOCKERROOT) $$(DOCKER_CMD_FLAGS)\ --file $$($$($1_IMG_TGT_NAME)_DOCKERFILE) \ --label $$(LABEL) \ --label "org.opencontainers.image.revision=$$(COMMIT)" \ --label "org.opencontainers.image.created=$$(shell date --rfc-3339=seconds --utc)" \ --label "org.opencontainers.image.title=$$($$($1_IMG_TGT_NAME)_IMG_TITLE)" \ --target $$($$($1_IMG_TGT_NAME)_DOCKERTGT) \ $$(addprefix --build-arg ,$$($$($1_IMG_TGT_NAME)_BUILD_ARG)) \ --build-arg MAKE_TARGET=$$($$($1_IMG_TGT_NAME)_MAKETGT) \ --tag $$(DOCKER_REGISTRY)/$$(DOCKER_IMAGE_PREFIX)/$$($$($1_IMG_TGT_NAME)_IMG_TITLE):$$($$($1_IMG_TGT_NAME)_IMG_TAG) \ $$(foreach tag,$$(DOCKER_IMAGE_EXTRA_TAGS),--tag $$(DOCKER_REGISTRY)/$$(DOCKER_IMAGE_PREFIX)/$1:$$(tag) ) ifeq ($$(PUBLISH), true) @docker push $$(DOCKER_REGISTRY)/$$(DOCKER_IMAGE_PREFIX)/$$($$($1_IMG_TGT_NAME)_IMG_TITLE):$$($$($1_IMG_TGT_NAME)_IMG_TAG) endif images: $$($1_IMG_TGT_NAME) $$(warning Adding dynamic target $$($1_IMG_TGT_NAME)-publish) .PHONY: $$($1_IMG_TGT_NAME)-publish $$($1_IMG_TGT_NAME)-publish: $$($1_IMG_TGT_NAME) @docker push $$(DOCKER_REGISTRY)/$$(DOCKER_IMAGE_PREFIX)/$1:$$(DOCKER_IMAGE_TAG) images-publish: $$($1_IMG_TGT_NAME)-publish endef map_image_defaults = $(eval $(call map_image_defaults_tmpl,$1)) # go through components and render the template $(foreach img,$(IMGS),$(call map_image_defaults,$(img))) .PHONY: test test: lint test: cover test: check-copyright .PHONY: unit-tests unit-tests: TESTFLAGS += -race -v unit-tests: @echo "Performing unit test step..." @go test -run $(TESTS) $(PKG) $(TESTFLAGS) $(COVER_FLAGS) @echo "All unit tests passed" .PHONY: cover cover: COVER_FLAGS = -covermode=atomic -coverprofile=fullcover.out cover: unit-tests @grep -vE "$(COVER_EXCLUDE)" fullcover.out > $(COVER_PROFILE) @./tools/coverage_check $(COVER_PROFILE) .PHONY: fmt fmt: lint .PHONY: lint lint: tidy lint: $(LINTER) @echo "Performing linting step..." @./tools/whitespace_linter @./$(LINTER) run --config $(LINTER_CONFIG) @echo "Linting completed successfully" .PHONY: tidy tidy: @echo "Checking that go.mod is up to date..." @./tools/gomod_check @echo "go.mod is up to date" .PHONY: golint golint: @./tools/golint .PHONY: print-docker-image-tag print-docker-image-tag: @echo "$(DOCKER_IMAGE)" .PHONY: docker-image-test-suite docker-image-test-suite: docker-image_MAKETGT = "cover update-golden generate check-git-diff" docker-image-test-suite: docker-image_DOCKERTGT = builder docker-image-test-suite: docker-image .PHONY: docker-image-unit-tests docker-image-unit-tests: docker-image_MAKETGT = cover docker-image-unit-tests: docker-image_DOCKERTGT = builder docker-image-unit-tests: docker-image .PHONY: docker-image-lint docker-image-lint: docker-image_MAKETGT = "lint check-copyright" docker-image-lint: docker-image_DOCKERTGT = builder docker-image-lint: docker-image .PHONY: docker-image-golint docker-image-golint: docker-image_MAKETGT = golint docker-image-golint: docker-image_DOCKERTGT = builder docker-image-golint: docker-image .PHONY: docker-image-check-manifests docker-image-check-manifests: docker-image_MAKETGT = "generate manifests check-git-diff" docker-image-check-manifests: docker-image_DOCKERTGT = builder docker-image-check-manifests: docker-image .PHONY: clean clean: @rm -fr $(BIN_DIR) @rm -fr $(COVER_PROFILE) .PHONY: docs docs: tox .PHONY: godoc godoc: @go install golang.org/x/tools/cmd/godoc @echo "Follow this link to package documentation: http://localhost:${GD_PORT}/pkg/opendev.org/airship/airshipctl/" @godoc -http=":${GD_PORT}" .PHONY: cli-docs cli-docs: @echo "Generating CLI documentation..." @go run $(DOCS_DIR)/tools/generate_cli_docs.go @echo "CLI documentation generated" .PHONY: releasenotes releasenotes: @echo "TODO" $(TOOLBINDIR): mkdir -p $(TOOLBINDIR) $(LINTER): $(TOOLBINDIR) ./tools/install_linter .PHONY: update-golden update-golden: delete-golden update-golden: TESTFLAGS += -update update-golden: PKG = opendev.org/airship/airshipctl/cmd/... update-golden: unit-tests update-golden: cli-docs # The delete-golden target is a utility for update-golden .PHONY: delete-golden delete-golden: @find . -type f -name "*.golden" -delete # Used by gates after unit-tests and update-golden targets to ensure no files are deleted. .PHONY: check-git-diff check-git-diff: @./tools/git_diff_check # add-copyright is a utility to add copyright header to missing files .PHONY: add-copyright add-copyright: @./tools/add_license.sh # check-copyright is a utility to check if copyright header is present on all files .PHONY: check-copyright check-copyright: @./tools/check_copyright # Validate YAMLs for all sites .PHONY: validate-docs validate-docs: @./tools/validate_docs # Validate all URL references in documentation work .PHONY: dead-link-linter dead-link-linter: @./tools/dead-link-linter # Generate code generate: controller-gen $(CONTROLLER_GEN) object:headerFile="tools/license_go.txt" paths="./..." # find or download controller-gen # download controller-gen if necessary controller-gen: ifeq (, $(shell which controller-gen)) @{ \ set -e ;\ CONTROLLER_GEN_TMP_DIR=$$(mktemp -d) ;\ cd $$CONTROLLER_GEN_TMP_DIR ;\ go mod init tmp ;\ go get sigs.k8s.io/controller-tools/cmd/controller-gen@v0.6.2 ;\ rm -rf $$CONTROLLER_GEN_TMP_DIR ;\ } CONTROLLER_GEN=$(GOBIN)/controller-gen else CONTROLLER_GEN=$(shell which controller-gen) endif # Generate manifests e.g. CRD, RBAC etc. manifests: controller-gen find manifests/function/airshipctl-schemas/ -type f -not -name 'kustomization.yaml' -delete && $(CONTROLLER_GEN) $(CRD_OPTIONS) rbac:roleName=manager-role webhook paths="./..." output:crd:artifacts:config=manifests/function/airshipctl-schemas