Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
64 changes: 64 additions & 0 deletions .github/workflows/validate-crd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: Validate CRD Generation

# This workflow validates that generated CRD files are up-to-date when tool
# dependencies change. It ensures that if go.tool.mod or go.tool.sum are updated,
# the corresponding generated files (CRDs and deepcopy code) are also regenerated
# and committed in the same PR.
#
# Why this is needed:
# - controller-gen (from go.tool.mod) generates CRD YAML and deepcopy Go code
# - Different versions of controller-gen may produce different output
# - When tool versions change, generated code must be regenerated
# - This prevents CI failures and runtime issues from stale generated code

on:
pull_request:
paths:
- 'go.tool.mod'
- 'go.tool.sum'
- 'scripts/generate-crd.sh'
- '**/dnsendpoints.externaldns.k8s.io.yaml'

permissions:
contents: read

jobs:
validate-crd:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1

- name: Set up Go
uses: actions/setup-go@41dfa10bad2bb2ae585af6ee5bb4d7d973ad74ed # v5.1.0
with:
go-version-file: 'go.mod'

- name: Regenerate CRDs
run: ./scripts/generate-crd.sh

- name: Check for uncommitted changes
id: check_changes
run: |
# Check if there are any changes to generated files
if ! git diff --quiet; then
echo "::error::Generated CRD files are out of sync with go.tool.mod"
echo ""
echo "The following files have uncommitted changes after running 'make crd':"
git diff .
echo ""
echo "This usually means:"
echo "1. go.tool.mod or go.tool.sum was updated (new controller-gen version)"
echo "2. The generated CRD files were not regenerated"
echo ""
echo "To fix this:"
echo " make crd"
echo " git diff ."
echo " commit, push and update your PR:"
exit 1
fi

- name: Success
if: success()
run: |
echo "✅ Generated CRD files are up-to-date"
17 changes: 6 additions & 11 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@
.PHONY: cover cover-html
.DEFAULT_GOAL := build

CONTROLLER_GEN := go tool -modfile=go.tool.mod controller-gen
YQ := go tool -modfile=go.tool.mod yq
YAMLFMT := go tool -modfile=go.tool.mod yamlfmt

cover:
@go test -cover -coverprofile=cover.out -v ./...

Expand Down Expand Up @@ -64,13 +60,12 @@ lint: licensecheck go-lint
#? crd: Generates CRD using controller-gen and copy it into chart
.PHONY: crd
crd:
$(CONTROLLER_GEN) object crd:crdVersions=v1 paths="./endpoint/..."
$(CONTROLLER_GEN) object crd:crdVersions=v1 paths="./apis/..." output:crd:stdout | \
$(YAMLFMT) - | \
$(YQ) eval '.' --no-doc --split-exp '"./config/crd/standard/" + .metadata.name + ".yaml"'
$(YQ) eval '.metadata.annotations |= with_entries(select(.key | test("kubernetes\.io")))' \
--no-doc --split-exp '"./charts/external-dns/crds/" + .metadata.name + ".yaml"' \
./config/crd/standard/*.yaml
@./scripts/generate-crd.sh

# Required as long as dependabot does not support go.tool.mod https://github.com/dependabot/dependabot-core/issues/12050
#? update-tools-deps: Update go tools defined in go.tool.mod to latest versions
update-tools-deps:
@go get -modfile=go.tool.mod tool

#? test: The verify target runs tasks similar to the CI tasks, but without code coverage
.PHONY: test
Expand Down
2 changes: 1 addition & 1 deletion go.tool.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module sigs.k8s.io/external-dns/tools

go 1.25
go 1.25.0

tool (
github.com/google/yamlfmt/cmd/yamlfmt
Expand Down
77 changes: 77 additions & 0 deletions scripts/generate-crd.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
#!/usr/bin/env bash
# Copyright 2026 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# generate-crd.sh
#
# This script generates Kubernetes Custom Resource Definitions (CRDs) and related
# deepcopy code for external-dns using controller-gen from controller-tools.
#
## What this script does:
# 1. Generates DeepCopy methods for types in the endpoint package
# 2. Generates CRD manifests for API types in the apis package
# 3. Copies CRDs to the Helm chart directory
#
# Usage:
# ./scripts/generate-crd.sh
# make crd # calls this script

set -euo pipefail

# Get the script directory
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Get the project root (parent of scripts directory)
PROJECT_ROOT="$(cd "${SCRIPT_DIR}/.." && pwd)"

cd "${PROJECT_ROOT}"

# Define tool commands (using tools from go.tool.mod)
CONTROLLER_GEN="go tool -modfile=go.tool.mod controller-gen"
YQ="go tool -modfile=go.tool.mod yq"
YAMLFMT="go tool -modfile=go.tool.mod yamlfmt"

echo " Generating CRDs using controller-gen..."

# Step 1: Generate deepcopy methods for endpoint types
# This creates zz_generated.deepcopy.go with DeepCopy/DeepCopyInto/DeepCopyObject methods
# The 'object' generator adds these methods for types marked with +kubebuilder:object markers
echo " → Generating deepcopy for endpoint package..."
${CONTROLLER_GEN} object crd:crdVersions=v1 paths="./endpoint/..."

# Clean up empty import statements from generated files
# controller-gen sometimes adds empty import() blocks which create noise in diffs
find ./endpoint -name "zz_generated.deepcopy.go" -exec gofmt -s -w {} \;

# Step 2: Generate CRD manifests for API types
# - Generates CRDs from Go types with kubebuilder markers
# - Outputs to stdout, formats with yamlfmt, then splits into individual files
# - Each CRD is saved to config/crd/standard/<crd-name>.yaml
echo " → Generating CRDs for apis package..."
${CONTROLLER_GEN} object crd:crdVersions=v1 paths="./apis/..." output:crd:stdout | \
${YAMLFMT} - | \
${YQ} eval '.' --no-doc --split-exp '"./config/crd/standard/" + .metadata.name + ".yaml"'

# Clean up empty import statements from generated files
find ./apis -name "zz_generated.deepcopy.go" -exec gofmt -s -w {} \;

# Step 3: Copy CRDs to Helm chart with filtered annotations
# - Reads CRDs from config/crd/standard/
# - Filters annotations to only keep kubernetes.io/* (removes controller-gen annotations)
# - Splits and saves to charts/external-dns/crds/ for Helm chart packaging
echo " → Copying CRDs to chart directory..."
${YQ} eval '.metadata.annotations |= with_entries(select(.key | test("kubernetes\.io")))' \
--no-doc --split-exp '"./charts/external-dns/crds/" + .metadata.name + ".yaml"' \
./config/crd/standard/*.yaml

echo -e " ✅ CRD generation complete"
Loading