diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..afc0ad5 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,24 @@ +{ + "name": "Kubebuilder DevContainer", + "image": "golang:1.25", + "features": { + "ghcr.io/devcontainers/features/docker-in-docker:2": {}, + "ghcr.io/devcontainers/features/git:1": {} + }, + + "runArgs": ["--network=host"], + + "customizations": { + "vscode": { + "settings": { + "terminal.integrated.shell.linux": "/bin/bash" + }, + "extensions": [ + "ms-kubernetes-tools.vscode-kubernetes-tools", + "ms-azuretools.vscode-docker" + ] + } + }, + + "onCreateCommand": "bash .devcontainer/post-install.sh" +} diff --git a/.devcontainer/post-install.sh b/.devcontainer/post-install.sh new file mode 100644 index 0000000..265c43e --- /dev/null +++ b/.devcontainer/post-install.sh @@ -0,0 +1,23 @@ +#!/bin/bash +set -x + +curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64 +chmod +x ./kind +mv ./kind /usr/local/bin/kind + +curl -L -o kubebuilder https://go.kubebuilder.io/dl/latest/linux/amd64 +chmod +x kubebuilder +mv kubebuilder /usr/local/bin/ + +KUBECTL_VERSION=$(curl -L -s https://dl.k8s.io/release/stable.txt) +curl -LO "https://dl.k8s.io/release/$KUBECTL_VERSION/bin/linux/amd64/kubectl" +chmod +x kubectl +mv kubectl /usr/local/bin/kubectl + +docker network create -d=bridge --subnet=172.19.0.0/24 kind + +kind version +kubebuilder version +docker --version +go version +kubectl version --client diff --git a/.dockerignore b/.dockerignore index 0f04682..a3aab7a 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,3 @@ # More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file # Ignore build and test binaries. bin/ -testbin/ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index ad5748b..0000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,107 +0,0 @@ -name: ci -on: - push: - branches-ignore: - - "github-pages" - pull_request: {} -jobs: - changes: - runs-on: ubuntu-latest - outputs: - go: ${{ steps.filter.outputs.go }} - config: ${{ steps.filter.outputs.config }} - docker: ${{ steps.filter.outputs.docker }} - steps: - - uses: actions/checkout@v4 - - uses: dorny/paths-filter@v3 - id: filter - with: - token: ${{ secrets.GITHUB_TOKEN }} - filters: | - go: - - '**/*.go' - - 'go.mod' - - 'go.sum' - config: - - '.github/workflows/ci.yml' - - '.golangci.yaml' - - 'Makefile' - docker: - - 'Dockerfile' - lint: - if: | - (needs.changes.outputs.go == 'true') || - (needs.changes.outputs.config == 'true') - runs-on: ubuntu-latest - needs: - - changes - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 - with: - go-version: "1.22.2" - - uses: golangci/golangci-lint-action@v3 - with: - # Optional: version of golangci-lint to use in form of v1.2 or v1.2.3 or `latest` to use the latest version - version: v1.56.2 - - # Optional: working directory, useful for monorepos - # working-directory: somedir - - # Optional: golangci-lint command line arguments. - args: --timeout=3600s - - # Optional: show only new issues if it's a pull request. The default value is `false`. - # only-new-issues: true - - # Optional: if set to true then the action will use pre-installed Go. - # skip-go-installation: true - - # Optional: if set to true then the action don't cache or restore ~/go/pkg. - # skip-pkg-cache: true - - # Optional: if set to true then the action don't cache or restore ~/.cache/go-build. - # skip-build-cache: true - build: - if: | - (needs.changes.outputs.go == 'true') || - (needs.changes.outputs.config == 'true') - runs-on: ubuntu-latest - needs: - - changes - - lint - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 - with: - go-version: "1.22.2" - - run: make build - - run: make docker-build - test: - if: | - (needs.changes.outputs.go == 'true') || - (needs.changes.outputs.config == 'true') || - (needs.changes.outputs.docker == 'true') - runs-on: ubuntu-latest - needs: - - changes - - lint - - build - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 - with: - go-version: "1.22.2" - - run: | - if [ -f /usr/local/bin/dockerize ]; then - echo "dockerize found; skipping installation"; - else - wget https://github.com/jwilder/dockerize/releases/download/v0.6.0/dockerize-linux-amd64-v0.6.0.tar.gz \ - && sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-v0.6.0.tar.gz \ - && rm dockerize-linux-amd64-v0.6.0.tar.gz; - fi - - run: make setup/services - - run: dockerize -wait tcp://localhost:5432 -timeout 5m - - run: docker logs postgres - - run: make test/all - - run: make test/coverage diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml new file mode 100644 index 0000000..6a81049 --- /dev/null +++ b/.github/workflows/lint.yml @@ -0,0 +1,23 @@ +name: Lint + +on: + push: + pull_request: + +jobs: + lint: + name: Lint + runs-on: ubuntu-latest + steps: + - name: Clone the code + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Run linter + uses: golangci/golangci-lint-action@v8 + with: + version: v2.7.2 diff --git a/.github/workflows/test-e2e.yml b/.github/workflows/test-e2e.yml new file mode 100644 index 0000000..e5992bf --- /dev/null +++ b/.github/workflows/test-e2e.yml @@ -0,0 +1,32 @@ +name: E2E Tests + +on: + push: + pull_request: + +jobs: + test-e2e: + name: Test E2E + runs-on: ubuntu-latest + steps: + - name: Clone the code + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + + - name: Install the latest version of kind + run: | + curl -Lo ./kind https://kind.sigs.k8s.io/dl/latest/kind-linux-amd64 + chmod +x ./kind + sudo mv ./kind /usr/local/bin/kind + + - name: Verify kind installation + run: kind version + + - name: Running Test e2e + run: | + go mod tidy + make test-e2e diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..6a18c83 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,32 @@ +name: Tests + +on: + push: + pull_request: + +jobs: + test: + name: Test + runs-on: ubuntu-latest + steps: + - name: Clone the code + uses: actions/checkout@v4 + + - name: Setup Go + uses: actions/setup-go@v5 + with: + go-version-file: go.mod + - run: | + if [ -f /usr/local/bin/dockerize ]; then + echo "dockerize found; skipping installation"; + else + wget https://github.com/jwilder/dockerize/releases/download/v0.6.0/dockerize-linux-amd64-v0.6.0.tar.gz \ + && sudo tar -C /usr/local/bin -xzvf dockerize-linux-amd64-v0.6.0.tar.gz \ + && rm dockerize-linux-amd64-v0.6.0.tar.gz; + fi + - run: make setup/services + - run: dockerize -wait tcp://localhost:5432 -timeout 5m + - run: docker logs postgres + + - name: Running Tests + run: make test diff --git a/.gitignore b/.gitignore index a176334..559945b 100644 --- a/.gitignore +++ b/.gitignore @@ -4,23 +4,27 @@ *.dll *.so *.dylib -bin -testbin/* +bin/* +Dockerfile.cross -# Test binary, build with `go test -c` +# Test binary, built with `go test -c` *.test # Output of the go coverage tool, specifically when used with LiteIDE *.out -# Kubernetes Generated files - skip generated files, except for vendored files +# Go workspace file +go.work +# Kubernetes Generated files - skip generated files, except for vendored files !vendor/**/zz_generated.* # editor and IDE paraphernalia .idea +.vscode *.swp *.swo *~ -coverage.html +.run/ +gl-code-quality-report.json \ No newline at end of file diff --git a/.golangci.yaml b/.golangci.yaml deleted file mode 100644 index bc342a2..0000000 --- a/.golangci.yaml +++ /dev/null @@ -1,517 +0,0 @@ -# This file contains all available configuration options -# with their default values. -# options for analysis running -run: - go: "1.21" - # default concurrency is a available CPU number - concurrency: 4 - # timeout for analysis, e.g. 30s, 5m, default is 1m - timeout: 10m - # exit code when at least one issue was found, default is 1 - issues-exit-code: 1 - # include test files or not, default is true - tests: false - # list of build tags, all linters use it. Default is empty list. - build-tags: [] - # which dirs to skip: issues from them won't be reported; - # can use regexp here: generated.*, regexp is applied on full path; - # default value is empty list, but default dirs are skipped independently - # from this option's value (see skip-dirs-use-default). - skip-dirs: [] - # default is true. Enables skipping of directories: - # vendor$, third_party$, testdata$, examples$, Godeps$, builtin$ - skip-dirs-use-default: true - # which files to skip: they will be analyzed, but issues from them - # won't be reported. Default value is empty list, but there is - # no need to include all autogenerated files, we confidently recognize - # autogenerated files. If it's not please let us know. - skip-files: [] - # by default isn't set. If set we pass it to "go list -mod={option}". From "go help modules": - # If invoked with -mod=readonly, the go command is disallowed from the implicit - # automatic updating of go.mod described above. Instead, it fails when any changes - # to go.mod are needed. This setting is most useful to check that go.mod does - # not need updates, such as in a continuous integration and testing system. - # If invoked with -mod=vendor, the go command assumes that the vendor - # directory holds the correct copies of dependencies and ignores - # the dependency descriptions in go.mod. - # modules-download-mode: readonly|release|vendor - modules-download-mode: readonly - # Allow multiple parallel golangci-lint instances running. - # If false (default) - golangci-lint acquires file lock on start. - allow-parallel-runners: false -# output configuration options -output: - # colored-line-number|line-number|json|tab|checkstyle|code-climate, default is "colored-line-number" - format: colored-line-number - # print lines of code with issue, default is true - print-issued-lines: true - # print linter name in the end of issue text, default is true - print-linter-name: true - # make issues output unique by line, default is true - uniq-by-line: true - # add a prefix to the output file references; default is no prefix - path-prefix: "" -# all available settings of specific linters -linters-settings: - tagalign: - align: true - sort: true - godox: - # report any comments starting with keywords, this is useful for TODO or FIXME comments that - # might be left in the code accidentally and should be resolved before merging - keywords: # default keywords are TODO, BUG, and FIXME, these can be overwritten by this setting - - BUG - - FIXME - # - NOTE - # - OPTIMIZE # marks code that should be optimized before merging - # - HACK # marks hack-arounds that should be removed before merging - gofmt: - # simplify code: gofmt with `-s` option, true by default - simplify: true - govet: - # report about shadowed variables - check-shadowing: true - enable-all: true - revive: - # When set to false, ignores files with "GENERATED" header, similar to golint. - # See https://github.com/mgechev/revive#available-rules for details. - # Default: false - ignore-generated-header: true - rules: - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#atomic - - name: atomic - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#blank-imports - - name: blank-imports - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#bool-literal-in-expr - - name: bool-literal-in-expr - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#call-to-gc - - name: call-to-gc - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#confusing-results - - name: confusing-results - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#constant-logical-expr - - name: constant-logical-expr - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#context-as-argument - - name: context-as-argument - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#context-keys-type - - name: context-keys-type - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#datarace - - name: datarace - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#deep-exit - - name: deep-exit - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#defer - - name: defer - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#dot-imports - - name: dot-imports - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#duplicated-imports - - name: duplicated-imports - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#early-return - - name: early-return - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#empty-block - - name: empty-block - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#empty-lines - - name: empty-lines - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#error-naming - - name: error-naming - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#error-return - - name: error-return - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#error-strings - - name: error-strings - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#errorf - - name: errorf - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#function-result-limit - - name: function-result-limit - severity: warning - disabled: false - arguments: [4] - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#get-return - - name: get-return - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#identical-branches - - name: identical-branches - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#if-return - - name: if-return - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#increment-decrement - - name: increment-decrement - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#indent-error-flow - - name: indent-error-flow - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#import-shadowing - - name: import-shadowing - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#modifies-parameter - - name: modifies-parameter - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#modifies-value-receiver - - name: modifies-value-receiver - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#nested-structs - - name: nested-structs - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#optimize-operands-order - - name: optimize-operands-order - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#package-comments - - name: package-comments - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#range - - name: range - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#range-val-in-closure - - name: range-val-in-closure - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#range-val-address - - name: range-val-address - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#receiver-naming - - name: receiver-naming - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#redefines-builtin-id - - name: redefines-builtin-id - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#string-of-int - - name: string-of-int - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#superfluous-else - - name: superfluous-else - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#time-equal - - name: time-equal - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#time-naming - - name: time-naming - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#var-declaration - - name: var-declaration - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unconditional-recursion - - name: unconditional-recursion - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unexported-naming - - name: unexported-naming - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unexported-return - - name: unexported-return - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unnecessary-stmt - - name: unnecessary-stmt - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unreachable-code - - name: unreachable-code - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unused-parameter - - name: unused-parameter - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unused-receiver - - name: unused-receiver - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#useless-break - - name: useless-break - severity: warning - disabled: false - # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#waitgroup-by-value - - name: waitgroup-by-value - severity: warning - disabled: false - gosec: - # To select a subset of rules to run. - # Available rules: https://github.com/securego/gosec#available-rules - # Default: [] - means include all rules - includes: - - G101 # Look for hard coded credentials - - G102 # Bind to all interfaces - - G103 # Audit the use of unsafe block - - G104 # Audit errors not checked - - G106 # Audit the use of ssh.InsecureIgnoreHostKey - - G107 # Url provided to HTTP request as taint input - - G108 # Profiling endpoint automatically exposed on /debug/pprof - - G109 # Potential Integer overflow made by strconv.Atoi result conversion to int16/32 - - G110 # Potential DoS vulnerability via decompression bomb - - G111 # Potential directory traversal - - G112 # Potential slowloris attack - - G113 # Usage of Rat.SetString in math/big with an overflow (CVE-2022-23772) - - G114 # Use of net/http serve function that has no support for setting timeouts - - G201 # SQL query construction using format string - - G202 # SQL query construction using string concatenation - - G203 # Use of unescaped data in HTML templates - - G204 # Audit use of command execution - - G301 # Poor file permissions used when creating a directory - - G302 # Poor file permissions used with chmod - - G303 # Creating tempfile using a predictable path - - G304 # File path provided as taint input - - G305 # File traversal when extracting zip/tar archive - - G306 # Poor file permissions used when writing to a new file - - G307 # Deferring a method which returns an error - - G401 # Detect the usage of DES, RC4, MD5 or SHA1 - - G402 # Look for bad TLS connection settings - - G403 # Ensure minimum RSA key length of 2048 bits - - G404 # Insecure random number source (rand) - - G501 # Import blocklist: crypto/md5 - - G502 # Import blocklist: crypto/des - - G503 # Import blocklist: crypto/rc4 - - G504 # Import blocklist: net/http/cgi - - G505 # Import blocklist: crypto/sha1 - - G601 # Implicit memory aliasing of items from a range statement - lll: - # max line length, lines longer will be reported. Default is 120. - # '\t' is counted as 1 character by default, and can be changed with the tab-width option - line-length: 250 - # tab width in spaces. Default to 1. - tab-width: 1 - testpackage: - # regexp pattern to skip files - skip-regexp: _test\.go -linters: - enable: - - govet - - errcheck - - staticcheck - - unused - - gosimple - - ineffassign - - typecheck - - bodyclose - - noctx - - rowserrcheck - - stylecheck - - gosec - - unconvert - - goconst - - asciicheck - - gofmt - - goimports - - goheader - - misspell - - lll - - unparam - - dogsled - - nakedret - - gocritic - - gochecknoinits - - godox - - whitespace - - wsl - - goprintffuncname - - gomnd - - gomodguard - - godot - - exportloopref - - sqlclosecheck - - nlreturn - - nolintlint - - forcetypeassert - - gomoddirectives - - importas - - nilerr - - promlinter - - revive - - wastedassign - - errname - - tagliatelle - - bidichk - - contextcheck - - tenv - - containedctx - - errchkjson - - decorder - - grouper - - asasalint - - execinquery - - reassign - - usestdlibvars - - tagalign - - gosmopolitan - - mirror - - gochecksumtype - - inamedparam - - protogetter - - sloglint - - testifylint - disable: - - exhaustive - - nilnil - - deadcode - - dupl - - varcheck - - structcheck - - prealloc - - gochecknoglobals - - gocyclo - - gocognit - - gofumpt - - funlen - - testpackage - - gci - - goerr113 # Forbid dynamic errors - - nestif - - scopelint - - ireturn - - varnamelen - - maintidx - - exhaustruct - - nosnakecase - - interfacebloat - - zerologlint - - depguard - - perfsprint - fast: false -issues: - exclude-rules: - # Exclude lll issues for long lines with go:generate - - linters: - - lll - source: "^//go:generate " - - linters: - - lll - - gocritic - - govet - - dupl - - gochecknoinits - path: \_types\.go - - linters: - - gochecknoinits - path: main.go - - linters: - - dupl - path: \_controller\.go - # issues: - # # List of regexps of issue texts to exclude, empty list by default. - # # But independently from this option we use default exclude patterns, - # # it can be disabled by `exclude-use-default: false`. To list all - # # excluded by default patterns execute `golangci-lint run --help` - # exclude: - # - abcdef - # # Excluding configuration per-path, per-linter, per-text and per-source - # exclude-rules: - # # Exclude some linters from running on tests files. - # - path: _test\.go - # linters: - # - gocyclo - # - errcheck - # - dupl - # - gosec - # # Exclude known linters from partially hard-vendored code, - # # which is impossible to exclude via "nolint" comments. - # - path: internal/hmac/ - # text: "weak cryptographic primitive" - # linters: - # - gosec - # # Exclude some staticcheck messages - # - linters: - # - staticcheck - # text: "SA9003:" - # # Exclude lll issues for long lines with go:generate - # - linters: - # - lll - # source: "^//go:generate " - # # Independently from option `exclude` we use default exclude patterns, - # # it can be disabled by this option. To list all - # # excluded by default patterns execute `golangci-lint run --help`. - # # Default value for this option is true. - # exclude-use-default: false - # # Maximum issues count per one linter. Set to 0 to disable. Default is 50. - # max-issues-per-linter: 0 - # # Maximum count of issues with the same text. Set to 0 to disable. Default is 3. - # max-same-issues: 0 - # # Show only new issues: if there are unstaged changes or untracked files, - # # only those changes are analyzed, else only changes in HEAD~ are analyzed. - # # It's a super-useful option for integration of golangci-lint into existing - # # large codebase. It's not practical to fix all existing issues at the moment - # # of integration: much better don't allow issues in new code. - # # Default is false. - # new: false - # # Show only new issues created in git patch with set file path. - # new-from-patch: # path/to/patch/file - # Fix found issues (if it's supported by the linter) - fix: true -severity: - # Default value is empty string. - # Set the default severity for issues. If severity rules are defined and the issues - # do not match or no severity is provided to the rule this will be the default - # severity applied. Severities should match the supported severity names of the - # selected out format. - # - Code climate: https://docs.codeclimate.com/docs/issues#issue-severity - # - Checkstyle: https://checkstyle.sourceforge.io/property_types.html#severity - # - Github: https://help.github.com/en/actions/reference/workflow-commands-for-github-actions#setting-an-error-message - default-severity: error - # The default value is false. - # If set to true severity-rules regular expressions become case sensitive. - case-sensitive: false - # Default value is empty list. - # When a list of severity rules are provided, severity information will be added to lint - # issues. Severity rules have the same filtering capability as exclude rules except you - # are allowed to specify one matcher per severity rule. - # Only affects out formats that support setting severity information. - rules: - - linters: - - dupl - severity: info diff --git a/.golangci.yml b/.golangci.yml new file mode 100644 index 0000000..3f1b5c6 --- /dev/null +++ b/.golangci.yml @@ -0,0 +1,486 @@ +version: "2" +run: + concurrency: 4 + go: "1.25" + modules-download-mode: readonly + issues-exit-code: 1 + tests: true + allow-parallel-runners: true + timeout: 10m +output: + formats: + text: + path: stdout + print-linter-name: true + print-issued-lines: true + code-climate: + path: gl-code-quality-report.json + path-prefix: "" +linters: + enable: + - asasalint + - asciicheck + - bidichk + - bodyclose + - containedctx + - contextcheck + - copyloopvar + - decorder + - dogsled + - dupl + - errchkjson + - errname + - exhaustive + - forcetypeassert + - gochecksumtype + - goconst + - gocritic + - godot + - godox + - goheader + - gomoddirectives + - gomodguard + - goprintffuncname + - gosec + - gosmopolitan + - grouper + - importas + - inamedparam + - mirror + - misspell + - mnd + - nakedret + - nilerr + - nlreturn + - noctx + - nolintlint + - perfsprint + - promlinter + - protogetter + - reassign + - revive + - rowserrcheck + - sloglint + - sqlclosecheck + - staticcheck + - tagalign + - tagliatelle + - testifylint + - unconvert + - unparam + - usestdlibvars + - usetesting + - wastedassign + - whitespace + - wsl_v5 + - unqueryvet + - iotamixing + - modernize + disable: + - depguard + - err113 + - exhaustruct + - funlen + - gochecknoglobals + - gocognit + - gocyclo + - interfacebloat + - ireturn + - maintidx + - nestif + - prealloc + - testpackage + - varnamelen + - zerologlint + - noinlineerr + - lll + - funcorder + - nilnil + - gochecknoinits + settings: + godox: + # report any comments starting with keywords, this is useful for TODO or FIXME comments that + # might be left in the code accidentally and should be resolved before merging + keywords: # default keywords are TODO, BUG, and FIXME, these can be overwritten by this setting + - BUG + - FIXME + # - NOTE + # - OPTIMIZE # marks code that should be optimized before merging + # - HACK # marks hack-arounds that should be removed before merging + gosec: + # To select a subset of rules to run. + # Available rules: https://github.com/securego/gosec#available-rules + # Default: [] - means include all rules + includes: + - G101 # Look for hard coded credentials + - G102 # Bind to all interfaces + - G103 # Audit the use of unsafe block + - G104 # Audit errors not checked + - G106 # Audit the use of ssh.InsecureIgnoreHostKey + - G107 # Url provided to HTTP request as taint input + - G108 # Profiling endpoint automatically exposed on /debug/pprof + - G109 # Potential Integer overflow made by strconv.Atoi result conversion to int16/32 + - G110 # Potential DoS vulnerability via decompression bomb + - G111 # Potential directory traversal + - G112 # Potential slowloris attack + - G114 # Use of net/http serve function that has no support for setting timeouts + - G115 # Potential integer overflow when converting between integer types + - G201 # SQL query construction using format string + - G202 # SQL query construction using string concatenation + - G203 # Use of unescaped data in HTML templates + - G204 # Audit use of command execution + - G301 # Poor file permissions used when creating a directory + - G302 # Poor file permissions used with chmod + - G303 # Creating tempfile using a predictable path + - G304 # File path provided as taint input + - G305 # File traversal when extracting zip/tar archive + - G306 # Poor file permissions used when writing to a new file + - G307 # Poor file permissions used when creating a file with os.Create + - G401 # Detect the usage of MD5 or SHA1 + - G402 # Look for bad TLS connection settings + - G403 # Ensure minimum RSA key length of 2048 bits + - G404 # Insecure random number source (rand) + - G405 # Detect the usage of DES or RC4 + - G406 # Detect the usage of MD4 or RIPEMD160 + - G501 # Import blocklist: crypto/md5 + - G502 # Import blocklist: crypto/des + - G503 # Import blocklist: crypto/rc4 + - G504 # Import blocklist: net/http/cgi + - G505 # Import blocklist: crypto/sha1 + - G506 # Import blocklist: golang.org/x/crypto/md4 + - G507 # Import blocklist: golang.org/x/crypto/ripemd160 + - G601 # Implicit memory aliasing of items from a range statement + - G602 # Slice access out of bounds + govet: + enable-all: true + revive: + rules: + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#atomic + - name: atomic + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#blank-imports + - name: blank-imports + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#bool-literal-in-expr + - name: bool-literal-in-expr + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#call-to-gc + - name: call-to-gc + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#confusing-results + - name: confusing-results + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#constant-logical-expr + - name: constant-logical-expr + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#context-as-argument + - name: context-as-argument + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#context-keys-type + - name: context-keys-type + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#datarace + - name: datarace + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#deep-exit + - name: deep-exit + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#defer + - name: defer + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#dot-imports + - name: dot-imports + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#duplicated-imports + - name: duplicated-imports + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#early-return + - name: early-return + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#empty-block + - name: empty-block + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#empty-lines + - name: empty-lines + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#error-naming + - name: error-naming + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#error-return + - name: error-return + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#error-strings + - name: error-strings + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#errorf + - name: errorf + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#function-result-limit + - name: function-result-limit + severity: warning + disabled: false + arguments: [4] + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#get-return + - name: get-return + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#identical-branches + - name: identical-branches + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#if-return + - name: if-return + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#increment-decrement + - name: increment-decrement + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#indent-error-flow + - name: indent-error-flow + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#import-shadowing + - name: import-shadowing + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#modifies-parameter + - name: modifies-parameter + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#modifies-value-receiver + - name: modifies-value-receiver + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#nested-structs + - name: nested-structs + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#optimize-operands-order + - name: optimize-operands-order + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#package-comments + - name: package-comments + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#range + - name: range + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#range-val-in-closure + - name: range-val-in-closure + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#range-val-address + - name: range-val-address + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#receiver-naming + - name: receiver-naming + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#redefines-builtin-id + - name: redefines-builtin-id + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#string-of-int + - name: string-of-int + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#superfluous-else + - name: superfluous-else + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#time-equal + - name: time-equal + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#time-naming + - name: time-naming + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#var-declaration + - name: var-declaration + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unconditional-recursion + - name: unconditional-recursion + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unexported-naming + - name: unexported-naming + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unexported-return + - name: unexported-return + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unnecessary-stmt + - name: unnecessary-stmt + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unreachable-code + - name: unreachable-code + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unused-parameter + - name: unused-parameter + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#unused-receiver + - name: unused-receiver + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#useless-break + - name: useless-break + severity: warning + disabled: false + # https://github.com/mgechev/revive/blob/master/RULES_DESCRIPTIONS.md#waitgroup-by-value + - name: waitgroup-by-value + severity: warning + disabled: false + tagalign: + align: true + sort: true + order: + - mapstructure + - gorm + - validate + - binding + - json + - yaml + - yml + - toml + tagliatelle: + case: + overrides: + - pkg: ./pkg/rmf-services/business/site-package-revisions/models/parsing + rules: + yaml: snake + testpackage: + skip-regexp: _test\.go + exclusions: + generated: lax + presets: + - comments + - common-false-positives + - legacy + - std-error-handling + rules: + - linters: + - lll + - govet + path: api/.* + - linters: + - dupl + - lll + path: internal/.* + - linters: + - unparam + - noctx + - govet + - mnd + - dupl + - gosec + - goconst + path: internal/controller/.*/.*_test\.go + - linters: + - revive + - noctx + - govet + - mnd + - gosec + path: test/.* + paths: + - third_party$ + - builtin$ + - examples$ +issues: + uniq-by-line: true + fix: true +severity: + default: error + rules: + - linters: + - dupl + severity: info +formatters: + enable: + - gofmt + - goimports + - gci + - gofumpt + - golines + settings: + gofumpt: + # Choose whether to use the extra rules. + # Default: false + extra-rules: true + gofmt: + simplify: true + # Apply the rewrite rules to the source before reformatting. + # https://pkg.go.dev/cmd/gofmt + # Default: [] + rewrite-rules: + - pattern: "interface{}" + replacement: "any" + - pattern: "a[b:len(a)]" + replacement: "a[b:]" + golines: + max-len: 160 + # Shorten single-line comments. + # Default: false + shorten-comments: true + gci: + # Section configuration to compare against. + # Section names are case-insensitive and may contain parameters in (). + # The default order of sections is `standard > default > custom > blank > dot > alias > localmodule`. + # If `custom-order` is `true`, it follows the order of `sections` option. + # Default: ["standard", "default"] + sections: + - standard # Standard section: captures all standard packages. + - default # Default section: contains all imports that could not be matched to another section type. + - prefix(github.com/oxyno-zeta) # Custom section: groups all imports with the specified Prefix. + - blank # Blank section: contains all blank imports. This section is not present unless explicitly enabled. + - dot # Dot section: contains all dot imports. This section is not present unless explicitly enabled. + - alias # Alias section: contains all alias imports. This section is not present unless explicitly enabled. + - localmodule # Local module section: contains all local packages. This section is not present unless explicitly enabled. + # Checks that no inline comments are present. + # Default: false + no-inline-comments: true + # Checks that no prefix comments (comment lines above an import) are present. + # Default: false + no-prefix-comments: true + # Enable custom order of sections. + # If `true`, make the section order the same as the order of `sections`. + # Default: false + custom-order: true + # Drops lexical ordering for custom sections. + # Default: false + no-lex-order: true + exclusions: + generated: lax + paths: + - third_party$ + - builtin$ + - examples$ diff --git a/.mise.toml b/.mise.toml new file mode 100644 index 0000000..ee2b033 --- /dev/null +++ b/.mise.toml @@ -0,0 +1,7 @@ +#:schema https://mise.jdx.dev/schema/mise.json + +[tools] +go = '1.25.5' +golangci-lint = "2.7.2" +"aqua:operator-framework/operator-sdk" = "1.42.0" +"aqua:kubernetes-sigs/kind" = "0.31.0" diff --git a/Dockerfile b/Dockerfile index c389c09..17f3baa 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,5 +1,5 @@ # Build the manager binary -FROM golang:1.20 as builder +FROM golang:1.25 AS builder ARG TARGETOS ARG TARGETARCH @@ -14,7 +14,7 @@ RUN go mod download # Copy the go source COPY cmd/main.go cmd/main.go COPY api/ api/ -COPY internal/controller/ internal/controller/ +COPY internal/ internal/ # Build # the GOARCH has not a default value to allow the binary be built according to the host where the command diff --git a/Makefile b/Makefile index bc10254..6a0c7b1 100644 --- a/Makefile +++ b/Makefile @@ -3,7 +3,7 @@ # To re-generate a bundle for another specific version without changing the standard setup, you can: # - use the VERSION as arg of the bundle target (e.g make bundle VERSION=0.0.2) # - use environment variables to overwrite this value (e.g export VERSION=0.0.2) -VERSION ?= 2.0.0 +VERSION ?= 0.0.1 # CHANNELS define the bundle channels used in the bundle. # Add a new line here if you would like to change its default config. (E.g CHANNELS = "candidate,fast,stable") @@ -29,7 +29,7 @@ BUNDLE_METADATA_OPTS ?= $(BUNDLE_CHANNELS) $(BUNDLE_DEFAULT_CHANNEL) # # For example, running 'make bundle-build bundle-push catalog-build catalog-push' will build and push both # easymile.com/postgresql-operator-bundle:$VERSION and easymile.com/postgresql-operator-catalog:$VERSION. -IMAGE_TAG_BASE ?= easymile/postgresql-operator +IMAGE_TAG_BASE ?= easymile.com/postgresql-operator # BUNDLE_IMG defines the image:tag used for the bundle. # You can use it as an arg. (E.g make bundle-build BUNDLE_IMG=/:) @@ -48,12 +48,9 @@ endif # Set the Operator SDK version to use. By default, what is installed on the system is used. # This is useful for CI or a project to utilize a specific version of the operator-sdk toolkit. -OPERATOR_SDK_VERSION ?= v1.33.0 - +OPERATOR_SDK_VERSION ?= v1.42.0 # Image URL to use all building/pushing image targets IMG ?= controller:latest -# ENVTEST_K8S_VERSION refers to the version of kubebuilder assets to be downloaded by envtest binary. -ENVTEST_K8S_VERSION = 1.27.1 # Get the currently used golang install path (in GOPATH/bin, unless GOBIN is set) ifeq (,$(shell go env GOBIN)) @@ -80,7 +77,7 @@ all: build # The help target prints out all targets with their descriptions organized # beneath their categories. The categories are represented by '##@' and the -# target descriptions by '##'. The awk commands is responsible for reading the +# target descriptions by '##'. The awk command is responsible for reading the # entire set of makefiles included in this invocation, looking for lines of the # file as xyz: ## something, and then pretty-format the target and help. Then, # if there's a line with ##@ something, that gets pretty-printed as a category. @@ -112,8 +109,49 @@ vet: ## Run go vet against code. go vet ./... .PHONY: test -test: manifests generate fmt vet envtest ## Run tests. - KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test ./... -covermode=count -coverprofile cover.out -timeout 3600s +test: manifests generate fmt vet setup-envtest ## Run tests. + KUBEBUILDER_ASSETS="$(shell $(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path)" go test $$(go list ./... | grep -v /e2e) -coverprofile cover.out -timeout 3600s + +# TODO(user): To use a different vendor for e2e tests, modify the setup under 'tests/e2e'. +# The default setup assumes Kind is pre-installed and builds/loads the Manager Docker image locally. +# CertManager is installed by default; skip with: +# - CERT_MANAGER_INSTALL_SKIP=true +KIND_CLUSTER ?= postgresql-operator-test-e2e + +.PHONY: setup-test-e2e +setup-test-e2e: ## Set up a Kind cluster for e2e tests if it does not exist + @command -v $(KIND) >/dev/null 2>&1 || { \ + echo "Kind is not installed. Please install Kind manually."; \ + exit 1; \ + } + @case "$$($(KIND) get clusters)" in \ + *"$(KIND_CLUSTER)"*) \ + echo "Kind cluster '$(KIND_CLUSTER)' already exists. Skipping creation." ;; \ + *) \ + echo "Creating Kind cluster '$(KIND_CLUSTER)'..."; \ + $(KIND) create cluster --name $(KIND_CLUSTER) ;; \ + esac + +.PHONY: test-e2e +test-e2e: setup-test-e2e manifests generate fmt vet ## Run the e2e tests. Expected an isolated environment using Kind. + KIND_CLUSTER=$(KIND_CLUSTER) go test ./test/e2e/ -v -ginkgo.v + $(MAKE) cleanup-test-e2e + +.PHONY: cleanup-test-e2e +cleanup-test-e2e: ## Tear down the Kind cluster used for e2e tests + @$(KIND) delete cluster --name $(KIND_CLUSTER) + +.PHONY: lint +lint: golangci-lint ## Run golangci-lint linter + $(GOLANGCI_LINT) run + +.PHONY: lint-fix +lint-fix: golangci-lint ## Run golangci-lint linter and perform fixes + $(GOLANGCI_LINT) run --fix + +.PHONY: lint-config +lint-config: golangci-lint ## Verify golangci-lint linter configuration + $(GOLANGCI_LINT) config verify ##@ Build @@ -125,8 +163,8 @@ build: manifests generate fmt vet ## Build manager binary. run: manifests generate fmt vet ## Run a controller from your host. go run ./cmd/main.go -# If you wish built the manager image targeting other platforms you can use the --platform flag. -# (i.e. docker build --platform linux/arm64 ). However, you must enable docker buildKit for it. +# If you wish to build the manager image targeting other platforms you can use the --platform flag. +# (i.e. docker build --platform linux/arm64). However, you must enable docker buildKit for it. # More info: https://docs.docker.com/develop/develop-images/build_enhancements/ .PHONY: docker-build docker-build: ## Build docker image with the manager. @@ -136,23 +174,29 @@ docker-build: ## Build docker image with the manager. docker-push: ## Push docker image with the manager. $(CONTAINER_TOOL) push ${IMG} -# PLATFORMS defines the target platforms for the manager image be build to provide support to multiple +# PLATFORMS defines the target platforms for the manager image be built to provide support to multiple # architectures. (i.e. make docker-buildx IMG=myregistry/mypoperator:0.0.1). To use this option you need to: -# - able to use docker buildx . More info: https://docs.docker.com/build/buildx/ -# - have enable BuildKit, More info: https://docs.docker.com/develop/develop-images/build_enhancements/ -# - be able to push the image for your registry (i.e. if you do not inform a valid value via IMG=> then the export will fail) -# To properly provided solutions that supports more than one platform you should use this option. +# - be able to use docker buildx. More info: https://docs.docker.com/build/buildx/ +# - have enabled BuildKit. More info: https://docs.docker.com/develop/develop-images/build_enhancements/ +# - be able to push the image to your registry (i.e. if you do not set a valid value via IMG=> then the export will fail) +# To adequately provide solutions that are compatible with multiple platforms, you should consider using this option. PLATFORMS ?= linux/arm64,linux/amd64,linux/s390x,linux/ppc64le .PHONY: docker-buildx docker-buildx: ## Build and push docker image for the manager for cross-platform support # copy existing Dockerfile and insert --platform=${BUILDPLATFORM} into Dockerfile.cross, and preserve the original Dockerfile sed -e '1 s/\(^FROM\)/FROM --platform=\$$\{BUILDPLATFORM\}/; t' -e ' 1,// s//FROM --platform=\$$\{BUILDPLATFORM\}/' Dockerfile > Dockerfile.cross - - $(CONTAINER_TOOL) buildx create --name project-v3-builder - $(CONTAINER_TOOL) buildx use project-v3-builder + - $(CONTAINER_TOOL) buildx create --name postgresql-operator-builder + $(CONTAINER_TOOL) buildx use postgresql-operator-builder - $(CONTAINER_TOOL) buildx build --push --platform=$(PLATFORMS) --tag ${IMG} -f Dockerfile.cross . - - $(CONTAINER_TOOL) buildx rm project-v3-builder + - $(CONTAINER_TOOL) buildx rm postgresql-operator-builder rm Dockerfile.cross +.PHONY: build-installer +build-installer: manifests generate kustomize ## Generate a consolidated YAML with CRDs and deployment. + mkdir -p dist + cd config/manager && $(KUSTOMIZE) edit set image controller=${IMG} + $(KUSTOMIZE) build config/default > dist/install.yaml + ##@ Deployment ifndef ignore-not-found @@ -173,10 +217,10 @@ deploy: manifests kustomize ## Deploy controller to the K8s cluster specified in $(KUSTOMIZE) build config/default | $(KUBECTL) apply -f - .PHONY: undeploy -undeploy: ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. +undeploy: kustomize ## Undeploy controller from the K8s cluster specified in ~/.kube/config. Call with ignore-not-found=true to ignore resource not found errors during deletion. $(KUSTOMIZE) build config/default | $(KUBECTL) delete --ignore-not-found=$(ignore-not-found) -f - -##@ Build Dependencies +##@ Dependencies ## Location to install dependencies to LOCALBIN ?= $(shell pwd)/bin @@ -185,33 +229,64 @@ $(LOCALBIN): ## Tool Binaries KUBECTL ?= kubectl +KIND ?= kind KUSTOMIZE ?= $(LOCALBIN)/kustomize CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen ENVTEST ?= $(LOCALBIN)/setup-envtest +GOLANGCI_LINT = $(LOCALBIN)/golangci-lint ## Tool Versions -KUSTOMIZE_VERSION ?= v5.0.1 -CONTROLLER_TOOLS_VERSION ?= v0.14.0 +KUSTOMIZE_VERSION ?= v5.6.0 +CONTROLLER_TOOLS_VERSION ?= v0.18.0 +#ENVTEST_VERSION is the version of controller-runtime release branch to fetch the envtest setup script (i.e. release-0.20) +ENVTEST_VERSION ?= $(shell go list -m -f "{{ .Version }}" sigs.k8s.io/controller-runtime | awk -F'[v.]' '{printf "release-%d.%d", $$2, $$3}') +#ENVTEST_K8S_VERSION is the version of Kubernetes to use for setting up ENVTEST binaries (i.e. 1.31) +ENVTEST_K8S_VERSION ?= $(shell go list -m -f "{{ .Version }}" k8s.io/api | awk -F'[v.]' '{printf "1.%d", $$3}') +GOLANGCI_LINT_VERSION ?= v2.7.2 .PHONY: kustomize -kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. If wrong version is installed, it will be removed before downloading. +kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. $(KUSTOMIZE): $(LOCALBIN) - @if test -x $(LOCALBIN)/kustomize && ! $(LOCALBIN)/kustomize version | grep -q $(KUSTOMIZE_VERSION); then \ - echo "$(LOCALBIN)/kustomize version is not expected $(KUSTOMIZE_VERSION). Removing it before installing."; \ - rm -rf $(LOCALBIN)/kustomize; \ - fi - test -s $(LOCALBIN)/kustomize || GOBIN=$(LOCALBIN) GO111MODULE=on go install sigs.k8s.io/kustomize/kustomize/v5@$(KUSTOMIZE_VERSION) + $(call go-install-tool,$(KUSTOMIZE),sigs.k8s.io/kustomize/kustomize/v5,$(KUSTOMIZE_VERSION)) .PHONY: controller-gen -controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. If wrong version is installed, it will be overwritten. +controller-gen: $(CONTROLLER_GEN) ## Download controller-gen locally if necessary. $(CONTROLLER_GEN): $(LOCALBIN) - test -s $(LOCALBIN)/controller-gen && $(LOCALBIN)/controller-gen --version | grep -q $(CONTROLLER_TOOLS_VERSION) || \ - GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-tools/cmd/controller-gen@$(CONTROLLER_TOOLS_VERSION) + $(call go-install-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen,$(CONTROLLER_TOOLS_VERSION)) + +.PHONY: setup-envtest +setup-envtest: envtest ## Download the binaries required for ENVTEST in the local bin directory. + @echo "Setting up envtest binaries for Kubernetes version $(ENVTEST_K8S_VERSION)..." + @$(ENVTEST) use $(ENVTEST_K8S_VERSION) --bin-dir $(LOCALBIN) -p path || { \ + echo "Error: Failed to set up envtest binaries for version $(ENVTEST_K8S_VERSION)."; \ + exit 1; \ + } .PHONY: envtest -envtest: $(ENVTEST) ## Download envtest-setup locally if necessary. +envtest: $(ENVTEST) ## Download setup-envtest locally if necessary. $(ENVTEST): $(LOCALBIN) - test -s $(LOCALBIN)/setup-envtest || GOBIN=$(LOCALBIN) go install sigs.k8s.io/controller-runtime/tools/setup-envtest@latest + $(call go-install-tool,$(ENVTEST),sigs.k8s.io/controller-runtime/tools/setup-envtest,$(ENVTEST_VERSION)) + +.PHONY: golangci-lint +golangci-lint: $(GOLANGCI_LINT) ## Download golangci-lint locally if necessary. +$(GOLANGCI_LINT): $(LOCALBIN) + $(call go-install-tool,$(GOLANGCI_LINT),github.com/golangci/golangci-lint/v2/cmd/golangci-lint,$(GOLANGCI_LINT_VERSION)) + +# go-install-tool will 'go install' any package with custom target and name of binary, if it doesn't exist +# $1 - target path with name of binary +# $2 - package url which can be installed +# $3 - specific version of package +define go-install-tool +@[ -f "$(1)-$(3)" ] || { \ +set -e; \ +package=$(2)@$(3) ;\ +echo "Downloading $${package}" ;\ +rm -f $(1) || true ;\ +GOBIN=$(LOCALBIN) go install $${package} ;\ +mv $(1) $(1)-$(3) ;\ +} ;\ +ln -sf $(1)-$(3) $(1) +endef .PHONY: operator-sdk OPERATOR_SDK ?= $(LOCALBIN)/operator-sdk @@ -239,14 +314,14 @@ bundle: manifests kustomize operator-sdk ## Generate bundle manifests and metada .PHONY: bundle-build bundle-build: ## Build the bundle image. - docker build -f bundle.Dockerfile -t $(BUNDLE_IMG) . + $(CONTAINER_TOOL) build -f bundle.Dockerfile -t $(BUNDLE_IMG) . .PHONY: bundle-push bundle-push: ## Push the bundle image. $(MAKE) docker-push IMG=$(BUNDLE_IMG) .PHONY: opm -OPM = ./bin/opm +OPM = $(LOCALBIN)/opm opm: ## Download opm locally if necessary. ifeq (,$(wildcard $(OPM))) ifeq (,$(shell which opm 2>/dev/null)) @@ -254,7 +329,7 @@ ifeq (,$(shell which opm 2>/dev/null)) set -e ;\ mkdir -p $(dir $(OPM)) ;\ OS=$(shell go env GOOS) && ARCH=$(shell go env GOARCH) && \ - curl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/v1.23.0/$${OS}-$${ARCH}-opm ;\ + curl -sSLo $(OPM) https://github.com/operator-framework/operator-registry/releases/download/v1.55.0/$${OS}-$${ARCH}-opm ;\ chmod +x $(OPM) ;\ } else @@ -279,59 +354,28 @@ endif # https://github.com/operator-framework/community-operators/blob/7f1438c/docs/packaging-operator.md#updating-your-existing-operator .PHONY: catalog-build catalog-build: opm ## Build a catalog image. - $(OPM) index add --container-tool docker --mode semver --tag $(CATALOG_IMG) --bundles $(BUNDLE_IMGS) $(FROM_INDEX_OPT) + $(OPM) index add --container-tool $(CONTAINER_TOOL) --mode semver --tag $(CATALOG_IMG) --bundles $(BUNDLE_IMGS) $(FROM_INDEX_OPT) # Push the catalog image. .PHONY: catalog-push catalog-push: ## Push a catalog image. $(MAKE) docker-push IMG=$(CATALOG_IMG) -# EasyMile commands - -HAS_GOLANGCI_LINT := $(shell command -v golangci-lint;) -HAS_CURL:=$(shell command -v curl;) +# EasyMile commands .PHONY: down/services down/services: @echo "Down services" docker rm -f postgres || true -.PHONY: down/dev-services -down/dev-services: - @echo "Down dev services" - kind delete cluster - -.PHONY: setup/dev-services -setup/dev-services: - @echo "Setup dev services" - kind create cluster - .PHONY: setup/services setup/services: down/services @echo "Setup services" docker run -d --rm --name postgres -p 5432:5432 -p 5433:5432 -e POSTGRES_PASSWORD=postgres -e PGDATA=/var/lib/postgresql/data/pgdata -v $(CURDIR)/.run/postgres:/var/lib/postgresql/data postgres:16 postgres -c wal_level=logical -.PHONY: code/lint -code/lint: setup/dep/install - golangci-lint run ./... - .PHONY: test/coverage test/coverage: cat cover.out | grep -v "mock_" > c.out go tool cover -html=c.out -o coverage.html - go tool cover -func c.out - -.PHONY: test/all -test/all: test - -.PHONY: setup/dep/install -setup/dep/install: -ifndef HAS_GOLANGCI_LINT - @echo "=> Installing golangci-lint tool" -ifndef HAS_CURL - $(error You must install curl) -endif - curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(shell go env GOPATH)/bin v1.56.2 -endif - + go tool cover -func c.out \ No newline at end of file diff --git a/PROJECT b/PROJECT index 93004f9..01136b2 100644 --- a/PROJECT +++ b/PROJECT @@ -4,8 +4,7 @@ # More info: https://book.kubebuilder.io/reference/project-config.html domain: easymile.com layout: -- go.kubebuilder.io/v4 -multigroup: true + - go.kubebuilder.io/v4 plugins: grafana.kubebuilder.io/v1-alpha: {} manifests.sdk.operatorframework.io/v2: {} @@ -13,40 +12,40 @@ plugins: projectName: postgresql-operator repo: github.com/easymile/postgresql-operator resources: -- api: - crdVersion: v1 - namespaced: true - controller: true - domain: easymile.com - group: postgresql - kind: PostgresqlEngineConfiguration - path: github.com/easymile/postgresql-operator/apis/postgresql/v1alpha1 - version: v1alpha1 -- api: - crdVersion: v1 - namespaced: true - controller: true - domain: easymile.com - group: postgresql - kind: PostgresqlDatabase - path: github.com/easymile/postgresql-operator/apis/postgresql/v1alpha1 - version: v1alpha1 -- api: - crdVersion: v1 - namespaced: true - controller: true - domain: easymile.com - group: postgresql - kind: PostgresqlUserRole - path: github.com/easymile/postgresql-operator/apis/postgresql/v1alpha1 - version: v1alpha1 -- api: - crdVersion: v1 - namespaced: true - controller: true - domain: easymile.com - group: postgresql - kind: PostgresqlPublication - path: github.com/easymile/postgresql-operator/api/postgresql/v1alpha1 - version: v1alpha1 + - api: + crdVersion: v1 + namespaced: true + controller: true + domain: easymile.com + group: postgresql + kind: PostgresqlEngineConfiguration + path: github.com/easymile/postgresql-operator/apis/postgresql/v1alpha1 + version: v1alpha1 + - api: + crdVersion: v1 + namespaced: true + controller: true + domain: easymile.com + group: postgresql + kind: PostgresqlDatabase + path: github.com/easymile/postgresql-operator/apis/postgresql/v1alpha1 + version: v1alpha1 + - api: + crdVersion: v1 + namespaced: true + controller: true + domain: easymile.com + group: postgresql + kind: PostgresqlUserRole + path: github.com/easymile/postgresql-operator/apis/postgresql/v1alpha1 + version: v1alpha1 + - api: + crdVersion: v1 + namespaced: true + controller: true + domain: easymile.com + group: postgresql + kind: PostgresqlPublication + path: github.com/easymile/postgresql-operator/api/postgresql/v1alpha1 + version: v1alpha1 version: "3" diff --git a/api/postgresql/v1alpha1/postgresqldatabase_types.go b/api/postgresql/v1alpha1/postgresqldatabase_types.go index 481fe94..1c300ad 100644 --- a/api/postgresql/v1alpha1/postgresqldatabase_types.go +++ b/api/postgresql/v1alpha1/postgresqldatabase_types.go @@ -17,8 +17,9 @@ limitations under the License. package v1alpha1 import ( - "github.com/easymile/postgresql-operator/api/postgresql/common" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/easymile/postgresql-operator/api/postgresql/common" ) // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! @@ -73,9 +74,11 @@ type DatabaseModulesList struct { type DatabaseStatusPhase string -const DatabaseNoPhase DatabaseStatusPhase = "" -const DatabaseFailedPhase DatabaseStatusPhase = "Failed" -const DatabaseCreatedPhase DatabaseStatusPhase = "Created" +const ( + DatabaseNoPhase DatabaseStatusPhase = "" + DatabaseFailedPhase DatabaseStatusPhase = "Failed" + DatabaseCreatedPhase DatabaseStatusPhase = "Created" +) // PostgresqlDatabaseStatus defines the observed state of PostgresqlDatabase. type PostgresqlDatabaseStatus struct { diff --git a/api/postgresql/v1alpha1/postgresqlengineconfiguration_types.go b/api/postgresql/v1alpha1/postgresqlengineconfiguration_types.go index da1067a..58bb6d3 100644 --- a/api/postgresql/v1alpha1/postgresqlengineconfiguration_types.go +++ b/api/postgresql/v1alpha1/postgresqlengineconfiguration_types.go @@ -25,9 +25,11 @@ import ( type ProviderType string -const NoProvider ProviderType = "" -const AWSProvider ProviderType = "AWS" -const AzureProvider ProviderType = "AZURE" +const ( + NoProvider ProviderType = "" + AWSProvider ProviderType = "AWS" + AzureProvider ProviderType = "AZURE" +) // PostgresqlEngineConfigurationSpec defines the desired state of PostgresqlEngineConfiguration. type PostgresqlEngineConfigurationSpec struct { @@ -99,9 +101,11 @@ type GenericUserConnection struct { type EngineStatusPhase string -const EngineNoPhase EngineStatusPhase = "" -const EngineFailedPhase EngineStatusPhase = "Failed" -const EngineValidatedPhase EngineStatusPhase = "Validated" +const ( + EngineNoPhase EngineStatusPhase = "" + EngineFailedPhase EngineStatusPhase = "Failed" + EngineValidatedPhase EngineStatusPhase = "Validated" +) // PostgresqlEngineConfigurationStatus defines the observed state of PostgresqlEngineConfiguration. type PostgresqlEngineConfigurationStatus struct { diff --git a/api/postgresql/v1alpha1/postgresqlpublication_types.go b/api/postgresql/v1alpha1/postgresqlpublication_types.go index c8f798f..bd910bf 100644 --- a/api/postgresql/v1alpha1/postgresqlpublication_types.go +++ b/api/postgresql/v1alpha1/postgresqlpublication_types.go @@ -17,8 +17,9 @@ limitations under the License. package v1alpha1 import ( - "github.com/easymile/postgresql-operator/api/postgresql/common" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/easymile/postgresql-operator/api/postgresql/common" ) // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! @@ -84,9 +85,11 @@ type PostgresqlPublicationWith struct { type PublicationStatusPhase string -const PublicationNoPhase PublicationStatusPhase = "" -const PublicationFailedPhase PublicationStatusPhase = "Failed" -const PublicationCreatedPhase PublicationStatusPhase = "Created" +const ( + PublicationNoPhase PublicationStatusPhase = "" + PublicationFailedPhase PublicationStatusPhase = "Failed" + PublicationCreatedPhase PublicationStatusPhase = "Created" +) // PostgresqlPublicationStatus defines the observed state of PostgresqlPublication. type PostgresqlPublicationStatus struct { diff --git a/api/postgresql/v1alpha1/postgresqluserrole_types.go b/api/postgresql/v1alpha1/postgresqluserrole_types.go index 6013681..6f4aa49 100644 --- a/api/postgresql/v1alpha1/postgresqluserrole_types.go +++ b/api/postgresql/v1alpha1/postgresqluserrole_types.go @@ -17,8 +17,9 @@ limitations under the License. package v1alpha1 import ( - "github.com/easymile/postgresql-operator/api/postgresql/common" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "github.com/easymile/postgresql-operator/api/postgresql/common" ) // EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! @@ -26,14 +27,18 @@ import ( type PrivilegesSpecEnum string -const OwnerPrivilege PrivilegesSpecEnum = "OWNER" -const ReaderPrivilege PrivilegesSpecEnum = "READER" -const WriterPrivilege PrivilegesSpecEnum = "WRITER" +const ( + OwnerPrivilege PrivilegesSpecEnum = "OWNER" + ReaderPrivilege PrivilegesSpecEnum = "READER" + WriterPrivilege PrivilegesSpecEnum = "WRITER" +) type ConnectionTypesSpecEnum string -const PrimaryConnectionType ConnectionTypesSpecEnum = "PRIMARY" -const BouncerConnectionType ConnectionTypesSpecEnum = "BOUNCER" +const ( + PrimaryConnectionType ConnectionTypesSpecEnum = "PRIMARY" + BouncerConnectionType ConnectionTypesSpecEnum = "BOUNCER" +) type PostgresqlUserRolePrivilege struct { // User Connection type. @@ -75,8 +80,10 @@ type PostgresqlUserRoleAttributes struct { type ModeEnum string -const ProvidedMode ModeEnum = "PROVIDED" -const ManagedMode ModeEnum = "MANAGED" +const ( + ProvidedMode ModeEnum = "PROVIDED" + ManagedMode ModeEnum = "MANAGED" +) // PostgresqlUserRoleSpec defines the desired state of PostgresqlUserRole. type PostgresqlUserRoleSpec struct { @@ -113,9 +120,11 @@ type PostgresqlUserRoleSpec struct { type UserRoleStatusPhase string -const UserRoleNoPhase UserRoleStatusPhase = "" -const UserRoleFailedPhase UserRoleStatusPhase = "Failed" -const UserRoleCreatedPhase UserRoleStatusPhase = "Created" +const ( + UserRoleNoPhase UserRoleStatusPhase = "" + UserRoleFailedPhase UserRoleStatusPhase = "Failed" + UserRoleCreatedPhase UserRoleStatusPhase = "Created" +) // PostgresqlUserRoleStatus defines the observed state of PostgresqlUserRole. type PostgresqlUserRoleStatus struct { diff --git a/api/postgresql/v1alpha1/zz_generated.deepcopy.go b/api/postgresql/v1alpha1/zz_generated.deepcopy.go index e7d4b7a..05d6952 100644 --- a/api/postgresql/v1alpha1/zz_generated.deepcopy.go +++ b/api/postgresql/v1alpha1/zz_generated.deepcopy.go @@ -1,7 +1,7 @@ //go:build !ignore_autogenerated /* -Copyright 2022. +Copyright 2026. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/cmd/main.go b/cmd/main.go index 610bbeb..52b33c7 100644 --- a/cmd/main.go +++ b/cmd/main.go @@ -1,5 +1,5 @@ /* -Copyright 2022. +Copyright 2026. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -17,28 +17,33 @@ limitations under the License. package main import ( + "crypto/tls" "flag" "fmt" "os" + "path/filepath" "time" + "github.com/prometheus/client_golang/prometheus" + "k8s.io/apimachinery/pkg/runtime" + "sigs.k8s.io/controller-runtime/pkg/cache" + "sigs.k8s.io/controller-runtime/pkg/certwatcher" + "sigs.k8s.io/controller-runtime/pkg/healthz" + "sigs.k8s.io/controller-runtime/pkg/log/zap" + "sigs.k8s.io/controller-runtime/pkg/metrics/filters" + "sigs.k8s.io/controller-runtime/pkg/webhook" + // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) // to ensure that exec-entrypoint and run can make use of them. _ "k8s.io/client-go/plugin/pkg/client/auth" - "k8s.io/apimachinery/pkg/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime" clientgoscheme "k8s.io/client-go/kubernetes/scheme" ctrl "sigs.k8s.io/controller-runtime" - "sigs.k8s.io/controller-runtime/pkg/healthz" - "sigs.k8s.io/controller-runtime/pkg/log/zap" - "sigs.k8s.io/controller-runtime/pkg/metrics" - - "github.com/prometheus/client_golang/prometheus" + metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" postgresqlv1alpha1 "github.com/easymile/postgresql-operator/api/postgresql/v1alpha1" postgresqlcontrollers "github.com/easymile/postgresql-operator/internal/controller/postgresql" - //+kubebuilder:scaffold:imports ) var ( @@ -54,27 +59,45 @@ var ( ) func init() { - // Register custom metrics with the global prometheus registry - metrics.Registry.MustRegister(controllerRuntimeDetailedErrorTotal) - utilruntime.Must(clientgoscheme.AddToScheme(scheme)) utilruntime.Must(postgresqlv1alpha1.AddToScheme(scheme)) - //+kubebuilder:scaffold:scheme -} //nolint: wsl // Needed by operator + // +kubebuilder:scaffold:scheme +} +//nolint:gocyclo func main() { - var metricsAddr, probeAddr, resyncPeriodStr, reconcileTimeoutStr string - - var enableLeaderElection bool + var ( + metricsAddr string + metricsCertPath, metricsCertName, metricsCertKey string + webhookCertPath, webhookCertName, webhookCertKey string + enableLeaderElection bool + probeAddr string + resyncPeriodStr, reconcileTimeoutStr string + secureMetrics bool + enableHTTP2 bool + tlsOpts []func(*tls.Config) + ) - flag.StringVar(&metricsAddr, "metrics-bind-address", ":8080", "The address the metric endpoint binds to.") + flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+ + "Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.") flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.") - flag.StringVar(&resyncPeriodStr, "resync-period", "60s", "The resync period to reload all resources for auto-heal procedures.") - flag.StringVar(&reconcileTimeoutStr, "reconcile-timeout", "10s", "The reconcile max timeout.") flag.BoolVar(&enableLeaderElection, "leader-elect", false, "Enable leader election for controller manager. "+ "Enabling this will ensure there is only one active controller manager.") + flag.BoolVar(&secureMetrics, "metrics-secure", true, + "If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.") + flag.StringVar(&webhookCertPath, "webhook-cert-path", "", "The directory that contains the webhook certificate.") + flag.StringVar(&webhookCertName, "webhook-cert-name", "tls.crt", "The name of the webhook certificate file.") + flag.StringVar(&webhookCertKey, "webhook-cert-key", "tls.key", "The name of the webhook key file.") + flag.StringVar(&metricsCertPath, "metrics-cert-path", "", + "The directory that contains the metrics server certificate.") + flag.StringVar(&metricsCertName, "metrics-cert-name", "tls.crt", "The name of the metrics server certificate file.") + flag.StringVar(&metricsCertKey, "metrics-cert-key", "tls.key", "The name of the metrics server key file.") + flag.BoolVar(&enableHTTP2, "enable-http2", false, + "If set, HTTP/2 will be enabled for the metrics and webhook servers") + flag.StringVar(&resyncPeriodStr, "resync-period", "60s", "The resync period to reload all resources for auto-heal procedures.") + flag.StringVar(&reconcileTimeoutStr, "reconcile-timeout", "10s", "The reconcile max timeout.") opts := zap.Options{ Development: false, @@ -84,6 +107,98 @@ func main() { ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts))) + // if the enable-http2 flag is false (the default), http/2 should be disabled + // due to its vulnerabilities. More specifically, disabling http/2 will + // prevent from being vulnerable to the HTTP/2 Stream Cancellation and + // Rapid Reset CVEs. For more information see: + // - https://github.com/advisories/GHSA-qppj-fm5r-hxr3 + // - https://github.com/advisories/GHSA-4374-p667-p6c8 + disableHTTP2 := func(c *tls.Config) { + setupLog.Info("disabling http/2") + + c.NextProtos = []string{"http/1.1"} + } + + if !enableHTTP2 { + tlsOpts = append(tlsOpts, disableHTTP2) + } + + // Create watchers for metrics and webhooks certificates + var metricsCertWatcher, webhookCertWatcher *certwatcher.CertWatcher + + // Initial webhook TLS options + webhookTLSOpts := tlsOpts + + if len(webhookCertPath) > 0 { + setupLog.Info("Initializing webhook certificate watcher using provided certificates", + "webhook-cert-path", webhookCertPath, "webhook-cert-name", webhookCertName, "webhook-cert-key", webhookCertKey) + + var err error + + webhookCertWatcher, err = certwatcher.New( + filepath.Join(webhookCertPath, webhookCertName), + filepath.Join(webhookCertPath, webhookCertKey), + ) + if err != nil { + setupLog.Error(err, "Failed to initialize webhook certificate watcher") + os.Exit(1) + } + + webhookTLSOpts = append(webhookTLSOpts, func(config *tls.Config) { + config.GetCertificate = webhookCertWatcher.GetCertificate + }) + } + + webhookServer := webhook.NewServer(webhook.Options{ + TLSOpts: webhookTLSOpts, + }) + + // Metrics endpoint is enabled in 'config/default/kustomization.yaml'. The Metrics options configure the server. + // More info: + // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.21.0/pkg/metrics/server + // - https://book.kubebuilder.io/reference/metrics.html + metricsServerOptions := metricsserver.Options{ + BindAddress: metricsAddr, + SecureServing: secureMetrics, + TLSOpts: tlsOpts, + } + + if secureMetrics { + // FilterProvider is used to protect the metrics endpoint with authn/authz. + // These configurations ensure that only authorized users and service accounts + // can access the metrics endpoint. The RBAC are configured in 'config/rbac/kustomization.yaml'. More info: + // https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.21.0/pkg/metrics/filters#WithAuthenticationAndAuthorization + metricsServerOptions.FilterProvider = filters.WithAuthenticationAndAuthorization + } + + // If the certificate is not specified, controller-runtime will automatically + // generate self-signed certificates for the metrics server. While convenient for development and testing, + // this setup is not recommended for production. + // + // TODO(user): If you enable certManager, uncomment the following lines: + // - [METRICS-WITH-CERTS] at config/default/kustomization.yaml to generate and use certificates + // managed by cert-manager for the metrics server. + // - [PROMETHEUS-WITH-CERTS] at config/prometheus/kustomization.yaml for TLS certification. + if len(metricsCertPath) > 0 { + setupLog.Info("Initializing metrics certificate watcher using provided certificates", + "metrics-cert-path", metricsCertPath, "metrics-cert-name", metricsCertName, "metrics-cert-key", metricsCertKey) + + var err error + + metricsCertWatcher, err = certwatcher.New( + filepath.Join(metricsCertPath, metricsCertName), + filepath.Join(metricsCertPath, metricsCertKey), + ) + if err != nil { + setupLog.Error(err, "to initialize metrics certificate watcher", "error", err) + os.Exit(1) + } + + metricsServerOptions.TLSOpts = append(metricsServerOptions.TLSOpts, func(config *tls.Config) { + config.GetCertificate = metricsCertWatcher.GetCertificate + }) + } + // Parse duration resyncPeriod, err := time.ParseDuration(resyncPeriodStr) // Check error @@ -103,10 +218,10 @@ func main() { mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ Scheme: scheme, - MetricsBindAddress: metricsAddr, - Port: 9443, //nolint: gomnd // Because generated + Metrics: metricsServerOptions, + WebhookServer: webhookServer, HealthProbeBindAddress: probeAddr, - SyncPeriod: &resyncPeriod, + Cache: cache.Options{SyncPeriod: &resyncPeriod}, LeaderElection: enableLeaderElection, LeaderElectionID: "07c031df.easymile.com", // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily @@ -205,7 +320,25 @@ func main() { setupLog.Error(err, "unable to create controller", "controller", "PostgresqlPublication") os.Exit(1) } - //+kubebuilder:scaffold:builder + // +kubebuilder:scaffold:builder + + if metricsCertWatcher != nil { + setupLog.Info("Adding metrics certificate watcher to manager") + + if err := mgr.Add(metricsCertWatcher); err != nil { + setupLog.Error(err, "unable to add metrics certificate watcher to manager") + os.Exit(1) + } + } + + if webhookCertWatcher != nil { + setupLog.Info("Adding webhook certificate watcher to manager") + + if err := mgr.Add(webhookCertWatcher); err != nil { + setupLog.Error(err, "unable to add webhook certificate watcher to manager") + os.Exit(1) + } + } if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { setupLog.Error(err, "unable to set up health check") diff --git a/config/crd/bases/postgresql.easymile.com_postgresqldatabases.yaml b/config/crd/bases/postgresql.easymile.com_postgresqldatabases.yaml index 7292fc9..e66b36c 100644 --- a/config/crd/bases/postgresql.easymile.com_postgresqldatabases.yaml +++ b/config/crd/bases/postgresql.easymile.com_postgresqldatabases.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.18.0 name: postgresqldatabases.postgresql.easymile.com spec: group: postgresql.easymile.com diff --git a/config/crd/bases/postgresql.easymile.com_postgresqlengineconfigurations.yaml b/config/crd/bases/postgresql.easymile.com_postgresqlengineconfigurations.yaml index fe62d07..62df5d5 100644 --- a/config/crd/bases/postgresql.easymile.com_postgresqlengineconfigurations.yaml +++ b/config/crd/bases/postgresql.easymile.com_postgresqlengineconfigurations.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.18.0 name: postgresqlengineconfigurations.postgresql.easymile.com spec: group: postgresql.easymile.com diff --git a/config/crd/bases/postgresql.easymile.com_postgresqlpublications.yaml b/config/crd/bases/postgresql.easymile.com_postgresqlpublications.yaml index 32977f0..4c33bce 100644 --- a/config/crd/bases/postgresql.easymile.com_postgresqlpublications.yaml +++ b/config/crd/bases/postgresql.easymile.com_postgresqlpublications.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.18.0 name: postgresqlpublications.postgresql.easymile.com spec: group: postgresql.easymile.com diff --git a/config/crd/bases/postgresql.easymile.com_postgresqluserroles.yaml b/config/crd/bases/postgresql.easymile.com_postgresqluserroles.yaml index 2d80e67..2e3cb54 100644 --- a/config/crd/bases/postgresql.easymile.com_postgresqluserroles.yaml +++ b/config/crd/bases/postgresql.easymile.com_postgresqluserroles.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.14.0 + controller-gen.kubebuilder.io/version: v0.18.0 name: postgresqluserroles.postgresql.easymile.com spec: group: postgresql.easymile.com @@ -147,6 +147,7 @@ spec: description: Simple user password tuple generated secret name type: string required: + - mode - privileges type: object status: diff --git a/config/crd/kustomization.yaml b/config/crd/kustomization.yaml index 677d86d..8c9e94d 100644 --- a/config/crd/kustomization.yaml +++ b/config/crd/kustomization.yaml @@ -5,7 +5,7 @@ resources: - bases/postgresql.easymile.com_postgresqlengineconfigurations.yaml - bases/postgresql.easymile.com_postgresqldatabases.yaml - bases/postgresql.easymile.com_postgresqluserroles.yaml -- bases/postgresql.easymile.com_postgresqlpublications.yaml + - bases/postgresql.easymile.com_postgresqlpublications.yaml #+kubebuilder:scaffold:crdkustomizeresource patchesStrategicMerge: diff --git a/config/default/cert_metrics_manager_patch.yaml b/config/default/cert_metrics_manager_patch.yaml new file mode 100644 index 0000000..d975015 --- /dev/null +++ b/config/default/cert_metrics_manager_patch.yaml @@ -0,0 +1,30 @@ +# This patch adds the args, volumes, and ports to allow the manager to use the metrics-server certs. + +# Add the volumeMount for the metrics-server certs +- op: add + path: /spec/template/spec/containers/0/volumeMounts/- + value: + mountPath: /tmp/k8s-metrics-server/metrics-certs + name: metrics-certs + readOnly: true + +# Add the --metrics-cert-path argument for the metrics server +- op: add + path: /spec/template/spec/containers/0/args/- + value: --metrics-cert-path=/tmp/k8s-metrics-server/metrics-certs + +# Add the metrics-server certs volume configuration +- op: add + path: /spec/template/spec/volumes/- + value: + name: metrics-certs + secret: + secretName: metrics-server-cert + optional: false + items: + - key: ca.crt + path: ca.crt + - key: tls.crt + path: tls.crt + - key: tls.key + path: tls.key diff --git a/config/default/kustomization.yaml b/config/default/kustomization.yaml index 612a4ae..dd13e4a 100644 --- a/config/default/kustomization.yaml +++ b/config/default/kustomization.yaml @@ -15,130 +15,219 @@ namePrefix: postgresql-operator- # someName: someValue resources: -- ../crd -- ../rbac -- ../manager -# [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in -# crd/kustomization.yaml -#- ../webhook -# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. -#- ../certmanager -# [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. -#- ../prometheus - -patchesStrategicMerge: -# Protect the /metrics endpoint by putting it behind auth. -# If you want your controller-manager to expose the /metrics -# endpoint w/o any authn/z, please comment the following line. -- manager_auth_proxy_patch.yaml - + # [METRICS] Expose the controller manager metrics service. + - metrics_service.yaml + - ../crd + - ../rbac + - ../manager + # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in + # crd/kustomization.yaml + #- ../webhook + # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required. + #- ../certmanager + # [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'. + #- ../prometheus +# [NETWORK POLICY] Protect the /metrics endpoint and Webhook Server with NetworkPolicy. +# Only Pod(s) running a namespace labeled with 'metrics: enabled' will be able to gather the metrics. +# Only CR(s) which requires webhooks and are applied on namespaces labeled with 'webhooks: enabled' will +# be able to communicate with the Webhook Server. +#- ../network-policy +# Uncomment the patches line if you enable Metrics +patches: + # [METRICS] The following patch will enable the metrics endpoint using HTTPS and the port :8443. + # More info: https://book.kubebuilder.io/reference/metrics + - path: manager_metrics_patch.yaml + target: + kind: Deployment +# Uncomment the patches line if you enable Metrics and CertManager +# [METRICS-WITH-CERTS] To enable metrics protected with certManager, uncomment the following line. +# This patch will protect the metrics with certManager self-signed certs. +#- path: cert_metrics_manager_patch.yaml +# target: +# kind: Deployment # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in # crd/kustomization.yaml -#- manager_webhook_patch.yaml - -# [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. -# Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks. -# 'CERTMANAGER' needs to be enabled to use ca injection -#- webhookcainjection_patch.yaml +#- path: manager_webhook_patch.yaml +# target: +# kind: Deployment # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix. # Uncomment the following replacements to add the cert-manager CA injection annotations #replacements: -# - source: # Add cert-manager annotation to ValidatingWebhookConfiguration, MutatingWebhookConfiguration and CRDs -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert # this name should match the one in certificate.yaml -# fieldPath: .metadata.namespace # namespace of the certificate CR -# targets: -# - select: -# kind: ValidatingWebhookConfiguration -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 0 -# create: true -# - select: -# kind: MutatingWebhookConfiguration -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 0 -# create: true -# - select: -# kind: CustomResourceDefinition -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 0 -# create: true -# - source: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# name: serving-cert # this name should match the one in certificate.yaml -# fieldPath: .metadata.name -# targets: -# - select: -# kind: ValidatingWebhookConfiguration -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 1 -# create: true -# - select: -# kind: MutatingWebhookConfiguration -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 1 -# create: true -# - select: -# kind: CustomResourceDefinition -# fieldPaths: -# - .metadata.annotations.[cert-manager.io/inject-ca-from] -# options: -# delimiter: '/' -# index: 1 -# create: true -# - source: # Add cert-manager annotation to the webhook Service -# kind: Service -# version: v1 -# name: webhook-service -# fieldPath: .metadata.name # namespace of the service -# targets: -# - select: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# fieldPaths: -# - .spec.dnsNames.0 -# - .spec.dnsNames.1 -# options: -# delimiter: '.' -# index: 0 -# create: true -# - source: -# kind: Service -# version: v1 -# name: webhook-service -# fieldPath: .metadata.namespace # namespace of the service -# targets: -# - select: -# kind: Certificate -# group: cert-manager.io -# version: v1 -# fieldPaths: -# - .spec.dnsNames.0 -# - .spec.dnsNames.1 -# options: -# delimiter: '.' -# index: 1 -# create: true +# - source: # Uncomment the following block to enable certificates for metrics +# kind: Service +# version: v1 +# name: controller-manager-metrics-service +# fieldPath: metadata.name +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: metrics-certs +# fieldPaths: +# - spec.dnsNames.0 +# - spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 0 +# create: true +# - select: # Uncomment the following to set the Service name for TLS config in Prometheus ServiceMonitor +# kind: ServiceMonitor +# group: monitoring.coreos.com +# version: v1 +# name: controller-manager-metrics-monitor +# fieldPaths: +# - spec.endpoints.0.tlsConfig.serverName +# options: +# delimiter: '.' +# index: 0 +# create: true +# +# - source: +# kind: Service +# version: v1 +# name: controller-manager-metrics-service +# fieldPath: metadata.namespace +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: metrics-certs +# fieldPaths: +# - spec.dnsNames.0 +# - spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 1 +# create: true +# - select: # Uncomment the following to set the Service namespace for TLS in Prometheus ServiceMonitor +# kind: ServiceMonitor +# group: monitoring.coreos.com +# version: v1 +# name: controller-manager-metrics-monitor +# fieldPaths: +# - spec.endpoints.0.tlsConfig.serverName +# options: +# delimiter: '.' +# index: 1 +# create: true +# +# - source: # Uncomment the following block if you have any webhook +# kind: Service +# version: v1 +# name: webhook-service +# fieldPath: .metadata.name # Name of the service +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPaths: +# - .spec.dnsNames.0 +# - .spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 0 +# create: true +# - source: +# kind: Service +# version: v1 +# name: webhook-service +# fieldPath: .metadata.namespace # Namespace of the service +# targets: +# - select: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPaths: +# - .spec.dnsNames.0 +# - .spec.dnsNames.1 +# options: +# delimiter: '.' +# index: 1 +# create: true +# +# - source: # Uncomment the following block if you have a ValidatingWebhook (--programmatic-validation) +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert # This name should match the one in certificate.yaml +# fieldPath: .metadata.namespace # Namespace of the certificate CR +# targets: +# - select: +# kind: ValidatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 0 +# create: true +# - source: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPath: .metadata.name +# targets: +# - select: +# kind: ValidatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 1 +# create: true +# +# - source: # Uncomment the following block if you have a DefaultingWebhook (--defaulting ) +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPath: .metadata.namespace # Namespace of the certificate CR +# targets: +# - select: +# kind: MutatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 0 +# create: true +# - source: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPath: .metadata.name +# targets: +# - select: +# kind: MutatingWebhookConfiguration +# fieldPaths: +# - .metadata.annotations.[cert-manager.io/inject-ca-from] +# options: +# delimiter: '/' +# index: 1 +# create: true +# +# - source: # Uncomment the following block if you have a ConversionWebhook (--conversion) +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPath: .metadata.namespace # Namespace of the certificate CR +# targets: # Do not remove or uncomment the following scaffold marker; required to generate code for target CRD. +# +kubebuilder:scaffold:crdkustomizecainjectionns +# - source: +# kind: Certificate +# group: cert-manager.io +# version: v1 +# name: serving-cert +# fieldPath: .metadata.name +# targets: # Do not remove or uncomment the following scaffold marker; required to generate code for target CRD. +# +kubebuilder:scaffold:crdkustomizecainjectionname diff --git a/config/default/manager_auth_proxy_patch.yaml b/config/default/manager_auth_proxy_patch.yaml deleted file mode 100644 index 73fad2a..0000000 --- a/config/default/manager_auth_proxy_patch.yaml +++ /dev/null @@ -1,39 +0,0 @@ -# This patch inject a sidecar container which is a HTTP proxy for the -# controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews. -apiVersion: apps/v1 -kind: Deployment -metadata: - name: controller-manager - namespace: system -spec: - template: - spec: - containers: - - name: kube-rbac-proxy - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - "ALL" - image: gcr.io/kubebuilder/kube-rbac-proxy:v0.14.1 - args: - - "--secure-listen-address=0.0.0.0:8443" - - "--upstream=http://127.0.0.1:8080/" - - "--logtostderr=true" - - "--v=0" - ports: - - containerPort: 8443 - protocol: TCP - name: https - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 5m - memory: 64Mi - - name: manager - args: - - "--health-probe-bind-address=:8081" - - "--metrics-bind-address=127.0.0.1:8080" - - "--leader-elect" diff --git a/config/default/manager_config_patch.yaml b/config/default/manager_config_patch.yaml deleted file mode 100644 index f6f5891..0000000 --- a/config/default/manager_config_patch.yaml +++ /dev/null @@ -1,10 +0,0 @@ -apiVersion: apps/v1 -kind: Deployment -metadata: - name: controller-manager - namespace: system -spec: - template: - spec: - containers: - - name: manager diff --git a/config/default/manager_metrics_patch.yaml b/config/default/manager_metrics_patch.yaml new file mode 100644 index 0000000..46f55c6 --- /dev/null +++ b/config/default/manager_metrics_patch.yaml @@ -0,0 +1,10 @@ +# This patch adds the args to allow exposing the metrics endpoint using HTTPS +- op: add + path: /spec/template/spec/containers/0/args/0 + value: --metrics-bind-address=:8443 +- op: add + path: /spec/template/spec/containers/0/ports/0 + value: + name: https-metrics + containerPort: 8443 + protocol: TCP diff --git a/config/default/metrics_service.yaml b/config/default/metrics_service.yaml new file mode 100644 index 0000000..912498d --- /dev/null +++ b/config/default/metrics_service.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +kind: Service +metadata: + labels: + control-plane: controller-manager + app.kubernetes.io/name: postgresql-operator + app.kubernetes.io/managed-by: kustomize + name: controller-manager-metrics-service + namespace: system +spec: + ports: + - name: https + port: 8443 + protocol: TCP + targetPort: 8443 + selector: + control-plane: controller-manager + app.kubernetes.io/name: test diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 5c5f0b8..2c4c315 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -1,2 +1,8 @@ resources: - manager.yaml +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +images: +- name: controller + newName: example.com/postgresql-operator + newTag: v0.0.1 diff --git a/config/manager/manager.yaml b/config/manager/manager.yaml index 3cb00c0..0205444 100644 --- a/config/manager/manager.yaml +++ b/config/manager/manager.yaml @@ -3,11 +3,7 @@ kind: Namespace metadata: labels: control-plane: controller-manager - app.kubernetes.io/name: namespace - app.kubernetes.io/instance: system - app.kubernetes.io/component: manager - app.kubernetes.io/created-by: postgresql-operator - app.kubernetes.io/part-of: postgresql-operator + app.kubernetes.io/name: test app.kubernetes.io/managed-by: kustomize name: system --- @@ -18,16 +14,13 @@ metadata: namespace: system labels: control-plane: controller-manager - app.kubernetes.io/name: deployment - app.kubernetes.io/instance: controller-manager - app.kubernetes.io/component: manager - app.kubernetes.io/created-by: postgresql-operator - app.kubernetes.io/part-of: postgresql-operator + app.kubernetes.io/name: test app.kubernetes.io/managed-by: kustomize spec: selector: matchLabels: control-plane: controller-manager + app.kubernetes.io/name: test replicas: 1 template: metadata: @@ -35,6 +28,7 @@ spec: kubectl.kubernetes.io/default-container: manager labels: control-plane: controller-manager + app.kubernetes.io/name: test spec: # TODO(user): Uncomment the following code to configure the nodeAffinity expression # according to the platforms which are supported by your solution. @@ -57,46 +51,48 @@ spec: # values: # - linux securityContext: + # Projects are configured by default to adhere to the "restricted" Pod Security Standards. + # This ensures that deployments meet the highest security requirements for Kubernetes. + # For more details, see: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted runAsNonRoot: true - # TODO(user): For common cases that do not require escalating privileges - # it is recommended to ensure that all your Pods/Containers are restrictive. - # More info: https://kubernetes.io/docs/concepts/security/pod-security-standards/#restricted - # Please uncomment the following code if your project does NOT have to work on old Kubernetes - # versions < 1.19 or on vendors versions which do NOT support this field by default (i.e. Openshift < 4.11 ). - # seccompProfile: - # type: RuntimeDefault + seccompProfile: + type: RuntimeDefault containers: - - command: - - /manager - args: - - --leader-elect - image: controller:latest - name: manager - securityContext: - allowPrivilegeEscalation: false - capabilities: - drop: - - "ALL" - livenessProbe: - httpGet: - path: /healthz - port: 8081 - initialDelaySeconds: 15 - periodSeconds: 20 - readinessProbe: - httpGet: - path: /readyz - port: 8081 - initialDelaySeconds: 5 - periodSeconds: 10 - # TODO(user): Configure the resources accordingly based on the project requirements. - # More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ - resources: - limits: - cpu: 500m - memory: 128Mi - requests: - cpu: 10m - memory: 64Mi + - command: + - /manager + args: + - --leader-elect + - --health-probe-bind-address=:8081 + image: controller:latest + name: manager + ports: [] + securityContext: + allowPrivilegeEscalation: false + capabilities: + drop: + - "ALL" + livenessProbe: + httpGet: + path: /healthz + port: 8081 + initialDelaySeconds: 15 + periodSeconds: 20 + readinessProbe: + httpGet: + path: /readyz + port: 8081 + initialDelaySeconds: 5 + periodSeconds: 10 + # TODO(user): Configure the resources accordingly based on the project requirements. + # More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/ + resources: + limits: + cpu: 500m + memory: 128Mi + requests: + cpu: 10m + memory: 64Mi + volumeMounts: [] + volumes: [] serviceAccountName: controller-manager terminationGracePeriodSeconds: 10 diff --git a/config/network-policy/allow-metrics-traffic.yaml b/config/network-policy/allow-metrics-traffic.yaml new file mode 100644 index 0000000..4f04372 --- /dev/null +++ b/config/network-policy/allow-metrics-traffic.yaml @@ -0,0 +1,27 @@ +# This NetworkPolicy allows ingress traffic +# with Pods running on namespaces labeled with 'metrics: enabled'. Only Pods on those +# namespaces are able to gather data from the metrics endpoint. +apiVersion: networking.k8s.io/v1 +kind: NetworkPolicy +metadata: + labels: + app.kubernetes.io/name: postgresql-operator + app.kubernetes.io/managed-by: kustomize + name: allow-metrics-traffic + namespace: system +spec: + podSelector: + matchLabels: + control-plane: controller-manager + app.kubernetes.io/name: postgresql-operator + policyTypes: + - Ingress + ingress: + # This allows ingress traffic from any namespace with the label metrics: enabled + - from: + - namespaceSelector: + matchLabels: + metrics: enabled # Only from namespaces with this label + ports: + - port: 8443 + protocol: TCP diff --git a/config/network-policy/kustomization.yaml b/config/network-policy/kustomization.yaml new file mode 100644 index 0000000..ec0fb5e --- /dev/null +++ b/config/network-policy/kustomization.yaml @@ -0,0 +1,2 @@ +resources: +- allow-metrics-traffic.yaml diff --git a/config/rbac/auth_proxy_client_clusterrole.yaml b/config/rbac/auth_proxy_client_clusterrole.yaml deleted file mode 100644 index 11f8600..0000000 --- a/config/rbac/auth_proxy_client_clusterrole.yaml +++ /dev/null @@ -1,16 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/name: clusterrole - app.kubernetes.io/instance: metrics-reader - app.kubernetes.io/component: kube-rbac-proxy - app.kubernetes.io/created-by: postgresql-operator - app.kubernetes.io/part-of: postgresql-operator - app.kubernetes.io/managed-by: kustomize - name: metrics-reader -rules: -- nonResourceURLs: - - "/metrics" - verbs: - - get diff --git a/config/rbac/auth_proxy_role.yaml b/config/rbac/auth_proxy_role.yaml deleted file mode 100644 index 6a965ce..0000000 --- a/config/rbac/auth_proxy_role.yaml +++ /dev/null @@ -1,24 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - labels: - app.kubernetes.io/name: clusterrole - app.kubernetes.io/instance: proxy-role - app.kubernetes.io/component: kube-rbac-proxy - app.kubernetes.io/created-by: postgresql-operator - app.kubernetes.io/part-of: postgresql-operator - app.kubernetes.io/managed-by: kustomize - name: proxy-role -rules: -- apiGroups: - - authentication.k8s.io - resources: - - tokenreviews - verbs: - - create -- apiGroups: - - authorization.k8s.io - resources: - - subjectaccessreviews - verbs: - - create diff --git a/config/rbac/auth_proxy_role_binding.yaml b/config/rbac/auth_proxy_role_binding.yaml deleted file mode 100644 index 16b3e3b..0000000 --- a/config/rbac/auth_proxy_role_binding.yaml +++ /dev/null @@ -1,19 +0,0 @@ -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRoleBinding -metadata: - labels: - app.kubernetes.io/name: clusterrolebinding - app.kubernetes.io/instance: proxy-rolebinding - app.kubernetes.io/component: kube-rbac-proxy - app.kubernetes.io/created-by: postgresql-operator - app.kubernetes.io/part-of: postgresql-operator - app.kubernetes.io/managed-by: kustomize - name: proxy-rolebinding -roleRef: - apiGroup: rbac.authorization.k8s.io - kind: ClusterRole - name: proxy-role -subjects: -- kind: ServiceAccount - name: controller-manager - namespace: system diff --git a/config/rbac/auth_proxy_service.yaml b/config/rbac/auth_proxy_service.yaml deleted file mode 100644 index 659ccd7..0000000 --- a/config/rbac/auth_proxy_service.yaml +++ /dev/null @@ -1,21 +0,0 @@ -apiVersion: v1 -kind: Service -metadata: - labels: - control-plane: controller-manager - app.kubernetes.io/name: service - app.kubernetes.io/instance: controller-manager-metrics-service - app.kubernetes.io/component: kube-rbac-proxy - app.kubernetes.io/created-by: postgresql-operator - app.kubernetes.io/part-of: postgresql-operator - app.kubernetes.io/managed-by: kustomize - name: controller-manager-metrics-service - namespace: system -spec: - ports: - - name: https - port: 8443 - protocol: TCP - targetPort: https - selector: - control-plane: controller-manager diff --git a/config/rbac/kustomization.yaml b/config/rbac/kustomization.yaml index 731832a..89066a3 100644 --- a/config/rbac/kustomization.yaml +++ b/config/rbac/kustomization.yaml @@ -1,18 +1,32 @@ resources: -# All RBAC will be applied under this service account in -# the deployment namespace. You may comment out this resource -# if your manager will use a service account that exists at -# runtime. Be sure to update RoleBinding and ClusterRoleBinding -# subjects if changing service account names. -- service_account.yaml -- role.yaml -- role_binding.yaml -- leader_election_role.yaml -- leader_election_role_binding.yaml -# Comment the following 4 lines if you want to disable -# the auth proxy (https://github.com/brancz/kube-rbac-proxy) -# which protects your /metrics endpoint. -- auth_proxy_service.yaml -- auth_proxy_role.yaml -- auth_proxy_role_binding.yaml -- auth_proxy_client_clusterrole.yaml + # All RBAC will be applied under this service account in + # the deployment namespace. You may comment out this resource + # if your manager will use a service account that exists at + # runtime. Be sure to update RoleBinding and ClusterRoleBinding + # subjects if changing service account names. + - service_account.yaml + - role.yaml + - role_binding.yaml + - leader_election_role.yaml + - leader_election_role_binding.yaml + # The following RBAC configurations are used to protect + # the metrics endpoint with authn/authz. These configurations + # ensure that only authorized users and service accounts + # can access the metrics endpoint. Comment the following + # permissions if you want to disable this protection. + # More info: https://book.kubebuilder.io/reference/metrics.html + - metrics_auth_role.yaml + - metrics_auth_role_binding.yaml + - metrics_reader_role.yaml + # For each CRD, "Admin", "Editor" and "Viewer" roles are scaffolded by + # default, aiding admins in cluster management. Those roles are + # not used by the test itself. You can comment the following lines + # if you do not want those helpers be installed with your Project. + - postgresql_postgresqldatabase_editor_role.yaml + - postgresql_postgresqldatabase_viewer_role.yaml + - postgresql_postgresqlengineconfiguration_editor_role.yaml + - postgresql_postgresqlengineconfiguration_viewer_role.yaml + - postgresql_postgresqlpublication_editor_role.yaml + - postgresql_postgresqlpublication_viewer_role.yaml + - postgresql_postgresqluserrole_editor_role.yaml + - postgresql_postgresqluserrole_viewer_role.yaml diff --git a/config/rbac/leader_election_role.yaml b/config/rbac/leader_election_role.yaml index 8eda809..f42e007 100644 --- a/config/rbac/leader_election_role.yaml +++ b/config/rbac/leader_election_role.yaml @@ -3,11 +3,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: Role metadata: labels: - app.kubernetes.io/name: role - app.kubernetes.io/instance: leader-election-role - app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: postgresql-operator - app.kubernetes.io/part-of: postgresql-operator + app.kubernetes.io/name: test app.kubernetes.io/managed-by: kustomize name: leader-election-role rules: diff --git a/config/rbac/leader_election_role_binding.yaml b/config/rbac/leader_election_role_binding.yaml index 3f87a8e..3dec59b 100644 --- a/config/rbac/leader_election_role_binding.yaml +++ b/config/rbac/leader_election_role_binding.yaml @@ -2,11 +2,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: RoleBinding metadata: labels: - app.kubernetes.io/name: rolebinding - app.kubernetes.io/instance: leader-election-rolebinding - app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: postgresql-operator - app.kubernetes.io/part-of: postgresql-operator + app.kubernetes.io/name: test app.kubernetes.io/managed-by: kustomize name: leader-election-rolebinding roleRef: diff --git a/config/rbac/metrics_auth_role.yaml b/config/rbac/metrics_auth_role.yaml new file mode 100644 index 0000000..32d2e4e --- /dev/null +++ b/config/rbac/metrics_auth_role.yaml @@ -0,0 +1,17 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: metrics-auth-role +rules: +- apiGroups: + - authentication.k8s.io + resources: + - tokenreviews + verbs: + - create +- apiGroups: + - authorization.k8s.io + resources: + - subjectaccessreviews + verbs: + - create diff --git a/config/rbac/metrics_auth_role_binding.yaml b/config/rbac/metrics_auth_role_binding.yaml new file mode 100644 index 0000000..e775d67 --- /dev/null +++ b/config/rbac/metrics_auth_role_binding.yaml @@ -0,0 +1,12 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: metrics-auth-rolebinding +roleRef: + apiGroup: rbac.authorization.k8s.io + kind: ClusterRole + name: metrics-auth-role +subjects: +- kind: ServiceAccount + name: controller-manager + namespace: system diff --git a/config/rbac/metrics_reader_role.yaml b/config/rbac/metrics_reader_role.yaml new file mode 100644 index 0000000..51a75db --- /dev/null +++ b/config/rbac/metrics_reader_role.yaml @@ -0,0 +1,9 @@ +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + name: metrics-reader +rules: +- nonResourceURLs: + - "/metrics" + verbs: + - get diff --git a/config/rbac/postgresql_postgresqluser_editor_role.yaml b/config/rbac/postgresql_postgresqluser_editor_role.yaml deleted file mode 100644 index 1f3202e..0000000 --- a/config/rbac/postgresql_postgresqluser_editor_role.yaml +++ /dev/null @@ -1,24 +0,0 @@ -# permissions for end users to edit postgresqlusers. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: postgresqluser-editor-role -rules: -- apiGroups: - - postgresql.easymile.com - resources: - - postgresqlusers - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - postgresql.easymile.com - resources: - - postgresqlusers/status - verbs: - - get diff --git a/config/rbac/postgresql_postgresqluser_viewer_role.yaml b/config/rbac/postgresql_postgresqluser_viewer_role.yaml deleted file mode 100644 index ad36289..0000000 --- a/config/rbac/postgresql_postgresqluser_viewer_role.yaml +++ /dev/null @@ -1,20 +0,0 @@ -# permissions for end users to view postgresqlusers. -apiVersion: rbac.authorization.k8s.io/v1 -kind: ClusterRole -metadata: - name: postgresqluser-viewer-role -rules: -- apiGroups: - - postgresql.easymile.com - resources: - - postgresqlusers - verbs: - - get - - list - - watch -- apiGroups: - - postgresql.easymile.com - resources: - - postgresqlusers/status - verbs: - - get diff --git a/config/rbac/role.yaml b/config/rbac/role.yaml index efe92f3..8c58a5e 100644 --- a/config/rbac/role.yaml +++ b/config/rbac/role.yaml @@ -27,83 +27,8 @@ rules: - postgresql.easymile.com resources: - postgresqldatabases - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - postgresql.easymile.com - resources: - - postgresqldatabases/finalizers - verbs: - - update -- apiGroups: - - postgresql.easymile.com - resources: - - postgresqldatabases/status - verbs: - - get - - patch - - update -- apiGroups: - - postgresql.easymile.com - resources: - postgresqlengineconfigurations - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - postgresql.easymile.com - resources: - - postgresqlengineconfigurations/finalizers - verbs: - - update -- apiGroups: - - postgresql.easymile.com - resources: - - postgresqlengineconfigurations/status - verbs: - - get - - patch - - update -- apiGroups: - - postgresql.easymile.com - resources: - postgresqlpublications - verbs: - - create - - delete - - get - - list - - patch - - update - - watch -- apiGroups: - - postgresql.easymile.com - resources: - - postgresqlpublications/finalizers - verbs: - - update -- apiGroups: - - postgresql.easymile.com - resources: - - postgresqlpublications/status - verbs: - - get - - patch - - update -- apiGroups: - - postgresql.easymile.com - resources: - postgresqluserroles verbs: - create @@ -116,12 +41,18 @@ rules: - apiGroups: - postgresql.easymile.com resources: + - postgresqldatabases/finalizers + - postgresqlengineconfigurations/finalizers + - postgresqlpublications/finalizers - postgresqluserroles/finalizers verbs: - update - apiGroups: - postgresql.easymile.com resources: + - postgresqldatabases/status + - postgresqlengineconfigurations/status + - postgresqlpublications/status - postgresqluserroles/status verbs: - get diff --git a/config/rbac/role_binding.yaml b/config/rbac/role_binding.yaml index 812f259..cbbaeb2 100644 --- a/config/rbac/role_binding.yaml +++ b/config/rbac/role_binding.yaml @@ -2,11 +2,7 @@ apiVersion: rbac.authorization.k8s.io/v1 kind: ClusterRoleBinding metadata: labels: - app.kubernetes.io/name: clusterrolebinding - app.kubernetes.io/instance: manager-rolebinding - app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: postgresql-operator - app.kubernetes.io/part-of: postgresql-operator + app.kubernetes.io/name: test app.kubernetes.io/managed-by: kustomize name: manager-rolebinding roleRef: diff --git a/config/rbac/service_account.yaml b/config/rbac/service_account.yaml index cbafbe7..5803e58 100644 --- a/config/rbac/service_account.yaml +++ b/config/rbac/service_account.yaml @@ -2,11 +2,7 @@ apiVersion: v1 kind: ServiceAccount metadata: labels: - app.kubernetes.io/name: serviceaccount - app.kubernetes.io/instance: controller-manager-sa - app.kubernetes.io/component: rbac - app.kubernetes.io/created-by: postgresql-operator - app.kubernetes.io/part-of: postgresql-operator + app.kubernetes.io/name: test app.kubernetes.io/managed-by: kustomize name: controller-manager namespace: system diff --git a/config/scorecard/kustomization.yaml b/config/scorecard/kustomization.yaml index 50cd2d0..54e8aa5 100644 --- a/config/scorecard/kustomization.yaml +++ b/config/scorecard/kustomization.yaml @@ -1,16 +1,18 @@ resources: - bases/config.yaml -patchesJson6902: +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +patches: - path: patches/basic.config.yaml target: group: scorecard.operatorframework.io - version: v1alpha3 kind: Configuration name: config + version: v1alpha3 - path: patches/olm.config.yaml target: group: scorecard.operatorframework.io - version: v1alpha3 kind: Configuration name: config -#+kubebuilder:scaffold:patchesJson6902 + version: v1alpha3 +# +kubebuilder:scaffold:patches diff --git a/config/scorecard/patches/basic.config.yaml b/config/scorecard/patches/basic.config.yaml index a2f1589..0b45f2f 100644 --- a/config/scorecard/patches/basic.config.yaml +++ b/config/scorecard/patches/basic.config.yaml @@ -4,7 +4,7 @@ entrypoint: - scorecard-test - basic-check-spec - image: quay.io/operator-framework/scorecard-test:v1.33.0 + image: quay.io/operator-framework/scorecard-test:v1.42.0 labels: suite: basic test: basic-check-spec-test diff --git a/config/scorecard/patches/olm.config.yaml b/config/scorecard/patches/olm.config.yaml index 9b7ca41..8cc1258 100644 --- a/config/scorecard/patches/olm.config.yaml +++ b/config/scorecard/patches/olm.config.yaml @@ -4,7 +4,7 @@ entrypoint: - scorecard-test - olm-bundle-validation - image: quay.io/operator-framework/scorecard-test:v1.33.0 + image: quay.io/operator-framework/scorecard-test:v1.42.0 labels: suite: olm test: olm-bundle-validation-test @@ -14,7 +14,7 @@ entrypoint: - scorecard-test - olm-crds-have-validation - image: quay.io/operator-framework/scorecard-test:v1.33.0 + image: quay.io/operator-framework/scorecard-test:v1.42.0 labels: suite: olm test: olm-crds-have-validation-test @@ -24,7 +24,7 @@ entrypoint: - scorecard-test - olm-crds-have-resources - image: quay.io/operator-framework/scorecard-test:v1.33.0 + image: quay.io/operator-framework/scorecard-test:v1.42.0 labels: suite: olm test: olm-crds-have-resources-test @@ -34,7 +34,7 @@ entrypoint: - scorecard-test - olm-spec-descriptors - image: quay.io/operator-framework/scorecard-test:v1.33.0 + image: quay.io/operator-framework/scorecard-test:v1.42.0 labels: suite: olm test: olm-spec-descriptors-test @@ -44,7 +44,7 @@ entrypoint: - scorecard-test - olm-status-descriptors - image: quay.io/operator-framework/scorecard-test:v1.33.0 + image: quay.io/operator-framework/scorecard-test:v1.42.0 labels: suite: olm test: olm-status-descriptors-test diff --git a/go.mod b/go.mod index 8dd7293..a517be9 100644 --- a/go.mod +++ b/go.mod @@ -1,76 +1,118 @@ module github.com/easymile/postgresql-operator -go 1.20 +go 1.25.0 require ( - github.com/go-logr/logr v1.2.4 + github.com/go-logr/logr v1.4.3 github.com/lib/pq v1.10.9 - github.com/onsi/ginkgo/v2 v2.9.5 - github.com/onsi/gomega v1.27.7 - github.com/prometheus/client_golang v1.15.1 - github.com/samber/lo v1.47.0 + github.com/onsi/ginkgo/v2 v2.27.3 + github.com/onsi/gomega v1.38.2 + github.com/prometheus/client_golang v1.23.2 + github.com/samber/lo v1.52.0 github.com/thoas/go-funk v0.9.3 - k8s.io/api v0.27.2 - k8s.io/apimachinery v0.27.2 - k8s.io/client-go v0.27.2 - sigs.k8s.io/controller-runtime v0.15.0 + k8s.io/api v0.35.0 + k8s.io/apimachinery v0.35.0 + k8s.io/client-go v0.35.0 + sigs.k8s.io/controller-runtime v0.22.4 ) require ( + cel.dev/expr v0.25.1 // indirect + github.com/Masterminds/semver/v3 v3.4.0 // indirect + github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/blang/semver/v4 v4.0.0 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cenkalti/backoff/v5 v5.0.3 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/emicklei/go-restful/v3 v3.9.0 // indirect - github.com/evanphx/json-patch/v5 v5.6.0 // indirect - github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/go-logr/zapr v1.2.4 // indirect - github.com/go-openapi/jsonpointer v0.19.6 // indirect - github.com/go-openapi/jsonreference v0.20.1 // indirect - github.com/go-openapi/swag v0.22.3 // indirect - github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect + github.com/emicklei/go-restful/v3 v3.13.0 // indirect + github.com/evanphx/json-patch/v5 v5.9.11 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsnotify v1.9.0 // indirect + github.com/fxamacker/cbor/v2 v2.9.0 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-logr/zapr v1.3.0 // indirect + github.com/go-openapi/jsonpointer v0.22.4 // indirect + github.com/go-openapi/jsonreference v0.21.4 // indirect + github.com/go-openapi/swag v0.25.4 // indirect + github.com/go-openapi/swag/cmdutils v0.25.4 // indirect + github.com/go-openapi/swag/conv v0.25.4 // indirect + github.com/go-openapi/swag/fileutils v0.25.4 // indirect + github.com/go-openapi/swag/jsonname v0.25.4 // indirect + github.com/go-openapi/swag/jsonutils v0.25.4 // indirect + github.com/go-openapi/swag/loading v0.25.4 // indirect + github.com/go-openapi/swag/mangling v0.25.4 // indirect + github.com/go-openapi/swag/netutils v0.25.4 // indirect + github.com/go-openapi/swag/stringutils v0.25.4 // indirect + github.com/go-openapi/swag/typeutils v0.25.4 // indirect + github.com/go-openapi/swag/yamlutils v0.25.4 // indirect + github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect - github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.3 // indirect - github.com/google/gnostic v0.5.7-v3refs // indirect - github.com/google/go-cmp v0.6.0 // indirect - github.com/google/gofuzz v1.1.0 // indirect - github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect - github.com/google/uuid v1.3.0 // indirect - github.com/imdario/mergo v0.3.6 // indirect + github.com/google/btree v1.1.3 // indirect + github.com/google/cel-go v0.26.1 // indirect + github.com/google/gnostic-models v0.7.1 // indirect + github.com/google/go-cmp v0.7.0 // indirect + github.com/google/pprof v0.0.0-20260106004452-d7df1bf2cac7 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.4 // indirect + github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/mailru/easyjson v0.7.7 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/mailru/easyjson v0.9.1 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/prometheus/client_model v0.4.0 // indirect - github.com/prometheus/common v0.42.0 // indirect - github.com/prometheus/procfs v0.9.0 // indirect - github.com/spf13/pflag v1.0.5 // indirect - go.uber.org/atomic v1.7.0 // indirect - go.uber.org/multierr v1.6.0 // indirect - go.uber.org/zap v1.24.0 // indirect - golang.org/x/net v0.25.0 // indirect - golang.org/x/oauth2 v0.5.0 // indirect - golang.org/x/sys v0.20.0 // indirect - golang.org/x/term v0.20.0 // indirect - golang.org/x/text v0.16.0 // indirect - golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect - gomodules.xyz/jsonpatch/v2 v2.3.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.30.0 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.67.5 // indirect + github.com/prometheus/procfs v0.19.2 // indirect + github.com/spf13/cobra v1.10.2 // indirect + github.com/spf13/pflag v1.0.10 // indirect + github.com/stoewer/go-strcase v1.3.1 // indirect + github.com/x448/float16 v0.8.4 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 // indirect + go.opentelemetry.io/otel v1.39.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0 // indirect + go.opentelemetry.io/otel/metric v1.39.0 // indirect + go.opentelemetry.io/otel/sdk v1.39.0 // indirect + go.opentelemetry.io/otel/trace v1.39.0 // indirect + go.opentelemetry.io/proto/otlp v1.9.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.1 // indirect + go.yaml.in/yaml/v2 v2.4.3 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 // indirect + golang.org/x/mod v0.31.0 // indirect + golang.org/x/net v0.48.0 // indirect + golang.org/x/oauth2 v0.34.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.39.0 // indirect + golang.org/x/term v0.38.0 // indirect + golang.org/x/text v0.32.0 // indirect + golang.org/x/time v0.14.0 // indirect + golang.org/x/tools v0.40.0 // indirect + gomodules.xyz/jsonpatch/v2 v2.5.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b // indirect + google.golang.org/grpc v1.78.0 // indirect + google.golang.org/protobuf v1.36.11 // indirect + gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.27.2 // indirect - k8s.io/component-base v0.27.2 // indirect - k8s.io/klog/v2 v2.90.1 // indirect - k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f // indirect - k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect - sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect - sigs.k8s.io/yaml v1.3.0 // indirect + k8s.io/apiextensions-apiserver v0.35.0 // indirect + k8s.io/apiserver v0.35.0 // indirect + k8s.io/component-base v0.35.0 // indirect + k8s.io/klog/v2 v2.130.1 // indirect + k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e // indirect + k8s.io/utils v0.0.0-20260106112306-0fe9cd71b2f8 // indirect + sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 // indirect + sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect + sigs.k8s.io/randfill v1.0.0 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect + sigs.k8s.io/structured-merge-diff/v6 v6.3.1 // indirect + sigs.k8s.io/yaml v1.6.0 // indirect ) diff --git a/go.sum b/go.sum index 982c582..66df69d 100644 --- a/go.sum +++ b/go.sum @@ -1,290 +1,422 @@ -cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= -github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= +cel.dev/expr v0.19.1 h1:NciYrtDRIR0lNCnH1LFJegdjspNx9fI59O7TWcua/W4= +cel.dev/expr v0.19.1/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= +cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= +cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= +github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= +github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= +github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= +github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= +github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/emicklei/go-restful/v3 v3.9.0 h1:XwGDlfxEnQZzuopoqxwSEllNcCOM9DhhFyhFIIGKwxE= -github.com/emicklei/go-restful/v3 v3.9.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= -github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= -github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= -github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= -github.com/evanphx/json-patch/v5 v5.6.0/go.mod h1:G79N1coSVB93tBe7j6PhzjmR3/2VvlbKOFpnXhI9Bw4= -github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= -github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= -github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= -github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/zapr v1.2.4 h1:QHVo+6stLbfJmYGkQ7uGHUCu5hnAFAj6mDe6Ea0SeOo= -github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA= -github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= +github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k= +github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= +github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= +github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= +github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= +github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= +github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= -github.com/go-openapi/jsonreference v0.20.1 h1:FBLnyygC4/IZZr893oiomc9XaghoveYTrLC1F86HID8= -github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= -github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= +github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= +github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= +github.com/go-openapi/jsonpointer v0.22.4 h1:dZtK82WlNpVLDW2jlA1YCiVJFVqkED1MegOUy9kR5T4= +github.com/go-openapi/jsonpointer v0.22.4/go.mod h1:elX9+UgznpFhgBuaMQ7iu4lvvX1nvNsesQ3oxmYTw80= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/jsonreference v0.21.4 h1:24qaE2y9bx/q3uRK/qN+TDwbok1NhbSmGjjySRCHtC8= +github.com/go-openapi/jsonreference v0.21.4/go.mod h1:rIENPTjDbLpzQmQWCj5kKj3ZlmEh+EFVbz3RTUh30/4= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI= -github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE= +github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ= +github.com/go-openapi/swag v0.25.4 h1:OyUPUFYDPDBMkqyxOTkqDYFnrhuhi9NR6QVUvIochMU= +github.com/go-openapi/swag v0.25.4/go.mod h1:zNfJ9WZABGHCFg2RnY0S4IOkAcVTzJ6z2Bi+Q4i6qFQ= +github.com/go-openapi/swag/cmdutils v0.25.4 h1:8rYhB5n6WawR192/BfUu2iVlxqVR9aRgGJP6WaBoW+4= +github.com/go-openapi/swag/cmdutils v0.25.4/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0= +github.com/go-openapi/swag/conv v0.25.4 h1:/Dd7p0LZXczgUcC/Ikm1+YqVzkEeCc9LnOWjfkpkfe4= +github.com/go-openapi/swag/conv v0.25.4/go.mod h1:3LXfie/lwoAv0NHoEuY1hjoFAYkvlqI/Bn5EQDD3PPU= +github.com/go-openapi/swag/fileutils v0.25.4 h1:2oI0XNW5y6UWZTC7vAxC8hmsK/tOkWXHJQH4lKjqw+Y= +github.com/go-openapi/swag/fileutils v0.25.4/go.mod h1:cdOT/PKbwcysVQ9Tpr0q20lQKH7MGhOEb6EwmHOirUk= +github.com/go-openapi/swag/jsonname v0.25.4 h1:bZH0+MsS03MbnwBXYhuTttMOqk+5KcQ9869Vye1bNHI= +github.com/go-openapi/swag/jsonname v0.25.4/go.mod h1:GPVEk9CWVhNvWhZgrnvRA6utbAltopbKwDu8mXNUMag= +github.com/go-openapi/swag/jsonutils v0.25.4 h1:VSchfbGhD4UTf4vCdR2F4TLBdLwHyUDTd1/q4i+jGZA= +github.com/go-openapi/swag/jsonutils v0.25.4/go.mod h1:7OYGXpvVFPn4PpaSdPHJBtF0iGnbEaTk8AvBkoWnaAY= +github.com/go-openapi/swag/loading v0.25.4 h1:jN4MvLj0X6yhCDduRsxDDw1aHe+ZWoLjW+9ZQWIKn2s= +github.com/go-openapi/swag/loading v0.25.4/go.mod h1:rpUM1ZiyEP9+mNLIQUdMiD7dCETXvkkC30z53i+ftTE= +github.com/go-openapi/swag/mangling v0.25.4 h1:2b9kBJk9JvPgxr36V23FxJLdwBrpijI26Bx5JH4Hp48= +github.com/go-openapi/swag/mangling v0.25.4/go.mod h1:6dxwu6QyORHpIIApsdZgb6wBk/DPU15MdyYj/ikn0Hg= +github.com/go-openapi/swag/netutils v0.25.4 h1:Gqe6K71bGRb3ZQLusdI8p/y1KLgV4M/k+/HzVSqT8H0= +github.com/go-openapi/swag/netutils v0.25.4/go.mod h1:m2W8dtdaoX7oj9rEttLyTeEFFEBvnAx9qHd5nJEBzYg= +github.com/go-openapi/swag/stringutils v0.25.4 h1:O6dU1Rd8bej4HPA3/CLPciNBBDwZj9HiEpdVsb8B5A8= +github.com/go-openapi/swag/stringutils v0.25.4/go.mod h1:GTsRvhJW5xM5gkgiFe0fV3PUlFm0dr8vki6/VSRaZK0= +github.com/go-openapi/swag/typeutils v0.25.4 h1:1/fbZOUN472NTc39zpa+YGHn3jzHWhv42wAJSN91wRw= +github.com/go-openapi/swag/typeutils v0.25.4/go.mod h1:Ou7g//Wx8tTLS9vG0UmzfCsjZjKhpjxayRKTHXf2pTE= +github.com/go-openapi/swag/yamlutils v0.25.4 h1:6jdaeSItEUb7ioS9lFoCZ65Cne1/RZtPBZ9A56h92Sw= +github.com/go-openapi/swag/yamlutils v0.25.4/go.mod h1:MNzq1ulQu+yd8Kl7wPOut/YHAAU/H6hL91fF+E2RFwc= +github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= +github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= -github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= -github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= -github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= -github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= -github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= -github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= +github.com/google/cel-go v0.23.2 h1:UdEe3CvQh3Nv+E/j9r1Y//WO0K0cSyD7/y0bzyLIMI4= +github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo= +github.com/google/cel-go v0.26.1 h1:iPbVVEdkhTX++hpe3lzSk7D3G3QSYqLGoHOcEio+UXQ= +github.com/google/cel-go v0.26.1/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM= +github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw= +github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw= +github.com/google/gnostic-models v0.7.1 h1:SisTfuFKJSKM5CPZkffwi6coztzzeYUhc3v4yxLWH8c= +github.com/google/gnostic-models v0.7.1/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/gofuzz v1.1.0 h1:Hsa8mG0dQ46ij8Sl2AYJDUv1oA9/d6Vk+3LG99Oe02g= -github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= -github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= -github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= -github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20260106004452-d7df1bf2cac7 h1:kmPAX+IJBcUAFTddx2+xC0H7sk2U9ijIIxZLLrPLNng= +github.com/google/pprof v0.0.0-20260106004452-d7df1bf2cac7/go.mod h1:67FPmZWbr+KDT/VlpWtw6sO9XSjpJmLuHpoLmWiTGgY= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0 h1:TmHmbvxPmaegwhDubVz0lICL0J5Ka2vwTzhoePEXsGE= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.4 h1:kEISI/Gx67NzH3nJxAmY/dGac80kKZgZt134u7Y/k1s= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.4/go.mod h1:6Nz966r3vQYCqIzWsuEl9d7cf7mRhtDmm++sOxlnfxI= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/mailru/easyjson v0.9.1 h1:LbtsOm5WAswyWbvTEOqhypdPeZzHavpZx96/n553mR8= +github.com/mailru/easyjson v0.9.1/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= -github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q= -github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k= -github.com/onsi/gomega v1.27.7 h1:fVih9JD6ogIiHUN6ePK7HJidyEDpWGVB5mzM7cWNXoU= -github.com/onsi/gomega v1.27.7/go.mod h1:1p8OOlwo2iUUDsHnOrjE5UKYJ+e3W8eQ3qSlRahPmr4= -github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= +github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/ginkgo/v2 v2.27.3 h1:ICsZJ8JoYafeXFFlFAG75a7CxMsJHwgKwtO+82SE9L8= +github.com/onsi/ginkgo/v2 v2.27.3/go.mod h1:ArE1D/XhNXBXCBkKOLkbsb2c81dQHCRcF5zwn/ykDRo= +github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= +github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/onsi/gomega v1.38.2 h1:eZCjf2xjZAqe+LeWvKb5weQ+NcPwX84kqJ0cZNxok2A= +github.com/onsi/gomega v1.38.2/go.mod h1:W2MJcYxRGV63b418Ai34Ud0hEdTVXq9NW9+Sx6uXf3k= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/prometheus/client_golang v1.15.1 h1:8tXpTmJbyH5lydzFPoxSIJ0J46jdh3tylbvM1xCv0LI= -github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt1N9XgF6zxWmaC0xOk= -github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= -github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= -github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= -github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= -github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= -github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= -github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= -github.com/samber/lo v1.47.0 h1:z7RynLwP5nbyRscyvcD043DWYoOcYRv3mV8lBeqOCLc= -github.com/samber/lo v1.47.0/go.mod h1:RmDH9Ct32Qy3gduHQuKJ3gW1fMHAnE/fAzQuf6He5cU= +github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q= +github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0= +github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= +github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E= +github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= +github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk= +github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE= +github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io= +github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I= +github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4= +github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw= +github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc= +github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk= +github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= +github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= +github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= +github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= +github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw= +github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0= +github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= +github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= +github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= +github.com/stoewer/go-strcase v1.3.1 h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs= +github.com/stoewer/go-strcase v1.3.1/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= -github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= -github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/thoas/go-funk v0.9.3 h1:7+nAEx3kn5ZJcnDm2Bh23N2yOtweO14bi//dvRtgLpw= github.com/thoas/go-funk v0.9.3/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q= +github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= +github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= -go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= -go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= -go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= -go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= -go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= -go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0 h1:ssfIgGNANqpVFCndZvcuyKbl0g+UAVcbBcqGkG28H0Y= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.64.0/go.mod h1:GQ/474YrbE4Jx8gZ4q5I4hrhUzM6UPzyrqJYV2AqPoQ= +go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= +go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0 h1:Vh5HayB/0HHfOQA7Ctx69E/Y/DcQSMPpKANYVMQ7fBA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0 h1:f0cb2XPmrqn4XMy9PNliTgRKJgS5WcL/u0/WRYGz4t0= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.39.0/go.mod h1:vnakAaFckOMiMtOIhFI2MNH4FYrZzXCYxmb1LlhoGz8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0 h1:5pojmb1U1AogINhN3SurB+zm/nIcusopeBNp42f45QM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.33.0/go.mod h1:57gTHJSE5S1tqg+EKsLPlTWhpHMsWlVmer+LA926XiA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0 h1:in9O8ESIOlwJAEGTkkf34DesGRAc/Pn8qJ7k3r/42LM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.39.0/go.mod h1:Rp0EXBm5tfnv0WL+ARyO/PHBEaEAT8UUHQ6AGJcSq6c= +go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= +go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/sdk v1.33.0 h1:iax7M131HuAm9QkZotNHEfstof92xM+N8sr3uHXc2IM= +go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= +go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= +go.opentelemetry.io/proto/otlp v1.4.0 h1:TA9WRvW6zMwP+Ssb6fLoUIuirti1gGbP28GcKG1jgeg= +go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY= +go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= +go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc= +go.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= +go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= -golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= -golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8= +golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY= +golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93 h1:fQsdNF2N+/YewlRZiricy4P1iimyPKZ/xwniHj8Q2a0= +golang.org/x/exp v0.0.0-20251219203646-944ab1f22d93/go.mod h1:EPRbTFwzwjXj9NpYyyrvenVh9Y+GFeEvMNh7Xuz7xgU= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/mod v0.31.0 h1:HaW9xtz0+kOcWKwli0ZXy79Ix+UW/vOfmWI5QVd2tgI= +golang.org/x/mod v0.31.0/go.mod h1:43JraMp9cGx1Rx3AqioxrbrhNsLl2l/iNAvuBkrezpg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= -golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= -golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.5.0 h1:HuArIo48skDwlrvM3sEdHXElYslAMsf3KwRkkW4MC4s= -golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= +golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= +golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU= +golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY= +golang.org/x/oauth2 v0.27.0 h1:da9Vo7/tDv5RH/7nZDz1eMGS/q1Vv1N/7FCrBhI9I3M= +golang.org/x/oauth2 v0.27.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= +golang.org/x/oauth2 v0.34.0 h1:hqK/t4AKgbqWkdkcAeI8XLmbK+4m4G5YeQRrmiotGlw= +golang.org/x/oauth2 v0.34.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= +golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= -golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.20.0 h1:VnkxpohqXaOBYJtBmEppKUG6mXpi+4O6purfc2+sMhw= -golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY= +golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= +golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= +golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q= +golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= +golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= +golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY= +golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= -golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= -golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= -golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= +golang.org/x/tools v0.40.0 h1:yLkxfA+Qnul4cs9QA3KnlFu0lVmd8JJfoq+E41uSutA= +golang.org/x/tools v0.40.0/go.mod h1:Ik/tzLRlbscWpqqMRjyWYDisX8bG13FrdXp3o4Sr9lc= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -gomodules.xyz/jsonpatch/v2 v2.3.0 h1:8NFhfS6gzxNqjLIYnZxg319wZ5Qjnx4m/CcX+Klzazc= -gomodules.xyz/jsonpatch/v2 v2.3.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= -google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= -google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= -google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.30.0 h1:kPPoIgf3TsEvrm0PFe15JQ+570QVxYzEvvHqChK+cng= -google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= +gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +gomodules.xyz/jsonpatch/v2 v2.5.0 h1:JELs8RLM12qJGXU4u/TO3V25KW8GreMKl9pdkk14RM0= +gomodules.xyz/jsonpatch/v2 v2.5.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q= +google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= +google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b h1:uA40e2M6fYRBf0+8uN5mLlqUtV192iiksiICIBkYJ1E= +google.golang.org/genproto/googleapis/api v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:Xa7le7qx2vmqB/SzWUBa7KdMjpdpAHlh5QCSnjessQk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576 h1:8ZmaLZE4XWrtU3MyClkYqqtl6Oegr3235h7jxsDyqCY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b h1:Mv8VFug0MP9e5vUxfBcE3vUkV6CImK3cMNMIDFjmzxU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251222181119-0a764e51fe1b/go.mod h1:j9x/tPzZkyxcgEFkiKEEGxfvyumM01BEtsW8xzOahRQ= +google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0= +google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= +google.golang.org/grpc v1.78.0 h1:K1XZG/yGDJnzMdd/uZHAkVqJE+xIDOcmdSFZkBUicNc= +google.golang.org/grpc v1.78.0/go.mod h1:I47qjTo4OKbMkjA/aOOwxDIiPSBofUtQUI5EfpWvW7U= +google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= +google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= +gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo= +gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -k8s.io/api v0.27.2 h1:+H17AJpUMvl+clT+BPnKf0E3ksMAzoBBg7CntpSuADo= -k8s.io/api v0.27.2/go.mod h1:ENmbocXfBT2ADujUXcBhHV55RIT31IIEvkntP6vZKS4= -k8s.io/apiextensions-apiserver v0.27.2 h1:iwhyoeS4xj9Y7v8YExhUwbVuBhMr3Q4bd/laClBV6Bo= -k8s.io/apiextensions-apiserver v0.27.2/go.mod h1:Oz9UdvGguL3ULgRdY9QMUzL2RZImotgxvGjdWRq6ZXQ= -k8s.io/apimachinery v0.27.2 h1:vBjGaKKieaIreI+oQwELalVG4d8f3YAMNpWLzDXkxeg= -k8s.io/apimachinery v0.27.2/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E= -k8s.io/client-go v0.27.2 h1:vDLSeuYvCHKeoQRhCXjxXO45nHVv2Ip4Fe0MfioMrhE= -k8s.io/client-go v0.27.2/go.mod h1:tY0gVmUsHrAmjzHX9zs7eCjxcBsf8IiNe7KQ52biTcQ= -k8s.io/component-base v0.27.2 h1:neju+7s/r5O4x4/txeUONNTS9r1HsPbyoPBAtHsDCpo= -k8s.io/component-base v0.27.2/go.mod h1:5UPk7EjfgrfgRIuDBFtsEFAe4DAvP3U+M8RTzoSJkpo= -k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= -k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f h1:2kWPakN3i/k81b0gvD5C5FJ2kxm1WrQFanWchyKuqGg= -k8s.io/kube-openapi v0.0.0-20230501164219-8b0f38b5fd1f/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= -k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY= -k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -sigs.k8s.io/controller-runtime v0.15.0 h1:ML+5Adt3qZnMSYxZ7gAverBLNPSMQEibtzAgp0UPojU= -sigs.k8s.io/controller-runtime v0.15.0/go.mod h1:7ngYvp1MLT+9GeZ+6lH3LOlcHkp/+tzA/fmHa4iq9kk= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= -sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= +k8s.io/api v0.33.0 h1:yTgZVn1XEe6opVpP1FylmNrIFWuDqe2H0V8CT5gxfIU= +k8s.io/api v0.33.0/go.mod h1:CTO61ECK/KU7haa3qq8sarQ0biLq2ju405IZAd9zsiM= +k8s.io/api v0.35.0 h1:iBAU5LTyBI9vw3L5glmat1njFK34srdLmktWwLTprlY= +k8s.io/api v0.35.0/go.mod h1:AQ0SNTzm4ZAczM03QH42c7l3bih1TbAXYo0DkF8ktnA= +k8s.io/apiextensions-apiserver v0.33.0 h1:d2qpYL7Mngbsc1taA4IjJPRJ9ilnsXIrndH+r9IimOs= +k8s.io/apiextensions-apiserver v0.33.0/go.mod h1:VeJ8u9dEEN+tbETo+lFkwaaZPg6uFKLGj5vyNEwwSzc= +k8s.io/apiextensions-apiserver v0.35.0 h1:3xHk2rTOdWXXJM+RDQZJvdx0yEOgC0FgQ1PlJatA5T4= +k8s.io/apiextensions-apiserver v0.35.0/go.mod h1:E1Ahk9SADaLQ4qtzYFkwUqusXTcaV2uw3l14aqpL2LU= +k8s.io/apimachinery v0.33.0 h1:1a6kHrJxb2hs4t8EE5wuR/WxKDwGN1FKH3JvDtA0CIQ= +k8s.io/apimachinery v0.33.0/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= +k8s.io/apimachinery v0.35.0 h1:Z2L3IHvPVv/MJ7xRxHEtk6GoJElaAqDCCU0S6ncYok8= +k8s.io/apimachinery v0.35.0/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns= +k8s.io/apiserver v0.33.0 h1:QqcM6c+qEEjkOODHppFXRiw/cE2zP85704YrQ9YaBbc= +k8s.io/apiserver v0.33.0/go.mod h1:EixYOit0YTxt8zrO2kBU7ixAtxFce9gKGq367nFmqI8= +k8s.io/apiserver v0.35.0 h1:CUGo5o+7hW9GcAEF3x3usT3fX4f9r8xmgQeCBDaOgX4= +k8s.io/apiserver v0.35.0/go.mod h1:QUy1U4+PrzbJaM3XGu2tQ7U9A4udRRo5cyxkFX0GEds= +k8s.io/client-go v0.33.0 h1:UASR0sAYVUzs2kYuKn/ZakZlcs2bEHaizrrHUZg0G98= +k8s.io/client-go v0.33.0/go.mod h1:kGkd+l/gNGg8GYWAPr0xF1rRKvVWvzh9vmZAMXtaKOg= +k8s.io/client-go v0.35.0 h1:IAW0ifFbfQQwQmga0UdoH0yvdqrbwMdq9vIFEhRpxBE= +k8s.io/client-go v0.35.0/go.mod h1:q2E5AAyqcbeLGPdoRB+Nxe3KYTfPce1Dnu1myQdqz9o= +k8s.io/component-base v0.33.0 h1:Ot4PyJI+0JAD9covDhwLp9UNkUja209OzsJ4FzScBNk= +k8s.io/component-base v0.33.0/go.mod h1:aXYZLbw3kihdkOPMDhWbjGCO6sg+luw554KP51t8qCU= +k8s.io/component-base v0.35.0 h1:+yBrOhzri2S1BVqyVSvcM3PtPyx5GUxCK2tinZz1G94= +k8s.io/component-base v0.35.0/go.mod h1:85SCX4UCa6SCFt6p3IKAPej7jSnF3L8EbfSyMZayJR0= +k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= +k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4= +k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= +k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e h1:iW9ChlU0cU16w8MpVYjXk12dqQ4BPFBEgif+ap7/hqQ= +k8s.io/kube-openapi v0.0.0-20251125145642-4e65d59e963e/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20260106112306-0fe9cd71b2f8 h1:oV4uULAC2QPIdMQwjMaNIwykyhWhnhBwX40yd5h9u3U= +k8s.io/utils v0.0.0-20260106112306-0fe9cd71b2f8/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2 h1:jpcvIRr3GLoUoEKRkHKSmGjxb6lWwrBlJsXc+eUYQHM= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 h1:hSfpvjjTQXQY2Fol2CS0QHMNs/WI1MOSGzCm1KhM5ec= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= +sigs.k8s.io/controller-runtime v0.21.0 h1:CYfjpEuicjUecRk+KAeyYh+ouUBn4llGyDYytIGcJS8= +sigs.k8s.io/controller-runtime v0.21.0/go.mod h1:OSg14+F65eWqIu4DceX7k/+QRAbTTvxeQSNSOQpukWM= +sigs.k8s.io/controller-runtime v0.22.4 h1:GEjV7KV3TY8e+tJ2LCTxUTanW4z/FmNB7l327UfMq9A= +sigs.k8s.io/controller-runtime v0.22.4/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU= +sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +sigs.k8s.io/structured-merge-diff/v4 v4.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc= +sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= +sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI= +sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= +sigs.k8s.io/structured-merge-diff/v6 v6.3.1 h1:JrhdFMqOd/+3ByqlP2I45kTOZmTRLBUm5pvRjeheg7E= +sigs.k8s.io/structured-merge-diff/v6 v6.3.1/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= +sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= diff --git a/hack/boilerplate.go.txt b/hack/boilerplate.go.txt index 29c55ec..9786798 100644 --- a/hack/boilerplate.go.txt +++ b/hack/boilerplate.go.txt @@ -1,5 +1,5 @@ /* -Copyright 2022. +Copyright 2026. Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/internal/controller/postgresql/postgres/database.go b/internal/controller/postgresql/postgres/database.go index 5fb0d86..f4926fe 100644 --- a/internal/controller/postgresql/postgres/database.go +++ b/internal/controller/postgresql/postgres/database.go @@ -39,7 +39,7 @@ AND n.nspname = '%s';` DuplicateDatabaseErrorCode = "42P04" ) -func (c *pg) GetColumnNamesFromTable(ctx context.Context, database string, schemaName string, tableName string) ([]string, error) { +func (c *pg) GetColumnNamesFromTable(ctx context.Context, database, schemaName, tableName string) ([]string, error) { err := c.connect(database) if err != nil { return nil, err @@ -311,7 +311,7 @@ func (c *pg) DropDatabase(ctx context.Context, database string) error { } } - c.log.Info(fmt.Sprintf("Dropped database %s", database)) + c.log.Info("Dropped database " + database) return nil } diff --git a/internal/controller/postgresql/postgres/pool_manager.go b/internal/controller/postgresql/postgres/pool_manager.go index 48868b1..4e4909c 100644 --- a/internal/controller/postgresql/postgres/pool_manager.go +++ b/internal/controller/postgresql/postgres/pool_manager.go @@ -162,11 +162,11 @@ func CloseAllSavedPoolsForName(name string) error { ps, _ := psInt.(*poolSaved) // Save all keys to be removed - keysToBeRemoved := make([]interface{}, 0) + keysToBeRemoved := make([]any, 0) // Error var err error // Loop over pools - ps.pools.Range(func(k, val interface{}) bool { + ps.pools.Range(func(k, val any) bool { // Cast sql db v, _ := val.(*sql.DB) // Close pool diff --git a/internal/controller/postgresql/postgres/postgres.go b/internal/controller/postgresql/postgres/postgres.go index 167b93a..005dd64 100644 --- a/internal/controller/postgresql/postgres/postgres.go +++ b/internal/controller/postgresql/postgres/postgres.go @@ -5,8 +5,9 @@ import ( "database/sql" "fmt" - "github.com/easymile/postgresql-operator/api/postgresql/v1alpha1" "github.com/go-logr/logr" + + "github.com/easymile/postgresql-operator/api/postgresql/v1alpha1" ) const MaxIdentifierLength = 63 @@ -67,12 +68,12 @@ type PG interface { //nolint:interfacebloat // This is needed GetPublication(ctx context.Context, dbname, name string) (*PublicationResult, error) CreatePublication(ctx context.Context, dbname string, builder *CreatePublicationBuilder) error UpdatePublication(ctx context.Context, dbname, publicationName string, builder *UpdatePublicationBuilder) error - ChangePublicationOwner(ctx context.Context, dbname string, publicationName string, owner string) error + ChangePublicationOwner(ctx context.Context, dbname, publicationName, owner string) error GetPublicationTablesDetails(ctx context.Context, db, publicationName string) ([]*PublicationTableDetail, error) DropReplicationSlot(ctx context.Context, name string) error CreateReplicationSlot(ctx context.Context, dbname, name, plugin string) error GetReplicationSlot(ctx context.Context, name string) (*ReplicationSlotResult, error) - GetColumnNamesFromTable(ctx context.Context, database string, schemaName string, tableName string) ([]string, error) + GetColumnNamesFromTable(ctx context.Context, database, schemaName, tableName string) ([]string, error) GetUser() string GetHost() string GetPort() int @@ -115,6 +116,7 @@ func NewPG( name: name, } + //nolint:exhaustive switch cloudType { case v1alpha1.AWSProvider: return newAWSPG(postgres) diff --git a/internal/controller/postgresql/postgres/publication.go b/internal/controller/postgresql/postgres/publication.go index c143c6d..af890a5 100644 --- a/internal/controller/postgresql/postgres/publication.go +++ b/internal/controller/postgresql/postgres/publication.go @@ -175,7 +175,7 @@ func (c *pg) UpdatePublication(ctx context.Context, dbname, publicationName stri // Build builder.Build() - tx, err := c.db.Begin() + tx, err := c.db.BeginTx(ctx, nil) if err != nil { return err } @@ -224,7 +224,7 @@ func (c *pg) UpdatePublication(ctx context.Context, dbname, publicationName stri return nil } -func (c *pg) ChangePublicationOwner(ctx context.Context, dbname string, publicationName string, owner string) error { +func (c *pg) ChangePublicationOwner(ctx context.Context, dbname, publicationName, owner string) error { // Connect to db err := c.connect(dbname) if err != nil { diff --git a/internal/controller/postgresql/postgresqldatabase_controller.go b/internal/controller/postgresql/postgresqldatabase_controller.go index 8b5d046..33f058f 100644 --- a/internal/controller/postgresql/postgresqldatabase_controller.go +++ b/internal/controller/postgresql/postgresqldatabase_controller.go @@ -22,20 +22,21 @@ import ( "reflect" "time" + "github.com/go-logr/logr" + "github.com/prometheus/client_golang/prometheus" + "github.com/thoas/go-funk" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/tools/record" - ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + ctrl "sigs.k8s.io/controller-runtime" + postgresqlv1alpha1 "github.com/easymile/postgresql-operator/api/postgresql/v1alpha1" "github.com/easymile/postgresql-operator/internal/controller/config" "github.com/easymile/postgresql-operator/internal/controller/postgresql/postgres" "github.com/easymile/postgresql-operator/internal/controller/utils" - "github.com/go-logr/logr" - "github.com/prometheus/client_golang/prometheus" - "github.com/thoas/go-funk" ) const ( @@ -73,7 +74,6 @@ func (r *PostgresqlDatabaseReconciler) Reconcile(ctx context.Context, req ctrl.R // Issue with this logger: controller and controllerKind are incorrect // Build another logger from upper to fix this. // reqLogger := log.FromContext(ctx) - reqLogger := r.Log.WithValues("Request.Namespace", req.Namespace, "Request.Name", req.Name) reqLogger.Info("Reconciling PostgresqlDatabase") @@ -207,11 +207,11 @@ func (r *PostgresqlDatabaseReconciler) mainReconcile( // Create all identifiers now to check length owner := instance.Spec.MasterRole if owner == "" { - owner = fmt.Sprintf("%s-owner", instance.Spec.Database) + owner = instance.Spec.Database + "-owner" } - reader := fmt.Sprintf("%s-reader", instance.Spec.Database) - writer := fmt.Sprintf("%s-writer", instance.Spec.Database) + reader := instance.Spec.Database + "-reader" + writer := instance.Spec.Database + "-writer" // Check identifier length if len(owner) > postgres.MaxIdentifierLength { @@ -462,7 +462,11 @@ func (r *PostgresqlDatabaseReconciler) shouldDropDatabase( if existingUserRole != nil { // Wait for children removal - err = fmt.Errorf("cannot remove resource because found user role %s in namespace %s linked to this resource and wait for deletion flag is enabled", existingUserRole.Name, existingUserRole.Namespace) + err = fmt.Errorf( + "cannot remove resource because found user role %s in namespace %s linked to this resource and wait for deletion flag is enabled", + existingUserRole.Name, + existingUserRole.Namespace, + ) return false, err } @@ -475,7 +479,11 @@ func (r *PostgresqlDatabaseReconciler) shouldDropDatabase( if existingPublication != nil { // Wait for children removal - err = fmt.Errorf("cannot remove resource because found publication %s in namespace %s linked to this resource and wait for deletion flag is enabled", existingPublication.Name, existingPublication.Namespace) + err = fmt.Errorf( + "cannot remove resource because found publication %s in namespace %s linked to this resource and wait for deletion flag is enabled", + existingPublication.Name, + existingPublication.Namespace, + ) return false, err } @@ -714,7 +722,13 @@ func (*PostgresqlDatabaseReconciler) manageExtensions(ctx context.Context, pg po return nil } -func (*PostgresqlDatabaseReconciler) manageReaderRole(ctx context.Context, pg postgres.PG, reader string, instance *postgresqlv1alpha1.PostgresqlDatabase, allowGrantAdminOption bool) error { +func (*PostgresqlDatabaseReconciler) manageReaderRole( + ctx context.Context, + pg postgres.PG, + reader string, + instance *postgresqlv1alpha1.PostgresqlDatabase, + allowGrantAdminOption bool, +) error { // Check if role was already created in the past if instance.Status.Roles.Reader != "" { // Check if role doesn't already exists diff --git a/internal/controller/postgresql/postgresqldatabase_controller_test.go b/internal/controller/postgresql/postgresqldatabase_controller_test.go index 98129b4..dd82d88 100644 --- a/internal/controller/postgresql/postgresqldatabase_controller_test.go +++ b/internal/controller/postgresql/postgresqldatabase_controller_test.go @@ -2,17 +2,21 @@ package postgresql import ( "errors" - gerrors "errors" "fmt" "reflect" - "github.com/easymile/postgresql-operator/api/postgresql/common" - postgresqlv1alpha1 "github.com/easymile/postgresql-operator/api/postgresql/v1alpha1" + "k8s.io/apimachinery/pkg/types" + + //nolint:revive . "github.com/onsi/ginkgo/v2" + //nolint:revive . "github.com/onsi/gomega" + apimachineryErrors "k8s.io/apimachinery/pkg/api/errors" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" + + "github.com/easymile/postgresql-operator/api/postgresql/common" + postgresqlv1alpha1 "github.com/easymile/postgresql-operator/api/postgresql/v1alpha1" ) var _ = Describe("PostgresqlDatabase tests", func() { @@ -156,9 +160,9 @@ var _ = Describe("PostgresqlDatabase tests", func() { Should(Succeed()) // Checks - ownerRole := fmt.Sprintf("%s-owner", pgdbDBName) - readerRole := fmt.Sprintf("%s-reader", pgdbDBName) - writerRole := fmt.Sprintf("%s-writer", pgdbDBName) + ownerRole := pgdbDBName + "-owner" + readerRole := pgdbDBName + "-reader" + writerRole := pgdbDBName + "-writer" Expect(item.Status.Ready).To(BeTrue()) Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.DatabaseCreatedPhase)) @@ -265,8 +269,8 @@ var _ = Describe("PostgresqlDatabase tests", func() { // Checks ownerRole := "master" - readerRole := fmt.Sprintf("%s-reader", pgdbDBName) - writerRole := fmt.Sprintf("%s-writer", pgdbDBName) + readerRole := pgdbDBName + "-reader" + writerRole := pgdbDBName + "-writer" Expect(item.Status.Ready).To(BeTrue()) Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.DatabaseCreatedPhase)) @@ -481,7 +485,7 @@ var _ = Describe("PostgresqlDatabase tests", func() { // Create pgdb item := setupPGDB(true) - ownerRole := fmt.Sprintf("%s-owner", pgdbDBName) + ownerRole := pgdbDBName + "-owner" Expect(item.Status.Ready).To(BeTrue()) Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.DatabaseCreatedPhase)) @@ -522,9 +526,9 @@ var _ = Describe("PostgresqlDatabase tests", func() { item := setupPGDB(true) // Checks - ownerRole := fmt.Sprintf("%s-owner", pgdbDBName) - readerRole := fmt.Sprintf("%s-reader", pgdbDBName) - writerRole := fmt.Sprintf("%s-writer", pgdbDBName) + ownerRole := pgdbDBName + "-owner" + readerRole := pgdbDBName + "-reader" + writerRole := pgdbDBName + "-writer" Expect(item.Status.Ready).To(BeTrue()) Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.DatabaseCreatedPhase)) @@ -574,13 +578,13 @@ var _ = Describe("PostgresqlDatabase tests", func() { Expect(item.Status.Message).To(BeEmpty()) // Check if roles exists and are granted to default user - ownerRole := fmt.Sprintf("%s-owner", pgdbDBName) + ownerRole := pgdbDBName + "-owner" checkRoleInSQLDb(ownerRole) - readerRole := fmt.Sprintf("%s-reader", pgdbDBName) + readerRole := pgdbDBName + "-reader" checkRoleInSQLDb(readerRole) - writerRole := fmt.Sprintf("%s-writer", pgdbDBName) + writerRole := pgdbDBName + "-writer" checkRoleInSQLDb(writerRole) // Check role members and rights @@ -663,7 +667,7 @@ var _ = Describe("PostgresqlDatabase tests", func() { It("should be ok to have a pgdb referencing an existing editor role", func() { // Create SQL role - sqlRole := fmt.Sprintf("%s-writer", pgdbDBName) // -> This is default writer role name used by pgdb + sqlRole := pgdbDBName + "-writer" // -> This is default writer role name used by pgdb errRole := createSQLRole(sqlRole) Expect(errRole).ToNot(HaveOccurred()) @@ -932,7 +936,8 @@ var _ = Describe("PostgresqlDatabase tests", func() { // Add table to schema tableName := "tt" - createTableInSchemaAsAdmin(pgPublicSchemaName, tableName) + err = createTableInSchemaAsAdmin(pgPublicSchemaName, tableName) + Expect(err).ToNot(HaveOccurred()) Eventually( func() error { @@ -1161,7 +1166,8 @@ var _ = Describe("PostgresqlDatabase tests", func() { // Add table to schema tableName := "tt" - createTableInSchemaAsAdmin(pgdbSchemaName1, tableName) + err = createTableInSchemaAsAdmin(pgdbSchemaName1, tableName) + Expect(err).ToNot(HaveOccurred()) // Then remove schema from pgdb item.Spec.Schemas.List = make([]string, 0) @@ -1461,7 +1467,6 @@ var _ = Describe("PostgresqlDatabase tests", func() { secondExists, secondErr := isSQLExtensionExists(pgdbExtensionName2) Expect(secondErr).ToNot(HaveOccurred()) Expect(secondExists).To(BeTrue()) - }) It("should be ok to declare 2 extensions", func() { @@ -1967,7 +1972,7 @@ var _ = Describe("PostgresqlDatabase tests", func() { Expect(exists).To(BeTrue()) // Check old role does not exist anymore - ownerRole := fmt.Sprintf("%s-owner", pgdbDBName) + ownerRole := pgdbDBName + "-owner" exists, err = isSQLRoleExists(ownerRole) Expect(err).ToNot(HaveOccurred()) Expect(exists).To(BeFalse()) @@ -2273,7 +2278,7 @@ var _ = Describe("PostgresqlDatabase tests", func() { // Check if status is no more ready if pgdb.Status.Phase != postgresqlv1alpha1.DatabaseFailedPhase { - return gerrors.New("pgdb should not be valid anymore") + return errors.New("pgdb should not be valid anymore") } return nil @@ -2286,7 +2291,12 @@ var _ = Describe("PostgresqlDatabase tests", func() { Expect(pgdb.Status.Ready).To(BeFalse()) Expect(pgdb.Status.Phase).To(Equal(postgresqlv1alpha1.DatabaseFailedPhase)) Expect(pgdb.Status.Message).To(Equal( - fmt.Sprintf("cannot remove resource because found user role %s in namespace %s linked to this resource and wait for deletion flag is enabled", pgurName, pgurNamespace))) + fmt.Sprintf( + "cannot remove resource because found user role %s in namespace %s linked to this resource and wait for deletion flag is enabled", + pgurName, + pgurNamespace, + ), + )) // Check DB has not been deleted exists, err := isSQLDBExists(pgdbDBName) @@ -2365,7 +2375,7 @@ var _ = Describe("PostgresqlDatabase tests", func() { // Check if status is no more ready if pgdb.Status.Phase != postgresqlv1alpha1.DatabaseFailedPhase { - return gerrors.New("pgdb should not be valid anymore") + return errors.New("pgdb should not be valid anymore") } return nil @@ -2378,7 +2388,12 @@ var _ = Describe("PostgresqlDatabase tests", func() { Expect(pgdb.Status.Ready).To(BeFalse()) Expect(pgdb.Status.Phase).To(Equal(postgresqlv1alpha1.DatabaseFailedPhase)) Expect(pgdb.Status.Message).To(Equal( - fmt.Sprintf("cannot remove resource because found publication %s in namespace %s linked to this resource and wait for deletion flag is enabled", pgpublicationName, pgpublicationNamespace))) + fmt.Sprintf( + "cannot remove resource because found publication %s in namespace %s linked to this resource and wait for deletion flag is enabled", + pgpublicationName, + pgpublicationNamespace, + ), + )) // Check DB has not been deleted exists, err := isSQLDBExists(pgdbDBName) diff --git a/internal/controller/postgresql/postgresqlengineconfiguration_controller.go b/internal/controller/postgresql/postgresqlengineconfiguration_controller.go index 5e6ac52..e8a81b5 100644 --- a/internal/controller/postgresql/postgresqlengineconfiguration_controller.go +++ b/internal/controller/postgresql/postgresqlengineconfiguration_controller.go @@ -22,19 +22,20 @@ import ( "reflect" "time" + "github.com/go-logr/logr" + "github.com/prometheus/client_golang/prometheus" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/tools/record" - ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + + ctrl "sigs.k8s.io/controller-runtime" postgresqlv1alpha1 "github.com/easymile/postgresql-operator/api/postgresql/v1alpha1" "github.com/easymile/postgresql-operator/internal/controller/config" "github.com/easymile/postgresql-operator/internal/controller/postgresql/postgres" "github.com/easymile/postgresql-operator/internal/controller/utils" - "github.com/go-logr/logr" - "github.com/prometheus/client_golang/prometheus" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" ) const ( @@ -72,7 +73,6 @@ func (r *PostgresqlEngineConfigurationReconciler) Reconcile(ctx context.Context, // Issue with this logger: controller and controllerKind are incorrect // Build another logger from upper to fix this. // reqLogger := log.FromContext(ctx) - reqLogger := r.Log.WithValues("Request.Namespace", req.Namespace, "Request.Name", req.Name) reqLogger.Info("Reconciling PostgresqlEngineConfiguration") @@ -145,7 +145,11 @@ func (r *PostgresqlEngineConfigurationReconciler) mainReconcile( if existingDB != nil { // Wait for children removal - err = fmt.Errorf("cannot remove resource because found database %s in namespace %s linked to this resource and wait for deletion flag is enabled", existingDB.Name, existingDB.Namespace) + err = fmt.Errorf( + "cannot remove resource because found database %s in namespace %s linked to this resource and wait for deletion flag is enabled", + existingDB.Name, + existingDB.Namespace, + ) return r.manageError(ctx, reqLogger, instance, originalPatch, err) } @@ -282,7 +286,8 @@ func (r *PostgresqlEngineConfigurationReconciler) getAnyDatabaseLinked( // Loop over the list for _, db := range dbL.Items { // Check db is linked to pgengineconfig - if db.Spec.EngineConfiguration.Name == instance.Name && (db.Spec.EngineConfiguration.Namespace == instance.Namespace || db.Namespace == instance.Namespace) { + if db.Spec.EngineConfiguration.Name == instance.Name && + (db.Spec.EngineConfiguration.Namespace == instance.Namespace || db.Namespace == instance.Namespace) { return &db, nil } } diff --git a/internal/controller/postgresql/postgresqlengineconfiguration_controller_test.go b/internal/controller/postgresql/postgresqlengineconfiguration_controller_test.go index 3e3c5e6..c9b9342 100644 --- a/internal/controller/postgresql/postgresqlengineconfiguration_controller_test.go +++ b/internal/controller/postgresql/postgresqlengineconfiguration_controller_test.go @@ -2,16 +2,20 @@ package postgresql import ( "errors" - gerrors "errors" "fmt" - postgresqlv1alpha1 "github.com/easymile/postgresql-operator/api/postgresql/v1alpha1" + "k8s.io/apimachinery/pkg/types" + + //nolint:revive . "github.com/onsi/ginkgo/v2" + //nolint:revive . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" apimachineryErrors "k8s.io/apimachinery/pkg/api/errors" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" + + postgresqlv1alpha1 "github.com/easymile/postgresql-operator/api/postgresql/v1alpha1" ) var _ = Describe("PostgresqlEngineConfiguration tests", func() { @@ -973,7 +977,7 @@ var _ = Describe("PostgresqlEngineConfiguration tests", func() { // Check if status is no more ready if pgec.Status.Phase != postgresqlv1alpha1.EngineFailedPhase { - return gerrors.New("pgec should not be valid anymore") + return errors.New("pgec should not be valid anymore") } return nil @@ -986,8 +990,12 @@ var _ = Describe("PostgresqlEngineConfiguration tests", func() { Expect(pgec.Status.Ready).To(BeFalse()) Expect(pgec.Status.Phase).To(BeEquivalentTo(postgresqlv1alpha1.EngineFailedPhase)) Expect(pgec.Status.Message).To(BeEquivalentTo( - fmt.Sprintf("cannot remove resource because found database %s in namespace %s linked to this resource and wait for deletion flag is enabled", pgdbName, pgdbNamespace))) - + fmt.Sprintf( + "cannot remove resource because found database %s in namespace %s linked to this resource and wait for deletion flag is enabled", + pgdbName, + pgdbNamespace, + ), + )) }) It("should be ok to delete it without wait and something linked", func() { diff --git a/internal/controller/postgresql/postgresqlpublication_controller.go b/internal/controller/postgresql/postgresqlpublication_controller.go index d6aa3a0..de493f1 100644 --- a/internal/controller/postgresql/postgresqlpublication_controller.go +++ b/internal/controller/postgresql/postgresqlpublication_controller.go @@ -23,21 +23,22 @@ import ( "strings" "time" + "github.com/go-logr/logr" + "github.com/prometheus/client_golang/prometheus" + "github.com/samber/lo" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/tools/record" - ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/reconcile" + ctrl "sigs.k8s.io/controller-runtime" + "github.com/easymile/postgresql-operator/api/postgresql/v1alpha1" "github.com/easymile/postgresql-operator/internal/controller/config" "github.com/easymile/postgresql-operator/internal/controller/postgresql/postgres" "github.com/easymile/postgresql-operator/internal/controller/utils" - "github.com/go-logr/logr" - "github.com/prometheus/client_golang/prometheus" - "github.com/samber/lo" ) const DefaultReplicationSlotPlugin = "pgoutput" @@ -70,15 +71,14 @@ func (r *PostgresqlPublicationReconciler) Reconcile(ctx context.Context, req ctr // Issue with this logger: controller and controllerKind are incorrect // Build another logger from upper to fix this. // reqLogger := log.FromContext(ctx) - reqLogger := r.Log.WithValues("Request.Namespace", req.Namespace, "Request.Name", req.Name) reqLogger.Info("Reconciling PostgresqlPublication") // Fetch the PostgresqlPublication instance instance := &v1alpha1.PostgresqlPublication{} - err := r.Get(ctx, req.NamespacedName, instance) + err := r.Get(ctx, req.NamespacedName, instance) if err != nil { if errors.IsNotFound(err) { // Request object not found, could have been deleted after reconcile request. @@ -665,7 +665,9 @@ func (*PostgresqlPublicationReconciler) validate( }) // Check if found { - return errors.NewBadRequest("tables cannot have a columns list with an empty name or have a columns list with a table schema list enabled or an empty additional where") + return errors.NewBadRequest( + "tables cannot have a columns list with an empty name or have a columns list with a table schema list enabled or an empty additional where", + ) } // Default diff --git a/internal/controller/postgresql/postgresqlpublication_controller_test.go b/internal/controller/postgresql/postgresqlpublication_controller_test.go index 7ed92f3..d2f74c9 100644 --- a/internal/controller/postgresql/postgresqlpublication_controller_test.go +++ b/internal/controller/postgresql/postgresqlpublication_controller_test.go @@ -2,17 +2,21 @@ package postgresql import ( "errors" - gerrors "errors" "fmt" "time" - "github.com/easymile/postgresql-operator/api/postgresql/common" - postgresqlv1alpha1 "github.com/easymile/postgresql-operator/api/postgresql/v1alpha1" + "k8s.io/apimachinery/pkg/types" + + //nolint:revive . "github.com/onsi/ginkgo/v2" + //nolint:revive . "github.com/onsi/gomega" + apimachineryErrors "k8s.io/apimachinery/pkg/api/errors" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" + + "github.com/easymile/postgresql-operator/api/postgresql/common" + postgresqlv1alpha1 "github.com/easymile/postgresql-operator/api/postgresql/v1alpha1" ) var _ = Describe("PostgresqlPublication tests", func() { @@ -362,7 +366,9 @@ var _ = Describe("PostgresqlPublication tests", func() { // Checks Expect(item.Status.Ready).To(BeFalse()) Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.PublicationFailedPhase)) - Expect(item.Status.Message).To(Equal("tables cannot have a columns list with an empty name or have a columns list with a table schema list enabled or an empty additional where")) + Expect( + item.Status.Message, + ).To(Equal("tables cannot have a columns list with an empty name or have a columns list with a table schema list enabled or an empty additional where")) }) It("should fail when tables with a empty string in columns is provided", func() { @@ -417,7 +423,9 @@ var _ = Describe("PostgresqlPublication tests", func() { // Checks Expect(item.Status.Ready).To(BeFalse()) Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.PublicationFailedPhase)) - Expect(item.Status.Message).To(Equal("tables cannot have a columns list with an empty name or have a columns list with a table schema list enabled or an empty additional where")) + Expect( + item.Status.Message, + ).To(Equal("tables cannot have a columns list with an empty name or have a columns list with a table schema list enabled or an empty additional where")) }) It("should fail when tables with a empty string in additional where is provided", func() { @@ -472,7 +480,9 @@ var _ = Describe("PostgresqlPublication tests", func() { // Checks Expect(item.Status.Ready).To(BeFalse()) Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.PublicationFailedPhase)) - Expect(item.Status.Message).To(Equal("tables cannot have a columns list with an empty name or have a columns list with a table schema list enabled or an empty additional where")) + Expect( + item.Status.Message, + ).To(Equal("tables cannot have a columns list with an empty name or have a columns list with a table schema list enabled or an empty additional where")) }) It("should fail when tables with columns and tables in schema are provided", func() { @@ -528,7 +538,9 @@ var _ = Describe("PostgresqlPublication tests", func() { // Checks Expect(item.Status.Ready).To(BeFalse()) Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.PublicationFailedPhase)) - Expect(item.Status.Message).To(Equal("tables cannot have a columns list with an empty name or have a columns list with a table schema list enabled or an empty additional where")) + Expect( + item.Status.Message, + ).To(Equal("tables cannot have a columns list with an empty name or have a columns list with a table schema list enabled or an empty additional where")) }) }) @@ -570,8 +582,8 @@ var _ = Describe("PostgresqlPublication tests", func() { })) // Get details - details, err := getPublicationTableDetails(item.Status.Name) - if Expect(err).NotTo(HaveOccurred()) { + details, err2 := getPublicationTableDetails(item.Status.Name) + if Expect(err2).NotTo(HaveOccurred()) { Expect(details).To(HaveLen(0)) } } @@ -626,8 +638,8 @@ var _ = Describe("PostgresqlPublication tests", func() { })) // Get details - details, err := getPublicationTableDetails(item.Status.Name) - if Expect(err).NotTo(HaveOccurred()) { + details, err2 := getPublicationTableDetails(item.Status.Name) + if Expect(err2).NotTo(HaveOccurred()) { Expect(details).To(HaveLen(2)) Expect(details).To(Equal([]*PublicationTableDetail{ { @@ -701,8 +713,8 @@ var _ = Describe("PostgresqlPublication tests", func() { })) // Get details - details, err := getPublicationTableDetails(item.Status.Name) - if Expect(err).NotTo(HaveOccurred()) { + details, err2 := getPublicationTableDetails(item.Status.Name) + if Expect(err2).NotTo(HaveOccurred()) { Expect(details).To(HaveLen(2)) Expect(details).To(Equal([]*PublicationTableDetail{ { @@ -777,8 +789,8 @@ var _ = Describe("PostgresqlPublication tests", func() { })) // Get details - details, err := getPublicationTableDetails(item.Status.Name) - if Expect(err).NotTo(HaveOccurred()) { + details, err2 := getPublicationTableDetails(item.Status.Name) + if Expect(err2).NotTo(HaveOccurred()) { Expect(details).To(HaveLen(2)) Expect(details).To(Equal([]*PublicationTableDetail{ { @@ -850,8 +862,8 @@ var _ = Describe("PostgresqlPublication tests", func() { })) // Get details - details, err := getPublicationTableDetails(item.Status.Name) - if Expect(err).NotTo(HaveOccurred()) { + details, err2 := getPublicationTableDetails(item.Status.Name) + if Expect(err2).NotTo(HaveOccurred()) { Expect(details).To(HaveLen(2)) Expect(details).To(Equal([]*PublicationTableDetail{ { @@ -918,8 +930,8 @@ var _ = Describe("PostgresqlPublication tests", func() { })) // Get details - details, err := getPublicationTableDetails(item.Status.Name) - if Expect(err).NotTo(HaveOccurred()) { + details, err2 := getPublicationTableDetails(item.Status.Name) + if Expect(err2).NotTo(HaveOccurred()) { Expect(details).To(HaveLen(0)) } } @@ -974,8 +986,8 @@ var _ = Describe("PostgresqlPublication tests", func() { })) // Get details - details, err := getPublicationTableDetails(item.Status.Name) - if Expect(err).NotTo(HaveOccurred()) { + details, err2 := getPublicationTableDetails(item.Status.Name) + if Expect(err2).NotTo(HaveOccurred()) { Expect(details).To(HaveLen(2)) Expect(details).To(Equal([]*PublicationTableDetail{ { @@ -1049,8 +1061,8 @@ var _ = Describe("PostgresqlPublication tests", func() { })) // Get details - details, err := getPublicationTableDetails(item.Status.Name) - if Expect(err).NotTo(HaveOccurred()) { + details, err2 := getPublicationTableDetails(item.Status.Name) + if Expect(err2).NotTo(HaveOccurred()) { Expect(details).To(HaveLen(2)) Expect(details).To(Equal([]*PublicationTableDetail{ { @@ -1125,8 +1137,8 @@ var _ = Describe("PostgresqlPublication tests", func() { })) // Get details - details, err := getPublicationTableDetails(item.Status.Name) - if Expect(err).NotTo(HaveOccurred()) { + details, err2 := getPublicationTableDetails(item.Status.Name) + if Expect(err2).NotTo(HaveOccurred()) { Expect(details).To(HaveLen(2)) Expect(details).To(Equal([]*PublicationTableDetail{ { @@ -1198,8 +1210,8 @@ var _ = Describe("PostgresqlPublication tests", func() { })) // Get details - details, err := getPublicationTableDetails(item.Status.Name) - if Expect(err).NotTo(HaveOccurred()) { + details, err2 := getPublicationTableDetails(item.Status.Name) + if Expect(err2).NotTo(HaveOccurred()) { Expect(details).To(HaveLen(2)) Expect(details).To(Equal([]*PublicationTableDetail{ { @@ -1303,8 +1315,8 @@ var _ = Describe("PostgresqlPublication tests", func() { })) // Get details - details, err := getPublicationTableDetails(item.Status.Name) - if Expect(err).NotTo(HaveOccurred()) { + details, err2 := getPublicationTableDetails(item.Status.Name) + if Expect(err2).NotTo(HaveOccurred()) { Expect(details).To(HaveLen(1)) Expect(details).To(Equal([]*PublicationTableDetail{ { @@ -1371,8 +1383,8 @@ var _ = Describe("PostgresqlPublication tests", func() { })) // Get details - details, err := getPublicationTableDetails(item.Status.Name) - if Expect(err).NotTo(HaveOccurred()) { + details, err2 := getPublicationTableDetails(item.Status.Name) + if Expect(err2).NotTo(HaveOccurred()) { Expect(details).To(HaveLen(1)) Expect(details).To(Equal([]*PublicationTableDetail{ { @@ -1439,8 +1451,8 @@ var _ = Describe("PostgresqlPublication tests", func() { })) // Get details - details, err := getPublicationTableDetails(item.Status.Name) - if Expect(err).NotTo(HaveOccurred()) { + details, err2 := getPublicationTableDetails(item.Status.Name) + if Expect(err2).NotTo(HaveOccurred()) { Expect(details).To(HaveLen(1)) Expect(details).To(Equal([]*PublicationTableDetail{ { @@ -1507,8 +1519,8 @@ var _ = Describe("PostgresqlPublication tests", func() { })) // Get details - details, err := getPublicationTableDetails(item.Status.Name) - if Expect(err).NotTo(HaveOccurred()) { + details, err2 := getPublicationTableDetails(item.Status.Name) + if Expect(err2).NotTo(HaveOccurred()) { Expect(details).To(HaveLen(1)) Expect(details).To(Equal([]*PublicationTableDetail{ { @@ -1578,8 +1590,8 @@ var _ = Describe("PostgresqlPublication tests", func() { })) // Get details - details, err := getPublicationTableDetails(item.Status.Name) - if Expect(err).NotTo(HaveOccurred()) { + details, err2 := getPublicationTableDetails(item.Status.Name) + if Expect(err2).NotTo(HaveOccurred()) { Expect(details).To(HaveLen(1)) Expect(details).To(Equal([]*PublicationTableDetail{ { @@ -1650,8 +1662,8 @@ var _ = Describe("PostgresqlPublication tests", func() { })) // Get details - details, err := getPublicationTableDetails(item.Status.Name) - if Expect(err).NotTo(HaveOccurred()) { + details, err2 := getPublicationTableDetails(item.Status.Name) + if Expect(err2).NotTo(HaveOccurred()) { Expect(details).To(HaveLen(1)) Expect(details).To(Equal([]*PublicationTableDetail{ { @@ -1719,8 +1731,8 @@ var _ = Describe("PostgresqlPublication tests", func() { })) // Get details - details, err := getPublicationTableDetails(item.Status.Name) - if Expect(err).NotTo(HaveOccurred()) { + details, err2 := getPublicationTableDetails(item.Status.Name) + if Expect(err2).NotTo(HaveOccurred()) { Expect(details).To(HaveLen(1)) Expect(details).To(Equal([]*PublicationTableDetail{ { @@ -1782,7 +1794,7 @@ var _ = Describe("PostgresqlPublication tests", func() { // Check if status hasn't been updated if updatedItem.Status.Phase == item.Status.Phase { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil @@ -1838,7 +1850,7 @@ var _ = Describe("PostgresqlPublication tests", func() { // Check if status hasn't been updated if updatedItem.Status.Phase == item.Status.Phase { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil @@ -1898,7 +1910,7 @@ var _ = Describe("PostgresqlPublication tests", func() { // Check if status hasn't been updated if updatedItem.Status.Hash == hash { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil @@ -1932,8 +1944,8 @@ var _ = Describe("PostgresqlPublication tests", func() { })) // Get details - details, err := getPublicationTableDetails(item.Status.Name) - if Expect(err).NotTo(HaveOccurred()) { + details, err2 := getPublicationTableDetails(item.Status.Name) + if Expect(err2).NotTo(HaveOccurred()) { Expect(details).To(HaveLen(2)) Expect(details).To(Equal([]*PublicationTableDetail{ { @@ -2005,7 +2017,7 @@ var _ = Describe("PostgresqlPublication tests", func() { // Check if status hasn't been updated if updatedItem.Status.Hash == hash { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil @@ -2112,7 +2124,7 @@ var _ = Describe("PostgresqlPublication tests", func() { // Check if status hasn't been updated if updatedItem.Status.Phase == item.Status.Phase { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil @@ -2174,7 +2186,7 @@ var _ = Describe("PostgresqlPublication tests", func() { // Check if status hasn't been updated if updatedItem.Status.Hash == hash { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil @@ -2208,8 +2220,8 @@ var _ = Describe("PostgresqlPublication tests", func() { })) // Get details - details, err := getPublicationTableDetails(item.Status.Name) - if Expect(err).NotTo(HaveOccurred()) { + details, err2 := getPublicationTableDetails(item.Status.Name) + if Expect(err2).NotTo(HaveOccurred()) { Expect(details).To(HaveLen(1)) Expect(details).To(Equal([]*PublicationTableDetail{ { @@ -2275,7 +2287,7 @@ var _ = Describe("PostgresqlPublication tests", func() { // Check if status hasn't been updated if updatedItem.Status.Hash == hash { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil @@ -2309,8 +2321,8 @@ var _ = Describe("PostgresqlPublication tests", func() { })) // Get details - details, err := getPublicationTableDetails(item.Status.Name) - if Expect(err).NotTo(HaveOccurred()) { + details, err2 := getPublicationTableDetails(item.Status.Name) + if Expect(err2).NotTo(HaveOccurred()) { Expect(details).To(HaveLen(2)) Expect(details).To(Equal([]*PublicationTableDetail{ { @@ -2383,7 +2395,7 @@ var _ = Describe("PostgresqlPublication tests", func() { // Check if status hasn't been updated if updatedItem.Status.Hash == hash { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil @@ -2493,7 +2505,7 @@ var _ = Describe("PostgresqlPublication tests", func() { // Check if status hasn't been updated if updatedItem.Status.Phase == item.Status.Phase { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil @@ -2553,7 +2565,7 @@ var _ = Describe("PostgresqlPublication tests", func() { // Check if status hasn't been updated if updatedItem.Status.Hash == hash { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil @@ -2587,8 +2599,8 @@ var _ = Describe("PostgresqlPublication tests", func() { })) // Get details - details, err := getPublicationTableDetails(item.Status.Name) - if Expect(err).NotTo(HaveOccurred()) { + details, err2 := getPublicationTableDetails(item.Status.Name) + if Expect(err2).NotTo(HaveOccurred()) { Expect(details).To(HaveLen(2)) Expect(details).To(Equal([]*PublicationTableDetail{ { @@ -2663,7 +2675,7 @@ var _ = Describe("PostgresqlPublication tests", func() { // Check if status hasn't been updated if updatedItem.Status.Hash == hash { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil @@ -2697,8 +2709,8 @@ var _ = Describe("PostgresqlPublication tests", func() { })) // Get details - details, err := getPublicationTableDetails(item.Status.Name) - if Expect(err).NotTo(HaveOccurred()) { + details, err2 := getPublicationTableDetails(item.Status.Name) + if Expect(err2).NotTo(HaveOccurred()) { Expect(details).To(HaveLen(1)) Expect(details).To(Equal([]*PublicationTableDetail{ { @@ -2767,7 +2779,7 @@ var _ = Describe("PostgresqlPublication tests", func() { // Check if status hasn't been updated if updatedItem.Status.Hash == hash { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil @@ -2801,8 +2813,8 @@ var _ = Describe("PostgresqlPublication tests", func() { })) // Get details - details, err := getPublicationTableDetails(item.Status.Name) - if Expect(err).NotTo(HaveOccurred()) { + details, err2 := getPublicationTableDetails(item.Status.Name) + if Expect(err2).NotTo(HaveOccurred()) { Expect(details).To(HaveLen(2)) Expect(details).To(Equal([]*PublicationTableDetail{ { @@ -2876,7 +2888,7 @@ var _ = Describe("PostgresqlPublication tests", func() { // Check if status hasn't been updated if updatedItem.Status.Hash == hash { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil @@ -2910,8 +2922,8 @@ var _ = Describe("PostgresqlPublication tests", func() { })) // Get details - details, err := getPublicationTableDetails(item.Status.Name) - if Expect(err).NotTo(HaveOccurred()) { + details, err2 := getPublicationTableDetails(item.Status.Name) + if Expect(err2).NotTo(HaveOccurred()) { Expect(details).To(HaveLen(1)) Expect(details).To(Equal([]*PublicationTableDetail{ { @@ -2979,7 +2991,7 @@ var _ = Describe("PostgresqlPublication tests", func() { // Check if status hasn't been updated if updatedItem.Status.Hash == hash { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil @@ -3013,8 +3025,8 @@ var _ = Describe("PostgresqlPublication tests", func() { })) // Get details - details, err := getPublicationTableDetails(item.Status.Name) - if Expect(err).NotTo(HaveOccurred()) { + details, err2 := getPublicationTableDetails(item.Status.Name) + if Expect(err2).NotTo(HaveOccurred()) { Expect(details).To(HaveLen(1)) Expect(details).To(Equal([]*PublicationTableDetail{ { @@ -3082,7 +3094,7 @@ var _ = Describe("PostgresqlPublication tests", func() { // Check if status hasn't been updated if updatedItem.Status.Hash == hash { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil @@ -3116,8 +3128,8 @@ var _ = Describe("PostgresqlPublication tests", func() { })) // Get details - details, err := getPublicationTableDetails(item.Status.Name) - if Expect(err).NotTo(HaveOccurred()) { + details, err2 := getPublicationTableDetails(item.Status.Name) + if Expect(err2).NotTo(HaveOccurred()) { Expect(details).To(HaveLen(1)) Expect(details).To(Equal([]*PublicationTableDetail{ { @@ -3185,7 +3197,7 @@ var _ = Describe("PostgresqlPublication tests", func() { // Check if status hasn't been updated if updatedItem.Status.Hash == hash { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil @@ -3219,8 +3231,8 @@ var _ = Describe("PostgresqlPublication tests", func() { })) // Get details - details, err := getPublicationTableDetails(item.Status.Name) - if Expect(err).NotTo(HaveOccurred()) { + details, err2 := getPublicationTableDetails(item.Status.Name) + if Expect(err2).NotTo(HaveOccurred()) { Expect(details).To(HaveLen(1)) Expect(details).To(Equal([]*PublicationTableDetail{ { @@ -3288,7 +3300,7 @@ var _ = Describe("PostgresqlPublication tests", func() { // Check if status hasn't been updated if updatedItem.Status.Hash == hash { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil @@ -3322,8 +3334,8 @@ var _ = Describe("PostgresqlPublication tests", func() { })) // Get details - details, err := getPublicationTableDetails(item.Status.Name) - if Expect(err).NotTo(HaveOccurred()) { + details, err2 := getPublicationTableDetails(item.Status.Name) + if Expect(err2).NotTo(HaveOccurred()) { Expect(details).To(HaveLen(1)) Expect(details).To(Equal([]*PublicationTableDetail{ { @@ -3391,7 +3403,7 @@ var _ = Describe("PostgresqlPublication tests", func() { // Check if status hasn't been updated if updatedItem.Status.Hash == hash { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil @@ -3425,8 +3437,8 @@ var _ = Describe("PostgresqlPublication tests", func() { })) // Get details - details, err := getPublicationTableDetails(item.Status.Name) - if Expect(err).NotTo(HaveOccurred()) { + details, err2 := getPublicationTableDetails(item.Status.Name) + if Expect(err2).NotTo(HaveOccurred()) { Expect(details).To(HaveLen(1)) Expect(details).To(Equal([]*PublicationTableDetail{ { @@ -3495,7 +3507,7 @@ var _ = Describe("PostgresqlPublication tests", func() { // Check if status hasn't been updated if updatedItem.Status.Hash == hash { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil @@ -4017,7 +4029,9 @@ var _ = Describe("PostgresqlPublication tests", func() { // Checks Expect(item.Status.Ready).To(BeFalse()) Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.PublicationFailedPhase)) - Expect(item.Status.Message).To(Equal(`publication in database and spec are out of sync for 'for all tables' and values must be aligned to continue`)) + Expect( + item.Status.Message, + ).To(Equal(`publication in database and spec are out of sync for 'for all tables' and values must be aligned to continue`)) Expect(item.Status.AllTables).To(BeNil()) Expect(item.Status.Hash).To(Equal("")) Expect(item.Status.Name).To(Equal("")) @@ -4060,7 +4074,9 @@ var _ = Describe("PostgresqlPublication tests", func() { // Checks Expect(item.Status.Ready).To(BeFalse()) Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.PublicationFailedPhase)) - Expect(item.Status.Message).To(Equal(`publication in database and spec are out of sync for 'for all tables' and values must be aligned to continue`)) + Expect( + item.Status.Message, + ).To(Equal(`publication in database and spec are out of sync for 'for all tables' and values must be aligned to continue`)) Expect(item.Status.AllTables).To(BeNil()) Expect(item.Status.Hash).To(Equal("")) Expect(item.Status.Name).To(Equal("")) @@ -4088,10 +4104,11 @@ var _ = Describe("PostgresqlPublication tests", func() { setupPGDB(false) // Create replication slot - createReplicationSlotInMainDB(pgpublicationPublicationName1, DefaultReplicationSlotPlugin) + err := createReplicationSlotInMainDB(pgpublicationPublicationName1, DefaultReplicationSlotPlugin) + Expect(err).NotTo(HaveOccurred()) // Create tables - err := create2KnownTablesWithColumnsInPublicSchema() + err = create2KnownTablesWithColumnsInPublicSchema() Expect(err).NotTo(HaveOccurred()) // Setup a pg publication @@ -4162,7 +4179,7 @@ var _ = Describe("PostgresqlPublication tests", func() { // Check if status hasn't been updated if data.Owner != pgdb.Status.Roles.Owner { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil @@ -4222,7 +4239,7 @@ var _ = Describe("PostgresqlPublication tests", func() { // Check if status hasn't been updated if data.PublicationViaRoot { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil @@ -4285,7 +4302,7 @@ var _ = Describe("PostgresqlPublication tests", func() { // Check if status hasn't been updated if !data.PublicationViaRoot { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil @@ -4345,7 +4362,7 @@ var _ = Describe("PostgresqlPublication tests", func() { // Check if status hasn't been updated if !data.Truncate { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil @@ -4406,7 +4423,7 @@ var _ = Describe("PostgresqlPublication tests", func() { // Check if status hasn't been updated if !data.Truncate { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil @@ -4469,7 +4486,7 @@ var _ = Describe("PostgresqlPublication tests", func() { // Check if status hasn't been updated if data.Truncate { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil @@ -4530,7 +4547,7 @@ var _ = Describe("PostgresqlPublication tests", func() { // Check hasn't been updated if len(details) == 1 { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil @@ -4596,7 +4613,7 @@ var _ = Describe("PostgresqlPublication tests", func() { // Check hasn't been updated if len(details) == 1 { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil @@ -4664,12 +4681,12 @@ var _ = Describe("PostgresqlPublication tests", func() { } if len(details) == 0 { - return gerrors.New("must have tables") + return errors.New("must have tables") } // Check hasn't been updated if details[0].AdditionalWhere == nil || *details[0].AdditionalWhere != `('id'::text = 'value'::text)` { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil @@ -4730,12 +4747,12 @@ var _ = Describe("PostgresqlPublication tests", func() { } if len(details) == 0 { - return gerrors.New("must have tables") + return errors.New("must have tables") } // Check hasn't been updated if details[0].AdditionalWhere != nil { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil @@ -4797,12 +4814,12 @@ var _ = Describe("PostgresqlPublication tests", func() { } if len(details) == 0 { - return gerrors.New("must have tables") + return errors.New("must have tables") } // Check hasn't been updated if details[0].AdditionalWhere == nil || *details[0].AdditionalWhere != `('id'::text = 'value'::text)` { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil @@ -4864,12 +4881,12 @@ var _ = Describe("PostgresqlPublication tests", func() { } if len(details) == 0 { - return gerrors.New("must have tables") + return errors.New("must have tables") } // Check hasn't been updated if len(details[0].Columns) > 1 { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil @@ -4931,12 +4948,12 @@ var _ = Describe("PostgresqlPublication tests", func() { } if len(details) == 0 { - return gerrors.New("must have tables") + return errors.New("must have tables") } // Check hasn't been updated if len(details[0].Columns) == 1 { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil @@ -4998,12 +5015,12 @@ var _ = Describe("PostgresqlPublication tests", func() { } if len(details) == 0 { - return gerrors.New("must have tables") + return errors.New("must have tables") } // Check hasn't been updated if len(details[0].Columns) == 2 { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil @@ -5065,15 +5082,15 @@ var _ = Describe("PostgresqlPublication tests", func() { } if len(details) == 0 { - return gerrors.New("must have tables") + return errors.New("must have tables") } if len(details[0].Columns) == 0 { - return gerrors.New("must have columns") + return errors.New("must have columns") } // Check hasn't been updated if details[0].Columns[0] == "id" { - return gerrors.New("hasn't been updated by operator") + return errors.New("hasn't been updated by operator") } return nil diff --git a/internal/controller/postgresql/postgresqluserrole_controller.go b/internal/controller/postgresql/postgresqluserrole_controller.go index 4a042b5..595b817 100644 --- a/internal/controller/postgresql/postgresqluserrole_controller.go +++ b/internal/controller/postgresql/postgresqluserrole_controller.go @@ -24,24 +24,25 @@ import ( "strings" "time" - corev1 "k8s.io/api/core/v1" + "github.com/go-logr/logr" + "github.com/prometheus/client_golang/prometheus" + "github.com/thoas/go-funk" "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/tools/record" - ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/reconcile" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ctrl "sigs.k8s.io/controller-runtime" + "github.com/easymile/postgresql-operator/api/postgresql/v1alpha1" "github.com/easymile/postgresql-operator/internal/controller/config" "github.com/easymile/postgresql-operator/internal/controller/postgresql/postgres" "github.com/easymile/postgresql-operator/internal/controller/utils" - "github.com/go-logr/logr" - "github.com/prometheus/client_golang/prometheus" - "github.com/thoas/go-funk" ) const ( @@ -101,15 +102,14 @@ func (r *PostgresqlUserRoleReconciler) Reconcile(ctx context.Context, req ctrl.R // Issue with this logger: controller and controllerKind are incorrect // Build another logger from upper to fix this. // reqLogger := log.FromContext(ctx) - reqLogger := r.Log.WithValues("Request.Namespace", req.Namespace, "Request.Name", req.Name) reqLogger.Info("Reconciling PostgresqlUserRole") // Fetch the PostgresqlUser instance instance := &v1alpha1.PostgresqlUserRole{} - err := r.Get(ctx, req.NamespacedName, instance) + err := r.Get(ctx, req.NamespacedName, instance) if err != nil { if errors.IsNotFound(err) { // Request object not found, could have been deleted after reconcile request. @@ -304,7 +304,13 @@ func (r *PostgresqlUserRoleReconciler) mainReconcile( // Ensure they aren't empty if username == "" || password == "" { - return r.manageError(ctx, reqLogger, instance, originalPatch, errors.NewBadRequest("username or password in work secret are empty so something is interfering with operator")) + return r.manageError( + ctx, + reqLogger, + instance, + originalPatch, + errors.NewBadRequest("username or password in work secret are empty so something is interfering with operator"), + ) } // Compute username changed @@ -527,7 +533,7 @@ func (r *PostgresqlUserRoleReconciler) cleanOldSecrets( } // Check if secret is owned by the current instance - foundMarker := funk.Find(item.ObjectMeta.OwnerReferences, func(it metav1.OwnerReference) bool { + foundMarker := funk.Find(item.OwnerReferences, func(it metav1.OwnerReference) bool { return it.UID == instance.UID }) @@ -746,8 +752,24 @@ func (r *PostgresqlUserRoleReconciler) managePGUserRights( return err } - logger.Info("Successfully revoked set role from user on specific database in engine", "postgresqlEngine", key, "role", item.Role, "database", item.Database) - r.Recorder.Eventf(instance, "Normal", "Updated", "Successfully revoked set role %s from user on specific database %s in engine %s", item.Role, item.Database, key) + logger.Info( + "Successfully revoked set role from user on specific database in engine", + "postgresqlEngine", + key, + "role", + item.Role, + "database", + item.Database, + ) + r.Recorder.Eventf( + instance, + "Normal", + "Updated", + "Successfully revoked set role %s from user on specific database %s in engine %s", + item.Role, + item.Database, + key, + ) } } @@ -759,6 +781,7 @@ func (*PostgresqlUserRoleReconciler) getDBRoleFromPrivilege( dbInstance *v1alpha1.PostgresqlDatabase, userRolePrivilege *v1alpha1.PostgresqlUserRolePrivilege, ) string { + //nolint:exhaustive switch userRolePrivilege.Privilege { case v1alpha1.ReaderPrivilege: return dbInstance.Status.Roles.Reader @@ -818,7 +841,8 @@ func diffAttributes(sqlAttributes, wantedAttributes *postgres.RoleAttributes) *p // Check differences for ConnectionLimit if !reflect.DeepEqual(sqlAttributes.ConnectionLimit, wantedAttributes.ConnectionLimit) { // Check if we are in a reset case - if wantedAttributes.ConnectionLimit == nil && sqlAttributes.ConnectionLimit != nil && *sqlAttributes.ConnectionLimit != postgres.DefaultAttributeConnectionLimit { + if wantedAttributes.ConnectionLimit == nil && sqlAttributes.ConnectionLimit != nil && + *sqlAttributes.ConnectionLimit != postgres.DefaultAttributeConnectionLimit { // Change value needed => Reset to default attributes.ConnectionLimit = &postgres.DefaultAttributeConnectionLimit } else { @@ -943,7 +967,7 @@ func (r *PostgresqlUserRoleReconciler) createOrUpdateWorkSecretForManagedMode( / ctx context.Context, logger logr.Logger, instance *v1alpha1.PostgresqlUserRole, -) (*corev1.Secret, string, bool, bool, error) { +) (*corev1.Secret, string, bool, bool, error) { //nolint:revive // We have multiple return, we know // Prepare values oldUsername := "" passwordChanged := false @@ -1453,7 +1477,12 @@ func (r *PostgresqlUserRoleReconciler) validateInstance( // Check if username length is acceptable if len(username) > postgres.MaxIdentifierLength { - errStr := fmt.Sprintf("Username is too long. It must be <= %d. %s is %d character. Username length must be reduced", postgres.MaxIdentifierLength, username, len(username)) + errStr := fmt.Sprintf( + "Username is too long. It must be <= %d. %s is %d character. Username length must be reduced", + postgres.MaxIdentifierLength, + username, + len(username), + ) return errors.NewBadRequest(errStr) } @@ -1538,7 +1567,8 @@ func (r *PostgresqlUserRoleReconciler) validateInstance( for _, userInstance := range list.Items { // Check that role prefix isn't declared in another user // TODO Try to validate that this is unique per engine and not for the whole cluster - if userInstance.Name != instance.Name && userInstance.Namespace != instance.Namespace && userInstance.Spec.RolePrefix == instance.Spec.RolePrefix { + if userInstance.Name != instance.Name && userInstance.Namespace != instance.Namespace && + userInstance.Spec.RolePrefix == instance.Spec.RolePrefix { return errors.NewBadRequest("RolePrefix is declared in another PostgresqlUser. This field value must be unique.") } } @@ -1561,7 +1591,9 @@ func (r *PostgresqlUserRoleReconciler) updateInstance( // Update work generated secret with a generated uuid if instance.Spec.WorkGeneratedSecretName == "" { - instance.Spec.WorkGeneratedSecretName = DefaultWorkGeneratedSecretNamePrefix + strings.ToLower(utils.GetRandomString(DefaultWorkGeneratedSecretNameRandomLength)) + instance.Spec.WorkGeneratedSecretName = DefaultWorkGeneratedSecretNamePrefix + strings.ToLower( + utils.GetRandomString(DefaultWorkGeneratedSecretNameRandomLength), + ) } // Check if update is needed diff --git a/internal/controller/postgresql/postgresqluserrole_controller_test.go b/internal/controller/postgresql/postgresqluserrole_controller_test.go index 53766d4..377b369 100644 --- a/internal/controller/postgresql/postgresqluserrole_controller_test.go +++ b/internal/controller/postgresql/postgresqluserrole_controller_test.go @@ -6,22 +6,26 @@ import ( "strings" "time" - "github.com/easymile/postgresql-operator/api/postgresql/common" - "github.com/easymile/postgresql-operator/api/postgresql/v1alpha1" - postgresqlv1alpha1 "github.com/easymile/postgresql-operator/api/postgresql/v1alpha1" + "k8s.io/apimachinery/pkg/types" + + //nolint:revive . "github.com/onsi/ginkgo/v2" + //nolint:revive . "github.com/onsi/gomega" + corev1 "k8s.io/api/core/v1" apimachineryErrors "k8s.io/apimachinery/pkg/api/errors" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/types" + + "github.com/easymile/postgresql-operator/api/postgresql/common" + "github.com/easymile/postgresql-operator/api/postgresql/v1alpha1" ) var _ = Describe("PostgresqlUserRole tests", func() { AfterEach(cleanupFunction) It("shouldn't accept input without any specs", func() { - err := k8sClient.Create(ctx, &postgresqlv1alpha1.PostgresqlUserRole{ + err := k8sClient.Create(ctx, &v1alpha1.PostgresqlUserRole{ ObjectMeta: v1.ObjectMeta{ Name: pgurName, Namespace: pgurNamespace, @@ -38,11 +42,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Check that content is correct causes := stErr.Status().Details.Causes - Expect(causes).To(HaveLen(1)) + Expect(causes).To(HaveLen(2)) // Search all fields fields := map[string]bool{ "spec.privileges": false, + "spec.mode": false, } // Loop over all causes @@ -61,16 +66,16 @@ var _ = Describe("PostgresqlUserRole tests", func() { Describe("Provided mode", func() { It("should fail when import secret isn't provided", func() { - it := &postgresqlv1alpha1.PostgresqlUserRole{ + it := &v1alpha1.PostgresqlUserRole{ ObjectMeta: v1.ObjectMeta{ Name: pgurName, Namespace: pgurNamespace, }, - Spec: postgresqlv1alpha1.PostgresqlUserRoleSpec{ - Mode: postgresqlv1alpha1.ProvidedMode, - Privileges: []*postgresqlv1alpha1.PostgresqlUserRolePrivilege{ + Spec: v1alpha1.PostgresqlUserRoleSpec{ + Mode: v1alpha1.ProvidedMode, + Privileges: []*v1alpha1.PostgresqlUserRolePrivilege{ { - Privilege: postgresqlv1alpha1.OwnerPrivilege, + Privilege: v1alpha1.OwnerPrivilege, Database: &common.CRLink{Name: pgdbName, Namespace: pgdbNamespace}, GeneratedSecretName: pgurDBSecretName, }, @@ -81,7 +86,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Create user Expect(k8sClient.Create(ctx, it)).Should(Succeed()) - item := &postgresqlv1alpha1.PostgresqlUserRole{} + item := &v1alpha1.PostgresqlUserRole{} // Get updated user Eventually( func() error { @@ -95,7 +100,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { } // Check if status hasn't been updated - if item.Status.Phase == postgresqlv1alpha1.UserRoleNoPhase { + if item.Status.Phase == v1alpha1.UserRoleNoPhase { return errors.New("pgur hasn't been updated by operator") } @@ -108,29 +113,29 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeFalse()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleFailedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleFailedPhase)) Expect(item.Status.Message).To(Equal("PostgresqlUserRole is in provided mode without any ImportSecretName")) }) It("should fail when privileges contains 2 times the same db (twice fully declared)", func() { setupPGURImportSecret() - it := &postgresqlv1alpha1.PostgresqlUserRole{ + it := &v1alpha1.PostgresqlUserRole{ ObjectMeta: v1.ObjectMeta{ Name: pgurName, Namespace: pgurNamespace, }, - Spec: postgresqlv1alpha1.PostgresqlUserRoleSpec{ - Mode: postgresqlv1alpha1.ProvidedMode, + Spec: v1alpha1.PostgresqlUserRoleSpec{ + Mode: v1alpha1.ProvidedMode, ImportSecretName: pgurImportSecretName, - Privileges: []*postgresqlv1alpha1.PostgresqlUserRolePrivilege{ + Privileges: []*v1alpha1.PostgresqlUserRolePrivilege{ { - Privilege: postgresqlv1alpha1.OwnerPrivilege, + Privilege: v1alpha1.OwnerPrivilege, Database: &common.CRLink{Name: pgdbName, Namespace: pgdbNamespace}, GeneratedSecretName: pgurDBSecretName, }, { - Privilege: postgresqlv1alpha1.WriterPrivilege, + Privilege: v1alpha1.WriterPrivilege, Database: &common.CRLink{Name: pgdbName, Namespace: pgdbNamespace}, GeneratedSecretName: pgurDBSecretName, }, @@ -141,7 +146,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Create user Expect(k8sClient.Create(ctx, it)).Should(Succeed()) - item := &postgresqlv1alpha1.PostgresqlUserRole{} + item := &v1alpha1.PostgresqlUserRole{} // Get updated user Eventually( func() error { @@ -155,7 +160,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { } // Check if status hasn't been updated - if item.Status.Phase == postgresqlv1alpha1.UserRoleNoPhase { + if item.Status.Phase == v1alpha1.UserRoleNoPhase { return errors.New("pgur hasn't been updated by operator") } @@ -168,29 +173,29 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeFalse()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleFailedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleFailedPhase)) Expect(item.Status.Message).To(Equal("Privilege list mustn't have the same database listed multiple times")) }) It("should fail when privileges contains 2 times the same db (1 fully declared, 1 without namespace)", func() { setupPGURImportSecret() - it := &postgresqlv1alpha1.PostgresqlUserRole{ + it := &v1alpha1.PostgresqlUserRole{ ObjectMeta: v1.ObjectMeta{ Name: pgurName, Namespace: pgurNamespace, }, - Spec: postgresqlv1alpha1.PostgresqlUserRoleSpec{ - Mode: postgresqlv1alpha1.ProvidedMode, + Spec: v1alpha1.PostgresqlUserRoleSpec{ + Mode: v1alpha1.ProvidedMode, ImportSecretName: pgurImportSecretName, - Privileges: []*postgresqlv1alpha1.PostgresqlUserRolePrivilege{ + Privileges: []*v1alpha1.PostgresqlUserRolePrivilege{ { - Privilege: postgresqlv1alpha1.OwnerPrivilege, + Privilege: v1alpha1.OwnerPrivilege, Database: &common.CRLink{Name: pgdbName, Namespace: pgurNamespace}, GeneratedSecretName: pgurDBSecretName, }, { - Privilege: postgresqlv1alpha1.WriterPrivilege, + Privilege: v1alpha1.WriterPrivilege, Database: &common.CRLink{Name: pgdbName}, GeneratedSecretName: pgurDBSecretName, }, @@ -201,7 +206,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Create user Expect(k8sClient.Create(ctx, it)).Should(Succeed()) - item := &postgresqlv1alpha1.PostgresqlUserRole{} + item := &v1alpha1.PostgresqlUserRole{} // Get updated user Eventually( func() error { @@ -215,7 +220,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { } // Check if status hasn't been updated - if item.Status.Phase == postgresqlv1alpha1.UserRoleNoPhase { + if item.Status.Phase == v1alpha1.UserRoleNoPhase { return errors.New("pgur hasn't been updated by operator") } @@ -228,7 +233,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeFalse()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleFailedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleFailedPhase)) Expect(item.Status.Message).To(Equal("Privilege list mustn't have the same database listed multiple times")) }) @@ -244,17 +249,17 @@ var _ = Describe("PostgresqlUserRole tests", func() { Expect(k8sClient.Create(ctx, sec)).To(Succeed()) - it := &postgresqlv1alpha1.PostgresqlUserRole{ + it := &v1alpha1.PostgresqlUserRole{ ObjectMeta: v1.ObjectMeta{ Name: pgurName, Namespace: pgurNamespace, }, - Spec: postgresqlv1alpha1.PostgresqlUserRoleSpec{ - Mode: postgresqlv1alpha1.ProvidedMode, + Spec: v1alpha1.PostgresqlUserRoleSpec{ + Mode: v1alpha1.ProvidedMode, ImportSecretName: pgurImportSecretName, - Privileges: []*postgresqlv1alpha1.PostgresqlUserRolePrivilege{ + Privileges: []*v1alpha1.PostgresqlUserRolePrivilege{ { - Privilege: postgresqlv1alpha1.OwnerPrivilege, + Privilege: v1alpha1.OwnerPrivilege, Database: &common.CRLink{Name: pgdbName, Namespace: pgdbNamespace}, GeneratedSecretName: pgurDBSecretName, }, @@ -265,7 +270,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Create user Expect(k8sClient.Create(ctx, it)).Should(Succeed()) - item := &postgresqlv1alpha1.PostgresqlUserRole{} + item := &v1alpha1.PostgresqlUserRole{} // Get updated user Eventually( func() error { @@ -279,7 +284,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { } // Check if status hasn't been updated - if item.Status.Phase == postgresqlv1alpha1.UserRoleNoPhase { + if item.Status.Phase == v1alpha1.UserRoleNoPhase { return errors.New("pgur hasn't been updated by operator") } @@ -292,7 +297,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeFalse()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleFailedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleFailedPhase)) Expect(item.Status.Message).To(Equal("Import secret must have a USERNAME and PASSWORD valuated keys")) }) @@ -310,17 +315,17 @@ var _ = Describe("PostgresqlUserRole tests", func() { Expect(k8sClient.Create(ctx, sec)).To(Succeed()) - it := &postgresqlv1alpha1.PostgresqlUserRole{ + it := &v1alpha1.PostgresqlUserRole{ ObjectMeta: v1.ObjectMeta{ Name: pgurName, Namespace: pgurNamespace, }, - Spec: postgresqlv1alpha1.PostgresqlUserRoleSpec{ - Mode: postgresqlv1alpha1.ProvidedMode, + Spec: v1alpha1.PostgresqlUserRoleSpec{ + Mode: v1alpha1.ProvidedMode, ImportSecretName: pgurImportSecretName, - Privileges: []*postgresqlv1alpha1.PostgresqlUserRolePrivilege{ + Privileges: []*v1alpha1.PostgresqlUserRolePrivilege{ { - Privilege: postgresqlv1alpha1.OwnerPrivilege, + Privilege: v1alpha1.OwnerPrivilege, Database: &common.CRLink{Name: pgdbName, Namespace: pgdbNamespace}, GeneratedSecretName: pgurDBSecretName, }, @@ -331,7 +336,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Create user Expect(k8sClient.Create(ctx, it)).Should(Succeed()) - item := &postgresqlv1alpha1.PostgresqlUserRole{} + item := &v1alpha1.PostgresqlUserRole{} // Get updated user Eventually( func() error { @@ -345,7 +350,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { } // Check if status hasn't been updated - if item.Status.Phase == postgresqlv1alpha1.UserRoleNoPhase { + if item.Status.Phase == v1alpha1.UserRoleNoPhase { return errors.New("pgur hasn't been updated by operator") } @@ -358,7 +363,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeFalse()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleFailedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleFailedPhase)) Expect(item.Status.Message).To(Equal("Import secret must have a USERNAME and PASSWORD valuated keys")) }) @@ -376,17 +381,17 @@ var _ = Describe("PostgresqlUserRole tests", func() { Expect(k8sClient.Create(ctx, sec)).To(Succeed()) - it := &postgresqlv1alpha1.PostgresqlUserRole{ + it := &v1alpha1.PostgresqlUserRole{ ObjectMeta: v1.ObjectMeta{ Name: pgurName, Namespace: pgurNamespace, }, - Spec: postgresqlv1alpha1.PostgresqlUserRoleSpec{ - Mode: postgresqlv1alpha1.ProvidedMode, + Spec: v1alpha1.PostgresqlUserRoleSpec{ + Mode: v1alpha1.ProvidedMode, ImportSecretName: pgurImportSecretName, - Privileges: []*postgresqlv1alpha1.PostgresqlUserRolePrivilege{ + Privileges: []*v1alpha1.PostgresqlUserRolePrivilege{ { - Privilege: postgresqlv1alpha1.OwnerPrivilege, + Privilege: v1alpha1.OwnerPrivilege, Database: &common.CRLink{Name: pgdbName, Namespace: pgdbNamespace}, GeneratedSecretName: pgurDBSecretName, }, @@ -397,7 +402,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Create user Expect(k8sClient.Create(ctx, it)).Should(Succeed()) - item := &postgresqlv1alpha1.PostgresqlUserRole{} + item := &v1alpha1.PostgresqlUserRole{} // Get updated user Eventually( func() error { @@ -411,7 +416,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { } // Check if status hasn't been updated - if item.Status.Phase == postgresqlv1alpha1.UserRoleNoPhase { + if item.Status.Phase == v1alpha1.UserRoleNoPhase { return errors.New("pgur hasn't been updated by operator") } @@ -424,7 +429,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeFalse()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleFailedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleFailedPhase)) Expect(item.Status.Message).To(Equal("Import secret must have a USERNAME and PASSWORD valuated keys")) }) @@ -443,17 +448,17 @@ var _ = Describe("PostgresqlUserRole tests", func() { Expect(k8sClient.Create(ctx, sec)).To(Succeed()) - it := &postgresqlv1alpha1.PostgresqlUserRole{ + it := &v1alpha1.PostgresqlUserRole{ ObjectMeta: v1.ObjectMeta{ Name: pgurName, Namespace: pgurNamespace, }, - Spec: postgresqlv1alpha1.PostgresqlUserRoleSpec{ - Mode: postgresqlv1alpha1.ProvidedMode, + Spec: v1alpha1.PostgresqlUserRoleSpec{ + Mode: v1alpha1.ProvidedMode, ImportSecretName: pgurImportSecretName, - Privileges: []*postgresqlv1alpha1.PostgresqlUserRolePrivilege{ + Privileges: []*v1alpha1.PostgresqlUserRolePrivilege{ { - Privilege: postgresqlv1alpha1.OwnerPrivilege, + Privilege: v1alpha1.OwnerPrivilege, Database: &common.CRLink{Name: pgdbName, Namespace: pgdbNamespace}, GeneratedSecretName: pgurDBSecretName, }, @@ -464,7 +469,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Create user Expect(k8sClient.Create(ctx, it)).Should(Succeed()) - item := &postgresqlv1alpha1.PostgresqlUserRole{} + item := &v1alpha1.PostgresqlUserRole{} // Get updated user Eventually( func() error { @@ -478,7 +483,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { } // Check if status hasn't been updated - if item.Status.Phase == postgresqlv1alpha1.UserRoleNoPhase { + if item.Status.Phase == v1alpha1.UserRoleNoPhase { return errors.New("pgur hasn't been updated by operator") } @@ -491,25 +496,27 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeFalse()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleFailedPhase)) - Expect(item.Status.Message).To(Equal("Username is too long. It must be <= 63. fakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefake is 96 character. Username length must be reduced")) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleFailedPhase)) + Expect( + item.Status.Message, + ).To(Equal("Username is too long. It must be <= 63. fakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefake is 96 character. Username length must be reduced")) }) It("should fail to look a not found pgdb", func() { // Create secret setupPGURImportSecret() - it := &postgresqlv1alpha1.PostgresqlUserRole{ + it := &v1alpha1.PostgresqlUserRole{ ObjectMeta: v1.ObjectMeta{ Name: pgurName, Namespace: pgurNamespace, }, - Spec: postgresqlv1alpha1.PostgresqlUserRoleSpec{ - Mode: postgresqlv1alpha1.ProvidedMode, + Spec: v1alpha1.PostgresqlUserRoleSpec{ + Mode: v1alpha1.ProvidedMode, ImportSecretName: pgurImportSecretName, - Privileges: []*postgresqlv1alpha1.PostgresqlUserRolePrivilege{ + Privileges: []*v1alpha1.PostgresqlUserRolePrivilege{ { - Privilege: postgresqlv1alpha1.OwnerPrivilege, + Privilege: v1alpha1.OwnerPrivilege, Database: &common.CRLink{Name: "fake", Namespace: "fake"}, GeneratedSecretName: "pgur", }, @@ -520,7 +527,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Create user Expect(k8sClient.Create(ctx, it)).Should(Succeed()) - item := &postgresqlv1alpha1.PostgresqlUserRole{} + item := &v1alpha1.PostgresqlUserRole{} // Get updated user Eventually( func() error { @@ -534,7 +541,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { } // Check if status hasn't been updated - if item.Status.Phase == postgresqlv1alpha1.UserRoleNoPhase { + if item.Status.Phase == v1alpha1.UserRoleNoPhase { return errors.New("pgur hasn't been updated by operator") } @@ -547,18 +554,18 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeFalse()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleFailedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleFailedPhase)) Expect(item.Status.Message).To(ContainSubstring("\"fake\" not found")) }) It("should fail with a non ready pgdb", func() { // Create pgdb - pgdb := &postgresqlv1alpha1.PostgresqlDatabase{ + pgdb := &v1alpha1.PostgresqlDatabase{ ObjectMeta: v1.ObjectMeta{ Name: pgdbName, Namespace: pgdbNamespace, }, - Spec: postgresqlv1alpha1.PostgresqlDatabaseSpec{ + Spec: v1alpha1.PostgresqlDatabaseSpec{ Database: pgdbDBName, EngineConfiguration: &common.CRLink{ Name: "fake", @@ -574,18 +581,18 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Create secret setupPGURImportSecret() - it := &postgresqlv1alpha1.PostgresqlUserRole{ + it := &v1alpha1.PostgresqlUserRole{ ObjectMeta: v1.ObjectMeta{ Name: pgurName, Namespace: pgurNamespace, }, - Spec: postgresqlv1alpha1.PostgresqlUserRoleSpec{ - Mode: postgresqlv1alpha1.ProvidedMode, + Spec: v1alpha1.PostgresqlUserRoleSpec{ + Mode: v1alpha1.ProvidedMode, ImportSecretName: pgurImportSecretName, WorkGeneratedSecretName: pgurWorkSecretName, - Privileges: []*postgresqlv1alpha1.PostgresqlUserRolePrivilege{ + Privileges: []*v1alpha1.PostgresqlUserRolePrivilege{ { - Privilege: postgresqlv1alpha1.OwnerPrivilege, + Privilege: v1alpha1.OwnerPrivilege, Database: &common.CRLink{Name: pgdbName, Namespace: pgdbNamespace}, GeneratedSecretName: pgurDBSecretName, }, @@ -598,7 +605,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { time.Sleep(5 * time.Second) - item := &postgresqlv1alpha1.PostgresqlUserRole{} + item := &v1alpha1.PostgresqlUserRole{} err := k8sClient.Get(ctx, types.NamespacedName{ Name: pgurName, Namespace: pgurNamespace, @@ -608,7 +615,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeFalse()) Expect(item.Status.Message).To(Equal("")) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleNoPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleNoPhase)) }) It("should be ok without work secret name", func() { @@ -622,17 +629,17 @@ var _ = Describe("PostgresqlUserRole tests", func() { preDate := time.Now().Add(-time.Second) - it := &postgresqlv1alpha1.PostgresqlUserRole{ + it := &v1alpha1.PostgresqlUserRole{ ObjectMeta: v1.ObjectMeta{ Name: pgurName, Namespace: pgurNamespace, }, - Spec: postgresqlv1alpha1.PostgresqlUserRoleSpec{ - Mode: postgresqlv1alpha1.ProvidedMode, + Spec: v1alpha1.PostgresqlUserRoleSpec{ + Mode: v1alpha1.ProvidedMode, ImportSecretName: pgurImportSecretName, - Privileges: []*postgresqlv1alpha1.PostgresqlUserRolePrivilege{ + Privileges: []*v1alpha1.PostgresqlUserRolePrivilege{ { - Privilege: postgresqlv1alpha1.OwnerPrivilege, + Privilege: v1alpha1.OwnerPrivilege, Database: &common.CRLink{Name: pgdbName, Namespace: pgdbNamespace}, GeneratedSecretName: pgurDBSecretName, }, @@ -643,7 +650,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Create user Expect(k8sClient.Create(ctx, it)).Should(Succeed()) - item := &postgresqlv1alpha1.PostgresqlUserRole{} + item := &v1alpha1.PostgresqlUserRole{} // Get updated user Eventually( func() error { @@ -657,7 +664,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { } // Check if status hasn't been updated - if item.Status.Phase == postgresqlv1alpha1.UserRoleNoPhase { + if item.Status.Phase == v1alpha1.UserRoleNoPhase { return errors.New("pgur hasn't been updated by operator") } @@ -670,13 +677,13 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal("")) Expect(item.Status.PostgresRole).To(Equal(pgurImportUsername)) Expect(item.Spec.WorkGeneratedSecretName).ToNot(Equal(pgurWorkSecretName)) Expect(item.Spec.WorkGeneratedSecretName).To(MatchRegexp(DefaultWorkGeneratedSecretNamePrefix + ".*")) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) Expect(d.After(preDate)).To(BeTrue()) @@ -740,17 +747,17 @@ var _ = Describe("PostgresqlUserRole tests", func() { preDate := time.Now().Add(-time.Second) - it := &postgresqlv1alpha1.PostgresqlUserRole{ + it := &v1alpha1.PostgresqlUserRole{ ObjectMeta: v1.ObjectMeta{ Name: pgurName, Namespace: pgurNamespace, }, - Spec: postgresqlv1alpha1.PostgresqlUserRoleSpec{ - Mode: postgresqlv1alpha1.ProvidedMode, + Spec: v1alpha1.PostgresqlUserRoleSpec{ + Mode: v1alpha1.ProvidedMode, ImportSecretName: pgurImportSecretName, - Privileges: []*postgresqlv1alpha1.PostgresqlUserRolePrivilege{ + Privileges: []*v1alpha1.PostgresqlUserRolePrivilege{ { - Privilege: postgresqlv1alpha1.OwnerPrivilege, + Privilege: v1alpha1.OwnerPrivilege, Database: &common.CRLink{Name: pgdbName, Namespace: pgdbNamespace}, GeneratedSecretName: pgurDBSecretName, }, @@ -761,7 +768,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Create user Expect(k8sClient.Create(ctx, it)).Should(Succeed()) - item := &postgresqlv1alpha1.PostgresqlUserRole{} + item := &v1alpha1.PostgresqlUserRole{} // Get updated user Eventually( func() error { @@ -775,7 +782,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { } // Check if status hasn't been updated - if item.Status.Phase == postgresqlv1alpha1.UserRoleNoPhase { + if item.Status.Phase == v1alpha1.UserRoleNoPhase { return errors.New("pgur hasn't been updated by operator") } @@ -788,13 +795,13 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal("")) Expect(item.Status.PostgresRole).To(Equal(pgurImportUsername)) Expect(item.Spec.WorkGeneratedSecretName).ToNot(Equal(pgurWorkSecretName)) Expect(item.Spec.WorkGeneratedSecretName).To(MatchRegexp(DefaultWorkGeneratedSecretNamePrefix + ".*")) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) Expect(d.After(preDate)).To(BeTrue()) @@ -862,12 +869,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal("")) Expect(item.Status.PostgresRole).To(Equal(pgurImportUsername)) Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) Expect(d.After(preDate)).To(BeTrue()) @@ -935,12 +942,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal("")) Expect(item.Status.PostgresRole).To(Equal(pgurImportUsername)) Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) Expect(d.After(preDate)).To(BeTrue()) @@ -1009,12 +1016,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal("")) Expect(item.Status.PostgresRole).To(Equal(pgurImportUsername)) Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) Expect(d.After(preDate)).To(BeTrue()) @@ -1096,12 +1103,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal("")) Expect(item.Status.PostgresRole).To(Equal(pgurImportUsername)) Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) Expect(d.After(preDate)).To(BeTrue()) @@ -1182,12 +1189,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal("")) Expect(item.Status.PostgresRole).To(Equal(pgurImportUsername)) Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) Expect(d.After(preDate)).To(BeTrue()) @@ -1247,12 +1254,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal("")) Expect(item.Status.PostgresRole).To(Equal(pgurImportUsername)) Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) Expect(d.After(preDate)).To(BeTrue()) @@ -1312,12 +1319,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal("")) Expect(item.Status.PostgresRole).To(Equal(pgurImportUsername)) Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) Expect(d.After(preDate)).To(BeTrue()) @@ -1374,12 +1381,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal("")) Expect(item.Status.PostgresRole).To(Equal(pgurImportUsername)) Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) Expect(d.After(preDate)).To(BeTrue()) @@ -1439,12 +1446,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal("")) Expect(item.Status.PostgresRole).To(Equal(pgurImportUsername)) Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) Expect(d.After(preDate)).To(BeTrue()) @@ -1462,7 +1469,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { sec := &corev1.Secret{} Eventually( func() error { - err := k8sClient.Get(ctx, types.NamespacedName{ + err = k8sClient.Get(ctx, types.NamespacedName{ Name: item.Spec.WorkGeneratedSecretName, Namespace: pgurNamespace, }, sec) @@ -1490,7 +1497,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { Namespace: pgurNamespace, }, item)).To(Succeed()) Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) d2, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) Expect(d2.After(d)).To(BeTrue()) @@ -1541,12 +1548,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal("")) Expect(item.Status.PostgresRole).To(Equal(pgurImportUsername)) Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) Expect(d.After(preDate)).To(BeTrue()) @@ -1592,7 +1599,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { Namespace: pgurNamespace, }, item)).To(Succeed()) Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) d2, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) Expect(d2.After(d)).To(BeTrue()) @@ -1650,7 +1657,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) updatedUser := "updated" @@ -1852,7 +1859,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { Expect(sett).To(BeTrue()) // Update - item.Spec.Privileges[0].Privilege = postgresqlv1alpha1.WriterPrivilege + item.Spec.Privileges[0].Privilege = v1alpha1.WriterPrivilege Expect(k8sClient.Update(ctx, item)).To(Succeed()) Eventually( @@ -1899,16 +1906,16 @@ var _ = Describe("PostgresqlUserRole tests", func() { }) It("should be ok to remove a non valid item", func() { - it := &postgresqlv1alpha1.PostgresqlUserRole{ + it := &v1alpha1.PostgresqlUserRole{ ObjectMeta: v1.ObjectMeta{ Name: pgurName, Namespace: pgurNamespace, }, - Spec: postgresqlv1alpha1.PostgresqlUserRoleSpec{ - Mode: postgresqlv1alpha1.ProvidedMode, - Privileges: []*postgresqlv1alpha1.PostgresqlUserRolePrivilege{ + Spec: v1alpha1.PostgresqlUserRoleSpec{ + Mode: v1alpha1.ProvidedMode, + Privileges: []*v1alpha1.PostgresqlUserRolePrivilege{ { - Privilege: postgresqlv1alpha1.OwnerPrivilege, + Privilege: v1alpha1.OwnerPrivilege, Database: &common.CRLink{Name: pgdbName, Namespace: pgdbNamespace}, GeneratedSecretName: pgurDBSecretName, }, @@ -1919,7 +1926,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Create user Expect(k8sClient.Create(ctx, it)).Should(Succeed()) - item := &postgresqlv1alpha1.PostgresqlUserRole{} + item := &v1alpha1.PostgresqlUserRole{} // Get updated user Eventually( func() error { @@ -1933,7 +1940,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { } // Check if status hasn't been updated - if item.Status.Phase == postgresqlv1alpha1.UserRoleNoPhase { + if item.Status.Phase == v1alpha1.UserRoleNoPhase { return errors.New("pgur hasn't been updated by operator") } @@ -1947,7 +1954,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Delete item Expect(k8sClient.Delete(ctx, item)).To(Succeed()) - item2 := &postgresqlv1alpha1.PostgresqlUserRole{} + item2 := &v1alpha1.PostgresqlUserRole{} // Ensure this is deleted Eventually( func() error { @@ -1987,7 +1994,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Delete item Expect(k8sClient.Delete(ctx, item)).To(Succeed()) - item2 := &postgresqlv1alpha1.PostgresqlUserRole{} + item2 := &v1alpha1.PostgresqlUserRole{} // Ensure this is deleted Eventually( func() error { @@ -2034,7 +2041,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Delete item Expect(k8sClient.Delete(ctx, item)).To(Succeed()) - it := &postgresqlv1alpha1.PostgresqlUserRole{} + it := &v1alpha1.PostgresqlUserRole{} // Ensure this is deleted Eventually( func() error { @@ -2046,7 +2053,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { return err } - if it.Status.Phase != postgresqlv1alpha1.UserRoleFailedPhase { + if it.Status.Phase != v1alpha1.UserRoleFailedPhase { return errors.New("not updated by operator") } @@ -2057,13 +2064,13 @@ var _ = Describe("PostgresqlUserRole tests", func() { ). Should(Succeed()) - Expect(it.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleFailedPhase)) + Expect(it.Status.Phase).To(Equal(v1alpha1.UserRoleFailedPhase)) Expect(it.Status.OldPostgresRoles).To(Equal([]string{pgurImportUsername})) Expect(it.Status.Message).To(Equal("old postgres roles still present")) Expect(disconnectConnFromKey(k)).To(Succeed()) - item2 := &postgresqlv1alpha1.PostgresqlUserRole{} + item2 := &v1alpha1.PostgresqlUserRole{} Expect(k8sClient.Get(ctx, types.NamespacedName{ Name: pgurName, Namespace: pgurNamespace, @@ -2111,7 +2118,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) dbsecOri := &corev1.Secret{} Expect(k8sClient.Get(ctx, types.NamespacedName{ @@ -2151,7 +2158,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { ). Should(Succeed()) - item2 := &postgresqlv1alpha1.PostgresqlUserRole{} + item2 := &v1alpha1.PostgresqlUserRole{} Expect(k8sClient.Get(ctx, types.NamespacedName{ Name: pgurName, Namespace: pgurNamespace, @@ -2194,7 +2201,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) worksecOri := &corev1.Secret{} Expect(k8sClient.Get(ctx, types.NamespacedName{ @@ -2234,7 +2241,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { ). Should(Succeed()) - item2 := &postgresqlv1alpha1.PostgresqlUserRole{} + item2 := &v1alpha1.PostgresqlUserRole{} Expect(k8sClient.Get(ctx, types.NamespacedName{ Name: pgurName, Namespace: pgurNamespace, @@ -2277,7 +2284,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) // Edit item.Spec.RoleAttributes.Replication = starAny(false) @@ -2303,7 +2310,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { ). Should(Succeed()) - item2 := &postgresqlv1alpha1.PostgresqlUserRole{} + item2 := &v1alpha1.PostgresqlUserRole{} Expect(k8sClient.Get(ctx, types.NamespacedName{ Name: pgurName, Namespace: pgurNamespace, @@ -2333,20 +2340,20 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) // Edit item.Spec.RoleAttributes.ConnectionLimit = nil Expect(k8sClient.Update(ctx, item)).To(Succeed()) - item3 := &postgresqlv1alpha1.PostgresqlUserRole{} + item3 := &v1alpha1.PostgresqlUserRole{} Expect(k8sClient.Get(ctx, types.NamespacedName{ Name: pgurName, Namespace: pgurNamespace, }, item3)).To(Succeed()) - Expect(item3.Spec.RoleAttributes).To(Equal(&postgresqlv1alpha1.PostgresqlUserRoleAttributes{ + Expect(item3.Spec.RoleAttributes).To(Equal(&v1alpha1.PostgresqlUserRoleAttributes{ Replication: starAny(true), BypassRLS: nil, ConnectionLimit: nil, @@ -2370,7 +2377,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { ). Should(Succeed()) - item2 := &postgresqlv1alpha1.PostgresqlUserRole{} + item2 := &v1alpha1.PostgresqlUserRole{} Expect(k8sClient.Get(ctx, types.NamespacedName{ Name: pgurName, Namespace: pgurNamespace, @@ -2400,7 +2407,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) worksecOri := &corev1.Secret{} Expect(k8sClient.Get(ctx, types.NamespacedName{ @@ -2409,7 +2416,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { }, worksecOri)).To(Succeed()) // Edit - item.Spec.RoleAttributes = &postgresqlv1alpha1.PostgresqlUserRoleAttributes{ + item.Spec.RoleAttributes = &v1alpha1.PostgresqlUserRoleAttributes{ ConnectionLimit: starAny(50), } @@ -2433,7 +2440,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { ). Should(Succeed()) - item2 := &postgresqlv1alpha1.PostgresqlUserRole{} + item2 := &v1alpha1.PostgresqlUserRole{} Expect(k8sClient.Get(ctx, types.NamespacedName{ Name: pgurName, Namespace: pgurNamespace, @@ -2463,10 +2470,18 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) // Validate - checkPGURSecretValues(item.Spec.Privileges[0].GeneratedSecretName, pgurNamespace, pgdbDBName, pgurImportUsername, pgurImportPassword, pgec, v1alpha1.PrimaryConnectionType) + checkPGURSecretValues( + item.Spec.Privileges[0].GeneratedSecretName, + pgurNamespace, + pgdbDBName, + pgurImportUsername, + pgurImportPassword, + pgec, + v1alpha1.PrimaryConnectionType, + ) }) It("should be ok to generate a bouncer secret with a bouncer user role", func() { @@ -2482,10 +2497,18 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) // Validate - checkPGURSecretValues(item.Spec.Privileges[0].GeneratedSecretName, pgurNamespace, pgdbDBName, pgurImportUsername, pgurImportPassword, pgec, v1alpha1.BouncerConnectionType) + checkPGURSecretValues( + item.Spec.Privileges[0].GeneratedSecretName, + pgurNamespace, + pgdbDBName, + pgurImportUsername, + pgurImportPassword, + pgec, + v1alpha1.BouncerConnectionType, + ) }) It("should be fail when a bouncer user role is asked but pgec isn't supporting it", func() { @@ -2501,7 +2524,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeFalse()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleFailedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleFailedPhase)) Expect(item.Status.Message).To(Equal("bouncer connection asked but not supported in engine configuration")) }) @@ -2519,7 +2542,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) // Validate checkPGURSecretValues( @@ -2547,10 +2570,18 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) // Validate - checkPGURSecretValues(item.Spec.Privileges[0].GeneratedSecretName, pgurNamespace, pgdbDBName, pgurImportUsername, pgurImportPassword, pgec, v1alpha1.PrimaryConnectionType) + checkPGURSecretValues( + item.Spec.Privileges[0].GeneratedSecretName, + pgurNamespace, + pgdbDBName, + pgurImportUsername, + pgurImportPassword, + pgec, + v1alpha1.PrimaryConnectionType, + ) }) It("should be ok to generate a bouncer secret with a bouncer user role and replica", func() { @@ -2566,10 +2597,18 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) // Validate - checkPGURSecretValues(item.Spec.Privileges[0].GeneratedSecretName, pgurNamespace, pgdbDBName, pgurImportUsername, pgurImportPassword, pgec, v1alpha1.BouncerConnectionType) + checkPGURSecretValues( + item.Spec.Privileges[0].GeneratedSecretName, + pgurNamespace, + pgdbDBName, + pgurImportUsername, + pgurImportPassword, + pgec, + v1alpha1.BouncerConnectionType, + ) }) It("should be ok to generate a bouncer and a primary secret with a bouncer and a primary user role with replica", func() { @@ -2586,7 +2625,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) // Validate checkPGURSecretValues( @@ -2614,7 +2653,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) // Validate checkPGURSecretValues( @@ -2680,7 +2719,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) // Validate checkPGURSecretValues( @@ -2746,7 +2785,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) // Edit item.Spec.Privileges[0].ExtraConnectionURLParameters = map[string]string{ @@ -2769,9 +2808,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { } // Check if sec have been updated - if !strings.Contains(string(sec2.Data["POSTGRES_URL_ARGS"]), "fake=fake&fake2=fake2") { + if !strings.Contains(string(sec2.Data["POSTGRES_URL_ARGS"]), "fake2=fake2") { return errors.New("Secret not updated") } + if !strings.Contains(string(sec2.Data["POSTGRES_URL_ARGS"]), "fake=fake") { + return errors.New("Secret not updated 2") + } return nil }, @@ -2780,7 +2822,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { ). Should(Succeed()) - item2 := &postgresqlv1alpha1.PostgresqlUserRole{} + item2 := &v1alpha1.PostgresqlUserRole{} Expect(k8sClient.Get(ctx, types.NamespacedName{ Name: pgurName, Namespace: pgurNamespace, @@ -2809,7 +2851,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) // Edit item.Spec.Privileges[0].ExtraConnectionURLParameters = map[string]string{ @@ -2832,9 +2874,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { } // Check if sec have been updated - if !strings.Contains(string(sec2.Data["POSTGRES_URL_ARGS"]), "fake=fake&fake2=fake2") { + if !strings.Contains(string(sec2.Data["POSTGRES_URL_ARGS"]), "fake2=fake2") { return errors.New("Secret not updated") } + if !strings.Contains(string(sec2.Data["POSTGRES_URL_ARGS"]), "fake=fake") { + return errors.New("Secret not updated 2") + } return nil }, @@ -2843,7 +2888,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { ). Should(Succeed()) - item2 := &postgresqlv1alpha1.PostgresqlUserRole{} + item2 := &v1alpha1.PostgresqlUserRole{} Expect(k8sClient.Get(ctx, types.NamespacedName{ Name: pgurName, Namespace: pgurNamespace, @@ -2876,9 +2921,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { } // Check if sec have been updated - if strings.Contains(string(sec2.Data["POSTGRES_URL_ARGS"]), "fake=fake&fake2=fake2") { + if strings.Contains(string(sec2.Data["POSTGRES_URL_ARGS"]), "fake2=fake2") { return errors.New("Secret not updated") } + if strings.Contains(string(sec2.Data["POSTGRES_URL_ARGS"]), "fake=fake") { + return errors.New("Secret not updated 2") + } return nil }, @@ -2887,7 +2935,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { ). Should(Succeed()) - item3 := &postgresqlv1alpha1.PostgresqlUserRole{} + item3 := &v1alpha1.PostgresqlUserRole{} Expect(k8sClient.Get(ctx, types.NamespacedName{ Name: pgurName, Namespace: pgurNamespace, @@ -2906,16 +2954,16 @@ var _ = Describe("PostgresqlUserRole tests", func() { Describe("Managed mode", func() { It("should fail when role prefix isn't provided", func() { - it := &postgresqlv1alpha1.PostgresqlUserRole{ + it := &v1alpha1.PostgresqlUserRole{ ObjectMeta: v1.ObjectMeta{ Name: pgurName, Namespace: pgurNamespace, }, - Spec: postgresqlv1alpha1.PostgresqlUserRoleSpec{ - Mode: postgresqlv1alpha1.ManagedMode, - Privileges: []*postgresqlv1alpha1.PostgresqlUserRolePrivilege{ + Spec: v1alpha1.PostgresqlUserRoleSpec{ + Mode: v1alpha1.ManagedMode, + Privileges: []*v1alpha1.PostgresqlUserRolePrivilege{ { - Privilege: postgresqlv1alpha1.OwnerPrivilege, + Privilege: v1alpha1.OwnerPrivilege, Database: &common.CRLink{Name: pgdbName, Namespace: pgdbNamespace}, GeneratedSecretName: pgurDBSecretName, }, @@ -2926,7 +2974,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Create user Expect(k8sClient.Create(ctx, it)).Should(Succeed()) - item := &postgresqlv1alpha1.PostgresqlUserRole{} + item := &v1alpha1.PostgresqlUserRole{} // Get updated user Eventually( func() error { @@ -2940,7 +2988,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { } // Check if status hasn't been updated - if item.Status.Phase == postgresqlv1alpha1.UserRoleNoPhase { + if item.Status.Phase == v1alpha1.UserRoleNoPhase { return errors.New("pgur hasn't been updated by operator") } @@ -2953,27 +3001,27 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeFalse()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleFailedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleFailedPhase)) Expect(item.Status.Message).To(Equal("PostgresqlUserRole is in managed mode without any RolePrefix")) }) It("should fail when privileges contains 2 times the same db (twice fully declared)", func() { - it := &postgresqlv1alpha1.PostgresqlUserRole{ + it := &v1alpha1.PostgresqlUserRole{ ObjectMeta: v1.ObjectMeta{ Name: pgurName, Namespace: pgurNamespace, }, - Spec: postgresqlv1alpha1.PostgresqlUserRoleSpec{ - Mode: postgresqlv1alpha1.ManagedMode, + Spec: v1alpha1.PostgresqlUserRoleSpec{ + Mode: v1alpha1.ManagedMode, RolePrefix: pgurRolePrefix, - Privileges: []*postgresqlv1alpha1.PostgresqlUserRolePrivilege{ + Privileges: []*v1alpha1.PostgresqlUserRolePrivilege{ { - Privilege: postgresqlv1alpha1.OwnerPrivilege, + Privilege: v1alpha1.OwnerPrivilege, Database: &common.CRLink{Name: pgdbName, Namespace: pgdbNamespace}, GeneratedSecretName: pgurDBSecretName, }, { - Privilege: postgresqlv1alpha1.WriterPrivilege, + Privilege: v1alpha1.WriterPrivilege, Database: &common.CRLink{Name: pgdbName, Namespace: pgdbNamespace}, GeneratedSecretName: pgurDBSecretName, }, @@ -2984,7 +3032,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Create user Expect(k8sClient.Create(ctx, it)).Should(Succeed()) - item := &postgresqlv1alpha1.PostgresqlUserRole{} + item := &v1alpha1.PostgresqlUserRole{} // Get updated user Eventually( func() error { @@ -2998,7 +3046,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { } // Check if status hasn't been updated - if item.Status.Phase == postgresqlv1alpha1.UserRoleNoPhase { + if item.Status.Phase == v1alpha1.UserRoleNoPhase { return errors.New("pgur hasn't been updated by operator") } @@ -3011,27 +3059,27 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeFalse()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleFailedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleFailedPhase)) Expect(item.Status.Message).To(Equal("Privilege list mustn't have the same database listed multiple times")) }) It("should fail when privileges contains 2 times the same db (1 fully declared, 1 without namespace)", func() { - it := &postgresqlv1alpha1.PostgresqlUserRole{ + it := &v1alpha1.PostgresqlUserRole{ ObjectMeta: v1.ObjectMeta{ Name: pgurName, Namespace: pgurNamespace, }, - Spec: postgresqlv1alpha1.PostgresqlUserRoleSpec{ - Mode: postgresqlv1alpha1.ManagedMode, + Spec: v1alpha1.PostgresqlUserRoleSpec{ + Mode: v1alpha1.ManagedMode, RolePrefix: pgurRolePrefix, - Privileges: []*postgresqlv1alpha1.PostgresqlUserRolePrivilege{ + Privileges: []*v1alpha1.PostgresqlUserRolePrivilege{ { - Privilege: postgresqlv1alpha1.OwnerPrivilege, + Privilege: v1alpha1.OwnerPrivilege, Database: &common.CRLink{Name: pgdbName, Namespace: pgurNamespace}, GeneratedSecretName: pgurDBSecretName, }, { - Privilege: postgresqlv1alpha1.WriterPrivilege, + Privilege: v1alpha1.WriterPrivilege, Database: &common.CRLink{Name: pgdbName}, GeneratedSecretName: pgurDBSecretName, }, @@ -3042,7 +3090,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Create user Expect(k8sClient.Create(ctx, it)).Should(Succeed()) - item := &postgresqlv1alpha1.PostgresqlUserRole{} + item := &v1alpha1.PostgresqlUserRole{} // Get updated user Eventually( func() error { @@ -3056,7 +3104,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { } // Check if status hasn't been updated - if item.Status.Phase == postgresqlv1alpha1.UserRoleNoPhase { + if item.Status.Phase == v1alpha1.UserRoleNoPhase { return errors.New("pgur hasn't been updated by operator") } @@ -3069,22 +3117,22 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeFalse()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleFailedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleFailedPhase)) Expect(item.Status.Message).To(Equal("Privilege list mustn't have the same database listed multiple times")) }) It("should fail when role prefix is too long", func() { - it := &postgresqlv1alpha1.PostgresqlUserRole{ + it := &v1alpha1.PostgresqlUserRole{ ObjectMeta: v1.ObjectMeta{ Name: pgurName, Namespace: pgurNamespace, }, - Spec: postgresqlv1alpha1.PostgresqlUserRoleSpec{ - Mode: postgresqlv1alpha1.ManagedMode, + Spec: v1alpha1.PostgresqlUserRoleSpec{ + Mode: v1alpha1.ManagedMode, RolePrefix: "fakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefake", - Privileges: []*postgresqlv1alpha1.PostgresqlUserRolePrivilege{ + Privileges: []*v1alpha1.PostgresqlUserRolePrivilege{ { - Privilege: postgresqlv1alpha1.OwnerPrivilege, + Privilege: v1alpha1.OwnerPrivilege, Database: &common.CRLink{Name: pgdbName, Namespace: pgdbNamespace}, GeneratedSecretName: pgurDBSecretName, }, @@ -3095,7 +3143,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Create user Expect(k8sClient.Create(ctx, it)).Should(Succeed()) - item := &postgresqlv1alpha1.PostgresqlUserRole{} + item := &v1alpha1.PostgresqlUserRole{} // Get updated user Eventually( func() error { @@ -3109,7 +3157,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { } // Check if status hasn't been updated - if item.Status.Phase == postgresqlv1alpha1.UserRoleNoPhase { + if item.Status.Phase == v1alpha1.UserRoleNoPhase { return errors.New("pgur hasn't been updated by operator") } @@ -3122,23 +3170,25 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeFalse()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleFailedPhase)) - Expect(item.Status.Message).To(Equal("Role prefix is too long. It must be <= 63. fakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefake-0X is 99 character. Role prefix length must be reduced")) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleFailedPhase)) + Expect( + item.Status.Message, + ).To(Equal("Role prefix is too long. It must be <= 63. fakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefakefake-0X is 99 character. Role prefix length must be reduced")) }) It("should fail when UserPasswordRotationDuration isn't a valid duration", func() { - it := &postgresqlv1alpha1.PostgresqlUserRole{ + it := &v1alpha1.PostgresqlUserRole{ ObjectMeta: v1.ObjectMeta{ Name: pgurName, Namespace: pgurNamespace, }, - Spec: postgresqlv1alpha1.PostgresqlUserRoleSpec{ - Mode: postgresqlv1alpha1.ManagedMode, + Spec: v1alpha1.PostgresqlUserRoleSpec{ + Mode: v1alpha1.ManagedMode, RolePrefix: pgurRolePrefix, UserPasswordRotationDuration: "fake", - Privileges: []*postgresqlv1alpha1.PostgresqlUserRolePrivilege{ + Privileges: []*v1alpha1.PostgresqlUserRolePrivilege{ { - Privilege: postgresqlv1alpha1.OwnerPrivilege, + Privilege: v1alpha1.OwnerPrivilege, Database: &common.CRLink{Name: pgdbName, Namespace: pgdbNamespace}, GeneratedSecretName: pgurDBSecretName, }, @@ -3149,7 +3199,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Create user Expect(k8sClient.Create(ctx, it)).Should(Succeed()) - item := &postgresqlv1alpha1.PostgresqlUserRole{} + item := &v1alpha1.PostgresqlUserRole{} // Get updated user Eventually( func() error { @@ -3163,7 +3213,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { } // Check if status hasn't been updated - if item.Status.Phase == postgresqlv1alpha1.UserRoleNoPhase { + if item.Status.Phase == v1alpha1.UserRoleNoPhase { return errors.New("pgur hasn't been updated by operator") } @@ -3176,22 +3226,22 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeFalse()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleFailedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleFailedPhase)) Expect(item.Status.Message).To(Equal(`time: invalid duration "fake"`)) }) It("should fail to look a not found pgdb", func() { - it := &postgresqlv1alpha1.PostgresqlUserRole{ + it := &v1alpha1.PostgresqlUserRole{ ObjectMeta: v1.ObjectMeta{ Name: pgurName, Namespace: pgurNamespace, }, - Spec: postgresqlv1alpha1.PostgresqlUserRoleSpec{ - Mode: postgresqlv1alpha1.ManagedMode, + Spec: v1alpha1.PostgresqlUserRoleSpec{ + Mode: v1alpha1.ManagedMode, RolePrefix: pgurRolePrefix, - Privileges: []*postgresqlv1alpha1.PostgresqlUserRolePrivilege{ + Privileges: []*v1alpha1.PostgresqlUserRolePrivilege{ { - Privilege: postgresqlv1alpha1.OwnerPrivilege, + Privilege: v1alpha1.OwnerPrivilege, Database: &common.CRLink{Name: "fake", Namespace: "fake"}, GeneratedSecretName: "pgur", }, @@ -3202,7 +3252,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Create user Expect(k8sClient.Create(ctx, it)).Should(Succeed()) - item := &postgresqlv1alpha1.PostgresqlUserRole{} + item := &v1alpha1.PostgresqlUserRole{} // Get updated user Eventually( func() error { @@ -3216,7 +3266,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { } // Check if status hasn't been updated - if item.Status.Phase == postgresqlv1alpha1.UserRoleNoPhase { + if item.Status.Phase == v1alpha1.UserRoleNoPhase { return errors.New("pgur hasn't been updated by operator") } @@ -3229,18 +3279,18 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeFalse()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleFailedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleFailedPhase)) Expect(item.Status.Message).To(ContainSubstring("\"fake\" not found")) }) It("should fail with a non ready pgdb", func() { // Create pgdb - pgdb := &postgresqlv1alpha1.PostgresqlDatabase{ + pgdb := &v1alpha1.PostgresqlDatabase{ ObjectMeta: v1.ObjectMeta{ Name: pgdbName, Namespace: pgdbNamespace, }, - Spec: postgresqlv1alpha1.PostgresqlDatabaseSpec{ + Spec: v1alpha1.PostgresqlDatabaseSpec{ Database: pgdbDBName, EngineConfiguration: &common.CRLink{ Name: "fake", @@ -3253,18 +3303,18 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Create Expect(k8sClient.Create(ctx, pgdb)).Should(Succeed()) - it := &postgresqlv1alpha1.PostgresqlUserRole{ + it := &v1alpha1.PostgresqlUserRole{ ObjectMeta: v1.ObjectMeta{ Name: pgurName, Namespace: pgurNamespace, }, - Spec: postgresqlv1alpha1.PostgresqlUserRoleSpec{ - Mode: postgresqlv1alpha1.ManagedMode, + Spec: v1alpha1.PostgresqlUserRoleSpec{ + Mode: v1alpha1.ManagedMode, RolePrefix: pgurRolePrefix, WorkGeneratedSecretName: pgurWorkSecretName, - Privileges: []*postgresqlv1alpha1.PostgresqlUserRolePrivilege{ + Privileges: []*v1alpha1.PostgresqlUserRolePrivilege{ { - Privilege: postgresqlv1alpha1.OwnerPrivilege, + Privilege: v1alpha1.OwnerPrivilege, Database: &common.CRLink{Name: pgdbName, Namespace: pgdbNamespace}, GeneratedSecretName: pgurDBSecretName, }, @@ -3277,7 +3327,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { time.Sleep(5 * time.Second) - item := &postgresqlv1alpha1.PostgresqlUserRole{} + item := &v1alpha1.PostgresqlUserRole{} err := k8sClient.Get(ctx, types.NamespacedName{ Name: pgurName, Namespace: pgurNamespace, @@ -3286,7 +3336,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeFalse()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleNoPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleNoPhase)) Expect(item.Status.Message).To(Equal("")) }) @@ -3298,17 +3348,17 @@ var _ = Describe("PostgresqlUserRole tests", func() { preDate := time.Now().Add(-time.Second) - it := &postgresqlv1alpha1.PostgresqlUserRole{ + it := &v1alpha1.PostgresqlUserRole{ ObjectMeta: v1.ObjectMeta{ Name: pgurName, Namespace: pgurNamespace, }, - Spec: postgresqlv1alpha1.PostgresqlUserRoleSpec{ - Mode: postgresqlv1alpha1.ManagedMode, + Spec: v1alpha1.PostgresqlUserRoleSpec{ + Mode: v1alpha1.ManagedMode, RolePrefix: pgurRolePrefix, - Privileges: []*postgresqlv1alpha1.PostgresqlUserRolePrivilege{ + Privileges: []*v1alpha1.PostgresqlUserRolePrivilege{ { - Privilege: postgresqlv1alpha1.OwnerPrivilege, + Privilege: v1alpha1.OwnerPrivilege, Database: &common.CRLink{Name: pgdbName, Namespace: pgdbNamespace}, GeneratedSecretName: pgurDBSecretName, }, @@ -3319,7 +3369,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Create user Expect(k8sClient.Create(ctx, it)).Should(Succeed()) - item := &postgresqlv1alpha1.PostgresqlUserRole{} + item := &v1alpha1.PostgresqlUserRole{} // Get updated user Eventually( func() error { @@ -3333,7 +3383,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { } // Check if status hasn't been updated - if item.Status.Phase == postgresqlv1alpha1.UserRoleNoPhase { + if item.Status.Phase == v1alpha1.UserRoleNoPhase { return errors.New("pgur hasn't been updated by operator") } @@ -3347,7 +3397,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { username := pgurRolePrefix + Login0Suffix // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal(pgurRolePrefix)) Expect(item.Status.PostgresRole).To(Equal(username)) @@ -3376,7 +3426,15 @@ var _ = Describe("PostgresqlUserRole tests", func() { }, dbsec)).Should(Succeed()) // Validate - checkPGURSecretValues(pgurDBSecretName, pgurNamespace, pgdbDBName, username, string(sec.Data[PasswordSecretKey]), pgec, v1alpha1.PrimaryConnectionType) + checkPGURSecretValues( + pgurDBSecretName, + pgurNamespace, + pgdbDBName, + username, + string(sec.Data[PasswordSecretKey]), + pgec, + v1alpha1.PrimaryConnectionType, + ) // Connect to check user _, err = connectAs(username, string(sec.Data[PasswordSecretKey])) @@ -3414,17 +3472,17 @@ var _ = Describe("PostgresqlUserRole tests", func() { preDate := time.Now().Add(-time.Second) - it := &postgresqlv1alpha1.PostgresqlUserRole{ + it := &v1alpha1.PostgresqlUserRole{ ObjectMeta: v1.ObjectMeta{ Name: pgurName, Namespace: pgurNamespace, }, - Spec: postgresqlv1alpha1.PostgresqlUserRoleSpec{ - Mode: postgresqlv1alpha1.ManagedMode, + Spec: v1alpha1.PostgresqlUserRoleSpec{ + Mode: v1alpha1.ManagedMode, RolePrefix: pgurRolePrefix, - Privileges: []*postgresqlv1alpha1.PostgresqlUserRolePrivilege{ + Privileges: []*v1alpha1.PostgresqlUserRolePrivilege{ { - Privilege: postgresqlv1alpha1.OwnerPrivilege, + Privilege: v1alpha1.OwnerPrivilege, Database: &common.CRLink{Name: pgdbName, Namespace: pgdbNamespace}, GeneratedSecretName: pgurDBSecretName, }, @@ -3435,7 +3493,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Create user Expect(k8sClient.Create(ctx, it)).Should(Succeed()) - item := &postgresqlv1alpha1.PostgresqlUserRole{} + item := &v1alpha1.PostgresqlUserRole{} // Get updated user Eventually( func() error { @@ -3449,7 +3507,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { } // Check if status hasn't been updated - if item.Status.Phase == postgresqlv1alpha1.UserRoleNoPhase { + if item.Status.Phase == v1alpha1.UserRoleNoPhase { return errors.New("pgur hasn't been updated by operator") } @@ -3463,7 +3521,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { username := pgurRolePrefix + Login0Suffix // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal(pgurRolePrefix)) Expect(item.Status.PostgresRole).To(Equal(username)) @@ -3492,7 +3550,15 @@ var _ = Describe("PostgresqlUserRole tests", func() { }, dbsec)).Should(Succeed()) // Validate - checkPGURSecretValues(pgurDBSecretName, pgurNamespace, pgdbDBName, username, string(sec.Data[PasswordSecretKey]), pgec, v1alpha1.PrimaryConnectionType) + checkPGURSecretValues( + pgurDBSecretName, + pgurNamespace, + pgdbDBName, + username, + string(sec.Data[PasswordSecretKey]), + pgec, + v1alpha1.PrimaryConnectionType, + ) // Connect to check user _, err = connectAs(username, string(sec.Data[PasswordSecretKey])) @@ -3535,12 +3601,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { username := pgurRolePrefix + Login0Suffix // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal(pgurRolePrefix)) Expect(item.Status.PostgresRole).To(Equal(username)) Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) Expect(d.After(preDate)).To(BeTrue()) @@ -3564,7 +3630,15 @@ var _ = Describe("PostgresqlUserRole tests", func() { }, dbsec)).Should(Succeed()) // Validate - checkPGURSecretValues(pgurDBSecretName, pgurNamespace, pgdbDBName, username, string(sec.Data[PasswordSecretKey]), pgec, v1alpha1.PrimaryConnectionType) + checkPGURSecretValues( + pgurDBSecretName, + pgurNamespace, + pgdbDBName, + username, + string(sec.Data[PasswordSecretKey]), + pgec, + v1alpha1.PrimaryConnectionType, + ) // Connect to check user _, err = connectAs(username, string(sec.Data[PasswordSecretKey])) @@ -3607,12 +3681,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { username := pgurRolePrefix + Login0Suffix // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal(pgurRolePrefix)) Expect(item.Status.PostgresRole).To(Equal(username)) Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) Expect(d.After(preDate)).To(BeTrue()) @@ -3636,7 +3710,15 @@ var _ = Describe("PostgresqlUserRole tests", func() { }, dbsec)).Should(Succeed()) // Validate - checkPGURSecretValues(pgurDBSecretName, pgurNamespace, pgdbDBName, username, string(sec.Data[PasswordSecretKey]), pgec, v1alpha1.PrimaryConnectionType) + checkPGURSecretValues( + pgurDBSecretName, + pgurNamespace, + pgdbDBName, + username, + string(sec.Data[PasswordSecretKey]), + pgec, + v1alpha1.PrimaryConnectionType, + ) // Connect to check user _, err = connectAs(username, string(sec.Data[PasswordSecretKey])) @@ -3681,12 +3763,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal(pgurRolePrefix)) Expect(item.Status.PostgresRole).To(Equal(username)) Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) Expect(d.After(preDate)).To(BeTrue()) @@ -3715,8 +3797,24 @@ var _ = Describe("PostgresqlUserRole tests", func() { }, dbsec2)).Should(Succeed()) // Validate - checkPGURSecretValues(pgurDBSecretName, pgurNamespace, pgdbDBName, username, string(sec.Data[PasswordSecretKey]), pgec, v1alpha1.PrimaryConnectionType) - checkPGURSecretValues(pgurDBSecretName2, pgurNamespace, pgdbDBName2, username, string(sec.Data[PasswordSecretKey]), pgec, v1alpha1.PrimaryConnectionType) + checkPGURSecretValues( + pgurDBSecretName, + pgurNamespace, + pgdbDBName, + username, + string(sec.Data[PasswordSecretKey]), + pgec, + v1alpha1.PrimaryConnectionType, + ) + checkPGURSecretValues( + pgurDBSecretName2, + pgurNamespace, + pgdbDBName2, + username, + string(sec.Data[PasswordSecretKey]), + pgec, + v1alpha1.PrimaryConnectionType, + ) // Connect to check user _, err = connectAs(username, string(sec.Data[PasswordSecretKey])) @@ -3768,12 +3866,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal(pgurRolePrefix)) Expect(item.Status.PostgresRole).To(Equal(username)) Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) Expect(d.After(preDate)).To(BeTrue()) @@ -3802,8 +3900,24 @@ var _ = Describe("PostgresqlUserRole tests", func() { }, dbsec2)).Should(Succeed()) // Validate - checkPGURSecretValues(pgurDBSecretName, pgurNamespace, pgdbDBName, username, string(sec.Data[PasswordSecretKey]), pgec, v1alpha1.PrimaryConnectionType) - checkPGURSecretValues(pgurDBSecretName2, pgurNamespace, pgdbDBName2, username, string(sec.Data[PasswordSecretKey]), pgec, v1alpha1.PrimaryConnectionType) + checkPGURSecretValues( + pgurDBSecretName, + pgurNamespace, + pgdbDBName, + username, + string(sec.Data[PasswordSecretKey]), + pgec, + v1alpha1.PrimaryConnectionType, + ) + checkPGURSecretValues( + pgurDBSecretName2, + pgurNamespace, + pgdbDBName2, + username, + string(sec.Data[PasswordSecretKey]), + pgec, + v1alpha1.PrimaryConnectionType, + ) // Connect to check user _, err = connectAs(username, string(sec.Data[PasswordSecretKey])) @@ -3854,12 +3968,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal(pgurRolePrefix)) Expect(item.Status.PostgresRole).To(Equal(username)) Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) Expect(d.After(preDate)).To(BeTrue()) @@ -3887,7 +4001,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { ). Should(Succeed()) - item2 := &postgresqlv1alpha1.PostgresqlUserRole{} + item2 := &v1alpha1.PostgresqlUserRole{} Expect(k8sClient.Get(ctx, types.NamespacedName{ Name: pgurName, Namespace: pgurNamespace, @@ -3918,12 +4032,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal(pgurRolePrefix)) Expect(item.Status.PostgresRole).To(Equal(username)) Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) Expect(d.After(preDate)).To(BeTrue()) @@ -3932,13 +4046,13 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Save Expect(k8sClient.Update(ctx, item)).To(Succeed()) - item3 := &postgresqlv1alpha1.PostgresqlUserRole{} + item3 := &v1alpha1.PostgresqlUserRole{} Expect(k8sClient.Get(ctx, types.NamespacedName{ Name: pgurName, Namespace: pgurNamespace, }, item3)).To(Succeed()) - Expect(item3.Spec.RoleAttributes).To(Equal(&postgresqlv1alpha1.PostgresqlUserRoleAttributes{ + Expect(item3.Spec.RoleAttributes).To(Equal(&v1alpha1.PostgresqlUserRoleAttributes{ Replication: starAny(true), BypassRLS: nil, ConnectionLimit: nil, @@ -3962,7 +4076,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { ). Should(Succeed()) - item2 := &postgresqlv1alpha1.PostgresqlUserRole{} + item2 := &v1alpha1.PostgresqlUserRole{} Expect(k8sClient.Get(ctx, types.NamespacedName{ Name: pgurName, Namespace: pgurNamespace, @@ -3993,17 +4107,17 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal(pgurRolePrefix)) Expect(item.Status.PostgresRole).To(Equal(username)) Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) Expect(d.After(preDate)).To(BeTrue()) - item.Spec.RoleAttributes = &postgresqlv1alpha1.PostgresqlUserRoleAttributes{ + item.Spec.RoleAttributes = &v1alpha1.PostgresqlUserRoleAttributes{ ConnectionLimit: starAny(50), } // Save @@ -4027,7 +4141,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { ). Should(Succeed()) - item2 := &postgresqlv1alpha1.PostgresqlUserRole{} + item2 := &v1alpha1.PostgresqlUserRole{} Expect(k8sClient.Get(ctx, types.NamespacedName{ Name: pgurName, Namespace: pgurNamespace, @@ -4058,12 +4172,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal(pgurRolePrefix)) Expect(item.Status.PostgresRole).To(Equal(username)) Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) Expect(d.After(preDate)).To(BeTrue()) @@ -4121,12 +4235,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal(pgurRolePrefix)) Expect(item.Status.PostgresRole).To(Equal(username)) Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) Expect(d.After(preDate)).To(BeTrue()) @@ -4181,12 +4295,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal(pgurRolePrefix)) Expect(item.Status.PostgresRole).To(Equal(username)) Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) Expect(d.After(preDate)).To(BeTrue()) @@ -4241,12 +4355,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal(pgurRolePrefix)) Expect(item.Status.PostgresRole).To(Equal(username)) Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) Expect(d.After(preDate)).To(BeTrue()) @@ -4301,12 +4415,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { username := pgurRolePrefix + Login0Suffix // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal(pgurRolePrefix)) Expect(item.Status.PostgresRole).To(Equal(username)) Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) Expect(d.After(preDate)).To(BeTrue()) @@ -4355,7 +4469,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { Namespace: pgurNamespace, }, item)).To(Succeed()) Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) Expect(item.Status.RolePrefix).To(Equal(updatedUserPrefix)) d2, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) @@ -4371,7 +4485,15 @@ var _ = Describe("PostgresqlUserRole tests", func() { }, dbsec)).Should(Succeed()) // Validate - checkPGURSecretValues(pgurDBSecretName, pgurNamespace, pgdbDBName, username, string(sec.Data[PasswordSecretKey]), pgec, v1alpha1.PrimaryConnectionType) + checkPGURSecretValues( + pgurDBSecretName, + pgurNamespace, + pgdbDBName, + username, + string(sec.Data[PasswordSecretKey]), + pgec, + v1alpha1.PrimaryConnectionType, + ) // Connect to check user _, err = connectAs(username, string(sec.Data[PasswordSecretKey])) @@ -4450,7 +4572,15 @@ var _ = Describe("PostgresqlUserRole tests", func() { }, dbsec)).Should(Succeed()) // Validate - checkPGURSecretValues(pgurDBSecretName, pgurNamespace, pgdbDBName, username, string(workSec2.Data[PasswordSecretKey]), pgec, v1alpha1.PrimaryConnectionType) + checkPGURSecretValues( + pgurDBSecretName, + pgurNamespace, + pgdbDBName, + username, + string(workSec2.Data[PasswordSecretKey]), + pgec, + v1alpha1.PrimaryConnectionType, + ) // Connect to check user _, err := connectAs(username, string(workSec2.Data[PasswordSecretKey])) @@ -4540,7 +4670,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { Expect(usernameWithAdminOption).To(Equal(map[string]bool{postgresUser: false})) // Update - item.Spec.Privileges[0].Privilege = postgresqlv1alpha1.WriterPrivilege + item.Spec.Privileges[0].Privilege = v1alpha1.WriterPrivilege Expect(k8sClient.Update(ctx, item)).To(Succeed()) Eventually( @@ -4599,12 +4729,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { username := pgurRolePrefix + Login0Suffix // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal(pgurRolePrefix)) Expect(item.Status.PostgresRole).To(Equal(username)) Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) Expect(d.After(preDate)).To(BeTrue()) @@ -4628,7 +4758,15 @@ var _ = Describe("PostgresqlUserRole tests", func() { }, dbsec)).Should(Succeed()) // Validate - checkPGURSecretValues(pgurDBSecretName, pgurNamespace, pgdbDBName, username, string(sec.Data[PasswordSecretKey]), pgec, v1alpha1.PrimaryConnectionType) + checkPGURSecretValues( + pgurDBSecretName, + pgurNamespace, + pgdbDBName, + username, + string(sec.Data[PasswordSecretKey]), + pgec, + v1alpha1.PrimaryConnectionType, + ) // Connect to check user _, err = connectAs(username, string(sec.Data[PasswordSecretKey])) @@ -4665,12 +4803,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal(pgurRolePrefix)) Expect(item.Status.PostgresRole).To(Equal(username)) Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) Expect(item.Status.OldPostgresRoles).To(Equal([]string{})) d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) @@ -4686,10 +4824,10 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Wait time.Sleep(4 * time.Second) - item2 := &postgresqlv1alpha1.PostgresqlUserRole{} + item2 := &v1alpha1.PostgresqlUserRole{} Eventually( func() error { - k8sClient.Get(ctx, types.NamespacedName{ + err = k8sClient.Get(ctx, types.NamespacedName{ Name: pgurName, Namespace: pgurNamespace, }, item2) @@ -4711,7 +4849,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item2.Status.Ready).To(BeTrue()) - Expect(item2.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item2.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item2.Status.Message).To(Equal("")) Expect(item2.Status.RolePrefix).To(Equal(pgurRolePrefix)) Expect(item2.Status.PostgresRole).To(Equal(username2)) @@ -4742,7 +4880,15 @@ var _ = Describe("PostgresqlUserRole tests", func() { }, dbsec)).Should(Succeed()) // Validate - checkPGURSecretValues(pgurDBSecretName, pgurNamespace, pgdbDBName, username2, string(workSec2.Data[PasswordSecretKey]), pgec, v1alpha1.PrimaryConnectionType) + checkPGURSecretValues( + pgurDBSecretName, + pgurNamespace, + pgdbDBName, + username2, + string(workSec2.Data[PasswordSecretKey]), + pgec, + v1alpha1.PrimaryConnectionType, + ) // Connect to check user _, err = connectAs(username2, string(workSec2.Data[PasswordSecretKey])) @@ -4783,12 +4929,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal(pgurRolePrefix)) Expect(item.Status.PostgresRole).To(Equal(username)) Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) Expect(item.Status.OldPostgresRoles).To(Equal([]string{})) d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) @@ -4804,10 +4950,10 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Wait time.Sleep(4 * time.Second) - item2 := &postgresqlv1alpha1.PostgresqlUserRole{} + item2 := &v1alpha1.PostgresqlUserRole{} Eventually( func() error { - k8sClient.Get(ctx, types.NamespacedName{ + err = k8sClient.Get(ctx, types.NamespacedName{ Name: pgurName, Namespace: pgurNamespace, }, item2) @@ -4829,7 +4975,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item2.Status.Ready).To(BeTrue()) - Expect(item2.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item2.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item2.Status.Message).To(Equal("")) Expect(item2.Status.RolePrefix).To(Equal(pgurRolePrefix)) Expect(item2.Status.PostgresRole).To(Equal(username2)) @@ -4860,7 +5006,15 @@ var _ = Describe("PostgresqlUserRole tests", func() { }, dbsec)).Should(Succeed()) // Validate - checkPGURSecretValues(pgurDBSecretName, pgurNamespace, pgdbDBName, username2, string(workSec2.Data[PasswordSecretKey]), pgec, v1alpha1.PrimaryConnectionType) + checkPGURSecretValues( + pgurDBSecretName, + pgurNamespace, + pgdbDBName, + username2, + string(workSec2.Data[PasswordSecretKey]), + pgec, + v1alpha1.PrimaryConnectionType, + ) // Connect to check user _, err = connectAs(username2, string(workSec2.Data[PasswordSecretKey])) @@ -4901,12 +5055,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal(pgurRolePrefix)) Expect(item.Status.PostgresRole).To(Equal(username)) Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) Expect(item.Status.OldPostgresRoles).To(Equal([]string{})) d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) @@ -4926,10 +5080,10 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Wait time.Sleep(4 * time.Second) - item2 := &postgresqlv1alpha1.PostgresqlUserRole{} + item2 := &v1alpha1.PostgresqlUserRole{} Eventually( func() error { - k8sClient.Get(ctx, types.NamespacedName{ + err = k8sClient.Get(ctx, types.NamespacedName{ Name: pgurName, Namespace: pgurNamespace, }, item2) @@ -4951,7 +5105,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item2.Status.Ready).To(BeTrue()) - Expect(item2.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item2.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item2.Status.Message).To(Equal("")) Expect(item2.Status.RolePrefix).To(Equal(pgurRolePrefix)) Expect(item2.Status.PostgresRole).To(Equal(username2)) @@ -4982,7 +5136,15 @@ var _ = Describe("PostgresqlUserRole tests", func() { }, dbsec)).Should(Succeed()) // Validate - checkPGURSecretValues(pgurDBSecretName, pgurNamespace, pgdbDBName, username2, string(workSec2.Data[PasswordSecretKey]), pgec, v1alpha1.PrimaryConnectionType) + checkPGURSecretValues( + pgurDBSecretName, + pgurNamespace, + pgdbDBName, + username2, + string(workSec2.Data[PasswordSecretKey]), + pgec, + v1alpha1.PrimaryConnectionType, + ) // Connect to check user _, err = connectAs(username2, string(workSec2.Data[PasswordSecretKey])) @@ -5015,134 +5177,145 @@ var _ = Describe("PostgresqlUserRole tests", func() { Expect(username2WithAdminOption).To(Equal(map[string]bool{postgresUser: false})) }) - It("should be ok to have rolling password enabled and performed with old user still connected and with a pgec with allow grant admin option enabled", func() { - // Setup pgec - pgec, _ := setupPGECWithAllowGrantAdminOption("30s", false) - // Create pgdb - pgdb := setupPGDB(false) - - preDate := time.Now().Add(-time.Second) - - item := setupManagedPGUR("5s") - - username := pgurRolePrefix + Login0Suffix - username2 := pgurRolePrefix + Login1Suffix - - // Checks - Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) - Expect(item.Status.Message).To(Equal("")) - Expect(item.Status.RolePrefix).To(Equal(pgurRolePrefix)) - Expect(item.Status.PostgresRole).To(Equal(username)) - Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) - Expect(item.Status.OldPostgresRoles).To(Equal([]string{})) - d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) - Expect(err).To(Succeed()) - Expect(d.After(preDate)).To(BeTrue()) - - // Get work secret - workSec := &corev1.Secret{} - Expect(k8sClient.Get(ctx, types.NamespacedName{ - Name: item.Spec.WorkGeneratedSecretName, - Namespace: pgurNamespace, - }, workSec)).Should(Succeed()) - - // Connect - _, err = connectAs(username, string(workSec.Data[PasswordSecretKey])) - Expect(err).To(Succeed()) - - // Wait - time.Sleep(4 * time.Second) - - item2 := &postgresqlv1alpha1.PostgresqlUserRole{} - Eventually( - func() error { - k8sClient.Get(ctx, types.NamespacedName{ - Name: pgurName, - Namespace: pgurNamespace, - }, item2) - // Check error - if err != nil { - return err - } - - if item.Status.PostgresRole == item2.Status.PostgresRole { - return errors.New("pgur not updated") - } - - return nil - }, - generalEventuallyTimeout, - generalEventuallyInterval, - ). - Should(Succeed()) - - // Checks - Expect(item2.Status.Ready).To(BeTrue()) - Expect(item2.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) - Expect(item2.Status.Message).To(Equal("")) - Expect(item2.Status.RolePrefix).To(Equal(pgurRolePrefix)) - Expect(item2.Status.PostgresRole).To(Equal(username2)) - Expect(item2.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item2.Status.OldPostgresRoles).To(Equal([]string{username})) - d2, err := time.Parse(time.RFC3339, item2.Status.LastPasswordChangedTime) - Expect(err).To(Succeed()) - Expect(d2.After(d)).To(BeTrue()) - Expect(d2.After(preDate)).To(BeTrue()) + It( + "should be ok to have rolling password enabled and performed with old user still connected and with a pgec with allow grant admin option enabled", + func() { + // Setup pgec + pgec, _ := setupPGECWithAllowGrantAdminOption("30s", false) + // Create pgdb + pgdb := setupPGDB(false) + + preDate := time.Now().Add(-time.Second) + + item := setupManagedPGUR("5s") + + username := pgurRolePrefix + Login0Suffix + username2 := pgurRolePrefix + Login1Suffix + + // Checks + Expect(item.Status.Ready).To(BeTrue()) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Message).To(Equal("")) + Expect(item.Status.RolePrefix).To(Equal(pgurRolePrefix)) + Expect(item.Status.PostgresRole).To(Equal(username)) + Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) + Expect(item.Status.OldPostgresRoles).To(Equal([]string{})) + d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) + Expect(err).To(Succeed()) + Expect(d.After(preDate)).To(BeTrue()) + + // Get work secret + workSec := &corev1.Secret{} + Expect(k8sClient.Get(ctx, types.NamespacedName{ + Name: item.Spec.WorkGeneratedSecretName, + Namespace: pgurNamespace, + }, workSec)).Should(Succeed()) + + // Connect + _, err = connectAs(username, string(workSec.Data[PasswordSecretKey])) + Expect(err).To(Succeed()) + + // Wait + time.Sleep(4 * time.Second) + + item2 := &v1alpha1.PostgresqlUserRole{} + Eventually( + func() error { + err = k8sClient.Get(ctx, types.NamespacedName{ + Name: pgurName, + Namespace: pgurNamespace, + }, item2) + // Check error + if err != nil { + return err + } + + if item.Status.PostgresRole == item2.Status.PostgresRole { + return errors.New("pgur not updated") + } - // Get work secret - workSec2 := &corev1.Secret{} - Expect(k8sClient.Get(ctx, types.NamespacedName{ - Name: item2.Spec.WorkGeneratedSecretName, - Namespace: pgurNamespace, - }, workSec2)).Should(Succeed()) + return nil + }, + generalEventuallyTimeout, + generalEventuallyInterval, + ). + Should(Succeed()) + + // Checks + Expect(item2.Status.Ready).To(BeTrue()) + Expect(item2.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) + Expect(item2.Status.Message).To(Equal("")) + Expect(item2.Status.RolePrefix).To(Equal(pgurRolePrefix)) + Expect(item2.Status.PostgresRole).To(Equal(username2)) + Expect(item2.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) + Expect(item2.Status.OldPostgresRoles).To(Equal([]string{username})) + d2, err := time.Parse(time.RFC3339, item2.Status.LastPasswordChangedTime) + Expect(err).To(Succeed()) + Expect(d2.After(d)).To(BeTrue()) + Expect(d2.After(preDate)).To(BeTrue()) + + // Get work secret + workSec2 := &corev1.Secret{} + Expect(k8sClient.Get(ctx, types.NamespacedName{ + Name: item2.Spec.WorkGeneratedSecretName, + Namespace: pgurNamespace, + }, workSec2)).Should(Succeed()) - Expect(string(workSec2.Data[UsernameSecretKey])).To(Equal(username2)) - Expect(string(workSec2.Data[PasswordSecretKey])).ToNot(Equal("")) - Expect(string(workSec2.Data[PasswordSecretKey])).To(HaveLen(ManagedPasswordSize)) - Expect(string(workSec2.Data[PasswordSecretKey])).ToNot(Equal(string(workSec.Data[PasswordSecretKey]))) + Expect(string(workSec2.Data[UsernameSecretKey])).To(Equal(username2)) + Expect(string(workSec2.Data[PasswordSecretKey])).ToNot(Equal("")) + Expect(string(workSec2.Data[PasswordSecretKey])).To(HaveLen(ManagedPasswordSize)) + Expect(string(workSec2.Data[PasswordSecretKey])).ToNot(Equal(string(workSec.Data[PasswordSecretKey]))) - // Get db secret - dbsec := &corev1.Secret{} - Expect(k8sClient.Get(ctx, types.NamespacedName{ - Name: pgurDBSecretName, - Namespace: pgurNamespace, - }, dbsec)).Should(Succeed()) + // Get db secret + dbsec := &corev1.Secret{} + Expect(k8sClient.Get(ctx, types.NamespacedName{ + Name: pgurDBSecretName, + Namespace: pgurNamespace, + }, dbsec)).Should(Succeed()) - // Validate - checkPGURSecretValues(pgurDBSecretName, pgurNamespace, pgdbDBName, username2, string(workSec2.Data[PasswordSecretKey]), pgec, v1alpha1.PrimaryConnectionType) + // Validate + checkPGURSecretValues( + pgurDBSecretName, + pgurNamespace, + pgdbDBName, + username2, + string(workSec2.Data[PasswordSecretKey]), + pgec, + v1alpha1.PrimaryConnectionType, + ) - // Connect to check user - _, err = connectAs(username2, string(workSec2.Data[PasswordSecretKey])) - Expect(err).To(Succeed()) + // Connect to check user + _, err = connectAs(username2, string(workSec2.Data[PasswordSecretKey])) + Expect(err).To(Succeed()) - exists, err := isSQLRoleExists(username) - Expect(err).To(Succeed()) - Expect(exists).To(BeTrue()) + exists, err := isSQLRoleExists(username) + Expect(err).To(Succeed()) + Expect(exists).To(BeTrue()) - exists, err = isSQLRoleExists(username2) - Expect(err).To(Succeed()) - Expect(exists).To(BeTrue()) + exists, err = isSQLRoleExists(username2) + Expect(err).To(Succeed()) + Expect(exists).To(BeTrue()) - sett, err := isSetRoleOnDatabasesRoleSettingsExists(username, pgdbDBName, pgdb.Status.Roles.Owner) - Expect(err).To(Succeed()) - Expect(sett).To(BeTrue()) + sett, err := isSetRoleOnDatabasesRoleSettingsExists(username, pgdbDBName, pgdb.Status.Roles.Owner) + Expect(err).To(Succeed()) + Expect(sett).To(BeTrue()) - sett, err = isSetRoleOnDatabasesRoleSettingsExists(username2, pgdbDBName, pgdb.Status.Roles.Owner) - Expect(err).To(Succeed()) - Expect(sett).To(BeTrue()) + sett, err = isSetRoleOnDatabasesRoleSettingsExists(username2, pgdbDBName, pgdb.Status.Roles.Owner) + Expect(err).To(Succeed()) + Expect(sett).To(BeTrue()) - ownerMemberWithAdminOption, err := getSQLRoleMembershipWithAdminOption(pgdb.Status.Roles.Owner) - Expect(err).ToNot(HaveOccurred()) - Expect(ownerMemberWithAdminOption).To(Equal(map[string]bool{postgresUser: true, username2: false, username: false})) - usernameWithAdminOption, err := getSQLRoleMembershipWithAdminOption(username) - Expect(err).ToNot(HaveOccurred()) - Expect(usernameWithAdminOption).To(Equal(map[string]bool{postgresUser: true})) - username2WithAdminOption, err := getSQLRoleMembershipWithAdminOption(username2) - Expect(err).ToNot(HaveOccurred()) - Expect(username2WithAdminOption).To(Equal(map[string]bool{postgresUser: true})) - }) + ownerMemberWithAdminOption, err := getSQLRoleMembershipWithAdminOption(pgdb.Status.Roles.Owner) + Expect(err).ToNot(HaveOccurred()) + Expect(ownerMemberWithAdminOption).To(Equal(map[string]bool{postgresUser: true, username2: false, username: false})) + usernameWithAdminOption, err := getSQLRoleMembershipWithAdminOption(username) + Expect(err).ToNot(HaveOccurred()) + Expect(usernameWithAdminOption).To(Equal(map[string]bool{postgresUser: true})) + username2WithAdminOption, err := getSQLRoleMembershipWithAdminOption(username2) + Expect(err).ToNot(HaveOccurred()) + Expect(username2WithAdminOption).To(Equal(map[string]bool{postgresUser: true})) + }, + ) It("should be ok to have rolling password enabled and performed with old user still connected and finally released", func() { // Setup pgec @@ -5159,12 +5332,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal(pgurRolePrefix)) Expect(item.Status.PostgresRole).To(Equal(username)) Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) Expect(item.Status.OldPostgresRoles).To(Equal([]string{})) d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) @@ -5184,10 +5357,10 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Wait time.Sleep(9 * time.Second) - item2 := &postgresqlv1alpha1.PostgresqlUserRole{} + item2 := &v1alpha1.PostgresqlUserRole{} Eventually( func() error { - k8sClient.Get(ctx, types.NamespacedName{ + err = k8sClient.Get(ctx, types.NamespacedName{ Name: pgurName, Namespace: pgurNamespace, }, item2) @@ -5209,7 +5382,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item2.Status.Ready).To(BeTrue()) - Expect(item2.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item2.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item2.Status.Message).To(Equal("")) Expect(item2.Status.RolePrefix).To(Equal(pgurRolePrefix)) Expect(item2.Status.PostgresRole).To(Equal(username2)) @@ -5240,7 +5413,15 @@ var _ = Describe("PostgresqlUserRole tests", func() { }, dbsec)).Should(Succeed()) // Validate - checkPGURSecretValues(pgurDBSecretName, pgurNamespace, pgdbDBName, username2, string(workSec2.Data[PasswordSecretKey]), pgec, v1alpha1.PrimaryConnectionType) + checkPGURSecretValues( + pgurDBSecretName, + pgurNamespace, + pgdbDBName, + username2, + string(workSec2.Data[PasswordSecretKey]), + pgec, + v1alpha1.PrimaryConnectionType, + ) // Connect to check user _, err = connectAs(username2, string(workSec2.Data[PasswordSecretKey])) @@ -5275,10 +5456,10 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Disconnect old Expect(disconnectConnFromKey(key)).To(Succeed()) - item3 := &postgresqlv1alpha1.PostgresqlUserRole{} + item3 := &v1alpha1.PostgresqlUserRole{} Eventually( func() error { - k8sClient.Get(ctx, types.NamespacedName{ + err = k8sClient.Get(ctx, types.NamespacedName{ Name: pgurName, Namespace: pgurNamespace, }, item3) @@ -5314,12 +5495,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal(pgurRolePrefix)) Expect(item.Status.PostgresRole).To(Equal(username)) Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) Expect(item.Status.OldPostgresRoles).To(Equal([]string{})) d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) @@ -5339,10 +5520,10 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Wait time.Sleep(2 * time.Second) - item2 := &postgresqlv1alpha1.PostgresqlUserRole{} + item2 := &v1alpha1.PostgresqlUserRole{} Eventually( func() error { - k8sClient.Get(ctx, types.NamespacedName{ + err = k8sClient.Get(ctx, types.NamespacedName{ Name: pgurName, Namespace: pgurNamespace, }, item2) @@ -5376,10 +5557,10 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Wait time.Sleep(4 * time.Second) - item3 := &postgresqlv1alpha1.PostgresqlUserRole{} + item3 := &v1alpha1.PostgresqlUserRole{} Eventually( func() error { - k8sClient.Get(ctx, types.NamespacedName{ + err = k8sClient.Get(ctx, types.NamespacedName{ Name: pgurName, Namespace: pgurNamespace, }, item3) @@ -5388,7 +5569,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { return err } - if item3.Status.Phase != postgresqlv1alpha1.UserRoleFailedPhase { + if item3.Status.Phase != v1alpha1.UserRoleFailedPhase { return errors.New("pgur not in failure status") } @@ -5400,7 +5581,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { Should(Succeed()) Expect(item3.Status.Ready).To(BeFalse()) - Expect(item3.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleFailedPhase)) + Expect(item3.Status.Phase).To(Equal(v1alpha1.UserRoleFailedPhase)) Expect(item3.Status.PostgresRole).To(Equal(username2)) Expect(item3.Status.RolePrefix).To(Equal(item3.Spec.RolePrefix)) Expect(item3.Status.OldPostgresRoles).To(Equal([]string{username})) @@ -5421,12 +5602,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal(pgurRolePrefix)) Expect(item.Status.PostgresRole).To(Equal(username)) Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) Expect(item.Status.OldPostgresRoles).To(Equal([]string{})) d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) @@ -5470,7 +5651,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { ). Should(Succeed()) - item2 := &postgresqlv1alpha1.PostgresqlUserRole{} + item2 := &v1alpha1.PostgresqlUserRole{} Expect(k8sClient.Get(ctx, types.NamespacedName{ Name: pgurName, Namespace: pgurNamespace, @@ -5514,12 +5695,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) Expect(item.Status.Message).To(Equal("")) Expect(item.Status.RolePrefix).To(Equal(pgurRolePrefix)) Expect(item.Status.PostgresRole).To(Equal(username)) Expect(item.Spec.WorkGeneratedSecretName).To(Equal(pgurWorkSecretName)) - Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(postgresqlv1alpha1.PrimaryConnectionType)) + Expect(item.Spec.Privileges[0].ConnectionType).To(Equal(v1alpha1.PrimaryConnectionType)) Expect(item.Status.OldPostgresRoles).To(Equal([]string{})) d, err := time.Parse(time.RFC3339, item.Status.LastPasswordChangedTime) Expect(err).To(Succeed()) @@ -5563,7 +5744,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { ). Should(Succeed()) - item2 := &postgresqlv1alpha1.PostgresqlUserRole{} + item2 := &v1alpha1.PostgresqlUserRole{} Expect(k8sClient.Get(ctx, types.NamespacedName{ Name: pgurName, Namespace: pgurNamespace, @@ -5603,7 +5784,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) username := pgurRolePrefix + Login0Suffix // Get work secret @@ -5631,7 +5812,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) username := pgurRolePrefix + Login0Suffix // Get work secret @@ -5659,7 +5840,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeFalse()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleFailedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleFailedPhase)) Expect(item.Status.Message).To(Equal("bouncer connection asked but not supported in engine configuration")) }) @@ -5674,7 +5855,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) username := pgurRolePrefix + Login0Suffix // Get work secret @@ -5707,7 +5888,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) username := pgurRolePrefix + Login0Suffix // Get work secret @@ -5778,7 +5959,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) username := pgurRolePrefix + Login0Suffix // Get work secret @@ -5849,7 +6030,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) username := pgurRolePrefix + Login0Suffix // Get work secret @@ -5877,7 +6058,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) username := pgurRolePrefix + Login0Suffix // Get work secret @@ -5906,7 +6087,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) username := pgurRolePrefix + Login0Suffix // Get work secret @@ -5939,7 +6120,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) username := pgurRolePrefix + Login0Suffix // Get work secret @@ -6010,7 +6191,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) username := pgurRolePrefix + Login0Suffix // Get work secret @@ -6084,7 +6265,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) username := pgurRolePrefix + Login0Suffix // Get work secret @@ -6115,9 +6296,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { } // Check if sec have been updated - if !strings.Contains(string(sec2.Data["POSTGRES_URL_ARGS"]), "fake=fake&fake2=fake2") { + if !strings.Contains(string(sec2.Data["POSTGRES_URL_ARGS"]), "fake2=fake2") { return errors.New("Secret not updated") } + if !strings.Contains(string(sec2.Data["POSTGRES_URL_ARGS"]), "fake=fake") { + return errors.New("Secret not updated 2") + } return nil }, @@ -6126,7 +6310,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { ). Should(Succeed()) - item2 := &postgresqlv1alpha1.PostgresqlUserRole{} + item2 := &v1alpha1.PostgresqlUserRole{} Expect(k8sClient.Get(ctx, types.NamespacedName{ Name: item.Name, Namespace: pgurNamespace, @@ -6155,7 +6339,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { // Checks Expect(item.Status.Ready).To(BeTrue()) - Expect(item.Status.Phase).To(Equal(postgresqlv1alpha1.UserRoleCreatedPhase)) + Expect(item.Status.Phase).To(Equal(v1alpha1.UserRoleCreatedPhase)) username := pgurRolePrefix + Login0Suffix // Get work secret @@ -6186,9 +6370,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { } // Check if sec have been updated - if !strings.Contains(string(sec2.Data["POSTGRES_URL_ARGS"]), "fake=fake&fake2=fake2") { + if !strings.Contains(string(sec2.Data["POSTGRES_URL_ARGS"]), "fake2=fake2") { return errors.New("Secret not updated") } + if !strings.Contains(string(sec2.Data["POSTGRES_URL_ARGS"]), "fake=fake") { + return errors.New("Secret not updated 2") + } return nil }, @@ -6197,7 +6384,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { ). Should(Succeed()) - item2 := &postgresqlv1alpha1.PostgresqlUserRole{} + item2 := &v1alpha1.PostgresqlUserRole{} Expect(k8sClient.Get(ctx, types.NamespacedName{ Name: item.Name, Namespace: pgurNamespace, @@ -6230,9 +6417,12 @@ var _ = Describe("PostgresqlUserRole tests", func() { } // Check if sec have been updated - if strings.Contains(string(sec2.Data["POSTGRES_URL_ARGS"]), "fake=fake&fake2=fake2") { + if strings.Contains(string(sec2.Data["POSTGRES_URL_ARGS"]), "fake2=fake2") { return errors.New("Secret not updated") } + if strings.Contains(string(sec2.Data["POSTGRES_URL_ARGS"]), "fake=fake") { + return errors.New("Secret not updated 2") + } return nil }, @@ -6241,7 +6431,7 @@ var _ = Describe("PostgresqlUserRole tests", func() { ). Should(Succeed()) - item3 := &postgresqlv1alpha1.PostgresqlUserRole{} + item3 := &v1alpha1.PostgresqlUserRole{} Expect(k8sClient.Get(ctx, types.NamespacedName{ Name: pgurName, Namespace: pgurNamespace, diff --git a/internal/controller/postgresql/suite_test.go b/internal/controller/postgresql/suite_test.go index 033a4aa..46935da 100644 --- a/internal/controller/postgresql/suite_test.go +++ b/internal/controller/postgresql/suite_test.go @@ -19,7 +19,6 @@ package postgresql import ( "context" "database/sql" - gerrors "errors" "fmt" "path/filepath" "strconv" @@ -28,86 +27,95 @@ import ( "time" "github.com/lib/pq" - . "github.com/onsi/ginkgo/v2" - . "github.com/onsi/gomega" "github.com/prometheus/client_golang/prometheus" - - corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/errors" - v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/kubernetes/scheme" "k8s.io/client-go/rest" - ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/cache" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/envtest" - logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/log/zap" + //nolint:revive + . "github.com/onsi/ginkgo/v2" + //nolint:revive + . "github.com/onsi/gomega" + + gerrors "errors" + // + corev1 "k8s.io/api/core/v1" + v1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ctrl "sigs.k8s.io/controller-runtime" + logf "sigs.k8s.io/controller-runtime/pkg/log" + "github.com/easymile/postgresql-operator/api/postgresql/common" postgresqlv1alpha1 "github.com/easymile/postgresql-operator/api/postgresql/v1alpha1" "github.com/easymile/postgresql-operator/internal/controller/config" "github.com/easymile/postgresql-operator/internal/controller/postgresql/postgres" "github.com/easymile/postgresql-operator/internal/controller/utils" - //+kubebuilder:scaffold:imports ) // These tests use Ginkgo (BDD-style Go testing framework). Refer to // http://onsi.github.io/ginkgo/ to learn more about Ginkgo. -var cfg *rest.Config -var k8sClient client.Client -var testEnv *envtest.Environment -var ctx context.Context -var cancel context.CancelFunc -var generalEventuallyTimeout = 60 * time.Second -var generalEventuallyInterval = time.Second -var pgpublicationNamespace = "pgpub-ns" -var pgpublicationName = "pgpub-object" -var pgpublicationPublicationName1 = "pub1" -var pgpublicationCustomReplicationSlotName = "replslotname" -var pgecNamespace = "pgec-ns" -var pgecName = "pgec-object" -var pgecSecretName = "pgec-secret" -var pgdbNamespace = "pgdb-ns" -var pgdbName = "pgdb-object" -var pgdbName2 = "pgdb-object2" -var pgdbDBName = "super-db" -var pgdbDBName2 = "super-db2" -var pguNamespace = "pgu-ns" -var pguName = "pgu-object" -var pgurNamespace = "pgur-ns" -var pgurName = "pgur-object" -var pgurWorkSecretName = "pgur-work-secret" -var pgurDBSecretName = "pgur-db-secret" -var pgurDBSecretName2 = "pgur-db-secret2" -var pgurImportSecretName = "pgu-import-secret" -var pgurImportUsername = "fake-username" -var pgurImportPassword = "fake-password" -var pgurRolePrefix = "role-prefix" -var pgdbSchemaName1 = "one_schema" -var pgdbSchemaName2 = "second_schema" -var pgPublicSchemaName = "public" -var pgdbExtensionName1 = "uuid-ossp" -var pgdbExtensionName2 = "cube" -var postgresUser = "postgres" -var postgresPassword = "postgres" -var postgresUrlWithDbTemplate = "postgresql://%s:%s@localhost:5432/%s?sslmode=disable" -var postgresUrl = "postgresql://postgres:postgres@localhost:5432/?sslmode=disable" -var postgresUrlToDB = "postgresql://postgres:postgres@localhost:5432/" + pgdbDBName + "?sslmode=disable" -var editedSecretName = "updated-secret-name" -var dbConns = map[string]*struct { - tx *sql.Tx - db *sql.DB -}{} -var mainDBConn *sql.DB -var controllerRuntimeDetailedErrorTotal = prometheus.NewCounterVec( - prometheus.CounterOpts{ - Name: "controller_runtime_reconcile_detailed_errors_total", - Help: "Total number of reconciliation errors per controller detailed with resource namespace and name.", - }, - []string{"controller", "namespace", "name"}, +var ( + cfg *rest.Config + k8sClient client.Client + testEnv *envtest.Environment + ctx context.Context + cancel context.CancelFunc + generalEventuallyTimeout = 60 * time.Second + generalEventuallyInterval = time.Second + pgpublicationNamespace = "pgpub-ns" + pgpublicationName = "pgpub-object" + pgpublicationPublicationName1 = "pub1" + pgpublicationCustomReplicationSlotName = "replslotname" + pgecNamespace = "pgec-ns" + pgecName = "pgec-object" + pgecSecretName = "pgec-secret" + pgdbNamespace = "pgdb-ns" + pgdbName = "pgdb-object" + pgdbName2 = "pgdb-object2" + pgdbDBName = "super-db" + pgdbDBName2 = "super-db2" + pguNamespace = "pgu-ns" + pgurNamespace = "pgur-ns" + pgurName = "pgur-object" + pgurWorkSecretName = "pgur-work-secret" + pgurDBSecretName = "pgur-db-secret" + pgurDBSecretName2 = "pgur-db-secret2" + pgurImportSecretName = "pgu-import-secret" + pgurImportUsername = "fake-username" + pgurImportPassword = "fake-password" + pgurRolePrefix = "role-prefix" + pgdbSchemaName1 = "one_schema" + pgdbSchemaName2 = "second_schema" + pgPublicSchemaName = "public" + pgdbExtensionName1 = "uuid-ossp" + pgdbExtensionName2 = "cube" + postgresUser = "postgres" + postgresPassword = "postgres" + postgresUrlWithDbTemplate = "postgresql://%s:%s@localhost:5432/%s?sslmode=disable" + postgresUrl = "postgresql://postgres:postgres@localhost:5432/?sslmode=disable" + postgresUrlToDB = "postgresql://postgres:postgres@localhost:5432/" + pgdbDBName + "?sslmode=disable" + editedSecretName = "updated-secret-name" + dbConns = map[string]*struct { + tx *sql.Tx + db *sql.DB + }{} +) + +var ( + mainDBConn *sql.DB + controllerRuntimeDetailedErrorTotal = prometheus.NewCounterVec( + prometheus.CounterOpts{ + Name: "controller_runtime_reconcile_detailed_errors_total", + Help: "Total number of reconciliation errors per controller detailed with resource namespace and name.", + }, + []string{"controller", "namespace", "name"}, + ) ) func TestControllers(t *testing.T) { @@ -144,8 +152,8 @@ var _ = BeforeSuite(func(_ context.Context) { resyncPeriod := 5 * time.Second k8sManager, err := ctrl.NewManager(cfg, ctrl.Options{ - Scheme: scheme.Scheme, - SyncPeriod: &resyncPeriod, + Scheme: scheme.Scheme, + Cache: cache.Options{SyncPeriod: &resyncPeriod}, }) Expect(err).ToNot(HaveOccurred()) Expect(k8sManager).ToNot(BeNil()) @@ -234,8 +242,9 @@ var _ = AfterSuite(func() { Expect(err).NotTo(HaveOccurred()) // Close db - for k, _ := range dbConns { - disconnectConnFromKey(k) + for k := range dbConns { + err = disconnectConnFromKey(k) + Expect(err).NotTo(HaveOccurred()) } if mainDBConn != nil { Expect(mainDBConn.Close()).To(Succeed()) @@ -247,8 +256,9 @@ func starAny[T any](s T) *T { } func cleanupFunction() { - for k, _ := range dbConns { - disconnectConnFromKey(k) + for k := range dbConns { + err := disconnectConnFromKey(k) + Expect(err).NotTo(HaveOccurred()) } // Force delete pgec @@ -292,14 +302,6 @@ func cleanupFunction() { Expect(err).ToNot(HaveOccurred()) } -func getSecret(ctx context.Context, cli client.Client, name, namespace string) (*corev1.Secret, error) { - sec := &corev1.Secret{} - // Get secret - err := cli.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, sec) - - return sec, err -} - func deleteSecret(ctx context.Context, cl client.Client, name, namespace string) error { // Create secret structure secret := &corev1.Secret{} @@ -354,7 +356,7 @@ func deleteObject( // Get item to force cache clean // Loop until it is cleaned or max try - for i := 0; i < 1000; i++ { + for range 1000 { err = cl.Get(ctx, types.NamespacedName{Name: name, Namespace: namespace}, obj) // Check error if err != nil { @@ -432,6 +434,7 @@ func setupProvidedPGUR() *postgresqlv1alpha1.PostgresqlUserRole { return setupSavePGURInternal(it) } + func setupProvidedPGURAndPartialCustomAttributes() *postgresqlv1alpha1.PostgresqlUserRole { it := &postgresqlv1alpha1.PostgresqlUserRole{ ObjectMeta: v1.ObjectMeta{ @@ -785,18 +788,6 @@ func setupPGECWithBouncer( }, nil, nil, false) } -func setupPGECWithReplica( - checkInterval string, - waitLinkedResourcesDeletion bool, -) (*postgresqlv1alpha1.PostgresqlEngineConfiguration, *corev1.Secret) { - uc := &postgresqlv1alpha1.GenericUserConnection{ - Host: "localhost", - Port: 5432, - URIArgs: "sslmode=disable", - } - return setupPGECInternal(checkInterval, waitLinkedResourcesDeletion, uc, nil, []*postgresqlv1alpha1.GenericUserConnection{uc}, nil, false) -} - func setupPGECWithBouncerAndReplica( checkInterval string, waitLinkedResourcesDeletion bool, @@ -811,7 +802,16 @@ func setupPGECWithBouncerAndReplica( Port: 5433, URIArgs: "sslmode=disable", } - return setupPGECInternal(checkInterval, waitLinkedResourcesDeletion, uc, buc, []*postgresqlv1alpha1.GenericUserConnection{uc}, []*postgresqlv1alpha1.GenericUserConnection{buc}, false) + + return setupPGECInternal( + checkInterval, + waitLinkedResourcesDeletion, + uc, + buc, + []*postgresqlv1alpha1.GenericUserConnection{uc}, + []*postgresqlv1alpha1.GenericUserConnection{buc}, + false, + ) } func setupPGECWithAllowGrantAdminOption( @@ -992,21 +992,28 @@ func deletePGPublication(ctx context.Context, cl client.Client, name, namespace func deleteSQLDBs(name string) error { // Query template - GetAllCreatedSQLDBTemplate := "SELECT datname FROM pg_database WHERE datname LIKE '%" + name + "%';" + getAllCreatedSQLDBTemplate := "SELECT datname FROM pg_database WHERE datname LIKE '%" + name + "%';" if mainDBConn == nil { db, err := sql.Open("postgres", postgresUrl) if err != nil { return err } + mainDBConn = db } - res, err := mainDBConn.Query(GetAllCreatedSQLDBTemplate) + res, err := mainDBConn.Query(getAllCreatedSQLDBTemplate) if err != nil { return err } + if res.Err() != nil { + return res.Err() + } + + defer res.Close() + var dbname string for res.Next() { err = res.Scan(&dbname) @@ -1015,7 +1022,7 @@ func deleteSQLDBs(name string) error { } // Try to delete - for i := 0; i < 1000; i++ { + for range 1000 { _, err = mainDBConn.Exec(fmt.Sprintf(postgres.DropDatabaseSQLTemplate, dbname)) if err == nil { break @@ -1042,6 +1049,7 @@ func createSQLDB(name, role string) error { if err != nil { return err } + mainDBConn = db } @@ -1064,6 +1072,7 @@ func isSQLDBExists(name string) (bool, error) { if err != nil { return false, err } + mainDBConn = db } @@ -1082,21 +1091,28 @@ func isSQLDBExists(name string) (bool, error) { func deleteSQLRoles() error { // Query template - GetAllCreatedRolesSQLTemplate := `SELECT rolname FROM pg_roles WHERE rolname NOT LIKE 'pg\_%' AND rolname != 'postgres'` + getAllCreatedRolesSQLTemplate := `SELECT rolname FROM pg_roles WHERE rolname NOT LIKE 'pg\_%' AND rolname != 'postgres'` if mainDBConn == nil { db, err := sql.Open("postgres", postgresUrl) if err != nil { return err } + mainDBConn = db } - res, err := mainDBConn.Query(GetAllCreatedRolesSQLTemplate) + res, err := mainDBConn.Query(getAllCreatedRolesSQLTemplate) if err != nil { return err } + if res.Err() != nil { + return res.Err() + } + + defer res.Close() + var role string for res.Next() { err = res.Scan(&role) @@ -1119,6 +1135,7 @@ func createSQLRole(role string) error { if err != nil { return err } + mainDBConn = db } @@ -1141,6 +1158,7 @@ func isSQLRoleExists(name string) (bool, error) { if err != nil { return false, err } + mainDBConn = db } @@ -1159,7 +1177,7 @@ func isSQLRoleExists(name string) (bool, error) { func isSQLSchemaExists(name string) (bool, error) { // Query template - IsSchemaExistSQLTemplate := `SELECT 1 FROM information_schema.schemata WHERE schema_name='%s'` + isSchemaExistSQLTemplate := `SELECT 1 FROM information_schema.schemata WHERE schema_name='%s'` // Connect db, err := sql.Open("postgres", postgresUrlToDB) @@ -1168,11 +1186,9 @@ func isSQLSchemaExists(name string) (bool, error) { return false, err } - defer func() error { - return db.Close() - }() + defer db.Close() - res, err := db.Exec(fmt.Sprintf(IsSchemaExistSQLTemplate, name)) + res, err := db.Exec(fmt.Sprintf(isSchemaExistSQLTemplate, name)) if err != nil { return false, err } @@ -1187,7 +1203,7 @@ func isSQLSchemaExists(name string) (bool, error) { func isSQLExtensionExists(name string) (bool, error) { // Query template - IsExtensionExistSQLTemplate := `SELECT 1 FROM pg_extension WHERE extname='%s'` + isExtensionExistSQLTemplate := `SELECT 1 FROM pg_extension WHERE extname='%s'` // Connect db, err := sql.Open("postgres", postgresUrlToDB) @@ -1196,11 +1212,9 @@ func isSQLExtensionExists(name string) (bool, error) { return false, err } - defer func() error { - return db.Close() - }() + defer db.Close() - res, err := db.Exec(fmt.Sprintf(IsExtensionExistSQLTemplate, name)) + res, err := db.Exec(fmt.Sprintf(isExtensionExistSQLTemplate, name)) if err != nil { return false, err } @@ -1224,11 +1238,14 @@ func getTableOwnerInSchema(dbName, schemaName, tableName string) (string, error) defer db.Close() sqlTemplate := `select tableowner from pg_tables where tablename = '%s' and schemaname = '%s';` + res, err := db.Query(fmt.Sprintf(sqlTemplate, tableName, schemaName)) if err != nil { return "", err } + defer res.Close() + var owner string for res.Next() { err = res.Scan(&owner) @@ -1255,9 +1272,7 @@ func rawSQLQuery(raw string) error { return err } - defer func() error { - return db.Close() - }() + defer db.Close() _, err = db.Exec(raw) if err != nil { @@ -1269,7 +1284,7 @@ func rawSQLQuery(raw string) error { func createTableInSchemaAsAdmin(schema, table string) error { // Query template - CreateTableInSchemaTemplate := `CREATE TABLE %s.%s()` + createTableInSchemaTemplate := `CREATE TABLE %s.%s()` // Connect db, err := sql.Open("postgres", postgresUrlToDB) @@ -1278,11 +1293,9 @@ func createTableInSchemaAsAdmin(schema, table string) error { return err } - defer func() error { - return db.Close() - }() + defer db.Close() - _, err = db.Exec(fmt.Sprintf(CreateTableInSchemaTemplate, schema, table)) + _, err = db.Exec(fmt.Sprintf(createTableInSchemaTemplate, schema, table)) if err != nil { return err } @@ -1300,9 +1313,7 @@ func createColumnInTable(table, columnName, columnType string) error { return err } - defer func() error { - return db.Close() - }() + defer db.Close() _, err = db.Exec(fmt.Sprintf(tmpl, table, columnName, columnType)) if err != nil { @@ -1327,10 +1338,12 @@ func create2KnownTablesWithColumnsInPublicSchema() error { if err != nil { return err } + err = createColumnInTable("public.fake", "nb", "integer") if err != nil { return err } + err = createColumnInTable("public.fake", "nb2", "integer") if err != nil { return err @@ -1340,6 +1353,7 @@ func create2KnownTablesWithColumnsInPublicSchema() error { if err != nil { return err } + err = createColumnInTable("public.fake2", "test", "integer") if err != nil { return err @@ -1361,11 +1375,14 @@ func getTypeOwner(dbName, typeName string) (string, error) { defer db.Close() sqlTemplate := `SELECT typowner::regrole FROM pg_type WHERE typname = '%s';` + res, err := db.Query(fmt.Sprintf(sqlTemplate, typeName)) if err != nil { return "", err } + defer res.Close() + var owner string for res.Next() { err = res.Scan(&owner) @@ -1389,7 +1406,7 @@ func getTypeOwner(dbName, typeName string) (string, error) { func createTypeInSchemaAsAdmin(schema, typeName string) error { // Query template - CreateTypeInSchemaTemplate := `CREATE TYPE "%s"."%s" AS ENUM ('new', 'open', 'closed');` + createTypeInSchemaTemplate := `CREATE TYPE "%s"."%s" AS ENUM ('new', 'open', 'closed');` // Connect db, err := sql.Open("postgres", postgresUrlToDB) @@ -1398,11 +1415,9 @@ func createTypeInSchemaAsAdmin(schema, typeName string) error { return err } - defer func() error { - return db.Close() - }() + defer db.Close() - _, err = db.Exec(fmt.Sprintf(CreateTypeInSchemaTemplate, schema, typeName)) + _, err = db.Exec(fmt.Sprintf(createTypeInSchemaTemplate, schema, typeName)) if err != nil { return err } @@ -1428,9 +1443,7 @@ func getPublication(name string) (*PublicationResult, error) { return nil, err } - defer func() error { - return db.Close() - }() + defer db.Close() // Get rows rows, err := db.Query(fmt.Sprintf(postgres.GetPublicationSQLTemplate, name)) @@ -1472,10 +1485,10 @@ func getPublication(name string) (*PublicationResult, error) { } type PublicationTableDetail struct { + AdditionalWhere *string SchemaName string TableName string Columns []string - AdditionalWhere *string } func getPublicationTableDetails(name string) ([]*PublicationTableDetail, error) { @@ -1486,9 +1499,7 @@ func getPublicationTableDetails(name string) ([]*PublicationTableDetail, error) return nil, err } - defer func() error { - return db.Close() - }() + defer db.Close() // Get rows rows, err := db.Query(fmt.Sprintf(`SELECT @@ -1504,8 +1515,10 @@ WHERE pubname = '%s';`, name)) res := make([]*PublicationTableDetail, 0) for rows.Next() { - var it PublicationTableDetail - var pqSA pq.StringArray + var ( + it PublicationTableDetail + pqSA pq.StringArray + ) // Scan err = rows.Scan(&it.SchemaName, &it.TableName, &pqSA, &it.AdditionalWhere) // Check error @@ -1539,11 +1552,9 @@ func dropReplicationSlot(name string) error { return err } - defer func() error { - return db.Close() - }() + defer db.Close() - DropReplicationSlotSQLTemplate := `SELECT pg_drop_replication_slot('%s')` + dropReplicationSlotSQLTemplate := `SELECT pg_drop_replication_slot('%s')` count := 0 for count <= 20 { @@ -1552,18 +1563,18 @@ func dropReplicationSlot(name string) error { return err } - if repl1 != nil { - _, err = db.Exec(fmt.Sprintf(DropReplicationSlotSQLTemplate, name)) - if err != nil { - return err - } - - count += 1 - // Wait - time.Sleep(100 * time.Millisecond) - } else { + if repl1 == nil { break } + + _, err = db.Exec(fmt.Sprintf(dropReplicationSlotSQLTemplate, name)) + if err != nil { + return err + } + + count++ + // Wait + time.Sleep(100 * time.Millisecond) } repl1, err := getReplicationSlotInternal(db, name) @@ -1587,13 +1598,11 @@ func createReplicationSlotInMainDB(name, plugin string) error { return err } - defer func() error { - return db.Close() - }() + defer db.Close() - CreateReplicationSlotSQLTemplate := `SELECT pg_create_logical_replication_slot('%s', '%s')` + createReplicationSlotSQLTemplate := `SELECT pg_create_logical_replication_slot('%s', '%s')` - _, err = db.Exec(fmt.Sprintf(CreateReplicationSlotSQLTemplate, name, plugin)) + _, err = db.Exec(fmt.Sprintf(createReplicationSlotSQLTemplate, name, plugin)) if err != nil { return err } @@ -1616,18 +1625,16 @@ func getReplicationSlot(name string) (*replicationSlotResult, error) { return nil, err } - defer func() error { - return db.Close() - }() + defer db.Close() return getReplicationSlotInternal(db, name) } func getReplicationSlotInternal(db *sql.DB, name string) (*replicationSlotResult, error) { - GetReplicationSlotSQLTemplate := `SELECT slot_name,plugin,database FROM pg_replication_slots WHERE slot_name = '%s'` + getReplicationSlotSQLTemplate := `SELECT slot_name,plugin,database FROM pg_replication_slots WHERE slot_name = '%s'` // Get rows - rows, err := db.Query(fmt.Sprintf(GetReplicationSlotSQLTemplate, name)) + rows, err := db.Query(fmt.Sprintf(getReplicationSlotSQLTemplate, name)) if err != nil { return nil, err } @@ -1685,12 +1692,11 @@ func getRoleAttributes(role string) (*RoleAttributes, error) { return nil, err } - defer func() error { - return db.Close() - }() + defer db.Close() - GetRoleAttributesSQLTemplate := `select rolconnlimit, rolreplication, rolbypassrls FROM pg_roles WHERE rolname = '%s'` - rows, err := db.Query(fmt.Sprintf(GetRoleAttributesSQLTemplate, role)) + getRoleAttributesSQLTemplate := `select rolconnlimit, rolreplication, rolbypassrls FROM pg_roles WHERE rolname = '%s'` + + rows, err := db.Query(fmt.Sprintf(getRoleAttributesSQLTemplate, role)) if err != nil { return res, err } @@ -1775,10 +1781,12 @@ func changeDBOwner(dbname, role string) error { if err != nil { return err } + mainDBConn = db } sqlTemplate := `ALTER DATABASE "%s" OWNER TO "%s"` + _, err := mainDBConn.Exec(fmt.Sprintf(sqlTemplate, dbname, role)) if err != nil { return err @@ -1789,17 +1797,18 @@ func changeDBOwner(dbname, role string) error { func isRoleOwnerofSQLDB(dbname, role string) (bool, error) { // Query template - IsRoleOwnerOfDbSQLTemplate := `SELECT 1 FROM pg_catalog.pg_database d WHERE d.datname = '%s' AND pg_catalog.pg_get_userbyid(d.datdba) = '%s';` + isRoleOwnerOfDbSQLTemplate := `SELECT 1 FROM pg_catalog.pg_database d WHERE d.datname = '%s' AND pg_catalog.pg_get_userbyid(d.datdba) = '%s';` if mainDBConn == nil { db, err := sql.Open("postgres", postgresUrl) if err != nil { return false, err } + mainDBConn = db } - res, err := mainDBConn.Exec(fmt.Sprintf(IsRoleOwnerOfDbSQLTemplate, dbname, role)) + res, err := mainDBConn.Exec(fmt.Sprintf(isRoleOwnerOfDbSQLTemplate, dbname, role)) if err != nil { return false, err } @@ -1820,6 +1829,7 @@ func getSQLRoleMembershipWithAdminOption(role string) (map[string]bool, error) { if err != nil { return nil, err } + mainDBConn = db } @@ -1858,17 +1868,18 @@ func getSQLRoleMembershipWithAdminOption(role string) (map[string]bool, error) { } func isSetRoleOnDatabasesRoleSettingsExists(username, databaseInput, groupRole string) (bool, error) { - GetRoleSettingsSQLTemplate := `SELECT pg_catalog.split_part(pg_catalog.unnest(setconfig), '=', 1) as parameter_type, pg_catalog.split_part(pg_catalog.unnest(setconfig), '=', 2) as parameter_value, d.datname as database FROM pg_catalog.pg_roles r JOIN pg_catalog.pg_db_role_setting c ON (c.setrole = r.oid) JOIN pg_catalog.pg_database d ON (d.oid = c.setdatabase) WHERE r.rolcanlogin AND r.rolname='%s'` + getRoleSettingsSQLTemplate := `SELECT pg_catalog.split_part(pg_catalog.unnest(setconfig), '=', 1) as parameter_type, pg_catalog.split_part(pg_catalog.unnest(setconfig), '=', 2) as parameter_value, d.datname as database FROM pg_catalog.pg_roles r JOIN pg_catalog.pg_db_role_setting c ON (c.setrole = r.oid) JOIN pg_catalog.pg_database d ON (d.oid = c.setdatabase) WHERE r.rolcanlogin AND r.rolname='%s'` if mainDBConn == nil { db, err := sql.Open("postgres", postgresUrl) if err != nil { return false, err } + mainDBConn = db } - rows, err := mainDBConn.Query(fmt.Sprintf(GetRoleSettingsSQLTemplate, username)) + rows, err := mainDBConn.Query(fmt.Sprintf(getRoleSettingsSQLTemplate, username)) if err != nil { return false, err } @@ -1935,14 +1946,12 @@ func checkPGURSecretValuesWithExtraArgs( userCon = pgec.Spec.UserConnections.BouncerConnection } - // Compute uri args from main ones to user defined ones - uriArgList := []string{userCon.URIArgs} // Loop over user defined list for k, v := range extraArgsMap { - uriArgList = append(uriArgList, fmt.Sprintf("%s=%s", k, v)) + Expect( + strings.Contains(string(secret.Data["ARGS"]), fmt.Sprintf("%s=%s", k, v)), + ).To(BeTrue(), string(secret.Data["ARGS"])+" contains "+fmt.Sprintf("%s=%s", k, v)) } - // Join - uriArgs := strings.Join(uriArgList, "&") Expect(string(secret.Data["POSTGRES_URL"])).To(Equal( fmt.Sprintf("postgresql://%s:%s@%s:%d/%s", secret.Data["LOGIN"], secret.Data["PASSWORD"], userCon.Host, userCon.Port, dbName), @@ -1953,8 +1962,7 @@ func checkPGURSecretValuesWithExtraArgs( Expect(string(secret.Data["LOGIN"])).To(Equal(username)) Expect(string(secret.Data["DATABASE"])).To(Equal(dbName)) Expect(string(secret.Data["HOST"])).To(Equal(userCon.Host)) - Expect(string(secret.Data["PORT"])).To(Equal(fmt.Sprint(userCon.Port))) - Expect(string(secret.Data["ARGS"])).To(Equal(uriArgs)) + Expect(string(secret.Data["PORT"])).To(Equal(strconv.Itoa(userCon.Port))) // Check replica data rucList := pgec.Spec.UserConnections.ReplicaConnections @@ -1976,13 +1984,15 @@ func checkPGURSecretValuesWithExtraArgs( Expect(string(secret.Data["REPLICA_"+strconv.Itoa(i)+"_POSTGRES_URL"])).To(Equal( fmt.Sprintf("postgresql://%s:%s@%s:%d/%s", secret.Data["LOGIN"], secret.Data["PASSWORD"], userCon.Host, userCon.Port, dbName), )) - Expect(string(secret.Data["REPLICA_"+strconv.Itoa(i)+"_POSTGRES_URL_ARGS"])).To(Equal(fmt.Sprintf("%s?%s", secret.Data["POSTGRES_URL"], secret.Data["ARGS"]))) + Expect( + string(secret.Data["REPLICA_"+strconv.Itoa(i)+"_POSTGRES_URL_ARGS"]), + ).To(Equal(fmt.Sprintf("%s?%s", secret.Data["POSTGRES_URL"], secret.Data["ARGS"]))) Expect(secret.Data["REPLICA_"+strconv.Itoa(i)+"_PASSWORD"]).ToNot(BeEmpty()) Expect(string(secret.Data["REPLICA_"+strconv.Itoa(i)+"_PASSWORD"])).To(Equal(password)) Expect(string(secret.Data["REPLICA_"+strconv.Itoa(i)+"_LOGIN"])).To(Equal(username)) Expect(string(secret.Data["REPLICA_"+strconv.Itoa(i)+"_DATABASE"])).To(Equal(dbName)) Expect(string(secret.Data["REPLICA_"+strconv.Itoa(i)+"_HOST"])).To(Equal(userCon.Host)) - Expect(string(secret.Data["REPLICA_"+strconv.Itoa(i)+"_PORT"])).To(Equal(fmt.Sprint(userCon.Port))) + Expect(string(secret.Data["REPLICA_"+strconv.Itoa(i)+"_PORT"])).To(Equal(strconv.Itoa(userCon.Port))) Expect(string(secret.Data["REPLICA_"+strconv.Itoa(i)+"_ARGS"])).To(Equal(uriArgs)) } } diff --git a/internal/controller/utils/utils.go b/internal/controller/utils/utils.go index 5aa8f4a..2e49ea6 100644 --- a/internal/controller/utils/utils.go +++ b/internal/controller/utils/utils.go @@ -6,16 +6,18 @@ import ( "encoding/hex" "encoding/json" - "github.com/easymile/postgresql-operator/api/postgresql/common" - postgresqlv1alpha1 "github.com/easymile/postgresql-operator/api/postgresql/v1alpha1" - "github.com/easymile/postgresql-operator/internal/controller/postgresql/postgres" "github.com/go-logr/logr" - corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" + + corev1 "k8s.io/api/core/v1" + + "github.com/easymile/postgresql-operator/api/postgresql/common" + postgresqlv1alpha1 "github.com/easymile/postgresql-operator/api/postgresql/v1alpha1" + "github.com/easymile/postgresql-operator/internal/controller/postgresql/postgres" ) -func CalculateHash(spec interface{}) (string, error) { +func CalculateHash(spec any) (string, error) { // Json marshal spec bytes, err := json.Marshal(spec) if err != nil { diff --git a/test/e2e/e2e_suite_test.go b/test/e2e/e2e_suite_test.go new file mode 100644 index 0000000..9cf8f6e --- /dev/null +++ b/test/e2e/e2e_suite_test.go @@ -0,0 +1,90 @@ +/* +Copyright 2026. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + "fmt" + "os/exec" + "testing" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/easymile/postgresql-operator/test/utils" +) + +var ( + // Optional Environment Variables: + // - CERT_MANAGER_INSTALL_SKIP=true: Skips CertManager installation during test setup. + // These variables are useful if CertManager is already installed, avoiding + // re-installation and conflicts. + skipCertManagerInstall = true // os.Getenv("CERT_MANAGER_INSTALL_SKIP") == "true" + // isCertManagerAlreadyInstalled will be set true when CertManager CRDs be found on the cluster. + isCertManagerAlreadyInstalled = false + + // projectImage is the name of the image which will be build and loaded + // with the code source changes to be tested. + projectImage = "example.com/postgresql-operator:v0.0.1" +) + +// TestE2E runs the end-to-end (e2e) test suite for the project. These tests execute in an isolated, +// temporary environment to validate project changes with the purposed to be used in CI jobs. +// The default setup requires Kind, builds/loads the Manager Docker image locally, and installs +// CertManager. +func TestE2E(t *testing.T) { + RegisterFailHandler(Fail) + + _, _ = fmt.Fprintf(GinkgoWriter, "Starting postgresql-operator integration test suite\n") + + RunSpecs(t, "e2e suite") +} + +var _ = BeforeSuite(func() { + By("building the manager(Operator) image") + cmd := exec.Command("make", "docker-build", "IMG="+projectImage) + _, err := utils.Run(cmd) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to build the manager(Operator) image") + + // TODO(user): If you want to change the e2e test vendor from Kind, ensure the image is + // built and available before running the tests. Also, remove the following block. + By("loading the manager(Operator) image on Kind") + err = utils.LoadImageToKindClusterWithName(projectImage) + ExpectWithOffset(1, err).NotTo(HaveOccurred(), "Failed to load the manager(Operator) image into Kind") + + // The tests-e2e are intended to run on a temporary cluster that is created and destroyed for testing. + // To prevent errors when tests run in environments with CertManager already installed, + // we check for its presence before execution. + // Setup CertManager before the suite if not skipped and if not already installed + if !skipCertManagerInstall { + By("checking if cert manager is installed already") + isCertManagerAlreadyInstalled = utils.IsCertManagerCRDsInstalled() + if !isCertManagerAlreadyInstalled { + _, _ = fmt.Fprintf(GinkgoWriter, "Installing CertManager...\n") + Expect(utils.InstallCertManager()).To(Succeed(), "Failed to install CertManager") + } else { + _, _ = fmt.Fprintf(GinkgoWriter, "WARNING: CertManager is already installed. Skipping installation...\n") + } + } +}) + +var _ = AfterSuite(func() { + // Teardown CertManager after the suite if not skipped and if it was not already installed + if !skipCertManagerInstall && !isCertManagerAlreadyInstalled { + _, _ = fmt.Fprintf(GinkgoWriter, "Uninstalling CertManager...\n") + utils.UninstallCertManager() + } +}) diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go new file mode 100644 index 0000000..6d3a026 --- /dev/null +++ b/test/e2e/e2e_test.go @@ -0,0 +1,335 @@ +/* +Copyright 2026. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + "encoding/json" + "fmt" + "os" + "os/exec" + "path/filepath" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + "github.com/easymile/postgresql-operator/test/utils" +) + +// namespace where the project is deployed in. +const namespace = "postgresql-operator-system" + +// serviceAccountName created for the project. +const serviceAccountName = "postgresql-operator-controller-manager" + +// metricsServiceName is the name of the metrics service of the project. +const metricsServiceName = "postgresql-operator-controller-manager-metrics-service" + +// metricsRoleBindingName is the name of the RBAC that will be created to allow get the metrics data. +const metricsRoleBindingName = "postgresql-operator-metrics-binding" + +var _ = Describe("Manager", Ordered, func() { + var controllerPodName string + + // Before running the tests, set up the environment by creating the namespace, + // enforce the restricted security policy to the namespace, installing CRDs, + // and deploying the controller. + BeforeAll(func() { + By("creating manager namespace") + cmd := exec.Command("kubectl", "create", "ns", namespace) + _, err := utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Failed to create namespace") + + By("labeling the namespace to enforce the restricted security policy") + cmd = exec.Command("kubectl", "label", "--overwrite", "ns", namespace, + "pod-security.kubernetes.io/enforce=restricted") + _, err = utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Failed to label namespace with restricted policy") + + By("installing CRDs") + cmd = exec.Command("make", "install") + _, err = utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Failed to install CRDs") + + By("deploying the controller-manager") + cmd = exec.Command("make", "deploy", "IMG="+projectImage) + str, err := utils.Run(cmd) + By(str) + Expect(err).NotTo(HaveOccurred(), "Failed to deploy the controller-manager") + }) + + // After all tests have been executed, clean up by undeploying the controller, uninstalling CRDs, + // and deleting the namespace. + AfterAll(func() { + By("cleaning up the curl pod for metrics") + cmd := exec.Command("kubectl", "delete", "pod", "curl-metrics", "-n", namespace) + _, _ = utils.Run(cmd) + + By("undeploying the controller-manager") + cmd = exec.Command("make", "undeploy") + _, _ = utils.Run(cmd) + + By("uninstalling CRDs") + cmd = exec.Command("make", "uninstall") + _, _ = utils.Run(cmd) + + By("removing manager namespace") + cmd = exec.Command("kubectl", "delete", "ns", namespace) + _, _ = utils.Run(cmd) + }) + + // After each test, check for failures and collect logs, events, + // and pod descriptions for debugging. + AfterEach(func() { + specReport := CurrentSpecReport() + if specReport.Failed() { + By("Fetching controller manager pod logs") + cmd := exec.Command("kubectl", "logs", controllerPodName, "-n", namespace) + controllerLogs, err := utils.Run(cmd) + if err == nil { + _, _ = fmt.Fprintf(GinkgoWriter, "Controller logs:\n %s", controllerLogs) + } else { + _, _ = fmt.Fprintf(GinkgoWriter, "Failed to get Controller logs: %s", err) + } + + By("Fetching Kubernetes events") + cmd = exec.Command("kubectl", "get", "events", "-n", namespace, "--sort-by=.lastTimestamp") + eventsOutput, err := utils.Run(cmd) + if err == nil { + _, _ = fmt.Fprintf(GinkgoWriter, "Kubernetes events:\n%s", eventsOutput) + } else { + _, _ = fmt.Fprintf(GinkgoWriter, "Failed to get Kubernetes events: %s", err) + } + + By("Fetching curl-metrics logs") + cmd = exec.Command("kubectl", "logs", "curl-metrics", "-n", namespace) + metricsOutput, err := utils.Run(cmd) + if err == nil { + _, _ = fmt.Fprintf(GinkgoWriter, "Metrics logs:\n %s", metricsOutput) + } else { + _, _ = fmt.Fprintf(GinkgoWriter, "Failed to get curl-metrics logs: %s", err) + } + + By("Fetching controller manager pod description") + cmd = exec.Command("kubectl", "describe", "pod", controllerPodName, "-n", namespace) + podDescription, err := utils.Run(cmd) + if err == nil { + fmt.Println("Pod description:\n", podDescription) + } else { + fmt.Println("Failed to describe controller pod") + } + } + }) + + SetDefaultEventuallyTimeout(2 * time.Minute) + SetDefaultEventuallyPollingInterval(time.Second) + + Context("Manager", func() { + It("should run successfully", func() { + By("validating that the controller-manager pod is running as expected") + verifyControllerUp := func(g Gomega) { + // Get the name of the controller-manager pod + cmd := exec.Command("kubectl", "get", + "pods", "-l", "control-plane=controller-manager", + "-o", "go-template={{ range .items }}"+ + "{{ if not .metadata.deletionTimestamp }}"+ + "{{ .metadata.name }}"+ + "{{ \"\\n\" }}{{ end }}{{ end }}", + "-n", namespace, + ) + + podOutput, err := utils.Run(cmd) + g.Expect(err).NotTo(HaveOccurred(), "Failed to retrieve controller-manager pod information") + podNames := utils.GetNonEmptyLines(podOutput) + g.Expect(podNames).To(HaveLen(1), "expected 1 controller pod running") + controllerPodName = podNames[0] + g.Expect(controllerPodName).To(ContainSubstring("controller-manager")) + + // Validate the pod's status + cmd = exec.Command("kubectl", "get", + "pods", controllerPodName, "-o", "jsonpath={.status.phase}", + "-n", namespace, + ) + output, err := utils.Run(cmd) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(output).To(Equal("Running"), "Incorrect controller-manager pod status") + } + Eventually(verifyControllerUp).Should(Succeed()) + }) + + It("should ensure the metrics endpoint is serving metrics", func() { + By("creating a ClusterRoleBinding for the service account to allow access to metrics") + cmd := exec.Command("kubectl", "create", "clusterrolebinding", metricsRoleBindingName, + "--clusterrole=postgresql-operator-metrics-reader", + fmt.Sprintf("--serviceaccount=%s:%s", namespace, serviceAccountName), + ) + _, err := utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Failed to create ClusterRoleBinding") + + By("validating that the metrics service is available") + cmd = exec.Command("kubectl", "get", "service", metricsServiceName, "-n", namespace) + _, err = utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Metrics service should exist") + + By("getting the service account token") + token, err := serviceAccountToken() + Expect(err).NotTo(HaveOccurred()) + Expect(token).NotTo(BeEmpty()) + + By("waiting for the metrics endpoint to be ready") + verifyMetricsEndpointReady := func(g Gomega) { + cmd := exec.Command("kubectl", "get", "endpoints", metricsServiceName, "-n", namespace) + output, err := utils.Run(cmd) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(output).To(ContainSubstring("8443"), "Metrics endpoint is not ready") + } + Eventually(verifyMetricsEndpointReady).Should(Succeed()) + + By("verifying that the controller manager is serving the metrics server") + verifyMetricsServerStarted := func(g Gomega) { + cmd := exec.Command("kubectl", "logs", controllerPodName, "-n", namespace) + output, err := utils.Run(cmd) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(output).To(ContainSubstring("controller-runtime.metrics\",\"msg\":\"Serving metrics server"), + "Metrics server not yet started") + } + Eventually(verifyMetricsServerStarted).Should(Succeed()) + + By("creating the curl-metrics pod to access the metrics endpoint") + cmd = exec.Command("kubectl", "run", "curl-metrics", "--restart=Never", + "--namespace", namespace, + "--image=curlimages/curl:latest", + "--overrides", + fmt.Sprintf(`{ + "spec": { + "containers": [{ + "name": "curl", + "image": "curlimages/curl:latest", + "command": ["/bin/sh", "-c"], + "args": ["curl -v -k -H 'Authorization: Bearer %s' https://%s.%s.svc.cluster.local:8443/metrics"], + "securityContext": { + "allowPrivilegeEscalation": false, + "capabilities": { + "drop": ["ALL"] + }, + "runAsNonRoot": true, + "runAsUser": 1000, + "seccompProfile": { + "type": "RuntimeDefault" + } + } + }], + "serviceAccount": "%s" + } + }`, token, metricsServiceName, namespace, serviceAccountName)) + _, err = utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Failed to create curl-metrics pod") + + By("waiting for the curl-metrics pod to complete.") + verifyCurlUp := func(g Gomega) { + cmd := exec.Command("kubectl", "get", "pods", "curl-metrics", + "-o", "jsonpath={.status.phase}", + "-n", namespace) + output, err := utils.Run(cmd) + g.Expect(err).NotTo(HaveOccurred()) + g.Expect(output).To(Equal("Succeeded"), "curl pod in wrong status") + } + Eventually(verifyCurlUp, 5*time.Minute).Should(Succeed()) + + By("getting the metrics by checking curl-metrics logs") + metricsOutput := getMetricsOutput() + Expect(metricsOutput).To(ContainSubstring( + "controller_runtime_reconcile_total", + )) + }) + + // +kubebuilder:scaffold:e2e-webhooks-checks + + // TODO: Customize the e2e test suite with scenarios specific to your project. + // Consider applying sample/CR(s) and check their status and/or verifying + // the reconciliation by using the metrics, i.e.: + // metricsOutput := getMetricsOutput() + // Expect(metricsOutput).To(ContainSubstring( + // fmt.Sprintf(`controller_runtime_reconcile_total{controller="%s",result="success"} 1`, + // strings.ToLower(), + // )) + }) +}) + +// serviceAccountToken returns a token for the specified service account in the given namespace. +// It uses the Kubernetes TokenRequest API to generate a token by directly sending a request +// and parsing the resulting token from the API response. +func serviceAccountToken() (string, error) { + const tokenRequestRawString = `{ + "apiVersion": "authentication.k8s.io/v1", + "kind": "TokenRequest" + }` + + // Temporary file to store the token request + secretName := serviceAccountName + "-token-request" + tokenRequestFile := filepath.Join("/tmp", secretName) + + err := os.WriteFile(tokenRequestFile, []byte(tokenRequestRawString), os.FileMode(0o644)) + if err != nil { + return "", err + } + + var out string + + verifyTokenCreation := func(g Gomega) { + // Execute kubectl command to create the token + cmd := exec.Command("kubectl", "create", "--raw", fmt.Sprintf( + "/api/v1/namespaces/%s/serviceaccounts/%s/token", + namespace, + serviceAccountName, + ), "-f", tokenRequestFile) + + output, err := cmd.CombinedOutput() + g.Expect(err).NotTo(HaveOccurred()) + + // Parse the JSON output to extract the token + var token tokenRequest + + err = json.Unmarshal(output, &token) + g.Expect(err).NotTo(HaveOccurred()) + + out = token.Status.Token + } + Eventually(verifyTokenCreation).Should(Succeed()) + + return out, err +} + +// getMetricsOutput retrieves and returns the logs from the curl pod used to access the metrics endpoint. +func getMetricsOutput() string { + By("getting the curl-metrics logs") + + cmd := exec.Command("kubectl", "logs", "curl-metrics", "-n", namespace) + metricsOutput, err := utils.Run(cmd) + Expect(err).NotTo(HaveOccurred(), "Failed to retrieve logs from curl pod") + Expect(metricsOutput).To(ContainSubstring("< HTTP/1.1 200 OK")) + + return metricsOutput +} + +// tokenRequest is a simplified representation of the Kubernetes TokenRequest API response, +// containing only the token field that we need to extract. +type tokenRequest struct { + Status struct { + Token string `json:"token"` + } `json:"status"` +} diff --git a/test/utils/utils.go b/test/utils/utils.go new file mode 100644 index 0000000..9798455 --- /dev/null +++ b/test/utils/utils.go @@ -0,0 +1,272 @@ +/* +Copyright 2026. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package utils + +import ( + "bufio" + "bytes" + "fmt" + "os" + "os/exec" + "strings" + + //nolint:staticcheck + . "github.com/onsi/ginkgo/v2" +) + +const ( + prometheusOperatorVersion = "v0.77.1" + prometheusOperatorURL = "https://github.com/prometheus-operator/prometheus-operator/" + + "releases/download/%s/bundle.yaml" + + certmanagerVersion = "v1.16.3" + certmanagerURLTmpl = "https://github.com/cert-manager/cert-manager/releases/download/%s/cert-manager.yaml" +) + +func warnError(err error) { + _, _ = fmt.Fprintf(GinkgoWriter, "warning: %v\n", err) +} + +// Run executes the provided command within this context. +func Run(cmd *exec.Cmd) (string, error) { + dir, _ := GetProjectDir() + cmd.Dir = dir + + if err := os.Chdir(cmd.Dir); err != nil { + _, _ = fmt.Fprintf(GinkgoWriter, "chdir dir: %q\n", err) + } + + cmd.Env = append(os.Environ(), "GO111MODULE=on") + command := strings.Join(cmd.Args, " ") + _, _ = fmt.Fprintf(GinkgoWriter, "running: %q\n", command) + + output, err := cmd.CombinedOutput() + if err != nil { + return string(output), fmt.Errorf("%q failed with error %q: %w", command, string(output), err) + } + + return string(output), nil +} + +// InstallPrometheusOperator installs the prometheus Operator to be used to export the enabled metrics. +func InstallPrometheusOperator() error { + url := fmt.Sprintf(prometheusOperatorURL, prometheusOperatorVersion) + cmd := exec.Command("kubectl", "create", "-f", url) + _, err := Run(cmd) + + return err +} + +// UninstallPrometheusOperator uninstalls the prometheus. +func UninstallPrometheusOperator() { + url := fmt.Sprintf(prometheusOperatorURL, prometheusOperatorVersion) + + cmd := exec.Command("kubectl", "delete", "-f", url) + if _, err := Run(cmd); err != nil { + warnError(err) + } +} + +// IsPrometheusCRDsInstalled checks if any Prometheus CRDs are installed +// by verifying the existence of key CRDs related to Prometheus. +func IsPrometheusCRDsInstalled() bool { + // List of common Prometheus CRDs + prometheusCRDs := []string{ + "prometheuses.monitoring.coreos.com", + "prometheusrules.monitoring.coreos.com", + "prometheusagents.monitoring.coreos.com", + } + + cmd := exec.Command("kubectl", "get", "crds", "-o", "custom-columns=NAME:.metadata.name") + + output, err := Run(cmd) + if err != nil { + return false + } + + crdList := GetNonEmptyLines(output) + for _, crd := range prometheusCRDs { + for _, line := range crdList { + if strings.Contains(line, crd) { + return true + } + } + } + + return false +} + +// UninstallCertManager uninstalls the cert manager. +func UninstallCertManager() { + url := fmt.Sprintf(certmanagerURLTmpl, certmanagerVersion) + + cmd := exec.Command("kubectl", "delete", "-f", url) + if _, err := Run(cmd); err != nil { + warnError(err) + } +} + +// InstallCertManager installs the cert manager bundle. +func InstallCertManager() error { + url := fmt.Sprintf(certmanagerURLTmpl, certmanagerVersion) + + cmd := exec.Command("kubectl", "apply", "-f", url) + if _, err := Run(cmd); err != nil { + return err + } + // Wait for cert-manager-webhook to be ready, which can take time if cert-manager + // was re-installed after uninstalling on a cluster. + cmd = exec.Command("kubectl", "wait", "deployment.apps/cert-manager-webhook", + "--for", "condition=Available", + "--namespace", "cert-manager", + "--timeout", "5m", + ) + + _, err := Run(cmd) + + return err +} + +// IsCertManagerCRDsInstalled checks if any Cert Manager CRDs are installed +// by verifying the existence of key CRDs related to Cert Manager. +func IsCertManagerCRDsInstalled() bool { + // List of common Cert Manager CRDs + certManagerCRDs := []string{ + "certificates.cert-manager.io", + "issuers.cert-manager.io", + "clusterissuers.cert-manager.io", + "certificaterequests.cert-manager.io", + "orders.acme.cert-manager.io", + "challenges.acme.cert-manager.io", + } + + // Execute the kubectl command to get all CRDs + cmd := exec.Command("kubectl", "get", "crds") + + output, err := Run(cmd) + if err != nil { + return false + } + + // Check if any of the Cert Manager CRDs are present + crdList := GetNonEmptyLines(output) + for _, crd := range certManagerCRDs { + for _, line := range crdList { + if strings.Contains(line, crd) { + return true + } + } + } + + return false +} + +// LoadImageToKindClusterWithName loads a local docker image to the kind cluster. +func LoadImageToKindClusterWithName(name string) error { + cluster := "kind" + if v, ok := os.LookupEnv("KIND_CLUSTER"); ok { + cluster = v + } + + kindOptions := []string{"load", "docker-image", name, "--name", cluster} + cmd := exec.Command("kind", kindOptions...) + _, err := Run(cmd) + + return err +} + +// GetNonEmptyLines converts given command output string into individual objects +// according to line breakers, and ignores the empty elements in it. +func GetNonEmptyLines(output string) []string { + var res []string + + elements := strings.SplitSeq(output, "\n") + for element := range elements { + if element != "" { + res = append(res, element) + } + } + + return res +} + +// GetProjectDir will return the directory where the project is. +func GetProjectDir() (string, error) { + wd, err := os.Getwd() + if err != nil { + return wd, fmt.Errorf("failed to get current working directory: %w", err) + } + + wd = strings.ReplaceAll(wd, "/test/e2e", "") + + return wd, nil +} + +// UncommentCode searches for target in the file and remove the comment prefix +// of the target content. The target content may span multiple lines. +func UncommentCode(filename, target, prefix string) error { + // false positive + content, err := os.ReadFile(filename) + if err != nil { + return fmt.Errorf("failed to read file %q: %w", filename, err) + } + + strContent := string(content) + + idx := strings.Index(strContent, target) + if idx < 0 { + return fmt.Errorf("unable to find the code %q to be uncomment", target) + } + + out := new(bytes.Buffer) + + _, err = out.Write(content[:idx]) + if err != nil { + return fmt.Errorf("failed to write to output: %w", err) + } + + scanner := bufio.NewScanner(bytes.NewBufferString(target)) + if !scanner.Scan() { + return nil + } + + for { + if _, err = out.WriteString(strings.TrimPrefix(scanner.Text(), prefix)); err != nil { + return fmt.Errorf("failed to write to output: %w", err) + } + // Avoid writing a newline in case the previous line was the last in target. + if !scanner.Scan() { + break + } + + if _, err = out.WriteString("\n"); err != nil { + return fmt.Errorf("failed to write to output: %w", err) + } + } + + if _, err = out.Write(content[idx+len(target):]); err != nil { + return fmt.Errorf("failed to write to output: %w", err) + } + + // false positive + + if err = os.WriteFile(filename, out.Bytes(), 0o644); err != nil { + return fmt.Errorf("failed to write file %q: %w", filename, err) + } + + return nil +}