mirror of
https://github.com/cloudnative-pg/plugin-barman-cloud.git
synced 2026-01-11 05:13:10 +01:00
feat: operator plugin and manifests (#18)
Signed-off-by: Leonardo Cecchi <leonardo.cecchi@enterprisedb.com>
This commit is contained in:
parent
88e8762858
commit
dd6548c4a2
@ -1,177 +1,90 @@
|
||||
/*
|
||||
Copyright 2024.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package main contains the implementation of the CNPG-i operator plugin
|
||||
// Package main is the entrypoint of operator plugin
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"flag"
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
// +kubebuilder:scaffold:imports
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
"github.com/cloudnative-pg/machinery/pkg/log"
|
||||
"github.com/sourcegraph/conc/pool"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/healthz"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
||||
"sigs.k8s.io/controller-runtime/pkg/metrics/filters"
|
||||
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
||||
|
||||
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
|
||||
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator"
|
||||
"github.com/cloudnative-pg/plugin-barman-cloud/internal/controller"
|
||||
|
||||
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
|
||||
// to ensure that exec-entrypoint and run can make use of them.
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||
"github.com/cloudnative-pg/plugin-barman-cloud/internal/manager"
|
||||
)
|
||||
|
||||
var (
|
||||
scheme = runtime.NewScheme()
|
||||
setupLog = ctrl.Log.WithName("setup")
|
||||
)
|
||||
|
||||
func init() {
|
||||
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
|
||||
|
||||
utilruntime.Must(barmancloudv1.AddToScheme(scheme))
|
||||
// +kubebuilder:scaffold:scheme
|
||||
}
|
||||
|
||||
func main() {
|
||||
var metricsAddr string
|
||||
var enableLeaderElection bool
|
||||
var probeAddr string
|
||||
var secureMetrics bool
|
||||
var enableHTTP2 bool
|
||||
var tlsOpts []func(*tls.Config)
|
||||
flag.StringVar(&metricsAddr, "metrics-bind-address", "0", "The address the metrics endpoint binds to. "+
|
||||
"Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.")
|
||||
flag.StringVar(&probeAddr, "health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
|
||||
flag.BoolVar(&enableLeaderElection, "leader-elect", false,
|
||||
"Enable leader election for controller manager. "+
|
||||
"Enabling this will ensure there is only one active controller manager.")
|
||||
flag.BoolVar(&secureMetrics, "metrics-secure", true,
|
||||
"If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.")
|
||||
flag.BoolVar(&enableHTTP2, "enable-http2", false,
|
||||
"If set, HTTP/2 will be enabled for the metrics and webhook servers")
|
||||
opts := zap.Options{
|
||||
Development: true,
|
||||
}
|
||||
opts.BindFlags(flag.CommandLine)
|
||||
flag.Parse()
|
||||
cobra.EnableTraverseRunHooks = true
|
||||
|
||||
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
|
||||
|
||||
// if the enable-http2 flag is false (the default), http/2 should be disabled
|
||||
// due to its vulnerabilities. More specifically, disabling http/2 will
|
||||
// prevent from being vulnerable to the HTTP/2 Stream Cancellation and
|
||||
// Rapid Reset CVEs. For more information see:
|
||||
// - https://github.com/advisories/GHSA-qppj-fm5r-hxr3
|
||||
// - https://github.com/advisories/GHSA-4374-p667-p6c8
|
||||
disableHTTP2 := func(c *tls.Config) {
|
||||
setupLog.Info("disabling http/2")
|
||||
c.NextProtos = []string{"http/1.1"}
|
||||
logFlags := &log.Flags{}
|
||||
rootCmd := &cobra.Command{
|
||||
Use: "plugin-barman-cloud",
|
||||
PersistentPreRunE: func(_ *cobra.Command, _ []string) error {
|
||||
logFlags.ConfigureLogging()
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
if !enableHTTP2 {
|
||||
tlsOpts = append(tlsOpts, disableHTTP2)
|
||||
}
|
||||
logFlags.AddFlags(rootCmd.PersistentFlags())
|
||||
rootCmd.AddCommand(newOperatorCommand())
|
||||
|
||||
webhookServer := webhook.NewServer(webhook.Options{
|
||||
TLSOpts: tlsOpts,
|
||||
})
|
||||
|
||||
// Metrics endpoint is enabled in 'config/default/kustomization.yaml'. The Metrics options configure the server.
|
||||
// More info:
|
||||
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.0/pkg/metrics/server
|
||||
// - https://book.kubebuilder.io/reference/metrics.html
|
||||
metricsServerOptions := metricsserver.Options{
|
||||
BindAddress: metricsAddr,
|
||||
SecureServing: secureMetrics,
|
||||
// TODO(user): TLSOpts is used to allow configuring the TLS config used for the server. If certificates are
|
||||
// not provided, self-signed certificates will be generated by default. This option is not recommended for
|
||||
// production environments as self-signed certificates do not offer the same level of trust and security
|
||||
// as certificates issued by a trusted Certificate Authority (CA). The primary risk is potentially allowing
|
||||
// unauthorized access to sensitive metrics data. Consider replacing with CertDir, CertName, and KeyName
|
||||
// to provide certificates, ensuring the server communicates using trusted and secure certificates.
|
||||
TLSOpts: tlsOpts,
|
||||
}
|
||||
|
||||
if secureMetrics {
|
||||
// FilterProvider is used to protect the metrics endpoint with authn/authz.
|
||||
// These configurations ensure that only authorized users and service accounts
|
||||
// can access the metrics endpoint. The RBAC are configured in 'config/rbac/kustomization.yaml'. More info:
|
||||
// https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.0/pkg/metrics/filters#WithAuthenticationAndAuthorization
|
||||
metricsServerOptions.FilterProvider = filters.WithAuthenticationAndAuthorization
|
||||
}
|
||||
|
||||
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
|
||||
Scheme: scheme,
|
||||
Metrics: metricsServerOptions,
|
||||
WebhookServer: webhookServer,
|
||||
HealthProbeBindAddress: probeAddr,
|
||||
LeaderElection: enableLeaderElection,
|
||||
LeaderElectionID: "822e3f5c.cnpg.io",
|
||||
// LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily
|
||||
// when the Manager ends. This requires the binary to immediately end when the
|
||||
// Manager is stopped, otherwise, this setting is unsafe. Setting this significantly
|
||||
// speeds up voluntary leader transitions as the new leader don't have to wait
|
||||
// LeaseDuration time first.
|
||||
//
|
||||
// In the default scaffold provided, the program ends immediately after
|
||||
// the manager stops, so would be fine to enable this option. However,
|
||||
// if you are doing or is intended to do any operation such as perform cleanups
|
||||
// after the manager stops then its usage might be unsafe.
|
||||
// LeaderElectionReleaseOnCancel: true,
|
||||
})
|
||||
if err != nil {
|
||||
setupLog.Error(err, "unable to start manager")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err = (&controller.ObjectStoreReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "ObjectStore")
|
||||
os.Exit(1)
|
||||
}
|
||||
// +kubebuilder:scaffold:builder
|
||||
|
||||
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
|
||||
setupLog.Error(err, "unable to set up health check")
|
||||
os.Exit(1)
|
||||
}
|
||||
if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
|
||||
setupLog.Error(err, "unable to set up ready check")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err := mgr.Add(&operator.CNPGI{}); err != nil {
|
||||
setupLog.Error(err, "unable to create CNPGI webserver")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
setupLog.Info("starting manager")
|
||||
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
|
||||
setupLog.Error(err, "problem running manager")
|
||||
if err := rootCmd.Execute(); err != nil {
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func newOperatorCommand() *cobra.Command {
|
||||
cmd := operator.NewCommand()
|
||||
cmd.Use = "operator"
|
||||
cmd.Short = "Starts the BarmanObjectStore reconciler and the Barman Cloud CNPG-i plugin"
|
||||
grpcServer := cmd.RunE
|
||||
|
||||
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||
ctrl.SetupSignalHandler()
|
||||
operatorPool := pool.
|
||||
New().
|
||||
WithContext(cmd.Context()).
|
||||
WithCancelOnError().
|
||||
WithFirstError()
|
||||
operatorPool.Go(func(ctx context.Context) error {
|
||||
cmd.SetContext(ctx)
|
||||
|
||||
if len(viper.GetString("sidecar-image")) == 0 {
|
||||
return fmt.Errorf("missing required SIDECAR_IMAGE environment variable")
|
||||
}
|
||||
|
||||
err := grpcServer(cmd, args)
|
||||
return err
|
||||
})
|
||||
operatorPool.Go(manager.Start)
|
||||
return operatorPool.Wait()
|
||||
}
|
||||
|
||||
cmd.Flags().String("metrics-bind-address", "0", "The address the metrics endpoint binds to. "+
|
||||
"Use :8443 for HTTPS or :8080 for HTTP, or leave as 0 to disable the metrics service.")
|
||||
_ = viper.BindPFlag("metrics-bind-address", cmd.Flags().Lookup("metrics-bind-address"))
|
||||
|
||||
cmd.Flags().String("health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
|
||||
_ = viper.BindPFlag("health-probe-bind-address", cmd.Flags().Lookup("health-probe-bind-address"))
|
||||
|
||||
cmd.Flags().Bool("leader-elect", false,
|
||||
"Enable leader election for controller manager. "+
|
||||
"Enabling this will ensure there is only one active controller manager.")
|
||||
_ = viper.BindPFlag("leader-elect", cmd.Flags().Lookup("leader-elect"))
|
||||
|
||||
cmd.Flags().Bool("metrics-secure", true,
|
||||
"If set, the metrics endpoint is served securely via HTTPS. Use --metrics-secure=false to use HTTP instead.")
|
||||
_ = viper.BindPFlag("metrics-secure", cmd.Flags().Lookup("metrics-secure"))
|
||||
|
||||
cmd.Flags().Bool("enable-http2", false,
|
||||
"If set, HTTP/2 will be enabled for the metrics and webhook servers")
|
||||
_ = viper.BindPFlag("enable-http2", cmd.Flags().Lookup("enable-http2"))
|
||||
|
||||
_ = viper.BindEnv("sidecar-image", "SIDECAR_IMAGE")
|
||||
|
||||
return cmd
|
||||
}
|
||||
|
||||
@ -91,5 +91,5 @@ spec:
|
||||
requests:
|
||||
cpu: 10m
|
||||
memory: 64Mi
|
||||
serviceAccountName: controller-manager
|
||||
serviceAccountName: plugin-barman-cloud
|
||||
terminationGracePeriodSeconds: 10
|
||||
|
||||
@ -11,5 +11,5 @@ roleRef:
|
||||
name: leader-election-role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: controller-manager
|
||||
namespace: system
|
||||
name: plugin-barman-cloud
|
||||
namespace: cnpg-system
|
||||
|
||||
@ -8,5 +8,5 @@ roleRef:
|
||||
name: metrics-auth-role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: controller-manager
|
||||
namespace: system
|
||||
name: plugin-barman-cloud
|
||||
namespace: cnpg-system
|
||||
|
||||
@ -11,5 +11,5 @@ roleRef:
|
||||
name: manager-role
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: controller-manager
|
||||
namespace: system
|
||||
name: plugin-barman-cloud
|
||||
namespace: cnpg-system
|
||||
|
||||
@ -4,5 +4,4 @@ metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: plugin-barman-cloud
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: controller-manager
|
||||
namespace: system
|
||||
name: plugin-barman-cloud
|
||||
|
||||
12
docs/examples/cluster-example.yaml
Normal file
12
docs/examples/cluster-example.yaml
Normal file
@ -0,0 +1,12 @@
|
||||
apiVersion: postgresql.cnpg.io/v1
|
||||
kind: Cluster
|
||||
metadata:
|
||||
name: cluster-example
|
||||
spec:
|
||||
instances: 3
|
||||
|
||||
plugins:
|
||||
- name: barman-cloud.cloudnative-pg.io
|
||||
|
||||
storage:
|
||||
size: 1Gi
|
||||
9
go.mod
9
go.mod
@ -10,7 +10,11 @@ require (
|
||||
github.com/cloudnative-pg/machinery v0.0.0-20241001075747-34c8797af80f
|
||||
github.com/onsi/ginkgo/v2 v2.20.2
|
||||
github.com/onsi/gomega v1.34.2
|
||||
github.com/sourcegraph/conc v0.3.0
|
||||
github.com/spf13/cobra v1.8.1
|
||||
github.com/spf13/viper v1.19.0
|
||||
google.golang.org/grpc v1.67.1
|
||||
k8s.io/api v0.31.1
|
||||
k8s.io/apimachinery v0.31.1
|
||||
k8s.io/client-go v0.31.1
|
||||
sigs.k8s.io/controller-runtime v0.19.0
|
||||
@ -75,12 +79,10 @@ require (
|
||||
github.com/robfig/cron v1.2.0 // indirect
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/snorwin/jsonpatch v1.5.0 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/spf13/cobra v1.8.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.19.0 // indirect
|
||||
github.com/stoewer/go-strcase v1.3.0 // indirect
|
||||
github.com/subosito/gotenv v1.6.0 // indirect
|
||||
github.com/thoas/go-funk v0.9.3 // indirect
|
||||
@ -112,7 +114,6 @@ require (
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/api v0.31.1 // indirect
|
||||
k8s.io/apiextensions-apiserver v0.31.0 // indirect
|
||||
k8s.io/apiserver v0.31.0 // indirect
|
||||
k8s.io/component-base v0.31.0 // indirect
|
||||
|
||||
4
go.sum
4
go.sum
@ -43,6 +43,8 @@ github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nos
|
||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/go-faker/faker/v4 v4.4.1 h1:LY1jDgjVkBZWIhATCt+gkl0x9i/7wC61gZx73GTFb+Q=
|
||||
github.com/go-faker/faker/v4 v4.4.1/go.mod h1:HRLrjis+tYsbFtIHufEPTAIzcZiRu0rS9EYl2Ccwme4=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
@ -155,6 +157,8 @@ github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6ke
|
||||
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||
github.com/snorwin/jsonpatch v1.5.0 h1:0m56YSt9cHiJOn8U+OcqdPGcDQZmhPM/zsG7Dv5QQP0=
|
||||
github.com/snorwin/jsonpatch v1.5.0/go.mod h1:e0IDKlyFBLTFPqM0wa79dnMwjMs3XFvmKcrgCRpDqok=
|
||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||
|
||||
@ -8,6 +8,7 @@ import (
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
|
||||
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata"
|
||||
)
|
||||
|
||||
// IdentityImplementation implements IdentityServer
|
||||
@ -22,7 +23,7 @@ func (i IdentityImplementation) GetPluginMetadata(
|
||||
_ context.Context,
|
||||
_ *identity.GetPluginMetadataRequest,
|
||||
) (*identity.GetPluginMetadataResponse, error) {
|
||||
return &Data, nil
|
||||
return &metadata.Data, nil
|
||||
}
|
||||
|
||||
// GetPluginCapabilities implements IdentityServer
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
package instance
|
||||
package metadata
|
||||
|
||||
import "github.com/cloudnative-pg/cnpg-i/pkg/identity"
|
||||
|
||||
3
internal/cnpgi/metadata/doc.go
Normal file
3
internal/cnpgi/metadata/doc.go
Normal file
@ -0,0 +1,3 @@
|
||||
// Package metadata contains the common metadata on the operator
|
||||
// and on the instance manager
|
||||
package metadata
|
||||
@ -1,18 +0,0 @@
|
||||
package operator
|
||||
|
||||
import "github.com/cloudnative-pg/cnpg-i/pkg/identity"
|
||||
|
||||
// PluginName is the name of this plugin
|
||||
const PluginName = "operator.barman-cloud.cloudnative-pg.io"
|
||||
|
||||
// Data is the metadata of this plugin.
|
||||
var Data = identity.GetPluginMetadataResponse{
|
||||
Name: PluginName,
|
||||
Version: "0.0.1",
|
||||
DisplayName: "BarmanCloudOperator",
|
||||
ProjectUrl: "https://github.com/cloudnative-pg/plugin-barman-cloud",
|
||||
RepositoryUrl: "https://github.com/cloudnative-pg/plugin-barman-cloud",
|
||||
License: "APACHE 2.0",
|
||||
LicenseUrl: "https://github.com/cloudnative-pg/plugin-barman-cloud/LICENSE",
|
||||
Maturity: "alpha",
|
||||
}
|
||||
@ -4,6 +4,8 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/cloudnative-pg/cnpg-i/pkg/identity"
|
||||
|
||||
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata"
|
||||
)
|
||||
|
||||
// IdentityImplementation is the implementation of the CNPG-i
|
||||
@ -17,7 +19,7 @@ func (i IdentityImplementation) GetPluginMetadata(
|
||||
_ context.Context,
|
||||
_ *identity.GetPluginMetadataRequest,
|
||||
) (*identity.GetPluginMetadataResponse, error) {
|
||||
return &Data, nil
|
||||
return &metadata.Data, nil
|
||||
}
|
||||
|
||||
// GetPluginCapabilities implements identity
|
||||
|
||||
79
internal/cnpgi/operator/lifecycle.go
Normal file
79
internal/cnpgi/operator/lifecycle.go
Normal file
@ -0,0 +1,79 @@
|
||||
package operator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/cloudnative-pg/cnpg-i-machinery/pkg/pluginhelper/decoder"
|
||||
"github.com/cloudnative-pg/cnpg-i-machinery/pkg/pluginhelper/object"
|
||||
"github.com/cloudnative-pg/cnpg-i/pkg/lifecycle"
|
||||
"github.com/cloudnative-pg/machinery/pkg/log"
|
||||
"github.com/spf13/viper"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
// LifecycleImplementation is the implementation of the lifecycle handler
|
||||
type LifecycleImplementation struct {
|
||||
lifecycle.UnimplementedOperatorLifecycleServer
|
||||
}
|
||||
|
||||
// GetCapabilities exposes the lifecycle capabilities
|
||||
func (impl LifecycleImplementation) GetCapabilities(
|
||||
_ context.Context,
|
||||
_ *lifecycle.OperatorLifecycleCapabilitiesRequest,
|
||||
) (*lifecycle.OperatorLifecycleCapabilitiesResponse, error) {
|
||||
return &lifecycle.OperatorLifecycleCapabilitiesResponse{
|
||||
LifecycleCapabilities: []*lifecycle.OperatorLifecycleCapabilities{
|
||||
{
|
||||
Group: "",
|
||||
Kind: "Pod",
|
||||
OperationTypes: []*lifecycle.OperatorOperationType{
|
||||
{
|
||||
Type: lifecycle.OperatorOperationType_TYPE_CREATE,
|
||||
},
|
||||
{
|
||||
Type: lifecycle.OperatorOperationType_TYPE_PATCH,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
// LifecycleHook is called when creating Kubernetes services
|
||||
func (impl LifecycleImplementation) LifecycleHook(
|
||||
ctx context.Context,
|
||||
request *lifecycle.OperatorLifecycleRequest,
|
||||
) (*lifecycle.OperatorLifecycleResponse, error) {
|
||||
contextLogger := log.FromContext(ctx).WithName("plugin-barman-cloud-lifecycle")
|
||||
|
||||
operation := request.GetOperationType().GetType().Enum()
|
||||
if operation == nil {
|
||||
return nil, errors.New("no operation set")
|
||||
}
|
||||
|
||||
pod, err := decoder.DecodePodJSON(request.GetObjectDefinition())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mutatedPod := pod.DeepCopy()
|
||||
err = object.InjectPluginSidecar(mutatedPod, &corev1.Container{
|
||||
Name: "plugin-barman-cloud",
|
||||
Image: viper.GetString("sidecar-image"),
|
||||
}, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
patch, err := object.CreatePatch(mutatedPod, pod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TODO: change to debug
|
||||
contextLogger.Info("generated patch", "content", string(patch))
|
||||
return &lifecycle.OperatorLifecycleResponse{
|
||||
JsonPatch: patch,
|
||||
}, nil
|
||||
}
|
||||
@ -1,26 +1,21 @@
|
||||
package operator
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cloudnative-pg/cnpg-i-machinery/pkg/pluginhelper/http"
|
||||
"github.com/cloudnative-pg/cnpg-i/pkg/identity"
|
||||
"github.com/cloudnative-pg/cnpg-i/pkg/lifecycle"
|
||||
"github.com/cloudnative-pg/cnpg-i/pkg/reconciler"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
// CNPGI is the implementation of the Operator plugin
|
||||
type CNPGI struct{}
|
||||
|
||||
// Start starts the GRPC server
|
||||
func (c *CNPGI) Start(ctx context.Context) error {
|
||||
// NewCommand creates the command to start the GRPC server
|
||||
// of the operator plugin
|
||||
func NewCommand() *cobra.Command {
|
||||
cmd := http.CreateMainCmd(IdentityImplementation{}, func(server *grpc.Server) error {
|
||||
// Register the declared implementations
|
||||
identity.RegisterIdentityServer(server, IdentityImplementation{})
|
||||
reconciler.RegisterReconcilerHooksServer(server, ReconcilerImplementation{})
|
||||
lifecycle.RegisterOperatorLifecycleServer(server, LifecycleImplementation{})
|
||||
return nil
|
||||
})
|
||||
cmd.Use = "plugin-operator"
|
||||
|
||||
return cmd.ExecuteContext(ctx) //nolint:wrapcheck
|
||||
cmd.Use = "plugin"
|
||||
return cmd
|
||||
}
|
||||
|
||||
161
internal/manager/manager.go
Normal file
161
internal/manager/manager.go
Normal file
@ -0,0 +1,161 @@
|
||||
/*
|
||||
Copyright 2024.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
*/
|
||||
|
||||
// Package manager contains the implementation of the ObjectStore controller manager
|
||||
package manager
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"flag"
|
||||
|
||||
// +kubebuilder:scaffold:imports
|
||||
"github.com/spf13/viper"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
|
||||
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/healthz"
|
||||
"sigs.k8s.io/controller-runtime/pkg/log/zap"
|
||||
"sigs.k8s.io/controller-runtime/pkg/metrics/filters"
|
||||
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
||||
|
||||
barmancloudv1 "github.com/cloudnative-pg/plugin-barman-cloud/api/v1"
|
||||
"github.com/cloudnative-pg/plugin-barman-cloud/internal/controller"
|
||||
|
||||
// Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.)
|
||||
// to ensure that exec-entrypoint and run can make use of them.
|
||||
_ "k8s.io/client-go/plugin/pkg/client/auth"
|
||||
)
|
||||
|
||||
var (
|
||||
scheme = runtime.NewScheme()
|
||||
setupLog = ctrl.Log.WithName("setup")
|
||||
)
|
||||
|
||||
func init() {
|
||||
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
|
||||
|
||||
utilruntime.Must(barmancloudv1.AddToScheme(scheme))
|
||||
// +kubebuilder:scaffold:scheme
|
||||
}
|
||||
|
||||
// Start starts the manager
|
||||
func Start(ctx context.Context) error {
|
||||
var tlsOpts []func(*tls.Config)
|
||||
|
||||
opts := zap.Options{
|
||||
Development: true,
|
||||
}
|
||||
opts.BindFlags(flag.CommandLine)
|
||||
flag.Parse()
|
||||
|
||||
ctrl.SetLogger(zap.New(zap.UseFlagOptions(&opts)))
|
||||
|
||||
// if the enable-http2 flag is false (the default), http/2 should be disabled
|
||||
// due to its vulnerabilities. More specifically, disabling http/2 will
|
||||
// prevent from being vulnerable to the HTTP/2 Stream Cancellation and
|
||||
// Rapid Reset CVEs. For more information see:
|
||||
// - https://github.com/advisories/GHSA-qppj-fm5r-hxr3
|
||||
// - https://github.com/advisories/GHSA-4374-p667-p6c8
|
||||
disableHTTP2 := func(c *tls.Config) {
|
||||
setupLog.Info("disabling http/2")
|
||||
c.NextProtos = []string{"http/1.1"}
|
||||
}
|
||||
|
||||
if !viper.GetBool("enable-http2") {
|
||||
tlsOpts = append(tlsOpts, disableHTTP2)
|
||||
}
|
||||
|
||||
webhookServer := webhook.NewServer(webhook.Options{
|
||||
TLSOpts: tlsOpts,
|
||||
})
|
||||
|
||||
// Metrics endpoint is enabled in 'config/default/kustomization.yaml'. The Metrics options configure the server.
|
||||
// More info:
|
||||
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.0/pkg/metrics/server
|
||||
// - https://book.kubebuilder.io/reference/metrics.html
|
||||
metricsServerOptions := metricsserver.Options{
|
||||
BindAddress: viper.GetString("metrics-bind-address"),
|
||||
SecureServing: viper.GetBool("metrics-secure"),
|
||||
// TODO(user): TLSOpts is used to allow configuring the TLS config used for the server. If certificates are
|
||||
// not provided, self-signed certificates will be generated by default. This option is not recommended for
|
||||
// production environments as self-signed certificates do not offer the same level of trust and security
|
||||
// as certificates issued by a trusted Certificate Authority (CA). The primary risk is potentially allowing
|
||||
// unauthorized access to sensitive metrics data. Consider replacing with CertDir, CertName, and KeyName
|
||||
// to provide certificates, ensuring the server communicates using trusted and secure certificates.
|
||||
TLSOpts: tlsOpts,
|
||||
}
|
||||
|
||||
if viper.GetBool("metrics-secure") {
|
||||
// FilterProvider is used to protect the metrics endpoint with authn/authz.
|
||||
// These configurations ensure that only authorized users and service accounts
|
||||
// can access the metrics endpoint. The RBAC are configured in 'config/rbac/kustomization.yaml'. More info:
|
||||
// https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.0/pkg/metrics/filters#WithAuthenticationAndAuthorization
|
||||
metricsServerOptions.FilterProvider = filters.WithAuthenticationAndAuthorization
|
||||
}
|
||||
|
||||
mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{
|
||||
Scheme: scheme,
|
||||
Metrics: metricsServerOptions,
|
||||
WebhookServer: webhookServer,
|
||||
HealthProbeBindAddress: viper.GetString("health-probe-bind-address"),
|
||||
LeaderElection: viper.GetBool("leader-elect"),
|
||||
LeaderElectionID: "822e3f5c.cnpg.io",
|
||||
// LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily
|
||||
// when the Manager ends. This requires the binary to immediately end when the
|
||||
// Manager is stopped, otherwise, this setting is unsafe. Setting this significantly
|
||||
// speeds up voluntary leader transitions as the new leader don't have to wait
|
||||
// LeaseDuration time first.
|
||||
//
|
||||
// In the default scaffold provided, the program ends immediately after
|
||||
// the manager stops, so would be fine to enable this option. However,
|
||||
// if you are doing or is intended to do any operation such as perform cleanups
|
||||
// after the manager stops then its usage might be unsafe.
|
||||
// LeaderElectionReleaseOnCancel: true,
|
||||
})
|
||||
if err != nil {
|
||||
setupLog.Error(err, "unable to start manager")
|
||||
return err
|
||||
}
|
||||
|
||||
if err = (&controller.ObjectStoreReconciler{
|
||||
Client: mgr.GetClient(),
|
||||
Scheme: mgr.GetScheme(),
|
||||
}).SetupWithManager(mgr); err != nil {
|
||||
setupLog.Error(err, "unable to create controller", "controller", "ObjectStore")
|
||||
return err
|
||||
}
|
||||
// +kubebuilder:scaffold:builder
|
||||
|
||||
if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil {
|
||||
setupLog.Error(err, "unable to set up health check")
|
||||
return err
|
||||
}
|
||||
if err := mgr.AddReadyzCheck("readyz", healthz.Ping); err != nil {
|
||||
setupLog.Error(err, "unable to set up ready check")
|
||||
return err
|
||||
}
|
||||
|
||||
setupLog.Info("starting manager")
|
||||
if err := mgr.Start(ctx); err != nil {
|
||||
setupLog.Error(err, "problem running manager")
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
6
kubernetes/certificate-issuer.yaml
Normal file
6
kubernetes/certificate-issuer.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Issuer
|
||||
metadata:
|
||||
name: selfsigned-issuer
|
||||
spec:
|
||||
selfSigned: {}
|
||||
19
kubernetes/client-certificate.yaml
Normal file
19
kubernetes/client-certificate.yaml
Normal file
@ -0,0 +1,19 @@
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: barman-cloud-client
|
||||
spec:
|
||||
secretName: barman-cloud-client-tls
|
||||
|
||||
commonName: "barman-cloud-client"
|
||||
duration: 2160h # 90d
|
||||
renewBefore: 360h # 15d
|
||||
|
||||
isCA: false
|
||||
usages:
|
||||
- client auth
|
||||
|
||||
issuerRef:
|
||||
name: selfsigned-issuer
|
||||
kind: Issuer
|
||||
group: cert-manager.io
|
||||
50
kubernetes/deployment.yaml
Normal file
50
kubernetes/deployment.yaml
Normal file
@ -0,0 +1,50 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
labels:
|
||||
app: barman-cloud
|
||||
name: barman-cloud
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: barman-cloud
|
||||
strategy: {}
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: barman-cloud
|
||||
spec:
|
||||
serviceAccountName: plugin-barman-cloud
|
||||
containers:
|
||||
- image: plugin-barman-cloud:latest
|
||||
name: barman-cloud
|
||||
ports:
|
||||
- containerPort: 9090
|
||||
protocol: TCP
|
||||
env:
|
||||
- name: SIDECAR_IMAGE
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
key: SIDECAR_IMAGE
|
||||
name: plugin-barman-cloud
|
||||
args:
|
||||
- operator
|
||||
- --server-cert=/server/tls.crt
|
||||
- --server-key=/server/tls.key
|
||||
- --client-cert=/client/tls.crt
|
||||
- --server-address=:9090
|
||||
- --leader-elect
|
||||
volumeMounts:
|
||||
- mountPath: /server
|
||||
name: server
|
||||
- mountPath: /client
|
||||
name: client
|
||||
resources: {}
|
||||
volumes:
|
||||
- name: server
|
||||
secret:
|
||||
secretName: barman-cloud-server-tls
|
||||
- name: client
|
||||
secret:
|
||||
secretName: barman-cloud-client-tls
|
||||
17
kubernetes/kustomization.yaml
Normal file
17
kubernetes/kustomization.yaml
Normal file
@ -0,0 +1,17 @@
|
||||
apiVersion: kustomize.config.k8s.io/v1beta1
|
||||
kind: Kustomization
|
||||
namespace: cnpg-system
|
||||
resources:
|
||||
- certificate-issuer.yaml
|
||||
- client-certificate.yaml
|
||||
- deployment.yaml
|
||||
- server-certificate.yaml
|
||||
- service.yaml
|
||||
- ../config/crd
|
||||
- ../config/rbac
|
||||
images:
|
||||
- name: plugin-barman-cloud
|
||||
secretGenerator:
|
||||
- literals:
|
||||
- SIDECAR_IMAGE=plugin-sidecar
|
||||
name: plugin-barman-cloud
|
||||
21
kubernetes/server-certificate.yaml
Normal file
21
kubernetes/server-certificate.yaml
Normal file
@ -0,0 +1,21 @@
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
name: barman-cloud-server
|
||||
spec:
|
||||
secretName: barman-cloud-server-tls
|
||||
commonName: barman-cloud
|
||||
dnsNames:
|
||||
- barman-cloud
|
||||
|
||||
duration: 2160h # 90d
|
||||
renewBefore: 360h # 15d
|
||||
|
||||
isCA: false
|
||||
usages:
|
||||
- server auth
|
||||
|
||||
issuerRef:
|
||||
name: selfsigned-issuer
|
||||
kind: Issuer
|
||||
group: cert-manager.io
|
||||
18
kubernetes/service.yaml
Normal file
18
kubernetes/service.yaml
Normal file
@ -0,0 +1,18 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
labels:
|
||||
app: barman-cloud
|
||||
cnpg.io/pluginName: barman-cloud.cloudnative-pg.io
|
||||
annotations:
|
||||
cnpg.io/pluginClientSecret: barman-cloud-client-tls
|
||||
cnpg.io/pluginServerSecret: barman-cloud-server-tls
|
||||
cnpg.io/pluginPort: "9090"
|
||||
name: barman-cloud
|
||||
spec:
|
||||
ports:
|
||||
- port: 9090
|
||||
protocol: TCP
|
||||
targetPort: 9090
|
||||
selector:
|
||||
app: barman-cloud
|
||||
22
scripts/run.sh
Executable file
22
scripts/run.sh
Executable file
@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -eu
|
||||
|
||||
cd "$(dirname "$0")/.." || exit
|
||||
|
||||
if [ -f .env ]; then
|
||||
source .env
|
||||
fi
|
||||
|
||||
current_context=$(kubectl config view --raw -o json | jq -r '."current-context"' | sed "s/kind-//")
|
||||
operator_image=$(KIND_CLUSTER_NAME="$current_context" KO_DOCKER_REPO=kind.local ko build -BP ./cmd/operator)
|
||||
instance_image=$(KIND_CLUSTER_NAME="$current_context" KO_DOCKER_REPO=kind.local ko build -BP ./cmd/instance)
|
||||
|
||||
(
|
||||
cd kubernetes;
|
||||
kustomize edit set image "plugin-barman-cloud=$operator_image"
|
||||
kustomize edit set secret plugin-barman-cloud "--from-literal=SIDECAR_IMAGE=$instance_image"
|
||||
)
|
||||
|
||||
# Now we deploy the plugin inside the `cnpg-system` workspace
|
||||
kubectl apply -k kubernetes/
|
||||
Loading…
Reference in New Issue
Block a user