diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..a7b73b8b --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,40 @@ +--- +name: Bug Report +about: Help us diagnose and fix bugs in Crossplane +labels: bug +--- + + +### What happened? + + + +### How can we reproduce it? + + +### What environment did it happen in? +Crossplane version: + + diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..c385d797 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,24 @@ +--- +name: Feature Request +about: Help us make Crossplane more useful +labels: enhancement +--- + + +### What problem are you facing? + + +### How could Crossplane help solve your problem? + diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 00000000..46211600 --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,36 @@ + + +### Description of your changes + + +Fixes # + +I have: + +- [ ] Read and followed Crossplane's [contribution process]. +- [ ] Run `make reviewable` to ensure this PR is ready for review. +- [ ] Added `backport release-x.y` labels to auto-backport this PR if necessary. + +### 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 00000000..1ee4208e --- /dev/null +++ b/.github/workflows/backport.yml @@ -0,0 +1,28 @@ +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: + # NOTE(negz): I tested many backport GitHub actions before landing on this + # one. Many do not support merge commits, or do not support pull requests with + # more than one commit. This one does. It also handily links backport PRs with + # new PRs, and provides commentary and instructions when it can't backport. + # The main gotcha with this action is that PRs _must_ be labelled before they're + # merged to trigger a backport. + open-pr: + runs-on: ubuntu-22.04 + if: github.event.pull_request.merged + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Open Backport PR + uses: korthout/backport-action@v1 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..0d3cd0ae --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,250 @@ +name: CI + +on: + push: + branches: + - main + - release-* + pull_request: {} + workflow_dispatch: {} + +env: + # Common versions + GO_VERSION: '1.21' + GOLANGCI_VERSION: 'v1.54.0' + DOCKER_BUILDX_VERSION: 'v0.9.1' + + # 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.2.0 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + paths_ignore: '["**.md", "**.png", "**.jpg"]' + do_not_skip: '["workflow_dispatch", "schedule", "push"]' + + + lint: + runs-on: ubuntu-22.04 + needs: detect-noop + if: needs.detect-noop.outputs.noop != 'true' + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + submodules: true + + - name: Setup Go + uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + + - name: Find the Go Build Cache + id: go + run: echo "cachedir=$(make go.cachedir)" >> $GITHUB_ENV + + - name: Cache the Go Build Cache + uses: actions/cache@v3 + with: + path: ${{ env.cachedir }} + 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: Download Go Modules + run: make modules.download modules.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@v3 + with: + submodules: true + + - name: Setup Go + uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + + - name: Find the Go Build Cache + id: go + run: echo "cachedir=$(make go.cachedir)" >> $GITHUB_ENV + + - name: Cache the Go Build Cache + uses: actions/cache@v3 + with: + path: ${{ env.cachedir }} + 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: Download Go Modules + run: make modules.download modules.check + + - name: Check Diff + id: check-diff + run: | + mkdir _output + make check-diff + + - name: Show diff + if: failure() && steps.check-diff.outcome == 'failure' + run: git diff + + unit-tests: + runs-on: ubuntu-22.04 + needs: detect-noop + if: needs.detect-noop.outputs.noop != 'true' + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + submodules: true + + - name: Fetch History + run: git fetch --prune --unshallow + + - name: Setup Go + uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + + - name: Find the Go Build Cache + id: go + run: echo "cachedir=$(make go.cachedir)" >> $GITHUB_ENV + + - name: Cache the Go Build Cache + uses: actions/cache@v3 + with: + path: ${{ env.cachedir }} + 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: Download Go Modules + run: make modules.download modules.check + + - name: Run Unit Tests + run: make -j2 test + + - name: Publish Unit Test Coverage + uses: codecov/codecov-action@v3 + with: + flags: unittests + file: _output/tests/linux_amd64/coverage.txt + + 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@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@v1 + 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@v3 + with: + submodules: true + + - name: Fetch History + run: git fetch --prune --unshallow + + - name: Setup Go + uses: actions/setup-go@v3 + with: + go-version: ${{ env.GO_VERSION }} + + - name: Find the Go Build Cache + id: go + run: echo "cachedir=$(make go.cachedir)" >> $GITHUB_ENV + + - name: Cache the Go Build Cache + uses: actions/cache@v3 + with: + path: ${{ env.cachedir }} + 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: Download Go Modules + run: make modules.download modules.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: Publish Artifacts to GitHub + uses: actions/upload-artifact@v3 + with: + name: output + path: _output/** + + - name: Publish Artifacts + if: env.UPBOUND_MARKETPLACE_PUSH_ROBOT_USR != '' + run: make publish BRANCH_NAME=${GITHUB_REF##*/} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml new file mode 100644 index 00000000..fee244fe --- /dev/null +++ b/.github/workflows/codeql.yml @@ -0,0 +1,42 @@ +name: CodeQL + +on: + push: + branches: + - main + - release-* + workflow_dispatch: {} + +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.2.0 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + paths_ignore: '["**.md", "**.png", "**.jpg"]' + do_not_skip: '["workflow_dispatch", "schedule", "push"]' + concurrent_skipping: false + + analyze: + runs-on: ubuntu-22.04 + needs: detect-noop + if: needs.detect-noop.outputs.noop != 'true' + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + submodules: true + + - name: Initialize CodeQL + uses: github/codeql-action/init@v2 + with: + languages: go + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@v2 diff --git a/.github/workflows/commands.yml b/.github/workflows/commands.yml new file mode 100644 index 00000000..df101a8e --- /dev/null +++ b/.github/workflows/commands.yml @@ -0,0 +1,86 @@ +name: Comment Commands + +on: issue_comment + +jobs: + points: + runs-on: ubuntu-22.04 + if: startsWith(github.event.comment.body, '/points') + + steps: + - name: Extract Command + id: command + uses: xt0rted/slash-command-action@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + command: points + reaction: "true" + reaction-type: "eyes" + allow-edits: "false" + permission-level: write + - name: Handle Command + uses: actions/github-script@v4 + env: + POINTS: ${{ steps.command.outputs.command-arguments }} + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + script: | + const points = process.env.POINTS + + if (isNaN(parseInt(points))) { + console.log("Malformed command - expected '/points '") + github.reactions.createForIssueComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: context.payload.comment.id, + content: "confused" + }) + return + } + const label = "points/" + points + + // Delete our needs-points-label label. + try { + await github.issues.deleteLabel({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + name: ['needs-points-label'] + }) + console.log("Deleted 'needs-points-label' label.") + } + catch(e) { + console.log("Label 'needs-points-label' probably didn't exist.") + } + + // Add our points label. + github.issues.addLabels({ + issue_number: context.issue.number, + owner: context.repo.owner, + repo: context.repo.repo, + labels: [label] + }) + console.log("Added '" + label + "' label.") + + # NOTE(negz): See also backport.yml, which is the variant that triggers on PR + # merge rather than on comment. + backport: + runs-on: ubuntu-22.04 + if: github.event.issue.pull_request && startsWith(github.event.comment.body, '/backport') + steps: + - name: Extract Command + id: command + uses: xt0rted/slash-command-action@v1 + with: + repo-token: ${{ secrets.GITHUB_TOKEN }} + command: backport + reaction: "true" + reaction-type: "eyes" + allow-edits: "false" + permission-level: write + + - name: Checkout + uses: actions/checkout@v3 + + - name: Open Backport PR + uses: korthout/backport-action@v1 diff --git a/.github/workflows/promote.yml b/.github/workflows/promote.yml new file mode 100644 index 00000000..a63c4aea --- /dev/null +++ b/.github/workflows/promote.yml @@ -0,0 +1,48 @@ +name: Promote + +on: + workflow_dispatch: + inputs: + version: + description: 'Release version (e.g. v0.1.0)' + required: true + channel: + description: 'Release channel' + required: true + default: 'stable' + +env: + # Common users. We can't run a step 'if secrets.AWS_USR != ""' but we can run + # a step 'if env.AWS_USR' != ""', so we copy these to succinctly test whether + # credentials have been provided before trying to run steps that need them. + DOCKER_USR: ${{ secrets.DOCKER_USR }} + AWS_USR: ${{ secrets.AWS_USR }} + +jobs: + promote-artifacts: + runs-on: ubuntu-22.04 + + steps: + - name: Checkout + uses: actions/checkout@v3 + with: + submodules: true + + - name: Fetch History + run: git fetch --prune --unshallow + + - name: Login to Docker + uses: docker/login-action@v1 + if: env.DOCKER_USR != '' + with: + username: ${{ secrets.DOCKER_USR }} + password: ${{ secrets.DOCKER_PSW }} + + - name: Promote Artifacts in S3 and Docker Hub + if: env.AWS_USR != '' && env.DOCKER_USR != '' + run: make -j2 promote BRANCH_NAME=${GITHUB_REF##*/} + env: + VERSION: ${{ github.event.inputs.version }} + CHANNEL: ${{ github.event.inputs.channel }} + AWS_ACCESS_KEY_ID: ${{ secrets.AWS_USR }} + AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_PSW }} diff --git a/.github/workflows/tag.yml b/.github/workflows/tag.yml new file mode 100644 index 00000000..db32dd0f --- /dev/null +++ b/.github/workflows/tag.yml @@ -0,0 +1,26 @@ +name: Tag + +on: + workflow_dispatch: + inputs: + version: + description: 'Release version (e.g. v0.1.0)' + required: true + message: + description: 'Tag message' + required: true + +jobs: + create-tag: + runs-on: ubuntu-22.04 + + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Create Tag + uses: negz/create-tag@v1 + with: + version: ${{ github.event.inputs.version }} + message: ${{ github.event.inputs.message }} + token: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..3f43da78 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +/.cache +/.work +/_output +cover.out +/vendor +/.vendor-new +.vscode +.idea +.DS_Store diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..8f84209c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "build"] + path = build + url = https://github.com/crossplane/build diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 00000000..30e843bf --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,193 @@ +run: + deadline: 2m + + skip-files: + - "zz_generated\\..+\\.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/crossplane/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 + - goconst + - goimports + - gofmt # We enable this as well as goimports for its simplify mode. + - prealloc + - 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 + - exportloopref + - 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/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 00000000..a3ee6a8a --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,3 @@ +## Community Code of Conduct + +This project follows the [CNCF Code of Conduct](https://github.com/cncf/foundation/blob/master/code-of-conduct.md). diff --git a/DCO b/DCO new file mode 100644 index 00000000..716561d5 --- /dev/null +++ b/DCO @@ -0,0 +1,36 @@ +Developer Certificate of Origin +Version 1.1 + +Copyright (C) 2004, 2006 The Linux Foundation and its contributors. +660 York Street, Suite 102, +San Francisco, CA 94110 USA + +Everyone is permitted to copy and distribute verbatim copies of this +license document, but changing it is not allowed. + + +Developer's Certificate of Origin 1.1 + +By making a contribution to this project, I certify that: + +(a) The contribution was created in whole or in part by me and I + have the right to submit it under the open source license + indicated in the file; or + +(b) The contribution is based upon previous work that, to the best + of my knowledge, is covered under an appropriate open source + license and I have the right under that license to submit that + work with modifications, whether created in whole or in part + by me, under the same open source license (unless I am + permitted to submit under a different license), as indicated + in the file; or + +(c) The contribution was provided directly to me by some other + person who certified (a), (b) or (c) and I have not modified + it. + +(d) I understand and agree that this project and the contribution + are public and that a record of the contribution (including all + personal information I submit with it, including my sign-off) is + maintained indefinitely and may be redistributed consistent with + this project or the open source license(s) involved. diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..ae6b59dc --- /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 2016 The Crossplane Authors. 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. \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..4de642ea --- /dev/null +++ b/Makefile @@ -0,0 +1,162 @@ +# ==================================================================================== +# Setup Project +PROJECT_NAME := provider-template +PROJECT_REPO := github.com/crossplane/$(PROJECT_NAME) + +PLATFORMS ?= linux_amd64 linux_arm64 +-include build/makelib/common.mk + +# ==================================================================================== +# Setup Output + +-include build/makelib/output.mk + +# ==================================================================================== +# Setup Go + +NPROCS ?= 1 +GO_TEST_PARALLEL := $(shell echo $$(( $(NPROCS) / 2 ))) +GO_STATIC_PACKAGES = $(GO_PROJECT)/cmd/provider +GO_LDFLAGS += -X $(GO_PROJECT)/internal/version.Version=$(VERSION) +GO_SUBDIRS += cmd internal apis +GO111MODULE = on +-include build/makelib/golang.mk + +# ==================================================================================== +# Setup Kubernetes tools + +-include build/makelib/k8s_tools.mk + +# ==================================================================================== +# Setup Images + +IMAGES = provider-template +-include build/makelib/imagelight.mk + +# ==================================================================================== +# Setup XPKG + +XPKG_REG_ORGS ?= xpkg.upbound.io/crossplane +# NOTE(hasheddan): skip promoting on xpkg.upbound.io as channel tags are +# inferred. +XPKG_REG_ORGS_NO_PROMOTE ?= xpkg.upbound.io/crossplane +XPKGS = provider-template +-include build/makelib/xpkg.mk + +# NOTE(hasheddan): we force image building to happen prior to xpkg build so that +# we ensure image is present in daemon. +xpkg.build.provider-template: do.build.images + +fallthrough: submodules + @echo Initial setup complete. Running make again . . . + @make + +# integration tests +e2e.run: test-integration + +# Run integration tests. +test-integration: $(KIND) $(KUBECTL) $(UP) $(HELM3) + @$(INFO) running integration tests using kind $(KIND_VERSION) + @KIND_NODE_IMAGE_TAG=${KIND_NODE_IMAGE_TAG} $(ROOT_DIR)/cluster/local/integration_tests.sh || $(FAIL) + @$(OK) integration tests passed + +# Update the submodules, such as the common build scripts. +submodules: + @git submodule sync + @git submodule update --init --recursive + +# NOTE(hasheddan): 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 + +# NOTE(hasheddan): we must ensure up is installed in tool cache prior to build +# as including the k8s_tools machinery prior to the xpkg machinery sets UP to +# point to tool cache. +build.init: $(UP) + +# 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 + $(GO_OUT_DIR)/provider --debug + +dev: $(KIND) $(KUBECTL) + @$(INFO) Creating kind cluster + @$(KIND) create cluster --name=$(PROJECT_NAME)-dev + @$(KUBECTL) cluster-info --context kind-$(PROJECT_NAME)-dev + @$(INFO) Installing Crossplane CRDs + @$(KUBECTL) apply --server-side -k https://github.com/crossplane/crossplane//cluster?ref=master + @$(INFO) Installing Provider Template CRDs + @$(KUBECTL) apply -R -f package/crds + @$(INFO) Starting Provider Template controllers + @$(GO) run cmd/provider/main.go --debug + +dev-clean: $(KIND) $(KUBECTL) + @$(INFO) Deleting kind cluster + @$(KIND) delete cluster --name=$(PROJECT_NAME)-dev + +.PHONY: submodules fallthrough test-integration run dev dev-clean + +# ==================================================================================== +# Special Targets + +# Install gomplate +GOMPLATE_VERSION := 3.10.0 +GOMPLATE := $(TOOLS_HOST_DIR)/gomplate-$(GOMPLATE_VERSION) + +$(GOMPLATE): + @$(INFO) installing gomplate $(SAFEHOSTPLATFORM) + @mkdir -p $(TOOLS_HOST_DIR) + @curl -fsSLo $(GOMPLATE) https://github.com/hairyhenderson/gomplate/releases/download/v$(GOMPLATE_VERSION)/gomplate_$(SAFEHOSTPLATFORM) || $(FAIL) + @chmod +x $(GOMPLATE) + @$(OK) installing gomplate $(SAFEHOSTPLATFORM) + +export GOMPLATE + +# This target prepares repo for your provider by replacing all "template" +# occurrences with your provider name. +# This target can only be run once, if you want to rerun for some reason, +# consider stashing/resetting your git state. +# Arguments: +# provider: Camel case name of your provider, e.g. GitHub, PlanetScale +provider.prepare: + @[ "${provider}" ] || ( echo "argument \"provider\" is not set"; exit 1 ) + @PROVIDER=$(provider) ./hack/helpers/prepare.sh + +# This target adds a new api type and its controller. +# You would still need to register new api in "apis/.go" and +# controller in "internal/controller/.go". +# Arguments: +# provider: Camel case name of your provider, e.g. GitHub, PlanetScale +# group: API group for the type you want to add. +# kind: Kind of the type you want to add +# apiversion: API version of the type you want to add. Optional and defaults to "v1alpha1" +provider.addtype: $(GOMPLATE) + @[ "${provider}" ] || ( echo "argument \"provider\" is not set"; exit 1 ) + @[ "${group}" ] || ( echo "argument \"group\" is not set"; exit 1 ) + @[ "${kind}" ] || ( echo "argument \"kind\" is not set"; exit 1 ) + @PROVIDER=$(provider) GROUP=$(group) KIND=$(kind) APIVERSION=$(apiversion) PROJECT_REPO=$(PROJECT_REPO) ./hack/helpers/addtype.sh + +define CROSSPLANE_MAKE_HELP +Crossplane Targets: + 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 00000000..1a803d42 --- /dev/null +++ b/OWNERS.md @@ -0,0 +1,15 @@ +# OWNERS + +This page lists all maintainers for **this** repository. Each repository in the [Crossplane +organization](https://github.com/crossplane/) will list their repository maintainers in their own +`OWNERS.md` file. + +Please see the Crossplane +[GOVERNANCE.md](https://github.com/crossplane/crossplane/blob/master/GOVERNANCE.md) for governance +guidelines and responsibilities for the steering committee and maintainers. + +## Maintainers + +* Nic Cope ([negz](https://github.com/negz)) +* Daniel Mangum ([hasheddan](https://github.com/hasheddan)) +* Muvaffak Onuş ([muvaf](https://github.com/muvaf)) diff --git a/PROVIDER_CHECKLIST.md b/PROVIDER_CHECKLIST.md new file mode 100644 index 00000000..116848d2 --- /dev/null +++ b/PROVIDER_CHECKLIST.md @@ -0,0 +1,118 @@ +# Provider Checklist + +Crossplane manages Resources external via +[Providers](https://crossplane.io/docs/master/concepts/providers.html). +Providers are composed of Kubernetes [Custom Resource +Definitions](https://kubernetes.io/docs/concepts/extend-kubernetes/api-extension/custom-resources/#customresourcedefinitions) +and [Controllers](https://kubernetes.io/docs/concepts/architecture/controller) +that communicate to a remote API and manages the lifecycle of a resource from +Creation to Deletion. + +To build a new provider, please refer to the [Provider Development +Guide](https://crossplane.io/docs/master/contributing/provider_development_guide.html). + +This document contains list of items that are usually involved in managing Open +Source contributions to a Crossplane provider. In general your provider should +follow the guidelines documented in the [Crossplane +Contributing](https://github.com/crossplane/crossplane/blob/master/CONTRIBUTING.md) +guide. + +## Repository + +Although providers can be hosted in any source code repository, the [crossplane-contrib](https://github.com/orgs/crossplane-contrib) Github Organization is available as a neutral home that is under Crossplane's project [governance](https://github.com/crossplane/crossplane/blob/master/GOVERNANCE.md). + +Members of the Crossplane community are happy to create a repository in the *crossplane-contrib* organization and configure access, continuous integration, and storage +for software artifacts. Please open an issue in the Crossplane +[org](https://github.com/crossplane/org) repository or reach out to the Crossplane +[#dev](https://crossplane.slack.com/archives/CEF5N8X08) channel. + +Generally projects are named `provider-`, with `name` being the API being +managed. Example project names are `provider-aws`, `provider-kubernetes`, +and `provider-github`. + +The [provider-template](https://github.com/crossplane/provider-template) repository can be +used as a starting point for new providers. For [terrajet](https://github.com/crossplane/terrajet)-based providers, the +[provider-jet-template](https://github.com/crossplane-contrib/provider-jet-template) is +available. + +## Files + +Most Crossplane providers include the following files: + +- [ ] A descriptive README.md at the root of the project (see + [provider-gcp/README.md](https://github.com/crossplane/provider-gcp/blob/master/README.md) + as an example) +- [ ] Code is licensed under the [Apache 2.0 + License](https://github.com/crossplane/provider-template/blob/main/LICENSE) +- [ ] Include a “Developer Certificate of Origin”. Example: + [DCO](https://github.com/upbound/build/blob/master/DCO) +- [ ] Include the CNCF [Code of + Conduct](https://github.com/crossplane/crossplane/blob/master/CODE_OF_CONDUCT.md) +- [ ] Update + [OWNERS.md](https://github.com/crossplane/provider-template/blob/main/OWNERS.md) + with contacts for project Owners +- [ ] Ensure `hack/boilerplate.go.txt` (used in Code generation) includes + Crossplane Authors, Apache license and any other Copyright statements: + [https://github.com/crossplane/provider-template/blob/main/hack/boilerplate.go.txt](https://github.com/crossplane/provider-template/blob/main/hack/boilerplate.go.txt) +- [ ] Include Documentation on how to: + - [ ] Install Provider + - [ ] Contribute to Development + - [ ] Authenticate to backend API, including creating Kubernetes secrets for + the ProviderConfig +- [ ] Include examples for the ProviderConfig and each resource in the + `examples/` directory. + +## Build Process + +There are a number of build tools and processes that are common across the +Crossplane ecosystem. Using these ensures a consistent development environment +across projects. + +The [provider-template](https://github.com/crossplane/provider-template) +repository contains most of these settings. + +- [ ] Use the [Upbound build](https://github.com/upbound/build) submodule. (see + [https://github.com/crossplane/crossplane/blob/master/CONTRIBUTING.md#establishing-a-development-environment](https://github.com/crossplane/crossplane/blob/master/CONTRIBUTING.md#establishing-a-development-environment)) +- [ ] Include a + [Makefile](https://github.com/crossplane/provider-gcp/blob/master/Makefile) + that supports common build targets. +- [ ] Use a Golang linter. Example: + [https://github.com/crossplane/provider-aws/blob/master/.golangci.yml](https://github.com/crossplane/provider-aws/blob/master/.golangci.yml) +- [ ] Create a [Crossplane + Package](https://crossplane.io/docs/master/concepts/packages.html) + configuration (see + [package/crossplane.yaml)](https://github.com/crossplane/provider-template/blob/main/package/crossplane.yaml) + +## Deployment of Artifacts + +When deploying provider artifacts, projects should generally follow the Crossplane +[release process](https://crossplane.io/docs/master/contributing/release-process.html). + +Most Crossplane projects use [Github Actions](https://docs.github.com/en/actions/learn-github-actions/understanding-github-actions) to build, tag, and promote software releases. + +Providers are packaged as OCI (Docker) images and pushed to an OCI registry as part of +the publish and promotion workflows. + +In general, providers should: + +- [ ] Utilize GitHub workflows from + +- [ ] Create OCI image repos to push Package and Controller images. +- [ ] Automatically push Provider images and packages via CI +- [ ] Add GitHub Secrets to push to Docker repository. (To be performed by + Crossplane-contrib administrators). + +If you're part of the crossplane-contrib org and want to enable Github CI, push +OCI images or packages to the crossplane org in Docker Hub please ask a +[steering committee +member](https://github.com/crossplane/crossplane/blob/master/OWNERS.md#steering-committee) +to grant your project access to the GitHub org scoped secrets. + +## Governance + +- [ ] Follow recommendations at + [https://github.com/crossplane/crossplane/blob/master/GOVERNANCE.md#repository-governance](https://github.com/crossplane/crossplane/blob/master/GOVERNANCE.md#repository-governance) +- [ ] Enable Issues on your project and configure Issue templates (examples at: + [.github/ISSUE_TEMPLATE](https://github.com/crossplane/provider-template/tree/master/.github/ISSUE_TEMPLATE)) +- [ ] Create Pull Request Templates: (example: + [PULL_REQUEST_TEMPLATE.md](https://github.com/crossplane/provider-template/blob/master/.github/PULL_REQUEST_TEMPLATE.md)) diff --git a/README.md b/README.md new file mode 100644 index 00000000..f06a7afa --- /dev/null +++ b/README.md @@ -0,0 +1,38 @@ +# provider-template + +`provider-template` is a minimal [Crossplane](https://crossplane.io/) Provider +that is meant to be used as a template for implementing new Providers. It comes +with the following features that are meant to be refactored: + +- A `ProviderConfig` type that only points to a credentials `Secret`. +- A `MyType` resource type that serves as an example managed resource. +- A managed resource controller that reconciles `MyType` objects and simply + prints their configuration in its `Observe` method. + +## Developing + +1. Use this repository as a template to create a new one. +1. Run `make submodules` to initialize the "build" Make submodule we use for CI/CD. +1. Rename the provider by running the following command: +```shell + export provider_name=MyProvider # Camel case, e.g. GitHub + make provider.prepare provider=${provider_name} +``` +4. Add your new type by running the following command: +```shell + export group=sample # lower case e.g. core, cache, database, storage, etc. + export type=MyType # Camel casee.g. Bucket, Database, CacheCluster, etc. + make provider.addtype provider=${provider_name} group=${group} kind=${type} +``` +5. Replace the *sample* group with your new group in apis/{provider}.go +5. Replace the *mytype* type with your new type in internal/controller/{provider}.go +5. Replace the default controller and ProviderConfig implementations with your own +5. Run `make reviewable` to run code generation, linters, and tests. +5. Run `make build` to build the provider. + +Refer to Crossplane's [CONTRIBUTING.md] file for more information on how the +Crossplane community prefers to work. The [Provider Development][provider-dev] +guide may also be of use. + +[CONTRIBUTING.md]: https://github.com/crossplane/crossplane/blob/master/CONTRIBUTING.md +[provider-dev]: https://github.com/crossplane/crossplane/blob/master/contributing/guide-provider-development.md diff --git a/apis/generate.go b/apis/generate.go new file mode 100644 index 00000000..fe371aa5 --- /dev/null +++ b/apis/generate.go @@ -0,0 +1,37 @@ +// +build generate + +/* +Copyright 2020 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// NOTE: See the below link for details on what is happening here. +// https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module + +// Remove existing CRDs +//go:generate rm -rf ../package/crds + +// Generate deepcopy methodsets and CRD manifests +//go:generate go run -tags generate sigs.k8s.io/controller-tools/cmd/controller-gen object:headerFile=../hack/boilerplate.go.txt paths=./... crd:crdVersions=v1 output:artifacts:config=../package/crds + +// Generate crossplane-runtime methodsets (resource.Claim, etc) +//go:generate go run -tags generate github.com/crossplane/crossplane-tools/cmd/angryjet generate-methodsets --header-file=../hack/boilerplate.go.txt ./... + +package apis + +import ( + _ "sigs.k8s.io/controller-tools/cmd/controller-gen" //nolint:typecheck + + _ "github.com/crossplane/crossplane-tools/cmd/angryjet" //nolint:typecheck +) diff --git a/apis/sample/sample.go b/apis/sample/sample.go new file mode 100644 index 00000000..4772f5f7 --- /dev/null +++ b/apis/sample/sample.go @@ -0,0 +1,18 @@ +/* +Copyright 2020 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package sample contains group Sample API versions +package sample diff --git a/apis/sample/v1alpha1/doc.go b/apis/sample/v1alpha1/doc.go new file mode 100644 index 00000000..9c30a179 --- /dev/null +++ b/apis/sample/v1alpha1/doc.go @@ -0,0 +1,17 @@ +/* +Copyright 2020 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 diff --git a/apis/sample/v1alpha1/groupversion_info.go b/apis/sample/v1alpha1/groupversion_info.go new file mode 100644 index 00000000..dfb12a16 --- /dev/null +++ b/apis/sample/v1alpha1/groupversion_info.go @@ -0,0 +1,40 @@ +/* +Copyright 2020 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package v1alpha1 contains the v1alpha1 group Sample resources of the Template provider. +// +kubebuilder:object:generate=true +// +groupName=sample.template.crossplane.io +// +versionName=v1alpha1 +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +// Package type metadata. +const ( + Group = "sample.template.crossplane.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} +) diff --git a/apis/sample/v1alpha1/mytype_types.go b/apis/sample/v1alpha1/mytype_types.go new file mode 100644 index 00000000..d66b6b78 --- /dev/null +++ b/apis/sample/v1alpha1/mytype_types.go @@ -0,0 +1,87 @@ +/* +Copyright 2020 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "reflect" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" +) + +// MyTypeParameters are the configurable fields of a MyType. +type MyTypeParameters struct { + ConfigurableField string `json:"configurableField"` +} + +// MyTypeObservation are the observable fields of a MyType. +type MyTypeObservation struct { + ConfigurableField string `json:"configurableField"` + ObservableField string `json:"observableField,omitempty"` +} + +// A MyTypeSpec defines the desired state of a MyType. +type MyTypeSpec struct { + xpv1.ResourceSpec `json:",inline"` + ForProvider MyTypeParameters `json:"forProvider"` +} + +// A MyTypeStatus represents the observed state of a MyType. +type MyTypeStatus struct { + xpv1.ResourceStatus `json:",inline"` + AtProvider MyTypeObservation `json:"atProvider,omitempty"` +} + +// +kubebuilder:object:root=true + +// A MyType is an example API type. +// +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 MyType struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec MyTypeSpec `json:"spec"` + Status MyTypeStatus `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// MyTypeList contains a list of MyType +type MyTypeList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []MyType `json:"items"` +} + +// MyType type metadata. +var ( + MyTypeKind = reflect.TypeOf(MyType{}).Name() + MyTypeGroupKind = schema.GroupKind{Group: Group, Kind: MyTypeKind}.String() + MyTypeKindAPIVersion = MyTypeKind + "." + SchemeGroupVersion.String() + MyTypeGroupVersionKind = SchemeGroupVersion.WithKind(MyTypeKind) +) + +func init() { + SchemeBuilder.Register(&MyType{}, &MyTypeList{}) +} diff --git a/apis/sample/v1alpha1/zz_generated.deepcopy.go b/apis/sample/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 00000000..8f369c14 --- /dev/null +++ b/apis/sample/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,148 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2020 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// 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 *MyType) DeepCopyInto(out *MyType) { + *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 MyType. +func (in *MyType) DeepCopy() *MyType { + if in == nil { + return nil + } + out := new(MyType) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *MyType) 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 *MyTypeList) DeepCopyInto(out *MyTypeList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]MyType, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MyTypeList. +func (in *MyTypeList) DeepCopy() *MyTypeList { + if in == nil { + return nil + } + out := new(MyTypeList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *MyTypeList) 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 *MyTypeObservation) DeepCopyInto(out *MyTypeObservation) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MyTypeObservation. +func (in *MyTypeObservation) DeepCopy() *MyTypeObservation { + if in == nil { + return nil + } + out := new(MyTypeObservation) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MyTypeParameters) DeepCopyInto(out *MyTypeParameters) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MyTypeParameters. +func (in *MyTypeParameters) DeepCopy() *MyTypeParameters { + if in == nil { + return nil + } + out := new(MyTypeParameters) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MyTypeSpec) DeepCopyInto(out *MyTypeSpec) { + *out = *in + in.ResourceSpec.DeepCopyInto(&out.ResourceSpec) + out.ForProvider = in.ForProvider +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MyTypeSpec. +func (in *MyTypeSpec) DeepCopy() *MyTypeSpec { + if in == nil { + return nil + } + out := new(MyTypeSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *MyTypeStatus) DeepCopyInto(out *MyTypeStatus) { + *out = *in + in.ResourceStatus.DeepCopyInto(&out.ResourceStatus) + out.AtProvider = in.AtProvider +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new MyTypeStatus. +func (in *MyTypeStatus) DeepCopy() *MyTypeStatus { + if in == nil { + return nil + } + out := new(MyTypeStatus) + in.DeepCopyInto(out) + return out +} diff --git a/apis/sample/v1alpha1/zz_generated.managed.go b/apis/sample/v1alpha1/zz_generated.managed.go new file mode 100644 index 00000000..b1dba71b --- /dev/null +++ b/apis/sample/v1alpha1/zz_generated.managed.go @@ -0,0 +1,80 @@ +/* +Copyright 2020 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +// Code generated by angryjet. DO NOT EDIT. + +package v1alpha1 + +import xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + +// GetCondition of this MyType. +func (mg *MyType) GetCondition(ct xpv1.ConditionType) xpv1.Condition { + return mg.Status.GetCondition(ct) +} + +// GetDeletionPolicy of this MyType. +func (mg *MyType) GetDeletionPolicy() xpv1.DeletionPolicy { + return mg.Spec.DeletionPolicy +} + +// GetManagementPolicies of this MyType. +func (mg *MyType) GetManagementPolicies() xpv1.ManagementPolicies { + return mg.Spec.ManagementPolicies +} + +// GetProviderConfigReference of this MyType. +func (mg *MyType) GetProviderConfigReference() *xpv1.Reference { + return mg.Spec.ProviderConfigReference +} + +// GetPublishConnectionDetailsTo of this MyType. +func (mg *MyType) GetPublishConnectionDetailsTo() *xpv1.PublishConnectionDetailsTo { + return mg.Spec.PublishConnectionDetailsTo +} + +// GetWriteConnectionSecretToReference of this MyType. +func (mg *MyType) GetWriteConnectionSecretToReference() *xpv1.SecretReference { + return mg.Spec.WriteConnectionSecretToReference +} + +// SetConditions of this MyType. +func (mg *MyType) SetConditions(c ...xpv1.Condition) { + mg.Status.SetConditions(c...) +} + +// SetDeletionPolicy of this MyType. +func (mg *MyType) SetDeletionPolicy(r xpv1.DeletionPolicy) { + mg.Spec.DeletionPolicy = r +} + +// SetManagementPolicies of this MyType. +func (mg *MyType) SetManagementPolicies(r xpv1.ManagementPolicies) { + mg.Spec.ManagementPolicies = r +} + +// SetProviderConfigReference of this MyType. +func (mg *MyType) SetProviderConfigReference(r *xpv1.Reference) { + mg.Spec.ProviderConfigReference = r +} + +// SetPublishConnectionDetailsTo of this MyType. +func (mg *MyType) SetPublishConnectionDetailsTo(r *xpv1.PublishConnectionDetailsTo) { + mg.Spec.PublishConnectionDetailsTo = r +} + +// SetWriteConnectionSecretToReference of this MyType. +func (mg *MyType) SetWriteConnectionSecretToReference(r *xpv1.SecretReference) { + mg.Spec.WriteConnectionSecretToReference = r +} diff --git a/apis/sample/v1alpha1/zz_generated.managedlist.go b/apis/sample/v1alpha1/zz_generated.managedlist.go new file mode 100644 index 00000000..2b6ee905 --- /dev/null +++ b/apis/sample/v1alpha1/zz_generated.managedlist.go @@ -0,0 +1,29 @@ +/* +Copyright 2020 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +// Code generated by angryjet. DO NOT EDIT. + +package v1alpha1 + +import resource "github.com/crossplane/crossplane-runtime/pkg/resource" + +// GetItems of this MyTypeList. +func (l *MyTypeList) GetItems() []resource.Managed { + items := make([]resource.Managed, len(l.Items)) + for i := range l.Items { + items[i] = &l.Items[i] + } + return items +} diff --git a/apis/template.go b/apis/template.go new file mode 100644 index 00000000..b848fb47 --- /dev/null +++ b/apis/template.go @@ -0,0 +1,41 @@ +/* +Copyright 2020 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package apis contains Kubernetes API for the Template provider. +package apis + +import ( + "k8s.io/apimachinery/pkg/runtime" + + samplev1alpha1 "github.com/crossplane/provider-template/apis/sample/v1alpha1" + templatev1alpha1 "github.com/crossplane/provider-template/apis/v1alpha1" +) + +func init() { + // Register the types with the Scheme so the components can map objects to GroupVersionKinds and back + AddToSchemes = append(AddToSchemes, + templatev1alpha1.SchemeBuilder.AddToScheme, + samplev1alpha1.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/apis/v1alpha1/doc.go b/apis/v1alpha1/doc.go new file mode 100644 index 00000000..9c30a179 --- /dev/null +++ b/apis/v1alpha1/doc.go @@ -0,0 +1,17 @@ +/* +Copyright 2020 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 diff --git a/apis/v1alpha1/groupversion_info.go b/apis/v1alpha1/groupversion_info.go new file mode 100644 index 00000000..2ced458c --- /dev/null +++ b/apis/v1alpha1/groupversion_info.go @@ -0,0 +1,40 @@ +/* +Copyright 2020 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package v1alpha1 contains the core resources of the Template provider. +// +kubebuilder:object:generate=true +// +groupName=template.crossplane.io +// +versionName=v1alpha1 +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +// Package type metadata. +const ( + Group = "template.crossplane.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} +) diff --git a/apis/v1alpha1/providerconfig_types.go b/apis/v1alpha1/providerconfig_types.go new file mode 100644 index 00000000..d3bd7966 --- /dev/null +++ b/apis/v1alpha1/providerconfig_types.go @@ -0,0 +1,82 @@ +/* +Copyright 2020 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "reflect" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + + 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 +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"` +} + +// 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) +) + +func init() { + SchemeBuilder.Register(&ProviderConfig{}, &ProviderConfigList{}) +} diff --git a/apis/v1alpha1/providerconfigusage_types.go b/apis/v1alpha1/providerconfigusage_types.go new file mode 100644 index 00000000..a751a42f --- /dev/null +++ b/apis/v1alpha1/providerconfigusage_types.go @@ -0,0 +1,67 @@ +/* +Copyright 2021 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "reflect" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" +) + +// +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"` +} + +// 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(&ProviderConfigUsage{}, &ProviderConfigUsageList{}) +} diff --git a/apis/v1alpha1/storeconfig_types.go b/apis/v1alpha1/storeconfig_types.go new file mode 100644 index 00000000..6b88e32e --- /dev/null +++ b/apis/v1alpha1/storeconfig_types.go @@ -0,0 +1,90 @@ +/* +Copyright 2020 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "reflect" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + + 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 GCP 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,gcp} +// +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...) +} + +// 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/zz_generated.deepcopy.go b/apis/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 00000000..59b2ead3 --- /dev/null +++ b/apis/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,281 @@ +//go:build !ignore_autogenerated + +/* +Copyright 2020 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// 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 *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 +} + +// 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/v1alpha1/zz_generated.pc.go b/apis/v1alpha1/zz_generated.pc.go new file mode 100644 index 00000000..28fad33d --- /dev/null +++ b/apis/v1alpha1/zz_generated.pc.go @@ -0,0 +1,40 @@ +/* +Copyright 2020 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +// Code generated by angryjet. DO NOT EDIT. + +package v1alpha1 + +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/v1alpha1/zz_generated.pcu.go b/apis/v1alpha1/zz_generated.pcu.go new file mode 100644 index 00000000..710c6c6a --- /dev/null +++ b/apis/v1alpha1/zz_generated.pcu.go @@ -0,0 +1,40 @@ +/* +Copyright 2020 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +// Code generated by angryjet. DO NOT EDIT. + +package v1alpha1 + +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/v1alpha1/zz_generated.pculist.go b/apis/v1alpha1/zz_generated.pculist.go new file mode 100644 index 00000000..468bad08 --- /dev/null +++ b/apis/v1alpha1/zz_generated.pculist.go @@ -0,0 +1,29 @@ +/* +Copyright 2020 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ +// Code generated by angryjet. DO NOT EDIT. + +package v1alpha1 + +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/build b/build new file mode 160000 index 00000000..231258db --- /dev/null +++ b/build @@ -0,0 +1 @@ +Subproject commit 231258db281237379d8ec0c6e4af9d7c1ae5cc4a diff --git a/cluster/Dockerfile b/cluster/Dockerfile new file mode 100644 index 00000000..6486c7b5 --- /dev/null +++ b/cluster/Dockerfile @@ -0,0 +1,26 @@ +# Build the manager binary +FROM golang:1.16 as builder + +WORKDIR /workspace +# Copy the Go Modules manifests +COPY go.mod go.mod +COPY go.sum go.sum +# cache deps before building and copying source so that we don't need to re-download as much +# and so that source changes don't invalidate our downloaded layer +RUN go mod download + +# Copy the go source +COPY cmd/ cmd/ +COPY apis/ apis/ +COPY internal/ internal/ + +# Build +RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 GO111MODULE=on go build -a -o provider cmd/provider/main.go + +FROM alpine:3.7 +WORKDIR / +COPY --from=builder /workspace/provider . +COPY package / + +EXPOSE 8080 +ENTRYPOINT ["/provider"] diff --git a/cluster/images/provider-template/Dockerfile b/cluster/images/provider-template/Dockerfile new file mode 100644 index 00000000..9535a9e1 --- /dev/null +++ b/cluster/images/provider-template/Dockerfile @@ -0,0 +1,9 @@ +FROM gcr.io/distroless/static@sha256:41972110a1c1a5c0b6adb283e8aa092c43c31f7c5d79b8656fbffff2c3e61f05 + +ARG TARGETOS +ARG TARGETARCH + +ADD bin/$TARGETOS\_$TARGETARCH/provider /usr/local/bin/crossplane-template-provider + +USER 65532 +ENTRYPOINT ["crossplane-template-provider"] diff --git a/cluster/images/provider-template/Makefile b/cluster/images/provider-template/Makefile new file mode 100755 index 00000000..cfe2c3a4 --- /dev/null +++ b/cluster/images/provider-template/Makefile @@ -0,0 +1,35 @@ +# ==================================================================================== +# 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 -r $(OUTPUT_DIR)/bin/ $(IMAGE_TEMP_DIR)/bin || $(FAIL) + @docker buildx build $(BUILD_ARGS) \ + --platform $(IMAGE_PLATFORMS) \ + -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/local/integration_tests.sh b/cluster/local/integration_tests.sh new file mode 100755 index 00000000..831731f9 --- /dev/null +++ b/cluster/local/integration_tests.sh @@ -0,0 +1,193 @@ +#!/usr/bin/env bash +set -e + +# setting up colors +BLU='\033[0;34m' +YLW='\033[0;33m' +GRN='\033[0;32m' +RED='\033[0;31m' +NOC='\033[0m' # No Color +echo_info(){ + printf "\n${BLU}%s${NOC}" "$1" +} +echo_step(){ + printf "\n${BLU}>>>>>>> %s${NOC}\n" "$1" +} +echo_sub_step(){ + printf "\n${BLU}>>> %s${NOC}\n" "$1" +} + +echo_step_completed(){ + printf "${GRN} [✔]${NOC}" +} + +echo_success(){ + printf "\n${GRN}%s${NOC}\n" "$1" +} +echo_warn(){ + printf "\n${YLW}%s${NOC}" "$1" +} +echo_error(){ + printf "\n${RED}%s${NOC}" "$1" + exit 1 +} + + +# The name of your provider. Many provider Makefiles override this value. +PACKAGE_NAME="provider-template" + + +# ------------------------------ +projectdir="$( cd "$( dirname "${BASH_SOURCE[0]}")"/../.. && pwd )" + +# get the build environment variables from the special build.vars target in the main makefile +eval $(make --no-print-directory -C ${projectdir} build.vars) + +# ------------------------------ + +SAFEHOSTARCH="${SAFEHOSTARCH:-amd64}" +BUILD_IMAGE="${BUILD_REGISTRY}/${PROJECT_NAME}-${SAFEHOSTARCH}" +PACKAGE_IMAGE="crossplane.io/inttests/${PROJECT_NAME}:${VERSION}" +CONTROLLER_IMAGE="${BUILD_REGISTRY}/${PROJECT_NAME}-controller-${SAFEHOSTARCH}" + +version_tag="$(cat ${projectdir}/_output/version)" +# tag as latest version to load into kind cluster +PACKAGE_CONTROLLER_IMAGE="${DOCKER_REGISTRY}/${PROJECT_NAME}-controller:${VERSION}" +K8S_CLUSTER="${K8S_CLUSTER:-${BUILD_REGISTRY}-inttests}" + +CROSSPLANE_NAMESPACE="crossplane-system" + +# cleanup on exit +if [ "$skipcleanup" != true ]; then + function cleanup { + echo_step "Cleaning up..." + export KUBECONFIG= + "${KIND}" delete cluster --name="${K8S_CLUSTER}" + } + + trap cleanup EXIT +fi + +# setup package cache +echo_step "setting up local package cache" +CACHE_PATH="${projectdir}/.work/inttest-package-cache" +mkdir -p "${CACHE_PATH}" +echo "created cache dir at ${CACHE_PATH}" +docker tag "${BUILD_IMAGE}" "${PACKAGE_IMAGE}" +"${UP}" xpkg xp-extract --from-daemon "${PACKAGE_IMAGE}" -o "${CACHE_PATH}/${PACKAGE_NAME}.gz" && chmod 644 "${CACHE_PATH}/${PACKAGE_NAME}.gz" + +# create kind cluster with extra mounts +KIND_NODE_IMAGE="kindest/node:${KIND_NODE_IMAGE_TAG}" +echo_step "creating k8s cluster using kind ${KIND_VERSION} and node image ${KIND_NODE_IMAGE}" +KIND_CONFIG="$( cat < $current ]]; then + echo_error "timeout of ${timeout}s has been reached" + fi + sleep $step; +done + +echo_success "Integration tests succeeded!" diff --git a/cmd/provider/main.go b/cmd/provider/main.go new file mode 100644 index 00000000..2cf89776 --- /dev/null +++ b/cmd/provider/main.go @@ -0,0 +1,131 @@ +/* +Copyright 2020 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package main + +import ( + "context" + "os" + "path/filepath" + "time" + + "gopkg.in/alecthomas/kingpin.v2" + kerrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/client-go/tools/leaderelection/resourcelock" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/cache" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" + "github.com/crossplane/crossplane-runtime/pkg/controller" + "github.com/crossplane/crossplane-runtime/pkg/feature" + "github.com/crossplane/crossplane-runtime/pkg/logging" + "github.com/crossplane/crossplane-runtime/pkg/ratelimiter" + "github.com/crossplane/crossplane-runtime/pkg/resource" + + "github.com/crossplane/provider-template/apis" + "github.com/crossplane/provider-template/apis/v1alpha1" + template "github.com/crossplane/provider-template/internal/controller" + "github.com/crossplane/provider-template/internal/features" +) + +func main() { + var ( + app = kingpin.New(filepath.Base(os.Args[0]), "Template support for Crossplane.").DefaultEnvars() + debug = app.Flag("debug", "Run with debug logging.").Short('d').Bool() + leaderElection = app.Flag("leader-election", "Use leader election for the controller manager.").Short('l').Default("false").OverrideDefaultFromEnvar("LEADER_ELECTION").Bool() + + syncInterval = app.Flag("sync", "How often all resources will be double-checked for drift from the desired state.").Short('s').Default("1h").Duration() + pollInterval = app.Flag("poll", "How often individual resources will be checked for drift from the desired state").Default("1m").Duration() + maxReconcileRate = app.Flag("max-reconcile-rate", "The global maximum rate per second at which resources may checked for drift from the desired state.").Default("10").Int() + + namespace = app.Flag("namespace", "Namespace used to set as default scope in default secret store config.").Default("crossplane-system").Envar("POD_NAMESPACE").String() + enableExternalSecretStores = app.Flag("enable-external-secret-stores", "Enable support for ExternalSecretStores.").Default("false").Envar("ENABLE_EXTERNAL_SECRET_STORES").Bool() + enableManagementPolicies = app.Flag("enable-management-policies", "Enable support for Management Policies.").Default("false").Envar("ENABLE_MANAGEMENT_POLICIES").Bool() + ) + kingpin.MustParse(app.Parse(os.Args[1:])) + + zl := zap.New(zap.UseDevMode(*debug)) + log := logging.NewLogrLogger(zl.WithName("provider-template")) + if *debug { + // The controller-runtime runs with a no-op logger by default. It is + // *very* verbose even at info level, so we only provide it a real + // logger when we're running in debug mode. + ctrl.SetLogger(zl) + } + + cfg, err := ctrl.GetConfig() + kingpin.FatalIfError(err, "Cannot get API server rest config") + + mgr, err := ctrl.NewManager(ratelimiter.LimitRESTConfig(cfg, *maxReconcileRate), ctrl.Options{ + // SyncPeriod in ctrl.Options has been removed since controller-runtime v0.16.0 + // The recommended way is to move it to cache.Options instead + Cache: cache.Options{ + SyncPeriod: syncInterval, + }, + + // controller-runtime uses both ConfigMaps and Leases for leader + // election by default. Leases expire after 15 seconds, with a + // 10 second renewal deadline. We've observed leader loss due to + // renewal deadlines being exceeded when under high load - i.e. + // hundreds of reconciles per second and ~200rps to the API + // server. Switching to Leases only and longer leases appears to + // alleviate this. + LeaderElection: *leaderElection, + LeaderElectionID: "crossplane-leader-election-provider-template", + LeaderElectionResourceLock: resourcelock.LeasesResourceLock, + LeaseDuration: func() *time.Duration { d := 60 * time.Second; return &d }(), + RenewDeadline: func() *time.Duration { d := 50 * time.Second; return &d }(), + }) + kingpin.FatalIfError(err, "Cannot create controller manager") + kingpin.FatalIfError(apis.AddToScheme(mgr.GetScheme()), "Cannot add Template APIs to scheme") + + o := controller.Options{ + Logger: log, + MaxConcurrentReconciles: *maxReconcileRate, + PollInterval: *pollInterval, + GlobalRateLimiter: ratelimiter.NewGlobal(*maxReconcileRate), + Features: &feature.Flags{}, + } + + if *enableExternalSecretStores { + o.Features.Enable(features.EnableAlphaExternalSecretStores) + log.Info("Alpha feature enabled", "flag", features.EnableAlphaExternalSecretStores) + + // Ensure default store config exists. + kingpin.FatalIfError(resource.Ignore(kerrors.IsAlreadyExists, mgr.GetClient().Create(context.Background(), &v1alpha1.StoreConfig{ + ObjectMeta: metav1.ObjectMeta{ + Name: "default", + }, + Spec: v1alpha1.StoreConfigSpec{ + // NOTE(turkenh): We only set required spec and expect optional + // ones to properly be initialized with CRD level default values. + SecretStoreConfig: xpv1.SecretStoreConfig{ + DefaultScope: *namespace, + }, + }, + })), "cannot create default store config") + } + + if *enableManagementPolicies { + o.Features.Enable(features.EnableAlphaManagementPolicies) + log.Info("Alpha feature enabled", "flag", features.EnableAlphaManagementPolicies) + } + + kingpin.FatalIfError(template.Setup(mgr, o), "Cannot setup Template controllers") + kingpin.FatalIfError(mgr.Start(ctrl.SetupSignalHandler()), "Cannot start controller manager") +} diff --git a/examples/provider/config.yaml b/examples/provider/config.yaml new file mode 100644 index 00000000..6a536457 --- /dev/null +++ b/examples/provider/config.yaml @@ -0,0 +1,25 @@ +apiVersion: v1 +kind: Namespace +metadata: + name: crossplane-system +--- +apiVersion: v1 +kind: Secret +metadata: + namespace: crossplane-system + name: example-provider-secret +type: Opaque +data: + # credentials: BASE64ENCODED_PROVIDER_CREDS +--- +apiVersion: template.crossplane.io/v1alpha1 +kind: ProviderConfig +metadata: + name: example +spec: + credentials: + source: Secret + secretRef: + namespace: crossplane-system + name: example-provider-secret + key: credentials diff --git a/examples/sample/mytype.yaml b/examples/sample/mytype.yaml new file mode 100644 index 00000000..a73ff5c2 --- /dev/null +++ b/examples/sample/mytype.yaml @@ -0,0 +1,9 @@ +apiVersion: sample.template.crossplane.io/v1alpha1 +kind: MyType +metadata: + name: example +spec: + forProvider: + configurableField: test + providerConfigRef: + name: example \ No newline at end of file diff --git a/examples/storeconfig/vault.yaml b/examples/storeconfig/vault.yaml new file mode 100644 index 00000000..1f2d881a --- /dev/null +++ b/examples/storeconfig/vault.yaml @@ -0,0 +1,19 @@ +apiVersion: template.crossplane.io/v1alpha1 +kind: StoreConfig +metadata: + name: vault +spec: + type: Vault + defaultScope: crossplane-system + vault: + server: http://vault.vault-system:8200 + mountPath: secret/ + version: v2 + auth: + method: Token + token: + source: Secret + secretRef: + namespace: crossplane-system + name: vault-token + key: token diff --git a/go.mod b/go.mod new file mode 100644 index 00000000..88bfe275 --- /dev/null +++ b/go.mod @@ -0,0 +1,89 @@ +module github.com/crossplane/provider-template + +go 1.21 + +require ( + github.com/crossplane/crossplane-runtime v1.16.0 + github.com/crossplane/crossplane-tools v0.0.0-20230925130601-628280f8bf79 + github.com/google/go-cmp v0.6.0 + github.com/pkg/errors v0.9.1 + gopkg.in/alecthomas/kingpin.v2 v2.2.6 + k8s.io/apimachinery v0.29.2 + k8s.io/client-go v0.29.2 + sigs.k8s.io/controller-runtime v0.17.2 + sigs.k8s.io/controller-tools v0.14.0 +) + +require ( + dario.cat/mergo v1.0.0 // indirect + github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect + github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/blang/semver/v4 v4.0.0 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/dave/jennifer v1.4.1 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/evanphx/json-patch v5.6.0+incompatible // indirect + github.com/evanphx/json-patch/v5 v5.8.0 // indirect + github.com/fatih/color v1.16.0 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-logr/logr v1.4.1 // indirect + github.com/go-logr/zapr v1.3.0 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect + github.com/go-openapi/swag v0.22.3 // indirect + github.com/gobuffalo/flect v1.0.2 // indirect + github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/gnostic-models v0.6.8 // indirect + github.com/google/gofuzz v1.2.0 // indirect + github.com/google/uuid v1.4.0 // indirect + github.com/imdario/mergo v0.3.16 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/mattn/go-colorable v0.1.13 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect + github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/prometheus/client_golang v1.18.0 // indirect + github.com/prometheus/client_model v0.5.0 // indirect + github.com/prometheus/common v0.45.0 // indirect + github.com/prometheus/procfs v0.12.0 // indirect + github.com/spf13/afero v1.11.0 // indirect + github.com/spf13/cobra v1.8.0 // indirect + github.com/spf13/pflag v1.0.5 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.26.0 // indirect + golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect + golang.org/x/mod v0.14.0 // indirect + golang.org/x/net v0.23.0 // indirect + golang.org/x/oauth2 v0.15.0 // indirect + golang.org/x/sys v0.18.0 // indirect + golang.org/x/term v0.18.0 // indirect + golang.org/x/text v0.14.0 // indirect + golang.org/x/time v0.5.0 // indirect + golang.org/x/tools v0.17.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect + google.golang.org/appengine v1.6.8 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f // indirect + google.golang.org/grpc v1.61.0 // indirect + google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + k8s.io/api v0.29.2 // indirect + k8s.io/apiextensions-apiserver v0.29.1 // indirect + k8s.io/component-base v0.29.1 // indirect + k8s.io/klog/v2 v2.110.1 // indirect + k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect + k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/yaml v1.4.0 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 00000000..4df3d559 --- /dev/null +++ b/go.sum @@ -0,0 +1,265 @@ +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/crossplane/crossplane-runtime v1.16.0 h1:lz+l0wEB3qowdTmN7t0PZkfuNSvfOoEhQrEYFbYqMow= +github.com/crossplane/crossplane-runtime v1.16.0/go.mod h1:Pz2tdGVMF6KDGzHZOkvKro0nKc8EzK0sb/nSA7pH4Dc= +github.com/crossplane/crossplane-tools v0.0.0-20230925130601-628280f8bf79 h1:HigXs5tEQxWz0fcj8hzbU2UAZgEM7wPe0XRFOsrtF8Y= +github.com/crossplane/crossplane-tools v0.0.0-20230925130601-628280f8bf79/go.mod h1:+e4OaFlOcmr0JvINHl/yvEYBrZawzTgj6pQumOH1SS0= +github.com/dave/jennifer v1.4.1 h1:XyqG6cn5RQsTj3qlWQTKlRGAyrTcsk1kUmWdZBzRjDw= +github.com/dave/jennifer v1.4.1/go.mod h1:7jEdnm+qBcxl8PC0zyp7vxcpSRnzXSt9r39tpTVGlwA= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= +github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/evanphx/json-patch/v5 v5.8.0 h1:lRj6N9Nci7MvzrXuX6HFzU8XjmhPiXPlsKEy1u0KQro= +github.com/evanphx/json-patch/v5 v5.8.0/go.mod h1:VNkHZ/282BpEyt/tObQO8s5CMPmYYq14uClGH4abBuQ= +github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= +github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= +github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= +github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= +github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/gobuffalo/flect v1.0.2 h1:eqjPGSo2WmjgY2XlpGwo2NXgL3RucAKo4k4qQMNA5sA= +github.com/gobuffalo/flect v1.0.2/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= +github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20240117000934-35fc243c5815 h1:WzfWbQz/Ze8v6l++GGbGNFZnUShVpP/0xffCPLL+ax8= +github.com/google/pprof v0.0.0-20240117000934-35fc243c5815/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= +github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= +github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 h1:jWpvCLoY8Z/e3VKvlsiIGKtc+UG6U5vzxaoagmhXfyg= +github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= +github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= +github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= +github.com/onsi/ginkgo/v2 v2.14.0 h1:vSmGj2Z5YPb9JwCWT6z6ihcUvDhuXLc3sJiqd3jMKAY= +github.com/onsi/ginkgo/v2 v2.14.0/go.mod h1:JkUdW7JkN0V6rFvsHcJ478egV3XH9NxpD27Hal/PhZw= +github.com/onsi/gomega v1.30.0 h1:hvMK7xYz4D3HapigLTeGdId/NcfQx1VHMJc60ew99+8= +github.com/onsi/gomega v1.30.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.18.0 h1:HzFfmkOzH5Q8L8G+kSJKUx5dtG87sewO+FoDDqP5Tbk= +github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= +github.com/prometheus/client_model v0.5.0 h1:VQw1hfvPvk3Uv6Qf29VrPF32JB6rtbgI6cYPYQjL0Qw= +github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +github.com/prometheus/common v0.45.0 h1:2BGz0eBc2hdMDLnO/8n0jeB3oPrt2D08CekT0lneoxM= +github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= +github.com/prometheus/procfs v0.12.0 h1:jluTpSng7V9hY0O2R9DzzJHYb2xULk9VTR1V1R/k6Bo= +github.com/prometheus/procfs v0.12.0/go.mod h1:pcuDEFsWDnvcgNzo4EEweacyhjeA9Zk3cnaOZAZEfOo= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8= +github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY= +github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0= +github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= +go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o= +golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08= +golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= +golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= +golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= +golang.org/x/oauth2 v0.15.0 h1:s8pnnxNVzjWyrvYdFUQq5llS1PX2zhPXmccZv99h7uQ= +golang.org/x/oauth2 v0.15.0/go.mod h1:q48ptWNTY5XWf+JNten23lcvHpLJ0ZSxF5ttTHKVCAM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ= +golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= +golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= +golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= +golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc= +golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= +gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM= +google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f h1:ultW7fxlIvee4HYrtnaRPon9HpEgFk5zYpmfMgtKB5I= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231120223509-83a465c0220f/go.mod h1:L9KNLi232K1/xB6f7AlSX692koaRnKaWSR0stBki0Yc= +google.golang.org/grpc v1.61.0 h1:TOvOcuXn30kRao+gfcvsebNEa5iZIiLkisYEkf7R7o0= +google.golang.org/grpc v1.61.0/go.mod h1:VUbo7IFqmF1QtCAstipjG0GIoq49KvMe9+h1jFLBNJs= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +k8s.io/api v0.29.2 h1:hBC7B9+MU+ptchxEqTNW2DkUosJpp1P+Wn6YncZ474A= +k8s.io/api v0.29.2/go.mod h1:sdIaaKuU7P44aoyyLlikSLayT6Vb7bvJNCX105xZXY0= +k8s.io/apiextensions-apiserver v0.29.1 h1:S9xOtyk9M3Sk1tIpQMu9wXHm5O2MX6Y1kIpPMimZBZw= +k8s.io/apiextensions-apiserver v0.29.1/go.mod h1:zZECpujY5yTW58co8V2EQR4BD6A9pktVgHhvc0uLfeU= +k8s.io/apimachinery v0.29.2 h1:EWGpfJ856oj11C52NRCHuU7rFDwxev48z+6DSlGNsV8= +k8s.io/apimachinery v0.29.2/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU= +k8s.io/client-go v0.29.2 h1:FEg85el1TeZp+/vYJM7hkDlSTFZ+c5nnK44DJ4FyoRg= +k8s.io/client-go v0.29.2/go.mod h1:knlvFZE58VpqbQpJNbCbctTVXcd35mMyAAwBdpt4jrA= +k8s.io/component-base v0.29.1 h1:MUimqJPCRnnHsskTTjKD+IC1EHBbRCVyi37IoFBrkYw= +k8s.io/component-base v0.29.1/go.mod h1:fP9GFjxYrLERq1GcWWZAE3bqbNcDKDytn2srWuHTtKc= +k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= +k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= +k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= +k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= +k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/controller-runtime v0.17.2 h1:FwHwD1CTUemg0pW2otk7/U5/i5m2ymzvOXdbeGOUvw0= +sigs.k8s.io/controller-runtime v0.17.2/go.mod h1:+MngTvIQQQhfXtwfdGw/UOQ/aIaqsYywfCINOtwMO/s= +sigs.k8s.io/controller-tools v0.14.0 h1:rnNoCC5wSXlrNoBKKzL70LNJKIQKEzT6lloG6/LF73A= +sigs.k8s.io/controller-tools v0.14.0/go.mod h1:TV7uOtNNnnR72SpzhStvPkoS/U5ir0nMudrkrC4M9Sc= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= +sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= +sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= diff --git a/hack/boilerplate.go.txt b/hack/boilerplate.go.txt new file mode 100644 index 00000000..978e1a18 --- /dev/null +++ b/hack/boilerplate.go.txt @@ -0,0 +1,15 @@ +/* +Copyright 2020 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ \ No newline at end of file diff --git a/hack/helpers/addtype.sh b/hack/helpers/addtype.sh new file mode 100755 index 00000000..1ce58d37 --- /dev/null +++ b/hack/helpers/addtype.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash + +# Copyright 2022 The Crossplane Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Please set ProviderNameLower & ProviderNameUpper environment variables before running this script. +# See: https://github.com/crossplane/terrajet/blob/main/docs/generating-a-provider.md +set -euo pipefail + +APIVERSION="${APIVERSION:-v1alpha1}" +echo "Adding type ${KIND} to group ${GROUP} with version ${APIVERSION}" + +export GROUP +export KIND +export APIVERSION +export PROVIDER +export PROJECT_REPO + +kind_lower=$(echo "${KIND}" | tr "[:upper:]" "[:lower:]") +group_lower=$(echo "${GROUP}" | tr "[:upper:]" "[:lower:]") + +mkdir -p "apis/${group_lower}/${APIVERSION}" +${GOMPLATE} < "hack/helpers/apis/GROUP_LOWER/GROUP_LOWER.go.tmpl" > "apis/${group_lower}/${group_lower}.go" +${GOMPLATE} < "hack/helpers/apis/GROUP_LOWER/APIVERSION/KIND_LOWER_types.go.tmpl" > "apis/${group_lower}/${APIVERSION}/${kind_lower}_types.go" +${GOMPLATE} < "hack/helpers/apis/GROUP_LOWER/APIVERSION/doc.go.tmpl" > "apis/${group_lower}/${APIVERSION}/doc.go" +${GOMPLATE} < "hack/helpers/apis/GROUP_LOWER/APIVERSION/groupversion_info.go.tmpl" > "apis/${group_lower}/${APIVERSION}/groupversion_info.go" + +mkdir -p "internal/controller/${kind_lower}" +${GOMPLATE} < "hack/helpers/controller/KIND_LOWER/KIND_LOWER.go.tmpl" > "internal/controller/${kind_lower}/${kind_lower}.go" +${GOMPLATE} < "hack/helpers/controller/KIND_LOWER/KIND_LOWER_test.go.tmpl" > "internal/controller/${kind_lower}/${kind_lower}_test.go" + + + diff --git a/hack/helpers/apis/GROUP_LOWER/APIVERSION/KIND_LOWER_types.go.tmpl b/hack/helpers/apis/GROUP_LOWER/APIVERSION/KIND_LOWER_types.go.tmpl new file mode 100644 index 00000000..755cdef0 --- /dev/null +++ b/hack/helpers/apis/GROUP_LOWER/APIVERSION/KIND_LOWER_types.go.tmpl @@ -0,0 +1,86 @@ +/* +Copyright 2022 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package {{ .Env.APIVERSION }} + +import ( + "reflect" + + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/runtime/schema" + + xpv1 "github.com/crossplane/crossplane-runtime/apis/common/v1" +) + +// {{ .Env.KIND }}Parameters are the configurable fields of a {{ .Env.KIND }}. +type {{ .Env.KIND }}Parameters struct { + ConfigurableField string `json:"configurableField"` +} + +// {{ .Env.KIND }}Observation are the observable fields of a {{ .Env.KIND }}. +type {{ .Env.KIND }}Observation struct { + ObservableField string `json:"observableField,omitempty"` +} + +// A {{ .Env.KIND }}Spec defines the desired state of a {{ .Env.KIND }}. +type {{ .Env.KIND }}Spec struct { + xpv1.ResourceSpec `json:",inline"` + ForProvider {{ .Env.KIND }}Parameters `json:"forProvider"` +} + +// A {{ .Env.KIND }}Status represents the observed state of a {{ .Env.KIND }}. +type {{ .Env.KIND }}Status struct { + xpv1.ResourceStatus `json:",inline"` + AtProvider {{ .Env.KIND }}Observation `json:"atProvider,omitempty"` +} + +// +kubebuilder:object:root=true + +// A {{ .Env.KIND }} is an example API type. +// +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,{{ .Env.PROVIDER | strings.ToLower }}} +type {{ .Env.KIND }} struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec {{ .Env.KIND }}Spec `json:"spec"` + Status {{ .Env.KIND }}Status `json:"status,omitempty"` +} + +// +kubebuilder:object:root=true + +// {{ .Env.KIND }}List contains a list of {{ .Env.KIND }} +type {{ .Env.KIND }}List struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []{{ .Env.KIND }} `json:"items"` +} + +// {{ .Env.KIND }} type metadata. +var ( + {{ .Env.KIND }}Kind = reflect.TypeOf({{ .Env.KIND }}{}).Name() + {{ .Env.KIND }}GroupKind = schema.GroupKind{Group: Group, Kind: {{ .Env.KIND }}Kind}.String() + {{ .Env.KIND }}KindAPIVersion = {{ .Env.KIND }}Kind + "." + SchemeGroupVersion.String() + {{ .Env.KIND }}GroupVersionKind = SchemeGroupVersion.WithKind({{ .Env.KIND }}Kind) +) + +func init() { + SchemeBuilder.Register(&{{ .Env.KIND }}{}, &{{ .Env.KIND }}List{}) +} diff --git a/hack/helpers/apis/GROUP_LOWER/APIVERSION/doc.go.tmpl b/hack/helpers/apis/GROUP_LOWER/APIVERSION/doc.go.tmpl new file mode 100644 index 00000000..d83c5899 --- /dev/null +++ b/hack/helpers/apis/GROUP_LOWER/APIVERSION/doc.go.tmpl @@ -0,0 +1,17 @@ +/* +Copyright 2022 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package {{ .Env.APIVERSION | strings.ToLower }} diff --git a/hack/helpers/apis/GROUP_LOWER/APIVERSION/groupversion_info.go.tmpl b/hack/helpers/apis/GROUP_LOWER/APIVERSION/groupversion_info.go.tmpl new file mode 100644 index 00000000..de518a94 --- /dev/null +++ b/hack/helpers/apis/GROUP_LOWER/APIVERSION/groupversion_info.go.tmpl @@ -0,0 +1,40 @@ +/* +Copyright 2020 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package v1alpha1 contains the v1alpha1 group Sample resources of the {{ .Env.PROVIDER }} provider. +// +kubebuilder:object:generate=true +// +groupName={{ .Env.GROUP | strings.ToLower }}.{{ .Env.PROVIDER | strings.ToLower }}.crossplane.io +// +versionName={{ .Env.APIVERSION | strings.ToLower }} +package {{ .Env.APIVERSION | strings.ToLower }} + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +// Package type metadata. +const ( + Group = "{{ .Env.GROUP | strings.ToLower }}.{{ .Env.PROVIDER | strings.ToLower }}.crossplane.io" + Version = "{{ .Env.APIVERSION | strings.ToLower }}" +) + +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} +) diff --git a/hack/helpers/apis/GROUP_LOWER/GROUP_LOWER.go.tmpl b/hack/helpers/apis/GROUP_LOWER/GROUP_LOWER.go.tmpl new file mode 100644 index 00000000..f067ddad --- /dev/null +++ b/hack/helpers/apis/GROUP_LOWER/GROUP_LOWER.go.tmpl @@ -0,0 +1,18 @@ +/* +Copyright 2022 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package {{ .Env.GROUP | strings.ToLower }} contains group {{ .Env.GROUP }} API versions +package {{ .Env.GROUP | strings.ToLower }} diff --git a/hack/helpers/controller/KIND_LOWER/KIND_LOWER.go.tmpl b/hack/helpers/controller/KIND_LOWER/KIND_LOWER.go.tmpl new file mode 100644 index 00000000..9ce8ada1 --- /dev/null +++ b/hack/helpers/controller/KIND_LOWER/KIND_LOWER.go.tmpl @@ -0,0 +1,199 @@ +/* +Copyright 2022 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package {{ .Env.KIND | strings.ToLower }} + +import ( + "context" + "fmt" + + "github.com/pkg/errors" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/crossplane/crossplane-runtime/pkg/connection" + "github.com/crossplane/crossplane-runtime/pkg/controller" + "github.com/crossplane/crossplane-runtime/pkg/event" + "github.com/crossplane/crossplane-runtime/pkg/ratelimiter" + "github.com/crossplane/crossplane-runtime/pkg/reconciler/managed" + "github.com/crossplane/crossplane-runtime/pkg/resource" + + "{{ .Env.PROJECT_REPO | strings.ToLower }}/apis/{{ .Env.GROUP | strings.ToLower }}/{{ .Env.APIVERSION | strings.ToLower }}" + apisv1alpha1 "{{ .Env.PROJECT_REPO | strings.ToLower }}/apis/v1alpha1" + "{{ .Env.PROJECT_REPO | strings.ToLower }}/internal/features" +) + +const ( + errNot{{ .Env.KIND }} = "managed resource is not a {{ .Env.KIND }} custom resource" + errTrackPCUsage = "cannot track ProviderConfig usage" + errGetPC = "cannot get ProviderConfig" + errGetCreds = "cannot get credentials" + + errNewClient = "cannot create new Service" +) + +// A NoOpService does nothing. +type NoOpService struct{} + +var ( + newNoOpService = func(_ []byte) (interface{}, error) { return &NoOpService{}, nil } +) + +// Setup adds a controller that reconciles {{ .Env.KIND }} managed resources. +func Setup(mgr ctrl.Manager, o controller.Options) error { + name := managed.ControllerName(v1alpha1.{{ .Env.KIND }}GroupKind) + + cps := []managed.ConnectionPublisher{managed.NewAPISecretPublisher(mgr.GetClient(), mgr.GetScheme())} + if o.Features.Enabled(features.EnableAlphaExternalSecretStores) { + cps = append(cps, connection.NewDetailsManager(mgr.GetClient(), apisv1alpha1.StoreConfigGroupVersionKind)) + } + + r := managed.NewReconciler(mgr, + resource.ManagedKind(v1alpha1.{{ .Env.KIND }}GroupVersionKind), + managed.WithExternalConnecter(&connector{ + kube: mgr.GetClient(), + usage: resource.NewProviderConfigUsageTracker(mgr.GetClient(), &apisv1alpha1.ProviderConfigUsage{}), + newServiceFn: newNoOpService}), + managed.WithLogger(o.Logger.WithValues("controller", name)), + managed.WithPollInterval(o.PollInterval), + managed.WithRecorder(event.NewAPIRecorder(mgr.GetEventRecorderFor(name))), + managed.WithConnectionPublishers(cps...)) + + return ctrl.NewControllerManagedBy(mgr). + Named(name). + WithOptions(o.ForControllerRuntime()). + WithEventFilter(resource.DesiredStateChanged()). + For(&v1alpha1.{{ .Env.KIND }}{}). + Complete(ratelimiter.NewReconciler(name, r, o.GlobalRateLimiter)) +} + +// A connector is expected to produce an ExternalClient when its Connect method +// is called. +type connector struct { + kube client.Client + usage resource.Tracker + newServiceFn func(creds []byte) (interface{}, error) +} + +// Connect typically produces an ExternalClient by: +// 1. Tracking that the managed resource is using a ProviderConfig. +// 2. Getting the managed resource's ProviderConfig. +// 3. Getting the credentials specified by the ProviderConfig. +// 4. Using the credentials to form a client. +func (c *connector) Connect(ctx context.Context, mg resource.Managed) (managed.ExternalClient, error) { + cr, ok := mg.(*v1alpha1.{{ .Env.KIND }}) + if !ok { + return nil, errors.New(errNot{{ .Env.KIND }}) + } + + if err := c.usage.Track(ctx, mg); err != nil { + return nil, errors.Wrap(err, errTrackPCUsage) + } + + pc := &apisv1alpha1.ProviderConfig{} + if err := c.kube.Get(ctx, types.NamespacedName{Name: cr.GetProviderConfigReference().Name}, pc); err != nil { + return nil, errors.Wrap(err, errGetPC) + } + + cd := pc.Spec.Credentials + data, err := resource.CommonCredentialExtractor(ctx, cd.Source, c.kube, cd.CommonCredentialSelectors) + if err != nil { + return nil, errors.Wrap(err, errGetCreds) + } + + svc, err := c.newServiceFn(data) + if err != nil { + return nil, errors.Wrap(err, errNewClient) + } + + return &external{service: svc}, nil +} + +// An ExternalClient observes, then either creates, updates, or deletes an +// external resource to ensure it reflects the managed resource's desired state. +type external struct { + // A 'client' used to connect to the external resource API. In practice this + // would be something like an AWS SDK client. + service interface{} +} + +func (c *external) Observe(ctx context.Context, mg resource.Managed) (managed.ExternalObservation, error) { + cr, ok := mg.(*v1alpha1.{{ .Env.KIND }}) + if !ok { + return managed.ExternalObservation{}, errors.New(errNot{{ .Env.KIND }}) + } + + // These fmt statements should be removed in the real implementation. + fmt.Printf("Observing: %+v", cr) + + return managed.ExternalObservation{ + // Return false when the external resource does not exist. This lets + // the managed resource reconciler know that it needs to call Create to + // (re)create the resource, or that it has successfully been deleted. + ResourceExists: true, + + // Return false when the external resource exists, but it not up to date + // with the desired managed resource state. This lets the managed + // resource reconciler know that it needs to call Update. + ResourceUpToDate: true, + + // Return any details that may be required to connect to the external + // resource. These will be stored as the connection secret. + ConnectionDetails: managed.ConnectionDetails{}, + }, nil +} + +func (c *external) Create(ctx context.Context, mg resource.Managed) (managed.ExternalCreation, error) { + cr, ok := mg.(*v1alpha1.{{ .Env.KIND }}) + if !ok { + return managed.ExternalCreation{}, errors.New(errNot{{ .Env.KIND }}) + } + + fmt.Printf("Creating: %+v", cr) + + return managed.ExternalCreation{ + // Optionally return any details that may be required to connect to the + // external resource. These will be stored as the connection secret. + ConnectionDetails: managed.ConnectionDetails{}, + }, nil +} + +func (c *external) Update(ctx context.Context, mg resource.Managed) (managed.ExternalUpdate, error) { + cr, ok := mg.(*v1alpha1.{{ .Env.KIND }}) + if !ok { + return managed.ExternalUpdate{}, errors.New(errNot{{ .Env.KIND }}) + } + + fmt.Printf("Updating: %+v", cr) + + return managed.ExternalUpdate{ + // Optionally return any details that may be required to connect to the + // external resource. These will be stored as the connection secret. + ConnectionDetails: managed.ConnectionDetails{}, + }, nil +} + +func (c *external) Delete(ctx context.Context, mg resource.Managed) error { + cr, ok := mg.(*v1alpha1.{{ .Env.KIND }}) + if !ok { + return errors.New(errNot{{ .Env.KIND }}) + } + + fmt.Printf("Deleting: %+v", cr) + + return nil +} diff --git a/hack/helpers/controller/KIND_LOWER/KIND_LOWER_test.go.tmpl b/hack/helpers/controller/KIND_LOWER/KIND_LOWER_test.go.tmpl new file mode 100644 index 00000000..40f5c689 --- /dev/null +++ b/hack/helpers/controller/KIND_LOWER/KIND_LOWER_test.go.tmpl @@ -0,0 +1,74 @@ +/* +Copyright 2022 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package {{ .Env.KIND | strings.ToLower }} + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + + "github.com/crossplane/crossplane-runtime/pkg/reconciler/managed" + "github.com/crossplane/crossplane-runtime/pkg/resource" + "github.com/crossplane/crossplane-runtime/pkg/test" +) + +// Unlike many Kubernetes projects Crossplane does not use third party testing +// libraries, per the common Go test review comments. Crossplane encourages the +// use of table driven unit tests. The tests of the crossplane-runtime project +// are representative of the testing style Crossplane encourages. +// +// https://github.com/golang/go/wiki/TestComments +// https://github.com/crossplane/crossplane/blob/master/CONTRIBUTING.md#contributing-code + +func TestObserve(t *testing.T) { + type fields struct { + service interface{} + } + + type args struct { + ctx context.Context + mg resource.Managed + } + + type want struct { + o managed.ExternalObservation + err error + } + + cases := map[string]struct { + reason string + fields fields + args args + want want + }{ + // TODO: Add test cases. + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + e := external{service: tc.fields.service} + got, err := e.Observe(tc.args.ctx, tc.args.mg) + if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { + t.Errorf("\n%s\ne.Observe(...): -want error, +got error:\n%s\n", tc.reason, diff) + } + if diff := cmp.Diff(tc.want.o, got); diff != "" { + t.Errorf("\n%s\ne.Observe(...): -want, +got:\n%s\n", tc.reason, diff) + } + }) + } +} diff --git a/hack/helpers/prepare.sh b/hack/helpers/prepare.sh new file mode 100755 index 00000000..a6480938 --- /dev/null +++ b/hack/helpers/prepare.sh @@ -0,0 +1,41 @@ +#!/usr/bin/env bash + +# Copyright 2022 The Crossplane Authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Please set ProviderNameLower & ProviderNameUpper environment variables before running this script. +# See: https://github.com/crossplane/terrajet/blob/main/docs/generating-a-provider.md +set -euo pipefail + +ProviderNameUpper=${PROVIDER} +ProviderNameLower=$(echo "${PROVIDER}" | tr "[:upper:]" "[:lower:]") + +git rm -r apis/sample +git rm -r internal/controller/mytype + +REPLACE_FILES='./* ./.github :!build/** :!go.* :!hack/**' +# shellcheck disable=SC2086 +git grep -l 'template' -- ${REPLACE_FILES} | xargs sed -i.bak "s/template/${ProviderNameLower}/g" +# shellcheck disable=SC2086 +git grep -l 'Template' -- ${REPLACE_FILES} | xargs sed -i.bak "s/Template/${ProviderNameUpper}/g" +# We need to be careful while replacing "template" keyword in go.mod as it could tamper +# some imported packages under require section. +sed -i.bak "s/provider-template/provider-${ProviderNameLower}/g" go.mod + +# Clean up the .bak files created by sed +git clean -fd + +git mv "apis/template.go" "apis/${ProviderNameLower}.go" +git mv "internal/controller/template.go" "internal/controller/${ProviderNameLower}.go" +git mv "cluster/images/provider-template" "cluster/images/provider-${ProviderNameLower}" diff --git a/internal/controller/config/config.go b/internal/controller/config/config.go new file mode 100644 index 00000000..7ff60e02 --- /dev/null +++ b/internal/controller/config/config.go @@ -0,0 +1,50 @@ +/* +Copyright 2020 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package config + +import ( + "github.com/crossplane/crossplane-runtime/pkg/controller" + "github.com/crossplane/crossplane-runtime/pkg/event" + "github.com/crossplane/crossplane-runtime/pkg/ratelimiter" + "github.com/crossplane/crossplane-runtime/pkg/reconciler/providerconfig" + "github.com/crossplane/crossplane-runtime/pkg/resource" + ctrl "sigs.k8s.io/controller-runtime" + + "github.com/crossplane/provider-template/apis/v1alpha1" +) + +// 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(v1alpha1.ProviderConfigGroupKind) + + of := resource.ProviderConfigKinds{ + Config: v1alpha1.ProviderConfigGroupVersionKind, + UsageList: v1alpha1.ProviderConfigUsageListGroupVersionKind, + } + + r := providerconfig.NewReconciler(mgr, of, + providerconfig.WithLogger(o.Logger.WithValues("controller", name)), + providerconfig.WithRecorder(event.NewAPIRecorder(mgr.GetEventRecorderFor(name)))) + + return ctrl.NewControllerManagedBy(mgr). + Named(name). + WithOptions(o.ForControllerRuntime()). + For(&v1alpha1.ProviderConfig{}). + Watches(&v1alpha1.ProviderConfigUsage{}, &resource.EnqueueRequestForProviderConfig{}). + Complete(ratelimiter.NewReconciler(name, r, o.GlobalRateLimiter)) +} diff --git a/internal/controller/doc.go b/internal/controller/doc.go new file mode 100644 index 00000000..3682235f --- /dev/null +++ b/internal/controller/doc.go @@ -0,0 +1,17 @@ +/* +Copyright 2020 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller diff --git a/internal/controller/mytype/mytype.go b/internal/controller/mytype/mytype.go new file mode 100644 index 00000000..58be0642 --- /dev/null +++ b/internal/controller/mytype/mytype.go @@ -0,0 +1,205 @@ +/* +Copyright 2020 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package mytype + +import ( + "context" + "fmt" + + "github.com/pkg/errors" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/crossplane/crossplane-runtime/pkg/connection" + "github.com/crossplane/crossplane-runtime/pkg/controller" + "github.com/crossplane/crossplane-runtime/pkg/event" + "github.com/crossplane/crossplane-runtime/pkg/ratelimiter" + "github.com/crossplane/crossplane-runtime/pkg/reconciler/managed" + "github.com/crossplane/crossplane-runtime/pkg/resource" + + "github.com/crossplane/provider-template/apis/sample/v1alpha1" + apisv1alpha1 "github.com/crossplane/provider-template/apis/v1alpha1" + "github.com/crossplane/provider-template/internal/features" +) + +const ( + errNotMyType = "managed resource is not a MyType custom resource" + errTrackPCUsage = "cannot track ProviderConfig usage" + errGetPC = "cannot get ProviderConfig" + errGetCreds = "cannot get credentials" + + errNewClient = "cannot create new Service" +) + +// A NoOpService does nothing. +type NoOpService struct{} + +var ( + newNoOpService = func(_ []byte) (interface{}, error) { return &NoOpService{}, nil } +) + +// Setup adds a controller that reconciles MyType managed resources. +func Setup(mgr ctrl.Manager, o controller.Options) error { + name := managed.ControllerName(v1alpha1.MyTypeGroupKind) + + cps := []managed.ConnectionPublisher{managed.NewAPISecretPublisher(mgr.GetClient(), mgr.GetScheme())} + if o.Features.Enabled(features.EnableAlphaExternalSecretStores) { + cps = append(cps, connection.NewDetailsManager(mgr.GetClient(), apisv1alpha1.StoreConfigGroupVersionKind)) + } + + opts := []managed.ReconcilerOption{ + managed.WithExternalConnecter(&connector{ + kube: mgr.GetClient(), + usage: resource.NewProviderConfigUsageTracker(mgr.GetClient(), &apisv1alpha1.ProviderConfigUsage{}), + newServiceFn: newNoOpService}), + managed.WithLogger(o.Logger.WithValues("controller", name)), + managed.WithPollInterval(o.PollInterval), + managed.WithRecorder(event.NewAPIRecorder(mgr.GetEventRecorderFor(name))), + managed.WithConnectionPublishers(cps...), + } + + if o.Features.Enabled(features.EnableAlphaManagementPolicies) { + opts = append(opts, managed.WithManagementPolicies()) + } + + r := managed.NewReconciler(mgr, resource.ManagedKind(v1alpha1.MyTypeGroupVersionKind), opts...) + + return ctrl.NewControllerManagedBy(mgr). + Named(name). + WithOptions(o.ForControllerRuntime()). + WithEventFilter(resource.DesiredStateChanged()). + For(&v1alpha1.MyType{}). + Complete(ratelimiter.NewReconciler(name, r, o.GlobalRateLimiter)) +} + +// A connector is expected to produce an ExternalClient when its Connect method +// is called. +type connector struct { + kube client.Client + usage resource.Tracker + newServiceFn func(creds []byte) (interface{}, error) +} + +// Connect typically produces an ExternalClient by: +// 1. Tracking that the managed resource is using a ProviderConfig. +// 2. Getting the managed resource's ProviderConfig. +// 3. Getting the credentials specified by the ProviderConfig. +// 4. Using the credentials to form a client. +func (c *connector) Connect(ctx context.Context, mg resource.Managed) (managed.ExternalClient, error) { + cr, ok := mg.(*v1alpha1.MyType) + if !ok { + return nil, errors.New(errNotMyType) + } + + if err := c.usage.Track(ctx, mg); err != nil { + return nil, errors.Wrap(err, errTrackPCUsage) + } + + pc := &apisv1alpha1.ProviderConfig{} + if err := c.kube.Get(ctx, types.NamespacedName{Name: cr.GetProviderConfigReference().Name}, pc); err != nil { + return nil, errors.Wrap(err, errGetPC) + } + + cd := pc.Spec.Credentials + data, err := resource.CommonCredentialExtractor(ctx, cd.Source, c.kube, cd.CommonCredentialSelectors) + if err != nil { + return nil, errors.Wrap(err, errGetCreds) + } + + svc, err := c.newServiceFn(data) + if err != nil { + return nil, errors.Wrap(err, errNewClient) + } + + return &external{service: svc}, nil +} + +// An ExternalClient observes, then either creates, updates, or deletes an +// external resource to ensure it reflects the managed resource's desired state. +type external struct { + // A 'client' used to connect to the external resource API. In practice this + // would be something like an AWS SDK client. + service interface{} +} + +func (c *external) Observe(ctx context.Context, mg resource.Managed) (managed.ExternalObservation, error) { + cr, ok := mg.(*v1alpha1.MyType) + if !ok { + return managed.ExternalObservation{}, errors.New(errNotMyType) + } + + // These fmt statements should be removed in the real implementation. + fmt.Printf("Observing: %+v", cr) + + return managed.ExternalObservation{ + // Return false when the external resource does not exist. This lets + // the managed resource reconciler know that it needs to call Create to + // (re)create the resource, or that it has successfully been deleted. + ResourceExists: true, + + // Return false when the external resource exists, but it not up to date + // with the desired managed resource state. This lets the managed + // resource reconciler know that it needs to call Update. + ResourceUpToDate: true, + + // Return any details that may be required to connect to the external + // resource. These will be stored as the connection secret. + ConnectionDetails: managed.ConnectionDetails{}, + }, nil +} + +func (c *external) Create(ctx context.Context, mg resource.Managed) (managed.ExternalCreation, error) { + cr, ok := mg.(*v1alpha1.MyType) + if !ok { + return managed.ExternalCreation{}, errors.New(errNotMyType) + } + + fmt.Printf("Creating: %+v", cr) + + return managed.ExternalCreation{ + // Optionally return any details that may be required to connect to the + // external resource. These will be stored as the connection secret. + ConnectionDetails: managed.ConnectionDetails{}, + }, nil +} + +func (c *external) Update(ctx context.Context, mg resource.Managed) (managed.ExternalUpdate, error) { + cr, ok := mg.(*v1alpha1.MyType) + if !ok { + return managed.ExternalUpdate{}, errors.New(errNotMyType) + } + + fmt.Printf("Updating: %+v", cr) + + return managed.ExternalUpdate{ + // Optionally return any details that may be required to connect to the + // external resource. These will be stored as the connection secret. + ConnectionDetails: managed.ConnectionDetails{}, + }, nil +} + +func (c *external) Delete(ctx context.Context, mg resource.Managed) error { + cr, ok := mg.(*v1alpha1.MyType) + if !ok { + return errors.New(errNotMyType) + } + + fmt.Printf("Deleting: %+v", cr) + + return nil +} diff --git a/internal/controller/mytype/mytype_test.go b/internal/controller/mytype/mytype_test.go new file mode 100644 index 00000000..a49acc9f --- /dev/null +++ b/internal/controller/mytype/mytype_test.go @@ -0,0 +1,75 @@ +/* +Copyright 2020 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package mytype + +import ( + "context" + "testing" + + "github.com/google/go-cmp/cmp" + + "github.com/crossplane/crossplane-runtime/pkg/reconciler/managed" + "github.com/crossplane/crossplane-runtime/pkg/resource" + "github.com/crossplane/crossplane-runtime/pkg/test" +) + +// Unlike many Kubernetes projects Crossplane does not use third party testing +// libraries, per the common Go test review comments. Crossplane encourages the +// use of table driven unit tests. The tests of the crossplane-runtime project +// are representative of the testing style Crossplane encourages. +// +// https://github.com/golang/go/wiki/TestComments +// https://github.com/crossplane/crossplane/blob/master/CONTRIBUTING.md#contributing-code + +func TestObserve(t *testing.T) { + type fields struct { + service interface{} + } + + type args struct { + ctx context.Context + mg resource.Managed + } + + type want struct { + o managed.ExternalObservation + err error + } + + cases := map[string]struct { + reason string + fields fields + args args + want want + }{ + // TODO: Add test cases. + } + + for name, tc := range cases { + tc := tc + t.Run(name, func(t *testing.T) { + e := external{service: tc.fields.service} + got, err := e.Observe(tc.args.ctx, tc.args.mg) + if diff := cmp.Diff(tc.want.err, err, test.EquateErrors()); diff != "" { + t.Errorf("\n%s\ne.Observe(...): -want error, +got error:\n%s\n", tc.reason, diff) + } + if diff := cmp.Diff(tc.want.o, got); diff != "" { + t.Errorf("\n%s\ne.Observe(...): -want, +got:\n%s\n", tc.reason, diff) + } + }) + } +} diff --git a/internal/controller/template.go b/internal/controller/template.go new file mode 100644 index 00000000..e4bc5186 --- /dev/null +++ b/internal/controller/template.go @@ -0,0 +1,39 @@ +/* +Copyright 2020 The Crossplane Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package controller + +import ( + "github.com/crossplane/crossplane-runtime/pkg/controller" + ctrl "sigs.k8s.io/controller-runtime" + + "github.com/crossplane/provider-template/internal/controller/config" + "github.com/crossplane/provider-template/internal/controller/mytype" +) + +// Setup creates all Template 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{ + config.Setup, + mytype.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 00000000..9e48846e --- /dev/null +++ b/internal/features/features.go @@ -0,0 +1,32 @@ +/* + Copyright 2022 The Crossplane Authors. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ + +package features + +import "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 feature.Flag = "EnableAlphaExternalSecretStores" + + // EnableAlphaManagementPolicies enables alpha support for + // Management Policies. See the below design for more details. + // https://github.com/crossplane/crossplane/blob/master/design/design-doc-observe-only-resources.md + EnableAlphaManagementPolicies feature.Flag = "EnableAlphaManagementPolicies" +) diff --git a/package/crds/sample.template.crossplane.io_mytypes.yaml b/package/crds/sample.template.crossplane.io_mytypes.yaml new file mode 100644 index 00000000..34106763 --- /dev/null +++ b/package/crds/sample.template.crossplane.io_mytypes.yaml @@ -0,0 +1,321 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: mytypes.sample.template.crossplane.io +spec: + group: sample.template.crossplane.io + names: + categories: + - crossplane + - managed + - template + kind: MyType + listKind: MyTypeList + plural: mytypes + singular: mytype + 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: A MyType is an example API type. + 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 MyTypeSpec defines the desired state of a MyType. + 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: + description: MyTypeParameters are the configurable fields of a MyType. + properties: + configurableField: + type: string + required: + - configurableField + 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: A MyTypeStatus represents the observed state of a MyType. + properties: + atProvider: + description: MyTypeObservation are the observable fields of a MyType. + properties: + configurableField: + type: string + observableField: + type: string + required: + - configurableField + 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 + observedGeneration: + description: |- + ObservedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + type: integer + 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 + observedGeneration: + description: |- + ObservedGeneration is the latest metadata.generation + which resulted in either a ready state, or stalled due to error + it can not recover from without human intervention. + format: int64 + type: integer + type: object + required: + - spec + type: object + served: true + storage: true + subresources: + status: {} diff --git a/package/crds/template.crossplane.io_providerconfigs.yaml b/package/crds/template.crossplane.io_providerconfigs.yaml new file mode 100644 index 00000000..c176358f --- /dev/null +++ b/package/crds/template.crossplane.io_providerconfigs.yaml @@ -0,0 +1,169 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: providerconfigs.template.crossplane.io +spec: + group: template.crossplane.io + names: + 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: v1alpha1 + 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 + observedGeneration: + description: |- + ObservedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + type: integer + 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.crossplane.io_providerconfigusages.yaml b/package/crds/template.crossplane.io_providerconfigusages.yaml new file mode 100644 index 00000000..b1b7b3d4 --- /dev/null +++ b/package/crds/template.crossplane.io_providerconfigusages.yaml @@ -0,0 +1,117 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: providerconfigusages.template.crossplane.io +spec: + group: template.crossplane.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: v1alpha1 + 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.crossplane.io_storeconfigs.yaml b/package/crds/template.crossplane.io_storeconfigs.yaml new file mode 100644 index 00000000..f2dd54b4 --- /dev/null +++ b/package/crds/template.crossplane.io_storeconfigs.yaml @@ -0,0 +1,223 @@ +--- +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + annotations: + controller-gen.kubebuilder.io/version: v0.14.0 + name: storeconfigs.template.crossplane.io +spec: + group: template.crossplane.io + names: + categories: + - crossplane + - store + - gcp + 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 GCP 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 + observedGeneration: + description: |- + ObservedGeneration represents the .metadata.generation that the condition was set based upon. + For instance, if .metadata.generation is currently 12, but the .status.conditions[x].observedGeneration is 9, the condition is out of date + with respect to the current state of the instance. + format: int64 + type: integer + 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 00000000..e14f9f78 --- /dev/null +++ b/package/crossplane.yaml @@ -0,0 +1,10 @@ +apiVersion: meta.pkg.crossplane.io/v1alpha1 +kind: Provider +metadata: + name: provider-template + annotations: + meta.crossplane.io/maintainer: Crossplane Maintainers + meta.crossplane.io/source: github.com/crossplane/provider-template + meta.crossplane.io/license: Apache-2.0 + meta.crossplane.io/description: | + A template that can be used to create Crossplane providers. diff --git a/renovate.json b/renovate.json new file mode 100644 index 00000000..5db72dd6 --- /dev/null +++ b/renovate.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": [ + "config:recommended" + ] +}