diff --git a/.github/workflows/update-container-images.yaml b/.github/workflows/update-container-images.yaml deleted file mode 100644 index ddbe0fdd..00000000 --- a/.github/workflows/update-container-images.yaml +++ /dev/null @@ -1,95 +0,0 @@ -name: Update container images - -on: - schedule: - # Update the container images every morning at 05:00 (or 04:00, depending on whether daylight - # savings time is on or not) CEST; skip Saturdays and Sundays. The hour in the expression below - # is 03 because the timezone is in UTC. - - cron: '0 3 * * 1-5' - -jobs: - get-branches-to-update-container-images: - runs-on: ubuntu-latest - outputs: - branches: ${{ steps.get-branches-to-update-container-images.outputs.branches }} - steps: - - name: Checkout this repo - uses: actions/checkout@v2 - with: - # Use fetch-depth of 0 to fetch all history for all branches. Otherwise, only the tip of - # the default branch is fetched while this job needs to get all branches whose name - # matches a certain pattern. - fetch-depth: '0' - - name: Get branches to update container images - id: get-branches-to-update-container-images - run: | - branches_to_update_container_images=$(git -P branch --remotes --list 'origin/update_container_images_*') && \ - echo "::set-output name=branches::${branches_to_update_container_images}" - - update-container-images: - runs-on: ubuntu-latest - needs: get-branches-to-update-container-images - # Proceed to create a new branch and a PR to update container images (if there are any updates) - # if and only if there aren't already open branches to do that. The idea is that if there's an - # already open branch it should be merged first, for example to avoid cumulating branches during - # holidays, when no one will merge the PRs from said branches. - if: ${{ needs.get-branches-to-update-container-images.outputs.branches == '' }} - steps: - - name: Configure AWS credentials - uses: aws-actions/configure-aws-credentials@v1 - with: - aws-access-key-id: ${{ secrets.A8S_ECR_AWS_ACCESS_KEY_ID }} - aws-secret-access-key: ${{ secrets.A8S_ECR_AWS_SECRET_ACCESS_KEY }} - aws-region: eu-central-1 - - name: Store most recent version tag for each image - env: - REPO_NAMESPACE: a9s-ds-for-k8s/dev - IMAGES: "postgresql-operator service-binding-controller backup-manager fluentd opensearch-dashboards" - run: | - VERSIONED_IMAGES="" - for IMG in ${IMAGES[*]} - do - read NEW_VERSION <<< $(aws ecr-public describe-images \ - --repository-name $REPO_NAMESPACE/$IMG \ - --query 'sort_by(imageDetails, &imagePushedAt)[-1].imageTags[0]' \ - --output text \ - --region us-east-1) - if [ -z "${NEW_VERSION}" ] || [ "${NEW_VERSION}" == "None" ]; then - echo "Found no images in repo $REPO_NAMESPACE/$IMG; should never happen" - exit 1 - fi - VERSIONED_IMAGES+="$IMG:$NEW_VERSION " - done - echo "VERSIONED_IMAGES=$VERSIONED_IMAGES" >> $GITHUB_ENV - - name: Store current date to use it as a suffix for the name of the branch to update images - id: get-date - run: echo "::set-output name=date::$(date +'%d_%m_%Y')" - - name: Checkout this repo - uses: actions/checkout@v2 - - name: Update images if needed - run: | - git config user.name github-actions && \ - git config user.email github-actions@github.com && \ - chmod +x automation-scripts/update-container-images.sh && \ - automation-scripts/update-container-images.sh "${{ env.VERSIONED_IMAGES }}" - - name: Create pull request if at least one image was updated - uses: peter-evans/create-pull-request@v3 - with: - delete-branch: true - branch: 'update_container_images_${{ steps.get-date.outputs.date }}' - # Strictly speaking this is not needed since 'develop' is picked as the base branch by - # default when the action runs in github. But when testing the action locally on a - # different branch that branch is picked instead, while we still want 'develop' as base. - # So we set the parameter explicitly to ease local testing. - base: 'develop' - author: 'github-actions ' - title: 'Update Container Images' - body: | - Automated update of container images to new available versions. - - For the a8s core components (postgresql operator, backup manager, service binding - controller), please check that a github release with the same name as the new version - exists in the repo of the relevant component. If that's not the case, it's likely - because the new image was pushed spuriously during testing by a developer who forgot to - delete it, so it's not a legitimate image and should NOT be used; remove the - corresponding commit from this PR and delete the image from ECR. diff --git a/CHANGELOG.md b/CHANGELOG.md index 8775d75f..221480e8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,73 @@ All notable changes to the a9s Dataservices on Kubernetes will be documented here, the format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). +## [0.3.0] - 2023-05-15 +### Migration Instructions +* If you use the extensions feature, delete the stateful set objects (**not the PostgreSQL objects**) +for instances that use extensions. Your data will be preserved, and the stateful sets will +automatically be recreated. +* Remove old finalizer `postgresql.operator.a8s.anynines.com` from all PostgreSQL objects. +* Migrate all `v1alpha1` PostgreSQL custom resources to `v1beta3` before migrating to v0.3.0, as PostgreSQL +version `v1alpha1` will not be available in v0.3.0. + +### Added + +* Protect PostgreSQL secrets against accidental or unwanted deletion by adding a finalizer. +* Protect ServiceBinding secret against accidental or unwanted deletion by adding a finalizer. +* Add optional read-only service to the PostgreSQL-Operator. The read-only service can be used + to distribute the load of read operations across the PostgreSQL instance. It can be enabled via + the optional `enableReadOnlyService` field on the Custom Resource. +* Add field `spec.resources.claims` to the PostgreSQL CRD to allow configuring + dynamic resources for the PostgreSQL instance Pods. This feature is in alpha, + is implemented only on K8s clusters with version 1.26 or higher and the + feature gate DynamicResourceAllocation enabled. +* Add field `spec.expose` to expose a PostgreSQL instance to outside the K8s cluster where it runs. + +### Changed + +* Updated description of `NamespacedName` in servicebinding. +* Deletion of PostgreSQL instance pods now runs in parallel, improving the deletion time in high + availability setups. +* In the Postgresql CRD, the description of the `namespaceSelector` field (which is one of the many + fields that control pod affinity and anti-affinity) has been updated to reflect the fact that the + field has graduated from beta to stable (in Kubernetes v1.24). +* **Breaking change:** PostgreSQL CRD has been updated with the following: + * `spec.parameters.maxConnections` has a minimum value of 1 and a maximum value + of 262143 enforced. + * `spec.parameters.maxReplicationSlots` has a minimum value of 0 and a maximum + value of 262143 enforced. + * `spec.parameters.maxWALSenders` has a minimum value of 0 and a maximum value + of 262143 enforced. + * `spec.parameters.sharedBuffers` has a minimum value of 16 and a maximum value + of 1073741823 enforced. + * `spec.parameters.statementTimeoutMillis` has a minimum value of 0 and a + maximum value of 2147483647 enforced. + > Attempts to create a PostgreSQL API object with one or more values not + compliant with the min and max listed above will be rejected with an error by + the K8s API server. +* **Breaking change**: Postgresql-controllers finalizer has been updated from + `postgresql.operator.a8s.anynines.com` to `a8s.anynines.com/postgresql.operator`. +* **Breaking change**: Postgresql-operator now uses an emptyDir instead of a persistent volume. +* **Breaking change**: The field `postgresConfiguration` has been renamed to + `parameters` in API Version `v1beta3`. +* Make the secrets that store the credentials of the admin and replication roles + of each PostgreSQL instance immutable. Preventing changes to credentials + protects against accidental (or unwanted) updates that could cause service + outages and improves performance by significantly reducing load on + kube-apiserver for clusters that make extensive use of secrets. +* Make service binding secrets immutable. Preventing changes to credentials + protects against accidental (or unwanted) updates that could cause service + outages and improves performance by significantly reducing load on + kube-apiserver for clusters that make extensive use of secrets. +* The backup\_agent has been updated, and is now using a smaller image. The new version of the + backup\_agent logs to stderr instead of the previously used stdout. +* Defaulting webhooks have been moved to API version `v1beta3`. + +### Removed + +* **Breaking change:** PostgreSQL version `v1alpha1` has been removed and is replaced by version +`v1beta3`. + ## [0.2.0] - 2022-11-08 ### Added diff --git a/automation-scripts/update-container-images.sh b/automation-scripts/update-container-images.sh deleted file mode 100755 index e0c2f4e2..00000000 --- a/automation-scripts/update-container-images.sh +++ /dev/null @@ -1,116 +0,0 @@ -#!/bin/bash - -set -o errexit -set -o nounset -set -o pipefail - -# This script is invoked by a github action that runs within an ubuntu VM. But it might be invoked -# manually by developers on their macos work laptop for testing. This is problematic because the -# script uses "sed", and sed's syntax is different between linux and macos. So we assume that the -# developer testing from macos also has "gsed" (the macos version of linux's sed; i.e. gsed on -# macos has the same syntax as sed on linux) installed on his machine. The following if selects -# gsed as the command to use if this script runs on macos, while picks "sed" otherwise (it assumes -# that if the OS isn't macos then it's linux). -SED="sed" -if [[ $OSTYPE == 'darwin'* ]] -then - SED="gsed" -fi -readonly SED - -# new_version_is_newer assumes that the two versions that it receives as arguments are in the -# same format, and will fail (in some cases, silently) if that's not the case. -new_version_is_newer () { - # Replace ".", "-" and "v" with " " in the versions so that it becomes easier to compare each - # version token from the new version to the corresponding token from the current version. What - # do I mean by token? For example I see a semver 2 version as: - # v... - local NEW_VERSION=$($SED "s/[\.v-]/ /g" <<< $1) - local CURRENT_VERSION=$($SED "s/[\.v-]/ /g" <<< $2) - - # From a string containing all the tokens of a version to an array where each item represents - # a single token (in descending order of priority), to ease comparison between new and current - # version. - local NEW_VERSION_TOKENS=( $NEW_VERSION ) - local CURRENT_VERSION_TOKENS=( $CURRENT_VERSION ) - - # Now, compare each token between new and current version in descending order of priority, to - # establish which version is newer. - for i in "${!NEW_VERSION_TOKENS[@]}" - do - if [ ${NEW_VERSION_TOKENS[$i]} -gt ${CURRENT_VERSION_TOKENS[$i]} ] - then - return 0 - fi - if [ ${NEW_VERSION_TOKENS[$i]} -lt ${CURRENT_VERSION_TOKENS[$i]} ] - then - return 1 - fi - done - - return 1 -} - -ensure_image_is_fresh_and_commit () { - local IMG=$1 - local NEW_VERSION=$2 - local MANIFEST=$3 - - # Prepare sed expression to extract the current version of the image from its yaml manifest. - # The regexp isn't strict: it matches the image version, but it'll match also incorrect - # formats. I started with an extremely precise regexp but it was overly long and complex, so I - # opted for allowing some incorrect formats for simplicity's sake. Since we control the parsed - # manifests we can have strong guarantees that the versions will be in the right formats, so - # there should be no issues. Notice that the group that captures the version matches more than - # just semver 2 versions, because we have some images (fluentd and opensearch-dashboards) that - # don't follow semver 2. - local GET_VERSION_SED_CMD="s/^[[:space:]-]\{1,\}image:[[:space:]].\{1,\}\/$IMG:\(v[\.[:digit:]-]\{1,\}\)\"\{0,1\}$/\1/p" - local CURRENT_VERSION=$($SED -n $GET_VERSION_SED_CMD $MANIFEST) - - if new_version_is_newer "$NEW_VERSION" "$CURRENT_VERSION" - then - # Prepare sed expression to update the version of the image in its yaml manifest. The regexp - # isn't strict: it matches the image version, but it'll match also incorrect formats. I - # started with an extremely precise regexp but it was overly long and complex, so I opted - # for allowing some incorrect formats for simplicity's sake. Since we control the parsed - # manifests we can have strong guarantees that the versions will be in the right formats, so - # there should be no issues. Notice that the group that captures the version matches more - # than just semver 2 versions, because we have some images (fluentd and - # opensearch-dashboards) that don't follow semver 2. - local UPDATE_VERSION_SED_CMD="s/^\([[:space:]-]\{1,\}image:[[:space:]].\{1,\}\/$IMG:\)v[\.[:digit:]-]\{1,\}\(\"\{0,1\}\)$/\1$NEW_VERSION\2/" - $SED -i $UPDATE_VERSION_SED_CMD $MANIFEST - git add "$MANIFEST" - git commit -m "Bump $IMG to $NEW_VERSION" - else - echo "Current version of $IMG is $CURRENT_VERSION, most recent version found is $NEW_VERSION, no update needed" - fi -} - -main () { - local VERSIONED_IMGS="$1" - for VERSIONED_IMG in $VERSIONED_IMGS - do - # Extract image name and version as separate variables - local IMG=$(echo $VERSIONED_IMG | cut -d ':' -f 1) - local NEW_VERSION=$(echo $VERSIONED_IMG | cut -d ':' -f 2) - - # Each image needs to be updated in a yaml manifest with an ad-hoc name (i.e. there's no - # regular pattern), so we have to branch and manually build the manifest name differently - # for each component. - if [[ "$IMG" == "fluentd" ]] - then - local MANIFEST="deploy/logging/collection-infrastructure/fluentd-aggregator.yaml" - elif [[ "$IMG" == "opensearch-dashboards" ]] - then - local MANIFEST="deploy/logging/dashboard/opensearch-dashboards.yaml" - else - local MANIFEST="deploy/a8s/$IMG.yaml" - fi - - # If needed, update the image version in the relevant yaml manifests and commit each update - # individually to easily pinpoint which update broke things in case tests fail. - ensure_image_is_fresh_and_commit $IMG $NEW_VERSION $MANIFEST - done -} - -main "$1" diff --git a/deploy/a8s/backup-config/kustomization.yaml b/deploy/a8s/backup-config/kustomization.yaml index 4999b8b3..36568cea 100644 --- a/deploy/a8s/backup-config/kustomization.yaml +++ b/deploy/a8s/backup-config/kustomization.yaml @@ -15,5 +15,3 @@ secretGenerator: - ./access-key-id - ./secret-access-key - ./encryption-password -generatorOptions: - disableNameSuffixHash: true diff --git a/deploy/a8s/manifests/backup-manager.yaml b/deploy/a8s/manifests/backup-manager.yaml index a805a5e0..d0558ca8 100644 --- a/deploy/a8s/manifests/backup-manager.yaml +++ b/deploy/a8s/manifests/backup-manager.yaml @@ -2,23 +2,9 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - cert-manager.io/inject-ca-from: a8s-system/a8s-backup-serving-cert controller-gen.kubebuilder.io/version: v0.4.1 - creationTimestamp: null name: backups.backups.anynines.com spec: - conversion: - strategy: Webhook - webhook: - clientConfig: - service: - name: a8s-backup-webhook-service - namespace: a8s-system - path: /convert - port: 443 - conversionReviewVersions: - - v1 - - v1beta1 group: backups.anynines.com names: kind: Backup @@ -29,164 +15,6 @@ spec: singular: backup scope: Namespaced versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: Backup is the Schema for the backups API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: BackupSpec defines the desired state of Backup. - properties: - maxRetries: - default: Infinite - description: 'How many times the backup will be retried before aborting. - Allowed values: any positive integer, or "Infinite"' - pattern: ^\d+|Infinite$ - type: string - serviceInstance: - description: ServiceInstance identifies the Data Service Instance - to backup. - properties: - apiGroup: - description: APIGroup is the API group of the Kubernetes API resource - that represents the Data Service Instance to backup or restore - (e.g. postgresql.anynines.com, redis.anynines.com, etc...). - type: string - kind: - description: Kind is the kind of the Kubernetes API resource that - represents the Data Service Instance to backup or restore (e.g. - Postgresql, Redis, etc...). - type: string - name: - description: Name is the name of the Kubernetes API resource that - represents the Data Service Instance to backup or restore. - type: string - required: - - apiGroup - - kind - - name - type: object - required: - - serviceInstance - type: object - status: - description: BackupStatus defines the observed state of Backup. - properties: - backupID: - description: BackupID is the ID of the Backup; clients can use this - to poll the status of the Backup at the Pod identified by `PodUsedID`. - type: string - conditions: - description: Conditions include a set of not mutually exclusive states - the Backup can be in, as well as the last observed time stamp for - these conditions. They include "Ready", "InProgress", "UploadedToS3", - "Terminating". - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - type FooStatus struct{ // Represents the observations of a - foo's current state. // Known .status.conditions.type are: - \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type - \ // +patchStrategy=merge // +listType=map // +listMapKey=type - \ Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` - \n // other fields }" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastObservationTime: - description: LastObservationTime is the timestamp of the last time - the Condition was observed to be true. - format: date-time - type: string - podUsedNamespacedName: - description: 'PodUsedNamespacedName is the namespaced name of the - DSI Pod to which the backup request was sent. TODO: Represent this - jointly with `PodUsedID` (below) via a PodRef.' - type: string - podUsedUID: - description: 'PodUsedUID is the UID of the DSI Pod to which the backup - request was sent. TODO: Represent this jointly with `PodUsedNamespacedName` - (above) via a PodRef.' - type: string - retries: - description: Number of times the backup has been retried - type: integer - type: object - required: - - spec - type: object - served: true - storage: false - subresources: - status: {} - name: v1beta3 schema: openAPIV3Schema: @@ -255,13 +83,14 @@ spec: description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, - type FooStatus struct{ // Represents the observations of a - foo's current state. // Known .status.conditions.type are: - \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type - \ // +patchStrategy=merge // +listType=map // +listMapKey=type - \ Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` - \n // other fields }" + \n \ttype FooStatus struct{ \t // Represents the observations + of a foo's current state. \t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\" \t // + +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map + \t // +listMapKey=type \t Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields + \t}" properties: lastTransitionTime: description: lastTransitionTime is the last time the condition @@ -356,23 +185,9 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - cert-manager.io/inject-ca-from: a8s-system/a8s-backup-serving-cert controller-gen.kubebuilder.io/version: v0.4.1 - creationTimestamp: null name: restores.backups.anynines.com spec: - conversion: - strategy: Webhook - webhook: - clientConfig: - service: - name: a8s-backup-webhook-service - namespace: a8s-system - path: /convert - port: 443 - conversionReviewVersions: - - v1 - - v1beta1 group: backups.anynines.com names: kind: Restore @@ -383,167 +198,6 @@ spec: singular: restore scope: Namespaced versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: Restore is the Schema for the restore API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: RestoreSpec defines the desired state of Restore. - properties: - backupName: - description: BackupName is the name of the Backup API object to use - for the Restore; the namespace is assumed to be the same as the - one for the Restore object, we might reconsider this assumption - in the future. - type: string - serviceInstance: - description: ServiceInstance identifies the Data Service Instance - to restore. - properties: - apiGroup: - description: APIGroup is the API group of the Kubernetes API resource - that represents the Data Service Instance to backup or restore - (e.g. postgresql.anynines.com, redis.anynines.com, etc...). - type: string - kind: - description: Kind is the kind of the Kubernetes API resource that - represents the Data Service Instance to backup or restore (e.g. - Postgresql, Redis, etc...). - type: string - name: - description: Name is the name of the Kubernetes API resource that - represents the Data Service Instance to backup or restore. - type: string - required: - - apiGroup - - kind - - name - type: object - required: - - backupName - - serviceInstance - type: object - status: - description: RestoreStatus defines the observed state of Restore. - properties: - conditions: - description: Conditions include a set of not mutually exclusive states - the Restore can be in, as well as the last observed time stamp for - these conditions. They include "Ready", "InProgress", "Terminating". - items: - description: "Condition contains details for one aspect of the current - state of this API Resource. --- This struct is intended for direct - use as an array at the field path .status.conditions. For example, - type FooStatus struct{ // Represents the observations of a - foo's current state. // Known .status.conditions.type are: - \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type - \ // +patchStrategy=merge // +listType=map // +listMapKey=type - \ Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` - \n // other fields }" - properties: - lastTransitionTime: - description: lastTransitionTime is the last time the condition - transitioned from one status to another. This should be when - the underlying condition changed. If that is not known, then - using the time when the API field changed is acceptable. - format: date-time - type: string - message: - description: message is a human readable message indicating - details about the transition. This may be an empty string. - maxLength: 32768 - type: string - observedGeneration: - description: observedGeneration represents the .metadata.generation - that the condition was set based upon. For instance, if .metadata.generation - is currently 12, but the .status.conditions[x].observedGeneration - is 9, the condition is out of date with respect to the current - state of the instance. - format: int64 - minimum: 0 - type: integer - reason: - description: reason contains a programmatic identifier indicating - the reason for the condition's last transition. Producers - of specific condition types may define expected values and - meanings for this field, and whether the values are considered - a guaranteed API. The value should be a CamelCase string. - This field may not be empty. - maxLength: 1024 - minLength: 1 - pattern: ^[A-Za-z]([A-Za-z0-9_,:]*[A-Za-z0-9_])?$ - type: string - status: - description: status of the condition, one of True, False, Unknown. - enum: - - "True" - - "False" - - Unknown - type: string - type: - description: type of condition in CamelCase or in foo.example.com/CamelCase. - --- Many .condition.type values are consistent across resources - like Available, but because arbitrary conditions can be useful - (see .node.status.conditions), the ability to deconflict is - important. The regex it matches is (dns1123SubdomainFmt/)?(qualifiedNameFmt) - maxLength: 316 - pattern: ^([a-z0-9]([-a-z0-9]*[a-z0-9])?(\.[a-z0-9]([-a-z0-9]*[a-z0-9])?)*/)?(([A-Za-z0-9][-A-Za-z0-9_.]*)?[A-Za-z0-9])$ - type: string - required: - - lastTransitionTime - - message - - reason - - status - - type - type: object - type: array - lastObservationTime: - description: LastObservationTime is the timestamp of the last time - the Condition was observed to be true. - format: date-time - type: string - podToPoll: - description: The Pod to poll to learn the status of the Restore, if - the restore is in Progress. - properties: - ip: - description: IP is the IP of the Pod. - type: string - namespacedName: - description: NamespacedName is the namespaced name of the Pod. - type: string - uid: - description: UID is the UID of the Pod. - type: string - required: - - ip - - namespacedName - - uid - type: object - restoreID: - description: RestoreID is the ID of the Restore; clients can use this - to poll the status of the Restore at the Pod identified by `PodToHit`. - type: string - type: object - type: object - served: true - storage: false - subresources: - status: {} - name: v1beta3 schema: openAPIV3Schema: @@ -608,13 +262,14 @@ spec: description: "Condition contains details for one aspect of the current state of this API Resource. --- This struct is intended for direct use as an array at the field path .status.conditions. For example, - type FooStatus struct{ // Represents the observations of a - foo's current state. // Known .status.conditions.type are: - \"Available\", \"Progressing\", and \"Degraded\" // +patchMergeKey=type - \ // +patchStrategy=merge // +listType=map // +listMapKey=type - \ Conditions []metav1.Condition `json:\"conditions,omitempty\" - patchStrategy:\"merge\" patchMergeKey:\"type\" protobuf:\"bytes,1,rep,name=conditions\"` - \n // other fields }" + \n \ttype FooStatus struct{ \t // Represents the observations + of a foo's current state. \t // Known .status.conditions.type + are: \"Available\", \"Progressing\", and \"Degraded\" \t // + +patchMergeKey=type \t // +patchStrategy=merge \t // +listType=map + \t // +listMapKey=type \t Conditions []metav1.Condition + `json:\"conditions,omitempty\" patchStrategy:\"merge\" patchMergeKey:\"type\" + protobuf:\"bytes,1,rep,name=conditions\"` \n \t // other fields + \t}" properties: lastTransitionTime: description: lastTransitionTime is the last time the condition @@ -941,21 +596,6 @@ spec: app.kubernetes.io/name: backup-manager app.kubernetes.io/part-of: a8s-backup --- -apiVersion: v1 -kind: Service -metadata: - name: a8s-backup-webhook-service - namespace: a8s-system -spec: - ports: - - port: 443 - protocol: TCP - targetPort: 9443 - selector: - app.kubernetes.io/component: controller-manager - app.kubernetes.io/name: backup-manager - app.kubernetes.io/part-of: a8s-backup ---- apiVersion: apps/v1 kind: Deployment metadata: @@ -996,7 +636,7 @@ spec: fieldPath: metadata.namespace - name: BACKUP_CREDENTIAL_PATH value: /etc/backup-store-secrets - image: public.ecr.aws/w5n9a2g2/a9s-ds-for-k8s/dev/backup-manager:878e5ef789335feada36e27dd40733e281536aa2 + image: public.ecr.aws/w5n9a2g2/a9s-ds-for-k8s/dev/backup-manager:2616f22c4fe670541c3c78131ae018902f8471bf livenessProbe: httpGet: path: /healthz @@ -1004,10 +644,6 @@ spec: initialDelaySeconds: 15 periodSeconds: 20 name: manager - ports: - - containerPort: 9443 - name: webhook-server - protocol: TCP readinessProbe: httpGet: path: /readyz @@ -1024,9 +660,6 @@ spec: securityContext: allowPrivilegeEscalation: false volumeMounts: - - mountPath: /tmp/k8s-webhook-server/serving-certs - name: cert - readOnly: true - mountPath: /etc/config name: backup-store-config-volume - mountPath: /etc/backup-store-secrets @@ -1046,10 +679,6 @@ spec: serviceAccountName: a8s-backup-manager-account terminationGracePeriodSeconds: 10 volumes: - - name: cert - secret: - defaultMode: 420 - secretName: bkp-webhook-server-cert - configMap: items: - key: backup-store-config.yaml @@ -1059,25 +688,3 @@ spec: - name: secret-volume secret: secretName: a8s-backup-storage-credentials ---- -apiVersion: cert-manager.io/v1 -kind: Certificate -metadata: - name: a8s-backup-serving-cert - namespace: a8s-system -spec: - dnsNames: - - a8s-backup-webhook-service.a8s-system.svc - - a8s-backup-webhook-service.a8s-system.svc.cluster.local - issuerRef: - kind: Issuer - name: a8s-backup-selfsigned-issuer - secretName: bkp-webhook-server-cert ---- -apiVersion: cert-manager.io/v1 -kind: Issuer -metadata: - name: a8s-backup-selfsigned-issuer - namespace: a8s-system -spec: - selfSigned: {} diff --git a/deploy/a8s/manifests/postgresql-images.yaml b/deploy/a8s/manifests/postgresql-images.yaml index 1b0f6437..cd6ca37b 100644 --- a/deploy/a8s/manifests/postgresql-images.yaml +++ b/deploy/a8s/manifests/postgresql-images.yaml @@ -1,10 +1,11 @@ apiVersion: v1 data: - backupAgentImage: public.ecr.aws/w5n9a2g2/a9s-ds-for-k8s/dev/backup-agent:726a6660fa911e554356d6d3dbe929e5485f79d7 - spiloImage: registry.opensource.zalan.do/acid/spilo-14:2.1-p6 - extensionImageRegistry: public.ecr.aws/w5n9a2g2/a9s-ds-for-k8s/dev/postgresql-operator/postgresql-extensions + backupAgentImage: public.ecr.aws/w5n9a2g2/a9s-ds-for-k8s/dev/backup-agent:781ca7b6e9bb4af66aeedb5046b50dc1c2b6c638 extensionCleanupImage: alpine:3.16.0 + extensionImageRegistry: public.ecr.aws/w5n9a2g2/a9s-ds-for-k8s/dev/postgresql-operator/postgresql-extensions + spiloImage: ghcr.io/zalando/spilo-15:3.0-p1 kind: ConfigMap metadata: name: postgresql-images namespace: a8s-system + diff --git a/deploy/a8s/manifests/postgresql-operator.yaml b/deploy/a8s/manifests/postgresql-operator.yaml index 33ecff33..43b4d6ba 100644 --- a/deploy/a8s/manifests/postgresql-operator.yaml +++ b/deploy/a8s/manifests/postgresql-operator.yaml @@ -4,1313 +4,18 @@ metadata: annotations: cert-manager.io/inject-ca-from: a8s-system/postgresql-serving-cert controller-gen.kubebuilder.io/version: v0.4.1 - creationTimestamp: null - name: postgresqls.postgresql.anynines.com -spec: - conversion: - strategy: Webhook - webhook: - clientConfig: - service: - name: postgresql-webhook-service - namespace: a8s-system - path: /convert - conversionReviewVersions: - - v1 - - v1beta1 - group: postgresql.anynines.com - names: - kind: Postgresql - listKind: PostgresqlList - plural: postgresqls - shortNames: - - pg - singular: postgresql - scope: Namespaced - versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: Postgresql is the Schema for the postgresqls API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: PostgresqlSpec defines the desired state of Postgresql - properties: - extensions: - description: Extensions defines a list of PostgreSQL extensions which - should be installed. Installing means that the binaries and libraries - of the defined extensions are moved to the PostgreSQL extension - directory. The extensions are NOT loaded by default (i.e. by using - the PostgreSQL "CREATE EXTENSION" command). Updating the list of - extensions will cause a rolling update of the PostgreSQL instance. - items: - type: string - type: array - postgresConfiguration: - properties: - archiveTimeoutSeconds: - default: 0 - description: ArchiveTimeoutSeconds is the timeout in seconds which - defines the limit how old unarchived data can be, you can set - ArchiveTimeoutSeconds to force the server to switch to a new - WAL segment file periodically. When this parameter is greater - than zero, the server will switch to a new segment file whenever - this amount of time has elapsed since the last segment file - switch. - maximum: 1073741823 - minimum: 0 - type: integer - clientMinMessages: - default: notice - description: ClientMinMessages specifies which message levels - are sent to the client. - enum: - - debug5 - - debug4 - - debug3 - - debug2 - - debug1 - - log - - notice - - warning - - error - type: string - logErrorVerbosity: - default: DEFAULT - description: LogErrorVerbosity controls the amount of detail written - in the server log for each message that is logged. - enum: - - DEFAULT - - VERBOSE - - TERSE - type: string - logMinErrorStatement: - default: error - description: LogMinErrorStatement controls which SQL statements - that cause an error condition are recorded in the server log. - The current SQL statement is included in the log entry for any - message of the specified severity or higher. - enum: - - debug5 - - debug4 - - debug3 - - debug2 - - debug1 - - info - - notice - - warning - - error - - log - - fatal - - panic - type: string - logMinMessages: - default: warning - description: LogMinMessages controls which message levels are - written to the server log. - enum: - - debug5 - - debug4 - - debug3 - - debug2 - - debug1 - - info - - notice - - warning - - error - - log - - fatal - - panic - type: string - logStatement: - default: none - description: LogStatement controls which SQL statements are logged. - enum: - - none - - ddl - - mod - - all - type: string - maxConnections: - default: 100 - description: MaxConnections determines the maximum number of concurrent - connections to the database server. Updating MaxConnections - will trigger a restart of the PostgreSQL instance. - maximum: 262143 - type: integer - maxLocksPerTransaction: - description: MaxLocksPerTransaction sets the maximum number of - locks per transaction. The shared lock table tracks locks on - max_locks_per_transaction * (max_connections + max_prepared_transactions) - objects (e.g., tables); hence, no more than this many distinct - objects can be locked at any one time. This parameter controls - the average number of object locks allocated for each transaction; - individual transactions can lock more objects as long as the - locks of all transactions fit in the lock table. This is not - the number of rows that can be locked; that value is unlimited. - The default, 64, has historically proven sufficient, but you - might need to raise this value if you have queries that touch - many different tables in a single transaction, e.g., query of - a parent table with many children. Updating MaxLocksPerTransaction - will trigger a restart of the PostgreSQL instance. - maximum: 2147483647 - minimum: 10 - type: integer - maxReplicationSlots: - default: 10 - description: MaxReplicationSlots specifies the maximum number - of replication slots that the server can support. Updating MaxReplicationSlots - will trigger a restart of the PostgreSQL instance. - type: integer - maxWALSenders: - default: 10 - description: MaxWALSenders specifies the maximum number of concurrent - connections from standby servers or streaming base backup clients - (i.e., the maximum number of simultaneously running WAL sender - processes). The value 0 means replication is disabled. Abrupt - disconnection of a streaming client might leave an orphaned - connection slot behind until a timeout is reached, so this parameter - should be set slightly higher than the maximum number of expected - clients so disconnected clients can immediately reconnect. Updating - MaxWALSenders will trigger a restart of the PostgreSQL instance. - type: integer - sharedBuffers: - default: 1024 - description: SharedBuffers sets the amount of memory (usually - in 8KB) the database server uses for shared memory buffers. - If this value is specified without units, it is taken as blocks, - that is BLCKSZ bytes, typically 8kB. This setting must be at - least 128 kilobytes. However, settings significantly higher - than the minimum are usually needed for good performance. Updating - SharedBuffers will trigger a restart of the PostgreSQL instance. - type: integer - sslCiphers: - default: HIGH:MEDIUM:+3DES:!aNULL - description: SSLCiphers specifies the allowed SSL ciphers (https://www.postgresql.org/docs/13/runtime-config-connection.html#GUC-SSL-CIPHERS) - type: string - sslMinProtocolVersion: - default: TLSv1.2 - description: SSLMinProtocolVersion sets the minimum SSL/TLS protocol - version to use - enum: - - TLSv1 - - TLSv1.1 - - TLSv1.2 - - TLSv1.3 - type: string - statementTimeoutMillis: - default: 0 - description: StatementTimeoutMillis is the timeout in milliseconds - after which any statement that takes more than the specified - number is aborted. The counter is started from the time the - command arrives at the server from the client. If LogMinErrorStatement - statement is set to ERROR or lower, the statement that timed - out will also be logged. A value of zero (the default) turns - this off. - maximum: 2147483647 - type: integer - synchronousCommit: - default: "on" - description: SynchronousCommit specifies whether transaction commit - will wait for WAL records to be written to disk before the command - returns a success indication to the client. - enum: - - "off" - - local - - remote_write - - remote_apply - - "on" - type: string - tempFileLimitKiloBytes: - default: -1 - description: TempFileLimitKiloBytes specifies the maximum amount - of disk space that a process can use for temporary files, such - as sort and hash temporary files, or the storage file for a - held cursor. - maximum: 2147483647 - minimum: -1 - type: integer - trackIOTiming: - default: "off" - description: TrackIOTiming enables timing of database I/O calls. - This parameter is off by default, because it will repeatedly - query the operating system for the current time, which may cause - significant overhead on some platforms. - enum: - - "on" - - "off" - type: string - walWriterDelayMillis: - default: 200 - description: WALWriterDelayMillis specifies the time (in milliseconds) - between WAL flushed performed in the WAL writer. After flushing - WAL the writer sleeps for the length of time given by WALWriterDelayMillis, - unless woken up sooner by an asynchronously committing transaction. - maximum: 10000 - minimum: 1 - type: integer - type: object - replicas: - default: 1 - description: Replicas is the number of replicas of the data service - in the cluster. Replicas of the PostgreSQL resource will constitute - a streaming replication cluster. This value should be an odd number - (with the exception of the value 0) to ensure the resultant cluster - can establish quorum. Only scaling up is supported and not scaling - down of replicas. - format: int32 - minimum: 0 - type: integer - resources: - default: - limits: - cpu: "2" - memory: 2Gi - requests: - cpu: "2" - memory: 2Gi - description: Resources is the desired compute resource requirements - of PostgreSQL container within a pod in the cluster. Updating resources - causes the replicas of the PostgreSQL cluster to be killed and recreated - one at a time, which could potentially lead to downtime if something - goes wrong during the update. - properties: - limits: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Limits describes the maximum amount of compute resources - allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - requests: - additionalProperties: - anyOf: - - type: integer - - type: string - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - description: 'Requests describes the minimum amount of compute - resources required. If Requests is omitted for a container, - it defaults to Limits if that is explicitly specified, otherwise - to an implementation-defined value. More info: https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/' - type: object - type: object - schedulingConstraints: - description: SchedulingConstraints contains subfields that affect - how the Pods of the Postgresql instance will be scheduled onto Kubernetes - cluster nodes. The subfields map directly to Kubernetes API primitives - such as node taints, tolerations, affinity and (anti)affinity. See - the documentation of each subfield for more details. Together, the - subfields of SchedulingConstraints allow you to express constraints - such as "Pods of this Postgresql instance MUST be scheduled to different - availability zones", or "Pods of this Postgresql instance SHOULD - preferrably (but not mandatorily) be scheduled to nodes that have - a SSD", and many more. As a warning, the subfields of SchedulingConstraints - can interfere with each other, so when you set one of them you should - consider how it will interact with the values that you set for other - subfields. - properties: - affinity: - description: 'Affinity groups the fields that govern node affinity, - Pod affinity, and Pod anti affinity. It has exactly the same - syntax and semantics of the built-in Kubernetes affinity type: - https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#affinity-v1-core - . It is copied as it is in the spec of the Pods of the PostgreSQL - instance, and can be used to attract said Pods to specific Kubernetes - cluster nodes based on those nodes'' labels and on the labels - of other Pods running on the nodes. In the latter case repulsion - rules between Pods can also be configured via the PodAntiAffinity - field. An important use case for repulsion rules would be HA: - it can be ensured that the replicas of the same PostgreSQL instance - repel each other and end up on different nodes or even availability - zones. More information at https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity - . To properly configure this field you might need to know which - labels the DSI pods get. That''s the union of the DSI''s `metadata.labels`; - the a8s-reserved labels "a8s.a9s/dsi-name": "", - "a8s.a9s/dsi-group": "postgresql.anynines.com", "a8s.a9s/dsi-kind": - "Postgresql"; the label "a8s.a9s/replication-role", set to the - value "master" for the primary pod and "replica" for the standby - pods; the standard labels that Kubernetes adds to all pods managed - via a statefulset. Updating this field will result in re-creation - and re-scheduling of all the Pods of the PostgreSQL instance, - so *there may be downtime*.' - properties: - nodeAffinity: - description: Describes node affinity scheduling rules for - the pod. - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule pods - to nodes that satisfy the affinity expressions specified - by this field, but it may choose a node that violates - one or more of the expressions. The node that is most - preferred is the one with the greatest sum of weights, - i.e. for each node that meets all of the scheduling - requirements (resource request, requiredDuringScheduling - affinity expressions, etc.), compute a sum by iterating - through the elements of this field and adding "weight" - to the sum if the node matches the corresponding matchExpressions; - the node(s) with the highest sum are the most preferred. - items: - description: An empty preferred scheduling term matches - all objects with implicit weight 0 (i.e. it's a no-op). - A null preferred scheduling term matches no objects - (i.e. is also a no-op). - properties: - preference: - description: A node selector term, associated with - the corresponding weight. - properties: - matchExpressions: - description: A list of node selector requirements - by node's labels. - items: - description: A node selector requirement is - a selector that contains values, a key, - and an operator that relates the key and - values. - properties: - key: - description: The label key that the selector - applies to. - type: string - operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. - type: string - values: - description: An array of string values. - If the operator is In or NotIn, the - values array must be non-empty. If the - operator is Exists or DoesNotExist, - the values array must be empty. If the - operator is Gt or Lt, the values array - must have a single element, which will - be interpreted as an integer. This array - is replaced during a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - description: A list of node selector requirements - by node's fields. - items: - description: A node selector requirement is - a selector that contains values, a key, - and an operator that relates the key and - values. - properties: - key: - description: The label key that the selector - applies to. - type: string - operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. - type: string - values: - description: An array of string values. - If the operator is In or NotIn, the - values array must be non-empty. If the - operator is Exists or DoesNotExist, - the values array must be empty. If the - operator is Gt or Lt, the values array - must have a single element, which will - be interpreted as an integer. This array - is replaced during a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - weight: - description: Weight associated with matching the - corresponding nodeSelectorTerm, in the range 1-100. - format: int32 - type: integer - required: - - preference - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified by - this field are not met at scheduling time, the pod will - not be scheduled onto the node. If the affinity requirements - specified by this field cease to be met at some point - during pod execution (e.g. due to an update), the system - may or may not try to eventually evict the pod from - its node. - properties: - nodeSelectorTerms: - description: Required. A list of node selector terms. - The terms are ORed. - items: - description: A null or empty node selector term - matches no objects. The requirements of them are - ANDed. The TopologySelectorTerm type implements - a subset of the NodeSelectorTerm. - properties: - matchExpressions: - description: A list of node selector requirements - by node's labels. - items: - description: A node selector requirement is - a selector that contains values, a key, - and an operator that relates the key and - values. - properties: - key: - description: The label key that the selector - applies to. - type: string - operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. - type: string - values: - description: An array of string values. - If the operator is In or NotIn, the - values array must be non-empty. If the - operator is Exists or DoesNotExist, - the values array must be empty. If the - operator is Gt or Lt, the values array - must have a single element, which will - be interpreted as an integer. This array - is replaced during a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchFields: - description: A list of node selector requirements - by node's fields. - items: - description: A node selector requirement is - a selector that contains values, a key, - and an operator that relates the key and - values. - properties: - key: - description: The label key that the selector - applies to. - type: string - operator: - description: Represents a key's relationship - to a set of values. Valid operators - are In, NotIn, Exists, DoesNotExist. - Gt, and Lt. - type: string - values: - description: An array of string values. - If the operator is In or NotIn, the - values array must be non-empty. If the - operator is Exists or DoesNotExist, - the values array must be empty. If the - operator is Gt or Lt, the values array - must have a single element, which will - be interpreted as an integer. This array - is replaced during a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - type: object - type: array - required: - - nodeSelectorTerms - type: object - type: object - podAffinity: - description: Describes pod affinity scheduling rules (e.g. - co-locate this pod in the same node, zone, etc. as some - other pod(s)). - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule pods - to nodes that satisfy the affinity expressions specified - by this field, but it may choose a node that violates - one or more of the expressions. The node that is most - preferred is the one with the greatest sum of weights, - i.e. for each node that meets all of the scheduling - requirements (resource request, requiredDuringScheduling - affinity expressions, etc.), compute a sum by iterating - through the elements of this field and adding "weight" - to the sum if the node has pods which matches the corresponding - podAffinityTerm; the node(s) with the highest sum are - the most preferred. - items: - description: The weights of all of the matched WeightedPodAffinityTerm - fields are added per-node to find the most preferred - node(s) - properties: - podAffinityTerm: - description: Required. A pod affinity term, associated - with the corresponding weight. - properties: - labelSelector: - description: A label query over a set of resources, - in this case pods. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The requirements - are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. - type: string - values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied - to the union of the namespaces selected by - this field and the ones listed in the namespaces - field. null selector and null or empty namespaces - list means "this pod's namespace". An empty - selector ({}) matches all namespaces. This - field is beta-level and is only honored when - PodAffinityNamespaceSelector feature is enabled. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The requirements - are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. - type: string - values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - namespaces: - description: namespaces specifies a static list - of namespace names that the term applies to. - The term is applied to the union of the namespaces - listed in this field and the ones selected - by namespaceSelector. null or empty namespaces - list and null namespaceSelector means "this - pod's namespace" - items: - type: string - type: array - topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the - pods matching the labelSelector in the specified - namespaces, where co-located is defined as - running on a node whose value of the label - with key topologyKey matches that of any node - on which any of the selected pods is running. - Empty topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - weight: - description: weight associated with matching the - corresponding podAffinityTerm, in the range 1-100. - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - description: If the affinity requirements specified by - this field are not met at scheduling time, the pod will - not be scheduled onto the node. If the affinity requirements - specified by this field cease to be met at some point - during pod execution (e.g. due to a pod label update), - the system may or may not try to eventually evict the - pod from its node. When there are multiple elements, - the lists of nodes corresponding to each podAffinityTerm - are intersected, i.e. all terms must be satisfied. - items: - description: Defines a set of pods (namely those matching - the labelSelector relative to the given namespace(s)) - that this pod should be co-located (affinity) or not - co-located (anti-affinity) with, where co-located - is defined as running on a node whose value of the - label with key matches that of any node - on which a pod of the set of pods is running - properties: - labelSelector: - description: A label query over a set of resources, - in this case pods. - properties: - matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are - ANDed. - items: - description: A label selector requirement - is a selector that contains values, a key, - and an operator that relates the key and - values. - properties: - key: - description: key is the label key that - the selector applies to. - type: string - operator: - description: operator represents a key's - relationship to a set of values. Valid - operators are In, NotIn, Exists and - DoesNotExist. - type: string - values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. This - array is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is - "In", and the values array contains only "value". - The requirements are ANDed. - type: object - type: object - namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied - to the union of the namespaces selected by this - field and the ones listed in the namespaces field. - null selector and null or empty namespaces list - means "this pod's namespace". An empty selector - ({}) matches all namespaces. This field is beta-level - and is only honored when PodAffinityNamespaceSelector - feature is enabled. - properties: - matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are - ANDed. - items: - description: A label selector requirement - is a selector that contains values, a key, - and an operator that relates the key and - values. - properties: - key: - description: key is the label key that - the selector applies to. - type: string - operator: - description: operator represents a key's - relationship to a set of values. Valid - operators are In, NotIn, Exists and - DoesNotExist. - type: string - values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. This - array is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is - "In", and the values array contains only "value". - The requirements are ANDed. - type: object - type: object - namespaces: - description: namespaces specifies a static list - of namespace names that the term applies to. The - term is applied to the union of the namespaces - listed in this field and the ones selected by - namespaceSelector. null or empty namespaces list - and null namespaceSelector means "this pod's namespace" - items: - type: string - type: array - topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the pods - matching the labelSelector in the specified namespaces, - where co-located is defined as running on a node - whose value of the label with key topologyKey - matches that of any node on which any of the selected - pods is running. Empty topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - type: array - type: object - podAntiAffinity: - description: Describes pod anti-affinity scheduling rules - (e.g. avoid putting this pod in the same node, zone, etc. - as some other pod(s)). - properties: - preferredDuringSchedulingIgnoredDuringExecution: - description: The scheduler will prefer to schedule pods - to nodes that satisfy the anti-affinity expressions - specified by this field, but it may choose a node that - violates one or more of the expressions. The node that - is most preferred is the one with the greatest sum of - weights, i.e. for each node that meets all of the scheduling - requirements (resource request, requiredDuringScheduling - anti-affinity expressions, etc.), compute a sum by iterating - through the elements of this field and adding "weight" - to the sum if the node has pods which matches the corresponding - podAffinityTerm; the node(s) with the highest sum are - the most preferred. - items: - description: The weights of all of the matched WeightedPodAffinityTerm - fields are added per-node to find the most preferred - node(s) - properties: - podAffinityTerm: - description: Required. A pod affinity term, associated - with the corresponding weight. - properties: - labelSelector: - description: A label query over a set of resources, - in this case pods. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The requirements - are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. - type: string - values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied - to the union of the namespaces selected by - this field and the ones listed in the namespaces - field. null selector and null or empty namespaces - list means "this pod's namespace". An empty - selector ({}) matches all namespaces. This - field is beta-level and is only honored when - PodAffinityNamespaceSelector feature is enabled. - properties: - matchExpressions: - description: matchExpressions is a list - of label selector requirements. The requirements - are ANDed. - items: - description: A label selector requirement - is a selector that contains values, - a key, and an operator that relates - the key and values. - properties: - key: - description: key is the label key - that the selector applies to. - type: string - operator: - description: operator represents a - key's relationship to a set of values. - Valid operators are In, NotIn, Exists - and DoesNotExist. - type: string - values: - description: values is an array of - string values. If the operator is - In or NotIn, the values array must - be non-empty. If the operator is - Exists or DoesNotExist, the values - array must be empty. This array - is replaced during a strategic merge - patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator - is "In", and the values array contains - only "value". The requirements are ANDed. - type: object - type: object - namespaces: - description: namespaces specifies a static list - of namespace names that the term applies to. - The term is applied to the union of the namespaces - listed in this field and the ones selected - by namespaceSelector. null or empty namespaces - list and null namespaceSelector means "this - pod's namespace" - items: - type: string - type: array - topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the - pods matching the labelSelector in the specified - namespaces, where co-located is defined as - running on a node whose value of the label - with key topologyKey matches that of any node - on which any of the selected pods is running. - Empty topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - weight: - description: weight associated with matching the - corresponding podAffinityTerm, in the range 1-100. - format: int32 - type: integer - required: - - podAffinityTerm - - weight - type: object - type: array - requiredDuringSchedulingIgnoredDuringExecution: - description: If the anti-affinity requirements specified - by this field are not met at scheduling time, the pod - will not be scheduled onto the node. If the anti-affinity - requirements specified by this field cease to be met - at some point during pod execution (e.g. due to a pod - label update), the system may or may not try to eventually - evict the pod from its node. When there are multiple - elements, the lists of nodes corresponding to each podAffinityTerm - are intersected, i.e. all terms must be satisfied. - items: - description: Defines a set of pods (namely those matching - the labelSelector relative to the given namespace(s)) - that this pod should be co-located (affinity) or not - co-located (anti-affinity) with, where co-located - is defined as running on a node whose value of the - label with key matches that of any node - on which a pod of the set of pods is running - properties: - labelSelector: - description: A label query over a set of resources, - in this case pods. - properties: - matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are - ANDed. - items: - description: A label selector requirement - is a selector that contains values, a key, - and an operator that relates the key and - values. - properties: - key: - description: key is the label key that - the selector applies to. - type: string - operator: - description: operator represents a key's - relationship to a set of values. Valid - operators are In, NotIn, Exists and - DoesNotExist. - type: string - values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. This - array is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is - "In", and the values array contains only "value". - The requirements are ANDed. - type: object - type: object - namespaceSelector: - description: A label query over the set of namespaces - that the term applies to. The term is applied - to the union of the namespaces selected by this - field and the ones listed in the namespaces field. - null selector and null or empty namespaces list - means "this pod's namespace". An empty selector - ({}) matches all namespaces. This field is beta-level - and is only honored when PodAffinityNamespaceSelector - feature is enabled. - properties: - matchExpressions: - description: matchExpressions is a list of label - selector requirements. The requirements are - ANDed. - items: - description: A label selector requirement - is a selector that contains values, a key, - and an operator that relates the key and - values. - properties: - key: - description: key is the label key that - the selector applies to. - type: string - operator: - description: operator represents a key's - relationship to a set of values. Valid - operators are In, NotIn, Exists and - DoesNotExist. - type: string - values: - description: values is an array of string - values. If the operator is In or NotIn, - the values array must be non-empty. - If the operator is Exists or DoesNotExist, - the values array must be empty. This - array is replaced during a strategic - merge patch. - items: - type: string - type: array - required: - - key - - operator - type: object - type: array - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} - pairs. A single {key,value} in the matchLabels - map is equivalent to an element of matchExpressions, - whose key field is "key", the operator is - "In", and the values array contains only "value". - The requirements are ANDed. - type: object - type: object - namespaces: - description: namespaces specifies a static list - of namespace names that the term applies to. The - term is applied to the union of the namespaces - listed in this field and the ones selected by - namespaceSelector. null or empty namespaces list - and null namespaceSelector means "this pod's namespace" - items: - type: string - type: array - topologyKey: - description: This pod should be co-located (affinity) - or not co-located (anti-affinity) with the pods - matching the labelSelector in the specified namespaces, - where co-located is defined as running on a node - whose value of the label with key topologyKey - matches that of any node on which any of the selected - pods is running. Empty topologyKey is not allowed. - type: string - required: - - topologyKey - type: object - type: array - type: object - type: object - tolerations: - description: Tolerations is the list of tolerations that the Pods - of the PostgreSQL instance will have with respect to the taints - of the Kubernetes cluster nodes. It can be used to affect scheduling - of the Pods of the PostgreSQL instance on the Kubernetes cluster - nodes. More information at https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ - and https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#toleration-v1-core - . If you don't know what are the specific taints on the nodes - of the Kubernetes cluster you're using, you should ask your - cluster administrator. Updating this field will result in re-creation - and re-scheduling of all the Pods of the PostgreSQL instance, - so *there may be downtime*. - items: - description: The pod this Toleration is attached to tolerates - any taint that matches the triple using - the matching operator . - properties: - effect: - description: Effect indicates the taint effect to match. - Empty means match all taint effects. When specified, allowed - values are NoSchedule, PreferNoSchedule and NoExecute. - type: string - key: - description: Key is the taint key that the toleration applies - to. Empty means match all taint keys. If the key is empty, - operator must be Exists; this combination means to match - all values and all keys. - type: string - operator: - description: Operator represents a key's relationship to - the value. Valid operators are Exists and Equal. Defaults - to Equal. Exists is equivalent to wildcard for value, - so that a pod can tolerate all taints of a particular - category. - type: string - tolerationSeconds: - description: TolerationSeconds represents the period of - time the toleration (which must be of effect NoExecute, - otherwise this field is ignored) tolerates the taint. - By default, it is not set, which means tolerate the taint - forever (do not evict). Zero and negative values will - be treated as 0 (evict immediately) by the system. - format: int64 - type: integer - value: - description: Value is the taint value the toleration matches - to. If the operator is Exists, the value should be empty, - otherwise just a regular string. - type: string - type: object - type: array - type: object - version: - description: Version specifies the PostgreSQL version that the instance - should use. It is required to pick a version and it cannot be changed - afterwards, since major version upgrades are currently unsupported. - maximum: 14 - minimum: 13 - type: integer - volumeSize: - anyOf: - - type: integer - - type: string - default: 1Gi - description: 'VolumeSize sets the size of the persistent volume of - the PostgreSQL instance, the minimum size is 0.5Gi. The size is - to be specified as a plain integer or as a fixed-point number using - one of these suffixes: E, P, T, G, M, K, corresponding to kilo-, - mega-, gigabytes, etc. You can also use the power-of-two equivalents: - Ei, Pi, Ti, Gi, Mi, Ki, corresponding to kibi-, mebi-, gibibytes, - etc. For example a value of "0.5Gi" corresponds to an instance - with a persistent volume of 0.5 gibibytes.' - pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ - x-kubernetes-int-or-string: true - required: - - version - type: object - status: - description: PostgresqlStatus defines the observed state of Postgresql - properties: - clusterStatus: - description: ClusterStatus is a summary of the current status of the - cluster. Careful, if ReadyReplicas < 'spec.Replicas' or `spec.Replicas` - == 0 the status equals "NotRunning". - type: string - error: - type: string - readyReplicas: - format: int32 - type: integer - type: object - type: object - served: true - storage: false - subresources: - scale: - specReplicasPath: .spec.replicas - statusReplicasPath: .status.readyReplicas - status: {} + name: postgresqls.postgresql.anynines.com +spec: + group: postgresql.anynines.com + names: + kind: Postgresql + listKind: PostgresqlList + plural: postgresqls + shortNames: + - pg + singular: postgresql + scope: Namespaced + versions: - name: v1beta3 schema: openAPIV3Schema: @@ -1331,6 +36,29 @@ spec: spec: description: PostgresqlSpec defines the desired state of Postgresql. properties: + enableReadOnlyService: + default: false + description: EnableReadOnlyService enables the creation of a read-only + service. A read-only service is meant for read operations and allows + for load-balancing across different PostgreSQL cluster members. + Stale reads are possible when reading from this service. By default + this feature is disabled. + type: boolean + expose: + default: Internal + description: "Expose determines where (and how) the DSI can be accessed + from. Currently supported values are - \"Internal\": the DSI will + be accessible only from inside the K8s cluster - \"LoadBalancer\": + the DSI will receive dedicated load balancers with reachable IP + addresses \t that can be used from external locations . This is + only supported on K8s clusters that support external load balancers. + This field applies to all the services backed by the DSI, that is, + both the master-only one and the read-only one; the \"LoadBalancer\" + value means that each service will get a dedicated LoadBalancer." + enum: + - Internal + - LoadBalancer + type: string extensions: description: Extensions defines a list of PostgreSQL extensions which should be installed. Installing means that the binaries and libraries @@ -1341,7 +69,7 @@ spec: items: type: string type: array - postgresConfiguration: + parameters: properties: archiveTimeoutSeconds: default: 0 @@ -1432,6 +160,7 @@ spec: connections to the database server. Updating MaxConnections will trigger a restart of the PostgreSQL instance. maximum: 262143 + minimum: 1 type: integer maxLocksPerTransaction: description: MaxLocksPerTransaction sets the maximum number of @@ -1456,6 +185,8 @@ spec: description: MaxReplicationSlots specifies the maximum number of replication slots that the server can support. Updating MaxReplicationSlots will trigger a restart of the PostgreSQL instance. + maximum: 262143 + minimum: 0 type: integer maxWALSenders: default: 10 @@ -1468,6 +199,8 @@ spec: should be set slightly higher than the maximum number of expected clients so disconnected clients can immediately reconnect. Updating MaxWALSenders will trigger a restart of the PostgreSQL instance. + maximum: 262143 + minimum: 0 type: integer sharedBuffers: default: 1024 @@ -1478,6 +211,8 @@ spec: least 128 kilobytes. However, settings significantly higher than the minimum are usually needed for good performance. Updating SharedBuffers will trigger a restart of the PostgreSQL instance. + maximum: 1073741823 + minimum: 16 type: integer sslCiphers: default: HIGH:MEDIUM:+3DES:!aNULL @@ -1503,6 +238,7 @@ spec: out will also be logged. A value of zero (the default) turns this off. maximum: 2147483647 + minimum: 0 type: integer synchronousCommit: default: "on" @@ -1545,6 +281,18 @@ spec: minimum: 1 type: integer type: object + readOnlyTargetNodes: + default: replicas + description: ReadOnlyTargetNodes specifies the target PostgreSQL cluster + members which the read-only service points to. "replicas" means + that only the PostgreSQL cluster replicas are used for the read-only + service. "all" means that the read-only service points to all PostgreSQL + cluster members. By default this feature only points to the PostgreSQL + cluster replicas. + enum: + - replicas + - all + type: string replicas: default: 1 description: Replicas is the number of replicas of the data service @@ -1570,6 +318,26 @@ spec: one at a time, which could potentially lead to downtime if something goes wrong during the update. properties: + claims: + description: "Claims lists the names of resources, defined in + spec.resourceClaims, that are used by this container. \n This + is an alpha field and requires enabling the DynamicResourceAllocation + feature gate. \n This field is immutable." + items: + description: ResourceClaim references one entry in PodSpec.ResourceClaims. + properties: + name: + description: Name must match the name of one entry in pod.spec.resourceClaims + of the Pod where this field is used. It makes that resource + available inside a container. + type: string + required: + - name + type: object + type: array + x-kubernetes-list-map-keys: + - name + x-kubernetes-list-type: map limits: additionalProperties: anyOf: @@ -1612,7 +380,7 @@ spec: description: 'Affinity groups the fields that govern node affinity, Pod affinity, and Pod anti affinity. It has exactly the same syntax and semantics of the built-in Kubernetes affinity type: - https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#affinity-v1-core + https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#affinity-v1-core . It is copied as it is in the spec of the Pods of the PostgreSQL instance, and can be used to attract said Pods to specific Kubernetes cluster nodes based on those nodes'' labels and on the labels @@ -1932,9 +700,7 @@ spec: this field and the ones listed in the namespaces field. null selector and null or empty namespaces list means "this pod's namespace". An empty - selector ({}) matches all namespaces. This - field is beta-level and is only honored when - PodAffinityNamespaceSelector feature is enabled. + selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list @@ -1991,7 +757,7 @@ spec: listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector means "this - pod's namespace" + pod's namespace". items: type: string type: array @@ -2095,9 +861,7 @@ spec: field and the ones listed in the namespaces field. null selector and null or empty namespaces list means "this pod's namespace". An empty selector - ({}) matches all namespaces. This field is beta-level - and is only honored when PodAffinityNamespaceSelector - feature is enabled. + ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label @@ -2152,7 +916,7 @@ spec: term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list - and null namespaceSelector means "this pod's namespace" + and null namespaceSelector means "this pod's namespace". items: type: string type: array @@ -2256,9 +1020,7 @@ spec: this field and the ones listed in the namespaces field. null selector and null or empty namespaces list means "this pod's namespace". An empty - selector ({}) matches all namespaces. This - field is beta-level and is only honored when - PodAffinityNamespaceSelector feature is enabled. + selector ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list @@ -2315,7 +1077,7 @@ spec: listed in this field and the ones selected by namespaceSelector. null or empty namespaces list and null namespaceSelector means "this - pod's namespace" + pod's namespace". items: type: string type: array @@ -2419,9 +1181,7 @@ spec: field and the ones listed in the namespaces field. null selector and null or empty namespaces list means "this pod's namespace". An empty selector - ({}) matches all namespaces. This field is beta-level - and is only honored when PodAffinityNamespaceSelector - feature is enabled. + ({}) matches all namespaces. properties: matchExpressions: description: matchExpressions is a list of label @@ -2476,7 +1236,7 @@ spec: term is applied to the union of the namespaces listed in this field and the ones selected by namespaceSelector. null or empty namespaces list - and null namespaceSelector means "this pod's namespace" + and null namespaceSelector means "this pod's namespace". items: type: string type: array @@ -2501,7 +1261,7 @@ spec: of the Kubernetes cluster nodes. It can be used to affect scheduling of the Pods of the PostgreSQL instance on the Kubernetes cluster nodes. More information at https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ - and https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#toleration-v1-core + and https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#toleration-v1-core . If you don't know what are the specific taints on the nodes of the Kubernetes cluster you're using, you should ask your cluster administrator. Updating this field will result in re-creation @@ -2641,6 +1401,16 @@ metadata: creationTimestamp: null name: postgresql-manager-role rules: +- apiGroups: + - "" + resources: + - configmaps + verbs: + - create + - delete + - list + - update + - watch - apiGroups: - "" resources: @@ -2685,6 +1455,7 @@ rules: - statefulsets verbs: - create + - delete - get - list - update @@ -2902,7 +1673,7 @@ spec: - --leader-elect command: - postgresql-operator - image: public.ecr.aws/w5n9a2g2/a9s-ds-for-k8s/dev/postgresql-operator:4880de8355bac36308e2f6f4ec3f4273e42af7b1 + image: public.ecr.aws/w5n9a2g2/a9s-ds-for-k8s/dev/postgresql-operator:c01af5af858b4a5186460a51d5a1f34a5d428317 livenessProbe: httpGet: path: /healthz @@ -2995,14 +1766,14 @@ webhooks: service: name: postgresql-webhook-service namespace: a8s-system - path: /mutate-postgresql-anynines-com-v1alpha1-postgresql + path: /mutate-postgresql-anynines-com-v1beta3-postgresql failurePolicy: Fail name: mpostgresql.kb.io rules: - apiGroups: - postgresql.anynines.com apiVersions: - - v1alpha1 + - v1beta3 operations: - CREATE - UPDATE @@ -3023,14 +1794,14 @@ webhooks: service: name: postgresql-webhook-service namespace: a8s-system - path: /validate-postgresql-anynines-com-v1alpha1-postgresql + path: /validate-postgresql-anynines-com-v1beta3-postgresql failurePolicy: Fail name: vpostgresql.kb.io rules: - apiGroups: - postgresql.anynines.com apiVersions: - - v1alpha1 + - v1beta3 operations: - CREATE - UPDATE diff --git a/deploy/a8s/manifests/service-binding-controller.yaml b/deploy/a8s/manifests/service-binding-controller.yaml index 11f343c8..2a52a2d3 100644 --- a/deploy/a8s/manifests/service-binding-controller.yaml +++ b/deploy/a8s/manifests/service-binding-controller.yaml @@ -3,22 +3,8 @@ kind: CustomResourceDefinition metadata: annotations: controller-gen.kubebuilder.io/version: v0.4.1 - cert-manager.io/inject-ca-from: a8s-system/service-binding-serving-cert - creationTimestamp: null name: servicebindings.servicebindings.anynines.com spec: - conversion: - strategy: Webhook - webhook: - clientConfig: - service: - name: service-binding-webhook-service - namespace: a8s-system - path: /convert - port: 443 - conversionReviewVersions: - - v1 - - v1beta1 group: servicebindings.anynines.com names: categories: @@ -32,88 +18,6 @@ spec: singular: servicebinding scope: Namespaced versions: - - name: v1alpha1 - schema: - openAPIV3Schema: - description: ServiceBinding is the Schema for the servicebindings API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation - of an object. Servers should convert recognized schemas to the latest - internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this - object represents. Servers may infer this from the endpoint the client - submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: ServiceBindingSpec defines the desired state of the ServiceBinding - properties: - instance: - description: Instance identifies the Data Service Instance that the - ServiceBinding binds to. - properties: - apiVersion: - description: APIVersion is the / of the referenced - Data Service Instance, e.g. "postgresql.anynines.com/v1alpha1" - or "redis.anynines.com/v1alpha1". - type: string - kind: - description: Kind is the Kubernetes API Kind of the referenced - Data Service Instance. - type: string - name: - type: string - namespace: - type: string - required: - - apiVersion - - kind - - name - - namespace - type: object - required: - - instance - type: object - status: - description: ServiceBindingStatus defines the observed state of the ServiceBinding - properties: - error: - description: Error is a message explaining why the service binding - could not be implemented if that's the case. - type: string - implemented: - description: Implemented is `true` if and only if the service binding - has been implemented by creating a user with the appropriate permissions - in the bound Data Service Instance. Users can safely consume the - service binding secret identified by `Secret` IF AND ONLY IF `Implemented` - is true. In other words, even if the secret identified by `Secret` - gets created before `Implemented` becomes true, users MUST NOT consume - that secret before `Implemented` has become true. - type: boolean - secret: - description: Secret contains the namespace and name of the Kubernetes - API secret that stores the credentials and information (e.g. URL) - associated to the service binding to access the bound Data Service - Instance. - properties: - name: - type: string - namespace: - type: string - required: - - name - - namespace - type: object - type: object - type: object - served: true - storage: false - subresources: - status: {} - name: v1beta3 schema: openAPIV3Schema: @@ -150,18 +54,19 @@ spec: name: type: string namespace: + description: Namepace of the Instance, if not provided the same + namespace as the service bindings will be used type: string required: - apiVersion - kind - name - - namespace type: object required: - instance type: object status: - description: ServiceBindingStatus defines the observed state of the ServiceBinding + description: ServiceBindingStatus defines the observed state of the ServiceBinding. properties: error: description: Error is a message explaining why the service binding @@ -185,10 +90,11 @@ spec: name: type: string namespace: + description: Namepace of the Instance, if not provided the same + namespace as the service bindings will be used type: string required: - name - - namespace type: object type: object type: object @@ -266,6 +172,7 @@ rules: - create - get - list + - update - watch - apiGroups: - postgresql.anynines.com @@ -408,21 +315,6 @@ spec: app.kubernetes.io/name: service-binding-controller-manager app.kubernetes.io/part-of: a8s-service-binding --- -apiVersion: v1 -kind: Service -metadata: - name: service-binding-webhook-service - namespace: a8s-system -spec: - ports: - - port: 443 - protocol: TCP - targetPort: 9443 - selector: - app.kubernetes.io/component: controller-manager - app.kubernetes.io/name: service-binding-controller-manager - app.kubernetes.io/part-of: a8s-service-binding ---- apiVersion: apps/v1 kind: Deployment metadata: @@ -456,7 +348,7 @@ spec: - a8s-service-binding-controller - --postgresql-root-role=a9s_user - --postgresql-default-database=a9s_apps_default_db - image: public.ecr.aws/w5n9a2g2/a9s-ds-for-k8s/dev/service-binding-controller:7c91f7c206308935f6e9e576c71d032c53560fa1 + image: public.ecr.aws/w5n9a2g2/a9s-ds-for-k8s/dev/service-binding-controller:a2903616cb2f52885f1cdb93b5fd1c95d6a7694a livenessProbe: httpGet: path: /healthz @@ -464,10 +356,6 @@ spec: initialDelaySeconds: 15 periodSeconds: 20 name: manager - ports: - - containerPort: 9443 - name: webhook-server - protocol: TCP readinessProbe: httpGet: path: /readyz @@ -484,9 +372,6 @@ spec: securityContext: allowPrivilegeEscalation: false volumeMounts: - - mountPath: /tmp/k8s-webhook-server/serving-certs - name: cert - readOnly: true - mountPath: /config/controller_manager_config.yaml name: manager-config subPath: controller_manager_config.yaml @@ -505,32 +390,6 @@ spec: serviceAccountName: service-binding-manager-account terminationGracePeriodSeconds: 10 volumes: - - name: cert - secret: - defaultMode: 420 - secretName: sb-webhook-server-cert - configMap: name: service-binding-manager-config name: manager-config ---- -apiVersion: cert-manager.io/v1 -kind: Certificate -metadata: - name: service-binding-serving-cert - namespace: a8s-system -spec: - dnsNames: - - service-binding-webhook-service.a8s-system.svc - - service-binding-webhook-service.a8s-system.svc.cluster.local - issuerRef: - kind: Issuer - name: service-binding-selfsigned-issuer - secretName: sb-webhook-server-cert ---- -apiVersion: cert-manager.io/v1 -kind: Issuer -metadata: - name: service-binding-selfsigned-issuer - namespace: a8s-system -spec: - selfSigned: {} diff --git a/deploy/cert-manager/kustomization.yaml b/deploy/cert-manager/kustomization.yaml index 15c1a4a9..f77c5126 100644 --- a/deploy/cert-manager/kustomization.yaml +++ b/deploy/cert-manager/kustomization.yaml @@ -1,2 +1,2 @@ resources: - - https://github.com/cert-manager/cert-manager/releases/download/v1.7.1/cert-manager.yaml \ No newline at end of file + - https://github.com/cert-manager/cert-manager/releases/download/v1.11.0/cert-manager.yaml diff --git a/docs/application-developers/advanced_configuration.md b/docs/application-developers/advanced_configuration.md index f7ebc451..9e7281e4 100644 --- a/docs/application-developers/advanced_configuration.md +++ b/docs/application-developers/advanced_configuration.md @@ -5,6 +5,7 @@ of a PostgreSQL instance. ## Index +- [Usage Outside the Kubernetes Cluster](#usage-outside-the-kubernetes-cluster) - [High Availability & Scheduling Constraints](#high-availability--scheduling-constraints) - [Affinity and Anti-Affinity](#affinity-and-anti-affinity) - [Example : High Availability 1 - Distributing Replicas to Zones](#example--high-availability-1---distributing-replicas-to-zones) @@ -13,6 +14,43 @@ of a PostgreSQL instance. - [Example: Node dedicated to PostgreSQL Instance](#example-node-dedicated-to-postgresql-instance) - [Caveats and Known Limitations](#caveats-and-known-limitations) +## Usage Outside the Kubernetes Cluster + +To use your instances outside the K8s cluster they are running in, you can expose them via a load +balancer. You can do so by setting the field `spec.expose` equal to `LoadBalancer`, as in this +example: + +```yaml +apiVersion: postgresql.anynines.com/v1beta3 +kind: Postgresql +metadata: + name: sample-pg-cluster +spec: + version: 14 + expose: LoadBalancer +``` + +The operator will set up a K8s ConfigMap containing the hosts and ports you can use to connect to +your instance. + +The name of the ConfigMap will be `-connection`, the namespace will be the same as +the instance's. You can find it and inspect the contents by running: + +```sh +kubectl get configmap -connection -o yaml +``` + +*Note 1*: Exposing the instance will always create at least one dedicated load balancer, which might +cause additional cost, depending on your infrastructure. + +*Note 2*: If your instance also has a read-only service (`spec.enableReadOnlyService: true`), +exposing it outside the cluster will create two load balancers, one for the read-only service and +one for the read-write service. This might cost even more (the actual amount depends on your +infrastructure). + +*Note 3*: Some infrastructure providers might not support load balancers, if you run a8s on one such +provider even if you specify `spec.expose: LoadBalancer` the instance won't get a load balancer. + ## High Availability & Scheduling Constraints With the help of scheduling constraints you can make better use of your clusters @@ -20,7 +58,7 @@ resource and/or make PostgreSQL instances more resilient against failures by setting up highly available instances. In general, these settings are exposed through the `spec.schedulingConstraints` field for example in the `Postgresql` objects (see [API -Documentation](/docs/application-developers/api-documentation/api_reference.md#postgresqlschedulingconstraints)). +Documentation](/docs/application-developers/api-documentation/postgresql-operator/v1beta3.md#postgresqlschedulingconstraints)). Subfields of `schedulingConstraints` allow you to configure [tolerations][taints-and-tolerations], [node @@ -35,7 +73,7 @@ Thus the a8s framework fully relies on Kubernetes mechanisms and inherits its limitations, therefore it is highly recommended going through the Kubernetes documentation on the topic: - [Affinity and Anti-Affinity][affinity] -- [Taints and Tolerations][taints-and-tolerations] +- [Taints and Tolerations][taints-and-tolerations] The next sections will guide you through the configuration process. @@ -50,7 +88,7 @@ The next sections will guide you through the configuration process. [Affinity and Anti-Affinity][affinity] is used to attract or repel pods to/from K8s cluster nodes at scheduling time or runtime, based on the nodes labels or on the labels of other pods running on the nodes. The former case is called node -affinity and the latter one inter-pod (anti-)affinity. +affinity and the latter one inter-pod (anti-)affinity. You can read more about what constraints are possible in the [Kubernetes documentation][affinity], as mentioned before the a8s framework does not place @@ -63,14 +101,14 @@ affinity can be expressed analogously. In this section we will assume that: -- you are using a cluster that has nodes in at least 3 availability zones (AZ). +- you are using a cluster that has nodes in at least 3 availability zones (AZ). - you want to use a 3 replica PostgreSQL instance. - the AZ of a node is indicated by the node's label `topology.kubernetes.io/zone`. In this case, for a high available PostgreSQL, the replicas have to be distributed among those AZs, here is an example `Postgresql` CustomResource -(CR) object that will achieve this: +(CR) object that will achieve this: ```yml apiVersion: postgresql.anynines.com/v1beta3 @@ -94,16 +132,16 @@ spec: matchExpressions: - key: a8s.a9s/dsi-name operator: In - values: + values: - ha-1-sample-pg-cluster - key: a8s.a9s/dsi-kind operator: In - values: + values: - Postgresql topologyKey: topology.kubernetes.io/zone ``` -Let's go through the specs in detail: +Let's go through the specs in detail: ```yml podAntiAffinity: @@ -112,15 +150,15 @@ podAntiAffinity: Since we want PostgreSQL pods to repel each other, we use **anti-affinity** here, the `requiredDuringScheduling` part will then indicate which conditions **must** be met -before a pod gets scheduled. +before a pod gets scheduled. The `IgnoredDuringExecution` implies that in case we are modifying an already running instance, pods will not get evicted to enforce this policy, so it will only take effect when pods restart. Therefore, if you want to try out the -examples make sure to always create a new instance. +examples make sure to always create a new instance. Goal of our constraints is to express, that no other pod of the same DSI should -be in the same zone, which is done through: +be in the same zone, which is done through: ```yml - podAffinityTerm: @@ -128,11 +166,11 @@ be in the same zone, which is done through: matchExpressions: - key: a8s.a9s/dsi-name operator: In - values: + values: - ha-1-sample-pg-cluster - key: a8s.a9s/dsi-kind operator: In - values: + values: - Postgresql topologyKey: topology.kubernetes.io/zone ``` @@ -157,7 +195,7 @@ section][well-known-annotations]. > annotations][well-known-annotations] such as `topology.kubernetes.io/zone` and > most providers use them, Kubernetes does not enforce them. Thus, you will > have to make sure that your cluster uses them or replace them with the ones -> that are used in your cluster. +> that are used in your cluster. > You can find out by asking your admin or inspecting your cluster nodes. You can test this example using: @@ -181,11 +219,11 @@ kubectl get nodes -o go-template='{{range .items}}{{printf "%s : %s\n" .metadata > Note: > The documentation warns you that specifying pod constraints can result in > significantly increased amount of processing at scheduling time, which can -> slow down your cluster. +> slow down your cluster. For a more detailed and complete guide, please refer to the [Kubernetes documentation][affinity], everything mentioned there is directly applicable to -the a8s framework. +the a8s framework. #### Example: High Availability 2 - More Replicas than Zones @@ -196,7 +234,7 @@ possible without having to worry about scheduling fields. We are now going to assume you have a cluster with 3 AZs which all contain multiple nodes and want to run a 5 replicas DSI on it. We want to ensure that pods are distributed among zones and also that there are no two Pods of the same -DSI running on the same node. This can be achieved using: +DSI running on the same node. This can be achieved using: ```yml apiVersion: postgresql.anynines.com/v1beta3 @@ -220,11 +258,11 @@ spec: matchExpressions: - key: a8s.a9s/dsi-name operator: In - values: + values: - ha-2-sample-pg-cluster - key: a8s.a9s/dsi-kind operator: In - values: + values: - Postgresql topologyKey: kubernetes.io/hostname preferredDuringSchedulingIgnoredDuringExecution: @@ -234,11 +272,11 @@ spec: matchExpressions: - key: a8s.a9s/dsi-name operator: In - values: + values: - ha-2-sample-pg-cluster - key: a8s.a9s/dsi-kind operator: In - values: + values: - Postgresql topologyKey: topology.kubernetes.io/zone ``` @@ -256,17 +294,17 @@ wouldn't be satisfiable. Instead, we have now made this constraint matchExpressions: - key: a8s.a9s/dsi-name operator: In - values: + values: - ha-2-sample-pg-cluster - key: a8s.a9s/dsi-kind operator: In - values: + values: - Postgresql topologyKey: topology.kubernetes.io/zone ``` This will not prevent scheduling of pods in the same availability zone, but will -minimize the likelihood of having them in the same AZ. +minimize the likelihood of having them in the same AZ. Additionally, when using `preferredDuringSchedulingIgnoredDuringExecution` one has to give each constraint a weight. This weight conveys to the scheduler how @@ -297,7 +335,7 @@ kubectl get pods -l a8s.a9s/dsi-name=ha-1-sample-pg-cluster -o go-template='{{r ``` And additionally verify that the nodes are not running in a single AZs by -inspecting the output of: +inspecting the output of: ```bash kubectl get nodes -o go-template='{{range .items}}{{printf "%s : %s\n" .metadata.name (index .metadata.labels "topology.kubernetes.io/zone") }}{{end}}' @@ -317,7 +355,7 @@ taint is `node-role.kubernetes.io/control-plane` which is typically used on the nodes reserved for the Kubernetes control plane components. Since taints can stop pods, be careful when tainting your nodes, you could end -up leaving the cluster in a broken state. Please read the +up leaving the cluster in a broken state. Please read the [documentation][taints-and-tolerations] before using taints and tolerations. #### Example: Node dedicated to PostgreSQL Instance @@ -401,7 +439,7 @@ kubectl apply -f examples/postgresql-toleration-instance.yaml ``` If you now add another DSI, for example our `sample-pg-cluster` from the usage -overview, by using: +overview, by using: ```bash kubectl apply -f examples/postgresql-instance.yaml @@ -413,7 +451,7 @@ You will see that none of its replicas will be scheduled on the tainted node. This section will point out **some** of the current limitations and caveats you might be experiencing when working with scheduling constraints, the indicated -Kubernetes documentation pages will provide a more complete overview. +Kubernetes documentation pages will provide a more complete overview. - Using scheduling constraints can evict pods and cause some pods to not be scheduled regardless of resources available on the cluster. This is especially @@ -441,11 +479,11 @@ Kubernetes documentation pages will provide a more complete overview. cluster. - For some storage classes, the PersistentVolumeClaims can cause the pod to stick to a specific node. For example, if a pod was already scheduled on the - node and after a change in scheduling constraints it can no longer run on it + node and after a change in scheduling constraints it can no longer run on it the pod can get stuck in pending. The reason is that the PersistentVolumeClaim of the pod is bound to the node and therefore this node becomes the only eligible node for scheduling the pod, but the constraints forbid scheduling. - This behavior will be addressed in future releases of Kubernetes. + This behavior will be addressed in future releases of Kubernetes. [affinity]:https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity [taints-and-tolerations]:https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ diff --git a/docs/application-developers/api-documentation/README.md b/docs/application-developers/api-documentation/README.md index 02abb8ab..da4b02bc 100644 --- a/docs/application-developers/api-documentation/README.md +++ b/docs/application-developers/api-documentation/README.md @@ -1,13 +1,11 @@ # API Documentation This folder contains documentation on specific details of the a8s framework API: + - [a8s-backup-manager](/docs/application-developers/api-documentation/a8s-backup-manager) - - [v1alpha1](/docs/application-developers/api-documentation/a8s-backup-manager/v1alpha1.md) - - [v1beta3](/docs/application-developers/api-documentation/a8s-backup-manager/v1beta3.md) + - [v1beta3](/docs/application-developers/api-documentation/a8s-backup-manager/v1beta3.md) - [a8s-service-binding-controller](/docs/application-developers/api-documentation/a8s-service-binding-controller) - - [v1alpha1](/docs/application-developers/api-documentation/a8s-service-binding-controller/v1alpha1.md) - - [v1beta3](/docs/application-developers/api-documentation/a8s-service-binding-controller/v1beta3.md) + - [v1beta3](/docs/application-developers/api-documentation/a8s-service-binding-controller/v1beta3.md) - [postgresql-operator](/docs/application-developers/api-documentation/postgresql-operator) - - [v1alpha1](/docs/application-developers/api-documentation/postgresql-operator/v1alpha1.md) - - [v1beta3](/docs/application-developers/api-documentation/postgresql-operator/v1beta3.md) + - [v1beta3](/docs/application-developers/api-documentation/postgresql-operator/v1beta3.md) - [Labels of DSI secondary resources](/docs/application-developers/api-documentation/labels_secondary_dsi_objects.md) diff --git a/docs/application-developers/api-documentation/a8s-backup-manager/v1alpha1.md b/docs/application-developers/api-documentation/a8s-backup-manager/v1alpha1.md deleted file mode 100644 index ccc4952e..00000000 --- a/docs/application-developers/api-documentation/a8s-backup-manager/v1alpha1.md +++ /dev/null @@ -1,148 +0,0 @@ -# API Reference - -## Packages -- [backups.anynines.com/v1alpha1](#backupsanyninescomv1alpha1) - -## backups.anynines.com/v1alpha1 - -Package v1alpha1 contains API Schema definitions for the backups v1alpha1 API group - -### Resource Types -- [Backup](#backup) -- [BackupList](#backuplist) -- [Restore](#restore) -- [RestoreList](#restorelist) - -#### Backup - -Backup is the Schema for the backups API - -_Appears in:_ -- [BackupList](#backuplist) - -| Field | Description | -| --- | --- | -| `apiVersion` _string_ | `backups.anynines.com/v1alpha1` -| `kind` _string_ | `Backup` -| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | -| `spec` _[BackupSpec](#backupspec)_ | | -| `status` _[BackupStatus](#backupstatus)_ | | - -#### BackupList - -BackupList contains a list of Backup - -| Field | Description | -| --- | --- | -| `apiVersion` _string_ | `backups.anynines.com/v1alpha1` -| `kind` _string_ | `BackupList` -| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | -| `items` _[Backup](#backup) array_ | | - -#### BackupSpec - -BackupSpec defines the desired state of Backup. - -_Appears in:_ -- [Backup](#backup) - -| Field | Description | -| --- | --- | -| `serviceInstance` _[ServiceInstanceRef](#serviceinstanceref)_ | ServiceInstance identifies the Data Service Instance to backup. | -| `maxRetries` _string_ | How many times the backup will be retried before aborting. Allowed values: any positive integer, or "Infinite" | - -#### BackupStatus - -BackupStatus defines the observed state of Backup. - -_Appears in:_ -- [Backup](#backup) - -| Field | Description | -| --- | --- | -| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#condition-v1-meta) array_ | Conditions include a set of not mutually exclusive states the Backup can be in, as well as the last observed time stamp for these conditions. They include "Ready", "InProgress", "UploadedToS3", "Terminating". | -| `lastObservationTime` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#time-v1-meta)_ | LastObservationTime is the timestamp of the last time the Condition was observed to be true. | -| `retries` _integer_ | Number of times the backup has been retried | -| `podUsedNamespacedName` _string_ | PodUsedNamespacedName is the namespaced name of the DSI Pod to which the backup request was sent. TODO: Represent this jointly with `PodUsedID` (below) via a PodRef. | -| `podUsedUID` _UID_ | PodUsedUID is the UID of the DSI Pod to which the backup request was sent. TODO: Represent this jointly with `PodUsedNamespacedName` (above) via a PodRef. | -| `backupID` _string_ | BackupID is the ID of the Backup; clients can use this to poll the status of the Backup at the Pod identified by `PodUsedID`. | - -#### PodRef - -PodRef describes a reference to a Pod. - -_Appears in:_ -- [RestoreStatus](#restorestatus) - -| Field | Description | -| --- | --- | -| `namespacedName` _string_ | NamespacedName is the namespaced name of the Pod. | -| `uid` _UID_ | UID is the UID of the Pod. | -| `ip` _string_ | IP is the IP of the Pod. | - -#### Restore - -Restore is the Schema for the restore API - -_Appears in:_ -- [RestoreList](#restorelist) - -| Field | Description | -| --- | --- | -| `apiVersion` _string_ | `backups.anynines.com/v1alpha1` -| `kind` _string_ | `Restore` -| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | -| `spec` _[RestoreSpec](#restorespec)_ | | -| `status` _[RestoreStatus](#restorestatus)_ | | - -#### RestoreList - -RestoreList contains a list of Restore. - -| Field | Description | -| --- | --- | -| `apiVersion` _string_ | `backups.anynines.com/v1alpha1` -| `kind` _string_ | `RestoreList` -| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | -| `items` _[Restore](#restore) array_ | | - -#### RestoreSpec - -RestoreSpec defines the desired state of Restore. - -_Appears in:_ -- [Restore](#restore) - -| Field | Description | -| --- | --- | -| `serviceInstance` _[ServiceInstanceRef](#serviceinstanceref)_ | ServiceInstance identifies the Data Service Instance to restore. | -| `backupName` _string_ | BackupName is the name of the Backup API object to use for the Restore; the namespace is assumed to be the same as the one for the Restore object, we might reconsider this assumption in the future. | - -#### RestoreStatus - -RestoreStatus defines the observed state of Restore. - -_Appears in:_ -- [Restore](#restore) - -| Field | Description | -| --- | --- | -| `lastObservationTime` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#time-v1-meta)_ | LastObservationTime is the timestamp of the last time the Condition was observed to be true. | -| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#condition-v1-meta) array_ | Conditions include a set of not mutually exclusive states the Restore can be in, as well as the last observed time stamp for these conditions. They include "Ready", "InProgress", "Terminating". | -| `podToPoll` _[PodRef](#podref)_ | The Pod to poll to learn the status of the Restore, if the restore is in Progress. | -| `restoreID` _string_ | RestoreID is the ID of the Restore; clients can use this to poll the status of the Restore at the Pod identified by `PodToHit`. | - -#### ServiceInstanceRef - -ServiceInstanceRef references a Data Service Instance to backup or restore. The referenced Data Service Instance is always assumed to be in the same Kubernetes API namespace as the parent Backup/Restore API object, so there's no namespace field; we might reconsider this assumption in the future. - -_Appears in:_ -- [BackupSpec](#backupspec) -- [RestoreSpec](#restorespec) - -| Field | Description | -| --- | --- | -| `name` _string_ | Name is the name of the Kubernetes API resource that represents the Data Service Instance to backup or restore. | -| `kind` _string_ | Kind is the kind of the Kubernetes API resource that represents the Data Service Instance to backup or restore (e.g. Postgresql, Redis, etc...). | -| `apiGroup` _string_ | APIGroup is the API group of the Kubernetes API resource that represents the Data Service Instance to backup or restore (e.g. postgresql.anynines.com, redis.anynines.com, etc...). | - diff --git a/docs/application-developers/api-documentation/a8s-backup-manager/v1beta3.md b/docs/application-developers/api-documentation/a8s-backup-manager/v1beta3.md index 7f5a5ea7..abd1d002 100644 --- a/docs/application-developers/api-documentation/a8s-backup-manager/v1beta3.md +++ b/docs/application-developers/api-documentation/a8s-backup-manager/v1beta3.md @@ -24,7 +24,7 @@ _Appears in:_ | --- | --- | | `apiVersion` _string_ | `backups.anynines.com/v1beta3` | `kind` _string_ | `Backup` -| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | `spec` _[BackupSpec](#backupspec)_ | | | `status` _[BackupStatus](#backupstatus)_ | | @@ -36,7 +36,7 @@ BackupList contains a list of Backup. | --- | --- | | `apiVersion` _string_ | `backups.anynines.com/v1beta3` | `kind` _string_ | `BackupList` -| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | `items` _[Backup](#backup) array_ | | #### BackupSpec @@ -60,8 +60,8 @@ _Appears in:_ | Field | Description | | --- | --- | -| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#condition-v1-meta) array_ | Conditions include a set of not mutually exclusive states the Backup can be in, as well as the last observed time stamp for these conditions. They include "Ready", "InProgress", "UploadedToS3", "Terminating". | -| `lastObservationTime` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#time-v1-meta)_ | LastObservationTime is the timestamp of the last time the Condition was observed to be true. | +| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#condition-v1-meta) array_ | Conditions include a set of not mutually exclusive states the Backup can be in, as well as the last observed time stamp for these conditions. They include "Ready", "InProgress", "UploadedToS3", "Terminating". | +| `lastObservationTime` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#time-v1-meta)_ | LastObservationTime is the timestamp of the last time the Condition was observed to be true. | | `retries` _integer_ | Number of times the backup has been retried | | `podUsedNamespacedName` _string_ | PodUsedNamespacedName is the namespaced name of the DSI Pod to which the backup request was sent. TODO: Represent this jointly with `PodUsedID` (below) via a PodRef. | | `podUsedUID` _UID_ | PodUsedUID is the UID of the DSI Pod to which the backup request was sent. TODO: Represent this jointly with `PodUsedNamespacedName` (above) via a PodRef. | @@ -91,7 +91,7 @@ _Appears in:_ | --- | --- | | `apiVersion` _string_ | `backups.anynines.com/v1beta3` | `kind` _string_ | `Restore` -| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | `spec` _[RestoreSpec](#restorespec)_ | | | `status` _[RestoreStatus](#restorestatus)_ | | @@ -103,7 +103,7 @@ RestoreList contains a list of Restore. | --- | --- | | `apiVersion` _string_ | `backups.anynines.com/v1beta3` | `kind` _string_ | `RestoreList` -| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | `items` _[Restore](#restore) array_ | | #### RestoreSpec @@ -127,8 +127,8 @@ _Appears in:_ | Field | Description | | --- | --- | -| `lastObservationTime` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#time-v1-meta)_ | LastObservationTime is the timestamp of the last time the Condition was observed to be true. | -| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#condition-v1-meta) array_ | Conditions include a set of not mutually exclusive states the Restore can be in, as well as the last observed time stamp for these conditions. They include "Ready", "InProgress", "Terminating". | +| `lastObservationTime` _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#time-v1-meta)_ | LastObservationTime is the timestamp of the last time the Condition was observed to be true. | +| `conditions` _[Condition](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#condition-v1-meta) array_ | Conditions include a set of not mutually exclusive states the Restore can be in, as well as the last observed time stamp for these conditions. They include "Ready", "InProgress", "Terminating". | | `podToPoll` _[PodRef](#podref)_ | The Pod to poll to learn the status of the Restore, if the restore is in Progress. | | `restoreID` _string_ | RestoreID is the ID of the Restore; clients can use this to poll the status of the Restore at the Pod identified by `PodToHit`. | diff --git a/docs/application-developers/api-documentation/a8s-service-binding-controller/v1alpha1.md b/docs/application-developers/api-documentation/a8s-service-binding-controller/v1alpha1.md deleted file mode 100644 index bed81dce..00000000 --- a/docs/application-developers/api-documentation/a8s-service-binding-controller/v1alpha1.md +++ /dev/null @@ -1,89 +0,0 @@ -# API Reference - -## Packages -- [servicebindings.anynines.com/v1alpha1](#servicebindingsanyninescomv1alpha1) - -## servicebindings.anynines.com/v1alpha1 - -Package v1alpha1 contains API Schema definitions for the servicebindings v1alpha1 API group - -### Resource Types -- [ServiceBinding](#servicebinding) -- [ServiceBindingList](#servicebindinglist) - -#### InstanceRef - -InstanceRef is a reference to a Data Service Instance. - -_Appears in:_ -- [ServiceBindingSpec](#servicebindingspec) - -| Field | Description | -| --- | --- | -| `apiVersion` _string_ | APIVersion is the / of the referenced Data Service Instance, e.g. "postgresql.anynines.com/v1alpha1" or "redis.anynines.com/v1alpha1". | -| `kind` _string_ | Kind is the Kubernetes API Kind of the referenced Data Service Instance. | -| `NamespacedName` _[NamespacedName](#namespacedname)_ | NamespacedName is the Kubernetes API Kind of the referenced Data Service Instance. | - -#### NamespacedName - -NamespacedName represents a Kubernetes API namespace and name. It's factored out to its own type for reusability. - -_Appears in:_ -- [InstanceRef](#instanceref) -- [ServiceBindingStatus](#servicebindingstatus) - -| Field | Description | -| --- | --- | -| `namespace` _string_ | | -| `name` _string_ | | - -#### ServiceBinding - -ServiceBinding is the Schema for the servicebindings API - -_Appears in:_ -- [ServiceBindingList](#servicebindinglist) - -| Field | Description | -| --- | --- | -| `apiVersion` _string_ | `servicebindings.anynines.com/v1alpha1` -| `kind` _string_ | `ServiceBinding` -| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | -| `spec` _[ServiceBindingSpec](#servicebindingspec)_ | | -| `status` _[ServiceBindingStatus](#servicebindingstatus)_ | | - -#### ServiceBindingList - -ServiceBindingList contains a list of ServiceBinding - -| Field | Description | -| --- | --- | -| `apiVersion` _string_ | `servicebindings.anynines.com/v1alpha1` -| `kind` _string_ | `ServiceBindingList` -| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | -| `items` _[ServiceBinding](#servicebinding) array_ | | - -#### ServiceBindingSpec - -ServiceBindingSpec defines the desired state of the ServiceBinding - -_Appears in:_ -- [ServiceBinding](#servicebinding) - -| Field | Description | -| --- | --- | -| `instance` _[InstanceRef](#instanceref)_ | Instance identifies the Data Service Instance that the ServiceBinding binds to. | - -#### ServiceBindingStatus - -ServiceBindingStatus defines the observed state of the ServiceBinding - -_Appears in:_ -- [ServiceBinding](#servicebinding) - -| Field | Description | -| --- | --- | -| `secret` _[NamespacedName](#namespacedname)_ | Secret contains the namespace and name of the Kubernetes API secret that stores the credentials and information (e.g. URL) associated to the service binding to access the bound Data Service Instance. | -| `implemented` _boolean_ | Implemented is `true` if and only if the service binding has been implemented by creating a user with the appropriate permissions in the bound Data Service Instance. Users can safely consume the service binding secret identified by `Secret` IF AND ONLY IF `Implemented` is true. In other words, even if the secret identified by `Secret` gets created before `Implemented` becomes true, users MUST NOT consume that secret before `Implemented` has become true. | -| `error` _string_ | Error is a message explaining why the service binding could not be implemented if that's the case. | - diff --git a/docs/application-developers/api-documentation/a8s-service-binding-controller/v1beta3.md b/docs/application-developers/api-documentation/a8s-service-binding-controller/v1beta3.md index 92dcebf5..11517ceb 100644 --- a/docs/application-developers/api-documentation/a8s-service-binding-controller/v1beta3.md +++ b/docs/application-developers/api-documentation/a8s-service-binding-controller/v1beta3.md @@ -22,7 +22,7 @@ _Appears in:_ | --- | --- | | `apiVersion` _string_ | APIVersion is the / of the referenced Data Service Instance, e.g. "postgresql.anynines.com/v1beta3" or "redis.anynines.com/v1alpha1". | | `kind` _string_ | Kind is the Kubernetes API Kind of the referenced Data Service Instance. | -| `NamespacedName` _[NamespacedName](#namespacedname)_ | NamespacedName is the Kubernetes API Kind of the referenced Data Service Instance. | +| `NamespacedName` _[NamespacedName](#namespacedname)_ | NamespacedName represents the referenced Data Service Instance namespace and name. | #### NamespacedName @@ -34,7 +34,7 @@ _Appears in:_ | Field | Description | | --- | --- | -| `namespace` _string_ | | +| `namespace` _string_ | Namepace of the Instance, if not provided the same namespace as the service bindings will be used | | `name` _string_ | | #### ServiceBinding @@ -48,7 +48,7 @@ _Appears in:_ | --- | --- | | `apiVersion` _string_ | `servicebindings.anynines.com/v1beta3` | `kind` _string_ | `ServiceBinding` -| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | `spec` _[ServiceBindingSpec](#servicebindingspec)_ | | | `status` _[ServiceBindingStatus](#servicebindingstatus)_ | | @@ -60,7 +60,7 @@ ServiceBindingList contains a list of ServiceBinding. | --- | --- | | `apiVersion` _string_ | `servicebindings.anynines.com/v1beta3` | `kind` _string_ | `ServiceBindingList` -| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | `items` _[ServiceBinding](#servicebinding) array_ | | #### ServiceBindingSpec diff --git a/docs/application-developers/api-documentation/postgresql-operator/v1alpha1.md b/docs/application-developers/api-documentation/postgresql-operator/v1alpha1.md deleted file mode 100644 index e525437a..00000000 --- a/docs/application-developers/api-documentation/postgresql-operator/v1alpha1.md +++ /dev/null @@ -1,105 +0,0 @@ -# API Reference - -## Packages -- [postgresql.anynines.com/v1alpha1](#postgresqlanyninescomv1alpha1) - -## postgresql.anynines.com/v1alpha1 - -Package v1alpha1 contains API Schema definitions for the postgresql v1alpha1 API group - -### Resource Types -- [Postgresql](#postgresql) -- [PostgresqlList](#postgresqllist) - -#### PostgresConfiguration - -_Appears in:_ -- [PostgresqlSpec](#postgresqlspec) - -| Field | Description | -| --- | --- | -| `maxConnections` _integer_ | MaxConnections determines the maximum number of concurrent connections to the database server. Updating MaxConnections will trigger a restart of the PostgreSQL instance. | -| `sharedBuffers` _integer_ | SharedBuffers sets the amount of memory (usually in 8KB) the database server uses for shared memory buffers. If this value is specified without units, it is taken as blocks, that is BLCKSZ bytes, typically 8kB. This setting must be at least 128 kilobytes. However, settings significantly higher than the minimum are usually needed for good performance. Updating SharedBuffers will trigger a restart of the PostgreSQL instance. | -| `maxReplicationSlots` _integer_ | MaxReplicationSlots specifies the maximum number of replication slots that the server can support. Updating MaxReplicationSlots will trigger a restart of the PostgreSQL instance. | -| `maxWALSenders` _integer_ | MaxWALSenders specifies the maximum number of concurrent connections from standby servers or streaming base backup clients (i.e., the maximum number of simultaneously running WAL sender processes). The value 0 means replication is disabled. Abrupt disconnection of a streaming client might leave an orphaned connection slot behind until a timeout is reached, so this parameter should be set slightly higher than the maximum number of expected clients so disconnected clients can immediately reconnect. Updating MaxWALSenders will trigger a restart of the PostgreSQL instance. | -| `maxLocksPerTransaction` _integer_ | MaxLocksPerTransaction sets the maximum number of locks per transaction. The shared lock table tracks locks on max_locks_per_transaction * (max_connections + max_prepared_transactions) objects (e.g., tables); hence, no more than this many distinct objects can be locked at any one time. This parameter controls the average number of object locks allocated for each transaction; individual transactions can lock more objects as long as the locks of all transactions fit in the lock table. This is not the number of rows that can be locked; that value is unlimited. The default, 64, has historically proven sufficient, but you might need to raise this value if you have queries that touch many different tables in a single transaction, e.g., query of a parent table with many children. Updating MaxLocksPerTransaction will trigger a restart of the PostgreSQL instance. | -| `statementTimeoutMillis` _integer_ | StatementTimeoutMillis is the timeout in milliseconds after which any statement that takes more than the specified number is aborted. The counter is started from the time the command arrives at the server from the client. If LogMinErrorStatement statement is set to ERROR or lower, the statement that timed out will also be logged. A value of zero (the default) turns this off. | -| `sslCiphers` _string_ | SSLCiphers specifies the allowed SSL ciphers (https://www.postgresql.org/docs/13/runtime-config-connection.html#GUC-SSL-CIPHERS) | -| `sslMinProtocolVersion` _string_ | SSLMinProtocolVersion sets the minimum SSL/TLS protocol version to use | -| `tempFileLimitKiloBytes` _integer_ | TempFileLimitKiloBytes specifies the maximum amount of disk space that a process can use for temporary files, such as sort and hash temporary files, or the storage file for a held cursor. | -| `walWriterDelayMillis` _integer_ | WALWriterDelayMillis specifies the time (in milliseconds) between WAL flushed performed in the WAL writer. After flushing WAL the writer sleeps for the length of time given by WALWriterDelayMillis, unless woken up sooner by an asynchronously committing transaction. | -| `synchronousCommit` _string_ | SynchronousCommit specifies whether transaction commit will wait for WAL records to be written to disk before the command returns a success indication to the client. | -| `trackIOTiming` _string_ | TrackIOTiming enables timing of database I/O calls. This parameter is off by default, because it will repeatedly query the operating system for the current time, which may cause significant overhead on some platforms. | -| `archiveTimeoutSeconds` _integer_ | ArchiveTimeoutSeconds is the timeout in seconds which defines the limit how old unarchived data can be, you can set ArchiveTimeoutSeconds to force the server to switch to a new WAL segment file periodically. When this parameter is greater than zero, the server will switch to a new segment file whenever this amount of time has elapsed since the last segment file switch. | -| `clientMinMessages` _string_ | ClientMinMessages specifies which message levels are sent to the client. | -| `logMinMessages` _string_ | LogMinMessages controls which message levels are written to the server log. | -| `logMinErrorStatement` _string_ | LogMinErrorStatement controls which SQL statements that cause an error condition are recorded in the server log. The current SQL statement is included in the log entry for any message of the specified severity or higher. | -| `logStatement` _string_ | LogStatement controls which SQL statements are logged. | -| `logErrorVerbosity` _string_ | LogErrorVerbosity controls the amount of detail written in the server log for each message that is logged. | - -#### Postgresql - -Postgresql is the Schema for the postgresqls API - -_Appears in:_ -- [PostgresqlList](#postgresqllist) - -| Field | Description | -| --- | --- | -| `apiVersion` _string_ | `postgresql.anynines.com/v1alpha1` -| `kind` _string_ | `Postgresql` -| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | -| `spec` _[PostgresqlSpec](#postgresqlspec)_ | | -| `status` _[PostgresqlStatus](#postgresqlstatus)_ | | - -#### PostgresqlList - -PostgresqlList contains a list of Postgresql - -| Field | Description | -| --- | --- | -| `apiVersion` _string_ | `postgresql.anynines.com/v1alpha1` -| `kind` _string_ | `PostgresqlList` -| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | -| `items` _[Postgresql](#postgresql) array_ | | - -#### PostgresqlSchedulingConstraints - -_Appears in:_ -- [PostgresqlSpec](#postgresqlspec) - -| Field | Description | -| --- | --- | -| `tolerations` _[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#toleration-v1-core) array_ | Tolerations is the list of tolerations that the Pods of the PostgreSQL instance will have with respect to the taints of the Kubernetes cluster nodes. It can be used to affect scheduling of the Pods of the PostgreSQL instance on the Kubernetes cluster nodes. More information at https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ and https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#toleration-v1-core . If you don't know what are the specific taints on the nodes of the Kubernetes cluster you're using, you should ask your cluster administrator. Updating this field will result in re-creation and re-scheduling of all the Pods of the PostgreSQL instance, so *there may be downtime*. | -| `affinity` _[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#affinity-v1-core)_ | Affinity groups the fields that govern node affinity, Pod affinity, and Pod anti affinity. It has exactly the same syntax and semantics of the built-in Kubernetes affinity type: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#affinity-v1-core . It is copied as it is in the spec of the Pods of the PostgreSQL instance, and can be used to attract said Pods to specific Kubernetes cluster nodes based on those nodes' labels and on the labels of other Pods running on the nodes. In the latter case repulsion rules between Pods can also be configured via the PodAntiAffinity field. An important use case for repulsion rules would be HA: it can be ensured that the replicas of the same PostgreSQL instance repel each other and end up on different nodes or even availability zones. More information at https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity . To properly configure this field you might need to know which labels the DSI pods get. That's the union of the DSI's `metadata.labels`; the a8s-reserved labels "a8s.a9s/dsi-name": "", "a8s.a9s/dsi-group": "postgresql.anynines.com", "a8s.a9s/dsi-kind": "Postgresql"; the label "a8s.a9s/replication-role", set to the value "master" for the primary pod and "replica" for the standby pods; the standard labels that Kubernetes adds to all pods managed via a statefulset. Updating this field will result in re-creation and re-scheduling of all the Pods of the PostgreSQL instance, so *there may be downtime*. | - -#### PostgresqlSpec - -PostgresqlSpec defines the desired state of Postgresql - -_Appears in:_ -- [Postgresql](#postgresql) - -| Field | Description | -| --- | --- | -| `replicas` _integer_ | Replicas is the number of replicas of the data service in the cluster. Replicas of the PostgreSQL resource will constitute a streaming replication cluster. This value should be an odd number (with the exception of the value 0) to ensure the resultant cluster can establish quorum. Only scaling up is supported and not scaling down of replicas. | -| `version` _integer_ | Version specifies the PostgreSQL version that the instance should use. It is required to pick a version and it cannot be changed afterwards, since major version upgrades are currently unsupported. | -| `resources` _[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#resourcerequirements-v1-core)_ | Resources is the desired compute resource requirements of PostgreSQL container within a pod in the cluster. Updating resources causes the replicas of the PostgreSQL cluster to be killed and recreated one at a time, which could potentially lead to downtime if something goes wrong during the update. | -| `volumeSize` _Quantity_ | VolumeSize sets the size of the persistent volume of the PostgreSQL instance, the minimum size is 0.5Gi. The size is to be specified as a plain integer or as a fixed-point number using one of these suffixes: E, P, T, G, M, K, corresponding to kilo-, mega-, gigabytes, etc. You can also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki, corresponding to kibi-, mebi-, gibibytes, etc. For example a value of "0.5Gi" corresponds to an instance with a persistent volume of 0.5 gibibytes. | -| `postgresConfiguration` _[PostgresConfiguration](#postgresconfiguration)_ | | -| `schedulingConstraints` _[PostgresqlSchedulingConstraints](#postgresqlschedulingconstraints)_ | SchedulingConstraints contains subfields that affect how the Pods of the Postgresql instance will be scheduled onto Kubernetes cluster nodes. The subfields map directly to Kubernetes API primitives such as node taints, tolerations, affinity and (anti)affinity. See the documentation of each subfield for more details. Together, the subfields of SchedulingConstraints allow you to express constraints such as "Pods of this Postgresql instance MUST be scheduled to different availability zones", or "Pods of this Postgresql instance SHOULD preferrably (but not mandatorily) be scheduled to nodes that have a SSD", and many more. As a warning, the subfields of SchedulingConstraints can interfere with each other, so when you set one of them you should consider how it will interact with the values that you set for other subfields. | -| `extensions` _string array_ | Extensions defines a list of PostgreSQL extensions which should be installed. Installing means that the binaries and libraries of the defined extensions are moved to the PostgreSQL extension directory. The extensions are NOT loaded by default (i.e. by using the PostgreSQL "CREATE EXTENSION" command). Updating the list of extensions will cause a rolling update of the PostgreSQL instance. | - -#### PostgresqlStatus - -PostgresqlStatus defines the observed state of Postgresql - -_Appears in:_ -- [Postgresql](#postgresql) - -| Field | Description | -| --- | --- | -| `readyReplicas` _integer_ | | -| `clusterStatus` _string_ | ClusterStatus is a summary of the current status of the cluster. Careful, if ReadyReplicas < 'spec.Replicas' or `spec.Replicas` == 0 the status equals "NotRunning". | -| `error` _string_ | | - diff --git a/docs/application-developers/api-documentation/postgresql-operator/v1beta3.md b/docs/application-developers/api-documentation/postgresql-operator/v1beta3.md index 813f7350..0e9c19f6 100644 --- a/docs/application-developers/api-documentation/postgresql-operator/v1beta3.md +++ b/docs/application-developers/api-documentation/postgresql-operator/v1beta3.md @@ -11,32 +11,13 @@ Package v1beta3 contains API Schema definitions for the postgresql v1beta3 API g - [Postgresql](#postgresql) - [PostgresqlList](#postgresqllist) -#### PostgresConfiguration +#### ExposeOption + +_Underlying type:_ `string` _Appears in:_ - [PostgresqlSpec](#postgresqlspec) -| Field | Description | -| --- | --- | -| `maxConnections` _integer_ | MaxConnections determines the maximum number of concurrent connections to the database server. Updating MaxConnections will trigger a restart of the PostgreSQL instance. | -| `sharedBuffers` _integer_ | SharedBuffers sets the amount of memory (usually in 8KB) the database server uses for shared memory buffers. If this value is specified without units, it is taken as blocks, that is BLCKSZ bytes, typically 8kB. This setting must be at least 128 kilobytes. However, settings significantly higher than the minimum are usually needed for good performance. Updating SharedBuffers will trigger a restart of the PostgreSQL instance. | -| `maxReplicationSlots` _integer_ | MaxReplicationSlots specifies the maximum number of replication slots that the server can support. Updating MaxReplicationSlots will trigger a restart of the PostgreSQL instance. | -| `maxWALSenders` _integer_ | MaxWALSenders specifies the maximum number of concurrent connections from standby servers or streaming base backup clients (i.e., the maximum number of simultaneously running WAL sender processes). The value 0 means replication is disabled. Abrupt disconnection of a streaming client might leave an orphaned connection slot behind until a timeout is reached, so this parameter should be set slightly higher than the maximum number of expected clients so disconnected clients can immediately reconnect. Updating MaxWALSenders will trigger a restart of the PostgreSQL instance. | -| `maxLocksPerTransaction` _integer_ | MaxLocksPerTransaction sets the maximum number of locks per transaction. The shared lock table tracks locks on max_locks_per_transaction * (max_connections + max_prepared_transactions) objects (e.g., tables); hence, no more than this many distinct objects can be locked at any one time. This parameter controls the average number of object locks allocated for each transaction; individual transactions can lock more objects as long as the locks of all transactions fit in the lock table. This is not the number of rows that can be locked; that value is unlimited. The default, 64, has historically proven sufficient, but you might need to raise this value if you have queries that touch many different tables in a single transaction, e.g., query of a parent table with many children. Updating MaxLocksPerTransaction will trigger a restart of the PostgreSQL instance. | -| `statementTimeoutMillis` _integer_ | StatementTimeoutMillis is the timeout in milliseconds after which any statement that takes more than the specified number is aborted. The counter is started from the time the command arrives at the server from the client. If LogMinErrorStatement statement is set to ERROR or lower, the statement that timed out will also be logged. A value of zero (the default) turns this off. | -| `sslCiphers` _string_ | SSLCiphers specifies the allowed SSL ciphers (https://www.postgresql.org/docs/13/runtime-config-connection.html#GUC-SSL-CIPHERS) | -| `sslMinProtocolVersion` _string_ | SSLMinProtocolVersion sets the minimum SSL/TLS protocol version to use | -| `tempFileLimitKiloBytes` _integer_ | TempFileLimitKiloBytes specifies the maximum amount of disk space that a process can use for temporary files, such as sort and hash temporary files, or the storage file for a held cursor. | -| `walWriterDelayMillis` _integer_ | WALWriterDelayMillis specifies the time (in milliseconds) between WAL flushed performed in the WAL writer. After flushing WAL the writer sleeps for the length of time given by WALWriterDelayMillis, unless woken up sooner by an asynchronously committing transaction. | -| `synchronousCommit` _string_ | SynchronousCommit specifies whether transaction commit will wait for WAL records to be written to disk before the command returns a success indication to the client. | -| `trackIOTiming` _string_ | TrackIOTiming enables timing of database I/O calls. This parameter is off by default, because it will repeatedly query the operating system for the current time, which may cause significant overhead on some platforms. | -| `archiveTimeoutSeconds` _integer_ | ArchiveTimeoutSeconds is the timeout in seconds which defines the limit how old unarchived data can be, you can set ArchiveTimeoutSeconds to force the server to switch to a new WAL segment file periodically. When this parameter is greater than zero, the server will switch to a new segment file whenever this amount of time has elapsed since the last segment file switch. | -| `clientMinMessages` _string_ | ClientMinMessages specifies which message levels are sent to the client. | -| `logMinMessages` _string_ | LogMinMessages controls which message levels are written to the server log. | -| `logMinErrorStatement` _string_ | LogMinErrorStatement controls which SQL statements that cause an error condition are recorded in the server log. The current SQL statement is included in the log entry for any message of the specified severity or higher. | -| `logStatement` _string_ | LogStatement controls which SQL statements are logged. | -| `logErrorVerbosity` _string_ | LogErrorVerbosity controls the amount of detail written in the server log for each message that is logged. | - #### Postgresql Postgresql is the Schema for the postgresqls API @@ -48,7 +29,7 @@ _Appears in:_ | --- | --- | | `apiVersion` _string_ | `postgresql.anynines.com/v1beta3` | `kind` _string_ | `Postgresql` -| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `metadata` _[ObjectMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#objectmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | `spec` _[PostgresqlSpec](#postgresqlspec)_ | | | `status` _[PostgresqlStatus](#postgresqlstatus)_ | | @@ -60,9 +41,35 @@ PostgresqlList contains a list of Postgresql. | --- | --- | | `apiVersion` _string_ | `postgresql.anynines.com/v1beta3` | `kind` _string_ | `PostgresqlList` -| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | +| `metadata` _[ListMeta](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#listmeta-v1-meta)_ | Refer to Kubernetes API documentation for fields of `metadata`. | | `items` _[Postgresql](#postgresql) array_ | | +#### PostgresqlParameters + +_Appears in:_ +- [PostgresqlSpec](#postgresqlspec) + +| Field | Description | +| --- | --- | +| `maxConnections` _integer_ | MaxConnections determines the maximum number of concurrent connections to the database server. Updating MaxConnections will trigger a restart of the PostgreSQL instance. | +| `sharedBuffers` _integer_ | SharedBuffers sets the amount of memory (usually in 8KB) the database server uses for shared memory buffers. If this value is specified without units, it is taken as blocks, that is BLCKSZ bytes, typically 8kB. This setting must be at least 128 kilobytes. However, settings significantly higher than the minimum are usually needed for good performance. Updating SharedBuffers will trigger a restart of the PostgreSQL instance. | +| `maxReplicationSlots` _integer_ | MaxReplicationSlots specifies the maximum number of replication slots that the server can support. Updating MaxReplicationSlots will trigger a restart of the PostgreSQL instance. | +| `maxWALSenders` _integer_ | MaxWALSenders specifies the maximum number of concurrent connections from standby servers or streaming base backup clients (i.e., the maximum number of simultaneously running WAL sender processes). The value 0 means replication is disabled. Abrupt disconnection of a streaming client might leave an orphaned connection slot behind until a timeout is reached, so this parameter should be set slightly higher than the maximum number of expected clients so disconnected clients can immediately reconnect. Updating MaxWALSenders will trigger a restart of the PostgreSQL instance. | +| `maxLocksPerTransaction` _integer_ | MaxLocksPerTransaction sets the maximum number of locks per transaction. The shared lock table tracks locks on max_locks_per_transaction * (max_connections + max_prepared_transactions) objects (e.g., tables); hence, no more than this many distinct objects can be locked at any one time. This parameter controls the average number of object locks allocated for each transaction; individual transactions can lock more objects as long as the locks of all transactions fit in the lock table. This is not the number of rows that can be locked; that value is unlimited. The default, 64, has historically proven sufficient, but you might need to raise this value if you have queries that touch many different tables in a single transaction, e.g., query of a parent table with many children. Updating MaxLocksPerTransaction will trigger a restart of the PostgreSQL instance. | +| `statementTimeoutMillis` _integer_ | StatementTimeoutMillis is the timeout in milliseconds after which any statement that takes more than the specified number is aborted. The counter is started from the time the command arrives at the server from the client. If LogMinErrorStatement statement is set to ERROR or lower, the statement that timed out will also be logged. A value of zero (the default) turns this off. | +| `sslCiphers` _string_ | SSLCiphers specifies the allowed SSL ciphers (https://www.postgresql.org/docs/13/runtime-config-connection.html#GUC-SSL-CIPHERS) | +| `sslMinProtocolVersion` _string_ | SSLMinProtocolVersion sets the minimum SSL/TLS protocol version to use | +| `tempFileLimitKiloBytes` _integer_ | TempFileLimitKiloBytes specifies the maximum amount of disk space that a process can use for temporary files, such as sort and hash temporary files, or the storage file for a held cursor. | +| `walWriterDelayMillis` _integer_ | WALWriterDelayMillis specifies the time (in milliseconds) between WAL flushed performed in the WAL writer. After flushing WAL the writer sleeps for the length of time given by WALWriterDelayMillis, unless woken up sooner by an asynchronously committing transaction. | +| `synchronousCommit` _string_ | SynchronousCommit specifies whether transaction commit will wait for WAL records to be written to disk before the command returns a success indication to the client. | +| `trackIOTiming` _string_ | TrackIOTiming enables timing of database I/O calls. This parameter is off by default, because it will repeatedly query the operating system for the current time, which may cause significant overhead on some platforms. | +| `archiveTimeoutSeconds` _integer_ | ArchiveTimeoutSeconds is the timeout in seconds which defines the limit how old unarchived data can be, you can set ArchiveTimeoutSeconds to force the server to switch to a new WAL segment file periodically. When this parameter is greater than zero, the server will switch to a new segment file whenever this amount of time has elapsed since the last segment file switch. | +| `clientMinMessages` _string_ | ClientMinMessages specifies which message levels are sent to the client. | +| `logMinMessages` _string_ | LogMinMessages controls which message levels are written to the server log. | +| `logMinErrorStatement` _string_ | LogMinErrorStatement controls which SQL statements that cause an error condition are recorded in the server log. The current SQL statement is included in the log entry for any message of the specified severity or higher. | +| `logStatement` _string_ | LogStatement controls which SQL statements are logged. | +| `logErrorVerbosity` _string_ | LogErrorVerbosity controls the amount of detail written in the server log for each message that is logged. | + #### PostgresqlSchedulingConstraints _Appears in:_ @@ -70,8 +77,8 @@ _Appears in:_ | Field | Description | | --- | --- | -| `tolerations` _[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#toleration-v1-core) array_ | Tolerations is the list of tolerations that the Pods of the PostgreSQL instance will have with respect to the taints of the Kubernetes cluster nodes. It can be used to affect scheduling of the Pods of the PostgreSQL instance on the Kubernetes cluster nodes. More information at https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ and https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#toleration-v1-core . If you don't know what are the specific taints on the nodes of the Kubernetes cluster you're using, you should ask your cluster administrator. Updating this field will result in re-creation and re-scheduling of all the Pods of the PostgreSQL instance, so *there may be downtime*. | -| `affinity` _[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#affinity-v1-core)_ | Affinity groups the fields that govern node affinity, Pod affinity, and Pod anti affinity. It has exactly the same syntax and semantics of the built-in Kubernetes affinity type: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.22/#affinity-v1-core . It is copied as it is in the spec of the Pods of the PostgreSQL instance, and can be used to attract said Pods to specific Kubernetes cluster nodes based on those nodes' labels and on the labels of other Pods running on the nodes. In the latter case repulsion rules between Pods can also be configured via the PodAntiAffinity field. An important use case for repulsion rules would be HA: it can be ensured that the replicas of the same PostgreSQL instance repel each other and end up on different nodes or even availability zones. More information at https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity . To properly configure this field you might need to know which labels the DSI pods get. That's the union of the DSI's `metadata.labels`; the a8s-reserved labels "a8s.a9s/dsi-name": "", "a8s.a9s/dsi-group": "postgresql.anynines.com", "a8s.a9s/dsi-kind": "Postgresql"; the label "a8s.a9s/replication-role", set to the value "master" for the primary pod and "replica" for the standby pods; the standard labels that Kubernetes adds to all pods managed via a statefulset. Updating this field will result in re-creation and re-scheduling of all the Pods of the PostgreSQL instance, so *there may be downtime*. | +| `tolerations` _[Toleration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#toleration-v1-core) array_ | Tolerations is the list of tolerations that the Pods of the PostgreSQL instance will have with respect to the taints of the Kubernetes cluster nodes. It can be used to affect scheduling of the Pods of the PostgreSQL instance on the Kubernetes cluster nodes. More information at https://kubernetes.io/docs/concepts/scheduling-eviction/taint-and-toleration/ and https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#toleration-v1-core . If you don't know what are the specific taints on the nodes of the Kubernetes cluster you're using, you should ask your cluster administrator. Updating this field will result in re-creation and re-scheduling of all the Pods of the PostgreSQL instance, so *there may be downtime*. | +| `affinity` _[Affinity](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#affinity-v1-core)_ | Affinity groups the fields that govern node affinity, Pod affinity, and Pod anti affinity. It has exactly the same syntax and semantics of the built-in Kubernetes affinity type: https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.24/#affinity-v1-core . It is copied as it is in the spec of the Pods of the PostgreSQL instance, and can be used to attract said Pods to specific Kubernetes cluster nodes based on those nodes' labels and on the labels of other Pods running on the nodes. In the latter case repulsion rules between Pods can also be configured via the PodAntiAffinity field. An important use case for repulsion rules would be HA: it can be ensured that the replicas of the same PostgreSQL instance repel each other and end up on different nodes or even availability zones. More information at https://kubernetes.io/docs/concepts/scheduling-eviction/assign-pod-node/#affinity-and-anti-affinity . To properly configure this field you might need to know which labels the DSI pods get. That's the union of the DSI's `metadata.labels`; the a8s-reserved labels "a8s.a9s/dsi-name": "", "a8s.a9s/dsi-group": "postgresql.anynines.com", "a8s.a9s/dsi-kind": "Postgresql"; the label "a8s.a9s/replication-role", set to the value "master" for the primary pod and "replica" for the standby pods; the standard labels that Kubernetes adds to all pods managed via a statefulset. Updating this field will result in re-creation and re-scheduling of all the Pods of the PostgreSQL instance, so *there may be downtime*. | #### PostgresqlSpec @@ -84,11 +91,14 @@ _Appears in:_ | --- | --- | | `replicas` _integer_ | Replicas is the number of replicas of the data service in the cluster. Replicas of the PostgreSQL resource will constitute a streaming replication cluster. This value should be an odd number (with the exception of the value 0) to ensure the resultant cluster can establish quorum. Only scaling up is supported and not scaling down of replicas. | | `version` _integer_ | Version specifies the PostgreSQL version that the instance should use. It is required to pick a version and it cannot be changed afterwards, since major version upgrades are currently unsupported. | -| `resources` _[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.23/#resourcerequirements-v1-core)_ | Resources is the desired compute resource requirements of PostgreSQL container within a pod in the cluster. Updating resources causes the replicas of the PostgreSQL cluster to be killed and recreated one at a time, which could potentially lead to downtime if something goes wrong during the update. | +| `resources` _[ResourceRequirements](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.26/#resourcerequirements-v1-core)_ | Resources is the desired compute resource requirements of PostgreSQL container within a pod in the cluster. Updating resources causes the replicas of the PostgreSQL cluster to be killed and recreated one at a time, which could potentially lead to downtime if something goes wrong during the update. | | `volumeSize` _Quantity_ | VolumeSize sets the size of the persistent volume of the PostgreSQL instance, the minimum size is 0.5Gi. The size is to be specified as a plain integer or as a fixed-point number using one of these suffixes: E, P, T, G, M, K, corresponding to kilo-, mega-, gigabytes, etc. You can also use the power-of-two equivalents: Ei, Pi, Ti, Gi, Mi, Ki, corresponding to kibi-, mebi-, gibibytes, etc. For example a value of "0.5Gi" corresponds to an instance with a persistent volume of 0.5 gibibytes. | -| `postgresConfiguration` _[PostgresConfiguration](#postgresconfiguration)_ | | +| `parameters` _[PostgresqlParameters](#postgresqlparameters)_ | | | `schedulingConstraints` _[PostgresqlSchedulingConstraints](#postgresqlschedulingconstraints)_ | SchedulingConstraints contains subfields that affect how the Pods of the Postgresql instance will be scheduled onto Kubernetes cluster nodes. The subfields map directly to Kubernetes API primitives such as node taints, tolerations, affinity and (anti)affinity. See the documentation of each subfield for more details. Together, the subfields of SchedulingConstraints allow you to express constraints such as "Pods of this Postgresql instance MUST be scheduled to different availability zones", or "Pods of this Postgresql instance SHOULD preferably (but not mandatorily) be scheduled to nodes that have a SSD", and many more. As a warning, the subfields of SchedulingConstraints can interfere with each other, so when you set one of them you should consider how it will interact with the values that you set for other subfields. | | `extensions` _string array_ | Extensions defines a list of PostgreSQL extensions which should be installed. Installing means that the binaries and libraries of the defined extensions are moved to the PostgreSQL extension directory. The extensions are NOT loaded by default (i.e. by using the PostgreSQL "CREATE EXTENSION" command). Updating the list of extensions will cause a rolling update of the PostgreSQL instance. | +| `enableReadOnlyService` _boolean_ | EnableReadOnlyService enables the creation of a read-only service. A read-only service is meant for read operations and allows for load-balancing across different PostgreSQL cluster members. Stale reads are possible when reading from this service. By default this feature is disabled. | +| `readOnlyTargetNodes` _string_ | ReadOnlyTargetNodes specifies the target PostgreSQL cluster members which the read-only service points to. "replicas" means that only the PostgreSQL cluster replicas are used for the read-only service. "all" means that the read-only service points to all PostgreSQL cluster members. By default this feature only points to the PostgreSQL cluster replicas. | +| `expose` _[ExposeOption](#exposeoption)_ | Expose determines where (and how) the DSI can be accessed from. Currently supported values are - "Internal": the DSI will be accessible only from inside the K8s cluster - "LoadBalancer": the DSI will receive dedicated load balancers with reachable IP addresses that can be used from external locations . This is only supported on K8s clusters that support external load balancers. This field applies to all the services backed by the DSI, that is, both the master-only one and the read-only one; the "LoadBalancer" value means that each service will get a dedicated LoadBalancer. | #### PostgresqlStatus diff --git a/docs/application-developers/usage_overview.md b/docs/application-developers/usage_overview.md index 28acf9d3..f275b229 100644 --- a/docs/application-developers/usage_overview.md +++ b/docs/application-developers/usage_overview.md @@ -204,33 +204,33 @@ If you want to restore a data service instance to a previous state it had, you c a previously taken backup (as shown in section [Take a Backup of the PostgreSQL Instance](#take-a-backup-of-the-postgresql-instance)). -To do that, you have to create a custom API object of kind `Recovery` (a custom kind which is part -of a8s). A `Recovery` API object fields identify the `Backup` API object to use to perform the -restore. The `Recovery` will always be performed on the data service instance from which the backup -was taken. Stay tuned for a complete reference of all the fields of `Recovery` API objects. +To do that, you have to create a custom API object of kind `Restore` (a custom kind which is part +of a8s). A `Restore` API object fields identify the `Backup` API object to use to perform the +restore. The `Restore` will always be performed on the data service instance from which the backup +was taken. Stay tuned for a complete reference of all the fields of `Restore` API objects. At [examples/restore.yaml](/examples/restore.yaml) there's the yaml manifest of an example -`Recovery` that points to the PostgreSQL instance that you previously deployed. Run: +`Restore` that points to the PostgreSQL instance that you previously deployed. Run: ```shell kubectl apply --filename examples/restore.yaml ``` The a8s control plane will react by downloading the relevant backup and using it to restore the data -service instance. This might take some time, to learn when the recovery has completed, run: +service instance. This might take some time, to learn when the restore has completed, run: ```shell -kubectl wait recovery recovery-sample --for=condition=complete +kubectl wait restore recovery-sample --for=condition=complete ``` -and wait until the command has finished. If your recovery takes a long time to complete, you may +and wait until the command has finished. If your restore takes a long time to complete, you may need to run the `kubectl wait` command multiple times or adjust the timeout using the `--timeout=s` flag. -When you want to delete a `Recovery`, run: +When you want to delete a `Restore`, run: ```shell -kubectl delete recovery +kubectl delete restore ``` ## Visualize the Logs of the PostgreSQL Instance diff --git a/docs/current_limitations.md b/docs/current_limitations.md index aa1e3d96..ad2cb43b 100644 --- a/docs/current_limitations.md +++ b/docs/current_limitations.md @@ -36,7 +36,6 @@ and use it to write/read data to/from the instance, etc...). - Given an instance, there's no multi-tenancy: all service bindings to it will share the same database. -- Instances cannot be used from outside the cluster. ## Backup and Restore diff --git a/docs/platform-operators/installing_framework.md b/docs/platform-operators/installing_framework.md index ca8709d7..508cfcf5 100644 --- a/docs/platform-operators/installing_framework.md +++ b/docs/platform-operators/installing_framework.md @@ -1,20 +1,23 @@ # Install the a8s Control Plane -- [Prerequisites](#prerequisites) - - [Configure Backups Store](#configure-backups-store) -- [Configure Images](#configure-images) -- [Install the a8s Control Plane](#install-the-a8s-control-plane-1) +- [Install the a8s Control Plane](#install-the-a8s-control-plane) + - [Prerequisites](#prerequisites) + - [Configure Backups Store](#configure-backups-store) + - [Configure Images](#configure-images) + - [Install the a8s Control Plane](#install-the-a8s-control-plane-1) - [Using Static Manifests](#using-static-manifests) - - [Install the cert-manager](#install-the-cert-manager) - - [Install the Control Plane with Manifests](#install-the-control-plane-with-manifests) + - [Install the cert-manager](#install-the-cert-manager) + - [Install the Control Plane with Manifests](#install-the-control-plane-with-manifests) - [Using the OLM](#using-the-olm) - - [Install the OLM](#install-the-olm) - - [Install the Control Plane with OLM](#install-the-control-plane-with-olm) - - [Uninstalling the Control Plane](#uninstalling-the-control-plane) -- [(Optional) Install the Logging Infrastructure](#optional-install-the-logging-infrastructure) -- [Uninstall the Logging Infrastructure](#uninstall-the-logging-infrastructure) -- [(Optional) Install the Metrics Infrastructure](#optional-install-the-metrics-infrastructure) -- [Uninstall the Metrics Infrastructure](#uninstall-the-metrics-infrastructure) + - [Install the OLM](#install-the-olm) + - [Install the Control Plane with OLM](#install-the-control-plane-with-olm) + - [Uninstalling the Control Plane](#uninstalling-the-control-plane) + - [(Optional) Install the Logging Infrastructure](#optional-install-the-logging-infrastructure) + - [Virtual Memory Usage](#virtual-memory-usage) + - [Disabling Virtual Memory Usage](#disabling-virtual-memory-usage) + - [Uninstall the Logging Infrastructure](#uninstall-the-logging-infrastructure) + - [(Optional) Install the Metrics Infrastructure](#optional-install-the-metrics-infrastructure) + - [Uninstall the Metrics Infrastructure](#uninstall-the-metrics-infrastructure) ## Prerequisites @@ -37,13 +40,13 @@ file. You MUST use the file names shown in the subsequent commands. ```shell # create file that stores the ID of the key -echo > deploy/a8s/backup-config/access-key-id +echo -n > deploy/a8s/backup-config/access-key-id # create file that stores the secret value of the key -echo > deploy/a8s/backup-config/secret-access-key +echo -n > deploy/a8s/backup-config/secret-access-key # create file that stores password for backup encryption -echo > deploy/a8s/backup-config/encryption-password +echo -n > deploy/a8s/backup-config/encryption-password # create file with other information about the bucket cp deploy/a8s/backup-config/backup-store-config.yaml.template deploy/a8s/backup-config/backup-store-config.yaml @@ -81,6 +84,8 @@ Also, your changes will be overwritten when deploying with the OLM and during an update. If you need to edit the configMap, reapply it when you deployed or updated the framework. In this case you might want to disable automatic updates. +> WARNING: Issues may occure when running spilo with the MobilityDB Postgresql extension on arm-based systems. The latest spilo images natively support both amd and arm architectures, which is not the case for MobilityDB. + ## Install the a8s Control Plane The a8s Control Plane can be deployed with the help of the static manifests you @@ -93,7 +98,7 @@ OLM. ### Using Static Manifests -#### Install the Cert-Manager +#### Install the cert-manager The a8s framework relies on the [cert-manager][cert-manager] to generate TLS certificates, therefore you will first have to install it on your cluster. @@ -134,10 +139,9 @@ order. More precisely, it will: -1. Create two namespaces called `a8s-system` and `postgresql-system`. - The `postgresql-system` namespace is used for the `postgresql-controller-manager`, the rest of - the a8s framework components (`a8s-backup-controller-manager` and - `service-binding-controller-manager`) are running in `a8s-system`. +1. Create a namespace called `a8s-system`. + The a8s framework components `postgresql-controller-manager`, `a8s-backup-controller-manager` and + `service-binding-controller-manager` will be running in `a8s-system` namespace. 2. Register multiple CustomResourceDefinitions (CRDs) 3. Create three deployments, one for each a8s framework component 4. Create multiple ClusterRoles and ClusterRoleBindings: @@ -151,7 +155,7 @@ More precisely, it will: - postgresql-spilo-role: gives spilo the required permissions to access Kubernetes resources -5. Create one Role and RoleBinding: +5. Create one Role and RoleBinding for each a8s framework component: - -leader-election-role and -leader-election-rolebinding: used for communication between multiple controllers of the same type. @@ -168,22 +172,31 @@ are ready (value `1/1` under the `READY` column): ```shell watch kubectl get deployment --namespace a8s-system -watch kubectl get deployment --namespace postgresql-system ``` -the output of the first command should be similar to: +the output of the command should be similar to: ```shell NAME READY UP-TO-DATE AVAILABLE AGE a8s-backup-controller-manager 1/1 1 1 105s service-binding-controller-manager 1/1 1 1 105s +postgresql-controller-manager 1/1 1 1 105s ``` -the output of the second command should be similar to: +#### Uninstall the Control Plane with Manifests + +To uninstall the control plane, use: + +```shell +kubectl delete --kustomize deploy/a8s/manifests +``` + +This will delete the Kubernetes resources that make up the a8s control plane and their associated CRDs. + +To uninstall the cert-manager components, use: ```shell -NAME READY UP-TO-DATE AVAILABLE AGE -postgresql-controller-manager 1/1 1 1 25m +kubectl delete --kustomize deploy/cert-manager ``` ### Using the OLM @@ -226,7 +239,7 @@ to apply all OLM resources necessary. In more detail, this will create: Additionally, the kustomization creates the secret and configMap for the backup bucket configuration. -#### Uninstalling the Control Plane +#### Uninstalling the Control Plane with OLM To uninstall the control plane use: diff --git a/docs/technical_requirements.md b/docs/technical_requirements.md index b880f24e..48d1d10e 100644 --- a/docs/technical_requirements.md +++ b/docs/technical_requirements.md @@ -10,6 +10,11 @@ To try out a8s you will need: - one [StorageClass][storage-class] marked as `default` in the Kubernetes cluster - one AWS S3 bucket for storing Backups +If you want your data service instances to have IPs reachable from outside the K8s cluster +(by using the feature described +[here](./application-developers/advanced_configuration.md#usage-outside-the-kubernetes-cluster)), +the K8s cluster must support [external load balancers][k8s-load-balancer-services]. + To access the included Dashboards (Grafana, OpenSearch Dashboards) it is also recommended to deploy an Ingress Controller on your cluster. To access the Dashboards you can then expose the Dashboard Services, for more information on @@ -55,3 +60,4 @@ marginally. [storage-class]: https://kubernetes.io/docs/concepts/storage/storage-classes/ [k8s-ingress]: https://kubernetes.io/docs/concepts/services-networking/ingress/ +[k8s-load-balancer-services]: https://kubernetes.io/docs/concepts/services-networking/service/#loadbalancer diff --git a/examples/postgresql-instance-exposed.yaml b/examples/postgresql-instance-exposed.yaml new file mode 100644 index 00000000..c0a1df0e --- /dev/null +++ b/examples/postgresql-instance-exposed.yaml @@ -0,0 +1,14 @@ +apiVersion: postgresql.anynines.com/v1beta3 +kind: Postgresql +metadata: + name: sample-pg-cluster +spec: + replicas: 3 + volumeSize: 1Gi + version: 14 + expose: LoadBalancer + resources: + requests: + cpu: 100m + limits: + memory: 100Mi diff --git a/test/chaos-tests/backup/backup_chaos_suite_test.go b/test/chaos-tests/backup/backup_chaos_suite_test.go index 789503e9..49879bb9 100644 --- a/test/chaos-tests/backup/backup_chaos_suite_test.go +++ b/test/chaos-tests/backup/backup_chaos_suite_test.go @@ -38,8 +38,7 @@ var _ = BeforeSuite(func() { // Parse environmental variable configuration config, err := framework.ParseEnv() Expect(err).To(BeNil(), "failed to parse environmental variables as configuration") - kubeconfigPath, instanceNamePrefix, dataservice, testingNamespace = - framework.ConfigToVars(config) + kubeconfigPath, instanceNamePrefix, dataservice, testingNamespace = framework.ConfigToVars(config) Expect(strings.ToLower(dataservice) == "postgresql").To(BeTrue(), "This test suite only supports PostgreSQL") @@ -57,7 +56,6 @@ var _ = BeforeSuite(func() { Expect(namespace.CreateIfNotExists(ctx, testingNamespace, k8sClient)). To(Succeed(), "failed to create testing namespace") - }) var _ = AfterSuite(func() { diff --git a/test/chaos-tests/backup/backup_chaos_test.go b/test/chaos-tests/backup/backup_chaos_test.go index 2b5bb027..18758124 100644 --- a/test/chaos-tests/backup/backup_chaos_test.go +++ b/test/chaos-tests/backup/backup_chaos_test.go @@ -8,8 +8,8 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - backupv1alpha1 "github.com/anynines/a8s-backup-manager/api/v1alpha1" - sbv1alpha1 "github.com/anynines/a8s-service-binding-controller/api/v1alpha1" + backupv1beta3 "github.com/anynines/a8s-backup-manager/api/v1beta3" + sbv1beta3 "github.com/anynines/a8s-service-binding-controller/api/v1beta3" "github.com/anynines/a8s-deployment/test/framework" bkp "github.com/anynines/a8s-deployment/test/framework/backup" @@ -49,8 +49,8 @@ var ( portForwardStopCh chan struct{} localPort int - sb *sbv1alpha1.ServiceBinding - backup *backupv1alpha1.Backup + sb *sbv1beta3.ServiceBinding + backup *backupv1beta3.Backup serviceBindingData secret.SecretData instance *postgresql.Postgresql client dsi.DSIClient @@ -112,7 +112,7 @@ var _ = Describe("Backup Chaos Tests", func() { fmt.Sprintf("failed to delete service binding %s/%s", sb.GetNamespace(), sb.GetName())) dsi.WaitForDeletion(ctx, instance.GetClientObject(), k8sClient) - //TODO: Wait for deletion for all secondary objects + // TODO: Wait for deletion for all secondary objects }) It("Backup agent crashes while processing a backup", func() { diff --git a/test/chaos-tests/postgresql/postgresql_chaos_suite_test.go b/test/chaos-tests/postgresql/postgresql_chaos_suite_test.go index aab316ff..4b5f12c3 100644 --- a/test/chaos-tests/postgresql/postgresql_chaos_suite_test.go +++ b/test/chaos-tests/postgresql/postgresql_chaos_suite_test.go @@ -39,8 +39,7 @@ var _ = BeforeSuite(func() { // Parse environmental variable configuration config, err := framework.ParseEnv() Expect(err).To(BeNil(), "failed to parse environmental variables as configuration") - kubeconfigPath, instanceNamePrefix, dataservice, testingNamespace = - framework.ConfigToVars(config) + kubeconfigPath, instanceNamePrefix, dataservice, testingNamespace = framework.ConfigToVars(config) Expect(strings.ToLower(dataservice) == "postgresql").To(BeTrue(), "This test suite only supports PostgreSQL") @@ -58,7 +57,6 @@ var _ = BeforeSuite(func() { Expect(namespace.CreateIfNotExists(ctx, testingNamespace, k8sClient)). To(Succeed(), "failed to create testing namespace") - }) var _ = AfterSuite(func() { diff --git a/test/chaos-tests/postgresql/postgresql_chaos_test.go b/test/chaos-tests/postgresql/postgresql_chaos_test.go index b9cb1b62..d3714ef1 100644 --- a/test/chaos-tests/postgresql/postgresql_chaos_test.go +++ b/test/chaos-tests/postgresql/postgresql_chaos_test.go @@ -14,7 +14,7 @@ import ( "github.com/anynines/a8s-deployment/test/framework/postgresql" "github.com/anynines/a8s-deployment/test/framework/secret" "github.com/anynines/a8s-deployment/test/framework/servicebinding" - sbv1alpha1 "github.com/anynines/a8s-service-binding-controller/api/v1alpha1" + sbv1beta3 "github.com/anynines/a8s-service-binding-controller/api/v1beta3" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" ) @@ -39,7 +39,7 @@ var ( portForwardStopCh chan struct{} localPort int - sb *sbv1alpha1.ServiceBinding + sb *sbv1beta3.ServiceBinding serviceBindingData secret.SecretData instance *postgresql.Postgresql client dsi.DSIClient diff --git a/test/e2e/backup/backup_test.go b/test/e2e/backup/backup_test.go index 66419534..56f33fe9 100644 --- a/test/e2e/backup/backup_test.go +++ b/test/e2e/backup/backup_test.go @@ -8,14 +8,14 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - backupv1alpha1 "github.com/anynines/a8s-backup-manager/api/v1alpha1" + backupv1beta3 "github.com/anynines/a8s-backup-manager/api/v1beta3" "github.com/anynines/a8s-deployment/test/framework" bkp "github.com/anynines/a8s-deployment/test/framework/backup" "github.com/anynines/a8s-deployment/test/framework/dsi" rst "github.com/anynines/a8s-deployment/test/framework/restore" "github.com/anynines/a8s-deployment/test/framework/secret" "github.com/anynines/a8s-deployment/test/framework/servicebinding" - sbv1alpha1 "github.com/anynines/a8s-service-binding-controller/api/v1alpha1" + sbv1beta3 "github.com/anynines/a8s-service-binding-controller/api/v1beta3" ) const ( @@ -35,9 +35,9 @@ var ( portForwardStopCh chan struct{} localPort int - sb *sbv1alpha1.ServiceBinding - backup *backupv1alpha1.Backup - restore *backupv1alpha1.Restore + sb *sbv1beta3.ServiceBinding + backup *backupv1beta3.Backup + restore *backupv1beta3.Restore instance dsi.Object client dsi.DSIClient ) @@ -100,7 +100,7 @@ var _ = Describe("Backup", func() { fmt.Sprintf("failed to delete restore %s/%s", restore.GetNamespace(), restore.GetName())) dsi.WaitForDeletion(ctx, instance.GetClientObject(), k8sClient) - //TODO: Wait for deletion for all secondary objects + // TODO: Wait for deletion for all secondary objects }) It("Performs backup and restore of instance", func() { diff --git a/test/e2e/patroni/patroni_test.go b/test/e2e/patroni/patroni_test.go index 4ecff8c9..73bf65f2 100644 --- a/test/e2e/patroni/patroni_test.go +++ b/test/e2e/patroni/patroni_test.go @@ -17,7 +17,7 @@ import ( "github.com/anynines/a8s-deployment/test/framework/dsi" "github.com/anynines/a8s-deployment/test/framework/postgresql" "github.com/anynines/a8s-deployment/test/framework/secret" - "github.com/anynines/postgresql-operator/api/v1alpha1" + "github.com/anynines/postgresql-operator/api/v1beta3" ) const ( @@ -57,7 +57,7 @@ var ( instance dsi.Object client dsi.DSIClient - pg *v1alpha1.Postgresql + pg *v1beta3.Postgresql adminSecretData secret.SecretData ) @@ -136,7 +136,7 @@ var _ = Describe("Patroni end-to-end Tests", func() { }) By("checking that the defaults are set correctly", func() { - var expectedConfig = []struct { + expectedConfig := []struct { parameter, value string }{ {ArchiveTimeout, strconv.Itoa(defaultArchiveTimeoutSeconds)}, @@ -187,7 +187,7 @@ var _ = Describe("Patroni end-to-end Tests", func() { // Cast interface to concrete struct so that we can access fields // directly - pg, ok = instance.GetClientObject().(*v1alpha1.Postgresql) + pg, ok = instance.GetClientObject().(*v1beta3.Postgresql) Expect(ok).To(BeTrue(), "failed to cast object interface to PostgreSQL struct") @@ -223,29 +223,29 @@ var _ = Describe("Patroni end-to-end Tests", func() { }) By("checking that the custom configuration is set correctly", func() { - var expectedConfig = []struct { + expectedConfig := []struct { parameter, value string }{ - {ArchiveTimeout, strconv.Itoa(pg.Spec.PostgresConfiguration.ArchiveTimeoutSeconds) + "s"}, - {TempFileLimit, strconv.Itoa(pg.Spec.PostgresConfiguration.TempFileLimitKiloBytes)}, - {TrackIOTiming, pg.Spec.PostgresConfiguration.TrackIOTiming}, - {StatementTimeout, strconv.Itoa(pg.Spec.PostgresConfiguration.StatementTimeoutMillis) + "ms"}, - {ClientMinMessages, pg.Spec.PostgresConfiguration.ClientMinMessages}, - {LogMinMessages, pg.Spec.PostgresConfiguration.LogMinMessages}, - {LogMinErrorStatement, pg.Spec.PostgresConfiguration.LogMinErrorStatement}, - {LogStatement, pg.Spec.PostgresConfiguration.LogStatement}, - {LogErrorVerbosity, strings.ToLower(pg.Spec.PostgresConfiguration.LogErrorVerbosity)}, - {SSLCiphers, pg.Spec.PostgresConfiguration.SSLCiphers}, - {SSLMinProtocolVersion, pg.Spec.PostgresConfiguration.SSLMinProtocolVersion}, - {WALWriterDelay, strconv.Itoa(pg.Spec.PostgresConfiguration.WALWriterDelayMillis) + "ms"}, - {SynchronousCommit, pg.Spec.PostgresConfiguration.SynchronousCommit}, - {MaxConnections, strconv.Itoa(pg.Spec.PostgresConfiguration.MaxConnections)}, + {ArchiveTimeout, strconv.Itoa(pg.Spec.Parameters.ArchiveTimeoutSeconds) + "s"}, + {TempFileLimit, strconv.Itoa(pg.Spec.Parameters.TempFileLimitKiloBytes)}, + {TrackIOTiming, pg.Spec.Parameters.TrackIOTiming}, + {StatementTimeout, strconv.Itoa(pg.Spec.Parameters.StatementTimeoutMillis) + "ms"}, + {ClientMinMessages, pg.Spec.Parameters.ClientMinMessages}, + {LogMinMessages, pg.Spec.Parameters.LogMinMessages}, + {LogMinErrorStatement, pg.Spec.Parameters.LogMinErrorStatement}, + {LogStatement, pg.Spec.Parameters.LogStatement}, + {LogErrorVerbosity, strings.ToLower(pg.Spec.Parameters.LogErrorVerbosity)}, + {SSLCiphers, pg.Spec.Parameters.SSLCiphers}, + {SSLMinProtocolVersion, pg.Spec.Parameters.SSLMinProtocolVersion}, + {WALWriterDelay, strconv.Itoa(pg.Spec.Parameters.WALWriterDelayMillis) + "ms"}, + {SynchronousCommit, pg.Spec.Parameters.SynchronousCommit}, + {MaxConnections, strconv.Itoa(pg.Spec.Parameters.MaxConnections)}, // SharedBuffers is not being set or updated. // https://github.com/anynines/postgresql-operator/issues/75 // {SharedBuffers, "200MB"}, // 2024 is converted to 200MB - {MaxReplicationSlots, strconv.Itoa(pg.Spec.PostgresConfiguration.MaxReplicationSlots)}, - {MaxWALSenders, strconv.Itoa(pg.Spec.PostgresConfiguration.MaxWALSenders)}, - {MaxLocksPerTransaction, strconv.Itoa(*pg.Spec.PostgresConfiguration.MaxLocksPerTransaction)}, + {MaxReplicationSlots, strconv.Itoa(pg.Spec.Parameters.MaxReplicationSlots)}, + {MaxWALSenders, strconv.Itoa(pg.Spec.Parameters.MaxWALSenders)}, + {MaxLocksPerTransaction, strconv.Itoa(*pg.Spec.Parameters.MaxLocksPerTransaction)}, } for _, setting := range expectedConfig { @@ -309,7 +309,7 @@ var _ = Describe("Patroni end-to-end Tests", func() { To(Succeed(), fmt.Sprintf("failed to get instance %s/%s", instance.GetNamespace(), instance.GetName())) - pg, ok = newInstance.GetClientObject().(*v1alpha1.Postgresql) + pg, ok = newInstance.GetClientObject().(*v1beta3.Postgresql) Expect(ok).To(BeTrue(), "failed to cast object interface to PostgreSQL struct") @@ -348,7 +348,7 @@ var _ = Describe("Patroni end-to-end Tests", func() { // parameter in the table driven tests below for the sake // of verbosity. probeErr := client.CheckParameter(ctx, ArchiveTimeout, - strconv.Itoa(pg.Spec.PostgresConfiguration.ArchiveTimeoutSeconds)+"s") + strconv.Itoa(pg.Spec.Parameters.ArchiveTimeoutSeconds)+"s") if probeErr != nil { close(portForwardStopCh) @@ -361,29 +361,29 @@ var _ = Describe("Patroni end-to-end Tests", func() { }) By("checking that the custom config is set correctly", func() { - var expectedConfig = []struct { + expectedConfig := []struct { parameter, value string }{ - {ArchiveTimeout, strconv.Itoa(pg.Spec.PostgresConfiguration.ArchiveTimeoutSeconds) + "s"}, - {TempFileLimit, strconv.Itoa(pg.Spec.PostgresConfiguration.TempFileLimitKiloBytes)}, - {TrackIOTiming, pg.Spec.PostgresConfiguration.TrackIOTiming}, - {StatementTimeout, strconv.Itoa(pg.Spec.PostgresConfiguration.StatementTimeoutMillis) + "ms"}, - {ClientMinMessages, pg.Spec.PostgresConfiguration.ClientMinMessages}, - {LogMinMessages, pg.Spec.PostgresConfiguration.LogMinMessages}, - {LogMinErrorStatement, pg.Spec.PostgresConfiguration.LogMinErrorStatement}, - {LogStatement, pg.Spec.PostgresConfiguration.LogStatement}, - {LogErrorVerbosity, strings.ToLower(pg.Spec.PostgresConfiguration.LogErrorVerbosity)}, - {SSLCiphers, pg.Spec.PostgresConfiguration.SSLCiphers}, - {SSLMinProtocolVersion, pg.Spec.PostgresConfiguration.SSLMinProtocolVersion}, - {WALWriterDelay, strconv.Itoa(pg.Spec.PostgresConfiguration.WALWriterDelayMillis) + "ms"}, - {SynchronousCommit, pg.Spec.PostgresConfiguration.SynchronousCommit}, - {MaxConnections, strconv.Itoa(pg.Spec.PostgresConfiguration.MaxConnections)}, + {ArchiveTimeout, strconv.Itoa(pg.Spec.Parameters.ArchiveTimeoutSeconds) + "s"}, + {TempFileLimit, strconv.Itoa(pg.Spec.Parameters.TempFileLimitKiloBytes)}, + {TrackIOTiming, pg.Spec.Parameters.TrackIOTiming}, + {StatementTimeout, strconv.Itoa(pg.Spec.Parameters.StatementTimeoutMillis) + "ms"}, + {ClientMinMessages, pg.Spec.Parameters.ClientMinMessages}, + {LogMinMessages, pg.Spec.Parameters.LogMinMessages}, + {LogMinErrorStatement, pg.Spec.Parameters.LogMinErrorStatement}, + {LogStatement, pg.Spec.Parameters.LogStatement}, + {LogErrorVerbosity, strings.ToLower(pg.Spec.Parameters.LogErrorVerbosity)}, + {SSLCiphers, pg.Spec.Parameters.SSLCiphers}, + {SSLMinProtocolVersion, pg.Spec.Parameters.SSLMinProtocolVersion}, + {WALWriterDelay, strconv.Itoa(pg.Spec.Parameters.WALWriterDelayMillis) + "ms"}, + {SynchronousCommit, pg.Spec.Parameters.SynchronousCommit}, + {MaxConnections, strconv.Itoa(pg.Spec.Parameters.MaxConnections)}, // SharedBuffers is not being set or updated. // https://github.com/anynines/postgresql-operator/issues/75 // {SharedBuffers, "200MB"}, // 2024 is converted to 200MB - {MaxReplicationSlots, strconv.Itoa(pg.Spec.PostgresConfiguration.MaxReplicationSlots)}, - {MaxWALSenders, strconv.Itoa(pg.Spec.PostgresConfiguration.MaxWALSenders)}, - {MaxLocksPerTransaction, strconv.Itoa(*pg.Spec.PostgresConfiguration.MaxLocksPerTransaction)}, + {MaxReplicationSlots, strconv.Itoa(pg.Spec.Parameters.MaxReplicationSlots)}, + {MaxWALSenders, strconv.Itoa(pg.Spec.Parameters.MaxWALSenders)}, + {MaxLocksPerTransaction, strconv.Itoa(*pg.Spec.Parameters.MaxLocksPerTransaction)}, } for _, setting := range expectedConfig { @@ -398,7 +398,6 @@ var _ = Describe("Patroni end-to-end Tests", func() { }, framework.AsyncOpsTimeoutMins, 1*time.Second).Should(Succeed(), fmt.Sprintf("unable to check custom config is set correctly on update for %s/%s", instance.GetNamespace(), instance.GetName())) - } }) @@ -413,7 +412,8 @@ var _ = Describe("Patroni end-to-end Tests", func() { FieldSelector: fields.AndSelectors( fields.OneTermEqualSelector("reason", "Updated"), fields.OneTermEqualSelector("involvedObject.uid", - string(instance.GetUID())))})).To(Succeed(), + string(instance.GetUID()))), + })).To(Succeed(), "failed to list events emitted for the config update of the DSI") return len(events.Items) > 0 @@ -441,27 +441,31 @@ var _ = Describe("Patroni end-to-end Tests", func() { }) }) -func setCustomPostgresConfig(pg *v1alpha1.Postgresql) { +func setCustomPostgresConfig(pg *v1beta3.Postgresql) { maxLocksPerTransaction := 120 - pg.Spec.PostgresConfiguration.MaxConnections = 101 - pg.Spec.PostgresConfiguration.MaxLocksPerTransaction = &maxLocksPerTransaction + pg.Spec.Parameters.MaxConnections = 101 + pg.Spec.Parameters.MaxLocksPerTransaction = &maxLocksPerTransaction // SharedBuffers is not being set or updated. // https://github.com/anynines/postgresql-operator/issues/75 - pg.Spec.PostgresConfiguration.SharedBuffers = 200 - pg.Spec.PostgresConfiguration.MaxReplicationSlots = 11 - pg.Spec.PostgresConfiguration.MaxWALSenders = 11 - pg.Spec.PostgresConfiguration.StatementTimeoutMillis = 2147483647 - pg.Spec.PostgresConfiguration.SSLCiphers = "high:medium:+3des:!anull" - pg.Spec.PostgresConfiguration.SSLMinProtocolVersion = "TLSv1.2" - pg.Spec.PostgresConfiguration.TempFileLimitKiloBytes = 0 - pg.Spec.PostgresConfiguration.WALWriterDelayMillis = 201 - pg.Spec.PostgresConfiguration.SynchronousCommit = "off" - pg.Spec.PostgresConfiguration.TrackIOTiming = "on" - pg.Spec.PostgresConfiguration.ArchiveTimeoutSeconds = 10 - pg.Spec.PostgresConfiguration.ClientMinMessages = "warning" - pg.Spec.PostgresConfiguration.LogMinMessages = "notice" - pg.Spec.PostgresConfiguration.LogMinErrorStatement = "warning" - pg.Spec.PostgresConfiguration.LogStatement = "all" - pg.Spec.PostgresConfiguration.LogErrorVerbosity = "DEFAULT" + pg.Spec.Parameters.SharedBuffers = 200 + pg.Spec.Parameters.MaxReplicationSlots = 11 + pg.Spec.Parameters.MaxWALSenders = 11 + pg.Spec.Parameters.StatementTimeoutMillis = 2147483647 + // There is a list of SSL cipher suites that are allowed to be used by SSL connections + // https://www.postgresql.org/docs/14/runtime-config-connection.html#GUC-SSL-CIPHERS + // non-allowed values cause validation errors + // https://github.com/postgres/postgres/blob/REL_14_STABLE/src/backend/libpq/be-secure-openssl.c#L270 + pg.Spec.Parameters.SSLCiphers = "!aNULL:HIGH" + pg.Spec.Parameters.SSLMinProtocolVersion = "TLSv1.2" + pg.Spec.Parameters.TempFileLimitKiloBytes = 0 + pg.Spec.Parameters.WALWriterDelayMillis = 201 + pg.Spec.Parameters.SynchronousCommit = "off" + pg.Spec.Parameters.TrackIOTiming = "on" + pg.Spec.Parameters.ArchiveTimeoutSeconds = 10 + pg.Spec.Parameters.ClientMinMessages = "warning" + pg.Spec.Parameters.LogMinMessages = "notice" + pg.Spec.Parameters.LogMinErrorStatement = "warning" + pg.Spec.Parameters.LogStatement = "all" + pg.Spec.Parameters.LogErrorVerbosity = "DEFAULT" } diff --git a/test/e2e/postgresql/exposed_cluster_test.go b/test/e2e/postgresql/exposed_cluster_test.go new file mode 100644 index 00000000..27621a68 --- /dev/null +++ b/test/e2e/postgresql/exposed_cluster_test.go @@ -0,0 +1,163 @@ +package postgresql + +import ( + "fmt" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + v1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + + pgv1beta3 "github.com/anynines/postgresql-operator/api/v1beta3" + + "github.com/anynines/a8s-deployment/test/framework" + "github.com/anynines/a8s-deployment/test/framework/dsi" + "github.com/anynines/a8s-deployment/test/framework/secret" + "github.com/anynines/a8s-deployment/test/framework/servicebinding" +) + +const ( + SSLModeRequired = "require" +) + +var _ = Describe("end-to-end tests for exposed instances", func() { + + Context("Instance exposed via Load Balancer", Ordered, func() { + AfterAll(func() { + Expect(k8sClient.Delete(ctx, instance.GetClientObject())).To( + Succeed(), "failed to delete instance") + }) + + It("Provisions the exposed PostgreSQL instance", func() { + By("Accepting instance creation") + instance, err = dsi.New( + dataservice, + testingNamespace, + framework.GenerateName(instanceNamePrefix, + GinkgoParallelProcess(), suffixLength), + 3, + ) + Expect(err).To(BeNil(), "failed to generate new DSI resource") + + // Cast interface to concrete struct so that we can access fields + // directly + pg, ok = instance.GetClientObject().(*pgv1beta3.Postgresql) + Expect(ok).To(BeTrue(), + "failed to cast object interface to PostgreSQL struct") + + // We break the framework abstraction here to access a feature + // that is available in the pg data service. If we make access from outside the cluster a + // feature throughout the framework, we should adjust our testing framework instead + pg.Spec.Expose = "LoadBalancer" + pg.Spec.EnableReadOnlyService = true + + Expect(k8sClient.Create(ctx, instance.GetClientObject())). + To(Succeed(), fmt.Sprintf("failed to create instance %s/%s", + instance.GetNamespace(), instance.GetName())) + + By("Setting the DSI status to Running") + + dsi.WaitForReadiness(ctx, instance.GetClientObject(), k8sClient) + }) + + var serviceBindingData secret.SecretData + It("supports service bindings", func() { + By("accepting service binding creation") + // Create service binding for instance and get secret data + sb = servicebinding.New( + servicebinding.SetNamespacedName(instance.GetClientObject()), + servicebinding.SetInstanceRef(instance.GetClientObject()), + ) + Expect(k8sClient.Create(ctx, sb)).To(Succeed(), + fmt.Sprintf("failed to create new servicebinding for DSI %s/%s", + instance.GetNamespace(), instance.GetName())) + + By("Setting the service binding status to ready") + servicebinding.WaitForReadiness(ctx, sb, k8sClient) + + By("Creating a service binding secret") + serviceBindingData, err = secret.Data( + ctx, k8sClient, servicebinding.SecretName(sb.Name), testingNamespace) + Expect(err).To(BeNil(), + fmt.Sprintf("failed to parse secret data for service binding %s/%s", + sb.GetNamespace(), sb.GetName())) + + }) + + var connInfo v1.ConfigMap + It("Creates a connection ConfigMap", func() { + Expect(k8sClient.Get(ctx, types.NamespacedName{ + Namespace: instance.GetNamespace(), + Name: dsi.ConnectionInfoName(instance.GetName()), + }, &connInfo)).To(Succeed()) + + Expect(connInfo.Data).To(HaveKey("primary"), + "connInfo does not contain information about primary database") + + Expect(connInfo.Data).To(HaveKey("port"), + "connInfo does not contain information about port") + + Expect(connInfo.Data).To(HaveKey("readOnly"), + "connInfo does not contain information about read only service") + + }) + + It("can write data via public address", func() { + By("allowing client creation") + // Create client for interacting with the new instance. + client, err = dsi.NewClientForURL( + dataservice, connInfo.Data["primary"], connInfo.Data["port"], + SSLModeRequired, // use ssl, as unencrypted connections from outside the cluster + // are not allowed + serviceBindingData) + Expect(err).To(BeNil(), "failed to create new dsi client") + + By("accepting writes") + // we need retry logic, as could provider load balancers may take some time + // to become available from the internet + Eventually(func() error { + return client.Write(ctx, entity, testInput) + }, 5*time.Minute).Should(Succeed()) + }) + + It("can read data via public address", func() { + By("allowing reading from primary service") + // Create client for interacting with the new instance. + client, err = dsi.NewClientForURL( + dataservice, connInfo.Data["primary"], connInfo.Data["port"], + SSLModeRequired, // use ssl, as unencrypted connections from outside the cluster + // are not allowed + serviceBindingData) + Expect(err).To(BeNil(), "failed to create new dsi client") + + By("accepting reads") + // we need retry logic, as cloud provider load balancers may take some time + // to become available from the internet + Eventually(func(g Gomega) { + entityData, err := client.Read(ctx, entity) + g.Expect(err).To(BeNil(), "error reading from the instances primary service") + g.Expect(entityData).To(Equal(testInput), "data service returned unexpected entry") + }, 5*time.Minute).Should(Succeed()) + + By("allowing reading from read-only service") + + // Create client for interacting with the new instance. + client, err = dsi.NewClientForURL( + dataservice, connInfo.Data["readOnly"], connInfo.Data["port"], + "require", // use ssl, as unencrypted connections from outside the cluster + // are not allowed + serviceBindingData) + Expect(err).To(BeNil(), "failed to create new dsi client") + + By("accepting reads via the read-only service") + // we need retry logic, as cloud provider load balancers may take some time + // to become available from the internet + Eventually(func(g Gomega) { + entityData, err := client.Read(ctx, entity) + g.Expect(err).To(BeNil(), "error reading from the instances read-only service") + g.Expect(entityData).To(Equal(testInput), "data service returned unexpected entry") + }, 5*time.Minute).Should(Succeed()) + }) + }) +}) diff --git a/test/e2e/postgresql/postgresql_test.go b/test/e2e/postgresql/postgresql_test.go index 3b930c94..3718e998 100644 --- a/test/e2e/postgresql/postgresql_test.go +++ b/test/e2e/postgresql/postgresql_test.go @@ -24,8 +24,9 @@ import ( "github.com/anynines/a8s-deployment/test/framework/postgresql" "github.com/anynines/a8s-deployment/test/framework/secret" "github.com/anynines/a8s-deployment/test/framework/servicebinding" - sbv1alpha1 "github.com/anynines/a8s-service-binding-controller/api/v1alpha1" - pgv1alpha1 "github.com/anynines/postgresql-operator/api/v1alpha1" + sbv1beta3 "github.com/anynines/a8s-service-binding-controller/api/v1beta3" + "github.com/anynines/postgresql-operator/api/v1beta3" + pgv1beta3 "github.com/anynines/postgresql-operator/api/v1beta3" ) const ( @@ -37,6 +38,8 @@ const ( DbAdminUsernameKey = "username" DbAdminPasswordKey = "password" + numA8SLabels = 3 + // TODO: Make configurable and generalizable using Data interface // testInput is data input used for testing data service functionality. testInput = "test_input" @@ -52,10 +55,10 @@ var ( localPort int ok bool - sb *sbv1alpha1.ServiceBinding + sb *sbv1beta3.ServiceBinding instance dsi.Object client dsi.DSIClient - pg *pgv1alpha1.Postgresql + pg *pgv1beta3.Postgresql ) var _ = Describe("PostgreSQL Operator end-to-end tests", func() { @@ -78,13 +81,16 @@ var _ = Describe("PostgreSQL Operator end-to-end tests", func() { // Cast interface to concrete struct so that we can access fields // directly - pg, ok = instance.GetClientObject().(*pgv1alpha1.Postgresql) + pg, ok = instance.GetClientObject().(*pgv1beta3.Postgresql) Expect(ok).To(BeTrue(), "failed to cast object interface to PostgreSQL struct") Expect(k8sClient.Create(ctx, instance.GetClientObject())). To(Succeed(), fmt.Sprintf("failed to create instance %s/%s", instance.GetNamespace(), instance.GetName())) + }) + + By("waiting for DSI to get to cluster status Running", func() { dsi.WaitForReadiness(ctx, instance.GetClientObject(), k8sClient) }) @@ -98,15 +104,32 @@ var _ = Describe("PostgreSQL Operator end-to-end tests", func() { Expect(*sts.Spec.Replicas).To(Equal(*pg.Spec.Replicas)) Expect(sts.Status.ReadyReplicas).To(Equal(*pg.Spec.Replicas)) - // Labels and Annotations are tested since other a8s framework - // components rely on them. - Expect(sts.Spec.Template.Labels).To(HaveKeyWithValue("a8s.a9s/dsi-name", pg.Name)) - // TODO: find a way to avoid hardcoding - Expect(sts.Spec.Template.Labels). - To(HaveKeyWithValue("a8s.a9s/dsi-group", "postgresql.anynines.com")) - Expect(sts.Spec.Template.Labels). - To(HaveKeyWithValue("a8s.a9s/dsi-kind", "Postgresql")) - Expect(len(sts.Spec.Template.Labels)).To(Equal(3)) + By("checking a8s labels added to StatefulSet", func() { + // TODO: find a way to avoid hardcoding + Expect(sts.Labels).To(HaveKeyWithValue("a8s.a9s/dsi-name", pg.Name)) + Expect(sts.Labels). + To(HaveKeyWithValue("a8s.a9s/dsi-group", "postgresql.anynines.com")) + Expect(sts.Labels). + To(HaveKeyWithValue("a8s.a9s/dsi-kind", "Postgresql")) + }) + + By("check a8s labels are present in pod template", func() { + Expect(sts.Spec.Template.Labels).To(HaveKeyWithValue("a8s.a9s/dsi-name", pg.Name)) + Expect(sts.Spec.Template.Labels). + To(HaveKeyWithValue("a8s.a9s/dsi-group", "postgresql.anynines.com")) + Expect(sts.Spec.Template.Labels). + To(HaveKeyWithValue("a8s.a9s/dsi-kind", "Postgresql")) + }) + + By("test a8s labels as the StatefulSet label selector", func() { + Expect(sts.Spec.Selector.MatchLabels). + To(HaveKeyWithValue("a8s.a9s/dsi-name", pg.Name)) + Expect(sts.Spec.Selector.MatchLabels). + To(HaveKeyWithValue("a8s.a9s/dsi-group", "postgresql.anynines.com")) + Expect(sts.Spec.Selector.MatchLabels). + To(HaveKeyWithValue("a8s.a9s/dsi-kind", "Postgresql")) + Expect(len(sts.Spec.Selector.MatchLabels)).To(Equal(numA8SLabels)) + }) Expect(sts.Spec.Template.Annotations). To(HaveKeyWithValue("prometheus.io/port", "9187")) @@ -128,12 +151,22 @@ var _ = Describe("PostgreSQL Operator end-to-end tests", func() { Namespace: instance.GetNamespace()}, svc)).To(Succeed()) - Expect(svc.Spec.Selector).To(HaveKeyWithValue("a8s.a9s/dsi-name", pg.Name)) - Expect(svc.Spec.Selector). - To(HaveKeyWithValue("a8s.a9s/dsi-group", "postgresql.anynines.com")) - Expect(svc.Spec.Selector).To(HaveKeyWithValue("a8s.a9s/dsi-kind", "Postgresql")) - Expect(svc.Spec.Selector).To(HaveKeyWithValue("a8s.a9s/replication-role", "master")) - Expect(len(svc.Spec.Selector)).To(Equal(4)) + By("checking a8s labels added to Service", func() { + Expect(svc.Labels).To(HaveKeyWithValue("a8s.a9s/dsi-name", pg.Name)) + Expect(svc.Labels). + To(HaveKeyWithValue("a8s.a9s/dsi-group", "postgresql.anynines.com")) + Expect(svc.Labels). + To(HaveKeyWithValue("a8s.a9s/dsi-kind", "Postgresql")) + }) + + By("checking a8s labels as selector", func() { + Expect(svc.Spec.Selector).To(HaveKeyWithValue("a8s.a9s/dsi-name", pg.Name)) + Expect(svc.Spec.Selector). + To(HaveKeyWithValue("a8s.a9s/dsi-group", "postgresql.anynines.com")) + Expect(svc.Spec.Selector).To(HaveKeyWithValue("a8s.a9s/dsi-kind", "Postgresql")) + Expect(svc.Spec.Selector).To(HaveKeyWithValue("a8s.a9s/replication-role", "master")) + Expect(len(svc.Spec.Selector)).To(Equal(4)) + }) Expect(svc.Spec.Type).To(Equal(corev1.ServiceTypeClusterIP)) Expect(svc.Spec.Ports[0].Name).To(Equal("postgresql")) @@ -142,12 +175,21 @@ var _ = Describe("PostgreSQL Operator end-to-end tests", func() { }) By("creating the ServiceAccount", func() { + sa := &corev1.ServiceAccount{} Expect(k8sClient.Get( ctx, types.NamespacedName{Name: instance.GetName(), Namespace: instance.GetNamespace()}, - &corev1.ServiceAccount{}, + sa, )).To(Succeed(), "failed to get serviceaccount") + + By("checking a8s labels added to ServiceAccount", func() { + Expect(sa.Labels).To(HaveKeyWithValue("a8s.a9s/dsi-name", pg.Name)) + Expect(sa.Labels). + To(HaveKeyWithValue("a8s.a9s/dsi-group", "postgresql.anynines.com")) + Expect(sa.Labels). + To(HaveKeyWithValue("a8s.a9s/dsi-kind", "Postgresql")) + }) }) By("creating a RoleBinding between the PostgreSQL instance service account and the Spilo role", func() { @@ -159,6 +201,14 @@ var _ = Describe("PostgreSQL Operator end-to-end tests", func() { rolebinding, )).To(Succeed(), "failed to get rolebinding") + By("checking a8s labels added to RoleBinding", func() { + Expect(rolebinding.Labels).To(HaveKeyWithValue("a8s.a9s/dsi-name", pg.Name)) + Expect(rolebinding.Labels). + To(HaveKeyWithValue("a8s.a9s/dsi-group", "postgresql.anynines.com")) + Expect(rolebinding.Labels). + To(HaveKeyWithValue("a8s.a9s/dsi-kind", "Postgresql")) + }) + Expect(rolebinding.RoleRef.Name).To(Equal("postgresql-spilo-role")) Expect(rolebinding.RoleRef.Kind).To(Equal("ClusterRole")) Expect(rolebinding.RoleRef.APIGroup).To(Equal(rbacv1.GroupName)) @@ -263,29 +313,16 @@ var _ = Describe("PostgreSQL Operator end-to-end tests", func() { sort.Slice(instanceEvents.Items, func(i, j int) bool { return instanceEvents.Items[i].Message <= instanceEvents.Items[j].Message }) - masterSvcEvent := instanceEvents.Items[0] - roleBindingEvent := instanceEvents.Items[1] - adminSecretEvent := instanceEvents.Items[2] - standbySecretsEvent := instanceEvents.Items[3] + + log.Printf("\n\n\n %+v \n\n\n", instanceEvents) + + roleBindingEvent := instanceEvents.Items[0] + adminSecretEvent := instanceEvents.Items[1] + standbySecretsEvent := instanceEvents.Items[2] + masterSvcEvent := instanceEvents.Items[3] svcAccountEvent := instanceEvents.Items[4] ssetEvent := instanceEvents.Items[5] - By("emitting an event for the creation of the master service", func() { - Expect(masterSvcEvent.Message).To(Equal("Successfully created master service"), - "wrong event message") - Expect(masterSvcEvent.Type).To(Equal(corev1.EventTypeNormal), - "wrong event type") - Expect(masterSvcEvent.Reason).To(Equal("Created"), "wrong event reason") - Expect(masterSvcEvent.Count).To(Equal(int32(1)), "wrong event count") - Expect(masterSvcEvent.Source.Component).To(Equal("postgresql-controller"), - "wrong event source.component") - Expect(masterSvcEvent.InvolvedObject.Kind).To(Equal("Postgresql"), - "wrong event involvedObject.kind") - Expect(masterSvcEvent.InvolvedObject.APIVersion). - To(Equal("postgresql.anynines.com/v1beta3"), - "wrong event involvedObject.apiVersion") - }) - By("emitting an event for the creation of the roleBinding", func() { Expect(roleBindingEvent.Message).To(Equal("Successfully created roleBinding"), "wrong event message") @@ -342,6 +379,24 @@ var _ = Describe("PostgreSQL Operator end-to-end tests", func() { "wrong event involvedObject.apiVersion") }) + By("emitting an event for the creation of the master service", func() { + Expect(masterSvcEvent.Message).To(Equal(fmt.Sprintf("Successfully created service: %s/%s-master", + pg.Namespace, + pg.Name)), + "wrong event message") + Expect(masterSvcEvent.Type).To(Equal(corev1.EventTypeNormal), + "wrong event type") + Expect(masterSvcEvent.Reason).To(Equal("Created"), "wrong event reason") + Expect(masterSvcEvent.Count).To(Equal(int32(1)), "wrong event count") + Expect(masterSvcEvent.Source.Component).To(Equal("postgresql-controller"), + "wrong event source.component") + Expect(masterSvcEvent.InvolvedObject.Kind).To(Equal("Postgresql"), + "wrong event involvedObject.kind") + Expect(masterSvcEvent.InvolvedObject.APIVersion). + To(Equal("postgresql.anynines.com/v1beta3"), + "wrong event involvedObject.apiVersion") + }) + By("emitting an event for the creation of the serviceAcccount", func() { Expect(svcAccountEvent.Message).To(Equal("Successfully created serviceAccount"), "wrong event message") @@ -398,26 +453,28 @@ var _ = Describe("PostgreSQL Operator end-to-end tests", func() { }) It("Updates cpu and memory requirements and limits", func() { - var old pgv1alpha1.Postgresql - err := k8sClient.Get(ctx, types.NamespacedName{ - Namespace: instance.GetNamespace(), - Name: instance.GetName(), - }, - &old, - ) - Expect(err).To(BeNil(), "failed to fetch instance resource") - - old.Spec.Resources = &corev1.ResourceRequirements{ - Limits: map[corev1.ResourceName]k8sresource.Quantity{ - corev1.ResourceCPU: k8sresource.MustParse("200m"), - corev1.ResourceMemory: k8sresource.MustParse("200Mi"), - }, - Requests: map[corev1.ResourceName]k8sresource.Quantity{ - corev1.ResourceCPU: k8sresource.MustParse("200m"), - corev1.ResourceMemory: k8sresource.MustParse("200Mi"), + var old pgv1beta3.Postgresql + Eventually(func(g Gomega) { + err := k8sClient.Get(ctx, types.NamespacedName{ + Namespace: instance.GetNamespace(), + Name: instance.GetName(), }, - } - Expect(k8sClient.Update(ctx, &old)).To(Succeed()) + &old, + ) + g.Expect(err).To(BeNil(), "failed to fetch instance resource") + + old.Spec.Resources = &corev1.ResourceRequirements{ + Limits: map[corev1.ResourceName]k8sresource.Quantity{ + corev1.ResourceCPU: k8sresource.MustParse("200m"), + corev1.ResourceMemory: k8sresource.MustParse("200Mi"), + }, + Requests: map[corev1.ResourceName]k8sresource.Quantity{ + corev1.ResourceCPU: k8sresource.MustParse("200m"), + corev1.ResourceMemory: k8sresource.MustParse("200Mi"), + }, + } + g.Expect(k8sClient.Update(ctx, &old)).To(Succeed()) + }, asyncOpsTimeoutMins, 1*time.Second).Should(Succeed()) Eventually(func() *corev1.ResourceRequirements { sts := &appsv1.StatefulSet{} @@ -435,17 +492,19 @@ var _ = Describe("PostgreSQL Operator end-to-end tests", func() { }) It("Updates replicas", func() { - var old pgv1alpha1.Postgresql - err := k8sClient.Get(ctx, types.NamespacedName{ - Namespace: instance.GetNamespace(), - Name: instance.GetName(), - }, - &old, - ) - Expect(err).To(BeNil(), "failed to fetch instance resource") + var old pgv1beta3.Postgresql + Eventually(func(g Gomega) { + err := k8sClient.Get(ctx, types.NamespacedName{ + Namespace: instance.GetNamespace(), + Name: instance.GetName(), + }, + &old, + ) + g.Expect(err).To(BeNil(), "failed to fetch instance resource") - old.Spec.Replicas = pointer.Int32(3) - Expect(k8sClient.Update(ctx, &old)).To(Succeed()) + old.Spec.Replicas = pointer.Int32(3) + g.Expect(k8sClient.Update(ctx, &old)).To(Succeed()) + }, asyncOpsTimeoutMins, 1*time.Second).Should(Succeed()) Eventually(func() *int32 { sts := &appsv1.StatefulSet{} @@ -461,6 +520,184 @@ var _ = Describe("PostgreSQL Operator end-to-end tests", func() { return sts.Spec.Replicas }, asyncOpsTimeoutMins, 1*time.Second).Should(Equal(pointer.Int32(3))) }) + + It("Updates labels", func() { + var currDSI v1beta3.Postgresql + Eventually(func(g Gomega) { + err := k8sClient.Get(ctx, types.NamespacedName{ + Namespace: instance.GetNamespace(), + Name: instance.GetName(), + }, &currDSI) + g.Expect(err).To(BeNil()) + + // The new labels are selected to represent all possible changes: there's one removal + // (test-label-2), one addition (test-label-4), and one value modification + // (test-label-1). It would be better to have separate cases, but these tests are + // already painfully slow and we want to replace them soon, so in the meantime this + // single test case was deemed good enough to ensure future changes don't break the + // behavior. + currDSI.Labels = map[string]string{ + "test-label-1": "val3", + "test-label-4": "val4", + } + + g.Expect(k8sClient.Update(ctx, &currDSI)).To(Succeed()) + }, asyncOpsTimeoutMins, 1*time.Second).Should(Succeed()) + + By("Ensuring StatefulSet labels are updated", func() { + Eventually(func(g Gomega) { + sts := &appsv1.StatefulSet{} + err := k8sClient.Get(ctx, + types.NamespacedName{Name: instance.GetName(), + Namespace: instance.GetNamespace()}, + sts) + g.Expect(err).To(BeNil()) + + g.Expect(sts.Labels).To(HaveKeyWithValue("test-label-1", "val3")) + g.Expect(sts.Labels).To(HaveKeyWithValue("test-label-4", "val4")) + g.Expect(sts.Labels).To(HaveKeyWithValue("a8s.a9s/dsi-name", instance.GetName())) + g.Expect(sts.Labels). + To(HaveKeyWithValue("a8s.a9s/dsi-group", "postgresql.anynines.com")) + g.Expect(sts.Labels).To(HaveKeyWithValue("a8s.a9s/dsi-kind", "Postgresql")) + g.Expect(len(sts.Labels)).To(Equal(numA8SLabels + 2)) + + g.Expect(sts.Spec.Template.Labels).To(HaveKeyWithValue("test-label-1", "val3")) + g.Expect(sts.Spec.Template.Labels).To(HaveKeyWithValue("test-label-4", "val4")) + g.Expect(sts.Spec.Template.Labels). + To(HaveKeyWithValue("a8s.a9s/dsi-name", instance.GetName())) + g.Expect(sts.Spec.Template.Labels). + To(HaveKeyWithValue("a8s.a9s/dsi-group", "postgresql.anynines.com")) + g.Expect(sts.Spec.Template.Labels). + To(HaveKeyWithValue("a8s.a9s/dsi-kind", "Postgresql")) + g.Expect(len(sts.Spec.Template.Labels)).To(Equal(numA8SLabels + 2)) + + By("test statefulset label selector only uses a8s-reserved labels", + func() { + g.Expect(sts.Spec.Selector.MatchLabels). + To(HaveKeyWithValue("a8s.a9s/dsi-name", instance.GetName())) + g.Expect(sts.Spec.Selector.MatchLabels). + To(HaveKeyWithValue("a8s.a9s/dsi-group", "postgresql.anynines.com")) + g.Expect(sts.Spec.Selector.MatchLabels). + To(HaveKeyWithValue("a8s.a9s/dsi-kind", "Postgresql")) + g.Expect(len(sts.Spec.Selector.MatchLabels)).To(Equal(numA8SLabels)) + }, + ) + }, asyncOpsTimeoutMins, 1*time.Second).Should(Succeed()) + }) + + By("Ensuring master service labels are updated", func() { + Eventually(func(g Gomega) { + svc := &corev1.Service{} + Expect(k8sClient.Get(ctx, + types.NamespacedName{ + Name: postgresql.MasterService( + instance.GetName()), + Namespace: instance.GetNamespace()}, + svc)).To(Succeed()) + g.Expect(err).To(BeNil()) + + g.Expect(svc.Labels).To(HaveKeyWithValue("test-label-1", "val3")) + g.Expect(svc.Labels).To(HaveKeyWithValue("test-label-4", "val4")) + g.Expect(svc.Labels).To(HaveKeyWithValue("a8s.a9s/replication-role", "master")) + g.Expect(svc.Labels).To(HaveKeyWithValue("a8s.a9s/dsi-name", instance.GetName())) + g.Expect(svc.Labels). + To(HaveKeyWithValue("a8s.a9s/dsi-group", "postgresql.anynines.com")) + g.Expect(svc.Labels).To(HaveKeyWithValue("a8s.a9s/dsi-kind", "Postgresql")) + g.Expect(len(svc.Labels)).To(Equal(numA8SLabels + 3)) + + g.Expect(svc.Spec.Selector). + To(HaveKeyWithValue("a8s.a9s/replication-role", "master")) + g.Expect(svc.Spec.Selector).To(HaveKeyWithValue("a8s.a9s/dsi-name", instance.GetName())) + g.Expect(svc.Spec.Selector). + To(HaveKeyWithValue("a8s.a9s/dsi-group", "postgresql.anynines.com")) + g.Expect(svc.Spec.Selector).To(HaveKeyWithValue("a8s.a9s/dsi-kind", "Postgresql")) + g.Expect(len(svc.Spec.Selector)).To(Equal(numA8SLabels + 1)) + }, asyncOpsTimeoutMins, 1*time.Second).Should(Succeed()) + }) + + By("Ensuring ServiceAccount labels are updated", func() { + Eventually(func(g Gomega) { + sa := &corev1.ServiceAccount{} + Expect(k8sClient.Get( + ctx, + types.NamespacedName{Name: instance.GetName(), + Namespace: instance.GetNamespace()}, + sa, + )).To(Succeed(), "failed to get serviceaccount") + + g.Expect(sa.Labels).To(HaveKeyWithValue("test-label-1", "val3")) + g.Expect(sa.Labels).To(HaveKeyWithValue("test-label-4", "val4")) + g.Expect(sa.Labels).To(HaveKeyWithValue("a8s.a9s/dsi-name", instance.GetName())) + g.Expect(sa.Labels). + To(HaveKeyWithValue("a8s.a9s/dsi-group", "postgresql.anynines.com")) + g.Expect(sa.Labels).To(HaveKeyWithValue("a8s.a9s/dsi-kind", "Postgresql")) + g.Expect(len(sa.Labels)).To(Equal(numA8SLabels + 2)) + }, asyncOpsTimeoutMins, 1*time.Second).Should(Succeed()) + }) + + By("Ensuring RoleBinding labels are updated", func() { + Eventually(func(g Gomega) { + rb := &rbacv1.RoleBinding{} + Expect(k8sClient.Get( + ctx, + types.NamespacedName{Name: instance.GetName(), + Namespace: instance.GetNamespace()}, + rb, + )).To(Succeed(), "failed to get rolebinding") + + g.Expect(rb.Labels).To(HaveKeyWithValue("test-label-1", "val3")) + g.Expect(rb.Labels).To(HaveKeyWithValue("test-label-4", "val4")) + g.Expect(rb.Labels).To(HaveKeyWithValue("a8s.a9s/dsi-name", instance.GetName())) + g.Expect(rb.Labels). + To(HaveKeyWithValue("a8s.a9s/dsi-group", "postgresql.anynines.com")) + g.Expect(rb.Labels).To(HaveKeyWithValue("a8s.a9s/dsi-kind", "Postgresql")) + g.Expect(len(rb.Labels)).To(Equal(numA8SLabels + 2)) + }, asyncOpsTimeoutMins, 1*time.Second).Should(Succeed()) + }) + + By("Ensuring admin user secret labels are updated", func() { + Eventually(func(g Gomega) { + adminSecret := &corev1.Secret{} + g.Expect(k8sClient.Get( + ctx, + types.NamespacedName{ + Name: postgresql.AdminRoleSecretName(instance.GetName()), + Namespace: instance.GetNamespace()}, + adminSecret, + )).To(Succeed(), "failed to get admin role secret") + + g.Expect(adminSecret.Labels).To(HaveKeyWithValue("test-label-1", "val3")) + g.Expect(adminSecret.Labels).To(HaveKeyWithValue("test-label-4", "val4")) + g.Expect(adminSecret.Labels).To(HaveKeyWithValue("a8s.a9s/dsi-name", instance.GetName())) + g.Expect(adminSecret.Labels). + To(HaveKeyWithValue("a8s.a9s/dsi-group", "postgresql.anynines.com")) + g.Expect(adminSecret.Labels).To(HaveKeyWithValue("a8s.a9s/dsi-kind", "Postgresql")) + g.Expect(len(adminSecret.Labels)).To(Equal(numA8SLabels + 2)) + }, asyncOpsTimeoutMins, 1*time.Second).Should(Succeed()) + }) + + By("Ensuring standby user secret labels are updated", func() { + Eventually(func(g Gomega) { + standbySecret := &corev1.Secret{} + g.Expect(k8sClient.Get( + ctx, + types.NamespacedName{ + Name: postgresql.StandbyRoleSecretName(instance.GetName()), + Namespace: instance.GetNamespace()}, + standbySecret, + )).To(Succeed(), "failed to get admin role secret") + + g.Expect(standbySecret.Labels).To(HaveKeyWithValue("test-label-1", "val3")) + g.Expect(standbySecret.Labels).To(HaveKeyWithValue("test-label-4", "val4")) + g.Expect(standbySecret.Labels).To(HaveKeyWithValue("a8s.a9s/dsi-name", instance.GetName())) + g.Expect(standbySecret.Labels). + To(HaveKeyWithValue("a8s.a9s/dsi-group", "postgresql.anynines.com")) + g.Expect(standbySecret.Labels). + To(HaveKeyWithValue("a8s.a9s/dsi-kind", "Postgresql")) + g.Expect(len(standbySecret.Labels)).To(Equal(numA8SLabels + 2)) + }, asyncOpsTimeoutMins, 1*time.Second).Should(Succeed()) + }) + }) }) Context("PostgreSQL Instance deletion", func() { @@ -475,7 +712,7 @@ var _ = Describe("PostgreSQL Operator end-to-end tests", func() { ) Expect(err).To(BeNil(), "failed to generate new DSI resource") - pg, ok = instance.GetClientObject().(*pgv1alpha1.Postgresql) + pg, ok = instance.GetClientObject().(*pgv1beta3.Postgresql) Expect(ok).To(BeTrue(), "failed to cast instance object interface to PostgreSQL struct") @@ -584,6 +821,23 @@ var _ = Describe("PostgreSQL Operator end-to-end tests", func() { }, asyncOpsTimeoutMins).Should(BeTrue()) }) + By("removing the Patroni leader election endpoint", func() { + Eventually(func() bool { + err := k8sClient.Get( + ctx, + types.NamespacedName{ + Name: instance.GetName(), + Namespace: instance.GetNamespace()}, + &corev1.Endpoints{}, + ) + if err == nil || !k8serrors.IsNotFound(err) { + return false + } + + return true + }, asyncOpsTimeoutMins).Should(BeTrue()) + }) + By("emitting an event about the instance deletion", func() { events := &corev1.EventList{} Expect(k8sClient.List(ctx, events, &ctrlruntimeclient.ListOptions{ @@ -685,16 +939,17 @@ var _ = Describe("PostgreSQL Operator end-to-end tests", func() { }) By("testing whether data persists after primary pod deletion", func() { - // Fetch and delete the primary pod - pod, err := framework.GetPrimaryPodUsingServiceSelector( - ctx, instance.GetClientObject(), k8sClient) - Expect(err).To(BeNil(), fmt.Sprintf( - "failed to get primary pod using service selector for %s/%s", - instance.GetNamespace(), instance.GetName())) - Expect(k8sClient.Delete(ctx, pod)). - To(Succeed(), fmt.Sprintf("failed to delete pod %s/%s", - pod.GetNamespace(), pod.GetName())) - dsi.WaitForPodDeletion(ctx, pod, k8sClient) + By("deleting the primary pod", func() { + pod, err := framework.GetPrimaryPodUsingServiceSelector( + ctx, instance.GetClientObject(), k8sClient) + Expect(err).To(BeNil(), fmt.Sprintf( + "failed to get primary pod using service selector for %s/%s", + instance.GetNamespace(), instance.GetName())) + Expect(k8sClient.Delete(ctx, pod)). + To(Succeed(), fmt.Sprintf("failed to delete pod %s/%s", + pod.GetNamespace(), pod.GetName())) + dsi.WaitForPodDeletion(ctx, pod, k8sClient) + }) // TODO: This is only a temporary solution to an issue that was introduced // by the PostgreSQL extensions feature. In order to install PostgreSQL extensions @@ -898,4 +1153,287 @@ var _ = Describe("PostgreSQL Operator end-to-end tests", func() { }) }) }) + + Context("PostgreSQL Extensions", func() { + const singleReplica int32 = 1 + AfterEach(func() { + Expect(k8sClient.Delete(ctx, instance.GetClientObject())).To(Succeed(), + fmt.Sprintf("failed to delete instance %s/%s", + instance.GetNamespace(), instance.GetName())) + }) + + It("Provisions a PostgreSQL instance without PostgreSQL extensions", func() { + instance, err = dsi.New( + dataservice, + testingNamespace, + framework.GenerateName( + instanceNamePrefix, GinkgoParallelProcess(), suffixLength), + singleReplica, + ) + + Expect(err).To(BeNil(), "failed to generate new DSI resource") + + Expect(k8sClient.Create(ctx, instance.GetClientObject())). + To(Succeed(), fmt.Sprintf("failed to create instance %s/%s", + instance.GetNamespace(), instance.GetName())) + dsi.WaitForReadiness(ctx, instance.GetClientObject(), k8sClient) + + sts := &appsv1.StatefulSet{} + Expect(k8sClient.Get(ctx, + types.NamespacedName{Name: instance.GetName(), + Namespace: instance.GetNamespace()}, + sts)).To(Succeed(), "failed to get statefulset") + + Expect(len(sts.Spec.Template.Spec.InitContainers)).To(Equal(0)) + }) + + It("Provisions the PostgreSQL instance with one PostgreSQL extension", func() { + extensions := []string{"MobilityDB"} + + instance, err = dsi.New( + dataservice, + testingNamespace, + framework.GenerateName( + instanceNamePrefix, GinkgoParallelProcess(), suffixLength), + singleReplica, + ) + Expect(err).To(BeNil(), "failed to generate new DSI resource") + + pg, ok = instance.GetClientObject().(*pgv1beta3.Postgresql) + Expect(ok).To(BeTrue(), + "failed to cast object interface to PostgreSQL struct") + pg.Spec.Extensions = extensions + + Expect(k8sClient.Create(ctx, pg)). + To(Succeed(), fmt.Sprintf("failed to create instance %s/%s", + instance.GetNamespace(), instance.GetName())) + dsi.WaitForReadiness(ctx, instance.GetClientObject(), k8sClient) + + sts := &appsv1.StatefulSet{} + Expect(k8sClient.Get(ctx, + types.NamespacedName{Name: instance.GetName(), + Namespace: instance.GetNamespace()}, + sts)).To(Succeed(), "failed to get statefulset") + + Expect(len(sts.Spec.Template.Spec.InitContainers)).To(Equal(1)) + Expect(sts.Spec.Template.Spec.InitContainers[0].Name).To(Equal("mobilitydb")) + + Expect(k8sClient.Delete(ctx, instance.GetClientObject())).To( + Succeed(), + "failed to delete PostgreSQL instance", + ) + }) + + It("Provisions the PostgreSQL instance with multiple PostgreSQL extensions", func() { + extensions := []string{"MobilityDB", "pg-qualstats"} + instance, err = dsi.New( + dataservice, + testingNamespace, + framework.GenerateName( + instanceNamePrefix, GinkgoParallelProcess(), suffixLength), + singleReplica, + ) + Expect(err).To(BeNil(), "failed to generate new DSI resource") + + pg, ok = instance.GetClientObject().(*pgv1beta3.Postgresql) + Expect(ok).To(BeTrue(), + "failed to cast object interface to PostgreSQL struct") + pg.Spec.Extensions = extensions + + Expect(k8sClient.Create(ctx, pg)). + To(Succeed(), fmt.Sprintf("failed to create instance %s/%s", + instance.GetNamespace(), instance.GetName())) + dsi.WaitForReadiness(ctx, instance.GetClientObject(), k8sClient) + + sts := &appsv1.StatefulSet{} + Expect(k8sClient.Get(ctx, + types.NamespacedName{Name: instance.GetName(), + Namespace: instance.GetNamespace()}, + sts)).To(Succeed(), "failed to get statefulset") + + Expect(len(sts.Spec.Template.Spec.InitContainers)).To(Equal(2)) + Expect(sts.Spec.Template.Spec.InitContainers[0].Name).To(Equal("mobilitydb")) + Expect(sts.Spec.Template.Spec.InitContainers[1].Name).To(Equal("pg-qualstats")) + + Expect(k8sClient.Delete(ctx, instance.GetClientObject())).To( + Succeed(), + "failed to delete PostgreSQL instance", + ) + }) + + It("Adds one PostgreSQL extension on update", func() { + instance, err = dsi.New( + dataservice, + testingNamespace, + framework.GenerateName( + instanceNamePrefix, GinkgoParallelProcess(), suffixLength), + singleReplica, + ) + Expect(err).To(BeNil(), "failed to generate new DSI resource") + + Expect(k8sClient.Create(ctx, instance.GetClientObject())). + To(Succeed(), fmt.Sprintf("failed to create instance %s/%s", + instance.GetNamespace(), instance.GetName())) + dsi.WaitForReadiness(ctx, instance.GetClientObject(), k8sClient) + + var currDSI v1beta3.Postgresql + Eventually(func() error { + if err := k8sClient.Get(ctx, types.NamespacedName{ + Namespace: instance.GetNamespace(), + Name: instance.GetName(), + }, &currDSI); err != nil { + return err + } + + currDSI.Spec.Extensions = []string{"MobilityDB"} + return k8sClient.Update(ctx, &currDSI) + }, asyncOpsTimeoutMins, 1*time.Second).Should(BeNil()) + + Eventually(func(g Gomega) { + sts := &appsv1.StatefulSet{} + g.Expect(k8sClient.Get(ctx, + types.NamespacedName{Name: instance.GetName(), + Namespace: instance.GetNamespace()}, + sts)).To(Succeed(), "failed to get statefulset") + + g.Expect(len(sts.Spec.Template.Spec.InitContainers)).To(Equal(1)) + g.Expect(sts.Spec.Template.Spec.InitContainers[0].Name).To(Equal("mobilitydb")) + }, asyncOpsTimeoutMins, 1*time.Second).Should(Succeed()) + }) + + It("Adds multiple PostgreSQL extensions on update", func() { + instance, err = dsi.New( + dataservice, + testingNamespace, + framework.GenerateName( + instanceNamePrefix, GinkgoParallelProcess(), suffixLength), + singleReplica, + ) + Expect(err).To(BeNil(), "failed to generate new DSI resource") + + Expect(k8sClient.Create(ctx, instance.GetClientObject())). + To(Succeed(), fmt.Sprintf("failed to create instance %s/%s", + instance.GetNamespace(), instance.GetName())) + dsi.WaitForReadiness(ctx, instance.GetClientObject(), k8sClient) + + var currDSI v1beta3.Postgresql + Eventually(func() error { + if err := k8sClient.Get(ctx, types.NamespacedName{ + Namespace: instance.GetNamespace(), + Name: instance.GetName(), + }, &currDSI); err != nil { + return err + } + + currDSI.Spec.Extensions = []string{"MobilityDB", "pg-qualstats"} + return k8sClient.Update(ctx, &currDSI) + }, asyncOpsTimeoutMins, 1*time.Second).Should(BeNil()) + + Eventually(func(g Gomega) { + sts := &appsv1.StatefulSet{} + g.Expect(k8sClient.Get(ctx, + types.NamespacedName{Name: instance.GetName(), + Namespace: instance.GetNamespace()}, + sts)).To(Succeed(), "failed to get statefulset") + + g.Expect(len(sts.Spec.Template.Spec.InitContainers)).To(Equal(2)) + g.Expect(sts.Spec.Template.Spec.InitContainers[0].Name).To(Equal("mobilitydb")) + g.Expect(sts.Spec.Template.Spec.InitContainers[1].Name).To(Equal("pg-qualstats")) + }, asyncOpsTimeoutMins, 1*time.Second).Should(Succeed()) + }) + + It("Removes one PostgreSQL extension on update", func() { + extensions := []string{"MobilityDB", "pg-qualstats"} + instance, err = dsi.New( + dataservice, + testingNamespace, + framework.GenerateName( + instanceNamePrefix, GinkgoParallelProcess(), suffixLength), + singleReplica, + ) + Expect(err).To(BeNil(), "failed to generate new DSI resource") + + pg, ok = instance.GetClientObject().(*pgv1beta3.Postgresql) + Expect(ok).To(BeTrue(), + "failed to cast object interface to PostgreSQL struct") + pg.Spec.Extensions = extensions + + Expect(k8sClient.Create(ctx, pg)). + To(Succeed(), fmt.Sprintf("failed to create instance %s/%s", + instance.GetNamespace(), instance.GetName())) + dsi.WaitForReadiness(ctx, instance.GetClientObject(), k8sClient) + + var currDSI v1beta3.Postgresql + Eventually(func() error { + if err := k8sClient.Get(ctx, types.NamespacedName{ + Namespace: instance.GetNamespace(), + Name: instance.GetName(), + }, &currDSI); err != nil { + return err + } + + currDSI.Spec.Extensions = []string{"MobilityDB"} + return k8sClient.Update(ctx, &currDSI) + }, asyncOpsTimeoutMins, 1*time.Second).Should(BeNil()) + + Eventually(func(g Gomega) { + sts := &appsv1.StatefulSet{} + g.Expect(k8sClient.Get(ctx, + types.NamespacedName{Name: instance.GetName(), + Namespace: instance.GetNamespace()}, + sts)).To(Succeed(), "failed to get statefulset") + + g.Expect(len(sts.Spec.Template.Spec.InitContainers)).To(Equal(1)) + g.Expect(sts.Spec.Template.Spec.InitContainers[0].Name).To(Equal("mobilitydb")) + }, asyncOpsTimeoutMins, 1*time.Second).Should(Succeed()) + }) + + It("Removes all PostgreSQL extensions on update", func() { + extensions := []string{"MobilityDB"} + instance, err = dsi.New( + dataservice, + testingNamespace, + framework.GenerateName( + instanceNamePrefix, GinkgoParallelProcess(), suffixLength), + singleReplica, + ) + Expect(err).To(BeNil(), "failed to generate new DSI resource") + + pg, ok = instance.GetClientObject().(*pgv1beta3.Postgresql) + Expect(ok).To(BeTrue(), + "failed to cast object interface to PostgreSQL struct") + pg.Spec.Extensions = extensions + + Expect(k8sClient.Create(ctx, pg)). + To(Succeed(), fmt.Sprintf("failed to create instance %s/%s", + instance.GetNamespace(), instance.GetName())) + dsi.WaitForReadiness(ctx, instance.GetClientObject(), k8sClient) + + var currDSI v1beta3.Postgresql + Eventually(func() error { + if err := k8sClient.Get(ctx, types.NamespacedName{ + Namespace: instance.GetNamespace(), + Name: instance.GetName(), + }, &currDSI); err != nil { + return err + } + + currDSI.Spec.Extensions = []string{} + return k8sClient.Update(ctx, &currDSI) + }, asyncOpsTimeoutMins, 1*time.Second).Should(BeNil()) + + Eventually(func(g Gomega) { + sts := &appsv1.StatefulSet{} + g.Expect(k8sClient.Get(ctx, + types.NamespacedName{Name: instance.GetName(), + Namespace: instance.GetNamespace()}, + sts)).To(Succeed(), "failed to get statefulset") + + // After removing all extensions the cleanup-extensions init container is still part + // of the statefulSet so that the extension related files are removed from the + // persistentVolume. + g.Expect(len(sts.Spec.Template.Spec.InitContainers)).To(Equal(0)) + }, asyncOpsTimeoutMins, 1*time.Second).Should(Succeed()) + }) + }) }) diff --git a/test/e2e/postgresql/webhooks/v1beta3/v1beta3_suite_test.go b/test/e2e/postgresql/webhooks/v1beta3/v1beta3_suite_test.go new file mode 100644 index 00000000..4acb577a --- /dev/null +++ b/test/e2e/postgresql/webhooks/v1beta3/v1beta3_suite_test.go @@ -0,0 +1,1188 @@ +package v1beta3 + +import ( + "context" + "fmt" + "strconv" + "strings" + "testing" + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + + //+kubebuilder:scaffold:imports + "github.com/anynines/a8s-deployment/test/framework" + "github.com/anynines/a8s-deployment/test/framework/dsi" + "github.com/anynines/a8s-deployment/test/framework/namespace" + pgv1beta3 "github.com/anynines/postgresql-operator/api/v1beta3" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/pointer" + runtimeClient "sigs.k8s.io/controller-runtime/pkg/client" +) + +// These tests use Ginkgo (BDD-style Go testing framework). Refer to +// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. +const ( + suffixLength = 5 + asyncOpsTimeoutMins = time.Minute * 5 +) + +var ( + ctx context.Context + cancel context.CancelFunc + testingNamespace, kubeconfigPath, dataservice, instanceNamePrefix string + + k8sClient runtimeClient.Client + + reservedLabelsKeys []string = []string{ + pgv1beta3.DSINameLabelKey, + pgv1beta3.DSIGroupLabelKey, + pgv1beta3.DSIKindLabelKey, + pgv1beta3.ReplicationRoleLabelKey, + } +) + +func TestAPIs(t *testing.T) { + RegisterFailHandler(Fail) + + RunSpecs(t, "Validating Webhook Suite") +} + +var _ = BeforeSuite(func() { + ctx, cancel = context.WithCancel(context.Background()) + + // Parse environmental variable configuration + config, err := framework.ParseEnv() + Expect(err).To(BeNil(), "failed to parse environmental variables as configuration") + kubeconfigPath, instanceNamePrefix, dataservice, testingNamespace = framework.ConfigToVars(config) + + // Create Kubernetes client for interacting with the Kubernetes API + k8sClient, err = dsi.NewK8sClient(dataservice, kubeconfigPath) + Expect(err).To(BeNil(), + fmt.Sprintf("error creating Kubernetes client for dataservice %s", dataservice)) + + Expect(namespace.CreateIfNotExists(ctx, testingNamespace, k8sClient)). + To(Succeed(), "failed to create testing namespace") +}) + +var _ = Describe("Validating webhook", func() { + Context("DSI name length validation on creation", func() { + It("Allows a DSI with name of only one character", func() { + dsi := newDSI(withNameOfLength(1)) + err := k8sClient.Create(ctx, dsi) + Expect(err).NotTo(HaveOccurred()) + }) + + It("Allows a DSI with name half the maximum length", func() { + dsi := newDSI(withNameOfLength(pgv1beta3.MaxNameLengthChars / 2)) + err := k8sClient.Create(ctx, dsi) + Expect(err).NotTo(HaveOccurred()) + }) + + It("Allows a DSI with name shorter than the maximum by one", func() { + dsi := newDSI(withNameOfLength(pgv1beta3.MaxNameLengthChars - 1)) + err := k8sClient.Create(ctx, dsi) + Expect(err).NotTo(HaveOccurred()) + }) + + It("Allows a DSI with name as long as the maximum", func() { + dsi := newDSI(withNameOfLength(pgv1beta3.MaxNameLengthChars)) + err := k8sClient.Create(ctx, dsi) + Expect(err).NotTo(HaveOccurred()) + }) + + It("Rejects a DSI with name longer than the maximum by one", func() { + dsi := newDSI(withNameOfLength(pgv1beta3.MaxNameLengthChars + 1)) + err := k8sClient.Create(ctx, dsi) + Expect(errors.IsInvalid(err)).To(BeTrue()) + Expect(err.Error()).To(ContainSubstring("metadata.name"), + "error message doesn't mention invalid field name") + Expect(err.Error()).To(ContainSubstring(fmt.Sprint(pgv1beta3.MaxNameLengthChars+1)), + "error message doesn't mention the actual name length") + Expect(err.Error()).To(ContainSubstring(fmt.Sprint(pgv1beta3.MaxNameLengthChars)), + "error message doesn't mention the maximum name length") + }) + + It("Rejects a DSI with name twice the maximum length", func() { + dsi := newDSI(withNameOfLength(2 * pgv1beta3.MaxNameLengthChars)) + err := k8sClient.Create(ctx, dsi) + Expect(errors.IsInvalid(err)).To(BeTrue()) + Expect(err.Error()).To(ContainSubstring("metadata.name"), + "error message doesn't mention invalid field name") + Expect(err.Error()).To(ContainSubstring(fmt.Sprint(2*pgv1beta3.MaxNameLengthChars)), + "error message doesn't mention the actual name length") + Expect(err.Error()).To(ContainSubstring(fmt.Sprint(pgv1beta3.MaxNameLengthChars)), + "error message doesn't mention the maximum name length") + }) + }) + + Context("Storage size validation on creation", func() { + It("Allows a DSI with storage size of 1Gi", func() { + dsi := newDSI(withName("dsi-1gi-pass"), withStorageSize("1Gi")) + err := k8sClient.Create(ctx, dsi) + Expect(err).NotTo(HaveOccurred()) + }) + + It("Allows a DSI with storage size of 42Gi", func() { + dsi := newDSI(withName("dsi-42gi-pass"), withStorageSize("42Gi")) + err := k8sClient.Create(ctx, dsi) + Expect(err).NotTo(HaveOccurred()) + }) + + It("Allows a DSI with storage size of 2000M", func() { + dsi := newDSI(withName("dsi-2000m-pass"), withStorageSize("2000M")) + err := k8sClient.Create(ctx, dsi) + Expect(err).NotTo(HaveOccurred()) + }) + + It("Allows a DSI with storage size of 0.5Gi", func() { + dsi := newDSI(withName("dsi-0.5gi-pass"), withStorageSize("0.5Gi")) + err := k8sClient.Create(ctx, dsi) + Expect(err).NotTo(HaveOccurred()) + }) + + It("Rejects a DSI with storage size of 1Mi", func() { + dsi := newDSI(withName("dsi-1mi-fail"), withStorageSize("1Mi")) + err := k8sClient.Create(ctx, dsi) + Expect(errors.IsInvalid(err)).To(BeTrue()) + Expect(err.Error()).To(ContainSubstring("spec.volumeSize"), + "error message doesn't mention name of the invalid field") + Expect(err.Error()).To(ContainSubstring(pgv1beta3.MinVolumeSize), + "error message doesn't mention the minimum storage size") + Expect(err.Error()).To(ContainSubstring("1Mi"), + "error message doesn't mention the specified storage size") + }) + + It("Rejects a DSI with storage size of 1k", func() { + dsi := newDSI(withName("dsi-1k-fail"), withStorageSize("1k")) + err := k8sClient.Create(ctx, dsi) + Expect(errors.IsInvalid(err)).To(BeTrue()) + Expect(err.Error()).To(ContainSubstring("spec.volumeSize"), + "error message doesn't mention name of the invalid field") + Expect(err.Error()).To(ContainSubstring(pgv1beta3.MinVolumeSize), + "error message doesn't mention the minimum storage size") + Expect(err.Error()).To(ContainSubstring("1k"), + "error message doesn't mention the specified storage size") + }) + }) + + Context("Labels validation on creation", func() { + var dsi *pgv1beta3.Postgresql + + Context("Valid labels", func() { + AfterEach(func() { + Expect(k8sClient.Delete(ctx, dsi)).To(Succeed(), "failed to delete DSI after test") + }) + + It("Allows a DSI with nil labels", func() { + dsi = newDSI(withLabels(nil)) + Expect(k8sClient.Create(ctx, dsi)).To(Succeed(), + "failed to create DSI with nil labels even if it's allowed") + }) + + It("Allows a DSI with empty labels", func() { + dsi = newDSI(withLabels(map[string]string{})) + Expect(k8sClient.Create(ctx, dsi)).To(Succeed(), + "failed to create DSI with empty labels even if it's allowed") + }) + + It("Allows a DSI with only allowed labels", func() { + labels := map[string]string{ + "allowed-label-1": "val1", + "allowed-label-2": "val2", + } + dsi = newDSI(withLabels(labels)) + Expect(k8sClient.Create(ctx, dsi)).To(Succeed(), + fmt.Sprintf("failed to create DSI with allowed labels %s", labels)) + }) + + It("Allows a DSI with only allowed labels which are similar to the reserved ones", + func() { + reservedLabelKeyWithExtraCharAtTheBeginning := "x" + reservedLabelsKeys[0] + reservedLabelKeyWithExtraCharAtTheEnd := reservedLabelsKeys[1] + "a" + reservedLabelKeyWithoutFirstChar := reservedLabelsKeys[2][1:] + reservedLabelKeyWithoutLastChar := reservedLabelsKeys[3][:len(reservedLabelsKeys[3])-1] + reservedLabelKeyWithoutMiddleChar := reservedLabelsKeys[0][:len(reservedLabelsKeys[0])/2] + + reservedLabelsKeys[0][1+len(reservedLabelsKeys[0])/2:] + + labels := map[string]string{ + reservedLabelKeyWithExtraCharAtTheBeginning: "val1", + reservedLabelKeyWithExtraCharAtTheEnd: "val2", + reservedLabelKeyWithoutFirstChar: "val3", + reservedLabelKeyWithoutLastChar: "val4", + reservedLabelKeyWithoutMiddleChar: "val5", + } + dsi = newDSI(withLabels(labels)) + Expect(k8sClient.Create(ctx, dsi)).To(Succeed(), + fmt.Sprintf("failed to create DSI with allowed labels %s", labels)) + }) + }) + + Context("Invalid labels", func() { + It("Rejects a DSI with just one reserved label", func() { + labels := map[string]string{ + reservedLabelsKeys[0]: "val1", + } + dsi = newDSI(withLabels(labels)) + + err := k8sClient.Create(ctx, dsi) + + Expect(errors.IsInvalid(err)).To(BeTrue()) + Expect(err.Error()).To(ContainSubstring(reservedLabelsKeys[0]), + "got error that doesn't mention the reserved labels while it should") + Expect(strings.ToLower(err.Error())).To(ContainSubstring("reserved"), + "got error that doesn't mention the word \"reserved\" while it should") + Expect(err.Error()).To(ContainSubstring("metadata.labels"), + "got error that doesn't mention the name of the invalid field while it should") + }) + + It("Rejects a DSI with two reserved labels", func() { + labels := map[string]string{ + reservedLabelsKeys[1]: "val1", + reservedLabelsKeys[2]: "val2", + } + dsi = newDSI(withLabels(labels)) + + err := k8sClient.Create(ctx, dsi) + + Expect(errors.IsInvalid(err)).To(BeTrue()) + Expect(err.Error()).To(ContainSubstring(reservedLabelsKeys[1]), + "got error that doesn't mention the reserved labels while it should") + Expect(err.Error()).To(ContainSubstring(reservedLabelsKeys[2]), + "got error that doesn't mention the reserved labels while it should") + Expect(strings.ToLower(err.Error())).To(ContainSubstring("reserved"), + "got error that doesn't mention the word \"reserved\" while it should") + Expect(err.Error()).To(ContainSubstring("metadata.labels"), + "got error that doesn't mention the name of the invalid field while it should") + }) + + It("Rejects a DSI whose labels exactly match the reserved ones", func() { + labels := make(map[string]string, len(reservedLabelsKeys)) + for i, k := range reservedLabelsKeys { + labels[k] = "val" + strconv.Itoa(i) + } + dsi = newDSI(withLabels(labels)) + + err := k8sClient.Create(ctx, dsi) + + Expect(errors.IsInvalid(err)).To(BeTrue()) + for _, k := range reservedLabelsKeys { + Expect(err.Error()).To(ContainSubstring(k), + "got error that doesn't mention the reserved labels while it should") + } + Expect(strings.ToLower(err.Error())).To(ContainSubstring("reserved"), + "got error that doesn't mention the word \"reserved\" while it should") + Expect(err.Error()).To(ContainSubstring("metadata.labels"), + "got error that doesn't mention the name of the invalid field while it should") + }) + + It("Rejects a DSI with one reserved label plus some allowed ones", func() { + labels := map[string]string{ + "allowed-label-1": "val1", + "allowed-label-2": "val2", + reservedLabelsKeys[3]: "val3", + } + dsi = newDSI(withLabels(labels)) + + err := k8sClient.Create(ctx, dsi) + + Expect(errors.IsInvalid(err)).To(BeTrue()) + Expect(err.Error()).To(ContainSubstring(reservedLabelsKeys[3]), + "got error that doesn't mention the reserved labels while it should") + Expect(strings.ToLower(err.Error())).To(ContainSubstring("reserved"), + "got error that doesn't mention the word \"reserved\" while it should") + Expect(err.Error()).To(ContainSubstring("metadata.labels"), + "got error that doesn't mention the name of the invalid field while it should") + }) + + It("Rejects a DSI with all reserved labels plus some allowed ones", func() { + labels := make(map[string]string, len(reservedLabelsKeys)+3) + labels["allowed-label-1"] = "val100" + labels["allowed-label-2"] = "val101" + labels["allowed-label-3"] = "val102" + for i, k := range reservedLabelsKeys { + labels[k] = "val" + strconv.Itoa(i) + } + dsi = newDSI(withLabels(labels)) + + err := k8sClient.Create(ctx, dsi) + + Expect(errors.IsInvalid(err)).To(BeTrue()) + for _, k := range reservedLabelsKeys { + Expect(err.Error()).To(ContainSubstring(k), + "got error that doesn't mention the reserved labels while it should") + } + Expect(strings.ToLower(err.Error())).To(ContainSubstring("reserved"), + "got error that doesn't mention the word \"reserved\" while it should") + Expect(err.Error()).To(ContainSubstring("metadata.labels"), + "got error that doesn't mention the name of the invalid field while it should") + }) + }) + }) + + Context("Labels validation on update", func() { + var dsi *pgv1beta3.Postgresql + + AfterEach(func() { + Expect(k8sClient.Delete(ctx, dsi)).To(Succeed(), "failed to delete DSI after test") + }) + + Context("Valid labels", func() { + It("Allows update from valid labels to nil ones", func() { + labels := map[string]string{ + "allowed-label-1": "val1", + "allowed-label-2": "val2", + } + dsi = newDSI(withLabels(labels)) + Expect(k8sClient.Create(ctx, dsi)).To(Succeed()) + + Eventually(func() error { + var currDSI pgv1beta3.Postgresql + if err := k8sClient.Get(ctx, types.NamespacedName{ + Namespace: dsi.GetNamespace(), + Name: dsi.GetName(), + }, &currDSI); err != nil { + return err + } + + currDSI.Labels = nil + + return k8sClient.Update(ctx, &currDSI) + }, asyncOpsTimeoutMins, 1*time.Second).Should(BeNil()) + }) + + It("Allows update from valid labels to empty ones", func() { + labels := map[string]string{ + "allowed-label-1": "val1", + } + dsi = newDSI(withLabels(labels)) + Expect(k8sClient.Create(ctx, dsi)).To(Succeed()) + + Eventually(func() error { + var currDSI pgv1beta3.Postgresql + if err := k8sClient.Get(ctx, types.NamespacedName{ + Namespace: dsi.GetNamespace(), + Name: dsi.GetName(), + }, &currDSI); err != nil { + return err + } + + currDSI.Labels = map[string]string{} + + return k8sClient.Update(ctx, &currDSI) + }, asyncOpsTimeoutMins, 1*time.Second).Should(BeNil()) + }) + + It("Allows update from nil labels to valid ones", func() { + dsi = newDSI(withLabels(nil)) + Expect(k8sClient.Create(ctx, dsi)).To(Succeed()) + + Eventually(func() error { + var currDSI pgv1beta3.Postgresql + if err := k8sClient.Get(ctx, types.NamespacedName{ + Namespace: dsi.GetNamespace(), + Name: dsi.GetName(), + }, &currDSI); err != nil { + return err + } + + currDSI.Labels = map[string]string{ + "allowed-label-1": "val1", + } + + return k8sClient.Update(ctx, &currDSI) + }, asyncOpsTimeoutMins, 1*time.Second).Should(BeNil()) + }) + + It("Allows update from empty labels to valid ones", func() { + dsi = newDSI(withLabels(map[string]string{})) + Expect(k8sClient.Create(ctx, dsi)).To(Succeed()) + + Eventually(func() error { + var currDSI pgv1beta3.Postgresql + if err := k8sClient.Get(ctx, types.NamespacedName{ + Namespace: dsi.GetNamespace(), + Name: dsi.GetName(), + }, &currDSI); err != nil { + return err + } + + currDSI.Labels = map[string]string{ + "allowed-label-1": "val1", + "allowed-label-2": "val2", + } + + return k8sClient.Update(ctx, &currDSI) + }, asyncOpsTimeoutMins, 1*time.Second).Should(BeNil()) + }) + + It("Allows addition of valid labels", func() { + labels := map[string]string{ + "allowed-label-1": "val1", + } + dsi = newDSI(withLabels(labels)) + Expect(k8sClient.Create(ctx, dsi)).To(Succeed()) + + Eventually(func() error { + var currDSI pgv1beta3.Postgresql + if err := k8sClient.Get(ctx, types.NamespacedName{ + Namespace: dsi.GetNamespace(), + Name: dsi.GetName(), + }, &currDSI); err != nil { + return err + } + + currDSI.Labels = map[string]string{ + "allowed-label-1": "val1", + "allowed-label-2": "val2", + "allowed-label-3": "val3", + } + + return k8sClient.Update(ctx, &currDSI) + }, asyncOpsTimeoutMins, 1*time.Second).Should(BeNil()) + }) + + It("Allows removal of labels", func() { + labels := map[string]string{ + "allowed-label-1": "val1", + "allowed-label-2": "val2", + "allowed-label-3": "val3", + } + dsi = newDSI(withLabels(labels)) + Expect(k8sClient.Create(ctx, dsi)).To(Succeed()) + + Eventually(func() error { + var currDSI pgv1beta3.Postgresql + if err := k8sClient.Get(ctx, types.NamespacedName{ + Namespace: dsi.GetNamespace(), + Name: dsi.GetName(), + }, &currDSI); err != nil { + return err + } + + currDSI.Labels = map[string]string{ + "allowed-label-1": "val1", + } + + return k8sClient.Update(ctx, &currDSI) + }, asyncOpsTimeoutMins, 1*time.Second).Should(BeNil()) + }) + }) + + Context("Invalid labels", func() { + It("Rejects update from nil labels to reserved only labels", func() { + dsi = newDSI(withLabels(nil)) + Expect(k8sClient.Create(ctx, dsi)).To(Succeed()) + + var err error + Eventually(func(g Gomega) { + var currDSI pgv1beta3.Postgresql + err = k8sClient.Get(ctx, types.NamespacedName{ + Namespace: dsi.GetNamespace(), + Name: dsi.GetName(), + }, &currDSI) + g.Expect(err).To(BeNil(), "failed to get DSI object") + + currDSI.Labels = map[string]string{ + reservedLabelsKeys[0]: "val1", + } + + err = k8sClient.Update(ctx, &currDSI) + g.Expect(errors.IsInvalid(err)).To(BeTrue()) + }, asyncOpsTimeoutMins, 1*time.Second).Should(Succeed()) + + Expect(err.Error()).To(ContainSubstring(reservedLabelsKeys[0]), + "got error that doesn't mention the reserved labels while it should") + Expect(strings.ToLower(err.Error())).To(ContainSubstring("reserved"), + "got error that doesn't mention the word \"reserved\" while it should") + Expect(err.Error()).To(ContainSubstring("metadata.labels"), + "got error that doesn't mention the name of the invalid field while it should") + }) + + It("Rejects update from nil labels to reserved and valid labels", func() { + dsi = newDSI(withLabels(nil)) + Expect(k8sClient.Create(ctx, dsi)).To(Succeed()) + + var err error + Eventually(func(g Gomega) { + var currDSI pgv1beta3.Postgresql + err = k8sClient.Get(ctx, types.NamespacedName{ + Namespace: dsi.GetNamespace(), + Name: dsi.GetName(), + }, &currDSI) + g.Expect(err).To(BeNil(), "failed to get DSI object") + + currDSI.Labels = map[string]string{ + reservedLabelsKeys[1]: "val1", + "allowed-label": "val2", + } + + err = k8sClient.Update(ctx, &currDSI) + g.Expect(errors.IsInvalid(err)).To(BeTrue()) + }, asyncOpsTimeoutMins, 1*time.Second).Should(Succeed()) + + Expect(err.Error()).To(ContainSubstring(reservedLabelsKeys[1]), + "got error that doesn't mention the reserved labels while it should") + Expect(strings.ToLower(err.Error())).To(ContainSubstring("reserved"), + "got error that doesn't mention the word \"reserved\" while it should") + Expect(err.Error()).To(ContainSubstring("metadata.labels"), + "got error that doesn't mention the name of the invalid field while it should") + }) + + It("Rejects update from empty labels to reserved only labels", func() { + dsi = newDSI(withLabels(map[string]string{})) + Expect(k8sClient.Create(ctx, dsi)).To(Succeed()) + + var err error + Eventually(func(g Gomega) { + var currDSI pgv1beta3.Postgresql + err = k8sClient.Get(ctx, types.NamespacedName{ + Namespace: dsi.GetNamespace(), + Name: dsi.GetName(), + }, &currDSI) + g.Expect(err).To(BeNil(), "failed to get DSI object") + + currDSI.Labels = map[string]string{ + reservedLabelsKeys[2]: "val1", + reservedLabelsKeys[3]: "val1", + } + + err = k8sClient.Update(ctx, &currDSI) + g.Expect(errors.IsInvalid(err)).To(BeTrue()) + }, asyncOpsTimeoutMins, 1*time.Second).Should(Succeed()) + + Expect(err.Error()).To(ContainSubstring(reservedLabelsKeys[2]), + "got error that doesn't mention the reserved labels while it should") + Expect(err.Error()).To(ContainSubstring(reservedLabelsKeys[3]), + "got error that doesn't mention the reserved labels while it should") + Expect(strings.ToLower(err.Error())).To(ContainSubstring("reserved"), + "got error that doesn't mention the word \"reserved\" while it should") + Expect(err.Error()).To(ContainSubstring("metadata.labels"), + "got error that doesn't mention the name of the invalid field while it should") + }) + + It("Rejects update from empty labels to reserved and valid labels", func() { + dsi = newDSI(withLabels(map[string]string{})) + Expect(k8sClient.Create(ctx, dsi)).To(Succeed()) + + var err error + Eventually(func(g Gomega) { + var currDSI pgv1beta3.Postgresql + err = k8sClient.Get(ctx, types.NamespacedName{ + Namespace: dsi.GetNamespace(), + Name: dsi.GetName(), + }, &currDSI) + g.Expect(err).To(BeNil(), "failed to get DSI object") + + currDSI.Labels = map[string]string{ + reservedLabelsKeys[1]: "val1", + "allowed-label-1": "val2", + "allowed-label-2": "val3", + } + + err = k8sClient.Update(ctx, &currDSI) + g.Expect(errors.IsInvalid(err)).To(BeTrue()) + }, asyncOpsTimeoutMins, 1*time.Second).Should(Succeed()) + + Expect(err.Error()).To(ContainSubstring(reservedLabelsKeys[1]), + "got error that doesn't mention the reserved labels while it should") + Expect(strings.ToLower(err.Error())).To(ContainSubstring("reserved"), + "got error that doesn't mention the word \"reserved\" while it should") + Expect(err.Error()).To(ContainSubstring("metadata.labels"), + "got error that doesn't mention the name of the invalid field while it should") + }) + + It("Rejects update from valid labels to reserved labels only", func() { + labels := map[string]string{ + "allowed-label-1": "val1", + "allowed-label-2": "val2", + } + dsi = newDSI(withLabels(labels)) + Expect(k8sClient.Create(ctx, dsi)).To(Succeed()) + + var err error + Eventually(func(g Gomega) { + var currDSI pgv1beta3.Postgresql + err = k8sClient.Get(ctx, types.NamespacedName{ + Namespace: dsi.GetNamespace(), + Name: dsi.GetName(), + }, &currDSI) + g.Expect(err).To(BeNil(), "failed to get DSI object") + + currDSI.Labels = map[string]string{ + reservedLabelsKeys[0]: "val2", + reservedLabelsKeys[2]: "val2", + } + + err = k8sClient.Update(ctx, &currDSI) + g.Expect(errors.IsInvalid(err)).To(BeTrue()) + }, asyncOpsTimeoutMins, 1*time.Second).Should(Succeed()) + + Expect(err.Error()).To(ContainSubstring(reservedLabelsKeys[0]), + "got error that doesn't mention the reserved labels while it should") + Expect(err.Error()).To(ContainSubstring(reservedLabelsKeys[2]), + "got error that doesn't mention the reserved labels while it should") + Expect(strings.ToLower(err.Error())).To(ContainSubstring("reserved"), + "got error that doesn't mention the word \"reserved\" while it should") + Expect(err.Error()).To(ContainSubstring("metadata.labels"), + "got error that doesn't mention the name of the invalid field while it should") + }) + + It("Rejects update from valid labels to reserved and valid labels", func() { + labels := map[string]string{ + "allowed-label-1": "val1", + } + dsi = newDSI(withLabels(labels)) + Expect(k8sClient.Create(ctx, dsi)).To(Succeed()) + + var err error + Eventually(func(g Gomega) { + var currDSI pgv1beta3.Postgresql + err = k8sClient.Get(ctx, types.NamespacedName{ + Namespace: dsi.GetNamespace(), + Name: dsi.GetName(), + }, &currDSI) + g.Expect(err).To(BeNil(), "failed to get DSI object") + + currDSI.Labels = map[string]string{ + "allowed-label-1": "val1", + reservedLabelsKeys[0]: "val2", + } + + err = k8sClient.Update(ctx, &currDSI) + g.Expect(errors.IsInvalid(err)).To(BeTrue()) + }, asyncOpsTimeoutMins, 1*time.Second).Should(Succeed()) + + Expect(err.Error()).To(ContainSubstring(reservedLabelsKeys[0]), + "got error that doesn't mention the reserved labels while it should") + Expect(strings.ToLower(err.Error())).To(ContainSubstring("reserved"), + "got error that doesn't mention the word \"reserved\" while it should") + Expect(err.Error()).To(ContainSubstring("metadata.labels"), + "got error that doesn't mention the name of the invalid field while it should") + }) + }) + }) + + Context("Test validation of creation with invalid storage size, name length and labels "+ + "together", func() { + It("Rejects a DSI with storage of 1k, name longer than the max by two and a reserved label", + func() { + labels := map[string]string{ + reservedLabelsKeys[3]: "val1", + "allowed-label": "val2", + } + dsi := newDSI(withNameOfLength(pgv1beta3.MaxNameLengthChars+2), + withStorageSize("1k"), + withLabels(labels)) + err := k8sClient.Create(ctx, dsi) + Expect(errors.IsInvalid(err)).To(BeTrue()) + Expect(err.Error()).To(ContainSubstring("metadata.name"), + "error message doesn't mention invalid field name") + Expect(err.Error()).To(ContainSubstring(fmt.Sprint(pgv1beta3.MaxNameLengthChars+2)), + "error message doesn't mention the actual name length") + Expect(err.Error()).To(ContainSubstring(fmt.Sprint(pgv1beta3.MaxNameLengthChars)), + "error message doesn't mention the maximum name length") + Expect(err.Error()).To(ContainSubstring("spec.volumeSize"), + "error message doesn't mention name of the invalid field") + Expect(err.Error()).To(ContainSubstring(pgv1beta3.MinVolumeSize), + "error message doesn't mention the minimum storage size") + Expect(err.Error()).To(ContainSubstring("1k"), + "error message doesn't mention the specified storage size") + Expect(err.Error()).To(ContainSubstring(reservedLabelsKeys[3]), + "error message doesn't mention the reserved labels") + Expect(strings.ToLower(err.Error())).To(ContainSubstring("reserved"), + "error message doesn't mention the word \"reserved\"") + Expect(err.Error()).To(ContainSubstring("metadata.labels"), + "error message doesn't mention invalid field name") + }) + }) + + Context("Storage size update validation", func() { + It("Allows update with no adjustment to volume size", func() { + dsi := newDSI(withName("dsi-1gi-nochange-pass"), + withStorageSize("1Gi"), + withReplicas(1)) + err := k8sClient.Create(ctx, dsi) + Expect(err).NotTo(HaveOccurred()) + + // Update the replicas from 1 to 3 because we just want to update something different + // than the resources. + Eventually(func() error { + var currDSI pgv1beta3.Postgresql + if err := k8sClient.Get(ctx, types.NamespacedName{ + Namespace: dsi.GetNamespace(), + Name: dsi.GetName(), + }, &currDSI); err != nil { + return err + } + + currDSI.Spec.Replicas = pointer.Int32(3) + + return k8sClient.Update(ctx, &currDSI) + }, asyncOpsTimeoutMins, 1*time.Second).Should(BeNil()) + Expect(err).NotTo(HaveOccurred()) + }) + + It("Rejects scaling a DSI volume from 2Gi to 1Gi", func() { + var err error + dsi := newDSI(withName("dsi-2-to-1gi-fail"), withStorageSize("2Gi")) + err = k8sClient.Create(ctx, dsi) + Expect(err).NotTo(HaveOccurred()) + + Eventually(func(g Gomega) { + var currDSI pgv1beta3.Postgresql + err = k8sClient.Get(ctx, types.NamespacedName{ + Namespace: dsi.GetNamespace(), + Name: dsi.GetName(), + }, &currDSI) + g.Expect(err).To(BeNil(), "failed to get DSI object") + + currDSI.Spec.VolumeSize = resource.MustParse("1Gi") + + err = k8sClient.Update(ctx, &currDSI) + g.Expect(errors.IsInvalid(err)).To(BeTrue()) + }, asyncOpsTimeoutMins, 1*time.Second).Should(Succeed()) + + Expect(err.Error()).To(ContainSubstring("spec.volumeSize"), + "error message doesn't mention name of the invalid field") + Expect(err.Error()).To(ContainSubstring("2Gi"), + "error message doesn't mention the current volume size") + Expect(err.Error()).To(ContainSubstring("1Gi"), + "error message doesn't mention the new volume size") + }) + + It("Rejects scaling a DSI volume from 2Gi to 3k", func() { + var err error + dsi := newDSI(withName("dsi-2-to-3k-fail"), withStorageSize("2Gi")) + err = k8sClient.Create(ctx, dsi) + Expect(err).NotTo(HaveOccurred()) + + Eventually(func(g Gomega) { + var currDSI pgv1beta3.Postgresql + err = k8sClient.Get(ctx, types.NamespacedName{ + Namespace: dsi.GetNamespace(), + Name: dsi.GetName(), + }, &currDSI) + g.Expect(err).To(BeNil(), "failed to get DSI object") + + currDSI.Spec.VolumeSize = resource.MustParse("3k") + + err = k8sClient.Update(ctx, &currDSI) + g.Expect(errors.IsInvalid(err)).To(BeTrue()) + }, asyncOpsTimeoutMins, 1*time.Second).Should(Succeed()) + + Expect(err.Error()).To(ContainSubstring("spec.volumeSize"), + "error message doesn't mention name of the invalid field") + Expect(err.Error()).To(ContainSubstring("2Gi"), + "error message doesn't mention the current volume size") + Expect(err.Error()).To(ContainSubstring("3k"), + "error message doesn't mention the new volume size") + }) + }) + + Context("Update validation with storage size and labels both invalid", func() { + var dsi *pgv1beta3.Postgresql + + AfterEach(func() { + Expect(k8sClient.Delete(ctx, dsi)).To(Succeed(), "failed to delete DSI after test") + }) + + It("Rejects update of DSI to reserved labels and smaller volume", func() { + labels := map[string]string{ + "allowed-label-1": "val1", + "allowed-label-2": "val2", + } + dsi = newDSI(withStorageSize("4Gi"), withLabels(labels)) + Expect(k8sClient.Create(ctx, dsi)).To(Succeed()) + + var err error + Eventually(func(g Gomega) { + var currDSI pgv1beta3.Postgresql + err = k8sClient.Get(ctx, types.NamespacedName{ + Namespace: dsi.GetNamespace(), + Name: dsi.GetName(), + }, &currDSI) + g.Expect(err).To(BeNil(), "failed to get DSI object") + + currDSI.Spec.VolumeSize = resource.MustParse("1Gi") + currDSI.Labels = map[string]string{ + "allowed-label-1": "val1", + reservedLabelsKeys[2]: "val2", + } + + err = k8sClient.Update(ctx, &currDSI) + g.Expect(errors.IsInvalid(err)).To(BeTrue()) + }, asyncOpsTimeoutMins, 1*time.Second).Should(Succeed()) + + Expect(err.Error()).To(ContainSubstring("spec.volumeSize"), + "error message doesn't mention name of the invalid field") + Expect(err.Error()).To(ContainSubstring("4Gi"), + "error message doesn't mention the current volume size") + Expect(err.Error()).To(ContainSubstring("1Gi"), + "error message doesn't mention the new volume size") + Expect(err.Error()).To(ContainSubstring(reservedLabelsKeys[2]), + "error message doesn't mention the reserved labels") + Expect(strings.ToLower(err.Error())).To(ContainSubstring("reserved"), + "error message doesn't mention the word \"reserved\"") + Expect(err.Error()).To(ContainSubstring("metadata.labels"), + "error message doesn't mention name of the invalid field") + }) + }) +}) + +const ( + maxLocksPerTransactionDefault int = 64 + maxLocksPerTransactionDefaultWithMobilityDB int = 100 +) + +var _ = Describe("Defaulting webhook", func() { + Context("Defaulting of configuration", func() { + var dsi *pgv1beta3.Postgresql + + AfterEach(func() { + Expect(k8sClient.Delete(ctx, dsi)).To(Succeed(), "failed to delete DSI after test") + }) + + It("Applies defaulting when maxLocksPerTransaction is not set", func() { + dsi = newDSI(withName("dsi-defaulting-with-default-config")) + Expect(k8sClient.Create(ctx, dsi)).To(Succeed()) + + dsiNSN := types.NamespacedName{Namespace: dsi.Namespace, Name: dsi.Name} + Expect(k8sClient.Get(ctx, dsiNSN, dsi)).To(Succeed()) + Expect(*dsi.Spec.Parameters.MaxLocksPerTransaction). + To(Equal(maxLocksPerTransactionDefault)) + }) + + It("Does not apply defaulting when maxLocksPerTransaction is set", func() { + maxLocksPerTransaction := 150 + customConfig := pgv1beta3.PostgresqlParameters{ + MaxLocksPerTransaction: &maxLocksPerTransaction, + } + + dsi = newDSI(withName("dsi-defaulting-with-custom-config"), + withCustomConfiguration(customConfig)) + + Expect(k8sClient.Create(ctx, dsi)).To(Succeed()) + + dsiNSN := types.NamespacedName{Namespace: dsi.Namespace, Name: dsi.Name} + Expect(k8sClient.Get(ctx, dsiNSN, dsi)).To(Succeed()) + Expect(*dsi.Spec.Parameters.MaxLocksPerTransaction). + To(Equal(maxLocksPerTransaction)) + }) + + It("Applies defaulting when maxLocksPerTransaction is not set and MobilityDB is defined", func() { + dsi = newDSI(withName("dsi-defaulting-with-mobilitydb"), + withExtensions("MobilityDB")) + + Expect(k8sClient.Create(ctx, dsi)).To(Succeed()) + + dsiNSN := types.NamespacedName{Namespace: dsi.Namespace, Name: dsi.Name} + Expect(k8sClient.Get(ctx, dsiNSN, dsi)).To(Succeed()) + Expect(*dsi.Spec.Parameters.MaxLocksPerTransaction). + To(Equal(maxLocksPerTransactionDefaultWithMobilityDB)) + }) + + It("Does not apply defaulting when maxLocksPerTransaction is set and MobilityDB is defined", func() { + dsi = newDSI(withName("dsi-defaulting-with-custom-config-and-mobilitydb"), + withExtensions("MobilityDB")) + + Expect(k8sClient.Create(ctx, dsi)).To(Succeed()) + + dsiNSN := types.NamespacedName{Namespace: dsi.Namespace, Name: dsi.Name} + Expect(k8sClient.Get(ctx, dsiNSN, dsi)).To(Succeed()) + Expect(*dsi.Spec.Parameters.MaxLocksPerTransaction). + To(Equal(maxLocksPerTransactionDefaultWithMobilityDB)) + }) + }) + + Context("Creation of an invalid PostgreSQL instance", func() { + It("Fails if the custom configuration is out of range", func() { + maxLocksPerTransaction := 5 + customConfig := pgv1beta3.PostgresqlParameters{ + MaxLocksPerTransaction: &maxLocksPerTransaction, + } + + dsi := newDSI(withName("dsi-with-custom-config-fail"), + withCustomConfiguration(customConfig)) + + err := k8sClient.Create(ctx, dsi) + Expect(errors.IsInvalid(err)).To(BeTrue()) + }) + }) + + Context("Update of custom configuration", func() { + var dsi *pgv1beta3.Postgresql + + AfterEach(func() { + Expect(k8sClient.Delete(ctx, dsi)).To(Succeed(), "failed to delete DSI after test") + }) + + It("Allows update with no adjustments to the PostgreSQL configuration", func() { + dsi = newDSI(withName("dsi-default-nochange-pass")) + Expect(k8sClient.Create(ctx, dsi)).To(Succeed()) + + Eventually(func() error { + var currDSI pgv1beta3.Postgresql + if err := k8sClient.Get(ctx, types.NamespacedName{ + Namespace: dsi.GetNamespace(), + Name: dsi.GetName(), + }, &currDSI); err != nil { + return err + } + + // Update the replicas from 1 to 3 because we just want to update something different + // than the resources. + currDSI.Spec.Replicas = pointer.Int32(3) + return k8sClient.Update(ctx, &currDSI) + }, asyncOpsTimeoutMins, 1*time.Second).Should(BeNil()) + }) + + It("Allows a DSI without default configuration being updated", func() { + dsi = newDSI(withName("dsi-default-to-custom-pass")) + Expect(k8sClient.Create(ctx, dsi)).To(Succeed()) + + maxLocksPerTransaction := 200 + Eventually(func() error { + var currDSI pgv1beta3.Postgresql + if err := k8sClient.Get(ctx, types.NamespacedName{ + Namespace: dsi.GetNamespace(), + Name: dsi.GetName(), + }, &currDSI); err != nil { + return err + } + + currDSI.Spec.Parameters.MaxLocksPerTransaction = &maxLocksPerTransaction + + return k8sClient.Update(ctx, &currDSI) + }, asyncOpsTimeoutMins, 1*time.Second).Should(BeNil()) + + dsiNSN := types.NamespacedName{Namespace: dsi.Namespace, Name: dsi.Name} + Expect(k8sClient.Get(ctx, dsiNSN, dsi)).To(Succeed()) + Expect(*dsi.Spec.Parameters.MaxLocksPerTransaction). + To(Equal(maxLocksPerTransaction)) + }) + + It("Allows a DSI with MaxLocksPerTransaction being updated", func() { + maxLocksPerTransaction := 150 + customConfig := pgv1beta3.PostgresqlParameters{ + MaxLocksPerTransaction: &maxLocksPerTransaction, + } + + dsi = newDSI(withName("dsi-150-to-200-pass"), withCustomConfiguration(customConfig)) + Expect(k8sClient.Create(ctx, dsi)).To(Succeed()) + + Eventually(func() error { + var currDSI pgv1beta3.Postgresql + if err := k8sClient.Get(ctx, types.NamespacedName{ + Namespace: dsi.GetNamespace(), + Name: dsi.GetName(), + }, &currDSI); err != nil { + return err + } + + maxLocksPerTransaction = 200 + currDSI.Spec.Parameters.MaxLocksPerTransaction = &maxLocksPerTransaction + return k8sClient.Update(ctx, &currDSI) + }, asyncOpsTimeoutMins, 1*time.Second).Should(BeNil()) + + dsiNSN := types.NamespacedName{Namespace: dsi.Namespace, Name: dsi.Name} + Expect(k8sClient.Get(ctx, dsiNSN, dsi)).To(Succeed()) + Expect(*dsi.Spec.Parameters.MaxLocksPerTransaction). + To(Equal(maxLocksPerTransaction)) + }) + + It("Fails if the updated custom configuration is out of range", func() { + maxLocksPerTransaction := 150 + customConfig := pgv1beta3.PostgresqlParameters{ + MaxLocksPerTransaction: &maxLocksPerTransaction, + } + + dsi = newDSI(withName("dsi-150-to-5-fail"), withCustomConfiguration(customConfig)) + Expect(k8sClient.Create(ctx, dsi)).To(Succeed()) + + // Minimum value for MaxLocksPerTransaction is 10 + maxLocksPerTransaction = 5 + + Eventually(func(g Gomega) { + var currDSI pgv1beta3.Postgresql + err := k8sClient.Get(ctx, types.NamespacedName{ + Namespace: dsi.GetNamespace(), + Name: dsi.GetName(), + }, &currDSI) + g.Expect(err).To(BeNil(), "failed to get DSI object") + + currDSI.Spec.Parameters.MaxLocksPerTransaction = &maxLocksPerTransaction + + err = k8sClient.Update(ctx, &currDSI) + g.Expect(errors.IsInvalid(err)).To(BeTrue()) + }, asyncOpsTimeoutMins, 1*time.Second).Should(Succeed()) + }) + + It("Succeeds if the custom configuration is removed from the CR", func() { + maxLocksPerTransaction := 150 + customConfig := pgv1beta3.PostgresqlParameters{ + MaxLocksPerTransaction: &maxLocksPerTransaction, + } + + dsi = newDSI(withName("dsi-150-to-default-pass"), withCustomConfiguration(customConfig)) + Expect(k8sClient.Create(ctx, dsi)).To(Succeed()) + + Eventually(func() error { + var currDSI pgv1beta3.Postgresql + if err := k8sClient.Get(ctx, types.NamespacedName{ + Namespace: dsi.GetNamespace(), + Name: dsi.GetName(), + }, &currDSI); err != nil { + return err + } + + currDSI.Spec.Parameters.MaxLocksPerTransaction = nil + return k8sClient.Update(ctx, &currDSI) + }, asyncOpsTimeoutMins, 1*time.Second).Should(BeNil()) + + dsiNSN := types.NamespacedName{Namespace: dsi.Namespace, Name: dsi.Name} + Expect(k8sClient.Get(ctx, dsiNSN, dsi)).To(Succeed()) + Expect(*dsi.Spec.Parameters.MaxLocksPerTransaction). + To(Equal(maxLocksPerTransactionDefault)) + }) + }) +}) + +func withCustomConfiguration(config pgv1beta3.PostgresqlParameters) func(*pgv1beta3.Postgresql) { + return func(dsi *pgv1beta3.Postgresql) { + dsi.Spec.Parameters = config + } +} + +func withExtensions(extensions ...string) func(*pgv1beta3.Postgresql) { + return func(dsi *pgv1beta3.Postgresql) { + dsi.Spec.Extensions = extensions + } +} + +func newDSI(opts ...func(*pgv1beta3.Postgresql)) *pgv1beta3.Postgresql { + instance, err := dsi.New( + dataservice, + testingNamespace, + framework.GenerateName( + instanceNamePrefix, GinkgoParallelProcess(), suffixLength), + 1, + ) + Expect(err).To(BeNil(), "failed to generate new DSI resource") + + dsi, ok := instance.GetClientObject().(*pgv1beta3.Postgresql) + Expect(ok).To(BeTrue(), + "failed to cast object interface to PostgreSQL struct") + + for _, applyOption := range opts { + applyOption(dsi) + } + return dsi +} + +func withCPURequest(cpu string) func(*pgv1beta3.Postgresql) { + return func(pg *pgv1beta3.Postgresql) { + initResourcesStructs(pg) + pg.Spec.Resources.Requests["cpu"] = resource.MustParse(cpu) + } +} + +func withCPULimit(cpu string) func(*pgv1beta3.Postgresql) { + return func(pg *pgv1beta3.Postgresql) { + initResourcesStructs(pg) + pg.Spec.Resources.Limits["cpu"] = resource.MustParse(cpu) + } +} + +func withMemoryRequest(mem string) func(*pgv1beta3.Postgresql) { + return func(pg *pgv1beta3.Postgresql) { + initResourcesStructs(pg) + pg.Spec.Resources.Requests["memory"] = resource.MustParse(mem) + } +} + +func withMemoryLimit(mem string) func(*pgv1beta3.Postgresql) { + return func(pg *pgv1beta3.Postgresql) { + initResourcesStructs(pg) + pg.Spec.Resources.Limits["memory"] = resource.MustParse(mem) + } +} + +func initResourcesStructs(pg *pgv1beta3.Postgresql) { + if pg.Spec.Resources == nil { + pg.Spec.Resources = &corev1.ResourceRequirements{} + } + if pg.Spec.Resources.Requests == nil { + pg.Spec.Resources.Requests = corev1.ResourceList{} + } + if pg.Spec.Resources.Limits == nil { + pg.Spec.Resources.Limits = corev1.ResourceList{} + } +} + +func withName(name string) func(*pgv1beta3.Postgresql) { + return func(dsi *pgv1beta3.Postgresql) { + dsi.Name = name + } +} + +func withNameOfLength(length int) func(*pgv1beta3.Postgresql) { + return func(dsi *pgv1beta3.Postgresql) { + dsi.Name = nameOfLength(length) + } +} + +func withStorageSize(size string) func(*pgv1beta3.Postgresql) { + return func(dsi *pgv1beta3.Postgresql) { + dsi.Spec.VolumeSize = resource.MustParse(size) + } +} + +func withReplicas(r int32) func(*pgv1beta3.Postgresql) { + return func(dsi *pgv1beta3.Postgresql) { + dsi.Spec.Replicas = &r + } +} + +func withLabels(l map[string]string) func(*pgv1beta3.Postgresql) { + return func(dsi *pgv1beta3.Postgresql) { + dsi.Labels = l + } +} + +// nameOfLength is a testing helper that concatenates the letter 'a' `length` times to produce a +// string of length `length`. Used to produce names of arbitrary length for PostgreSQL instances in +// tests where the length of the name matters. +// Example: nameOfLength(2) will return "aa". +// TODO: Make the generated name random. +func nameOfLength(length int) string { + name := make([]rune, length) + for i := 0; i < length; i++ { + name[i] = 'a' + } + return string(name) +} + +// now returns a string representing the date and time at which its invocation occurs, sanitized by +// removing the characters that make it unusable in an API object name (so that it can be used +// in an API object name). +func now() string { + now := time.Now().Format(time.RFC3339) + + // Replace ":" and "+" with "-" as the former are not allowed in API object names. + now = strings.Replace(now, ":", "-", -1) + now = strings.Replace(now, "+", "-", -1) + + // Only lowercase letters are allowed in API object names. + return strings.ToLower(now) +} + +var _ = AfterSuite(func() { + Expect(namespace.DeleteIfAllowed(ctx, testingNamespace, k8sClient)). + To(Succeed(), "failed to delete testing namespace") + cancel() +}) diff --git a/test/e2e/servicebinding/servicebinding_test.go b/test/e2e/servicebinding/servicebinding_test.go index 2e9aaa04..ad01ba14 100644 --- a/test/e2e/servicebinding/servicebinding_test.go +++ b/test/e2e/servicebinding/servicebinding_test.go @@ -14,7 +14,7 @@ import ( "github.com/anynines/a8s-deployment/test/framework/dsi" "github.com/anynines/a8s-deployment/test/framework/secret" "github.com/anynines/a8s-deployment/test/framework/servicebinding" - sbv1alpha1 "github.com/anynines/a8s-service-binding-controller/api/v1alpha1" + sbv1beta3 "github.com/anynines/a8s-service-binding-controller/api/v1beta3" ) const ( @@ -32,11 +32,11 @@ const ( var ( portForwardStopCh chan struct{} - sb *sbv1alpha1.ServiceBinding + sb *sbv1beta3.ServiceBinding instance dsi.Object dsiAdminClient dsi.DSIClient dsiSbClient dsi.DSIClient - sbClientMap map[*sbv1alpha1.ServiceBinding]dsi.DSIClient + sbClientMap map[*sbv1beta3.ServiceBinding]dsi.DSIClient ) var _ = Describe("Service binding", func() { @@ -255,7 +255,7 @@ var _ = Describe("Service binding", func() { }) It("Performs basic ServiceBinding lifecycle operations", func() { - sbs := make([]*sbv1alpha1.ServiceBinding, sbAmount) + sbs := make([]*sbv1beta3.ServiceBinding, sbAmount) By("Creating the ServiceBinding CR", func() { for i := 0; i < sbAmount; i++ { @@ -276,7 +276,7 @@ var _ = Describe("Service binding", func() { } }) - serviceBindingData := make(map[*sbv1alpha1.ServiceBinding]secret.SecretData) + serviceBindingData := make(map[*sbv1beta3.ServiceBinding]secret.SecretData) var serviceBindingSecret v1.Secret By("Creating the service binding secret", func() { for _, sb := range sbs { @@ -298,7 +298,7 @@ var _ = Describe("Service binding", func() { // Create one DSIClient per ServiceBinding user for interacting with the // DSI. - sbClientMap = make(map[*sbv1alpha1.ServiceBinding]dsi.DSIClient) + sbClientMap = make(map[*sbv1beta3.ServiceBinding]dsi.DSIClient) for _, sb := range sbs { dsiSbClient, err = dsi.NewClient(dataservice, strconv.Itoa(localPort), @@ -480,8 +480,8 @@ var _ = Describe("Service binding", func() { It("Performs basic ServiceBinding lifecycle operations", func() { for _, instance := range instances { - serviceBindingData := make(map[*sbv1alpha1.ServiceBinding]secret.SecretData) - sbs := make([]*sbv1alpha1.ServiceBinding, sbAmount) + serviceBindingData := make(map[*sbv1beta3.ServiceBinding]secret.SecretData) + sbs := make([]*sbv1beta3.ServiceBinding, sbAmount) By("Creating the ServiceBinding CR", func() { for i := 0; i < sbAmount; i++ { @@ -528,7 +528,7 @@ var _ = Describe("Service binding", func() { // Create one DSIClient per ServiceBinding user for interacting // with the DSI. - sbClientMap = make(map[*sbv1alpha1.ServiceBinding]dsi.DSIClient) + sbClientMap = make(map[*sbv1beta3.ServiceBinding]dsi.DSIClient) for _, sb := range sbs { dsiSbClient, err = dsi.NewClient(dataservice, strconv.Itoa(dataServiceInstanceMap[instance].portForward), diff --git a/test/e2e/topology_awareness/topology_awareness_test.go b/test/e2e/topology_awareness/topology_awareness_test.go index ad3f9347..2ff1b89f 100644 --- a/test/e2e/topology_awareness/topology_awareness_test.go +++ b/test/e2e/topology_awareness/topology_awareness_test.go @@ -16,7 +16,7 @@ import ( "github.com/anynines/a8s-deployment/test/framework/dsi" "github.com/anynines/a8s-deployment/test/framework/secret" "github.com/anynines/a8s-deployment/test/framework/servicebinding" - sbv1alpha1 "github.com/anynines/a8s-service-binding-controller/api/v1alpha1" + sbv1beta3 "github.com/anynines/a8s-service-binding-controller/api/v1beta3" ) // TODO: Test broken cases where the DSI has no tolerations: 1 taint - 0 tolerations; @@ -50,7 +50,7 @@ var _ = Describe("DSIs topology awareness", func() { instanceNSN string instanceClient dsi.DSIClient - sb *sbv1alpha1.ServiceBinding + sb *sbv1beta3.ServiceBinding sbCredentials secret.SecretData portForwardStopCh chan struct{} @@ -726,27 +726,26 @@ var _ = Describe("DSIs topology awareness", func() { }) }) }) - }) func verifyDSIPodsAreInDifferentAZs(ctx context.Context, dsiPods []corev1.Pod, - azLabelKey string) error { - + azLabelKey string, +) error { return verifyDSIPodsAreInDifferentTopologyDomains(ctx, dsiPods, "AZ", azLabelKey) } func verifyDSIPodsAreOnDifferentNodes(ctx context.Context, dsiPods []corev1.Pod, - nodeLabelKey string) error { - + nodeLabelKey string, +) error { return verifyDSIPodsAreInDifferentTopologyDomains(ctx, dsiPods, "node", nodeLabelKey) } func verifyDSIPodsAreInDifferentTopologyDomains(ctx context.Context, dsiPods []corev1.Pod, - topologyDomainName, topologyDomainKey string) error { - + topologyDomainName, topologyDomainKey string, +) error { domainToPod := map[string]string{} for _, p := range dsiPods { podNodeLabels, err := nodes.GetLabels(ctx, p.Spec.NodeName) @@ -769,8 +768,8 @@ func verifyDSIPodsAreInDifferentTopologyDomains(ctx context.Context, func verifyDSIPodsAreInMoreThanOneAZ(ctx context.Context, dsiPods []corev1.Pod, - azLabelKey string) error { - + azLabelKey string, +) error { seenAZs := map[string]struct{}{} podAZ := "" for _, p := range dsiPods { @@ -790,8 +789,8 @@ func verifyDSIPodsAreInMoreThanOneAZ(ctx context.Context, } func labelNode(ctx context.Context, nodes NodesClient, nodeName string, - labels map[string]string) error { - + labels map[string]string, +) error { // We do a Get to base the update off of a fresh version of the node, to minimize the risk of // MVCC conflicts. // For details: https://github.com/kubernetes/community/blob/master/contributors/devel/sig-architecture/api-conventions.md#concurrency-control-and-consistency diff --git a/test/framework/backup/backup.go b/test/framework/backup/backup.go index 43dbf55d..d4cbc481 100644 --- a/test/framework/backup/backup.go +++ b/test/framework/backup/backup.go @@ -11,7 +11,7 @@ import ( "k8s.io/apimachinery/pkg/types" runtimeClient "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/anynines/a8s-backup-manager/api/v1alpha1" + "github.com/anynines/a8s-backup-manager/api/v1beta3" "github.com/anynines/a8s-deployment/test/framework" ) @@ -27,10 +27,10 @@ const ( // Option represents a functional option for backup objects. To learn what a functional option is, // read here: https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis -type Option func(*v1alpha1.Backup) +type Option func(*v1beta3.Backup) func SetInstanceRef(dsi runtimeClient.Object) Option { - return func(b *v1alpha1.Backup) { + return func(b *v1beta3.Backup) { b.Spec.ServiceInstance.APIGroup = dsi.GetObjectKind().GroupVersionKind().Group b.Spec.ServiceInstance.Kind = dsi.GetObjectKind().GroupVersionKind().Kind b.Spec.ServiceInstance.Name = dsi.GetName() @@ -38,20 +38,20 @@ func SetInstanceRef(dsi runtimeClient.Object) Option { } func SetNamespacedName(dsi runtimeClient.Object) Option { - return func(b *v1alpha1.Backup) { + return func(b *v1beta3.Backup) { b.Name = framework.UniqueName(backupPrefix(dsi.GetName()), suffixLength) b.Namespace = dsi.GetNamespace() } } func MaxRetries(maxRetries string) Option { - return func(b *v1alpha1.Backup) { + return func(b *v1beta3.Backup) { b.Spec.MaxRetries = maxRetries } } -func New(opts ...Option) *v1alpha1.Backup { - b := &v1alpha1.Backup{} +func New(opts ...Option) *v1beta3.Backup { + b := &v1beta3.Backup{} for _, opt := range opts { opt(b) } @@ -64,9 +64,9 @@ func backupPrefix(dsiName string) string { // WaitForReadiness waits for the backup object status condition of type "Complete" to indicate // true. -func WaitForReadiness(ctx context.Context, backup *v1alpha1.Backup, timeoutMins time.Duration, - c runtimeClient.Client) { - +func WaitForReadiness(ctx context.Context, backup *v1beta3.Backup, timeoutMins time.Duration, + c runtimeClient.Client, +) { var err error EventuallyWithOffset(1, func() bool { backupCreated := New() @@ -97,7 +97,7 @@ func WaitForReadiness(ctx context.Context, backup *v1alpha1.Backup, timeoutMins } // WaitForDeletion waits for the backup object to be deleted from the API server. -func WaitForDeletion(ctx context.Context, backup *v1alpha1.Backup, c runtimeClient.Client) { +func WaitForDeletion(ctx context.Context, backup *v1beta3.Backup, c runtimeClient.Client) { var err error EventuallyWithOffset(1, func() bool { b := New() diff --git a/test/framework/backup/s3.go b/test/framework/backup/s3.go index 19ba4d87..342a25aa 100644 --- a/test/framework/backup/s3.go +++ b/test/framework/backup/s3.go @@ -12,7 +12,7 @@ import ( "k8s.io/apimachinery/pkg/types" "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/anynines/a8s-backup-manager/api/v1alpha1" + "github.com/anynines/a8s-backup-manager/api/v1beta3" ) const ( @@ -40,7 +40,7 @@ type S3Client struct { bucketName string } -func (c S3Client) HasPartialBackupData(ctx context.Context, bkp v1alpha1.Backup) (bool, error) { +func (c S3Client) HasPartialBackupData(ctx context.Context, bkp v1beta3.Backup) (bool, error) { for object := range c.client.ListObjects(ctx, c.bucketName, minio.ListObjectsOptions{}) { diff --git a/test/framework/chaos/networkchaos/networkchaos.go b/test/framework/chaos/networkchaos/networkchaos.go index 520d3070..9b8692e0 100644 --- a/test/framework/chaos/networkchaos/networkchaos.go +++ b/test/framework/chaos/networkchaos/networkchaos.go @@ -12,8 +12,10 @@ import ( runtimeClient "sigs.k8s.io/controller-runtime/pkg/client" ) -type NetworkChaos chmv1alpha1.NetworkChaos -type PodSelector = chmv1alpha1.PodSelector +type ( + NetworkChaos chmv1alpha1.NetworkChaos + PodSelector = chmv1alpha1.PodSelector +) // NetworkChaos Actions const ( @@ -119,8 +121,8 @@ func (nc NetworkChaos) KubernetesObject() client.Object { // NewPodLabelSelector returns a new PodSelector configured using labels and provided options. func NewPodLabelSelector(labels map[string]string, - opts ...func(*PodSelector)) *PodSelector { - + opts ...func(*PodSelector), +) *PodSelector { podSelector := &chmv1alpha1.PodSelector{ Selector: chmv1alpha1.PodSelectorSpec{ GenericSelectorSpec: chmv1alpha1.GenericSelectorSpec{ diff --git a/test/framework/chaos/podchaos/podchaos.go b/test/framework/chaos/podchaos/podchaos.go index 26f95c51..b219617c 100644 --- a/test/framework/chaos/podchaos/podchaos.go +++ b/test/framework/chaos/podchaos/podchaos.go @@ -12,8 +12,10 @@ import ( runtimeClient "sigs.k8s.io/controller-runtime/pkg/client" ) -type PodChaos chmv1alpha1.PodChaos -type PodSelector = chmv1alpha1.PodSelector +type ( + PodChaos chmv1alpha1.PodChaos + PodSelector = chmv1alpha1.PodSelector +) const ( CRDName string = "podchaos.chaos-mesh.org" @@ -99,8 +101,8 @@ func (pc PodChaos) KubernetesObject() client.Object { // NewPodLabelSelector returns a new PodSelector configured using labels and provided options. func NewPodLabelSelector(labels map[string]string, - opts ...func(*PodSelector)) *PodSelector { - + opts ...func(*PodSelector), +) *PodSelector { podSelector := &chmv1alpha1.PodSelector{ Selector: chmv1alpha1.PodSelectorSpec{ GenericSelectorSpec: chmv1alpha1.GenericSelectorSpec{ diff --git a/test/framework/dsi/connection_info.go b/test/framework/dsi/connection_info.go new file mode 100644 index 00000000..3301e976 --- /dev/null +++ b/test/framework/dsi/connection_info.go @@ -0,0 +1,5 @@ +package dsi + +func ConnectionInfoName(dsiName string) string { + return dsiName + "-connection" +} diff --git a/test/framework/dsi/dsiclient.go b/test/framework/dsi/dsiclient.go index ef9cf9ec..3371bbdb 100644 --- a/test/framework/dsi/dsiclient.go +++ b/test/framework/dsi/dsiclient.go @@ -45,7 +45,19 @@ type DSIConfigurationValidator interface { func NewClient(ds, port string, sbData map[string]string) (DSIClient, error) { switch strings.ToLower(ds) { case "postgresql": - return postgresql.NewClient(sbData, port), nil + return postgresql.NewClientOverPortForwarding(sbData, port), nil + } + return nil, fmt.Errorf( + "dsi client factory received request to create dsi client for unknown data service %s; only supported data services are %s", + ds, + supportedDataServices(), + ) +} + +func NewClientForURL(ds, host, port, sslmode string, sbData map[string]string) (DSIClient, error) { + switch strings.ToLower(ds) { + case "postgresql": + return postgresql.NewClient(sbData, host, port, sslmode), nil } return nil, fmt.Errorf( "dsi client factory received request to create dsi client for unknown data service %s; only supported data services are %s", diff --git a/test/framework/postgresql/dsiclient.go b/test/framework/postgresql/dsiclient.go index ec6b72c1..db8f5a9f 100644 --- a/test/framework/postgresql/dsiclient.go +++ b/test/framework/postgresql/dsiclient.go @@ -13,21 +13,27 @@ import ( const ( protocol = "postgres" - hostname = "localhost" + localhost = "localhost" - DbAdminUsernameKey = "username" - DbAdminPasswordKey = "password" - DatabaseKey = "database" + DBKey = "database" + DBUsernameKey = "username" + DBPasswordKey = "password" // TODO: Make configurable at runtime when the need arises for tests with TLS. Warning: // enabling SSLMODE breaks port forwarding. - sslmode = "disable" + sslmodeDisable = "disable" ) -func NewClient(credentials map[string]string, port string) Client { +func NewClientOverPortForwarding(credentials map[string]string, port string) Client { + return NewClient(credentials, localhost, port, sslmodeDisable) +} + +func NewClient(credentials map[string]string, host string, port string, sslmode string) Client { return Client{ credentials: credentials, port: port, + hostname: host, + sslmode: sslmode, } } @@ -37,12 +43,14 @@ func NewClient(credentials map[string]string, port string) Client { type Client struct { // credentials is servicebinding data needed for connecting to a DSI credentials map[string]string - // port is a dynmically determined port on localhost that has been portforwarded for opening connection to the DSI - port string + // port is the destination port number the client communicates to + port string + hostname string + sslmode string } func (c Client) Write(ctx context.Context, tableName, data string) error { - dbConn, err := connectToDB(ctx, c.credentials, c.port) + dbConn, err := c.connectToDB(ctx) if err != nil { return err } @@ -59,7 +67,7 @@ func (c Client) Write(ctx context.Context, tableName, data string) error { } func (c Client) Read(ctx context.Context, tableName string) (string, error) { - dbConn, err := connectToDB(ctx, c.credentials, c.port) + dbConn, err := c.connectToDB(ctx) if err != nil { return "", err } @@ -85,7 +93,7 @@ func (c Client) Read(ctx context.Context, tableName string) (string, error) { } func (c Client) UserExists(ctx context.Context, username string) (bool, error) { - dbConn, err := connectToDB(ctx, c.credentials, c.port) + dbConn, err := c.connectToDB(ctx) if err != nil { return false, fmt.Errorf("failed to connect to the database: %w", err) } @@ -101,7 +109,7 @@ func (c Client) UserExists(ctx context.Context, username string) (bool, error) { } func (c Client) CollectionExists(ctx context.Context, collection string) bool { - dbConn, err := connectToDB(ctx, c.credentials, c.port) + dbConn, err := c.connectToDB(ctx) if err != nil { return false } @@ -117,7 +125,7 @@ func (c Client) CollectionExists(ctx context.Context, collection string) bool { } func (c Client) CheckParameter(ctx context.Context, parameter, expectedValue string) error { - dbConn, err := connectToDB(ctx, c.credentials, c.port) + dbConn, err := c.connectToDB(ctx) if err != nil { return err } @@ -167,11 +175,8 @@ func createTableIfNotExists(ctx context.Context, dbConn *pgx.Conn, tableName str return nil } -func connectToDB(ctx context.Context, - credentials map[string]string, - port string) (*pgx.Conn, error) { - - dbURL := dbURL(credentials, port) +func (c Client) connectToDB(ctx context.Context) (*pgx.Conn, error) { + dbURL := c.dbURL() conn, err := pgx.Connect(ctx, dbURL) if err != nil { return nil, fmt.Errorf("failed to connect to database with %s: %w", dbURL, err) @@ -199,10 +204,10 @@ func endTransaction(ctx context.Context, tx pgx.Tx, err error) error { return err } -func dbURL(credentials map[string]string, port string) string { - user := credentials[DbAdminUsernameKey] - password := credentials[DbAdminPasswordKey] - database := credentials[DatabaseKey] +func (c Client) dbURL() string { + user := c.credentials[DBUsernameKey] + password := c.credentials[DBPasswordKey] + database := c.credentials[DBKey] // In recent versions of client-go the functionality of port forwards was "fixed" to // close connections and stop listening when port forwarding errors occur so that kubectl // can exit. https://github.com/kubernetes/kubernetes/pull/103526 @@ -216,9 +221,12 @@ func dbURL(credentials map[string]string, port string) string { // So for now we disable SSLMODE for our PostgreSQL client which fixes the port forward // closing. // + // SSL can be enabled by overwriting c.sslmode for testing exposed instances where a port + // forward is not necessary + // // TODO: We may want to perform tests involving SSL in future. Find alternative approach so // that we can have SSLMODE enabled and reliable port forwards. return strings.Join([]string{ - protocol, "://", user, ":", password, "@", hostname, ":", port, "/", database, "?", "sslmode=", sslmode}, - "") + protocol, "://", user, ":", password, "@", c.hostname, ":", c.port, "/", database, "?", "sslmode=", c.sslmode}, + "") } diff --git a/test/framework/postgresql/postgresql.go b/test/framework/postgresql/postgresql.go index af72315e..112d7d07 100644 --- a/test/framework/postgresql/postgresql.go +++ b/test/framework/postgresql/postgresql.go @@ -15,9 +15,9 @@ import ( "k8s.io/utils/pointer" runtimeClient "sigs.k8s.io/controller-runtime/pkg/client" - backupv1alpha1 "github.com/anynines/a8s-backup-manager/api/v1alpha1" - sbv1alpha1 "github.com/anynines/a8s-service-binding-controller/api/v1alpha1" - pgv1alpha1 "github.com/anynines/postgresql-operator/api/v1alpha1" + backupv1beta3 "github.com/anynines/a8s-backup-manager/api/v1beta3" + sbv1beta3 "github.com/anynines/a8s-service-binding-controller/api/v1beta3" + pgv1beta3 "github.com/anynines/postgresql-operator/api/v1beta3" ) const ( @@ -30,7 +30,7 @@ const ( ) type Postgresql struct { - *pgv1alpha1.Postgresql + *pgv1beta3.Postgresql } func (pg Postgresql) ClusterStatus() string { @@ -39,8 +39,8 @@ func (pg Postgresql) ClusterStatus() string { // TODO: make the K8s client a field of pg rather than something to pass to its functions. func (pg Postgresql) StatefulSet(ctx context.Context, - k8sClient runtimeClient.Client) (*appsv1.StatefulSet, error) { - + k8sClient runtimeClient.Client, +) (*appsv1.StatefulSet, error) { nsn := types.NamespacedName{Namespace: pg.Namespace, Name: pg.Name} ss := &appsv1.StatefulSet{} @@ -56,12 +56,12 @@ func (pg Postgresql) StatefulSet(ctx context.Context, // older DSI with same namespace, name and kind. // TODO: Improve robustness by checking that the pods actually belong to DSI. func (pg Postgresql) Pods(ctx context.Context, - k8sClient runtimeClient.Client) ([]corev1.Pod, error) { - + k8sClient runtimeClient.Client, +) ([]corev1.Pod, error) { podsLabels := labels.Set{ - pgv1alpha1.DSINameLabelKey: pg.Name, - pgv1alpha1.DSIGroupLabelKey: "postgresql.anynines.com", - pgv1alpha1.DSIKindLabelKey: "Postgresql", + pgv1beta3.DSINameLabelKey: pg.Name, + pgv1beta3.DSIGroupLabelKey: "postgresql.anynines.com", + pgv1beta3.DSIKindLabelKey: "Postgresql", } podsSelector, err := podsLabels.AsValidatedSelector() if err != nil { @@ -84,7 +84,7 @@ func (pg Postgresql) Pods(ctx context.Context, func (pg Postgresql) SetTolerations(ts ...corev1.Toleration) { if pg.Postgresql.Spec.SchedulingConstraints == nil { - pg.Postgresql.Spec.SchedulingConstraints = &pgv1alpha1.PostgresqlSchedulingConstraints{} + pg.Postgresql.Spec.SchedulingConstraints = &pgv1beta3.PostgresqlSchedulingConstraints{} } pg.Postgresql.Spec.SchedulingConstraints.Tolerations = ts } @@ -92,23 +92,21 @@ func (pg Postgresql) SetTolerations(ts ...corev1.Toleration) { func (pg Postgresql) AddRequiredPodAntiAffinityTerm(at corev1.PodAffinityTerm) { pg.initPodAntiAffinity() paa := pg.Postgresql.Spec.SchedulingConstraints.Affinity.PodAntiAffinity - paa.RequiredDuringSchedulingIgnoredDuringExecution = - append(paa.RequiredDuringSchedulingIgnoredDuringExecution, at) + paa.RequiredDuringSchedulingIgnoredDuringExecution = append(paa.RequiredDuringSchedulingIgnoredDuringExecution, at) } func (pg Postgresql) AddPreferredPodAntiAffinityTerm(weight int, at corev1.PodAffinityTerm) { pg.initPodAntiAffinity() paa := pg.Postgresql.Spec.SchedulingConstraints.Affinity.PodAntiAffinity - paa.PreferredDuringSchedulingIgnoredDuringExecution = - append(paa.PreferredDuringSchedulingIgnoredDuringExecution, corev1.WeightedPodAffinityTerm{ - Weight: int32(weight), - PodAffinityTerm: at, - }) + paa.PreferredDuringSchedulingIgnoredDuringExecution = append(paa.PreferredDuringSchedulingIgnoredDuringExecution, corev1.WeightedPodAffinityTerm{ + Weight: int32(weight), + PodAffinityTerm: at, + }) } func (pg Postgresql) initPodAntiAffinity() { if pg.Postgresql.Spec.SchedulingConstraints == nil { - pg.Postgresql.Spec.SchedulingConstraints = &pgv1alpha1.PostgresqlSchedulingConstraints{} + pg.Postgresql.Spec.SchedulingConstraints = &pgv1beta3.PostgresqlSchedulingConstraints{} } if pg.Postgresql.Spec.SchedulingConstraints.Affinity == nil { pg.Postgresql.Spec.SchedulingConstraints.Affinity = &corev1.Affinity{} @@ -123,28 +121,28 @@ func (pg Postgresql) initPodAntiAffinity() { // type. We also need to set the APIVersion and Kind since Kubernetes will remove these fields when // marshalling API objects. func (pg Postgresql) GetClientObject() runtimeClient.Object { - pg.Postgresql.APIVersion = pgv1alpha1.GroupVersion.String() + pg.Postgresql.APIVersion = pgv1beta3.GroupVersion.String() pg.Postgresql.Kind = kind return pg.Postgresql } func (pg Postgresql) GetReplicaLabels() map[string]string { return map[string]string{ - pgv1alpha1.DSINameLabelKey: pg.GetName(), - pgv1alpha1.ReplicationRoleLabelKey: "replica", + pgv1beta3.DSINameLabelKey: pg.GetName(), + pgv1beta3.ReplicationRoleLabelKey: "replica", } } func (pg Postgresql) GetMasterLabels() map[string]string { return map[string]string{ - pgv1alpha1.DSINameLabelKey: pg.GetName(), - pgv1alpha1.ReplicationRoleLabelKey: "master", + pgv1beta3.DSINameLabelKey: pg.GetName(), + pgv1beta3.ReplicationRoleLabelKey: "master", } } func (pg Postgresql) CheckPatroniLabelsAssigned(ctx context.Context, - c runtimeClient.Client) (bool, error) { - + c runtimeClient.Client, +) (bool, error) { l, err := pg.Pods(ctx, c) if err != nil { return false, err @@ -169,9 +167,9 @@ func NewK8sClient(kubeconfig string) (runtimeClient.Client, error) { return nil, fmt.Errorf("unable to build config from kubeconfig path: %w", err) } - pgv1alpha1.AddToScheme(scheme.Scheme) - sbv1alpha1.AddToScheme(scheme.Scheme) - backupv1alpha1.AddToScheme(scheme.Scheme) + pgv1beta3.AddToScheme(scheme.Scheme) + sbv1beta3.AddToScheme(scheme.Scheme) + backupv1beta3.AddToScheme(scheme.Scheme) k8sClient, err := runtimeClient.New(cfg, runtimeClient.Options{Scheme: scheme.Scheme}) if err != nil { @@ -181,16 +179,16 @@ func NewK8sClient(kubeconfig string) (runtimeClient.Client, error) { } func New(namespace, name string, replicas int32, opts ...func(*Postgresql)) *Postgresql { - p := &Postgresql{&pgv1alpha1.Postgresql{ + p := &Postgresql{&pgv1beta3.Postgresql{ ObjectMeta: metav1.ObjectMeta{ Name: name, Namespace: namespace, }, TypeMeta: metav1.TypeMeta{ Kind: kind, - APIVersion: pgv1alpha1.GroupVersion.String(), + APIVersion: pgv1beta3.GroupVersion.String(), }, - Spec: pgv1alpha1.PostgresqlSpec{ + Spec: pgv1beta3.PostgresqlSpec{ Replicas: pointer.Int32Ptr(replicas), VolumeSize: k8sresource.MustParse(volumeSize), Version: version, @@ -221,10 +219,10 @@ func WithVolumeSize(s string) func(*Postgresql) { } func NewEmpty() Postgresql { - return Postgresql{&pgv1alpha1.Postgresql{ + return Postgresql{&pgv1beta3.Postgresql{ TypeMeta: metav1.TypeMeta{ Kind: kind, - APIVersion: pgv1alpha1.GroupVersion.String(), + APIVersion: pgv1beta3.GroupVersion.String(), }, }} } @@ -246,5 +244,5 @@ func PvcName(instanceName string, index int) string { } func IsMaster(pod *corev1.Pod) bool { - return pod.Labels[pgv1alpha1.ReplicationRoleLabelKey] == "master" + return pod.Labels[pgv1beta3.ReplicationRoleLabelKey] == "master" } diff --git a/test/framework/postgresql/postgresql_test.go b/test/framework/postgresql/postgresql_test.go index be3a532a..02e6046f 100644 --- a/test/framework/postgresql/postgresql_test.go +++ b/test/framework/postgresql/postgresql_test.go @@ -13,7 +13,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client/fake" "github.com/anynines/a8s-deployment/test/framework/postgresql" - "github.com/anynines/postgresql-operator/api/v1alpha1" + "github.com/anynines/postgresql-operator/api/v1beta3" ) // TODO: Test failure cases (e.g. the K8s API calls return an error). Not already done because the @@ -53,10 +53,10 @@ func TestPodsRetrievalHappyPaths(t *testing.T) { withName("p0"), withNamespace("ns1"), withLabels(map[string]string{ - v1alpha1.DSINameLabelKey: "pg0", - v1alpha1.DSIKindLabelKey: "Postgresql", - v1alpha1.DSIGroupLabelKey: "postgresql.anynines.com", - v1alpha1.ReplicationRoleLabelKey: "master", + v1beta3.DSINameLabelKey: "pg0", + v1beta3.DSIKindLabelKey: "Postgresql", + v1beta3.DSIGroupLabelKey: "postgresql.anynines.com", + v1beta3.ReplicationRoleLabelKey: "master", }), ), }, @@ -70,10 +70,10 @@ func TestPodsRetrievalHappyPaths(t *testing.T) { withName("p0"), withNamespace("ns0"), withLabels(map[string]string{ - v1alpha1.DSINameLabelKey: "pg1", - v1alpha1.DSIKindLabelKey: "Postgresql", - v1alpha1.DSIGroupLabelKey: "postgresql.anynines.com", - v1alpha1.ReplicationRoleLabelKey: "replica", + v1beta3.DSINameLabelKey: "pg1", + v1beta3.DSIKindLabelKey: "Postgresql", + v1beta3.DSIGroupLabelKey: "postgresql.anynines.com", + v1beta3.ReplicationRoleLabelKey: "replica", }), ), }, @@ -87,9 +87,9 @@ func TestPodsRetrievalHappyPaths(t *testing.T) { withName("p0"), withNamespace("ns0"), withLabels(map[string]string{ - v1alpha1.DSINameLabelKey: "r1", - v1alpha1.DSIKindLabelKey: "Redis", - v1alpha1.DSIGroupLabelKey: "redis.anynines.com", + v1beta3.DSINameLabelKey: "r1", + v1beta3.DSIKindLabelKey: "Redis", + v1beta3.DSIGroupLabelKey: "redis.anynines.com", }), ), }, @@ -102,30 +102,30 @@ func TestPodsRetrievalHappyPaths(t *testing.T) { withName("p0"), withNamespace("ns0"), withLabels(map[string]string{ - v1alpha1.DSINameLabelKey: "pg0", - v1alpha1.DSIKindLabelKey: "Postgresql", - v1alpha1.DSIGroupLabelKey: "postgresql.anynines.com", - v1alpha1.ReplicationRoleLabelKey: "master", + v1beta3.DSINameLabelKey: "pg0", + v1beta3.DSIKindLabelKey: "Postgresql", + v1beta3.DSIGroupLabelKey: "postgresql.anynines.com", + v1beta3.ReplicationRoleLabelKey: "master", }), ), newPod( withName("p1"), withNamespace("ns0"), withLabels(map[string]string{ - v1alpha1.DSINameLabelKey: "pg0", - v1alpha1.DSIKindLabelKey: "Postgresql", - v1alpha1.DSIGroupLabelKey: "postgresql.anynines.com", - v1alpha1.ReplicationRoleLabelKey: "replica", + v1beta3.DSINameLabelKey: "pg0", + v1beta3.DSIKindLabelKey: "Postgresql", + v1beta3.DSIGroupLabelKey: "postgresql.anynines.com", + v1beta3.ReplicationRoleLabelKey: "replica", }), ), newPod( withName("p2"), withNamespace("ns0"), withLabels(map[string]string{ - v1alpha1.DSINameLabelKey: "pg0", - v1alpha1.DSIKindLabelKey: "Postgresql", - v1alpha1.DSIGroupLabelKey: "postgresql.anynines.com", - v1alpha1.ReplicationRoleLabelKey: "replica", + v1beta3.DSINameLabelKey: "pg0", + v1beta3.DSIKindLabelKey: "Postgresql", + v1beta3.DSIGroupLabelKey: "postgresql.anynines.com", + v1beta3.ReplicationRoleLabelKey: "replica", }), ), }, @@ -139,20 +139,20 @@ func TestPodsRetrievalHappyPaths(t *testing.T) { withName("p0"), withNamespace("ns0"), withLabels(map[string]string{ - v1alpha1.DSINameLabelKey: "pg0", - v1alpha1.DSIKindLabelKey: "Postgresql", - v1alpha1.DSIGroupLabelKey: "postgresql.anynines.com", - v1alpha1.ReplicationRoleLabelKey: "master", + v1beta3.DSINameLabelKey: "pg0", + v1beta3.DSIKindLabelKey: "Postgresql", + v1beta3.DSIGroupLabelKey: "postgresql.anynines.com", + v1beta3.ReplicationRoleLabelKey: "master", }), ), newPod( withName("p1"), withNamespace("ns0"), withLabels(map[string]string{ - v1alpha1.DSINameLabelKey: "pg0", - v1alpha1.DSIKindLabelKey: "Postgresql", - v1alpha1.DSIGroupLabelKey: "postgresql.anynines.com", - v1alpha1.ReplicationRoleLabelKey: "replica", + v1beta3.DSINameLabelKey: "pg0", + v1beta3.DSIKindLabelKey: "Postgresql", + v1beta3.DSIGroupLabelKey: "postgresql.anynines.com", + v1beta3.ReplicationRoleLabelKey: "replica", }), ), }, @@ -166,10 +166,10 @@ func TestPodsRetrievalHappyPaths(t *testing.T) { withName("p0"), withNamespace("ns0"), withLabels(map[string]string{ - v1alpha1.DSINameLabelKey: "pg0", - v1alpha1.DSIKindLabelKey: "Postgresql", - v1alpha1.DSIGroupLabelKey: "postgresql.anynines.com", - v1alpha1.ReplicationRoleLabelKey: "master", + v1beta3.DSINameLabelKey: "pg0", + v1beta3.DSIKindLabelKey: "Postgresql", + v1beta3.DSIGroupLabelKey: "postgresql.anynines.com", + v1beta3.ReplicationRoleLabelKey: "master", }), ), }, @@ -189,10 +189,10 @@ func TestPodsRetrievalHappyPaths(t *testing.T) { withName("p0"), withNamespace("ns0"), withLabels(map[string]string{ - v1alpha1.DSINameLabelKey: "pg0", - v1alpha1.DSIKindLabelKey: "Postgresql", - v1alpha1.DSIGroupLabelKey: "postgresql.anynines.com", - v1alpha1.ReplicationRoleLabelKey: "master", + v1beta3.DSINameLabelKey: "pg0", + v1beta3.DSIKindLabelKey: "Postgresql", + v1beta3.DSIGroupLabelKey: "postgresql.anynines.com", + v1beta3.ReplicationRoleLabelKey: "master", }), ), }, @@ -201,9 +201,9 @@ func TestPodsRetrievalHappyPaths(t *testing.T) { withName("p1"), withNamespace("ns0"), withLabels(map[string]string{ - v1alpha1.DSINameLabelKey: "r1", - v1alpha1.DSIKindLabelKey: "Redis", - v1alpha1.DSIGroupLabelKey: "redis.anynines.com", + v1beta3.DSINameLabelKey: "r1", + v1beta3.DSIKindLabelKey: "Redis", + v1beta3.DSIGroupLabelKey: "redis.anynines.com", }), ), }, @@ -216,10 +216,10 @@ func TestPodsRetrievalHappyPaths(t *testing.T) { withName("p0"), withNamespace("ns0"), withLabels(map[string]string{ - v1alpha1.DSINameLabelKey: "pg0", - v1alpha1.DSIKindLabelKey: "Postgresql", - v1alpha1.DSIGroupLabelKey: "postgresql.anynines.com", - v1alpha1.ReplicationRoleLabelKey: "master", + v1beta3.DSINameLabelKey: "pg0", + v1beta3.DSIKindLabelKey: "Postgresql", + v1beta3.DSIGroupLabelKey: "postgresql.anynines.com", + v1beta3.ReplicationRoleLabelKey: "master", }), ), }, @@ -228,10 +228,10 @@ func TestPodsRetrievalHappyPaths(t *testing.T) { withName("p1"), withNamespace("ns0"), withLabels(map[string]string{ - v1alpha1.DSINameLabelKey: "pg1", - v1alpha1.DSIKindLabelKey: "Postgresql", - v1alpha1.DSIGroupLabelKey: "postgresql.anynines.com", - v1alpha1.ReplicationRoleLabelKey: "master", + v1beta3.DSINameLabelKey: "pg1", + v1beta3.DSIKindLabelKey: "Postgresql", + v1beta3.DSIGroupLabelKey: "postgresql.anynines.com", + v1beta3.ReplicationRoleLabelKey: "master", }), ), }, @@ -254,7 +254,6 @@ func TestPodsRetrievalHappyPaths(t *testing.T) { // Invoke the method under test gotPods, err := tc.dsi.Pods(context.Background(), k8sClient) - if err != nil { t.Fatalf("Expected no error when listing DSI Pods, got: \"%v\"", err) } diff --git a/test/framework/restore/restore.go b/test/framework/restore/restore.go index ee138c56..f2e36210 100644 --- a/test/framework/restore/restore.go +++ b/test/framework/restore/restore.go @@ -10,7 +10,7 @@ import ( "k8s.io/apimachinery/pkg/types" runtimeClient "sigs.k8s.io/controller-runtime/pkg/client" - "github.com/anynines/a8s-backup-manager/api/v1alpha1" + "github.com/anynines/a8s-backup-manager/api/v1beta3" "github.com/anynines/a8s-deployment/test/framework" ) @@ -25,10 +25,10 @@ const ( // Option represents a functional option for restore objects. To learn what a functional option is, // read here: https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis -type Option func(*v1alpha1.Restore) +type Option func(*v1beta3.Restore) func SetInstanceRef(dsi runtimeClient.Object) Option { - return func(rst *v1alpha1.Restore) { + return func(rst *v1beta3.Restore) { rst.Spec.ServiceInstance.APIGroup = dsi.GetObjectKind().GroupVersionKind().Group rst.Spec.ServiceInstance.Kind = dsi.GetObjectKind().GroupVersionKind().Kind rst.Spec.ServiceInstance.Name = dsi.GetName() @@ -38,20 +38,20 @@ func SetInstanceRef(dsi runtimeClient.Object) Option { // TODO: Make two separate options for name and namespace. We only need to pass string as // parameters func SetNamespacedName(dsi runtimeClient.Object) Option { - return func(rst *v1alpha1.Restore) { + return func(rst *v1beta3.Restore) { rst.Name = framework.UniqueName(restorePrefix(dsi.GetName()), suffixLength) rst.Namespace = dsi.GetNamespace() } } func SetBackupName(backupName string) Option { - return func(rst *v1alpha1.Restore) { + return func(rst *v1beta3.Restore) { rst.Spec.BackupName = backupName } } -func New(opts ...Option) *v1alpha1.Restore { - rst := &v1alpha1.Restore{} +func New(opts ...Option) *v1beta3.Restore { + rst := &v1beta3.Restore{} for _, opt := range opts { opt(rst) } @@ -62,7 +62,7 @@ func restorePrefix(dsiName string) string { return fmt.Sprintf("%s-restore", dsiName) } -func WaitForReadiness(ctx context.Context, restore *v1alpha1.Restore, c runtimeClient.Client) { +func WaitForReadiness(ctx context.Context, restore *v1beta3.Restore, c runtimeClient.Client) { var err error EventuallyWithOffset(1, func() bool { restoreCreated := New() diff --git a/test/framework/servicebinding/servicebinding.go b/test/framework/servicebinding/servicebinding.go index a9c520e0..bffc2d8b 100644 --- a/test/framework/servicebinding/servicebinding.go +++ b/test/framework/servicebinding/servicebinding.go @@ -10,7 +10,7 @@ import ( runtimeclient "sigs.k8s.io/controller-runtime/pkg/client" "github.com/anynines/a8s-deployment/test/framework" - "github.com/anynines/a8s-service-binding-controller/api/v1alpha1" + "github.com/anynines/a8s-service-binding-controller/api/v1beta3" ) const ( @@ -20,14 +20,14 @@ const ( // Option represents a functional option for service binding objects. To learn what a functional // option is, read here: https://dave.cheney.net/2014/10/17/functional-options-for-friendly-apis -type Option func(*v1alpha1.ServiceBinding) +type Option func(*v1beta3.ServiceBinding) func SetInstanceRef(dsi runtimeclient.Object) Option { - return func(sb *v1alpha1.ServiceBinding) { + return func(sb *v1beta3.ServiceBinding) { sb.Spec.Instance.APIVersion = dsi. GetObjectKind().GroupVersionKind().GroupVersion().String() sb.Spec.Instance.Kind = dsi.GetObjectKind().GroupVersionKind().Kind - sb.Spec.Instance.NamespacedName = v1alpha1.NamespacedName{ + sb.Spec.Instance.NamespacedName = v1beta3.NamespacedName{ Name: dsi.GetName(), Namespace: dsi.GetNamespace(), } @@ -37,7 +37,7 @@ func SetInstanceRef(dsi runtimeclient.Object) Option { // TODO: Make two separate options for name and namespace. We only need to pass string as // parameters func SetNamespacedName(dsi runtimeclient.Object) Option { - return func(sb *v1alpha1.ServiceBinding) { + return func(sb *v1beta3.ServiceBinding) { sb.Name = framework.UniqueName(sbPrefix(dsi.GetName()), suffixLength) sb.Namespace = dsi.GetNamespace() } @@ -47,8 +47,8 @@ func sbPrefix(dsiName string) string { return fmt.Sprintf("%s-sb", dsiName) } -func New(opts ...Option) *v1alpha1.ServiceBinding { - sb := &v1alpha1.ServiceBinding{} +func New(opts ...Option) *v1beta3.ServiceBinding { + sb := &v1beta3.ServiceBinding{} for _, opt := range opts { opt(sb) } @@ -59,7 +59,7 @@ func SecretName(sbName string) string { return fmt.Sprintf("%s-%s", sbName, "service-binding") } -func WaitForReadiness(ctx context.Context, sb *v1alpha1.ServiceBinding, c runtimeclient.Client) { +func WaitForReadiness(ctx context.Context, sb *v1beta3.ServiceBinding, c runtimeclient.Client) { var err error EventuallyWithOffset(1, func() bool { sbCreated := New() diff --git a/test/go.mod b/test/go.mod index 1ea6210f..599c5f7d 100644 --- a/test/go.mod +++ b/test/go.mod @@ -3,111 +3,110 @@ module github.com/anynines/a8s-deployment/test go 1.19 require ( - github.com/anynines/a8s-backup-manager v0.34.0 - github.com/anynines/a8s-service-binding-controller v0.29.0 - github.com/anynines/postgresql-operator v0.46.0 - github.com/go-logr/logr v1.2.3 + github.com/anynines/a8s-backup-manager v0.39.1-0.20230423231133-2616f22c4fe6 + github.com/anynines/a8s-service-binding-controller v0.37.1-0.20230423204020-a2903616cb2f + github.com/anynines/postgresql-operator v0.86.1-0.20230421115007-c01af5af858b + github.com/chaos-mesh/chaos-mesh/api v0.0.0-20230209235359-64dc83baed9b + github.com/go-logr/logr v1.2.4 github.com/go-logr/zapr v1.2.3 - github.com/jackc/pgx/v4 v4.17.2 - github.com/minio/minio-go/v7 v7.0.20 - github.com/onsi/ginkgo/v2 v2.1.6 - github.com/onsi/gomega v1.20.2 - go.uber.org/zap v1.23.0 - k8s.io/api v0.25.0 - k8s.io/apimachinery v0.25.0 + github.com/jackc/pgx/v4 v4.18.1 + github.com/minio/minio-go/v7 v7.0.50 + github.com/onsi/ginkgo/v2 v2.8.0 + github.com/onsi/gomega v1.26.0 + go.uber.org/zap v1.24.0 + k8s.io/api v0.26.3 + k8s.io/apimachinery v0.26.3 k8s.io/cli-runtime v0.25.0 - k8s.io/client-go v0.25.0 - k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73 - sigs.k8s.io/controller-runtime v0.13.0 + k8s.io/client-go v0.26.3 + k8s.io/utils v0.0.0-20230313181309-38a27ef9d749 + sigs.k8s.io/controller-runtime v0.14.6 ) require ( github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect github.com/docker/go-units v0.5.0 // indirect - github.com/dustin/go-humanize v1.0.0 // indirect - github.com/emicklei/go-restful/v3 v3.9.0 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/emicklei/go-restful/v3 v3.10.2 // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/google/gnostic v0.6.9 // indirect - github.com/klauspost/compress v1.13.6 // indirect - github.com/klauspost/cpuid/v2 v2.0.9 // indirect + github.com/klauspost/compress v1.16.4 // indirect + github.com/klauspost/cpuid/v2 v2.2.4 // indirect github.com/minio/md5-simd v1.1.2 // indirect github.com/minio/sha256-simd v1.0.0 // indirect - github.com/mitchellh/go-homedir v1.1.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/robfig/cron/v3 v3.0.1 // indirect - github.com/rs/xid v1.3.0 // indirect - github.com/sirupsen/logrus v1.8.1 // indirect - gopkg.in/ini.v1 v1.66.2 // indirect + github.com/rs/xid v1.4.0 // indirect + github.com/sirupsen/logrus v1.9.0 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect ) require ( github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect - github.com/chaos-mesh/chaos-mesh/api/v1alpha1 v0.0.0-20220226050744-799408773657 + github.com/cespare/xxhash/v2 v2.2.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/evanphx/json-patch v5.6.0+incompatible // indirect - github.com/fsnotify/fsnotify v1.5.4 // indirect + github.com/fsnotify/fsnotify v1.6.0 // indirect github.com/go-errors/errors v1.4.2 // indirect - github.com/go-openapi/jsonpointer v0.19.5 // indirect - github.com/go-openapi/jsonreference v0.20.0 // indirect + github.com/go-openapi/jsonpointer v0.19.6 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.22.3 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.2 // indirect + github.com/golang/protobuf v1.5.3 // indirect github.com/google/btree v1.1.2 // indirect - github.com/google/go-cmp v0.5.8 // indirect + github.com/google/go-cmp v0.5.9 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.3.0 // indirect github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect - github.com/imdario/mergo v0.3.13 // indirect + github.com/imdario/mergo v0.3.15 // indirect github.com/inconshreveable/mousetrap v1.0.1 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect - github.com/jackc/pgconn v1.13.0 // indirect + github.com/jackc/pgconn v1.14.0 // indirect github.com/jackc/pgio v1.0.0 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect - github.com/jackc/pgproto3/v2 v2.3.1 // indirect - github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect - github.com/jackc/pgtype v1.12.0 // indirect + github.com/jackc/pgproto3/v2 v2.3.2 // indirect + github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect + github.com/jackc/pgtype v1.14.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect github.com/mailru/easyjson v0.7.7 // indirect - github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect github.com/moby/spdystream v0.2.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 // indirect github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect - github.com/prometheus/client_golang v1.13.0 // indirect - github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.37.0 // indirect - github.com/prometheus/procfs v0.8.0 // indirect - github.com/spf13/cobra v1.5.0 // indirect + github.com/prometheus/client_golang v1.14.0 // indirect + github.com/prometheus/client_model v0.3.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.9.0 // indirect + github.com/spf13/cobra v1.6.0 // indirect github.com/spf13/pflag v1.0.5 // indirect github.com/xlab/treeprint v1.1.0 // indirect go.starlark.net v0.0.0-20220817180228-f738f5508c12 // indirect go.uber.org/atomic v1.10.0 // indirect - go.uber.org/multierr v1.8.0 // indirect - golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 // indirect - golang.org/x/net v0.0.0-20220909164309-bea034e7d591 // indirect - golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 // indirect - golang.org/x/sys v0.0.0-20220907062415-87db552b00fd // indirect - golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 // indirect - golang.org/x/text v0.3.7 // indirect - golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/crypto v0.7.0 // indirect + golang.org/x/net v0.8.0 // indirect + golang.org/x/oauth2 v0.6.0 // indirect + golang.org/x/sys v0.7.0 // indirect + golang.org/x/term v0.7.0 // indirect + golang.org/x/text v0.9.0 // indirect + golang.org/x/time v0.3.0 // indirect gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.28.1 // indirect + google.golang.org/protobuf v1.30.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 - k8s.io/apiextensions-apiserver v0.25.0 - k8s.io/component-base v0.25.0 // indirect - k8s.io/klog/v2 v2.80.0 // indirect - k8s.io/kube-openapi v0.0.0-20220803164354-a70c9af30aea // indirect - sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect + k8s.io/apiextensions-apiserver v0.26.3 + k8s.io/component-base v0.26.3 // indirect + k8s.io/klog/v2 v2.90.1 // indirect + k8s.io/kube-openapi v0.0.0-20230327201221-f5883ff37f0c // indirect + sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/kustomize/api v0.12.1 // indirect sigs.k8s.io/kustomize/kyaml v0.13.9 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect diff --git a/test/go.sum b/test/go.sum index 841ab71e..1bc89069 100644 --- a/test/go.sum +++ b/test/go.sum @@ -1,70 +1,30 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= -cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU= -cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY= -cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc= -cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0= -cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= -cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= -cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= -cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc= -cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk= -cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs= -cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc= -cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= -cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= -cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= -cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= -cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= -cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= -cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= -cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= -cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= -cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= -cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= -cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= -cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU= -cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw= -cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos= -cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= -cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= -cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= -github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= -github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= -github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= -github.com/anynines/a8s-backup-manager v0.34.0 h1:VBfA7kVjkzo26+zrYC3t4dTtTPkiXDLHfvFzGTcMAsY= -github.com/anynines/a8s-backup-manager v0.34.0/go.mod h1:yayIzv5u31WcEGsa+vQExuPjAxZzdirZ5gIrqoxftIA= -github.com/anynines/a8s-service-binding-controller v0.29.0 h1:rUWrlsW29e7Gw/BSQRjg2opf5QuiG1DO7YCyH7f/Kbg= -github.com/anynines/a8s-service-binding-controller v0.29.0/go.mod h1:OYImFkivowoGccggaA72iuDuS6GOghqvVrtfTEIk8F0= -github.com/anynines/postgresql-operator v0.46.0 h1:W8XPSqOlk0QrcWoFUbZvUeREQSZ3dYI9VYk9ZFVb+nY= -github.com/anynines/postgresql-operator v0.46.0/go.mod h1:bjR1nqZ4s1CCEUqmz4O6KKFPfpA3KoEDxz6kXF6o594= +github.com/anynines/a8s-backup-manager v0.39.1-0.20230423231133-2616f22c4fe6 h1:J4COPXhMLcg8v9jVzbRWKwrCHw61cjWqf5gVUYOPWW4= +github.com/anynines/a8s-backup-manager v0.39.1-0.20230423231133-2616f22c4fe6/go.mod h1:MRhnkudjwLIDfbwFOdBCUpAwOZjrj4hs5CrVmsH8Wo0= +github.com/anynines/a8s-service-binding-controller v0.37.1-0.20230423204020-a2903616cb2f h1:Txme8Wm41MtQ3BplAiifW/ids95mMSpTnou/G+mTREY= +github.com/anynines/a8s-service-binding-controller v0.37.1-0.20230423204020-a2903616cb2f/go.mod h1:cJO23NMhBr+Qz6EkgFsQi4pStCdhh3vnEsdqY+ezLvo= +github.com/anynines/postgresql-operator v0.86.1-0.20230421115007-c01af5af858b h1:JUqVx8xZnI/kvHDgikAsL0OtHahdvyPmFijwfn/NrHI= +github.com/anynines/postgresql-operator v0.86.1-0.20230421115007-c01af5af858b/go.mod h1:5ybUenv1qWBvtsPvx+oOTIFTwYCIHqcpgRKcO2G4wuo= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= -github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= -github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= -github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= 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/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0= github.com/bxcodec/faker v2.0.1+incompatible h1:P0KUpUw5w6WJXwrPfv35oc91i4d8nf40Nwln+M/+faA= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= -github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/chaos-mesh/chaos-mesh/api/v1alpha1 v0.0.0-20220226050744-799408773657 h1:CyuI+igIjadM/GRnE2o0q+WCwipDh0n2cUYFPAvxziM= -github.com/chaos-mesh/chaos-mesh/api/v1alpha1 v0.0.0-20220226050744-799408773657/go.mod h1:JRiumF+RFsH1mrrP8FUsi9tExPylKkO/oSRWeQEUdLE= +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/chaos-mesh/chaos-mesh/api v0.0.0-20230209235359-64dc83baed9b h1:am6IJSVZb/JIMffv4bOJ+BwPbrnwUovCTVP/gY38F1Q= +github.com/chaos-mesh/chaos-mesh/api v0.0.0-20230209235359-64dc83baed9b/go.mod h1:tMUOEjRuThuozqrIDKhyViTZXaEZhIVV18/cSiGC3NQ= 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= @@ -78,17 +38,18 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7 github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +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/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= -github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= -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/emicklei/go-restful/v3 v3.10.2 h1:hIovbnmBTLjHXkqEBUz3HGpXZdM7ZrE9fJIZIqlJLqE= +github.com/emicklei/go-restful/v3 v3.10.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -101,61 +62,38 @@ github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLi 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/flowstack/go-jsonschema v0.1.1/go.mod h1:yL7fNggx1o8rm9RlgXv7hTBWxdBM0rVwpMwimd3F3N0= -github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= -github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4HY= +github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= -github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= -github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= -github.com/go-kit/log v0.2.0/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= -github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= -github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= -github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= -github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= -github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0= -github.com/go-logr/logr v1.2.3/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.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= -github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= -github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= -github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= -github.com/go-openapi/jsonreference v0.20.0/go.mod h1:Ag74Ico3lPc+zR+qjn4XBUmXymS4zJbYVCZmcgkasdo= -github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/jsonpointer v0.19.6 h1:eCs3fxoIi3Wh6vtgmLTOjdhSpiqphQ+DaPn38N2ZdrE= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= github.com/go-openapi/swag v0.22.3 h1:yMBqmnQ0gyZvEb/+KzuWZOXgllrXT4SADYbvDaXHv/g= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= -github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= 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-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= -github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 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/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= -github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= -github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= 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.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= 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= @@ -166,10 +104,9 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= -github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= -github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= +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/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU= github.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/gnostic v0.6.9 h1:ZK/5VhkoX835RikCHpSUJV9a+S3e1zLh59YnyWeBW+0= @@ -178,43 +115,26 @@ github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5a 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.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.4/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.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= -github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= -github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= -github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= -github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= -github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 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/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= -github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= -github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= -github.com/imdario/mergo v0.3.13/go.mod h1:4lJ1jqUDcsbIECGy0RUJAXNIhg+6ocWgb1ALK2O4oXg= -github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/imdario/mergo v0.3.15 h1:M8XP7IuFNsqUx6VPK2P9OSmsYsI/YFaGil0uD21V3dM= +github.com/imdario/mergo v0.3.15/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.0.1 h1:U3uMjPSQEBMNp1lFxmllqCPM6P5u/Xq7Pgzkat/bFNc= github.com/inconshreveable/mousetrap v1.0.1/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= @@ -227,8 +147,8 @@ github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsU github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= -github.com/jackc/pgconn v1.13.0 h1:3L1XMNV2Zvca/8BYhzcRFS70Lr0WlDg16Di6SFGAbys= -github.com/jackc/pgconn v1.13.0/go.mod h1:AnowpAqO4CMIIJNZl2VJp+KrkAZciAkhEl0W0JIobpI= +github.com/jackc/pgconn v1.14.0 h1:vrbA9Ud87g6JdFWkHTJXppVce58qPIdP7N8y0Ml/A7Q= +github.com/jackc/pgconn v1.14.0/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E= github.com/jackc/pgio v1.0.0 h1:g12B9UwVnzGhueNavwioyEEpAmqMe1E/BN9ES+8ovkE= github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= @@ -244,22 +164,23 @@ github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvW github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgproto3/v2 v2.3.1 h1:nwj7qwf0S+Q7ISFfBndqeLwSwxs+4DPsbRFjECT1Y4Y= -github.com/jackc/pgproto3/v2 v2.3.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= -github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b h1:C8S2+VttkHFdOOCXJe+YGfa4vHYwlt4Zx+IVXQ97jYg= +github.com/jackc/pgproto3/v2 v2.3.2 h1:7eY55bdBeCz1F2fTzSz69QC+pG46jYq9/jtSPiJ5nn0= +github.com/jackc/pgproto3/v2 v2.3.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= -github.com/jackc/pgtype v1.12.0 h1:Dlq8Qvcch7kiehm8wPGIW0W3KsCCHJnRacKW0UM8n5w= -github.com/jackc/pgtype v1.12.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= +github.com/jackc/pgtype v1.14.0 h1:y+xUdabmyMkJLyApYuPj38mW+aAIqCe5uuBB51rH3Vw= +github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= -github.com/jackc/pgx/v4 v4.17.2 h1:0Ut0rpeKwvIVbMQ1KbMBU4h6wxehBI535LK6Flheh8E= -github.com/jackc/pgx/v4 v4.17.2/go.mod h1:lcxIZN44yMIrWI78a5CpucdD14hX0SBDbNRvjDBItsw= +github.com/jackc/pgx/v4 v4.18.1 h1:YP7G1KABtKpB5IHrO9vYwSrCOhs7p3uqhvhhQBptya0= +github.com/jackc/pgx/v4 v4.18.1/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE= github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= @@ -267,35 +188,27 @@ github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dv github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= 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/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= -github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= -github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= -github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= 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/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= -github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= -github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= 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/klauspost/compress v1.13.6 h1:P76CopJELS0TiO2mebmnzgWaajssP/EszplttgQxcgc= -github.com/klauspost/compress v1.13.6/go.mod h1:/3/Vjq9QcHkK5uEr5lBEmyoZ1iFhe47etQ6QUkpK6sk= +github.com/klauspost/compress v1.16.4 h1:91KN02FnsOYhuunwU4ssRe8lc2JosWmizWa91B5v1PU= +github.com/klauspost/compress v1.16.4/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= github.com/klauspost/cpuid/v2 v2.0.4/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= -github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4= -github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.4 h1:acbojRNwl3o09bUq+yDCtZFc1aiwaAAxtcn8YkZXnvk= +github.com/klauspost/cpuid/v2 v2.2.4/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= 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/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= @@ -303,8 +216,6 @@ github.com/lib/pq v1.10.2 h1:AqzbZs4ZoCBp+GtejcpCpcxM3zlSMx29dXbUSeVtJb8= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= -github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= -github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= @@ -312,78 +223,53 @@ github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= -github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 h1:I0XW9+e1XWDxdcEniV4rQAIOPUGDq67JSCiRCgGCZLI= -github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +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/minio/md5-simd v1.1.2 h1:Gdi1DZK69+ZVMoNHRXJyNcxrMA4dSxoYHZSQbirFg34= github.com/minio/md5-simd v1.1.2/go.mod h1:MzdKDxYpY2BT9XQFocsiZf/NKVtR7nkE4RoEpN+20RM= -github.com/minio/minio-go/v7 v7.0.20 h1:0+Xt1SkCKDgcx5cmo3UxXcJ37u5Gy+/2i/+eQYqmYJw= -github.com/minio/minio-go/v7 v7.0.20/go.mod h1:ei5JjmxwHaMrgsMrn4U/+Nmg+d8MKS1U2DAn1ou4+Do= +github.com/minio/minio-go/v7 v7.0.50 h1:4IL4V8m/kI90ZL6GupCARZVrBv8/XrcKcJhaJ3iz68k= +github.com/minio/minio-go/v7 v7.0.50/go.mod h1:IbbodHyjUAguneyucUaahv+VMNs/EOTV9du7A7/Z3HU= github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= github.com/minio/sha256-simd v1.0.0/go.mod h1:OuYzVNI5vcoYIAmbIvHPl3N3jUzVedXbKy5RFepssQM= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c= 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 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= 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/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= 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/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= -github.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE= -github.com/onsi/ginkgo/v2 v2.1.6 h1:Fx2POJZfKRQcM1pH49qSZiYeu319wji004qX+GDovrU= -github.com/onsi/ginkgo/v2 v2.1.6/go.mod h1:MEH45j8TBi6u9BMogfbp0stKC5cdGjumZj5Y7AG4VIk= -github.com/onsi/gomega v1.20.2 h1:8uQq0zMgLEfa0vRrrBgaJF2gyW9Da9BmfGV+OyUzfkY= -github.com/onsi/gomega v1.20.2/go.mod h1:iYAIXgPSaDHak0LCMA+AWBpIKBr8WZicMxnE8luStNc= +github.com/onsi/ginkgo/v2 v2.8.0 h1:pAM+oBNPrpXRs+E/8spkeGx9QgekbRVyr74EUvRVOUI= +github.com/onsi/ginkgo/v2 v2.8.0/go.mod h1:6JsQiECmxCa3V5st74AL/AmsV482EDdVrGaVW6z3oYU= +github.com/onsi/gomega v1.26.0 h1:03cDLK28U6hWvCAns6NeydX3zIm4SF3ci69ulidS32Q= +github.com/onsi/gomega v1.26.0/go.mod h1:r+zV744Re+DiYCIPRlYOTxn0YkOLcAnW8k1xXdMPGhM= github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= -github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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 v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= -github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= -github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= -github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0= -github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY= -github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU= -github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ= -github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= -github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw= +github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M= -github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= -github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= -github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= -github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= -github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE= -github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= -github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= -github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= -github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= -github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= -github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo= -github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= +github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4= +github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= +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/robfig/cron/v3 v3.0.1 h1:WdRxkvbJztn8LMz/QEvLN5sBU+xKpSqwwUO1Pjr4qDs= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= -github.com/rs/xid v1.3.0 h1:6NjYksEUlhurdVehpc7S7dk6DAmcKv8V9gG0FsVN2U4= -github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= +github.com/rs/xid v1.4.0 h1:qd7wPTDkN6KQx2VmMBLrpHkiyQwgFXRnkOLacUiaSNY= +github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg= github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -392,47 +278,40 @@ github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= github.com/shopspring/decimal v1.2.0 h1:abSATXmQEYyShuxI4/vyW3tV1MrKAJzCZ/0zLUXYbsQ= github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= -github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= -github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= -github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= -github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/cobra v1.5.0 h1:X+jTBEBqF0bHN+9cSMgmfuvv2VHJ9ezmFNf9Y/XstYU= -github.com/spf13/cobra v1.5.0/go.mod h1:dWXEIy2H428czQCjInthrTRUg7yKbok+2Qi/yBIJoUM= +github.com/spf13/cobra v1.6.0 h1:42a0n6jwCot1pUmomAp4T7DeMD+20LFv4Q54pxLf2LI= +github.com/spf13/cobra v1.6.0/go.mod h1:IOw/AERYS7UzyrGinqmz6HLUo219MORXGxhbaJUqzrY= 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/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= 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.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 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk= 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/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= github.com/xlab/treeprint v1.1.0 h1:G/1DjNkPpfZCFt9CSh6b5/nY4VimlbHF3Rh4obvtzDk= github.com/xlab/treeprint v1.1.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= -go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= -go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= -go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= -go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.starlark.net v0.0.0-20220817180228-f738f5508c12 h1:xOBJXWGEDwU5xSDxH6macxO11Us0AH2fTa9rmsbbF7g= go.starlark.net v0.0.0-20220817180228-f738f5508c12/go.mod h1:VZcBMdr3cT3PnBoWunTabuSEXwVAH+ZJ5zxfs3AdASk= @@ -444,240 +323,134 @@ go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.10.0 h1:9qC72Qh0+3MqyJbAn8YU5xVq1frD8bn3JtD2oXtafVQ= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= -go.uber.org/goleak v1.1.12 h1:gZAh5/EyT/HQwlpkCy6wTpqfH9H8Lz8zbm3dZh+OyzA= +go.uber.org/goleak v1.2.0 h1:xqgm/S+aQvhWFTtR0XK3Jvg7z8kGV8P4X14IzwN3Eqk= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= -go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8= -go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak= +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/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.19.0/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= -go.uber.org/zap v1.23.0 h1:OjGQ5KQDEUawVHxNwQgPpiypGHOxo2mNZsOqTak4fFY= -go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= -golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60= +go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= -golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90 h1:Y/gsMcFOcR+6S6f3YeMKl5g+dZMEWqcz5Czj/GWYbkM= -golang.org/x/crypto v0.0.0-20220829220503-c86fa9a7ed90/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= +golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= +golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= -golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= -golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek= -golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY= -golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= -golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= -golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js= -golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= 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-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= -golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= -golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= -golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/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-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/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/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/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-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/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-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= -golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= -golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= -golang.org/x/net v0.0.0-20220909164309-bea034e7d591 h1:D0B/7al0LLrVC8aWF4+oxpv/m8bc7ViFfVS8/gXGdqI= -golang.org/x/net v0.0.0-20220909164309-bea034e7d591/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ= +golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= -golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 h1:lxqLZaMad/dJHMFZH0NiNpiEZI/nhgWhe4wgzpE+MuA= -golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1/go.mod h1:h4gKUeWbJ4rQPri7E0u6Gs4e9Ri2zaLxzw5DI5XGrYg= +golang.org/x/oauth2 v0.6.0 h1:Lh8GPgSKBfWSwFvtuWOfeI3aAAnbXTSutYxJiOJFgIw= +golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw= 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/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/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-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/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-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/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-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20220907062415-87db552b00fd h1:AZeIEzg+8RCELJYq8w+ODLVxFgLMMigSwO/ffKPEd9U= -golang.org/x/sys v0.0.0-20220907062415-87db552b00fd/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.7.0 h1:3jlCCIQZPdOYu1h8BkNvLz8Kgwtae2cagcG/VamtZRU= +golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc= -golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.7.0 h1:BEvjmm5fURWqcfbSKTdpkDXYBrUS1c0m8agp14W48vQ= +golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/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.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9 h1:ftMN5LMiBFjbzleLqtoBZk7KdJwhuybIU+FckUHgoyQ= -golang.org/x/time v0.0.0-20220722155302-e5dcc9cfc0b9/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0 h1:2sjJmO8cDvYveuX97RDLsxlyUxLl+GHoLxBiRdHllBE= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +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/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-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= -golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= -golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= -golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw= -golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= -golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= -golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= @@ -686,73 +459,19 @@ golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.2.0 h1:4pT439QV83L+G9FkcCriY6EkpcK6r6bK+A5FBUMI7qY= gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= -google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= -google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= -google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= -google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= -google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE= -google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE= -google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM= -google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc= 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.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= -google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0= -google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= 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-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= -google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= -google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= -google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= -google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= -google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= -google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= -google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= -google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= -google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= -google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= -google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= -google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60= -google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk= -google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= -google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= @@ -764,73 +483,58 @@ google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzi google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.23.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.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= 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.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w= -google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +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= 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/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= -gopkg.in/ini.v1 v1.66.2 h1:XfR1dOYubytKy4Shzc2LHrrGhU0lDCfDGG1yLPmpgsI= -gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= -gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= -gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/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.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= -gopkg.in/yaml.v3 v3.0.0/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-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= -honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.25.0 h1:H+Q4ma2U/ww0iGB78ijZx6DRByPz6/733jIuFpX70e0= -k8s.io/api v0.25.0/go.mod h1:ttceV1GyV1i1rnmvzT3BST08N6nGt+dudGrquzVQWPk= -k8s.io/apiextensions-apiserver v0.25.0 h1:CJ9zlyXAbq0FIW8CD7HHyozCMBpDSiH7EdrSTCZcZFY= -k8s.io/apiextensions-apiserver v0.25.0/go.mod h1:3pAjZiN4zw7R8aZC5gR0y3/vCkGlAjCazcg1me8iB/E= -k8s.io/apimachinery v0.25.0 h1:MlP0r6+3XbkUG2itd6vp3oxbtdQLQI94fD5gCS+gnoU= -k8s.io/apimachinery v0.25.0/go.mod h1:qMx9eAk0sZQGsXGu86fab8tZdffHbwUfsvzqKn4mfB0= +k8s.io/api v0.26.3 h1:emf74GIQMTik01Aum9dPP0gAypL8JTLl/lHa4V9RFSU= +k8s.io/api v0.26.3/go.mod h1:PXsqwPMXBSBcL1lJ9CYDKy7kIReUydukS5JiRlxC3qE= +k8s.io/apiextensions-apiserver v0.26.3 h1:5PGMm3oEzdB1W/FTMgGIDmm100vn7IaUP5er36dB+YE= +k8s.io/apiextensions-apiserver v0.26.3/go.mod h1:jdA5MdjNWGP+njw1EKMZc64xAT5fIhN6VJrElV3sfpQ= +k8s.io/apimachinery v0.26.3 h1:dQx6PNETJ7nODU3XPtrwkfuubs6w7sX0M8n61zHIV/k= +k8s.io/apimachinery v0.26.3/go.mod h1:ats7nN1LExKHvJ9TmwootT00Yz05MuYqPXEXaVeOy5I= k8s.io/cli-runtime v0.25.0 h1:XBnTc2Fi+w818jcJGzhiJKQuXl8479sZ4FhtV5hVJ1Q= k8s.io/cli-runtime v0.25.0/go.mod h1:bHOI5ZZInRHhbq12OdUiYZQN8ml8aKZLwQgt9QlLINw= -k8s.io/client-go v0.25.0 h1:CVWIaCETLMBNiTUta3d5nzRbXvY5Hy9Dpl+VvREpu5E= -k8s.io/client-go v0.25.0/go.mod h1:lxykvypVfKilxhTklov0wz1FoaUZ8X4EwbhS6rpRfN8= -k8s.io/component-base v0.25.0 h1:haVKlLkPCFZhkcqB6WCvpVxftrg6+FK5x1ZuaIDaQ5Y= -k8s.io/component-base v0.25.0/go.mod h1:F2Sumv9CnbBlqrpdf7rKZTmmd2meJq0HizeyY/yAFxk= -k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= -k8s.io/klog/v2 v2.80.0 h1:lyJt0TWMPaGoODa8B8bUuxgHS3W/m/bNr2cca3brA/g= -k8s.io/klog/v2 v2.80.0/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= -k8s.io/kube-openapi v0.0.0-20220803164354-a70c9af30aea h1:3QOH5+2fGsY8e1qf+GIFpg+zw/JGNrgyZRQR7/m6uWg= -k8s.io/kube-openapi v0.0.0-20220803164354-a70c9af30aea/go.mod h1:C/N6wCaBHeBHkHUesQOQy2/MZqGgMAFPqGsGQLdbZBU= -k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73 h1:H9TCJUUx+2VA0ZiD9lvtaX8fthFsMoD+Izn93E/hm8U= -k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= -rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= -rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= -rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= -sigs.k8s.io/controller-runtime v0.13.0 h1:iqa5RNciy7ADWnIc8QxCbOX5FEKVR3uxVxKHRMc2WIQ= -sigs.k8s.io/controller-runtime v0.13.0/go.mod h1:Zbz+el8Yg31jubvAEyglRZGdLAjplZl+PgtYNI6WNTI= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +k8s.io/client-go v0.26.3 h1:k1UY+KXfkxV2ScEL3gilKcF7761xkYsSD6BC9szIu8s= +k8s.io/client-go v0.26.3/go.mod h1:ZPNu9lm8/dbRIPAgteN30RSXea6vrCpFvq+MateTUuQ= +k8s.io/component-base v0.26.3 h1:oC0WMK/ggcbGDTkdcqefI4wIZRYdK3JySx9/HADpV0g= +k8s.io/component-base v0.26.3/go.mod h1:5kj1kZYwSC6ZstHJN7oHBqcJC6yyn41eR+Sqa/mQc8E= +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-20230327201221-f5883ff37f0c h1:EFfsozyzZ/pggw5qNx7ftTVZdp7WZl+3ih89GEjYEK8= +k8s.io/kube-openapi v0.0.0-20230327201221-f5883ff37f0c/go.mod h1:byini6yhqGC14c3ebc/QwanvYwhuMWF6yz2F8uwW8eg= +k8s.io/utils v0.0.0-20230313181309-38a27ef9d749 h1:xMMXJlJbsU8w3V5N2FLDQ8YgU8s1EoULdbQBcAeNJkY= +k8s.io/utils v0.0.0-20230313181309-38a27ef9d749/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +sigs.k8s.io/controller-runtime v0.14.6 h1:oxstGVvXGNnMvY7TAESYk+lzr6S3V5VFxQ6d92KcwQA= +sigs.k8s.io/controller-runtime v0.14.6/go.mod h1:WqIdsAY6JBsjfc/CqO0CORmNtoCtE4S6qbPc9s68h+0= +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/kustomize/api v0.12.1 h1:7YM7gW3kYBwtKvoY216ZzY+8hM+lV53LUayghNRJ0vM= sigs.k8s.io/kustomize/api v0.12.1/go.mod h1:y3JUhimkZkR6sbLNwfJHxvo1TCLwuwm14sCYnkH6S1s= sigs.k8s.io/kustomize/kyaml v0.13.9 h1:Qz53EAaFFANyNgyOEJbT/yoIHygK40/ZcvU3rgry2Tk=