├── .dockerignore ├── .github └── workflows │ └── main.yml ├── .gitignore ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── bin └── docker.sh ├── go.mod ├── go.sum ├── jsonnet ├── config.jsonnet ├── files │ ├── id_rsa.pub │ └── mkdocs.css ├── github_action.jsonnet ├── mkdocs.jsonnet ├── objectmeta.jsonnet └── terraform.jsonnet ├── libs ├── actions-runner-controller │ └── config.jsonnet ├── aiven │ └── config.jsonnet ├── amazon-vpc-resource-controller │ └── config.jsonnet ├── argo-cd │ └── config.jsonnet ├── argo-rollouts │ └── config.jsonnet ├── argo-workflows │ └── config.jsonnet ├── aws-load-balancer-controller │ └── config.jsonnet ├── aws-rds-controller │ └── config.jsonnet ├── banzai-logging │ └── config.jsonnet ├── banzaicloud-bank-vaults │ └── config.jsonnet ├── calico │ └── config.jsonnet ├── cert-manager │ └── config.jsonnet ├── cilium │ └── config.jsonnet ├── clickhouse-operator │ └── config.jsonnet ├── cloudnative-pg │ └── config.jsonnet ├── cluster-api-provider-aws │ └── config.jsonnet ├── cluster-api │ └── config.jsonnet ├── cnrm │ └── config.jsonnet ├── composable │ ├── config.jsonnet │ └── custom │ │ └── helpers │ │ └── ibmcloud.libsonnet ├── consul │ └── config.jsonnet ├── contour │ └── config.jsonnet ├── crossplane-core │ ├── config.jsonnet │ └── custom │ │ └── crossplane │ │ ├── compositeResourceDefinition.libsonnet │ │ ├── composition.libsonnet │ │ └── resource.libsonnet ├── crossplane │ ├── Makefile │ ├── config.jsonnet │ ├── custom │ │ └── crossplane │ │ │ ├── compositeResourceDefinition.libsonnet │ │ │ ├── composition.libsonnet │ │ │ └── resource.libsonnet │ ├── upbound_aws_crds.libsonnet │ ├── upbound_azure_crds.libsonnet │ └── upbound_gcp_crds.libsonnet ├── datadog-operator │ └── config.jsonnet ├── eck-operator │ └── config.jsonnet ├── edp-keycloak-operator │ └── config.jsonnet ├── emissary │ └── config.jsonnet ├── envoy-gateway │ └── config.jsonnet ├── external-dns │ └── config.jsonnet ├── external-secrets │ └── config.jsonnet ├── flagger │ └── config.jsonnet ├── fluxcd │ └── config.jsonnet ├── gatekeeper │ └── config.jsonnet ├── gateway-api │ └── config.jsonnet ├── google-cloud-sql-proxy-operator │ └── config.jsonnet ├── grafana-agent │ └── config.jsonnet ├── grafana-alloy │ └── config.jsonnet ├── grafana-operator │ └── config.jsonnet ├── harbor-operator │ └── config.jsonnet ├── hcp-terraform-operator │ └── config.jsonnet ├── istio │ ├── config.jsonnet │ └── custom │ │ └── virtualservice.libsonnet ├── k8s │ ├── README_docs.md.tmpl │ ├── README_root.md.tmpl │ ├── config.jsonnet │ └── custom │ │ └── core │ │ ├── apps.libsonnet │ │ ├── autoscaling.libsonnet │ │ ├── batch.libsonnet │ │ ├── core.libsonnet │ │ ├── list.libsonnet │ │ ├── mapContainers.libsonnet │ │ ├── rbac.libsonnet │ │ └── volumeMounts.libsonnet ├── karpenter │ └── config.jsonnet ├── keda │ └── config.jsonnet ├── knative-eventing │ └── config.jsonnet ├── knative-serving │ ├── config.jsonnet │ └── custom │ │ └── serving │ │ └── mapContainers.libsonnet ├── kube-prometheus │ ├── config.jsonnet │ └── custom │ │ └── monitoring │ │ ├── endpoint.libsonnet │ │ ├── pod_metrics_endpoint.libsonnet │ │ └── relabel_config.libsonnet ├── kubernetes-nmstate │ └── config.jsonnet ├── kubernetes-secret-generator │ └── config.jsonnet ├── kubevela │ └── config.jsonnet ├── kyverno │ └── config.jsonnet ├── litmus-chaos │ └── config.jsonnet ├── metallb │ └── config.jsonnet ├── milvus-operator │ └── config.jsonnet ├── minio-operator │ └── config.jsonnet ├── mysql-operator │ └── config.jsonnet ├── openshift │ └── config.jsonnet ├── prometheus-operator │ └── config.jsonnet ├── pyrra │ └── config.jsonnet ├── rabbitmq-messaging-topology-operator │ └── config.jsonnet ├── rabbitmq │ └── config.jsonnet ├── secrets-store-csi-driver │ ├── config.jsonnet │ └── custom │ │ └── secrets-store-csi-driver │ │ └── secretProviderClass.libsonnet ├── securecodebox │ └── config.jsonnet ├── spicedb-operator │ └── config.jsonnet ├── strimzi │ └── config.jsonnet ├── tektoncd │ └── config.jsonnet ├── teleport-operator │ └── config.jsonnet ├── traefik │ └── config.jsonnet ├── triggermesh │ └── config.jsonnet ├── upbound-provider-opentofu │ └── config.jsonnet ├── vault-secrets-operator │ └── config.jsonnet ├── vertical-pod-autoscaler │ ├── config.jsonnet │ └── custom │ │ └── core │ │ └── autoscaling.libsonnet ├── victoria-metrics-operator │ └── config.jsonnet └── zalando-postgres-operator │ └── config.jsonnet ├── main.go ├── pkg ├── builder │ ├── builder.go │ ├── collections.go │ ├── comments.go │ ├── condition.go │ ├── docsonnet │ │ └── docsonnet.go │ ├── func_call.go │ ├── funcs.go │ ├── funcs_test.go │ ├── import.go │ ├── locals.go │ ├── marshal.go │ ├── objects.go │ ├── objects_test.go │ ├── operators.go │ ├── primitives.go │ ├── reference.go │ └── utils_test.go ├── model │ ├── gvk.go │ ├── gvk_test.go │ ├── load.go │ └── modifiers.go ├── render │ ├── modifiers.go │ ├── render.go │ ├── sort.go │ └── sort_test.go └── swagger │ ├── crd.go │ ├── definitions.go │ ├── load.go │ ├── objectmeta.json │ ├── schema.go │ └── swagger.go ├── scripts ├── configure_github_ssh.sh ├── docs.sh └── gen.sh └── tf ├── .gitignore └── .keep /.dockerignore: -------------------------------------------------------------------------------- 1 | gen/ 2 | k8s 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # k8s-gen binary name 2 | k8s 3 | 4 | # really want to make sure I don't commit that 5 | id_rsa 6 | 7 | # default output directory 8 | gen/ 9 | 10 | # autogenerated 11 | libs/*/skel 12 | libs/*/config.yml 13 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.20 as base 2 | 3 | ENV GO111MODULE=on 4 | WORKDIR /app 5 | 6 | COPY go.mod . 7 | COPY go.sum . 8 | 9 | RUN go mod download 10 | 11 | COPY pkg pkg 12 | COPY main.go main.go 13 | 14 | FROM base AS builder 15 | RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \ 16 | go build \ 17 | -ldflags='-s -w -extldflags "-static"' \ 18 | -o k8s-gen . 19 | 20 | FROM golang:1.20-alpine3.18 as jsonnet 21 | 22 | RUN apk add --no-cache git 23 | RUN go install github.com/google/go-jsonnet/cmd/jsonnet@latest 24 | 25 | FROM alpine:3.18 26 | 27 | WORKDIR /app 28 | 29 | RUN apk add --no-cache bash curl git openssh diffutils 30 | 31 | COPY --from=mikefarah/yq:4.34.1 /usr/bin/yq /usr/local/bin/yq2 32 | COPY --from=ghcr.io/jsonnet-libs/docsonnet:0.0.5 /usr/bin/docsonnet /usr/local/bin/ 33 | COPY --from=builder /app/k8s-gen /usr/local/bin/ 34 | COPY --from=jsonnet /go/bin/jsonnet /usr/local/bin/ 35 | 36 | COPY scripts . 37 | COPY jsonnet jsonnet 38 | COPY LICENSE . 39 | 40 | ENTRYPOINT ["./gen.sh"] 41 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | IMAGE_NAME ?= k8s-gen 2 | IMAGE_PREFIX ?= ghcr.io/jsonnet-libs 3 | IMAGE_TAG ?= 0.0.8 4 | 5 | OUTPUT_DIR ?= ${PWD}/gen 6 | ABS_OUTPUT_DIR := $(shell realpath $(OUTPUT_DIR)) 7 | 8 | IMPORTS=$(shell find libs -name config.jsonnet | xargs -I {} echo "(import '{}'),") 9 | 10 | PAGES ?= false 11 | GEN_COMMIT ?= false 12 | DIFF ?= true 13 | GITHUB_SHA ?= $(shell git rev-parse HEAD) 14 | GIT_AUTHOR_NAME ?= $(shell git --no-pager log --format=format:'%an' -n 1) 15 | GIT_AUTHOR_EMAIL ?= $(shell git --no-pager log --format=format:'%ae' -n 1) 16 | GIT_COMMITTER_NAME ?= $(shell git --no-pager log --format=format:'%an' -n 1) 17 | GIT_COMMITTER_EMAIL ?= $(shell git --no-pager log --format=format:'%ae' -n 1) 18 | 19 | OBJECTMETA_VERSION := v1.23.4 20 | 21 | .DEFAULT_GOAL: default 22 | default: 23 | 24 | ## Requires go-jsonnet for -c flag 25 | .PHONY: .github/workflows/main.yml 26 | .github/workflows/main.yml: 27 | jsonnet -c -m . -S -J . --tla-code "libs=[$(IMPORTS)]" jsonnet/github_action.jsonnet 28 | 29 | ## Requires go-jsonnet for -c flag 30 | .PHONY: tf/main.tf.json 31 | tf/main.tf.json: 32 | jsonnet -c -m . -S -J . --tla-code "pages=$(PAGES)" --tla-code "libs=[$(IMPORTS)]" jsonnet/terraform.jsonnet 33 | 34 | clean: 35 | rm -f .github/workflows/main.yml: 36 | rm -f tf/main.tf.json 37 | 38 | configure: clean .github/workflows/main.yml tf/main.tf.json 39 | 40 | update_objectmeta: 41 | curl -sL "https://raw.githubusercontent.com/kubernetes/kubernetes/$(OBJECTMETA_VERSION)/api/openapi-spec/swagger.json" | jsonnet -o pkg/swagger/objectmeta.json jsonnet/objectmeta.jsonnet 42 | 43 | debug: build 44 | mkdir -p $(ABS_OUTPUT_DIR) && \ 45 | DEBUG=true ./bin/docker.sh \ 46 | -v $(shell realpath $@):/config \ 47 | -v $(ABS_OUTPUT_DIR):/output \ 48 | -e GEN_COMMIT="$(GEN_COMMIT)" \ 49 | -e SSH_KEY="$${SSH_KEY}" \ 50 | $(IMAGE_PREFIX)/$(IMAGE_NAME):$(IMAGE_TAG) 51 | 52 | all: build libs/* 53 | 54 | libs/*: 55 | mkdir -p $(ABS_OUTPUT_DIR) && \ 56 | ./bin/docker.sh \ 57 | -v $(shell realpath $@):/config \ 58 | -v $(ABS_OUTPUT_DIR):/output \ 59 | -e DIFF="$(DIFF)" \ 60 | -e GEN_COMMIT="$(GEN_COMMIT)" \ 61 | -e GITHUB_SHA="$(GITHUB_SHA)" \ 62 | -e GIT_AUTHOR_NAME="$(GIT_AUTHOR_NAME)" \ 63 | -e GIT_AUTHOR_EMAIL="$(GIT_AUTHOR_EMAIL)" \ 64 | -e GIT_COMMITTER_NAME="$(GIT_COMMITTER_NAME)" \ 65 | -e GIT_COMMITTER_EMAIL="$(GIT_COMMITTER_EMAIL)" \ 66 | -e SSH_KEY="$${SSH_KEY}" \ 67 | $(IMAGE_PREFIX)/$(IMAGE_NAME):$(IMAGE_TAG) /config /output 68 | 69 | build: 70 | docker build -t $(IMAGE_PREFIX)/$(IMAGE_NAME):$(IMAGE_TAG) . 71 | 72 | save: 73 | mkdir -p artifacts 74 | docker save $(IMAGE_PREFIX)/$(IMAGE_NAME):$(IMAGE_TAG) > artifacts/docker-image.tar 75 | 76 | load: 77 | docker load < artifacts/docker-image.tar 78 | 79 | push: build push-image 80 | 81 | push-image: 82 | docker push $(IMAGE_PREFIX)/$(IMAGE_NAME):$(IMAGE_TAG) 83 | docker push $(IMAGE_PREFIX)/$(IMAGE_NAME):latest 84 | 85 | .PHONY: clean configure update_objectmeta debug run all libs/* build push push-image 86 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # k8s-gen 2 | 3 | Code generator for Jsonnet Kubernetes libraries. 4 | 5 | This repository contains the generator code and relevant bits to generate the jsonnet 6 | libraries. It can generate libraries directly from OpenAPI spec v2 (Swagger) or by 7 | providing CustomResourceDefinitions. 8 | 9 | The CI job is set up to generate and update the content of a corresponding Github 10 | repository for each library. The management of these repositories is done through 11 | Terraform. 12 | 13 | ## Usage 14 | 15 | ### Create or update a new lib 16 | 17 | Create a folder in `libs/`: 18 | 19 | ```bash 20 | mkdir libs/ 21 | ``` 22 | 23 | Setup `config.jsonnet`, this example is for rendering a lib from CRDs: 24 | 25 | ```jsonnet 26 | # libs//config.jsonnet 27 | local config = import 'jsonnet/config.jsonnet'; 28 | 29 | config.new( 30 | name='', 31 | specs=[ 32 | { 33 | # output directory, usually the version of the upstream application/CRD 34 | output: '', 35 | 36 | # crds Endpoints of the CRD manifests, should be omitted if there is an openapi spec 37 | # Only resources of kind CustomResourceDefintion are applied; the default policy is to just 38 | # pass in the CRDs here though. 39 | crds: ['https://url.to.crd.manifest//manifests/crd-all.gen.yaml'], 40 | 41 | # openapi spec v2 endpoint 42 | # No need to define this if `crds` is defined 43 | openapi: 'http://localhost:8001/openapi/v2', 44 | 45 | # prefix Regex that should match the reverse of the CRDs spec.group 46 | # for example `group: networking.istio.io` 47 | # would become ^io\\.istio\\.networking\\..*" 48 | prefix: '^\\.\\..*', 49 | 50 | # localName used in the docs for the example(s) 51 | localName: '', 52 | }, 53 | ] 54 | ) 55 | ``` 56 | 57 | Dry run the generate process: 58 | 59 | ```bash 60 | $ make build # Build the image 61 | $ make libs/ # Generate the library 62 | ``` 63 | 64 | Set up CI and Terraform code: 65 | 66 | ``` 67 | $ make configure 68 | ``` 69 | 70 | That is it, commit the changes to a branch and make a PR to get things rolling. The CI 71 | should take care of the rest. 72 | 73 | 74 | ## Customizing 75 | 76 | Because the generator only creates the most minimal yet functional code, more 77 | sophisticated utilities like constructors (`deployment.new(name, replicas, 78 | containers)`, etc) are not created. 79 | 80 | For that, there are two methods for extending: 81 | 82 | ### `custom` patches 83 | 84 | The [`custom/`](https://github.com/jsonnet-libs/k8s/tree/master/libs/k8s/custom) 85 | directory contains a set of `.libsonnet` files, that are _automatically merged_ 86 | with the generated result in `main.libsonnet`, so they become part of the 87 | exported API. 88 | 89 | For example the patches in `libs/k8s`: 90 | 91 | ``` 92 | 93 | libs/k8s/ 94 | ├── config.jsonnet # Config to generate the k8s jsonnet libraries 95 | ├── README.md.tmpl # Template for the index of the generated docs 96 | └── custom 97 | └── core 98 |    ├── apps.libsonnet # Constructors for `core/v1`, ported from `ksonnet-gen` and `kausal.libsonnet` 99 | ├── autoscaling.libsonnet # Extends `autoscaling/v2beta2` 100 |    ├── batch.libsonnet # Constructors for `batch/v1beta1`, `batch/v2alpha1`, ported from `kausal.libsonnet` 101 |    ├── core.libsonnet # Constructors for `apps/v1`, `apps/v1beta1`, ported from `ksonnet-gen` and `kausal.libsonnet` 102 | ├── list.libsonnet # Adds `core.v1.List` 103 |    ├── mapContainers.libsonnet # Adds `mapContainers` functions for fields that support them 104 |    ├── rbac.libsonnet # Adds helper functions to rbac objects 105 | └── volumeMounts.libsonnet # Adds helper functions to mount volumes 106 | ``` 107 | 108 | A reference for these must also be made in the `config.jsonnet`: 109 | 110 | ```jsonnet 111 | # libs/k8s/config.jsonnet 112 | local config = import 'jsonnet/config.jsonnet'; 113 | 114 | config.new( 115 | name='k8s', 116 | specs=[ 117 | { 118 | ... 119 | patchDir: 'custom/core', 120 | }, 121 | ] 122 | ) 123 | ``` 124 | 125 | ### Extensions 126 | 127 | Extensions serve a similar purpose as `custom/` patches, but are **not 128 | automatically applied**. However, they are still part of the final artifact, but 129 | need to added by the user themselves. 130 | 131 | Extensions can be applied as so: 132 | 133 | ```jsonnet 134 | (import "github.com/jsonnet-libs/k8s-libsonnet/1.21/main.libsonnet") 135 | + (import "github.com/jsonnet-libs/k8s-libsonnet/extensions/.libsonnet") 136 | ``` 137 | 138 | A reference for these must also be made in the `config.jsonnet`: 139 | 140 | ```jsonnet 141 | # libs/k8s/config.jsonnet 142 | local config = import 'jsonnet/config.jsonnet'; 143 | 144 | config.new( 145 | name='k8s', 146 | specs=[ 147 | { 148 | ... 149 | extensionsDir: 'extensions/core', 150 | }, 151 | ] 152 | ) 153 | ``` 154 | -------------------------------------------------------------------------------- /bin/docker.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | set -x 4 | 5 | CI=${CI:-false} 6 | DEBUG=${DEBUG:-false} 7 | 8 | OPTS="" 9 | if [ "$CI" != "true" ]; then 10 | # When run locally volume mounts match ownership of current user. 11 | OPTS="$OPTS --user $(id -u):$(id -g)" 12 | OPTS="$OPTS -v /etc/passwd:/etc/passwd:ro" 13 | OPTS="$OPTS -v /etc/group:/etc/group:ro" 14 | fi 15 | 16 | if [ "$DEBUG" == "true" ]; then 17 | # Start a shell 18 | OPTS="$OPTS -ti --entrypoint /bin/bash" 19 | fi 20 | 21 | docker run --rm $OPTS "$@" 22 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/jsonnet-libs/k8s 2 | 3 | go 1.20 4 | 5 | require ( 6 | github.com/fatih/camelcase v1.0.0 7 | github.com/go-clix/cli v0.2.0 8 | github.com/google/go-jsonnet v0.20.0 9 | github.com/stretchr/testify v1.8.1 10 | gopkg.in/yaml.v2 v2.4.0 11 | gopkg.in/yaml.v3 v3.0.1 12 | k8s.io/apiextensions-apiserver v0.27.3 13 | k8s.io/apimachinery v0.27.3 14 | ) 15 | 16 | require ( 17 | github.com/davecgh/go-spew v1.1.1 // indirect 18 | github.com/go-logr/logr v1.2.4 // indirect 19 | github.com/gogo/protobuf v1.3.2 // indirect 20 | github.com/google/gofuzz v1.2.0 // indirect 21 | github.com/hashicorp/errwrap v1.1.0 // indirect 22 | github.com/hashicorp/go-multierror v1.1.1 // indirect 23 | github.com/json-iterator/go v1.1.12 // indirect 24 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 25 | github.com/modern-go/reflect2 v1.0.2 // indirect 26 | github.com/pmezard/go-difflib v1.0.0 // indirect 27 | github.com/posener/complete v1.2.3 // indirect 28 | github.com/spf13/pflag v1.0.5 // indirect 29 | golang.org/x/net v0.33.0 // indirect 30 | golang.org/x/text v0.21.0 // indirect 31 | gopkg.in/inf.v0 v0.9.1 // indirect 32 | k8s.io/klog/v2 v2.100.1 // indirect 33 | k8s.io/utils v0.0.0-20230505201702-9f6742963106 // indirect 34 | sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect 35 | sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect 36 | sigs.k8s.io/yaml v1.3.0 // indirect 37 | ) 38 | -------------------------------------------------------------------------------- /jsonnet/config.jsonnet: -------------------------------------------------------------------------------- 1 | { 2 | new( 3 | name, 4 | specs 5 | ):: { 6 | local this = self, 7 | 8 | suffix:: '-libsonnet', 9 | 10 | name:: name, 11 | description:: this.name + ' jsonnet library', 12 | repository:: 'github.com/jsonnet-libs/' + this.name + this.suffix, 13 | branch:: 'main', 14 | site_url:: 'https://jsonnet-libs.github.io/' + this.name + this.suffix, 15 | 16 | 'skel/LICENSE': importstr '../LICENSE', 17 | 18 | 'config.yml': std.manifestYamlDoc({ 19 | repository: this.repository, 20 | specs: [ 21 | local proxy_port = 8001 + i; 22 | { proxy_port: proxy_port, openapi: 'http://localhost:%s/openapi/v2' % proxy_port, repository: this.repository } 23 | + specs[i] 24 | for i in std.range(0, std.length(specs) - 1) 25 | ], 26 | }, true), 27 | 28 | readme_template(name, data):: ||| 29 | # %s Jsonnet library 30 | 31 | This library is generated with [`k8s`](https://github.com/jsonnet-libs/k8s). 32 | 33 | %s 34 | ||| % [name, data], 35 | 36 | 'skel/README.md': this.readme_template( 37 | name=name, 38 | data='[Docs](' + this.site_url + ')', 39 | ), 40 | 41 | 'skel/docs/README.md': this.readme_template( 42 | name=name, 43 | data=std.join('\n', [ 44 | '- [%(output)s](%(output)s/README.md)' % spec 45 | for spec in specs 46 | ]), 47 | ), 48 | 49 | local mkdocs = import './mkdocs.jsonnet', 50 | mkdocs_config:: mkdocs.config { 51 | site_name: this.description, 52 | site_url: this.site_url, 53 | repo_url: 'https://' + this.repository, 54 | }, 55 | 56 | mkdocs_github_action:: mkdocs.action { 57 | branch:: this.branch, 58 | }, 59 | 60 | 'skel/mkdocs.yml': std.manifestYamlDoc(this.mkdocs_config, true), 61 | 62 | 'skel/.github/workflows/main.yml': std.manifestYamlDoc(this.mkdocs_github_action, true), 63 | 'skel/docs/stylesheets/extra.css': importstr 'files/mkdocs.css', 64 | 65 | 'skel/requirements.txt': ||| 66 | # mkdocs 67 | mkdocs>=1.3.0 68 | 69 | # To limit search to a subset 70 | mkdocs-exclude-search>=0.5 71 | 72 | # To minify the HTML 73 | mkdocs-minify-plugin>=0.3 74 | 75 | # Include the theme 76 | mkdocs-material>=7.1.6 77 | 78 | # Deal with list indent of 2 spaces 79 | mdx-truly-sane-lists>=1.3 80 | |||, 81 | }, 82 | } 83 | -------------------------------------------------------------------------------- /jsonnet/files/id_rsa.pub: -------------------------------------------------------------------------------- 1 | ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQC9HjP4reQEoJqS90XCxH16gv6EArFHr5sytG8m0M3B+vc36BPMR1GGad7lAIHB8/T5SK5SvT9z5GmDtY/xocDiH6T9VDZqXAok1w+I6h9lGPyjP4C6X8XVFHH6yDbtRqF2eDQZIaNFA3/zNyWEy+fc3QEuGnkXM6cm9BUQunOy83veove5fGfYLYO9xc3Yi3EI4b6pP8omiU0EoMvpeWIE3jX6lkNbZrGukUYQiCftdNaAds8/bCgCfmsuYuNH2yk2QbgzGIiSv9zuLl0yUwwBOjmDYCj9QQdOeqW3HJPW4oo+wX842bWflwy2BEj3A8dSsZMOeFyez2MEI2XQmvyd duologic@jsonnet-libs 2 | -------------------------------------------------------------------------------- /jsonnet/files/mkdocs.css: -------------------------------------------------------------------------------- 1 | /* Menu */ 2 | /* Decapitalize menu items, Mkdocs capilizes by default and can't be configured */ 3 | nav nav .md-nav__link .md-ellipsis::first-letter { text-transform: lowercase } 4 | 5 | /* TOC */ 6 | /* Don't wrap TOC links with spaces */ 7 | .md-sidebar--secondary .md-nav__item { white-space: nowrap } 8 | /* Scroll horizontal to make them accessible */ 9 | .md-sidebar--secondary .md-sidebar__scrollwrap { overflow-x: auto } 10 | 11 | /* Code */ 12 | /* Make codeblocks stand out */ 13 | .md-typeset pre>code { border-left: 0.2rem solid var(--md-accent-fg-color) } 14 | 15 | /* Headings */ 16 | /* Hide headings for functions but don't remove them, they function as deeplink targets */ 17 | h2[id^="fn-"], 18 | h3[id^="fn-"], 19 | h4[id^="fn-"], 20 | h5[id^="fn-"], 21 | h6[id^="fn-"], 22 | h7[id^="fn-"], 23 | h8[id^="fn-"], 24 | h9[id^="fn-"] { 25 | visibility: hidden; 26 | width: 0; 27 | height: 0; 28 | padding: 0; 29 | margin: 0; 30 | } 31 | 32 | /* Parameters */ 33 | /* Format 'PARAMETERS' after highlight (div.highlight) */ 34 | /* Match first paragraph (p) but only if it is followed by an unsorted list (ul) */ 35 | article.md-content__inner.md-typeset div.highlight+p:has(+ul) { 36 | padding-left: 1em; 37 | margin-top: 0; 38 | margin-bottom: 0; 39 | } 40 | /* Match first unsorted list (ul) after paragraph (p) */ 41 | article.md-content__inner.md-typeset div.highlight+p+ul { 42 | padding-left: 1em; 43 | margin-top:0; 44 | } 45 | article.md-content__inner.md-typeset div.highlight+p+ul li { 46 | margin-top: 0; 47 | margin-bottom: 0; 48 | } 49 | -------------------------------------------------------------------------------- /jsonnet/github_action.jsonnet: -------------------------------------------------------------------------------- 1 | local onMaster = { 'if': "${{ github.ref == 'refs/heads/master' && github.repository == 'jsonnet-libs/k8s' }}" }; 2 | local notFork = { 'if': "github.event.pull_request.head.repo.full_name == 'jsonnet-libs/k8s'" }; 3 | 4 | local terraform = { 5 | job: { 6 | make_env:: { 7 | env: { 8 | PAGES: 'false', 9 | }, 10 | }, 11 | tf_env:: { 12 | 'working-directory': 'tf', 13 | env: { 14 | GITHUB_TOKEN: '${{ secrets.PAT }}', 15 | TF_IN_AUTOMATION: '1', 16 | }, 17 | }, 18 | name: 'Create repositories', 19 | 'runs-on': 'ubuntu-latest', 20 | steps: [ 21 | { uses: 'actions/checkout@v4' }, 22 | { uses: 'zendesk/setup-jsonnet@v12' }, 23 | 24 | self.make_env + { run: 'make tf/main.tf.json' }, 25 | 26 | { 27 | uses: 'hashicorp/setup-terraform@v3', 28 | with: { 29 | cli_config_credentials_token: '${{ secrets.TF_API_TOKEN }}', 30 | }, 31 | }, 32 | self.tf_env + notFork + { run: 'terraform init' }, 33 | self.tf_env + notFork + { run: 'terraform validate -no-color' }, 34 | self.tf_env + notFork + { run: 'terraform plan -no-color' }, 35 | self.tf_env + onMaster + { run: 'terraform init && terraform apply -no-color -auto-approve' }, 36 | ], 37 | }, 38 | withPages(needs): { 39 | name: 'Set up gh-pages branch', 40 | needs: needs, 41 | make_env:: { 42 | env: { 43 | PAGES: 'true', 44 | }, 45 | }, 46 | }, 47 | }; 48 | 49 | local libJob(name) = { 50 | name: 'Generate ' + name + ' Jsonnet library and docs', 51 | needs: ['build', 'repos'], 52 | 'runs-on': 'ubuntu-latest', 53 | 54 | steps: [ 55 | { uses: 'actions/checkout@v4' }, 56 | { 57 | uses: 'dorny/paths-filter@v3', 58 | id: 'filter', 59 | with: { 60 | filters: ||| 61 | workflows: 62 | - '.github/**' 63 | - 'bin/**' 64 | - 'Dockerfile' 65 | - 'go.mod' 66 | - 'go.sum' 67 | - 'jsonnet/**' 68 | - 'main.go' 69 | - 'Makefile' 70 | - 'pkg/**' 71 | - 'scripts/**' 72 | - 'tf/**' 73 | - 'libs/%s/**' 74 | ||| % name, 75 | }, 76 | } 77 | , 78 | { 79 | uses: 'actions/download-artifact@v4', 80 | 'if': "steps.filter.outputs.workflows == 'true'", 81 | with: { 82 | name: 'docker-artifact', 83 | path: 'artifacts', 84 | }, 85 | }, 86 | // Load docker image from cache 87 | { 88 | run: 'make load', 89 | 'if': "steps.filter.outputs.workflows == 'true'", 90 | }, 91 | { 92 | run: 'make libs/' + name, 93 | 'if': "steps.filter.outputs.workflows == 'true'", 94 | env: { 95 | GIT_COMMITTER_NAME: 'jsonnet-libs-bot', 96 | GIT_COMMITTER_EMAIL: '86770550+jsonnet-libs-bot@users.noreply.github.com', 97 | SSH_KEY: '${{ secrets.DEPLOY_KEY }}', 98 | GEN_COMMIT: "${{ github.ref == 'refs/heads/master' && github.repository == 'jsonnet-libs/k8s' }}", 99 | DIFF: 'true', 100 | }, 101 | }, 102 | ], 103 | }; 104 | 105 | // Build and cache docker image 106 | local build = { 107 | name: 'Build docker image', 108 | 'runs-on': 'ubuntu-latest', 109 | steps: [ 110 | { uses: 'actions/checkout@v4' }, 111 | { run: 'make build save' }, 112 | { 113 | uses: 'actions/upload-artifact@v4', 114 | with: { 115 | name: 'docker-artifact', 116 | path: 'artifacts', 117 | 'retention-days': 1, 118 | }, 119 | }, 120 | ], 121 | }; 122 | 123 | function(libs) { 124 | '.github/workflows/main.yml': 125 | '# Generated by `make configure`, please do not edit manually.\n' 126 | + std.manifestYamlDoc({ 127 | on: { 128 | push: { 129 | branches: ['master'], 130 | }, 131 | pull_request: { 132 | branches: ['master'], 133 | }, 134 | }, 135 | jobs: { 136 | [lib.name]: libJob(lib.name) 137 | for lib in libs 138 | } + { 139 | build: build, 140 | repos: terraform.job, 141 | repos_with_pages: terraform.job + terraform.withPages(std.sort([lib.name for lib in libs])), 142 | debugging: { 143 | name: 'Debugging Github Action values', 144 | 'runs-on': 'ubuntu-latest', 145 | steps: [ 146 | { run: 'echo onMaster? ' + onMaster['if'] }, 147 | { run: 'echo ${{ github.repository }}' }, 148 | { run: 'echo ${{ github.ref }}' }, 149 | { run: 'echo ${{ github.event_name }}' }, 150 | ], 151 | }, 152 | }, 153 | }), 154 | } 155 | -------------------------------------------------------------------------------- /jsonnet/mkdocs.jsonnet: -------------------------------------------------------------------------------- 1 | { 2 | config:: { 3 | site_name: error 'must define site_name', 4 | site_url: error 'must define site_url', 5 | repo_url: error 'must define repo_url', 6 | repo_name: std.strReplace(self.repo_url, 'https://github.com/', ''), 7 | edit_uri: '', 8 | extra_css: ['stylesheets/extra.css'], 9 | theme: { 10 | name: 'material', 11 | features: [ 12 | 'navigation.tabs', 13 | 'navigation.indexes', 14 | ], 15 | palette: [ 16 | { 17 | scheme: 'default', 18 | primary: 'indigo', 19 | accent: 'indigo', 20 | media: '(prefers-color-scheme: light)', 21 | toggle: { 22 | icon: 'material/toggle-switch-off-outline', 23 | name: 'Switch to dark mode', 24 | }, 25 | }, 26 | { 27 | scheme: 'slate', 28 | primary: 'red', 29 | accent: 'red', 30 | media: '(prefers-color-scheme: dark)', 31 | toggle: { 32 | icon: 'material/toggle-switch', 33 | name: 'Switch to light mode', 34 | }, 35 | }, 36 | ], 37 | }, 38 | plugins: [ 39 | { 40 | search: { 41 | separator: '[\\s\\-\\.]+', 42 | }, 43 | }, 44 | { 45 | minify: { 46 | minify_html: true, 47 | }, 48 | }, 49 | ], 50 | markdown_extensions: [ 51 | 'pymdownx.highlight', 52 | 'pymdownx.superfences', 53 | { 54 | mdx_truly_sane_lists: { 55 | nested_indent: 2, 56 | truly_sane: true, 57 | }, 58 | }, 59 | ], 60 | }, 61 | 62 | action:: { 63 | local this = self, 64 | branch:: error 'must define branch', 65 | name: 'Publish docs via GitHub Pages', 66 | on: { 67 | push: { 68 | branches: [ 69 | this.branch, 70 | ], 71 | }, 72 | }, 73 | jobs: { 74 | build: { 75 | name: 'Deploy docs', 76 | 'runs-on': 'ubuntu-latest', 77 | steps: [ 78 | { 79 | name: 'Checkout main', 80 | uses: 'actions/checkout@v3', 81 | with: { 82 | 'fetch-depth': 0, 83 | }, 84 | }, 85 | { uses: 'actions/setup-python@v2' }, 86 | { run: 'pip install -r requirements.txt' }, 87 | { run: "git config user.name 'github-actions[bot]' && git config user.email 'github-actions[bot]@users.noreply.github.com'" }, 88 | { 89 | name: 'Publish docs', 90 | run: 'mkdocs gh-deploy --force', 91 | }, 92 | ], 93 | }, 94 | }, 95 | }, 96 | } 97 | -------------------------------------------------------------------------------- /jsonnet/objectmeta.jsonnet: -------------------------------------------------------------------------------- 1 | local spec = import '/dev/stdin'; 2 | 3 | local extractDefinitions = [ 4 | 'io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta', 5 | 'io.k8s.apimachinery.pkg.apis.meta.v1.OwnerReference', 6 | 'io.k8s.apimachinery.pkg.apis.meta.v1.Time', 7 | ]; 8 | 9 | { 10 | definitions: { 11 | [k]: spec.definitions[k] 12 | for k in std.objectFields(spec.definitions) 13 | if std.member(extractDefinitions, k) 14 | }, 15 | info: { 16 | title: 'Kubernetes', 17 | version: 'unversioned', 18 | }, 19 | swagger: '2.0', 20 | } 21 | -------------------------------------------------------------------------------- /jsonnet/terraform.jsonnet: -------------------------------------------------------------------------------- 1 | function(libs, pages=false) { 2 | 'tf/main.tf.json': 3 | std.manifestJsonEx( 4 | { 5 | '//': 'Generated by `make configure`, please do not edit manually.', 6 | terraform: { 7 | required_providers: { 8 | github: { 9 | source: 'integrations/github', 10 | version: '~>4.20.0', 11 | }, 12 | }, 13 | backend: { 14 | remote: { 15 | organization: 'jsonnet-libs', 16 | workspaces: { 17 | name: 'jsonnet-libs', 18 | }, 19 | }, 20 | }, 21 | }, 22 | provider: { 23 | github: { owner: 'jsonnet-libs' }, 24 | }, 25 | } 26 | + std.foldl( 27 | function(acc, lib) 28 | acc { 29 | resource+: 30 | [{ 31 | github_repository: { 32 | [lib.name]: { 33 | name: lib.name + lib.suffix, 34 | description: lib.description, 35 | homepage_url: lib.site_url, 36 | topics: ['jsonnet', 'jsonnet-lib'], 37 | auto_init: true, 38 | has_downloads: false, 39 | has_issues: false, 40 | has_projects: false, 41 | has_wiki: false, 42 | allow_merge_commit: false, 43 | allow_rebase_merge: false, 44 | lifecycle: { 45 | ignore_changes: ['pages'], 46 | }, 47 | } + ( 48 | if pages 49 | then { 50 | lifecycle: { 51 | ignore_changes: [], 52 | }, 53 | pages: 54 | { 55 | source: 56 | { 57 | branch: 'gh-pages', 58 | path: '/', 59 | }, 60 | }, 61 | } 62 | else {} 63 | ), 64 | }, 65 | }], 66 | }, 67 | libs, 68 | {} 69 | ) 70 | , ' ' 71 | ), 72 | } 73 | -------------------------------------------------------------------------------- /libs/actions-runner-controller/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local versions = [ 4 | {output: '0.26', version: '0.26.0', legacy: true}, 5 | {output: '0.27', version: '0.27.6', legacy: true}, 6 | {output: '0.4', version: 'gha-runner-scale-set-0.4.0'}, 7 | {output: '0.5', version: 'gha-runner-scale-set-0.5.0'}, 8 | ]; 9 | 10 | config.new( 11 | name='actions-runner-controller', 12 | specs=[ 13 | { 14 | local url = 15 | if std.objectHas(v, 'legacy') then 16 | 'https://raw.githubusercontent.com/actions/actions-runner-controller/v%s/charts/actions-runner-controller/crds' % v.version 17 | else 18 | 'https://raw.githubusercontent.com/actions/actions-runner-controller/%s/charts/gha-runner-scale-set-controller/crds' % v.version, 19 | 20 | output: v.output, 21 | prefix: if std.objectHas(v, 'legacy') then '^dev\\.summerwind\\.actions\\..*' else '^com\\.github\\.actions\\..*', 22 | crds: 23 | if std.objectHas(v, 'legacy') then 24 | [ 25 | '%s/actions.summerwind.dev_horizontalrunnerautoscalers.yaml' % url, 26 | '%s/actions.summerwind.dev_runnerdeployments.yaml' % url, 27 | '%s/actions.summerwind.dev_runnerreplicasets.yaml' % url, 28 | '%s/actions.summerwind.dev_runners.yaml' % url, 29 | '%s/actions.summerwind.dev_runnersets.yaml' % url, 30 | ] 31 | else 32 | [ 33 | '%s/actions.github.com_autoscalinglisteners.yaml' % url, 34 | '%s/actions.github.com_autoscalingrunnersets.yaml' % url, 35 | '%s/actions.github.com_ephemeralrunners.yaml' % url, 36 | '%s/actions.github.com_ephemeralrunnersets.yaml' % url, 37 | ], 38 | localName: 'actions-runner-controller', 39 | } 40 | for v in versions 41 | ] 42 | ) 43 | -------------------------------------------------------------------------------- /libs/aiven/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local versions = [ 4 | '0.3.0', 5 | '0.4.0', 6 | '0.5.2', 7 | '0.6.0', 8 | '0.7.1', 9 | '0.8.0', 10 | ]; 11 | 12 | config.new( 13 | name='aiven', 14 | specs=[ 15 | { 16 | # output directory, usually the version of the upstream application/CRD 17 | output: version, 18 | 19 | # crds Endpoints of the CRD manifests, should be omitted if there is an openapi spec 20 | # Only resources of kind CustomResourceDefintion are applied; the default policy is to just 21 | # pass in the CRDs here though. 22 | crds: ['https://github.com/aiven/aiven-operator/releases/download/v' + version + '/deployment.yaml'], 23 | 24 | # openapi spec v2 endpoint 25 | # No need to define this if `crds` is defined 26 | // openapi: 'http://localhost:8001/openapi/v2', 27 | 28 | # prefix Regex that should match the reverse of the CRDs spec.group 29 | # for example `group: networking.istio.io` 30 | # would become ^io\\.istio\\.networking\\..*" 31 | prefix: '^io\\.aiven\\..*', 32 | 33 | # localName used in the docs for the example(s) 34 | localName: 'aiven', 35 | }, 36 | for version in versions 37 | ] 38 | ) 39 | 40 | 41 | -------------------------------------------------------------------------------- /libs/amazon-vpc-resource-controller/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local versions = [ 4 | { version: '1.2', tag: 'v1.2.2' }, 5 | { version: '1.3', tag: 'v1.3.4' }, 6 | { version: '1.4', tag: 'v1.4.7' }, 7 | { version: '1.5', tag: 'v1.5.0' }, 8 | { version: '1.6', tag: 'v1.6.0' }, 9 | ]; 10 | 11 | config.new( 12 | name='amazon-vpc-resource-controller', 13 | specs=[ 14 | { 15 | output: v.version, 16 | prefix: '^aws\\.k8s\\.vpcresources\\..*', 17 | crds: [ 18 | 'https://raw.githubusercontent.com/aws/amazon-vpc-resource-controller-k8s/%s/config/crd/bases/vpcresources.k8s.aws_cninodes.yaml' % v.tag, 19 | 'https://raw.githubusercontent.com/aws/amazon-vpc-resource-controller-k8s/%s/config/crd/bases/vpcresources.k8s.aws_securitygrouppolicies.yaml' % v.tag, 20 | ], 21 | localName: 'amazon_vpc_resource_controller', 22 | } 23 | for v in versions 24 | ] 25 | ) 26 | -------------------------------------------------------------------------------- /libs/argo-cd/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | local versions = 3 | [ 4 | '2.11.12', 5 | '2.12.8', 6 | '2.13.2', 7 | ]; 8 | local manifests = ['application-crd.yaml', 'appproject-crd.yaml', 'applicationset-crd.yaml']; 9 | 10 | // Source: https://argo-cd.readthedocs.io/en/stable/developer-guide/release-process-and-cadence/#patch-releases-eg-25x 11 | assert std.length(versions) <= 3 : 'Only the three most recent minor versions are eligible for patch releases. Versions older than the three most recent minor versions are considered EOL and will not receive bug fixes or security updates.'; 12 | 13 | config.new( 14 | name='argo-cd', 15 | specs=[ 16 | { 17 | output: std.join('.', std.split(version, '.')[:2]), 18 | prefix: '^io\\.argoproj\\..*', 19 | localName: 'argo_cd', 20 | crds: [ 21 | 'https://raw.githubusercontent.com/argoproj/argo-cd/v%s/manifests/crds/%s' % 22 | [version, manifest] 23 | for manifest in manifests 24 | ], 25 | } 26 | for version in versions 27 | ] 28 | ) 29 | -------------------------------------------------------------------------------- /libs/argo-rollouts/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | local manifests = [ 3 | 'analysis-run-crd.yaml', 4 | 'analysis-template-crd.yaml', 5 | 'cluster-analysis-template-crd.yaml', 6 | 'experiment-crd.yaml', 7 | 'rollout-crd.yaml', 8 | ]; 9 | local versions = [ 10 | '1.6.0', 11 | '1.7.0', 12 | '1.8.0', 13 | ]; 14 | 15 | config.new( 16 | name='argo-rollouts', 17 | specs=[ 18 | { 19 | output: std.join('.', std.split(version, '.')[:2]), 20 | prefix: '^io\\.argoproj\\..*', 21 | localName: 'argo_rollouts', 22 | crds: [ 23 | 'https://raw.githubusercontent.com/argoproj/argo-rollouts/v%s/manifests/crds/%s' % 24 | [version, manifest] 25 | for manifest in manifests 26 | ], 27 | } 28 | for version in versions 29 | ] 30 | ) 31 | -------------------------------------------------------------------------------- /libs/argo-workflows/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | local versions = [ 3 | { version: '3.2', tag: 'v3.2.2' }, 4 | { version: '3.3', tag: 'v3.3.3' }, 5 | { version: '3.4', tag: 'v3.4.4' }, 6 | { version: '3.5', tag: 'v3.5.2' }, 7 | { version: '3.6', tag: 'v3.6.2' }, 8 | ]; 9 | 10 | config.new( 11 | name='argo-workflows', 12 | specs=[ 13 | { 14 | output: v.version, 15 | prefix: '^io\\.argoproj\\..*', 16 | openapi: 'https://raw.githubusercontent.com/argoproj/argo-workflows/%s/api/jsonschema/schema.json' % v.tag, 17 | localName: 'argo_workflows', 18 | } 19 | for v in versions 20 | ] 21 | ) 22 | -------------------------------------------------------------------------------- /libs/aws-load-balancer-controller/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local versions = [ 4 | { version: '2.2', tag: 'v2.2.4'}, 5 | { version: '2.3', tag: 'v2.3.1'}, 6 | { version: '2.4', tag: 'v2.4.7'}, 7 | { version: '2.5', tag: 'v2.5.4'}, 8 | { version: '2.6', tag: 'v2.6.0'}, 9 | { version: '2.11', tag: 'v2.11.0' }, 10 | ]; 11 | 12 | config.new( 13 | name='aws-load-balancer-controller', 14 | specs=[ 15 | { 16 | output: v.version, 17 | prefix: '^aws\\.k8s\\.elbv2\\..*', 18 | crds: [ 19 | 'https://raw.githubusercontent.com/kubernetes-sigs/aws-load-balancer-controller/%s/helm/aws-load-balancer-controller/crds/crds.yaml' % v.tag, 20 | ], 21 | localName: 'aws_load_balancer_controller', 22 | } 23 | for v in versions 24 | ] 25 | ) 26 | -------------------------------------------------------------------------------- /libs/aws-rds-controller/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local versions = [ 4 | { version: '1.4', tag: 'v1.4.12' }, 5 | 6 | ]; 7 | 8 | config.new( 9 | name='aws-rds-controller', 10 | specs=[ 11 | { 12 | local url = 'https://raw.githubusercontent.com/aws-controllers-k8s/rds-controller/%s/helm/crds' % v.tag, 13 | output: v.version, 14 | crds: [ 15 | '%s/rds.services.k8s.aws_dbclusterparametergroups.yaml' % url, 16 | '%s/rds.services.k8s.aws_dbclusters.yaml' % url, 17 | '%s/rds.services.k8s.aws_dbclustersnapshots.yaml' % url, 18 | '%s/rds.services.k8s.aws_dbinstances.yaml' % url, 19 | '%s/rds.services.k8s.aws_dbparametergroups.yaml' % url, 20 | '%s/rds.services.k8s.aws_dbproxies.yaml' % url, 21 | '%s/rds.services.k8s.aws_dbsnapshots.yaml' % url, 22 | '%s/rds.services.k8s.aws_dbsubnetgroups.yaml' % url, 23 | '%s/rds.services.k8s.aws_globalclusters.yaml' % url, 24 | '%s/services.k8s.aws_adoptedresources.yaml' % url, 25 | '%s/services.k8s.aws_fieldexports.yaml' % url, 26 | ], 27 | localName: 'aws-rds-controller', 28 | } 29 | for v in versions 30 | ] 31 | ) 32 | -------------------------------------------------------------------------------- /libs/banzai-logging/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local versions = [ 4 | { output: '3.17.10', version: '3.17.10' }, 5 | ]; 6 | 7 | local crds = [ 8 | 'logging.banzaicloud.io_flows.yaml', 9 | 'logging.banzaicloud.io_clusterflows.yaml', 10 | 'logging.banzaicloud.io_clusteroutputs.yaml', 11 | 'logging.banzaicloud.io_outputs.yaml', 12 | ]; 13 | 14 | config.new( 15 | name='banzai-logging', 16 | specs=[ 17 | { 18 | output: v.output, 19 | prefix: '^io\\.banzaicloud\\.logging\\..*', 20 | crds: ['https://raw.githubusercontent.com/banzaicloud/logging-operator/chart/logging-operator-logging/%s/charts/logging-operator/crds/%s' % [v.version, c] 21 | for c in crds], 22 | localName: 'banzai-logging', 23 | } 24 | for v in versions 25 | ] 26 | ) 27 | -------------------------------------------------------------------------------- /libs/banzaicloud-bank-vaults/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | local versions = [ 3 | { version: '1.17', tag: 'v1.17.0'}, 4 | { version: '1.16', tag: 'v1.16.0'}, 5 | { version: '1.15', tag: 'v1.15.3'}, 6 | ]; 7 | 8 | config.new( 9 | name='banzaicloud-bank-vaults', 10 | specs=[ 11 | { 12 | output: v.version, 13 | prefix: '^com\\.banzaicloud\\.vault\\..*', 14 | crds: ['https://raw.githubusercontent.com/'+v.tag+'/charts/vault-operator/crds/crd.yaml'], 15 | localName: 'banzaicloud_bank_vaults', 16 | } 17 | for v in versions 18 | ] 19 | ) 20 | -------------------------------------------------------------------------------- /libs/calico/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local legacy_versions = [ 4 | ['3.24', '3.24.1'], 5 | ['3.25', '3.25.0'], 6 | ]; 7 | 8 | local legacy_path = 'https://raw.githubusercontent.com/projectcalico/calico/v%s/_includes/charts/calico/crds/kdd/'; 9 | 10 | local versions = [ 11 | ['3.28', '3.28.1'], 12 | ]; 13 | 14 | // The files in new versions were moved here: 15 | local path = 'https://raw.githubusercontent.com/projectcalico/calico/v%s/libcalico-go/config/crd/'; 16 | 17 | config.new( 18 | name='calico', 19 | specs=[ 20 | { 21 | output: version[0], 22 | prefix: '^org\\.projectcalico\\.crd\\..*', 23 | crds: [ 24 | (path % version[1]) + 'crd.projectcalico.org_bgpconfigurations.yaml', 25 | (path % version[1]) + 'crd.projectcalico.org_bgpfilters.yaml', 26 | (path % version[1]) + 'crd.projectcalico.org_bgppeers.yaml', 27 | (path % version[1]) + 'crd.projectcalico.org_blockaffinities.yaml', 28 | (path % version[1]) + 'crd.projectcalico.org_caliconodestatuses.yaml', 29 | (path % version[1]) + 'crd.projectcalico.org_clusterinformations.yaml', 30 | (path % version[1]) + 'crd.projectcalico.org_felixconfigurations.yaml', 31 | (path % version[1]) + 'crd.projectcalico.org_globalnetworkpolicies.yaml', 32 | (path % version[1]) + 'crd.projectcalico.org_globalnetworksets.yaml', 33 | (path % version[1]) + 'crd.projectcalico.org_hostendpoints.yaml', 34 | (path % version[1]) + 'crd.projectcalico.org_ipamblocks.yaml', 35 | (path % version[1]) + 'crd.projectcalico.org_ipamconfigs.yaml', 36 | (path % version[1]) + 'crd.projectcalico.org_ipamhandles.yaml', 37 | (path % version[1]) + 'crd.projectcalico.org_ipreservations.yaml', 38 | // IPPools resource is currently not included because the CRD breaks jsonnet by having a `-` within the deprecated key name `nat-outgoing`. 39 | // Once this has been removed, this file can be included and the comment removed. 40 | // https://github.com/projectcalico/calico/issues/6638 41 | // (path % version[1]) + 'crd.projectcalico.org_ippools.yaml', 42 | (path % version[1]) + 'crd.projectcalico.org_kubecontrollersconfigurations.yaml', 43 | (path % version[1]) + 'crd.projectcalico.org_networkpolicies.yaml', 44 | (path % version[1]) + 'crd.projectcalico.org_networksets.yaml', 45 | ], 46 | localName: 'calico', 47 | } 48 | for version in versions 49 | ] + [ 50 | { 51 | output: version[0], 52 | prefix: '^org\\.projectcalico\\.crd\\..*', 53 | crds: [ 54 | (legacy_path % version[1]) + 'crd.projectcalico.org_bgpconfigurations.yaml', 55 | (legacy_path % version[1]) + 'crd.projectcalico.org_bgppeers.yaml', 56 | (legacy_path % version[1]) + 'crd.projectcalico.org_blockaffinities.yaml', 57 | (legacy_path % version[1]) + 'crd.projectcalico.org_clusterinformations.yaml', 58 | (legacy_path % version[1]) + 'crd.projectcalico.org_felixconfigurations.yaml', 59 | (legacy_path % version[1]) + 'crd.projectcalico.org_globalnetworkpolicies.yaml', 60 | (legacy_path % version[1]) + 'crd.projectcalico.org_globalnetworksets.yaml', 61 | (legacy_path % version[1]) + 'crd.projectcalico.org_hostendpoints.yaml', 62 | (legacy_path % version[1]) + 'crd.projectcalico.org_ipamblocks.yaml', 63 | (legacy_path % version[1]) + 'crd.projectcalico.org_ipamconfigs.yaml', 64 | (legacy_path % version[1]) + 'crd.projectcalico.org_ipamhandles.yaml', 65 | // IPPools resource is currently not included because the CRD breaks jsonnet by having a `-` within the deprecated key name `nat-outgoing`. 66 | // Once this has been removed, this file can be included and the comment removed. 67 | // (legacy_path % version[1]) + 'crd.projectcalico.org_ippools.yaml', 68 | (legacy_path % version[1]) + 'crd.projectcalico.org_kubecontrollersconfigurations.yaml', 69 | (legacy_path % version[1]) + 'crd.projectcalico.org_networkpolicies.yaml', 70 | (legacy_path % version[1]) + 'crd.projectcalico.org_networksets.yaml', 71 | ], 72 | localName: 'calico', 73 | } 74 | for version in legacy_versions 75 | ] 76 | ) 77 | -------------------------------------------------------------------------------- /libs/cert-manager/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | local versions = [ 3 | { version: '1.15', tag: 'v1.15.3' }, 4 | { version: '1.14', tag: 'v1.14.2' }, 5 | { version: '1.13', tag: 'v1.13.3' }, 6 | { version: '1.12', tag: 'v1.12.7' }, 7 | { version: '1.11', tag: 'v1.11.5' }, 8 | { version: '1.10', tag: 'v1.10.2' }, 9 | { version: '1.9', tag: 'v1.9.2' }, 10 | { version: '1.8', tag: 'v1.8.2' }, 11 | { version: '1.7', tag: 'v1.7.3' }, 12 | { version: '1.6', tag: 'v1.6.3' }, 13 | { version: '1.5', tag: 'v1.5.5' }, 14 | { version: '1.4', tag: 'v1.4.4' }, 15 | { version: '1.3', tag: 'v1.3.3' }, 16 | ]; 17 | 18 | config.new( 19 | name='cert-manager', 20 | specs=[ 21 | { 22 | output: v.version, 23 | prefix: '^io\\.cert-manager\\..*', 24 | crds: ['https://github.com/cert-manager/cert-manager/releases/download/' + v.tag + '/cert-manager.crds.yaml'], 25 | localName: 'cert_manager', 26 | } 27 | for v in versions 28 | ] 29 | ) 30 | -------------------------------------------------------------------------------- /libs/cilium/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | local versions = [ 3 | { version: '1.12', tag: 'v1.12.19' }, 4 | { version: '1.13', tag: 'v1.13.12' }, 5 | { version: '1.14', tag: 'v1.14.7' }, 6 | { version: '1.15', tag: 'v1.15.1' }, 7 | { version: '1.16', tag: 'v1.16.1' }, 8 | ]; 9 | 10 | local pathPrefix = 'https://raw.githubusercontent.com/cilium/cilium/%s/pkg/k8s/apis/cilium.io/client/crds/v2/'; 11 | local crdFiles = [ 12 | 'ciliumclusterwideenvoyconfigs.yaml', 13 | 'ciliumclusterwidenetworkpolicies.yaml', 14 | 'ciliumegressgatewaypolicies.yaml', 15 | 'ciliumendpoints.yaml', 16 | 'ciliumenvoyconfigs.yaml', 17 | 'ciliumexternalworkloads.yaml', 18 | 'ciliumidentities.yaml', 19 | 'ciliumlocalredirectpolicies.yaml', 20 | 'ciliumnetworkpolicies.yaml', 21 | 'ciliumnodeconfigs.yaml', 22 | 'ciliumnodes.yaml', 23 | ]; 24 | 25 | config.new( 26 | name='cilium', 27 | specs=[ 28 | { 29 | output: v.version, 30 | prefix: '^io\\.cilium\\..*', 31 | crds: [ 32 | (pathPrefix % v.tag) + crdFile 33 | for crdFile in crdFiles 34 | ], 35 | localName: 'cilium', 36 | } 37 | for v in versions 38 | ] 39 | ) 40 | -------------------------------------------------------------------------------- /libs/clickhouse-operator/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | local versions = [ 3 | { output: '0.23', version: 'release-0.23.5' }, 4 | { output: '0.22', version: 'release-0.22.2' }, 5 | { output: '0.21', version: 'release-0.21.3' }, 6 | ]; 7 | 8 | config.new( 9 | name='clickhouse-operator', 10 | specs=[ 11 | { 12 | local url = 'https://raw.githubusercontent.com/Altinity/clickhouse-operator/%s/deploy/operator/parts' % v.version, 13 | output: v.output, 14 | prefix: '^com\\.altinity\\.clickhouse\\..*', 15 | crds: [ 16 | '%s/crd.yaml' % url, 17 | ], 18 | localName: 'clickhouse-operator', 19 | } 20 | for v in versions 21 | ] 22 | ) 23 | -------------------------------------------------------------------------------- /libs/cloudnative-pg/config.jsonnet: -------------------------------------------------------------------------------- 1 | // libs/cloudnative-pg/config.jsonnet 2 | 3 | local config = import 'jsonnet/config.jsonnet'; 4 | 5 | local versions = [ 6 | { version: '1.25.0' }, // released on 23 DEC 2024 7 | { version: '1.24.2' }, // released on 23 DEC 2024 8 | { version: '1.23.6' }, // released on 23 DEC, 2024 9 | ]; 10 | 11 | config.new( 12 | name='cloudnative-pg', 13 | specs=[ 14 | { 15 | local url = 'https://raw.githubusercontent.com/cloudnative-pg/cloudnative-pg/main/config/crd/bases/', 16 | output: v.version, 17 | crds: 18 | [ 19 | '%s/postgresql.cnpg.io_backups.yaml' % url, 20 | '%s/postgresql.cnpg.io_clusterimagecatalogs.yaml' % url, 21 | '%s/postgresql.cnpg.io_clusters.yaml' % url, 22 | '%s/postgresql.cnpg.io_imagecatalogs.yaml' % url, 23 | '%s/postgresql.cnpg.io_poolers.yaml' % url, 24 | '%s/postgresql.cnpg.io_scheduledbackups.yaml' % url, 25 | ] 26 | + ( 27 | if (v.version == '1.25.0') 28 | then [ 29 | '%s/postgresql.cnpg.io_databases.yaml' % url, 30 | '%s/postgresql.cnpg.io_publications.yaml' % url, 31 | '%s/postgresql.cnpg.io_subscriptions.yaml' % url, 32 | ] 33 | else [] 34 | ), 35 | prefix: '^io\\.cnpg\\.postgresql\\..*', 36 | localName: 'cloudnative-pg', 37 | } 38 | for v in versions 39 | ] 40 | ) 41 | -------------------------------------------------------------------------------- /libs/cluster-api-provider-aws/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local versions = [ 4 | { version: '1.2', tag: 'v1.2.0'}, 5 | { version: '1.3', tag: 'v1.3.0'}, 6 | { version: '1.4', tag: 'v1.4.1'}, 7 | { version: '1.5', tag: 'v1.5.2'}, 8 | { version: '2.0', tag: 'v2.0.2'}, 9 | ]; 10 | 11 | config.new( 12 | name='cluster-api-provider-aws', 13 | specs=[ 14 | { 15 | output: v.version, 16 | openapi: 'http://localhost:8001/openapi/v2', 17 | prefix: '^io\\.x-k8s\\.cluster\\..*', 18 | crds: [ 19 | 'https://github.com/kubernetes-sigs/cluster-api-provider-aws/releases/download/%s/infrastructure-components.yaml' % v.tag 20 | ], 21 | localName: 'cluster-api-provider-aws', 22 | } 23 | for v in versions 24 | ] 25 | ) 26 | -------------------------------------------------------------------------------- /libs/cluster-api/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | config.new( 4 | name='cluster-api', 5 | specs=[ 6 | { 7 | output: 'v1.0.2', 8 | openapi: 'http://localhost:8001/openapi/v2', 9 | prefix: '^io\\.x-k8s\\.cluster\\..*', 10 | crds: ['https://github.com/kubernetes-sigs/cluster-api/releases/download/v1.0.2/core-components.yaml'], 11 | localName: 'cluster-api', 12 | }, 13 | ] 14 | ) 15 | -------------------------------------------------------------------------------- /libs/cnrm/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | local versions = [ 3 | ['1.74', '1.74.0'], 4 | ['1.82', '1.82.0'], 5 | ['1.93', '1.93.0'], 6 | ['1.99', '1.99.0'], 7 | ['1.130', '1.130.2'], 8 | ]; 9 | 10 | 11 | config.new( 12 | name='cnrm', 13 | specs=[ 14 | { 15 | output: version[0], 16 | prefix: '^com\\.google\\.cloud\\.cnrm\\..*', 17 | crds: ['https://raw.githubusercontent.com/GoogleCloudPlatform/k8s-config-connector/v' + version[1] + '/install-bundles/install-bundle-workload-identity/crds.yaml'], 18 | localName: 'cnrm', 19 | } 20 | for version in versions 21 | ], 22 | ) 23 | -------------------------------------------------------------------------------- /libs/composable/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | config.new( 4 | name='composable', 5 | specs=[ 6 | { 7 | output: '0.2.0', 8 | prefix: '^com\\.ibm\\.ibmcloud\\..*', 9 | crds: ['https://github.com/ankorstore/composable/releases/download/v0.2.0/crds.yaml'], 10 | localName: 'composable', 11 | patchDir: 'custom/helpers', 12 | }, 13 | ], 14 | ) 15 | -------------------------------------------------------------------------------- /libs/composable/custom/helpers/ibmcloud.libsonnet: -------------------------------------------------------------------------------- 1 | { 2 | ibmcloud+:: { 3 | v1alpha1+: { 4 | // Fieldrefs 5 | composableGetValueFrom+: { 6 | new(kind, name, path): 7 | { getValueFrom: {} } 8 | + self.withKind(kind) 9 | + self.withName(name) 10 | + self.withPath(path), 11 | 12 | // Motifiers 13 | withKind(kind): { getValueFrom+: { kind: kind } }, 14 | withName(name): { getValueFrom+: { name: name } }, 15 | withNamespace(namespace): { getValueFrom+: { namespace: namespace } }, 16 | withApiVersion(apiversion): { getValueFrom+: { apiVersion: apiversion } }, 17 | withLabels(labels): { getValueFrom+: { labels: labels } }, 18 | withLabelsMixin(labels): { getValueFrom+: { labels+: labels } }, 19 | withPath(path): { getValueFrom+: { path: path } }, 20 | withFormatTransformers(transformers): { getValueFrom+: { formatTransformers: transformers } }, 21 | withFormatTransformersMixin(transformers): { getValueFrom+: { formatTransformers+: transformers } }, 22 | }, 23 | 24 | // Transformers 25 | composableTransformer+: { 26 | toString(): 'ToString', 27 | arrayToCommaSeparatedString(): 'ArrayToCSString', 28 | base64ToString(): 'Base64ToString', 29 | stringToBase64(): 'StringToBase64', 30 | stringToInt(): 'StringToInt', 31 | stringToFloat(): 'StringToFloat', 32 | stringToBool(): 'StringToBool', 33 | jsonToObject(): 'JsonToOject', 34 | objectToJson(): 'ObjectToJson', 35 | }, 36 | }, 37 | }, 38 | } 39 | -------------------------------------------------------------------------------- /libs/consul/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local old_versions = [ 4 | { output: '0.34', version: '0.34.1' }, 5 | ]; 6 | 7 | local versions = [ 8 | { output: '1.3', version: '1.3.1' }, 9 | ]; 10 | 11 | config.new( 12 | name='consul', 13 | specs=[ 14 | { 15 | local url = 'https://raw.githubusercontent.com/hashicorp/consul-k8s/v%s/control-plane/config/crd/bases' % v.version, 16 | output: v.output, 17 | prefix: '^com\\.hashicorp\\.consul\\..*', 18 | crds: [ 19 | '%s/consul.hashicorp.com_ingressgateways.yaml' % url, 20 | '%s/consul.hashicorp.com_meshes.yaml' % url, 21 | '%s/consul.hashicorp.com_proxydefaults.yaml' % url, 22 | '%s/consul.hashicorp.com_servicedefaults.yaml' % url, 23 | '%s/consul.hashicorp.com_serviceintentions.yaml' % url, 24 | '%s/consul.hashicorp.com_serviceresolvers.yaml' % url, 25 | '%s/consul.hashicorp.com_servicerouters.yaml' % url, 26 | '%s/consul.hashicorp.com_servicesplitters.yaml' % url, 27 | '%s/consul.hashicorp.com_terminatinggateways.yaml' % url, 28 | ], 29 | localName: 'consul', 30 | } 31 | for v in old_versions 32 | ] + [ 33 | { 34 | local url = 'https://raw.githubusercontent.com/hashicorp/consul-k8s/v%s/control-plane/config/crd/bases' % v.version, 35 | output: v.output, 36 | prefix: '^com\\.hashicorp\\.consul\\..*', 37 | crds: [ 38 | '%s/consul.hashicorp.com_controlplanerequestlimits.yaml' % url, 39 | '%s/consul.hashicorp.com_exportedservices.yaml' % url, 40 | '%s/consul.hashicorp.com_gatewayclassconfigs.yaml' % url, 41 | '%s/consul.hashicorp.com_gatewaypolicies.yaml' % url, 42 | '%s/consul.hashicorp.com_ingressgateways.yaml' % url, 43 | '%s/consul.hashicorp.com_jwtproviders.yaml' % url, 44 | '%s/consul.hashicorp.com_meshes.yaml' % url, 45 | '%s/consul.hashicorp.com_meshservices.yaml' % url, 46 | '%s/consul.hashicorp.com_peeringacceptors.yaml' % url, 47 | '%s/consul.hashicorp.com_peeringdialers.yaml' % url, 48 | '%s/consul.hashicorp.com_proxydefaults.yaml' % url, 49 | '%s/consul.hashicorp.com_routeauthfilters.yaml' % url, 50 | '%s/consul.hashicorp.com_routeretryfilters.yaml' % url, 51 | '%s/consul.hashicorp.com_routetimeoutfilters.yaml' % url, 52 | '%s/consul.hashicorp.com_samenessgroups.yaml' % url, 53 | '%s/consul.hashicorp.com_servicedefaults.yaml' % url, 54 | '%s/consul.hashicorp.com_serviceintentions.yaml' % url, 55 | '%s/consul.hashicorp.com_serviceresolvers.yaml' % url, 56 | '%s/consul.hashicorp.com_servicerouters.yaml' % url, 57 | '%s/consul.hashicorp.com_servicesplitters.yaml' % url, 58 | '%s/consul.hashicorp.com_terminatinggateways.yaml' % url, 59 | ], 60 | localName: 'consul', 61 | } 62 | for v in versions 63 | ] 64 | ) 65 | -------------------------------------------------------------------------------- /libs/contour/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | local versions = [ 3 | { version: '1.30', tag: 'v1.30.0'}, 4 | { version: '1.29', tag: 'v1.29.0'}, 5 | { version: '1.28', tag: 'v1.28.1'}, 6 | { version: '1.27', tag: 'v1.27.1'}, 7 | { version: '1.26', tag: 'v1.26.2'}, 8 | ]; 9 | 10 | config.new( 11 | name='contour', 12 | specs=[ 13 | { 14 | output: v.version, 15 | prefix: '^io\\.projectcontour\\..*', 16 | crds: ['https://raw.githubusercontent.com/projectcontour/contour/%(tag)s/examples/contour/01-crds.yaml' % v], 17 | localName: 'contour', 18 | } 19 | for v in versions 20 | ] 21 | ) 22 | -------------------------------------------------------------------------------- /libs/crossplane-core/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | config.new( 4 | name='crossplane-core', 5 | specs=[ 6 | // Crossplane itself 7 | // Release support table: https://github.com/crossplane/crossplane#releases 8 | { 9 | output: 'crossplane/1.18', 10 | prefix: '^io\\.crossplane\\.(pkg|apiextensions)\\..*', 11 | crds: ['https://doc.crds.dev/raw/github.com/crossplane/crossplane@v1.18.0'], 12 | localName: 'crossplane', 13 | patchDir: 'custom/crossplane', 14 | }, 15 | 16 | // Common functions 17 | { 18 | output: 'function-patch-and-transform/0.7', 19 | prefix: '^io\\.crossplane\\.fn\\.pt\\..*', 20 | crds: ['https://raw.githubusercontent.com/crossplane-contrib/function-patch-and-transform/refs/tags/v0.7.0/package/input/pt.fn.crossplane.io_resources.yaml'], 21 | localName: 'function_patch_and_transform', 22 | }, 23 | { 24 | output: 'function-cel-filter/0.1', 25 | prefix: '^io\\.crossplane\\.fn\\.cel\\..*', 26 | crds: ['https://raw.githubusercontent.com/crossplane-contrib/function-cel-filter/refs/tags/v0.1.1/package/input/cel.fn.crossplane.io_filters.yaml'], 27 | localName: 'function_cel_filter', 28 | }, 29 | { 30 | output: 'function-status-transformer/0.4', 31 | prefix: '^io\\.crossplane\\.fn\\.function-status-transformer\\..*', 32 | crds: ['https://raw.githubusercontent.com/crossplane-contrib/function-status-transformer/refs/tags/v0.4.0/package/input/function-status-transformer.fn.crossplane.io_statustransformations.yaml'], 33 | localName: 'function_status_transformer', 34 | }, 35 | { 36 | output: 'function-go-templating/0.9', 37 | prefix: '^io\\.crossplane\\.fn\\.gotemplating\\..*', 38 | crds: ['https://raw.githubusercontent.com/crossplane-contrib/function-go-templating/refs/tags/v0.9.1/package/input/gotemplating.fn.crossplane.io_gotemplates.yaml'], 39 | localName: 'function_go_templating', 40 | }, 41 | ] 42 | ) 43 | -------------------------------------------------------------------------------- /libs/crossplane-core/custom/crossplane/compositeResourceDefinition.libsonnet: -------------------------------------------------------------------------------- 1 | local d = import 'doc-util/main.libsonnet'; 2 | 3 | { 4 | apiextensions+: { 5 | v1+: { 6 | xrd: self.compositeResourceDefinition, 7 | compositeResourceDefinition+: { 8 | 9 | '#new':: d.fn(help=||| 10 | new returns an instance of CompositeResourceDefinition= 11 | 12 | For example: xpostgresqlinstances.example.org 13 | 14 | - `kind`: XPostgreSQLInstance 15 | - `plural`: xpostgresqlinstances 16 | - `group`: example.org 17 | 18 | A common convention is that the XR (composite resource) are prefixed with 'X' 19 | while claim names are not. This lets app team members think of creating a claim 20 | as (e.g.) 'creating a PostgreSQLInstance'. Use `withClaimNames` to set this. 21 | |||, args=[ 22 | d.arg('kind', d.T.string), 23 | d.arg('plural', d.T.string), 24 | d.arg('group', d.T.string), 25 | ]), 26 | new(kind, plural, group): 27 | super.new(plural + '.' + group) 28 | + super.metadata.withAnnotations({ 29 | // Tell Tanka to not set metadata.namespace. 30 | 'tanka.dev/namespaced': 'false', 31 | }) 32 | + super.spec.withGroup(group) 33 | + super.spec.names.withKind(kind) 34 | + super.spec.names.withPlural(plural) 35 | , 36 | 37 | '#withClaimNames':: d.fn(help=||| 38 | Sets the ClaimNames attribute. 39 | 40 | Example: 41 | - `kind`: PostgreSQLInstance 42 | - `plural`: postgresqlinstances 43 | 44 | A common convention is that the XR (composite resource) are prefixed with 'X' 45 | while claim names are not. This lets app team members think of creating a claim 46 | as (e.g.) 'creating a PostgreSQLInstance'. 47 | |||, args=[ 48 | d.arg('kind', d.T.string), 49 | d.arg('plural', d.T.string), 50 | ]), 51 | withClaimNames(kind, plural): 52 | super.spec.claimNames.withKind(kind) 53 | + super.spec.claimNames.withPlural(plural), 54 | 55 | '#mapVersions':: d.fn(help=||| 56 | Sets the ClaimNames attribute. 57 | 58 | Example: 59 | - `kind`: PostgreSQLInstance 60 | - `plural`: postgresqlinstances 61 | 62 | A common convention is that the XR (composite resource) are prefixed with 'X' 63 | while claim names are not. This lets app team members think of creating a claim 64 | as (e.g.) 'creating a PostgreSQLInstance'. 65 | |||, args=[ 66 | d.arg('kind', d.T.string), 67 | d.arg('plural', d.T.string), 68 | ]), 69 | mapVersions(f): { 70 | local versions = super.spec.versions, 71 | spec+: { 72 | versions: std.map(f, versions), 73 | }, 74 | }, 75 | }, 76 | }, 77 | }, 78 | } 79 | -------------------------------------------------------------------------------- /libs/crossplane-core/custom/crossplane/composition.libsonnet: -------------------------------------------------------------------------------- 1 | local d = import 'doc-util/main.libsonnet'; 2 | 3 | { 4 | apiextensions+: { 5 | v1+: { 6 | composition+: { 7 | '#fromXRD':: d.fn(help=||| 8 | Create a Composition based on an XRD. 9 | 10 | Attributes: 11 | - `name` of the composition 12 | - `namespace` where connectionDetails are propagated too, commonly the the 13 | management namespace (ie. crossplane) 14 | - `provider` of the resources in this composition 15 | - `xrdRef` XRD object with which this composition is compatible 16 | - `xrdVersion` Version of XRD object with which this composition is compatible 17 | |||, args=[ 18 | d.arg('name', d.T.string), 19 | d.arg('namespace', d.T.string), 20 | d.arg('provider', d.T.string), 21 | d.arg('xrdRef', d.T.object), 22 | d.arg('xrdVersion', d.T.string), 23 | ]), 24 | fromXRD(name, namespace, provider, xrdRef, xrdVersion): 25 | super.new(name) 26 | + super.metadata.withAnnotations({ 27 | // Tell Tanka to not set metadata.namespace. 28 | 'tanka.dev/namespaced': 'false', 29 | }) 30 | + super.metadata.withLabels({ 31 | // An optional convention is to include a label of the XRD. This allows easy 32 | // discovery of compatible Compositions. 33 | 'crossplane.io/xrd': xrdRef.metadata.name, 34 | // Another optional convention is to include a label of the (most common) 35 | // provider for the resource(s) in this composition. This label can be used in 36 | // 'compositionSelector' in an XR or Claim. 37 | provider: provider, 38 | }) 39 | 40 | // Each Composition must declare that it is compatible with a particular type of 41 | // Composite Resource using its 'compositeTypeRef' field. The referenced 42 | // version must be marked 'referenceable' in the XRD that defines the XR. 43 | + super.spec.compositeTypeRef.withApiVersion(xrdRef.spec.group + '/' + xrdVersion) 44 | + super.spec.compositeTypeRef.withKind(xrdRef.spec.names.kind) 45 | 46 | 47 | // When an XR is created in response to a claim Crossplane needs to know where it 48 | // should create the XR's connection secret. This is configured using the 49 | // 'writeConnectionSecretsToNamespace' field. 50 | + super.spec.withWriteConnectionSecretsToNamespace(namespace), 51 | }, 52 | }, 53 | }, 54 | } 55 | -------------------------------------------------------------------------------- /libs/crossplane/Makefile: -------------------------------------------------------------------------------- 1 | ROOT_DIR:=$(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) 2 | TMP:=$(shell mktemp -d) 3 | AWS_VERSION?=v1.14.0 4 | 5 | .PHONY: upbound_aws_crds.libsonnet 6 | upbound_aws_crds.libsonnet: 7 | cd $(TMP) && \ 8 | jb init && \ 9 | jb install github.com/crossplane-contrib/provider-upjet-aws/package/crds@$(AWS_VERSION) && \ 10 | echo '[' > $(ROOT_DIR)/upbound_aws_crds.libsonnet && \ 11 | cd vendor/github.com/crossplane-contrib/provider-upjet-aws/package/crds && \ 12 | find . -type f -printf "%f\n" | sort | xargs -I {} echo "'{}'," >> $(ROOT_DIR)/upbound_aws_crds.libsonnet && \ 13 | echo ']' >> $(ROOT_DIR)/upbound_aws_crds.libsonnet && \ 14 | jsonnetfmt -i $(ROOT_DIR)/upbound_aws_crds.libsonnet && \ 15 | rm -rf $(TMP) || \ 16 | rm -rf $(TMP) 17 | 18 | 19 | GCP_VERSION?=v1.8.3 20 | 21 | .PHONY: upbound_gcp_crds.libsonnet 22 | upbound_gcp_crds.libsonnet: 23 | cd $(TMP) && \ 24 | jb init && \ 25 | jb install github.com/crossplane-contrib/provider-upjet-gcp/package/crds@$(GCP_VERSION) && \ 26 | echo '[' > $(ROOT_DIR)/upbound_gcp_crds.libsonnet && \ 27 | cd vendor/github.com/crossplane-contrib/provider-upjet-gcp/package/crds && \ 28 | find . -type f -printf "%f\n" | sort | xargs -I {} echo "'{}'," >> $(ROOT_DIR)/upbound_gcp_crds.libsonnet && \ 29 | echo ']' >> $(ROOT_DIR)/upbound_gcp_crds.libsonnet && \ 30 | jsonnetfmt -i $(ROOT_DIR)/upbound_gcp_crds.libsonnet && \ 31 | rm -rf $(TMP) || \ 32 | rm -rf $(TMP) 33 | 34 | 35 | AZURE_VERSION?=v1.3.0 36 | 37 | .PHONY: upbound_azure_crds.libsonnet 38 | upbound_azure_crds.libsonnet: 39 | cd $(TMP) && \ 40 | jb init && \ 41 | jb install github.com/crossplane-contrib/provider-upjet-azure/package/crds@$(AZURE_VERSION) && \ 42 | echo '[' > $(ROOT_DIR)/upbound_azure_crds.libsonnet && \ 43 | cd vendor/github.com/crossplane-contrib/provider-upjet-azure/package/crds && \ 44 | find . -type f -printf "%f\n" | sort | xargs -I {} echo "'{}'," >> $(ROOT_DIR)/upbound_azure_crds.libsonnet && \ 45 | echo ']' >> $(ROOT_DIR)/upbound_azure_crds.libsonnet && \ 46 | jsonnetfmt -i $(ROOT_DIR)/upbound_azure_crds.libsonnet && \ 47 | rm -rf $(TMP) || \ 48 | rm -rf $(TMP) 49 | -------------------------------------------------------------------------------- /libs/crossplane/custom/crossplane/compositeResourceDefinition.libsonnet: -------------------------------------------------------------------------------- 1 | local d = import 'doc-util/main.libsonnet'; 2 | 3 | { 4 | apiextensions+: { 5 | v1+: { 6 | xrd: self.compositeResourceDefinition, 7 | compositeResourceDefinition+: { 8 | 9 | '#new':: d.fn(help=||| 10 | new returns an instance of CompositeResourceDefinition= 11 | 12 | For example: xpostgresqlinstances.example.org 13 | 14 | - `kind`: XPostgreSQLInstance 15 | - `plural`: xpostgresqlinstances 16 | - `group`: example.org 17 | 18 | A common convention is that the XR (composite resource) are prefixed with 'X' 19 | while claim names are not. This lets app team members think of creating a claim 20 | as (e.g.) 'creating a PostgreSQLInstance'. Use `withClaimNames` to set this. 21 | |||, args=[ 22 | d.arg('kind', d.T.string), 23 | d.arg('plural', d.T.string), 24 | d.arg('group', d.T.string), 25 | ]), 26 | new(kind, plural, group): 27 | super.new(plural + '.' + group) 28 | + super.metadata.withAnnotations({ 29 | // Tell Tanka to not set metadata.namespace. 30 | 'tanka.dev/namespaced': 'false', 31 | }) 32 | + super.spec.withGroup(group) 33 | + super.spec.names.withKind(kind) 34 | + super.spec.names.withPlural(plural) 35 | , 36 | 37 | '#withClaimNames':: d.fn(help=||| 38 | Sets the ClaimNames attribute. 39 | 40 | Example: 41 | - `kind`: PostgreSQLInstance 42 | - `plural`: postgresqlinstances 43 | 44 | A common convention is that the XR (composite resource) are prefixed with 'X' 45 | while claim names are not. This lets app team members think of creating a claim 46 | as (e.g.) 'creating a PostgreSQLInstance'. 47 | |||, args=[ 48 | d.arg('kind', d.T.string), 49 | d.arg('plural', d.T.string), 50 | ]), 51 | withClaimNames(kind, plural): 52 | super.spec.claimNames.withKind(kind) 53 | + super.spec.claimNames.withPlural(plural), 54 | 55 | '#mapVersions':: d.fn(help=||| 56 | Sets the ClaimNames attribute. 57 | 58 | Example: 59 | - `kind`: PostgreSQLInstance 60 | - `plural`: postgresqlinstances 61 | 62 | A common convention is that the XR (composite resource) are prefixed with 'X' 63 | while claim names are not. This lets app team members think of creating a claim 64 | as (e.g.) 'creating a PostgreSQLInstance'. 65 | |||, args=[ 66 | d.arg('kind', d.T.string), 67 | d.arg('plural', d.T.string), 68 | ]), 69 | mapVersions(f): { 70 | local versions = super.spec.versions, 71 | spec+: { 72 | versions: std.map(f, versions), 73 | }, 74 | }, 75 | }, 76 | }, 77 | }, 78 | } 79 | -------------------------------------------------------------------------------- /libs/crossplane/custom/crossplane/composition.libsonnet: -------------------------------------------------------------------------------- 1 | local d = import 'doc-util/main.libsonnet'; 2 | 3 | { 4 | apiextensions+: { 5 | v1+: { 6 | composition+: { 7 | '#fromXRD':: d.fn(help=||| 8 | Create a Composition based on an XRD. 9 | 10 | Attributes: 11 | - `name` of the composition 12 | - `namespace` where connectionDetails are propagated too, commonly the the 13 | management namespace (ie. crossplane) 14 | - `provider` of the resources in this composition 15 | - `xrdRef` XRD object with which this composition is compatible 16 | - `xrdVersion` Version of XRD object with which this composition is compatible 17 | |||, args=[ 18 | d.arg('name', d.T.string), 19 | d.arg('namespace', d.T.string), 20 | d.arg('provider', d.T.string), 21 | d.arg('xrdRef', d.T.object), 22 | d.arg('xrdVersion', d.T.string), 23 | ]), 24 | fromXRD(name, namespace, provider, xrdRef, xrdVersion): 25 | super.new(name) 26 | + super.metadata.withAnnotations({ 27 | // Tell Tanka to not set metadata.namespace. 28 | 'tanka.dev/namespaced': 'false', 29 | }) 30 | + super.metadata.withLabels({ 31 | // An optional convention is to include a label of the XRD. This allows easy 32 | // discovery of compatible Compositions. 33 | 'crossplane.io/xrd': xrdRef.metadata.name, 34 | // Another optional convention is to include a label of the (most common) 35 | // provider for the resource(s) in this composition. This label can be used in 36 | // 'compositionSelector' in an XR or Claim. 37 | provider: provider, 38 | }) 39 | 40 | // Each Composition must declare that it is compatible with a particular type of 41 | // Composite Resource using its 'compositeTypeRef' field. The referenced 42 | // version must be marked 'referenceable' in the XRD that defines the XR. 43 | + super.spec.compositeTypeRef.withApiVersion(xrdRef.spec.group + '/' + xrdVersion) 44 | + super.spec.compositeTypeRef.withKind(xrdRef.spec.names.kind) 45 | 46 | 47 | // When an XR is created in response to a claim Crossplane needs to know where it 48 | // should create the XR's connection secret. This is configured using the 49 | // 'writeConnectionSecretsToNamespace' field. 50 | + super.spec.withWriteConnectionSecretsToNamespace(namespace), 51 | }, 52 | }, 53 | }, 54 | } 55 | -------------------------------------------------------------------------------- /libs/datadog-operator/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local versions = [ 4 | { version: '1.6.0', tag: 'v1.6.0' }, 5 | { version: '1.7.0', tag: 'v1.7.0' }, 6 | { version: '1.8.0', tag: 'v1.8.0' }, 7 | { version: '1.9.0', tag: 'v1.9.0' }, 8 | ]; 9 | 10 | config.new( 11 | name='datadog-operator', 12 | specs=[ 13 | { 14 | output: v.version, 15 | prefix: '^com\\.datadoghq\\..*', 16 | crds: [ 17 | 'https://raw.githubusercontent.com/DataDog/datadog-operator/%s/config/crd/bases/v1/datadoghq.com_datadogagentprofiles.yaml' % [v.tag], 18 | 'https://raw.githubusercontent.com/DataDog/datadog-operator/%s/config/crd/bases/v1/datadoghq.com_datadogagents.yaml' % [v.tag], 19 | 'https://raw.githubusercontent.com/DataDog/datadog-operator/%s/config/crd/bases/v1/datadoghq.com_datadogmetrics.yaml' % [v.tag], 20 | 'https://raw.githubusercontent.com/DataDog/datadog-operator/%s/config/crd/bases/v1/datadoghq.com_datadogmonitors.yaml' % [v.tag], 21 | 'https://raw.githubusercontent.com/DataDog/datadog-operator/%s/config/crd/bases/v1/datadoghq.com_datadogslos.yaml' % [v.tag], 22 | 'https://raw.githubusercontent.com/DataDog/datadog-operator/%s/config/crd/bases/v1/datadoghq.com_datadogpodautoscalers.yaml' % [v.tag], // new in v1.8.0 but we can do it this lazy way because the k8s build will silently succeed regardless of remote http code 23 | 'https://raw.githubusercontent.com/DataDog/datadog-operator/%s/config/crd/bases/v1/datadoghq.com_datadogdashboards.yaml' % [v.tag], 24 | 25 | ], 26 | localName: 'datadog-operator', 27 | } 28 | for v in versions 29 | ] 30 | ) 31 | -------------------------------------------------------------------------------- /libs/eck-operator/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local versions = [ 4 | { version: '2.9', tag: 'v2.9.0' }, 5 | { version: '2.8', tag: 'v2.8.0' }, 6 | { version: '2.7', tag: 'v2.7.0' }, 7 | { version: '2.6', tag: 'v2.6.1' }, 8 | { version: '2.5', tag: 'v2.5.0' }, 9 | { version: '2.4', tag: 'v2.4.0' }, 10 | { version: '2.3', tag: 'v2.3.0' }, 11 | { version: '2.2', tag: 'v2.2.0' }, 12 | { version: '2.1', tag: 'v2.1.0' }, 13 | { version: '2.0', tag: 'v2.0.0' }, 14 | { version: '1.9', tag: 'v1.9.1' }, 15 | ]; 16 | 17 | config.new( 18 | name='eck-operator', 19 | specs=[ 20 | { 21 | output: v.version, 22 | prefix: '^co\\.elastic\\.k8s\\..*', 23 | crds: ['https://raw.githubusercontent.com/elastic/cloud-on-k8s/%s/deploy/eck-operator/charts/eck-operator-crds/templates/all-crds.yaml' % v.tag], 24 | localName: 'eck-operator', 25 | } 26 | for v in versions 27 | ] 28 | ) 29 | -------------------------------------------------------------------------------- /libs/edp-keycloak-operator/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local versions = [ 4 | { output: '1.15.0', version: 'v1.15.0' }, 5 | { output: '1.17.0', version: 'v1.17.0' }, 6 | { output: '1.18.1', version: 'v1.18.1' }, 7 | { output: '1.18.2', version: 'v1.18.2' }, 8 | { output: '1.19.0', version: 'v1.19.0' }, 9 | { output: '1.20.0', version: 'v1.20.0' }, 10 | { output: '1.21.0', version: 'v1.21.0' }, 11 | { output: '1.22.0', version: 'v1.22.0' }, 12 | ]; 13 | 14 | config.new( 15 | name='edp-keycloak-operator', 16 | specs=[ 17 | { 18 | local url = 'https://raw.githubusercontent.com/epam/edp-keycloak-operator/%s/deploy-templates/crds/' % v.version, 19 | output: v.output, 20 | prefix: '^com\\.epam\\.edp\\..*', 21 | crds: [ 22 | '%s/v1.edp.epam.com_clusterkeycloakrealms.yaml' % url, 23 | '%s/v1.edp.epam.com_clusterkeycloaks.yaml' % url, 24 | '%s/v1.edp.epam.com_keycloakauthflows.yaml' % url, 25 | '%s/v1.edp.epam.com_keycloakclients.yaml' % url, 26 | '%s/v1.edp.epam.com_keycloakclientscopes.yaml' % url, 27 | '%s/v1.edp.epam.com_keycloakrealmcomponents.yaml' % url, 28 | '%s/v1.edp.epam.com_keycloakrealmgroups.yaml' % url, 29 | '%s/v1.edp.epam.com_keycloakrealmidentityproviders.yaml' % url, 30 | '%s/v1.edp.epam.com_keycloakrealmrolebatches.yaml' % url, 31 | '%s/v1.edp.epam.com_keycloakrealmroles.yaml' % url, 32 | '%s/v1.edp.epam.com_keycloakrealms.yaml' % url, 33 | '%s/v1.edp.epam.com_keycloakrealmusers.yaml' % url, 34 | '%s/v1.edp.epam.com_keycloaks.yaml' % url, 35 | ], 36 | localName: 'edp-keycloak-operator', 37 | } 38 | for v in versions 39 | ] 40 | ) 41 | -------------------------------------------------------------------------------- /libs/emissary/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local versions = [ 4 | { version: '3.7', tag: '3.7.2' }, 5 | { version: '3.8', tag: '3.8.2' }, 6 | { version: '3.9', tag: '3.9.1' }, 7 | ]; 8 | 9 | config.new( 10 | name='emissary', 11 | specs=[ 12 | { 13 | output: v.version, 14 | prefix: '^io\\.getambassador\\..*', 15 | crds: ['https://app.getambassador.io/yaml/emissary/%s/emissary-crds.yaml' % v.tag], 16 | localName: 'emissary', 17 | } 18 | for v in versions 19 | ] 20 | ) 21 | -------------------------------------------------------------------------------- /libs/envoy-gateway/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | local versions = ['v1.2.1', 'v1.3.0']; 3 | 4 | config.new( 5 | name='envoy-gateway', 6 | specs=[ 7 | { 8 | output: version, 9 | prefix: '^io\\.envoyproxy\\.gateway\\..*', 10 | crds: ['https://github.com/envoyproxy/gateway/releases/download/%s/install.yaml' % version], 11 | localName: 'envoy-gateway', 12 | } 13 | for version in versions 14 | ] 15 | ) 16 | -------------------------------------------------------------------------------- /libs/external-dns/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local versions = [ 4 | { version: '0.13', tag: '0.13.6' }, 5 | ]; 6 | 7 | config.new( 8 | name='external-dns', 9 | specs=[ 10 | { 11 | output: v.version, 12 | prefix: '^io\\.k8s\\.externaldns\\..*', 13 | crds: ['https://raw.githubusercontent.com/kubernetes-sigs/external-dns/v%s/docs/contributing/crd-source/crd-manifest.yaml' % v.tag], 14 | localName: 'external_dns', 15 | } 16 | for v in versions 17 | ] 18 | ) 19 | -------------------------------------------------------------------------------- /libs/external-secrets/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local 4 | version( 5 | output, 6 | version, 7 | crdFiles=['bundle.yaml'] // from v0.5 onward, a single bundle.yaml contains all of the external-secrets CRDs. 8 | ) = 9 | { 10 | output: output, 11 | version: version, 12 | crdFiles: crdFiles, 13 | }; 14 | 15 | local versions = [ 16 | version('0.4', '0.4.4', crdFiles=[ 17 | // 0.4.4 is the last version with individual CRD files 18 | 'external-secrets.io_clustersecretstores.yaml', 19 | 'external-secrets.io_externalsecrets.yaml', 20 | 'external-secrets.io_secretstores.yaml', 21 | ]), 22 | version('0.5', '0.5.9'), 23 | version('0.6', '0.6.1'), 24 | // From v0.7, the upstream kustomization.yaml was actually not properly containing all CRDs, we parse the bundle.yaml instead across the board for now. 25 | version('0.7', '0.7.3'), 26 | version('0.8', '0.8.12'), 27 | version('0.9', '0.9.12'), 28 | version('0.15', '0.15.1'), 29 | ]; 30 | 31 | config.new( 32 | name='external-secrets', 33 | specs=[ 34 | { 35 | local urlTemplate = 'https://raw.githubusercontent.com/external-secrets/external-secrets/v%s/deploy/crds/%s', 36 | output: v.output, 37 | prefix: '^io\\.external-secrets\\..*', 38 | crds: [ 39 | urlTemplate % [v.version, crdFile] 40 | for crdFile in v.crdFiles 41 | ], 42 | localName: 'external_secrets', 43 | } 44 | for v in versions 45 | ] 46 | ) 47 | -------------------------------------------------------------------------------- /libs/flagger/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local versions = [ 4 | { output: '1.38', version: '1.38.0' }, 5 | { output: '1.39', version: '1.39.0' }, 6 | { output: '1.40', version: '1.40.0' }, 7 | { output: '1.41', version: '1.41.0' }, 8 | ]; 9 | 10 | config.new( 11 | name='flagger', 12 | specs=[ 13 | { 14 | output: v.output, 15 | prefix: '^app\\.flagger\\..*', 16 | crds: ['https://raw.githubusercontent.com/fluxcd/flagger/v%s/charts/flagger/crds/crd.yaml' % v.version], 17 | localName: 'flagger', 18 | } 19 | for v in versions 20 | ] 21 | ) 22 | -------------------------------------------------------------------------------- /libs/gatekeeper/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local versions = [ 4 | { output: '3.12', version: '3.12.0' }, 5 | ]; 6 | 7 | config.new( 8 | name='gatekeeper', 9 | specs=[ 10 | { 11 | local url = 'https://raw.githubusercontent.com/open-policy-agent/gatekeeper/v%s/charts/gatekeeper/crds' % v.version, 12 | output: v.output, 13 | prefix: '^sh\\.gatekeeper\\..*', 14 | crds: [ 15 | '%s/%s' % [url, crd] 16 | 17 | for crd in [ 18 | 'assign-customresourcedefinition.yaml', 19 | 'assignimage-customresourcedefinition.yaml', 20 | 'assignmetadata-customresourcedefinition.yaml', 21 | 'config-customresourcedefinition.yaml', 22 | 'constraintpodstatus-customresourcedefinition.yaml', 23 | 'constrainttemplate-customresourcedefinition.yaml', 24 | 'constrainttemplatepodstatus-customresourcedefinition.yaml', 25 | 'expansiontemplate-customresourcedefinition.yaml', 26 | 'modifyset-customresourcedefinition.yaml', 27 | 'mutatorpodstatus-customresourcedefinition.yaml', 28 | 'provider-customresourcedefinition.yaml', 29 | ] 30 | ], 31 | localName: 'external_secrets', 32 | } 33 | for v in versions 34 | ] 35 | ) 36 | -------------------------------------------------------------------------------- /libs/gateway-api/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local versions = [ 4 | {output: '0.7', version: '0.7.1'}, 5 | {output: '0.8', version: '0.8.1'}, 6 | {output: '1.0', version: '1.0.0'}, 7 | {output: '1.1', version: '1.1.0'}, 8 | {output: '1.2', version: '1.2.0'}, 9 | ]; 10 | 11 | config.new( 12 | name='gateway-api', 13 | specs=[ 14 | { 15 | output: v.output, 16 | prefix: '^io\\.k8s\\.networking\\.gateway\\..*', 17 | crds: [ 18 | 'https://github.com/kubernetes-sigs/gateway-api/releases/download/v%(version)s/standard-install.yaml' % { version: v.version } 19 | ], 20 | localName: 'gateway-api', 21 | } 22 | for v in versions 23 | ] + [ 24 | { 25 | output: v.output + "-experimental", 26 | prefix: '^io\\.k8s\\.networking\\.gateway\\..*', 27 | crds: [ 28 | 'https://github.com/kubernetes-sigs/gateway-api/releases/download/v%(version)s/experimental-install.yaml' % { version: v.version } 29 | ], 30 | localName: 'gateway-api', 31 | } 32 | for v in versions 33 | ] 34 | ) 35 | -------------------------------------------------------------------------------- /libs/google-cloud-sql-proxy-operator/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | local versions = ['0.5.0', '1.4.2']; 3 | local manifests = ['cloud-sql-proxy-operator.yaml']; 4 | 5 | config.new( 6 | name='google-cloud-sql-proxy-operator', 7 | specs=[ 8 | { 9 | output: std.join('.', std.split(version, '.')[:2]), 10 | prefix: '^com\\.google\\.cloud\\.cloudsql\\..*', 11 | localName: 'google-cloud-sql-proxy-operator', 12 | crds: [ 13 | 'https://raw.githubusercontent.com/GoogleCloudPlatform/cloud-sql-proxy-operator/v%s/installer/%s' % 14 | [version, manifest] 15 | for manifest in manifests 16 | ], 17 | } 18 | for version in versions 19 | ] 20 | ) 21 | -------------------------------------------------------------------------------- /libs/grafana-agent/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local versions = [ 4 | {output: '0.26', version:'0.26.1'}, 5 | {output: '0.30', version:'0.30.2'}, 6 | ]; 7 | local old_versions = [ 8 | {output: '0.20', version:'0.20.0'}, 9 | ]; 10 | 11 | config.new( 12 | name='grafana-agent', 13 | specs=[ 14 | { 15 | local url = 'https://raw.githubusercontent.com/grafana/agent/v%s/production/operator/crds' % v.version, 16 | output: v.output, 17 | prefix: '^com\\.grafana\\..*', 18 | crds: [ 19 | '%s/monitoring.grafana.com_grafanaagents.yaml' % url, 20 | '%s/monitoring.grafana.com_logsinstances.yaml' % url, 21 | '%s/monitoring.grafana.com_metricsinstances.yaml' % url, 22 | '%s/monitoring.grafana.com_podlogs.yaml' % url, 23 | '%s/monitoring.grafana.com_integrations.yaml' % url, 24 | ], 25 | localName: 'grafana-agent', 26 | } 27 | for v in versions 28 | ] + [ 29 | { 30 | local url = 'https://raw.githubusercontent.com/grafana/agent/v%s/production/operator/crds' % v.version, 31 | output: v.output, 32 | prefix: '^com\\.grafana\\..*', 33 | crds: [ 34 | '%s/monitoring.grafana.com_grafanaagents.yaml' % url, 35 | '%s/monitoring.grafana.com_logsinstances.yaml' % url, 36 | '%s/monitoring.grafana.com_metricsinstances.yaml' % url, 37 | '%s/monitoring.grafana.com_podlogs.yaml' % url, 38 | ], 39 | localName: 'grafana-agent', 40 | } 41 | for v in old_versions 42 | ] 43 | ) 44 | -------------------------------------------------------------------------------- /libs/grafana-alloy/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local versions = [ 4 | { output: '1.6', version: '1.6.1' }, 5 | ]; 6 | 7 | config.new( 8 | name='grafana-alloy', 9 | specs=[ 10 | { 11 | local url = 'https://raw.githubusercontent.com/grafana/alloy/refs/tags/v%s/operations/helm/charts/alloy/charts/crds/crds' % v.version, 12 | output: v.output, 13 | prefix: '^com\\.grafana\\..*', 14 | crds: [ 15 | '%s/monitoring.grafana.com_podlogs.yaml' % url, 16 | ], 17 | localName: 'grafana-alloy', 18 | } 19 | for v in versions 20 | ] 21 | ) 22 | -------------------------------------------------------------------------------- /libs/grafana-operator/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local versions = [ 4 | { output: '4.1', version: '4.1.1' }, 5 | { output: '4.2', version: '4.2.0' }, 6 | { output: '4.3', version: '4.3.0' }, 7 | { output: '4.4', version: '4.4.1' }, 8 | { output: '4.5', version: '4.5.1' }, 9 | { output: '4.6', version: '4.6.0' }, 10 | { output: '4.7', version: '4.7.1' }, 11 | { output: '4.8', version: '4.8.0' }, 12 | { output: '4.9', version: '4.9.0' }, 13 | { output: '4.10', version: '4.10.1' }, 14 | { output: '5.0', version: '5.0.2' }, 15 | { output: '5.4', version: '5.4.2' }, 16 | { output: '5.5', version: '5.5.2' }, 17 | { output: '5.6', version: '5.6.3' }, 18 | { output: '5.7', version: '5.7.0' }, 19 | { output: '5.8', version: '5.8.1' }, 20 | { output: '5.9', version: '5.9.2' }, 21 | { output: '5.10', version: '5.10.0' }, 22 | { output: '5.11', version: '5.11.0' }, 23 | { output: '5.12', version: '5.12.0' }, 24 | { output: '5.13', version: '5.13.0' }, 25 | { output: '5.14', version: '5.14.0' }, 26 | { output: '5.15', version: '5.15.1' }, 27 | ]; 28 | 29 | config.new( 30 | name='grafana-operator', 31 | specs=[ 32 | { 33 | local url = if std.startsWith(v.output, '4.') then 34 | 'https://raw.githubusercontent.com/grafana-operator/grafana-operator/v%s/deploy/manifests/v%s/crds.yaml' % [v.version, v.version] 35 | else 36 | 'https://raw.githubusercontent.com/grafana-operator/grafana-operator/v%s/deploy/kustomize/base/crds.yaml' % v.version, 37 | output: v.output, 38 | prefix: '^org\\.integreatly\\..*', 39 | crds: [url], 40 | localName: 'grafana_operator', 41 | } 42 | for v in versions 43 | ] 44 | ) 45 | -------------------------------------------------------------------------------- /libs/harbor-operator/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | local versions = [ 3 | { version: '1.3', tag: 'v1.3.0'}, 4 | ]; 5 | 6 | config.new( 7 | name='harbor-operator', 8 | specs=[ 9 | { 10 | output: v.version, 11 | prefix: '^io\\.goharbor\\..*', 12 | crds: ['https://raw.githubusercontent.com/goharbor/harbor-operator/'+v.tag+'/manifests/cluster/deployment.yaml'], 13 | localName: 'harbor-operator', 14 | } 15 | for v in versions 16 | ] 17 | ) 18 | -------------------------------------------------------------------------------- /libs/hcp-terraform-operator/config.jsonnet: -------------------------------------------------------------------------------- 1 | // libs/hcp-terraform-operator/config.jsonnet 2 | local config = import 'jsonnet/config.jsonnet'; 3 | 4 | local versions = [ 5 | '2.7.1', 6 | ]; 7 | 8 | config.new( 9 | name='hcp-terraform-operator', 10 | specs=[ 11 | { 12 | crds: [ 13 | 'https://raw.githubusercontent.com/hashicorp/hcp-terraform-operator/v%s/config/crd/bases/app.terraform.io_agentpools.yaml' %v, 14 | 'https://raw.githubusercontent.com/hashicorp/hcp-terraform-operator/v%s/config/crd/bases/app.terraform.io_modules.yaml' %v, 15 | 'https://raw.githubusercontent.com/hashicorp/hcp-terraform-operator/v%s/config/crd/bases/app.terraform.io_projects.yaml' %v, 16 | 'https://raw.githubusercontent.com/hashicorp/hcp-terraform-operator/v%s/config/crd/bases/app.terraform.io_workspaces.yaml' %v, 17 | ], 18 | localName: 'hcp-terraform-operator', 19 | output: v, 20 | prefix: '^io\\.terraform\\..*', 21 | } 22 | for v in versions 23 | ] 24 | ) 25 | -------------------------------------------------------------------------------- /libs/istio/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | local versions = [ 3 | ['1.24', '1.24.0'], 4 | ['1.23', '1.23.3'], 5 | ['1.22', '1.22.3'], 6 | ['1.21', '1.21.5'], 7 | ['1.20', '1.20.0'], 8 | ['1.19', '1.19.1'], 9 | ['1.18', '1.18.6'], 10 | ['1.17', '1.17.6'], 11 | ]; 12 | 13 | config.new( 14 | name='istio', 15 | specs=[ 16 | { 17 | output: version[0], 18 | prefix: '^io\\.istio\\..*', 19 | crds: ['https://raw.githubusercontent.com/istio/istio/' + version[1] + '/manifests/charts/base/crds/crd-all.gen.yaml'], 20 | localName: 'istio', 21 | patchDir: 'custom', 22 | } 23 | for version in versions 24 | ] 25 | ) 26 | -------------------------------------------------------------------------------- /libs/istio/custom/virtualservice.libsonnet: -------------------------------------------------------------------------------- 1 | local d = import 'doc-util/main.libsonnet'; 2 | 3 | local patch = { 4 | local matchMixin = { 5 | local headersPatch = { 6 | '#withExact': d.fn( 7 | '`withExact` matches the header in an exact string', 8 | [d.arg('header', d.T.string), d.arg('value', d.T.string)] 9 | ), 10 | withExact(header, value): 11 | { [header]: { exact: value } }, 12 | 13 | '#withPrefix': d.fn( 14 | '`withPrefix` matches the header in an prefix-based', 15 | [d.arg('header', d.T.string), d.arg('value', d.T.string)] 16 | ), 17 | withPrefix(header, value): 18 | { [header]: { prefix: value } }, 19 | 20 | '#withRegex': d.fn( 21 | '`withRegex` matches the header in an RE2 style regex-based', 22 | [d.arg('header', d.T.string), d.arg('value', d.T.string)] 23 | ), 24 | withRegex(header, value): 25 | { [header]: { regex: value } }, 26 | 27 | '#withExactMixin': d.fn( 28 | '`withExact` matches the header in an exact string', 29 | [d.arg('header', d.T.string), d.arg('value', d.T.string)] 30 | ), 31 | withExactMixin(header, value): 32 | { [header]+: { exact: value } }, 33 | 34 | '#withPrefixMixin': d.fn( 35 | '`withPrefix` matches the header in an prefix-based', 36 | [d.arg('header', d.T.string), d.arg('value', d.T.string)] 37 | ), 38 | withPrefixMixin(header, value): 39 | { [header]+: { prefix: value } }, 40 | 41 | '#withRegexMixin': d.fn( 42 | '`withRegex` matches the header in an RE2 style regex-based', 43 | [d.arg('header', d.T.string), d.arg('value', d.T.string)] 44 | ), 45 | withRegexMixin(header, value): 46 | { [header]+: { regex: value } }, 47 | }, 48 | 49 | // headers+: headersPatch, 50 | headers: headersPatch, 51 | withoutHeaders+: headersPatch, 52 | 53 | queryParams+: { 54 | '#withExact': d.fn( 55 | '`withExact` matches the header in an exact string', 56 | [d.arg('key', d.T.string), d.arg('value', d.T.string)] 57 | ), 58 | withExact(key, value): 59 | { [key]: { exact: value } }, 60 | 61 | '#withRegex': d.fn( 62 | '`withRegex` matches the header in an RE2 style regex-based', 63 | [d.arg('key', d.T.string), d.arg('value', d.T.string)] 64 | ), 65 | withRegex(key, value): 66 | { [key]: { regex: value } }, 67 | 68 | '#withExactMixin': d.fn( 69 | '`withExact` matches the header in an exact string', 70 | [d.arg('key', d.T.string), d.arg('value', d.T.string)] 71 | ), 72 | withExactMixin(key, value): 73 | { [key]+: { exact: value } }, 74 | 75 | '#withRegexMixin': d.fn( 76 | '`withRegex` matches the header in an RE2 style regex-based', 77 | [d.arg('key', d.T.string), d.arg('value', d.T.string)] 78 | ), 79 | withRegexMixin(key, value): 80 | { [key]+: { regex: value } }, 81 | }, 82 | }, 83 | 84 | virtualService+: { 85 | spec+: { 86 | http+: { 87 | match+: matchMixin, 88 | }, 89 | }, 90 | }, 91 | }; 92 | 93 | { 94 | networking+: { 95 | v1beta1+: patch, 96 | v1alpha3+: patch, 97 | }, 98 | } 99 | -------------------------------------------------------------------------------- /libs/k8s/README_docs.md.tmpl: -------------------------------------------------------------------------------- 1 | --- 2 | permalink: / 3 | --- 4 | 5 | # Jsonnet Kubernetes library 6 | 7 | The Jsonnet Kubernetes library is a generated with 8 | [`k8s`](https://github.com/jsonnet-libs/k8s) from the OpenAPI specification 9 | (`swagger.json`) of the Kubernetes API. 10 | 11 | Currently, artifacts for the following Kubernetes versions are provided: 12 | 13 | %(pages)s 14 | 15 | ## FAQ 16 | 17 | ### What is this? 18 | 19 | This is a [Jsonnet](https://jsonnet.org) library that helps the Jsonnet user 20 | create Kubernetes objects such as `Deployment` as concisely as possible. 21 | 22 | For example, this YAML: 23 | 24 | ```yaml 25 | apiVersion: apps/v1 26 | kind: Deployment 27 | metadata: 28 | name: foo 29 | spec: 30 | replicas: 1 31 | template: 32 | metadata: 33 | labels: 34 | app: name 35 | spec: 36 | containers: 37 | - image: foo/bar 38 | name: foo 39 | ``` 40 | 41 | can be rendered with this jsonnet: 42 | 43 | ```jsonnet 44 | { 45 | local k = (import "github.com/jsonnet-libs/k8s-libsonnet/1.18/main.libsonnet"), 46 | foo: k.apps.v1.deployment.new(name="foo", containers=[ 47 | k.core.v1.container.new(name="foo", image="foo/bar") 48 | ]) 49 | } 50 | ``` 51 | 52 | This library works particularly well with [Grafana Tanka](https://tanka.dev), as Tanka is 53 | designed to ease the use of Jsonnet with Kubernetes clusters. 54 | 55 | ### Relationship to [ksonnet-lib](https://github.com/ksonnet/ksonnet-lib) 56 | 57 | [ksonnet-lib](https://github.com/ksonnet/ksonnet-lib) was a similar library to this. However, 58 | as a part of the [now deprecated ksonnet project](https://github.com/ksonnet/ksonnet#ksonnet), 59 | it has not seen much love in recent days. 60 | 61 | To continue the success story, community members (especially teams of [Grafana 62 | Labs](https://grafana.com) and [RedHat OpenShift](https://www.openshift.com/)) decided to 63 | pick up the challenge of creating this fresh, more modern and future proof replacement for 64 | ksonnet-lib. 65 | -------------------------------------------------------------------------------- /libs/k8s/README_root.md.tmpl: -------------------------------------------------------------------------------- 1 | # k8s-libsonnet 2 | 3 | This repository contains artifacts of the `k8s-libsonnet` Jsonnet Kubernetes library. 4 | It is generated with `k8s-gen`, located at https://github.com/jsonnet-libs/k8s. 5 | 6 | ## Usage 7 | 8 | #### With [Tanka](https://tanka.dev) 9 | 10 | ```bash 11 | # set up Tanka project, matching the Kubernetes version 12 | $ tk init --k8s=1.21 13 | ``` 14 | 15 | For more info, refer to the Tanka tutorial at https://tanka.dev/tutorial/k-lib. 16 | 17 | #### Standalone 18 | 19 | ```bash 20 | $ jb install github.com/jsonnet-libs/k8s-libsonnet/%(version)s@main 21 | ``` 22 | 23 | Then import it in your project: 24 | 25 | ```jsonnet 26 | local k = import "github.com/jsonnet-libs/k8s-libsonnet/%(version)s/main.libsonnet" 27 | ``` 28 | 29 | ## FAQ 30 | 31 | #### Is it stable? 32 | 33 | We don't know. It seems to work pretty well, but it is very young code, so it is 34 | not battle tested. 35 | 36 | The API aims to be very similar to `ksonnet-gen`, but not 100%% the same, to 37 | allow enough room for important improvements. 38 | 39 | If something does not look as expected, please open an issue at 40 | https://github.com/jsonnet-libs/k8s 41 | 42 | #### Shall I use it? 43 | 44 | Yes, please! We need to get as much feedback as possible to enhance it, so it's 45 | your chance to be part of that! 46 | 47 | #### Why a new generator? 48 | 49 | The original generator located at https://github.com/ksonnet/ksonnet-gen is not 50 | maintained anymore, and only provides artifacts for Kubernetes versions up to 51 | `v1.14` 52 | 53 | #### Can I use it to replace [ksonnet-lib](https://github.com/ksonnet/ksonnet-lib)? 54 | 55 | Yes, however there are a few incompatibilities. Most of them are now covered by the 56 | [`ksonnet-util`](https://github.com/grafana/jsonnet-libs/blob/master/ksonnet-util/) 57 | library, please have a look here if you intend to support both libraries. 58 | 59 | Known incompatibilities: 60 | 61 | * `ksonnet-lib` has hidden objects that are exposed as 'Type' objects inside 62 | other objects, for example `core.v1.container.envType`, these are now 63 | available as first class citizens, for this example `core.v1.envVar`. The 64 | `ksonnet-util` library covers this problem. 65 | * `new(name)` constructors have a mandatory `name` argument, this was not always 66 | the case in `ksonnet-lib`. The `ksonnet-util` library covers this problem. 67 | * `ksonnet-lib` as nested many functions in `.mixin.`, `k8s-libsonnet` also supports 68 | this. However, library maintainers should also include this if they intend 69 | to support both libraries. 70 | * This library does not support chaining of functions, for example 71 | `container.new(name).withEnv(env)`. Turns out this practice had a big 72 | performance penalty on the Jsonnet compiler. A mixin pattern is therefor 73 | encouraged: `container.new(name) + container.withEnv(env)`. 74 | 75 | #### What about [kube-jsonnet/k](https://github.com/kube-jsonnet/k)? 76 | 77 | The `ksonnet-gen` software was forked and updated at 78 | https://github.com/kube-jsonnet/k, by RedHat and Grafana Labs, the same company 79 | behind this project. 80 | 81 | While working on it, we quickly realized that the overall code complexity, 82 | performance and correctness is far from what we desire and decided to switch 83 | efforts to rewriting it from scratch. 84 | 85 | The result of that is https://github.com/jsonnet-libs/k8s and expected to 86 | replace both, https://github.com/ksonnet/ksonnet-gen and 87 | https://github.com/kube-jsonnet/k as soon as possible. 88 | 89 | #### Where can I find documentation? 90 | 91 | The generated docs can be found here: https://jsonnet-libs.github.io/k8s-libsonnet/. 92 | 93 | #### I found a bug, where can I report this? 94 | 95 | Great, let's try and fix that, please report issues in https://github.com/jsonnet-libs/k8s. 96 | -------------------------------------------------------------------------------- /libs/k8s/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | local versions = [ 3 | '1.32', 4 | '1.31', 5 | '1.30', 6 | '1.29', 7 | '1.28', 8 | ]; 9 | 10 | config.new( 11 | name='k8s', 12 | specs=[ 13 | { 14 | output: version, 15 | openapi: 'https://raw.githubusercontent.com/kubernetes/kubernetes/release-' + version + '/api/openapi-spec/swagger.json', 16 | prefix: '^io\\.k8s\\.(api|(kube-aggregator|apimachinery)\\.pkg\\.apis)\\..*', 17 | patchDir: 'custom/core', 18 | extensionsDir: 'extensions/core', 19 | localName: 'k', 20 | description: 'Generated Jsonnet library for Kubernetes v' + version, 21 | } 22 | for version in versions 23 | ] 24 | ) 25 | + { 26 | 27 | mkdocs_config+: { 28 | plugins+: [{ 29 | 'exclude-search': { 30 | exclude: [ 31 | version + '/*' 32 | for version in versions[1:] 33 | ], 34 | }, 35 | }], 36 | }, 37 | 38 | 'skel/README.md': (importstr './README_root.md.tmpl') % { 39 | version: versions[0], 40 | }, 41 | 42 | 'skel/docs/README.md': (importstr './README_docs.md.tmpl') % { 43 | pages: std.join('\n', [ 44 | '- [v%(version)s](%(version)s/README.md)' % { version: version } 45 | for version in versions 46 | ]), 47 | }, 48 | } 49 | -------------------------------------------------------------------------------- /libs/k8s/custom/core/apps.libsonnet: -------------------------------------------------------------------------------- 1 | local gen = import '../gen.libsonnet'; 2 | local d = import 'doc-util/main.libsonnet'; 3 | 4 | local patch = { 5 | daemonSet+: { 6 | '#new'+: d.func.withArgs([ 7 | d.arg('name', d.T.string), 8 | d.arg('containers', d.T.array), 9 | d.arg('podLabels', d.T.object, {}), 10 | ]), 11 | new( 12 | name, 13 | containers=[], 14 | podLabels={} 15 | ):: 16 | local labels = { name: name } + podLabels; 17 | super.new(name) 18 | + super.spec.template.spec.withContainers(containers) 19 | + super.spec.template.metadata.withLabels(labels) 20 | + super.spec.selector.withMatchLabels(labels), 21 | }, 22 | deployment+: { 23 | '#new'+: d.func.withArgs([ 24 | d.arg('name', d.T.string), 25 | d.arg('replicas', d.T.int, 1), 26 | d.arg('containers', d.T.array), 27 | d.arg('podLabels', d.T.object, {}), 28 | ]), 29 | new( 30 | name, 31 | replicas=1, 32 | containers=error 'containers unset', 33 | podLabels={}, 34 | ):: 35 | local labels = { name: name } + podLabels; 36 | super.new(name) 37 | + (if replicas == null then {} else super.spec.withReplicas(replicas)) 38 | + super.spec.template.spec.withContainers(containers) 39 | + super.spec.template.metadata.withLabels(labels) 40 | + super.spec.selector.withMatchLabels(labels), 41 | }, 42 | 43 | statefulSet+: { 44 | '#new'+: d.func.withArgs([ 45 | d.arg('name', d.T.string), 46 | d.arg('replicas', d.T.int, 1), 47 | d.arg('containers', d.T.array), 48 | d.arg('volumeClaims', d.T.array, []), 49 | d.arg('podLabels', d.T.object, {}), 50 | ]), 51 | new( 52 | name, 53 | replicas=1, 54 | containers=error 'containers unset', 55 | volumeClaims=[], 56 | podLabels={}, 57 | ):: 58 | local labels = { name: name } + podLabels; 59 | super.new(name) 60 | + super.spec.withReplicas(replicas) 61 | + super.spec.template.spec.withContainers(containers) 62 | + super.spec.template.metadata.withLabels(labels) 63 | + super.spec.selector.withMatchLabels(labels) 64 | 65 | // remove volumeClaimTemplates if empty 66 | // (otherwise it will create a diff all the time) 67 | + ( 68 | if std.length(volumeClaims) > 0 69 | then super.spec.withVolumeClaimTemplates(volumeClaims) 70 | else {} 71 | ), 72 | }, 73 | }; 74 | 75 | { 76 | apps+: { 77 | v1+: patch, 78 | }, 79 | } 80 | -------------------------------------------------------------------------------- /libs/k8s/custom/core/autoscaling.libsonnet: -------------------------------------------------------------------------------- 1 | local d = import 'doc-util/main.libsonnet'; 2 | 3 | local withApiVersion = { 4 | '#withApiVersion':: d.fn(help='API version of the referent', args=[d.arg(name='apiversion', type=d.T.string)]), 5 | withApiVersion(apiversion): { apiVersion: apiversion }, 6 | }; 7 | 8 | 9 | local withScaleTargetRef = { 10 | '#withScaleTargetRef':: d.fn(help='Set spec.ScaleTargetRef to `object`', args=[d.arg(name='object', type=d.T.object)]), 11 | withScaleTargetRef(object): 12 | { spec+: { scaleTargetRef+: { 13 | apiVersion: object.apiVersion, 14 | kind: object.kind, 15 | name: object.metadata.name, 16 | } } }, 17 | }; 18 | 19 | local patch = { 20 | crossVersionObjectReference+: withApiVersion, 21 | horizontalPodAutoscaler+: { 22 | spec+: withScaleTargetRef, 23 | }, 24 | }; 25 | 26 | { 27 | autoscaling+: { 28 | v1+: patch, 29 | v2+: patch, 30 | }, 31 | } 32 | -------------------------------------------------------------------------------- /libs/k8s/custom/core/batch.libsonnet: -------------------------------------------------------------------------------- 1 | local d = import 'doc-util/main.libsonnet'; 2 | 3 | local patch = { 4 | cronJob+: { 5 | '#new'+: d.func.withArgs([ 6 | d.arg('name', d.T.string), 7 | d.arg('schedule', d.T.string), 8 | d.arg('containers', d.T.array), 9 | ]), 10 | new( 11 | name, 12 | schedule='', 13 | containers=[] 14 | ):: 15 | super.new(name) 16 | + super.spec.withSchedule(schedule) 17 | + super.spec.jobTemplate.spec.template.spec.withContainers(containers) 18 | + super.spec.jobTemplate.spec.template.metadata.withLabels({ name: name }), 19 | }, 20 | }; 21 | 22 | { 23 | batch+: { 24 | v1+: patch, 25 | }, 26 | } 27 | -------------------------------------------------------------------------------- /libs/k8s/custom/core/list.libsonnet: -------------------------------------------------------------------------------- 1 | local d = import 'doc-util/main.libsonnet'; 2 | 3 | { 4 | core+: { 5 | v1+: { 6 | list: { 7 | '#':: d.pkg(name='list', url='', help='List represents a generic list of items.'), 8 | '#new': d.fn( 9 | '`new` returns an instance of List.', 10 | [d.arg('items', d.T.array)] 11 | ), 12 | new(items):: { 13 | apiVersion: 'v1', 14 | kind: 'List', 15 | } + self.withItems(items), 16 | '#withItems': d.fn( 17 | '`withItems` List of items to populate the items in a list.', 18 | [d.arg('items', d.T.array)] 19 | ), 20 | withItems(items):: 21 | if std.isArray(v=items) 22 | then { items+: items } 23 | else { items+: [items] }, 24 | }, 25 | }, 26 | }, 27 | } 28 | -------------------------------------------------------------------------------- /libs/k8s/custom/core/mapContainers.libsonnet: -------------------------------------------------------------------------------- 1 | local d = import 'doc-util/main.libsonnet'; 2 | 3 | local patch = { 4 | '#mapContainers': d.fn( 5 | ||| 6 | `mapContainers` applies the function f to each container. 7 | It works exactly as `std.map`, but on the containers of this object. 8 | 9 | **Signature of `f`**: 10 | ```ts 11 | f(container: Object) Object 12 | ``` 13 | |||, 14 | [d.arg('f', d.T.func)] 15 | ), 16 | mapContainers(f, includeInitContainers=false):: { 17 | local podContainers = super.spec.template.spec.containers, 18 | local podInitContainers = super.spec.template.spec.initContainers, 19 | spec+: { 20 | template+: { 21 | spec+: { 22 | containers: std.map(f, podContainers), 23 | [if includeInitContainers then 'initContainers']: std.map(f, podInitContainers), 24 | }, 25 | }, 26 | }, 27 | }, 28 | 29 | '#mapContainersWithName': d.fn('`mapContainersWithName` is like `mapContainers`, but only applies to those containers in the `names` array', 30 | [d.arg('names', d.T.array), d.arg('f', d.T.func)]), 31 | mapContainersWithName(names, f, includeInitContainers=false):: 32 | local nameSet = if std.type(names) == 'array' then std.set(names) else std.set([names]); 33 | local inNameSet(name) = std.length(std.setInter(nameSet, std.set([name]))) > 0; 34 | 35 | self.mapContainers(function(c) if std.objectHas(c, 'name') && inNameSet(c.name) then f(c) else c, includeInitContainers), 36 | }; 37 | 38 | // batch.job and batch.cronJob have the podSpec at a different location 39 | local cronPatch = patch { 40 | mapContainers(f, includeInitContainers=false):: { 41 | local podContainers = super.spec.jobTemplate.spec.template.spec.containers, 42 | local podInitContainers = super.spec.jobTemplate.spec.template.spec.initContainers, 43 | spec+: { 44 | jobTemplate+: { 45 | spec+: { 46 | template+: { 47 | spec+: { 48 | containers: std.map(f, podContainers), 49 | [if includeInitContainers then 'initContainers']: std.map(f, podInitContainers), 50 | }, 51 | }, 52 | }, 53 | }, 54 | }, 55 | }, 56 | }; 57 | 58 | { 59 | core+: { 60 | v1+: { 61 | pod+: patch, 62 | podTemplate+: patch, 63 | replicationController+: patch, 64 | }, 65 | }, 66 | batch+: { 67 | v1+: { 68 | job+: patch, 69 | cronJob+: cronPatch, 70 | }, 71 | }, 72 | apps+: { 73 | v1+: { 74 | daemonSet+: patch, 75 | deployment+: patch, 76 | replicaSet+: patch, 77 | statefulSet+: patch, 78 | }, 79 | }, 80 | } 81 | -------------------------------------------------------------------------------- /libs/k8s/custom/core/rbac.libsonnet: -------------------------------------------------------------------------------- 1 | local d = import 'doc-util/main.libsonnet'; 2 | 3 | local bindRoleDoc = d.fn( 4 | '`bindRole` returns a roleRef for a Role or ClusterRole object.', 5 | [d.arg('role', d.T.object)] 6 | ); 7 | 8 | local bindRole(role) = { 9 | roleRef: { 10 | name: role.metadata.name, 11 | kind: role.kind, 12 | apiGroup: std.split(role.apiVersion, '/')[0], 13 | }, 14 | }; 15 | 16 | local patch = { 17 | roleBinding+: { 18 | '#bindRole': bindRoleDoc, 19 | bindRole(role):: bindRole(role), 20 | }, 21 | clusterRoleBinding+: { 22 | '#bindRole': bindRoleDoc, 23 | bindRole(role):: bindRole(role), 24 | }, 25 | subject+: { 26 | '#fromServiceAccount': d.fn( 27 | '`fromServiceAccount` returns a subject for a service account.', 28 | [d.arg('service_account', d.T.object)] 29 | ), 30 | fromServiceAccount(service_account):: 31 | super.withKind('ServiceAccount') + 32 | super.withName(service_account.metadata.name) + 33 | super.withNamespace(service_account.metadata.namespace), 34 | }, 35 | }; 36 | 37 | { 38 | rbac+: { 39 | v1+: patch, 40 | }, 41 | } 42 | -------------------------------------------------------------------------------- /libs/karpenter/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local versions = [ 4 | { output: '0.27', version: '0.27.0' }, 5 | { output: '0.31', version: '0.31.3' }, 6 | { output: '0.32', version: '0.32.0' }, 7 | { output: '0.33', version: '0.33.0' }, 8 | { output: '0.34', version: '0.34.0' }, 9 | { output: '0.35', version: '0.35.0' }, 10 | { output: '0.37', version: '0.37.0' }, 11 | { output: '1.0', version: '1.0.0' }, 12 | ]; 13 | local getURL(v) = 14 | if std.objectHas(v, 'url') 15 | then v.url 16 | else 'https://raw.githubusercontent.com/aws/karpenter-provider-aws/v%s/pkg/apis/crds' % v.version; 17 | 18 | config.new( 19 | name='karpenter', 20 | specs=[ 21 | { 22 | local url = getURL(v), 23 | output: v.output + '/aws', 24 | prefix: '^(aws\\.k8s\\.karpenter)\\..*', 25 | crds: [ 26 | '%s/karpenter.k8s.aws_awsnodetemplates.yaml' % url, 27 | '%s/karpenter.k8s.aws_ec2nodeclasses.yaml' % url, 28 | ], 29 | localName: 'karpenter', 30 | } 31 | for v in versions 32 | ] + [ 33 | { 34 | local url = getURL(v), 35 | output: v.output + '/sh', 36 | prefix: '^(sh\\.karpenter)\\..*', 37 | crds: [ 38 | '%s/karpenter.sh_provisioners.yaml' % url, 39 | '%s/karpenter.sh_nodepools.yaml' % url, 40 | '%s/karpenter.sh_nodeclaims.yaml' % url, 41 | ], 42 | localName: 'karpenter', 43 | } 44 | for v in versions 45 | ] 46 | ) 47 | -------------------------------------------------------------------------------- /libs/keda/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local versions = [ 4 | { output: '2.10', version: '2.10.1' }, 5 | { output: '2.11', version: '2.11.2' }, 6 | { output: '2.12', version: '2.12.1' }, 7 | { output: '2.13', version: '2.13.1' }, 8 | { output: '2.14', version: '2.14.1' }, 9 | { output: '2.15', version: '2.15.1' }, 10 | { output: '2.16', version: '2.16.1' }, 11 | ]; 12 | 13 | config.new( 14 | name='keda', 15 | specs=[ 16 | { 17 | local url = 'https://raw.githubusercontent.com/kedacore/keda/v%s/config/crd/bases' % v.version, 18 | output: v.output, 19 | prefix: '^sh\\.keda\\..*', 20 | crds: [ 21 | '%s/keda.sh_clustertriggerauthentications.yaml' % url, 22 | '%s/keda.sh_scaledjobs.yaml' % url, 23 | '%s/keda.sh_scaledobjects.yaml' % url, 24 | '%s/keda.sh_triggerauthentications.yaml' % url, 25 | ], 26 | localName: 'keda', 27 | } 28 | for v in versions 29 | ] 30 | ) 31 | -------------------------------------------------------------------------------- /libs/knative-eventing/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local versions = ['1.17.0', '1.16.0', '1.15.0']; 4 | 5 | config.new( 6 | name='knative-eventing', 7 | specs=[ 8 | { 9 | output: v, 10 | crds: ['https://github.com/knative/eventing/releases/download/knative-v%s/eventing-crds.yaml' % v], 11 | prefix: '^dev\\.knative\\..*', 12 | localName: 'knative-eventing', 13 | }, 14 | for v in versions 15 | ] 16 | ) -------------------------------------------------------------------------------- /libs/knative-serving/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local versions = ['1.17.0', '1.16.0', '1.15.0']; 4 | 5 | config.new( 6 | name='knative-serving', 7 | specs=[ 8 | { 9 | output: v, 10 | crds: ['https://github.com/knative/serving/releases/download/knative-v%s/serving-crds.yaml' % v], 11 | prefix: '^dev\\.knative\\..*', 12 | patchDir: 'custom/serving', 13 | localName: 'knative-serving', 14 | }, 15 | for v in versions 16 | ] 17 | ) -------------------------------------------------------------------------------- /libs/knative-serving/custom/serving/mapContainers.libsonnet: -------------------------------------------------------------------------------- 1 | local d = import 'doc-util/main.libsonnet'; 2 | 3 | local patch = { 4 | '#mapContainers': d.fn( 5 | ||| 6 | `mapContainers` applies the function f to each container. 7 | It works exactly as `std.map`, but on the containers of this object. 8 | 9 | **Signature of `f`**: 10 | ```ts 11 | f(container: Object) Object 12 | ``` 13 | |||, 14 | [d.arg('f', d.T.func)] 15 | ), 16 | mapContainers(f, includeInitContainers=false):: { 17 | local podContainers = super.spec.template.spec.containers, 18 | local podInitContainers = super.spec.template.spec.initContainers, 19 | spec+: { 20 | template+: { 21 | spec+: { 22 | containers: std.map(f, podContainers), 23 | [if includeInitContainers then 'initContainers']: std.map(f, podInitContainers), 24 | }, 25 | }, 26 | }, 27 | }, 28 | 29 | '#mapContainersWithName': d.fn('`mapContainersWithName` is like `mapContainers`, but only applies to those containers in the `names` array', 30 | [d.arg('names', d.T.array), d.arg('f', d.T.func)]), 31 | mapContainersWithName(names, f, includeInitContainers=false):: 32 | local nameSet = if std.type(names) == 'array' then std.set(names) else std.set([names]); 33 | local inNameSet(name) = std.length(std.setInter(nameSet, std.set([name]))) > 0; 34 | 35 | self.mapContainers(function(c) if std.objectHas(c, 'name') && inNameSet(c.name) then f(c) else c, includeInitContainers), 36 | }; 37 | 38 | { 39 | serving+: { v1+: { 40 | service+: patch, 41 | } }, 42 | } 43 | -------------------------------------------------------------------------------- /libs/kube-prometheus/custom/monitoring/relabel_config.libsonnet: -------------------------------------------------------------------------------- 1 | local d = import 'doc-util/main.libsonnet'; 2 | 3 | local relabelConfig = { 4 | '#':: d.pkg(name='relabelConfig', url='', help='RelabelConfig is a help to build RelabelConfig objects for ServiceMonitors and PodMonitors'), 5 | 6 | '#withSourceLabels':: d.fn('The source labels select values from existing labels. Their content is concatenated using the configured separator and matched against the configured regular expression for the replace, keep, and drop actions.', [d.arg('labels', d.T.array)]), 7 | withSourceLabels(labels): { sourceLabels: labels }, 8 | 9 | '#withSeparator':: d.fn('Separator placed between concatenated source label values. default is ‘;’.', [d.arg('separator', d.T.string)]), 10 | withSeparator(separator): { separator: separator }, 11 | 12 | '#withTargetLabel':: d.fn('Label to which the resulting value is written in a replace action. It is mandatory for replace actions. Regex capture groups are available.', [d.arg('separator', d.T.string)]), 13 | withTargetLabel(label): { targetLabel: label }, 14 | 15 | '#withRegex':: d.fn('Regular expression against which the extracted value is matched. Default is ‘(.*)’', [d.arg('separator', d.T.string)]), 16 | withRegex(regex): { regex: regex }, 17 | 18 | '#withModulus':: d.fn('Modulus to take of the hash of the source label values.', [d.arg('modulus', d.T.number)]), 19 | withModulus(modulus): { modulus: modulus }, 20 | 21 | '#withReplacement':: d.fn('Replacement value against which a regex replace is performed if the regular expression matches. Regex capture groups are available. Default is ‘$1’', [d.arg('separator', d.T.string)]), 22 | withReplacement(replacement): { replacement: replacement }, 23 | 24 | '#withAction':: d.fn('Action to perform based on regex matching. Default is ‘replace’', [d.arg('separator', d.T.string)]), 25 | withAction(action): { action: action }, 26 | 27 | '#mixin': 'ignore', 28 | mixin: self, 29 | }; 30 | 31 | { 32 | monitoring+:: { 33 | v1+: { 34 | relabelConfig: relabelConfig, 35 | }, 36 | v1alpha1+: { 37 | relabelConfig: relabelConfig, 38 | }, 39 | }, 40 | } 41 | -------------------------------------------------------------------------------- /libs/kubernetes-nmstate/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | local versions = [ 3 | { version: '0.74', tag: 'v0.74.0' }, 4 | { version: '0.73', tag: 'v0.73.0' }, 5 | { version: '0.72', tag: 'v0.72.0' }, 6 | { version: '0.71', tag: 'v0.71.0' }, 7 | { version: '0.70', tag: 'v0.70.1' }, 8 | ]; 9 | 10 | config.new( 11 | name='kubernetes-nmstate', 12 | specs=[ 13 | { 14 | output: v.version, 15 | prefix: '^io\\.nmstate\\..*', 16 | crds: ['https://github.com/nmstate/kubernetes-nmstate/releases/download/' + v.tag + '/nmstate.io_nmstates.yaml'], 17 | localName: 'nmstate', 18 | } 19 | for v in versions 20 | ] 21 | ) 22 | -------------------------------------------------------------------------------- /libs/kubernetes-secret-generator/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local version = '3.4.0'; 4 | 5 | config.new( 6 | name='kubernetes-secret-generator', 7 | specs=[ 8 | { 9 | local url = 'https://raw.githubusercontent.com/mittwald/kubernetes-secret-generator/v%s/deploy/crds/' % version, 10 | output: version, 11 | prefix: '^de\\.mittwald\\.secretgenerator\\..*', 12 | crds: [ 13 | '%s/secretgenerator.mittwald.de_basicauths_crd.yaml' % url, 14 | '%s/secretgenerator.mittwald.de_sshkeypairs_crd.yaml' % url, 15 | '%s/secretgenerator.mittwald.de_stringsecrets_crd.yaml' % url, 16 | ], 17 | localName: 'kubernetes_secret_generator', 18 | }, 19 | ], 20 | ) 21 | -------------------------------------------------------------------------------- /libs/kubevela/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | local versions = [ 3 | {output: '1.4', version:'1.4.14'}, 4 | {output: '1.5', version:'1.5.10'}, 5 | {output: '1.6', version:'1.6.7'}, 6 | {output: '1.7', version:'1.7.0'}, 7 | ]; 8 | config.new( 9 | name='kubevela', 10 | specs=[ 11 | { 12 | local url = 'https://raw.githubusercontent.com/kubevela/kubevela/v%s/charts/vela-core/crds' % v.version, 13 | output: v.output, 14 | prefix: '^dev\\.oam\\..*', 15 | crds: [ 16 | '%s/core.oam.dev_applicationrevisions.yaml' % url, 17 | '%s/core.oam.dev_applications.yaml' % url, 18 | '%s/core.oam.dev_componentdefinitions.yaml' % url, 19 | '%s/core.oam.dev_definitionrevisions.yaml' % url, 20 | '%s/core.oam.dev_envbindings.yaml' % url, 21 | '%s/core.oam.dev_healthscopes.yaml' % url, 22 | '%s/core.oam.dev_manualscalertraits.yaml' % url, 23 | '%s/core.oam.dev_policies.yaml' % url, 24 | '%s/core.oam.dev_policydefinitions.yaml' % url, 25 | '%s/core.oam.dev_resourcetrackers.yaml' % url, 26 | '%s/core.oam.dev_scopedefinitions.yaml' % url, 27 | '%s/core.oam.dev_traitdefinitions.yaml' % url, 28 | '%s/core.oam.dev_workflows.yaml' % url, 29 | '%s/core.oam.dev_workflowstepdefinitions.yaml' % url, 30 | '%s/core.oam.dev_workloaddefinitions.yaml' % url, 31 | '%s/standard.oam.dev_rollouts.yaml' % url, 32 | ], 33 | localName: 'kubevela', 34 | } 35 | for v in versions 36 | ] 37 | ) 38 | -------------------------------------------------------------------------------- /libs/kyverno/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | config.new( 4 | name='kyverno', 5 | specs=[ 6 | { 7 | output: '1.6', 8 | prefix: '^io\\.kyverno\\..*', 9 | localName: 'kyverno', 10 | crds: [ 11 | // CRDs retrieved from https://github.com/kyverno/kyverno/blob/v1.6.2/config/crds/kustomization.yaml 12 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.6.2/config/crds/kyverno.io_clusterpolicies.yaml', 13 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.6.2/config/crds/kyverno.io_clusterpolicies.yaml', 14 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.6.2/config/crds/kyverno.io_clusterreportchangerequests.yaml', 15 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.6.2/config/crds/kyverno.io_generaterequests.yaml', 16 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.6.2/config/crds/kyverno.io_policies.yaml', 17 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.6.2/config/crds/kyverno.io_reportchangerequests.yaml', 18 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.6.2/config/crds/wgpolicyk8s.io_clusterpolicyreports.yaml', 19 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.6.2/config/crds/wgpolicyk8s.io_policyreports.yaml', 20 | ], 21 | }, 22 | { 23 | output: '1.9', 24 | prefix: '^io\\.kyverno\\..*', 25 | localName: 'kyverno', 26 | crds: [ 27 | // CRDs retrieved from https://github.com/kyverno/kyverno/blob/v1.9.0/config/crds 28 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.9.0/config/crds/kyverno.io_admissionreports.yaml', 29 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.9.0/config/crds/kyverno.io_backgroundscanreports.yaml', 30 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.9.0/config/crds/kyverno.io_cleanuppolicies.yaml', 31 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.9.0/config/crds/kyverno.io_clusteradmissionreports.yaml', 32 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.9.0/config/crds/kyverno.io_clusterbackgroundscanreports.yaml', 33 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.9.0/config/crds/kyverno.io_clustercleanuppolicies.yaml', 34 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.9.0/config/crds/kyverno.io_clusterpolicies.yaml', 35 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.9.0/config/crds/kyverno.io_generaterequests.yaml', 36 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.9.0/config/crds/kyverno.io_policies.yaml', 37 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.9.0/config/crds/kyverno.io_policyexceptions.yaml', 38 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.9.0/config/crds/kyverno.io_updaterequests.yaml', 39 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.9.0/config/crds/wgpolicyk8s.io_clusterpolicyreports.yaml', 40 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.9.0/config/crds/wgpolicyk8s.io_policyreports.yaml', 41 | ], 42 | }, 43 | { 44 | output: '1.11', 45 | prefix: '^io\\.kyverno\\..*', 46 | localName: 'kyverno', 47 | crds: [ 48 | // CRDs retrieved from https://github.com/kyverno/kyverno/tree/v1.11.0/config/crds 49 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.11.0/config/crds/kyverno.io_admissionreports.yaml', 50 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.11.0/config/crds/kyverno.io_backgroundscanreports.yaml', 51 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.11.0/config/crds/kyverno.io_cleanuppolicies.yaml', 52 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.11.0/config/crds/kyverno.io_clusteradmissionreports.yaml', 53 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.11.0/config/crds/kyverno.io_clusterbackgroundscanreports.yaml', 54 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.11.0/config/crds/kyverno.io_clustercleanuppolicies.yaml', 55 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.11.0/config/crds/kyverno.io_clusterpolicies.yaml', 56 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.11.0/config/crds/kyverno.io_policies.yaml', 57 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.11.0/config/crds/kyverno.io_policyexceptions.yaml', 58 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.11.0/config/crds/kyverno.io_updaterequests.yaml', 59 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.11.0/config/crds/wgpolicyk8s.io_clusterpolicyreports.yaml', 60 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.11.0/config/crds/wgpolicyk8s.io_policyreports.yaml', 61 | ], 62 | }, 63 | { 64 | output: '1.12', 65 | prefix: '^io\\.kyverno\\..*', 66 | localName: 'kyverno', 67 | crds: [ 68 | // CRDs retrieved from https://github.com/kyverno/kyverno/tree/v1.12.0/config/crds 69 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.12.0/config/crds/kyverno/kyverno.io_admissionreports.yaml', 70 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.12.0/config/crds/kyverno/kyverno.io_backgroundscanreports.yaml', 71 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.12.0/config/crds/kyverno/kyverno.io_cleanuppolicies.yaml', 72 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.12.0/config/crds/kyverno/kyverno.io_clusteradmissionreports.yaml', 73 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.12.0/config/crds/kyverno/kyverno.io_clusterbackgroundscanreports.yaml', 74 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.12.0/config/crds/kyverno/kyverno.io_clustercleanuppolicies.yaml', 75 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.12.0/config/crds/kyverno/kyverno.io_clusterpolicies.yaml', 76 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.12.0/config/crds/kyverno/kyverno.io_globalcontextentries.yaml', 77 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.12.0/config/crds/kyverno/kyverno.io_policies.yaml', 78 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.12.0/config/crds/kyverno/kyverno.io_policyexceptions.yaml', 79 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.12.0/config/crds/kyverno/kyverno.io_updaterequests.yaml', 80 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.12.0/config/crds/policyreport/wgpolicyk8s.io_clusterpolicyreports.yaml', 81 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.12.0/config/crds/policyreport/wgpolicyk8s.io_policyreports.yaml', 82 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.12.0/config/crds/reports/reports.kyverno.io_clusterephemeralreports.yaml', 83 | 'https://raw.githubusercontent.com/kyverno/kyverno/v1.12.0/config/crds/reports/reports.kyverno.io_ephemeralreports.yaml', 84 | ], 85 | }, 86 | ], 87 | ) 88 | -------------------------------------------------------------------------------- /libs/litmus-chaos/config.jsonnet: -------------------------------------------------------------------------------- 1 | # libs//config.jsonnet 2 | local config = (import 'jsonnet/config.jsonnet'); 3 | local versions = ['2.8.0', '2.9.0', '2.10.0']; 4 | local crdFiles = ['chaosengine_crd.yaml','chaosexperiment_crd.yaml','chaosresults_crd.yaml']; 5 | 6 | config.new( 7 | name='litmus-chaos', 8 | specs=[ 9 | { 10 | local baseUrl = 'https://raw.githubusercontent.com/litmuschaos/chaos-operator/%s/deploy/crds' % version, 11 | output: version, 12 | crds: [ '%s/%s' % [baseUrl, crd] for crd in crdFiles], 13 | prefix: '^io\\.litmuschaos\\..*', 14 | localName: 'litmus-chaos', 15 | } 16 | for version in versions 17 | ] 18 | ) -------------------------------------------------------------------------------- /libs/metallb/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | local versions = ['0.14.8']; 3 | local manifests = [ 4 | 'bfdprofiles', 5 | 'bgpadvertisements', 6 | 'bgppeers', 7 | 'communities', 8 | 'ipaddresspools', 9 | 'l2advertisements', 10 | 'servicel2statuses', 11 | ]; 12 | 13 | config.new( 14 | name='metallb', 15 | specs=[ 16 | { 17 | localName: 'metallb', 18 | output: std.join('.', std.split(version, '.')[:2]), 19 | prefix: '^io\\.metallb\\..*', 20 | crds: [ 21 | 'https://raw.githubusercontent.com/metallb/metallb/v%s/config/crd/bases/metallb.io_%s.yaml' 22 | % [version, manifest] 23 | for manifest in manifests 24 | ], 25 | } 26 | for version in versions 27 | ] 28 | ) 29 | -------------------------------------------------------------------------------- /libs/milvus-operator/config.jsonnet: -------------------------------------------------------------------------------- 1 | // libs/milvus-operator/config.jsonnet 2 | local config = import 'jsonnet/config.jsonnet'; 3 | 4 | local versions = [ 5 | '0.9.11', 6 | '1.0.0', 7 | '1.0.7', 8 | ]; 9 | 10 | config.new( 11 | name='milvus-operator', 12 | specs=[ 13 | { 14 | crds: [ 15 | 'https://raw.githubusercontent.com/zilliztech/milvus-operator/v%s/config/crd/bases/milvus.io_milvusclusters.yaml' % v, 16 | 'https://raw.githubusercontent.com/zilliztech/milvus-operator/v%s/config/crd/bases/milvus.io_milvuses.yaml' % v, 17 | 'https://raw.githubusercontent.com/zilliztech/milvus-operator/v%s/config/crd/bases/milvus.io_milvusupgrades.yaml' % v, 18 | ], 19 | localName: 'milvus-operator', 20 | output: v, 21 | prefix: '^io\\.milvus\\..*', 22 | } 23 | for v in versions 24 | ] 25 | ) 26 | -------------------------------------------------------------------------------- /libs/minio-operator/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | local versions = [ 3 | 'v6.0.0', 4 | 'v5.0.15', 5 | ]; 6 | 7 | config.new( 8 | name='minio-operator', 9 | specs=[ 10 | { 11 | output: v, 12 | prefix: '^io\\.min\\..*', 13 | crds: [ 14 | 'https://raw.githubusercontent.com/minio/operator/%s/resources/base/crds/job.min.io_miniojobs.yaml' % v, 15 | 'https://raw.githubusercontent.com/minio/operator/%s/resources/base/crds/minio.min.io_tenants.yaml' % v, 16 | 'https://raw.githubusercontent.com/minio/operator/%s/resources/base/crds/sts.min.io_policybindings.yaml.yaml' % v, 17 | ], 18 | localName: 'minio-operator', 19 | } 20 | for v in versions 21 | ] 22 | ) 23 | -------------------------------------------------------------------------------- /libs/mysql-operator/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local versions = [ 4 | {output: '2.1.1', version: '8.2.0-2.1.1'} 5 | ]; 6 | 7 | config.new( 8 | name='mysql-operator', 9 | specs=[ 10 | { 11 | output: v.output, 12 | prefix: '^com\\.oracle\\.mysql\\..*', 13 | crds: ['https://raw.githubusercontent.com/mysql/mysql-operator/%s/helm/mysql-operator/crds/crd.yaml' % v.version], 14 | } 15 | for v in versions 16 | ] 17 | ) 18 | -------------------------------------------------------------------------------- /libs/openshift/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | local versions = [ 3 | '3.11', 4 | '4.0', 5 | '4.1', 6 | '4.2', 7 | '4.3', 8 | '4.4', 9 | '4.5', 10 | ]; 11 | 12 | config.new( 13 | name='openshift', 14 | specs=[ 15 | { 16 | output: version, 17 | openapi: 'https://raw.githubusercontent.com/openshift/origin/release-'+version+'/api/swagger-spec/openshift-openapi-spec.json', 18 | prefix: '^com\\.github\\.openshift\\.api\\..*', 19 | localName: 'o', 20 | description: 'Generated Jsonnet library for OpenShift v' + version, 21 | } 22 | for version in versions 23 | ] 24 | ) 25 | -------------------------------------------------------------------------------- /libs/prometheus-operator/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local versions = [ 4 | { output: '0.67', version: '0.67.1' }, 5 | { output: '0.68', version: '0.68.0' }, 6 | { output: '0.69', version: '0.69.1' }, 7 | { output: '0.70', version: '0.70.0' }, 8 | { output: '0.71', version: '0.71.2' }, 9 | { output: '0.72', version: '0.72.0' }, 10 | { output: '0.73', version: '0.73.2' }, 11 | { output: '0.74', version: '0.74.0' }, 12 | { output: '0.75', version: '0.75.2' }, 13 | { output: '0.76', version: '0.76.2' }, 14 | { output: '0.77', version: '0.77.2' }, 15 | ]; 16 | 17 | config.new( 18 | name='prometheus-operator', 19 | specs=[ 20 | { 21 | local url = 'https://raw.githubusercontent.com/prometheus-operator/prometheus-operator/v%s/example/prometheus-operator-crd' % v.version, 22 | output: v.output, 23 | crds: [ 24 | '%s/monitoring.coreos.com_alertmanagerconfigs.yaml' % url, 25 | '%s/monitoring.coreos.com_alertmanagers.yaml' % url, 26 | '%s/monitoring.coreos.com_podmonitors.yaml' % url, 27 | '%s/monitoring.coreos.com_probes.yaml' % url, 28 | '%s/monitoring.coreos.com_prometheusagents.yaml' % url, // Added in 0.64 29 | '%s/monitoring.coreos.com_prometheuses.yaml' % url, 30 | '%s/monitoring.coreos.com_prometheusrules.yaml' % url, 31 | '%s/monitoring.coreos.com_scrapeconfigs.yaml' % url, // Added in 0.65 32 | '%s/monitoring.coreos.com_servicemonitors.yaml' % url, 33 | '%s/monitoring.coreos.com_thanosrulers.yaml' % url, 34 | ], 35 | prefix: '^com\\.coreos\\..*', 36 | localName: 'prometheus-operator', 37 | } 38 | for v in versions 39 | ] 40 | ) 41 | -------------------------------------------------------------------------------- /libs/pyrra/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local versions = [ 4 | { output: '0.1', version: '0.1.0' }, 5 | { output: '0.2', version: '0.2.0' }, 6 | { output: '0.3', version: '0.3.4' }, 7 | { output: '0.4', version: '0.4.4' }, 8 | { output: '0.5', version: '0.5.7' }, 9 | { output: '0.6', version: '0.6.4' }, 10 | { output: '0.7', version: '0.7.7' }, 11 | ]; 12 | 13 | config.new( 14 | name='pyrra', 15 | specs=[ 16 | { 17 | // in version 0.7, the CRD was moved from 'config/crd/bases' into 'examples/kubernetes/manifests/setup' 18 | local url = if std.parseInt(std.split(v.output, '.')[1]) <= 6 then 19 | 'https://raw.githubusercontent.com/pyrra-dev/pyrra/v%s/config/crd/bases/pyrra.dev_servicelevelobjectives.yaml' % v.version 20 | else 21 | 'https://raw.githubusercontent.com/pyrra-dev/pyrra/v%s/examples/kubernetes/manifests/setup/pyrra-slo-CustomResourceDefinition.yaml' % v.version, 22 | output: v.output, 23 | prefix: '^dev\\.pyrra\\..*', 24 | crds: [url], 25 | localName: 'pyrra', 26 | } 27 | for v in versions 28 | ] 29 | ) 30 | -------------------------------------------------------------------------------- /libs/rabbitmq-messaging-topology-operator/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | local versions = [ 3 | { tag: 'v1.8.0', version: '1.8' }, 4 | { tag: 'v1.9.0', version: '1.9' }, 5 | { tag: 'v1.10.0', version: '1.10' }, 6 | { tag: 'v1.11.0', version: '1.11' }, 7 | { tag: 'v1.12.0', version: '1.12' }, 8 | { tag: 'v1.13.0', version: '1.13' }, 9 | { tag: 'v1.14.0', version: '1.14' }, 10 | { tag: 'v1.16.0', version: '1.16' }, 11 | ]; 12 | 13 | 14 | config.new( 15 | name='rabbitmq-messaging-topology-operator', 16 | specs=[ 17 | { 18 | output: v.version, 19 | openapi: 'http://localhost:8001/openapi/v2', 20 | prefix: '^com\\.rabbitmq\\..*', 21 | crds: [ 22 | 'https://github.com/rabbitmq/messaging-topology-operator/releases/download/%s/messaging-topology-operator.yaml' % v.tag, 23 | ], 24 | localName: 'rabbitmq-messaging-topology-operator', 25 | } 26 | for v in versions 27 | ] 28 | ) 29 | -------------------------------------------------------------------------------- /libs/rabbitmq/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | local versions = [ 3 | { tag: "1.7.0", version: "1.7" }, 4 | { tag: "1.14.0", version: "1.14" } 5 | { tag: "2.0.0", version: "2.0" }, 6 | { tag: "2.1.0", version: "2.1" }, 7 | { tag: "2.6.0", version: "2.6" }, 8 | { tag: "2.7.0", version: "2.7" }, 9 | { tag: "2.8.0", version: "2.8" }, 10 | { tag: "2.9.0", version: "2.9" }, 11 | { tag: "2.12.1", version: "2.12" }, 12 | ]; 13 | 14 | 15 | config.new( 16 | name='rabbitmq', 17 | specs=[ 18 | { 19 | output: v.version, 20 | openapi: 'http://localhost:8001/openapi/v2', 21 | prefix: '^com\\.rabbitmq\\..*', 22 | crds: [ 23 | 'https://github.com/rabbitmq/cluster-operator/releases/download/v%s/cluster-operator.yml' % v.tag 24 | ], 25 | localName: 'rabbitmq', 26 | } 27 | for v in versions 28 | ] 29 | ) 30 | -------------------------------------------------------------------------------- /libs/secrets-store-csi-driver/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | local versions = ['1.2.4', '1.3.3', '1.4.8']; 3 | local manifests = [ 4 | 'secrets-store.csi.x-k8s.io_secretproviderclasses.yaml', 5 | ]; 6 | 7 | config.new( 8 | name='secrets-store-csi-driver', 9 | specs=[ 10 | { 11 | output: std.join('.', std.split(version, '.')[:2]), 12 | prefix: '^io\\.x-k8s\\.csi\\.secrets-store\\..*', 13 | localName: 'secrets_store_csi_driver', 14 | patchDir: 'custom/secrets-store-csi-driver', 15 | crds: [ 16 | 'https://github.com/kubernetes-sigs/secrets-store-csi-driver/releases/download/v%s/%s' % 17 | [version, manifest] 18 | for manifest in manifests 19 | ], 20 | } 21 | for version in versions 22 | ] 23 | ) 24 | -------------------------------------------------------------------------------- /libs/securecodebox/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local versions = [ 4 | {output: '3.7', version:'3.7.0'}, 5 | {output: '3.14', version:'3.14.3'}, 6 | {output: '3.15', version:'3.15.2'}, 7 | ]; 8 | 9 | config.new( 10 | name='securecodebox', 11 | specs=[ 12 | { 13 | local url = 'https://raw.githubusercontent.com/secureCodeBox/secureCodeBox/v%s/operator/crds' % v.version, 14 | output: v.output, 15 | prefix: '^io\\.securecodebox\\..*', 16 | crds: [ 17 | '%s/cascading.securecodebox.io_cascadingrules.yaml' % url, 18 | '%s/execution.securecodebox.io_parsedefinitions.yaml' % url, 19 | '%s/execution.securecodebox.io_scancompletionhooks.yaml' % url, 20 | '%s/execution.securecodebox.io_scans.yaml' % url, 21 | '%s/execution.securecodebox.io_scantypes.yaml' % url, 22 | '%s/execution.securecodebox.io_scheduledscans.yaml' % url, 23 | ], 24 | localName: 'securecodebox', 25 | } 26 | for v in versions 27 | ] 28 | ) 29 | -------------------------------------------------------------------------------- /libs/spicedb-operator/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | local versions = [ 3 | { version: '1.14', tag: 'v1.14.0' }, 4 | ]; 5 | 6 | config.new( 7 | name='spicedb-operator', 8 | specs=[ 9 | { 10 | output: v.version, 11 | prefix: '^com\\.authzed\\..*', 12 | crds: ['https://raw.githubusercontent.com/authzed/spicedb-operator/%(tag)s/config/crds/authzed.com_spicedbclusters.yaml' % v], 13 | localName: 'spicedb-operator', 14 | } 15 | for v in versions 16 | ] 17 | ) 18 | -------------------------------------------------------------------------------- /libs/strimzi/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local versions = ['0.40', '0.41', '0.42','0.43', '0.44', '0.45']; 4 | 5 | config.new( 6 | name='strimzi', 7 | specs=[ 8 | { 9 | output: version, 10 | prefix: '^io\\.strimzi\\..*', 11 | crds: ['https://github.com/strimzi/strimzi-kafka-operator/releases/download/%(version)s.0/strimzi-crds-%(version)s.0.yaml' % { version: version }], 12 | localName: 'strimzi', 13 | } 14 | for version in versions 15 | ] 16 | ) 17 | -------------------------------------------------------------------------------- /libs/tektoncd/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local urlTemplate = 'https://raw.githubusercontent.com/tektoncd/pipeline/v%(version)s/config/300-crds/300-%(crd)s.yaml'; 4 | local crds = [ 5 | 'clustertask', 6 | 'customrun', 7 | 'pipeline', 8 | 'pipelinerun', 9 | 'resolutionrequest', 10 | 'task', 11 | 'taskrun', 12 | 'verificationpolicy', 13 | ]; 14 | 15 | local versions = ['0.62.1']; 16 | 17 | config.new( 18 | name='tektoncd', 19 | specs=[ 20 | { 21 | output: version, 22 | prefix: '^dev\\.tekton.*', 23 | crds: [ 24 | urlTemplate % { crd: crd, version: version } 25 | for crd in crds 26 | ], 27 | localName: 'tektoncd', 28 | } 29 | for version in versions 30 | ] 31 | ) 32 | -------------------------------------------------------------------------------- /libs/teleport-operator/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | local versions = ['12.2.2', '13.0.4', '13.1.5', '13.2.3', '14.1.1', '15.0.1', '16.4.8']; 3 | local manifests = [ 4 | 'resources.teleport.dev_accesslists.yaml', // added in 15.0 5 | 'resources.teleport.dev_githubconnectors.yaml', 6 | 'resources.teleport.dev_loginrules.yaml', 7 | 'resources.teleport.dev_oidcconnectors.yaml', 8 | 'resources.teleport.dev_oktaimportrules.yaml', // added in 13.2 9 | 'resources.teleport.dev_provisiontokens.yaml', // added in 13.0 10 | 'resources.teleport.dev_roles.yaml', 11 | 'resources.teleport.dev_rolesv6.yaml', // added in 15.0 12 | 'resources.teleport.dev_rolesv7.yaml', // added in 15.0 13 | 'resources.teleport.dev_samlconnectors.yaml', 14 | 'resources.teleport.dev_users.yaml', 15 | ]; 16 | 17 | config.new( 18 | name='teleport-operator', 19 | specs=[ 20 | { 21 | output: std.join('.', std.split(version, '.')[:2]), 22 | prefix: '^dev\\.teleport\\.resources\\..*', 23 | localName: 'teleport-operator', 24 | crds: [ 25 | 'https://raw.githubusercontent.com/gravitational/teleport/refs/tags/v%s/examples/chart/teleport-cluster/charts/teleport-operator/operator-crds/%s' % 26 | [version, manifest] 27 | for manifest in manifests 28 | ], 29 | } 30 | for version in versions 31 | ] 32 | ) 33 | -------------------------------------------------------------------------------- /libs/traefik/config.jsonnet: -------------------------------------------------------------------------------- 1 | // libs/traefik/config.jsonnet 2 | 3 | local config = import 'jsonnet/config.jsonnet'; 4 | local versions = [ 5 | { version: '3.3.3', chartTag: 'v34.3.0' }, 6 | { version: '2.11.2', chartTag: 'v27.0.2' }, 7 | { version: '2.10.6', chartTag: 'v26.0.0' }, 8 | ]; 9 | 10 | config.new( 11 | name='traefik', 12 | 13 | specs=[ 14 | { 15 | local url = 'https://raw.githubusercontent.com/traefik/traefik-helm-chart/%s/traefik/crds' % v.chartTag, 16 | 17 | output: v.version, 18 | prefix: '^io\\.traefik\\..*', 19 | 20 | crds: 21 | if std.startsWith(v.version, '3') then 22 | [ 23 | '%s/gateway-standard-install.yaml' % url, 24 | '%s/hub.traefik.io_accesscontrolpolicies.yaml' % url, 25 | '%s/hub.traefik.io_aiservices.yaml' % url, 26 | '%s/hub.traefik.io_apiaccesses.yaml' % url, 27 | '%s/hub.traefik.io_apibundles.yaml' % url, 28 | '%s/hub.traefik.io_apicatalogitems.yaml' % url, 29 | '%s/hub.traefik.io_apiplans.yaml' % url, 30 | '%s/hub.traefik.io_apiportals.yaml' % url, 31 | '%s/hub.traefik.io_apiratelimits.yaml' % url, 32 | '%s/hub.traefik.io_apis.yaml' % url, 33 | '%s/hub.traefik.io_apiversions.yaml' % url, 34 | '%s/hub.traefik.io_managedsubscriptions.yaml' % url, 35 | '%s/traefik.io_ingressroutes.yaml' % url, 36 | '%s/traefik.io_ingressroutetcps.yaml' % url, 37 | '%s/traefik.io_ingressrouteudps.yaml' % url, 38 | '%s/traefik.io_middlewares.yaml' % url, 39 | '%s/traefik.io_middlewaretcps.yaml' % url, 40 | '%s/traefik.io_serverstransports.yaml' % url, 41 | '%s/traefik.io_serverstransporttcps.yaml' % url, 42 | '%s/traefik.io_tlsoptions.yaml' % url, 43 | '%s/traefik.io_tlsstores.yaml' % url, 44 | '%s/traefik.io_traefikservices.yaml' % url, 45 | ] 46 | else 47 | [ 48 | '%s/traefik.io_ingressroutes.yaml' % url, 49 | '%s/traefik.io_ingressroutetcps.yaml' % url, 50 | '%s/traefik.io_ingressrouteudps.yaml' % url, 51 | '%s/traefik.io_middlewares.yaml' % url, 52 | '%s/traefik.io_middlewaretcps.yaml' % url, 53 | '%s/traefik.io_serverstransports.yaml' % url, 54 | '%s/traefik.io_serverstransporttcps.yaml' % url, 55 | '%s/traefik.io_tlsoptions.yaml' % url, 56 | '%s/traefik.io_tlsstores.yaml' % url, 57 | '%s/traefik.io_traefikservices.yaml' % url, 58 | ], 59 | localName: 'traefik', 60 | } 61 | for v in versions 62 | ] 63 | ) 64 | -------------------------------------------------------------------------------- /libs/triggermesh/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local versions = ['1.27.0']; 4 | 5 | config.new( 6 | name='triggermesh', 7 | specs=[ 8 | { 9 | output: v, 10 | crds: ['https://github.com/triggermesh/triggermesh/releases/download/v%s/triggermesh-crds.yaml' % v], 11 | prefix: '^io\\.triggermesh\\..*', 12 | localName: 'triggermesh', 13 | }, 14 | for v in versions 15 | ] 16 | ) -------------------------------------------------------------------------------- /libs/upbound-provider-opentofu/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | config.new( 4 | 'upbound-provider-opentofu', 5 | [ 6 | { 7 | output: 'provider-opentofu/0.2', 8 | prefix: '^io\\.upbound\\.opentofu\\..*', 9 | crds: ['https://doc.crds.dev/raw/github.com/upbound/provider-opentofu@v0.2.3'], 10 | localName: 'upbound_opentofu', 11 | }, 12 | ] 13 | ) 14 | -------------------------------------------------------------------------------- /libs/vault-secrets-operator/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | local versions = [ 4 | { version: '0.1.0' }, 5 | { version: '0.2.0' }, 6 | { version: '0.4.0' }, 7 | { version: '0.5.0' }, 8 | { version: '0.6.0' }, 9 | { version: '0.7.0' }, 10 | { version: '0.8.0' }, 11 | ]; 12 | 13 | config.new( 14 | name='vault-secrets-operator', 15 | specs=[ 16 | { 17 | local url = 'https://raw.githubusercontent.com/hashicorp/vault-secrets-operator/main/config/crd/bases', 18 | prefix: '^com\\.hashicorp\\.secrets\\..*', 19 | output: v.version, 20 | crds: [ 21 | '%s/secrets.hashicorp.com_hcpauths.yaml' % url, 22 | '%s/secrets.hashicorp.com_hcpvaultsecretsapps.yaml' % url, 23 | '%s/secrets.hashicorp.com_secrettransformations.yaml' % url, 24 | '%s/secrets.hashicorp.com_vaultauthglobals.yaml' % url, 25 | '%s/secrets.hashicorp.com_vaultauths.yaml' % url, 26 | '%s/secrets.hashicorp.com_vaultconnections.yaml' % url, 27 | '%s/secrets.hashicorp.com_vaultdynamicsecrets.yaml' % url, 28 | '%s/secrets.hashicorp.com_vaultpkisecrets.yaml' % url, 29 | '%s/secrets.hashicorp.com_vaultstaticsecrets.yaml' % url, 30 | ], 31 | localName: 'vault-secrets-operator', 32 | } 33 | for v in versions 34 | ] 35 | ) 36 | -------------------------------------------------------------------------------- /libs/vertical-pod-autoscaler/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | 3 | config.new( 4 | name='vertical-pod-autoscaler', 5 | specs=[ 6 | { 7 | output: '0.10', 8 | prefix: '^io\\.k8s\\.autoscaling\\..*', 9 | crds: [ 10 | 'https://raw.githubusercontent.com/kubernetes/autoscaler/vertical-pod-autoscaler-0.10.0/vertical-pod-autoscaler/deploy/vpa-v1-crd-gen.yaml', 11 | ], 12 | localName: 'vertical-pod-autoscaler', 13 | patchDir: 'custom/core', 14 | }, 15 | { 16 | output: '0.11', 17 | prefix: '^io\\.k8s\\.autoscaling\\..*', 18 | crds: [ 19 | 'https://raw.githubusercontent.com/kubernetes/autoscaler/vertical-pod-autoscaler-0.11.0/vertical-pod-autoscaler/deploy/vpa-v1-crd-gen.yaml', 20 | ], 21 | localName: 'vertical-pod-autoscaler', 22 | patchDir: 'custom/core', 23 | }, 24 | { 25 | output: '0.12', 26 | prefix: '^io\\.k8s\\.autoscaling\\..*', 27 | crds: [ 28 | 'https://raw.githubusercontent.com/kubernetes/autoscaler/vertical-pod-autoscaler-0.12.0/vertical-pod-autoscaler/deploy/vpa-v1-crd-gen.yaml', 29 | ], 30 | localName: 'vertical-pod-autoscaler', 31 | patchDir: 'custom/core', 32 | }, 33 | { 34 | output: '0.14', 35 | prefix: '^io\\.k8s\\.autoscaling\\..*', 36 | crds: [ 37 | 'https://raw.githubusercontent.com/kubernetes/autoscaler/vertical-pod-autoscaler-0.14.0/vertical-pod-autoscaler/deploy/vpa-v1-crd-gen.yaml', 38 | ], 39 | localName: 'vertical-pod-autoscaler', 40 | patchDir: 'custom/core', 41 | }, 42 | { 43 | output: '1.0.0', 44 | prefix: '^io\\.k8s\\.autoscaling\\..*', 45 | crds: [ 46 | 'https://raw.githubusercontent.com/kubernetes/autoscaler/vertical-pod-autoscaler-1.0.0/vertical-pod-autoscaler/deploy/vpa-v1-crd-gen.yaml', 47 | ], 48 | localName: 'vertical-pod-autoscaler', 49 | patchDir: 'custom/core', 50 | }, 51 | ] 52 | ) 53 | -------------------------------------------------------------------------------- /libs/vertical-pod-autoscaler/custom/core/autoscaling.libsonnet: -------------------------------------------------------------------------------- 1 | local d = import 'doc-util/main.libsonnet'; 2 | 3 | local withTargetRef = { 4 | '#withTargetRef':: d.fn(help='Set spec.TargetRef to `object`', args=[d.arg(name='object', type=d.T.object)]), 5 | withTargetRef(object): 6 | { spec+: { targetRef+: { 7 | apiVersion: object.apiVersion, 8 | kind: object.kind, 9 | name: object.metadata.name, 10 | } } }, 11 | }; 12 | 13 | local patch = { 14 | verticalPodAutoscaler+: { 15 | spec+: withTargetRef, 16 | }, 17 | 18 | verticalPodAutoscalerContainerResourcePolicy+: { 19 | '#':: d.pkg( 20 | name='verticalPodAutoscalerContainerResourcePolicy', 21 | url='', 22 | help='"An array of these is used as the input to `verticalPodAutoscaler.spec.resourcePolicy.withContainerPolicies()`."', 23 | ), 24 | 25 | '#withContainerName': d.fn( 26 | 'The name of the container that the policy applies to. If not specified, the policy serves as the default policy.', 27 | [d.arg('name', d.T.string)] 28 | ), 29 | withContainerName(name):: { 30 | containerName: name, 31 | }, 32 | 33 | '#withControlledResources': d.fn( 34 | 'Specifies the type of recommendations that will be computed (and possibly applied) by VPA. If not specified, the default of [ResourceCPU, ResourceMemory] will be used.', 35 | [d.arg('resources', d.T.array)] 36 | ), 37 | withControlledResources(resources):: { 38 | controlledResources: std.uniq(std.sort(resources)), 39 | }, 40 | 41 | '#withControlledResourcesMixin': d.fn( 42 | 'withControlledResourcesMixin is like withControlledResources, but appends to the existing list', 43 | [d.arg('resources', d.T.array)] 44 | ), 45 | withControlledResourcesMixin(resources):: { 46 | controlledResources: std.uniq(std.sort(super.resources+resources)), 47 | }, 48 | 49 | '#withControlledValues': d.fn( 50 | 'Which resource values should be controlled by VPA. Valid values are "RequestsAndLimits" and "RequestsOnly". The default is "RequestsAndLimits".', 51 | [d.arg('values', d.T.string)] 52 | ), 53 | withControlledValues(values):: { 54 | controlledValues: values, 55 | }, 56 | 57 | '#withMaxAllowed': d.fn( 58 | 'Specifies the maximum amount of resources that will be recommended for the container. The default is no maximum.', 59 | [d.arg('maxAllowed', d.T.object)], 60 | ), 61 | withMaxAllowed(maxAllowed):: { 62 | maxAllowed: maxAllowed, 63 | }, 64 | 65 | '#withMaxAllowedMixin': d.fn( 66 | 'Like withMaxAllowed but merges with the existing object.', 67 | [d.arg('maxAllowed', d.T.object)], 68 | ), 69 | withMaxAllowedMixin(maxAllowed):: { 70 | maxAllowed+: maxAllowed, 71 | }, 72 | 73 | '#withMinAllowed': d.fn( 74 | 'Specifies the minimal amount of resources that will be recommended for the container. The default is no minimum.', 75 | [d.arg('maxAllowed', d.T.object)], 76 | ), 77 | withMinAllowed(minAllowed):: { 78 | minAllowed: minAllowed, 79 | }, 80 | 81 | '#withMinAllowedMixin': d.fn( 82 | 'Like withMinAllowed but merges with the existing object.', 83 | [d.arg('minAllowed', d.T.object)], 84 | ), 85 | withMinAllowedMixin(minAllowed):: { 86 | minAllowed+: minAllowed, 87 | }, 88 | 89 | '#withMode': d.fn( 90 | 'Whether autoscaler is enabled for the container. Valid values are "Off" and "Auto". The default is "Auto".', 91 | [d.arg('minAllowed', d.T.string)], 92 | ), 93 | withMode(mode):: { 94 | mode: mode, 95 | }, 96 | }, 97 | }; 98 | 99 | { 100 | autoscaling+:: { 101 | v1+: patch, 102 | v1beta2+: patch, 103 | }, 104 | } 105 | -------------------------------------------------------------------------------- /libs/victoria-metrics-operator/config.jsonnet: -------------------------------------------------------------------------------- 1 | # libs//config.jsonnet 2 | local config = import 'jsonnet/config.jsonnet'; 3 | local versions = [ 4 | { tag: "v0.50.0", version: "0.50" }, 5 | { tag: "v0.49.1", version: "0.49" }, 6 | { tag: "v0.48.4", version: "0.48" }, 7 | { tag: "v0.47.3", version: "0.47" }, 8 | { tag: "v0.46.4", version: "0.46" } 9 | ]; 10 | 11 | config.new( 12 | name='victoria-metrics-operator', 13 | specs=[ 14 | { 15 | output: v.version, 16 | crds: ['https://github.com/VictoriaMetrics/operator/releases/download/%s/crd.yaml' % v.tag] , 17 | prefix: '^com\\.victoriametrics\\.operator\\..*', 18 | localName: 'victoriametrics', 19 | }, 20 | for v in versions 21 | ] 22 | ) 23 | -------------------------------------------------------------------------------- /libs/zalando-postgres-operator/config.jsonnet: -------------------------------------------------------------------------------- 1 | local config = import 'jsonnet/config.jsonnet'; 2 | local versions = [ 3 | { version: '1.14', tag: 'v1.14.0' }, 4 | { version: '1.13', tag: 'v1.13.0' }, 5 | { version: '1.12', tag: 'v1.12.2' }, 6 | { version: '1.11', tag: 'v1.11.0' }, 7 | { version: '1.8', tag: 'v1.8.2' }, 8 | ]; 9 | 10 | config.new( 11 | name='zalando-postgres-operator', 12 | specs=[ 13 | { 14 | output: v.version, 15 | prefix: '^do\\.zalan\\.acid\\..*', 16 | crds: [ 17 | 'https://github.com/zalando/postgres-operator/raw/' + v.tag + '/charts/postgres-operator/crds/operatorconfigurations.yaml', 18 | 'https://github.com/zalando/postgres-operator/raw/' + v.tag + '/charts/postgres-operator/crds/postgresqls.yaml', 19 | 'https://github.com/zalando/postgres-operator/raw/' + v.tag + '/charts/postgres-operator/crds/postgresteams.yaml', 20 | ], 21 | localName: 'zalando_postgres_operator', 22 | } 23 | for v in versions 24 | ] 25 | ) 26 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "log" 7 | "os" 8 | "path/filepath" 9 | 10 | "github.com/go-clix/cli" 11 | "github.com/google/go-jsonnet/formatter" 12 | "github.com/jsonnet-libs/k8s/pkg/model" 13 | "github.com/jsonnet-libs/k8s/pkg/render" 14 | "github.com/jsonnet-libs/k8s/pkg/swagger" 15 | "gopkg.in/yaml.v2" 16 | ) 17 | 18 | // Target defines an API subset to generate 19 | type Target struct { 20 | Output string `yaml:"output"` 21 | Crds []string `yaml:"crds"` 22 | Openapi string `yaml:"openapi"` 23 | Prefix string `yaml:"prefix"` 24 | PatchDir string `yaml:"patchDir"` 25 | ExtensionDir string `yaml:"extensionDir"` 26 | LocalName string `yaml:"localName"` 27 | Repository string `yaml:"repository"` 28 | Description string `yaml:"description"` 29 | } 30 | 31 | // Config holds settings for this generator 32 | type Config struct { 33 | Specs []Target `yaml:"specs"` 34 | } 35 | 36 | func main() { 37 | log.SetFlags(0) 38 | cmd := &cli.Command{ 39 | Use: "k8s-gen [versions]", 40 | Short: "k8s-gen generates the Jsonnet Kubernetes library from OpenAPI specs", 41 | } 42 | 43 | configFile := cmd.Flags().StringP("config", "c", "config.yml", "YAML configuration file") 44 | output := cmd.Flags().StringP("output", "o", ".", "directory to put artifacts into") 45 | 46 | cmd.Run = func(cmd *cli.Command, args []string) error { 47 | config := loadConfig(*configFile) 48 | 49 | os.Chdir(filepath.Dir(*configFile)) 50 | 51 | for _, t := range config.Specs { 52 | if len(args) > 0 && !hasStr(args, t.Output) { 53 | continue 54 | } 55 | 56 | definitions := make(swagger.Definitions) 57 | 58 | if len(t.Crds) > 0 { 59 | for _, url := range t.Crds { 60 | log.Printf("Generating '%s' from '%s, %s'", t.Output, url, t.Prefix) 61 | 62 | defs, err := swagger.Load(&swagger.CRDLoader{}, url) 63 | if err != nil { 64 | return err 65 | } 66 | for k, v := range defs { 67 | definitions[k] = v 68 | } 69 | } 70 | 71 | } else { 72 | log.Printf("Generating '%s' from '%s, %s'", t.Output, t.Openapi, t.Prefix) 73 | 74 | d, err := swagger.Load(&swagger.SwaggerLoader{}, t.Openapi) 75 | if err != nil { 76 | return err 77 | } 78 | definitions = d 79 | } 80 | 81 | groups := model.Load(&definitions, t.Prefix) 82 | path := filepath.Join(*output, t.Output) 83 | renderJsonnet(path, groups, t) 84 | } 85 | return nil 86 | } 87 | 88 | if err := cmd.Execute(); err != nil { 89 | log.Fatalln(err) 90 | } 91 | } 92 | 93 | func hasStr(slice []string, s string) bool { 94 | for _, sl := range slice { 95 | if sl == s { 96 | return true 97 | } 98 | } 99 | 100 | return false 101 | } 102 | 103 | func loadConfig(file string) Config { 104 | data, err := ioutil.ReadFile(file) 105 | if err != nil { 106 | log.Fatalln(err) 107 | } 108 | 109 | var c Config 110 | if err := yaml.Unmarshal(data, &c); err != nil { 111 | log.Fatalln(err) 112 | } 113 | 114 | return c 115 | } 116 | 117 | func renderJsonnet(dir string, groups map[string]model.Group, target Target) { 118 | if err := os.MkdirAll(dir, os.ModePerm); err != nil { 119 | log.Fatalln(err) 120 | } 121 | 122 | // gen.libsonnet 123 | index := render.Index(groups, target.LocalName, target.Repository, target.Output, target.Description) 124 | indexFile := filepath.Join(dir, render.IndexFile) 125 | if err := writeJsonnet(indexFile, index.String()); err != nil { 126 | log.Fatalln("writing gen.libsonnet:", err) 127 | } 128 | 129 | // _gen///.libsonnet 130 | gen := filepath.Join(dir, render.GenPrefix) 131 | if err := os.MkdirAll(gen, os.ModePerm); err != nil { 132 | log.Fatalln(err) 133 | } 134 | for name, group := range groups { 135 | g := render.Group(name, group) 136 | 137 | for fn, o := range g { 138 | file := filepath.Join(gen, name, fn) 139 | os.MkdirAll(filepath.Dir(file), os.ModePerm) 140 | if err := writeJsonnet(file, o.String()); err != nil { 141 | log.Fatalln(err) 142 | } 143 | } 144 | } 145 | 146 | var adds []string 147 | var err error 148 | 149 | customDirStat, err := os.Stat(target.PatchDir) 150 | if err == nil && customDirStat.IsDir() { 151 | // custom patches 152 | adds, err = copyDirLibsonnet(target.PatchDir, filepath.Join(dir, render.CustomPrefix)) 153 | if err != nil { 154 | log.Fatalln("Copying custom patches:", err) 155 | } 156 | } 157 | 158 | extDirStat, err := os.Stat(target.ExtensionDir) 159 | if err == nil && extDirStat.IsDir() { 160 | if _, err := copyDirLibsonnet(target.ExtensionDir, filepath.Join(dir, render.ExtPrefix)); err != nil { 161 | log.Fatalln("Copying extensions:", err) 162 | } 163 | } 164 | 165 | // main.libsonnet 166 | main := render.Main(adds) 167 | mainFile := filepath.Join(dir, render.MainFile) 168 | if err := writeJsonnet(mainFile, main.String()); err != nil { 169 | log.Fatalln(err) 170 | } 171 | 172 | } 173 | 174 | func writeJsonnet(to, data string) error { 175 | s, err := formatter.Format("", data, formatter.DefaultOptions()) 176 | if err != nil { 177 | return fmt.Errorf("%s: %s", err, data) 178 | } 179 | 180 | return ioutil.WriteFile(to, []byte(s), 0644) 181 | } 182 | 183 | func copyDirLibsonnet(dir, to string) ([]string, error) { 184 | // custom patches 185 | var adds []string 186 | if _, err := os.Stat(dir); err != nil { 187 | return nil, fmt.Errorf("%s does not exist", dir) 188 | } 189 | filepath.Walk(dir, func(name string, fi os.FileInfo, err error) error { 190 | if fi.IsDir() { 191 | return nil 192 | } 193 | if filepath.Ext(name) != ".libsonnet" { 194 | return nil 195 | } 196 | 197 | adds = append(adds, name) 198 | return nil 199 | }) 200 | 201 | for _, a := range adds { 202 | content, err := ioutil.ReadFile(a) 203 | if err != nil { 204 | return nil, err 205 | } 206 | 207 | a = filepath.Join(to, filepath.Base(a)) 208 | os.MkdirAll(filepath.Dir(a), os.ModePerm) 209 | if err := ioutil.WriteFile(a, content, 0644); err != nil { 210 | return nil, err 211 | } 212 | } 213 | 214 | return adds, nil 215 | } 216 | -------------------------------------------------------------------------------- /pkg/builder/builder.go: -------------------------------------------------------------------------------- 1 | // Package builder implements a simple way to generate arbitrary Jsonnet 2 | // directly from Go, using a functional API inspired by how Jsonnet is written 3 | // itself. 4 | package builder 5 | 6 | import ( 7 | "fmt" 8 | "strings" 9 | ) 10 | 11 | type named string 12 | 13 | func (n named) Name() string { 14 | return string(n) 15 | } 16 | 17 | type Doc struct { 18 | Locals []LocalType 19 | Root Type 20 | } 21 | 22 | func (d Doc) String() string { 23 | s := "" 24 | for _, l := range d.Locals { 25 | s += fmt.Sprintf("local %s = %s;\n", l.Name(), l.String()) 26 | } 27 | 28 | s += d.Root.String() 29 | return s 30 | } 31 | 32 | type Type interface { 33 | String() string 34 | Name() string 35 | } 36 | 37 | func indent(s string) string { 38 | split := strings.Split(s, "\n") 39 | for i := range split { 40 | split[i] = " " + split[i] 41 | } 42 | return strings.Join(split, "\n") 43 | } 44 | 45 | func dedent(s string) string { 46 | split := strings.Split(s, "\n") 47 | for i := range split { 48 | split[i] = strings.TrimPrefix(split[i], " ") 49 | } 50 | return strings.Join(split, "\n") 51 | } 52 | -------------------------------------------------------------------------------- /pkg/builder/collections.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | // Lists (arrays) 9 | type ListType struct { 10 | named 11 | items []Type 12 | } 13 | 14 | func List(name string, items ...Type) ListType { 15 | return ListType{named: named(name), items: items} 16 | } 17 | 18 | func (t ListType) String() string { 19 | s := "" 20 | for _, l := range t.items { 21 | s += fmt.Sprintf(", %s", l.String()) 22 | } 23 | s = strings.TrimPrefix(s, ", ") 24 | return fmt.Sprintf("[%s]", s) 25 | } 26 | -------------------------------------------------------------------------------- /pkg/builder/comments.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | import "strings" 4 | 5 | type CommentType struct { 6 | comment string 7 | value Type 8 | } 9 | 10 | func Comment(value Type, comment string) CommentType { 11 | return CommentType{ 12 | comment: comment, 13 | value: value, 14 | } 15 | } 16 | 17 | func (t CommentType) String() string { 18 | return t.value.String() 19 | } 20 | 21 | func (t CommentType) Name() string { 22 | return t.value.Name() 23 | } 24 | 25 | func (t CommentType) Comment() string { 26 | lines := strings.Split(t.comment, "\n") 27 | for i := range lines { 28 | lines[i] = "// " + lines[i] 29 | } 30 | return strings.Join(lines, "\n") 31 | } 32 | -------------------------------------------------------------------------------- /pkg/builder/condition.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | import "fmt" 4 | 5 | type ConditionType struct { 6 | name string 7 | If Type 8 | Then Type 9 | Else Type 10 | } 11 | 12 | func (c ConditionType) Name() string { 13 | return c.name 14 | } 15 | 16 | func (c ConditionType) String() string { 17 | return fmt.Sprintf("if %s then %s else %s", 18 | c.If.String(), 19 | c.Then.String(), 20 | c.Else.String(), 21 | ) 22 | } 23 | 24 | func IfThenElse(name string, If, Then, Else Type) ConditionType { 25 | return ConditionType{ 26 | name: name, 27 | If: If, 28 | Then: Then, 29 | Else: Else, 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /pkg/builder/docsonnet/docsonnet.go: -------------------------------------------------------------------------------- 1 | package docsonnet 2 | 3 | import ( 4 | j "github.com/jsonnet-libs/k8s/pkg/builder" 5 | ) 6 | 7 | const dPkg = "doc-util/main.libsonnet" 8 | 9 | func Import() j.Type { 10 | return j.Local(j.Import("d", dPkg)) 11 | } 12 | 13 | func Args(s ...string) []j.Type { 14 | if len(s)%2 != 0 { 15 | panic("Args expects even number of arguments (pairs)") 16 | } 17 | 18 | args := make([]j.Type, 0, len(s)/2) 19 | for i := range s { 20 | if i%2 != 0 { 21 | continue 22 | } 23 | 24 | args = append(args, j.Call("", "d.arg", []j.Type{ 25 | j.String("name", s[i]), 26 | j.Ref("type", "d.T."+s[i+1]), 27 | })) 28 | } 29 | 30 | return args 31 | } 32 | 33 | func Func(name, help string, args []j.Type) j.Type { 34 | return j.Hidden(j.Call("#"+name, "d.fn", j.Args( 35 | j.String("help", help), 36 | j.List("args", args...), 37 | ))) 38 | } 39 | 40 | func Obj(name, help string) j.Type { 41 | return j.Hidden(j.Call("#"+name, "d.obj", j.Args( 42 | j.String("help", help), 43 | ))) 44 | } 45 | 46 | func Pkg(name, url, help string) j.Type { 47 | return j.Hidden(j.Call("#", "d.pkg", j.Args( 48 | j.String("name", name), 49 | j.String("url", url), 50 | j.String("help", help), 51 | ))) 52 | } 53 | -------------------------------------------------------------------------------- /pkg/builder/func_call.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | // function call 9 | type CallType struct { 10 | named 11 | funcName string 12 | args []Type 13 | } 14 | 15 | func (c CallType) String() string { 16 | args := argsString(c.args, len(c.args) > 3) 17 | return fmt.Sprintf("%s(%s)", c.funcName, args) 18 | } 19 | 20 | func Call(name, funcName string, args []Type) CallType { 21 | for k, v := range args { 22 | if v == nil { 23 | panic(fmt.Sprintf("argument `%v` in call to `%s` is nil", k, funcName)) 24 | } 25 | } 26 | 27 | return CallType{ 28 | named: named(name), 29 | funcName: funcName, 30 | args: args, 31 | } 32 | } 33 | 34 | // CallChain allows to chain multiple calls 35 | func CallChain(name string, calls ...CallType) CallType { 36 | if len(calls) == 1 { 37 | panic("callChain with a single call is redundant") 38 | } 39 | 40 | ln := "" 41 | if len(calls) > 1 { 42 | ln = "\n" 43 | } 44 | 45 | var last Type = Ref("", "") 46 | for i, c := range calls { 47 | last = Call("", 48 | strings.TrimPrefix( 49 | fmt.Sprintf("%s%s.%s", last.String(), ln, c.funcName), 50 | ln+".", 51 | ), 52 | c.args, 53 | ) 54 | 55 | if i == len(calls)-1 { 56 | l := last.(CallType) 57 | l.named = named(name) 58 | return l 59 | } 60 | } 61 | 62 | panic("loop did not return. This should never happen") 63 | } 64 | -------------------------------------------------------------------------------- /pkg/builder/funcs.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | // function definition 9 | type FuncType struct { 10 | named 11 | args []Type 12 | value Type 13 | large bool 14 | } 15 | 16 | func Func(name string, args []Type, returns Type) FuncType { 17 | return FuncType{ 18 | named: named(name), 19 | args: args, 20 | value: returns, 21 | } 22 | } 23 | 24 | func LargeFunc(name string, args []Type, returns Type) FuncType { 25 | f := Func(name, args, returns) 26 | f.large = true 27 | return f 28 | } 29 | 30 | func (f FuncType) Args() string { 31 | s := argsString(f.args, f.large) 32 | if f.large { 33 | return s + "\n" 34 | } 35 | return s 36 | } 37 | 38 | func argsString(m []Type, breakLine bool) string { 39 | sep := SeparatorConcise 40 | if breakLine { 41 | sep = SeparatorLong + " " 42 | } 43 | 44 | s := "" 45 | if breakLine { 46 | s = sep 47 | } 48 | 49 | for _, v := range m { 50 | if _, ok := v.(RequiredArgType); ok { 51 | s += fmt.Sprintf("%s"+sep, v.Name()) 52 | } else { 53 | s += fmt.Sprintf("%s=%s"+sep, v.Name(), v.String()) 54 | } 55 | } 56 | 57 | if breakLine { 58 | s = strings.TrimPrefix(s, ",") 59 | s = strings.TrimSuffix(s, sep) 60 | return s 61 | } 62 | 63 | s = strings.TrimSuffix(s, sep) 64 | return s 65 | } 66 | 67 | func (f FuncType) String() string { 68 | return f.value.String() 69 | } 70 | 71 | // function arguments 72 | func Args(args ...Type) []Type { 73 | return args 74 | } 75 | 76 | // required arguments (no default value) 77 | type RequiredArgType struct { 78 | value Type 79 | } 80 | 81 | func (r RequiredArgType) Name() string { 82 | return r.value.Name() 83 | } 84 | 85 | func (r RequiredArgType) String() string { 86 | return r.value.String() 87 | } 88 | 89 | func Required(t Type) RequiredArgType { 90 | return RequiredArgType{t} 91 | } 92 | -------------------------------------------------------------------------------- /pkg/builder/funcs_test.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | import "testing" 4 | 5 | func TestLargeFunc(t *testing.T) { 6 | want := ` 7 | { 8 | large( 9 | string="string", 10 | bool=true, 11 | number=5, 12 | map={ foo: "bar" } 13 | ):: { 14 | m: map, 15 | b: bool, 16 | s: string, 17 | n: number 18 | } 19 | } 20 | ` 21 | 22 | o := Object("", 23 | Hidden(LargeFunc("large", 24 | Args( 25 | String("string", "string"), 26 | Bool("bool", true), 27 | Int("number", 5), 28 | ConciseObject("map", String("foo", "bar")), 29 | ), 30 | Object("", 31 | Ref("m", "map"), 32 | Ref("b", "bool"), 33 | Ref("s", "string"), 34 | Ref("n", "number"), 35 | ), 36 | )), 37 | ) 38 | 39 | assertRender(t, o, want) 40 | } 41 | -------------------------------------------------------------------------------- /pkg/builder/import.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | import "fmt" 4 | 5 | type ImportType struct { 6 | named 7 | pkg string 8 | raw bool 9 | } 10 | 11 | func (i ImportType) String() string { 12 | op := "import" 13 | if i.raw { 14 | op += "str" 15 | } 16 | 17 | return fmt.Sprintf(`(%s '%s')`, op, i.pkg) 18 | } 19 | 20 | func Import(name, pkg string) ImportType { 21 | return ImportType{named: named(name), pkg: pkg, raw: false} 22 | } 23 | 24 | func ImportStr(name, pkg string) ImportType { 25 | return ImportType{named: named(name), pkg: pkg, raw: true} 26 | } 27 | -------------------------------------------------------------------------------- /pkg/builder/locals.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | type LocalType struct { 4 | value Type 5 | } 6 | 7 | func Local(value Type) LocalType { 8 | return LocalType{value} 9 | } 10 | 11 | func (t LocalType) String() string { 12 | return t.value.String() 13 | } 14 | 15 | func (t LocalType) Name() string { 16 | return t.value.Name() 17 | } 18 | -------------------------------------------------------------------------------- /pkg/builder/marshal.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | ) 7 | 8 | func Marshal(name string, ptr interface{}) Type { 9 | // get rid of custom types, but retain metadata (json) 10 | jsonData, err := json.Marshal(ptr) 11 | if err != nil { 12 | panic(err) 13 | } 14 | 15 | var data map[string]interface{} 16 | if err := json.Unmarshal(jsonData, &data); err != nil { 17 | panic(err) 18 | } 19 | 20 | return marshal(name, data) 21 | } 22 | 23 | func marshal(name string, ptr interface{}) Type { 24 | switch t := ptr.(type) { 25 | case int: 26 | return Int(name, t) 27 | case float64: 28 | return Int(name, int(t)) 29 | case string: 30 | return String(name, t) 31 | case bool: 32 | return Bool(name, t) 33 | case map[string]interface{}: 34 | childs := []Type{} 35 | for k, v := range t { 36 | childs = append(childs, marshal(k, v)) 37 | } 38 | return Object(name, childs...) 39 | case []interface{}: 40 | childs := []Type{} 41 | for _, v := range t { 42 | childs = append(childs, marshal("", v)) 43 | } 44 | return List(name, childs...) 45 | case nil: 46 | return Null(name) 47 | default: 48 | panic(fmt.Sprintf("unsupported type: %T", ptr)) 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /pkg/builder/objects.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | const ( 9 | SeparatorLong = ",\n" 10 | SeparatorConcise = ", " 11 | ) 12 | 13 | // Objects (dicts) 14 | type ObjectType struct { 15 | named 16 | order []string 17 | children map[string]Type 18 | concise bool 19 | } 20 | 21 | func Object(name string, children ...Type) ObjectType { 22 | c := make(map[string]Type) 23 | order := make([]string, len(children)) 24 | i := 0 25 | for _, child := range children { 26 | if v, ok := c[child.Name()]; ok { 27 | panic(fmt.Sprintf("key clash: trying to add `%v` as `%s`, but `%v` already uses this key", child, child.Name(), v)) 28 | } 29 | 30 | key := escapeKey(child.Name()) 31 | c[key] = child 32 | order[i] = key 33 | i++ 34 | } 35 | 36 | return ObjectType{ 37 | named: named(name), 38 | children: c, 39 | order: order, 40 | } 41 | } 42 | 43 | func escapeKey(s string) string { 44 | switch s { 45 | case "assert", "else", "error", "false", "for", "function", "if", 46 | "import", "importstr", "in", "local", "null", "tailstrict", 47 | "then", "self", "super", "true": 48 | return fmt.Sprintf(`'%s'`, s) 49 | default: 50 | if strings.HasPrefix(s, "#") { 51 | return fmt.Sprintf(`'%s'`, s) 52 | } 53 | if strings.ContainsAny(s, "-./") { 54 | return fmt.Sprintf(`'%s'`, s) 55 | } 56 | return s 57 | } 58 | } 59 | 60 | func ConciseObject(name string, children ...Type) ObjectType { 61 | o := Object(name, children...) 62 | o.concise = true 63 | return o 64 | } 65 | 66 | func (o ObjectType) String() string { 67 | if len(o.children) == 0 { 68 | return "{}" 69 | } 70 | if o.concise { 71 | return o.ConciseString() 72 | } 73 | 74 | s := printChildren(o.children, o.order, SeparatorLong) 75 | return fmt.Sprintf("{\n%s\n}", indent(s)) 76 | } 77 | 78 | func (o ObjectType) ConciseString() string { 79 | s := printChildren(o.children, o.order, SeparatorConcise) 80 | return fmt.Sprintf("{ %s }", strings.TrimSuffix(s, ",")) 81 | } 82 | 83 | func printChildren(children map[string]Type, order []string, s string) string { 84 | j := "" 85 | for _, name := range order { 86 | c := children[name] 87 | colon := ":" 88 | value := c.String() 89 | 90 | if cmt, ok := c.(CommentType); ok { 91 | c = cmt.value 92 | if s == SeparatorLong { 93 | j += cmt.Comment() + "\n" 94 | } 95 | } 96 | 97 | switch t := c.(type) { 98 | case FuncType: 99 | name = fmt.Sprintf("%s(%s)", name, t.Args()) 100 | case HiddenType: 101 | colon = "::" 102 | switch h := t.value.(type) { 103 | case MergeType: 104 | colon = "+::" 105 | case FuncType: 106 | name = fmt.Sprintf("%s(%s)", name, h.Args()) 107 | } 108 | case LocalType: 109 | colon = " =" 110 | // using t.Name() here (unescaped), cause we define an identifier 111 | name = "local " + t.Name() 112 | case MergeType: 113 | colon = "+:" 114 | } 115 | 116 | j += fmt.Sprintf("%s%s %s"+s, name, colon, value) 117 | 118 | } 119 | j = strings.TrimSuffix(j, s) 120 | return j 121 | } 122 | -------------------------------------------------------------------------------- /pkg/builder/objects_test.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestObjectPrimitive(t *testing.T) { 8 | want := ` 9 | { 10 | int: 1, 11 | string: "hello", 12 | bool: true, 13 | float: 23.5, 14 | null: null 15 | } 16 | ` 17 | 18 | o := Object("", 19 | Int("int", 1), 20 | String("string", "hello"), 21 | Bool("bool", true), 22 | Float("float", 23.5), 23 | Null("null"), 24 | ) 25 | 26 | assertRender(t, o, want) 27 | } 28 | 29 | func TestObjectFuncs(t *testing.T) { 30 | want := ` 31 | { 32 | regular(s="string", b=false, i=3, f=23.5): { 33 | s: s, 34 | b: b, 35 | i: i, 36 | f: f 37 | }, 38 | hidden(h=3): h 39 | } 40 | ` 41 | 42 | o := Object("", 43 | Func("regular", 44 | Args( 45 | String("s", "string"), 46 | Bool("b", false), 47 | Int("i", 3), 48 | Float("f", 23.5), 49 | ), 50 | Object("", 51 | Ref("s", "s"), 52 | Ref("b", "b"), 53 | Ref("i", "i"), 54 | Ref("f", "f"), 55 | ), 56 | ), 57 | Func("hidden", 58 | Args(Int("h", 3)), 59 | Ref("h", "h"), 60 | ), 61 | ) 62 | 63 | assertRender(t, o, want) 64 | } 65 | 66 | func TestObjectMerge(t *testing.T) { 67 | want := ` 68 | { 69 | regular+: { 70 | int: 2 71 | }, 72 | hidden+:: { 73 | incognito: "yes!" 74 | } 75 | } 76 | ` 77 | 78 | o := Object("", 79 | Merge(Object("regular", 80 | Int("int", 2), 81 | )), 82 | Hidden(Merge(Object("hidden", 83 | String("incognito", "yes!"), 84 | ))), 85 | ) 86 | 87 | assertRender(t, o, want) 88 | } 89 | 90 | func TestObjectLocal(t *testing.T) { 91 | want := ` 92 | { 93 | local msg = "hello", 94 | greet: msg, 95 | local sth = "else" 96 | } 97 | ` 98 | 99 | o := Object("", 100 | Local(String("msg", "hello")), 101 | Ref("greet", "msg"), 102 | Local(String("sth", "else")), 103 | ) 104 | 105 | assertRender(t, o, want) 106 | } 107 | 108 | func TestObjectConcise(t *testing.T) { 109 | want := `{ s: "string", b: false }` 110 | o := ConciseObject("", 111 | String("s", "string"), 112 | Bool("b", false), 113 | ) 114 | assertRender(t, o, want) 115 | } 116 | -------------------------------------------------------------------------------- /pkg/builder/operators.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | // merge 9 | type MergeType struct { 10 | value Type 11 | } 12 | 13 | func (t MergeType) String() string { 14 | return t.value.String() 15 | } 16 | 17 | func (t MergeType) Name() string { 18 | return t.value.Name() 19 | } 20 | 21 | func Merge(value Type) MergeType { 22 | if _, ok := value.(HiddenType); ok { 23 | panic("HiddenType cannot be a child of MergeType, it must be the other way around.") 24 | } 25 | return MergeType{value} 26 | } 27 | 28 | // hidden field (::) 29 | type HiddenType struct { 30 | value Type 31 | } 32 | 33 | func Hidden(value Type) HiddenType { 34 | if _, ok := value.(CommentType); ok { 35 | panic("CommentType cannot be a child of HiddenType, it must be the other way around.") 36 | } 37 | return HiddenType{value} 38 | } 39 | 40 | func (h HiddenType) Name() string { 41 | return h.value.Name() 42 | } 43 | 44 | func (h HiddenType) String() string { 45 | return h.value.String() 46 | } 47 | 48 | // arithmetic 49 | type ArithType struct { 50 | named 51 | operator string 52 | operands []Type 53 | } 54 | 55 | func (m ArithType) String() string { 56 | rendered := make([]string, len(m.operands)) 57 | for i, o := range m.operands { 58 | rendered[i] = o.String() 59 | } 60 | 61 | s := strings.Join(rendered, fmt.Sprintf(" %s ", m.operator)) 62 | return s 63 | } 64 | 65 | func Add(name string, o ...Type) ArithType { 66 | return ArithType{named: named(name), operator: "+", operands: o} 67 | } 68 | 69 | func Sub(name string, o ...Type) ArithType { 70 | return ArithType{named: named(name), operator: "-", operands: o} 71 | } 72 | 73 | func Div(name string, o ...Type) ArithType { 74 | return ArithType{named: named(name), operator: "/", operands: o} 75 | } 76 | 77 | func Mul(name string, o ...Type) ArithType { 78 | return ArithType{named: named(name), operator: "*", operands: o} 79 | } 80 | 81 | func Mod(name string, o ...Type) ArithType { 82 | return ArithType{named: named(name), operator: "%", operands: o} 83 | } 84 | 85 | // string formatting 86 | type SprintfType struct { 87 | named 88 | template string 89 | values []Type 90 | } 91 | 92 | func Sprintf(name, format string, values ...Type) SprintfType { 93 | return SprintfType{ 94 | named: named(name), 95 | template: format, 96 | values: values, 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /pkg/builder/primitives.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "strconv" 7 | ) 8 | 9 | // strings 10 | type StringType struct { 11 | named 12 | value string 13 | } 14 | 15 | func String(name, value string) StringType { 16 | return StringType{named: named(name), value: value} 17 | } 18 | 19 | func (s StringType) String() string { 20 | data, err := json.Marshal(s.value) 21 | if err != nil { 22 | panic(err) 23 | } 24 | return string(data) 25 | } 26 | 27 | // floats 28 | type FloatType struct { 29 | named 30 | value float64 31 | } 32 | 33 | func Float(name string, value float64) FloatType { 34 | return FloatType{named: named(name), value: value} 35 | } 36 | 37 | func (f FloatType) String() string { 38 | return fmt.Sprintf("%f", f.value) 39 | } 40 | 41 | // ints 42 | type IntType struct { 43 | named 44 | value int 45 | } 46 | 47 | func Int(name string, value int) IntType { 48 | return IntType{named: named(name), value: value} 49 | } 50 | 51 | func (s IntType) String() string { 52 | return strconv.Itoa(s.value) 53 | } 54 | 55 | // bools 56 | type BoolType struct { 57 | named 58 | value bool 59 | } 60 | 61 | func Bool(name string, value bool) BoolType { 62 | return BoolType{named: named(name), value: value} 63 | } 64 | 65 | func (s BoolType) String() string { 66 | return fmt.Sprintf(`%v`, s.value) 67 | } 68 | 69 | // null 70 | type NullType struct { 71 | named 72 | } 73 | 74 | func Null(name string) NullType { 75 | return NullType{named: named(name)} 76 | } 77 | 78 | func (s NullType) String() string { 79 | return "null" 80 | } 81 | -------------------------------------------------------------------------------- /pkg/builder/reference.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | type RefType struct { 4 | named 5 | to string 6 | } 7 | 8 | func Ref(name, to string) RefType { 9 | return RefType{named(name), to} 10 | } 11 | 12 | func (r RefType) String() string { 13 | return r.to 14 | } 15 | -------------------------------------------------------------------------------- /pkg/builder/utils_test.go: -------------------------------------------------------------------------------- 1 | package builder 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "io/ioutil" 7 | "os" 8 | "os/exec" 9 | "path/filepath" 10 | "strings" 11 | "testing" 12 | ) 13 | 14 | func assertRender(t *testing.T, o Type, s string) { 15 | got := Doc{Root: o}.String() 16 | s = strings.TrimPrefix(s, "\n") 17 | s = strings.TrimSuffix(s, "\n") 18 | 19 | diff(t, s, got) 20 | } 21 | 22 | func diff(t *testing.T, want, got string) { 23 | dir, err := ioutil.TempDir("", "diff") 24 | check(t, err) 25 | defer os.RemoveAll(dir) 26 | 27 | check(t, ioutil.WriteFile(filepath.Join(dir, "want"), []byte(want), os.ModePerm)) 28 | check(t, ioutil.WriteFile(filepath.Join(dir, "got"), []byte(got), os.ModePerm)) 29 | 30 | buf := bytes.Buffer{} 31 | want = filepath.Join(dir, "want") 32 | got = filepath.Join(dir, "got") 33 | cmd := exec.Command("bash", "-c", fmt.Sprintf("diff -u -N %s %s | cat -vet | colordiff", want, got)) 34 | cmd.Stderr = os.Stderr 35 | cmd.Stdout = &buf 36 | err = cmd.Run() 37 | 38 | // the diff utility exits with `1` if there are differences. We need to not fail there. 39 | if exitError, ok := err.(*exec.ExitError); ok && err != nil { 40 | if exitError.ExitCode() != 1 { 41 | return 42 | } 43 | } 44 | 45 | out := buf.String() 46 | if out == "" { 47 | return 48 | } 49 | 50 | out = fmt.Sprintf("diff -u -N %s %s\n%s", want, got, out) 51 | fmt.Println(out) 52 | t.FailNow() 53 | 54 | return 55 | } 56 | 57 | func check(t *testing.T, err error) { 58 | if err != nil { 59 | fmt.Println(err) 60 | t.FailNow() 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /pkg/model/gvk.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "regexp" 7 | "strings" 8 | 9 | "github.com/jsonnet-libs/k8s/pkg/swagger" 10 | ) 11 | 12 | // Group represents a group, like "core" or "apps" 13 | type Group map[string]Version 14 | 15 | // IDs represents a group name -> group mapping constructed by 16 | type IDs map[string]map[string]string 17 | 18 | // newGroups creates all Groups from the swagger definitions 19 | func newGroups(defs swagger.Definitions, ids IDs) map[string]Group { 20 | groups := make(map[string]Group) 21 | 22 | for groupName, group := range ids { 23 | g := make(Group) 24 | 25 | for versionName, id := range group { 26 | // add dot to end to avoid partial matches. 27 | // also escape the dots 28 | xp := regexp.QuoteMeta(id + ".") 29 | v := newVersion(defs.Sub(xp)) 30 | 31 | v.APIVersion = apiVersion(groupName, versionName) 32 | g[versionName] = v 33 | } 34 | 35 | groups[groupName] = g 36 | } 37 | 38 | return groups 39 | } 40 | 41 | // apiVersion formats group and version into the value used for `apiVersion:` 42 | // fields 43 | func apiVersion(group, version string) string { 44 | if group == "core" { 45 | return version 46 | } 47 | return fmt.Sprintf("%s/%s", group, version) 48 | } 49 | 50 | // Version represents a specific version of the API, like "apps/v1" or "core/v1" 51 | type Version struct { 52 | APIVersion string 53 | Kinds map[string]Kind 54 | } 55 | 56 | // MarshalJSON marsals wraps json.Marshal and adds the `_apiVersion` field 57 | func (v Version) MarshalJSON() ([]byte, error) { 58 | data := map[string]interface{}{ 59 | "_apiVersion": v.APIVersion, 60 | } 61 | for k, v := range v.Kinds { 62 | data[k] = v 63 | } 64 | return json.Marshal(data) 65 | } 66 | 67 | // UnmarshalJSON wraps json.Unmarshal and decodes the "private" `_apiVersion` field 68 | func (v *Version) UnmarshalJSON(d []byte) error { 69 | data := make(map[string]interface{}) 70 | if err := json.Unmarshal(d, &data); err != nil { 71 | return err 72 | } 73 | 74 | if val, ok := data["_apiVersion"]; ok { 75 | v.APIVersion = val.(string) 76 | } 77 | delete(data, "_apiVersion") 78 | 79 | return mapToStruct(data, &v.Kinds) 80 | } 81 | 82 | func mapToStruct(m map[string]interface{}, ptr interface{}) error { 83 | kinds, err := json.Marshal(m) 84 | if err != nil { 85 | return err 86 | } 87 | 88 | return json.Unmarshal(kinds, ptr) 89 | } 90 | 91 | // newVersion creates a new version from swagger definitions 92 | func newVersion(d swagger.Definitions) Version { 93 | ver := Version{ 94 | Kinds: make(map[string]Kind), 95 | } 96 | 97 | for k, v := range d { 98 | name := reSubMatchMap(expr, k)["kind"] 99 | ver.Kinds[CamelLower(name)] = newKind(*v, name) 100 | } 101 | return ver 102 | } 103 | 104 | // Kind represents both actual Kinds ("Deployment", "Version"), but also other 105 | // parts of the API, like "Container" or "ServicePort". 106 | type Kind struct { 107 | Help string `json:"help"` 108 | 109 | Kind string `json:"kind"` 110 | Group string `json:"group"` 111 | Version string `json:"version"` 112 | 113 | // constructor 114 | New *Constructor `json:"new"` 115 | 116 | // modifiers 117 | Modifiers modifiers `json:"modifiers,omitempty"` 118 | 119 | // Cluster or Namespaced scope, ignored if unset 120 | Scope *string 121 | } 122 | 123 | // APIVersion constructs the full api path for a group 124 | func (k Kind) APIVersion() string { 125 | if k.Group == "" { 126 | return k.Version 127 | } 128 | 129 | return k.Group + "/" + k.Version 130 | } 131 | 132 | type modifiers map[string]interface{} 133 | 134 | func (mPtr *modifiers) UnmarshalJSON(data []byte) error { 135 | if *mPtr == nil { 136 | *mPtr = make(modifiers) 137 | } 138 | m := *mPtr 139 | 140 | // handle object vs func 141 | tmp := make(map[string]interface{}) 142 | if err := json.Unmarshal(data, &tmp); err != nil { 143 | return err 144 | } 145 | 146 | for k, i := range tmp { 147 | c, ok := i.(map[string]interface{}) 148 | if !ok { 149 | continue 150 | } 151 | 152 | if _, ok := c["fields"]; ok { 153 | var obj Object 154 | if err := mapToStruct(c, &obj); err != nil { 155 | panic(err) 156 | } 157 | m[k] = obj 158 | } else { 159 | var mod Modifier 160 | if err := mapToStruct(c, &mod); err != nil { 161 | panic(err) 162 | } 163 | m[k] = mod 164 | } 165 | } 166 | 167 | return nil 168 | } 169 | 170 | // newKind creates a new kind from it's swagger schema, creating modifiers for 171 | // all fields it has. If not a real kind (no ObjectMeta), no constructor will be 172 | // created 173 | func newKind(d swagger.Schema, name string) Kind { 174 | kind := Kind{ 175 | // Help text: description 176 | Help: safeStr(d.Desc), 177 | Scope: d.Scope, 178 | } 179 | 180 | gvk, real := d.GroupVersionKind() 181 | if real { 182 | kind.Kind = gvk.Kind 183 | kind.Group = gvk.Group 184 | kind.Version = gvk.Version 185 | } 186 | 187 | // modifiers for properties 188 | kind.Modifiers = modsForProps(d.Props, "", true, false, false) 189 | 190 | // real resource? add constructor, remove withKind 191 | if real { 192 | delete(kind.Modifiers, "withKind") 193 | kind.New = &Constructor{ 194 | Help: fmt.Sprintf("new returns an instance of %s", strings.Title(name)), 195 | Args: []Parameter{{Key: "name"}}, 196 | } 197 | } 198 | 199 | return kind 200 | } 201 | 202 | // safeStr escapes control characters and double quotes 203 | func safeStr(s string) string { 204 | quotedS := fmt.Sprintf("%q", s) 205 | return strings.TrimPrefix(strings.TrimSuffix(quotedS, `""`), `""`) 206 | } 207 | 208 | func reSubMatchMap(r *regexp.Regexp, str string) map[string]string { 209 | match := r.FindStringSubmatch(str) 210 | subMatchMap := make(map[string]string) 211 | for i, name := range r.SubexpNames() { 212 | if i != 0 { 213 | subMatchMap[name] = match[i] 214 | } 215 | } 216 | 217 | return subMatchMap 218 | } 219 | -------------------------------------------------------------------------------- /pkg/model/gvk_test.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "testing" 7 | 8 | "github.com/stretchr/testify/assert" 9 | "github.com/stretchr/testify/require" 10 | ) 11 | 12 | func TestVersionRemarshal(t *testing.T) { 13 | version := Version{ 14 | APIVersion: "apps/v1", 15 | Kinds: map[string]Kind{ 16 | "deployment": { 17 | Help: "Deployments deploy things", 18 | }, 19 | "daemonSet": { 20 | Help: "DaemonSets are the worst demons", 21 | }, 22 | "statefulSet": { 23 | Help: "StatefulSets are sets of EmpireStateBuildings", 24 | }, 25 | }, 26 | } 27 | 28 | data, err := json.Marshal(version) 29 | require.NoError(t, err) 30 | 31 | fmt.Println(string(data)) 32 | 33 | var got Version 34 | json.Unmarshal(data, &got) 35 | 36 | assert.Equal(t, version, got) 37 | } 38 | -------------------------------------------------------------------------------- /pkg/model/load.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "fmt" 5 | "regexp" 6 | 7 | "github.com/jsonnet-libs/k8s/pkg/swagger" 8 | ) 9 | 10 | var expr = regexp.MustCompile(`(?mU)(?P.+)\.((?P\w*)\.)?(?P\w*)\.(?P\w*)$`) 11 | 12 | // Short handles for longer types 13 | const ( 14 | ListMetaID = "io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta" 15 | ObjectMetaID = "io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta" 16 | ) 17 | 18 | // Load parses swagger definitions into the data model 19 | func Load(definitions *swagger.Definitions, prefix string) map[string]Group { 20 | prefixExpr := regexp.MustCompile(prefix) 21 | defs := definitions.Filter(func(k string, v swagger.Schema) bool { 22 | if !expr.MatchString(k) { 23 | return false 24 | } 25 | 26 | meta := v.Props["metadata"] 27 | if meta == nil || meta.DollarRef == nil { 28 | // Check if domain is in the prefix regex 29 | return prefixExpr.MatchString(k) 30 | } 31 | return meta.Ref() != ListMetaID 32 | }) 33 | 34 | ids := transform(defs) 35 | return newGroups(defs, ids) 36 | } 37 | 38 | // transform creates an ID-table that maps our structure to the one of the 39 | // swagger spec: 40 | // 41 | // "apps": map[string]string{ 42 | // "v1": "io.k8s.api.apps.v1", 43 | // "v1beta1": "io.k8s.api.apps.v1beta1", 44 | // "v1beta2": "io.k8s.api.apps.v1beta2", 45 | // }, 46 | // 47 | // These are used in newGroups to match the all kinds for a given version 48 | func transform(defs swagger.Definitions) IDs { 49 | groups := make(IDs) 50 | for k := range defs { 51 | m := reSubMatchMap(expr, k) 52 | 53 | groupName := m["group"] 54 | versionName := m["version"] 55 | 56 | if groupName == "" { 57 | groupName = "nogroup" 58 | if groups[groupName] == nil { 59 | groups[groupName] = make(map[string]string) 60 | } 61 | groups[groupName][versionName] = fmt.Sprintf("%s.%s", 62 | m["domain"], m["version"], 63 | ) 64 | continue 65 | } 66 | 67 | if groups[groupName] == nil { 68 | groups[groupName] = make(map[string]string) 69 | } 70 | 71 | groups[groupName][versionName] = fmt.Sprintf("%s.%s.%s", 72 | m["domain"], m["group"], m["version"], 73 | ) 74 | } 75 | 76 | return groups 77 | } 78 | -------------------------------------------------------------------------------- /pkg/model/modifiers.go: -------------------------------------------------------------------------------- 1 | package model 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/fatih/camelcase" 8 | "github.com/jsonnet-libs/k8s/pkg/swagger" 9 | ) 10 | 11 | type excludeType string 12 | 13 | const ( 14 | notExcluded = "" 15 | excludeEverywhere = "everywhere" 16 | excludeInRootOnly = "root" 17 | ) 18 | 19 | var propertiesWithoutModifiers = map[string]excludeType{ 20 | "apiVersion": excludeInRootOnly, 21 | "status": excludeEverywhere, 22 | } 23 | 24 | // Modifier is a function that returns a patch to modify the value at `Target` 25 | type Modifier struct { 26 | Help string `json:"help"` 27 | 28 | // Arg is the name of the functions argument 29 | Arg Parameter `json:"arg"` 30 | // Target is the jsonpath to the field that is modified 31 | Target string `json:"target"` 32 | 33 | // Type is the type of the modified value 34 | Type swagger.Type `json:"type"` 35 | } 36 | 37 | // Constructor creates new objects 38 | type Constructor struct { 39 | Help string `json:"help"` 40 | // Args are the arguments this constructor takes. 41 | // Generated constructors only have one argument, `name` 42 | Args []Parameter `json:"args"` 43 | } 44 | 45 | // Parameter is a function argument, with an optional default value 46 | type Parameter struct { 47 | Key string `json:"key"` 48 | Default interface{} `json:"default"` 49 | } 50 | 51 | func (p Parameter) String() string { 52 | if p.Default == nil { 53 | return p.Key 54 | } 55 | return fmt.Sprintf("%s=%v", p.Key, p.Default) 56 | } 57 | 58 | // Object is the logical group for Modifiers that target fields of a nested 59 | // object 60 | type Object struct { 61 | Help string `json:"help"` 62 | Fields modifiers `json:"fields"` 63 | } 64 | 65 | // modsForProps generates Modifiers for a (nested) map of swagger properties 66 | // (object fields) 67 | func modsForProps(props map[string]*swagger.Schema, ctx string, root bool, 68 | inArray bool, defArray bool) map[string]interface{} { 69 | mods := make(map[string]interface{}) 70 | for k, p := range props { 71 | if excluded := propertiesWithoutModifiers[k]; excluded == excludeEverywhere || (root && excluded == excludeInRootOnly) { 72 | continue 73 | } 74 | 75 | // for an array field with object type, we define the withField first 76 | if p.Items != nil { 77 | name, mod := newModifier(k, p, ctx, inArray, true) 78 | mods[name] = mod 79 | } 80 | 81 | // for an array field with object type, here defines the 82 | // arrayWithSubField helper methods 83 | name, mod := newModifier(k, p, ctx, inArray, false) 84 | if name != "" { 85 | mods[name] = mod 86 | } 87 | } 88 | return mods 89 | } 90 | 91 | // newModifier returns a modifier for the given swagger Property. 92 | // calls modsForProps in case of a nested object. 93 | func newModifier(name string, p *swagger.Schema, ctx string, inArray bool, 94 | defArray bool) (string, interface{}) { 95 | name = CamelLower(name) 96 | 97 | switch p.Type { 98 | case swagger.TypeArray: 99 | // when defArray is true, create modifier directly 100 | if !defArray { 101 | if p.Items != nil && p.Items.ResolvedRef == "" && len(p.Items.Props) != 0 { 102 | // arrayWith is for sub elements and thus removes all 103 | // context 104 | o := Object{ 105 | Help: safeStr(p.Desc), 106 | Fields: modsForProps(p.Items.Props, "", false, true, false), 107 | } 108 | return name, o 109 | } 110 | return "", nil 111 | } 112 | 113 | // no children? create modifier 114 | fallthrough 115 | case swagger.TypeObject, "": 116 | // if it has children, return modifier group instead 117 | if len(p.Props) != 0 { 118 | o := Object{ 119 | Help: safeStr(p.Desc), 120 | Fields: modsForProps(p.Props, ctx+"."+name, false, inArray, defArray), 121 | } 122 | return name, o 123 | } 124 | 125 | fallthrough 126 | default: 127 | fn := Modifier{ 128 | Help: safeStr(p.Desc), 129 | Arg: Parameter{Key: fnArg(name)}, 130 | Target: strings.TrimPrefix(ctx+"."+name, "."), 131 | Type: p.Type, 132 | } 133 | 134 | return fmt.Sprintf("with%s", normalizedTitle(name)), fn 135 | } 136 | } 137 | 138 | // fnArg normalizes an arguments name so it does not use any reserved words 139 | func fnArg(name string) string { 140 | name = strings.Replace(name, "-", "_", -1) 141 | name = strings.Replace(name, ".", "_", -1) 142 | switch name { 143 | case "error": // for backward compatibility 144 | return "err" 145 | case "assert", "else", "false", "for", "function", "if", 146 | "import", "importstr", "in", "local", "null", "tailstrict", 147 | "then", "self", "super", "true": 148 | return normalizedTitle(name) 149 | default: 150 | return name 151 | } 152 | } 153 | 154 | // normalizedTitle normalizes a name and applied strings.Title() 155 | func normalizedTitle(name string) string { 156 | if strings.HasPrefix(name, "-") { 157 | name = strings.TrimPrefix(name, "-") 158 | } 159 | 160 | name = strings.Replace(name, ".", "_", -1) 161 | 162 | return strings.Title(name) 163 | } 164 | 165 | // CamelLower returns the string with the word lowercased. 166 | func CamelLower(s string) string { 167 | elems := camelcase.Split(s) 168 | elems[0] = strings.ToLower(elems[0]) 169 | return strings.Join(elems, "") 170 | } 171 | -------------------------------------------------------------------------------- /pkg/render/modifiers.go: -------------------------------------------------------------------------------- 1 | package render 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | j "github.com/jsonnet-libs/k8s/pkg/builder" 8 | d "github.com/jsonnet-libs/k8s/pkg/builder/docsonnet" 9 | "github.com/jsonnet-libs/k8s/pkg/model" 10 | "github.com/jsonnet-libs/k8s/pkg/swagger" 11 | ) 12 | 13 | const ( 14 | // LocalAPIVersion references a "private: json field containing the APIVersion 15 | LocalAPIVersion = "_apiVersion" 16 | ) 17 | 18 | // Modifier retrieves the correct modifier(s) for a given type 19 | func Modifier(name string, i interface{}) []j.Type { 20 | switch t := i.(type) { 21 | case model.Modifier: 22 | return modFunction(name, t) 23 | case model.Object: 24 | objMod := modObject(name, t) 25 | return []j.Type{ 26 | d.Obj(name, t.Help), 27 | objMod, 28 | } 29 | } 30 | panic(fmt.Sprintf("unexpected %T", i)) 31 | } 32 | 33 | func modObject(name string, o model.Object) j.ObjectType { 34 | childs := make([]j.Type, 0, len(o.Fields)) 35 | for k, m := range o.Fields { 36 | childs = append(childs, Modifier(k, m)...) 37 | } 38 | 39 | SortFields(childs) 40 | 41 | newObj := j.Object 42 | if len(childs) == 1 && !isFuncType(childs[0]) { 43 | newObj = j.ConciseObject 44 | } 45 | 46 | return newObj(name, childs...) 47 | } 48 | 49 | func modFunction(name string, f model.Modifier) []j.Type { 50 | // parameters 51 | args := j.Args( 52 | j.Required(j.String(f.Arg.Key, "")), 53 | ) 54 | 55 | if f.Type == "" { 56 | f.Type = "any" 57 | } 58 | dArgs := d.Args(f.Arg.Key, string(f.Type)) 59 | 60 | out := make([]j.Type, 0, 2) 61 | 62 | // withXyz() 63 | set := fnResult(f, false) 64 | out = append(out, 65 | d.Func(name, f.Help, dArgs), 66 | j.Func(name, args, j.ConciseObject("", set)), 67 | ) 68 | 69 | // withXyzMixin() 70 | if f.Type == swagger.TypeObject || f.Type == swagger.TypeArray { 71 | add := fnResult(f, true) 72 | mixName := name + "Mixin" 73 | out = append(out, 74 | d.Func(mixName, 75 | f.Help+"\n\n**Note:** This function appends passed data to existing values", 76 | dArgs, 77 | ), 78 | j.Func(mixName, args, j.ConciseObject("", add)), 79 | ) 80 | } 81 | 82 | return out 83 | } 84 | 85 | func fnResult(f model.Modifier, adder bool) j.Type { 86 | elems := strings.Split(f.Target, ".") 87 | ret := reduceReverse(elems, func(i int, s string, o j.Type) j.Type { 88 | switch i { 89 | case 0: 90 | // if array, also accept single value 91 | if f.Type == swagger.TypeArray { 92 | return j.IfThenElse(s, 93 | j.Call("", "std.isArray", j.Args(j.Ref("v", f.Arg.Key))), 94 | j.Ref("", f.Arg.Key), 95 | j.List("", j.Ref("", f.Arg.Key)), 96 | ) 97 | } 98 | return j.Ref(s, f.Arg.Key) 99 | case 1: 100 | if !adder { 101 | return j.ConciseObject(s, o) 102 | } 103 | fallthrough 104 | default: 105 | return j.ConciseObject(s, j.Merge(o)) 106 | } 107 | }) 108 | if len(elems) != 1 || adder { 109 | ret = j.Merge(ret) 110 | } 111 | 112 | return ret 113 | } 114 | 115 | // reduceReverse calls f for each in arr in reverse order. 116 | // o will the result of the previous element's invocation, nil if i==0 117 | func reduceReverse(arr []string, f func(i int, s string, o j.Type) j.Type) j.Type { 118 | size := len(arr) - 1 119 | var last j.Type 120 | for ii := range arr { 121 | i := size - ii 122 | last = f(ii, arr[i], last) 123 | } 124 | return last 125 | } 126 | 127 | func isFuncType(t j.Type) bool { 128 | _, ok := t.(j.FuncType) 129 | return ok 130 | } 131 | -------------------------------------------------------------------------------- /pkg/render/render.go: -------------------------------------------------------------------------------- 1 | package render 2 | 3 | import ( 4 | "path" 5 | "path/filepath" 6 | 7 | j "github.com/jsonnet-libs/k8s/pkg/builder" 8 | d "github.com/jsonnet-libs/k8s/pkg/builder/docsonnet" 9 | "github.com/jsonnet-libs/k8s/pkg/model" 10 | ) 11 | 12 | // Common set of directory structure / file extensions 13 | const ( 14 | ExtPrefix = "extensions" 15 | CustomPrefix = "_custom" 16 | GenPrefix = "_gen" 17 | GenExt = ".libsonnet" 18 | IndexFile = "gen" + GenExt 19 | MainFile = "main" + GenExt 20 | ) 21 | 22 | // Index creates gen.libsonnet, the index of all generated artifacts 23 | func Index(groups map[string]model.Group, name, repo, dir, description string) j.ObjectType { 24 | fields := []j.Type{ 25 | d.Import(), 26 | d.Pkg(name, path.Join(repo, dir, "main.libsonnet"), description), 27 | } 28 | 29 | for name := range groups { 30 | imp := filepath.Join(GenPrefix, name, MainFile) 31 | fields = append(fields, j.Hidden(j.Import(name, imp))) 32 | } 33 | 34 | SortFields(fields) 35 | 36 | return j.Object("", fields...) 37 | } 38 | 39 | // Main creates main.libsonnet: 40 | // - import the generated index (gen.libsonnet) 41 | // - add all hand-written patches on top 42 | func Main(adds []string) j.Type { 43 | index := j.Import("", IndexFile) 44 | if len(adds) == 0 { 45 | return index 46 | } 47 | 48 | elems := []j.Type{index} 49 | for _, a := range adds { 50 | a = filepath.Join(CustomPrefix, filepath.Base(a)) 51 | add := j.Import("", a) 52 | elems = append(elems, add) 53 | } 54 | 55 | return j.Add("", elems...) 56 | } 57 | 58 | // Objects is a collection of Jsonnet objects, indexed by their expected path on 59 | // the filesystem 60 | type Objects map[string]j.ObjectType 61 | 62 | // Add appends a jsonnet object to an object 63 | func (o Objects) Add(prefix string, set Objects) { 64 | for k, v := range set { 65 | o[filepath.Join(prefix, k)] = v 66 | } 67 | } 68 | 69 | // Group renders the entire given group, returning e.g.: 70 | // - main.libsonnet, the group index 71 | // - v1/main.libsonnet, the version v1 index 72 | // - v1/deployment.libsonnet, Deployment 73 | // - v1/daemonset.libsonnet, DaemonSet 74 | func Group(name string, g model.Group) Objects { 75 | fields := []j.Type{ 76 | d.Import(), 77 | d.Pkg(name, "", ""), 78 | } 79 | objects := make(Objects) 80 | 81 | for name, ver := range g { 82 | v := Version(name, ver) 83 | objects.Add(name, v) 84 | fields = append(fields, j.Import(name, filepath.Join(name, MainFile))) 85 | } 86 | 87 | SortFields(fields) 88 | objects[MainFile] = j.Object(name, fields...) 89 | 90 | return objects 91 | } 92 | 93 | // Version renders the entire given object, returning e.g.: 94 | // - /main.libsonnet, the version index 95 | // - /deployment.libsonnet, Deployment 96 | // - /daemonset.libsonnet, DaemonSet 97 | func Version(name string, v model.Version) Objects { 98 | fields := []j.Type{ 99 | d.Import(), 100 | d.Pkg(name, "", ""), 101 | } 102 | objects := make(Objects) 103 | 104 | for name, kind := range v.Kinds { 105 | k := Kind(name, kind) 106 | fn := name + GenExt 107 | objects[fn] = k 108 | fields = append(fields, j.Import(name, fn)) 109 | } 110 | 111 | SortFields(fields) 112 | objects[MainFile] = j.Object(name, fields...) 113 | 114 | return objects 115 | } 116 | 117 | // Kind renders the given Kind, including all modifiers and perhaps a 118 | // constructor 119 | func Kind(name string, k model.Kind) j.ObjectType { 120 | // docsonnet package 121 | fields := []j.Type{ 122 | d.Import(), 123 | d.Pkg(name, "", k.Help), 124 | } 125 | 126 | // perhaps constructor 127 | if k.New != nil { 128 | fn := constructor(*k.New, k.Kind, k.APIVersion(), k.Scope) 129 | doc := d.Func("new", k.New.Help, d.Args("name", "string")) 130 | fields = append(fields, fn, doc) 131 | } 132 | 133 | // with... functions 134 | for k, m := range k.Modifiers { 135 | if i := Modifier(k, m); i != nil { 136 | fields = append(fields, i...) 137 | } 138 | } 139 | 140 | SortFields(fields) 141 | 142 | // mixin field for compatibility (patch to avoid recursive result) 143 | fields = append(fields, 144 | j.String("#mixin", "ignore"), 145 | j.Ref("mixin", "self"), 146 | ) 147 | return j.Object(name, fields...) 148 | } 149 | 150 | // constructor creates a generic constructor, that 'just' adds apiVersion and 151 | // kind to an object. For more sophisticated constructors, the generated 152 | // artifact is overridden using hand-written files later on. 153 | func constructor(c model.Constructor, kind, apiVersion string, scope *string) j.FuncType { 154 | 155 | operands := []j.Type{ 156 | j.Object("", 157 | j.String("apiVersion", apiVersion), 158 | j.String("kind", kind), 159 | ), 160 | j.Call("", "self.metadata.withName", j.Args(j.Ref("name", "name"))), 161 | } 162 | 163 | if scope != nil && *scope == "Cluster" { 164 | operands = append(operands, 165 | j.Call("", "self.metadata.withAnnotations", j.Args( 166 | j.Object("annotations", 167 | j.String("tanka.dev/namespaced", "false"), 168 | ), 169 | )), 170 | ) 171 | } 172 | 173 | result := j.Add("", operands...) 174 | 175 | return j.Func("new", 176 | j.Args(j.Required(j.String("name", ""))), 177 | result, 178 | ) 179 | } 180 | -------------------------------------------------------------------------------- /pkg/render/sort.go: -------------------------------------------------------------------------------- 1 | package render 2 | 3 | import ( 4 | "sort" 5 | "strings" 6 | 7 | j "github.com/jsonnet-libs/k8s/pkg/builder" 8 | ) 9 | 10 | // SortFields sorts Jsonnet fields 11 | func SortFields(fields []j.Type) { 12 | sort.SliceStable(fields, func(i, j int) bool { 13 | a := fields[i].Name() 14 | b := fields[j].Name() 15 | 16 | if local(fields[i], fields[j]) { 17 | return true 18 | } 19 | if local(fields[j], fields[i]) { 20 | return false 21 | } 22 | 23 | if a == trim(b) { 24 | return false 25 | } 26 | if b == trim(a) { 27 | return true 28 | } 29 | 30 | return trim(a) < trim(b) 31 | }) 32 | } 33 | 34 | func trim(s string) string { 35 | return strings.TrimPrefix(s, "#") 36 | } 37 | 38 | func local(a, b j.Type) bool { 39 | _, aOk := a.(j.LocalType) 40 | _, bOk := b.(j.LocalType) 41 | return aOk && !bOk 42 | } 43 | -------------------------------------------------------------------------------- /pkg/render/sort_test.go: -------------------------------------------------------------------------------- 1 | package render 2 | 3 | import ( 4 | "testing" 5 | 6 | j "github.com/jsonnet-libs/k8s/pkg/builder" 7 | "github.com/stretchr/testify/assert" 8 | ) 9 | 10 | func TestSortFields(t *testing.T) { 11 | sorted := []j.Type{ 12 | j.Local(j.String("abc", "")), 13 | j.Local(j.String("def", "")), 14 | 15 | j.String("#", ""), 16 | 17 | j.String("#aaa", ""), 18 | j.String("aaa", ""), 19 | 20 | j.String("bbb", ""), 21 | 22 | j.String("#ccc", ""), 23 | j.String("ccc", ""), 24 | 25 | j.String("ddd", ""), 26 | } 27 | 28 | unsorted := []j.Type{ 29 | j.Local(j.String("def", "")), 30 | j.String("aaa", ""), 31 | j.String("bbb", ""), 32 | j.String("#aaa", ""), 33 | j.Local(j.String("abc", "")), 34 | j.String("#ccc", ""), 35 | j.String("ddd", ""), 36 | j.String("ccc", ""), 37 | j.String("#", ""), 38 | } 39 | 40 | SortFields(unsorted) 41 | 42 | assert.Equal(t, sorted, unsorted) 43 | } 44 | 45 | func TestLocal(t *testing.T) { 46 | cases := []struct { 47 | name string 48 | a, b j.Type 49 | is bool 50 | }{ 51 | { 52 | name: "a", 53 | a: j.Local(j.String("a", "")), 54 | b: j.String("b", ""), 55 | is: true, 56 | }, 57 | { 58 | name: "b", 59 | a: j.String("b", ""), 60 | b: j.Local(j.String("a", "")), 61 | is: false, 62 | }, 63 | { 64 | name: "both", 65 | a: j.Local(j.String("a", "")), 66 | b: j.Local(j.String("b", "")), 67 | is: false, 68 | }, 69 | { 70 | name: "none", 71 | a: j.String("a", ""), 72 | b: j.String("b", ""), 73 | is: false, 74 | }, 75 | } 76 | 77 | for _, c := range cases { 78 | t.Run(c.name, func(t *testing.T) { 79 | b := local(c.a, c.b) 80 | assert.Equal(t, c.is, b) 81 | }) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /pkg/swagger/crd.go: -------------------------------------------------------------------------------- 1 | package swagger 2 | 3 | import ( 4 | "bytes" 5 | _ "embed" 6 | "fmt" 7 | "io" 8 | "strings" 9 | 10 | goyaml "gopkg.in/yaml.v3" 11 | apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" 12 | "k8s.io/apimachinery/pkg/util/yaml" 13 | ) 14 | 15 | //go:embed objectmeta.json 16 | var objectmeta []byte 17 | 18 | type CRDLoader struct { 19 | objectMetaDefinitions *Schema 20 | } 21 | 22 | func (c *CRDLoader) Load(manifest []byte) (Definitions, error) { 23 | crds, err := c.splitYAML(manifest) 24 | if err != nil { 25 | return nil, err 26 | } 27 | 28 | if err := c.loadObjectMeta(); err != nil { 29 | return nil, err 30 | } 31 | 32 | definitions := []apiextensionsv1.CustomResourceDefinition{} 33 | for _, crdfile := range crds { 34 | crd := apiextensionsv1.CustomResourceDefinition{} 35 | 36 | decoder := yaml.NewYAMLOrJSONDecoder(bytes.NewBuffer(crdfile), 4096) 37 | if err := decoder.Decode(&crd); err != nil { 38 | return nil, err 39 | } 40 | definitions = append(definitions, crd) 41 | } 42 | 43 | defs := make(Definitions) 44 | for _, d := range definitions { 45 | toReverse := strings.Split(d.Spec.Group, ".") 46 | reversed := []string{} 47 | for i := range toReverse { 48 | n := toReverse[len(toReverse)-1-i] 49 | reversed = append(reversed, n) 50 | } 51 | 52 | for _, version := range d.Spec.Versions { 53 | schema := version.Schema.OpenAPIV3Schema 54 | 55 | nameArray := append(reversed, []string{version.Name, d.Spec.Names.Kind}...) 56 | name := strings.Join(nameArray, ".") 57 | 58 | scope := fmt.Sprint(d.Spec.Scope) 59 | 60 | defs[name] = &Schema{ 61 | Type: Type(schema.Type), 62 | Desc: schema.Description, 63 | Props: c.propToSchema(schema.Properties, true), 64 | Items: c.itemsToSchema(schema.Items), 65 | Scope: &scope, 66 | XGvk: []XGvk{ 67 | { 68 | Group: d.Spec.Group, 69 | Kind: d.Spec.Names.Kind, 70 | Version: version.Name, 71 | }, 72 | }, 73 | } 74 | } 75 | } 76 | return defs, nil 77 | } 78 | 79 | func (c *CRDLoader) loadObjectMeta() error { 80 | loader := &SwaggerLoader{} 81 | definitions, err := loader.Load(objectmeta) 82 | if err != nil { 83 | return err 84 | } 85 | 86 | c.objectMetaDefinitions = definitions["io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta"] 87 | return nil 88 | } 89 | 90 | func (c *CRDLoader) propToSchema(prop map[string]apiextensionsv1.JSONSchemaProps, addObjectMeta bool) Definitions { 91 | s := make(Definitions, len(prop)) 92 | 93 | for key, value := range prop { 94 | s[key] = &Schema{ 95 | Type: Type(value.Type), 96 | Desc: value.Description, 97 | Props: c.propToSchema(value.Properties, false), 98 | Items: c.itemsToSchema(value.Items), 99 | } 100 | } 101 | if addObjectMeta { 102 | s["metadata"] = c.objectMetaDefinitions 103 | } 104 | 105 | return s 106 | } 107 | 108 | func (c *CRDLoader) itemsToSchema(item *apiextensionsv1.JSONSchemaPropsOrArray) *Schema { 109 | if item == nil { 110 | return nil 111 | } 112 | schema := item.Schema 113 | return &Schema{ 114 | Type: Type(schema.Type), 115 | Desc: schema.Description, 116 | Props: c.propToSchema(schema.Properties, false), 117 | Items: c.itemsToSchema(schema.Items), 118 | } 119 | } 120 | 121 | func (c *CRDLoader) splitYAML(resources []byte) ([][]byte, error) { 122 | dec := goyaml.NewDecoder(bytes.NewReader(resources)) 123 | 124 | var res [][]byte 125 | for { 126 | var value interface{} 127 | err := dec.Decode(&value) 128 | if err == io.EOF { 129 | break 130 | } 131 | if err != nil { 132 | return nil, err 133 | } 134 | valueBytes, err := goyaml.Marshal(value) 135 | if err != nil { 136 | return nil, err 137 | } 138 | res = append(res, valueBytes) 139 | } 140 | return res, nil 141 | } 142 | 143 | -------------------------------------------------------------------------------- /pkg/swagger/definitions.go: -------------------------------------------------------------------------------- 1 | package swagger 2 | 3 | import ( 4 | "regexp" 5 | ) 6 | 7 | type Definitions map[string]*Schema 8 | 9 | func (ds Definitions) Filter(f func(k string, v Schema) bool) Definitions { 10 | out := make(Definitions) 11 | for k, v := range ds { 12 | if f(k, *v) { 13 | out[k] = v 14 | } 15 | } 16 | return out 17 | } 18 | 19 | func (ds Definitions) Sub(exp string) Definitions { 20 | rg := regexp.MustCompile(exp) 21 | return ds.Filter(func(k string, v Schema) bool { 22 | return rg.MatchString(k) 23 | }) 24 | } 25 | 26 | -------------------------------------------------------------------------------- /pkg/swagger/load.go: -------------------------------------------------------------------------------- 1 | package swagger 2 | 3 | import ( 4 | "io/ioutil" 5 | "net/http" 6 | "net/url" 7 | ) 8 | 9 | type Loader interface { 10 | Load([]byte) (Definitions, error) 11 | } 12 | 13 | func Load(loader Loader, uri string) (Definitions, error) { 14 | var data []byte 15 | var err error 16 | if isURL(uri) { 17 | r, err := http.Get(uri) 18 | if err != nil { 19 | return nil, err 20 | } 21 | data, err = ioutil.ReadAll(r.Body) 22 | if err != nil { 23 | return nil, err 24 | } 25 | } else { 26 | data, err = ioutil.ReadFile(uri) 27 | if err != nil { 28 | return nil, err 29 | } 30 | } 31 | return loader.Load(data) 32 | } 33 | 34 | func isURL(uri string) bool { 35 | endpoint, err := url.Parse(uri) 36 | if err != nil { 37 | return false 38 | } 39 | 40 | return len(endpoint.Scheme) > 0 41 | } 42 | -------------------------------------------------------------------------------- /pkg/swagger/schema.go: -------------------------------------------------------------------------------- 1 | package swagger 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | // swagger types 8 | type Type string 9 | 10 | const ( 11 | TypeString Type = "string" 12 | TypeInt Type = "integer" 13 | TypeBool Type = "boolean" 14 | TypeObject Type = "object" 15 | TypeArray Type = "array" 16 | ) 17 | 18 | // Schema is a general object definition 19 | type Schema struct { 20 | // general 21 | Type Type `json:"type"` 22 | Desc string `json:"description"` 23 | 24 | // type: object 25 | Props map[string]*Schema `json:"properties"` 26 | // type: array 27 | Items *Schema `json:"items"` 28 | 29 | // incomplete: reference 30 | DollarRef *string `json:"$ref"` 31 | ResolvedRef string 32 | 33 | // Cluster or Namespaced scope, ignored if unset 34 | Scope *string 35 | 36 | // vendor extensions 37 | XGvk []XGvk `json:"x-kubernetes-group-version-kind"` 38 | } 39 | 40 | type XGvk struct { 41 | Group string 42 | Kind string 43 | Version string 44 | } 45 | 46 | func (s Schema) Ref() string { 47 | if s.DollarRef == nil { 48 | return s.ResolvedRef 49 | } 50 | return strings.TrimPrefix(*s.DollarRef, "#/definitions/") 51 | } 52 | 53 | func (s Schema) GroupVersionKind() (*XGvk, bool) { 54 | if len(s.XGvk) == 0 { 55 | return nil, false 56 | } 57 | 58 | // sometimes multiple XGVKs exist for the same schema. In this case we want to 59 | // select the most specific one. 60 | var x *XGvk 61 | for _, g := range s.XGvk { 62 | if x == nil || (x.Group == "" && g.Group != "") { 63 | x = &g 64 | } 65 | if x.Version == "" && g.Version != "" { 66 | x = &g 67 | } 68 | if x.Kind == "" && g.Kind != "" { 69 | x = &g 70 | } 71 | } 72 | // safeguard against malformed schemas with completely empty XGVKs 73 | if x == nil { 74 | return nil, false 75 | } 76 | return x, true 77 | } 78 | -------------------------------------------------------------------------------- /pkg/swagger/swagger.go: -------------------------------------------------------------------------------- 1 | package swagger 2 | 3 | import ( 4 | "encoding/json" 5 | ) 6 | 7 | var skipRefs = map[string]bool{ 8 | // recursive 9 | "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1.JSONSchemaProps": true, 10 | "io.k8s.apiextensions-apiserver.pkg.apis.apiextensions.v1beta1.JSONSchemaProps": true, 11 | 12 | // list will be filtered anyways 13 | "io.k8s.apimachinery.pkg.apis.meta.v1.ListMeta": true, 14 | } 15 | 16 | type SwaggerLoader struct { 17 | resolveMap map[string]*Schema // store objects in a map in order to support recursive references 18 | Definitions Definitions `json:"definitions"` 19 | } 20 | 21 | func (s *SwaggerLoader) Load(data []byte) (Definitions, error) { 22 | if err := json.Unmarshal(data, &s); err != nil { 23 | return nil, err 24 | } 25 | s.resolveMap = map[string]*Schema{} 26 | 27 | for k, def := range s.Definitions { 28 | s.Definitions[k] = s.resolveRefs(def) 29 | } 30 | 31 | return s.Definitions, nil 32 | } 33 | 34 | func (s *SwaggerLoader) resolveRefs(d *Schema) *Schema { 35 | for key, prop := range d.Props { 36 | resolved := s.get(prop) 37 | resolved.Items = s.get(resolved.Items) 38 | 39 | d.Props[key] = resolved 40 | } 41 | 42 | return d 43 | } 44 | 45 | func (s *SwaggerLoader) get(prop *Schema) *Schema { 46 | if prop == nil || prop.DollarRef == nil { 47 | return prop 48 | } 49 | 50 | ref := prop.Ref() 51 | if skipRefs[ref] { 52 | return prop 53 | } 54 | 55 | if rs, ok := s.resolveMap[ref]; ok { 56 | return rs 57 | } 58 | rs := s.Definitions[ref] 59 | 60 | // return if cannot resolve reference 61 | if rs == nil { 62 | return nil 63 | } 64 | 65 | rs.ResolvedRef = ref 66 | s.resolveMap[ref] = rs 67 | rs = s.resolveRefs(rs) 68 | return rs 69 | } 70 | -------------------------------------------------------------------------------- /scripts/configure_github_ssh.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | set -x 4 | 5 | DIRNAME="$1" 6 | 7 | mkdir "${DIRNAME}"/ssh 8 | 9 | echo "${SSH_KEY}" > "${DIRNAME}"/ssh/id_rsa 10 | echo "Host github.com" > "${DIRNAME}"/ssh/config 11 | echo " IdentityFile ${DIRNAME}/ssh/id_rsa" >> "${DIRNAME}"/ssh/config 12 | echo " UserKnownHostsFile ${DIRNAME}/ssh/known_hosts" >> "${DIRNAME}"/ssh/config 13 | cat "${DIRNAME}"/ssh/config 14 | 15 | ssh-keyscan github.com >> "${DIRNAME}"/ssh/known_hosts 16 | 17 | chmod 700 -R "${DIRNAME}"/ssh 18 | chmod 600 "${DIRNAME}"/ssh/id_rsa 19 | -------------------------------------------------------------------------------- /scripts/docs.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | 5 | INPUT_DIR=$1 6 | OUTPUT_DIR=$2 7 | 8 | CONFIG_FILE="${INPUT_DIR}/config.yml" 9 | 10 | DOCS="${OUTPUT_DIR}/docs" 11 | 12 | mkdir -p "${DOCS}" 13 | 14 | LIBS=$(yq2 e '.specs[]|.output' - < "${CONFIG_FILE}") 15 | 16 | pushd "${OUTPUT_DIR}" 17 | for d in ${LIBS}; do 18 | [ -d "$d" ] && \ 19 | docsonnet \ 20 | -o "docs/$d" \ 21 | --urlPrefix "$d" \ 22 | "$d/main.libsonnet" 23 | done 24 | popd 25 | -------------------------------------------------------------------------------- /scripts/gen.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | 4 | DIRNAME=$(realpath "$(dirname "$0")") 5 | 6 | INPUT_DIR="$1" 7 | 8 | jsonnet -c -m "${INPUT_DIR}" -J . -S "${INPUT_DIR}/config.jsonnet" 9 | 10 | CONFIG_FILE="${INPUT_DIR}/config.yml" 11 | 12 | REPO=$(yq2 e '.repository' - < "${CONFIG_FILE}") 13 | 14 | OUTPUT_DIR="$2/${REPO}" 15 | 16 | if [ "${GEN_COMMIT}" != "true" ]; then 17 | mkdir -p "${OUTPUT_DIR}" 18 | else 19 | ./configure_github_ssh.sh "${DIRNAME}" 20 | export GIT_SSH_COMMAND="ssh -F '${DIRNAME}/ssh/config'" 21 | git clone --depth 1 "ssh://git@${REPO}" "${OUTPUT_DIR}" 22 | 23 | git config --global user.name "${GIT_COMMITTER_NAME}" 24 | git config --global user.email "${GIT_COMMITTER_EMAIL}" 25 | 26 | # Create the gh-pages branch if it doesn't exist. 27 | pushd "${OUTPUT_DIR}" 28 | set +eo pipefail 29 | git push origin gh-pages || (git branch -c gh-pages && git push origin gh-pages) 30 | set -eo pipefail 31 | popd 32 | fi 33 | 34 | # Remove everything except .git 35 | find "${OUTPUT_DIR}" \ 36 | -not -path "${OUTPUT_DIR}" \ 37 | -not -path "${OUTPUT_DIR}/.git/*" \ 38 | -not -name '.git' \ 39 | -delete 40 | 41 | 42 | shopt -s dotglob 43 | cp -r "${INPUT_DIR}/skel"/* "${OUTPUT_DIR}" 44 | 45 | k8s-gen -o "${OUTPUT_DIR}" -c "${CONFIG_FILE}" 46 | 47 | ./docs.sh "${INPUT_DIR}" "${OUTPUT_DIR}" 48 | 49 | 50 | if [ "${GEN_COMMIT}" != "true" ]; then 51 | ls -lah "${OUTPUT_DIR}" 52 | else 53 | pushd "${OUTPUT_DIR}" 54 | if ! git diff --exit-code -s; then 55 | git add . 56 | git commit -m "update: source github.com/jsonnet-libs/k8s@${GITHUB_SHA:0:8}" 57 | git push 58 | else 59 | echo "Nothing to commit." 60 | fi 61 | popd 62 | fi 63 | 64 | 65 | if [ "${DIFF:-false}" == "true" ]; then 66 | echo "Diffing..." 67 | DIFF_DIR="${TMPDIR:-/tmp/}${REPO}" 68 | # HTTPS should always be usable without creds 69 | git clone --depth 1 "https://${REPO}" "${DIFF_DIR}" || echo "error cloning the repository!" 70 | 71 | (diff -r --exclude .git "${DIFF_DIR}" "${OUTPUT_DIR}" && echo "No diff!") || echo "There's a diff!" 72 | fi 73 | -------------------------------------------------------------------------------- /tf/.gitignore: -------------------------------------------------------------------------------- 1 | main.tf.json 2 | *.tfstate* 3 | .terraform* 4 | -------------------------------------------------------------------------------- /tf/.keep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jsonnet-libs/k8s/5ec29d4237e45e903e35f2e1a911ddaa99df2cdf/tf/.keep --------------------------------------------------------------------------------