26 | CloudNativePG was originally built and sponsored by EDB.
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | ---
38 |
39 |
40 | Postgres, PostgreSQL, and the Slonik Logo
41 | are trademarks or registered trademarks of the PostgreSQL Community Association
42 | of Canada, and used with their permission.
43 |
44 |
45 | ---
46 |
--------------------------------------------------------------------------------
/RELEASE-PROCEDURE.md:
--------------------------------------------------------------------------------
1 | # Release Procedure
2 |
3 | The Barman Cloud Plugin follows [Semantic Versioning](https://semver.org/) and
4 | uses [Conventional Commits](https://www.conventionalcommits.org/) to structure
5 | commit messages.
6 | It leverages the [Release Please](https://github.com/googleapis/release-please)
7 | tool to automate the release process.
8 |
9 | The only requirement is that each release must include versioned documentation.
10 | For more details, please refer to [web/README.md](web/README.md).
11 |
--------------------------------------------------------------------------------
/api/v1/doc.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright The CloudNativePG Contributors
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | // Package v1 contains API Schema definitions for the barmancloud v1 API group
18 | // +kubebuilder:object:generate=true
19 | // +groupName=barmancloud.cnpg.io
20 | package v1
21 |
--------------------------------------------------------------------------------
/api/v1/groupversion_info.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package v1
18 |
19 | import (
20 | "k8s.io/apimachinery/pkg/runtime/schema"
21 | "sigs.k8s.io/controller-runtime/pkg/scheme"
22 | )
23 |
24 | var (
25 | // GroupVersion is group version used to register these objects.
26 | GroupVersion = schema.GroupVersion{Group: "barmancloud.cnpg.io", Version: "v1"}
27 |
28 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme.
29 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
30 |
31 | // AddToScheme adds the types in this group-version to the given scheme.
32 | AddToScheme = SchemeBuilder.AddToScheme
33 | )
34 |
--------------------------------------------------------------------------------
/cmd/manager/doc.go:
--------------------------------------------------------------------------------
1 | // Package main is the entrypoint for the plugin
2 | package main
3 |
--------------------------------------------------------------------------------
/cmd/manager/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "errors"
6 | "fmt"
7 | "os"
8 |
9 | "github.com/cloudnative-pg/machinery/pkg/log"
10 | "github.com/spf13/cobra"
11 | ctrl "sigs.k8s.io/controller-runtime"
12 |
13 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cmd/healthcheck"
14 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cmd/instance"
15 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cmd/operator"
16 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cmd/restore"
17 | )
18 |
19 | func main() {
20 | cobra.EnableTraverseRunHooks = true
21 |
22 | logFlags := &log.Flags{}
23 | rootCmd := &cobra.Command{
24 | Use: "manager [cmd]",
25 | PersistentPreRun: func(cmd *cobra.Command, _ []string) {
26 | logFlags.ConfigureLogging()
27 | cmd.SetContext(log.IntoContext(cmd.Context(), log.GetLogger()))
28 | },
29 | }
30 |
31 | logFlags.AddFlags(rootCmd.PersistentFlags())
32 |
33 | rootCmd.AddCommand(instance.NewCmd())
34 | rootCmd.AddCommand(operator.NewCmd())
35 | rootCmd.AddCommand(restore.NewCmd())
36 | rootCmd.AddCommand(healthcheck.NewCmd())
37 |
38 | if err := rootCmd.ExecuteContext(ctrl.SetupSignalHandler()); err != nil {
39 | if !errors.Is(err, context.Canceled) {
40 | fmt.Println(err)
41 | os.Exit(1)
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | const Configuration= {
2 | extends: ['@commitlint/config-conventional'],
3 | formatter: '@commitlint/format',
4 | rules: {
5 | 'body-empty': [1, 'never'],
6 | 'body-case': [2, 'always', 'sentence-case'],
7 | 'body-max-line-length': [1, 'always', 100],
8 | 'references-empty': [1, 'never'],
9 | 'signed-off-by': [2, 'always', 'Signed-off-by:'],
10 | },
11 | };
12 |
13 | module.exports = Configuration;
14 |
--------------------------------------------------------------------------------
/config/crd/kustomization.yaml:
--------------------------------------------------------------------------------
1 | # This kustomization.yaml is not intended to be run by itself,
2 | # since it depends on service name and namespace that are out of this kustomize package.
3 | # It should be run by config/default
4 | resources:
5 | - bases/barmancloud.cnpg.io_objectstores.yaml
6 | # +kubebuilder:scaffold:crdkustomizeresource
7 |
8 | patches:
9 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix.
10 | # patches here are for enabling the conversion webhook for each CRD
11 | # +kubebuilder:scaffold:crdkustomizewebhookpatch
12 |
13 | # [CERTMANAGER] To enable cert-manager, uncomment all the sections with [CERTMANAGER] prefix.
14 | # patches here are for enabling the CA injection for each CRD
15 | #- path: patches/cainjection_in_objectstores.yaml
16 | # +kubebuilder:scaffold:crdkustomizecainjectionpatch
17 |
18 | # [WEBHOOK] To enable webhook, uncomment the following section
19 | # the following config is for teaching kustomize how to do kustomization for CRDs.
20 |
21 | #configurations:
22 | #- kustomizeconfig.yaml
23 |
--------------------------------------------------------------------------------
/config/crd/kustomizeconfig.yaml:
--------------------------------------------------------------------------------
1 | # This file is for teaching kustomize how to substitute name and namespace reference in CRD
2 | nameReference:
3 | - kind: Service
4 | version: v1
5 | fieldSpecs:
6 | - kind: CustomResourceDefinition
7 | version: v1
8 | group: apiextensions.k8s.io
9 | path: spec/conversion/webhook/clientConfig/service/name
10 |
11 | namespace:
12 | - kind: CustomResourceDefinition
13 | version: v1
14 | group: apiextensions.k8s.io
15 | path: spec/conversion/webhook/clientConfig/service/namespace
16 | create: false
17 |
18 | varReference:
19 | - path: metadata/annotations
20 |
--------------------------------------------------------------------------------
/config/default/manager_metrics_patch.yaml:
--------------------------------------------------------------------------------
1 | # This patch adds the args to allow exposing the metrics endpoint using HTTPS
2 | - op: add
3 | path: /spec/template/spec/containers/0/args/0
4 | value: --metrics-bind-address=:8443
5 |
--------------------------------------------------------------------------------
/config/default/metrics_service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | labels:
5 | control-plane: controller-manager
6 | app.kubernetes.io/name: plugin-barman-cloud
7 | app.kubernetes.io/managed-by: kustomize
8 | name: controller-manager-metrics-service
9 | namespace: system
10 | spec:
11 | ports:
12 | - name: https
13 | port: 8443
14 | protocol: TCP
15 | targetPort: 8443
16 | selector:
17 | control-plane: controller-manager
18 |
--------------------------------------------------------------------------------
/config/manager/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | - manager.yaml
3 |
--------------------------------------------------------------------------------
/config/network-policy/allow-metrics-traffic.yaml:
--------------------------------------------------------------------------------
1 | # This NetworkPolicy allows ingress traffic
2 | # with Pods running on namespaces labeled with 'metrics: enabled'. Only Pods on those
3 | # namespaces are able to gathering data from the metrics endpoint.
4 | apiVersion: networking.k8s.io/v1
5 | kind: NetworkPolicy
6 | metadata:
7 | labels:
8 | app.kubernetes.io/name: plugin-barman-cloud
9 | app.kubernetes.io/managed-by: kustomize
10 | name: allow-metrics-traffic
11 | namespace: system
12 | spec:
13 | podSelector:
14 | matchLabels:
15 | control-plane: controller-manager
16 | policyTypes:
17 | - Ingress
18 | ingress:
19 | # This allows ingress traffic from any namespace with the label metrics: enabled
20 | - from:
21 | - namespaceSelector:
22 | matchLabels:
23 | metrics: enabled # Only from namespaces with this label
24 | ports:
25 | - port: 8443
26 | protocol: TCP
27 |
--------------------------------------------------------------------------------
/config/network-policy/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | - allow-metrics-traffic.yaml
3 |
--------------------------------------------------------------------------------
/config/prometheus/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | - monitor.yaml
3 |
--------------------------------------------------------------------------------
/config/prometheus/monitor.yaml:
--------------------------------------------------------------------------------
1 | # Prometheus Monitor Service (Metrics)
2 | apiVersion: monitoring.coreos.com/v1
3 | kind: ServiceMonitor
4 | metadata:
5 | labels:
6 | control-plane: controller-manager
7 | app.kubernetes.io/name: plugin-barman-cloud
8 | app.kubernetes.io/managed-by: kustomize
9 | name: controller-manager-metrics-monitor
10 | namespace: system
11 | spec:
12 | endpoints:
13 | - path: /metrics
14 | port: https # Ensure this is the name of the port that exposes HTTPS metrics
15 | scheme: https
16 | bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
17 | tlsConfig:
18 | # TODO(user): The option insecureSkipVerify: true is not recommended for production since it disables
19 | # certificate verification. This poses a significant security risk by making the system vulnerable to
20 | # man-in-the-middle attacks, where an attacker could intercept and manipulate the communication between
21 | # Prometheus and the monitored services. This could lead to unauthorized access to sensitive metrics data,
22 | # compromising the integrity and confidentiality of the information.
23 | # Please use the following options for secure configurations:
24 | # caFile: /etc/metrics-certs/ca.crt
25 | # certFile: /etc/metrics-certs/tls.crt
26 | # keyFile: /etc/metrics-certs/tls.key
27 | insecureSkipVerify: true
28 | selector:
29 | matchLabels:
30 | control-plane: controller-manager
31 |
--------------------------------------------------------------------------------
/config/rbac/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | # All RBAC will be applied under this service account in
3 | # the deployment namespace. You may comment out this resource
4 | # if your manager will use a service account that exists at
5 | # runtime. Be sure to update RoleBinding and ClusterRoleBinding
6 | # subjects if changing service account names.
7 | - service_account.yaml
8 | - role.yaml
9 | - role_binding.yaml
10 | - leader_election_role.yaml
11 | - leader_election_role_binding.yaml
12 | # The following RBAC configurations are used to protect
13 | # the metrics endpoint with authn/authz. These configurations
14 | # ensure that only authorized users and service accounts
15 | # can access the metrics endpoint. Comment the following
16 | # permissions if you want to disable this protection.
17 | # More info: https://book.kubebuilder.io/reference/metrics.html
18 | - metrics_auth_role.yaml
19 | - metrics_auth_role_binding.yaml
20 | - metrics_reader_role.yaml
21 | # For each CRD, "Editor" and "Viewer" roles are scaffolded by
22 | # default, aiding admins in cluster management. Those roles are
23 | # not used by the Project itself. You can comment the following lines
24 | # if you do not want those helpers be installed with your Project.
25 | - objectstore_editor_role.yaml
26 | - objectstore_viewer_role.yaml
27 |
28 |
--------------------------------------------------------------------------------
/config/rbac/leader_election_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions to do leader election.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: Role
4 | metadata:
5 | labels:
6 | app.kubernetes.io/name: plugin-barman-cloud
7 | app.kubernetes.io/managed-by: kustomize
8 | name: leader-election-role
9 | rules:
10 | - apiGroups:
11 | - ""
12 | resources:
13 | - configmaps
14 | verbs:
15 | - get
16 | - list
17 | - watch
18 | - create
19 | - update
20 | - patch
21 | - delete
22 | - apiGroups:
23 | - coordination.k8s.io
24 | resources:
25 | - leases
26 | verbs:
27 | - get
28 | - list
29 | - watch
30 | - create
31 | - update
32 | - patch
33 | - delete
34 | - apiGroups:
35 | - ""
36 | resources:
37 | - events
38 | verbs:
39 | - create
40 | - patch
41 |
--------------------------------------------------------------------------------
/config/rbac/leader_election_role_binding.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: RoleBinding
3 | metadata:
4 | labels:
5 | app.kubernetes.io/name: plugin-barman-cloud
6 | app.kubernetes.io/managed-by: kustomize
7 | name: leader-election-rolebinding
8 | roleRef:
9 | apiGroup: rbac.authorization.k8s.io
10 | kind: Role
11 | name: leader-election-role
12 | subjects:
13 | - kind: ServiceAccount
14 | name: plugin-barman-cloud
15 | namespace: cnpg-system
16 |
--------------------------------------------------------------------------------
/config/rbac/metrics_auth_role.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRole
3 | metadata:
4 | name: metrics-auth-role
5 | rules:
6 | - apiGroups:
7 | - authentication.k8s.io
8 | resources:
9 | - tokenreviews
10 | verbs:
11 | - create
12 | - apiGroups:
13 | - authorization.k8s.io
14 | resources:
15 | - subjectaccessreviews
16 | verbs:
17 | - create
18 |
--------------------------------------------------------------------------------
/config/rbac/metrics_auth_role_binding.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRoleBinding
3 | metadata:
4 | name: metrics-auth-rolebinding
5 | roleRef:
6 | apiGroup: rbac.authorization.k8s.io
7 | kind: ClusterRole
8 | name: metrics-auth-role
9 | subjects:
10 | - kind: ServiceAccount
11 | name: plugin-barman-cloud
12 | namespace: cnpg-system
13 |
--------------------------------------------------------------------------------
/config/rbac/metrics_reader_role.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRole
3 | metadata:
4 | name: metrics-reader
5 | rules:
6 | - nonResourceURLs:
7 | - "/metrics"
8 | verbs:
9 | - get
10 |
--------------------------------------------------------------------------------
/config/rbac/objectstore_editor_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to edit objectstores.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | labels:
6 | app.kubernetes.io/name: plugin-barman-cloud
7 | app.kubernetes.io/managed-by: kustomize
8 | name: objectstore-editor-role
9 | rules:
10 | - apiGroups:
11 | - barmancloud.cnpg.io
12 | resources:
13 | - objectstores
14 | verbs:
15 | - create
16 | - delete
17 | - get
18 | - list
19 | - patch
20 | - update
21 | - watch
22 | - apiGroups:
23 | - barmancloud.cnpg.io
24 | resources:
25 | - objectstores/status
26 | verbs:
27 | - get
28 |
--------------------------------------------------------------------------------
/config/rbac/objectstore_viewer_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to view objectstores.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | labels:
6 | app.kubernetes.io/name: plugin-barman-cloud
7 | app.kubernetes.io/managed-by: kustomize
8 | name: objectstore-viewer-role
9 | rules:
10 | - apiGroups:
11 | - barmancloud.cnpg.io
12 | resources:
13 | - objectstores
14 | verbs:
15 | - get
16 | - list
17 | - watch
18 | - apiGroups:
19 | - barmancloud.cnpg.io
20 | resources:
21 | - objectstores/status
22 | verbs:
23 | - get
24 |
--------------------------------------------------------------------------------
/config/rbac/role.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | name: plugin-barman-cloud
6 | rules:
7 | - apiGroups:
8 | - ""
9 | resources:
10 | - secrets
11 | verbs:
12 | - create
13 | - delete
14 | - get
15 | - list
16 | - watch
17 | - apiGroups:
18 | - barmancloud.cnpg.io
19 | resources:
20 | - objectstores
21 | verbs:
22 | - create
23 | - delete
24 | - get
25 | - list
26 | - patch
27 | - update
28 | - watch
29 | - apiGroups:
30 | - barmancloud.cnpg.io
31 | resources:
32 | - objectstores/finalizers
33 | verbs:
34 | - update
35 | - apiGroups:
36 | - barmancloud.cnpg.io
37 | resources:
38 | - objectstores/status
39 | verbs:
40 | - get
41 | - patch
42 | - update
43 | - apiGroups:
44 | - postgresql.cnpg.io
45 | resources:
46 | - backups
47 | verbs:
48 | - get
49 | - list
50 | - watch
51 | - apiGroups:
52 | - rbac.authorization.k8s.io
53 | resources:
54 | - rolebindings
55 | - roles
56 | verbs:
57 | - create
58 | - get
59 | - list
60 | - patch
61 | - update
62 | - watch
63 |
--------------------------------------------------------------------------------
/config/rbac/role_binding.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRoleBinding
3 | metadata:
4 | labels:
5 | app.kubernetes.io/name: plugin-barman-cloud
6 | app.kubernetes.io/managed-by: kustomize
7 | name: plugin-barman-cloud-binding
8 | roleRef:
9 | apiGroup: rbac.authorization.k8s.io
10 | kind: ClusterRole
11 | name: plugin-barman-cloud
12 | subjects:
13 | - kind: ServiceAccount
14 | name: plugin-barman-cloud
15 | namespace: cnpg-system
16 |
--------------------------------------------------------------------------------
/config/rbac/service_account.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ServiceAccount
3 | metadata:
4 | labels:
5 | app.kubernetes.io/name: plugin-barman-cloud
6 | app.kubernetes.io/managed-by: kustomize
7 | name: plugin-barman-cloud
8 |
--------------------------------------------------------------------------------
/config/samples/kustomization.yaml:
--------------------------------------------------------------------------------
1 | ## Append samples of your project ##
2 | resources:
3 | - barmancloud_v1_objectstore.yaml
4 | # +kubebuilder:scaffold:manifestskustomizesamples
5 |
--------------------------------------------------------------------------------
/containers/Dockerfile.plugin:
--------------------------------------------------------------------------------
1 | # Build the manager binary
2 | FROM --platform=$BUILDPLATFORM golang:1.24.3 AS gobuilder
3 | ARG TARGETOS
4 | ARG TARGETARCH
5 |
6 | WORKDIR /workspace
7 | # Copy the Go Modules manifests
8 | COPY ../go.mod go.mod
9 | COPY ../go.sum go.sum
10 | # cache deps before building and copying source so that we don't need to re-download as much
11 | # and so that source changes don't invalidate our downloaded layer
12 | RUN go mod download
13 |
14 | # Copy the go source
15 | COPY ../cmd/manager/main.go cmd/manager/main.go
16 | COPY ../api/ api/
17 | COPY ../internal/ internal/
18 |
19 | ENV GOCACHE=/root/.cache/go-build
20 | ENV GOMODCACHE=/go/pkg/mod
21 |
22 | # Build
23 | # the GOARCH has not a default value to allow the binary be built according to the host where the command
24 | # was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO
25 | # the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore,
26 | # by leaving it empty we can ensure that the container and binary shipped on it will have the same platform.
27 | RUN --mount=type=cache,target=/go/pkg/mod --mount=type=cache,target=/root/.cache/go-build \
28 | CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager cmd/manager/main.go
29 |
30 | # Use a minimal base image to package the manager binary
31 | # Refer to https://www.redhat.com/en/blog/introduction-ubi-micro for more details
32 | FROM registry.access.redhat.com/ubi9/ubi-micro
33 |
34 | ENV SUMMARY="CloudNativePG Barman plugin" \
35 | DESCRIPTION="Container image that provides the barman-cloud plugin"
36 |
37 | LABEL summary="$SUMMARY" \
38 | description="$DESCRIPTION" \
39 | io.k8s.display-name="$SUMMARY" \
40 | io.k8s.description="$DESCRIPTION" \
41 | name="$SUMMARY" \
42 | vendor="CloudNativePG Contributors" \
43 | url="https://cloudnative-pg.io/" \
44 | version="" \
45 | release="1"
46 |
47 | WORKDIR /
48 | COPY --from=gobuilder /workspace/manager .
49 | USER 65532:65532
50 |
51 | ENTRYPOINT ["/manager"]
52 |
--------------------------------------------------------------------------------
/containers/Dockerfile.sidecar:
--------------------------------------------------------------------------------
1 | # Sidecar
2 | # The container needs to provide and build two components:
3 | # * barman-cloud
4 | # * instance plugin
5 | # Both components are built before going into a distroless container
6 |
7 | # Build the manager binary
8 | FROM --platform=$BUILDPLATFORM golang:1.24.3 AS gobuilder
9 | ARG TARGETOS
10 | ARG TARGETARCH
11 |
12 | WORKDIR /workspace
13 | # Copy the Go Modules manifests
14 | COPY ../go.mod go.mod
15 | COPY ../go.sum go.sum
16 | # cache deps before building and copying source so that we don't need to re-download as much
17 | # and so that source changes don't invalidate our downloaded layer
18 | RUN go mod download
19 |
20 | ENV GOCACHE=/root/.cache/go-build
21 | ENV GOMODCACHE=/go/pkg/mod
22 |
23 | # Copy the go source
24 | COPY ../cmd/manager/main.go cmd/manager/main.go
25 | COPY ../api/ api/
26 | COPY ../internal/ internal/
27 |
28 | # Build
29 | # the GOARCH has not a default value to allow the binary be built according to the host where the command
30 | # was called. For example, if we call make docker-build in a local env which has the Apple Silicon M1 SO
31 | # the docker BUILDPLATFORM arg will be linux/arm64 when for Apple x86 it will be linux/amd64. Therefore,
32 | # by leaving it empty we can ensure that the container and binary shipped on it will have the same platform.
33 | RUN --mount=type=cache,target=/go/pkg/mod --mount=type=cache,target=/root/.cache/go-build \
34 | CGO_ENABLED=0 GOOS=${TARGETOS:-linux} GOARCH=${TARGETARCH} go build -a -o manager cmd/manager/main.go
35 |
36 | # Build barman-cloud
37 | # pip will build everything inside /usr/ since this is the case
38 | # we should build and then copy every file into a destination that will
39 | # then copy into the distroless container
40 | FROM python:3.13-slim AS pythonbuilder
41 | COPY containers/sidecar-requirements.txt .
42 | RUN apt-get update && \
43 | apt-get install -y postgresql-common build-essential && \
44 | /usr/share/postgresql-common/pgdg/apt.postgresql.org.sh -y && \
45 | apt-get install -y libpq-dev && \
46 | pip install -r sidecar-requirements.txt
47 | # Prepare a new /usr/ directory with the files we'll need in the final image
48 | RUN mkdir /new-usr/ && \
49 | cp -r --parents /usr/local/lib/ /usr/lib/*-linux-gnu/ /usr/local/bin/ \
50 | /new-usr/
51 |
52 | # Joint process
53 | # Now we put everything that was build from the origin into our
54 | # distroless container
55 | FROM gcr.io/distroless/python3-debian12:nonroot
56 |
57 | ENV SUMMARY="CloudNativePG Barman plugin" \
58 | DESCRIPTION="Container image that provides the barman-cloud sidecar"
59 |
60 | LABEL summary="$SUMMARY" \
61 | description="$DESCRIPTION" \
62 | io.k8s.display-name="$SUMMARY" \
63 | io.k8s.description="$DESCRIPTION" \
64 | name="CloudNativePG Barman plugin sidecar" \
65 | vendor="CloudNativePG Contributors" \
66 | url="https://cloudnative-pg.io/" \
67 | version="" \
68 | release="1"
69 |
70 | COPY --from=pythonbuilder /new-usr/* /usr/
71 | COPY --from=gobuilder /workspace/manager /manager
72 | USER 26:26
73 | ENTRYPOINT ["/manager"]
74 |
--------------------------------------------------------------------------------
/containers/sidecar-requirements.in:
--------------------------------------------------------------------------------
1 | barman[azure,cloud,google,snappy,zstandard,lz4]==3.14.0
2 | setuptools==80.9.0
3 |
--------------------------------------------------------------------------------
/dagger/check-doc-version/.gitattributes:
--------------------------------------------------------------------------------
1 | /dagger.gen.go linguist-generated
2 | /internal/dagger/** linguist-generated
3 | /internal/querybuilder/** linguist-generated
4 | /internal/telemetry/** linguist-generated
5 |
--------------------------------------------------------------------------------
/dagger/check-doc-version/.gitignore:
--------------------------------------------------------------------------------
1 | /dagger.gen.go
2 | /internal/dagger
3 | /internal/querybuilder
4 | /internal/telemetry
5 |
--------------------------------------------------------------------------------
/dagger/check-doc-version/dagger.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "check-doc-version",
3 | "engineVersion": "v0.18.5",
4 | "sdk": {
5 | "source": "go"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/dagger/check-doc-version/go.mod:
--------------------------------------------------------------------------------
1 | module dagger/check-doc-version
2 |
3 | go 1.23.6
4 |
5 | require (
6 | github.com/99designs/gqlgen v0.17.70
7 | github.com/Khan/genqlient v0.8.0
8 | github.com/vektah/gqlparser/v2 v2.5.23
9 | go.opentelemetry.io/otel v1.34.0
10 | go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0
11 | go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0
12 | go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0
13 | go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0
14 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0
15 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0
16 | go.opentelemetry.io/otel/log v0.8.0
17 | go.opentelemetry.io/otel/metric v1.34.0
18 | go.opentelemetry.io/otel/sdk v1.34.0
19 | go.opentelemetry.io/otel/sdk/log v0.8.0
20 | go.opentelemetry.io/otel/sdk/metric v1.34.0
21 | go.opentelemetry.io/otel/trace v1.34.0
22 | go.opentelemetry.io/proto/otlp v1.3.1
23 | golang.org/x/sync v0.12.0
24 | google.golang.org/grpc v1.71.0
25 | )
26 |
27 | require (
28 | github.com/cenkalti/backoff/v4 v4.3.0 // indirect
29 | github.com/go-logr/logr v1.4.2 // indirect
30 | github.com/go-logr/stdr v1.2.2 // indirect
31 | github.com/google/uuid v1.6.0 // indirect
32 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 // indirect
33 | github.com/sosodev/duration v1.3.1 // indirect
34 | go.opentelemetry.io/auto/sdk v1.1.0 // indirect
35 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 // indirect
36 | golang.org/x/net v0.38.0 // indirect
37 | golang.org/x/sys v0.31.0 // indirect
38 | golang.org/x/text v0.23.0 // indirect
39 | google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect
40 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
41 | google.golang.org/protobuf v1.36.6 // indirect
42 | )
43 |
44 | replace go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc => go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0
45 |
46 | replace go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp => go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0
47 |
48 | replace go.opentelemetry.io/otel/log => go.opentelemetry.io/otel/log v0.8.0
49 |
50 | replace go.opentelemetry.io/otel/sdk/log => go.opentelemetry.io/otel/sdk/log v0.8.0
51 |
--------------------------------------------------------------------------------
/dagger/check-doc-version/main.go:
--------------------------------------------------------------------------------
1 | // The CheckDocVersion module is designed to check if the version of the
2 | // documentation exists for the version specified in the release-please manifest.
3 | // This is used to ensure that we do not release a new version of the plugin
4 | // without having the corresponding documentation ready.
5 |
6 | package main
7 |
8 | import (
9 | "context"
10 | "fmt"
11 | "strings"
12 |
13 | "dagger/check-doc-version/internal/dagger"
14 | )
15 |
16 | type CheckDocVersion struct{}
17 |
18 | // HasVersionDocumentation checks if a version of the documentation exists for the
19 | // version in the release-please manifest.
20 | func (m *CheckDocVersion) HasVersionDocumentation(ctx context.Context, src *dagger.Directory) (bool, error) {
21 | releasePleaseManifest := ".release-please-manifest.json"
22 | docusaurusVersions := "web/versions.json"
23 | ctr := dag.Container().From("alpine:latest").
24 | WithDirectory("/src", src).
25 | WithWorkdir("/src").
26 | WithExec([]string{"apk", "add", "jq"})
27 | nextVersion, err := ctr.
28 | WithExec([]string{"jq", "-r", ".[\".\"]", releasePleaseManifest}).
29 | Stdout(ctx)
30 | nextVersion = strings.TrimSpace(nextVersion)
31 | if err != nil {
32 | return false, fmt.Errorf("cannot find proposed release-please version in %v: %w", releasePleaseManifest,
33 | err)
34 | }
35 | currVersion, err := ctr.WithExec([]string{"jq", "-r", fmt.Sprintf(". | index(\"%v\")", nextVersion),
36 | docusaurusVersions}).Stdout(ctx)
37 | currVersion = strings.TrimSpace(currVersion)
38 | if err != nil {
39 | return false, fmt.Errorf("error querying versions in %v: %w", docusaurusVersions, err)
40 | }
41 | if currVersion == "null" {
42 | return false, nil
43 | }
44 | return true, nil
45 | }
46 |
--------------------------------------------------------------------------------
/dagger/e2e/.gitattributes:
--------------------------------------------------------------------------------
1 | /dagger.gen.go linguist-generated
2 | /internal/dagger/** linguist-generated
3 | /internal/querybuilder/** linguist-generated
4 | /internal/telemetry/** linguist-generated
5 |
--------------------------------------------------------------------------------
/dagger/e2e/.gitignore:
--------------------------------------------------------------------------------
1 | /dagger.gen.go
2 | /internal/dagger
3 | /internal/querybuilder
4 | /internal/telemetry
5 |
--------------------------------------------------------------------------------
/dagger/e2e/dagger.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "e2e",
3 | "engineVersion": "v0.18.5",
4 | "sdk": {
5 | "source": "go"
6 | },
7 | "dependencies": [
8 | {
9 | "name": "go",
10 | "source": "github.com/sagikazarmark/daggerverse/go@go/v0.9.0",
11 | "pin": "d9ba06776c4c1ccf6f329bd862b9b439c4582ab6"
12 | },
13 | {
14 | "name": "k3s",
15 | "source": "github.com/marcosnils/daggerverse/k3s@k3s/v0.1.10",
16 | "pin": "28eea1fcf3b6ecb38a628186107760acd717442f"
17 | }
18 | ]
19 | }
20 |
--------------------------------------------------------------------------------
/dagger/e2e/go.mod:
--------------------------------------------------------------------------------
1 | module dagger/e-2-e
2 |
3 | go 1.23.2
4 |
5 | require (
6 | github.com/99designs/gqlgen v0.17.70
7 | github.com/Khan/genqlient v0.8.0
8 | github.com/vektah/gqlparser/v2 v2.5.23
9 | go.opentelemetry.io/otel v1.34.0
10 | go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0
11 | go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0
12 | go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0
13 | go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0
14 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0
15 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0
16 | go.opentelemetry.io/otel/log v0.8.0
17 | go.opentelemetry.io/otel/metric v1.34.0
18 | go.opentelemetry.io/otel/sdk v1.34.0
19 | go.opentelemetry.io/otel/sdk/log v0.8.0
20 | go.opentelemetry.io/otel/sdk/metric v1.34.0
21 | go.opentelemetry.io/otel/trace v1.34.0
22 | go.opentelemetry.io/proto/otlp v1.3.1
23 | golang.org/x/sync v0.12.0
24 | google.golang.org/grpc v1.71.0
25 | )
26 |
27 | require (
28 | github.com/cenkalti/backoff/v4 v4.3.0 // indirect
29 | github.com/go-logr/logr v1.4.2 // indirect
30 | github.com/go-logr/stdr v1.2.2 // indirect
31 | github.com/google/uuid v1.6.0 // indirect
32 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 // indirect
33 | github.com/sosodev/duration v1.3.1 // indirect
34 | go.opentelemetry.io/auto/sdk v1.1.0 // indirect
35 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 // indirect
36 | golang.org/x/net v0.38.0 // indirect
37 | golang.org/x/sys v0.31.0 // indirect
38 | golang.org/x/text v0.23.0 // indirect
39 | google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect
40 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
41 | google.golang.org/protobuf v1.36.6 // indirect
42 | )
43 |
44 | replace go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc => go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0
45 |
46 | replace go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp => go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0
47 |
48 | replace go.opentelemetry.io/otel/log => go.opentelemetry.io/otel/log v0.8.0
49 |
50 | replace go.opentelemetry.io/otel/sdk/log => go.opentelemetry.io/otel/sdk/log v0.8.0
51 |
--------------------------------------------------------------------------------
/dagger/e2e/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "context"
5 | "fmt"
6 |
7 | "dagger/e-2-e/internal/dagger"
8 | )
9 |
10 | type E2E struct{}
11 |
12 | // Run runs the E2E tests on a Kubernetes cluster. It returns the output of the tests.
13 | // We expect a kubeconfig file that allows access to the cluster, and optionally
14 | // a service to bind to, if the cluster is not directly exposed to the dagger container running the tests.
15 | func (m *E2E) Run(
16 | ctx context.Context,
17 | // source is the directory containing the source code for the project
18 | source *dagger.Directory,
19 | // kubeconfig is the kubeconfig file to use for the tests
20 | kubeconfig *dagger.File,
21 | // svc is the Kubernetes service to bind to. It will be known as "kubernetes" in the container.
22 | // +optional
23 | svc *dagger.Service,
24 | // version of the golang image to use
25 | // +optional
26 | // +default="latest"
27 | goVersion string,
28 | ) (string, error) {
29 | goDag := dag.Go(dagger.GoOpts{Version: goVersion}).WithCgoDisabled().WithSource(source)
30 | if svc != nil {
31 | goDag = goDag.WithServiceBinding("kubernetes", svc)
32 | }
33 | return goDag.Container().
34 | WithMountedFile("/kubeconfig", kubeconfig).
35 | WithEnvVariable("KUBECONFIG", "/kubeconfig").
36 | WithExec([]string{"go", "run", "github.com/onsi/ginkgo/v2/ginkgo",
37 | "--procs=4",
38 | "--randomize-all",
39 | "--randomize-suites",
40 | "--fail-on-pending",
41 | "--fail-on-empty",
42 | "--keep-going",
43 | "--timeout=45m",
44 | "--github-output",
45 | "./test/e2e"}).Stdout(ctx)
46 | }
47 |
48 | // RunEphemeral creates a k3s cluster in dagger and then runs the E2E tests on it.
49 | // If a private registry is used, its url and the ca certificate for the registry should be provided.
50 | func (m *E2E) RunEphemeral(
51 | ctx context.Context,
52 | // source is the directory containing the source code for the project
53 | source *dagger.Directory,
54 | // registry is a private registry
55 | // +optional
56 | // +default="registry.barman-cloud-plugin:5000"
57 | registry string,
58 | // ca is the certificate authority for the registry
59 | // +optional
60 | ca *dagger.File,
61 | // name is the name of the ephemeral container
62 | // +optional
63 | // +default="e2e"
64 | name string,
65 | // version of the golang image to use
66 | // +optional
67 | // +default="latest"
68 | goVersion string,
69 | ) (string, error) {
70 | k3s := dag.K3S(name)
71 | ctr := k3s.Container()
72 | if ca != nil {
73 | ctr = ctr.WithMountedFile("/usr/local/share/ca-certificates/ca.crt", ca)
74 | }
75 | if registry != "" {
76 | ctr = ctr.WithNewFile("/registries.yaml", fmt.Sprintf(`
77 | configs:
78 | "%s":
79 | tls:
80 | ca_file: "/usr/local/share/ca-certificates/ca.crt"
81 | `, registry)).
82 | WithExec([]string{"sh", "-c", "cat /registries.yaml > /etc/rancher/k3s/registries.yaml"})
83 | }
84 |
85 | ctr, err := ctr.Sync(ctx)
86 | if err != nil {
87 | return "", err
88 | }
89 | kServer := k3s.WithContainer(ctr).Server()
90 |
91 | kServer, err = kServer.Start(ctx)
92 | if err != nil {
93 | return "", err
94 | }
95 | defer kServer.Stop(ctx)
96 |
97 | return m.Run(ctx, source, k3s.Config(), kServer, goVersion)
98 | }
99 |
--------------------------------------------------------------------------------
/dagger/gotest/.gitattributes:
--------------------------------------------------------------------------------
1 | /dagger.gen.go linguist-generated
2 | /internal/dagger/** linguist-generated
3 | /internal/querybuilder/** linguist-generated
4 | /internal/telemetry/** linguist-generated
5 |
--------------------------------------------------------------------------------
/dagger/gotest/.gitignore:
--------------------------------------------------------------------------------
1 | /dagger.gen.go
2 | /internal/dagger
3 | /internal/querybuilder
4 | /internal/telemetry
5 |
--------------------------------------------------------------------------------
/dagger/gotest/dagger.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gotest",
3 | "engineVersion": "v0.18.5",
4 | "sdk": {
5 | "source": "go"
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/dagger/gotest/go.mod:
--------------------------------------------------------------------------------
1 | module dagger/gotest
2 |
3 | go 1.23.1
4 |
5 | require (
6 | github.com/99designs/gqlgen v0.17.70
7 | github.com/Khan/genqlient v0.8.0
8 | github.com/vektah/gqlparser/v2 v2.5.23
9 | go.opentelemetry.io/otel v1.34.0
10 | go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0
11 | go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0
12 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0
13 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0
14 | go.opentelemetry.io/otel/log v0.8.0
15 | go.opentelemetry.io/otel/sdk v1.34.0
16 | go.opentelemetry.io/otel/sdk/log v0.8.0
17 | go.opentelemetry.io/otel/trace v1.34.0
18 | go.opentelemetry.io/proto/otlp v1.3.1
19 | golang.org/x/sync v0.12.0
20 | google.golang.org/grpc v1.71.0
21 | )
22 |
23 | require (
24 | github.com/cenkalti/backoff/v4 v4.3.0 // indirect
25 | github.com/go-logr/logr v1.4.2 // indirect
26 | github.com/go-logr/stdr v1.2.2 // indirect
27 | github.com/google/uuid v1.6.0 // indirect
28 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 // indirect
29 | github.com/sosodev/duration v1.3.1 // indirect
30 | go.opentelemetry.io/auto/sdk v1.1.0 // indirect
31 | go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0
32 | go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0
33 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 // indirect
34 | go.opentelemetry.io/otel/metric v1.34.0
35 | go.opentelemetry.io/otel/sdk/metric v1.34.0
36 | golang.org/x/net v0.38.0 // indirect
37 | golang.org/x/sys v0.31.0 // indirect
38 | golang.org/x/text v0.23.0 // indirect
39 | google.golang.org/genproto/googleapis/api v0.0.0-20250106144421-5f5ef82da422 // indirect
40 | google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
41 | google.golang.org/protobuf v1.36.6 // indirect
42 | )
43 |
44 | replace go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc => go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0
45 |
46 | replace go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp => go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0
47 |
48 | replace go.opentelemetry.io/otel/log => go.opentelemetry.io/otel/log v0.8.0
49 |
50 | replace go.opentelemetry.io/otel/sdk/log => go.opentelemetry.io/otel/sdk/log v0.8.0
51 |
--------------------------------------------------------------------------------
/dagger/gotest/main.go:
--------------------------------------------------------------------------------
1 | // A generated module for Gotest functions
2 | //
3 | // This module has been generated via dagger init and serves as a reference to
4 | // basic module structure as you get started with Dagger.
5 | //
6 | // Two functions have been pre-created. You can modify, delete, or add to them,
7 | // as needed. They demonstrate usage of arguments and return types using simple
8 | // echo and grep commands. The functions can be called from the dagger CLI or
9 | // from one of the SDKs.
10 | //
11 | // The first line in this comment block is a short description line and the
12 | // rest is a long description with more detail on the module's purpose or usage,
13 | // if appropriate. All modules should have a short description.
14 |
15 | package main
16 |
17 | import (
18 | "context"
19 | "fmt"
20 |
21 | "dagger/gotest/internal/dagger"
22 | )
23 |
24 | type Gotest struct {
25 | // +private
26 | Ctr *dagger.Container
27 | // +private
28 | KubeVersion string
29 | }
30 |
31 | func New(
32 | // Go version
33 | //
34 | // +optional
35 | // +default="latest"
36 | goVersion string,
37 | // setup-envtest version
38 | // +optional
39 | // +default="0.19.0"
40 | setupEnvtestVersion string,
41 | // Kubernetes version
42 | // +optional
43 | // +default="1.31.0"
44 | kubeVersion string,
45 | // Container to run the tests
46 | // +optional
47 | ctr *dagger.Container,
48 | ) *Gotest {
49 | if ctr != nil {
50 | return &Gotest{Ctr: ctr}
51 | }
52 |
53 | user := "noroot"
54 | modCachePath := fmt.Sprintf("/home/%s/go/pkg/mod", user)
55 | goCachePath := fmt.Sprintf("/home/%s/.cache/go-build", user)
56 | ctr = dag.Container().From("golang:"+goVersion).
57 | WithExec([]string{"curl", "-L",
58 | fmt.Sprintf("https://dl.k8s.io/release/v%v/bin/linux/amd64/kubectl", kubeVersion),
59 | "-o", "/usr/local/bin/kubectl"}).
60 | WithExec([]string{"chmod", "+x", "/usr/local/bin/kubectl"}).
61 | WithExec([]string{"curl", "-L",
62 | fmt.Sprintf(
63 | "https://github.com/kubernetes-sigs/controller-runtime/releases/download/v%v/setup-envtest-linux-amd64",
64 | setupEnvtestVersion),
65 | "-o", "/usr/local/bin/setup-envtest"}).
66 | WithExec([]string{"chmod", "+x", "/usr/local/bin/setup-envtest"}).
67 | WithExec([]string{"useradd", "-m", user}).
68 | WithUser(user).
69 | WithEnvVariable("CGO_ENABLED", "0").
70 | WithEnvVariable("GOMODCACHE", modCachePath).
71 | WithEnvVariable("GOCACHE", goCachePath).
72 | WithMountedCache(modCachePath, dag.CacheVolume("go-mod"),
73 | dagger.ContainerWithMountedCacheOpts{Owner: user}).
74 | WithMountedCache(goCachePath, dag.CacheVolume("go-build"),
75 | dagger.ContainerWithMountedCacheOpts{Owner: user})
76 |
77 | return &Gotest{Ctr: ctr, KubeVersion: kubeVersion}
78 | }
79 |
80 | func (m *Gotest) UnitTest(
81 | ctx context.Context,
82 | // Source directory
83 | // +required
84 | src *dagger.Directory,
85 | ) (string, error) {
86 | envtestCmd := []string{"setup-envtest", "use", "-p", "path", m.KubeVersion}
87 | return m.Ctr.WithDirectory("/src", src).
88 | // Setup envtest. There is no proper way to install it from a git release, so we use the go install command
89 | WithExec(envtestCmd).
90 | WithEnvVariable("KUBEBUILDER_ASSETS",
91 | fmt.Sprintf("/home/noroot/.local/share/kubebuilder-envtest/k8s/%v-linux-amd64", m.KubeVersion),
92 | ).
93 | WithWorkdir("/src").
94 | // Exclude the e2e tests, we don't want to run them here
95 | WithoutDirectory("/src/test/e2e").
96 | WithExec([]string{"go", "test", "./..."}).Stdout(ctx)
97 | }
98 |
--------------------------------------------------------------------------------
/hack/boilerplate.go.txt:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
--------------------------------------------------------------------------------
/hack/build-dev-image.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env sh
2 |
3 | # This script builds the images of the barman cloud plugin, to be used
4 | # to quickly test images in a development environment.
5 | #
6 | # After each run, the built images will have these names:
7 | #
8 | # - `plugin-barman-cloud:dev`
9 | # - `plugin-barman-cloud-sidecar:dev`
10 |
11 | set -eu
12 |
13 | docker build -t plugin-barman-cloud:dev --file containers/Dockerfile.plugin .
14 | docker build -t plugin-barman-cloud-sidecar:dev --file containers/Dockerfile.sidecar .
15 |
--------------------------------------------------------------------------------
/hack/crd-gen-refs/config.yaml:
--------------------------------------------------------------------------------
1 | processor:
2 | ignoreGroupVersions:
3 | - "GVK"
4 | customMarkers:
5 | - name: "optional"
6 | target: field
7 | ignoreFields:
8 | # - "status$"
9 | - "TypeMeta$"
10 | ignoreTypes:
11 | - "ObjectStoreList$"
12 |
13 | render:
14 | # Version of Kubernetes to use when generating links to Kubernetes API documentation.
15 | # renovate: datasource=git-refs depName=kubernetes/kubernetes lookupName=https://github.com/kubernetes/kubernetes
16 | kubernetesVersion: 1.32
17 | knownTypes:
18 | - name: BarmanObjectStoreConfiguration
19 | package: github.com/cloudnative-pg/barman-cloud/pkg/api
20 | link: https://pkg.go.dev/github.com/cloudnative-pg/barman-cloud/pkg/api#BarmanObjectStoreConfiguration
21 |
--------------------------------------------------------------------------------
/hack/crd-gen-refs/markdown/gv_details.tpl:
--------------------------------------------------------------------------------
1 | {{- define "gvDetails" -}}
2 | {{- $gv := . -}}
3 |
4 | ## {{ $gv.GroupVersionString }}
5 |
6 | {{ $gv.Doc }}
7 |
8 | {{- if $gv.Kinds }}
9 | ### Resource Types
10 | {{- range $gv.SortedKinds }}
11 | - {{ $gv.TypeForKind . | markdownRenderTypeLink }}
12 | {{- end }}
13 | {{ end }}
14 |
15 | {{ range $gv.SortedTypes }}
16 | {{ template "type" . }}
17 | {{ end }}
18 |
19 | {{- end -}}
20 |
--------------------------------------------------------------------------------
/hack/crd-gen-refs/markdown/gv_list.tpl:
--------------------------------------------------------------------------------
1 | {{- define "gvList" -}}
2 | {{- $groupVersions := . -}}
3 |
4 | # API Reference
5 |
6 | ## Packages
7 | {{- range $groupVersions }}
8 | - {{ markdownRenderGVLink . }}
9 | {{- end }}
10 |
11 | {{ range $groupVersions }}
12 | {{ template "gvDetails" . }}
13 | {{ end }}
14 |
15 | {{- end -}}
16 |
--------------------------------------------------------------------------------
/hack/crd-gen-refs/markdown/type.tpl:
--------------------------------------------------------------------------------
1 | {{- define "type" -}}
2 | {{- $type := . -}}
3 | {{- if markdownShouldRenderType $type -}}
4 |
5 | #### {{ $type.Name }}
6 |
7 | {{ if $type.IsAlias }}_Underlying type:_ _{{ markdownRenderTypeLink $type.UnderlyingType }}_{{ end }}
8 |
9 | {{ $type.Doc }}
10 |
11 | {{ if $type.Validation -}}
12 | _Validation:_
13 | {{- range $type.Validation }}
14 | - {{ . }}
15 | {{- end }}
16 | {{- end }}
17 |
18 | {{ if $type.References -}}
19 | _Appears in:_
20 | {{- range $type.SortedReferences }}
21 | - {{ markdownRenderTypeLink . }}
22 | {{- end }}
23 | {{- end }}
24 |
25 | {{ if $type.Members -}}
26 | | Field | Description | Required | Default | Validation |
27 | | --- | --- | --- | --- | --- |
28 | {{ if $type.GVK -}}
29 | | `apiVersion` _string_ | `{{ $type.GVK.Group }}/{{ $type.GVK.Version }}` | True | | |
30 | | `kind` _string_ | `{{ $type.GVK.Kind }}` | True | | |
31 | {{ end -}}
32 |
33 | {{ range $type.Members -}}
34 | | `{{ .Name }}` _{{ markdownRenderType .Type }}_ | {{ template "type_members" . }} | {{ if not .Markers.optional -}}True{{- end }} | {{ markdownRenderDefault .Default }} | {{ range .Validation -}} {{ markdownRenderFieldDoc . }} {{ end }} |
35 | {{ end -}}
36 |
37 | {{ end -}}
38 |
39 | {{ if $type.EnumValues -}}
40 | | Field | Description |
41 | | --- | --- |
42 | {{ range $type.EnumValues -}}
43 | | `{{ .Name }}` | {{ markdownRenderFieldDoc .Doc }} |
44 | {{ end -}}
45 | {{ end -}}
46 |
47 |
48 | {{- end -}}
49 | {{- end -}}
50 |
--------------------------------------------------------------------------------
/hack/crd-gen-refs/markdown/type_members.tpl:
--------------------------------------------------------------------------------
1 | {{- define "type_members" -}}
2 | {{- $field := . -}}
3 | {{- if eq $field.Name "metadata" -}}
4 | Refer to Kubernetes API documentation for fields of `metadata`.
5 | {{- else -}}
6 | {{ markdownRenderFieldDoc $field.Doc }}
7 | {{- end -}}
8 | {{- end -}}
9 |
--------------------------------------------------------------------------------
/hack/examples/backup-example.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: postgresql.cnpg.io/v1
2 | kind: Backup
3 | metadata:
4 | name: backup-example
5 | spec:
6 | method: plugin
7 |
8 | cluster:
9 | name: cluster-example
10 |
11 | pluginConfiguration:
12 | name: barman-cloud.cloudnative-pg.io
--------------------------------------------------------------------------------
/hack/examples/cluster-example-legacy.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: postgresql.cnpg.io/v1
2 | kind: Cluster
3 | metadata:
4 | name: cluster-example
5 | spec:
6 | instances: 3
7 |
8 | backup:
9 | barmanObjectStore:
10 | endpointCA:
11 | name: minio-server-tls
12 | key: tls.crt
13 | destinationPath: s3://backups/
14 | endpointURL: https://minio:9000
15 | s3Credentials:
16 | accessKeyId:
17 | name: minio
18 | key: ACCESS_KEY_ID
19 | secretAccessKey:
20 | name: minio
21 | key: ACCESS_SECRET_KEY
22 | wal:
23 | compression: gzip
24 | data:
25 | additionalCommandArgs:
26 | - "--min-chunk-size=5MB"
27 | - "--read-timeout=60"
28 | - "-vv"
29 |
30 | storage:
31 | size: 1Gi
32 |
--------------------------------------------------------------------------------
/hack/examples/cluster-example.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: postgresql.cnpg.io/v1
2 | kind: Cluster
3 | metadata:
4 | name: cluster-example
5 | spec:
6 | instances: 3
7 | plugins:
8 | - name: barman-cloud.cloudnative-pg.io
9 | isWALArchiver: true
10 | parameters:
11 | barmanObjectName: minio-store
12 |
13 | storage:
14 | size: 1Gi
15 |
--------------------------------------------------------------------------------
/hack/examples/cluster-replica-log-shipping.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: postgresql.cnpg.io/v1
2 | kind: Cluster
3 | metadata:
4 | name: cluster-replica
5 | spec:
6 | instances: 3
7 | bootstrap:
8 | recovery:
9 | source: source
10 | replica:
11 | enabled: true
12 | source: source
13 | externalClusters:
14 | - name: source
15 | plugin:
16 | name: barman-cloud.cloudnative-pg.io
17 | parameters:
18 | barmanObjectName: minio-store
19 | serverName: cluster-example
20 | storage:
21 | size: 1Gi
22 |
23 |
--------------------------------------------------------------------------------
/hack/examples/cluster-restore-archive.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: postgresql.cnpg.io/v1
2 | kind: Cluster
3 | metadata:
4 | name: cluster-restore
5 | spec:
6 | instances: 3
7 | imagePullPolicy: IfNotPresent
8 |
9 | bootstrap:
10 | recovery:
11 | source: source
12 |
13 | plugins:
14 | - name: barman-cloud.cloudnative-pg.io
15 | isWALArchiver: true
16 | parameters:
17 | barmanObjectName: minio-store-bis
18 |
19 | externalClusters:
20 | - name: source
21 | plugin:
22 | name: barman-cloud.cloudnative-pg.io
23 | parameters:
24 | barmanObjectName: minio-store
25 | serverName: cluster-example
26 |
27 | storage:
28 | size: 1Gi
29 |
--------------------------------------------------------------------------------
/hack/examples/cluster-restore.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: postgresql.cnpg.io/v1
2 | kind: Cluster
3 | metadata:
4 | name: cluster-restore
5 | spec:
6 | instances: 3
7 | imagePullPolicy: IfNotPresent
8 |
9 | bootstrap:
10 | recovery:
11 | source: source
12 |
13 | externalClusters:
14 | - name: source
15 | plugin:
16 | name: barman-cloud.cloudnative-pg.io
17 | parameters:
18 | barmanObjectName: minio-store
19 | serverName: cluster-example
20 |
21 | storage:
22 | size: 1Gi
23 |
--------------------------------------------------------------------------------
/hack/examples/minio-store.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: barmancloud.cnpg.io/v1
2 | kind: ObjectStore
3 | metadata:
4 | name: minio-store
5 | spec:
6 | retentionPolicy: "1m"
7 | instanceSidecarConfiguration:
8 | retentionPolicyIntervalSeconds: 1800
9 | resources:
10 | requests:
11 | memory: "64Mi"
12 | cpu: "250m"
13 | limits:
14 | memory: "512Mi"
15 | cpu: "500m"
16 | configuration:
17 | endpointCA:
18 | name: minio-server-tls
19 | key: tls.crt
20 | destinationPath: s3://backups/
21 | endpointURL: https://minio:9000
22 | s3Credentials:
23 | accessKeyId:
24 | name: minio
25 | key: ACCESS_KEY_ID
26 | secretAccessKey:
27 | name: minio
28 | key: ACCESS_SECRET_KEY
29 | wal:
30 | compression: gzip
31 | maxParallel: 8
32 | data:
33 | additionalCommandArgs:
34 | - "--min-chunk-size=5MB"
35 | - "--read-timeout=60"
36 | - "-vv"
37 |
--------------------------------------------------------------------------------
/hack/kind-config.yaml:
--------------------------------------------------------------------------------
1 | # Kind configuration file for running e2e tests
2 | # Certificates must be mounted on each node because the registry is using TLS
3 |
4 | kind: Cluster
5 | apiVersion: kind.x-k8s.io/v1alpha4
6 | nodes:
7 | - role: control-plane
8 | extraMounts:
9 | - hostPath: certs/ca.pem
10 | containerPath: /usr/local/share/ca-certificates/ca.crt
11 | readOnly: true
12 |
--------------------------------------------------------------------------------
/hack/minio/minio-client.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Pod
3 | metadata:
4 | labels:
5 | run: mc
6 | name: mc
7 | spec:
8 | containers:
9 | - env:
10 | - name: MC_HOST_minio
11 | value: http://chooJeiroroo2noquomei2uuceisheth:ongeiqueitohL0queeLohkiur2quaing@minio:9000
12 | image: minio/mc
13 | name: mc
14 | resources: {}
15 | # Keep the pod up to exec stuff on it
16 | command:
17 | - sleep
18 | - "3600"
19 | dnsPolicy: ClusterFirst
20 | restartPolicy: Always
21 |
--------------------------------------------------------------------------------
/hack/minio/minio-deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: minio
5 | labels:
6 | app: minio
7 | spec:
8 | replicas: 1
9 | selector:
10 | matchLabels:
11 | app: minio
12 | template:
13 | metadata:
14 | labels:
15 | app: minio
16 | spec:
17 | containers:
18 | - name: minio
19 | image: minio/minio
20 | ports:
21 | - containerPort: 9000
22 | volumeMounts:
23 | - mountPath: /data
24 | name: data
25 | args:
26 | - server
27 | - /data
28 | env:
29 | - name: MINIO_ACCESS_KEY
30 | valueFrom:
31 | secretKeyRef:
32 | name: minio
33 | key: ACCESS_KEY_ID
34 | - name: MINIO_SECRET_KEY
35 | valueFrom:
36 | secretKeyRef:
37 | name: minio
38 | key: ACCESS_SECRET_KEY
39 | volumes:
40 | - name: data
41 | persistentVolumeClaim:
42 | claimName: minio
43 |
--------------------------------------------------------------------------------
/hack/minio/minio-pvc.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: PersistentVolumeClaim
3 | metadata:
4 | name: minio
5 | spec:
6 | accessModes:
7 | - ReadWriteOnce
8 | volumeMode: Filesystem
9 | resources:
10 | requests:
11 | storage: 1Gi
12 |
--------------------------------------------------------------------------------
/hack/minio/minio-secret.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | data:
3 | ACCESS_KEY_ID: Y2hvb0plaXJvcm9vMm5vcXVvbWVpMnV1Y2Vpc2hldGg=
4 | ACCESS_SECRET_KEY: b25nZWlxdWVpdG9oTDBxdWVlTG9oa2l1cjJxdWFpbmc=
5 | kind: Secret
6 | metadata:
7 | name: minio
8 |
--------------------------------------------------------------------------------
/hack/minio/minio-service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: minio
5 | spec:
6 | selector:
7 | app: minio
8 | ports:
9 | - protocol: TCP
10 | port: 9000
11 | targetPort: 9000
12 |
--------------------------------------------------------------------------------
/internal/cmd/healthcheck/doc.go:
--------------------------------------------------------------------------------
1 | // Package healthcheck contains the logic to execute an healthcheck on the plugin through a command
2 | package healthcheck
3 |
--------------------------------------------------------------------------------
/internal/cmd/healthcheck/main.go:
--------------------------------------------------------------------------------
1 | package healthcheck
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "path"
7 |
8 | "github.com/cloudnative-pg/machinery/pkg/log"
9 | "github.com/spf13/cobra"
10 | "google.golang.org/grpc"
11 | "google.golang.org/grpc/credentials/insecure"
12 | "google.golang.org/grpc/health/grpc_health_v1"
13 |
14 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata"
15 | )
16 |
17 | // NewCmd returns the healthcheck command
18 | func NewCmd() *cobra.Command {
19 | cmd := &cobra.Command{
20 | Use: "healthcheck",
21 | Short: "healthcheck commands",
22 | }
23 |
24 | cmd.AddCommand(unixHealthCheck())
25 |
26 | return cmd
27 | }
28 |
29 | func unixHealthCheck() *cobra.Command {
30 | cmd := &cobra.Command{
31 | Use: "unix",
32 | Short: "executes the health check command on unix:///plugins/barman-cloud.cloudnative-pg.io",
33 | RunE: func(cmd *cobra.Command, _ []string) error {
34 | dialPath := fmt.Sprintf("unix://%s", path.Join("/plugins", metadata.PluginName))
35 | cli, cliErr := grpc.NewClient(dialPath, grpc.WithTransportCredentials(insecure.NewCredentials()))
36 | if cliErr != nil {
37 | log.Error(cliErr, "while building the client")
38 | return cliErr
39 | }
40 |
41 | healthCli := grpc_health_v1.NewHealthClient(cli)
42 | res, healthErr := healthCli.Check(
43 | cmd.Context(),
44 | &grpc_health_v1.HealthCheckRequest{},
45 | )
46 | if healthErr != nil {
47 | log.Error(healthErr, "while executing the healthcheck call")
48 | return healthErr
49 | }
50 |
51 | if res.Status == grpc_health_v1.HealthCheckResponse_SERVING {
52 | log.Trace("healthcheck response OK")
53 | os.Exit(0)
54 | return nil
55 | }
56 |
57 | log.Error(fmt.Errorf("unexpected healthcheck status: %v", res.Status),
58 | "while processing healthcheck response")
59 |
60 | // exit code 1 is returned when we exit from the function with an error
61 | switch res.Status {
62 | case grpc_health_v1.HealthCheckResponse_UNKNOWN:
63 | os.Exit(2)
64 | case grpc_health_v1.HealthCheckResponse_NOT_SERVING:
65 | os.Exit(3)
66 | default:
67 | os.Exit(125)
68 | }
69 |
70 | return nil
71 | },
72 | }
73 |
74 | return cmd
75 | }
76 |
--------------------------------------------------------------------------------
/internal/cmd/instance/main.go:
--------------------------------------------------------------------------------
1 | // Package instance is the entrypoint of instance plugin
2 | package instance
3 |
4 | import (
5 | "fmt"
6 |
7 | "github.com/spf13/cobra"
8 | "github.com/spf13/viper"
9 |
10 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/instance"
11 | )
12 |
13 | // NewCmd creates a new instance command
14 | func NewCmd() *cobra.Command {
15 | cmd := &cobra.Command{
16 | Use: "instance",
17 | Short: "Starts the Barman Cloud CNPG-I sidecar plugin",
18 | RunE: func(cmd *cobra.Command, _ []string) error {
19 | requiredSettings := []string{
20 | "namespace",
21 | "cluster-name",
22 | "pod-name",
23 | "spool-directory",
24 | }
25 |
26 | for _, k := range requiredSettings {
27 | if len(viper.GetString(k)) == 0 {
28 | return fmt.Errorf("missing required %s setting", k)
29 | }
30 | }
31 |
32 | return instance.Start(cmd.Context())
33 | },
34 | }
35 |
36 | _ = viper.BindEnv("namespace", "NAMESPACE")
37 | _ = viper.BindEnv("cluster-name", "CLUSTER_NAME")
38 | _ = viper.BindEnv("pod-name", "POD_NAME")
39 | _ = viper.BindEnv("pgdata", "PGDATA")
40 | _ = viper.BindEnv("spool-directory", "SPOOL_DIRECTORY")
41 | _ = viper.BindEnv("custom-cnpg-group", "CUSTOM_CNPG_GROUP")
42 | _ = viper.BindEnv("custom-cnpg-version", "CUSTOM_CNPG_VERSIONXS")
43 |
44 | return cmd
45 | }
46 |
--------------------------------------------------------------------------------
/internal/cmd/operator/main.go:
--------------------------------------------------------------------------------
1 | // Package operator is the entrypoint of operator plugin
2 | package operator
3 |
4 | import (
5 | "fmt"
6 |
7 | "github.com/spf13/cobra"
8 | "github.com/spf13/viper"
9 |
10 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator"
11 | )
12 |
13 | // NewCmd creates a new operator command
14 | func NewCmd() *cobra.Command {
15 | cmd := &cobra.Command{
16 | Use: "operator",
17 | Short: "Starts the BarmanObjectStore reconciler and the Barman Cloud CNPG-i plugin",
18 | RunE: func(cmd *cobra.Command, _ []string) error {
19 | if len(viper.GetString("sidecar-image")) == 0 {
20 | return fmt.Errorf("missing required SIDECAR_IMAGE environment variable")
21 | }
22 |
23 | return operator.Start(cmd.Context())
24 | },
25 | PersistentPreRunE: func(_ *cobra.Command, _ []string) error {
26 | return nil
27 | },
28 | }
29 |
30 | cmd.Flags().String("metrics-bind-address", "0", "The address the metrics endpoint binds to. "+
31 | "Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.")
32 | _ = viper.BindPFlag("metrics-bind-address", cmd.Flags().Lookup("metrics-bind-address"))
33 |
34 | cmd.Flags().String("health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
35 | _ = viper.BindPFlag("health-probe-bind-address", cmd.Flags().Lookup("health-probe-bind-address"))
36 |
37 | cmd.Flags().Bool("leader-elect", false,
38 | "Enable leader election for controller manager. "+
39 | "Enabling this will ensure there is only one active controller manager.")
40 | _ = viper.BindPFlag("leader-elect", cmd.Flags().Lookup("leader-elect"))
41 |
42 | cmd.Flags().Bool("metrics-secure", true,
43 | "If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.")
44 | _ = viper.BindPFlag("metrics-secure", cmd.Flags().Lookup("metrics-secure"))
45 |
46 | cmd.Flags().Bool("enable-http2", false,
47 | "If set, HTTP/2 will be enabled for the metrics and webhook servers")
48 | _ = viper.BindPFlag("enable-http2", cmd.Flags().Lookup("enable-http2"))
49 |
50 | cmd.Flags().String(
51 | "plugin-path",
52 | "",
53 | "The plugins socket path",
54 | )
55 | _ = viper.BindPFlag("plugin-path", cmd.Flags().Lookup("plugin-path"))
56 |
57 | cmd.Flags().String(
58 | "server-cert",
59 | "",
60 | "The public key to be used for the server process",
61 | )
62 | _ = viper.BindPFlag("server-cert", cmd.Flags().Lookup("server-cert"))
63 |
64 | cmd.Flags().String(
65 | "server-key",
66 | "",
67 | "The key to be used for the server process",
68 | )
69 | _ = viper.BindPFlag("server-key", cmd.Flags().Lookup("server-key"))
70 |
71 | cmd.Flags().String(
72 | "client-cert",
73 | "",
74 | "The client public key to verify the connection",
75 | )
76 | _ = viper.BindPFlag("client-cert", cmd.Flags().Lookup("client-cert"))
77 |
78 | cmd.Flags().String(
79 | "server-address",
80 | "",
81 | "The address where to listen (i.e. 0:9090)",
82 | )
83 | _ = viper.BindPFlag("server-address", cmd.Flags().Lookup("server-address"))
84 |
85 | _ = viper.BindEnv("sidecar-image", "SIDECAR_IMAGE")
86 |
87 | return cmd
88 | }
89 |
--------------------------------------------------------------------------------
/internal/cmd/restore/main.go:
--------------------------------------------------------------------------------
1 | // Package restore is the entrypoint of restore capabilities
2 | package restore
3 |
4 | import (
5 | "fmt"
6 |
7 | "github.com/spf13/cobra"
8 | "github.com/spf13/viper"
9 |
10 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/restore"
11 | )
12 |
13 | // NewCmd creates the "restore" subcommand
14 | func NewCmd() *cobra.Command {
15 | cobra.EnableTraverseRunHooks = true
16 |
17 | cmd := &cobra.Command{
18 | Use: "restore",
19 | Short: "Starts the Barman Cloud CNPG-I sidecar plugin",
20 | RunE: func(cmd *cobra.Command, _ []string) error {
21 | requiredSettings := []string{
22 | "namespace",
23 | "pod-name",
24 | "spool-directory",
25 | }
26 |
27 | for _, k := range requiredSettings {
28 | if len(viper.GetString(k)) == 0 {
29 | return fmt.Errorf("missing required %s setting", k)
30 | }
31 | }
32 |
33 | return restore.Start(cmd.Context())
34 | },
35 | }
36 |
37 | _ = viper.BindEnv("namespace", "NAMESPACE")
38 | _ = viper.BindEnv("pod-name", "POD_NAME")
39 | _ = viper.BindEnv("pgdata", "PGDATA")
40 | _ = viper.BindEnv("spool-directory", "SPOOL_DIRECTORY")
41 |
42 | return cmd
43 | }
44 |
--------------------------------------------------------------------------------
/internal/cnpgi/common/common.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "fmt"
5 | "path"
6 | "strings"
7 |
8 | barmanapi "github.com/cloudnative-pg/barman-cloud/pkg/api"
9 |
10 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata"
11 | )
12 |
13 | // TODO: refactor.
14 | const (
15 | // ScratchDataDirectory is the directory to be used for scratch data.
16 | ScratchDataDirectory = "/controller"
17 |
18 | // CertificatesDir location to store the certificates.
19 | CertificatesDir = ScratchDataDirectory + "/certificates/"
20 |
21 | // BarmanBackupEndpointCACertificateLocation is the location where the barman endpoint
22 | // CA certificate is stored.
23 | BarmanBackupEndpointCACertificateLocation = CertificatesDir + BarmanBackupEndpointCACertificateFileName
24 |
25 | // BarmanBackupEndpointCACertificateFileName is the name of the file in which the barman endpoint
26 | // CA certificate for backups is stored.
27 | BarmanBackupEndpointCACertificateFileName = "backup-" + BarmanEndpointCACertificateFileName
28 |
29 | // BarmanRestoreEndpointCACertificateFileName is the name of the file in which the barman endpoint
30 | // CA certificate for restores is stored.
31 | BarmanRestoreEndpointCACertificateFileName = "restore-" + BarmanEndpointCACertificateFileName
32 |
33 | // BarmanEndpointCACertificateFileName is the name of the file in which the barman endpoint
34 | // CA certificate is stored.
35 | BarmanEndpointCACertificateFileName = "barman-ca.crt"
36 | )
37 |
38 | // GetRestoreCABundleEnv gets the enveronment variables to be used when custom
39 | // Object Store CA is present
40 | func GetRestoreCABundleEnv(configuration *barmanapi.BarmanObjectStoreConfiguration) []string {
41 | var env []string
42 |
43 | if configuration.EndpointCA != nil && configuration.AWS != nil {
44 | env = append(env, fmt.Sprintf("AWS_CA_BUNDLE=%s", BarmanBackupEndpointCACertificateLocation))
45 | } else if configuration.EndpointCA != nil && configuration.Azure != nil {
46 | env = append(env, fmt.Sprintf("REQUESTS_CA_BUNDLE=%s", BarmanBackupEndpointCACertificateLocation))
47 | }
48 | return env
49 | }
50 |
51 | // MergeEnv merges all the values inside incomingEnv into env.
52 | func MergeEnv(env []string, incomingEnv []string) []string {
53 | result := make([]string, len(env), len(env)+len(incomingEnv))
54 | copy(result, env)
55 |
56 | for _, incomingItem := range incomingEnv {
57 | incomingKV := strings.SplitAfterN(incomingItem, "=", 2)
58 | if len(incomingKV) != 2 {
59 | continue
60 | }
61 |
62 | found := false
63 | for idx, item := range result {
64 | if strings.HasPrefix(item, incomingKV[0]) {
65 | result[idx] = incomingItem
66 | found = true
67 | }
68 | }
69 | if !found {
70 | result = append(result, incomingItem)
71 | }
72 | }
73 |
74 | return result
75 | }
76 |
77 | // BuildCertificateFilePath builds the path to the barman objectStore certificate
78 | func BuildCertificateFilePath(objectStoreName string) string {
79 | return path.Join(metadata.BarmanCertificatesPath, objectStoreName, metadata.BarmanCertificatesFileName)
80 | }
81 |
--------------------------------------------------------------------------------
/internal/cnpgi/common/doc.go:
--------------------------------------------------------------------------------
1 | // Package common contains reusable structs and methods for CNPGI plugins.
2 | package common
3 |
--------------------------------------------------------------------------------
/internal/cnpgi/common/errors.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | // walNotFoundError is raised when a WAL file has not been found in the object store
4 | type walNotFoundError struct{}
5 |
6 | func newWALNotFoundError() *walNotFoundError { return &walNotFoundError{} }
7 |
8 | // ShouldPrintStackTrace tells whether the sidecar log stream should contain the stack trace
9 | func (e walNotFoundError) ShouldPrintStackTrace() bool {
10 | return false
11 | }
12 |
13 | // Error implements the error interface
14 | func (e walNotFoundError) Error() string {
15 | return "WAL file not found"
16 | }
17 |
--------------------------------------------------------------------------------
/internal/cnpgi/common/health.go:
--------------------------------------------------------------------------------
1 | package common
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/cloudnative-pg/machinery/pkg/log"
7 | "google.golang.org/grpc"
8 | "google.golang.org/grpc/health/grpc_health_v1"
9 | )
10 |
11 | // AddHealthCheck adds a health check service to the gRPC server with the tag 'plugin-barman-cloud'
12 | func AddHealthCheck(server *grpc.Server) {
13 | grpc_health_v1.RegisterHealthServer(server, &healthServer{}) // replaces default registration
14 | }
15 |
16 | type healthServer struct {
17 | grpc_health_v1.UnimplementedHealthServer
18 | }
19 |
20 | // Check is the response handle for the healthcheck request
21 | func (h healthServer) Check(
22 | ctx context.Context,
23 | _ *grpc_health_v1.HealthCheckRequest,
24 | ) (*grpc_health_v1.HealthCheckResponse, error) {
25 | contextLogger := log.FromContext(ctx)
26 | contextLogger.Trace("serving health check response")
27 | return &grpc_health_v1.HealthCheckResponse{Status: grpc_health_v1.HealthCheckResponse_SERVING}, nil
28 | }
29 |
--------------------------------------------------------------------------------
/internal/cnpgi/instance/doc.go:
--------------------------------------------------------------------------------
1 | // Package instance implements the capabilities used by the operator sidecar
2 | package instance
3 |
--------------------------------------------------------------------------------
/internal/cnpgi/instance/identity.go:
--------------------------------------------------------------------------------
1 | package instance
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/cloudnative-pg/cnpg-i/pkg/identity"
7 | "sigs.k8s.io/controller-runtime/pkg/client"
8 |
9 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata"
10 | )
11 |
12 | // IdentityImplementation implements IdentityServer
13 | type IdentityImplementation struct {
14 | identity.UnimplementedIdentityServer
15 | Client client.Client
16 | }
17 |
18 | // GetPluginMetadata implements IdentityServer
19 | func (i IdentityImplementation) GetPluginMetadata(
20 | _ context.Context,
21 | _ *identity.GetPluginMetadataRequest,
22 | ) (*identity.GetPluginMetadataResponse, error) {
23 | return &metadata.Data, nil
24 | }
25 |
26 | // GetPluginCapabilities implements IdentityServer
27 | func (i IdentityImplementation) GetPluginCapabilities(
28 | _ context.Context,
29 | _ *identity.GetPluginCapabilitiesRequest,
30 | ) (*identity.GetPluginCapabilitiesResponse, error) {
31 | return &identity.GetPluginCapabilitiesResponse{
32 | Capabilities: []*identity.PluginCapability{
33 | {
34 | Type: &identity.PluginCapability_Service_{
35 | Service: &identity.PluginCapability_Service{
36 | Type: identity.PluginCapability_Service_TYPE_WAL_SERVICE,
37 | },
38 | },
39 | },
40 | {
41 | Type: &identity.PluginCapability_Service_{
42 | Service: &identity.PluginCapability_Service{
43 | Type: identity.PluginCapability_Service_TYPE_BACKUP_SERVICE,
44 | },
45 | },
46 | },
47 | },
48 | }, nil
49 | }
50 |
51 | // Probe implements IdentityServer
52 | func (i IdentityImplementation) Probe(
53 | _ context.Context,
54 | _ *identity.ProbeRequest,
55 | ) (*identity.ProbeResponse, error) {
56 | return &identity.ProbeResponse{
57 | Ready: true,
58 | }, nil
59 | }
60 |
--------------------------------------------------------------------------------
/internal/cnpgi/instance/internal/client/client_test.go:
--------------------------------------------------------------------------------
1 | package client
2 |
3 | import (
4 | "time"
5 |
6 | corev1 "k8s.io/api/core/v1"
7 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
8 | "k8s.io/apimachinery/pkg/runtime"
9 | "sigs.k8s.io/controller-runtime/pkg/client"
10 | "sigs.k8s.io/controller-runtime/pkg/client/fake"
11 |
12 | v1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
13 |
14 | . "github.com/onsi/ginkgo/v2"
15 | . "github.com/onsi/gomega"
16 | )
17 |
18 | var scheme = buildScheme()
19 |
20 | func buildScheme() *runtime.Scheme {
21 | scheme := runtime.NewScheme()
22 | _ = corev1.AddToScheme(scheme)
23 | _ = v1.AddToScheme(scheme)
24 |
25 | return scheme
26 | }
27 |
28 | var _ = Describe("ExtendedClient Get", func() {
29 | var (
30 | extendedClient *ExtendedClient
31 | secretInClient *corev1.Secret
32 | objectStore *v1.ObjectStore
33 | )
34 |
35 | BeforeEach(func() {
36 | secretInClient = &corev1.Secret{
37 | ObjectMeta: metav1.ObjectMeta{
38 | Namespace: "default",
39 | Name: "test-secret",
40 | },
41 | }
42 | objectStore = &v1.ObjectStore{
43 | ObjectMeta: metav1.ObjectMeta{
44 | Namespace: "default",
45 | Name: "test-object-store",
46 | },
47 | Spec: v1.ObjectStoreSpec{},
48 | }
49 |
50 | baseClient := fake.NewClientBuilder().
51 | WithScheme(scheme).
52 | WithObjects(secretInClient, objectStore).Build()
53 | extendedClient = NewExtendedClient(baseClient).(*ExtendedClient)
54 | })
55 |
56 | It("returns secret from cache if not expired", func(ctx SpecContext) {
57 | secretNotInClient := &corev1.Secret{
58 | ObjectMeta: metav1.ObjectMeta{
59 | Namespace: "default",
60 | Name: "test-secret-not-in-client",
61 | },
62 | }
63 |
64 | // manually add the secret to the cache, this is not present in the fake client so we are sure it is from the
65 | // cache
66 | extendedClient.cachedObjects = []cachedEntry{
67 | {
68 | entry: secretNotInClient,
69 | fetchUnixTime: time.Now().Unix(),
70 | },
71 | }
72 |
73 | err := extendedClient.Get(ctx, client.ObjectKeyFromObject(secretNotInClient), secretInClient)
74 | Expect(err).NotTo(HaveOccurred())
75 | Expect(secretNotInClient).To(Equal(extendedClient.cachedObjects[0].entry))
76 | })
77 |
78 | It("fetches secret from base client if cache is expired", func(ctx SpecContext) {
79 | extendedClient.cachedObjects = []cachedEntry{
80 | {
81 | entry: secretInClient.DeepCopy(),
82 | fetchUnixTime: time.Now().Add(-2 * time.Minute).Unix(),
83 | },
84 | }
85 |
86 | err := extendedClient.Get(ctx, client.ObjectKeyFromObject(secretInClient), secretInClient)
87 | Expect(err).NotTo(HaveOccurred())
88 | })
89 |
90 | It("fetches secret from base client if not in cache", func(ctx SpecContext) {
91 | err := extendedClient.Get(ctx, client.ObjectKeyFromObject(secretInClient), secretInClient)
92 | Expect(err).NotTo(HaveOccurred())
93 | })
94 |
95 | It("does not cache non-secret objects", func(ctx SpecContext) {
96 | configMap := &corev1.ConfigMap{
97 | ObjectMeta: metav1.ObjectMeta{
98 | Namespace: "default",
99 | Name: "test-configmap",
100 | },
101 | }
102 | err := extendedClient.Create(ctx, configMap)
103 | Expect(err).ToNot(HaveOccurred())
104 |
105 | err = extendedClient.Get(ctx, client.ObjectKeyFromObject(configMap), configMap)
106 | Expect(err).NotTo(HaveOccurred())
107 | Expect(extendedClient.cachedObjects).To(BeEmpty())
108 | })
109 | })
110 |
--------------------------------------------------------------------------------
/internal/cnpgi/instance/internal/client/doc.go:
--------------------------------------------------------------------------------
1 | // Package client provides an extended client that is capable of caching multiple secrets without relying on
2 | // informers
3 | package client
4 |
--------------------------------------------------------------------------------
/internal/cnpgi/instance/internal/client/suite_test.go:
--------------------------------------------------------------------------------
1 | package client_test
2 |
3 | import (
4 | "testing"
5 |
6 | . "github.com/onsi/ginkgo/v2"
7 | . "github.com/onsi/gomega"
8 | )
9 |
10 | func TestClient(t *testing.T) {
11 | RegisterFailHandler(Fail)
12 | RunSpecs(t, "Client Suite")
13 | }
14 |
--------------------------------------------------------------------------------
/internal/cnpgi/instance/start.go:
--------------------------------------------------------------------------------
1 | package instance
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/cloudnative-pg/cnpg-i-machinery/pkg/pluginhelper/http"
7 | "github.com/cloudnative-pg/cnpg-i/pkg/backup"
8 | "github.com/cloudnative-pg/cnpg-i/pkg/wal"
9 | "google.golang.org/grpc"
10 | "sigs.k8s.io/controller-runtime/pkg/client"
11 |
12 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/common"
13 | )
14 |
15 | // CNPGI is the implementation of the PostgreSQL sidecar
16 | type CNPGI struct {
17 | Client client.Client
18 | PGDataPath string
19 | PGWALPath string
20 | SpoolDirectory string
21 | // mutually exclusive with serverAddress
22 | PluginPath string
23 | InstanceName string
24 | }
25 |
26 | // Start starts the GRPC service
27 | func (c *CNPGI) Start(ctx context.Context) error {
28 | enrich := func(server *grpc.Server) error {
29 | wal.RegisterWALServer(server, common.WALServiceImplementation{
30 | InstanceName: c.InstanceName,
31 | Client: c.Client,
32 | SpoolDirectory: c.SpoolDirectory,
33 | PGDataPath: c.PGDataPath,
34 | PGWALPath: c.PGWALPath,
35 | })
36 | backup.RegisterBackupServer(server, BackupServiceImplementation{
37 | Client: c.Client,
38 | InstanceName: c.InstanceName,
39 | })
40 | common.AddHealthCheck(server)
41 | return nil
42 | }
43 |
44 | srv := http.Server{
45 | IdentityImpl: IdentityImplementation{
46 | Client: c.Client,
47 | },
48 | Enrichers: []http.ServerEnricher{enrich},
49 | PluginPath: c.PluginPath,
50 | }
51 |
52 | return srv.Start(ctx)
53 | }
54 |
--------------------------------------------------------------------------------
/internal/cnpgi/instance/types.go:
--------------------------------------------------------------------------------
1 | package instance
2 |
3 | import (
4 | "strconv"
5 |
6 | "k8s.io/apimachinery/pkg/types"
7 |
8 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata"
9 | )
10 |
11 | type backupResultMetadata struct {
12 | timeline string
13 | version string
14 | name string
15 | displayName string
16 | clusterUID string
17 | pluginName string
18 | }
19 |
20 | func (b backupResultMetadata) toMap() map[string]string {
21 | return map[string]string{
22 | "timeline": b.timeline,
23 | "version": b.version,
24 | "name": b.name,
25 | "displayName": b.displayName,
26 | "clusterUID": b.clusterUID,
27 | "pluginName": b.pluginName,
28 | }
29 | }
30 |
31 | func newBackupResultMetadata(clusterUID types.UID, timeline int) backupResultMetadata {
32 | return backupResultMetadata{
33 | timeline: strconv.Itoa(timeline),
34 | clusterUID: string(clusterUID),
35 | // static values
36 | version: metadata.Data.Version,
37 | name: metadata.Data.Name,
38 | displayName: metadata.Data.DisplayName,
39 | pluginName: metadata.PluginName,
40 | }
41 | }
42 |
43 | func newBackupResultMetadataFromMap(m map[string]string) backupResultMetadata {
44 | if m == nil {
45 | return backupResultMetadata{}
46 | }
47 |
48 | return backupResultMetadata{
49 | timeline: m["timeline"],
50 | version: m["version"],
51 | name: m["name"],
52 | displayName: m["displayName"],
53 | clusterUID: m["clusterUID"],
54 | pluginName: m["pluginName"],
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/internal/cnpgi/metadata/constants.go:
--------------------------------------------------------------------------------
1 | package metadata
2 |
3 | import "github.com/cloudnative-pg/cnpg-i/pkg/identity"
4 |
5 | // PluginName is the name of the plugin from the instance manager
6 | // Point-of-view
7 | const PluginName = "barman-cloud.cloudnative-pg.io"
8 |
9 | const (
10 | // CheckEmptyWalArchiveFile is the name of the file in the PGDATA that,
11 | // if present, requires the WAL archiver to check that the backup object
12 | // store is empty.
13 | CheckEmptyWalArchiveFile = ".check-empty-wal-archive"
14 |
15 | // BarmanCertificatesPath is the path where the Barman
16 | // certificates will be installed
17 | BarmanCertificatesPath = "/barman-certificates"
18 |
19 | // BarmanCertificatesFileName is the path where the Barman
20 | // certificates will be used
21 | BarmanCertificatesFileName = "barman-ca.crt"
22 | )
23 |
24 | // Data is the metadata of this plugin.
25 | var Data = identity.GetPluginMetadataResponse{
26 | Name: PluginName,
27 | Version: "0.5.0", // x-release-please-version
28 | DisplayName: "BarmanCloudInstance",
29 | ProjectUrl: "https://github.com/cloudnative-pg/plugin-barman-cloud",
30 | RepositoryUrl: "https://github.com/cloudnative-pg/plugin-barman-cloud",
31 | License: "APACHE 2.0",
32 | LicenseUrl: "https://github.com/cloudnative-pg/plugin-barman-cloud/LICENSE",
33 | Maturity: "alpha",
34 | }
35 |
--------------------------------------------------------------------------------
/internal/cnpgi/metadata/doc.go:
--------------------------------------------------------------------------------
1 | // Package metadata contains the common metadata on the operator
2 | // and on the instance manager
3 | package metadata
4 |
--------------------------------------------------------------------------------
/internal/cnpgi/operator/config/doc.go:
--------------------------------------------------------------------------------
1 | // Package config contains the functions to parse the plugin configuration
2 | package config
3 |
--------------------------------------------------------------------------------
/internal/cnpgi/operator/doc.go:
--------------------------------------------------------------------------------
1 | // Package operator implements the capabilities used by CNPG
2 | package operator
3 |
--------------------------------------------------------------------------------
/internal/cnpgi/operator/identity.go:
--------------------------------------------------------------------------------
1 | package operator
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/cloudnative-pg/cnpg-i/pkg/identity"
7 |
8 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata"
9 | )
10 |
11 | // IdentityImplementation is the implementation of the CNPG-i
12 | // Identity entrypoint
13 | type IdentityImplementation struct {
14 | identity.UnimplementedIdentityServer
15 | }
16 |
17 | // GetPluginMetadata implements Identity
18 | func (i IdentityImplementation) GetPluginMetadata(
19 | _ context.Context,
20 | _ *identity.GetPluginMetadataRequest,
21 | ) (*identity.GetPluginMetadataResponse, error) {
22 | return &metadata.Data, nil
23 | }
24 |
25 | // GetPluginCapabilities implements identity
26 | func (i IdentityImplementation) GetPluginCapabilities(
27 | _ context.Context,
28 | _ *identity.GetPluginCapabilitiesRequest,
29 | ) (*identity.GetPluginCapabilitiesResponse, error) {
30 | return &identity.GetPluginCapabilitiesResponse{
31 | Capabilities: []*identity.PluginCapability{
32 | {
33 | Type: &identity.PluginCapability_Service_{
34 | Service: &identity.PluginCapability_Service{
35 | Type: identity.PluginCapability_Service_TYPE_RECONCILER_HOOKS,
36 | },
37 | },
38 | },
39 | {
40 | Type: &identity.PluginCapability_Service_{
41 | Service: &identity.PluginCapability_Service{
42 | Type: identity.PluginCapability_Service_TYPE_LIFECYCLE_SERVICE,
43 | },
44 | },
45 | },
46 | },
47 | }, nil
48 | }
49 |
50 | // Probe implements Identity
51 | func (i IdentityImplementation) Probe(
52 | _ context.Context,
53 | _ *identity.ProbeRequest,
54 | ) (*identity.ProbeResponse, error) {
55 | return &identity.ProbeResponse{
56 | Ready: true,
57 | }, nil
58 | }
59 |
--------------------------------------------------------------------------------
/internal/cnpgi/operator/lifecycle_certificates.go:
--------------------------------------------------------------------------------
1 | package operator
2 |
3 | import (
4 | "context"
5 | "path"
6 |
7 | corev1 "k8s.io/api/core/v1"
8 | "k8s.io/apimachinery/pkg/types"
9 |
10 | barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
11 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata"
12 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/config"
13 | )
14 |
15 | // barmanCertificatesVolumeName is the name of the volume that hosts
16 | // the barman certificates to be used
17 | const barmanCertificatesVolumeName = "barman-certificates"
18 |
19 | func (impl LifecycleImplementation) collectAdditionalCertificates(
20 | ctx context.Context,
21 | pluginConfiguration *config.PluginConfiguration,
22 | ) ([]corev1.VolumeProjection, error) {
23 | var result []corev1.VolumeProjection
24 |
25 | for _, barmanObjectKey := range pluginConfiguration.GetReferredBarmanObjectsKey() {
26 | certs, err := impl.collectObjectStoreCertificates(ctx, barmanObjectKey)
27 | if err != nil {
28 | return nil, err
29 | }
30 | result = append(result, certs...)
31 | }
32 |
33 | return result, nil
34 | }
35 |
36 | func (impl LifecycleImplementation) collectObjectStoreCertificates(
37 | ctx context.Context,
38 | barmanObjectKey types.NamespacedName,
39 | ) ([]corev1.VolumeProjection, error) {
40 | var objectStore barmancloudv1.ObjectStore
41 | if err := impl.Client.Get(ctx, barmanObjectKey, &objectStore); err != nil {
42 | return nil, err
43 | }
44 |
45 | endpointCA := objectStore.Spec.Configuration.EndpointCA
46 | if endpointCA == nil {
47 | return nil, nil
48 | }
49 |
50 | return []corev1.VolumeProjection{
51 | {
52 | Secret: &corev1.SecretProjection{
53 | LocalObjectReference: corev1.LocalObjectReference{
54 | Name: endpointCA.Name,
55 | },
56 | Items: []corev1.KeyToPath{
57 | {
58 | Key: endpointCA.Key,
59 | Path: path.Join(
60 | barmanObjectKey.Name,
61 | metadata.BarmanCertificatesFileName,
62 | ),
63 | },
64 | },
65 | },
66 | },
67 | }, nil
68 | }
69 |
--------------------------------------------------------------------------------
/internal/cnpgi/operator/lifecycle_envs.go:
--------------------------------------------------------------------------------
1 | package operator
2 |
3 | import (
4 | "context"
5 |
6 | corev1 "k8s.io/api/core/v1"
7 | "k8s.io/apimachinery/pkg/types"
8 |
9 | barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
10 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/config"
11 | )
12 |
13 | func (impl LifecycleImplementation) collectAdditionalEnvs(
14 | ctx context.Context,
15 | namespace string,
16 | pluginConfiguration *config.PluginConfiguration,
17 | ) ([]corev1.EnvVar, error) {
18 | var result []corev1.EnvVar
19 |
20 | // TODO: check if the environment variables are clashing and in
21 | // that case raise an error
22 |
23 | if len(pluginConfiguration.BarmanObjectName) > 0 {
24 | envs, err := impl.collectObjectStoreEnvs(
25 | ctx,
26 | types.NamespacedName{
27 | Name: pluginConfiguration.BarmanObjectName,
28 | Namespace: namespace,
29 | },
30 | )
31 | if err != nil {
32 | return nil, err
33 | }
34 | result = append(result, envs...)
35 | }
36 |
37 | if len(pluginConfiguration.RecoveryBarmanObjectName) > 0 {
38 | envs, err := impl.collectObjectStoreEnvs(
39 | ctx,
40 | types.NamespacedName{
41 | Name: pluginConfiguration.RecoveryBarmanObjectName,
42 | Namespace: namespace,
43 | },
44 | )
45 | if err != nil {
46 | return nil, err
47 | }
48 | result = append(result, envs...)
49 | }
50 |
51 | if len(pluginConfiguration.ReplicaSourceBarmanObjectName) > 0 {
52 | envs, err := impl.collectObjectStoreEnvs(
53 | ctx,
54 | types.NamespacedName{
55 | Name: pluginConfiguration.ReplicaSourceBarmanObjectName,
56 | Namespace: namespace,
57 | },
58 | )
59 | if err != nil {
60 | return nil, err
61 | }
62 | result = append(result, envs...)
63 | }
64 |
65 | return result, nil
66 | }
67 |
68 | func (impl LifecycleImplementation) collectObjectStoreEnvs(
69 | ctx context.Context,
70 | barmanObjectKey types.NamespacedName,
71 | ) ([]corev1.EnvVar, error) {
72 | var objectStore barmancloudv1.ObjectStore
73 | if err := impl.Client.Get(ctx, barmanObjectKey, &objectStore); err != nil {
74 | return nil, err
75 | }
76 |
77 | return objectStore.Spec.InstanceSidecarConfiguration.Env, nil
78 | }
79 |
--------------------------------------------------------------------------------
/internal/cnpgi/operator/lifecycle_resources.go:
--------------------------------------------------------------------------------
1 | package operator
2 |
3 | import (
4 | "context"
5 |
6 | corev1 "k8s.io/api/core/v1"
7 |
8 | barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
9 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/config"
10 | )
11 |
12 | func (impl LifecycleImplementation) collectSidecarResourcesForRecoveryJob(
13 | ctx context.Context,
14 | configuration *config.PluginConfiguration,
15 | ) (corev1.ResourceRequirements, error) {
16 | if len(configuration.RecoveryBarmanObjectName) > 0 {
17 | var barmanObjectStore barmancloudv1.ObjectStore
18 | if err := impl.Client.Get(ctx, configuration.GetRecoveryBarmanObjectKey(), &barmanObjectStore); err != nil {
19 | return corev1.ResourceRequirements{}, err
20 | }
21 |
22 | return barmanObjectStore.Spec.InstanceSidecarConfiguration.Resources, nil
23 | }
24 |
25 | return corev1.ResourceRequirements{}, nil
26 | }
27 |
28 | func (impl LifecycleImplementation) collectSidecarResourcesForPod(
29 | ctx context.Context,
30 | configuration *config.PluginConfiguration,
31 | ) (corev1.ResourceRequirements, error) {
32 | if len(configuration.BarmanObjectName) > 0 {
33 | // On a replica cluster that also archives, the designated primary
34 | // will use both the replica source object store and the object store
35 | // of the cluster.
36 | // In this case, we use the cluster object store for configuring
37 | // the resources of the sidecar container.
38 |
39 | var barmanObjectStore barmancloudv1.ObjectStore
40 | if err := impl.Client.Get(ctx, configuration.GetBarmanObjectKey(), &barmanObjectStore); err != nil {
41 | return corev1.ResourceRequirements{}, err
42 | }
43 |
44 | return barmanObjectStore.Spec.InstanceSidecarConfiguration.Resources, nil
45 | }
46 |
47 | if len(configuration.RecoveryBarmanObjectName) > 0 {
48 | // On a replica cluster that doesn't archive, the designated primary
49 | // uses only the replica source object store.
50 | // In this case, we use the replica source object store for configuring
51 | // the resources of the sidecar container.
52 | var barmanObjectStore barmancloudv1.ObjectStore
53 | if err := impl.Client.Get(ctx, configuration.GetRecoveryBarmanObjectKey(), &barmanObjectStore); err != nil {
54 | return corev1.ResourceRequirements{}, err
55 | }
56 |
57 | return barmanObjectStore.Spec.InstanceSidecarConfiguration.Resources, nil
58 | }
59 |
60 | return corev1.ResourceRequirements{}, nil
61 | }
62 |
--------------------------------------------------------------------------------
/internal/cnpgi/operator/ownership.go:
--------------------------------------------------------------------------------
1 | package operator
2 |
3 | import (
4 | "fmt"
5 |
6 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
7 | "k8s.io/apimachinery/pkg/runtime"
8 | "k8s.io/utils/ptr"
9 | )
10 |
11 | // setOwnerReference explicitly set the owner reference between an
12 | // owner object and a controller one.
13 | //
14 | // Important: this function won't use any registered scheme and will
15 | // fail unless the metadata has been correctly set into the owner
16 | // object.
17 | func setOwnerReference(owner, controlled metav1.Object) error {
18 | ro, ok := owner.(runtime.Object)
19 | if !ok {
20 | return fmt.Errorf("%T is not a runtime.Object, cannot call setOwnerReference", owner)
21 | }
22 |
23 | if len(ro.DeepCopyObject().GetObjectKind().GroupVersionKind().Group) == 0 {
24 | return fmt.Errorf("%T metadata have not been set, cannot call setOwnerReference", owner)
25 | }
26 |
27 | controlled.SetOwnerReferences([]metav1.OwnerReference{
28 | {
29 | APIVersion: ro.GetObjectKind().GroupVersionKind().GroupVersion().String(),
30 | Kind: ro.GetObjectKind().GroupVersionKind().Kind,
31 | Name: owner.GetName(),
32 | UID: owner.GetUID(),
33 | BlockOwnerDeletion: ptr.To(true),
34 | Controller: ptr.To(true),
35 | },
36 | })
37 |
38 | return nil
39 | }
40 |
--------------------------------------------------------------------------------
/internal/cnpgi/operator/specs/role.go:
--------------------------------------------------------------------------------
1 | package specs
2 |
3 | import (
4 | "fmt"
5 |
6 | cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
7 | "github.com/cloudnative-pg/machinery/pkg/stringset"
8 | rbacv1 "k8s.io/api/rbac/v1"
9 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10 |
11 | barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
12 | )
13 |
14 | // BuildRole builds the Role object for this cluster
15 | func BuildRole(
16 | cluster *cnpgv1.Cluster,
17 | barmanObjects []barmancloudv1.ObjectStore,
18 | ) *rbacv1.Role {
19 | role := &rbacv1.Role{
20 | ObjectMeta: metav1.ObjectMeta{
21 | Namespace: cluster.Namespace,
22 | Name: GetRBACName(cluster.Name),
23 | },
24 |
25 | Rules: []rbacv1.PolicyRule{},
26 | }
27 |
28 | secretsSet := stringset.New()
29 | barmanObjectsSet := stringset.New()
30 |
31 | for _, barmanObject := range barmanObjects {
32 | barmanObjectsSet.Put(barmanObject.Name)
33 | for _, secret := range CollectSecretNamesFromCredentials(&barmanObject.Spec.Configuration.BarmanCredentials) {
34 | secretsSet.Put(secret)
35 | }
36 | }
37 |
38 | role.Rules = append(
39 | role.Rules,
40 | rbacv1.PolicyRule{
41 | APIGroups: []string{
42 | "barmancloud.cnpg.io",
43 | },
44 | Verbs: []string{
45 | "get",
46 | "watch",
47 | "list",
48 | },
49 | Resources: []string{
50 | "objectstores",
51 | },
52 | ResourceNames: barmanObjectsSet.ToSortedList(),
53 | },
54 | rbacv1.PolicyRule{
55 | APIGroups: []string{
56 | "barmancloud.cnpg.io",
57 | },
58 | Verbs: []string{
59 | "update",
60 | },
61 | Resources: []string{
62 | "objectstores/status",
63 | },
64 | ResourceNames: barmanObjectsSet.ToSortedList(),
65 | },
66 | rbacv1.PolicyRule{
67 | APIGroups: []string{
68 | "",
69 | },
70 | Resources: []string{
71 | "secrets",
72 | },
73 | Verbs: []string{
74 | "get",
75 | "watch",
76 | "list",
77 | },
78 | ResourceNames: secretsSet.ToSortedList(),
79 | },
80 | )
81 |
82 | return role
83 | }
84 |
85 | // BuildRoleBinding builds the role binding object for this cluster
86 | func BuildRoleBinding(
87 | cluster *cnpgv1.Cluster,
88 | ) *rbacv1.RoleBinding {
89 | return &rbacv1.RoleBinding{
90 | ObjectMeta: metav1.ObjectMeta{
91 | Namespace: cluster.Namespace,
92 | Name: GetRBACName(cluster.Name),
93 | },
94 | Subjects: []rbacv1.Subject{
95 | {
96 | Kind: "ServiceAccount",
97 | APIGroup: "",
98 | Name: cluster.Name,
99 | Namespace: cluster.Namespace,
100 | },
101 | },
102 | RoleRef: rbacv1.RoleRef{
103 | APIGroup: "rbac.authorization.k8s.io",
104 | Kind: "Role",
105 | Name: GetRBACName(cluster.Name),
106 | },
107 | }
108 | }
109 |
110 | // GetRBACName returns the name of the RBAC entities for the
111 | // barman cloud plugin
112 | func GetRBACName(clusterName string) string {
113 | return fmt.Sprintf("%s-barman-cloud", clusterName)
114 | }
115 |
--------------------------------------------------------------------------------
/internal/cnpgi/operator/specs/secrets.go:
--------------------------------------------------------------------------------
1 | package specs
2 |
3 | import (
4 | barmanapi "github.com/cloudnative-pg/barman-cloud/pkg/api"
5 | machineryapi "github.com/cloudnative-pg/machinery/pkg/api"
6 | )
7 |
8 | // CollectSecretNamesFromCredentials collects the names of the secrets
9 | func CollectSecretNamesFromCredentials(barmanCredentials *barmanapi.BarmanCredentials) []string {
10 | var references []*machineryapi.SecretKeySelector
11 | if barmanCredentials.AWS != nil {
12 | references = append(
13 | references,
14 | barmanCredentials.AWS.AccessKeyIDReference,
15 | barmanCredentials.AWS.SecretAccessKeyReference,
16 | barmanCredentials.AWS.RegionReference,
17 | barmanCredentials.AWS.SessionToken,
18 | )
19 | }
20 | if barmanCredentials.Azure != nil {
21 | references = append(
22 | references,
23 | barmanCredentials.Azure.ConnectionString,
24 | barmanCredentials.Azure.StorageAccount,
25 | barmanCredentials.Azure.StorageKey,
26 | barmanCredentials.Azure.StorageSasToken,
27 | )
28 | }
29 | if barmanCredentials.Google != nil {
30 | references = append(
31 | references,
32 | barmanCredentials.Google.ApplicationCredentials,
33 | )
34 | }
35 |
36 | result := make([]string, 0, len(references))
37 | for _, reference := range references {
38 | if reference == nil {
39 | continue
40 | }
41 | result = append(result, reference.Name)
42 | }
43 |
44 | // TODO: stringset belongs to machinery :(
45 |
46 | return result
47 | }
48 |
--------------------------------------------------------------------------------
/internal/cnpgi/operator/specs/specs.go:
--------------------------------------------------------------------------------
1 | // Package specs contains the specification of the kubernetes objects
2 | // that are created by the plugin
3 | package specs
4 |
--------------------------------------------------------------------------------
/internal/cnpgi/operator/start.go:
--------------------------------------------------------------------------------
1 | package operator
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/cloudnative-pg/cnpg-i-machinery/pkg/pluginhelper/http"
7 | "github.com/cloudnative-pg/cnpg-i/pkg/lifecycle"
8 | "github.com/cloudnative-pg/cnpg-i/pkg/reconciler"
9 | "google.golang.org/grpc"
10 | "sigs.k8s.io/controller-runtime/pkg/client"
11 | )
12 |
13 | // CNPGI is the implementation of the CNPG-i server
14 | type CNPGI struct {
15 | Client client.Client
16 | PluginPath string
17 | ServerCertPath string
18 | ServerKeyPath string
19 | ClientCertPath string
20 | ServerAddress string
21 | }
22 |
23 | // Start starts the GRPC server
24 | // of the operator plugin
25 | func (c *CNPGI) Start(ctx context.Context) error {
26 | enrich := func(server *grpc.Server) error {
27 | reconciler.RegisterReconcilerHooksServer(server, ReconcilerImplementation{
28 | Client: c.Client,
29 | })
30 | lifecycle.RegisterOperatorLifecycleServer(server, LifecycleImplementation{
31 | Client: c.Client,
32 | })
33 | return nil
34 | }
35 |
36 | srv := http.Server{
37 | IdentityImpl: IdentityImplementation{},
38 | Enrichers: []http.ServerEnricher{enrich},
39 | PluginPath: c.PluginPath,
40 | ServerCertPath: c.ServerCertPath,
41 | ServerKeyPath: c.ServerKeyPath,
42 | ClientCertPath: c.ClientCertPath,
43 | ServerAddress: c.ServerAddress,
44 | }
45 |
46 | return srv.Start(ctx)
47 | }
48 |
--------------------------------------------------------------------------------
/internal/cnpgi/operator/suite_test.go:
--------------------------------------------------------------------------------
1 | package operator
2 |
3 | import (
4 | "testing"
5 |
6 | . "github.com/onsi/ginkgo/v2"
7 | . "github.com/onsi/gomega"
8 | )
9 |
10 | func TestOperator(t *testing.T) {
11 | RegisterFailHandler(Fail)
12 | RunSpecs(t, "Operator Suite")
13 | }
14 |
--------------------------------------------------------------------------------
/internal/cnpgi/restore/doc.go:
--------------------------------------------------------------------------------
1 | // Package restore provides the restore functionality for CNPGI.
2 | package restore
3 |
--------------------------------------------------------------------------------
/internal/cnpgi/restore/identity.go:
--------------------------------------------------------------------------------
1 | package restore
2 |
3 | import (
4 | "context"
5 |
6 | "github.com/cloudnative-pg/cnpg-i/pkg/identity"
7 | "sigs.k8s.io/controller-runtime/pkg/client"
8 |
9 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata"
10 | )
11 |
12 | // IdentityImplementation implements IdentityServer
13 | type IdentityImplementation struct {
14 | identity.UnimplementedIdentityServer
15 | BarmanObjectKey client.ObjectKey
16 | Client client.Client
17 | }
18 |
19 | // GetPluginMetadata implements IdentityServer
20 | func (i IdentityImplementation) GetPluginMetadata(
21 | _ context.Context,
22 | _ *identity.GetPluginMetadataRequest,
23 | ) (*identity.GetPluginMetadataResponse, error) {
24 | return &metadata.Data, nil
25 | }
26 |
27 | // GetPluginCapabilities implements IdentityServer
28 | func (i IdentityImplementation) GetPluginCapabilities(
29 | _ context.Context,
30 | _ *identity.GetPluginCapabilitiesRequest,
31 | ) (*identity.GetPluginCapabilitiesResponse, error) {
32 | return &identity.GetPluginCapabilitiesResponse{
33 | Capabilities: []*identity.PluginCapability{
34 | {
35 | Type: &identity.PluginCapability_Service_{
36 | Service: &identity.PluginCapability_Service{
37 | Type: identity.PluginCapability_Service_TYPE_RESTORE_JOB,
38 | },
39 | },
40 | },
41 | {
42 | Type: &identity.PluginCapability_Service_{
43 | Service: &identity.PluginCapability_Service{
44 | Type: identity.PluginCapability_Service_TYPE_WAL_SERVICE,
45 | },
46 | },
47 | },
48 | },
49 | }, nil
50 | }
51 |
52 | // Probe implements IdentityServer
53 | func (i IdentityImplementation) Probe(
54 | _ context.Context,
55 | _ *identity.ProbeRequest,
56 | ) (*identity.ProbeResponse, error) {
57 | return &identity.ProbeResponse{
58 | Ready: true,
59 | }, nil
60 | }
61 |
--------------------------------------------------------------------------------
/internal/cnpgi/restore/manager.go:
--------------------------------------------------------------------------------
1 | package restore
2 |
3 | import (
4 | "context"
5 |
6 | cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
7 | "github.com/cloudnative-pg/machinery/pkg/log"
8 | "github.com/spf13/viper"
9 | corev1 "k8s.io/api/core/v1"
10 | "k8s.io/apimachinery/pkg/runtime"
11 | utilruntime "k8s.io/apimachinery/pkg/util/runtime"
12 | clientgoscheme "k8s.io/client-go/kubernetes/scheme"
13 | ctrl "sigs.k8s.io/controller-runtime"
14 | "sigs.k8s.io/controller-runtime/pkg/client"
15 |
16 | barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
17 | )
18 |
19 | var scheme = runtime.NewScheme()
20 |
21 | func init() {
22 | utilruntime.Must(barmancloudv1.AddToScheme(scheme))
23 | utilruntime.Must(cnpgv1.AddToScheme(scheme))
24 | utilruntime.Must(clientgoscheme.AddToScheme(scheme))
25 | }
26 |
27 | // Start starts the sidecar informers and CNPG-i server
28 | func Start(ctx context.Context) error {
29 | setupLog := log.FromContext(ctx)
30 | setupLog.Info("Starting barman cloud instance plugin")
31 |
32 | mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
33 | Scheme: scheme,
34 | Client: client.Options{
35 | Cache: &client.CacheOptions{
36 | DisableFor: []client.Object{
37 | &corev1.Secret{},
38 | &barmancloudv1.ObjectStore{},
39 | },
40 | },
41 | },
42 | })
43 | if err != nil {
44 | setupLog.Error(err, "unable to start manager")
45 | return err
46 | }
47 |
48 | if err := mgr.Add(&CNPGI{
49 | PluginPath: viper.GetString("plugin-path"),
50 | SpoolDirectory: viper.GetString("spool-directory"),
51 | Client: mgr.GetClient(),
52 | PGDataPath: viper.GetString("pgdata"),
53 | InstanceName: viper.GetString("pod-name"),
54 | }); err != nil {
55 | setupLog.Error(err, "unable to create CNPGI runnable")
56 | return err
57 | }
58 |
59 | if err := mgr.Start(ctx); err != nil {
60 | return err
61 | }
62 |
63 | return nil
64 | }
65 |
--------------------------------------------------------------------------------
/internal/cnpgi/restore/start.go:
--------------------------------------------------------------------------------
1 | package restore
2 |
3 | import (
4 | "context"
5 | "path"
6 |
7 | "github.com/cloudnative-pg/cnpg-i-machinery/pkg/pluginhelper/http"
8 | restore "github.com/cloudnative-pg/cnpg-i/pkg/restore/job"
9 | "github.com/cloudnative-pg/cnpg-i/pkg/wal"
10 | "google.golang.org/grpc"
11 | "sigs.k8s.io/controller-runtime/pkg/client"
12 |
13 | "github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/common"
14 | )
15 |
16 | // CNPGI is the implementation of the PostgreSQL sidecar
17 | type CNPGI struct {
18 | PluginPath string
19 | SpoolDirectory string
20 |
21 | Client client.Client
22 | PGDataPath string
23 | InstanceName string
24 | }
25 |
26 | // Start starts the GRPC service
27 | func (c *CNPGI) Start(ctx context.Context) error {
28 | // PgWalVolumePgWalPath is the path of pg_wal directory inside the WAL volume when present
29 | const PgWalVolumePgWalPath = "/var/lib/postgresql/wal/pg_wal"
30 |
31 | enrich := func(server *grpc.Server) error {
32 | wal.RegisterWALServer(server, common.WALServiceImplementation{
33 | InstanceName: c.InstanceName,
34 | Client: c.Client,
35 | SpoolDirectory: c.SpoolDirectory,
36 | PGDataPath: c.PGDataPath,
37 | PGWALPath: path.Join(c.PGDataPath, "pg_wal"),
38 | })
39 |
40 | restore.RegisterRestoreJobHooksServer(server, &JobHookImpl{
41 | Client: c.Client,
42 | SpoolDirectory: c.SpoolDirectory,
43 | PgDataPath: c.PGDataPath,
44 | PgWalFolderToSymlink: PgWalVolumePgWalPath,
45 | })
46 |
47 | common.AddHealthCheck(server)
48 |
49 | return nil
50 | }
51 |
52 | srv := http.Server{
53 | IdentityImpl: IdentityImplementation{},
54 | Enrichers: []http.ServerEnricher{enrich},
55 | PluginPath: c.PluginPath,
56 | }
57 |
58 | return srv.Start(ctx)
59 | }
60 |
--------------------------------------------------------------------------------
/internal/controller/doc.go:
--------------------------------------------------------------------------------
1 | // Package controller implements a controller for the CRDs as defined
2 | // by this operator
3 | package controller
4 |
--------------------------------------------------------------------------------
/internal/controller/objectstore_controller.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package controller
18 |
19 | import (
20 | "context"
21 | "fmt"
22 |
23 | "github.com/cloudnative-pg/machinery/pkg/log"
24 | "k8s.io/apimachinery/pkg/runtime"
25 | ctrl "sigs.k8s.io/controller-runtime"
26 | "sigs.k8s.io/controller-runtime/pkg/client"
27 |
28 | barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
29 | )
30 |
31 | // ObjectStoreReconciler reconciles a ObjectStore object.
32 | type ObjectStoreReconciler struct {
33 | client.Client
34 | Scheme *runtime.Scheme
35 | }
36 |
37 | // +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=rolebindings,verbs=create;patch;update;get;list;watch
38 | // +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=roles,verbs=create;patch;update;get;list;watch
39 | // +kubebuilder:rbac:groups="",resources=secrets,verbs=create;list;get;watch;delete
40 | // +kubebuilder:rbac:groups=postgresql.cnpg.io,resources=backups,verbs=get;list;watch
41 | // +kubebuilder:rbac:groups=barmancloud.cnpg.io,resources=objectstores,verbs=get;list;watch;create;update;patch;delete
42 | // +kubebuilder:rbac:groups=barmancloud.cnpg.io,resources=objectstores/status,verbs=get;update;patch
43 | // +kubebuilder:rbac:groups=barmancloud.cnpg.io,resources=objectstores/finalizers,verbs=update
44 |
45 | // Reconcile is part of the main kubernetes reconciliation loop which aims to
46 | // move the current state of the cluster closer to the desired state.
47 | // TODO(user): Modify the Reconcile function to compare the state specified by
48 | // the ObjectStore object against the actual cluster state, and then
49 | // perform operations to make the cluster state reflect the state specified by
50 | // the user.
51 | //
52 | // For more details, check Reconcile and its Result here:
53 | // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.0/pkg/reconcile
54 | func (r *ObjectStoreReconciler) Reconcile(ctx context.Context, _ ctrl.Request) (ctrl.Result, error) {
55 | _ = log.FromContext(ctx)
56 |
57 | // TODO(user): your logic here
58 |
59 | return ctrl.Result{}, nil
60 | }
61 |
62 | // SetupWithManager sets up the controller with the Manager.
63 | func (r *ObjectStoreReconciler) SetupWithManager(mgr ctrl.Manager) error {
64 | err := ctrl.NewControllerManagedBy(mgr).
65 | For(&barmancloudv1.ObjectStore{}).
66 | Complete(r)
67 | if err != nil {
68 | return fmt.Errorf("unable to create controller: %w", err)
69 | }
70 |
71 | return nil
72 | }
73 |
--------------------------------------------------------------------------------
/internal/controller/objectstore_controller_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package controller
18 |
19 | import (
20 | "context"
21 |
22 | barmanapi "github.com/cloudnative-pg/barman-cloud/pkg/api"
23 | "k8s.io/apimachinery/pkg/api/errors"
24 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
25 | "k8s.io/apimachinery/pkg/types"
26 | "sigs.k8s.io/controller-runtime/pkg/reconcile"
27 |
28 | barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
29 |
30 | . "github.com/onsi/ginkgo/v2"
31 | . "github.com/onsi/gomega"
32 | )
33 |
34 | var _ = Describe("ObjectStore Controller", func() {
35 | Context("When reconciling a resource", func() {
36 | const resourceName = "test-resource"
37 |
38 | ctx := context.Background()
39 |
40 | typeNamespacedName := types.NamespacedName{
41 | Name: resourceName,
42 | Namespace: "default", // TODO(user):Modify as needed
43 | }
44 | objectstore := &barmancloudv1.ObjectStore{}
45 |
46 | BeforeEach(func() {
47 | By("creating the custom resource for the Kind ObjectStore")
48 | err := k8sClient.Get(ctx, typeNamespacedName, objectstore)
49 | if err != nil && errors.IsNotFound(err) {
50 | resource := &barmancloudv1.ObjectStore{
51 | ObjectMeta: metav1.ObjectMeta{
52 | Name: resourceName,
53 | Namespace: "default",
54 | },
55 | Spec: barmancloudv1.ObjectStoreSpec{
56 | Configuration: barmanapi.BarmanObjectStoreConfiguration{DestinationPath: "/tmp"},
57 | },
58 | // TODO(user): Specify other spec details if needed.
59 | }
60 | Expect(k8sClient.Create(ctx, resource)).To(Succeed())
61 | }
62 | })
63 |
64 | AfterEach(func() {
65 | // TODO(user): Cleanup logic after each test, like removing the resource instance.
66 | resource := &barmancloudv1.ObjectStore{}
67 | err := k8sClient.Get(ctx, typeNamespacedName, resource)
68 | Expect(err).NotTo(HaveOccurred())
69 |
70 | By("Cleanup the specific resource instance ObjectStore")
71 | Expect(k8sClient.Delete(ctx, resource)).To(Succeed())
72 | })
73 | It("should successfully reconcile the resource", func() {
74 | By("Reconciling the created resource")
75 | controllerReconciler := &ObjectStoreReconciler{
76 | Client: k8sClient,
77 | Scheme: k8sClient.Scheme(),
78 | }
79 |
80 | _, err := controllerReconciler.Reconcile(ctx, reconcile.Request{
81 | NamespacedName: typeNamespacedName,
82 | })
83 | Expect(err).NotTo(HaveOccurred())
84 | // TODO(user): Add more specific assertions depending on your controller's reconciliation logic.
85 | // Example: If you expect a certain status condition after reconciliation, verify it here.
86 | })
87 | })
88 | })
89 |
--------------------------------------------------------------------------------
/internal/controller/suite_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package controller
18 |
19 | import (
20 | "context"
21 | "fmt"
22 | "path/filepath"
23 | "runtime"
24 | "testing"
25 |
26 | // +kubebuilder:scaffold:imports
27 | "k8s.io/client-go/kubernetes/scheme"
28 | "k8s.io/client-go/rest"
29 | "sigs.k8s.io/controller-runtime/pkg/client"
30 | "sigs.k8s.io/controller-runtime/pkg/envtest"
31 | logf "sigs.k8s.io/controller-runtime/pkg/log"
32 | "sigs.k8s.io/controller-runtime/pkg/log/zap"
33 |
34 | barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
35 |
36 | . "github.com/onsi/ginkgo/v2"
37 | . "github.com/onsi/gomega"
38 | )
39 |
40 | // These tests use Ginkgo (BDD-style Go testing framework). Refer to
41 | // http://onsi.github.io/ginkgo/ to learn more about Ginkgo.
42 |
43 | var (
44 | cfg *rest.Config
45 | k8sClient client.Client
46 | testEnv *envtest.Environment
47 | ctx context.Context
48 | cancel context.CancelFunc
49 | )
50 |
51 | func TestControllers(t *testing.T) {
52 | RegisterFailHandler(Fail)
53 |
54 | RunSpecs(t, "Controller Suite")
55 | }
56 |
57 | var _ = BeforeSuite(func() {
58 | logf.SetLogger(zap.New(zap.WriteTo(GinkgoWriter), zap.UseDevMode(true)))
59 |
60 | ctx, cancel = context.WithCancel(context.TODO())
61 |
62 | By("bootstrapping test environment")
63 | testEnv = &envtest.Environment{
64 | CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")},
65 | ErrorIfCRDPathMissing: true,
66 |
67 | // The BinaryAssetsDirectory is only required if you want to run the tests directly
68 | // without call the makefile target test. If not informed it will look for the
69 | // default path defined in controller-runtime which is /usr/local/kubebuilder/.
70 | // Note that you must have the required binaries setup under the bin directory to perform
71 | // the tests directly. When we run make test it will be setup and used automatically.
72 | BinaryAssetsDirectory: filepath.Join("..", "..", "bin", "k8s",
73 | fmt.Sprintf("1.31.0-%s-%s", runtime.GOOS, runtime.GOARCH)),
74 | }
75 |
76 | var err error
77 | // cfg is defined in this file globally.
78 | cfg, err = testEnv.Start()
79 | Expect(err).NotTo(HaveOccurred())
80 | Expect(cfg).NotTo(BeNil())
81 |
82 | err = barmancloudv1.AddToScheme(scheme.Scheme)
83 | Expect(err).NotTo(HaveOccurred())
84 |
85 | // +kubebuilder:scaffold:scheme
86 |
87 | k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme})
88 | Expect(err).NotTo(HaveOccurred())
89 | Expect(k8sClient).NotTo(BeNil())
90 | })
91 |
92 | var _ = AfterSuite(func() {
93 | By("tearing down the test environment")
94 | cancel()
95 | err := testEnv.Stop()
96 | Expect(err).NotTo(HaveOccurred())
97 | })
98 |
--------------------------------------------------------------------------------
/kubernetes/certificate-issuer.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: cert-manager.io/v1
2 | kind: Issuer
3 | metadata:
4 | name: selfsigned-issuer
5 | spec:
6 | selfSigned: {}
7 |
--------------------------------------------------------------------------------
/kubernetes/client-certificate.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: cert-manager.io/v1
2 | kind: Certificate
3 | metadata:
4 | name: barman-cloud-client
5 | spec:
6 | secretName: barman-cloud-client-tls
7 |
8 | commonName: "barman-cloud-client"
9 | duration: 2160h # 90d
10 | renewBefore: 360h # 15d
11 |
12 | isCA: false
13 | usages:
14 | - client auth
15 |
16 | issuerRef:
17 | name: selfsigned-issuer
18 | kind: Issuer
19 | group: cert-manager.io
20 |
--------------------------------------------------------------------------------
/kubernetes/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | labels:
5 | app: barman-cloud
6 | name: barman-cloud
7 | spec:
8 | replicas: 1
9 | selector:
10 | matchLabels:
11 | app: barman-cloud
12 | strategy:
13 | type: Recreate
14 | template:
15 | metadata:
16 | labels:
17 | app: barman-cloud
18 | spec:
19 | securityContext:
20 | runAsNonRoot: true
21 | seccompProfile:
22 | type: RuntimeDefault
23 | serviceAccountName: plugin-barman-cloud
24 | containers:
25 | - image: plugin-barman-cloud:latest
26 | name: barman-cloud
27 | ports:
28 | - containerPort: 9090
29 | protocol: TCP
30 | env:
31 | - name: SIDECAR_IMAGE
32 | valueFrom:
33 | secretKeyRef:
34 | key: SIDECAR_IMAGE
35 | name: plugin-barman-cloud
36 | args:
37 | - operator
38 | - --server-cert=/server/tls.crt
39 | - --server-key=/server/tls.key
40 | - --client-cert=/client/tls.crt
41 | - --server-address=:9090
42 | - --leader-elect
43 | - --log-level=debug
44 | readinessProbe:
45 | tcpSocket:
46 | port: 9090
47 | initialDelaySeconds: 10
48 | periodSeconds: 10
49 | volumeMounts:
50 | - mountPath: /server
51 | name: server
52 | - mountPath: /client
53 | name: client
54 | resources: {}
55 | securityContext:
56 | allowPrivilegeEscalation: false
57 | capabilities:
58 | drop:
59 | - ALL
60 | readOnlyRootFilesystem: true
61 | runAsGroup: 10001
62 | runAsUser: 10001
63 | seccompProfile:
64 | type: RuntimeDefault
65 | volumes:
66 | - name: server
67 | secret:
68 | secretName: barman-cloud-server-tls
69 | - name: client
70 | secret:
71 | secretName: barman-cloud-client-tls
72 |
--------------------------------------------------------------------------------
/kubernetes/kustomization.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: kustomize.config.k8s.io/v1beta1
2 | kind: Kustomization
3 | namespace: cnpg-system
4 | resources:
5 | - certificate-issuer.yaml
6 | - client-certificate.yaml
7 | - deployment.yaml
8 | - server-certificate.yaml
9 | - service.yaml
10 | - ../config/crd
11 | - ../config/rbac
12 | images:
13 | - name: plugin-barman-cloud
14 | newName: ghcr.io/cloudnative-pg/plugin-barman-cloud-testing
15 | newTag: main
16 | secretGenerator:
17 | - literals:
18 | - SIDECAR_IMAGE=ghcr.io/cloudnative-pg/plugin-barman-cloud-sidecar-testing:main
19 | name: plugin-barman-cloud
20 |
--------------------------------------------------------------------------------
/kubernetes/server-certificate.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: cert-manager.io/v1
2 | kind: Certificate
3 | metadata:
4 | name: barman-cloud-server
5 | spec:
6 | secretName: barman-cloud-server-tls
7 | commonName: barman-cloud
8 | dnsNames:
9 | - barman-cloud
10 |
11 | duration: 2160h # 90d
12 | renewBefore: 360h # 15d
13 |
14 | isCA: false
15 | usages:
16 | - server auth
17 |
18 | issuerRef:
19 | name: selfsigned-issuer
20 | kind: Issuer
21 | group: cert-manager.io
22 |
--------------------------------------------------------------------------------
/kubernetes/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | labels:
5 | app: barman-cloud
6 | cnpg.io/pluginName: barman-cloud.cloudnative-pg.io
7 | annotations:
8 | cnpg.io/pluginClientSecret: barman-cloud-client-tls
9 | cnpg.io/pluginServerSecret: barman-cloud-server-tls
10 | cnpg.io/pluginPort: "9090"
11 | name: barman-cloud
12 | spec:
13 | ports:
14 | - port: 9090
15 | protocol: TCP
16 | targetPort: 9090
17 | selector:
18 | app: barman-cloud
19 |
--------------------------------------------------------------------------------
/logo/cloudnativepg.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cloudnative-pg/plugin-barman-cloud/b45cddfc193c7e5ba1ab050c951031c86a244141/logo/cloudnativepg.png
--------------------------------------------------------------------------------
/release-please-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "changelog-path": "CHANGELOG.md",
3 | "release-type": "go",
4 | "bump-minor-pre-major": true,
5 | "bump-patch-for-minor-pre-major": false,
6 | "draft": false,
7 | "extra-files": [
8 | "README.md",
9 | "internal/cnpgi/metadata/constants.go"
10 | ],
11 | "prerelease": false,
12 | "packages": {
13 | ".": {}
14 | },
15 | "plugins": [ "sentence-case" ],
16 | "signoff": "Peggie ",
17 | "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json"
18 | }
19 |
--------------------------------------------------------------------------------
/scripts/cleanup.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -eu
4 |
5 | cd "$(dirname "$0")/.." || exit
6 |
7 | kubectl delete clusters --all
8 | kubectl delete backups --all
9 | kubectl exec -ti mc -- mc rm -r --force minio/backups
--------------------------------------------------------------------------------
/scripts/minio-delete.sh:
--------------------------------------------------------------------------------
1 | kubectl exec -ti mc -- mc rm -r --force minio/backups
2 |
--------------------------------------------------------------------------------
/scripts/run.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | set -eu
4 |
5 | cd "$(dirname "$0")/.." || exit
6 |
7 | if [ -f .env ]; then
8 | source .env
9 | fi
10 |
11 |
12 | MYTMPDIR="$(mktemp -d)"
13 | trap 'rm -rf -- "$MYTMPDIR"' EXIT
14 |
15 | current_context=$(kubectl config view --raw -o json | jq -r '."current-context"' | sed "s/kind-//")
16 | operator_image=$(KIND_CLUSTER_NAME="$current_context" KO_DOCKER_REPO=kind.local ko build -BP ./cmd/manager)
17 | instance_image=$(KIND_CLUSTER_NAME="$current_context" KO_DOCKER_REPO=kind.local KO_DEFAULTBASEIMAGE="ghcr.io/cloudnative-pg/postgresql:17.0" ko build -BP ./cmd/manager)
18 |
19 | # Now we deploy the plugin inside the `cnpg-system` workspace
20 | (
21 | cp -r kubernetes config "$MYTMPDIR"
22 | cd "$MYTMPDIR/kubernetes"
23 | kustomize edit set image "plugin-barman-cloud=$operator_image"
24 | kustomize edit set secret plugin-barman-cloud "--from-literal=SIDECAR_IMAGE=$instance_image"
25 | kubectl apply -k .
26 | )
27 |
--------------------------------------------------------------------------------
/test/e2e/internal/certmanager/doc.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | // Package certmanager provides utilities for setting up and managing
18 | // cert-manager for end-to-end testing.
19 | package certmanager
20 |
--------------------------------------------------------------------------------
/test/e2e/internal/client/doc.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | // Package client provides function to create Kubernetes clients.
18 | package client
19 |
--------------------------------------------------------------------------------
/test/e2e/internal/cloudnativepg/doc.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | // Package cloudnativepg provides utilities for setting up and managing
18 | // CloudNativePG environments for end-to-end testing.
19 | package cloudnativepg
20 |
--------------------------------------------------------------------------------
/test/e2e/internal/cluster/cluster.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package cluster
18 |
19 | import (
20 | v1 "github.com/cloudnative-pg/api/pkg/api/v1"
21 | )
22 |
23 | // TODO: improve this with what we already have in CloudNativePG e2e.
24 | func IsReady(cluster v1.Cluster) bool {
25 | if cluster.Status.ReadyInstances != cluster.Spec.Instances {
26 | return false
27 | }
28 | for _, condition := range cluster.Status.Conditions {
29 | if condition.Type == string(v1.ConditionClusterReady) {
30 | return string(condition.Status) == string(v1.ConditionTrue)
31 | }
32 | }
33 |
34 | return false
35 | }
36 |
--------------------------------------------------------------------------------
/test/e2e/internal/cluster/doc.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | // Package cluster contains functions to interact with the CloudNativePG clusters
18 | package cluster
19 |
--------------------------------------------------------------------------------
/test/e2e/internal/command/command.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package command
18 |
19 | import (
20 | "bytes"
21 | "context"
22 | "fmt"
23 | "time"
24 |
25 | "k8s.io/client-go/kubernetes"
26 | "k8s.io/client-go/rest"
27 | "k8s.io/client-go/tools/remotecommand"
28 | )
29 |
30 | // TODO: extract this and the one in CloudNativePG to a common library
31 |
32 | // ContainerLocator is a struct that contains the information needed to locate a container in a pod.
33 | type ContainerLocator struct {
34 | NamespaceName string
35 | PodName string
36 | ContainerName string
37 | }
38 |
39 | // ExecuteInContainer executes a command in a container. If timeout is not nil, the command will be
40 | // executed with the specified timeout. The function returns the stdout and stderr of the command.
41 | func ExecuteInContainer(
42 | ctx context.Context,
43 | clientSet kubernetes.Clientset,
44 | cfg *rest.Config,
45 | container ContainerLocator,
46 | timeout *time.Duration,
47 | command []string,
48 | ) (string, string, error) {
49 | req := clientSet.CoreV1().RESTClient().Post().
50 | Resource("pods").
51 | Name(container.PodName).
52 | Namespace(container.NamespaceName).
53 | SubResource("exec").
54 | Param("container", container.ContainerName).
55 | Param("stdout", "true").
56 | Param("stderr", "true")
57 | for _, cmd := range command {
58 | req.Param("command", cmd)
59 | }
60 |
61 | newConfig := *cfg // local copy avoids modifying the passed config arg
62 | if timeout != nil {
63 | req.Timeout(*timeout)
64 | newConfig.Timeout = *timeout
65 | timedCtx, cancelFunc := context.WithTimeout(ctx, *timeout)
66 | defer cancelFunc()
67 | ctx = timedCtx
68 | }
69 |
70 | exec, err := remotecommand.NewSPDYExecutor(cfg, "POST", req.URL())
71 | if err != nil {
72 | return "", "", fmt.Errorf("error creating executor: %w", err)
73 | }
74 |
75 | var stdout, stderr bytes.Buffer
76 | err = exec.StreamWithContext(ctx, remotecommand.StreamOptions{
77 | Stdout: &stdout,
78 | Stderr: &stderr,
79 | })
80 | if err != nil {
81 | return "", "", fmt.Errorf("error executing command in pod '%s/%s': %w",
82 | container.NamespaceName, container.PodName, err)
83 | }
84 |
85 | return stdout.String(), stderr.String(), nil
86 | }
87 |
--------------------------------------------------------------------------------
/test/e2e/internal/command/doc.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | // Package command provides function to execute commands in k8s pods.
18 | package command
19 |
--------------------------------------------------------------------------------
/test/e2e/internal/deployment/deployment.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package deployment
18 |
19 | import (
20 | "context"
21 | "fmt"
22 | "time"
23 |
24 | appsv1 "k8s.io/api/apps/v1"
25 | "k8s.io/apimachinery/pkg/types"
26 | "k8s.io/apimachinery/pkg/util/wait"
27 | "sigs.k8s.io/controller-runtime/pkg/client"
28 | )
29 |
30 | // IsReady checks if the deployment is ready.
31 | func IsReady(ctx context.Context, cl client.Client, name types.NamespacedName) (bool, error) {
32 | deployment := &appsv1.Deployment{}
33 | err := cl.Get(ctx, name, deployment)
34 | if err != nil {
35 | return false, fmt.Errorf("failed to get %s deployment: %w", name, err)
36 | }
37 |
38 | // Check if the deployment is ready
39 | ready := false
40 | for _, condition := range deployment.Status.Conditions {
41 | if condition.Type == appsv1.DeploymentAvailable && condition.Status == "True" {
42 | ready = true
43 | break
44 | }
45 | }
46 | if !ready {
47 | return false, nil
48 | }
49 |
50 | return true, nil
51 | }
52 |
53 | // WaitForDeploymentReady waits for the deployment to be ready. ctx should have a timeout set.
54 | func WaitForDeploymentReady(
55 | ctx context.Context, cl client.Client, namespacedName types.NamespacedName, interval time.Duration,
56 | ) error {
57 | err := wait.PollUntilContextCancel(ctx, interval, false,
58 | func(ctx context.Context) (bool, error) {
59 | ready, err := IsReady(ctx, cl, namespacedName)
60 | if err != nil {
61 | return false, fmt.Errorf("failed to check if %s is ready: %w", namespacedName, err)
62 | }
63 | if ready {
64 | return true, nil
65 | }
66 |
67 | return false, nil
68 | })
69 | if err != nil {
70 | return fmt.Errorf("failed to wait for %s to be ready: %w", namespacedName, err)
71 | }
72 |
73 | return nil
74 | }
75 |
--------------------------------------------------------------------------------
/test/e2e/internal/deployment/doc.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | // Package deployment provides utilities for managing Kubernetes deployments
18 | package deployment
19 |
--------------------------------------------------------------------------------
/test/e2e/internal/e2etestenv/doc.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | // Package e2etestenv provides a test environment for end-to-end tests.
18 | package e2etestenv
19 |
--------------------------------------------------------------------------------
/test/e2e/internal/kustomize/doc.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | // Package kustomize provides utilities for applying and managing Kubernetes
18 | // customizations using Kustomize.
19 | package kustomize
20 |
--------------------------------------------------------------------------------
/test/e2e/internal/namespace/doc.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | // Package namespace provides utilities to manage namespaces.
18 | package namespace
19 |
--------------------------------------------------------------------------------
/test/e2e/internal/namespace/namespace.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package namespace
18 |
19 | import (
20 | "context"
21 | "crypto/rand"
22 | "fmt"
23 | "math/big"
24 |
25 | corev1 "k8s.io/api/core/v1"
26 | apierrors "k8s.io/apimachinery/pkg/api/errors"
27 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28 | "sigs.k8s.io/controller-runtime/pkg/client"
29 | )
30 |
31 | // CreateUniqueNamespace creates a namespace with an unique suffix.
32 | func CreateUniqueNamespace(ctx context.Context, cl client.Client, prefix string) (*corev1.Namespace, error) {
33 | for {
34 | randInt, err := rand.Int(rand.Reader, big.NewInt(100000))
35 | if err != nil {
36 | return nil, fmt.Errorf("failed to generate random number: %w", err)
37 | }
38 | namespaceName := fmt.Sprintf("%s-%d", prefix, randInt)
39 | namespace := &corev1.Namespace{
40 | ObjectMeta: metav1.ObjectMeta{
41 | Name: namespaceName,
42 | },
43 | }
44 |
45 | err = cl.Create(ctx, namespace)
46 | if err == nil {
47 | return namespace, nil
48 | }
49 | if !apierrors.IsAlreadyExists(err) {
50 | return nil, fmt.Errorf("failed to create namespace: %w", err)
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/test/e2e/internal/objectstore/doc.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | // Package objectstore provides shared examples for object store resources.
18 | package objectstore
19 |
--------------------------------------------------------------------------------
/test/e2e/internal/objectstore/objectstore.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package objectstore
18 |
19 | import (
20 | "context"
21 | "fmt"
22 |
23 | appsv1 "k8s.io/api/apps/v1"
24 | corev1 "k8s.io/api/core/v1"
25 | "sigs.k8s.io/controller-runtime/pkg/client"
26 | )
27 |
28 | const (
29 | // DefaultSize is the default size of the PVCs for the object stores.
30 | DefaultSize = "1Gi"
31 | )
32 |
33 | // Resources represents the resources required to create an object store.
34 | type Resources struct {
35 | Deployment *appsv1.Deployment
36 | Service *corev1.Service
37 | Secret *corev1.Secret
38 | PVC *corev1.PersistentVolumeClaim
39 | }
40 |
41 | // Create creates the object store resources.
42 | func (osr Resources) Create(ctx context.Context, cl client.Client) error {
43 | if osr.PVC != nil {
44 | if err := cl.Create(ctx, osr.PVC); err != nil {
45 | return fmt.Errorf("failed to create PVC: %w", err)
46 | }
47 | }
48 | if osr.Secret != nil {
49 | if err := cl.Create(ctx, osr.Secret); err != nil {
50 | return fmt.Errorf("failed to create secret: %w", err)
51 | }
52 | }
53 | if osr.Deployment != nil {
54 | if err := cl.Create(ctx, osr.Deployment); err != nil {
55 | return fmt.Errorf("failed to create deployment: %w", err)
56 | }
57 | }
58 | if osr.Service != nil {
59 | if err := cl.Create(ctx, osr.Service); err != nil {
60 | return fmt.Errorf("failed to create service: %w", err)
61 | }
62 | }
63 |
64 | return nil
65 | }
66 |
--------------------------------------------------------------------------------
/test/e2e/internal/tests/backup/doc.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | // Package backup contains tests for the backup and restore functionality
18 | // of the Barman Cloud Plugin.
19 | package backup
20 |
--------------------------------------------------------------------------------
/test/e2e/internal/tests/replicacluster/doc.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | // Package replicacluster contains tests validating replica clusters
18 | // using the Barman Cloud Plugin.
19 | package replicacluster
20 |
--------------------------------------------------------------------------------
/test/e2e/kustomize/config:
--------------------------------------------------------------------------------
1 | ../../../config/
--------------------------------------------------------------------------------
/test/e2e/kustomize/kubernetes:
--------------------------------------------------------------------------------
1 | ../../../kubernetes/
--------------------------------------------------------------------------------
/web/.gitignore:
--------------------------------------------------------------------------------
1 | # Dependencies
2 | /node_modules
3 |
4 | # Production
5 | /build
6 |
7 | # Generated files
8 | .docusaurus
9 | .cache-loader
10 |
11 | # Misc
12 | .DS_Store
13 | .env.local
14 | .env.development.local
15 | .env.test.local
16 | .env.production.local
17 |
18 | npm-debug.log*
19 | yarn-debug.log*
20 | yarn-error.log*
21 | package-lock.json
22 |
--------------------------------------------------------------------------------
/web/README.md:
--------------------------------------------------------------------------------
1 | # Website
2 |
3 | This website is built using [Docusaurus](https://docusaurus.io/), a modern
4 | static website generator.
5 |
6 | ### Requirements
7 |
8 | - Docker
9 | - [Yarn](https://yarnpkg.com/)
10 | - [Dagger](https://dagger.io/)
11 | - [Task](https://taskfile.dev/)
12 |
13 | ### Installation
14 |
15 | ```shell
16 | $ yarn
17 | ```
18 |
19 | ### Local Development
20 |
21 | ```shell
22 | $ yarn start
23 | ```
24 |
25 | This command starts a local development server and opens up a browser window.
26 | Most changes are reflected live without having to restart the server.
27 |
28 | ### Build
29 |
30 | ```shell
31 | $ yarn build
32 | ```
33 |
34 | This command generates static content into the `build` directory and can be
35 | served using any static contents hosting service.
36 |
37 | ### Test the build
38 |
39 | ```shell
40 | $ yarn serve
41 | ```
42 |
43 | By default, this will load your site at http://localhost:3000/.
44 |
45 | ### Spellchecking
46 |
47 | From the top directory:
48 |
49 | ```shell
50 | task spellcheck
51 | ```
52 |
53 | ### Versioning
54 |
55 | Docusaurus allows versioning of the documentation to maintain separate sets of
56 | documentation for different software versions.
57 |
58 | To create a new documentation version:
59 |
60 | ```shell
61 | $ yarn docusaurus docs:version X.Y.Z
62 | ```
63 |
--------------------------------------------------------------------------------
/web/docs/compression.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 80
3 | ---
4 |
5 | # Compression
6 |
7 |
8 |
9 | By default, backups and WAL files are archived **uncompressed**. However, the
10 | Barman Cloud Plugin supports multiple compression algorithms via
11 | `barman-cloud-backup` and `barman-cloud-wal-archive`, allowing you to optimize
12 | for space, speed, or a balance of both.
13 |
14 | ### Supported Compression Algorithms
15 |
16 | - `bzip2`
17 | - `gzip`
18 | - `lz4` (WAL only)
19 | - `snappy`
20 | - `xz` (WAL only)
21 | - `zstd` (WAL only)
22 |
23 | Compression settings for base backups and WAL archives are configured
24 | independently. For implementation details, refer to the corresponding API
25 | definitions:
26 |
27 | - [`DataBackupConfiguration`](https://pkg.go.dev/github.com/cloudnative-pg/barman-cloud/pkg/api#DataBackupConfiguration)
28 | - [`WALBackupConfiguration`](https://pkg.go.dev/github.com/cloudnative-pg/barman-cloud/pkg/api#WalBackupConfiguration)
29 |
30 | :::important
31 | Compression impacts both performance and storage efficiency. Choose the right
32 | algorithm based on your recovery time objectives (RTO), storage capacity, and
33 | network throughput.
34 | :::
35 |
36 | ## Compression Benchmark (on MinIO)
37 |
38 | | Compression | Backup Time (ms) | Restore Time (ms) | Uncompressed Size (MB) | Compressed Size (MB) | Ratio |
39 | | ----------- | ---------------- | ----------------- | ---------------------- | -------------------- | ----- |
40 | | None | 10,927 | 7,553 | 395 | 395 | 1.0:1 |
41 | | bzip2 | 25,404 | 13,886 | 395 | 67 | 5.9:1 |
42 | | gzip | 116,281 | 3,077 | 395 | 91 | 4.3:1 |
43 | | snappy | 8,134 | 8,341 | 395 | 166 | 2.4:1 |
44 |
--------------------------------------------------------------------------------
/web/docs/images.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 99
3 | ---
4 |
5 | # Container Images
6 |
7 |
8 |
9 | The Barman Cloud Plugin is distributed using two container images:
10 |
11 | - One for deploying the plugin components
12 | - One for the sidecar that runs alongside each PostgreSQL instance in a
13 | CloudNativePG `Cluster` using the plugin
14 |
15 | ## Plugin Container Image
16 |
17 | The plugin image contains the logic required to operate the Barman Cloud Plugin
18 | within your Kubernetes environment with CloudNativePG. It is published on the
19 | GitHub Container Registry at `ghcr.io/cloudnative-pg/plugin-barman-cloud`.
20 |
21 | This image is built from the
22 | [`Dockerfile.plugin`](https://github.com/cloudnative-pg/plugin-barman-cloud/blob/main/containers/Dockerfile.plugin)
23 | in the plugin repository.
24 |
25 | ## Sidecar Container Image
26 |
27 | The sidecar image is used within each PostgreSQL pod in the cluster. It
28 | includes the latest supported version of Barman Cloud and is responsible for
29 | performing WAL archiving and backups on behalf of CloudNativePG.
30 |
31 | It is available at `ghcr.io/cloudnative-pg/plugin-barman-cloud-sidecar` and is
32 | built from the
33 | [`Dockerfile.sidecar`](https://github.com/cloudnative-pg/plugin-barman-cloud/blob/main/containers/Dockerfile.sidecar).
34 |
35 | These sidecar images are designed to work seamlessly with the
36 | [`minimal` PostgreSQL container images](https://github.com/cloudnative-pg/postgres-containers?tab=readme-ov-file#minimal-images)
37 | maintained by the CloudNativePG Community.
38 |
--------------------------------------------------------------------------------
/web/docs/intro.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 1
3 | sidebar_label: "Introduction"
4 | ---
5 |
6 | # Barman Cloud Plugin
7 |
8 |
9 |
10 | The **Barman Cloud Plugin** for [CloudNativePG](https://cloudnative-pg.io/)
11 | enables online continuous physical backups of PostgreSQL clusters to object storage
12 | using the `barman-cloud` suite from the [Barman](https://docs.pgbarman.org/release/latest/)
13 | project.
14 |
15 | :::important
16 | If you plan to migrate your existing CloudNativePG cluster to the new
17 | plugin-based approach using the Barman Cloud Plugin, see
18 | ["Migrating from Built-in CloudNativePG Backup"](migration.md)
19 | for detailed instructions.
20 | :::
21 |
22 | ## Requirements
23 |
24 | To use the Barman Cloud Plugin, you need:
25 |
26 | - [CloudNativePG](https://cloudnative-pg.io) version **1.26**
27 | - [cert-manager](https://cert-manager.io/) to enable TLS communication between
28 | the plugin and the operator
29 |
30 | ## Key Features
31 |
32 | This plugin provides the following capabilities:
33 |
34 | - Physical online backup of the data directory
35 | - Physical restore of the data directory
36 | - Write-Ahead Log (WAL) archiving
37 | - WAL restore
38 | - Full cluster recovery
39 | - Point-in-Time Recovery (PITR)
40 | - Seamless integration with replica clusters for bootstrap and WAL restore from archive
41 |
42 | :::important
43 | The Barman Cloud Plugin is designed to **replace the in-tree object storage support**
44 | previously provided via the `.spec.backup.barmanObjectStore` section in the
45 | `Cluster` resource.
46 | Backups created using the in-tree approach are fully supported and compatible
47 | with this plugin.
48 | :::
49 |
50 | ## Supported Object Storage Providers
51 |
52 | The plugin works with all storage backends supported by `barman-cloud`, including:
53 |
54 | - **Amazon S3**
55 | - **Google Cloud Storage**
56 | - **Microsoft Azure Blob Storage**
57 |
58 | In addition, the following S3-compatible and simulator solutions have been
59 | tested and verified:
60 |
61 | - [MinIO](https://min.io/) – An S3-compatible storage solution
62 | - [Azurite](https://github.com/Azure/Azurite) – A simulator for Azure Blob Storage
63 | - [fake-gcs-server](https://github.com/fsouza/fake-gcs-server) – A simulator for Google Cloud Storage
64 |
65 | :::tip
66 | For more details, refer to [Object Store Providers](object_stores.md).
67 | :::
68 |
--------------------------------------------------------------------------------
/web/docs/misc.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 90
3 | ---
4 |
5 | # Miscellaneous
6 |
7 |
8 |
9 | ## Backup Object Tagging
10 |
11 | You can attach key-value metadata tags to backup artifacts—such as base
12 | backups, WAL files, and history files—via the `.spec.configuration` section of
13 | the `ObjectStore` resource.
14 |
15 | - `tags`: applied to base backups and WAL files
16 | - `historyTags`: applied to history files only
17 |
18 | ### Example
19 |
20 | ```yaml
21 | apiVersion: barmancloud.cnpg.io/v1
22 | kind: ObjectStore
23 | metadata:
24 | name: my-store
25 | spec:
26 | configuration:
27 | [...]
28 | tags:
29 | backupRetentionPolicy: "expire"
30 | historyTags:
31 | backupRetentionPolicy: "keep"
32 | [...]
33 | ```
34 |
35 | ## Extra Options for Backup and WAL Archiving
36 |
37 | You can pass additional command-line arguments to `barman-cloud-backup` and
38 | `barman-cloud-wal-archive` using the `additionalCommandArgs` field in the
39 | `ObjectStore` configuration.
40 |
41 | - `.spec.configuration.data.additionalCommandArgs`: for `barman-cloud-backup`
42 | - `.spec.configuration.wal.additionalCommandArgs`: for `barman-cloud-wal-archive`
43 |
44 | Each field accepts a list of string arguments. If an argument is already
45 | configured elsewhere in the plugin, the duplicate will be ignored.
46 |
47 | ### Example: Extra Backup Options
48 |
49 | ```yaml
50 | kind: ObjectStore
51 | metadata:
52 | name: my-store
53 | spec:
54 | configuration:
55 | data:
56 | additionalCommandArgs:
57 | - "--min-chunk-size=5MB"
58 | - "--read-timeout=60"
59 | ```
60 |
61 | ### Example: Extra WAL Archive Options
62 |
63 | ```yaml
64 | kind: ObjectStore
65 | metadata:
66 | name: my-store
67 | spec:
68 | configuration:
69 | wal:
70 | additionalCommandArgs:
71 | - "--max-concurrency=1"
72 | - "--read-timeout=60"
73 | ```
74 |
75 | For a complete list of supported options, refer to the
76 | [official Barman Cloud documentation](https://docs.pgbarman.org/release/latest/).
77 |
--------------------------------------------------------------------------------
/web/docs/parameters.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 100
3 | ---
4 |
5 | # Parameters
6 |
7 |
8 |
9 | The following parameters are available for the Barman Cloud Plugin:
10 |
11 | - `barmanObjectName`: references the `ObjectStore` resource to be used by the
12 | plugin.
13 | - `serverName`: Specifies the server name in the object store.
14 |
15 | :::important
16 | The `serverName` parameter in the `ObjectStore` resource is retained solely for
17 | API compatibility with the in-tree `barmanObjectStore` and must always be left empty.
18 | When needed, use the `serverName` plugin parameter in the Cluster configuration instead.
19 | :::
20 |
--------------------------------------------------------------------------------
/web/docs/retention.md:
--------------------------------------------------------------------------------
1 | ---
2 | sidebar_position: 60
3 | ---
4 |
5 | # Retention Policies
6 |
7 |
8 |
9 | The Barman Cloud Plugin supports **automated cleanup of obsolete backups** via
10 | retention policies, configured in the `.spec.retentionPolicy` field of the
11 | `ObjectStore` resource.
12 |
13 | :::note
14 | This feature uses the `barman-cloud-backup-delete` command with the
15 | `--retention-policy "RECOVERY WINDOW OF {{ value }} {{ unit }}"` syntax.
16 | :::
17 |
18 | #### Example: 30-Day Retention Policy
19 |
20 | ```yaml
21 | apiVersion: barmancloud.cnpg.io/v1
22 | kind: ObjectStore
23 | metadata:
24 | name: my-store
25 | spec:
26 | [...]
27 | retentionPolicy: "30d"
28 | ````
29 |
30 | :::note
31 | A **recovery window retention policy** ensures the cluster can be restored to
32 | any point in time between the calculated *Point of Recoverability* (PoR) and
33 | the latest WAL archive. The PoR is defined as `current time - recovery window`.
34 | The **first valid backup** is the most recent backup completed before the PoR.
35 | Backups older than that are marked as *obsolete* and deleted after the next
36 | backup completes.
37 | :::
38 |
39 |
--------------------------------------------------------------------------------
/web/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "docs",
3 | "version": "0.0.0",
4 | "private": true,
5 | "scripts": {
6 | "docusaurus": "docusaurus",
7 | "start": "docusaurus start",
8 | "build": "docusaurus build",
9 | "swizzle": "docusaurus swizzle",
10 | "deploy": "docusaurus deploy",
11 | "clear": "docusaurus clear",
12 | "serve": "docusaurus serve",
13 | "write-translations": "docusaurus write-translations",
14 | "write-heading-ids": "docusaurus write-heading-ids",
15 | "typecheck": "tsc"
16 | },
17 | "dependencies": {
18 | "@docusaurus/core": "3.8.0",
19 | "@docusaurus/preset-classic": "3.8.0",
20 | "@easyops-cn/docusaurus-search-local": "^0.50.0",
21 | "@mdx-js/react": "^3.0.0",
22 | "clsx": "^2.0.0",
23 | "prism-react-renderer": "^2.3.0",
24 | "react": "^19.0.0",
25 | "react-dom": "^19.0.0"
26 | },
27 | "devDependencies": {
28 | "@docusaurus/module-type-aliases": "3.8.0",
29 | "@docusaurus/tsconfig": "3.8.0",
30 | "@docusaurus/types": "3.8.0",
31 | "typescript": "~5.8.0"
32 | },
33 | "browserslist": {
34 | "production": [
35 | ">0.5%",
36 | "not dead",
37 | "not op_mini all"
38 | ],
39 | "development": [
40 | "last 3 chrome version",
41 | "last 3 firefox version",
42 | "last 5 safari version"
43 | ]
44 | },
45 | "engines": {
46 | "node": ">=18.0"
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/web/sidebars.ts:
--------------------------------------------------------------------------------
1 | import type {SidebarsConfig} from '@docusaurus/plugin-content-docs';
2 |
3 | // This runs in Node.js - Don't use client-side code here (browser APIs, JSX...)
4 |
5 | /**
6 | * Creating a sidebar enables you to:
7 | - create an ordered group of docs
8 | - render a sidebar for each doc of that group
9 | - provide next/previous navigation
10 |
11 | The sidebars can be generated from the filesystem, or explicitly defined here.
12 |
13 | Create as many sidebars as you want.
14 | */
15 |
16 | // Export the combined sidebars
17 | export default {
18 | // The key 'documentation' is the sidebarId referenced in navbar
19 | docs: [{type: 'autogenerated', dirName: '.'}],
20 | };
21 |
--------------------------------------------------------------------------------
/web/src/components/HomepageFeatures/feature.tsx:
--------------------------------------------------------------------------------
1 | import type {ComponentProps, ComponentType, ReactElement} from "react";
2 | import clsx from "clsx";
3 | import styles from "@site/src/components/HomepageFeatures/styles.module.css";
4 | import Heading from "@theme/Heading";
5 |
6 | type FeatureItem = {
7 | title: string;
8 | Svg: ComponentType>;
9 | description: string;
10 | };
11 |
12 | function Feature({title, Svg, description}: FeatureItem): ReactElement {
13 | return (
14 |