From 7cbffade0cbff63eb485c30d50172785424bd35c Mon Sep 17 00:00:00 2001 From: Ismail Kaboubi Date: Tue, 2 Jan 2024 12:50:49 +0100 Subject: [PATCH] Initial commit --- .github/PULL_REQUEST_TEMPLATE.md | 35 + .github/workflows/backport.yml | 15 + .github/workflows/ci.yml | 315 ++++++++ .github/workflows/commands.yml | 7 + .github/workflows/e2e.yaml | 12 + .github/workflows/tag.yaml | 18 + .gitignore | 11 + .gitmodules | 3 + .golangci.yml | 195 +++++ CODEOWNERS | 17 + CODE_OF_CONDUCT.md | 3 + LICENSE | 201 +++++ Makefile | 244 ++++++ OWNERS.md | 13 + README.md | 60 ++ apis/generate.go | 39 + apis/null/v1alpha1/zz_generated.deepcopy.go | 205 +++++ apis/null/v1alpha1/zz_generated.managed.go | 68 ++ .../null/v1alpha1/zz_generated.managedlist.go | 17 + .../null/v1alpha1/zz_generated_terraformed.go | 102 +++ apis/null/v1alpha1/zz_groupversion_info.go | 36 + apis/null/v1alpha1/zz_resource_types.go | 98 +++ apis/v1alpha1/doc.go | 9 + apis/v1alpha1/register.go | 38 + apis/v1alpha1/types.go | 63 ++ apis/v1alpha1/zz_generated.deepcopy.go | 104 +++ apis/v1beta1/doc.go | 9 + apis/v1beta1/register.go | 52 ++ apis/v1beta1/types.go | 80 ++ apis/v1beta1/zz_generated.deepcopy.go | 178 +++++ apis/v1beta1/zz_generated.pc.go | 28 + apis/v1beta1/zz_generated.pcu.go | 28 + apis/v1beta1/zz_generated.pculist.go | 17 + apis/zz_register.go | 37 + build | 1 + .../images/upjet-provider-template/Dockerfile | 51 ++ .../images/upjet-provider-template/Makefile | 42 + .../upjet-provider-template/terraformrc.hcl | 9 + cluster/test/setup.sh | 26 + cmd/generator/main.go | 27 + cmd/provider/main.go | 124 +++ config/external_name.go | 38 + config/null/config.go | 17 + config/provider-metadata.yaml | 36 + config/provider.go | 46 ++ config/schema.json | 1 + examples-generated/null/resource.yaml | 18 + examples/install.yaml | 6 + examples/null/resource.yaml | 10 + examples/providerconfig/.gitignore | 1 + examples/providerconfig/providerconfig.yaml | 11 + examples/providerconfig/secret.yaml.tmpl | 12 + examples/storeconfig/vault.yaml | 19 + go.mod | 120 +++ go.sum | 756 ++++++++++++++++++ hack/boilerplate.go.txt | 3 + hack/prepare.sh | 40 + internal/clients/template.go | 72 ++ internal/controller/doc.go | 5 + .../controller/null/resource/zz_controller.go | 66 ++ internal/controller/providerconfig/config.go | 35 + internal/controller/zz_setup.go | 28 + internal/features/features.go | 22 + .../null.template.upbound.io_resources.yaml | 318 ++++++++ .../template.upbound.io_providerconfigs.yaml | 155 ++++ ...plate.upbound.io_providerconfigusages.yaml | 110 +++ .../template.upbound.io_storeconfigs.yaml | 205 +++++ package/crossplane.yaml | 4 + scripts/version_diff.py | 36 + 69 files changed, 4827 insertions(+) create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/workflows/backport.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/commands.yml create mode 100644 .github/workflows/e2e.yaml create mode 100644 .github/workflows/tag.yaml create mode 100644 .gitignore create mode 100644 .gitmodules create mode 100644 .golangci.yml create mode 100644 CODEOWNERS create mode 100644 CODE_OF_CONDUCT.md create mode 100644 LICENSE create mode 100644 Makefile create mode 100644 OWNERS.md create mode 100644 README.md create mode 100644 apis/generate.go create mode 100644 apis/null/v1alpha1/zz_generated.deepcopy.go create mode 100644 apis/null/v1alpha1/zz_generated.managed.go create mode 100644 apis/null/v1alpha1/zz_generated.managedlist.go create mode 100755 apis/null/v1alpha1/zz_generated_terraformed.go create mode 100755 apis/null/v1alpha1/zz_groupversion_info.go create mode 100755 apis/null/v1alpha1/zz_resource_types.go create mode 100644 apis/v1alpha1/doc.go create mode 100644 apis/v1alpha1/register.go create mode 100644 apis/v1alpha1/types.go create mode 100644 apis/v1alpha1/zz_generated.deepcopy.go create mode 100644 apis/v1beta1/doc.go create mode 100644 apis/v1beta1/register.go create mode 100644 apis/v1beta1/types.go create mode 100644 apis/v1beta1/zz_generated.deepcopy.go create mode 100644 apis/v1beta1/zz_generated.pc.go create mode 100644 apis/v1beta1/zz_generated.pcu.go create mode 100644 apis/v1beta1/zz_generated.pculist.go create mode 100755 apis/zz_register.go create mode 160000 build create mode 100644 cluster/images/upjet-provider-template/Dockerfile create mode 100755 cluster/images/upjet-provider-template/Makefile create mode 100644 cluster/images/upjet-provider-template/terraformrc.hcl create mode 100755 cluster/test/setup.sh create mode 100644 cmd/generator/main.go create mode 100644 cmd/provider/main.go create mode 100644 config/external_name.go create mode 100644 config/null/config.go create mode 100644 config/provider-metadata.yaml create mode 100644 config/provider.go create mode 100644 config/schema.json create mode 100644 examples-generated/null/resource.yaml create mode 100644 examples/install.yaml create mode 100644 examples/null/resource.yaml create mode 100644 examples/providerconfig/.gitignore create mode 100644 examples/providerconfig/providerconfig.yaml create mode 100644 examples/providerconfig/secret.yaml.tmpl create mode 100644 examples/storeconfig/vault.yaml create mode 100644 go.mod create mode 100644 go.sum create mode 100644 hack/boilerplate.go.txt create mode 100755 hack/prepare.sh create mode 100644 internal/clients/template.go create mode 100644 internal/controller/doc.go create mode 100755 internal/controller/null/resource/zz_controller.go create mode 100644 internal/controller/providerconfig/config.go create mode 100755 internal/controller/zz_setup.go create mode 100644 internal/features/features.go create mode 100644 package/crds/null.template.upbound.io_resources.yaml create mode 100644 package/crds/template.upbound.io_providerconfigs.yaml create mode 100644 package/crds/template.upbound.io_providerconfigusages.yaml create mode 100644 package/crds/template.upbound.io_storeconfigs.yaml create mode 100644 package/crossplane.yaml create mode 100644 scripts/version_diff.py diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000..7627d1a --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,35 @@ + + +### Description of your changes + + +Fixes # + +I have: + +- [ ] Read and followed Crossplane's [contribution process]. +- [ ] Run `make reviewable test` to ensure this PR is ready for review. + +### How has this code been tested + + + +[contribution process]: https://git.io/fj2m9 diff --git a/.github/workflows/backport.yml b/.github/workflows/backport.yml new file mode 100644 index 0000000..c77b21e --- /dev/null +++ b/.github/workflows/backport.yml @@ -0,0 +1,15 @@ +name: Backport + +on: + # NOTE(negz): This is a risky target, but we run this action only when and if + # a PR is closed, then filter down to specifically merged PRs. We also don't + # invoke any scripts, etc from within the repo. I believe the fact that we'll + # be able to review PRs before this runs makes this fairly safe. + # https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ + pull_request_target: + types: [closed] + # See also commands.yml for the /backport triggered variant of this workflow. + +jobs: + backport: + uses: upbound/uptest/.github/workflows/provider-backport.yml@main diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..339c610 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,315 @@ +name: CI + +on: + push: + branches: + - main + - release-* + pull_request: {} + workflow_dispatch: {} + +env: + # Common versions + GO_VERSION: '1.19' + GOLANGCI_VERSION: 'v1.50.0' + DOCKER_BUILDX_VERSION: 'v0.8.2' + + # Common users. We can't run a step 'if secrets.XXX != ""' but we can run a + # step 'if env.XXX != ""', so we copy these to succinctly test whether + # credentials have been provided before trying to run steps that need them. + UPBOUND_MARKETPLACE_PUSH_ROBOT_USR: ${{ secrets.UPBOUND_MARKETPLACE_PUSH_ROBOT_USR }} + +jobs: + detect-noop: + runs-on: ubuntu-22.04 + outputs: + noop: ${{ steps.noop.outputs.should_skip }} + steps: + - name: Detect No-op Changes + id: noop + uses: fkirc/skip-duplicate-actions@v5.3.0 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + paths_ignore: '["**.md", "**.png", "**.jpg"]' + do_not_skip: '["workflow_dispatch", "schedule", "push"]' + + report-breaking-changes: + runs-on: ubuntu-22.04 + needs: detect-noop + if: needs.detect-noop.outputs.noop != 'true' + steps: + - name: Checkout + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 #v3 + with: + submodules: true + + - name: Get modified CRDs + id: modified-crds + uses: tj-actions/changed-files@v34 + with: + files: | + package/crds/** + - name: Report breaking CRD OpenAPI v3 schema changes + if: steps.modified-crds.outputs.any_changed == 'true' + env: + MODIFIED_CRD_LIST: ${{ steps.modified-crds.outputs.all_changed_files }} + run: | + make crddiff + - name: Report native schema version changes + if: ${{ inputs.upjet-based-provider }} + run: | + make schema-version-diff + + lint: + runs-on: ubuntu-22.04 + needs: detect-noop + if: needs.detect-noop.outputs.noop != 'true' + + steps: + - name: Checkout + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 #v3 + with: + submodules: true + + - name: Setup Go + uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3 + with: + go-version: ${{ env.GO_VERSION }} + + - name: Find the Go Build Cache + id: go + run: echo "cache=$(make go.cachedir)" >> $GITHUB_OUTPUT + + - name: Cache the Go Build Cache + uses: actions/cache@v3 + with: + path: ${{ steps.go.outputs.cache }} + key: ${{ runner.os }}-build-lint-${{ hashFiles('**/go.sum') }} + restore-keys: ${{ runner.os }}-build-lint- + + - name: Cache Go Dependencies + uses: actions/cache@v3 + with: + path: .work/pkg + key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }} + restore-keys: ${{ runner.os }}-pkg- + + - name: Vendor Dependencies + run: make vendor vendor.check + + # We could run 'make lint' but we prefer this action because it leaves + # 'annotations' (i.e. it comments on PRs to point out linter violations). + - name: Lint + uses: golangci/golangci-lint-action@v3 + with: + version: ${{ env.GOLANGCI_VERSION }} + + check-diff: + runs-on: ubuntu-22.04 + needs: detect-noop + if: needs.detect-noop.outputs.noop != 'true' + + steps: + - name: Checkout + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 #v3 + with: + submodules: true + + - name: Setup Go + uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3 + with: + go-version: ${{ env.GO_VERSION }} + + - name: Install goimports + run: go install golang.org/x/tools/cmd/goimports + + - name: Find the Go Build Cache + id: go + run: echo "cache=$(make go.cachedir)" >> $GITHUB_OUTPUT + + - name: Cache the Go Build Cache + uses: actions/cache@v3 + with: + path: ${{ steps.go.outputs.cache }} + key: ${{ runner.os }}-build-check-diff-${{ hashFiles('**/go.sum') }} + restore-keys: ${{ runner.os }}-build-check-diff- + + - name: Cache Go Dependencies + uses: actions/cache@v3 + with: + path: .work/pkg + key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }} + restore-keys: ${{ runner.os }}-pkg- + + - name: Vendor Dependencies + run: make vendor vendor.check + + - name: Check Diff + run: make check-diff + + unit-tests: + runs-on: ubuntu-22.04 + needs: detect-noop + if: needs.detect-noop.outputs.noop != 'true' + + steps: + - name: Checkout + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 #v3 + with: + submodules: true + + - name: Fetch History + run: git fetch --prune --unshallow + + - name: Setup Go + uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3 + with: + go-version: ${{ env.GO_VERSION }} + + - name: Find the Go Build Cache + id: go + run: echo "cache=$(make go.cachedir)" >> $GITHUB_OUTPUT + + - name: Cache the Go Build Cache + uses: actions/cache@v3 + with: + path: ${{ steps.go.outputs.cache }} + key: ${{ runner.os }}-build-unit-tests-${{ hashFiles('**/go.sum') }} + restore-keys: ${{ runner.os }}-build-unit-tests- + + - name: Cache Go Dependencies + uses: actions/cache@v3 + with: + path: .work/pkg + key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }} + restore-keys: ${{ runner.os }}-pkg- + + - name: Vendor Dependencies + run: make vendor vendor.check + + - name: Run Unit Tests + run: make -j2 test + + - name: Publish Unit Test Coverage + uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3 + with: + flags: unittests + file: _output/tests/linux_amd64/coverage.txt + + local-deploy: + runs-on: ubuntu-22.04 + needs: detect-noop + if: needs.detect-noop.outputs.noop != 'true' + + steps: + - name: Checkout + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 #v3 + with: + submodules: true + + - name: Fetch History + run: git fetch --prune --unshallow + + - name: Setup Go + uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3 + with: + go-version: ${{ env.GO_VERSION }} + + - name: Find the Go Build Cache + id: go + run: echo "cache=$(make go.cachedir)" >> $GITHUB_OUTPUT + + - name: Cache the Go Build Cache + uses: actions/cache@v3 + with: + path: ${{ steps.go.outputs.cache }} + key: ${{ runner.os }}-build-unit-tests-${{ hashFiles('**/go.sum') }} + restore-keys: ${{ runner.os }}-build-unit-tests- + + - name: Cache Go Dependencies + uses: actions/cache@v3 + with: + path: .work/pkg + key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }} + restore-keys: ${{ runner.os }}-pkg- + + - name: Vendor Dependencies + run: make vendor vendor.check + + - name: Deploying locally built provider package + run: make local-deploy + + publish-artifacts: + runs-on: ubuntu-22.04 + needs: detect-noop + if: needs.detect-noop.outputs.noop != 'true' + + steps: + - name: Setup QEMU + uses: docker/setup-qemu-action@2b82ce82d56a2a04d2637cd93a637ae1b359c0a7 # v2 + with: + platforms: all + + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v2 + with: + version: ${{ env.DOCKER_BUILDX_VERSION }} + install: true + + - name: Login to Upbound + uses: docker/login-action@v2 + if: env.UPBOUND_MARKETPLACE_PUSH_ROBOT_USR != '' + with: + registry: xpkg.upbound.io + username: ${{ secrets.UPBOUND_MARKETPLACE_PUSH_ROBOT_USR }} + password: ${{ secrets.UPBOUND_MARKETPLACE_PUSH_ROBOT_PSW }} + + - name: Checkout + uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 #v3 + with: + submodules: true + + - name: Fetch History + run: git fetch --prune --unshallow + + - name: Setup Go + uses: actions/setup-go@6edd4406fa81c3da01a34fa6f6343087c207a568 # v3 + with: + go-version: ${{ env.GO_VERSION }} + + - name: Find the Go Build Cache + id: go + run: echo "cache=$(make go.cachedir)" >> $GITHUB_OUTPUT + + - name: Cache the Go Build Cache + uses: actions/cache@v3 + with: + path: ${{ steps.go.outputs.cache }} + key: ${{ runner.os }}-build-publish-artifacts-${{ hashFiles('**/go.sum') }} + restore-keys: ${{ runner.os }}-build-publish-artifacts- + + - name: Cache Go Dependencies + uses: actions/cache@v3 + with: + path: .work/pkg + key: ${{ runner.os }}-pkg-${{ hashFiles('**/go.sum') }} + restore-keys: ${{ runner.os }}-pkg- + + - name: Vendor Dependencies + run: make vendor vendor.check + + - name: Build Artifacts + run: make -j2 build.all + env: + # We're using docker buildx, which doesn't actually load the images it + # builds by default. Specifying --load does so. + BUILD_ARGS: "--load" + + - name: Upload Artifacts to GitHub + uses: actions/upload-artifact@a8a3f3ad30e3422c9c7b888a15615d19a852ae32 #v3 + with: + name: output + path: _output/** + + - name: Publish Artifacts + run: make publish BRANCH_NAME=${GITHUB_REF##*/} diff --git a/.github/workflows/commands.yml b/.github/workflows/commands.yml new file mode 100644 index 0000000..cbbd0db --- /dev/null +++ b/.github/workflows/commands.yml @@ -0,0 +1,7 @@ +name: Comment Commands + +on: issue_comment + +jobs: + comment-commands: + uses: upbound/uptest/.github/workflows/provider-commands.yml@main diff --git a/.github/workflows/e2e.yaml b/.github/workflows/e2e.yaml new file mode 100644 index 0000000..4aae1d9 --- /dev/null +++ b/.github/workflows/e2e.yaml @@ -0,0 +1,12 @@ +name: End to End Testing + +on: + issue_comment: + types: [created] + +jobs: + e2e: + uses: upbound/uptest/.github/workflows/pr-comment-trigger.yml@main + secrets: + UPTEST_CLOUD_CREDENTIALS: ${{ secrets.UPTEST_CLOUD_CREDENTIALS }} + UPTEST_DATASOURCE: ${{ secrets.UPTEST_DATASOURCE }} diff --git a/.github/workflows/tag.yaml b/.github/workflows/tag.yaml new file mode 100644 index 0000000..2a93895 --- /dev/null +++ b/.github/workflows/tag.yaml @@ -0,0 +1,18 @@ +name: Tag + +on: + workflow_dispatch: + inputs: + version: + description: 'Release version (e.g. v0.1.0)' + required: true + message: + description: 'Tag message' + required: true + +jobs: + tag: + uses: upbound/uptest/.github/workflows/provider-tag.yml@main + with: + version: ${{ github.event.inputs.version }} + message: ${{ github.event.inputs.message }} diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..a6d3db0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +/.cache +/.work +/_output +cover.out +/vendor +/.vendor-new +.DS_Store + +# ignore IDE folders +.vscode/ +.idea/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..c2fad47 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "build"] + path = build + url = https://github.com/upbound/build diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..494a907 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,195 @@ +run: + deadline: 10m + + skip-files: + - "zz_\\..+\\.go$" + +output: + # colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number" + format: colored-line-number + +linters-settings: + errcheck: + # report about not checking of errors in type assetions: `a := b.(MyStruct)`; + # default is false: such cases aren't reported by default. + check-type-assertions: false + + # report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`; + # default is false: such cases aren't reported by default. + check-blank: false + + # [deprecated] comma-separated list of pairs of the form pkg:regex + # the regex is used to ignore names within pkg. (default "fmt:.*"). + # see https://github.com/kisielk/errcheck#the-deprecated-method for details + ignore: fmt:.*,io/ioutil:^Read.* + + govet: + # report about shadowed variables + check-shadowing: false + + golint: + # minimal confidence for issues, default is 0.8 + min-confidence: 0.8 + + gofmt: + # simplify code: gofmt with `-s` option, true by default + simplify: true + + goimports: + # put imports beginning with prefix after 3rd-party packages; + # it's a comma-separated list of prefixes + local-prefixes: github.com/upbound/upjet-provider-template + + gocyclo: + # minimal code complexity to report, 30 by default (but we recommend 10-20) + min-complexity: 10 + + maligned: + # print struct with more effective memory layout or not, false by default + suggest-new: true + + dupl: + # tokens count to trigger issue, 150 by default + threshold: 100 + + goconst: + # minimal length of string constant, 3 by default + min-len: 3 + # minimal occurrences count to trigger, 3 by default + min-occurrences: 5 + + lll: + # tab width in spaces. Default to 1. + tab-width: 1 + + unused: + # treat code as a program (not a library) and report unused exported identifiers; default is false. + # XXX: if you enable this setting, unused will report a lot of false-positives in text editors: + # if it's called for subdir of a project it can't find funcs usages. All text editor integrations + # with golangci-lint call it on a directory with the changed file. + check-exported: false + + unparam: + # Inspect exported functions, default is false. Set to true if no external program/library imports your code. + # XXX: if you enable this setting, unparam will report a lot of false-positives in text editors: + # if it's called for subdir of a project it can't find external interfaces. All text editor integrations + # with golangci-lint call it on a directory with the changed file. + check-exported: false + + nakedret: + # make an issue if func has more lines of code than this setting and it has naked returns; default is 30 + max-func-lines: 30 + + prealloc: + # XXX: we don't recommend using this linter before doing performance profiling. + # For most programs usage of prealloc will be a premature optimization. + + # Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them. + # True by default. + simple: true + range-loops: true # Report preallocation suggestions on range loops, true by default + for-loops: false # Report preallocation suggestions on for loops, false by default + + gocritic: + # Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint` run to see all tags and checks. + # Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags". + enabled-tags: + - performance + + settings: # settings passed to gocritic + captLocal: # must be valid enabled check name + paramsOnly: true + rangeValCopy: + sizeThreshold: 32 + +linters: + enable: + - megacheck + - govet + - gocyclo + - gocritic + - interfacer + - goconst + - goimports + - gofmt # We enable this as well as goimports for its simplify mode. + - prealloc + - golint + - unconvert + - misspell + - nakedret + + presets: + - bugs + - unused + fast: false + + +issues: + # Excluding configuration per-path and per-linter + exclude-rules: + # Exclude some linters from running on tests files. + - path: _test(ing)?\.go + linters: + - gocyclo + - errcheck + - dupl + - gosec + - scopelint + - unparam + + # Ease some gocritic warnings on test files. + - path: _test\.go + text: "(unnamedResult|exitAfterDefer)" + linters: + - gocritic + + # These are performance optimisations rather than style issues per se. + # They warn when function arguments or range values copy a lot of memory + # rather than using a pointer. + - text: "(hugeParam|rangeValCopy):" + linters: + - gocritic + + # This "TestMain should call os.Exit to set exit code" warning is not clever + # enough to notice that we call a helper method that calls os.Exit. + - text: "SA3000:" + linters: + - staticcheck + + - text: "k8s.io/api/core/v1" + linters: + - goimports + + # This is a "potential hardcoded credentials" warning. It's triggered by + # any variable with 'secret' in the same, and thus hits a lot of false + # positives in Kubernetes land where a Secret is an object type. + - text: "G101:" + linters: + - gosec + - gas + + # This is an 'errors unhandled' warning that duplicates errcheck. + - text: "G104:" + linters: + - gosec + - gas + + # Independently from option `exclude` we use default exclude patterns, + # it can be disabled by this option. To list all + # excluded by default patterns execute `golangci-lint run --help`. + # Default value for this option is true. + exclude-use-default: false + + # Show only new issues: if there are unstaged changes or untracked files, + # only those changes are analyzed, else only changes in HEAD~ are analyzed. + # It's a super-useful option for integration of golangci-lint into existing + # large codebase. It's not practical to fix all existing issues at the moment + # of integration: much better don't allow issues in new code. + # Default is false. + new: false + + # Maximum issues count per one linter. Set to 0 to disable. Default is 50. + max-per-linter: 0 + + # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. + max-same-issues: 0 diff --git a/CODEOWNERS b/CODEOWNERS new file mode 100644 index 0000000..2645cd0 --- /dev/null +++ b/CODEOWNERS @@ -0,0 +1,17 @@ +# This file controls automatic PR reviewer assignment. See the following docs: +# +# * https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners +# * https://docs.github.com/en/organizations/organizing-members-into-teams/managing-code-review-settings-for-your-team +# +# The goal of this file is for most PRs to automatically and fairly have one +# maintainer set as PR reviewers. All maintainers have permission to approve +# and merge PRs. All PRs must be approved by at least one maintainer before being merged. +# +# Where possible, prefer explicitly specifying a maintainer who is a subject +# matter expert for a particular part of the codebase rather than using the +# @upbound/team-extensions group. +# +# See also OWNERS.md for governance details + +# Fallback owners +* @ulucinar @sergenyalcin diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..18edcaa --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,3 @@ +## Code of Conduct + +Upjet is under [the Apache 2.0 license](LICENSE) with [notice](NOTICE). \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..5695f4d --- /dev/null +++ b/LICENSE @@ -0,0 +1,201 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "{}" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [YEAR] Upbound Inc. All rights reserved. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..a9c9cdd --- /dev/null +++ b/Makefile @@ -0,0 +1,244 @@ +# ==================================================================================== +# Setup Project + +PROJECT_NAME ?= upjet-provider-template +PROJECT_REPO ?= github.com/upbound/$(PROJECT_NAME) + +export TERRAFORM_VERSION ?= 1.2.1 + +export TERRAFORM_PROVIDER_SOURCE ?= hashicorp/null +export TERRAFORM_PROVIDER_REPO ?= https://github.com/hashicorp/terraform-provider-null +export TERRAFORM_PROVIDER_VERSION ?= 3.1.0 +export TERRAFORM_PROVIDER_DOWNLOAD_NAME ?= terraform-provider-null +export TERRAFORM_PROVIDER_DOWNLOAD_URL_PREFIX ?= https://releases.hashicorp.com/$(TERRAFORM_PROVIDER_DOWNLOAD_NAME)/$(TERRAFORM_PROVIDER_VERSION) +export TERRAFORM_NATIVE_PROVIDER_BINARY ?= terraform-provider-null_v3.1.0_x5 +export TERRAFORM_DOCS_PATH ?= docs/resources + + +PLATFORMS ?= linux_amd64 linux_arm64 + +# -include will silently skip missing files, which allows us +# to load those files with a target in the Makefile. If only +# "include" was used, the make command would fail and refuse +# to run a target until the include commands succeeded. +-include build/makelib/common.mk + +# ==================================================================================== +# Setup Output + +-include build/makelib/output.mk + +# ==================================================================================== +# Setup Go + +# Set a sane default so that the nprocs calculation below is less noisy on the initial +# loading of this file +NPROCS ?= 1 + +# each of our test suites starts a kube-apiserver and running many test suites in +# parallel can lead to high CPU utilization. by default we reduce the parallelism +# to half the number of CPU cores. +GO_TEST_PARALLEL := $(shell echo $$(( $(NPROCS) / 2 ))) + +GO_REQUIRED_VERSION ?= 1.19 +GOLANGCILINT_VERSION ?= 1.50.0 +GO_STATIC_PACKAGES = $(GO_PROJECT)/cmd/provider $(GO_PROJECT)/cmd/generator +GO_LDFLAGS += -X $(GO_PROJECT)/internal/version.Version=$(VERSION) +GO_SUBDIRS += cmd internal apis +-include build/makelib/golang.mk + +# ==================================================================================== +# Setup Kubernetes tools + +KIND_VERSION = v0.15.0 +UP_VERSION = v0.18.0 +UP_CHANNEL = stable +UPTEST_VERSION = v0.5.0 +-include build/makelib/k8s_tools.mk + +# ==================================================================================== +# Setup Images + +REGISTRY_ORGS ?= xpkg.upbound.io/upbound +IMAGES = $(PROJECT_NAME) +-include build/makelib/imagelight.mk + +# ==================================================================================== +# Setup XPKG + +XPKG_REG_ORGS ?= xpkg.upbound.io/upbound +# NOTE(hasheddan): skip promoting on xpkg.upbound.io as channel tags are +# inferred. +XPKG_REG_ORGS_NO_PROMOTE ?= xpkg.upbound.io/upbound +XPKGS = $(PROJECT_NAME) +-include build/makelib/xpkg.mk + +# ==================================================================================== +# Fallthrough + +# run `make help` to see the targets and options + +# We want submodules to be set up the first time `make` is run. +# We manage the build/ folder and its Makefiles as a submodule. +# The first time `make` is run, the includes of build/*.mk files will +# all fail, and this target will be run. The next time, the default as defined +# by the includes will be run instead. +fallthrough: submodules + @echo Initial setup complete. Running make again . . . + @make + +# NOTE(hasheddan): we force image building to happen prior to xpkg build so that +# we ensure image is present in daemon. +xpkg.build.upjet-provider-template: do.build.images + +# NOTE(hasheddan): we ensure up is installed prior to running platform-specific +# build steps in parallel to avoid encountering an installation race condition. +build.init: $(UP) + +# ==================================================================================== +# Setup Terraform for fetching provider schema +TERRAFORM := $(TOOLS_HOST_DIR)/terraform-$(TERRAFORM_VERSION) +TERRAFORM_WORKDIR := $(WORK_DIR)/terraform +TERRAFORM_PROVIDER_SCHEMA := config/schema.json + +$(TERRAFORM): + @$(INFO) installing terraform $(HOSTOS)-$(HOSTARCH) + @mkdir -p $(TOOLS_HOST_DIR)/tmp-terraform + @curl -fsSL https://releases.hashicorp.com/terraform/$(TERRAFORM_VERSION)/terraform_$(TERRAFORM_VERSION)_$(SAFEHOST_PLATFORM).zip -o $(TOOLS_HOST_DIR)/tmp-terraform/terraform.zip + @unzip $(TOOLS_HOST_DIR)/tmp-terraform/terraform.zip -d $(TOOLS_HOST_DIR)/tmp-terraform + @mv $(TOOLS_HOST_DIR)/tmp-terraform/terraform $(TERRAFORM) + @rm -fr $(TOOLS_HOST_DIR)/tmp-terraform + @$(OK) installing terraform $(HOSTOS)-$(HOSTARCH) + +$(TERRAFORM_PROVIDER_SCHEMA): $(TERRAFORM) + @$(INFO) generating provider schema for $(TERRAFORM_PROVIDER_SOURCE) $(TERRAFORM_PROVIDER_VERSION) + @mkdir -p $(TERRAFORM_WORKDIR) + @echo '{"terraform":[{"required_providers":[{"provider":{"source":"'"$(TERRAFORM_PROVIDER_SOURCE)"'","version":"'"$(TERRAFORM_PROVIDER_VERSION)"'"}}],"required_version":"'"$(TERRAFORM_VERSION)"'"}]}' > $(TERRAFORM_WORKDIR)/main.tf.json + @$(TERRAFORM) -chdir=$(TERRAFORM_WORKDIR) init > $(TERRAFORM_WORKDIR)/terraform-logs.txt 2>&1 + @$(TERRAFORM) -chdir=$(TERRAFORM_WORKDIR) providers schema -json=true > $(TERRAFORM_PROVIDER_SCHEMA) 2>> $(TERRAFORM_WORKDIR)/terraform-logs.txt + @$(OK) generating provider schema for $(TERRAFORM_PROVIDER_SOURCE) $(TERRAFORM_PROVIDER_VERSION) + +pull-docs: + @if [ ! -d "$(WORK_DIR)/$(TERRAFORM_PROVIDER_SOURCE)" ]; then \ + mkdir -p "$(WORK_DIR)/$(TERRAFORM_PROVIDER_SOURCE)" && \ + git clone -c advice.detachedHead=false --depth 1 --filter=blob:none --branch "v$(TERRAFORM_PROVIDER_VERSION)" --sparse "$(TERRAFORM_PROVIDER_REPO)" "$(WORK_DIR)/$(TERRAFORM_PROVIDER_SOURCE)"; \ + fi + @git -C "$(WORK_DIR)/$(TERRAFORM_PROVIDER_SOURCE)" sparse-checkout set "$(TERRAFORM_DOCS_PATH)" + +generate.init: $(TERRAFORM_PROVIDER_SCHEMA) pull-docs + +.PHONY: $(TERRAFORM_PROVIDER_SCHEMA) pull-docs +# ==================================================================================== +# Targets + +# NOTE: the build submodule currently overrides XDG_CACHE_HOME in order to +# force the Helm 3 to use the .work/helm directory. This causes Go on Linux +# machines to use that directory as the build cache as well. We should adjust +# this behavior in the build submodule because it is also causing Linux users +# to duplicate their build cache, but for now we just make it easier to identify +# its location in CI so that we cache between builds. +go.cachedir: + @go env GOCACHE + +# Generate a coverage report for cobertura applying exclusions on +# - generated file +cobertura: + @cat $(GO_TEST_OUTPUT)/coverage.txt | \ + grep -v zz_ | \ + $(GOCOVER_COBERTURA) > $(GO_TEST_OUTPUT)/cobertura-coverage.xml + +# Update the submodules, such as the common build scripts. +submodules: + @git submodule sync + @git submodule update --init --recursive + +# This is for running out-of-cluster locally, and is for convenience. Running +# this make target will print out the command which was used. For more control, +# try running the binary directly with different arguments. +run: go.build + @$(INFO) Running Crossplane locally out-of-cluster . . . + @# To see other arguments that can be provided, run the command with --help instead + UPBOUND_CONTEXT="local" $(GO_OUT_DIR)/provider --debug + +# ==================================================================================== +# End to End Testing +CROSSPLANE_NAMESPACE = upbound-system +-include build/makelib/local.xpkg.mk +-include build/makelib/controlplane.mk + +# This target requires the following environment variables to be set: +# - UPTEST_EXAMPLE_LIST, a comma-separated list of examples to test +# To ensure the proper functioning of the end-to-end test resource pre-deletion hook, it is crucial to arrange your resources appropriately. +# You can check the basic implementation here: https://github.com/upbound/uptest/blob/main/internal/templates/01-delete.yaml.tmpl. +# - UPTEST_CLOUD_CREDENTIALS (optional), multiple sets of AWS IAM User credentials specified as key=value pairs. +# The support keys are currently `DEFAULT` and `PEER`. So, an example for the value of this env. variable is: +# DEFAULT='[default] +# aws_access_key_id = REDACTED +# aws_secret_access_key = REDACTED' +# PEER='[default] +# aws_access_key_id = REDACTED +# aws_secret_access_key = REDACTED' +# The associated `ProviderConfig`s will be named as `default` and `peer`. +# - UPTEST_DATASOURCE_PATH (optional), see https://github.com/upbound/uptest#injecting-dynamic-values-and-datasource +uptest: $(UPTEST) $(KUBECTL) $(KUTTL) + @$(INFO) running automated tests + @KUBECTL=$(KUBECTL) KUTTL=$(KUTTL) $(UPTEST) e2e "${UPTEST_EXAMPLE_LIST}" --data-source="${UPTEST_DATASOURCE_PATH}" --setup-script=cluster/test/setup.sh --default-conditions="Test" || $(FAIL) + @$(OK) running automated tests + +local-deploy: build controlplane.up local.xpkg.deploy.provider.$(PROJECT_NAME) + @$(INFO) running locally built provider + @$(KUBECTL) wait provider.pkg $(PROJECT_NAME) --for condition=Healthy --timeout 5m + @$(KUBECTL) -n upbound-system wait --for=condition=Available deployment --all --timeout=5m + @$(OK) running locally built provider + +e2e: local-deploy uptest + +crddiff: $(UPTEST) + @$(INFO) Checking breaking CRD schema changes + @for crd in $${MODIFIED_CRD_LIST}; do \ + if ! git cat-file -e "$${GITHUB_BASE_REF}:$${crd}" 2>/dev/null; then \ + echo "CRD $${crd} does not exist in the $${GITHUB_BASE_REF} branch. Skipping..." ; \ + continue ; \ + fi ; \ + echo "Checking $${crd} for breaking API changes..." ; \ + changes_detected=$$($(UPTEST) crddiff revision <(git cat-file -p "$${GITHUB_BASE_REF}:$${crd}") "$${crd}" 2>&1) ; \ + if [[ $$? != 0 ]] ; then \ + printf "\033[31m"; echo "Breaking change detected!"; printf "\033[0m" ; \ + echo "$${changes_detected}" ; \ + echo ; \ + fi ; \ + done + @$(OK) Checking breaking CRD schema changes + +schema-version-diff: + @$(INFO) Checking for native state schema version changes + @export PREV_PROVIDER_VERSION=$$(git cat-file -p "${GITHUB_BASE_REF}:Makefile" | sed -nr 's/^export[[:space:]]*TERRAFORM_PROVIDER_VERSION[[:space:]]*:=[[:space:]]*(.+)/\1/p'); \ + echo Detected previous Terraform provider version: $${PREV_PROVIDER_VERSION}; \ + echo Current Terraform provider version: $${TERRAFORM_PROVIDER_VERSION}; \ + mkdir -p $(WORK_DIR); \ + git cat-file -p "$${GITHUB_BASE_REF}:config/schema.json" > "$(WORK_DIR)/schema.json.$${PREV_PROVIDER_VERSION}"; \ + ./scripts/version_diff.py config/generated.lst "$(WORK_DIR)/schema.json.$${PREV_PROVIDER_VERSION}" config/schema.json + @$(OK) Checking for native state schema version changes + +.PHONY: cobertura submodules fallthrough run crds.clean + +# ==================================================================================== +# Special Targets + +define CROSSPLANE_MAKE_HELP +Crossplane Targets: + cobertura Generate a coverage report for cobertura applying exclusions on generated files. + submodules Update the submodules, such as the common build scripts. + run Run crossplane locally, out-of-cluster. Useful for development. + +endef +# The reason CROSSPLANE_MAKE_HELP is used instead of CROSSPLANE_HELP is because the crossplane +# binary will try to use CROSSPLANE_HELP if it is set, and this is for something different. +export CROSSPLANE_MAKE_HELP + +crossplane.help: + @echo "$$CROSSPLANE_MAKE_HELP" + +help-special: crossplane.help + +.PHONY: crossplane.help help-special diff --git a/OWNERS.md b/OWNERS.md new file mode 100644 index 0000000..599b40e --- /dev/null +++ b/OWNERS.md @@ -0,0 +1,13 @@ +# OWNERS + +This page lists all maintainers for **this** repository. Each repository in the [Upbound +organization](https://github.com/upbound/) will list their repository maintainers in their own +`OWNERS.md` file. + + +## Maintainers + +* Alper Ulucinar ([ulucinar](https://github.com/ulucinar)) +* Sergen Yalcin ([sergenyalcin](https://github.com/sergenyalcin)) + +See [CODEOWNERS](./CODEOWNERS) for automatic PR assignment. diff --git a/README.md b/README.md new file mode 100644 index 0000000..e57c399 --- /dev/null +++ b/README.md @@ -0,0 +1,60 @@ +# Provider Template + +`upjet-provider-template` is a [Crossplane](https://crossplane.io/) provider that +is built using [Upjet](https://github.com/crossplane/upjet) code +generation tools and exposes XRM-conformant managed resources for the +Template API. + +## Getting Started + +Install the provider by using the following command after changing the image tag +to the [latest release](https://marketplace.upbound.io/providers/upbound/upjet-provider-template): +``` +up ctp provider install upbound/upjet-provider-template:v0.1.0 +``` + +Alternatively, you can use declarative installation: +``` +cat < +// +// SPDX-License-Identifier: Apache-2.0 + +/* +Copyright 2022 Upbound Inc. +*/ + +// Code generated by upjet. DO NOT EDIT. + +package v1alpha1 + +import ( + "github.com/pkg/errors" + + "github.com/crossplane/upjet/pkg/resource" + "github.com/crossplane/upjet/pkg/resource/json" +) + +// GetTerraformResourceType returns Terraform resource type for this Resource +func (mg *Resource) GetTerraformResourceType() string { + return "null_resource" +} + +// GetConnectionDetailsMapping for this Resource +func (tr *Resource) GetConnectionDetailsMapping() map[string]string { + return nil +} + +// GetObservation of this Resource +func (tr *Resource) GetObservation() (map[string]any, error) { + o, err := json.TFParser.Marshal(tr.Status.AtProvider) + if err != nil { + return nil, err + } + base := map[string]any{} + return base, json.TFParser.Unmarshal(o, &base) +} + +// SetObservation for this Resource +func (tr *Resource) SetObservation(obs map[string]any) error { + p, err := json.TFParser.Marshal(obs) + if err != nil { + return err + } + return json.TFParser.Unmarshal(p, &tr.Status.AtProvider) +} + +// GetID returns ID of underlying Terraform resource of this Resource +func (tr *Resource) GetID() string { + if tr.Status.AtProvider.ID == nil { + return "" + } + return *tr.Status.AtProvider.ID +} + +// GetParameters of this Resource +func (tr *Resource) GetParameters() (map[string]any, error) { + p, err := json.TFParser.Marshal(tr.Spec.ForProvider) + if err != nil { + return nil, err + } + base := map[string]any{} + return base, json.TFParser.Unmarshal(p, &base) +} + +// SetParameters for this Resource +func (tr *Resource) SetParameters(params map[string]any) error { + p, err := json.TFParser.Marshal(params) + if err != nil { + return err + } + return json.TFParser.Unmarshal(p, &tr.Spec.ForProvider) +} + +// GetInitParameters of this Resource +func (tr *Resource) GetInitParameters() (map[string]any, error) { + p, err := json.TFParser.Marshal(tr.Spec.InitProvider) + if err != nil { + return nil, err + } + base := map[string]any{} + return base, json.TFParser.Unmarshal(p, &base) +} + +// LateInitialize this Resource using its observed tfState. +// returns True if there are any spec changes for the resource. +func (tr *Resource) LateInitialize(attrs []byte) (bool, error) { + params := &ResourceParameters{} + if err := json.TFParser.Unmarshal(attrs, params); err != nil { + return false, errors.Wrap(err, "failed to unmarshal Terraform state parameters for late-initialization") + } + opts := []resource.GenericLateInitializerOption{resource.WithZeroValueJSONOmitEmptyFilter(resource.CNameWildcard)} + + li := resource.NewGenericLateInitializer(opts...) + return li.LateInitialize(&tr.Spec.ForProvider, params) +} + +// GetTerraformSchemaVersion returns the associated Terraform schema version +func (tr *Resource) GetTerraformSchemaVersion() int { + return 0 +} diff --git a/apis/null/v1alpha1/zz_groupversion_info.go b/apis/null/v1alpha1/zz_groupversion_info.go new file mode 100755 index 0000000..3b570c9 --- /dev/null +++ b/apis/null/v1alpha1/zz_groupversion_info.go @@ -0,0 +1,36 @@ +// SPDX-FileCopyrightText: 2023 The Crossplane Authors +// +// SPDX-License-Identifier: Apache-2.0 + +/* +Copyright 2022 Upbound Inc. +*/ + +// Code generated by upjet. DO NOT EDIT. + +// +kubebuilder:object:generate=true +// +groupName=null.template.upbound.io +// +versionName=v1alpha1 +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +// Package type metadata. +const ( + CRDGroup = "null.template.upbound.io" + CRDVersion = "v1alpha1" +) + +var ( + // CRDGroupVersion is the API Group Version used to register the objects + CRDGroupVersion = schema.GroupVersion{Group: CRDGroup, Version: CRDVersion} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: CRDGroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/apis/null/v1alpha1/zz_resource_types.go b/apis/null/v1alpha1/zz_resource_types.go new file mode 100755 index 0000000..7801a3b --- /dev/null +++ b/apis/null/v1alpha1/zz_resource_types.go @@ -0,0 +1,98 @@ +// SPDX-FileCopyrightText: 2023 The Crossplane Authors +// +// SPDX-License-Identifier: Apache-2.0 + +/* +Copyright 2022 Upbound Inc. +*/ + +// Code generated by upjet. DO NOT EDIT. + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + + v1 "github.com/crossplane/crossplane-runtime/apis/common/v1" +) + +type ResourceInitParameters struct { + + // A map of arbitrary strings that, when changed, will force the null resource to be replaced, re-running any associated provisioners. + Triggers map[string]*string `json:"triggers,omitempty" tf:"triggers,omitempty"` +} + +type ResourceObservation struct { + ID *string `json:"id,omitempty" tf:"id,omitempty"` + + // A map of arbitrary strings that, when changed, will force the null resource to be replaced, re-running any associated provisioners. + Triggers map[string]*string `json:"triggers,omitempty" tf:"triggers,omitempty"` +} + +type ResourceParameters struct { + + // A map of arbitrary strings that, when changed, will force the null resource to be replaced, re-running any associated provisioners. + // +kubebuilder:validation:Optional + Triggers map[string]*string `json:"triggers,omitempty" tf:"triggers,omitempty"` +} + +// ResourceSpec defines the desired state of Resource +type ResourceSpec struct { + v1.ResourceSpec `json:",inline"` + ForProvider ResourceParameters `json:"forProvider"` + // THIS IS A BETA FIELD. It will be honored + // unless the Management Policies feature flag is disabled. + // InitProvider holds the same fields as ForProvider, with the exception + // of Identifier and other resource reference fields. The fields that are + // in InitProvider are merged into ForProvider when the resource is created. + // The same fields are also added to the terraform ignore_changes hook, to + // avoid updating them after creation. This is useful for fields that are + // required on creation, but we do not desire to update them after creation, + // for example because of an external controller is managing them, like an + // autoscaler. + InitProvider ResourceInitParameters `json:"initProvider,omitempty"` +} + +// ResourceStatus defines the observed state of Resource. +type ResourceStatus struct { + v1.ResourceStatus `json:",inline"` + AtProvider ResourceObservation `json:"atProvider,omitempty"` +} + +// +kubebuilder:object:root=true + +// Resource is the Schema for the Resources API. The null_resource resource implements the standard resource lifecycle but takes no further action. The triggers argument allows specifying an arbitrary set of values that, when changed, will cause the resource to be replaced. +// +kubebuilder:printcolumn:name="READY",type="string",JSONPath=".status.conditions[?(@.type=='Ready')].status" +// +kubebuilder:printcolumn:name="SYNCED",type="string",JSONPath=".status.conditions[?(@.type=='Synced')].status" +// +kubebuilder:printcolumn:name="EXTERNAL-NAME",type="string",JSONPath=".metadata.annotations.crossplane\\.io/external-name" +// +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" +// +kubebuilder:subresource:status +// +kubebuilder:resource:scope=Cluster,categories={crossplane,managed,template} +type Resource struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + Spec ResourceSpec `json:"spec"` + Status ResourceStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// ResourceList contains a list of Resources +type ResourceList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Resource `json:"items"` +} + +// Repository type metadata. +var ( + Resource_Kind = "Resource" + Resource_GroupKind = schema.GroupKind{Group: CRDGroup, Kind: Resource_Kind}.String() + Resource_KindAPIVersion = Resource_Kind + "." + CRDGroupVersion.String() + Resource_GroupVersionKind = CRDGroupVersion.WithKind(Resource_Kind) +) + +func init() { + SchemeBuilder.Register(&Resource{}, &ResourceList{}) +} diff --git a/apis/v1alpha1/doc.go b/apis/v1alpha1/doc.go new file mode 100644 index 0000000..468ffa7 --- /dev/null +++ b/apis/v1alpha1/doc.go @@ -0,0 +1,9 @@ +/* +Copyright 2021 Upbound Inc. +*/ + +// Package v1alpha1 contains the core resources of the template jet provider. +// +kubebuilder:object:generate=true +// +groupName=template.upbound.io +// +versionName=v1alpha1 +package v1alpha1 diff --git a/apis/v1alpha1/register.go b/apis/v1alpha1/register.go new file mode 100644 index 0000000..038d8b3 --- /dev/null +++ b/apis/v1alpha1/register.go @@ -0,0 +1,38 @@ +/* +Copyright 2021 Upbound Inc. +*/ + +package v1alpha1 + +import ( + "reflect" + + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +// Package type metadata. +const ( + Group = "template.upbound.io" + Version = "v1alpha1" +) + +var ( + // SchemeGroupVersion is group version used to register these objects + SchemeGroupVersion = schema.GroupVersion{Group: Group, Version: Version} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} +) + +// StoreConfig type metadata. +var ( + StoreConfigKind = reflect.TypeOf(StoreConfig{}).Name() + StoreConfigGroupKind = schema.GroupKind{Group: Group, Kind: StoreConfigKind}.String() + StoreConfigKindAPIVersion = StoreConfigKind + "." + SchemeGroupVersion.String() + StoreConfigGroupVersionKind = SchemeGroupVersion.WithKind(StoreConfigKind) +) + +func init() { + SchemeBuilder.Register(&StoreConfig{}, &StoreConfigList{}) +} diff --git a/apis/v1alpha1/types.go b/apis/v1alpha1/types.go new file mode 100644 index 0000000..3f8f25a --- /dev/null +++ b/apis/v1alpha1/types.go @@ -0,0 +1,63 @@ +/* +Copyright 2021 Upbound Inc. +*/ + +package v1alpha1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" +) + +// A StoreConfigSpec defines the desired state of a ProviderConfig. +type StoreConfigSpec struct { + xpv1.SecretStoreConfig `json:",inline"` +} + +// A StoreConfigStatus represents the status of a StoreConfig. +type StoreConfigStatus struct { + xpv1.ConditionedStatus `json:",inline"` +} + +// +kubebuilder:object:root=true + +// A StoreConfig configures how template controller should store connection details. +// +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" +// +kubebuilder:printcolumn:name="TYPE",type="string",JSONPath=".spec.type" +// +kubebuilder:printcolumn:name="DEFAULT-SCOPE",type="string",JSONPath=".spec.defaultScope" +// +kubebuilder:resource:scope=Cluster,categories={crossplane,store,template} +// +kubebuilder:subresource:status +type StoreConfig struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec StoreConfigSpec `json:"spec"` + Status StoreConfigStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// StoreConfigList contains a list of StoreConfig +type StoreConfigList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []StoreConfig `json:"items"` +} + +// Note(turkenh): To be generated with AngryJet + +// GetStoreConfig returns SecretStoreConfig +func (in *StoreConfig) GetStoreConfig() xpv1.SecretStoreConfig { + return in.Spec.SecretStoreConfig +} + +// GetCondition of this StoreConfig. +func (in *StoreConfig) GetCondition(ct xpv1.ConditionType) xpv1.Condition { + return in.Status.GetCondition(ct) +} + +// SetConditions of this StoreConfig. +func (in *StoreConfig) SetConditions(c ...xpv1.Condition) { + in.Status.SetConditions(c...) +} diff --git a/apis/v1alpha1/zz_generated.deepcopy.go b/apis/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 0000000..86f2d29 --- /dev/null +++ b/apis/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,104 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2022 Upbound Inc. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StoreConfig) DeepCopyInto(out *StoreConfig) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StoreConfig. +func (in *StoreConfig) DeepCopy() *StoreConfig { + if in == nil { + return nil + } + out := new(StoreConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *StoreConfig) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StoreConfigList) DeepCopyInto(out *StoreConfigList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]StoreConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StoreConfigList. +func (in *StoreConfigList) DeepCopy() *StoreConfigList { + if in == nil { + return nil + } + out := new(StoreConfigList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *StoreConfigList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StoreConfigSpec) DeepCopyInto(out *StoreConfigSpec) { + *out = *in + in.SecretStoreConfig.DeepCopyInto(&out.SecretStoreConfig) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StoreConfigSpec. +func (in *StoreConfigSpec) DeepCopy() *StoreConfigSpec { + if in == nil { + return nil + } + out := new(StoreConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *StoreConfigStatus) DeepCopyInto(out *StoreConfigStatus) { + *out = *in + in.ConditionedStatus.DeepCopyInto(&out.ConditionedStatus) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StoreConfigStatus. +func (in *StoreConfigStatus) DeepCopy() *StoreConfigStatus { + if in == nil { + return nil + } + out := new(StoreConfigStatus) + in.DeepCopyInto(out) + return out +} diff --git a/apis/v1beta1/doc.go b/apis/v1beta1/doc.go new file mode 100644 index 0000000..71a80b6 --- /dev/null +++ b/apis/v1beta1/doc.go @@ -0,0 +1,9 @@ +/* +Copyright 2022 Upbound Inc. +*/ + +// Package v1beta1 contains the core resources of the template upjet provider. +// +kubebuilder:object:generate=true +// +groupName=template.upbound.io +// +versionName=v1beta1 +package v1beta1 diff --git a/apis/v1beta1/register.go b/apis/v1beta1/register.go new file mode 100644 index 0000000..a99c36e --- /dev/null +++ b/apis/v1beta1/register.go @@ -0,0 +1,52 @@ +/* +Copyright 2022 Upbound Inc. +*/ + +package v1beta1 + +import ( + "reflect" + + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +// Package type metadata. +const ( + Group = "template.upbound.io" + Version = "v1beta1" +) + +var ( + // SchemeGroupVersion is group version used to register these objects + SchemeGroupVersion = schema.GroupVersion{Group: Group, Version: Version} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: SchemeGroupVersion} +) + +// ProviderConfig type metadata. +var ( + ProviderConfigKind = reflect.TypeOf(ProviderConfig{}).Name() + ProviderConfigGroupKind = schema.GroupKind{Group: Group, Kind: ProviderConfigKind}.String() + ProviderConfigKindAPIVersion = ProviderConfigKind + "." + SchemeGroupVersion.String() + ProviderConfigGroupVersionKind = SchemeGroupVersion.WithKind(ProviderConfigKind) +) + +// ProviderConfigUsage type metadata. +var ( + ProviderConfigUsageKind = reflect.TypeOf(ProviderConfigUsage{}).Name() + ProviderConfigUsageGroupKind = schema.GroupKind{Group: Group, Kind: ProviderConfigUsageKind}.String() + ProviderConfigUsageKindAPIVersion = ProviderConfigUsageKind + "." + SchemeGroupVersion.String() + ProviderConfigUsageGroupVersionKind = SchemeGroupVersion.WithKind(ProviderConfigUsageKind) + + ProviderConfigUsageListKind = reflect.TypeOf(ProviderConfigUsageList{}).Name() + ProviderConfigUsageListGroupKind = schema.GroupKind{Group: Group, Kind: ProviderConfigUsageListKind}.String() + ProviderConfigUsageListKindAPIVersion = ProviderConfigUsageListKind + "." + SchemeGroupVersion.String() + ProviderConfigUsageListGroupVersionKind = SchemeGroupVersion.WithKind(ProviderConfigUsageListKind) +) + +func init() { + SchemeBuilder.Register(&ProviderConfig{}, &ProviderConfigList{}) + SchemeBuilder.Register(&ProviderConfigUsage{}, &ProviderConfigUsageList{}) +} diff --git a/apis/v1beta1/types.go b/apis/v1beta1/types.go new file mode 100644 index 0000000..110474a --- /dev/null +++ b/apis/v1beta1/types.go @@ -0,0 +1,80 @@ +/* +Copyright 2022 Upbound Inc. +*/ + +package v1beta1 + +import ( + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" +) + +// A ProviderConfigSpec defines the desired state of a ProviderConfig. +type ProviderConfigSpec struct { + // Credentials required to authenticate to this provider. + Credentials ProviderCredentials `json:"credentials"` +} + +// ProviderCredentials required to authenticate. +type ProviderCredentials struct { + // Source of the provider credentials. + // +kubebuilder:validation:Enum=None;Secret;InjectedIdentity;Environment;Filesystem + Source xpv1.CredentialsSource `json:"source"` + + xpv1.CommonCredentialSelectors `json:",inline"` +} + +// A ProviderConfigStatus reflects the observed state of a ProviderConfig. +type ProviderConfigStatus struct { + xpv1.ProviderConfigStatus `json:",inline"` +} + +// +kubebuilder:object:root=true + +// A ProviderConfig configures a Template provider. +// +kubebuilder:subresource:status +// +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" +// +kubebuilder:printcolumn:name="SECRET-NAME",type="string",JSONPath=".spec.credentials.secretRef.name",priority=1 +// +kubebuilder:resource:scope=Cluster +// +kubebuilder:resource:scope=Cluster,categories={crossplane,provider,template} +type ProviderConfig struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec ProviderConfigSpec `json:"spec"` + Status ProviderConfigStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// ProviderConfigList contains a list of ProviderConfig. +type ProviderConfigList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ProviderConfig `json:"items"` +} + +// +kubebuilder:object:root=true + +// A ProviderConfigUsage indicates that a resource is using a ProviderConfig. +// +kubebuilder:printcolumn:name="AGE",type="date",JSONPath=".metadata.creationTimestamp" +// +kubebuilder:printcolumn:name="CONFIG-NAME",type="string",JSONPath=".providerConfigRef.name" +// +kubebuilder:printcolumn:name="RESOURCE-KIND",type="string",JSONPath=".resourceRef.kind" +// +kubebuilder:printcolumn:name="RESOURCE-NAME",type="string",JSONPath=".resourceRef.name" +// +kubebuilder:resource:scope=Cluster,categories={crossplane,provider,template} +type ProviderConfigUsage struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + xpv1.ProviderConfigUsage `json:",inline"` +} + +// +kubebuilder:object:root=true + +// ProviderConfigUsageList contains a list of ProviderConfigUsage +type ProviderConfigUsageList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []ProviderConfigUsage `json:"items"` +} diff --git a/apis/v1beta1/zz_generated.deepcopy.go b/apis/v1beta1/zz_generated.deepcopy.go new file mode 100644 index 0000000..09df2c1 --- /dev/null +++ b/apis/v1beta1/zz_generated.deepcopy.go @@ -0,0 +1,178 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2022 Upbound Inc. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package v1beta1 + +import ( + runtime "k8s.io/apimachinery/pkg/runtime" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProviderConfig) DeepCopyInto(out *ProviderConfig) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) + in.Status.DeepCopyInto(&out.Status) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProviderConfig. +func (in *ProviderConfig) DeepCopy() *ProviderConfig { + if in == nil { + return nil + } + out := new(ProviderConfig) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ProviderConfig) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProviderConfigList) DeepCopyInto(out *ProviderConfigList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ProviderConfig, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProviderConfigList. +func (in *ProviderConfigList) DeepCopy() *ProviderConfigList { + if in == nil { + return nil + } + out := new(ProviderConfigList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ProviderConfigList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProviderConfigSpec) DeepCopyInto(out *ProviderConfigSpec) { + *out = *in + in.Credentials.DeepCopyInto(&out.Credentials) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProviderConfigSpec. +func (in *ProviderConfigSpec) DeepCopy() *ProviderConfigSpec { + if in == nil { + return nil + } + out := new(ProviderConfigSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProviderConfigStatus) DeepCopyInto(out *ProviderConfigStatus) { + *out = *in + in.ProviderConfigStatus.DeepCopyInto(&out.ProviderConfigStatus) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProviderConfigStatus. +func (in *ProviderConfigStatus) DeepCopy() *ProviderConfigStatus { + if in == nil { + return nil + } + out := new(ProviderConfigStatus) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProviderConfigUsage) DeepCopyInto(out *ProviderConfigUsage) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.ProviderConfigUsage.DeepCopyInto(&out.ProviderConfigUsage) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProviderConfigUsage. +func (in *ProviderConfigUsage) DeepCopy() *ProviderConfigUsage { + if in == nil { + return nil + } + out := new(ProviderConfigUsage) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ProviderConfigUsage) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProviderConfigUsageList) DeepCopyInto(out *ProviderConfigUsageList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]ProviderConfigUsage, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProviderConfigUsageList. +func (in *ProviderConfigUsageList) DeepCopy() *ProviderConfigUsageList { + if in == nil { + return nil + } + out := new(ProviderConfigUsageList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *ProviderConfigUsageList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ProviderCredentials) DeepCopyInto(out *ProviderCredentials) { + *out = *in + in.CommonCredentialSelectors.DeepCopyInto(&out.CommonCredentialSelectors) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ProviderCredentials. +func (in *ProviderCredentials) DeepCopy() *ProviderCredentials { + if in == nil { + return nil + } + out := new(ProviderCredentials) + in.DeepCopyInto(out) + return out +} diff --git a/apis/v1beta1/zz_generated.pc.go b/apis/v1beta1/zz_generated.pc.go new file mode 100644 index 0000000..c597006 --- /dev/null +++ b/apis/v1beta1/zz_generated.pc.go @@ -0,0 +1,28 @@ +/* +Copyright 2022 Upbound Inc. +*/ +// Code generated by angryjet. DO NOT EDIT. + +package v1beta1 + +import xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + +// GetCondition of this ProviderConfig. +func (p *ProviderConfig) GetCondition(ct xpv1.ConditionType) xpv1.Condition { + return p.Status.GetCondition(ct) +} + +// GetUsers of this ProviderConfig. +func (p *ProviderConfig) GetUsers() int64 { + return p.Status.Users +} + +// SetConditions of this ProviderConfig. +func (p *ProviderConfig) SetConditions(c ...xpv1.Condition) { + p.Status.SetConditions(c...) +} + +// SetUsers of this ProviderConfig. +func (p *ProviderConfig) SetUsers(i int64) { + p.Status.Users = i +} diff --git a/apis/v1beta1/zz_generated.pcu.go b/apis/v1beta1/zz_generated.pcu.go new file mode 100644 index 0000000..a4f4986 --- /dev/null +++ b/apis/v1beta1/zz_generated.pcu.go @@ -0,0 +1,28 @@ +/* +Copyright 2022 Upbound Inc. +*/ +// Code generated by angryjet. DO NOT EDIT. + +package v1beta1 + +import xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + +// GetProviderConfigReference of this ProviderConfigUsage. +func (p *ProviderConfigUsage) GetProviderConfigReference() xpv1.Reference { + return p.ProviderConfigReference +} + +// GetResourceReference of this ProviderConfigUsage. +func (p *ProviderConfigUsage) GetResourceReference() xpv1.TypedReference { + return p.ResourceReference +} + +// SetProviderConfigReference of this ProviderConfigUsage. +func (p *ProviderConfigUsage) SetProviderConfigReference(r xpv1.Reference) { + p.ProviderConfigReference = r +} + +// SetResourceReference of this ProviderConfigUsage. +func (p *ProviderConfigUsage) SetResourceReference(r xpv1.TypedReference) { + p.ResourceReference = r +} diff --git a/apis/v1beta1/zz_generated.pculist.go b/apis/v1beta1/zz_generated.pculist.go new file mode 100644 index 0000000..b3ae1b4 --- /dev/null +++ b/apis/v1beta1/zz_generated.pculist.go @@ -0,0 +1,17 @@ +/* +Copyright 2022 Upbound Inc. +*/ +// Code generated by angryjet. DO NOT EDIT. + +package v1beta1 + +import resource "github.com/crossplane/crossplane-runtime/pkg/resource" + +// GetItems of this ProviderConfigUsageList. +func (p *ProviderConfigUsageList) GetItems() []resource.ProviderConfigUsage { + items := make([]resource.ProviderConfigUsage, len(p.Items)) + for i := range p.Items { + items[i] = &p.Items[i] + } + return items +} diff --git a/apis/zz_register.go b/apis/zz_register.go new file mode 100755 index 0000000..9a6d212 --- /dev/null +++ b/apis/zz_register.go @@ -0,0 +1,37 @@ +// SPDX-FileCopyrightText: 2023 The Crossplane Authors +// +// SPDX-License-Identifier: Apache-2.0 + +/* +Copyright 2022 Upbound Inc. +*/ + +// Code generated by upjet. DO NOT EDIT. + +// Package apis contains Kubernetes API for the provider. +package apis + +import ( + "k8s.io/apimachinery/pkg/runtime" + + v1alpha1 "github.com/upbound/upjet-provider-template/apis/null/v1alpha1" + v1alpha1apis "github.com/upbound/upjet-provider-template/apis/v1alpha1" + v1beta1 "github.com/upbound/upjet-provider-template/apis/v1beta1" +) + +func init() { + // Register the types with the Scheme so the components can map objects to GroupVersionKinds and back + AddToSchemes = append(AddToSchemes, + v1alpha1.SchemeBuilder.AddToScheme, + v1alpha1apis.SchemeBuilder.AddToScheme, + v1beta1.SchemeBuilder.AddToScheme, + ) +} + +// AddToSchemes may be used to add all resources defined in the project to a Scheme +var AddToSchemes runtime.SchemeBuilder + +// AddToScheme adds all Resources to the Scheme +func AddToScheme(s *runtime.Scheme) error { + return AddToSchemes.AddToScheme(s) +} diff --git a/build b/build new file mode 160000 index 0000000..bd5297b --- /dev/null +++ b/build @@ -0,0 +1 @@ +Subproject commit bd5297bd16c113cbc5ed1905b1d96aa1cb3078ec diff --git a/cluster/images/upjet-provider-template/Dockerfile b/cluster/images/upjet-provider-template/Dockerfile new file mode 100644 index 0000000..61193f6 --- /dev/null +++ b/cluster/images/upjet-provider-template/Dockerfile @@ -0,0 +1,51 @@ +FROM alpine:3.17.1 +RUN apk --no-cache add ca-certificates bash + +ARG TARGETOS +ARG TARGETARCH + +ADD "bin/${TARGETOS}_${TARGETARCH}/provider" /usr/local/bin/provider + +ENV USER_ID=65532 + +# Setup Terraform environment + +## Provider-dependent configuration +ARG TERRAFORM_VERSION +ARG TERRAFORM_PROVIDER_SOURCE +ARG TERRAFORM_PROVIDER_VERSION +ARG TERRAFORM_PROVIDER_DOWNLOAD_NAME +ARG TERRAFORM_NATIVE_PROVIDER_BINARY +ARG TERRAFORM_PROVIDER_DOWNLOAD_URL_PREFIX + +## End of - Provider-dependent configuration + +ENV PLUGIN_DIR /terraform/provider-mirror/registry.terraform.io/${TERRAFORM_PROVIDER_SOURCE}/${TERRAFORM_PROVIDER_VERSION}/${TARGETOS}_${TARGETARCH} +ENV TF_CLI_CONFIG_FILE /terraform/.terraformrc +ENV TF_FORK 0 + +RUN mkdir -p ${PLUGIN_DIR} + +ADD https://releases.hashicorp.com/terraform/${TERRAFORM_VERSION}/terraform_${TERRAFORM_VERSION}_${TARGETOS}_${TARGETARCH}.zip /tmp +ADD ${TERRAFORM_PROVIDER_DOWNLOAD_URL_PREFIX}/${TERRAFORM_PROVIDER_DOWNLOAD_NAME}_${TERRAFORM_PROVIDER_VERSION}_${TARGETOS}_${TARGETARCH}.zip /tmp +ADD terraformrc.hcl ${TF_CLI_CONFIG_FILE} + +RUN unzip /tmp/terraform_${TERRAFORM_VERSION}_${TARGETOS}_${TARGETARCH}.zip -d /usr/local/bin \ + && chmod +x /usr/local/bin/terraform \ + && rm /tmp/terraform_${TERRAFORM_VERSION}_${TARGETOS}_${TARGETARCH}.zip \ + && unzip /tmp/${TERRAFORM_PROVIDER_DOWNLOAD_NAME}_${TERRAFORM_PROVIDER_VERSION}_${TARGETOS}_${TARGETARCH}.zip -d ${PLUGIN_DIR} \ + && chmod +x ${PLUGIN_DIR}/* \ + && rm /tmp/${TERRAFORM_PROVIDER_DOWNLOAD_NAME}_${TERRAFORM_PROVIDER_VERSION}_${TARGETOS}_${TARGETARCH}.zip \ + && chown -R ${USER_ID}:${USER_ID} /terraform +# End of - Setup Terraform environment + +# Provider controller needs these environment variable at runtime +ENV TERRAFORM_VERSION ${TERRAFORM_VERSION} +ENV TERRAFORM_PROVIDER_SOURCE ${TERRAFORM_PROVIDER_SOURCE} +ENV TERRAFORM_PROVIDER_VERSION ${TERRAFORM_PROVIDER_VERSION} +ENV TERRAFORM_NATIVE_PROVIDER_PATH ${PLUGIN_DIR}/${TERRAFORM_NATIVE_PROVIDER_BINARY} + +USER ${USER_ID} +EXPOSE 8080 + +ENTRYPOINT ["provider"] diff --git a/cluster/images/upjet-provider-template/Makefile b/cluster/images/upjet-provider-template/Makefile new file mode 100755 index 0000000..083bec1 --- /dev/null +++ b/cluster/images/upjet-provider-template/Makefile @@ -0,0 +1,42 @@ +# ==================================================================================== +# Setup Project + +include ../../../build/makelib/common.mk + +# ==================================================================================== +# Options + +include ../../../build/makelib/imagelight.mk + +# ==================================================================================== +# Targets + +img.build: + @$(INFO) docker build $(IMAGE) + @$(MAKE) BUILD_ARGS="--load" img.build.shared + @$(OK) docker build $(IMAGE) + +img.publish: + @$(INFO) Skipping image publish for $(IMAGE) + @echo Publish is deferred to xpkg machinery + @$(OK) Image publish skipped for $(IMAGE) + +img.build.shared: + @cp Dockerfile $(IMAGE_TEMP_DIR) || $(FAIL) + @cp terraformrc.hcl $(IMAGE_TEMP_DIR) || $(FAIL) + @cp -r $(OUTPUT_DIR)/bin/ $(IMAGE_TEMP_DIR)/bin || $(FAIL) + @docker buildx build $(BUILD_ARGS) \ + --platform $(IMAGE_PLATFORMS) \ + --build-arg TERRAFORM_VERSION=$(TERRAFORM_VERSION) \ + --build-arg TERRAFORM_PROVIDER_SOURCE=$(TERRAFORM_PROVIDER_SOURCE) \ + --build-arg TERRAFORM_PROVIDER_VERSION=$(TERRAFORM_PROVIDER_VERSION) \ + --build-arg TERRAFORM_PROVIDER_DOWNLOAD_NAME=$(TERRAFORM_PROVIDER_DOWNLOAD_NAME) \ + --build-arg TERRAFORM_PROVIDER_DOWNLOAD_URL_PREFIX=$(TERRAFORM_PROVIDER_DOWNLOAD_URL_PREFIX) \ + --build-arg TERRAFORM_NATIVE_PROVIDER_BINARY=$(TERRAFORM_NATIVE_PROVIDER_BINARY) \ + -t $(IMAGE) \ + $(IMAGE_TEMP_DIR) || $(FAIL) + +img.promote: + @$(INFO) Skipping image promotion from $(FROM_IMAGE) to $(TO_IMAGE) + @echo Promote is deferred to xpkg machinery + @$(OK) Image promotion skipped for $(FROM_IMAGE) to $(TO_IMAGE) diff --git a/cluster/images/upjet-provider-template/terraformrc.hcl b/cluster/images/upjet-provider-template/terraformrc.hcl new file mode 100644 index 0000000..022203c --- /dev/null +++ b/cluster/images/upjet-provider-template/terraformrc.hcl @@ -0,0 +1,9 @@ +provider_installation { + filesystem_mirror { + path = "/terraform/provider-mirror" + include = ["*/*"] + } + direct { + exclude = ["*/*"] + } +} diff --git a/cluster/test/setup.sh b/cluster/test/setup.sh new file mode 100755 index 0000000..f1138cc --- /dev/null +++ b/cluster/test/setup.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash +set -aeuo pipefail + +echo "Running setup.sh" +echo "Creating cloud credential secret..." +${KUBECTL} -n upbound-system create secret generic provider-secret --from-literal=credentials="${UPTEST_CLOUD_CREDENTIALS}" --dry-run=client -o yaml | ${KUBECTL} apply -f - + +echo "Waiting until provider is healthy..." +${KUBECTL} wait provider.pkg --all --for condition=Healthy --timeout 5m + +echo "Waiting for all pods to come online..." +${KUBECTL} -n upbound-system wait --for=condition=Available deployment --all --timeout=5m + +echo "Creating a default provider config..." +cat < +// +// SPDX-License-Identifier: Apache-2.0 + +/* +Copyright 2022 Upbound Inc. +*/ + +// Code generated by upjet. DO NOT EDIT. + +package resource + +import ( + "time" + + "github.com/crossplane/crossplane-runtime/pkg/connection" + "github.com/crossplane/crossplane-runtime/pkg/event" + "github.com/crossplane/crossplane-runtime/pkg/ratelimiter" + "github.com/crossplane/crossplane-runtime/pkg/reconciler/managed" + xpresource "github.com/crossplane/crossplane-runtime/pkg/resource" + tjcontroller "github.com/crossplane/upjet/pkg/controller" + "github.com/crossplane/upjet/pkg/controller/handler" + "github.com/crossplane/upjet/pkg/terraform" + ctrl "sigs.k8s.io/controller-runtime" + + v1alpha1 "github.com/upbound/upjet-provider-template/apis/null/v1alpha1" + features "github.com/upbound/upjet-provider-template/internal/features" +) + +// Setup adds a controller that reconciles Resource managed resources. +func Setup(mgr ctrl.Manager, o tjcontroller.Options) error { + name := managed.ControllerName(v1alpha1.Resource_GroupVersionKind.String()) + var initializers managed.InitializerChain + cps := []managed.ConnectionPublisher{managed.NewAPISecretPublisher(mgr.GetClient(), mgr.GetScheme())} + if o.SecretStoreConfigGVK != nil { + cps = append(cps, connection.NewDetailsManager(mgr.GetClient(), *o.SecretStoreConfigGVK, connection.WithTLSConfig(o.ESSOptions.TLSConfig))) + } + eventHandler := handler.NewEventHandler(handler.WithLogger(o.Logger.WithValues("gvk", v1alpha1.Resource_GroupVersionKind))) + ac := tjcontroller.NewAPICallbacks(mgr, xpresource.ManagedKind(v1alpha1.Resource_GroupVersionKind), tjcontroller.WithEventHandler(eventHandler)) + opts := []managed.ReconcilerOption{ + managed.WithExternalConnecter(tjcontroller.NewConnector(mgr.GetClient(), o.WorkspaceStore, o.SetupFn, o.Provider.Resources["null_resource"], tjcontroller.WithLogger(o.Logger), tjcontroller.WithConnectorEventHandler(eventHandler), + tjcontroller.WithCallbackProvider(ac), + )), + managed.WithLogger(o.Logger.WithValues("controller", name)), + managed.WithRecorder(event.NewAPIRecorder(mgr.GetEventRecorderFor(name))), + managed.WithFinalizer(terraform.NewWorkspaceFinalizer(o.WorkspaceStore, xpresource.NewAPIFinalizer(mgr.GetClient(), managed.FinalizerName))), + managed.WithTimeout(3 * time.Minute), + managed.WithInitializers(initializers), + managed.WithConnectionPublishers(cps...), + managed.WithPollInterval(o.PollInterval), + } + if o.PollJitter != 0 { + opts = append(opts, managed.WithPollJitterHook(o.PollJitter)) + } + if o.Features.Enabled(features.EnableBetaManagementPolicies) { + opts = append(opts, managed.WithManagementPolicies()) + } + r := managed.NewReconciler(mgr, xpresource.ManagedKind(v1alpha1.Resource_GroupVersionKind), opts...) + + return ctrl.NewControllerManagedBy(mgr). + Named(name). + WithOptions(o.ForControllerRuntime()). + WithEventFilter(xpresource.DesiredStateChanged()). + Watches(&v1alpha1.Resource{}, eventHandler). + Complete(ratelimiter.NewReconciler(name, r, o.GlobalRateLimiter)) +} diff --git a/internal/controller/providerconfig/config.go b/internal/controller/providerconfig/config.go new file mode 100644 index 0000000..73c10b4 --- /dev/null +++ b/internal/controller/providerconfig/config.go @@ -0,0 +1,35 @@ +/* +Copyright 2021 Upbound Inc. +*/ + +package providerconfig + +import ( + "github.com/crossplane/crossplane-runtime/pkg/event" + "github.com/crossplane/crossplane-runtime/pkg/reconciler/providerconfig" + "github.com/crossplane/crossplane-runtime/pkg/resource" + "github.com/crossplane/upjet/pkg/controller" + ctrl "sigs.k8s.io/controller-runtime" + + "github.com/upbound/upjet-provider-template/apis/v1beta1" +) + +// Setup adds a controller that reconciles ProviderConfigs by accounting for +// their current usage. +func Setup(mgr ctrl.Manager, o controller.Options) error { + name := providerconfig.ControllerName(v1beta1.ProviderConfigGroupKind) + + of := resource.ProviderConfigKinds{ + Config: v1beta1.ProviderConfigGroupVersionKind, + UsageList: v1beta1.ProviderConfigUsageListGroupVersionKind, + } + + return ctrl.NewControllerManagedBy(mgr). + Named(name). + WithOptions(o.ForControllerRuntime()). + For(&v1beta1.ProviderConfig{}). + Watches(&v1beta1.ProviderConfigUsage{}, &resource.EnqueueRequestForProviderConfig{}). + Complete(providerconfig.NewReconciler(mgr, of, + providerconfig.WithLogger(o.Logger.WithValues("controller", name)), + providerconfig.WithRecorder(event.NewAPIRecorder(mgr.GetEventRecorderFor(name))))) +} diff --git a/internal/controller/zz_setup.go b/internal/controller/zz_setup.go new file mode 100755 index 0000000..80f7342 --- /dev/null +++ b/internal/controller/zz_setup.go @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: 2023 The Crossplane Authors +// +// SPDX-License-Identifier: Apache-2.0 + +package controller + +import ( + ctrl "sigs.k8s.io/controller-runtime" + + "github.com/crossplane/upjet/pkg/controller" + + resource "github.com/upbound/upjet-provider-template/internal/controller/null/resource" + providerconfig "github.com/upbound/upjet-provider-template/internal/controller/providerconfig" +) + +// Setup creates all controllers with the supplied logger and adds them to +// the supplied manager. +func Setup(mgr ctrl.Manager, o controller.Options) error { + for _, setup := range []func(ctrl.Manager, controller.Options) error{ + resource.Setup, + providerconfig.Setup, + } { + if err := setup(mgr, o); err != nil { + return err + } + } + return nil +} diff --git a/internal/features/features.go b/internal/features/features.go new file mode 100644 index 0000000..96fd480 --- /dev/null +++ b/internal/features/features.go @@ -0,0 +1,22 @@ +/* + Copyright 2022 Upbound Inc +*/ + +package features + +import ( + xpfeature "github.com/crossplane/crossplane-runtime/pkg/feature" +) + +// Feature flags. +const ( + // EnableAlphaExternalSecretStores enables alpha support for + // External Secret Stores. See the below design for more details. + // https://github.com/crossplane/crossplane/blob/390ddd/design/design-doc-external-secret-stores.md + EnableAlphaExternalSecretStores xpfeature.Flag = "EnableAlphaExternalSecretStores" + + // EnableBetaManagementPolicies enables beta support for + // Management Policies. See the below design for more details. + // https://github.com/crossplane/crossplane/pull/3531 + EnableBetaManagementPolicies xpfeature.Flag = xpfeature.EnableBetaManagementPolicies +) diff --git a/package/crds/null.template.upbound.io_resources.yaml b/package/crds/null.template.upbound.io_resources.yaml new file mode 100644 index 0000000..04973c1 --- /dev/null +++ b/package/crds/null.template.upbound.io_resources.yaml @@ -0,0 +1,318 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.13.0 + name: resources.null.template.upbound.io +spec: + group: null.template.upbound.io + names: + categories: + - crossplane + - managed + - template + kind: Resource + listKind: ResourceList + plural: resources + singular: resource + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .status.conditions[?(@.type=='Ready')].status + name: READY + type: string + - jsonPath: .status.conditions[?(@.type=='Synced')].status + name: SYNCED + type: string + - jsonPath: .metadata.annotations.crossplane\.io/external-name + name: EXTERNAL-NAME + type: string + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + name: v1alpha1 + schema: + openAPIV3Schema: + description: Resource is the Schema for the Resources API. The null_resource + resource implements the standard resource lifecycle but takes no further + action. The triggers argument allows specifying an arbitrary set of values + that, when changed, will cause the resource to be replaced. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ResourceSpec defines the desired state of Resource + properties: + deletionPolicy: + default: Delete + description: 'DeletionPolicy specifies what will happen to the underlying + external when this managed resource is deleted - either "Delete" + or "Orphan" the external resource. This field is planned to be deprecated + in favor of the ManagementPolicies field in a future release. Currently, + both could be set independently and non-default values would be + honored if the feature flag is enabled. See the design doc for more + information: https://github.com/crossplane/crossplane/blob/499895a25d1a1a0ba1604944ef98ac7a1a71f197/design/design-doc-observe-only-resources.md?plain=1#L223' + enum: + - Orphan + - Delete + type: string + forProvider: + properties: + triggers: + additionalProperties: + type: string + description: A map of arbitrary strings that, when changed, will + force the null resource to be replaced, re-running any associated + provisioners. + type: object + type: object + initProvider: + description: THIS IS A BETA FIELD. It will be honored unless the Management + Policies feature flag is disabled. InitProvider holds the same fields + as ForProvider, with the exception of Identifier and other resource + reference fields. The fields that are in InitProvider are merged + into ForProvider when the resource is created. The same fields are + also added to the terraform ignore_changes hook, to avoid updating + them after creation. This is useful for fields that are required + on creation, but we do not desire to update them after creation, + for example because of an external controller is managing them, + like an autoscaler. + properties: + triggers: + additionalProperties: + type: string + description: A map of arbitrary strings that, when changed, will + force the null resource to be replaced, re-running any associated + provisioners. + type: object + type: object + managementPolicies: + default: + - '*' + description: 'THIS IS A BETA FIELD. It is on by default but can be + opted out through a Crossplane feature flag. ManagementPolicies + specify the array of actions Crossplane is allowed to take on the + managed and external resources. This field is planned to replace + the DeletionPolicy field in a future release. Currently, both could + be set independently and non-default values would be honored if + the feature flag is enabled. If both are custom, the DeletionPolicy + field will be ignored. See the design doc for more information: + https://github.com/crossplane/crossplane/blob/499895a25d1a1a0ba1604944ef98ac7a1a71f197/design/design-doc-observe-only-resources.md?plain=1#L223 + and this one: https://github.com/crossplane/crossplane/blob/444267e84783136daa93568b364a5f01228cacbe/design/one-pager-ignore-changes.md' + items: + description: A ManagementAction represents an action that the Crossplane + controllers can take on an external resource. + enum: + - Observe + - Create + - Update + - Delete + - LateInitialize + - '*' + type: string + type: array + providerConfigRef: + default: + name: default + description: ProviderConfigReference specifies how the provider that + will be used to create, observe, update, and delete this managed + resource should be configured. + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: Resolution specifies whether resolution of this + reference is required. The default is 'Required', which + means the reconcile will fail if the reference cannot be + resolved. 'Optional' means this reference will be a no-op + if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: Resolve specifies when this reference should + be resolved. The default is 'IfNotPresent', which will attempt + to resolve the reference only when the corresponding field + is not present. Use 'Always' to resolve the reference on + every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + publishConnectionDetailsTo: + description: PublishConnectionDetailsTo specifies the connection secret + config which contains a name, metadata and a reference to secret + store config to which any connection details for this managed resource + should be written. Connection details frequently include the endpoint, + username, and password required to connect to the managed resource. + properties: + configRef: + default: + name: default + description: SecretStoreConfigRef specifies which secret store + config should be used for this ConnectionSecret. + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: Resolution specifies whether resolution of + this reference is required. The default is 'Required', + which means the reconcile will fail if the reference + cannot be resolved. 'Optional' means this reference + will be a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: Resolve specifies when this reference should + be resolved. The default is 'IfNotPresent', which will + attempt to resolve the reference only when the corresponding + field is not present. Use 'Always' to resolve the reference + on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + metadata: + description: Metadata is the metadata for connection secret. + properties: + annotations: + additionalProperties: + type: string + description: Annotations are the annotations to be added to + connection secret. - For Kubernetes secrets, this will be + used as "metadata.annotations". - It is up to Secret Store + implementation for others store types. + type: object + labels: + additionalProperties: + type: string + description: Labels are the labels/tags to be added to connection + secret. - For Kubernetes secrets, this will be used as "metadata.labels". + - It is up to Secret Store implementation for others store + types. + type: object + type: + description: Type is the SecretType for the connection secret. + - Only valid for Kubernetes Secret Stores. + type: string + type: object + name: + description: Name is the name of the connection secret. + type: string + required: + - name + type: object + writeConnectionSecretToRef: + description: WriteConnectionSecretToReference specifies the namespace + and name of a Secret to which any connection details for this managed + resource should be written. Connection details frequently include + the endpoint, username, and password required to connect to the + managed resource. This field is planned to be replaced in a future + release in favor of PublishConnectionDetailsTo. Currently, both + could be set independently and connection details would be published + to both without affecting each other. + properties: + name: + description: Name of the secret. + type: string + namespace: + description: Namespace of the secret. + type: string + required: + - name + - namespace + type: object + required: + - forProvider + type: object + status: + description: ResourceStatus defines the observed state of Resource. + properties: + atProvider: + properties: + id: + type: string + triggers: + additionalProperties: + type: string + description: A map of arbitrary strings that, when changed, will + force the null resource to be replaced, re-running any associated + provisioners. + type: object + type: object + conditions: + description: Conditions of the resource. + items: + description: A Condition that may apply to a resource. + properties: + lastTransitionTime: + description: LastTransitionTime is the last time this condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A Message containing details about this condition's + last transition from one status to another, if any. + type: string + reason: + description: A Reason for this condition's last transition from + one status to another. + type: string + status: + description: Status of this condition; is it currently True, + False, or Unknown? + type: string + type: + description: Type of this condition. At most one of each condition + type may apply to a resource at any point in time. + type: string + required: + - lastTransitionTime + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/package/crds/template.upbound.io_providerconfigs.yaml b/package/crds/template.upbound.io_providerconfigs.yaml new file mode 100644 index 0000000..595fa90 --- /dev/null +++ b/package/crds/template.upbound.io_providerconfigs.yaml @@ -0,0 +1,155 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.13.0 + name: providerconfigs.template.upbound.io +spec: + group: template.upbound.io + names: + categories: + - crossplane + - provider + - template + kind: ProviderConfig + listKind: ProviderConfigList + plural: providerconfigs + singular: providerconfig + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .spec.credentials.secretRef.name + name: SECRET-NAME + priority: 1 + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: A ProviderConfig configures a Template provider. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: A ProviderConfigSpec defines the desired state of a ProviderConfig. + properties: + credentials: + description: Credentials required to authenticate to this provider. + properties: + env: + description: Env is a reference to an environment variable that + contains credentials that must be used to connect to the provider. + properties: + name: + description: Name is the name of an environment variable. + type: string + required: + - name + type: object + fs: + description: Fs is a reference to a filesystem location that contains + credentials that must be used to connect to the provider. + properties: + path: + description: Path is a filesystem path. + type: string + required: + - path + type: object + secretRef: + description: A SecretRef is a reference to a secret key that contains + the credentials that must be used to connect to the provider. + properties: + key: + description: The key to select. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: Namespace of the secret. + type: string + required: + - key + - name + - namespace + type: object + source: + description: Source of the provider credentials. + enum: + - None + - Secret + - InjectedIdentity + - Environment + - Filesystem + type: string + required: + - source + type: object + required: + - credentials + type: object + status: + description: A ProviderConfigStatus reflects the observed state of a ProviderConfig. + properties: + conditions: + description: Conditions of the resource. + items: + description: A Condition that may apply to a resource. + properties: + lastTransitionTime: + description: LastTransitionTime is the last time this condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A Message containing details about this condition's + last transition from one status to another, if any. + type: string + reason: + description: A Reason for this condition's last transition from + one status to another. + type: string + status: + description: Status of this condition; is it currently True, + False, or Unknown? + type: string + type: + description: Type of this condition. At most one of each condition + type may apply to a resource at any point in time. + type: string + required: + - lastTransitionTime + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + users: + description: Users of this provider configuration. + format: int64 + type: integer + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/package/crds/template.upbound.io_providerconfigusages.yaml b/package/crds/template.upbound.io_providerconfigusages.yaml new file mode 100644 index 0000000..5a2bc1d --- /dev/null +++ b/package/crds/template.upbound.io_providerconfigusages.yaml @@ -0,0 +1,110 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.13.0 + name: providerconfigusages.template.upbound.io +spec: + group: template.upbound.io + names: + categories: + - crossplane + - provider + - template + kind: ProviderConfigUsage + listKind: ProviderConfigUsageList + plural: providerconfigusages + singular: providerconfigusage + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .providerConfigRef.name + name: CONFIG-NAME + type: string + - jsonPath: .resourceRef.kind + name: RESOURCE-KIND + type: string + - jsonPath: .resourceRef.name + name: RESOURCE-NAME + type: string + name: v1beta1 + schema: + openAPIV3Schema: + description: A ProviderConfigUsage indicates that a resource is using a ProviderConfig. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + providerConfigRef: + description: ProviderConfigReference to the provider config being used. + properties: + name: + description: Name of the referenced object. + type: string + policy: + description: Policies for referencing. + properties: + resolution: + default: Required + description: Resolution specifies whether resolution of this reference + is required. The default is 'Required', which means the reconcile + will fail if the reference cannot be resolved. 'Optional' means + this reference will be a no-op if it cannot be resolved. + enum: + - Required + - Optional + type: string + resolve: + description: Resolve specifies when this reference should be resolved. + The default is 'IfNotPresent', which will attempt to resolve + the reference only when the corresponding field is not present. + Use 'Always' to resolve the reference on every reconcile. + enum: + - Always + - IfNotPresent + type: string + type: object + required: + - name + type: object + resourceRef: + description: ResourceReference to the managed resource using the provider + config. + properties: + apiVersion: + description: APIVersion of the referenced object. + type: string + kind: + description: Kind of the referenced object. + type: string + name: + description: Name of the referenced object. + type: string + uid: + description: UID of the referenced object. + type: string + required: + - apiVersion + - kind + - name + type: object + required: + - providerConfigRef + - resourceRef + type: object + served: true + storage: true + subresources: {} diff --git a/package/crds/template.upbound.io_storeconfigs.yaml b/package/crds/template.upbound.io_storeconfigs.yaml new file mode 100644 index 0000000..4300dad --- /dev/null +++ b/package/crds/template.upbound.io_storeconfigs.yaml @@ -0,0 +1,205 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.13.0 + name: storeconfigs.template.upbound.io +spec: + group: template.upbound.io + names: + categories: + - crossplane + - store + - template + kind: StoreConfig + listKind: StoreConfigList + plural: storeconfigs + singular: storeconfig + scope: Cluster + versions: + - additionalPrinterColumns: + - jsonPath: .metadata.creationTimestamp + name: AGE + type: date + - jsonPath: .spec.type + name: TYPE + type: string + - jsonPath: .spec.defaultScope + name: DEFAULT-SCOPE + type: string + name: v1alpha1 + schema: + openAPIV3Schema: + description: A StoreConfig configures how template controller should store + connection details. + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: A StoreConfigSpec defines the desired state of a ProviderConfig. + properties: + defaultScope: + description: DefaultScope used for scoping secrets for "cluster-scoped" + resources. If store type is "Kubernetes", this would mean the default + namespace to store connection secrets for cluster scoped resources. + In case of "Vault", this would be used as the default parent path. + Typically, should be set as Crossplane installation namespace. + type: string + kubernetes: + description: Kubernetes configures a Kubernetes secret store. If the + "type" is "Kubernetes" but no config provided, in cluster config + will be used. + properties: + auth: + description: Credentials used to connect to the Kubernetes API. + properties: + env: + description: Env is a reference to an environment variable + that contains credentials that must be used to connect to + the provider. + properties: + name: + description: Name is the name of an environment variable. + type: string + required: + - name + type: object + fs: + description: Fs is a reference to a filesystem location that + contains credentials that must be used to connect to the + provider. + properties: + path: + description: Path is a filesystem path. + type: string + required: + - path + type: object + secretRef: + description: A SecretRef is a reference to a secret key that + contains the credentials that must be used to connect to + the provider. + properties: + key: + description: The key to select. + type: string + name: + description: Name of the secret. + type: string + namespace: + description: Namespace of the secret. + type: string + required: + - key + - name + - namespace + type: object + source: + description: Source of the credentials. + enum: + - None + - Secret + - Environment + - Filesystem + type: string + required: + - source + type: object + required: + - auth + type: object + plugin: + description: Plugin configures External secret store as a plugin. + properties: + configRef: + description: ConfigRef contains store config reference info. + properties: + apiVersion: + description: APIVersion of the referenced config. + type: string + kind: + description: Kind of the referenced config. + type: string + name: + description: Name of the referenced config. + type: string + required: + - apiVersion + - kind + - name + type: object + endpoint: + description: Endpoint is the endpoint of the gRPC server. + type: string + type: object + type: + default: Kubernetes + description: Type configures which secret store to be used. Only the + configuration block for this store will be used and others will + be ignored if provided. Default is Kubernetes. + enum: + - Kubernetes + - Vault + - Plugin + type: string + required: + - defaultScope + type: object + status: + description: A StoreConfigStatus represents the status of a StoreConfig. + properties: + conditions: + description: Conditions of the resource. + items: + description: A Condition that may apply to a resource. + properties: + lastTransitionTime: + description: LastTransitionTime is the last time this condition + transitioned from one status to another. + format: date-time + type: string + message: + description: A Message containing details about this condition's + last transition from one status to another, if any. + type: string + reason: + description: A Reason for this condition's last transition from + one status to another. + type: string + status: + description: Status of this condition; is it currently True, + False, or Unknown? + type: string + type: + description: Type of this condition. At most one of each condition + type may apply to a resource at any point in time. + type: string + required: + - lastTransitionTime + - reason + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/package/crossplane.yaml b/package/crossplane.yaml new file mode 100644 index 0000000..8e70739 --- /dev/null +++ b/package/crossplane.yaml @@ -0,0 +1,4 @@ +apiVersion: meta.pkg.crossplane.io/v1 +kind: Provider +metadata: + name: upjet-provider-template diff --git a/scripts/version_diff.py b/scripts/version_diff.py new file mode 100644 index 0000000..b373830 --- /dev/null +++ b/scripts/version_diff.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 + +import json +import sys + +# usage: version_diff.py +# example usage: version_diff.py config/generated.lst .work/schema.json.3.38.0 config/schema.json +if __name__ == "__main__": + base_path = sys.argv[2] + bumped_path = sys.argv[3] + print(f'Reporting schema changes between "{base_path}" as base version and "{bumped_path}" as bumped version') + with open(sys.argv[1]) as f: + resources = json.load(f) + with open(base_path) as f: + base = json.load(f) + with open(bumped_path) as f: + bump = json.load(f) + + provider_name = None + for k in base["provider_schemas"]: + # the first key is the provider name + provider_name = k + break + if provider_name is None: + print(f"Cannot extract the provider name from the base schema: {base_path}") + sys.exit(-1) + base_schemas = base["provider_schemas"][provider_name]["resource_schemas"] + bumped_schemas = bump["provider_schemas"][provider_name]["resource_schemas"] + + for name in resources: + try: + if base_schemas[name]["version"] != bumped_schemas[name]["version"]: + print(f'{name}:{base_schemas[name]["version"]}-{bumped_schemas[name]["version"]}') + except KeyError as ke: + print(f'{name} is not found in schema: {ke}') + continue