-
Notifications
You must be signed in to change notification settings - Fork 23
Create a plugin to add a WAL Receiver pod #74
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Draft
alaye-ms
wants to merge
12
commits into
documentdb:main
Choose a base branch
from
alaye-ms:walReceiverPlugin
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from 1 commit
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
206f6fc
add plugin
alaye-ms ee447cb
use new service account
alaye-ms 28cae36
fix reconciler and disable operator
alaye-ms bae9bbf
use proper user and different default values
alaye-ms d32bd56
Add examples and start making custom image
alaye-ms e8534ec
distroless image first go
alaye-ms 4469daf
add wal slot creation to the plugin
alaye-ms fa8b988
remove non-working dockerfile
alaye-ms ea15d4d
Update docs
alaye-ms e5ada08
remove plugin-side primary detection
alaye-ms 2fb1584
refactor: improve WAL replica CNPG-I plugin robustness and testing
a8cb9d8
fix: WAL replica health probes and PVC owner reference
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Some comments aren't visible on the classic Files Changed page.
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| bin/ | ||
| dist/ | ||
| .env | ||
| .vscode/ | ||
| .idea/ | ||
| .task/ | ||
| manifest.yaml |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| # Copyright (c) Microsoft Corporation. | ||
| # Licensed under the MIT License. | ||
|
|
||
| # Step 1: build image | ||
| FROM golang:1.24 AS builder | ||
|
|
||
| # Cache the dependencies | ||
| WORKDIR /app | ||
| COPY go.mod go.sum /app/ | ||
| RUN go mod download | ||
|
|
||
| # Compile the application | ||
| COPY . /app | ||
| RUN --mount=type=cache,target=/root/.cache/go-build ./scripts/build.sh | ||
|
|
||
| # Step 2: build the image to be actually run | ||
| FROM golang:1-alpine | ||
| USER 10001:10001 | ||
| COPY --from=builder /app/bin/cnpg-i-wal-replica /app/bin/cnpg-i-wal-replica | ||
| ENTRYPOINT ["/app/bin/cnpg-i-wal-replica"] | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| DocumentDB Kubernetes Operator | ||
|
|
||
| Copyright (c) Microsoft Corporation. | ||
|
|
||
| MIT License | ||
|
|
||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| of this software and associated documentation files (the "Software"), to deal | ||
| in the Software without restriction, including without limitation the rights | ||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| copies of the Software, and to permit persons to whom the Software is | ||
| furnished to do so, subject to the following conditions: | ||
|
|
||
| The above copyright notice and this permission notice shall be included in all | ||
| copies or substantial portions of the Software. | ||
|
|
||
| THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| SOFTWARE. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| # WAL Receiver Pod Manager (CNPG-I Plugin) | ||
|
|
||
| This plugin adds an optional standalone WAL receiver (pg_receivewal) Pod/Deployment | ||
| alongside a [CloudNativePG](https://github.com/cloudnative-pg/cloudnative-pg/) Cluster. | ||
| It reconciles a Deployment named | ||
| `<cluster-name>-wal-receiver` that continuously streams WAL files from the primary | ||
| cluster using `pg_receivewal`, supporting synchronous mode. | ||
|
|
||
| ## Parameters | ||
|
|
||
| Add the plugin in the Cluster spec (example): | ||
|
|
||
| ```yaml | ||
| spec: | ||
| plugins: | ||
| - name: cnpg-i-wal-replica.documentdb.io | ||
| parameters: | ||
| enabled: "true" | ||
| image: "ghcr.io/cloudnative-pg/postgresql:16" | ||
| replicationUser: streaming_replica | ||
| replicationPasswordSecretName: cluster-replication | ||
| replicationPasswordSecretKey: password # optional (default: password) | ||
| synchronous: "true" # optional (default true) | ||
| walDirectory: /var/lib/wal # optional (default /var/lib/wal) | ||
| # replicationHost: override-host.example # optional | ||
| ``` | ||
|
|
||
| | Parameter | Type | Default | Description | | ||
| |-----------|------|---------|-------------| | ||
| | enabled | bool | false | Enable or disable the plugin | | ||
| | image | string | ghcr.io/cloudnative-pg/postgresql:16 | Image providing pg_receivewal | | ||
| | replicationHost | string | <cluster>-rw | Host to connect for streaming | | ||
| | replicationUser | string | streaming_replica | Replication user | | ||
| | replicationPasswordSecretName | string | (required when enabled) | Secret containing replication password | | ||
| | replicationPasswordSecretKey | string | password | Key in the secret | | ||
| | synchronous | bool | true | Add --synchronous flag to pg_receivewal | | ||
| | walDirectory | string | /var/lib/wal | Local directory to store WAL | | ||
|
|
||
| The Deployment exposes a metrics port (9187) and creates a Service with the same name. | ||
|
|
||
| ## Build | ||
|
|
||
| ```bash | ||
| go build -o bin/cnpg-i-wal-replica main.go | ||
| ``` | ||
|
|
||
| ## Status | ||
|
|
||
| The plugin status reflects only whether it is enabled. | ||
|
|
||
| ## Future Work | ||
|
|
||
| * Add PVC / volume configuration for WAL directory | ||
| * Expose resource requests/limits and security context | ||
| * Garbage collection / retention policy for archived WAL | ||
| * Liveness/readiness refinements | ||
|
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| // Copyright (c) Microsoft Corporation. | ||
| // Licensed under the MIT License. | ||
|
|
||
| // Package plugin implements the command to start the plugin | ||
| package plugin |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| // Copyright (c) Microsoft Corporation. | ||
| // Licensed under the MIT License. | ||
|
|
||
| package plugin | ||
|
|
||
| import ( | ||
| "github.com/cloudnative-pg/cnpg-i-machinery/pkg/pluginhelper/http" | ||
| "github.com/cloudnative-pg/cnpg-i/pkg/operator" | ||
| "github.com/cloudnative-pg/cnpg-i/pkg/reconciler" | ||
| "github.com/spf13/cobra" | ||
| "google.golang.org/grpc" | ||
| "sigs.k8s.io/controller-runtime/pkg/log" | ||
| "sigs.k8s.io/controller-runtime/pkg/log/zap" | ||
|
|
||
| "github.com/documentdb/cnpg-i-wal-replica/internal/identity" | ||
| operatorImpl "github.com/documentdb/cnpg-i-wal-replica/internal/operator" | ||
| reconcilerImpl "github.com/documentdb/cnpg-i-wal-replica/internal/reconciler" | ||
| ) | ||
|
|
||
| // NewCmd creates the `plugin` command | ||
| func NewCmd() *cobra.Command { | ||
| cmd := http.CreateMainCmd(identity.Implementation{}, func(server *grpc.Server) error { | ||
| // Register the declared implementations | ||
| operator.RegisterOperatorServer(server, operatorImpl.Implementation{}) | ||
| reconciler.RegisterReconcilerHooksServer(server, reconcilerImpl.Implementation{}) | ||
| return nil | ||
| }) | ||
|
|
||
| // If you want to provide your own logr.Logger here, inject it into a context.Context | ||
| // with logr.NewContext(ctx, logger) and pass it to cmd.SetContext(ctx) | ||
| logger := zap.New(zap.UseDevMode(true)) | ||
| log.SetLogger(logger) | ||
|
|
||
| // Additional custom behaviour can be added by wrapping cmd.PersistentPreRun or cmd.Run | ||
|
|
||
| cmd.Use = "plugin" | ||
|
|
||
| return cmd | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,180 @@ | ||
| # Plugin Development | ||
|
|
||
| This section of the documentation illustrates the CNPG-I capabilities used by | ||
| the wal-replica plugin, how the plugin implementation uses them, and how | ||
| developers can build and deploy the plugin. | ||
|
|
||
| ## Concepts | ||
|
|
||
| ### Identity | ||
|
|
||
| The Identity interface defines the features supported by the plugin and is the | ||
| only interface that must always be implemented. | ||
|
|
||
| This information is essential for the operator to discover the plugin's | ||
| capabilities during startup. | ||
|
|
||
| The Identity interface provides: | ||
|
|
||
| - A mechanism for plugins to report readiness probes. Readiness is a | ||
| prerequisite for receiving events, and plugins are expected to always report | ||
| the most accurate readiness data available. | ||
| - The capabilities reported by the plugin, which determine the subsequent calls | ||
| the plugin will receive. | ||
| - Metadata about the plugin. | ||
|
|
||
| [API reference](https://github.com/cloudnative-pg/cnpg-i/blob/main/proto/identity.proto) | ||
|
|
||
| ### Capabilities | ||
|
|
||
| This plugin implements the Operator and the Lifecycle capabilities. | ||
|
|
||
| #### Operator | ||
|
|
||
| This feature enables the plugin to receive events about the cluster creation and | ||
| mutations, this is defined by the following | ||
|
|
||
| ``` proto | ||
| // ValidateCreate improves the behavior of the validating webhook that | ||
| // is called on creation of the Cluster resources | ||
| rpc ValidateClusterCreate(OperatorValidateClusterCreateRequest) returns (OperatorValidateClusterCreateResult) {} | ||
|
|
||
| // ValidateClusterChange improves the behavior of the validating webhook of | ||
| // is called on updates of the Cluster resources | ||
| rpc ValidateClusterChange(OperatorValidateClusterChangeRequest) returns (OperatorValidateClusterChangeResult) {} | ||
|
|
||
| // MutateCluster fills in the defaults inside a Cluster resource | ||
| rpc MutateCluster(OperatorMutateClusterRequest) returns (OperatorMutateClusterResult) {} | ||
| ``` | ||
|
|
||
| This interface allows plugins to implement important features like: | ||
|
|
||
| 1. validating the cluster manifest during the creation and mutations | ||
| (it is expected that the plugin validate the parameters assigned to their | ||
| configuration). | ||
|
|
||
| 2. mutating the cluster object before it is submitted to kubernetes API server, | ||
| for example to set default values for the plugin parameters. | ||
|
|
||
| [API reference](https://github.com/cloudnative-pg/cnpg-i/blob/main/proto/operator.proto) | ||
|
|
||
| The wal-replica plugin is using this to validate used-defined parameters, and to | ||
| set default values for the labels and annotations applied by the plugin if not | ||
| specified by the user. | ||
|
|
||
| #### Lifecycle | ||
|
|
||
| This feature enables the plugin to receive events and create patches for | ||
| Kubernetes resources `before` they are submitted to the API server. | ||
|
|
||
| To use this feature, the plugin must specify the resource and operation it wants | ||
| to be notified of. | ||
|
|
||
| Some examples of what it can be achieved through the lifecycle: | ||
|
|
||
| - add volume, volume mounts, sidecar containers, labels, annotations to pods, | ||
| especially necessary when implementing custom backup solutions | ||
| - modify any resource with some annotations or labels | ||
| - add/remove finalizers | ||
|
|
||
| [API reference](https://github.com/cloudnative-pg/cnpg-i/blob/main/proto/operator_lifecycle.proto): | ||
|
|
||
| The wal-replica plugin is using this to add labels, annotations and a sidecar | ||
| to the pods. | ||
|
|
||
| ## Implementation | ||
|
|
||
| ### Identity | ||
|
|
||
| 1. Define a struct inside the `internal/identity` package that implements | ||
| the `pluginhelper.IdentityServer` interface. | ||
|
|
||
| 2. Implement the following methods: | ||
|
|
||
| - `GetPluginMetadata`: return human-readable information about the plugin. | ||
| - `GetPluginCapabilities`: specify the features supported by the plugin. In | ||
| the wal-replica example, the | ||
| `PluginCapability_Service_TYPE_LIFECYCLE_SERVICE` is defined in the | ||
| corresponding Go [file](../internal/lifecycle/lifecycle.go). | ||
| - `Probe`: indicate whether the plugin is ready to serve requests; this | ||
| example is stateless, so it will always be ready. | ||
|
|
||
| ### Lifecycle | ||
|
|
||
| This example implements the lifecycle service capabilities to add labels and | ||
| annotations to the pods. The `OperatorLifecycleServer` interface is implemented | ||
| inside the `internal/lifecycle` package. | ||
|
|
||
| The `OperatorLifecycleServer` interface requires several methods: | ||
|
|
||
| - `GetCapabilities`: describe the resources and operations the plugin should be | ||
| notified for | ||
|
|
||
| - `LifecycleHook`: is invoked for every operation against the Kubernetes API | ||
| server that matches the specifications returned by `GetCapabilities` | ||
|
|
||
| In this function, the plugin is expected to do pattern matching using | ||
| the `Kind` and the operation `Type` and proceed with the proper logic. | ||
|
|
||
| ### Operator | ||
|
|
||
| The operator interface offers a way for the plugin to interact with the Cluster | ||
| resource webhooks. | ||
|
|
||
| Do that, the plugin should implement | ||
| the [operator](https://github.com/cloudnative-pg/cnpg-i/blob/main/proto/operator.proto) | ||
| interface, specifically the `MutateCluster`, `ValidateClusterCreate`, | ||
| and `ValidateClusterChange` rpc calls. | ||
|
|
||
| - `MutateCluster`: enriches the plugin defaulting webhook | ||
|
|
||
| - `ValidateClusterCreate` and `ValidateClusterChange`: enriches the plugin | ||
| validation logic. | ||
|
|
||
| The package `internal/operator` implements this interface. | ||
|
|
||
| ### Startup Command | ||
|
|
||
| The plugin runs in its own pod, and its main command is implemented in | ||
| the `main.go` file. | ||
|
|
||
| This function uses the plugin helper library to create a GRPC server and manage | ||
| TLS. | ||
|
|
||
| Plugin developers are expected to use the `pluginhelper.CreateMainCmd` | ||
| to implement the `main` function, passing an implemented `Identity` | ||
| struct. | ||
|
|
||
| Further implementations can be registered within the callback function. | ||
|
|
||
| In the example we propose, that's done for **operator** and for the | ||
| **lifecycle** services in [file](../cmd/plugin/plugin.go): | ||
|
|
||
| ``` proto | ||
| operator.RegisterOperatorServer(server, operatorImpl.Implementation{}) | ||
| lifecycle.RegisterOperatorLifecycleServer(server, lifecycleImpl.Implementation{}) | ||
| ``` | ||
|
|
||
| ## Build and deploy the plugin | ||
|
|
||
| Users can test their own changes to the plugin by building a container image | ||
| running it inside a Kubernetes cluster with CloudNativePG and cert-manager | ||
| installed. | ||
|
|
||
| ### Local build | ||
|
|
||
| The repository provides a [`Taskfile`](https://taskfile.dev/) that contains | ||
| several helpful commands to test the plugin in | ||
| a [CNPG development environment](https://github.com/cloudnative-pg/cloudnative-pg/tree/main/contribute/e2e_testing_environment#the-local-kubernetes-cluster-for-testing). | ||
|
|
||
| By executing `task local-kind-deploy`, a container image containing the | ||
| executable of the repository will be built and loaded inside the kind cluster. | ||
|
|
||
| Having done that, the wal-replica plugin deployment will be applied. | ||
|
|
||
| ### CI/CD build | ||
|
|
||
| The repository provides a GitHub Actions workflow that, on pushes, builds a | ||
| container image and generates a manifest file that can be used to deploy the | ||
| plugin. The manifest is attached to the workflow run as an artifact, and can be | ||
| applied to the cluster. |
12 changes: 12 additions & 0 deletions
12
plugins/wal-replica/doc/examples/cluster-example-no-parameters.yaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,12 @@ | ||
| apiVersion: postgresql.cnpg.io/v1 | ||
| kind: Cluster | ||
| metadata: | ||
| name: cluster-example | ||
| spec: | ||
| instances: 3 | ||
|
|
||
| plugins: | ||
| - name: cnpg-i-wal-replica.documentdb.io | ||
|
|
||
| storage: | ||
| size: 1Gi |
23 changes: 23 additions & 0 deletions
23
plugins/wal-replica/doc/examples/cluster-example-with-mistake.yaml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| apiVersion: postgresql.cnpg.io/v1 | ||
| kind: Cluster | ||
| metadata: | ||
| name: cluster-example | ||
| spec: | ||
| instances: 3 | ||
|
|
||
| plugins: | ||
| - name: cnpg-i-wal-replica.documentdb.io | ||
| parameters: | ||
| labels: | | ||
| { | ||
| "first-label": "first-label-value", | ||
| "second-label": "second-label-value" | ||
| } | ||
| annotations: | | ||
| { | ||
| "first-annotation": "first-annotation-value", | ||
| this is a mistake | ||
| } | ||
|
|
||
| storage: | ||
| size: 1Gi |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,16 @@ | ||
| apiVersion: postgresql.cnpg.io/v1 | ||
| kind: Cluster | ||
| metadata: | ||
| name: cluster-example | ||
| spec: | ||
| instances: 1 | ||
|
|
||
| plugins: | ||
| - name: cnpg-i-wal-replica.documentdb.io | ||
| parameters: | ||
| replicationHost: cluster-example-rw | ||
|
|
||
| storage: | ||
| size: 1Gi | ||
|
|
||
| logLevel: "debug" |
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is this what CNPG does? Or could we go distroless?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can do distroless, cnpg uses gcr.io/distroless/static-debian12:nonroot