Files
releases/Makefile
T
Sebastian Spaink 8e2f1807ac config: validate configuration with Rego and warn on unknown options (#8891)
Part of #2745

Like most of his ideas, @anderseknert's suggestion to use Rego to
replace the `validateAndInjectDefaults` functions throughout the
codebase is another winner.

This PR starts the migration by replacing the top-level
`validateAndInjectDefaults` in `v1/config/config.go` with an embedded
policy, `validate.rego`. The policy injects the top-level defaults
(`default_decision`, `default_authorization_decision`, `labels`) and
reports unrecognized configuration options, so a typo such as
`decision_log` instead of `decision_logs` is logged as a warning at
startup rather than silently ignored.

It's evaluated in `ParseConfig` using the low-level `ast`/`topdown`
packages rather than the top-level `rego` package. This keeps `config`
off the heavy `rego → bundle → …` dependency web (which would otherwise
create import cycles as more packages' tests reach `config`), and we
don't need any of the `rego` package's conveniences here — it's one
module compiled once and a single query. The Rego unit tests run in CI
via `build/run-rego-tests.sh` (and locally with `make rego-test`).

This sets the foundation for the other plugin
`validateAndInjectDefaults` functions to migrate to Rego as well; where
the logic isn't too complicated it should be a fairly easy replacement.
At the moment all known keys live in `validate.rego` under `_specs` to
support the "warn on unrecognized options" check, but the
plugin-specific entries can move closer to each plugin as it migrates.
It would also be nice for `_specs` to be auto-generated somehow in the
future.

Supporting extension of config validation with custom policies is
something I'd like to follow up with, so keeping #2745 open for now.

I also think these policies could be reusable with
[java-opa-sdk](https://github.com/open-policy-agent/java-opa-sdk) 👀

Signed-off-by: Sebastian Spaink <sebastianspaink@gmail.com>
2026-07-15 11:16:03 -05:00

562 lines
16 KiB
Makefile

# Copyright 2016 The OPA Authors. All rights reserved.
# Use of this source code is governed by an Apache2
# license that can be found in the LICENSE file.
VERSION := $(shell ./build/get-build-version.sh)
GOFLAGS ?= "-buildmode=exe"
# See https://golang.org/cmd/go/#hdr-Build_modes:
# > -buildmode=exe
# > Build the listed main packages and everything they import into
# > executables. Packages not named main are ignored.
GO := CGO_ENABLED=0 GOFLAGS="$(GOFLAGS)" go
GO_TEST_TIMEOUT := -timeout 30m
GOVERSION ?= $(shell cat ./.go-version)
GOARCH := $(shell go env GOARCH)
GOOS := $(shell go env GOOS)
GO_TAGS ?=
override GO_TAGS := $(GO_TAGS) -tags=opa_wasm
GOLANGCI_LINT_VERSION := v2.12.2
YAML_LINT_VERSION := 0.29.0
YAML_LINT_FORMAT ?= auto
export DOCKER_RUNNING ?= $(shell docker ps >/dev/null 2>&1 && echo 1 || echo 0)
# For image, the UID/GID is overridden so that the built binary isn't root-owned.
DOCKER_UID ?= 0
DOCKER_GID ?= 0
ifeq ($(shell tty > /dev/null && echo 1 || echo 0), 1)
DOCKER_FLAGS := --rm -it
else
DOCKER_FLAGS := --rm
endif
DOCKER := docker
# BuildKit is required for automatic platform arg injection (see Dockerfile)
export DOCKER_BUILDKIT := 1
# Supported platforms to include in image manifest lists
DOCKER_PLATFORMS := linux/amd64,linux/arm64
BIN := opa_$(GOOS)_$(GOARCH)
# Optional external configuration useful for forks of OPA
DOCKER_IMAGE ?= openpolicyagent/opa
S3_RELEASE_BUCKET ?= opa-releases
FUZZ_TIME ?= 1h
VERSION_CHECK_URL ?= #Default empty
BUILD_HOSTNAME := $(shell ./build/get-build-hostname.sh)
RELEASE_BUILD_IMAGE := golang:$(GOVERSION)-trixie
RELEASE_DIR ?= _release/$(VERSION)
ifneq (,$(VERSION_CHECK_URL))
VERSION_CHECK_SERVICE_FLAG := -X github.com/open-policy-agent/opa/internal/versioncheck.ExternalServiceURL=$(VERSION_CHECK_URL)
endif
LDFLAGS := "$(VERSION_CHECK_SERVICE_FLAG) \
-X github.com/open-policy-agent/opa/version.Hostname=$(BUILD_HOSTNAME)"
######################################################
#
# Development targets
#
######################################################
# If you update the 'all' target make sure the 'ci-release-test' target is consistent.
.PHONY: all
all: build test perf wasm-sdk-e2e-test check
.PHONY: version
version:
@echo $(VERSION)
.PHONY: release-dir
release-dir:
@echo $(RELEASE_DIR)
.PHONY: generate
generate: wasm-lib-build
ifeq ($(GOOS),windows)
cd build/tools && CGO_ENABLED=0 GOOS=linux go install tool
endif
$(GO) generate
.PHONY: generate-proto
generate-proto:
cd build/tools && $(GO) build -o $(CURDIR)/build/tools/bin/protoc-gen-go google.golang.org/protobuf/cmd/protoc-gen-go
PATH="$(CURDIR)/build/tools/bin:$$PATH" protoc \
--go_out=. \
--go_opt=module=github.com/open-policy-agent/opa \
v1/ir/plan.proto \
v1/bundle/manifest.proto
.PHONY: build
build: go-build
.PHONY: image
image:
DOCKER_UID=$(shell id -u) DOCKER_GID=$(shell id -g) $(MAKE) ci-go-ci-build-linux ci-go-ci-build-linux-static
@$(MAKE) image-quick
.PHONY: install
install: generate
$(GO) install $(GO_TAGS) -ldflags $(LDFLAGS)
.PHONY: test
test: go-test wasm-test
.PHONY: e2e e2e-prep
e2e: e2e-prep
cd e2e/ && OPA=$(CURDIR)/$(BIN) $(GO) test $(GO_TAGS) -v ./...
e2e-prep: build
cd e2e/api/compile/prisma && npm ci && DATABASE_URL='postgres://127.0.0.1/dummy' npx prisma generate
cd e2e/ && go mod tidy
.PHONY: test-short
test-short: go-test-short
.PHONY: go-build
go-build: generate
$(GO) build $(GO_TAGS) -o $(BIN) -ldflags $(LDFLAGS)
.PHONY: go-test
go-test: generate
$(GO) test $(GO_TAGS),slow ./...
.PHONY: go-test-short
go-test-short: generate
$(GO) test $(GO_TAGS) -short ./...
.PHONY: rego-test
rego-test: go-build
OPA=$(CURDIR)/$(BIN) ./build/run-rego-tests.sh
.PHONY: race-detector
race-detector: generate
CGO_ENABLED=1 GOFLAGS="$(GOFLAGS)" go test $(GO_TAGS),slow -race -vet=off ./...
.PHONY: test-coverage
test-coverage: generate
$(GO) test $(GO_TAGS),slow -coverprofile=coverage.txt -covermode=atomic ./...
.PHONY: perf
perf: generate
$(GO) test $(GO_TAGS),slow $(GO_TEST_TIMEOUT) -run=- -bench=. -benchmem ./...
.PHONY: perf-noisy
perf-noisy: generate
$(GO) test $(GO_TAGS),slow,noisy $(GO_TEST_TIMEOUT) -run=- -bench=. -benchmem ./...
.PHONY: wasm-sdk-e2e-test
wasm-sdk-e2e-test: generate
$(GO) test $(GO_TAGS),slow,wasm_sdk_e2e $(GO_TEST_TIMEOUT) ./internal/wasm/sdk/test/e2e
.PHONY: check
check:
ifeq ($(DOCKER_RUNNING), 1)
$(DOCKER) run --rm -v $(shell pwd):/app:ro,Z -w /app golangci/golangci-lint:${GOLANGCI_LINT_VERSION} golangci-lint run -v
else
@echo "Docker not installed or running. Skipping golangci run."
endif
.PHONY: fmt
fmt:
ifeq ($(DOCKER_RUNNING), 1)
$(DOCKER) run --rm -v $(shell pwd):/app:Z -w /app golangci/golangci-lint:${GOLANGCI_LINT_VERSION} golangci-lint run -v --fix
else
@echo "Docker not installed or running. Skipping golangci run."
endif
.PHONY: clean
clean: wasm-lib-clean
rm -f opa_*_*
.PHONY: fuzz
fuzz:
go test ./ast -fuzz FuzzParseStatementsAndCompileModules -fuzztime ${FUZZ_TIME} -v -run '^$$'
######################################################
#
# Documentation targets
#
######################################################
# The docs-% pattern target will shim to the
# makefile in ./docs
.PHONY: docs-%
docs-%:
$(MAKE) -C docs $*
.PHONY: man
man:
./build/gen-man.sh man
######################################################
#
# Linux distro package targets
#
######################################################
.PHONY: deb
deb:
VERSION=$(VERSION) ./build/gen-deb.sh
######################################################
#
# Wasm targets
#
######################################################
.PHONY: wasm-test
wasm-test: wasm-lib-test wasm-rego-test
.PHONY: wasm-lib-build
wasm-lib-build:
ifeq ($(DOCKER_RUNNING), 1)
@$(MAKE) -C wasm ensure-builder build
cp wasm/_obj/opa.wasm internal/compiler/wasm/opa/opa.wasm
cp wasm/_obj/callgraph.csv internal/compiler/wasm/opa/callgraph.csv
else
@echo "Docker not installed or not running. Skipping OPA-WASM library build."
endif
.PHONY: wasm-lib-test
wasm-lib-test:
ifeq ($(DOCKER_RUNNING), 1)
@$(MAKE) -C wasm ensure-builder test
else
@echo "Docker not installed or not running. Skipping OPA-WASM library test."
endif
.PHONY: wasm-rego-test
wasm-rego-test: generate
ifeq ($(DOCKER_RUNNING), 1)
GOVERSION=$(GOVERSION) DOCKER_UID=$(DOCKER_UID) DOCKER_GID=$(DOCKER_GID) ./build/run-wasm-rego-tests.sh
else
@echo "Docker not installed or not running. Skipping Rego-WASM test."
endif
.PHONY: wasm-lib-clean
wasm-lib-clean:
@$(MAKE) -C wasm clean
.PHONY: wasm-rego-testgen-install
wasm-rego-testgen-install:
$(GO) install ./v1/test/wasm/cmd/wasm-rego-testgen
######################################################
#
# CI targets
#
######################################################
CI_GOLANG_DOCKER_MAKE := $(DOCKER) run \
$(DOCKER_FLAGS) \
-u $(DOCKER_UID):$(DOCKER_GID) \
-v $(PWD):/src \
-w /src \
-e GOCACHE=/src/.go/cache \
-e GOARCH=$(GOARCH) \
-e FUZZ_TIME=$(FUZZ_TIME) \
-e VERSION_CHECK_URL=$(VERSION_CHECK_URL) \
$(RELEASE_BUILD_IMAGE)
.PHONY: ci-go-%
ci-go-%: generate
$(CI_GOLANG_DOCKER_MAKE) /bin/bash -c "git config --system --add safe.directory /src && make $*"
.PHONY: ci-release-test
ci-release-test: generate
$(CI_GOLANG_DOCKER_MAKE) make test perf wasm-sdk-e2e-test check
.PHONY: ci-check-working-copy
ci-check-working-copy: generate
./build/check-working-copy.sh
.PHONY: ci-wasm
ci-wasm: wasm-test
.PHONY: ci-build-linux
ci-build-linux: ensure-release-dir
@$(MAKE) build GOOS=linux
chmod +x opa_linux_$(GOARCH)
mv opa_linux_$(GOARCH) $(RELEASE_DIR)/
cd $(RELEASE_DIR)/ && shasum -a 256 opa_linux_$(GOARCH) > opa_linux_$(GOARCH).sha256
.PHONY: ci-build-linux-static
ci-build-linux-static: ensure-release-dir
@$(MAKE) build GOOS=linux
chmod +x opa_linux_$(GOARCH)
mv opa_linux_$(GOARCH) $(RELEASE_DIR)/opa_linux_$(GOARCH)_static
cd $(RELEASE_DIR)/ && shasum -a 256 opa_linux_$(GOARCH)_static > opa_linux_$(GOARCH)_static.sha256
.PHONY: ci-build-darwin
ci-build-darwin: ensure-release-dir
@$(MAKE) build GOOS=darwin
chmod +x opa_darwin_$(GOARCH)
mv opa_darwin_$(GOARCH) $(RELEASE_DIR)/
cd $(RELEASE_DIR)/ && shasum -a 256 opa_darwin_$(GOARCH) > opa_darwin_$(GOARCH).sha256
.PHONY: ci-build-darwin-arm64-static
ci-build-darwin-arm64-static: ensure-release-dir
@$(MAKE) build GOOS=darwin GOARCH=arm64
chmod +x opa_darwin_arm64
mv opa_darwin_arm64 $(RELEASE_DIR)/opa_darwin_arm64_static
cd $(RELEASE_DIR)/ && shasum -a 256 opa_darwin_arm64_static > opa_darwin_arm64_static.sha256
.PHONY: ci-build-windows
ci-build-windows: generate ensure-release-dir
@$(MAKE) build GOOS=windows CC="zig cc -target x86_64-windows-gnu -lunwind"
mv opa_windows_$(GOARCH) $(RELEASE_DIR)/opa_windows_$(GOARCH).exe
cd $(RELEASE_DIR)/ && shasum -a 256 opa_windows_$(GOARCH).exe > opa_windows_$(GOARCH).exe.sha256
rm resource.syso
.PHONY: ensure-release-dir
ensure-release-dir:
mkdir -p $(RELEASE_DIR)
.PHONY: ensure-executable-bin
ensure-executable-bin:
find $(RELEASE_DIR) -type f ! -name "*.sha256" | xargs chmod +x
.PHONY: build-all-platforms
build-all-platforms: ci-build-linux ci-build-linux-static ci-build-darwin ci-build-darwin-arm64-static ci-build-windows
.PHONY: image-quick
image-quick: image-quick-$(GOARCH)
# % = arch
.PHONY: image-quick-%
image-quick-%: ensure-executable-bin
$(DOCKER) build \
-t $(DOCKER_IMAGE):$(VERSION) \
--build-arg BASE=chainguard/static:latest \
--build-arg BIN_DIR=$(RELEASE_DIR) \
--platform linux/$* \
.
$(DOCKER) build \
-t $(DOCKER_IMAGE):$(VERSION)-debug \
--build-arg BASE=chainguard/busybox:latest \
--build-arg BIN_DIR=$(RELEASE_DIR) \
--platform linux/$* \
.
$(DOCKER) build \
-t $(DOCKER_IMAGE):$(VERSION)-static \
--build-arg BASE=chainguard/static:latest \
--build-arg BIN_DIR=$(RELEASE_DIR) \
--build-arg BIN_SUFFIX=_static \
--platform linux/$* \
.
$(DOCKER) build \
-t $(DOCKER_IMAGE):$(VERSION)-static-debug \
--build-arg BASE=chainguard/busybox:latest \
--build-arg BIN_DIR=$(RELEASE_DIR) \
--build-arg BIN_SUFFIX=_static \
--platform linux/$* \
.
# % = base tag
.PHONY: push-manifest-list-%
push-manifest-list-%: ensure-executable-bin
$(DOCKER) buildx build \
--tag $(DOCKER_IMAGE):$* \
--build-arg BASE=chainguard/static:latest \
--build-arg BIN_DIR=$(RELEASE_DIR) \
--platform $(DOCKER_PLATFORMS) \
--provenance=false \
--push \
.
$(DOCKER) buildx build \
--tag $(DOCKER_IMAGE):$*-debug \
--build-arg BASE=chainguard/busybox:latest \
--build-arg BIN_DIR=$(RELEASE_DIR) \
--platform $(DOCKER_PLATFORMS) \
--provenance=false \
--push \
.
$(DOCKER) buildx build \
--tag $(DOCKER_IMAGE):$*-static \
--build-arg BASE=chainguard/static:latest \
--build-arg BIN_DIR=$(RELEASE_DIR) \
--build-arg BIN_SUFFIX=_static \
--platform $(DOCKER_PLATFORMS) \
--provenance=false \
--push \
.
$(DOCKER) buildx build \
--tag $(DOCKER_IMAGE):$*-static-debug \
--build-arg BASE=chainguard/busybox:latest \
--build-arg BIN_DIR=$(RELEASE_DIR) \
--build-arg BIN_SUFFIX=_static \
--platform $(DOCKER_PLATFORMS) \
--provenance=false \
--push \
.
.PHONY: ci-image-smoke-test
ci-image-smoke-test: ci-image-smoke-test-$(GOARCH)
# % = arch
.PHONY: ci-image-smoke-test-%
ci-image-smoke-test-%: image-quick-%
$(DOCKER) run --platform linux/$* $(DOCKER_IMAGE):$(VERSION) version
$(DOCKER) run --platform linux/$* $(DOCKER_IMAGE):$(VERSION)-debug version
$(DOCKER) image inspect $(DOCKER_IMAGE):$(VERSION) |\
$(DOCKER) run --interactive --platform linux/$* $(DOCKER_IMAGE):$(VERSION) \
eval --fail --format raw --stdin-input 'input[0].Config.User = "1000:1000"'
$(DOCKER) run --platform linux/$* $(DOCKER_IMAGE):$(VERSION)-static version
.PHONY: docker-login
docker-login:
@echo "Docker Login..."
@echo ${DOCKER_PASSWORD} | $(DOCKER) login -u ${DOCKER_USER} --password-stdin
.PHONY: push-image
push-image: docker-login push-manifest-list-$(VERSION)
.PHONY: push-wasm-builder-image
push-wasm-builder-image: docker-login
$(MAKE) -C wasm push-builder
.PHONY: deploy-ci
deploy-ci: push-image push-manifest-list-edge
.PHONY: release-ci
# Don't tag and push "latest" image tags if the version is a release candidate or a bugfix branch
# where the changes don't exist in main
ifneq (,$(or $(findstring rc,$(VERSION)), $(findstring release-,$(shell git branch --contains HEAD))))
release-ci: push-image
else
release-ci: push-image push-manifest-list-latest
endif
.PHONY: netlify-latest
netlify-latest: docs-clean docs-ci docs-build-latest
.PHONY: netlify-edge
netlify-edge: docs-clean docs-ci docs-build
# Kept for compatibility. Use `make fuzz` instead.
.PHONY: check-fuzz
check-fuzz: fuzz
# GOPRIVATE=* causes go to fetch all dependencies from their corresponding VCS
# source, not through the golang-provided proxy services. We're cleaning out
# /src/.go by providing a tmpfs mount, so the `go mod vendor -v` command will
# not be able to use any module cache.
.PHONY: check-go-module
check-go-module:
$(DOCKER) run \
$(DOCKER_FLAGS) \
-w /src \
-v $(PWD):/src:Z \
-e 'GOPRIVATE=*' \
--tmpfs /src/.go \
$(RELEASE_BUILD_IMAGE) \
/bin/bash -c "git config --system --add safe.directory /src && go mod vendor -v"
.PHONY: check-yaml-tests
check-yaml-tests:
ifeq ($(DOCKER_RUNNING), 1)
$(DOCKER) run --rm -v $(shell pwd):/data:ro,Z -w /data pipelinecomponents/yamllint:${YAML_LINT_VERSION} yamllint -f $(YAML_LINT_FORMAT) v1/test/cases/testdata
else
@echo "Docker not installed or running. Skipping yamllint run."
endif
######################################################
#
# Release targets
#
######################################################
PATCH_CONTAINER_LABEL ?= opa-release-patcher
.PHONY: patch-container
patch-container:
ifeq ($(shell docker images -q $(PATCH_CONTAINER_LABEL) 2> /dev/null),)
@$(DOCKER) build \
-t $(PATCH_CONTAINER_LABEL) \
-f build/release-patcher-Dockerfile .
endif
.PHONY: release-patch
release-patch: patch-container
ifeq ($(GITHUB_TOKEN),)
@echo "\033[0;31mGITHUB_TOKEN environment variable missing.\033[33m Provide a GitHub Personal Access Token (PAT) with the 'read:org' scope.\033[0m"
endif
@$(DOCKER) run $(DOCKER_FLAGS) \
-e GITHUB_TOKEN=$(GITHUB_TOKEN) \
-e LAST_VERSION=$(LAST_VERSION) \
-v $(PWD):/_src:Z \
$(PATCH_CONTAINER_LABEL):latest \
/_src/build/gen-release-patch.sh --version=$(VERSION) --source-url=/_src
.PHONY: dev-patch
dev-patch: patch-container
@$(DOCKER) run $(DOCKER_FLAGS) \
-v $(PWD):/_src:Z \
$(PATCH_CONTAINER_LABEL):latest \
/_src/build/gen-dev-patch.sh --version=$(VERSION) --source-url=/_src
# Deprecated targets. To be removed.
.PHONY: build-linux depr-build-linux build-windows depr-build-windows build-darwin depr-build-darwin release release-local
build-linux: deprecation-build-linux
build-windows: deprecation-build-windows
build-darwin: deprecation-build-darwin
release: deprecation-release
release-local: deprecation-release-local
.PHONY: deprecation-%
deprecation-%:
@echo "----------------------------------------------"
@echo "The '$*' make target is deprecated!"
@echo "----------------------------------------------"
@echo "To run build for your platform, use 'make build'."
@echo "To cross-compile for a specific platform, use the corresponding 'ci-build-*' target."
@echo
@$(MAKE) depr-$*
depr-build-linux: ensure-release-dir
@$(MAKE) build GOOS=linux
mv opa_linux_$(GOARCH) $(RELEASE_DIR)/
depr-build-darwin: ensure-release-dir
@$(MAKE) build GOOS=darwin
mv opa_darwin_$(GOARCH) $(RELEASE_DIR)/
depr-build-windows: ensure-release-dir
@$(MAKE) build GOOS=windows
mv opa_windows_$(GOARCH) $(RELEASE_DIR)/opa_windows_$(GOARCH).exe
rm resource.syso
depr-release:
$(DOCKER) run $(DOCKER_FLAGS) \
-v $(PWD)/$(RELEASE_DIR):/$(RELEASE_DIR):Z \
-v $(PWD):/_src:Z \
-e VERSION_CHECK_URL=$(VERSION_CHECK_URL) \
$(RELEASE_BUILD_IMAGE) \
/_src/build/build-release.sh --version=$(VERSION) --output-dir=/$(RELEASE_DIR) --source-url=/_src
depr-release-local:
$(DOCKER) run $(DOCKER_FLAGS) \
-v $(PWD)/$(RELEASE_DIR):/$(RELEASE_DIR):Z \
-v $(PWD):/_src:Z \
-e VERSION_CHECK_URL=$(VERSION_CHECK_URL) \
$(RELEASE_BUILD_IMAGE) \
/_src/build/build-release.sh --output-dir=/$(RELEASE_DIR) --source-url=/_src