airshipctl/Makefile
2021-11-12 16:33:12 +00:00

405 lines
13 KiB
Makefile

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-<name of component> targets
airshipctl_IMG_TGT_NAME:=docker-image
# its main.go is in the root of repo - others have main.go in
# $(BIN_SRC_DIR)/<name of component>/main.go
airshipctl_FROM_PATH:=.
# and its Dockerfile is also in the root of repo - others have Dockerfile in
# $(BIN_SRC_DIR)/<name of component>/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-<component name> (possible to override)
# and docker-image-<component name>-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: 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