├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── PULL_REQUEST_TEMPLATE.md └── workflows │ ├── backport.yml │ └── ci.yml ├── .gitignore ├── .golangci.yml ├── Dockerfile ├── LICENSE ├── NOTES.txt ├── README.md ├── example ├── README.md ├── composition-regex.yaml ├── composition.yaml ├── functions.yaml ├── observed-regex.yaml ├── observed.yaml └── xr.yaml ├── fn.go ├── fn_test.go ├── go.mod ├── go.sum ├── init.sh ├── input ├── generate.go └── v1beta1 │ ├── input.go │ └── zz_generated.deepcopy.go ├── main.go ├── package ├── crossplane.yaml └── input │ └── sequencer.fn.crossplane.io_inputs.yaml └── renovate.json /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug Report 3 | about: Help us diagnose and fix bugs in this Function 4 | labels: bug 5 | --- 6 | 13 | 14 | ### What happened? 15 | 19 | 20 | 21 | ### How can we reproduce it? 22 | 27 | 28 | ### What environment did it happen in? 29 | Function version: 30 | 31 | 41 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature Request 3 | about: Help us make this Function more useful 4 | labels: enhancement 5 | --- 6 | 13 | 14 | ### What problem are you facing? 15 | 20 | 21 | ### How could this Function help solve your problem? 22 | 25 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 8 | 9 | ### Description of your changes 10 | 11 | 21 | 22 | Fixes # 23 | 24 | I have: 25 | 26 | - [ ] Read and followed Crossplane's [contribution process]. 27 | - [ ] Added or updated unit tests for my change. 28 | 29 | [contribution process]: https://git.io/fj2m9 30 | [docs]: https://docs.crossplane.io/contribute/contribute 31 | -------------------------------------------------------------------------------- /.github/workflows/backport.yml: -------------------------------------------------------------------------------- 1 | name: Backport 2 | 3 | on: 4 | # NOTE(negz): This is a risky target, but we run this action only when and if 5 | # a PR is closed, then filter down to specifically merged PRs. We also don't 6 | # invoke any scripts, etc from within the repo. I believe the fact that we'll 7 | # be able to review PRs before this runs makes this fairly safe. 8 | # https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ 9 | pull_request_target: 10 | types: [closed] 11 | # See also commands.yml for the /backport triggered variant of this workflow. 12 | 13 | permissions: 14 | contents: read 15 | 16 | jobs: 17 | open-pr: 18 | permissions: 19 | contents: write # for zeebe-io/backport-action to create branch 20 | pull-requests: write # for zeebe-io/backport-action to create PR to backport 21 | runs-on: ubuntu-22.04 22 | if: github.event.pull_request.merged 23 | steps: 24 | - name: Checkout 25 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4 26 | with: 27 | fetch-depth: 0 28 | 29 | - name: Open Backport PR 30 | uses: zeebe-io/backport-action@ef20d86abccbac3ee3a73cb2efbdc06344c390e5 # v2.5.0 31 | with: 32 | github_token: ${{ secrets.GITHUB_TOKEN }} 33 | github_workspace: ${{ github.workspace }} 34 | version: v0.0.4 35 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | - release-* 8 | pull_request: {} 9 | workflow_dispatch: 10 | inputs: 11 | version: 12 | description: Package version (e.g. v0.1.0) 13 | required: false 14 | 15 | env: 16 | # Common versions 17 | GO_VERSION: '1.21.3' 18 | GOLANGCI_VERSION: 'v1.54.2' 19 | DOCKER_BUILDX_VERSION: 'v0.11.2' 20 | 21 | # These environment variables are important to the Crossplane CLI install.sh 22 | # script. They determine what version it installs. 23 | XP_CHANNEL: master # TODO(negz): Pin to stable once v1.14 is released. 24 | XP_VERSION: current # TODO(negz): Pin to a version once v1.14 is released. 25 | 26 | # This CI job will automatically push new builds to xpkg.upbound.io if the 27 | # XPKG_ACCESS_ID and XPKG_TOKEN secrets are set in the GitHub respository (or 28 | # organization) settings. Create a token at https://accounts.upbound.io. 29 | XPKG_ACCESS_ID: ${{ secrets.XPKG_ACCESS_ID }} 30 | 31 | # The package to push, without a version tag. The default matches GitHub. For 32 | # example xpkg.upbound.io/crossplane/function-template-go. 33 | XPKG: xpkg.upbound.io/${{ github.repository}} 34 | 35 | # The package version to push. The default is 0.0.0-gitsha. 36 | XPKG_VERSION: ${{ inputs.version }} 37 | 38 | jobs: 39 | lint: 40 | runs-on: ubuntu-24.04 41 | steps: 42 | - name: Checkout 43 | uses: actions/checkout@v4 44 | 45 | - name: Setup Go 46 | uses: actions/setup-go@v5 47 | with: 48 | go-version: ${{ env.GO_VERSION }} 49 | cache: false # The golangci-lint action does its own caching. 50 | 51 | - name: Lint 52 | uses: golangci/golangci-lint-action@v6 53 | with: 54 | version: ${{ env.GOLANGCI_VERSION }} 55 | 56 | unit-test: 57 | runs-on: ubuntu-24.04 58 | steps: 59 | - name: Checkout 60 | uses: actions/checkout@v4 61 | 62 | - name: Setup Go 63 | uses: actions/setup-go@v5 64 | with: 65 | go-version: ${{ env.GO_VERSION }} 66 | 67 | - name: Run Unit Tests 68 | run: go test -v -cover ./... 69 | 70 | # We want to build most packages for the amd64 and arm64 architectures. To 71 | # speed this up we build single-platform packages in parallel. We then upload 72 | # those packages to GitHub as a build artifact. The push job downloads those 73 | # artifacts and pushes them as a single multi-platform package. 74 | build: 75 | runs-on: ubuntu-24.04 76 | strategy: 77 | fail-fast: true 78 | matrix: 79 | arch: 80 | - amd64 81 | - arm64 82 | steps: 83 | - name: Setup QEMU 84 | uses: docker/setup-qemu-action@v3 85 | with: 86 | platforms: all 87 | 88 | - name: Setup Docker Buildx 89 | uses: docker/setup-buildx-action@v3 90 | with: 91 | version: ${{ env.DOCKER_BUILDX_VERSION }} 92 | install: true 93 | 94 | - name: Checkout 95 | uses: actions/checkout@v4 96 | 97 | # We ask Docker to use GitHub Action's native caching support to speed up 98 | # the build, per https://docs.docker.com/build/cache/backends/gha/. 99 | - name: Build Runtime 100 | id: image 101 | uses: docker/build-push-action@v6 102 | with: 103 | context: . 104 | platforms: linux/${{ matrix.arch }} 105 | cache-from: type=gha 106 | cache-to: type=gha,mode=max 107 | target: image 108 | build-args: 109 | GO_VERSION=${{ env.GO_VERSION }} 110 | outputs: type=docker,dest=runtime-${{ matrix.arch }}.tar 111 | 112 | - name: Setup the Crossplane CLI 113 | run: "curl -sL https://raw.githubusercontent.com/crossplane/crossplane/master/install.sh | sh" 114 | 115 | - name: Build Package 116 | run: ./crossplane xpkg build --package-file=${{ matrix.arch }}.xpkg --package-root=package/ --embed-runtime-image-tarball=runtime-${{ matrix.arch }}.tar 117 | 118 | - name: Upload Single-Platform Package 119 | uses: actions/upload-artifact@v4 120 | with: 121 | name: package-${{ matrix.arch }} 122 | path: "*.xpkg" 123 | if-no-files-found: error 124 | retention-days: 1 125 | 126 | # This job downloads the single-platform packages built by the build job, and 127 | # pushes them as a multi-platform package. We only push the package it the 128 | # XPKG_ACCESS_ID and XPKG_TOKEN secrets were provided. 129 | push: 130 | runs-on: ubuntu-24.04 131 | needs: 132 | - build 133 | steps: 134 | - name: Checkout 135 | uses: actions/checkout@v4 136 | 137 | - name: Download Single-Platform Packages 138 | uses: actions/download-artifact@v4 139 | with: 140 | path: . 141 | merge-multiple: true 142 | 143 | - name: Setup the Crossplane CLI 144 | run: "curl -sL https://raw.githubusercontent.com/crossplane/crossplane/master/install.sh | sh" 145 | 146 | - name: Login to Upbound 147 | uses: docker/login-action@v3 148 | if: env.XPKG_ACCESS_ID != '' 149 | with: 150 | registry: xpkg.upbound.io 151 | username: ${{ secrets.XPKG_ACCESS_ID }} 152 | password: ${{ secrets.XPKG_TOKEN }} 153 | 154 | # If a version wasn't explicitly passed as a workflow_dispatch input we 155 | # default to version v0.0.0--, for example 156 | # v0.0.0-20231101115142-1091066df799. This is a simple implementation of 157 | # Go's pseudo-versions: https://go.dev/ref/mod#pseudo-versions. 158 | - name: Set Default Multi-Platform Package Version 159 | if: env.XPKG_VERSION == '' 160 | run: echo "XPKG_VERSION=v0.0.0-$(date -d@$(git show -s --format=%ct) +%Y%m%d%H%M%S)-$(git rev-parse --short=12 HEAD)" >> $GITHUB_ENV 161 | 162 | - name: Push Multi-Platform Package to Upbound 163 | if: env.XPKG_ACCESS_ID != '' 164 | run: "./crossplane --verbose xpkg push --package-files $(echo *.xpkg|tr ' ' ,) ${{ env.XPKG }}:${{ env.XPKG_VERSION }}" 165 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # If you prefer the allow list template instead of the deny list, see community template: 2 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore 3 | # 4 | # Binaries for programs and plugins 5 | *.exe 6 | *.exe~ 7 | *.dll 8 | *.so 9 | *.dylib 10 | 11 | # Test binary, built with `go test -c` 12 | *.test 13 | 14 | # Output of the go coverage tool, specifically when used with LiteIDE 15 | *.out 16 | 17 | # Dependency directories (remove the comment below to include it) 18 | # vendor/ 19 | 20 | # Go workspace file 21 | go.work 22 | -------------------------------------------------------------------------------- /.golangci.yml: -------------------------------------------------------------------------------- 1 | run: 2 | timeout: 10m 3 | 4 | skip-files: 5 | - "zz_generated\\..+\\.go$" 6 | 7 | output: 8 | # colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number" 9 | format: colored-line-number 10 | 11 | linters-settings: 12 | errcheck: 13 | # report about not checking of errors in type assetions: `a := b.(MyStruct)`; 14 | # default is false: such cases aren't reported by default. 15 | check-type-assertions: false 16 | 17 | # report about assignment of errors to blank identifier: `num, _ := strconv.Atoi(numStr)`; 18 | # default is false: such cases aren't reported by default. 19 | check-blank: false 20 | 21 | # [deprecated] comma-separated list of pairs of the form pkg:regex 22 | # the regex is used to ignore names within pkg. (default "fmt:.*"). 23 | # see https://github.com/kisielk/errcheck#the-deprecated-method for details 24 | ignore: fmt:.*,io/ioutil:^Read.* 25 | 26 | govet: 27 | # report about shadowed variables 28 | check-shadowing: false 29 | 30 | gofmt: 31 | # simplify code: gofmt with `-s` option, true by default 32 | simplify: true 33 | 34 | gci: 35 | custom-order: true 36 | sections: 37 | - standard 38 | - default 39 | - prefix(github.com/crossplane) 40 | - prefix(github.com/crossplane-contrib) 41 | - blank 42 | - dot 43 | 44 | gocyclo: 45 | # minimal code complexity to report, 30 by default (but we recommend 10-20) 46 | min-complexity: 10 47 | 48 | maligned: 49 | # print struct with more effective memory layout or not, false by default 50 | suggest-new: true 51 | 52 | dupl: 53 | # tokens count to trigger issue, 150 by default 54 | threshold: 100 55 | 56 | goconst: 57 | # minimal length of string constant, 3 by default 58 | min-len: 3 59 | # minimal occurrences count to trigger, 3 by default 60 | min-occurrences: 5 61 | 62 | lll: 63 | # tab width in spaces. Default to 1. 64 | tab-width: 1 65 | 66 | unparam: 67 | # Inspect exported functions, default is false. Set to true if no external program/library imports your code. 68 | # XXX: if you enable this setting, unparam will report a lot of false-positives in text editors: 69 | # if it's called for subdir of a project it can't find external interfaces. All text editor integrations 70 | # with golangci-lint call it on a directory with the changed file. 71 | check-exported: false 72 | 73 | nakedret: 74 | # make an issue if func has more lines of code than this setting and it has naked returns; default is 30 75 | max-func-lines: 30 76 | 77 | prealloc: 78 | # XXX: we don't recommend using this linter before doing performance profiling. 79 | # For most programs usage of prealloc will be a premature optimization. 80 | 81 | # Report preallocation suggestions only on simple loops that have no returns/breaks/continues/gotos in them. 82 | # True by default. 83 | simple: true 84 | range-loops: true # Report preallocation suggestions on range loops, true by default 85 | for-loops: false # Report preallocation suggestions on for loops, false by default 86 | 87 | gocritic: 88 | # Enable multiple checks by tags, run `GL_DEBUG=gocritic golangci-lint` run to see all tags and checks. 89 | # Empty list by default. See https://github.com/go-critic/go-critic#usage -> section "Tags". 90 | enabled-tags: 91 | - performance 92 | 93 | settings: # settings passed to gocritic 94 | captLocal: # must be valid enabled check name 95 | paramsOnly: true 96 | rangeValCopy: 97 | sizeThreshold: 32 98 | 99 | nolintlint: 100 | require-explanation: true 101 | require-specific: true 102 | 103 | 104 | linters: 105 | enable: 106 | - megacheck 107 | - govet 108 | - gocyclo 109 | - gocritic 110 | - goconst 111 | - gci 112 | - gofmt # We enable this as well as goimports for its simplify mode. 113 | - prealloc 114 | - revive 115 | - unconvert 116 | - misspell 117 | - nakedret 118 | - nolintlint 119 | 120 | disable: 121 | # These linters are all deprecated as of golangci-lint v1.49.0. We disable 122 | # them explicitly to avoid the linter logging deprecation warnings. 123 | - deadcode 124 | - varcheck 125 | - scopelint 126 | - structcheck 127 | - interfacer 128 | 129 | presets: 130 | - bugs 131 | - unused 132 | fast: false 133 | 134 | 135 | issues: 136 | # Excluding configuration per-path and per-linter 137 | exclude-rules: 138 | # Exclude some linters from running on tests files. 139 | - path: _test(ing)?\.go 140 | linters: 141 | - gocyclo 142 | - errcheck 143 | - dupl 144 | - gosec 145 | - scopelint 146 | - unparam 147 | 148 | # Ease some gocritic warnings on test files. 149 | - path: _test\.go 150 | text: "(unnamedResult|exitAfterDefer)" 151 | linters: 152 | - gocritic 153 | 154 | # These are performance optimisations rather than style issues per se. 155 | # They warn when function arguments or range values copy a lot of memory 156 | # rather than using a pointer. 157 | - text: "(hugeParam|rangeValCopy):" 158 | linters: 159 | - gocritic 160 | 161 | # This "TestMain should call os.Exit to set exit code" warning is not clever 162 | # enough to notice that we call a helper method that calls os.Exit. 163 | - text: "SA3000:" 164 | linters: 165 | - staticcheck 166 | 167 | - text: "k8s.io/api/core/v1" 168 | linters: 169 | - goimports 170 | 171 | # This is a "potential hardcoded credentials" warning. It's triggered by 172 | # any variable with 'secret' in the same, and thus hits a lot of false 173 | # positives in Kubernetes land where a Secret is an object type. 174 | - text: "G101:" 175 | linters: 176 | - gosec 177 | - gas 178 | 179 | # This is an 'errors unhandled' warning that duplicates errcheck. 180 | - text: "G104:" 181 | linters: 182 | - gosec 183 | - gas 184 | 185 | # Some k8s dependencies do not have JSON tags on all fields in structs. 186 | - path: k8s.io/ 187 | linters: 188 | - musttag 189 | 190 | # Independently from option `exclude` we use default exclude patterns, 191 | # it can be disabled by this option. To list all 192 | # excluded by default patterns execute `golangci-lint run --help`. 193 | # Default value for this option is true. 194 | exclude-use-default: false 195 | 196 | # Show only new issues: if there are unstaged changes or untracked files, 197 | # only those changes are analyzed, else only changes in HEAD~ are analyzed. 198 | # It's a super-useful option for integration of golangci-lint into existing 199 | # large codebase. It's not practical to fix all existing issues at the moment 200 | # of integration: much better don't allow issues in new code. 201 | # Default is false. 202 | new: false 203 | 204 | # Maximum issues count per one linter. Set to 0 to disable. Default is 50. 205 | max-per-linter: 0 206 | 207 | # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. 208 | max-same-issues: 0 209 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # syntax=docker/dockerfile:1 2 | 3 | # We use the latest Go 1.x version unless asked to use something else. 4 | # The GitHub Actions CI job sets this argument for a consistent Go version. 5 | ARG GO_VERSION=1 6 | 7 | # Setup the base environment. The BUILDPLATFORM is set automatically by Docker. 8 | # The --platform=${BUILDPLATFORM} flag tells Docker to build the function using 9 | # the OS and architecture of the host running the build, not the OS and 10 | # architecture that we're building the function for. 11 | FROM --platform=${BUILDPLATFORM} golang:${GO_VERSION} AS build 12 | 13 | WORKDIR /fn 14 | 15 | # Most functions don't want or need CGo support, so we disable it. 16 | ENV CGO_ENABLED=0 17 | 18 | # We run go mod download in a separate step so that we can cache its results. 19 | # This lets us avoid re-downloading modules if we don't need to. The type=target 20 | # mount tells Docker to mount the current directory read-only in the WORKDIR. 21 | # The type=cache mount tells Docker to cache the Go modules cache across builds. 22 | RUN --mount=target=. --mount=type=cache,target=/go/pkg/mod go mod download 23 | 24 | # The TARGETOS and TARGETARCH args are set by docker. We set GOOS and GOARCH to 25 | # these values to ask Go to compile a binary for these architectures. If 26 | # TARGETOS and TARGETOS are different from BUILDPLATFORM, Go will cross compile 27 | # for us (e.g. compile a linux/amd64 binary on a linux/arm64 build machine). 28 | ARG TARGETOS 29 | ARG TARGETARCH 30 | 31 | # Build the function binary. The type=target mount tells Docker to mount the 32 | # current directory read-only in the WORKDIR. The type=cache mount tells Docker 33 | # to cache the Go modules cache across builds. 34 | RUN --mount=target=. \ 35 | --mount=type=cache,target=/go/pkg/mod \ 36 | --mount=type=cache,target=/root/.cache/go-build \ 37 | GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o /function . 38 | 39 | # Produce the Function image. We use a very lightweight 'distroless' image that 40 | # does not include any of the build tools used in previous stages. 41 | FROM gcr.io/distroless/base-debian11 AS image 42 | WORKDIR / 43 | COPY --from=build /function /function 44 | EXPOSE 9443 45 | USER nonroot:nonroot 46 | ENTRYPOINT ["/function"] 47 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /NOTES.txt: -------------------------------------------------------------------------------- 1 | To get started: 2 | 3 | 1. Replace `function-template-go` with your function in `go.mod`, 4 | `package/crossplane.yaml`, and any Go imports. (You can also do this 5 | automatically by running the `./init.sh ` script.) 6 | 2. Update `input/v1beta1/` to reflect your desired input (and run `go generate`) 7 | 3. Add your logic to `RunFunction` in `fn.go` 8 | 4. Add tests for your logic in `fn_test.go` 9 | 5. Update `README.md`, to be about your function! -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # function-sequencer 2 | 3 | Function Sequencer is a Crossplane function that enables Composition authors to define sequencing rules delaying the 4 | creation of resources until other resources are ready. 5 | 6 | For example, the pipeline step below, will ensure that `second-resource` and `third-resource` not to be created until 7 | the `first-resource` is ready. 8 | 9 | ```yaml 10 | - step: sequence-creation 11 | functionRef: 12 | name: function-sequencer 13 | input: 14 | apiVersion: sequencer.fn.crossplane.io/v1beta1 15 | kind: Input 16 | rules: 17 | - sequence: 18 | - first-resource 19 | - second-resource 20 | - sequence: 21 | - first-resource 22 | - third-resource 23 | ``` 24 | 25 | See `example/composition.yaml` for a complete example. 26 | 27 | 28 | It also allows you to provide a regex to match and capture a whole group of resources and wait for them to be ready. 29 | 30 | For example, the pipeline step below, will ensure that `second-resource` is not created until all `first-subresource-.*` objects are ready. 31 | 32 | ```yaml 33 | - step: sequence-creation 34 | functionRef: 35 | name: function-sequencer 36 | input: 37 | apiVersion: sequencer.fn.crossplane.io/v1beta1 38 | kind: Input 39 | rules: 40 | - sequence: 41 | - first-subresource-.* 42 | - second-resource 43 | ``` 44 | 45 | You can write the regex as strict as you want, but keep in mind that it defaults to strict matching (start and end are enforced). 46 | In other words, the following rules apply: 47 | ```yaml 48 | - resource # this has no explicit start or end, so it will match EXACTLY ^resource$ (normal behaviour) 49 | - a-group-.* # this has no explicit start or end, so it will match EXACTLY ^a-group-.*$ 50 | - ^a-group # this has an explicit start, so it will match EVERYTHING that starts with a-group 51 | - a-group$ # this has an explicit end, so it will match EVERYTHING that ends with a-group 52 | ``` 53 | 54 | See `example/composition-regex.yaml` for a complete example. 55 | 56 | ## Installation 57 | 58 | It can be installed as follows from the Upbound marketplace: https://marketplace.upbound.io/functions/crossplane-contrib/function-sequencer 59 | 60 | ## Developing this function 61 | 62 | You can use `go run` to run your function locally 63 | ```sh 64 | go run . --insecure --debug 65 | ``` 66 | 67 | After that, you can use [Crossplane CLI's](https://docs.crossplane.io/latest/cli/) `crossplane render` to test what your function is doing. 68 | 69 | To test a sequence: 70 | ```sh 71 | # --observed-resources allows you to simulate already created objects 72 | crossplane render example/xr.yaml example/composition.yaml example/functions.yaml --observed-resources example/observed.yaml 73 | ``` 74 | 75 | To test a regex sequence: 76 | ```sh 77 | # --observed-resources allows you to simulate already created objects 78 | crossplane render example/xr.yaml example/composition-regex.yaml example/functions.yaml --observed-resources example/observed-regex.yaml 79 | ``` 80 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Example manifests 2 | 3 | You can run your function locally and test it using `crossplane beta render` 4 | with these example manifests. 5 | 6 | ```shell 7 | # Run the function locally 8 | $ go run . --insecure --debug 9 | ``` 10 | 11 | ```shell 12 | # Then, in another terminal, call it with these example manifests 13 | $ crossplane beta render xr.yaml composition.yaml functions.yaml -r 14 | --- 15 | apiVersion: example.crossplane.io/v1 16 | kind: XR 17 | metadata: 18 | name: example-xr 19 | --- 20 | apiVersion: render.crossplane.io/v1beta1 21 | kind: Result 22 | message: I was run with input "Hello world"! 23 | severity: SEVERITY_NORMAL 24 | step: run-the-template 25 | ``` 26 | -------------------------------------------------------------------------------- /example/composition-regex.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: function-sequencer-regex 5 | spec: 6 | compositeTypeRef: 7 | apiVersion: example.crossplane.io/v1 8 | kind: XR 9 | mode: Pipeline 10 | pipeline: 11 | - step: patch-and-transform 12 | functionRef: 13 | name: function-patch-and-transform 14 | input: 15 | apiVersion: pt.fn.crossplane.io/v1beta1 16 | kind: Resources 17 | resources: 18 | - name: first-subresource-1 19 | base: 20 | apiVersion: nop.crossplane.io/v1alpha1 21 | kind: NopResource 22 | metadata: 23 | name: first-subresource-1 24 | spec: 25 | forProvider: 26 | conditionAfter: 27 | - time: 5s 28 | conditionType: Ready 29 | conditionStatus: "False" 30 | - time: 10s 31 | conditionType: Ready 32 | conditionStatus: "True" 33 | # We should not delete the dependent resources if this turns back to unready. 34 | - time: 30s 35 | conditionType: Ready 36 | conditionStatus: "False" 37 | - time: 90s 38 | conditionType: Ready 39 | conditionStatus: "True" 40 | - name: first-subresource-2 41 | base: 42 | apiVersion: nop.crossplane.io/v1alpha1 43 | kind: NopResource 44 | metadata: 45 | name: first-subresource-2 46 | spec: 47 | forProvider: 48 | conditionAfter: 49 | - time: 5s 50 | conditionType: Ready 51 | conditionStatus: "False" 52 | - time: 10s 53 | conditionType: Ready 54 | conditionStatus: "True" 55 | - name: second-object 56 | base: 57 | apiVersion: nop.crossplane.io/v1alpha1 58 | kind: NopResource 59 | metadata: 60 | name: second-object 61 | spec: 62 | forProvider: 63 | conditionAfter: 64 | - time: 5s 65 | conditionType: Ready 66 | conditionStatus: "False" 67 | - time: 10s 68 | conditionType: Ready 69 | conditionStatus: "True" 70 | - name: third-resource 71 | base: 72 | apiVersion: nop.crossplane.io/v1alpha1 73 | kind: NopResource 74 | metadata: 75 | name: third-resource 76 | spec: 77 | forProvider: 78 | conditionAfter: 79 | - time: 5s 80 | conditionType: Ready 81 | conditionStatus: "False" 82 | - time: 10s 83 | conditionType: Ready 84 | conditionStatus: "True" 85 | - step: detect-readiness 86 | functionRef: 87 | name: function-auto-ready 88 | - step: sequence-creation 89 | functionRef: 90 | name: function-sequencer 91 | input: 92 | apiVersion: sequencer.fn.crossplane.io/v1beta1 93 | kind: Input 94 | rules: 95 | - sequence: 96 | - first-subresource-.* 97 | - object$ # this will match everything that ends with "object" 98 | - third-resource 99 | -------------------------------------------------------------------------------- /example/composition.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: apiextensions.crossplane.io/v1 2 | kind: Composition 3 | metadata: 4 | name: function-sequencer 5 | spec: 6 | compositeTypeRef: 7 | apiVersion: example.crossplane.io/v1 8 | kind: XR 9 | mode: Pipeline 10 | pipeline: 11 | - step: patch-and-transform 12 | functionRef: 13 | name: function-patch-and-transform 14 | input: 15 | apiVersion: pt.fn.crossplane.io/v1beta1 16 | kind: Resources 17 | resources: 18 | - name: first-resource 19 | base: 20 | apiVersion: nop.crossplane.io/v1alpha1 21 | kind: NopResource 22 | spec: 23 | forProvider: 24 | conditionAfter: 25 | - time: 5s 26 | conditionType: Ready 27 | conditionStatus: "False" 28 | - time: 10s 29 | conditionType: Ready 30 | conditionStatus: "True" 31 | # We should not delete the dependent resources if this turns back to unready. 32 | - time: 30s 33 | conditionType: Ready 34 | conditionStatus: "False" 35 | - time: 90s 36 | conditionType: Ready 37 | conditionStatus: "True" 38 | - name: second-resource 39 | base: 40 | apiVersion: nop.crossplane.io/v1alpha1 41 | kind: NopResource 42 | spec: 43 | forProvider: 44 | conditionAfter: 45 | - time: 5s 46 | conditionType: Ready 47 | conditionStatus: "False" 48 | - time: 10s 49 | conditionType: Ready 50 | conditionStatus: "True" 51 | - name: third-resource 52 | base: 53 | apiVersion: nop.crossplane.io/v1alpha1 54 | kind: NopResource 55 | spec: 56 | forProvider: 57 | conditionAfter: 58 | - time: 5s 59 | conditionType: Ready 60 | conditionStatus: "False" 61 | - time: 10s 62 | conditionType: Ready 63 | conditionStatus: "True" 64 | - step: detect-readiness 65 | functionRef: 66 | name: function-auto-ready 67 | - step: sequence-creation 68 | functionRef: 69 | name: function-sequencer 70 | input: 71 | apiVersion: sequencer.fn.crossplane.io/v1beta1 72 | kind: Input 73 | rules: 74 | - sequence: 75 | - first-resource 76 | - second-resource 77 | - sequence: 78 | - first-resource 79 | - third-resource 80 | -------------------------------------------------------------------------------- /example/functions.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: pkg.crossplane.io/v1beta1 3 | kind: Function 4 | metadata: 5 | name: function-sequencer 6 | annotations: 7 | # This tells crossplane beta render to connect to the function locally. 8 | render.crossplane.io/runtime: Development 9 | spec: 10 | # This is ignored when using the Development runtime. 11 | package: function-sequencer 12 | --- 13 | apiVersion: pkg.crossplane.io/v1beta1 14 | kind: Function 15 | metadata: 16 | name: function-patch-and-transform 17 | spec: 18 | package: xpkg.upbound.io/crossplane-contrib/function-patch-and-transform:v0.7.0 19 | --- 20 | apiVersion: pkg.crossplane.io/v1beta1 21 | kind: Function 22 | metadata: 23 | name: function-auto-ready 24 | spec: 25 | package: xpkg.upbound.io/crossplane-contrib/function-auto-ready:v0.3.0 -------------------------------------------------------------------------------- /example/observed-regex.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: nop.crossplane.io/v1alpha1 2 | kind: NopResource 3 | metadata: 4 | annotations: 5 | crossplane.io/external-name: foo 6 | crossplane.io/composition-resource-name: first-subresource-2 7 | name: first-subresource-2 8 | spec: 9 | forProvider: 10 | conditionAfter: 11 | - conditionStatus: "True" 12 | conditionType: Ready 13 | time: 1s 14 | - conditionStatus: "False" 15 | conditionType: Ready 16 | time: 0s 17 | status: 18 | atProvider: {} 19 | conditions: 20 | - lastTransitionTime: "2024-02-17T11:56:27Z" 21 | reason: ReconcileSuccess 22 | status: "True" 23 | type: Synced 24 | - lastTransitionTime: "2024-02-17T11:56:28Z" 25 | reason: "" 26 | status: "True" 27 | type: Ready 28 | --- 29 | apiVersion: nop.crossplane.io/v1alpha1 30 | kind: NopResource 31 | metadata: 32 | annotations: 33 | crossplane.io/external-name: foo 34 | crossplane.io/composition-resource-name: first-subresource-1 35 | name: first-subresource-1 36 | spec: 37 | forProvider: 38 | conditionAfter: 39 | - conditionStatus: "True" 40 | conditionType: Ready 41 | time: 1s 42 | - conditionStatus: "False" 43 | conditionType: Ready 44 | time: 0s 45 | status: 46 | atProvider: {} 47 | conditions: 48 | - lastTransitionTime: "2024-02-17T11:56:27Z" 49 | reason: ReconcileSuccess 50 | status: "True" 51 | type: Synced 52 | - lastTransitionTime: "2024-02-17T11:56:28Z" 53 | reason: "" 54 | status: "True" 55 | type: Ready 56 | -------------------------------------------------------------------------------- /example/observed.yaml: -------------------------------------------------------------------------------- 1 | apiVersion: nop.crossplane.io/v1alpha1 2 | kind: NopResource 3 | metadata: 4 | annotations: 5 | crossplane.io/external-name: foo 6 | crossplane.io/composition-resource-name: first-resource 7 | name: first 8 | spec: 9 | forProvider: 10 | conditionAfter: 11 | - conditionStatus: "True" 12 | conditionType: Ready 13 | time: 1s 14 | - conditionStatus: "False" 15 | conditionType: Ready 16 | time: 0s 17 | status: 18 | atProvider: {} 19 | conditions: 20 | - lastTransitionTime: "2024-02-17T11:56:27Z" 21 | reason: ReconcileSuccess 22 | status: "True" 23 | type: Synced 24 | - lastTransitionTime: "2024-02-17T11:56:28Z" 25 | reason: "" 26 | status: "True" 27 | type: Ready 28 | -------------------------------------------------------------------------------- /example/xr.yaml: -------------------------------------------------------------------------------- 1 | # Replace this with your XR! 2 | apiVersion: example.crossplane.io/v1 3 | kind: XR 4 | metadata: 5 | name: example-xr 6 | spec: {} 7 | -------------------------------------------------------------------------------- /fn.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "fmt" 6 | "regexp" 7 | "strings" 8 | 9 | "github.com/crossplane/crossplane-runtime/pkg/errors" 10 | "github.com/crossplane/crossplane-runtime/pkg/logging" 11 | fnv1beta1 "github.com/crossplane/function-sdk-go/proto/v1beta1" 12 | "github.com/crossplane/function-sdk-go/request" 13 | "github.com/crossplane/function-sdk-go/resource" 14 | "github.com/crossplane/function-sdk-go/response" 15 | "github.com/crossplane/function-sequencer/input/v1beta1" 16 | ) 17 | 18 | // Function returns whatever response you ask it to. 19 | type Function struct { 20 | fnv1beta1.UnimplementedFunctionRunnerServiceServer 21 | 22 | log logging.Logger 23 | } 24 | 25 | const ( 26 | // START marks the start of a regex pattern. 27 | START = "^" 28 | // END marks the end of a regex pattern. 29 | END = "$" 30 | ) 31 | 32 | // RunFunction runs the Function. 33 | func (f *Function) RunFunction(_ context.Context, req *fnv1beta1.RunFunctionRequest) (*fnv1beta1.RunFunctionResponse, error) { //nolint:gocyclo // This function is unavoidably complex. 34 | f.log.Info("Running function", "tag", req.GetMeta().GetTag()) 35 | 36 | rsp := response.To(req, response.DefaultTTL) 37 | 38 | in := &v1beta1.Input{} 39 | if err := request.GetInput(req, in); err != nil { 40 | response.Fatal(rsp, errors.Wrapf(err, "cannot get Function input from %T", req)) 41 | return rsp, nil 42 | } 43 | 44 | // Get the desired composed resources from the request. 45 | desiredComposed, err := request.GetDesiredComposedResources(req) 46 | if err != nil { 47 | response.Fatal(rsp, errors.Wrap(err, "cannot get desired composed resources")) 48 | return rsp, nil 49 | } 50 | 51 | observedComposed, err := request.GetObservedComposedResources(req) 52 | if err != nil { 53 | response.Fatal(rsp, errors.Wrap(err, "cannot get observed composed resources")) 54 | return rsp, nil 55 | } 56 | 57 | sequences := make([][]resource.Name, 0, len(in.Rules)) 58 | for _, rule := range in.Rules { 59 | sequences = append(sequences, rule.Sequence) 60 | } 61 | 62 | for _, sequence := range sequences { 63 | for i, r := range sequence { 64 | if i == 0 { 65 | // We don't need to do anything for the first resource in the sequence. 66 | continue 67 | } 68 | if _, created := observedComposed[r]; created { 69 | // We've already created this resource, so we don't need to do anything. 70 | // We only sequence creation of resources that don't exist yet. 71 | continue 72 | } 73 | for _, before := range sequence[:i] { 74 | beforeRegex, err := getStrictRegex(string(before)) 75 | if err != nil { 76 | response.Fatal(rsp, errors.Wrapf(err, "cannot compile regex %s", before)) 77 | return rsp, nil 78 | } 79 | keys := []resource.Name{} 80 | for k := range desiredComposed { 81 | if beforeRegex.MatchString(string(k)) { 82 | keys = append(keys, k) 83 | } 84 | } 85 | 86 | // We'll treat everything the same way adding all resources to the keys slice 87 | // and then checking if they are ready. 88 | desired := len(keys) 89 | readyResources := 0 90 | for _, k := range keys { 91 | if b, ok := desiredComposed[k]; ok && b.Ready == resource.ReadyTrue { 92 | // resource is ready, add it to the counter 93 | readyResources++ 94 | } 95 | } 96 | 97 | if desired == 0 || desired != readyResources { 98 | // no resource created 99 | msg := fmt.Sprintf("Delaying creation of resource(s) matching %q because %q does not exist yet", r, before) 100 | if desired > 0 { 101 | // provide a nicer message if there are resources. 102 | msg = fmt.Sprintf( 103 | "Delaying creation of resource(s) matching %q because %q is not fully ready (%d of %d)", 104 | r, 105 | before, 106 | readyResources, 107 | desired, 108 | ) 109 | } 110 | response.Normal(rsp, msg) 111 | f.log.Info(msg) 112 | // find all objects that match the regex and delete them from the desiredComposed map 113 | currentRegex, _ := getStrictRegex(string(r)) 114 | for k := range desiredComposed { 115 | if currentRegex.MatchString(string(k)) { 116 | if _, ok := observedComposed[k]; ok { 117 | // if the resource is already part of the observedComposed, we should not delete it 118 | continue 119 | } 120 | delete(desiredComposed, k) 121 | } 122 | } 123 | break 124 | } 125 | } 126 | } 127 | } 128 | 129 | rsp.Desired.Resources = nil 130 | return rsp, response.SetDesiredComposedResources(rsp, desiredComposed) 131 | } 132 | 133 | func getStrictRegex(pattern string) (*regexp.Regexp, error) { 134 | if !strings.HasPrefix(pattern, START) && !strings.HasSuffix(pattern, END) { 135 | // if the user provides a delimited regex, we'll use it as is 136 | // if not, add the regex with ^ & $ to match the entire string 137 | // possibly avoid using regex for matching literal strings 138 | pattern = fmt.Sprintf("%s%s%s", START, pattern, END) 139 | } 140 | return regexp.Compile(pattern) 141 | } 142 | -------------------------------------------------------------------------------- /fn_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "context" 5 | "testing" 6 | 7 | "github.com/google/go-cmp/cmp" 8 | "github.com/google/go-cmp/cmp/cmpopts" 9 | "google.golang.org/protobuf/testing/protocmp" 10 | "google.golang.org/protobuf/types/known/durationpb" 11 | 12 | "github.com/crossplane/crossplane-runtime/pkg/logging" 13 | fnv1beta1 "github.com/crossplane/function-sdk-go/proto/v1beta1" 14 | "github.com/crossplane/function-sdk-go/resource" 15 | "github.com/crossplane/function-sdk-go/response" 16 | "github.com/crossplane/function-sequencer/input/v1beta1" 17 | ) 18 | 19 | func TestRunFunction(t *testing.T) { 20 | 21 | var ( 22 | xr = `{"apiVersion":"example.org/v1","kind":"XR","metadata":{"name":"cool-xr"},"spec":{"count":2}}` 23 | mr = `{"apiVersion":"example.org/v1","kind":"MR","metadata":{"name":"cool-mr"}}` 24 | ) 25 | 26 | type args struct { 27 | ctx context.Context 28 | req *fnv1beta1.RunFunctionRequest 29 | } 30 | type want struct { 31 | rsp *fnv1beta1.RunFunctionResponse 32 | err error 33 | } 34 | 35 | cases := map[string]struct { 36 | reason string 37 | args args 38 | want want 39 | }{ 40 | "ObservedAreSkipped": { 41 | reason: "Even though that second is skipped because it's in the observed state, delay third resource because first is not ready", 42 | args: args{ 43 | req: &fnv1beta1.RunFunctionRequest{ 44 | Input: resource.MustStructObject(&v1beta1.Input{ 45 | Rules: []v1beta1.SequencingRule{ 46 | { 47 | Sequence: []resource.Name{ 48 | "first", 49 | "second", 50 | "third", 51 | }, 52 | }, 53 | }, 54 | }), 55 | Observed: &fnv1beta1.State{ 56 | Composite: &fnv1beta1.Resource{ 57 | Resource: resource.MustStructJSON(xr), 58 | }, 59 | Resources: map[string]*fnv1beta1.Resource{ 60 | "first": { 61 | Resource: resource.MustStructJSON(mr), 62 | }, 63 | "second": { 64 | Resource: resource.MustStructJSON(mr), 65 | }, 66 | }, 67 | }, 68 | Desired: &fnv1beta1.State{ 69 | Composite: &fnv1beta1.Resource{ 70 | Resource: resource.MustStructJSON(xr), 71 | }, 72 | Resources: map[string]*fnv1beta1.Resource{ 73 | "first": { 74 | Resource: resource.MustStructJSON(mr), 75 | }, 76 | "second": { 77 | Resource: resource.MustStructJSON(mr), 78 | }, 79 | "third": { 80 | Resource: resource.MustStructJSON(mr), 81 | }, 82 | }, 83 | }, 84 | }, 85 | }, 86 | want: want{ 87 | rsp: &fnv1beta1.RunFunctionResponse{ 88 | Meta: &fnv1beta1.ResponseMeta{Ttl: durationpb.New(response.DefaultTTL)}, 89 | Results: []*fnv1beta1.Result{ 90 | { 91 | Severity: fnv1beta1.Severity_SEVERITY_NORMAL, 92 | Message: "Delaying creation of resource(s) matching \"third\" because \"first\" is not fully ready (0 of 1)", 93 | }, 94 | }, 95 | Desired: &fnv1beta1.State{ 96 | Composite: &fnv1beta1.Resource{ 97 | Resource: resource.MustStructJSON(xr), 98 | }, 99 | Resources: map[string]*fnv1beta1.Resource{ 100 | "first": { 101 | Resource: resource.MustStructJSON(mr), 102 | }, 103 | "second": { 104 | Resource: resource.MustStructJSON(mr), 105 | }, 106 | }, 107 | }, 108 | }, 109 | }, 110 | }, 111 | "FirstNotCreated": { 112 | reason: "The function should delay the creation of the second resource because the first not created", 113 | args: args{ 114 | req: &fnv1beta1.RunFunctionRequest{ 115 | Input: resource.MustStructObject(&v1beta1.Input{ 116 | Rules: []v1beta1.SequencingRule{ 117 | { 118 | Sequence: []resource.Name{ 119 | "first", 120 | "second", 121 | }, 122 | }, 123 | }, 124 | }), 125 | Observed: &fnv1beta1.State{ 126 | Composite: &fnv1beta1.Resource{ 127 | Resource: resource.MustStructJSON(xr), 128 | }, 129 | Resources: map[string]*fnv1beta1.Resource{}, 130 | }, 131 | Desired: &fnv1beta1.State{ 132 | Composite: &fnv1beta1.Resource{ 133 | Resource: resource.MustStructJSON(xr), 134 | }, 135 | Resources: map[string]*fnv1beta1.Resource{}, 136 | }, 137 | }, 138 | }, 139 | want: want{ 140 | rsp: &fnv1beta1.RunFunctionResponse{ 141 | Meta: &fnv1beta1.ResponseMeta{Ttl: durationpb.New(response.DefaultTTL)}, 142 | Results: []*fnv1beta1.Result{ 143 | { 144 | Severity: fnv1beta1.Severity_SEVERITY_NORMAL, 145 | Message: "Delaying creation of resource(s) matching \"second\" because \"first\" does not exist yet", 146 | }, 147 | }, 148 | Desired: &fnv1beta1.State{ 149 | Composite: &fnv1beta1.Resource{ 150 | Resource: resource.MustStructJSON(xr), 151 | }, 152 | Resources: map[string]*fnv1beta1.Resource{}, 153 | }, 154 | }, 155 | }, 156 | }, 157 | "FirstNotReady": { 158 | reason: "The function should delay the creation of the second resource because the first is not ready", 159 | args: args{ 160 | req: &fnv1beta1.RunFunctionRequest{ 161 | Input: resource.MustStructObject(&v1beta1.Input{ 162 | Rules: []v1beta1.SequencingRule{ 163 | { 164 | Sequence: []resource.Name{ 165 | "first", 166 | "second", 167 | }, 168 | }, 169 | }, 170 | }), 171 | Observed: &fnv1beta1.State{ 172 | Composite: &fnv1beta1.Resource{ 173 | Resource: resource.MustStructJSON(xr), 174 | }, 175 | Resources: map[string]*fnv1beta1.Resource{}, 176 | }, 177 | Desired: &fnv1beta1.State{ 178 | Composite: &fnv1beta1.Resource{ 179 | Resource: resource.MustStructJSON(xr), 180 | }, 181 | Resources: map[string]*fnv1beta1.Resource{ 182 | "first": { 183 | Resource: resource.MustStructJSON(mr), 184 | Ready: fnv1beta1.Ready_READY_FALSE, 185 | }, 186 | "second": { 187 | Resource: resource.MustStructJSON(mr), 188 | Ready: fnv1beta1.Ready_READY_FALSE, 189 | }, 190 | }, 191 | }, 192 | }, 193 | }, 194 | want: want{ 195 | rsp: &fnv1beta1.RunFunctionResponse{ 196 | Meta: &fnv1beta1.ResponseMeta{Ttl: durationpb.New(response.DefaultTTL)}, 197 | Results: []*fnv1beta1.Result{ 198 | { 199 | Severity: fnv1beta1.Severity_SEVERITY_NORMAL, 200 | Message: "Delaying creation of resource(s) matching \"second\" because \"first\" is not fully ready (0 of 1)", 201 | }, 202 | }, 203 | Desired: &fnv1beta1.State{ 204 | Composite: &fnv1beta1.Resource{ 205 | Resource: resource.MustStructJSON(xr), 206 | }, 207 | Resources: map[string]*fnv1beta1.Resource{ 208 | "first": { 209 | Resource: resource.MustStructJSON(mr), 210 | Ready: fnv1beta1.Ready_READY_FALSE, 211 | }, 212 | }, 213 | }, 214 | }, 215 | }, 216 | }, 217 | "FirstReady": { 218 | reason: "The function should not delay the creation of the second resource because the first is ready", 219 | args: args{ 220 | req: &fnv1beta1.RunFunctionRequest{ 221 | Input: resource.MustStructObject(&v1beta1.Input{ 222 | Rules: []v1beta1.SequencingRule{ 223 | { 224 | Sequence: []resource.Name{ 225 | "first", 226 | "second", 227 | }, 228 | }, 229 | }, 230 | }), 231 | Observed: &fnv1beta1.State{ 232 | Composite: &fnv1beta1.Resource{ 233 | Resource: resource.MustStructJSON(xr), 234 | }, 235 | Resources: map[string]*fnv1beta1.Resource{}, 236 | }, 237 | Desired: &fnv1beta1.State{ 238 | Composite: &fnv1beta1.Resource{ 239 | Resource: resource.MustStructJSON(xr), 240 | }, 241 | Resources: map[string]*fnv1beta1.Resource{ 242 | "first": { 243 | Resource: resource.MustStructJSON(mr), 244 | Ready: fnv1beta1.Ready_READY_TRUE, 245 | }, 246 | "second": { 247 | Resource: resource.MustStructJSON(mr), 248 | }, 249 | }, 250 | }, 251 | }, 252 | }, 253 | want: want{ 254 | rsp: &fnv1beta1.RunFunctionResponse{ 255 | Meta: &fnv1beta1.ResponseMeta{Ttl: durationpb.New(response.DefaultTTL)}, 256 | Results: []*fnv1beta1.Result{}, 257 | Desired: &fnv1beta1.State{ 258 | Composite: &fnv1beta1.Resource{ 259 | Resource: resource.MustStructJSON(xr), 260 | }, 261 | Resources: map[string]*fnv1beta1.Resource{ 262 | "first": { 263 | Resource: resource.MustStructJSON(mr), 264 | Ready: fnv1beta1.Ready_READY_TRUE, 265 | }, 266 | "second": { 267 | Resource: resource.MustStructJSON(mr), 268 | }, 269 | }, 270 | }, 271 | }, 272 | }, 273 | }, 274 | "BothReady": { 275 | reason: "The function should return both of them", 276 | args: args{ 277 | req: &fnv1beta1.RunFunctionRequest{ 278 | Input: resource.MustStructObject(&v1beta1.Input{ 279 | Rules: []v1beta1.SequencingRule{ 280 | { 281 | Sequence: []resource.Name{ 282 | "first", 283 | "second", 284 | }, 285 | }, 286 | }, 287 | }), 288 | Observed: &fnv1beta1.State{ 289 | Composite: &fnv1beta1.Resource{ 290 | Resource: resource.MustStructJSON(xr), 291 | }, 292 | Resources: map[string]*fnv1beta1.Resource{}, 293 | }, 294 | Desired: &fnv1beta1.State{ 295 | Composite: &fnv1beta1.Resource{ 296 | Resource: resource.MustStructJSON(xr), 297 | }, 298 | Resources: map[string]*fnv1beta1.Resource{ 299 | "first": { 300 | Resource: resource.MustStructJSON(mr), 301 | Ready: fnv1beta1.Ready_READY_TRUE, 302 | }, 303 | "second": { 304 | Resource: resource.MustStructJSON(mr), 305 | Ready: fnv1beta1.Ready_READY_TRUE, 306 | }, 307 | }, 308 | }, 309 | }, 310 | }, 311 | want: want{ 312 | rsp: &fnv1beta1.RunFunctionResponse{ 313 | Meta: &fnv1beta1.ResponseMeta{Ttl: durationpb.New(response.DefaultTTL)}, 314 | Results: []*fnv1beta1.Result{}, 315 | Desired: &fnv1beta1.State{ 316 | Composite: &fnv1beta1.Resource{ 317 | Resource: resource.MustStructJSON(xr), 318 | }, 319 | Resources: map[string]*fnv1beta1.Resource{ 320 | "first": { 321 | Resource: resource.MustStructJSON(mr), 322 | Ready: fnv1beta1.Ready_READY_TRUE, 323 | }, 324 | "second": { 325 | Resource: resource.MustStructJSON(mr), 326 | Ready: fnv1beta1.Ready_READY_TRUE, 327 | }, 328 | }, 329 | }, 330 | }, 331 | }, 332 | }, 333 | "SequencesFirstNotReadyInBoth": { 334 | reason: "The function should delay the creation of second and fourth resources because the first and third are not ready", 335 | args: args{ 336 | req: &fnv1beta1.RunFunctionRequest{ 337 | Input: resource.MustStructObject(&v1beta1.Input{ 338 | Rules: []v1beta1.SequencingRule{ 339 | { 340 | Sequence: []resource.Name{ 341 | "first", 342 | "second", 343 | }, 344 | }, 345 | { 346 | Sequence: []resource.Name{ 347 | "third", 348 | "fourth", 349 | }, 350 | }, 351 | }, 352 | }), 353 | Observed: &fnv1beta1.State{ 354 | Composite: &fnv1beta1.Resource{ 355 | Resource: resource.MustStructJSON(xr), 356 | }, 357 | Resources: map[string]*fnv1beta1.Resource{}, 358 | }, 359 | Desired: &fnv1beta1.State{ 360 | Composite: &fnv1beta1.Resource{ 361 | Resource: resource.MustStructJSON(xr), 362 | }, 363 | Resources: map[string]*fnv1beta1.Resource{ 364 | "first": { 365 | Resource: resource.MustStructJSON(mr), 366 | }, 367 | "second": { 368 | Resource: resource.MustStructJSON(mr), 369 | }, 370 | "third": { 371 | Resource: resource.MustStructJSON(mr), 372 | }, 373 | "fourth": { 374 | Resource: resource.MustStructJSON(mr), 375 | }, 376 | }, 377 | }, 378 | }, 379 | }, 380 | want: want{ 381 | rsp: &fnv1beta1.RunFunctionResponse{ 382 | Meta: &fnv1beta1.ResponseMeta{Ttl: durationpb.New(response.DefaultTTL)}, 383 | Results: []*fnv1beta1.Result{ 384 | { 385 | Severity: fnv1beta1.Severity_SEVERITY_NORMAL, 386 | Message: "Delaying creation of resource(s) matching \"second\" because \"first\" is not fully ready (0 of 1)", 387 | }, 388 | { 389 | Severity: fnv1beta1.Severity_SEVERITY_NORMAL, 390 | Message: "Delaying creation of resource(s) matching \"fourth\" because \"third\" is not fully ready (0 of 1)", 391 | }, 392 | }, 393 | Desired: &fnv1beta1.State{ 394 | Composite: &fnv1beta1.Resource{ 395 | Resource: resource.MustStructJSON(xr), 396 | }, 397 | Resources: map[string]*fnv1beta1.Resource{ 398 | "first": { 399 | Resource: resource.MustStructJSON(mr), 400 | }, 401 | "third": { 402 | Resource: resource.MustStructJSON(mr), 403 | }, 404 | }, 405 | }, 406 | }, 407 | }, 408 | }, 409 | "SequencesFirstReadyInBoth": { 410 | reason: "The function should not delay the creation of any resource", 411 | args: args{ 412 | req: &fnv1beta1.RunFunctionRequest{ 413 | Input: resource.MustStructObject(&v1beta1.Input{ 414 | Rules: []v1beta1.SequencingRule{ 415 | { 416 | Sequence: []resource.Name{ 417 | "first", 418 | "second", 419 | }, 420 | }, 421 | { 422 | Sequence: []resource.Name{ 423 | "third", 424 | "fourth", 425 | }, 426 | }, 427 | }, 428 | }), 429 | Observed: &fnv1beta1.State{ 430 | Composite: &fnv1beta1.Resource{ 431 | Resource: resource.MustStructJSON(xr), 432 | }, 433 | Resources: map[string]*fnv1beta1.Resource{}, 434 | }, 435 | Desired: &fnv1beta1.State{ 436 | Composite: &fnv1beta1.Resource{ 437 | Resource: resource.MustStructJSON(xr), 438 | }, 439 | Resources: map[string]*fnv1beta1.Resource{ 440 | "first": { 441 | Resource: resource.MustStructJSON(mr), 442 | Ready: fnv1beta1.Ready_READY_TRUE, 443 | }, 444 | "second": { 445 | Resource: resource.MustStructJSON(mr), 446 | }, 447 | "third": { 448 | Resource: resource.MustStructJSON(mr), 449 | Ready: fnv1beta1.Ready_READY_TRUE, 450 | }, 451 | "fourth": { 452 | Resource: resource.MustStructJSON(mr), 453 | }, 454 | }, 455 | }, 456 | }, 457 | }, 458 | want: want{ 459 | rsp: &fnv1beta1.RunFunctionResponse{ 460 | Meta: &fnv1beta1.ResponseMeta{Ttl: durationpb.New(response.DefaultTTL)}, 461 | Results: []*fnv1beta1.Result{}, 462 | Desired: &fnv1beta1.State{ 463 | Composite: &fnv1beta1.Resource{ 464 | Resource: resource.MustStructJSON(xr), 465 | }, 466 | Resources: map[string]*fnv1beta1.Resource{ 467 | "first": { 468 | Resource: resource.MustStructJSON(mr), 469 | Ready: fnv1beta1.Ready_READY_TRUE, 470 | }, 471 | "second": { 472 | Resource: resource.MustStructJSON(mr), 473 | }, 474 | "third": { 475 | Resource: resource.MustStructJSON(mr), 476 | Ready: fnv1beta1.Ready_READY_TRUE, 477 | }, 478 | "fourth": { 479 | Resource: resource.MustStructJSON(mr), 480 | }, 481 | }, 482 | }, 483 | }, 484 | }, 485 | }, 486 | "OutOfSequence": { 487 | reason: "The function should delay the creation of second, but allow the creation of the other since its not in a sequence", 488 | args: args{ 489 | req: &fnv1beta1.RunFunctionRequest{ 490 | Input: resource.MustStructObject(&v1beta1.Input{ 491 | Rules: []v1beta1.SequencingRule{ 492 | { 493 | Sequence: []resource.Name{ 494 | "first", 495 | "second", 496 | }, 497 | }, 498 | }, 499 | }), 500 | Observed: &fnv1beta1.State{ 501 | Composite: &fnv1beta1.Resource{ 502 | Resource: resource.MustStructJSON(xr), 503 | }, 504 | Resources: map[string]*fnv1beta1.Resource{}, 505 | }, 506 | Desired: &fnv1beta1.State{ 507 | Composite: &fnv1beta1.Resource{ 508 | Resource: resource.MustStructJSON(xr), 509 | }, 510 | Resources: map[string]*fnv1beta1.Resource{ 511 | "first": { 512 | Resource: resource.MustStructJSON(mr), 513 | Ready: fnv1beta1.Ready_READY_FALSE, 514 | }, 515 | "second": { 516 | Resource: resource.MustStructJSON(mr), 517 | }, 518 | "outofsequence": { 519 | Resource: resource.MustStructJSON(mr), 520 | }, 521 | }, 522 | }, 523 | }, 524 | }, 525 | want: want{ 526 | rsp: &fnv1beta1.RunFunctionResponse{ 527 | Meta: &fnv1beta1.ResponseMeta{Ttl: durationpb.New(response.DefaultTTL)}, 528 | Results: []*fnv1beta1.Result{ 529 | { 530 | Severity: fnv1beta1.Severity_SEVERITY_NORMAL, 531 | Message: "Delaying creation of resource(s) matching \"second\" because \"first\" is not fully ready (0 of 1)", 532 | }, 533 | }, 534 | Desired: &fnv1beta1.State{ 535 | Composite: &fnv1beta1.Resource{ 536 | Resource: resource.MustStructJSON(xr), 537 | }, 538 | Resources: map[string]*fnv1beta1.Resource{ 539 | "first": { 540 | Resource: resource.MustStructJSON(mr), 541 | Ready: fnv1beta1.Ready_READY_FALSE, 542 | }, 543 | "outofsequence": { 544 | Resource: resource.MustStructJSON(mr), 545 | }, 546 | }, 547 | }, 548 | }, 549 | }, 550 | }, 551 | "SequenceRegexNotAllReady": { 552 | reason: "The function should delay the creation of second and fourth resources because the first and third are not ready", 553 | args: args{ 554 | req: &fnv1beta1.RunFunctionRequest{ 555 | Input: resource.MustStructObject(&v1beta1.Input{ 556 | Rules: []v1beta1.SequencingRule{ 557 | { 558 | Sequence: []resource.Name{ 559 | "first-.*", 560 | "second", 561 | }, 562 | }, 563 | }, 564 | }), 565 | Observed: &fnv1beta1.State{ 566 | Composite: &fnv1beta1.Resource{ 567 | Resource: resource.MustStructJSON(xr), 568 | }, 569 | Resources: map[string]*fnv1beta1.Resource{}, 570 | }, 571 | Desired: &fnv1beta1.State{ 572 | Composite: &fnv1beta1.Resource{ 573 | Resource: resource.MustStructJSON(xr), 574 | }, 575 | Resources: map[string]*fnv1beta1.Resource{ 576 | "first-0": { 577 | Resource: resource.MustStructJSON(mr), 578 | Ready: fnv1beta1.Ready_READY_TRUE, 579 | }, 580 | "first-1": { 581 | Resource: resource.MustStructJSON(mr), 582 | Ready: fnv1beta1.Ready_READY_TRUE, 583 | }, 584 | "first-2": { 585 | Resource: resource.MustStructJSON(mr), 586 | }, 587 | "second": { 588 | Resource: resource.MustStructJSON(mr), 589 | }, 590 | }, 591 | }, 592 | }, 593 | }, 594 | want: want{ 595 | rsp: &fnv1beta1.RunFunctionResponse{ 596 | Meta: &fnv1beta1.ResponseMeta{Ttl: durationpb.New(response.DefaultTTL)}, 597 | Results: []*fnv1beta1.Result{ 598 | { 599 | Severity: fnv1beta1.Severity_SEVERITY_NORMAL, 600 | Message: "Delaying creation of resource(s) matching \"second\" because \"first-.*\" is not fully ready (2 of 3)", 601 | }, 602 | }, 603 | Desired: &fnv1beta1.State{ 604 | Composite: &fnv1beta1.Resource{ 605 | Resource: resource.MustStructJSON(xr), 606 | }, 607 | Resources: map[string]*fnv1beta1.Resource{ 608 | "first-0": { 609 | Resource: resource.MustStructJSON(mr), 610 | Ready: fnv1beta1.Ready_READY_TRUE, 611 | }, 612 | "first-1": { 613 | Resource: resource.MustStructJSON(mr), 614 | Ready: fnv1beta1.Ready_READY_TRUE, 615 | }, 616 | "first-2": { 617 | Resource: resource.MustStructJSON(mr), 618 | }, 619 | }, 620 | }, 621 | }, 622 | }, 623 | }, 624 | "SequenceRegexObservedAreSkipped": { 625 | reason: "The function should not attempt to remove resources from the desired state when they are already in the observed state", 626 | args: args{ 627 | req: &fnv1beta1.RunFunctionRequest{ 628 | Input: resource.MustStructObject(&v1beta1.Input{ 629 | Rules: []v1beta1.SequencingRule{ 630 | { 631 | Sequence: []resource.Name{ 632 | "first-.*", 633 | "second-.*", 634 | }, 635 | }, 636 | }, 637 | }), 638 | Observed: &fnv1beta1.State{ 639 | Composite: &fnv1beta1.Resource{ 640 | Resource: resource.MustStructJSON(xr), 641 | }, 642 | Resources: map[string]*fnv1beta1.Resource{ 643 | "first-1": { 644 | Resource: resource.MustStructJSON(mr), 645 | Ready: fnv1beta1.Ready_READY_TRUE, 646 | }, 647 | "second-1": { 648 | Resource: resource.MustStructJSON(mr), 649 | Ready: fnv1beta1.Ready_READY_TRUE, 650 | }, 651 | }, 652 | }, 653 | Desired: &fnv1beta1.State{ 654 | Composite: &fnv1beta1.Resource{ 655 | Resource: resource.MustStructJSON(xr), 656 | }, 657 | Resources: map[string]*fnv1beta1.Resource{ 658 | "first-1": { 659 | Resource: resource.MustStructJSON(mr), 660 | Ready: fnv1beta1.Ready_READY_TRUE, 661 | }, 662 | "first-2": { 663 | Resource: resource.MustStructJSON(mr), 664 | }, 665 | "second-1": { 666 | Resource: resource.MustStructJSON(mr), 667 | Ready: fnv1beta1.Ready_READY_TRUE, 668 | }, 669 | }, 670 | }, 671 | }, 672 | }, 673 | want: want{ 674 | rsp: &fnv1beta1.RunFunctionResponse{ 675 | Meta: &fnv1beta1.ResponseMeta{Ttl: durationpb.New(response.DefaultTTL)}, 676 | Results: []*fnv1beta1.Result{ 677 | { 678 | Severity: fnv1beta1.Severity_SEVERITY_NORMAL, 679 | Message: "Delaying creation of resource(s) matching \"second-.*\" because \"first-.*\" is not fully ready (1 of 2)", 680 | }, 681 | }, 682 | Desired: &fnv1beta1.State{ 683 | Composite: &fnv1beta1.Resource{ 684 | Resource: resource.MustStructJSON(xr), 685 | }, 686 | Resources: map[string]*fnv1beta1.Resource{ 687 | "first-1": { 688 | Resource: resource.MustStructJSON(mr), 689 | Ready: fnv1beta1.Ready_READY_TRUE, 690 | }, 691 | "first-2": { 692 | Resource: resource.MustStructJSON(mr), 693 | }, 694 | "second-1": { 695 | Resource: resource.MustStructJSON(mr), 696 | Ready: fnv1beta1.Ready_READY_TRUE, 697 | }, 698 | }, 699 | }, 700 | }, 701 | }, 702 | }, 703 | "SequenceRegexFirstGroupReady": { 704 | reason: "The function should delay the creation of second and fourth resources because the first and third are not ready", 705 | args: args{ 706 | req: &fnv1beta1.RunFunctionRequest{ 707 | Input: resource.MustStructObject(&v1beta1.Input{ 708 | Rules: []v1beta1.SequencingRule{ 709 | { 710 | Sequence: []resource.Name{ 711 | "first-.*", 712 | "second-.*", 713 | "third", 714 | }, 715 | }, 716 | }, 717 | }), 718 | Observed: &fnv1beta1.State{ 719 | Composite: &fnv1beta1.Resource{ 720 | Resource: resource.MustStructJSON(xr), 721 | }, 722 | Resources: map[string]*fnv1beta1.Resource{}, 723 | }, 724 | Desired: &fnv1beta1.State{ 725 | Composite: &fnv1beta1.Resource{ 726 | Resource: resource.MustStructJSON(xr), 727 | }, 728 | Resources: map[string]*fnv1beta1.Resource{ 729 | "first-0": { 730 | Resource: resource.MustStructJSON(mr), 731 | Ready: fnv1beta1.Ready_READY_TRUE, 732 | }, 733 | "first-1": { 734 | Resource: resource.MustStructJSON(mr), 735 | Ready: fnv1beta1.Ready_READY_TRUE, 736 | }, 737 | "first-2": { 738 | Resource: resource.MustStructJSON(mr), 739 | Ready: fnv1beta1.Ready_READY_TRUE, 740 | }, 741 | "second-0": { 742 | Resource: resource.MustStructJSON(mr), 743 | Ready: fnv1beta1.Ready_READY_TRUE, 744 | }, 745 | "second-1": { 746 | Resource: resource.MustStructJSON(mr), 747 | }, 748 | "third": { 749 | Resource: resource.MustStructJSON(mr), 750 | }, 751 | }, 752 | }, 753 | }, 754 | }, 755 | want: want{ 756 | rsp: &fnv1beta1.RunFunctionResponse{ 757 | Meta: &fnv1beta1.ResponseMeta{Ttl: durationpb.New(response.DefaultTTL)}, 758 | Results: []*fnv1beta1.Result{ 759 | { 760 | Severity: fnv1beta1.Severity_SEVERITY_NORMAL, 761 | Message: "Delaying creation of resource(s) matching \"third\" because \"second-.*\" is not fully ready (1 of 2)", 762 | }, 763 | }, 764 | Desired: &fnv1beta1.State{ 765 | Composite: &fnv1beta1.Resource{ 766 | Resource: resource.MustStructJSON(xr), 767 | }, 768 | Resources: map[string]*fnv1beta1.Resource{ 769 | "first-0": { 770 | Resource: resource.MustStructJSON(mr), 771 | Ready: fnv1beta1.Ready_READY_TRUE, 772 | }, 773 | "first-1": { 774 | Resource: resource.MustStructJSON(mr), 775 | Ready: fnv1beta1.Ready_READY_TRUE, 776 | }, 777 | "first-2": { 778 | Resource: resource.MustStructJSON(mr), 779 | Ready: fnv1beta1.Ready_READY_TRUE, 780 | }, 781 | "second-0": { 782 | Resource: resource.MustStructJSON(mr), 783 | Ready: fnv1beta1.Ready_READY_TRUE, 784 | }, 785 | "second-1": { 786 | Resource: resource.MustStructJSON(mr), 787 | }, 788 | }, 789 | }, 790 | }, 791 | }, 792 | }, 793 | "MixedRegex": { 794 | reason: "The function should delay the creation of second and fourth resources because the first and third are not ready", 795 | args: args{ 796 | req: &fnv1beta1.RunFunctionRequest{ 797 | Input: resource.MustStructObject(&v1beta1.Input{ 798 | Rules: []v1beta1.SequencingRule{ 799 | { 800 | Sequence: []resource.Name{ 801 | "first", 802 | "second-.*", 803 | "third", 804 | }, 805 | }, 806 | }, 807 | }), 808 | Observed: &fnv1beta1.State{ 809 | Composite: &fnv1beta1.Resource{ 810 | Resource: resource.MustStructJSON(xr), 811 | }, 812 | Resources: map[string]*fnv1beta1.Resource{}, 813 | }, 814 | Desired: &fnv1beta1.State{ 815 | Composite: &fnv1beta1.Resource{ 816 | Resource: resource.MustStructJSON(xr), 817 | }, 818 | Resources: map[string]*fnv1beta1.Resource{ 819 | "first": { 820 | Resource: resource.MustStructJSON(mr), 821 | Ready: fnv1beta1.Ready_READY_TRUE, 822 | }, 823 | "second-0": { 824 | Resource: resource.MustStructJSON(mr), 825 | Ready: fnv1beta1.Ready_READY_TRUE, 826 | }, 827 | "second-1": { 828 | Resource: resource.MustStructJSON(mr), 829 | }, 830 | "third": { 831 | Resource: resource.MustStructJSON(mr), 832 | }, 833 | }, 834 | }, 835 | }, 836 | }, 837 | want: want{ 838 | rsp: &fnv1beta1.RunFunctionResponse{ 839 | Meta: &fnv1beta1.ResponseMeta{Ttl: durationpb.New(response.DefaultTTL)}, 840 | Results: []*fnv1beta1.Result{ 841 | { 842 | Severity: fnv1beta1.Severity_SEVERITY_NORMAL, 843 | Message: "Delaying creation of resource(s) matching \"third\" because \"second-.*\" is not fully ready (1 of 2)", 844 | }, 845 | }, 846 | Desired: &fnv1beta1.State{ 847 | Composite: &fnv1beta1.Resource{ 848 | Resource: resource.MustStructJSON(xr), 849 | }, 850 | Resources: map[string]*fnv1beta1.Resource{ 851 | "first": { 852 | Resource: resource.MustStructJSON(mr), 853 | Ready: fnv1beta1.Ready_READY_TRUE, 854 | }, 855 | "second-0": { 856 | Resource: resource.MustStructJSON(mr), 857 | Ready: fnv1beta1.Ready_READY_TRUE, 858 | }, 859 | "second-1": { 860 | Resource: resource.MustStructJSON(mr), 861 | }, 862 | }, 863 | }, 864 | }, 865 | }, 866 | }, 867 | "SequenceRegexWaitComplex": { 868 | reason: "The function should not modify the sequence regex, since it's already prefixed", 869 | args: args{ 870 | req: &fnv1beta1.RunFunctionRequest{ 871 | Input: resource.MustStructObject(&v1beta1.Input{ 872 | Rules: []v1beta1.SequencingRule{ 873 | { 874 | Sequence: []resource.Name{ 875 | "first-.*", 876 | "second$", 877 | "third-resource", 878 | }, 879 | }, 880 | }, 881 | }), 882 | Observed: &fnv1beta1.State{ 883 | Composite: &fnv1beta1.Resource{ 884 | Resource: resource.MustStructJSON(xr), 885 | }, 886 | Resources: map[string]*fnv1beta1.Resource{}, 887 | }, 888 | Desired: &fnv1beta1.State{ 889 | Composite: &fnv1beta1.Resource{ 890 | Resource: resource.MustStructJSON(xr), 891 | }, 892 | Resources: map[string]*fnv1beta1.Resource{ 893 | "first-0": { 894 | Resource: resource.MustStructJSON(mr), 895 | Ready: fnv1beta1.Ready_READY_TRUE, 896 | }, 897 | "first-1": { 898 | Resource: resource.MustStructJSON(mr), 899 | Ready: fnv1beta1.Ready_READY_FALSE, 900 | }, 901 | "0-second": { 902 | Resource: resource.MustStructJSON(mr), 903 | }, 904 | "1-second": { 905 | Resource: resource.MustStructJSON(mr), 906 | }, 907 | "third-resource": { 908 | Resource: resource.MustStructJSON(mr), 909 | }, 910 | }, 911 | }, 912 | }, 913 | }, 914 | want: want{ 915 | rsp: &fnv1beta1.RunFunctionResponse{ 916 | Meta: &fnv1beta1.ResponseMeta{Ttl: durationpb.New(response.DefaultTTL)}, 917 | Results: []*fnv1beta1.Result{ 918 | { 919 | Severity: fnv1beta1.Severity_SEVERITY_NORMAL, 920 | Message: "Delaying creation of resource(s) matching \"second$\" because \"first-.*\" is not fully ready (1 of 2)", 921 | }, 922 | { 923 | Severity: fnv1beta1.Severity_SEVERITY_NORMAL, 924 | Message: "Delaying creation of resource(s) matching \"third-resource\" because \"first-.*\" is not fully ready (1 of 2)", 925 | }, 926 | }, 927 | Desired: &fnv1beta1.State{ 928 | Composite: &fnv1beta1.Resource{ 929 | Resource: resource.MustStructJSON(xr), 930 | }, 931 | Resources: map[string]*fnv1beta1.Resource{ 932 | "first-0": { 933 | Resource: resource.MustStructJSON(mr), 934 | Ready: fnv1beta1.Ready_READY_TRUE, 935 | }, 936 | "first-1": { 937 | Resource: resource.MustStructJSON(mr), 938 | Ready: fnv1beta1.Ready_READY_FALSE, 939 | }, 940 | }, 941 | }, 942 | }, 943 | }, 944 | }, 945 | "SequenceRegexAlreadyPrefixed": { 946 | reason: "The function should not modify the sequence regex, since it's already prefixed", 947 | args: args{ 948 | req: &fnv1beta1.RunFunctionRequest{ 949 | Input: resource.MustStructObject(&v1beta1.Input{ 950 | Rules: []v1beta1.SequencingRule{ 951 | { 952 | Sequence: []resource.Name{ 953 | "^first-.*$", 954 | "^second-.*", 955 | "third-.*$", 956 | "fourth", 957 | }, 958 | }, 959 | }, 960 | }), 961 | Observed: &fnv1beta1.State{ 962 | Composite: &fnv1beta1.Resource{ 963 | Resource: resource.MustStructJSON(xr), 964 | }, 965 | Resources: map[string]*fnv1beta1.Resource{}, 966 | }, 967 | Desired: &fnv1beta1.State{ 968 | Composite: &fnv1beta1.Resource{ 969 | Resource: resource.MustStructJSON(xr), 970 | }, 971 | Resources: map[string]*fnv1beta1.Resource{ 972 | "first-0": { 973 | Resource: resource.MustStructJSON(mr), 974 | Ready: fnv1beta1.Ready_READY_TRUE, 975 | }, 976 | "first-1": { 977 | Resource: resource.MustStructJSON(mr), 978 | Ready: fnv1beta1.Ready_READY_TRUE, 979 | }, 980 | "second-0": { 981 | Resource: resource.MustStructJSON(mr), 982 | Ready: fnv1beta1.Ready_READY_TRUE, 983 | }, 984 | "third-0": { 985 | Resource: resource.MustStructJSON(mr), 986 | }, 987 | }, 988 | }, 989 | }, 990 | }, 991 | want: want{ 992 | rsp: &fnv1beta1.RunFunctionResponse{ 993 | Meta: &fnv1beta1.ResponseMeta{Ttl: durationpb.New(response.DefaultTTL)}, 994 | Results: []*fnv1beta1.Result{ 995 | { 996 | Severity: fnv1beta1.Severity_SEVERITY_NORMAL, 997 | Message: "Delaying creation of resource(s) matching \"fourth\" because \"third-.*$\" is not fully ready (0 of 1)", 998 | }, 999 | }, 1000 | Desired: &fnv1beta1.State{ 1001 | Composite: &fnv1beta1.Resource{ 1002 | Resource: resource.MustStructJSON(xr), 1003 | }, 1004 | Resources: map[string]*fnv1beta1.Resource{ 1005 | "first-0": { 1006 | Resource: resource.MustStructJSON(mr), 1007 | Ready: fnv1beta1.Ready_READY_TRUE, 1008 | }, 1009 | "first-1": { 1010 | Resource: resource.MustStructJSON(mr), 1011 | Ready: fnv1beta1.Ready_READY_TRUE, 1012 | }, 1013 | "second-0": { 1014 | Resource: resource.MustStructJSON(mr), 1015 | Ready: fnv1beta1.Ready_READY_TRUE, 1016 | }, 1017 | "third-0": { 1018 | Resource: resource.MustStructJSON(mr), 1019 | }, 1020 | }, 1021 | }, 1022 | }, 1023 | }, 1024 | }, 1025 | "SequenceRegexInvalidRegex": { 1026 | reason: "The function should return a fatal error because the regex is invalid", 1027 | args: args{ 1028 | req: &fnv1beta1.RunFunctionRequest{ 1029 | Input: resource.MustStructObject(&v1beta1.Input{ 1030 | Rules: []v1beta1.SequencingRule{ 1031 | { 1032 | Sequence: []resource.Name{ 1033 | `^(`, 1034 | "second", 1035 | }, 1036 | }, 1037 | }, 1038 | }), 1039 | Observed: &fnv1beta1.State{ 1040 | Composite: &fnv1beta1.Resource{ 1041 | Resource: resource.MustStructJSON(xr), 1042 | }, 1043 | Resources: map[string]*fnv1beta1.Resource{}, 1044 | }, 1045 | Desired: &fnv1beta1.State{ 1046 | Composite: &fnv1beta1.Resource{ 1047 | Resource: resource.MustStructJSON(xr), 1048 | }, 1049 | Resources: map[string]*fnv1beta1.Resource{ 1050 | "first-0": { 1051 | Resource: resource.MustStructJSON(mr), 1052 | Ready: fnv1beta1.Ready_READY_TRUE, 1053 | }, 1054 | "first-1": { 1055 | Resource: resource.MustStructJSON(mr), 1056 | Ready: fnv1beta1.Ready_READY_TRUE, 1057 | }, 1058 | "second": { 1059 | Resource: resource.MustStructJSON(mr), 1060 | Ready: fnv1beta1.Ready_READY_TRUE, 1061 | }, 1062 | }, 1063 | }, 1064 | }, 1065 | }, 1066 | want: want{ 1067 | rsp: &fnv1beta1.RunFunctionResponse{ 1068 | Meta: &fnv1beta1.ResponseMeta{Ttl: durationpb.New(response.DefaultTTL)}, 1069 | Results: []*fnv1beta1.Result{ 1070 | { 1071 | Severity: fnv1beta1.Severity_SEVERITY_FATAL, 1072 | Message: "cannot compile regex ^(: error parsing regexp: missing closing ): `^(`", 1073 | }, 1074 | }, 1075 | Desired: &fnv1beta1.State{ 1076 | Composite: &fnv1beta1.Resource{ 1077 | Resource: resource.MustStructJSON(xr), 1078 | }, 1079 | Resources: map[string]*fnv1beta1.Resource{ 1080 | "first-0": { 1081 | Resource: resource.MustStructJSON(mr), 1082 | Ready: fnv1beta1.Ready_READY_TRUE, 1083 | }, 1084 | "first-1": { 1085 | Resource: resource.MustStructJSON(mr), 1086 | Ready: fnv1beta1.Ready_READY_TRUE, 1087 | }, 1088 | "second": { 1089 | Resource: resource.MustStructJSON(mr), 1090 | Ready: fnv1beta1.Ready_READY_TRUE, 1091 | }, 1092 | }, 1093 | }, 1094 | }, 1095 | }, 1096 | }, 1097 | } 1098 | 1099 | for name, tc := range cases { 1100 | t.Run(name, func(t *testing.T) { 1101 | f := &Function{log: logging.NewNopLogger()} 1102 | rsp, err := f.RunFunction(tc.args.ctx, tc.args.req) 1103 | 1104 | if diff := cmp.Diff(tc.want.rsp, rsp, protocmp.Transform()); diff != "" { 1105 | t.Errorf("%s\nf.RunFunction(...): -want rsp, +got rsp:\n%s", tc.reason, diff) 1106 | } 1107 | 1108 | if diff := cmp.Diff(tc.want.err, err, cmpopts.EquateErrors()); diff != "" { 1109 | t.Errorf("%s\nf.RunFunction(...): -want err, +got err:\n%s", tc.reason, diff) 1110 | } 1111 | }) 1112 | } 1113 | } 1114 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/crossplane/function-sequencer 2 | 3 | go 1.21 4 | 5 | toolchain go1.21.3 6 | 7 | require ( 8 | github.com/alecthomas/kong v0.9.0 9 | github.com/crossplane/crossplane-runtime v1.14.4 10 | github.com/crossplane/function-sdk-go v0.1.0 11 | github.com/google/go-cmp v0.6.0 12 | google.golang.org/protobuf v1.32.0 13 | k8s.io/apimachinery v0.29.1 14 | sigs.k8s.io/controller-tools v0.13.0 15 | ) 16 | 17 | require ( 18 | dario.cat/mergo v1.0.0 // indirect 19 | github.com/davecgh/go-spew v1.1.1 // indirect 20 | github.com/emicklei/go-restful/v3 v3.11.0 // indirect 21 | github.com/evanphx/json-patch/v5 v5.6.0 // indirect 22 | github.com/fatih/color v1.15.0 // indirect 23 | github.com/go-json-experiment/json v0.0.0-20231013223334-54c864be5b8d // indirect 24 | github.com/go-logr/logr v1.3.0 // indirect 25 | github.com/go-logr/zapr v1.2.4 // indirect 26 | github.com/go-openapi/jsonpointer v0.19.6 // indirect 27 | github.com/go-openapi/jsonreference v0.20.2 // indirect 28 | github.com/go-openapi/swag v0.22.3 // indirect 29 | github.com/gobuffalo/flect v1.0.2 // indirect 30 | github.com/gogo/protobuf v1.3.2 // indirect 31 | github.com/golang/protobuf v1.5.3 // indirect 32 | github.com/google/gnostic-models v0.6.8 // indirect 33 | github.com/google/gofuzz v1.2.0 // indirect 34 | github.com/google/uuid v1.3.1 // indirect 35 | github.com/inconshreveable/mousetrap v1.1.0 // indirect 36 | github.com/josharian/intern v1.0.0 // indirect 37 | github.com/json-iterator/go v1.1.12 // indirect 38 | github.com/mailru/easyjson v0.7.7 // indirect 39 | github.com/mattn/go-colorable v0.1.13 // indirect 40 | github.com/mattn/go-isatty v0.0.17 // indirect 41 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect 42 | github.com/modern-go/reflect2 v1.0.2 // indirect 43 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect 44 | github.com/pkg/errors v0.9.1 // indirect 45 | github.com/spf13/afero v1.10.0 // indirect 46 | github.com/spf13/cobra v1.7.0 // indirect 47 | github.com/spf13/pflag v1.0.5 // indirect 48 | go.uber.org/multierr v1.11.0 // indirect 49 | go.uber.org/zap v1.26.0 // indirect 50 | golang.org/x/mod v0.14.0 // indirect 51 | golang.org/x/net v0.19.0 // indirect 52 | golang.org/x/oauth2 v0.11.0 // indirect 53 | golang.org/x/sys v0.15.0 // indirect 54 | golang.org/x/term v0.15.0 // indirect 55 | golang.org/x/text v0.14.0 // indirect 56 | golang.org/x/time v0.3.0 // indirect 57 | golang.org/x/tools v0.16.1 // indirect 58 | google.golang.org/appengine v1.6.7 // indirect 59 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d // indirect 60 | google.golang.org/grpc v1.59.0 // indirect 61 | gopkg.in/inf.v0 v0.9.1 // indirect 62 | gopkg.in/yaml.v2 v2.4.0 // indirect 63 | gopkg.in/yaml.v3 v3.0.1 // indirect 64 | k8s.io/api v0.28.3 // indirect 65 | k8s.io/apiextensions-apiserver v0.28.3 // indirect 66 | k8s.io/client-go v0.28.3 // indirect 67 | k8s.io/klog/v2 v2.110.1 // indirect 68 | k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 // indirect 69 | k8s.io/utils v0.0.0-20230726121419-3b25d923346b // indirect 70 | sigs.k8s.io/controller-runtime v0.16.3 // indirect 71 | sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect 72 | sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect 73 | sigs.k8s.io/yaml v1.4.0 // indirect 74 | ) 75 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 2 | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= 3 | cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= 4 | cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= 5 | cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 6 | cloud.google.com/go v0.44.3/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= 7 | cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= 8 | cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= 9 | cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= 10 | cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= 11 | cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= 12 | cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= 13 | cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= 14 | cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= 15 | cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= 16 | cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= 17 | cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= 18 | cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= 19 | cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= 20 | cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= 21 | cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= 22 | cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= 23 | cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= 24 | cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= 25 | cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= 26 | cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= 27 | cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= 28 | cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= 29 | cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= 30 | cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= 31 | cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= 32 | cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= 33 | cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= 34 | cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= 35 | cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= 36 | cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= 37 | cloud.google.com/go/storage v1.14.0/go.mod h1:GrKmX003DSIwi9o29oFT7YDnHYwZoctc3fOKtUw0Xmo= 38 | dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= 39 | dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= 40 | dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= 41 | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= 42 | github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= 43 | github.com/agext/levenshtein v1.2.3 h1:YB2fHEn0UJagG8T1rrWknE3ZQzWM06O8AMAatNn7lmo= 44 | github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= 45 | github.com/alecthomas/assert/v2 v2.1.0 h1:tbredtNcQnoSd3QBhQWI7QZ3XHOVkw1Moklp2ojoH/0= 46 | github.com/alecthomas/assert/v2 v2.1.0/go.mod h1:b/+1DI2Q6NckYi+3mXyH3wFb8qG37K/DuK80n7WefXA= 47 | github.com/alecthomas/kong v0.8.1 h1:acZdn3m4lLRobeh3Zi2S2EpnXTd1mOL6U7xVml+vfkY= 48 | github.com/alecthomas/kong v0.8.1/go.mod h1:n1iCIO2xS46oE8ZfYCNDqdR0b0wZNrXAIAqro/2132U= 49 | github.com/alecthomas/kong v0.9.0 h1:G5diXxc85KvoV2f0ZRVuMsi45IrBgx9zDNGNj165aPA= 50 | github.com/alecthomas/kong v0.9.0/go.mod h1:Y47y5gKfHp1hDc7CH7OeXgLIpp+Q2m1Ni0L5s3bI8Os= 51 | github.com/alecthomas/repr v0.1.0 h1:ENn2e1+J3k09gyj2shc0dHr/yjaWSHRlrJ4DPMevDqE= 52 | github.com/alecthomas/repr v0.1.0/go.mod h1:2kn6fqh/zIyPLmm3ugklbEi5hg5wS435eygvNfaDQL8= 53 | github.com/antchfx/htmlquery v1.2.4 h1:qLteofCMe/KGovBI6SQgmou2QNyedFUW+pE+BpeZ494= 54 | github.com/antchfx/htmlquery v1.2.4/go.mod h1:2xO6iu3EVWs7R2JYqBbp8YzG50gj/ofqs5/0VZoDZLc= 55 | github.com/antchfx/xpath v1.2.0 h1:mbwv7co+x0RwgeGAOHdrKy89GvHaGvxxBtPK0uF9Zr8= 56 | github.com/antchfx/xpath v1.2.0/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs= 57 | github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6iT90AvPUL1NNfNw= 58 | github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo= 59 | github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= 60 | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= 61 | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= 62 | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= 63 | github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= 64 | github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= 65 | github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= 66 | github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= 67 | github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= 68 | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= 69 | github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= 70 | github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 71 | github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= 72 | github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= 73 | github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 74 | github.com/crossplane/crossplane-runtime v1.14.4 h1:64zSZ75g1QXIMxR2zSQvz4+TTSq5qCUU5lmpiVovVKE= 75 | github.com/crossplane/crossplane-runtime v1.14.4/go.mod h1:aOP+5W2wKpvthVs3pFNbVOe1jwrKYbJho0ThGNCVz9o= 76 | github.com/crossplane/function-sdk-go v0.1.0 h1:WN3CUSaj6wgDqQPZZP1whMVIkTAZtN3HVCS67pTMzd8= 77 | github.com/crossplane/function-sdk-go v0.1.0/go.mod h1:oRqHZ6RufpfZJ1N8aT4EfkP+5KpkhOKpWz7SeUDwwcw= 78 | github.com/crossplane/upjet v0.11.0-rc.0.0.20231012093706-c4a76d2a7505 h1:eCmYgfRopVn6r8RM1Ra4XQAPwVsjTGfktBj2Dk7yy+Y= 79 | github.com/crossplane/upjet v0.11.0-rc.0.0.20231012093706-c4a76d2a7505/go.mod h1:Ov+eoYS2n0Zge/E50zm65meOTYbAHnU6jPt27fQrpbc= 80 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 81 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= 82 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 83 | github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= 84 | github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= 85 | github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 86 | github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= 87 | github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= 88 | github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= 89 | github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= 90 | github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= 91 | github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= 92 | github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= 93 | github.com/fatih/camelcase v1.0.0 h1:hxNvNX/xYBp0ovncs8WyWZrOrpBNub/JfaMvbURyft8= 94 | github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= 95 | github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs= 96 | github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= 97 | github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= 98 | github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= 99 | github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= 100 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 101 | github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= 102 | github.com/go-json-experiment/json v0.0.0-20231013223334-54c864be5b8d h1:zqfo2jECgX5eYQseB/X+uV4Y5ocGOG/vG/LTztUCyPA= 103 | github.com/go-json-experiment/json v0.0.0-20231013223334-54c864be5b8d/go.mod h1:6daplAwHHGbUGib4990V3Il26O0OC4aRyvewaaAihaA= 104 | github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 105 | github.com/go-logr/logr v1.3.0 h1:2y3SDp0ZXuc6/cjLSZ+Q3ir+QB9T/iG5yYRXqsagWSY= 106 | github.com/go-logr/logr v1.3.0/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 107 | github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo= 108 | github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA= 109 | github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= 110 | github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= 111 | github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= 112 | github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= 113 | github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= 114 | github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= 115 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= 116 | github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= 117 | github.com/gobuffalo/flect v1.0.2 h1:eqjPGSo2WmjgY2XlpGwo2NXgL3RucAKo4k4qQMNA5sA= 118 | github.com/gobuffalo/flect v1.0.2/go.mod h1:A5msMlrHtLqh9umBSnvabjsMrCcCpAyzglnDvkbYKHs= 119 | github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= 120 | github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= 121 | github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= 122 | github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 123 | github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 124 | github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 125 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= 126 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 127 | github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 128 | github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= 129 | github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= 130 | github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 131 | github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 132 | github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= 133 | github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 134 | github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 135 | github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 136 | github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= 137 | github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 138 | github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 139 | github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 140 | github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= 141 | github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= 142 | github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= 143 | github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= 144 | github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= 145 | github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= 146 | github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 147 | github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= 148 | github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= 149 | github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= 150 | github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= 151 | github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 152 | github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= 153 | github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= 154 | github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= 155 | github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= 156 | github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 157 | github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 158 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 159 | github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 160 | github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 161 | github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 162 | github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 163 | github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 164 | github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= 165 | github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 166 | github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= 167 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= 168 | github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 169 | github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= 170 | github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 171 | github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= 172 | github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 173 | github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= 174 | github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 175 | github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= 176 | github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 177 | github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 178 | github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 179 | github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 180 | github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= 181 | github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 182 | github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 183 | github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= 184 | github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98 h1:pUa4ghanp6q4IJHwE9RwLgmVFfReJN+KbQ8ExNEUUoQ= 185 | github.com/google/pprof v0.0.0-20230926050212-f7f687d19a98/go.mod h1:czg5+yv1E0ZGTi6S6vVK1mke0fV+FaUhNGcd6VRS9Ik= 186 | github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= 187 | github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 188 | github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4= 189 | github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 190 | github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= 191 | github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= 192 | github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8/go.mod h1:dvDLG8qkwmyD9a/MJJN3XJcT3xFxOKAvTZGvuZmac9g= 193 | github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= 194 | github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= 195 | github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320 h1:1/D3zfFHttUKaCaGKZ/dR2roBXv0vKbSCnssIldfQdI= 196 | github.com/hashicorp/go-cty v1.4.1-0.20200414143053-d3edf31b6320/go.mod h1:EiZBMaudVLy8fmjf9Npq1dq9RalhveqZG5w/yz3mHWs= 197 | github.com/hashicorp/go-hclog v1.2.1 h1:YQsLlGDJgwhXFpucSPyVbCBviQtjlHv3jLTlp8YmtEw= 198 | github.com/hashicorp/go-hclog v1.2.1/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M= 199 | github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= 200 | github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= 201 | github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= 202 | github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= 203 | github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= 204 | github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= 205 | github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 206 | github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= 207 | github.com/hashicorp/hcl/v2 v2.14.1 h1:x0BpjfZ+CYdbiz+8yZTQ+gdLO7IXvOut7Da+XJayx34= 208 | github.com/hashicorp/hcl/v2 v2.14.1/go.mod h1:e4z5nxYlWNPdDSNYX+ph14EvWYMFm3eP0zIUqPc2jr0= 209 | github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= 210 | github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64= 211 | github.com/hashicorp/terraform-json v0.14.0 h1:sh9iZ1Y8IFJLx+xQiKHGud6/TSUCM0N8e17dKDpqV7s= 212 | github.com/hashicorp/terraform-json v0.14.0/go.mod h1:5A9HIWPkk4e5aeeXIBbkcOvaZbIYnAIkEyqP2pNSckM= 213 | github.com/hashicorp/terraform-plugin-go v0.14.0 h1:ttnSlS8bz3ZPYbMb84DpcPhY4F5DsQtcAS7cHo8uvP4= 214 | github.com/hashicorp/terraform-plugin-go v0.14.0/go.mod h1:2nNCBeRLaenyQEi78xrGrs9hMbulveqG/zDMQSvVJTE= 215 | github.com/hashicorp/terraform-plugin-log v0.7.0 h1:SDxJUyT8TwN4l5b5/VkiTIaQgY6R+Y2BQ0sRZftGKQs= 216 | github.com/hashicorp/terraform-plugin-log v0.7.0/go.mod h1:p4R1jWBXRTvL4odmEkFfDdhUjHf9zcs/BCoNHAc7IK4= 217 | github.com/hashicorp/terraform-plugin-sdk/v2 v2.24.0 h1:FtCLTiTcykdsURXPt/ku7fYXm3y19nbzbZcUxHx9RbI= 218 | github.com/hashicorp/terraform-plugin-sdk/v2 v2.24.0/go.mod h1:80wf5oad1tW+oLnbXS4UTYmDCrl7BuN1Q+IA91X1a4Y= 219 | github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM= 220 | github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg= 221 | github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0= 222 | github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= 223 | github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 224 | github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= 225 | github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= 226 | github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= 227 | github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= 228 | github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= 229 | github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 230 | github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= 231 | github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= 232 | github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= 233 | github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= 234 | github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= 235 | github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= 236 | github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= 237 | github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= 238 | github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= 239 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 240 | github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= 241 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 242 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 243 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= 244 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= 245 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 246 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 247 | github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= 248 | github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= 249 | github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= 250 | github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= 251 | github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 252 | github.com/mattn/go-isatty v0.0.17 h1:BTarxUcIeDqL27Mc+vyvdWYSL28zpIhv3RoTdsLMPng= 253 | github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= 254 | github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= 255 | github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= 256 | github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= 257 | github.com/mitchellh/copystructure v1.2.0/go.mod h1:qLl+cE2AmVv+CoeAwDPye/v+N2HKCj9FbZEVFJRxO9s= 258 | github.com/mitchellh/go-testing-interface v1.14.1 h1:jrgshOhYAUVNMAJiKbEu7EqAwgJJ2JqpQmpLJOu07cU= 259 | github.com/mitchellh/go-testing-interface v1.14.1/go.mod h1:gfgS7OtZj6MA4U1UrDRp04twqAjfvlZyCfX3sDjEym8= 260 | github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= 261 | github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= 262 | github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= 263 | github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= 264 | github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= 265 | github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 266 | github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 267 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= 268 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= 269 | github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= 270 | github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 271 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= 272 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= 273 | github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= 274 | github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= 275 | github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= 276 | github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= 277 | github.com/onsi/ginkgo/v2 v2.13.0 h1:0jY9lJquiL8fcf3M4LAXN5aMlS/b2BV86HFFPCPMgE4= 278 | github.com/onsi/ginkgo/v2 v2.13.0/go.mod h1:TE309ZR8s5FsKKpuB1YAQYBzCaAfUgatB/xlT/ETL/o= 279 | github.com/onsi/gomega v1.29.0 h1:KIA/t2t5UBzoirT4H9tsML45GEbo3ouUnBHsCfD2tVg= 280 | github.com/onsi/gomega v1.29.0/go.mod h1:9sxs+SwGrKI0+PWe4Fxa9tFQQBG5xSsSbMXOI8PPpoQ= 281 | github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 282 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 283 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 284 | github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg= 285 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 286 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 287 | github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= 288 | github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= 289 | github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 290 | github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= 291 | github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= 292 | github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdOOfY= 293 | github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= 294 | github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= 295 | github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= 296 | github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= 297 | github.com/rogpeppe/go-internal v1.11.0 h1:cWPaGQEPrBb5/AsnsZesgZZ9yb1OQ+GOISoDNXVBh4M= 298 | github.com/rogpeppe/go-internal v1.11.0/go.mod h1:ddIwULY96R17DhadqLgMfk9H9tvdUzkipdSkR5nkCZA= 299 | github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= 300 | github.com/spf13/afero v1.10.0 h1:EaGW2JJh15aKOejeuJ+wpFSHnbd7GE6Wvp3TsNhb6LY= 301 | github.com/spf13/afero v1.10.0/go.mod h1:UBogFpq8E9Hx+xc5CNTTEpTnuHVmXDwZcZcE1eb/UhQ= 302 | github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I= 303 | github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= 304 | github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= 305 | github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= 306 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 307 | github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= 308 | github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= 309 | github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= 310 | github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= 311 | github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= 312 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 313 | github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 314 | github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= 315 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= 316 | github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= 317 | github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 318 | github.com/tmccombs/hcl2json v0.3.3 h1:+DLNYqpWE0CsOQiEZu+OZm5ZBImake3wtITYxQ8uLFQ= 319 | github.com/tmccombs/hcl2json v0.3.3/go.mod h1:Y2chtz2x9bAeRTvSibVRVgbLJhLJXKlUeIvjeVdnm4w= 320 | github.com/upbound/provider-aws v0.43.0 h1:ycb6ybc1Dauy0DKiuXShbcjuh7GmPJRBjNngd5dluz8= 321 | github.com/upbound/provider-aws v0.43.0/go.mod h1:m7hoCp3D469sk1vaRh8u4hC8SmpPBJO70JrZS9p2H/U= 322 | github.com/vmihailenco/msgpack v4.0.4+incompatible h1:dSLoQfGFAo3F6OoNhwUmLwVgaUXK79GlxNBwueZn0xI= 323 | github.com/vmihailenco/msgpack v4.0.4+incompatible/go.mod h1:fy3FlTQTDXWkZ7Bh6AcGMlsjHatGryHQYUTf1ShIgkk= 324 | github.com/vmihailenco/msgpack/v4 v4.3.12 h1:07s4sz9IReOgdikxLTKNbBdqDMLsjPKXwvCazn8G65U= 325 | github.com/vmihailenco/msgpack/v4 v4.3.12/go.mod h1:gborTTJjAo/GWTqqRjrLCn9pgNN+NXzzngzBKDPIqw4= 326 | github.com/vmihailenco/tagparser v0.1.1 h1:quXMXlA39OCbd2wAdTsGDlK9RkOk6Wuw+x37wVyIuWY= 327 | github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI= 328 | github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 329 | github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 330 | github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 331 | github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= 332 | github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= 333 | github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= 334 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= 335 | github.com/zclconf/go-cty v1.11.0 h1:726SxLdi2SDnjY+BStqB9J1hNp4+2WlzyXLuimibIe0= 336 | github.com/zclconf/go-cty v1.11.0/go.mod h1:s9IfD1LK5ccNMSWCVFCE2rJfHiZgi7JijgeWIMfhLvA= 337 | go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= 338 | go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= 339 | go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 340 | go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 341 | go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= 342 | go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= 343 | go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= 344 | go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= 345 | go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= 346 | go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= 347 | go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= 348 | go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= 349 | go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= 350 | go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= 351 | go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= 352 | go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= 353 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= 354 | golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 355 | golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 356 | golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= 357 | golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= 358 | golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= 359 | golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= 360 | golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 361 | golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= 362 | golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= 363 | golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= 364 | golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= 365 | golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 366 | golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 367 | golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= 368 | golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= 369 | golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= 370 | golang.org/x/exp v0.0.0-20231006140011-7918f672742d h1:jtJma62tbqLibJ5sFQz8bKtEM8rJBtfilJ2qTU199MI= 371 | golang.org/x/exp v0.0.0-20231006140011-7918f672742d/go.mod h1:ldy0pHrwJyGW56pPQzzkH36rKxoZW1tw7ZJpeKx+hdo= 372 | golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= 373 | golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 374 | golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 375 | golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= 376 | golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= 377 | golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 378 | golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 379 | golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 380 | golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 381 | golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= 382 | golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 383 | golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 384 | golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= 385 | golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= 386 | golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= 387 | golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= 388 | golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= 389 | golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 390 | golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= 391 | golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 392 | golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 393 | golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 394 | golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 395 | golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= 396 | golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0= 397 | golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 398 | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 399 | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 400 | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 401 | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= 402 | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 403 | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 404 | golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 405 | golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= 406 | golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= 407 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 408 | golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 409 | golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 410 | golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 411 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 412 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 413 | golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 414 | golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 415 | golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 416 | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 417 | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 418 | golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 419 | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 420 | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= 421 | golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 422 | golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 423 | golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= 424 | golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 425 | golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= 426 | golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 427 | golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 428 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= 429 | golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= 430 | golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= 431 | golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= 432 | golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= 433 | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= 434 | golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 435 | golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 436 | golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 437 | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= 438 | golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 439 | golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 440 | golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 441 | golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= 442 | golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU= 443 | golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk= 444 | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 445 | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 446 | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 447 | golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 448 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 449 | golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 450 | golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 451 | golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 452 | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 453 | golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 454 | golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 455 | golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE= 456 | golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 457 | golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 458 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= 459 | golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 460 | golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 461 | golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 462 | golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 463 | golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 464 | golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 465 | golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 466 | golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 467 | golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 468 | golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 469 | golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 470 | golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 471 | golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 472 | golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 473 | golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 474 | golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 475 | golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 476 | golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 477 | golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 478 | golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 479 | golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 480 | golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 481 | golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 482 | golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 483 | golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 484 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 485 | golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 486 | golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 487 | golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 488 | golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 489 | golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 490 | golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 491 | golang.org/x/sys v0.0.0-20210423185535-09eb48e85fd7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 492 | golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 493 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 494 | golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 495 | golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= 496 | golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= 497 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= 498 | golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4= 499 | golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= 500 | golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 501 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 502 | golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= 503 | golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= 504 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 505 | golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 506 | golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= 507 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= 508 | golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= 509 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= 510 | golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 511 | golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 512 | golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 513 | golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= 514 | golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= 515 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 516 | golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= 517 | golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= 518 | golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 519 | golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 520 | golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= 521 | golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 522 | golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 523 | golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= 524 | golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 525 | golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 526 | golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= 527 | golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 528 | golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 529 | golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 530 | golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 531 | golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 532 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 533 | golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 534 | golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= 535 | golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 536 | golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 537 | golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 538 | golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 539 | golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 540 | golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 541 | golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 542 | golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 543 | golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 544 | golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= 545 | golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 546 | golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= 547 | golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= 548 | golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 549 | golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 550 | golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 551 | golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 552 | golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= 553 | golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 554 | golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 555 | golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= 556 | golang.org/x/tools v0.0.0-20200904185747-39188db58858/go.mod h1:Cj7w3i3Rnn0Xh82ur9kSqwfTHTeVxaDqrfMjpcNT6bE= 557 | golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 558 | golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 559 | golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 560 | golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 561 | golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 562 | golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= 563 | golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= 564 | golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= 565 | golang.org/x/tools v0.16.1 h1:TLyB3WofjdOEepBHAU20JdNC1Zbg87elYofWYAY5oZA= 566 | golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= 567 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 568 | golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 569 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 570 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 571 | gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= 572 | gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= 573 | google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= 574 | google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= 575 | google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 576 | google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= 577 | google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 578 | google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 579 | google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= 580 | google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 581 | google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 582 | google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 583 | google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 584 | google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= 585 | google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 586 | google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= 587 | google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= 588 | google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 589 | google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= 590 | google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= 591 | google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= 592 | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= 593 | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 594 | google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= 595 | google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= 596 | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 597 | google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 598 | google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= 599 | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 600 | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= 601 | google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 602 | google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 603 | google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 604 | google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= 605 | google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 606 | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= 607 | google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= 608 | google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 609 | google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 610 | google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 611 | google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 612 | google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 613 | google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= 614 | google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= 615 | google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 616 | google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 617 | google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 618 | google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 619 | google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 620 | google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 621 | google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 622 | google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= 623 | google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= 624 | google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= 625 | google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= 626 | google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 627 | google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 628 | google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 629 | google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 630 | google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 631 | google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 632 | google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 633 | google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 634 | google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 635 | google.golang.org/genproto v0.0.0-20210226172003-ab064af71705/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= 636 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d h1:uvYuEyMHKNt+lT4K3bN6fGswmK8qSvcreM3BwjDh+y4= 637 | google.golang.org/genproto/googleapis/rpc v0.0.0-20230822172742-b8732ec3820d/go.mod h1:+Bk1OCOj40wS2hwAMA+aCW9ypzm63QTBBHp6lQ3p+9M= 638 | google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= 639 | google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= 640 | google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= 641 | google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= 642 | google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= 643 | google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 644 | google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 645 | google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= 646 | google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= 647 | google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= 648 | google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 649 | google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 650 | google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= 651 | google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= 652 | google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= 653 | google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= 654 | google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= 655 | google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= 656 | google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= 657 | google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= 658 | google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= 659 | google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= 660 | google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= 661 | google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 662 | google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 663 | google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= 664 | google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= 665 | google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 666 | google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= 667 | google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= 668 | google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I= 669 | google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= 670 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 671 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 672 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 673 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 674 | gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= 675 | gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= 676 | gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= 677 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= 678 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= 679 | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 680 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= 681 | gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= 682 | gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= 683 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 684 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 685 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 686 | honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 687 | honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 688 | honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 689 | honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= 690 | honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= 691 | honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 692 | honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= 693 | k8s.io/api v0.28.3 h1:Gj1HtbSdB4P08C8rs9AR94MfSGpRhJgsS+GF9V26xMM= 694 | k8s.io/api v0.28.3/go.mod h1:MRCV/jr1dW87/qJnZ57U5Pak65LGmQVkKTzf3AtKFHc= 695 | k8s.io/apiextensions-apiserver v0.28.3 h1:Od7DEnhXHnHPZG+W9I97/fSQkVpVPQx2diy+2EtmY08= 696 | k8s.io/apiextensions-apiserver v0.28.3/go.mod h1:NE1XJZ4On0hS11aWWJUTNkmVB03j9LM7gJSisbRt8Lc= 697 | k8s.io/apimachinery v0.29.1 h1:KY4/E6km/wLBguvCZv8cKTeOwwOBqFNjwJIdMkMbbRc= 698 | k8s.io/apimachinery v0.29.1/go.mod h1:6HVkd1FwxIagpYrHSwJlQqZI3G9LfYWRPAkUvLnXTKU= 699 | k8s.io/client-go v0.28.3 h1:2OqNb72ZuTZPKCl+4gTKvqao0AMOl9f3o2ijbAj3LI4= 700 | k8s.io/client-go v0.28.3/go.mod h1:LTykbBp9gsA7SwqirlCXBWtK0guzfhpoW4qSm7i9dxo= 701 | k8s.io/component-base v0.28.3 h1:rDy68eHKxq/80RiMb2Ld/tbH8uAE75JdCqJyi6lXMzI= 702 | k8s.io/component-base v0.28.3/go.mod h1:fDJ6vpVNSk6cRo5wmDa6eKIG7UlIQkaFmZN2fYgIUD8= 703 | k8s.io/klog/v2 v2.110.1 h1:U/Af64HJf7FcwMcXyKm2RPM22WZzyR7OSpYj5tg3cL0= 704 | k8s.io/klog/v2 v2.110.1/go.mod h1:YGtd1984u+GgbuZ7e08/yBuAfKLSO0+uR1Fhi6ExXjo= 705 | k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780= 706 | k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= 707 | k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI= 708 | k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= 709 | rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= 710 | rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= 711 | rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= 712 | sigs.k8s.io/controller-runtime v0.16.3 h1:2TuvuokmfXvDUamSx1SuAOO3eTyye+47mJCigwG62c4= 713 | sigs.k8s.io/controller-runtime v0.16.3/go.mod h1:j7bialYoSn142nv9sCOJmQgDXQXxnroFU4VnX/brVJ0= 714 | sigs.k8s.io/controller-tools v0.13.0 h1:NfrvuZ4bxyolhDBt/rCZhDnx3M2hzlhgo5n3Iv2RykI= 715 | sigs.k8s.io/controller-tools v0.13.0/go.mod h1:5vw3En2NazbejQGCeWKRrE7q4P+CW8/klfVqP8QZkgA= 716 | sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= 717 | sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= 718 | sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= 719 | sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= 720 | sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= 721 | sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= 722 | -------------------------------------------------------------------------------- /init.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # This script helps initialize a new function project by 4 | # replacing all instances of function-template-go with the 5 | # name of your function. The scripts accepts two arguments: 6 | # 1. The name of your function 7 | # 2. The path to your function directory 8 | 9 | set -e 10 | 11 | cd "$2" || return 12 | 13 | # Replace function-template-go with the name of your function 14 | # in go.mod 15 | perl -pi -e s,function-template-go,"$1",g go.mod 16 | # in fn.go 17 | perl -pi -e s,function-template-go,"$1",g fn.go 18 | # in examples 19 | perl -pi -e s,function-template-go,"$1",g example/* 20 | 21 | echo "Function $1 has been initialised successfully" 22 | -------------------------------------------------------------------------------- /input/generate.go: -------------------------------------------------------------------------------- 1 | //go:build generate 2 | // +build generate 3 | 4 | // NOTE(negz): See the below link for details on what is happening here. 5 | // https://github.com/golang/go/wiki/Modules#how-can-i-track-tool-dependencies-for-a-module 6 | 7 | // Remove existing and generate new input manifests 8 | //go:generate rm -rf ../package/input/ 9 | //go:generate go run -tags generate sigs.k8s.io/controller-tools/cmd/controller-gen paths=./v1beta1 object crd:crdVersions=v1 output:artifacts:config=../package/input 10 | 11 | package input 12 | 13 | import ( 14 | _ "sigs.k8s.io/controller-tools/cmd/controller-gen" //nolint:typecheck 15 | ) 16 | -------------------------------------------------------------------------------- /input/v1beta1/input.go: -------------------------------------------------------------------------------- 1 | // Package v1beta1 contains the input type for this Function 2 | // +kubebuilder:object:generate=true 3 | // +groupName=sequencer.fn.crossplane.io 4 | // +versionName=v1beta1 5 | package v1beta1 6 | 7 | import ( 8 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 9 | 10 | "github.com/crossplane/function-sdk-go/resource" 11 | ) 12 | 13 | // This isn't a custom resource, in the sense that we never install its CRD. 14 | // It is a KRM-like object, so we generate a CRD to describe its schema. 15 | 16 | // SequencingRule is a rule that describes a sequence of resources. 17 | type SequencingRule struct { 18 | // TODO: Should we add a way to infer sequencing from usages? e.g. InferFromUsages: true 19 | // InferFromUsages bool `json:"inferFromUsages,omitempty"` 20 | Sequence []resource.Name `json:"sequence,omitempty"` 21 | } 22 | 23 | // Input can be used to provide input to this Function. 24 | // +kubebuilder:object:root=true 25 | // +kubebuilder:storageversion 26 | // +kubebuilder:resource:categories=crossplane 27 | type Input struct { 28 | metav1.TypeMeta `json:",inline"` 29 | metav1.ObjectMeta `json:"metadata,omitempty"` 30 | 31 | // Rules is a list of rules that describe sequences of resources. 32 | Rules []SequencingRule `json:"rules"` 33 | } 34 | -------------------------------------------------------------------------------- /input/v1beta1/zz_generated.deepcopy.go: -------------------------------------------------------------------------------- 1 | //go:build !ignore_autogenerated 2 | 3 | // Code generated by controller-gen. DO NOT EDIT. 4 | 5 | package v1beta1 6 | 7 | import ( 8 | "github.com/crossplane/function-sdk-go/resource" 9 | runtime "k8s.io/apimachinery/pkg/runtime" 10 | ) 11 | 12 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 13 | func (in *Input) DeepCopyInto(out *Input) { 14 | *out = *in 15 | out.TypeMeta = in.TypeMeta 16 | in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) 17 | if in.Rules != nil { 18 | in, out := &in.Rules, &out.Rules 19 | *out = make([]SequencingRule, len(*in)) 20 | for i := range *in { 21 | (*in)[i].DeepCopyInto(&(*out)[i]) 22 | } 23 | } 24 | } 25 | 26 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Input. 27 | func (in *Input) DeepCopy() *Input { 28 | if in == nil { 29 | return nil 30 | } 31 | out := new(Input) 32 | in.DeepCopyInto(out) 33 | return out 34 | } 35 | 36 | // DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. 37 | func (in *Input) DeepCopyObject() runtime.Object { 38 | if c := in.DeepCopy(); c != nil { 39 | return c 40 | } 41 | return nil 42 | } 43 | 44 | // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. 45 | func (in *SequencingRule) DeepCopyInto(out *SequencingRule) { 46 | *out = *in 47 | if in.Sequence != nil { 48 | in, out := &in.Sequence, &out.Sequence 49 | *out = make([]resource.Name, len(*in)) 50 | copy(*out, *in) 51 | } 52 | } 53 | 54 | // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SequencingRule. 55 | func (in *SequencingRule) DeepCopy() *SequencingRule { 56 | if in == nil { 57 | return nil 58 | } 59 | out := new(SequencingRule) 60 | in.DeepCopyInto(out) 61 | return out 62 | } 63 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | // Package main implements a Composition Function. 2 | package main 3 | 4 | import ( 5 | "github.com/alecthomas/kong" 6 | 7 | "github.com/crossplane/function-sdk-go" 8 | ) 9 | 10 | // CLI of this Function. 11 | type CLI struct { 12 | Debug bool `short:"d" help:"Emit debug logs in addition to info logs."` 13 | 14 | Network string `help:"Network on which to listen for gRPC connections." default:"tcp"` 15 | Address string `help:"Address at which to listen for gRPC connections." default:":9443"` 16 | TLSCertsDir string `help:"Directory containing server certs (tls.key, tls.crt) and the CA used to verify client certificates (ca.crt)" env:"TLS_SERVER_CERTS_DIR"` 17 | Insecure bool `help:"Run without mTLS credentials. If you supply this flag --tls-server-certs-dir will be ignored."` 18 | } 19 | 20 | // Run this Function. 21 | func (c *CLI) Run() error { 22 | log, err := function.NewLogger(c.Debug) 23 | if err != nil { 24 | return err 25 | } 26 | 27 | return function.Serve(&Function{log: log}, 28 | function.Listen(c.Network, c.Address), 29 | function.MTLSCertificates(c.TLSCertsDir), 30 | function.Insecure(c.Insecure)) 31 | } 32 | 33 | func main() { 34 | ctx := kong.Parse(&CLI{}, kong.Description("A Crossplane Composition Function.")) 35 | ctx.FatalIfErrorf(ctx.Run()) 36 | } 37 | -------------------------------------------------------------------------------- /package/crossplane.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: meta.pkg.crossplane.io/v1beta1 3 | kind: Function 4 | metadata: 5 | name: function-sequencer 6 | annotations: 7 | meta.crossplane.io/maintainer: Crossplane Maintainers 8 | meta.crossplane.io/source: github.com/crossplane-contrib/function-sequencer 9 | meta.crossplane.io/license: Apache-2.0 10 | meta.crossplane.io/description: | 11 | A composition function to define sequencing rules delaying the creation of resources until other resources are ready. 12 | meta.crossplane.io/readme: | 13 | This composition function enables defining sequencing rules to delay 14 | the creation of resources until other resources are ready. It is useful 15 | for defining ordering between resources to optimize the provisioning flow 16 | when eventual consistency is not optimal. 17 | spec: {} 18 | -------------------------------------------------------------------------------- /package/input/sequencer.fn.crossplane.io_inputs.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | apiVersion: apiextensions.k8s.io/v1 3 | kind: CustomResourceDefinition 4 | metadata: 5 | annotations: 6 | controller-gen.kubebuilder.io/version: v0.13.0 7 | name: inputs.sequencer.fn.crossplane.io 8 | spec: 9 | group: sequencer.fn.crossplane.io 10 | names: 11 | categories: 12 | - crossplane 13 | kind: Input 14 | listKind: InputList 15 | plural: inputs 16 | singular: input 17 | scope: Namespaced 18 | versions: 19 | - name: v1beta1 20 | schema: 21 | openAPIV3Schema: 22 | description: Input can be used to provide input to this Function. 23 | properties: 24 | apiVersion: 25 | description: 'APIVersion defines the versioned schema of this representation 26 | of an object. Servers should convert recognized schemas to the latest 27 | internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' 28 | type: string 29 | kind: 30 | description: 'Kind is a string value representing the REST resource this 31 | object represents. Servers may infer this from the endpoint the client 32 | submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' 33 | type: string 34 | metadata: 35 | type: object 36 | rules: 37 | description: Rules is a list of rules that describe sequences of resources. 38 | items: 39 | description: SequencingRule is a rule that describes a sequence of resources. 40 | properties: 41 | sequence: 42 | description: 'TODO: Should we add a way to infer sequencing from 43 | usages? e.g. InferFromUsages: true InferFromUsages bool `json:"inferFromUsages,omitempty"`' 44 | items: 45 | description: A Name uniquely identifies a composed resource within 46 | a Composition Function pipeline. It's not the resource's metadata.name. 47 | type: string 48 | type: array 49 | type: object 50 | type: array 51 | required: 52 | - rules 53 | type: object 54 | served: true 55 | storage: true 56 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "config:recommended" 5 | ], 6 | "crossplane": { 7 | "fileMatch": ["(^|/)example/.*\\.ya?ml$"] 8 | }, 9 | "packageRules": [ 10 | { 11 | "matchManagers": ["crossplane"], 12 | "matchFileNames": ["example/**"], 13 | "groupName": "examples" 14 | } 15 | ] 16 | } 17 | --------------------------------------------------------------------------------