├── .github ├── CODEOWNERS ├── dependabot.yml └── workflows │ ├── build-pr.yaml │ └── ci.yaml ├── .gitignore ├── .golangci.yml ├── .mockery.yml ├── CHANGELOG.md ├── Makefile ├── README.md ├── _example ├── Makefile ├── apis │ └── comic │ │ ├── register.go │ │ └── v1 │ │ ├── doc.go │ │ ├── register.go │ │ ├── types.go │ │ └── zz_generated.deepcopy.go ├── client │ ├── applyconfiguration │ │ ├── comic │ │ │ └── v1 │ │ │ │ ├── hero.go │ │ │ │ ├── herospec.go │ │ │ │ └── herostatus.go │ │ ├── internal │ │ │ └── internal.go │ │ └── utils.go │ ├── clientset │ │ └── versioned │ │ │ ├── clientset.go │ │ │ ├── fake │ │ │ ├── clientset_generated.go │ │ │ ├── doc.go │ │ │ └── register.go │ │ │ ├── scheme │ │ │ ├── doc.go │ │ │ └── register.go │ │ │ └── typed │ │ │ └── comic │ │ │ └── v1 │ │ │ ├── comic_client.go │ │ │ ├── doc.go │ │ │ ├── fake │ │ │ ├── doc.go │ │ │ ├── fake_comic_client.go │ │ │ └── fake_hero.go │ │ │ ├── generated_expansion.go │ │ │ └── hero.go │ ├── informers │ │ └── externalversions │ │ │ ├── comic │ │ │ ├── interface.go │ │ │ └── v1 │ │ │ │ ├── hero.go │ │ │ │ └── interface.go │ │ │ ├── factory.go │ │ │ ├── generic.go │ │ │ └── internalinterfaces │ │ │ └── factory_interfaces.go │ └── listers │ │ └── comic │ │ └── v1 │ │ ├── expansion_generated.go │ │ └── hero.go ├── go.mod ├── go.sum └── manifests │ └── comic.kube-code-generator.slok.dev_heroes.yaml ├── cmd └── kube-code-generator │ ├── config.go │ └── main.go ├── docker ├── dev │ └── Dockerfile └── prod │ └── Dockerfile ├── go.mod ├── go.sum ├── internal ├── generate │ ├── client.go │ ├── client_test.go │ ├── crd.go │ ├── crd_test.go │ ├── generate.go │ ├── generatemock │ │ └── mocks.go │ ├── helpers.go │ └── helpers_test.go ├── info │ └── info.go ├── log │ ├── log.go │ └── logrus │ │ └── logrus.go └── util │ └── gomod │ ├── gomod.go │ └── gomod_test.go └── scripts ├── build ├── bin │ ├── build-all.sh │ ├── build-raw.sh │ └── build.sh └── docker │ ├── build-image-dev.sh │ ├── build-image.sh │ ├── build-publish-image-all.sh │ └── publish-image.sh ├── check ├── check.sh ├── integration-test.sh └── unit-test.sh ├── deps.sh └── gogen.sh /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @slok 2 | 3 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "gomod" 4 | directory: "/" 5 | schedule: 6 | interval: "daily" 7 | ignore: 8 | # Ignore Kubernetes dependencies to have full control on them. 9 | - dependency-name: "k8s.io/*" 10 | - package-ecosystem: "github-actions" 11 | directory: "/" 12 | schedule: 13 | interval: "daily" 14 | - package-ecosystem: "docker" 15 | directory: "/docker/dev" 16 | schedule: 17 | interval: "daily" 18 | - package-ecosystem: "docker" 19 | directory: "/docker/prod" 20 | schedule: 21 | interval: "daily" 22 | -------------------------------------------------------------------------------- /.github/workflows/build-pr.yaml: -------------------------------------------------------------------------------- 1 | name: Build pull request 2 | 3 | on: 4 | issue_comment: 5 | types: 6 | - created 7 | 8 | jobs: 9 | pr-release-image: 10 | if: ${{ github.event.issue.pull_request && github.event.comment.body == '/yolo'}} 11 | name: Release images 12 | runs-on: ubuntu-latest 13 | steps: 14 | - name: Get PR SHA 15 | id: sha 16 | uses: actions/github-script@v7 17 | with: 18 | result-encoding: string 19 | script: | 20 | const pr = await github.rest.pulls.get({ 21 | owner: context.issue.owner, 22 | repo: context.issue.repo, 23 | pull_number: context.issue.number, 24 | }); 25 | return pr.data.head.sha 26 | 27 | - uses: actions/checkout@v4 28 | with: 29 | ref: ${{ steps.sha.outputs.result }} 30 | 31 | - name: React to comment 32 | uses: actions/github-script@v7 33 | with: 34 | script: | 35 | github.rest.reactions.createForIssueComment({ 36 | owner: context.issue.owner, 37 | repo: context.issue.repo, 38 | comment_id: context.payload.comment.id, 39 | content: "rocket", 40 | }); 41 | 42 | - name: Docker auth 43 | uses: docker/login-action@v3 44 | with: 45 | registry: ghcr.io 46 | username: ${{ github.actor }} 47 | password: ${{ secrets.GITHUB_TOKEN }} 48 | 49 | - name: Build and publish docker images 50 | run: make build-publish-image-all 51 | env: 52 | PROD_IMAGE_NAME: ghcr.io/${GITHUB_REPOSITORY} 53 | VERSION: ${{ steps.sha.outputs.result }} 54 | 55 | - name: Message success 56 | if: ${{ success() }} 57 | uses: actions/github-script@v7 58 | with: 59 | script: | 60 | github.rest.issues.createComment({ 61 | issue_number: context.issue.number, 62 | owner: context.repo.owner, 63 | repo: context.repo.repo, 64 | body: '✔ Release ready: `${{ steps.sha.outputs.result }}`', 65 | }); 66 | 67 | - name: Message failure 68 | if: ${{ failure() }} 69 | uses: actions/github-script@v7 70 | with: 71 | script: | 72 | github.rest.issues.createComment({ 73 | issue_number: context.issue.number, 74 | owner: context.repo.owner, 75 | repo: context.repo.repo, 76 | body: '❌ Release failed: `${{ steps.sha.outputs.result }}`', 77 | }); 78 | -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | check: 7 | name: Check 8 | runs-on: ubuntu-latest 9 | container: golangci/golangci-lint:v2.1.5-alpine 10 | steps: 11 | - uses: actions/checkout@v4 12 | - run: | 13 | ./scripts/check/check.sh 14 | 15 | unit-test: 16 | name: Unit test 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v4 20 | - uses: actions/setup-go@v5 21 | with: 22 | go-version-file: go.mod 23 | - run: make ci-test 24 | 25 | release-images: 26 | # Only on main branch. 27 | if: startsWith(github.ref, 'refs/heads/main') 28 | env: 29 | TAG_IMAGE_LATEST: "true" 30 | PROD_IMAGE_NAME: ghcr.io/${GITHUB_REPOSITORY} 31 | VERSION: ${GITHUB_SHA} 32 | name: Release images 33 | runs-on: ubuntu-latest 34 | needs: [check, unit-test] 35 | steps: 36 | - uses: actions/checkout@v4 37 | - uses: docker/login-action@v3 38 | with: 39 | registry: ghcr.io 40 | username: ${{ github.actor }} 41 | password: ${{ secrets.GITHUB_TOKEN }} 42 | - name: Build and publish docker images 43 | run: make build-publish-image-all 44 | 45 | tagged-release-images: 46 | # Only on tags. 47 | if: startsWith(github.ref, 'refs/tags/') 48 | env: 49 | PROD_IMAGE_NAME: ghcr.io/${GITHUB_REPOSITORY} 50 | name: Tagged release images 51 | runs-on: ubuntu-latest 52 | needs: [check, unit-test] 53 | steps: 54 | - run: echo "VERSION=${GITHUB_REF#refs/*/}" >> ${GITHUB_ENV} # Sets VERSION env var. 55 | - uses: actions/checkout@v4 56 | - uses: docker/login-action@v3 57 | with: 58 | registry: ghcr.io 59 | username: ${{ github.actor }} 60 | password: ${{ secrets.GITHUB_TOKEN }} 61 | - name: Build and publish docker images 62 | run: make build-publish-image-all 63 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Editors 2 | .idea 3 | .vscode 4 | 5 | # Binaries for programs and plugins 6 | *.exe 7 | *.exe~ 8 | *.dll 9 | *.so 10 | *.dylib 11 | 12 | # Test binary, built with `go test -c` 13 | *.test 14 | 15 | # Output of the go coverage tool, specifically when used with LiteIDE 16 | *.out 17 | 18 | # Vendor directory 19 | vendor/ 20 | 21 | # Test coverage. 22 | .test_coverage.txt 23 | 24 | # Binaries 25 | /bin 26 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | run: 3 | build-tags: 4 | - integration 5 | linters: 6 | enable: 7 | - godot 8 | - misspell 9 | - revive 10 | settings: 11 | revive: 12 | rules: 13 | # Spammy linter and complex to fix on lots of parameters. Makes more harm that it solves. 14 | - name: unused-parameter 15 | disabled: true 16 | staticcheck: 17 | checks: 18 | - all 19 | # Omit embedded fields from selector expression. 20 | # https://staticcheck.dev/docs/checks/#QF1008 21 | - -QF1008 22 | exclusions: 23 | generated: lax 24 | presets: 25 | - comments 26 | - std-error-handling 27 | formatters: 28 | enable: 29 | - gofmt 30 | - goimports 31 | -------------------------------------------------------------------------------- /.mockery.yml: -------------------------------------------------------------------------------- 1 | dir: '{{.InterfaceDir}}/{{.SrcPackageName}}mock' 2 | filename: mocks.go 3 | force-file-write: true 4 | structname: '{{.InterfaceName}}' 5 | pkgname: '{{.SrcPackageName}}mock' 6 | template: testify 7 | packages: 8 | github.com/slok/kube-code-generator/internal/generate: {interfaces: {BashExecutor}} 9 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [Unreleased] 4 | 5 | ## [v0.7.0] - 2025-05-03 6 | 7 | ### Changed 8 | 9 | - Docker image uses `kubernetes/code-generator` v1.33.0. 10 | 11 | ## [v0.6.0] - 2025-03-22 12 | 13 | ### Changed 14 | 15 | - Use Go 1.24. 16 | 17 | ## [v0.5.0] - 2025-01-14 18 | 19 | ### Added 20 | 21 | - Support `Apply configurations` clients and types generation using `--apply-configurations` flag. 22 | 23 | ## [v0.4.0] - 2025-01-03 24 | 25 | ### Changed 26 | 27 | - Docker image uses `kubernetes/code-generator` v1.32.0. 28 | - Docker image uses `kubernetes-sigs/controller-tools` v0.17.0. 29 | 30 | ## [v0.3.2] - 2024-10-10 31 | 32 | ### Changed 33 | 34 | - Docker image uses `kubernetes/code-generator` v1.31.1. 35 | - Docker image uses `kubernetes-sigs/controller-tools` v0.16.4 (Comes with `selectablefield`support). 36 | 37 | ## [v0.3.1] - 2024-08-31 38 | 39 | ### Changed 40 | 41 | - Use Go 1.23. 42 | 43 | ## [v0.3.0] - 2024-08-31 44 | 45 | ### Changed 46 | 47 | - Docker image uses `kubernetes/code-generator` v1.31.0. 48 | - Docker image uses `kubernetes-sigs/controller-tools` v0.16.2. 49 | - Tested compatibility with Kubernetes v1.31. 50 | 51 | ## [v0.2.0] - 2024-04-22 52 | 53 | ### Changed 54 | 55 | - Docker image uses `kubernetes/code-generator` v1.30.0. 56 | - Docker image uses `kubernetes-sigs/controller-tools` v0.15.0. 57 | - Tested compatibility with Kubernetes v1.28. 58 | - Tested compatibility with Kubernetes v1.27. 59 | - Tested compatibility with Kubernetes v1.29. 60 | - Tested compatibility with Kubernetes v1.30. 61 | 62 | ## [v0.1.0] - 2024-03-27 63 | 64 | ### Added 65 | 66 | - CRD generator. 67 | - Go code generator. 68 | - Tested compatibility with Kubernetes v1.28. 69 | - Tested compatibility with Kubernetes v1.27. 70 | - Tested compatibility with Kubernetes v1.29. 71 | - Docker image uses `kubernetes/code-generator` v1.30.0-beta.0. 72 | - Docker image uses `kubernetes-sigs/controller-tools` v0.14.0. 73 | 74 | [unreleased]: https://github.com/slok/kube-code-generator/compare/v0.7.0...HEAD 75 | [v0.7.0]: https://github.com/slok/kube-code-generator/compare/v0.6.0...v0.7.0 76 | [v0.6.0]: https://github.com/slok/kube-code-generator/compare/v0.5.0...v0.6.0 77 | [v0.5.0]: https://github.com/slok/kube-code-generator/compare/v0.4.0...v0.5.0 78 | [v0.4.0]: https://github.com/slok/kube-code-generator/compare/v0.3.2...v0.4.0 79 | [v0.3.2]: https://github.com/slok/kube-code-generator/compare/v0.3.1...v0.3.2 80 | [v0.3.1]: https://github.com/slok/kube-code-generator/compare/v0.3.0...v0.3.1 81 | [v0.3.0]: https://github.com/slok/kube-code-generator/compare/v0.2.0...v0.3.0 82 | [v0.2.0]: https://github.com/slok/kube-code-generator/compare/v0.1.0...v0.2.0 83 | [v0.1.0]: https://github.com/slok/kube-code-generator/releases/tag/v0.1.0 84 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | SHELL := $(shell which bash) 3 | OSTYPE := $(shell uname) 4 | DOCKER := $(shell command -v docker) 5 | GID := $(shell id -g) 6 | UID := $(shell id -u) 7 | VERSION ?= $(shell git describe --tags --always) 8 | 9 | UNIT_TEST_CMD := ./scripts/check/unit-test.sh 10 | INTEGRATION_TEST_CMD := ./scripts/check/integration-test.sh 11 | CHECK_CMD := ./scripts/check/check.sh 12 | 13 | DEV_IMAGE_NAME := localdev/kube-code-generator-dev 14 | PROD_IMAGE_NAME ?= ghcr.io/slok/kube-code-generator 15 | 16 | DOCKER_RUN_CMD := docker run --env ostype=$(OSTYPE) -v ${PWD}:/src --rm ${DEV_IMAGE_NAME} 17 | BUILD_BINARY_CMD := VERSION=${VERSION} ./scripts/build/bin/build.sh 18 | BUILD_BINARY_ALL_CMD := VERSION=${VERSION} ./scripts/build/bin/build-all.sh 19 | BUILD_DEV_IMAGE_CMD := IMAGE=${DEV_IMAGE_NAME} DOCKER_FILE_PATH=./docker/dev/Dockerfile VERSION=latest ./scripts/build/docker/build-image-dev.sh 20 | BUILD_PROD_IMAGE_CMD := IMAGE=${PROD_IMAGE_NAME} DOCKER_FILE_PATH=./docker/prod/Dockerfile VERSION=${VERSION} ./scripts/build/docker/build-image.sh 21 | BUILD_PUBLSIH_PROD_IMAGE_ALL_CMD := IMAGE=${PROD_IMAGE_NAME} DOCKER_FILE_PATH=./docker/prod/Dockerfile VERSION=${VERSION} ./scripts/build/docker/build-publish-image-all.sh 22 | PUBLISH_PROD_IMAGE_CMD := IMAGE=${PROD_IMAGE_NAME} VERSION=${VERSION} ./scripts/build/docker/publish-image.sh 23 | 24 | 25 | help: ## Show this help 26 | @echo "Help" 27 | @grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf " \033[36m%-20s\033[93m %s\n", $$1, $$2}' 28 | 29 | .PHONY: default 30 | default: help 31 | 32 | .PHONY: build-image 33 | build-image: ## Builds the production docker image. 34 | @$(BUILD_PROD_IMAGE_CMD) 35 | 36 | .PHONY: build-publish-image-all 37 | build-publish-image-all: ## Builds and publishes all the production docker images (multiarch). 38 | @$(BUILD_PUBLSIH_PROD_IMAGE_ALL_CMD) 39 | 40 | .PHONY: build-dev-image 41 | build-dev-image: ## Builds the development docker image. 42 | @$(BUILD_DEV_IMAGE_CMD) 43 | 44 | .PHONY: build 45 | build: build-dev-image ## Builds the production binary. 46 | @$(DOCKER_RUN_CMD) /bin/sh -c '$(BUILD_BINARY_CMD)' 47 | 48 | .PHONY: build-all 49 | build-all: build-dev-image ## Builds all archs production binaries. 50 | @$(DOCKER_RUN_CMD) /bin/sh -c '$(BUILD_BINARY_ALL_CMD)' 51 | 52 | .PHONY: test 53 | test: build-dev-image ## Runs unit test. 54 | @$(DOCKER_RUN_CMD) /bin/sh -c '$(UNIT_TEST_CMD)' 55 | .PHONY: check 56 | check: build-dev-image ## Runs checks. 57 | @$(DOCKER_RUN_CMD) /bin/sh -c '$(CHECK_CMD)' 58 | 59 | .PHONY: integration 60 | integration: build-dev-image ## Runs integration test. 61 | @$(DOCKER_RUN_CMD) /bin/sh -c '$(INTEGRATION_TEST_CMD)' 62 | 63 | .PHONY: go-gen 64 | go-gen: build-dev-image ## Generates go based code. 65 | @$(DOCKER_RUN_CMD) /bin/sh -c './scripts/gogen.sh' 66 | 67 | .PHONY: gen 68 | gen: go-gen ## Generates all. 69 | 70 | .PHONY: deps 71 | deps: build-dev-image ## Fixes the dependencies. 72 | @$(DOCKER_RUN_CMD) /bin/sh -c './scripts/deps.sh' 73 | 74 | .PHONY: ci-build 75 | ci-build: ## Builds the production binary in CI environment (without docker). 76 | @$(BUILD_BINARY_CMD) 77 | 78 | .PHONY: ci-unit-test 79 | ci-test: ## Runs unit test in CI environment (without docker). 80 | @$(UNIT_TEST_CMD) 81 | 82 | .PHONY: ci-check 83 | ci-check: ## Runs checks in CI environment (without docker). 84 | @$(CHECK_CMD) 85 | 86 | .PHONY: ci-integration 87 | ci-integration: ## Runs integraton test in CI environment (without docker). 88 | @$(INTEGRATION_TEST_CMD) 89 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kube code generator 2 | 3 | ![Kubernetes release](https://img.shields.io/badge/Kubernetes-v1.33-green?logo=Kubernetes&style=flat&color=326CE5&logoColor=white) 4 | 5 | ## Introduction 6 | 7 | When we speak about Kubernetes operators or controllers, normally Go code, CR, CRDs... are required. To create all the autogenerated Kubernetes Go code (Clients, helpers...) and manifests (CRD), the process is a bit painful. 8 | 9 | This small project tries making easy this process, by creating a small layer between Kubernetes official tooling that are used to get all this autogenerated stuff, and abstract options and infer some others, making a better UX for the user. 10 | 11 | The projects that are used under the hood are: 12 | 13 | - [code-generator](https://github.com/kubernetes/code-generator) for Go code autogeneration. 14 | - [controller-tools](https://github.com/kubernetes-sigs/controller-tools) for CRD autogeneration. 15 | 16 | ## Why and when use this 17 | 18 | - You don't like, need or use kubebuilder for your CRDs. 19 | - You want simple tooling to generate Kubernetes CRD Go clients and manifests. 20 | - You like safe standards and simple things. 21 | - You use CRDs for more/other things than operators (e.g: generating CLIs, storing state on k8s as APIs...). 22 | - You don't want to do hacky and ugly stuff to start creating Kubernetes tooling. 23 | 24 | ## Features 25 | 26 | - Small API/configuration. 27 | - Safe standards 28 | - Ready to use Docker images. 29 | - Generates CR client Go code (Used in controllers and operators). 30 | - Generates CR informers Go code (Used in controllers and operators). 31 | - Generates CR listers Go code (Used in controllers and operators). 32 | - Generates CR ["apply configurations"](https://pkg.go.dev/k8s.io/client-go/applyconfigurations) Go code (Used in controllers and operators). 33 | - Generates CRD manifests (Used for API registration on k8s clusters). 34 | 35 | ## How to use it 36 | 37 | The easiest way is to use the provided Docker image as it has all the required upstream dependencies. 38 | 39 | Here is an example that mounts the current directory (a Go project) and generates the Go code and the CRDs by providing the APIs input directory and the generation output directories: 40 | 41 | ```bash 42 | docker run -it --rm -v ${PWD}:/app ghcr.io/slok/kube-code-generator \ 43 | --apis-in ./apis \ 44 | --go-gen-out ./gen \ 45 | --crd-gen-out ./gen/manifests 46 | ``` 47 | 48 | However, the best way to know how to use it is with a full example, you have it in [_example](_example/) dir. 49 | 50 | ### Optional features 51 | 52 | These are the list of features that can be enabled when generating Go code or CRDs: 53 | 54 | - `--apply-configurations`: Generates [apply configurations](https://pkg.go.dev/k8s.io/client-go/applyconfigurations) for CRs require for [server-side-apply](https://kubernetes.io/docs/reference/using-api/server-side-apply/). 55 | 56 | ## Kubernetes versions 57 | 58 | It's suggested that if you kind-of up to date Kubernetes version, you try the latest kube-code-generator: 59 | 60 | ```bash 61 | docker pull ghcr.io/slok/kube-code-generator:latest 62 | ``` 63 | 64 | However the ones described on the table here are known to work correctly on the specific version. 65 | 66 | | Kubernetes | Docker image | 67 | | ---------- | ------------------------------------------------------ | 68 | | v1.33 | `docker pull ghcr.io/slok/kube-code-generator:v0.7.0` | 69 | | v1.32 | `docker pull ghcr.io/slok/kube-code-generator:v0.6.0` | 70 | | v1.31 | `docker pull ghcr.io/slok/kube-code-generator:v0.3.2` | 71 | | v1.30 | `docker pull ghcr.io/slok/kube-code-generator:v0.2.0` | 72 | | v1.29 | `docker pull ghcr.io/slok/kube-code-generator:v0.2.0` | 73 | | v1.28 | `docker pull ghcr.io/slok/kube-code-generator:v0.2.0` | 74 | | v1.27 | `docker pull ghcr.io/slok/kube-code-generator:v0.2.0` | 75 | -------------------------------------------------------------------------------- /_example/Makefile: -------------------------------------------------------------------------------- 1 | IMAGE := ghcr.io/slok/kube-code-generator:v0.7.0 2 | 3 | DIRECTORY := $(PWD) 4 | DEPS_CMD := go mod tidy 5 | 6 | default: generate 7 | 8 | .PHONY: generate 9 | generate: 10 | @docker run -it --rm -v $(DIRECTORY):/app $(IMAGE) \ 11 | --apis-in ./apis \ 12 | --go-gen-out ./client \ 13 | --crd-gen-out ./manifests \ 14 | --apply-configurations 15 | 16 | .PHONY: deps 17 | deps: 18 | $(DEPS_CMD) 19 | 20 | .PHONY: clean 21 | clean: 22 | echo "Cleaning generated files..." 23 | rm -rf ./manifests 24 | rm -rf ./client 25 | rm -rf ./apis/comic/v1/zz_generated.deepcopy.go -------------------------------------------------------------------------------- /_example/apis/comic/register.go: -------------------------------------------------------------------------------- 1 | package comic 2 | 3 | const ( 4 | // GroupName is the name of the resources group. 5 | GroupName = "comic.kube-code-generator.slok.dev" 6 | ) 7 | -------------------------------------------------------------------------------- /_example/apis/comic/v1/doc.go: -------------------------------------------------------------------------------- 1 | // +k8s:deepcopy-gen=package 2 | // +k8s:openapi-gen=true 3 | // +groupName=comic.kube-code-generator.slok.dev 4 | // +versionName=v1 5 | // +kubebuilder:validation:Optional 6 | 7 | package v1 8 | -------------------------------------------------------------------------------- /_example/apis/comic/v1/register.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 5 | "k8s.io/apimachinery/pkg/runtime" 6 | "k8s.io/apimachinery/pkg/runtime/schema" 7 | 8 | kubecodegeneratorcomic "github.com/slok/kube-code-generator/example/apis/comic" 9 | ) 10 | 11 | const ( 12 | version = "v1" 13 | ) 14 | 15 | // SchemeGroupVersion is group version used to register these objects 16 | var SchemeGroupVersion = schema.GroupVersion{Group: kubecodegeneratorcomic.GroupName, Version: version} 17 | 18 | // Kind takes an unqualified kind and returns back a Group qualified GroupKind 19 | func Kind(kind string) schema.GroupKind { 20 | return VersionKind(kind).GroupKind() 21 | } 22 | 23 | // VersionKind takes an unqualified kind and returns back a Group qualified GroupVersionKind 24 | func VersionKind(kind string) schema.GroupVersionKind { 25 | return SchemeGroupVersion.WithKind(kind) 26 | } 27 | 28 | // Resource takes an unqualified resource and returns a Group qualified GroupResource 29 | func Resource(resource string) schema.GroupResource { 30 | return SchemeGroupVersion.WithResource(resource).GroupResource() 31 | } 32 | 33 | var ( 34 | // SchemeBuilder initializes a scheme builder 35 | SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes) 36 | // AddToScheme is a global function that registers this API group & version to a scheme 37 | AddToScheme = SchemeBuilder.AddToScheme 38 | ) 39 | 40 | // Adds the list of known types to Scheme. 41 | func addKnownTypes(scheme *runtime.Scheme) error { 42 | scheme.AddKnownTypes(SchemeGroupVersion, 43 | &Hero{}, 44 | &HeroList{}, 45 | ) 46 | metav1.AddToGroupVersion(scheme, SchemeGroupVersion) 47 | return nil 48 | } 49 | -------------------------------------------------------------------------------- /_example/apis/comic/v1/types.go: -------------------------------------------------------------------------------- 1 | package v1 2 | 3 | import ( 4 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 5 | ) 6 | 7 | // HeroType is the type of a hero. 8 | type HeroType string 9 | 10 | const ( 11 | // HeroTypeUnknown is a super hero unknown. 12 | HeroTypeUnknown = HeroType("unknown") 13 | // HeroTypeSuperHero is a super hero e.g Batman, Spiderman... 14 | HeroTypeSuperHero = HeroType("superhero") 15 | // HeroTypeAntiHero is a anti hero e.g Punisher, Deadpool... 16 | HeroTypeAntiHero = HeroType("antihero") 17 | // HeroTypeVillain is a Villain e.g Fisk, Joker... 18 | HeroTypeVillain = HeroType("villain") 19 | ) 20 | 21 | // Hero represents a comic hero. 22 | // 23 | // +genclient 24 | // +genclient:nonNamespaced 25 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 26 | // +kubebuilder:subresource:status 27 | // +kubebuilder:printcolumn:name="NAME",type="string",JSONPath=".spec.name" 28 | // +kubebuilder:printcolumn:name="CITY",type="string",JSONPath=".spec.city" 29 | // +kubebuilder:printcolumn:name="KIND",type="string",JSONPath=".spec.kind" 30 | // +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" 31 | // +kubebuilder:resource:singular=hero,path=heroes,shortName=he;sh,scope=Namespaced,categories=heroes;superheroes 32 | // +kubebuilder:selectablefield:JSONPath=`.spec.city` 33 | // +kubebuilder:selectablefield:JSONPath=`.spec.kind` 34 | type Hero struct { 35 | metav1.TypeMeta `json:",inline"` 36 | metav1.ObjectMeta `json:"metadata,omitempty"` 37 | 38 | Spec HeroSpec `json:"spec,omitempty"` 39 | Status HeroStatus `json:"status,omitempty"` 40 | } 41 | 42 | // HeroSpec is the spec of a Hero. 43 | type HeroSpec struct { 44 | // +kubebuilder:validation:Required 45 | // +kubebuilder:validation:MaxLength=128 46 | // +kubebuilder:validation:XValidation:message="name cannot be empty",rule="self.size() != 0" 47 | Name string `json:"name"` 48 | // +optional 49 | City string `json:"city,omitempty"` 50 | // +kubebuilder:validation:Required 51 | // +kubebuilder:validation:Enum=unknown;superhero;antihero;villain 52 | // +kubebuilder:default=unknown 53 | Kind HeroType `json:"kind"` 54 | // +optional 55 | BirthDate *metav1.Time `json:"birthDate"` 56 | // +listType=map 57 | // +optional 58 | SuperPowers []string `json:"superPowers"` 59 | } 60 | 61 | type HeroStatus struct { 62 | Moving bool `json:"moving"` 63 | CurrentCity string `json:"currentCity"` 64 | } 65 | 66 | // +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object 67 | 68 | // HeroList is a list of Hero resources. 69 | type HeroList struct { 70 | metav1.TypeMeta `json:",inline"` 71 | metav1.ListMeta `json:"metadata"` 72 | 73 | Items []Hero `json:"items"` 74 | } 75 | -------------------------------------------------------------------------------- /_example/apis/comic/v1/zz_generated.deepcopy.go: -------------------------------------------------------------------------------- 1 | //go:build !ignore_autogenerated 2 | // +build !ignore_autogenerated 3 | 4 | // Code generated by deepcopy-gen. DO NOT EDIT. 5 | 6 | package v1 7 | 8 | import ( 9 | runtime "k8s.io/apimachinery/pkg/runtime" 10 | ) 11 | 12 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 13 | func (in *Hero) DeepCopyInto(out *Hero) { 14 | *out = *in 15 | out.TypeMeta = in.TypeMeta 16 | in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) 17 | in.Spec.DeepCopyInto(&out.Spec) 18 | out.Status = in.Status 19 | return 20 | } 21 | 22 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Hero. 23 | func (in *Hero) DeepCopy() *Hero { 24 | if in == nil { 25 | return nil 26 | } 27 | out := new(Hero) 28 | in.DeepCopyInto(out) 29 | return out 30 | } 31 | 32 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 33 | func (in *Hero) DeepCopyObject() runtime.Object { 34 | if c := in.DeepCopy(); c != nil { 35 | return c 36 | } 37 | return nil 38 | } 39 | 40 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 41 | func (in *HeroList) DeepCopyInto(out *HeroList) { 42 | *out = *in 43 | out.TypeMeta = in.TypeMeta 44 | in.ListMeta.DeepCopyInto(&out.ListMeta) 45 | if in.Items != nil { 46 | in, out := &in.Items, &out.Items 47 | *out = make([]Hero, len(*in)) 48 | for i := range *in { 49 | (*in)[i].DeepCopyInto(&(*out)[i]) 50 | } 51 | } 52 | return 53 | } 54 | 55 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HeroList. 56 | func (in *HeroList) DeepCopy() *HeroList { 57 | if in == nil { 58 | return nil 59 | } 60 | out := new(HeroList) 61 | in.DeepCopyInto(out) 62 | return out 63 | } 64 | 65 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 66 | func (in *HeroList) DeepCopyObject() runtime.Object { 67 | if c := in.DeepCopy(); c != nil { 68 | return c 69 | } 70 | return nil 71 | } 72 | 73 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 74 | func (in *HeroSpec) DeepCopyInto(out *HeroSpec) { 75 | *out = *in 76 | if in.BirthDate != nil { 77 | in, out := &in.BirthDate, &out.BirthDate 78 | *out = (*in).DeepCopy() 79 | } 80 | if in.SuperPowers != nil { 81 | in, out := &in.SuperPowers, &out.SuperPowers 82 | *out = make([]string, len(*in)) 83 | copy(*out, *in) 84 | } 85 | return 86 | } 87 | 88 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HeroSpec. 89 | func (in *HeroSpec) DeepCopy() *HeroSpec { 90 | if in == nil { 91 | return nil 92 | } 93 | out := new(HeroSpec) 94 | in.DeepCopyInto(out) 95 | return out 96 | } 97 | 98 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 99 | func (in *HeroStatus) DeepCopyInto(out *HeroStatus) { 100 | *out = *in 101 | return 102 | } 103 | 104 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HeroStatus. 105 | func (in *HeroStatus) DeepCopy() *HeroStatus { 106 | if in == nil { 107 | return nil 108 | } 109 | out := new(HeroStatus) 110 | in.DeepCopyInto(out) 111 | return out 112 | } 113 | -------------------------------------------------------------------------------- /_example/client/applyconfiguration/comic/v1/hero.go: -------------------------------------------------------------------------------- 1 | // Code generated by applyconfiguration-gen. DO NOT EDIT. 2 | 3 | package v1 4 | 5 | import ( 6 | apismetav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 7 | types "k8s.io/apimachinery/pkg/types" 8 | metav1 "k8s.io/client-go/applyconfigurations/meta/v1" 9 | ) 10 | 11 | // HeroApplyConfiguration represents a declarative configuration of the Hero type for use 12 | // with apply. 13 | type HeroApplyConfiguration struct { 14 | metav1.TypeMetaApplyConfiguration `json:",inline"` 15 | *metav1.ObjectMetaApplyConfiguration `json:"metadata,omitempty"` 16 | Spec *HeroSpecApplyConfiguration `json:"spec,omitempty"` 17 | Status *HeroStatusApplyConfiguration `json:"status,omitempty"` 18 | } 19 | 20 | // Hero constructs a declarative configuration of the Hero type for use with 21 | // apply. 22 | func Hero(name string) *HeroApplyConfiguration { 23 | b := &HeroApplyConfiguration{} 24 | b.WithName(name) 25 | b.WithKind("Hero") 26 | b.WithAPIVersion("comic.kube-code-generator.slok.dev/v1") 27 | return b 28 | } 29 | 30 | // WithKind sets the Kind field in the declarative configuration to the given value 31 | // and returns the receiver, so that objects can be built by chaining "With" function invocations. 32 | // If called multiple times, the Kind field is set to the value of the last call. 33 | func (b *HeroApplyConfiguration) WithKind(value string) *HeroApplyConfiguration { 34 | b.TypeMetaApplyConfiguration.Kind = &value 35 | return b 36 | } 37 | 38 | // WithAPIVersion sets the APIVersion field in the declarative configuration to the given value 39 | // and returns the receiver, so that objects can be built by chaining "With" function invocations. 40 | // If called multiple times, the APIVersion field is set to the value of the last call. 41 | func (b *HeroApplyConfiguration) WithAPIVersion(value string) *HeroApplyConfiguration { 42 | b.TypeMetaApplyConfiguration.APIVersion = &value 43 | return b 44 | } 45 | 46 | // WithName sets the Name field in the declarative configuration to the given value 47 | // and returns the receiver, so that objects can be built by chaining "With" function invocations. 48 | // If called multiple times, the Name field is set to the value of the last call. 49 | func (b *HeroApplyConfiguration) WithName(value string) *HeroApplyConfiguration { 50 | b.ensureObjectMetaApplyConfigurationExists() 51 | b.ObjectMetaApplyConfiguration.Name = &value 52 | return b 53 | } 54 | 55 | // WithGenerateName sets the GenerateName field in the declarative configuration to the given value 56 | // and returns the receiver, so that objects can be built by chaining "With" function invocations. 57 | // If called multiple times, the GenerateName field is set to the value of the last call. 58 | func (b *HeroApplyConfiguration) WithGenerateName(value string) *HeroApplyConfiguration { 59 | b.ensureObjectMetaApplyConfigurationExists() 60 | b.ObjectMetaApplyConfiguration.GenerateName = &value 61 | return b 62 | } 63 | 64 | // WithNamespace sets the Namespace field in the declarative configuration to the given value 65 | // and returns the receiver, so that objects can be built by chaining "With" function invocations. 66 | // If called multiple times, the Namespace field is set to the value of the last call. 67 | func (b *HeroApplyConfiguration) WithNamespace(value string) *HeroApplyConfiguration { 68 | b.ensureObjectMetaApplyConfigurationExists() 69 | b.ObjectMetaApplyConfiguration.Namespace = &value 70 | return b 71 | } 72 | 73 | // WithUID sets the UID field in the declarative configuration to the given value 74 | // and returns the receiver, so that objects can be built by chaining "With" function invocations. 75 | // If called multiple times, the UID field is set to the value of the last call. 76 | func (b *HeroApplyConfiguration) WithUID(value types.UID) *HeroApplyConfiguration { 77 | b.ensureObjectMetaApplyConfigurationExists() 78 | b.ObjectMetaApplyConfiguration.UID = &value 79 | return b 80 | } 81 | 82 | // WithResourceVersion sets the ResourceVersion field in the declarative configuration to the given value 83 | // and returns the receiver, so that objects can be built by chaining "With" function invocations. 84 | // If called multiple times, the ResourceVersion field is set to the value of the last call. 85 | func (b *HeroApplyConfiguration) WithResourceVersion(value string) *HeroApplyConfiguration { 86 | b.ensureObjectMetaApplyConfigurationExists() 87 | b.ObjectMetaApplyConfiguration.ResourceVersion = &value 88 | return b 89 | } 90 | 91 | // WithGeneration sets the Generation field in the declarative configuration to the given value 92 | // and returns the receiver, so that objects can be built by chaining "With" function invocations. 93 | // If called multiple times, the Generation field is set to the value of the last call. 94 | func (b *HeroApplyConfiguration) WithGeneration(value int64) *HeroApplyConfiguration { 95 | b.ensureObjectMetaApplyConfigurationExists() 96 | b.ObjectMetaApplyConfiguration.Generation = &value 97 | return b 98 | } 99 | 100 | // WithCreationTimestamp sets the CreationTimestamp field in the declarative configuration to the given value 101 | // and returns the receiver, so that objects can be built by chaining "With" function invocations. 102 | // If called multiple times, the CreationTimestamp field is set to the value of the last call. 103 | func (b *HeroApplyConfiguration) WithCreationTimestamp(value apismetav1.Time) *HeroApplyConfiguration { 104 | b.ensureObjectMetaApplyConfigurationExists() 105 | b.ObjectMetaApplyConfiguration.CreationTimestamp = &value 106 | return b 107 | } 108 | 109 | // WithDeletionTimestamp sets the DeletionTimestamp field in the declarative configuration to the given value 110 | // and returns the receiver, so that objects can be built by chaining "With" function invocations. 111 | // If called multiple times, the DeletionTimestamp field is set to the value of the last call. 112 | func (b *HeroApplyConfiguration) WithDeletionTimestamp(value apismetav1.Time) *HeroApplyConfiguration { 113 | b.ensureObjectMetaApplyConfigurationExists() 114 | b.ObjectMetaApplyConfiguration.DeletionTimestamp = &value 115 | return b 116 | } 117 | 118 | // WithDeletionGracePeriodSeconds sets the DeletionGracePeriodSeconds field in the declarative configuration to the given value 119 | // and returns the receiver, so that objects can be built by chaining "With" function invocations. 120 | // If called multiple times, the DeletionGracePeriodSeconds field is set to the value of the last call. 121 | func (b *HeroApplyConfiguration) WithDeletionGracePeriodSeconds(value int64) *HeroApplyConfiguration { 122 | b.ensureObjectMetaApplyConfigurationExists() 123 | b.ObjectMetaApplyConfiguration.DeletionGracePeriodSeconds = &value 124 | return b 125 | } 126 | 127 | // WithLabels puts the entries into the Labels field in the declarative configuration 128 | // and returns the receiver, so that objects can be build by chaining "With" function invocations. 129 | // If called multiple times, the entries provided by each call will be put on the Labels field, 130 | // overwriting an existing map entries in Labels field with the same key. 131 | func (b *HeroApplyConfiguration) WithLabels(entries map[string]string) *HeroApplyConfiguration { 132 | b.ensureObjectMetaApplyConfigurationExists() 133 | if b.ObjectMetaApplyConfiguration.Labels == nil && len(entries) > 0 { 134 | b.ObjectMetaApplyConfiguration.Labels = make(map[string]string, len(entries)) 135 | } 136 | for k, v := range entries { 137 | b.ObjectMetaApplyConfiguration.Labels[k] = v 138 | } 139 | return b 140 | } 141 | 142 | // WithAnnotations puts the entries into the Annotations field in the declarative configuration 143 | // and returns the receiver, so that objects can be build by chaining "With" function invocations. 144 | // If called multiple times, the entries provided by each call will be put on the Annotations field, 145 | // overwriting an existing map entries in Annotations field with the same key. 146 | func (b *HeroApplyConfiguration) WithAnnotations(entries map[string]string) *HeroApplyConfiguration { 147 | b.ensureObjectMetaApplyConfigurationExists() 148 | if b.ObjectMetaApplyConfiguration.Annotations == nil && len(entries) > 0 { 149 | b.ObjectMetaApplyConfiguration.Annotations = make(map[string]string, len(entries)) 150 | } 151 | for k, v := range entries { 152 | b.ObjectMetaApplyConfiguration.Annotations[k] = v 153 | } 154 | return b 155 | } 156 | 157 | // WithOwnerReferences adds the given value to the OwnerReferences field in the declarative configuration 158 | // and returns the receiver, so that objects can be build by chaining "With" function invocations. 159 | // If called multiple times, values provided by each call will be appended to the OwnerReferences field. 160 | func (b *HeroApplyConfiguration) WithOwnerReferences(values ...*metav1.OwnerReferenceApplyConfiguration) *HeroApplyConfiguration { 161 | b.ensureObjectMetaApplyConfigurationExists() 162 | for i := range values { 163 | if values[i] == nil { 164 | panic("nil value passed to WithOwnerReferences") 165 | } 166 | b.ObjectMetaApplyConfiguration.OwnerReferences = append(b.ObjectMetaApplyConfiguration.OwnerReferences, *values[i]) 167 | } 168 | return b 169 | } 170 | 171 | // WithFinalizers adds the given value to the Finalizers field in the declarative configuration 172 | // and returns the receiver, so that objects can be build by chaining "With" function invocations. 173 | // If called multiple times, values provided by each call will be appended to the Finalizers field. 174 | func (b *HeroApplyConfiguration) WithFinalizers(values ...string) *HeroApplyConfiguration { 175 | b.ensureObjectMetaApplyConfigurationExists() 176 | for i := range values { 177 | b.ObjectMetaApplyConfiguration.Finalizers = append(b.ObjectMetaApplyConfiguration.Finalizers, values[i]) 178 | } 179 | return b 180 | } 181 | 182 | func (b *HeroApplyConfiguration) ensureObjectMetaApplyConfigurationExists() { 183 | if b.ObjectMetaApplyConfiguration == nil { 184 | b.ObjectMetaApplyConfiguration = &metav1.ObjectMetaApplyConfiguration{} 185 | } 186 | } 187 | 188 | // WithSpec sets the Spec field in the declarative configuration to the given value 189 | // and returns the receiver, so that objects can be built by chaining "With" function invocations. 190 | // If called multiple times, the Spec field is set to the value of the last call. 191 | func (b *HeroApplyConfiguration) WithSpec(value *HeroSpecApplyConfiguration) *HeroApplyConfiguration { 192 | b.Spec = value 193 | return b 194 | } 195 | 196 | // WithStatus sets the Status field in the declarative configuration to the given value 197 | // and returns the receiver, so that objects can be built by chaining "With" function invocations. 198 | // If called multiple times, the Status field is set to the value of the last call. 199 | func (b *HeroApplyConfiguration) WithStatus(value *HeroStatusApplyConfiguration) *HeroApplyConfiguration { 200 | b.Status = value 201 | return b 202 | } 203 | 204 | // GetName retrieves the value of the Name field in the declarative configuration. 205 | func (b *HeroApplyConfiguration) GetName() *string { 206 | b.ensureObjectMetaApplyConfigurationExists() 207 | return b.ObjectMetaApplyConfiguration.Name 208 | } 209 | -------------------------------------------------------------------------------- /_example/client/applyconfiguration/comic/v1/herospec.go: -------------------------------------------------------------------------------- 1 | // Code generated by applyconfiguration-gen. DO NOT EDIT. 2 | 3 | package v1 4 | 5 | import ( 6 | comicv1 "github.com/slok/kube-code-generator/example/apis/comic/v1" 7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | ) 9 | 10 | // HeroSpecApplyConfiguration represents a declarative configuration of the HeroSpec type for use 11 | // with apply. 12 | type HeroSpecApplyConfiguration struct { 13 | Name *string `json:"name,omitempty"` 14 | City *string `json:"city,omitempty"` 15 | Kind *comicv1.HeroType `json:"kind,omitempty"` 16 | BirthDate *metav1.Time `json:"birthDate,omitempty"` 17 | SuperPowers []string `json:"superPowers,omitempty"` 18 | } 19 | 20 | // HeroSpecApplyConfiguration constructs a declarative configuration of the HeroSpec type for use with 21 | // apply. 22 | func HeroSpec() *HeroSpecApplyConfiguration { 23 | return &HeroSpecApplyConfiguration{} 24 | } 25 | 26 | // WithName sets the Name field in the declarative configuration to the given value 27 | // and returns the receiver, so that objects can be built by chaining "With" function invocations. 28 | // If called multiple times, the Name field is set to the value of the last call. 29 | func (b *HeroSpecApplyConfiguration) WithName(value string) *HeroSpecApplyConfiguration { 30 | b.Name = &value 31 | return b 32 | } 33 | 34 | // WithCity sets the City field in the declarative configuration to the given value 35 | // and returns the receiver, so that objects can be built by chaining "With" function invocations. 36 | // If called multiple times, the City field is set to the value of the last call. 37 | func (b *HeroSpecApplyConfiguration) WithCity(value string) *HeroSpecApplyConfiguration { 38 | b.City = &value 39 | return b 40 | } 41 | 42 | // WithKind sets the Kind field in the declarative configuration to the given value 43 | // and returns the receiver, so that objects can be built by chaining "With" function invocations. 44 | // If called multiple times, the Kind field is set to the value of the last call. 45 | func (b *HeroSpecApplyConfiguration) WithKind(value comicv1.HeroType) *HeroSpecApplyConfiguration { 46 | b.Kind = &value 47 | return b 48 | } 49 | 50 | // WithBirthDate sets the BirthDate field in the declarative configuration to the given value 51 | // and returns the receiver, so that objects can be built by chaining "With" function invocations. 52 | // If called multiple times, the BirthDate field is set to the value of the last call. 53 | func (b *HeroSpecApplyConfiguration) WithBirthDate(value metav1.Time) *HeroSpecApplyConfiguration { 54 | b.BirthDate = &value 55 | return b 56 | } 57 | 58 | // WithSuperPowers adds the given value to the SuperPowers field in the declarative configuration 59 | // and returns the receiver, so that objects can be build by chaining "With" function invocations. 60 | // If called multiple times, values provided by each call will be appended to the SuperPowers field. 61 | func (b *HeroSpecApplyConfiguration) WithSuperPowers(values ...string) *HeroSpecApplyConfiguration { 62 | for i := range values { 63 | b.SuperPowers = append(b.SuperPowers, values[i]) 64 | } 65 | return b 66 | } 67 | -------------------------------------------------------------------------------- /_example/client/applyconfiguration/comic/v1/herostatus.go: -------------------------------------------------------------------------------- 1 | // Code generated by applyconfiguration-gen. DO NOT EDIT. 2 | 3 | package v1 4 | 5 | // HeroStatusApplyConfiguration represents a declarative configuration of the HeroStatus type for use 6 | // with apply. 7 | type HeroStatusApplyConfiguration struct { 8 | Moving *bool `json:"moving,omitempty"` 9 | CurrentCity *string `json:"currentCity,omitempty"` 10 | } 11 | 12 | // HeroStatusApplyConfiguration constructs a declarative configuration of the HeroStatus type for use with 13 | // apply. 14 | func HeroStatus() *HeroStatusApplyConfiguration { 15 | return &HeroStatusApplyConfiguration{} 16 | } 17 | 18 | // WithMoving sets the Moving field in the declarative configuration to the given value 19 | // and returns the receiver, so that objects can be built by chaining "With" function invocations. 20 | // If called multiple times, the Moving field is set to the value of the last call. 21 | func (b *HeroStatusApplyConfiguration) WithMoving(value bool) *HeroStatusApplyConfiguration { 22 | b.Moving = &value 23 | return b 24 | } 25 | 26 | // WithCurrentCity sets the CurrentCity field in the declarative configuration to the given value 27 | // and returns the receiver, so that objects can be built by chaining "With" function invocations. 28 | // If called multiple times, the CurrentCity field is set to the value of the last call. 29 | func (b *HeroStatusApplyConfiguration) WithCurrentCity(value string) *HeroStatusApplyConfiguration { 30 | b.CurrentCity = &value 31 | return b 32 | } 33 | -------------------------------------------------------------------------------- /_example/client/applyconfiguration/internal/internal.go: -------------------------------------------------------------------------------- 1 | // Code generated by applyconfiguration-gen. DO NOT EDIT. 2 | 3 | package internal 4 | 5 | import ( 6 | fmt "fmt" 7 | sync "sync" 8 | 9 | typed "sigs.k8s.io/structured-merge-diff/v4/typed" 10 | ) 11 | 12 | func Parser() *typed.Parser { 13 | parserOnce.Do(func() { 14 | var err error 15 | parser, err = typed.NewParser(schemaYAML) 16 | if err != nil { 17 | panic(fmt.Sprintf("Failed to parse schema: %v", err)) 18 | } 19 | }) 20 | return parser 21 | } 22 | 23 | var parserOnce sync.Once 24 | var parser *typed.Parser 25 | var schemaYAML = typed.YAMLObject(`types: 26 | - name: __untyped_atomic_ 27 | scalar: untyped 28 | list: 29 | elementType: 30 | namedType: __untyped_atomic_ 31 | elementRelationship: atomic 32 | map: 33 | elementType: 34 | namedType: __untyped_atomic_ 35 | elementRelationship: atomic 36 | - name: __untyped_deduced_ 37 | scalar: untyped 38 | list: 39 | elementType: 40 | namedType: __untyped_atomic_ 41 | elementRelationship: atomic 42 | map: 43 | elementType: 44 | namedType: __untyped_deduced_ 45 | elementRelationship: separable 46 | `) 47 | -------------------------------------------------------------------------------- /_example/client/applyconfiguration/utils.go: -------------------------------------------------------------------------------- 1 | // Code generated by applyconfiguration-gen. DO NOT EDIT. 2 | 3 | package applyconfiguration 4 | 5 | import ( 6 | v1 "github.com/slok/kube-code-generator/example/apis/comic/v1" 7 | comicv1 "github.com/slok/kube-code-generator/example/client/applyconfiguration/comic/v1" 8 | internal "github.com/slok/kube-code-generator/example/client/applyconfiguration/internal" 9 | runtime "k8s.io/apimachinery/pkg/runtime" 10 | schema "k8s.io/apimachinery/pkg/runtime/schema" 11 | testing "k8s.io/client-go/testing" 12 | ) 13 | 14 | // ForKind returns an apply configuration type for the given GroupVersionKind, or nil if no 15 | // apply configuration type exists for the given GroupVersionKind. 16 | func ForKind(kind schema.GroupVersionKind) interface{} { 17 | switch kind { 18 | // Group=comic.kube-code-generator.slok.dev, Version=v1 19 | case v1.SchemeGroupVersion.WithKind("Hero"): 20 | return &comicv1.HeroApplyConfiguration{} 21 | case v1.SchemeGroupVersion.WithKind("HeroSpec"): 22 | return &comicv1.HeroSpecApplyConfiguration{} 23 | case v1.SchemeGroupVersion.WithKind("HeroStatus"): 24 | return &comicv1.HeroStatusApplyConfiguration{} 25 | 26 | } 27 | return nil 28 | } 29 | 30 | func NewTypeConverter(scheme *runtime.Scheme) *testing.TypeConverter { 31 | return &testing.TypeConverter{Scheme: scheme, TypeResolver: internal.Parser()} 32 | } 33 | -------------------------------------------------------------------------------- /_example/client/clientset/versioned/clientset.go: -------------------------------------------------------------------------------- 1 | // Code generated by client-gen. DO NOT EDIT. 2 | 3 | package versioned 4 | 5 | import ( 6 | fmt "fmt" 7 | http "net/http" 8 | 9 | comicv1 "github.com/slok/kube-code-generator/example/client/clientset/versioned/typed/comic/v1" 10 | discovery "k8s.io/client-go/discovery" 11 | rest "k8s.io/client-go/rest" 12 | flowcontrol "k8s.io/client-go/util/flowcontrol" 13 | ) 14 | 15 | type Interface interface { 16 | Discovery() discovery.DiscoveryInterface 17 | ComicV1() comicv1.ComicV1Interface 18 | } 19 | 20 | // Clientset contains the clients for groups. 21 | type Clientset struct { 22 | *discovery.DiscoveryClient 23 | comicV1 *comicv1.ComicV1Client 24 | } 25 | 26 | // ComicV1 retrieves the ComicV1Client 27 | func (c *Clientset) ComicV1() comicv1.ComicV1Interface { 28 | return c.comicV1 29 | } 30 | 31 | // Discovery retrieves the DiscoveryClient 32 | func (c *Clientset) Discovery() discovery.DiscoveryInterface { 33 | if c == nil { 34 | return nil 35 | } 36 | return c.DiscoveryClient 37 | } 38 | 39 | // NewForConfig creates a new Clientset for the given config. 40 | // If config's RateLimiter is not set and QPS and Burst are acceptable, 41 | // NewForConfig will generate a rate-limiter in configShallowCopy. 42 | // NewForConfig is equivalent to NewForConfigAndClient(c, httpClient), 43 | // where httpClient was generated with rest.HTTPClientFor(c). 44 | func NewForConfig(c *rest.Config) (*Clientset, error) { 45 | configShallowCopy := *c 46 | 47 | if configShallowCopy.UserAgent == "" { 48 | configShallowCopy.UserAgent = rest.DefaultKubernetesUserAgent() 49 | } 50 | 51 | // share the transport between all clients 52 | httpClient, err := rest.HTTPClientFor(&configShallowCopy) 53 | if err != nil { 54 | return nil, err 55 | } 56 | 57 | return NewForConfigAndClient(&configShallowCopy, httpClient) 58 | } 59 | 60 | // NewForConfigAndClient creates a new Clientset for the given config and http client. 61 | // Note the http client provided takes precedence over the configured transport values. 62 | // If config's RateLimiter is not set and QPS and Burst are acceptable, 63 | // NewForConfigAndClient will generate a rate-limiter in configShallowCopy. 64 | func NewForConfigAndClient(c *rest.Config, httpClient *http.Client) (*Clientset, error) { 65 | configShallowCopy := *c 66 | if configShallowCopy.RateLimiter == nil && configShallowCopy.QPS > 0 { 67 | if configShallowCopy.Burst <= 0 { 68 | return nil, fmt.Errorf("burst is required to be greater than 0 when RateLimiter is not set and QPS is set to greater than 0") 69 | } 70 | configShallowCopy.RateLimiter = flowcontrol.NewTokenBucketRateLimiter(configShallowCopy.QPS, configShallowCopy.Burst) 71 | } 72 | 73 | var cs Clientset 74 | var err error 75 | cs.comicV1, err = comicv1.NewForConfigAndClient(&configShallowCopy, httpClient) 76 | if err != nil { 77 | return nil, err 78 | } 79 | 80 | cs.DiscoveryClient, err = discovery.NewDiscoveryClientForConfigAndClient(&configShallowCopy, httpClient) 81 | if err != nil { 82 | return nil, err 83 | } 84 | return &cs, nil 85 | } 86 | 87 | // NewForConfigOrDie creates a new Clientset for the given config and 88 | // panics if there is an error in the config. 89 | func NewForConfigOrDie(c *rest.Config) *Clientset { 90 | cs, err := NewForConfig(c) 91 | if err != nil { 92 | panic(err) 93 | } 94 | return cs 95 | } 96 | 97 | // New creates a new Clientset for the given RESTClient. 98 | func New(c rest.Interface) *Clientset { 99 | var cs Clientset 100 | cs.comicV1 = comicv1.New(c) 101 | 102 | cs.DiscoveryClient = discovery.NewDiscoveryClient(c) 103 | return &cs 104 | } 105 | -------------------------------------------------------------------------------- /_example/client/clientset/versioned/fake/clientset_generated.go: -------------------------------------------------------------------------------- 1 | // Code generated by client-gen. DO NOT EDIT. 2 | 3 | package fake 4 | 5 | import ( 6 | applyconfiguration "github.com/slok/kube-code-generator/example/client/applyconfiguration" 7 | clientset "github.com/slok/kube-code-generator/example/client/clientset/versioned" 8 | comicv1 "github.com/slok/kube-code-generator/example/client/clientset/versioned/typed/comic/v1" 9 | fakecomicv1 "github.com/slok/kube-code-generator/example/client/clientset/versioned/typed/comic/v1/fake" 10 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 11 | "k8s.io/apimachinery/pkg/runtime" 12 | "k8s.io/apimachinery/pkg/watch" 13 | "k8s.io/client-go/discovery" 14 | fakediscovery "k8s.io/client-go/discovery/fake" 15 | "k8s.io/client-go/testing" 16 | ) 17 | 18 | // NewSimpleClientset returns a clientset that will respond with the provided objects. 19 | // It's backed by a very simple object tracker that processes creates, updates and deletions as-is, 20 | // without applying any field management, validations and/or defaults. It shouldn't be considered a replacement 21 | // for a real clientset and is mostly useful in simple unit tests. 22 | // 23 | // DEPRECATED: NewClientset replaces this with support for field management, which significantly improves 24 | // server side apply testing. NewClientset is only available when apply configurations are generated (e.g. 25 | // via --with-applyconfig). 26 | func NewSimpleClientset(objects ...runtime.Object) *Clientset { 27 | o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder()) 28 | for _, obj := range objects { 29 | if err := o.Add(obj); err != nil { 30 | panic(err) 31 | } 32 | } 33 | 34 | cs := &Clientset{tracker: o} 35 | cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} 36 | cs.AddReactor("*", "*", testing.ObjectReaction(o)) 37 | cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { 38 | var opts metav1.ListOptions 39 | if watchActcion, ok := action.(testing.WatchActionImpl); ok { 40 | opts = watchActcion.ListOptions 41 | } 42 | gvr := action.GetResource() 43 | ns := action.GetNamespace() 44 | watch, err := o.Watch(gvr, ns, opts) 45 | if err != nil { 46 | return false, nil, err 47 | } 48 | return true, watch, nil 49 | }) 50 | 51 | return cs 52 | } 53 | 54 | // Clientset implements clientset.Interface. Meant to be embedded into a 55 | // struct to get a default implementation. This makes faking out just the method 56 | // you want to test easier. 57 | type Clientset struct { 58 | testing.Fake 59 | discovery *fakediscovery.FakeDiscovery 60 | tracker testing.ObjectTracker 61 | } 62 | 63 | func (c *Clientset) Discovery() discovery.DiscoveryInterface { 64 | return c.discovery 65 | } 66 | 67 | func (c *Clientset) Tracker() testing.ObjectTracker { 68 | return c.tracker 69 | } 70 | 71 | // NewClientset returns a clientset that will respond with the provided objects. 72 | // It's backed by a very simple object tracker that processes creates, updates and deletions as-is, 73 | // without applying any validations and/or defaults. It shouldn't be considered a replacement 74 | // for a real clientset and is mostly useful in simple unit tests. 75 | func NewClientset(objects ...runtime.Object) *Clientset { 76 | o := testing.NewFieldManagedObjectTracker( 77 | scheme, 78 | codecs.UniversalDecoder(), 79 | applyconfiguration.NewTypeConverter(scheme), 80 | ) 81 | for _, obj := range objects { 82 | if err := o.Add(obj); err != nil { 83 | panic(err) 84 | } 85 | } 86 | 87 | cs := &Clientset{tracker: o} 88 | cs.discovery = &fakediscovery.FakeDiscovery{Fake: &cs.Fake} 89 | cs.AddReactor("*", "*", testing.ObjectReaction(o)) 90 | cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) { 91 | var opts metav1.ListOptions 92 | if watchActcion, ok := action.(testing.WatchActionImpl); ok { 93 | opts = watchActcion.ListOptions 94 | } 95 | gvr := action.GetResource() 96 | ns := action.GetNamespace() 97 | watch, err := o.Watch(gvr, ns, opts) 98 | if err != nil { 99 | return false, nil, err 100 | } 101 | return true, watch, nil 102 | }) 103 | 104 | return cs 105 | } 106 | 107 | var ( 108 | _ clientset.Interface = &Clientset{} 109 | _ testing.FakeClient = &Clientset{} 110 | ) 111 | 112 | // ComicV1 retrieves the ComicV1Client 113 | func (c *Clientset) ComicV1() comicv1.ComicV1Interface { 114 | return &fakecomicv1.FakeComicV1{Fake: &c.Fake} 115 | } 116 | -------------------------------------------------------------------------------- /_example/client/clientset/versioned/fake/doc.go: -------------------------------------------------------------------------------- 1 | // Code generated by client-gen. DO NOT EDIT. 2 | 3 | // This package has the automatically generated fake clientset. 4 | package fake 5 | -------------------------------------------------------------------------------- /_example/client/clientset/versioned/fake/register.go: -------------------------------------------------------------------------------- 1 | // Code generated by client-gen. DO NOT EDIT. 2 | 3 | package fake 4 | 5 | import ( 6 | comicv1 "github.com/slok/kube-code-generator/example/apis/comic/v1" 7 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | runtime "k8s.io/apimachinery/pkg/runtime" 9 | schema "k8s.io/apimachinery/pkg/runtime/schema" 10 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 11 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 12 | ) 13 | 14 | var scheme = runtime.NewScheme() 15 | var codecs = serializer.NewCodecFactory(scheme) 16 | 17 | var localSchemeBuilder = runtime.SchemeBuilder{ 18 | comicv1.AddToScheme, 19 | } 20 | 21 | // AddToScheme adds all types of this clientset into the given scheme. This allows composition 22 | // of clientsets, like in: 23 | // 24 | // import ( 25 | // "k8s.io/client-go/kubernetes" 26 | // clientsetscheme "k8s.io/client-go/kubernetes/scheme" 27 | // aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" 28 | // ) 29 | // 30 | // kclientset, _ := kubernetes.NewForConfig(c) 31 | // _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) 32 | // 33 | // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types 34 | // correctly. 35 | var AddToScheme = localSchemeBuilder.AddToScheme 36 | 37 | func init() { 38 | v1.AddToGroupVersion(scheme, schema.GroupVersion{Version: "v1"}) 39 | utilruntime.Must(AddToScheme(scheme)) 40 | } 41 | -------------------------------------------------------------------------------- /_example/client/clientset/versioned/scheme/doc.go: -------------------------------------------------------------------------------- 1 | // Code generated by client-gen. DO NOT EDIT. 2 | 3 | // This package contains the scheme of the automatically generated clientset. 4 | package scheme 5 | -------------------------------------------------------------------------------- /_example/client/clientset/versioned/scheme/register.go: -------------------------------------------------------------------------------- 1 | // Code generated by client-gen. DO NOT EDIT. 2 | 3 | package scheme 4 | 5 | import ( 6 | comicv1 "github.com/slok/kube-code-generator/example/apis/comic/v1" 7 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 8 | runtime "k8s.io/apimachinery/pkg/runtime" 9 | schema "k8s.io/apimachinery/pkg/runtime/schema" 10 | serializer "k8s.io/apimachinery/pkg/runtime/serializer" 11 | utilruntime "k8s.io/apimachinery/pkg/util/runtime" 12 | ) 13 | 14 | var Scheme = runtime.NewScheme() 15 | var Codecs = serializer.NewCodecFactory(Scheme) 16 | var ParameterCodec = runtime.NewParameterCodec(Scheme) 17 | var localSchemeBuilder = runtime.SchemeBuilder{ 18 | comicv1.AddToScheme, 19 | } 20 | 21 | // AddToScheme adds all types of this clientset into the given scheme. This allows composition 22 | // of clientsets, like in: 23 | // 24 | // import ( 25 | // "k8s.io/client-go/kubernetes" 26 | // clientsetscheme "k8s.io/client-go/kubernetes/scheme" 27 | // aggregatorclientsetscheme "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/scheme" 28 | // ) 29 | // 30 | // kclientset, _ := kubernetes.NewForConfig(c) 31 | // _ = aggregatorclientsetscheme.AddToScheme(clientsetscheme.Scheme) 32 | // 33 | // After this, RawExtensions in Kubernetes types will serialize kube-aggregator types 34 | // correctly. 35 | var AddToScheme = localSchemeBuilder.AddToScheme 36 | 37 | func init() { 38 | v1.AddToGroupVersion(Scheme, schema.GroupVersion{Version: "v1"}) 39 | utilruntime.Must(AddToScheme(Scheme)) 40 | } 41 | -------------------------------------------------------------------------------- /_example/client/clientset/versioned/typed/comic/v1/comic_client.go: -------------------------------------------------------------------------------- 1 | // Code generated by client-gen. DO NOT EDIT. 2 | 3 | package v1 4 | 5 | import ( 6 | http "net/http" 7 | 8 | comicv1 "github.com/slok/kube-code-generator/example/apis/comic/v1" 9 | scheme "github.com/slok/kube-code-generator/example/client/clientset/versioned/scheme" 10 | rest "k8s.io/client-go/rest" 11 | ) 12 | 13 | type ComicV1Interface interface { 14 | RESTClient() rest.Interface 15 | HerosGetter 16 | } 17 | 18 | // ComicV1Client is used to interact with features provided by the comic.kube-code-generator.slok.dev group. 19 | type ComicV1Client struct { 20 | restClient rest.Interface 21 | } 22 | 23 | func (c *ComicV1Client) Heros() HeroInterface { 24 | return newHeros(c) 25 | } 26 | 27 | // NewForConfig creates a new ComicV1Client for the given config. 28 | // NewForConfig is equivalent to NewForConfigAndClient(c, httpClient), 29 | // where httpClient was generated with rest.HTTPClientFor(c). 30 | func NewForConfig(c *rest.Config) (*ComicV1Client, error) { 31 | config := *c 32 | setConfigDefaults(&config) 33 | httpClient, err := rest.HTTPClientFor(&config) 34 | if err != nil { 35 | return nil, err 36 | } 37 | return NewForConfigAndClient(&config, httpClient) 38 | } 39 | 40 | // NewForConfigAndClient creates a new ComicV1Client for the given config and http client. 41 | // Note the http client provided takes precedence over the configured transport values. 42 | func NewForConfigAndClient(c *rest.Config, h *http.Client) (*ComicV1Client, error) { 43 | config := *c 44 | setConfigDefaults(&config) 45 | client, err := rest.RESTClientForConfigAndClient(&config, h) 46 | if err != nil { 47 | return nil, err 48 | } 49 | return &ComicV1Client{client}, nil 50 | } 51 | 52 | // NewForConfigOrDie creates a new ComicV1Client for the given config and 53 | // panics if there is an error in the config. 54 | func NewForConfigOrDie(c *rest.Config) *ComicV1Client { 55 | client, err := NewForConfig(c) 56 | if err != nil { 57 | panic(err) 58 | } 59 | return client 60 | } 61 | 62 | // New creates a new ComicV1Client for the given RESTClient. 63 | func New(c rest.Interface) *ComicV1Client { 64 | return &ComicV1Client{c} 65 | } 66 | 67 | func setConfigDefaults(config *rest.Config) { 68 | gv := comicv1.SchemeGroupVersion 69 | config.GroupVersion = &gv 70 | config.APIPath = "/apis" 71 | config.NegotiatedSerializer = rest.CodecFactoryForGeneratedClient(scheme.Scheme, scheme.Codecs).WithoutConversion() 72 | 73 | if config.UserAgent == "" { 74 | config.UserAgent = rest.DefaultKubernetesUserAgent() 75 | } 76 | } 77 | 78 | // RESTClient returns a RESTClient that is used to communicate 79 | // with API server by this client implementation. 80 | func (c *ComicV1Client) RESTClient() rest.Interface { 81 | if c == nil { 82 | return nil 83 | } 84 | return c.restClient 85 | } 86 | -------------------------------------------------------------------------------- /_example/client/clientset/versioned/typed/comic/v1/doc.go: -------------------------------------------------------------------------------- 1 | // Code generated by client-gen. DO NOT EDIT. 2 | 3 | // This package has the automatically generated typed clients. 4 | package v1 5 | -------------------------------------------------------------------------------- /_example/client/clientset/versioned/typed/comic/v1/fake/doc.go: -------------------------------------------------------------------------------- 1 | // Code generated by client-gen. DO NOT EDIT. 2 | 3 | // Package fake has the automatically generated clients. 4 | package fake 5 | -------------------------------------------------------------------------------- /_example/client/clientset/versioned/typed/comic/v1/fake/fake_comic_client.go: -------------------------------------------------------------------------------- 1 | // Code generated by client-gen. DO NOT EDIT. 2 | 3 | package fake 4 | 5 | import ( 6 | v1 "github.com/slok/kube-code-generator/example/client/clientset/versioned/typed/comic/v1" 7 | rest "k8s.io/client-go/rest" 8 | testing "k8s.io/client-go/testing" 9 | ) 10 | 11 | type FakeComicV1 struct { 12 | *testing.Fake 13 | } 14 | 15 | func (c *FakeComicV1) Heros() v1.HeroInterface { 16 | return newFakeHeros(c) 17 | } 18 | 19 | // RESTClient returns a RESTClient that is used to communicate 20 | // with API server by this client implementation. 21 | func (c *FakeComicV1) RESTClient() rest.Interface { 22 | var ret *rest.RESTClient 23 | return ret 24 | } 25 | -------------------------------------------------------------------------------- /_example/client/clientset/versioned/typed/comic/v1/fake/fake_hero.go: -------------------------------------------------------------------------------- 1 | // Code generated by client-gen. DO NOT EDIT. 2 | 3 | package fake 4 | 5 | import ( 6 | v1 "github.com/slok/kube-code-generator/example/apis/comic/v1" 7 | comicv1 "github.com/slok/kube-code-generator/example/client/applyconfiguration/comic/v1" 8 | typedcomicv1 "github.com/slok/kube-code-generator/example/client/clientset/versioned/typed/comic/v1" 9 | gentype "k8s.io/client-go/gentype" 10 | ) 11 | 12 | // fakeHeros implements HeroInterface 13 | type fakeHeros struct { 14 | *gentype.FakeClientWithListAndApply[*v1.Hero, *v1.HeroList, *comicv1.HeroApplyConfiguration] 15 | Fake *FakeComicV1 16 | } 17 | 18 | func newFakeHeros(fake *FakeComicV1) typedcomicv1.HeroInterface { 19 | return &fakeHeros{ 20 | gentype.NewFakeClientWithListAndApply[*v1.Hero, *v1.HeroList, *comicv1.HeroApplyConfiguration]( 21 | fake.Fake, 22 | "", 23 | v1.SchemeGroupVersion.WithResource("heros"), 24 | v1.SchemeGroupVersion.WithKind("Hero"), 25 | func() *v1.Hero { return &v1.Hero{} }, 26 | func() *v1.HeroList { return &v1.HeroList{} }, 27 | func(dst, src *v1.HeroList) { dst.ListMeta = src.ListMeta }, 28 | func(list *v1.HeroList) []*v1.Hero { return gentype.ToPointerSlice(list.Items) }, 29 | func(list *v1.HeroList, items []*v1.Hero) { list.Items = gentype.FromPointerSlice(items) }, 30 | ), 31 | fake, 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /_example/client/clientset/versioned/typed/comic/v1/generated_expansion.go: -------------------------------------------------------------------------------- 1 | // Code generated by client-gen. DO NOT EDIT. 2 | 3 | package v1 4 | 5 | type HeroExpansion interface{} 6 | -------------------------------------------------------------------------------- /_example/client/clientset/versioned/typed/comic/v1/hero.go: -------------------------------------------------------------------------------- 1 | // Code generated by client-gen. DO NOT EDIT. 2 | 3 | package v1 4 | 5 | import ( 6 | context "context" 7 | 8 | comicv1 "github.com/slok/kube-code-generator/example/apis/comic/v1" 9 | applyconfigurationcomicv1 "github.com/slok/kube-code-generator/example/client/applyconfiguration/comic/v1" 10 | scheme "github.com/slok/kube-code-generator/example/client/clientset/versioned/scheme" 11 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 12 | types "k8s.io/apimachinery/pkg/types" 13 | watch "k8s.io/apimachinery/pkg/watch" 14 | gentype "k8s.io/client-go/gentype" 15 | ) 16 | 17 | // HerosGetter has a method to return a HeroInterface. 18 | // A group's client should implement this interface. 19 | type HerosGetter interface { 20 | Heros() HeroInterface 21 | } 22 | 23 | // HeroInterface has methods to work with Hero resources. 24 | type HeroInterface interface { 25 | Create(ctx context.Context, hero *comicv1.Hero, opts metav1.CreateOptions) (*comicv1.Hero, error) 26 | Update(ctx context.Context, hero *comicv1.Hero, opts metav1.UpdateOptions) (*comicv1.Hero, error) 27 | // Add a +genclient:noStatus comment above the type to avoid generating UpdateStatus(). 28 | UpdateStatus(ctx context.Context, hero *comicv1.Hero, opts metav1.UpdateOptions) (*comicv1.Hero, error) 29 | Delete(ctx context.Context, name string, opts metav1.DeleteOptions) error 30 | DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOpts metav1.ListOptions) error 31 | Get(ctx context.Context, name string, opts metav1.GetOptions) (*comicv1.Hero, error) 32 | List(ctx context.Context, opts metav1.ListOptions) (*comicv1.HeroList, error) 33 | Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) 34 | Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (result *comicv1.Hero, err error) 35 | Apply(ctx context.Context, hero *applyconfigurationcomicv1.HeroApplyConfiguration, opts metav1.ApplyOptions) (result *comicv1.Hero, err error) 36 | // Add a +genclient:noStatus comment above the type to avoid generating ApplyStatus(). 37 | ApplyStatus(ctx context.Context, hero *applyconfigurationcomicv1.HeroApplyConfiguration, opts metav1.ApplyOptions) (result *comicv1.Hero, err error) 38 | HeroExpansion 39 | } 40 | 41 | // heros implements HeroInterface 42 | type heros struct { 43 | *gentype.ClientWithListAndApply[*comicv1.Hero, *comicv1.HeroList, *applyconfigurationcomicv1.HeroApplyConfiguration] 44 | } 45 | 46 | // newHeros returns a Heros 47 | func newHeros(c *ComicV1Client) *heros { 48 | return &heros{ 49 | gentype.NewClientWithListAndApply[*comicv1.Hero, *comicv1.HeroList, *applyconfigurationcomicv1.HeroApplyConfiguration]( 50 | "heros", 51 | c.RESTClient(), 52 | scheme.ParameterCodec, 53 | "", 54 | func() *comicv1.Hero { return &comicv1.Hero{} }, 55 | func() *comicv1.HeroList { return &comicv1.HeroList{} }, 56 | ), 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /_example/client/informers/externalversions/comic/interface.go: -------------------------------------------------------------------------------- 1 | // Code generated by informer-gen. DO NOT EDIT. 2 | 3 | package comic 4 | 5 | import ( 6 | v1 "github.com/slok/kube-code-generator/example/client/informers/externalversions/comic/v1" 7 | internalinterfaces "github.com/slok/kube-code-generator/example/client/informers/externalversions/internalinterfaces" 8 | ) 9 | 10 | // Interface provides access to each of this group's versions. 11 | type Interface interface { 12 | // V1 provides access to shared informers for resources in V1. 13 | V1() v1.Interface 14 | } 15 | 16 | type group struct { 17 | factory internalinterfaces.SharedInformerFactory 18 | namespace string 19 | tweakListOptions internalinterfaces.TweakListOptionsFunc 20 | } 21 | 22 | // New returns a new Interface. 23 | func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { 24 | return &group{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} 25 | } 26 | 27 | // V1 returns a new v1.Interface. 28 | func (g *group) V1() v1.Interface { 29 | return v1.New(g.factory, g.namespace, g.tweakListOptions) 30 | } 31 | -------------------------------------------------------------------------------- /_example/client/informers/externalversions/comic/v1/hero.go: -------------------------------------------------------------------------------- 1 | // Code generated by informer-gen. DO NOT EDIT. 2 | 3 | package v1 4 | 5 | import ( 6 | context "context" 7 | time "time" 8 | 9 | apiscomicv1 "github.com/slok/kube-code-generator/example/apis/comic/v1" 10 | versioned "github.com/slok/kube-code-generator/example/client/clientset/versioned" 11 | internalinterfaces "github.com/slok/kube-code-generator/example/client/informers/externalversions/internalinterfaces" 12 | comicv1 "github.com/slok/kube-code-generator/example/client/listers/comic/v1" 13 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 14 | runtime "k8s.io/apimachinery/pkg/runtime" 15 | watch "k8s.io/apimachinery/pkg/watch" 16 | cache "k8s.io/client-go/tools/cache" 17 | ) 18 | 19 | // HeroInformer provides access to a shared informer and lister for 20 | // Heros. 21 | type HeroInformer interface { 22 | Informer() cache.SharedIndexInformer 23 | Lister() comicv1.HeroLister 24 | } 25 | 26 | type heroInformer struct { 27 | factory internalinterfaces.SharedInformerFactory 28 | tweakListOptions internalinterfaces.TweakListOptionsFunc 29 | } 30 | 31 | // NewHeroInformer constructs a new informer for Hero type. 32 | // Always prefer using an informer factory to get a shared informer instead of getting an independent 33 | // one. This reduces memory footprint and number of connections to the server. 34 | func NewHeroInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers) cache.SharedIndexInformer { 35 | return NewFilteredHeroInformer(client, resyncPeriod, indexers, nil) 36 | } 37 | 38 | // NewFilteredHeroInformer constructs a new informer for Hero type. 39 | // Always prefer using an informer factory to get a shared informer instead of getting an independent 40 | // one. This reduces memory footprint and number of connections to the server. 41 | func NewFilteredHeroInformer(client versioned.Interface, resyncPeriod time.Duration, indexers cache.Indexers, tweakListOptions internalinterfaces.TweakListOptionsFunc) cache.SharedIndexInformer { 42 | return cache.NewSharedIndexInformer( 43 | &cache.ListWatch{ 44 | ListFunc: func(options metav1.ListOptions) (runtime.Object, error) { 45 | if tweakListOptions != nil { 46 | tweakListOptions(&options) 47 | } 48 | return client.ComicV1().Heros().List(context.Background(), options) 49 | }, 50 | WatchFunc: func(options metav1.ListOptions) (watch.Interface, error) { 51 | if tweakListOptions != nil { 52 | tweakListOptions(&options) 53 | } 54 | return client.ComicV1().Heros().Watch(context.Background(), options) 55 | }, 56 | ListWithContextFunc: func(ctx context.Context, options metav1.ListOptions) (runtime.Object, error) { 57 | if tweakListOptions != nil { 58 | tweakListOptions(&options) 59 | } 60 | return client.ComicV1().Heros().List(ctx, options) 61 | }, 62 | WatchFuncWithContext: func(ctx context.Context, options metav1.ListOptions) (watch.Interface, error) { 63 | if tweakListOptions != nil { 64 | tweakListOptions(&options) 65 | } 66 | return client.ComicV1().Heros().Watch(ctx, options) 67 | }, 68 | }, 69 | &apiscomicv1.Hero{}, 70 | resyncPeriod, 71 | indexers, 72 | ) 73 | } 74 | 75 | func (f *heroInformer) defaultInformer(client versioned.Interface, resyncPeriod time.Duration) cache.SharedIndexInformer { 76 | return NewFilteredHeroInformer(client, resyncPeriod, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}, f.tweakListOptions) 77 | } 78 | 79 | func (f *heroInformer) Informer() cache.SharedIndexInformer { 80 | return f.factory.InformerFor(&apiscomicv1.Hero{}, f.defaultInformer) 81 | } 82 | 83 | func (f *heroInformer) Lister() comicv1.HeroLister { 84 | return comicv1.NewHeroLister(f.Informer().GetIndexer()) 85 | } 86 | -------------------------------------------------------------------------------- /_example/client/informers/externalversions/comic/v1/interface.go: -------------------------------------------------------------------------------- 1 | // Code generated by informer-gen. DO NOT EDIT. 2 | 3 | package v1 4 | 5 | import ( 6 | internalinterfaces "github.com/slok/kube-code-generator/example/client/informers/externalversions/internalinterfaces" 7 | ) 8 | 9 | // Interface provides access to all the informers in this group version. 10 | type Interface interface { 11 | // Heros returns a HeroInformer. 12 | Heros() HeroInformer 13 | } 14 | 15 | type version struct { 16 | factory internalinterfaces.SharedInformerFactory 17 | namespace string 18 | tweakListOptions internalinterfaces.TweakListOptionsFunc 19 | } 20 | 21 | // New returns a new Interface. 22 | func New(f internalinterfaces.SharedInformerFactory, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) Interface { 23 | return &version{factory: f, namespace: namespace, tweakListOptions: tweakListOptions} 24 | } 25 | 26 | // Heros returns a HeroInformer. 27 | func (v *version) Heros() HeroInformer { 28 | return &heroInformer{factory: v.factory, tweakListOptions: v.tweakListOptions} 29 | } 30 | -------------------------------------------------------------------------------- /_example/client/informers/externalversions/factory.go: -------------------------------------------------------------------------------- 1 | // Code generated by informer-gen. DO NOT EDIT. 2 | 3 | package externalversions 4 | 5 | import ( 6 | reflect "reflect" 7 | sync "sync" 8 | time "time" 9 | 10 | versioned "github.com/slok/kube-code-generator/example/client/clientset/versioned" 11 | comic "github.com/slok/kube-code-generator/example/client/informers/externalversions/comic" 12 | internalinterfaces "github.com/slok/kube-code-generator/example/client/informers/externalversions/internalinterfaces" 13 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 14 | runtime "k8s.io/apimachinery/pkg/runtime" 15 | schema "k8s.io/apimachinery/pkg/runtime/schema" 16 | cache "k8s.io/client-go/tools/cache" 17 | ) 18 | 19 | // SharedInformerOption defines the functional option type for SharedInformerFactory. 20 | type SharedInformerOption func(*sharedInformerFactory) *sharedInformerFactory 21 | 22 | type sharedInformerFactory struct { 23 | client versioned.Interface 24 | namespace string 25 | tweakListOptions internalinterfaces.TweakListOptionsFunc 26 | lock sync.Mutex 27 | defaultResync time.Duration 28 | customResync map[reflect.Type]time.Duration 29 | transform cache.TransformFunc 30 | 31 | informers map[reflect.Type]cache.SharedIndexInformer 32 | // startedInformers is used for tracking which informers have been started. 33 | // This allows Start() to be called multiple times safely. 34 | startedInformers map[reflect.Type]bool 35 | // wg tracks how many goroutines were started. 36 | wg sync.WaitGroup 37 | // shuttingDown is true when Shutdown has been called. It may still be running 38 | // because it needs to wait for goroutines. 39 | shuttingDown bool 40 | } 41 | 42 | // WithCustomResyncConfig sets a custom resync period for the specified informer types. 43 | func WithCustomResyncConfig(resyncConfig map[v1.Object]time.Duration) SharedInformerOption { 44 | return func(factory *sharedInformerFactory) *sharedInformerFactory { 45 | for k, v := range resyncConfig { 46 | factory.customResync[reflect.TypeOf(k)] = v 47 | } 48 | return factory 49 | } 50 | } 51 | 52 | // WithTweakListOptions sets a custom filter on all listers of the configured SharedInformerFactory. 53 | func WithTweakListOptions(tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerOption { 54 | return func(factory *sharedInformerFactory) *sharedInformerFactory { 55 | factory.tweakListOptions = tweakListOptions 56 | return factory 57 | } 58 | } 59 | 60 | // WithNamespace limits the SharedInformerFactory to the specified namespace. 61 | func WithNamespace(namespace string) SharedInformerOption { 62 | return func(factory *sharedInformerFactory) *sharedInformerFactory { 63 | factory.namespace = namespace 64 | return factory 65 | } 66 | } 67 | 68 | // WithTransform sets a transform on all informers. 69 | func WithTransform(transform cache.TransformFunc) SharedInformerOption { 70 | return func(factory *sharedInformerFactory) *sharedInformerFactory { 71 | factory.transform = transform 72 | return factory 73 | } 74 | } 75 | 76 | // NewSharedInformerFactory constructs a new instance of sharedInformerFactory for all namespaces. 77 | func NewSharedInformerFactory(client versioned.Interface, defaultResync time.Duration) SharedInformerFactory { 78 | return NewSharedInformerFactoryWithOptions(client, defaultResync) 79 | } 80 | 81 | // NewFilteredSharedInformerFactory constructs a new instance of sharedInformerFactory. 82 | // Listers obtained via this SharedInformerFactory will be subject to the same filters 83 | // as specified here. 84 | // Deprecated: Please use NewSharedInformerFactoryWithOptions instead 85 | func NewFilteredSharedInformerFactory(client versioned.Interface, defaultResync time.Duration, namespace string, tweakListOptions internalinterfaces.TweakListOptionsFunc) SharedInformerFactory { 86 | return NewSharedInformerFactoryWithOptions(client, defaultResync, WithNamespace(namespace), WithTweakListOptions(tweakListOptions)) 87 | } 88 | 89 | // NewSharedInformerFactoryWithOptions constructs a new instance of a SharedInformerFactory with additional options. 90 | func NewSharedInformerFactoryWithOptions(client versioned.Interface, defaultResync time.Duration, options ...SharedInformerOption) SharedInformerFactory { 91 | factory := &sharedInformerFactory{ 92 | client: client, 93 | namespace: v1.NamespaceAll, 94 | defaultResync: defaultResync, 95 | informers: make(map[reflect.Type]cache.SharedIndexInformer), 96 | startedInformers: make(map[reflect.Type]bool), 97 | customResync: make(map[reflect.Type]time.Duration), 98 | } 99 | 100 | // Apply all options 101 | for _, opt := range options { 102 | factory = opt(factory) 103 | } 104 | 105 | return factory 106 | } 107 | 108 | func (f *sharedInformerFactory) Start(stopCh <-chan struct{}) { 109 | f.lock.Lock() 110 | defer f.lock.Unlock() 111 | 112 | if f.shuttingDown { 113 | return 114 | } 115 | 116 | for informerType, informer := range f.informers { 117 | if !f.startedInformers[informerType] { 118 | f.wg.Add(1) 119 | // We need a new variable in each loop iteration, 120 | // otherwise the goroutine would use the loop variable 121 | // and that keeps changing. 122 | informer := informer 123 | go func() { 124 | defer f.wg.Done() 125 | informer.Run(stopCh) 126 | }() 127 | f.startedInformers[informerType] = true 128 | } 129 | } 130 | } 131 | 132 | func (f *sharedInformerFactory) Shutdown() { 133 | f.lock.Lock() 134 | f.shuttingDown = true 135 | f.lock.Unlock() 136 | 137 | // Will return immediately if there is nothing to wait for. 138 | f.wg.Wait() 139 | } 140 | 141 | func (f *sharedInformerFactory) WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool { 142 | informers := func() map[reflect.Type]cache.SharedIndexInformer { 143 | f.lock.Lock() 144 | defer f.lock.Unlock() 145 | 146 | informers := map[reflect.Type]cache.SharedIndexInformer{} 147 | for informerType, informer := range f.informers { 148 | if f.startedInformers[informerType] { 149 | informers[informerType] = informer 150 | } 151 | } 152 | return informers 153 | }() 154 | 155 | res := map[reflect.Type]bool{} 156 | for informType, informer := range informers { 157 | res[informType] = cache.WaitForCacheSync(stopCh, informer.HasSynced) 158 | } 159 | return res 160 | } 161 | 162 | // InformerFor returns the SharedIndexInformer for obj using an internal 163 | // client. 164 | func (f *sharedInformerFactory) InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer { 165 | f.lock.Lock() 166 | defer f.lock.Unlock() 167 | 168 | informerType := reflect.TypeOf(obj) 169 | informer, exists := f.informers[informerType] 170 | if exists { 171 | return informer 172 | } 173 | 174 | resyncPeriod, exists := f.customResync[informerType] 175 | if !exists { 176 | resyncPeriod = f.defaultResync 177 | } 178 | 179 | informer = newFunc(f.client, resyncPeriod) 180 | informer.SetTransform(f.transform) 181 | f.informers[informerType] = informer 182 | 183 | return informer 184 | } 185 | 186 | // SharedInformerFactory provides shared informers for resources in all known 187 | // API group versions. 188 | // 189 | // It is typically used like this: 190 | // 191 | // ctx, cancel := context.Background() 192 | // defer cancel() 193 | // factory := NewSharedInformerFactory(client, resyncPeriod) 194 | // defer factory.WaitForStop() // Returns immediately if nothing was started. 195 | // genericInformer := factory.ForResource(resource) 196 | // typedInformer := factory.SomeAPIGroup().V1().SomeType() 197 | // factory.Start(ctx.Done()) // Start processing these informers. 198 | // synced := factory.WaitForCacheSync(ctx.Done()) 199 | // for v, ok := range synced { 200 | // if !ok { 201 | // fmt.Fprintf(os.Stderr, "caches failed to sync: %v", v) 202 | // return 203 | // } 204 | // } 205 | // 206 | // // Creating informers can also be created after Start, but then 207 | // // Start must be called again: 208 | // anotherGenericInformer := factory.ForResource(resource) 209 | // factory.Start(ctx.Done()) 210 | type SharedInformerFactory interface { 211 | internalinterfaces.SharedInformerFactory 212 | 213 | // Start initializes all requested informers. They are handled in goroutines 214 | // which run until the stop channel gets closed. 215 | // Warning: Start does not block. When run in a go-routine, it will race with a later WaitForCacheSync. 216 | Start(stopCh <-chan struct{}) 217 | 218 | // Shutdown marks a factory as shutting down. At that point no new 219 | // informers can be started anymore and Start will return without 220 | // doing anything. 221 | // 222 | // In addition, Shutdown blocks until all goroutines have terminated. For that 223 | // to happen, the close channel(s) that they were started with must be closed, 224 | // either before Shutdown gets called or while it is waiting. 225 | // 226 | // Shutdown may be called multiple times, even concurrently. All such calls will 227 | // block until all goroutines have terminated. 228 | Shutdown() 229 | 230 | // WaitForCacheSync blocks until all started informers' caches were synced 231 | // or the stop channel gets closed. 232 | WaitForCacheSync(stopCh <-chan struct{}) map[reflect.Type]bool 233 | 234 | // ForResource gives generic access to a shared informer of the matching type. 235 | ForResource(resource schema.GroupVersionResource) (GenericInformer, error) 236 | 237 | // InformerFor returns the SharedIndexInformer for obj using an internal 238 | // client. 239 | InformerFor(obj runtime.Object, newFunc internalinterfaces.NewInformerFunc) cache.SharedIndexInformer 240 | 241 | Comic() comic.Interface 242 | } 243 | 244 | func (f *sharedInformerFactory) Comic() comic.Interface { 245 | return comic.New(f, f.namespace, f.tweakListOptions) 246 | } 247 | -------------------------------------------------------------------------------- /_example/client/informers/externalversions/generic.go: -------------------------------------------------------------------------------- 1 | // Code generated by informer-gen. DO NOT EDIT. 2 | 3 | package externalversions 4 | 5 | import ( 6 | fmt "fmt" 7 | 8 | v1 "github.com/slok/kube-code-generator/example/apis/comic/v1" 9 | schema "k8s.io/apimachinery/pkg/runtime/schema" 10 | cache "k8s.io/client-go/tools/cache" 11 | ) 12 | 13 | // GenericInformer is type of SharedIndexInformer which will locate and delegate to other 14 | // sharedInformers based on type 15 | type GenericInformer interface { 16 | Informer() cache.SharedIndexInformer 17 | Lister() cache.GenericLister 18 | } 19 | 20 | type genericInformer struct { 21 | informer cache.SharedIndexInformer 22 | resource schema.GroupResource 23 | } 24 | 25 | // Informer returns the SharedIndexInformer. 26 | func (f *genericInformer) Informer() cache.SharedIndexInformer { 27 | return f.informer 28 | } 29 | 30 | // Lister returns the GenericLister. 31 | func (f *genericInformer) Lister() cache.GenericLister { 32 | return cache.NewGenericLister(f.Informer().GetIndexer(), f.resource) 33 | } 34 | 35 | // ForResource gives generic access to a shared informer of the matching type 36 | // TODO extend this to unknown resources with a client pool 37 | func (f *sharedInformerFactory) ForResource(resource schema.GroupVersionResource) (GenericInformer, error) { 38 | switch resource { 39 | // Group=comic.kube-code-generator.slok.dev, Version=v1 40 | case v1.SchemeGroupVersion.WithResource("heros"): 41 | return &genericInformer{resource: resource.GroupResource(), informer: f.Comic().V1().Heros().Informer()}, nil 42 | 43 | } 44 | 45 | return nil, fmt.Errorf("no informer found for %v", resource) 46 | } 47 | -------------------------------------------------------------------------------- /_example/client/informers/externalversions/internalinterfaces/factory_interfaces.go: -------------------------------------------------------------------------------- 1 | // Code generated by informer-gen. DO NOT EDIT. 2 | 3 | package internalinterfaces 4 | 5 | import ( 6 | time "time" 7 | 8 | versioned "github.com/slok/kube-code-generator/example/client/clientset/versioned" 9 | v1 "k8s.io/apimachinery/pkg/apis/meta/v1" 10 | runtime "k8s.io/apimachinery/pkg/runtime" 11 | cache "k8s.io/client-go/tools/cache" 12 | ) 13 | 14 | // NewInformerFunc takes versioned.Interface and time.Duration to return a SharedIndexInformer. 15 | type NewInformerFunc func(versioned.Interface, time.Duration) cache.SharedIndexInformer 16 | 17 | // SharedInformerFactory a small interface to allow for adding an informer without an import cycle 18 | type SharedInformerFactory interface { 19 | Start(stopCh <-chan struct{}) 20 | InformerFor(obj runtime.Object, newFunc NewInformerFunc) cache.SharedIndexInformer 21 | } 22 | 23 | // TweakListOptionsFunc is a function that transforms a v1.ListOptions. 24 | type TweakListOptionsFunc func(*v1.ListOptions) 25 | -------------------------------------------------------------------------------- /_example/client/listers/comic/v1/expansion_generated.go: -------------------------------------------------------------------------------- 1 | // Code generated by lister-gen. DO NOT EDIT. 2 | 3 | package v1 4 | 5 | // HeroListerExpansion allows custom methods to be added to 6 | // HeroLister. 7 | type HeroListerExpansion interface{} 8 | -------------------------------------------------------------------------------- /_example/client/listers/comic/v1/hero.go: -------------------------------------------------------------------------------- 1 | // Code generated by lister-gen. DO NOT EDIT. 2 | 3 | package v1 4 | 5 | import ( 6 | comicv1 "github.com/slok/kube-code-generator/example/apis/comic/v1" 7 | labels "k8s.io/apimachinery/pkg/labels" 8 | listers "k8s.io/client-go/listers" 9 | cache "k8s.io/client-go/tools/cache" 10 | ) 11 | 12 | // HeroLister helps list Heros. 13 | // All objects returned here must be treated as read-only. 14 | type HeroLister interface { 15 | // List lists all Heros in the indexer. 16 | // Objects returned here must be treated as read-only. 17 | List(selector labels.Selector) (ret []*comicv1.Hero, err error) 18 | // Get retrieves the Hero from the index for a given name. 19 | // Objects returned here must be treated as read-only. 20 | Get(name string) (*comicv1.Hero, error) 21 | HeroListerExpansion 22 | } 23 | 24 | // heroLister implements the HeroLister interface. 25 | type heroLister struct { 26 | listers.ResourceIndexer[*comicv1.Hero] 27 | } 28 | 29 | // NewHeroLister returns a new HeroLister. 30 | func NewHeroLister(indexer cache.Indexer) HeroLister { 31 | return &heroLister{listers.New[*comicv1.Hero](indexer, comicv1.Resource("hero"))} 32 | } 33 | -------------------------------------------------------------------------------- /_example/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/slok/kube-code-generator/example 2 | 3 | go 1.24.0 4 | 5 | require ( 6 | k8s.io/apimachinery v0.33.0 7 | k8s.io/client-go v0.33.0 8 | sigs.k8s.io/structured-merge-diff/v4 v4.7.0 9 | ) 10 | 11 | require ( 12 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 13 | github.com/emicklei/go-restful/v3 v3.12.2 // indirect 14 | github.com/fxamacker/cbor/v2 v2.8.0 // indirect 15 | github.com/go-logr/logr v1.4.2 // indirect 16 | github.com/go-openapi/jsonpointer v0.21.1 // indirect 17 | github.com/go-openapi/jsonreference v0.21.0 // indirect 18 | github.com/go-openapi/swag v0.23.1 // indirect 19 | github.com/gogo/protobuf v1.3.2 // indirect 20 | github.com/golang/protobuf v1.5.4 // indirect 21 | github.com/google/gnostic-models v0.6.9 // indirect 22 | github.com/google/go-cmp v0.7.0 // indirect 23 | github.com/google/gofuzz v1.2.0 // indirect 24 | github.com/google/uuid v1.6.0 // indirect 25 | github.com/josharian/intern v1.0.0 // indirect 26 | github.com/json-iterator/go v1.1.12 // indirect 27 | github.com/mailru/easyjson v0.9.0 // indirect 28 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 29 | github.com/modern-go/reflect2 v1.0.2 // indirect 30 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 31 | github.com/pkg/errors v0.9.1 // indirect 32 | github.com/x448/float16 v0.8.4 // indirect 33 | golang.org/x/net v0.39.0 // indirect 34 | golang.org/x/oauth2 v0.29.0 // indirect 35 | golang.org/x/sys v0.32.0 // indirect 36 | golang.org/x/term v0.31.0 // indirect 37 | golang.org/x/text v0.24.0 // indirect 38 | golang.org/x/time v0.11.0 // indirect 39 | google.golang.org/protobuf v1.36.6 // indirect 40 | gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect 41 | gopkg.in/inf.v0 v0.9.1 // indirect 42 | gopkg.in/yaml.v3 v3.0.1 // indirect 43 | k8s.io/api v0.33.0 // indirect 44 | k8s.io/klog/v2 v2.130.1 // indirect 45 | k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect 46 | k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979 // indirect 47 | sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect 48 | sigs.k8s.io/randfill v1.0.0 // indirect 49 | sigs.k8s.io/yaml v1.4.0 // indirect 50 | ) 51 | -------------------------------------------------------------------------------- /_example/go.sum: -------------------------------------------------------------------------------- 1 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 2 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 3 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 4 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= 5 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= 7 | github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= 8 | github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU= 9 | github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= 10 | github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= 11 | github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= 12 | github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU= 13 | github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= 14 | github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= 15 | github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 16 | github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= 17 | github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= 18 | github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= 19 | github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic= 20 | github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk= 21 | github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= 22 | github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= 23 | github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= 24 | github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= 25 | github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= 26 | github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= 27 | github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= 28 | github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= 29 | github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= 30 | github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= 31 | github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= 32 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 33 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 34 | github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= 35 | github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= 36 | github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= 37 | github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= 38 | github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= 39 | github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= 40 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 41 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 42 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 43 | github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= 44 | github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 45 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 46 | github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= 47 | github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 48 | github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= 49 | github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= 50 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 51 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 52 | github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= 53 | github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 54 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 55 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 56 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 57 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 58 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 59 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 60 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 61 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 62 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 63 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 64 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 65 | github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= 66 | github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= 67 | github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= 68 | github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= 69 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 70 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 71 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 72 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 73 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 74 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= 75 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 76 | github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= 77 | github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= 78 | github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= 79 | github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= 80 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 81 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 82 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 83 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= 84 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 85 | github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= 86 | github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= 87 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 88 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 89 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 90 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 91 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 92 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 93 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 94 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 95 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 96 | github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= 97 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 98 | github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= 99 | github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= 100 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 101 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 102 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 103 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 104 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 105 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 106 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 107 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 108 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 109 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 110 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 111 | golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= 112 | golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= 113 | golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY= 114 | golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E= 115 | golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= 116 | golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= 117 | golang.org/x/oauth2 v0.29.0 h1:WdYw2tdTK1S8olAzWHdgeqfy+Mtm9XNhv/xJsY65d98= 118 | golang.org/x/oauth2 v0.29.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= 119 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 120 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 121 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 122 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 123 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 124 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 125 | golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo= 126 | golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 127 | golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= 128 | golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 129 | golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24= 130 | golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= 131 | golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o= 132 | golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw= 133 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 134 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 135 | golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM= 136 | golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= 137 | golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= 138 | golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= 139 | golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= 140 | golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= 141 | golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= 142 | golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= 143 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 144 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 145 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 146 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 147 | golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= 148 | golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= 149 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 150 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 151 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 152 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 153 | google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA= 154 | google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= 155 | google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY= 156 | google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= 157 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 158 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 159 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 160 | gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= 161 | gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= 162 | gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= 163 | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 164 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 165 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 166 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 167 | k8s.io/api v0.32.0 h1:OL9JpbvAU5ny9ga2fb24X8H6xQlVp+aJMFlgtQjR9CE= 168 | k8s.io/api v0.32.0/go.mod h1:4LEwHZEf6Q/cG96F3dqR965sYOfmPM7rq81BLgsE0p0= 169 | k8s.io/api v0.33.0 h1:yTgZVn1XEe6opVpP1FylmNrIFWuDqe2H0V8CT5gxfIU= 170 | k8s.io/api v0.33.0/go.mod h1:CTO61ECK/KU7haa3qq8sarQ0biLq2ju405IZAd9zsiM= 171 | k8s.io/apimachinery v0.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg= 172 | k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= 173 | k8s.io/apimachinery v0.33.0 h1:1a6kHrJxb2hs4t8EE5wuR/WxKDwGN1FKH3JvDtA0CIQ= 174 | k8s.io/apimachinery v0.33.0/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= 175 | k8s.io/client-go v0.32.0 h1:DimtMcnN/JIKZcrSrstiwvvZvLjG0aSxy8PxN8IChp8= 176 | k8s.io/client-go v0.32.0/go.mod h1:boDWvdM1Drk4NJj/VddSLnx59X3OPgwrOo0vGbtq9+8= 177 | k8s.io/client-go v0.33.0 h1:UASR0sAYVUzs2kYuKn/ZakZlcs2bEHaizrrHUZg0G98= 178 | k8s.io/client-go v0.33.0/go.mod h1:kGkd+l/gNGg8GYWAPr0xF1rRKvVWvzh9vmZAMXtaKOg= 179 | k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= 180 | k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= 181 | k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= 182 | k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4= 183 | k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= 184 | k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= 185 | k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= 186 | k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= 187 | k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979 h1:jgJW5IePPXLGB8e/1wvd0Ich9QE97RvvF3a8J3fP/Lg= 188 | k8s.io/utils v0.0.0-20250502105355-0f33e8f1c979/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= 189 | sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= 190 | sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= 191 | sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE= 192 | sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= 193 | sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= 194 | sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= 195 | sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= 196 | sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA= 197 | sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= 198 | sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI= 199 | sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= 200 | sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= 201 | sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= 202 | -------------------------------------------------------------------------------- /_example/manifests/comic.kube-code-generator.slok.dev_heroes.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | controller-gen.kubebuilder.io/version: (devel) 7 | name: heroes.comic.kube-code-generator.slok.dev 8 | spec: 9 | group: comic.kube-code-generator.slok.dev 10 | names: 11 | categories: 12 | - heroes 13 | - superheroes 14 | kind: Hero 15 | listKind: HeroList 16 | plural: heroes 17 | shortNames: 18 | - he 19 | - sh 20 | singular: hero 21 | scope: Namespaced 22 | versions: 23 | - additionalPrinterColumns: 24 | - jsonPath: .spec.name 25 | name: NAME 26 | type: string 27 | - jsonPath: .spec.city 28 | name: CITY 29 | type: string 30 | - jsonPath: .spec.kind 31 | name: KIND 32 | type: string 33 | - jsonPath: .metadata.creationTimestamp 34 | name: AGE 35 | type: date 36 | name: v1 37 | schema: 38 | openAPIV3Schema: 39 | description: Hero represents a comic hero. 40 | properties: 41 | apiVersion: 42 | description: |- 43 | APIVersion defines the versioned schema of this representation of an object. 44 | Servers should convert recognized schemas to the latest internal value, and 45 | may reject unrecognized values. 46 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources 47 | type: string 48 | kind: 49 | description: |- 50 | Kind is a string value representing the REST resource this object represents. 51 | Servers may infer this from the endpoint the client submits requests to. 52 | Cannot be updated. 53 | In CamelCase. 54 | More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds 55 | type: string 56 | metadata: 57 | type: object 58 | spec: 59 | description: HeroSpec is the spec of a Hero. 60 | properties: 61 | birthDate: 62 | format: date-time 63 | type: string 64 | city: 65 | type: string 66 | kind: 67 | default: unknown 68 | description: HeroType is the type of a hero. 69 | enum: 70 | - unknown 71 | - superhero 72 | - antihero 73 | - villain 74 | type: string 75 | name: 76 | maxLength: 128 77 | type: string 78 | x-kubernetes-validations: 79 | - message: name cannot be empty 80 | rule: self.size() != 0 81 | superPowers: 82 | items: 83 | type: string 84 | type: array 85 | x-kubernetes-list-type: map 86 | required: 87 | - kind 88 | - name 89 | type: object 90 | status: 91 | properties: 92 | currentCity: 93 | type: string 94 | moving: 95 | type: boolean 96 | type: object 97 | type: object 98 | selectableFields: 99 | - jsonPath: .spec.city 100 | - jsonPath: .spec.kind 101 | served: true 102 | storage: true 103 | subresources: 104 | status: {} 105 | -------------------------------------------------------------------------------- /cmd/kube-code-generator/config.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/alecthomas/kingpin/v2" 5 | ) 6 | 7 | // CmdConfig represents the configuration of the command. 8 | type CmdConfig struct { 9 | Debug bool 10 | 11 | CodeGenPath string 12 | ControllerGenBin string 13 | APIsPath string 14 | GoCodeOutPath string 15 | CRDsOutPath string 16 | BoilerplatePath string 17 | EnableApplyConfigs bool 18 | } 19 | 20 | // NewCmdConfig returns a new command configuration. 21 | func NewCmdConfig(args []string) (*CmdConfig, error) { 22 | c := &CmdConfig{} 23 | app := kingpin.New("kube-code-generator", "The easiest way to create Kubernetes CRD related Go code and manifests.") 24 | app.DefaultEnvars() 25 | 26 | app.Flag("debug", "Enable debug mode.").BoolVar(&c.Debug) 27 | 28 | app.Flag("codegen-path", "The path where github.com/kubernetes/code-generator app code lives.").Required().StringVar(&c.CodeGenPath) 29 | app.Flag("controller-gen-bin", "The path or binary name of controller-gen ().").Default("controller-gen").StringVar(&c.ControllerGenBin) 30 | 31 | app.Flag("apis-in", "The path to the APIs root, it must be a relative path from the root where this app is execute.").Required().StringVar(&c.APIsPath) 32 | app.Flag("go-gen-out", "The path to the Go auto generated code, it must be a relative path from the root where this app is execute.").StringVar(&c.GoCodeOutPath) 33 | app.Flag("crd-gen-out", "The path to the Go auto generated code, it must be a relative path from the root where this app is execute.").StringVar(&c.CRDsOutPath) 34 | app.Flag("boilerplate-path", "Path to boilerplate text file, this file will be used to add this information to all the autogenerated code.").StringVar(&c.BoilerplatePath) 35 | 36 | app.Flag("apply-configurations", "If enabled, apply configurations clients and types will be created for the CR types.").BoolVar(&c.EnableApplyConfigs) 37 | 38 | _, err := app.Parse(args[1:]) 39 | if err != nil { 40 | return nil, err 41 | } 42 | 43 | return c, nil 44 | } 45 | -------------------------------------------------------------------------------- /cmd/kube-code-generator/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "io" 7 | "io/fs" 8 | "os" 9 | "path/filepath" 10 | 11 | "github.com/sirupsen/logrus" 12 | 13 | generate "github.com/slok/kube-code-generator/internal/generate" 14 | "github.com/slok/kube-code-generator/internal/info" 15 | "github.com/slok/kube-code-generator/internal/log" 16 | loglogrus "github.com/slok/kube-code-generator/internal/log/logrus" 17 | utilgomod "github.com/slok/kube-code-generator/internal/util/gomod" 18 | ) 19 | 20 | func run(ctx context.Context, args []string, stdout, stderr io.Writer) error { 21 | ctx, cancel := context.WithCancel(ctx) 22 | defer cancel() 23 | 24 | // Load command flags and arguments. 25 | cmdCfg, err := NewCmdConfig(args) 26 | if err != nil { 27 | return fmt.Errorf("could not load command configuration: %w", err) 28 | } 29 | 30 | // If not logger disabled use logrus logger. 31 | logrusLog := logrus.New() 32 | logrusLog.Out = stderr // By default logger goes to stderr (so it can split stdout prints). 33 | logrusLogEntry := logrus.NewEntry(logrusLog) 34 | if cmdCfg.Debug { 35 | logrusLogEntry.Logger.SetLevel(logrus.DebugLevel) 36 | } 37 | logger := loglogrus.NewLogrus(logrusLogEntry).WithValues(log.Kv{ 38 | "version": info.Version, 39 | }) 40 | 41 | logger.Debugf("Debug level is enabled") // Will log only when debug enabled. 42 | 43 | if cmdCfg.GoCodeOutPath == "" && cmdCfg.CRDsOutPath == "" { 44 | return fmt.Errorf("at least a generated output path is required") 45 | } 46 | 47 | // Prepare. 48 | projectRootFS := os.DirFS(".") 49 | goMod, err := fs.ReadFile(projectRootFS, "go.mod") 50 | if err != nil { 51 | return fmt.Errorf(`error while reading "go.mod", you should execute this app from the project root: %w`, err) 52 | } 53 | 54 | goModule, err := utilgomod.GetGoModule(string(goMod)) 55 | if err != nil { 56 | return fmt.Errorf("could not get go module: %w", err) 57 | } 58 | goCodeGenOutPkg := utilgomod.GetGoPackageFromDir(goModule, cmdCfg.GoCodeOutPath) 59 | logger.WithValues(log.Kv{"module": goCodeGenOutPkg}).Infof("Go generated code package inferred") 60 | 61 | if filepath.IsAbs(cmdCfg.APIsPath) { 62 | return fmt.Errorf("APIs path should be relative") 63 | } 64 | 65 | // Start autogeneration. 66 | err = generateGoCode(ctx, *cmdCfg, logger, goCodeGenOutPkg) 67 | if err != nil { 68 | return fmt.Errorf("could not generate Go code: %w", err) 69 | } 70 | 71 | err = generateCRDManifests(ctx, *cmdCfg, logger) 72 | if err != nil { 73 | return fmt.Errorf("could not generate CRDs: %w", err) 74 | } 75 | 76 | return nil 77 | } 78 | 79 | func generateGoCode(ctx context.Context, cmdCfg CmdConfig, logger log.Logger, genOutPkg string) error { 80 | if cmdCfg.GoCodeOutPath == "" { 81 | logger.Infof("Ignoring Go code generation") 82 | return nil 83 | } 84 | 85 | if filepath.IsAbs(cmdCfg.GoCodeOutPath) { 86 | return fmt.Errorf("go generated code path should be relative") 87 | } 88 | 89 | err := os.MkdirAll(cmdCfg.GoCodeOutPath, os.ModePerm) 90 | if err != nil { 91 | return fmt.Errorf("could not create directory for go generated code: %w", err) 92 | } 93 | 94 | // We will require a boilerplate always. 95 | boilerplatePath := cmdCfg.BoilerplatePath 96 | if cmdCfg.BoilerplatePath == "" { 97 | f, err := os.CreateTemp("", "kube-code-generator-boilerplate-") 98 | if err != nil { 99 | return fmt.Errorf("could not create boilerplate empty file") 100 | } 101 | f.Close() 102 | defer os.Remove(f.Name()) 103 | boilerplatePath = f.Name() 104 | } 105 | 106 | logger.Infof("Generating Go code...") 107 | gen := generate.NewClientGenerator(logger, cmdCfg.CodeGenPath, generate.StdBashExecutor). 108 | WithWatch(). 109 | WithBoilerplate(boilerplatePath). 110 | WithOutputPkg(genOutPkg). 111 | WithOutputDir(cmdCfg.GoCodeOutPath). 112 | WithAPIsPath(cmdCfg.APIsPath) 113 | 114 | if cmdCfg.EnableApplyConfigs { 115 | gen = gen.WithApplyConfig() 116 | } 117 | 118 | err = gen.Run(ctx) 119 | if err != nil { 120 | return fmt.Errorf("could not generate Go clients code: %w", err) 121 | } 122 | err = generate.NewHelpersGenerator(logger, cmdCfg.CodeGenPath, generate.StdBashExecutor). 123 | WithBoilerplate(boilerplatePath). 124 | WithAPIsPath(cmdCfg.APIsPath).Run(ctx) 125 | if err != nil { 126 | return fmt.Errorf("could not generate types Go helper code: %w", err) 127 | } 128 | 129 | return nil 130 | } 131 | 132 | func generateCRDManifests(ctx context.Context, cmdCfg CmdConfig, logger log.Logger) error { 133 | if cmdCfg.CRDsOutPath == "" { 134 | logger.Infof("Ignoring CRD manifest generation") 135 | return nil 136 | } 137 | 138 | if filepath.IsAbs(cmdCfg.CRDsOutPath) { 139 | return fmt.Errorf("crd manifests generated path should be relative") 140 | } 141 | 142 | err := os.MkdirAll(cmdCfg.CRDsOutPath, os.ModePerm) 143 | if err != nil { 144 | return fmt.Errorf("could not create directory for CRD manifests generated content: %w", err) 145 | } 146 | 147 | logger.Infof("Generating CRDs...") 148 | err = generate.NewCRDGenerator(logger, cmdCfg.ControllerGenBin, generate.StdBashExecutor). 149 | WithAllowDangerousTypes(). 150 | WithOutputDir(cmdCfg.CRDsOutPath). 151 | WithAPIsPath(cmdCfg.APIsPath).Run(ctx) 152 | if err != nil { 153 | return fmt.Errorf("could not generate Go types code: %w", err) 154 | } 155 | 156 | return nil 157 | } 158 | 159 | func main() { 160 | err := run(context.Background(), os.Args, os.Stdout, os.Stderr) 161 | if err != nil { 162 | fmt.Fprintf(os.Stderr, "Error: %s\n", err) 163 | os.Exit(1) 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /docker/dev/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.24 2 | 3 | ARG GOLANGCI_LINT_VERSION="2.1.5" 4 | ARG MOCKERY_VERSION="3.2.5" 5 | ARG ostype=Linux 6 | 7 | RUN apt-get update && apt-get install -y \ 8 | git \ 9 | bash \ 10 | zip 11 | 12 | RUN wget https://github.com/golangci/golangci-lint/releases/download/v${GOLANGCI_LINT_VERSION}/golangci-lint-${GOLANGCI_LINT_VERSION}-linux-amd64.tar.gz && \ 13 | tar zxvf golangci-lint-${GOLANGCI_LINT_VERSION}-linux-amd64.tar.gz --strip 1 -C /usr/local/bin/ && \ 14 | rm golangci-lint-${GOLANGCI_LINT_VERSION}-linux-amd64.tar.gz && \ 15 | \ 16 | wget https://github.com/vektra/mockery/releases/download/v${MOCKERY_VERSION}/mockery_${MOCKERY_VERSION}_Linux_x86_64.tar.gz && \ 17 | tar zxvf mockery_${MOCKERY_VERSION}_Linux_x86_64.tar.gz -C /tmp && \ 18 | mv /tmp/mockery /usr/local/bin/ && \ 19 | rm mockery_${MOCKERY_VERSION}_Linux_x86_64.tar.gz 20 | 21 | # Create user. 22 | ARG uid=1000 23 | ARG gid=1000 24 | 25 | RUN bash -c 'if [ ${ostype} == Linux ]; then addgroup -gid $gid app; else addgroup app; fi && \ 26 | adduser --disabled-password -uid $uid --ingroup app --gecos "" app && \ 27 | chown app:app -R /go' 28 | 29 | # Prepare for Go cache: 30 | RUN mkdir -p /tmp/cache 31 | RUN chown app:app -R /tmp/cache 32 | USER app 33 | 34 | 35 | # Fill cache. 36 | COPY go.mod /tmp/cache 37 | COPY go.sum /tmp/cache 38 | RUN cd /tmp/cache && \ 39 | go mod download 40 | 41 | WORKDIR /src 42 | -------------------------------------------------------------------------------- /docker/prod/Dockerfile: -------------------------------------------------------------------------------- 1 | # Set also `ARCH` ARG here so we can use it on all the `FROM`s. 2 | ARG ARCH 3 | 4 | FROM golang:1.24 AS build-stage 5 | 6 | # Required by the built script for setting verion and cross-compiling. 7 | ARG VERSION 8 | ENV VERSION=${VERSION} 9 | ARG ARCH 10 | ENV GOARCH=${ARCH} 11 | 12 | # Compile. 13 | WORKDIR /src 14 | COPY . . 15 | RUN ./scripts/build/bin/build-raw.sh 16 | 17 | FROM golang:1.24 18 | ARG CODEGEN_VERSION="1.33.0" 19 | ARG CONTROLLER_GEN_VERSION="0.17.3" 20 | 21 | 22 | COPY --from=build-stage /src/bin/kube-code-generator /usr/local/bin/kube-code-generator 23 | 24 | RUN apt-get update && \ 25 | apt-get install -y \ 26 | git 27 | 28 | # Code generator stuff 29 | RUN wget https://github.com/kubernetes/code-generator/archive/kubernetes-${CODEGEN_VERSION}.tar.gz && \ 30 | mkdir -p /tmp/k8s-code-generator/ && \ 31 | tar zxvf kubernetes-${CODEGEN_VERSION}.tar.gz --strip 1 -C /tmp/k8s-code-generator/ && \ 32 | cd /tmp/k8s-code-generator/ && go mod tidy && cd - && \ 33 | rm kubernetes-${CODEGEN_VERSION}.tar.gz && \ 34 | \ 35 | wget https://github.com/kubernetes-sigs/controller-tools/archive/v${CONTROLLER_GEN_VERSION}.tar.gz && \ 36 | tar xvf ./v${CONTROLLER_GEN_VERSION}.tar.gz && \ 37 | cd ./controller-tools-${CONTROLLER_GEN_VERSION}/ && \ 38 | go mod tidy && \ 39 | go build -o controller-gen ./cmd/controller-gen/ && \ 40 | mv ./controller-gen /usr/bin/ && \ 41 | rm -rf ../v${CONTROLLER_GEN_VERSION}.tar.gz && \ 42 | rm -rf ../controller-tools-${CONTROLLER_GEN_VERSION} 43 | 44 | 45 | # Create user and workdir. 46 | ARG uid=1000 47 | ARG gid=1000 48 | RUN addgroup --gid $gid app && \ 49 | adduser --gecos "First Last,RoomNumber,WorkPhone,HomePhone" --disabled-password --uid $uid --ingroup app app && \ 50 | chown app:app -R /go 51 | RUN mkdir /app && chown app:app -R /app 52 | 53 | USER app 54 | WORKDIR /app 55 | 56 | ENV KUBE_CODE_GENERATOR_CODEGEN_PATH=/tmp/k8s-code-generator 57 | ENTRYPOINT ["kube-code-generator"] -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/slok/kube-code-generator 2 | 3 | go 1.24.0 4 | 5 | require ( 6 | github.com/alecthomas/kingpin/v2 v2.4.0 7 | github.com/sirupsen/logrus v1.9.3 8 | github.com/stretchr/testify v1.10.0 9 | ) 10 | 11 | require ( 12 | github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b // indirect 13 | github.com/davecgh/go-spew v1.1.1 // indirect 14 | github.com/pmezard/go-difflib v1.0.0 // indirect 15 | github.com/stretchr/objx v0.5.2 // indirect 16 | github.com/xhit/go-str2duration/v2 v2.1.0 // indirect 17 | golang.org/x/sys v0.32.0 // indirect 18 | gopkg.in/yaml.v3 v3.0.1 // indirect 19 | ) 20 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY= 2 | github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= 3 | github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b h1:mimo19zliBX/vSQ6PWWSL9lK8qwHozUj03+zLoEB8O0= 4 | github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs= 5 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 6 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 7 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 8 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 9 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 10 | github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= 11 | github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 12 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 13 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 14 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 15 | github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= 16 | github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= 17 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 18 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 19 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 20 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 21 | github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 22 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 23 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 24 | github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= 25 | github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= 26 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 27 | golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= 28 | golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 29 | golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= 30 | golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 31 | golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= 32 | golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= 33 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= 34 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 35 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 36 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 37 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 38 | -------------------------------------------------------------------------------- /internal/generate/client.go: -------------------------------------------------------------------------------- 1 | package generate 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "path/filepath" 7 | "strings" 8 | 9 | "github.com/slok/kube-code-generator/internal/log" 10 | ) 11 | 12 | type ClientGenerator struct { 13 | cmdArgs []string 14 | codeGenPath string 15 | apisPath string 16 | exec BashExecutor 17 | logger log.Logger 18 | } 19 | 20 | func NewClientGenerator(logger log.Logger, codeGenPath string, exec BashExecutor) *ClientGenerator { 21 | if exec == nil { 22 | exec = StdBashExecutor 23 | } 24 | 25 | return &ClientGenerator{ 26 | codeGenPath: codeGenPath, 27 | exec: exec, 28 | logger: logger, 29 | } 30 | } 31 | 32 | func (g *ClientGenerator) WithWatch() *ClientGenerator { 33 | g.cmdArgs = append(g.cmdArgs, `--with-watch`) 34 | return g 35 | } 36 | 37 | func (g *ClientGenerator) WithOutputPkg(pkg string) *ClientGenerator { 38 | g.cmdArgs = append(g.cmdArgs, `--output-pkg`, pkg) 39 | return g 40 | } 41 | 42 | func (g *ClientGenerator) WithOutputDir(path string) *ClientGenerator { 43 | g.cmdArgs = append(g.cmdArgs, `--output-dir`, path) 44 | return g 45 | } 46 | 47 | func (g *ClientGenerator) WithBoilerplate(path string) *ClientGenerator { 48 | g.cmdArgs = append(g.cmdArgs, `--boilerplate`, path) 49 | return g 50 | } 51 | 52 | func (g *ClientGenerator) WithAPIsPath(path string) *ClientGenerator { 53 | g.apisPath = path 54 | return g 55 | } 56 | 57 | func (g *ClientGenerator) WithApplyConfig() *ClientGenerator { 58 | g.cmdArgs = append(g.cmdArgs, `--with-applyconfig`) 59 | return g 60 | } 61 | 62 | func (g *ClientGenerator) Run(ctx context.Context) error { 63 | kubeCodeGenSHPath := filepath.Join(g.codeGenPath, "kube_codegen.sh") 64 | bashCmd := fmt.Sprintf("source %s ; kube::codegen::gen_client %s %s", kubeCodeGenSHPath, strings.Join(g.cmdArgs, " "), g.apisPath) 65 | 66 | g.logger.Debugf("Command executed: %s", bashCmd) 67 | out, err := g.exec.BashExec(ctx, bashCmd) 68 | if err != nil { 69 | return fmt.Errorf("error while executing bash script: %w", err) 70 | } 71 | g.logger.Debugf("Command output: %s", string(out)) 72 | 73 | return nil 74 | } 75 | -------------------------------------------------------------------------------- /internal/generate/client_test.go: -------------------------------------------------------------------------------- 1 | package generate_test 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/mock" 8 | 9 | "github.com/slok/kube-code-generator/internal/generate" 10 | "github.com/slok/kube-code-generator/internal/generate/generatemock" 11 | "github.com/slok/kube-code-generator/internal/log" 12 | ) 13 | 14 | func TestClientGenerator(t *testing.T) { 15 | tests := map[string]struct { 16 | exec func(g *generate.ClientGenerator) 17 | expCmd string 18 | }{ 19 | "Without options.": { 20 | exec: func(g *generate.ClientGenerator) { _ = g.Run(context.TODO()) }, 21 | expCmd: `source kube_codegen.sh ; kube::codegen::gen_client `, 22 | }, 23 | 24 | "Regular options.": { 25 | exec: func(g *generate.ClientGenerator) { 26 | _ = g.WithAPIsPath("./apis"). 27 | WithBoilerplate("./boilerplate.txt"). 28 | WithOutputDir("./out"). 29 | WithOutputPkg("my-pkg"). 30 | WithApplyConfig(). 31 | WithWatch(). 32 | Run(context.TODO()) 33 | }, 34 | expCmd: `source kube_codegen.sh ; kube::codegen::gen_client --boilerplate ./boilerplate.txt --output-dir ./out --output-pkg my-pkg --with-applyconfig --with-watch ./apis`, 35 | }, 36 | } 37 | 38 | for name, test := range tests { 39 | t.Run(name, func(t *testing.T) { 40 | m := generatemock.NewBashExecutor(t) 41 | m.On("BashExec", mock.Anything, test.expCmd).Once().Return("", nil) 42 | 43 | g := generate.NewClientGenerator(log.Noop, "", m) 44 | test.exec(g) 45 | 46 | m.AssertExpectations(t) 47 | }) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /internal/generate/crd.go: -------------------------------------------------------------------------------- 1 | package generate 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "path/filepath" 7 | "strings" 8 | 9 | "github.com/slok/kube-code-generator/internal/log" 10 | ) 11 | 12 | type CRDGenerator struct { 13 | controllerGenBin string 14 | apisPath string 15 | outPath string 16 | crdOptions []string 17 | exec BashExecutor 18 | logger log.Logger 19 | } 20 | 21 | const strSeparator = string(filepath.Separator) 22 | 23 | func NewCRDGenerator(logger log.Logger, controllerGenBin string, exec BashExecutor) *CRDGenerator { 24 | if exec == nil { 25 | exec = StdBashExecutor 26 | } 27 | 28 | if controllerGenBin == "" { 29 | controllerGenBin = "controller-gen" 30 | } 31 | 32 | return &CRDGenerator{ 33 | controllerGenBin: controllerGenBin, 34 | crdOptions: []string{ 35 | "crdVersions=v1", // Only one supported for now. 36 | }, 37 | exec: exec, 38 | logger: logger, 39 | } 40 | } 41 | 42 | func (g *CRDGenerator) WithAllowDangerousTypes() *CRDGenerator { 43 | g.crdOptions = append(g.crdOptions, "allowDangerousTypes=true") 44 | return g 45 | } 46 | 47 | func (g *CRDGenerator) WithIgnoreUnexportedFields() *CRDGenerator { 48 | g.crdOptions = append(g.crdOptions, "ignoreUnexportedFields=true") 49 | return g 50 | } 51 | 52 | func (g *CRDGenerator) WithIgnoreDescription() *CRDGenerator { 53 | g.crdOptions = append(g.crdOptions, "maxDescLen=0") 54 | return g 55 | } 56 | 57 | func (g *CRDGenerator) WithOutputDir(path string) *CRDGenerator { 58 | path = filepath.Clean(path) 59 | g.outPath = fmt.Sprintf(".%s%s", strSeparator, path) // We need `./` in front of it. 60 | return g 61 | } 62 | 63 | func (g *CRDGenerator) WithAPIsPath(path string) *CRDGenerator { 64 | path = filepath.Clean(path) 65 | g.apisPath = fmt.Sprintf(".%s%s%s...", strSeparator, path, strSeparator) // We need `./` in front of it. 66 | return g 67 | } 68 | 69 | func (g *CRDGenerator) Run(ctx context.Context) error { 70 | paths := fmt.Sprintf(`paths="%s"`, g.apisPath) 71 | outputDir := fmt.Sprintf(`output:dir="%s"`, g.outPath) 72 | crds := fmt.Sprintf("crd:%s", strings.Join(g.crdOptions, ",")) 73 | bashCmd := fmt.Sprintf("%s %s %s %s", g.controllerGenBin, paths, outputDir, crds) 74 | 75 | g.logger.Debugf("Command executed: %s", bashCmd) 76 | out, err := g.exec.BashExec(ctx, bashCmd) 77 | if err != nil { 78 | return fmt.Errorf("error while executing bash script: %w", err) 79 | } 80 | g.logger.Debugf("Command output: %s", string(out)) 81 | return nil 82 | } 83 | -------------------------------------------------------------------------------- /internal/generate/crd_test.go: -------------------------------------------------------------------------------- 1 | package generate_test 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/mock" 8 | 9 | "github.com/slok/kube-code-generator/internal/generate" 10 | "github.com/slok/kube-code-generator/internal/generate/generatemock" 11 | "github.com/slok/kube-code-generator/internal/log" 12 | ) 13 | 14 | func TestCRDGenerator(t *testing.T) { 15 | tests := map[string]struct { 16 | exec func(g *generate.CRDGenerator) 17 | expCmd string 18 | }{ 19 | "Without options.": { 20 | exec: func(g *generate.CRDGenerator) { _ = g.Run(context.TODO()) }, 21 | expCmd: `controller-gen paths="" output:dir="" crd:crdVersions=v1`, 22 | }, 23 | 24 | "Regular options.": { 25 | exec: func(g *generate.CRDGenerator) { 26 | _ = g.WithAPIsPath("./apis"). 27 | WithOutputDir("./out"). 28 | WithAllowDangerousTypes(). 29 | WithIgnoreDescription(). 30 | WithIgnoreUnexportedFields(). 31 | Run(context.TODO()) 32 | }, 33 | expCmd: `controller-gen paths="./apis/..." output:dir="./out" crd:crdVersions=v1,allowDangerousTypes=true,maxDescLen=0,ignoreUnexportedFields=true`, 34 | }, 35 | } 36 | 37 | for name, test := range tests { 38 | t.Run(name, func(t *testing.T) { 39 | m := generatemock.NewBashExecutor(t) 40 | m.On("BashExec", mock.Anything, test.expCmd).Once().Return("", nil) 41 | 42 | g := generate.NewCRDGenerator(log.Noop, "", m) 43 | test.exec(g) 44 | 45 | m.AssertExpectations(t) 46 | }) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /internal/generate/generate.go: -------------------------------------------------------------------------------- 1 | package generate 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "os/exec" 7 | ) 8 | 9 | type BashExecutor interface { 10 | BashExec(ctx context.Context, bashCmd string) (string, error) 11 | } 12 | 13 | // StdBashExecutor is an standard bash executor. 14 | var StdBashExecutor = stdBashExecutor(false) 15 | 16 | type stdBashExecutor bool 17 | 18 | func (stdBashExecutor) BashExec(ctx context.Context, bashCmd string) (string, error) { 19 | cmd := exec.CommandContext(ctx, "bash", "-c", bashCmd) 20 | out, err := cmd.CombinedOutput() 21 | if err != nil { 22 | return "", fmt.Errorf("%w: %s", err, string(out)) 23 | } 24 | 25 | return string(out), nil 26 | } 27 | -------------------------------------------------------------------------------- /internal/generate/generatemock/mocks.go: -------------------------------------------------------------------------------- 1 | // Code generated by mockery; DO NOT EDIT. 2 | // github.com/vektra/mockery 3 | // template: testify 4 | 5 | package generatemock 6 | 7 | import ( 8 | "context" 9 | 10 | mock "github.com/stretchr/testify/mock" 11 | ) 12 | 13 | // NewBashExecutor creates a new instance of BashExecutor. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. 14 | // The first argument is typically a *testing.T value. 15 | func NewBashExecutor(t interface { 16 | mock.TestingT 17 | Cleanup(func()) 18 | }) *BashExecutor { 19 | mock := &BashExecutor{} 20 | mock.Mock.Test(t) 21 | 22 | t.Cleanup(func() { mock.AssertExpectations(t) }) 23 | 24 | return mock 25 | } 26 | 27 | // BashExecutor is an autogenerated mock type for the BashExecutor type 28 | type BashExecutor struct { 29 | mock.Mock 30 | } 31 | 32 | type BashExecutor_Expecter struct { 33 | mock *mock.Mock 34 | } 35 | 36 | func (_m *BashExecutor) EXPECT() *BashExecutor_Expecter { 37 | return &BashExecutor_Expecter{mock: &_m.Mock} 38 | } 39 | 40 | // BashExec provides a mock function for the type BashExecutor 41 | func (_mock *BashExecutor) BashExec(ctx context.Context, bashCmd string) (string, error) { 42 | ret := _mock.Called(ctx, bashCmd) 43 | 44 | if len(ret) == 0 { 45 | panic("no return value specified for BashExec") 46 | } 47 | 48 | var r0 string 49 | var r1 error 50 | if returnFunc, ok := ret.Get(0).(func(context.Context, string) (string, error)); ok { 51 | return returnFunc(ctx, bashCmd) 52 | } 53 | if returnFunc, ok := ret.Get(0).(func(context.Context, string) string); ok { 54 | r0 = returnFunc(ctx, bashCmd) 55 | } else { 56 | r0 = ret.Get(0).(string) 57 | } 58 | if returnFunc, ok := ret.Get(1).(func(context.Context, string) error); ok { 59 | r1 = returnFunc(ctx, bashCmd) 60 | } else { 61 | r1 = ret.Error(1) 62 | } 63 | return r0, r1 64 | } 65 | 66 | // BashExecutor_BashExec_Call is a *mock.Call that shadows Run/Return methods with type explicit version for method 'BashExec' 67 | type BashExecutor_BashExec_Call struct { 68 | *mock.Call 69 | } 70 | 71 | // BashExec is a helper method to define mock.On call 72 | // - ctx 73 | // - bashCmd 74 | func (_e *BashExecutor_Expecter) BashExec(ctx interface{}, bashCmd interface{}) *BashExecutor_BashExec_Call { 75 | return &BashExecutor_BashExec_Call{Call: _e.mock.On("BashExec", ctx, bashCmd)} 76 | } 77 | 78 | func (_c *BashExecutor_BashExec_Call) Run(run func(ctx context.Context, bashCmd string)) *BashExecutor_BashExec_Call { 79 | _c.Call.Run(func(args mock.Arguments) { 80 | run(args[0].(context.Context), args[1].(string)) 81 | }) 82 | return _c 83 | } 84 | 85 | func (_c *BashExecutor_BashExec_Call) Return(s string, err error) *BashExecutor_BashExec_Call { 86 | _c.Call.Return(s, err) 87 | return _c 88 | } 89 | 90 | func (_c *BashExecutor_BashExec_Call) RunAndReturn(run func(ctx context.Context, bashCmd string) (string, error)) *BashExecutor_BashExec_Call { 91 | _c.Call.Return(run) 92 | return _c 93 | } 94 | -------------------------------------------------------------------------------- /internal/generate/helpers.go: -------------------------------------------------------------------------------- 1 | package generate 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "path/filepath" 7 | "strings" 8 | 9 | "github.com/slok/kube-code-generator/internal/log" 10 | ) 11 | 12 | type HelpersGenerator struct { 13 | cmdArgs []string 14 | codeGenPath string 15 | apisPath string 16 | exec BashExecutor 17 | logger log.Logger 18 | } 19 | 20 | func NewHelpersGenerator(logger log.Logger, codeGenPath string, exec BashExecutor) *HelpersGenerator { 21 | if exec == nil { 22 | exec = StdBashExecutor 23 | } 24 | 25 | return &HelpersGenerator{ 26 | codeGenPath: codeGenPath, 27 | exec: exec, 28 | logger: logger, 29 | } 30 | } 31 | 32 | func (g *HelpersGenerator) WithBoilerplate(path string) *HelpersGenerator { 33 | g.cmdArgs = append(g.cmdArgs, `--boilerplate`, path) 34 | return g 35 | } 36 | 37 | func (g *HelpersGenerator) WithAPIsPath(path string) *HelpersGenerator { 38 | g.apisPath = path 39 | return g 40 | } 41 | 42 | func (g *HelpersGenerator) Run(ctx context.Context) error { 43 | kubeCodeGenSHPath := filepath.Join(g.codeGenPath, "kube_codegen.sh") 44 | bashCmd := fmt.Sprintf("source %s ; kube::codegen::gen_helpers %s %s", kubeCodeGenSHPath, strings.Join(g.cmdArgs, " "), g.apisPath) 45 | 46 | g.logger.Debugf("Command executed: %s", bashCmd) 47 | out, err := g.exec.BashExec(ctx, bashCmd) 48 | if err != nil { 49 | return fmt.Errorf("error while executing bash script: %w", err) 50 | } 51 | g.logger.Debugf("Command output: %s", string(out)) 52 | 53 | return nil 54 | } 55 | -------------------------------------------------------------------------------- /internal/generate/helpers_test.go: -------------------------------------------------------------------------------- 1 | package generate_test 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/stretchr/testify/mock" 8 | 9 | "github.com/slok/kube-code-generator/internal/generate" 10 | "github.com/slok/kube-code-generator/internal/generate/generatemock" 11 | "github.com/slok/kube-code-generator/internal/log" 12 | ) 13 | 14 | func TestHelpersGenerator(t *testing.T) { 15 | tests := map[string]struct { 16 | exec func(g *generate.HelpersGenerator) 17 | expCmd string 18 | }{ 19 | "Without options.": { 20 | exec: func(g *generate.HelpersGenerator) { _ = g.Run(context.TODO()) }, 21 | expCmd: `source kube_codegen.sh ; kube::codegen::gen_helpers `, 22 | }, 23 | 24 | "Regular options.": { 25 | exec: func(g *generate.HelpersGenerator) { 26 | _ = g.WithAPIsPath("./apis"). 27 | WithBoilerplate("./boilerplate.txt"). 28 | Run(context.TODO()) 29 | }, 30 | expCmd: `source kube_codegen.sh ; kube::codegen::gen_helpers --boilerplate ./boilerplate.txt ./apis`, 31 | }, 32 | } 33 | 34 | for name, test := range tests { 35 | t.Run(name, func(t *testing.T) { 36 | m := generatemock.NewBashExecutor(t) 37 | m.On("BashExec", mock.Anything, test.expCmd).Once().Return("", nil) 38 | 39 | g := generate.NewHelpersGenerator(log.Noop, "", m) 40 | test.exec(g) 41 | 42 | m.AssertExpectations(t) 43 | }) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /internal/info/info.go: -------------------------------------------------------------------------------- 1 | package info 2 | 3 | // Version is the application version (normally set when building the binary). 4 | var Version = "dev" 5 | -------------------------------------------------------------------------------- /internal/log/log.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import "context" 4 | 5 | // Kv is a helper type for structured logging fields usage. 6 | type Kv = map[string]interface{} 7 | 8 | // Logger is the interface that the loggers use. 9 | type Logger interface { 10 | Infof(format string, args ...interface{}) 11 | Warningf(format string, args ...interface{}) 12 | Errorf(format string, args ...interface{}) 13 | Debugf(format string, args ...interface{}) 14 | WithValues(values map[string]interface{}) Logger 15 | WithCtxValues(ctx context.Context) Logger 16 | SetValuesOnCtx(parent context.Context, values map[string]interface{}) context.Context 17 | } 18 | 19 | // Noop logger doesn't log anything. 20 | const Noop = noop(false) 21 | 22 | type noop bool 23 | 24 | func (n noop) Infof(format string, args ...interface{}) {} 25 | func (n noop) Warningf(format string, args ...interface{}) {} 26 | func (n noop) Errorf(format string, args ...interface{}) {} 27 | func (n noop) Debugf(format string, args ...interface{}) {} 28 | func (n noop) WithValues(map[string]interface{}) Logger { return n } 29 | func (n noop) WithCtxValues(context.Context) Logger { return n } 30 | func (n noop) SetValuesOnCtx(parent context.Context, values Kv) context.Context { return parent } 31 | 32 | type contextKey string 33 | 34 | // contextLogValuesKey used as unique key to store log values in the context. 35 | const contextLogValuesKey = contextKey("internal-log") 36 | 37 | // CtxWithValues returns a copy of parent in which the key values passed have been 38 | // stored ready to be used using log.Logger. 39 | func CtxWithValues(parent context.Context, kv Kv) context.Context { 40 | // Maybe we have values already set. 41 | oldValues, ok := parent.Value(contextLogValuesKey).(Kv) 42 | if !ok { 43 | oldValues = Kv{} 44 | } 45 | 46 | // Copy old and received values into the new kv. 47 | newValues := Kv{} 48 | for k, v := range oldValues { 49 | newValues[k] = v 50 | } 51 | for k, v := range kv { 52 | newValues[k] = v 53 | } 54 | 55 | return context.WithValue(parent, contextLogValuesKey, newValues) 56 | } 57 | 58 | // ValuesFromCtx gets the log Key values from a context. 59 | func ValuesFromCtx(ctx context.Context) Kv { 60 | values, ok := ctx.Value(contextLogValuesKey).(Kv) 61 | if !ok { 62 | return Kv{} 63 | } 64 | 65 | return values 66 | } 67 | -------------------------------------------------------------------------------- /internal/log/logrus/logrus.go: -------------------------------------------------------------------------------- 1 | package logrus 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/sirupsen/logrus" 7 | 8 | "github.com/slok/kube-code-generator/internal/log" 9 | ) 10 | 11 | type logger struct { 12 | *logrus.Entry 13 | } 14 | 15 | // NewLogrus returns a new log.Logger for a logrus implementation. 16 | func NewLogrus(l *logrus.Entry) log.Logger { 17 | return logger{Entry: l} 18 | } 19 | 20 | func (l logger) WithValues(kv log.Kv) log.Logger { 21 | newLogger := l.Entry.WithFields(kv) 22 | return NewLogrus(newLogger) 23 | } 24 | 25 | func (l logger) WithCtxValues(ctx context.Context) log.Logger { 26 | return l.WithValues(log.ValuesFromCtx(ctx)) 27 | } 28 | 29 | func (l logger) SetValuesOnCtx(parent context.Context, values log.Kv) context.Context { 30 | return log.CtxWithValues(parent, values) 31 | } 32 | -------------------------------------------------------------------------------- /internal/util/gomod/gomod.go: -------------------------------------------------------------------------------- 1 | package gomod 2 | 3 | import ( 4 | "fmt" 5 | "path/filepath" 6 | "regexp" 7 | "strings" 8 | ) 9 | 10 | var ( 11 | goModuleRegexp = regexp.MustCompile(`(?m)^module ([^\s]+)$`) 12 | ) 13 | 14 | // GetGoModule will return the go module declaration from a go mod file content. 15 | func GetGoModule(goModFileContent string) (string, error) { 16 | match := goModuleRegexp.FindAllStringSubmatch(goModFileContent, 1) 17 | if len(match) < 1 || len(match[0]) < 2 { 18 | return "", fmt.Errorf(`could not find module declaration on "go.mod"`) 19 | } 20 | packageName := match[0][1] 21 | 22 | return packageName, nil 23 | } 24 | 25 | // GetImportPackageFromDir will return the go package based on a go project module and a relative directory. 26 | func GetGoPackageFromDir(goModule, relativeDir string) string { 27 | pkg := strings.TrimSuffix(relativeDir, "/") + "/" // Ensure slash. 28 | pkg = filepath.Dir(pkg) 29 | if pkg != "." && pkg != "" { 30 | return goModule + "/" + pkg 31 | } 32 | 33 | return pkg 34 | 35 | } 36 | -------------------------------------------------------------------------------- /internal/util/gomod/gomod_test.go: -------------------------------------------------------------------------------- 1 | package gomod_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/slok/kube-code-generator/internal/util/gomod" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestGetGoModule(t *testing.T) { 11 | tests := map[string]struct { 12 | goDotMod string 13 | expectedGoMod string 14 | expErr bool 15 | }{ 16 | "If not content, it should fail.": { 17 | goDotMod: "", 18 | expErr: true, 19 | }, 20 | 21 | "If there is content, but no go module, it should fail.": { 22 | goDotMod: ` 23 | go 1.22.0 24 | 25 | require ( 26 | github.com/alecthomas/kingpin/v2 v2.4.0 27 | github.com/sirupsen/logrus v1.9.3 28 | github.com/stretchr/testify v1.8.2 29 | ) 30 | 31 | `, 32 | expErr: true, 33 | }, 34 | 35 | "If there is content, and go module, it should return the module.": { 36 | goDotMod: ` 37 | module github.com/slok/kube-code-generator 38 | 39 | go 1.22.0 40 | 41 | require ( 42 | github.com/alecthomas/kingpin/v2 v2.4.0 43 | github.com/sirupsen/logrus v1.9.3 44 | github.com/stretchr/testify v1.8.2 45 | ) 46 | 47 | require ( 48 | github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect 49 | github.com/davecgh/go-spew v1.1.1 // indirect 50 | github.com/pmezard/go-difflib v1.0.0 // indirect 51 | github.com/xhit/go-str2duration/v2 v2.1.0 // indirect 52 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect 53 | gopkg.in/yaml.v3 v3.0.1 // indirect 54 | )`, 55 | expectedGoMod: "github.com/slok/kube-code-generator", 56 | }, 57 | } 58 | 59 | for name, test := range tests { 60 | t.Run(name, func(t *testing.T) { 61 | assert := assert.New(t) 62 | 63 | gotGoMod, err := gomod.GetGoModule(test.goDotMod) 64 | 65 | if test.expErr { 66 | assert.Error(err) 67 | } else if assert.NoError(err) { 68 | assert.Equal(test.expectedGoMod, gotGoMod) 69 | } 70 | }) 71 | } 72 | } 73 | 74 | func TestGetGoPackageFromDir(t *testing.T) { 75 | tests := map[string]struct { 76 | goMod string 77 | pkgDir string 78 | expectedPkg string 79 | }{ 80 | "Having a go module and a package dir, it should return the Go package.": { 81 | goMod: "github.com/slok/kube-code-generator/example", 82 | pkgDir: "./something/gen/otherthing", 83 | expectedPkg: "github.com/slok/kube-code-generator/example/something/gen/otherthing", 84 | }, 85 | 86 | "Having a go module and a package dir, it should return the Go package (not relative dir prefix).": { 87 | goMod: "github.com/slok/kube-code-generator/example", 88 | pkgDir: "something/gen/otherthing", 89 | expectedPkg: "github.com/slok/kube-code-generator/example/something/gen/otherthing", 90 | }, 91 | } 92 | 93 | for name, test := range tests { 94 | t.Run(name, func(t *testing.T) { 95 | assert := assert.New(t) 96 | 97 | gotGoPkg := gomod.GetGoPackageFromDir(test.goMod, test.pkgDir) 98 | assert.Equal(test.expectedPkg, gotGoPkg) 99 | 100 | }) 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /scripts/build/bin/build-all.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | 6 | # Build all. 7 | ostypes=("Linux" "Darwin" "Windows" "ARM") 8 | for ostype in "${ostypes[@]}" 9 | do 10 | ostype="${ostype}" ./scripts/build/bin/build.sh 11 | done 12 | 13 | # Create checksums. 14 | checksums_dir="./bin" 15 | cd ${checksums_dir} && sha256sum * > ./checksums.txt 16 | -------------------------------------------------------------------------------- /scripts/build/bin/build-raw.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | 6 | # Env vars that can be set. 7 | # - EXTENSION: The binary out extension. 8 | # - VERSION: Version for the binary. 9 | # - GOOS: OS compiling target 10 | # - GOARCH: Arch compiling target. 11 | # - GOARM: ARM version. 12 | 13 | version_path="github.com/slok/kube-code-generator/internal/info.Version" 14 | src=./cmd/kube-code-generator 15 | out=./bin/kube-code-generator 16 | 17 | # Prepare flags. 18 | final_out=${out}${EXTENSION:-} 19 | ldf_cmp="-s -w -extldflags '-static'" 20 | f_ver="-X ${version_path}=${VERSION:-dev}" 21 | 22 | # Build binary. 23 | echo "[*] Building binary at ${final_out} (GOOS=${GOOS:-}, GOARCH=${GOARCH:-}, GOARM=${GOARM:-}, VERSION=${VERSION:-}, EXTENSION=${EXTENSION:-})" 24 | CGO_ENABLED=0 go build -o ${final_out} --ldflags "${ldf_cmp} ${f_ver}" -buildvcs=false ${src} 25 | -------------------------------------------------------------------------------- /scripts/build/bin/build.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | 6 | build_script="./scripts/build/bin/build-raw.sh" 7 | ostype=${ostype:-"native"} 8 | 9 | echo "[+] Build OS type selected: ${ostype}" 10 | 11 | if [ $ostype == 'Linux' ]; then 12 | EXTENSION="-linux-amd64" GOOS="linux" GOARCH="amd64" ${build_script} 13 | elif [ $ostype == 'Darwin' ]; then 14 | EXTENSION="-darwin-amd64" GOOS="darwin" GOARCH="amd64" ${build_script} 15 | EXTENSION="-darwin-arm64" GOOS="darwin" GOARCH="arm64" ${build_script} 16 | elif [ $ostype == 'Windows' ]; then 17 | EXTENSION="-windows-amd64.exe" GOOS="windows" GOARCH="amd64" ${build_script} 18 | elif [ $ostype == 'ARM' ]; then 19 | EXTENSION="-linux-arm64" GOOS="linux" GOARCH="arm64" ${build_script} 20 | EXTENSION="-linux-arm-v7" GOOS="linux" GOARCH="arm" GOARM="7" ${build_script} 21 | else 22 | # Native. 23 | ${build_script} 24 | fi 25 | -------------------------------------------------------------------------------- /scripts/build/docker/build-image-dev.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -e 4 | 5 | [ -z "$VERSION" ] && echo "VERSION env var is required." && exit 1 6 | [ -z "$IMAGE" ] && echo "IMAGE env var is required." && exit 1 7 | [ -z "$DOCKER_FILE_PATH" ] && echo "DOCKER_FILE_PATH env var is required." && exit 1 8 | # Build image. 9 | echo "Building dev image ${IMAGE}:${VERSION}..." 10 | docker build \ 11 | -t "${IMAGE}:${VERSION}" \ 12 | -f "${DOCKER_FILE_PATH}" . 13 | -------------------------------------------------------------------------------- /scripts/build/docker/build-image.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -e 4 | 5 | [ -z "$VERSION" ] && echo "VERSION env var is required." && exit 1 6 | [ -z "$IMAGE" ] && echo "IMAGE env var is required." && exit 1 7 | [ -z "$DOCKER_FILE_PATH" ] && echo "DOCKER_FILE_PATH env var is required." && exit 1 8 | 9 | # By default use amd64 architecture. 10 | DEF_ARCH=amd64 11 | ARCH=${ARCH:-$DEF_ARCH} 12 | 13 | IMAGE_TAG_ARCH="${IMAGE}:${VERSION}-${ARCH}" 14 | 15 | # Build image. 16 | echo "Building image ${IMAGE_TAG_ARCH}..." 17 | docker build \ 18 | --build-arg VERSION="${VERSION}" \ 19 | --build-arg ARCH="${ARCH}" \ 20 | -t "${IMAGE_TAG_ARCH}" \ 21 | -f "${DOCKER_FILE_PATH}" . 22 | -------------------------------------------------------------------------------- /scripts/build/docker/build-publish-image-all.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -o errexit 4 | set -o nounset 5 | 6 | [ -z "$VERSION" ] && echo "VERSION env var is required." && exit 1 7 | [ -z "$IMAGE" ] && echo "IMAGE env var is required." && exit 1 8 | 9 | function build_and_publish() { 10 | local arch="${1}" 11 | ARCH="${arch}" ./scripts/build/docker/build-image.sh 12 | ARCH="${arch}" ./scripts/build/docker/publish-image.sh 13 | } 14 | 15 | # Build and publish images for all architectures. 16 | #archs=("amd64" "arm64" "arm" "ppc64le" "s390x") 17 | archs=("amd64") 18 | for arch in "${archs[@]}"; do 19 | build_and_publish "${arch}" 20 | done 21 | 22 | IMAGE_TAG="${IMAGE}:${VERSION}" 23 | 24 | # Create manifest to join all arch images under one virtual tag. 25 | MANIFEST="docker manifest create -a ${IMAGE_TAG}" 26 | for arch in "${archs[@]}"; do 27 | MANIFEST="${MANIFEST} ${IMAGE_TAG}-${arch}" 28 | done 29 | eval "${MANIFEST}" 30 | 31 | # Annotate each arch manifest to set which image is build for which CPU architecture. 32 | for arch in "${archs[@]}"; do 33 | docker manifest annotate --arch "${arch}" "${IMAGE_TAG}" "${IMAGE_TAG}-${arch}" 34 | done 35 | 36 | # Push virual tag metadata. 37 | docker manifest push "${IMAGE_TAG}" 38 | 39 | # Same as the regular virtual tag but for `:latest`. 40 | if [ ! -z "${TAG_IMAGE_LATEST:-}" ]; then 41 | IMAGE_TAG_LATEST="${IMAGE}:latest" 42 | 43 | # Clean latest manifest in case there is one. 44 | docker manifest rm ${IMAGE_TAG_LATEST} || true 45 | 46 | # Create manifest to join all arch images under one virtual tag. 47 | MANIFEST_LATEST="docker manifest create -a ${IMAGE_TAG_LATEST}" 48 | for arch in "${archs[@]}"; do 49 | MANIFEST_LATEST="${MANIFEST_LATEST} ${IMAGE_TAG}-${arch}" 50 | done 51 | eval "${MANIFEST_LATEST}" 52 | 53 | # Annotate each arch manifest to set which image is build for which CPU architecture. 54 | for arch in "${archs[@]}"; do 55 | docker manifest annotate --arch "${arch}" "${IMAGE_TAG_LATEST}" "${IMAGE_TAG}-${arch}" 56 | done 57 | 58 | # Push virual tag metadata. 59 | docker manifest push "${IMAGE_TAG_LATEST}" 60 | fi 61 | -------------------------------------------------------------------------------- /scripts/build/docker/publish-image.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -e 4 | 5 | 6 | [ -z "$VERSION" ] && echo "VERSION env var is required." && exit 1; 7 | [ -z "$IMAGE" ] && echo "IMAGE env var is required." && exit 1; 8 | 9 | DEF_ARCH=amd64 10 | ARCH=${ARCH:-$DEF_ARCH} 11 | 12 | IMAGE_TAG_ARCH="${IMAGE}:${VERSION}-${ARCH}" 13 | 14 | echo "Pushing image ${IMAGE_TAG_ARCH}..." 15 | docker push ${IMAGE_TAG_ARCH} 16 | -------------------------------------------------------------------------------- /scripts/check/check.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -o errexit 4 | set -o nounset 5 | 6 | GOFLAGS="-buildvcs=false" golangci-lint run 7 | -------------------------------------------------------------------------------- /scripts/check/integration-test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -o errexit 4 | set -o nounset 5 | 6 | go test -race -tags='integration' -v ./test/integration/... -------------------------------------------------------------------------------- /scripts/check/unit-test.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -o errexit 4 | set -o nounset 5 | 6 | go test -race -coverprofile=.test_coverage.txt $(go list ./... | grep -v /test/integration ) 7 | go tool cover -func=.test_coverage.txt | tail -n1 | awk '{print "Total test coverage: " $3}' -------------------------------------------------------------------------------- /scripts/deps.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -o errexit 4 | set -o nounset 5 | 6 | go mod tidy -------------------------------------------------------------------------------- /scripts/gogen.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | set -o errexit 4 | set -o nounset 5 | 6 | go generate ./... 7 | mockery 8 | --------------------------------------------------------------------------------