mirror of
https://github.com/cloudnative-pg/plugin-barman-cloud.git
synced 2026-01-11 13:23:09 +01:00
feat: sidecar role and rolebinding (#23)
Signed-off-by: Leonardo Cecchi <leonardo.cecchi@enterprisedb.com>
This commit is contained in:
parent
ea6ee30d2e
commit
2f62d539c9
2
Makefile
2
Makefile
@ -45,7 +45,7 @@ help: ## Display this help.
|
||||
|
||||
.PHONY: manifests
|
||||
manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and CustomResourceDefinition objects.
|
||||
$(CONTROLLER_GEN) rbac:roleName=manager-role crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases
|
||||
$(CONTROLLER_GEN) rbac:roleName=plugin-barman-cloud crd webhook paths="./..." output:crd:artifacts:config=config/crd/bases
|
||||
|
||||
.PHONY: generate
|
||||
generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations.
|
||||
|
||||
@ -29,7 +29,7 @@ func (in *ObjectStore) DeepCopyInto(out *ObjectStore) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
out.Spec = in.Spec
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
out.Status = in.Status
|
||||
}
|
||||
|
||||
@ -86,6 +86,7 @@ func (in *ObjectStoreList) DeepCopyObject() runtime.Object {
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *ObjectStoreSpec) DeepCopyInto(out *ObjectStoreSpec) {
|
||||
*out = *in
|
||||
in.Configuration.DeepCopyInto(&out.Configuration)
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ObjectStoreSpec.
|
||||
|
||||
@ -2,17 +2,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/cloudnative-pg/machinery/pkg/log"
|
||||
"github.com/sourcegraph/conc/pool"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
|
||||
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator"
|
||||
"github.com/cloudnative-pg/plugin-barman-cloud/internal/operator/manager"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -21,6 +18,14 @@ func main() {
|
||||
logFlags := &log.Flags{}
|
||||
rootCmd := &cobra.Command{
|
||||
Use: "plugin-barman-cloud",
|
||||
Short: "Starts the BarmanObjectStore reconciler and the Barman Cloud CNPG-i plugin",
|
||||
RunE: func(cmd *cobra.Command, _ []string) error {
|
||||
if len(viper.GetString("sidecar-image")) == 0 {
|
||||
return fmt.Errorf("missing required SIDECAR_IMAGE environment variable")
|
||||
}
|
||||
|
||||
return operator.Start(cmd.Context())
|
||||
},
|
||||
PersistentPreRunE: func(_ *cobra.Command, _ []string) error {
|
||||
logFlags.ConfigureLogging()
|
||||
return nil
|
||||
@ -28,61 +33,66 @@ func main() {
|
||||
}
|
||||
|
||||
logFlags.AddFlags(rootCmd.PersistentFlags())
|
||||
rootCmd.AddCommand(newOperatorCommand())
|
||||
|
||||
rootCmd.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", rootCmd.Flags().Lookup("metrics-bind-address"))
|
||||
|
||||
rootCmd.Flags().String("health-probe-bind-address", ":8081", "The address the probe endpoint binds to.")
|
||||
_ = viper.BindPFlag("health-probe-bind-address", rootCmd.Flags().Lookup("health-probe-bind-address"))
|
||||
|
||||
rootCmd.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", rootCmd.Flags().Lookup("leader-elect"))
|
||||
|
||||
rootCmd.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", rootCmd.Flags().Lookup("metrics-secure"))
|
||||
|
||||
rootCmd.Flags().Bool("enable-http2", false,
|
||||
"If set, HTTP/2 will be enabled for the metrics and webhook servers")
|
||||
_ = viper.BindPFlag("enable-http2", rootCmd.Flags().Lookup("enable-http2"))
|
||||
|
||||
rootCmd.Flags().String(
|
||||
"plugin-path",
|
||||
"",
|
||||
"The plugins socket path",
|
||||
)
|
||||
_ = viper.BindPFlag("plugin-path", rootCmd.Flags().Lookup("plugin-path"))
|
||||
|
||||
rootCmd.Flags().String(
|
||||
"server-cert",
|
||||
"",
|
||||
"The public key to be used for the server process",
|
||||
)
|
||||
_ = viper.BindPFlag("server-cert", rootCmd.Flags().Lookup("server-cert"))
|
||||
|
||||
rootCmd.Flags().String(
|
||||
"server-key",
|
||||
"",
|
||||
"The key to be used for the server process",
|
||||
)
|
||||
_ = viper.BindPFlag("server-key", rootCmd.Flags().Lookup("server-key"))
|
||||
|
||||
rootCmd.Flags().String(
|
||||
"client-cert",
|
||||
"",
|
||||
"The client public key to verify the connection",
|
||||
)
|
||||
_ = viper.BindPFlag("client-cert", rootCmd.Flags().Lookup("client-cert"))
|
||||
|
||||
rootCmd.Flags().String(
|
||||
"server-address",
|
||||
"",
|
||||
"The address where to listen (i.e. 0:9090)",
|
||||
)
|
||||
_ = viper.BindPFlag("server-address", rootCmd.Flags().Lookup("server-address"))
|
||||
|
||||
_ = viper.BindEnv("sidecar-image", "SIDECAR_IMAGE")
|
||||
|
||||
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 {
|
||||
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
|
||||
}
|
||||
|
||||
@ -17,7 +17,7 @@ spec:
|
||||
- name: v1
|
||||
schema:
|
||||
openAPIV3Schema:
|
||||
description: ObjectStore is the Schema for the objectstores API
|
||||
description: ObjectStore is the Schema for the objectstores API.
|
||||
properties:
|
||||
apiVersion:
|
||||
description: |-
|
||||
@ -37,7 +37,7 @@ spec:
|
||||
metadata:
|
||||
type: object
|
||||
spec:
|
||||
description: ObjectStoreSpec defines the desired state of ObjectStore
|
||||
description: ObjectStoreSpec defines the desired state of ObjectStore.
|
||||
properties:
|
||||
configuration:
|
||||
description: |-
|
||||
@ -382,7 +382,7 @@ spec:
|
||||
- configuration
|
||||
type: object
|
||||
status:
|
||||
description: ObjectStoreStatus defines the observed state of ObjectStore
|
||||
description: ObjectStoreStatus defines the observed state of ObjectStore.
|
||||
type: object
|
||||
type: object
|
||||
served: true
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: manager-role
|
||||
name: plugin-barman-cloud
|
||||
rules:
|
||||
- apiGroups:
|
||||
- barmancloud.cnpg.io
|
||||
@ -30,3 +30,15 @@ rules:
|
||||
- get
|
||||
- patch
|
||||
- update
|
||||
- apiGroups:
|
||||
- rbac.authorization.k8s.io
|
||||
resources:
|
||||
- rolebindings
|
||||
- roles
|
||||
verbs:
|
||||
- create
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
|
||||
@ -4,11 +4,11 @@ metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: plugin-barman-cloud
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: manager-rolebinding
|
||||
name: plugin-barman-cloud-binding
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: manager-role
|
||||
name: plugin-barman-cloud
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: plugin-barman-cloud
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
apiVersion: barmancloud.cnpg.io/v1
|
||||
kind: ObjectStore
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: plugin-barman-cloud
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: objectstore-sample
|
||||
spec:
|
||||
# TODO(user): Add fields here
|
||||
@ -8,7 +8,7 @@ spec:
|
||||
plugins:
|
||||
- name: barman-cloud.cloudnative-pg.io
|
||||
parameters:
|
||||
barmanObjectStore: minio-store
|
||||
barmanObjectName: minio-store
|
||||
|
||||
storage:
|
||||
size: 1Gi
|
||||
|
||||
23
docs/examples/minio-store.yaml
Normal file
23
docs/examples/minio-store.yaml
Normal file
@ -0,0 +1,23 @@
|
||||
apiVersion: barmancloud.cnpg.io/v1
|
||||
kind: ObjectStore
|
||||
metadata:
|
||||
name: minio-store
|
||||
spec:
|
||||
configuration:
|
||||
destinationPath: s3://backups/
|
||||
endpointURL: http://minio:9000
|
||||
s3Credentials:
|
||||
accessKeyId:
|
||||
name: minio
|
||||
key: ACCESS_KEY_ID
|
||||
secretAccessKey:
|
||||
name: minio
|
||||
key: ACCESS_SECRET_KEY
|
||||
wal:
|
||||
compression: gzip
|
||||
data:
|
||||
additionalCommandArgs:
|
||||
- "--min-chunk-size=5MB"
|
||||
- "--read-timeout=60"
|
||||
- "-vv"
|
||||
|
||||
20
docs/minio/minio-client.yaml
Normal file
20
docs/minio/minio-client.yaml
Normal file
@ -0,0 +1,20 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
labels:
|
||||
run: mc
|
||||
name: mc
|
||||
spec:
|
||||
containers:
|
||||
- env:
|
||||
- name: MC_HOST_minio
|
||||
value: http://chooJeiroroo2noquomei2uuceisheth:ongeiqueitohL0queeLohkiur2quaing@minio:9000
|
||||
image: minio/mc
|
||||
name: mc
|
||||
resources: {}
|
||||
# Keep the pod up to exec stuff on it
|
||||
command:
|
||||
- sleep
|
||||
- "3600"
|
||||
dnsPolicy: ClusterFirst
|
||||
restartPolicy: Always
|
||||
1
docs/minio/minio-delete.sh
Normal file
1
docs/minio/minio-delete.sh
Normal file
@ -0,0 +1 @@
|
||||
kubectl exec -ti mc -- mc rm -r --force minio/backups
|
||||
42
docs/minio/minio-deployment.yaml
Normal file
42
docs/minio/minio-deployment.yaml
Normal file
@ -0,0 +1,42 @@
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: minio
|
||||
labels:
|
||||
app: minio
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app: minio
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app: minio
|
||||
spec:
|
||||
containers:
|
||||
- name: minio
|
||||
image: minio/minio
|
||||
ports:
|
||||
- containerPort: 9000
|
||||
volumeMounts:
|
||||
- mountPath: /data
|
||||
name: data
|
||||
args:
|
||||
- server
|
||||
- /data
|
||||
env:
|
||||
- name: MINIO_ACCESS_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: minio
|
||||
key: ACCESS_KEY_ID
|
||||
- name: MINIO_SECRET_KEY
|
||||
valueFrom:
|
||||
secretKeyRef:
|
||||
name: minio
|
||||
key: ACCESS_SECRET_KEY
|
||||
volumes:
|
||||
- name: data
|
||||
persistentVolumeClaim:
|
||||
claimName: minio
|
||||
11
docs/minio/minio-pvc.yaml
Normal file
11
docs/minio/minio-pvc.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
apiVersion: v1
|
||||
kind: PersistentVolumeClaim
|
||||
metadata:
|
||||
name: minio
|
||||
spec:
|
||||
accessModes:
|
||||
- ReadWriteOnce
|
||||
volumeMode: Filesystem
|
||||
resources:
|
||||
requests:
|
||||
storage: 1Gi
|
||||
7
docs/minio/minio-secret.yaml
Normal file
7
docs/minio/minio-secret.yaml
Normal file
@ -0,0 +1,7 @@
|
||||
apiVersion: v1
|
||||
data:
|
||||
ACCESS_KEY_ID: Y2hvb0plaXJvcm9vMm5vcXVvbWVpMnV1Y2Vpc2hldGg=
|
||||
ACCESS_SECRET_KEY: b25nZWlxdWVpdG9oTDBxdWVlTG9oa2l1cjJxdWFpbmc=
|
||||
kind: Secret
|
||||
metadata:
|
||||
name: minio
|
||||
11
docs/minio/minio-service.yaml
Normal file
11
docs/minio/minio-service.yaml
Normal file
@ -0,0 +1,11 @@
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: minio
|
||||
spec:
|
||||
selector:
|
||||
app: minio
|
||||
ports:
|
||||
- protocol: TCP
|
||||
port: 9000
|
||||
targetPort: 9000
|
||||
2
go.mod
2
go.mod
@ -10,7 +10,6 @@ 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
|
||||
@ -80,6 +79,7 @@ require (
|
||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||
github.com/snorwin/jsonpatch v1.5.0 // indirect
|
||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.6.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
|
||||
71
internal/cnpgi/operator/config/config.go
Normal file
71
internal/cnpgi/operator/config/config.go
Normal file
@ -0,0 +1,71 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
|
||||
"github.com/cloudnative-pg/cnpg-i-machinery/pkg/pluginhelper/common"
|
||||
|
||||
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata"
|
||||
)
|
||||
|
||||
// ConfigurationError represents a mistake in the plugin configuration
|
||||
type ConfigurationError struct {
|
||||
messages []string
|
||||
}
|
||||
|
||||
// Error implements the error interface
|
||||
func (e *ConfigurationError) Error() string {
|
||||
return strings.Join(e.messages, ",")
|
||||
}
|
||||
|
||||
// NewConfigurationError creates a new empty configuration error
|
||||
func NewConfigurationError() *ConfigurationError {
|
||||
return &ConfigurationError{}
|
||||
}
|
||||
|
||||
// WithMessage adds a new error message to a potentially empty
|
||||
// ConfigurationError
|
||||
func (e *ConfigurationError) WithMessage(msg string) *ConfigurationError {
|
||||
if e == nil {
|
||||
return &ConfigurationError{
|
||||
messages: []string{msg},
|
||||
}
|
||||
}
|
||||
|
||||
return &ConfigurationError{
|
||||
messages: append(e.messages, msg),
|
||||
}
|
||||
}
|
||||
|
||||
// IsEmpty returns true if there's no error messages
|
||||
func (e *ConfigurationError) IsEmpty() bool {
|
||||
return len(e.messages) == 0
|
||||
}
|
||||
|
||||
// PluginConfiguration is the configuration of the plugin
|
||||
type PluginConfiguration struct {
|
||||
BarmanObjectName string
|
||||
}
|
||||
|
||||
// NewFromCluster extracts the configuration from the cluster
|
||||
func NewFromCluster(cluster *cnpgv1.Cluster) (*PluginConfiguration, error) {
|
||||
helper := common.NewPlugin(
|
||||
*cluster,
|
||||
metadata.PluginName,
|
||||
)
|
||||
|
||||
result := &PluginConfiguration{
|
||||
BarmanObjectName: helper.Parameters["barmanObjectName"],
|
||||
}
|
||||
|
||||
err := NewConfigurationError()
|
||||
if len(result.BarmanObjectName) == 0 {
|
||||
err = err.WithMessage("Missing barmanObjectName parameter")
|
||||
}
|
||||
|
||||
if err.IsEmpty() {
|
||||
return result, nil
|
||||
}
|
||||
return result, err
|
||||
}
|
||||
2
internal/cnpgi/operator/config/doc.go
Normal file
2
internal/cnpgi/operator/config/doc.go
Normal file
@ -0,0 +1,2 @@
|
||||
// Package config contains the functions to parse the plugin configuration
|
||||
package config
|
||||
@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/cloudnative-pg/cnpg-i-machinery/pkg/pluginhelper/common"
|
||||
"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"
|
||||
@ -12,7 +11,7 @@ import (
|
||||
"github.com/spf13/viper"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
|
||||
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/metadata"
|
||||
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/config"
|
||||
)
|
||||
|
||||
// LifecycleImplementation is the implementation of the lifecycle handler
|
||||
@ -65,12 +64,10 @@ func (impl LifecycleImplementation) LifecycleHook(
|
||||
return nil, err
|
||||
}
|
||||
|
||||
helper := common.NewPlugin(
|
||||
*cluster,
|
||||
metadata.PluginName,
|
||||
)
|
||||
|
||||
// TODO: Validation of the plugin configuration
|
||||
pluginConfiguration, err := config.NewFromCluster(cluster)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mutatedPod := pod.DeepCopy()
|
||||
err = object.InjectPluginSidecar(mutatedPod, &corev1.Container{
|
||||
@ -79,7 +76,7 @@ func (impl LifecycleImplementation) LifecycleHook(
|
||||
Env: []corev1.EnvVar{
|
||||
{
|
||||
Name: "BARMAN_OBJECT_NAME",
|
||||
Value: helper.Parameters["barmanObjectStore"],
|
||||
Value: pluginConfiguration.BarmanObjectName,
|
||||
},
|
||||
{
|
||||
// TODO: should we really use this one?
|
||||
|
||||
@ -14,15 +14,14 @@ 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
|
||||
package operator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"flag"
|
||||
|
||||
// +kubebuilder:scaffold:imports
|
||||
cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
|
||||
"github.com/cloudnative-pg/machinery/pkg/log"
|
||||
"github.com/spf13/viper"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
@ -30,13 +29,12 @@ import (
|
||||
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/operator/controller"
|
||||
"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.
|
||||
@ -47,8 +45,8 @@ var scheme = runtime.NewScheme()
|
||||
|
||||
func init() {
|
||||
utilruntime.Must(clientgoscheme.AddToScheme(scheme))
|
||||
|
||||
utilruntime.Must(barmancloudv1.AddToScheme(scheme))
|
||||
utilruntime.Must(cnpgv1.AddToScheme(scheme))
|
||||
// +kubebuilder:scaffold:scheme
|
||||
}
|
||||
|
||||
@ -58,14 +56,6 @@ 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
|
||||
@ -151,6 +141,18 @@ func Start(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := mgr.Add(&CNPGI{
|
||||
Client: mgr.GetClient(),
|
||||
PluginPath: viper.GetString("plugin-path"),
|
||||
ServerCertPath: viper.GetString("server-cert"),
|
||||
ServerKeyPath: viper.GetString("server-key"),
|
||||
ClientCertPath: viper.GetString("client-cert"),
|
||||
ServerAddress: viper.GetString("server-address"),
|
||||
}); err != nil {
|
||||
setupLog.Error(err, "unable to create CNPGI runnable")
|
||||
return err
|
||||
}
|
||||
|
||||
setupLog.Info("starting manager")
|
||||
if err := mgr.Start(ctx); err != nil {
|
||||
setupLog.Error(err, "problem running manager")
|
||||
@ -2,12 +2,24 @@ package operator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
cnpgv1 "github.com/cloudnative-pg/cloudnative-pg/api/v1"
|
||||
"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/reconciler"
|
||||
rbacv1 "k8s.io/api/rbac/v1"
|
||||
apierrs "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
ctrl "sigs.k8s.io/controller-runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
|
||||
"github.com/cloudnative-pg/plugin-barman-cloud/internal/cnpgi/operator/config"
|
||||
)
|
||||
|
||||
// ReconcilerImplementation implements the Reconciler capability
|
||||
type ReconcilerImplementation struct {
|
||||
Client client.Client
|
||||
reconciler.UnimplementedReconcilerHooksServer
|
||||
}
|
||||
|
||||
@ -30,9 +42,37 @@ func (r ReconcilerImplementation) GetCapabilities(
|
||||
|
||||
// Pre implements the reconciler interface
|
||||
func (r ReconcilerImplementation) Pre(
|
||||
_ context.Context,
|
||||
_ *reconciler.ReconcilerHooksRequest,
|
||||
ctx context.Context,
|
||||
request *reconciler.ReconcilerHooksRequest,
|
||||
) (*reconciler.ReconcilerHooksResult, error) {
|
||||
reconciledKind, err := object.GetKind(request.GetResourceDefinition())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if reconciledKind != "Cluster" {
|
||||
return &reconciler.ReconcilerHooksResult{
|
||||
Behavior: reconciler.ReconcilerHooksResult_BEHAVIOR_CONTINUE,
|
||||
}, nil
|
||||
}
|
||||
|
||||
cluster, err := decoder.DecodeClusterJSON(request.GetResourceDefinition())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
pluginConfiguration, err := config.NewFromCluster(cluster)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.ensureRole(ctx, cluster, pluginConfiguration.BarmanObjectName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := r.ensureRoleBinding(ctx, cluster); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &reconciler.ReconcilerHooksResult{
|
||||
Behavior: reconciler.ReconcilerHooksResult_BEHAVIOR_CONTINUE,
|
||||
}, nil
|
||||
@ -47,3 +87,126 @@ func (r ReconcilerImplementation) Post(
|
||||
Behavior: reconciler.ReconcilerHooksResult_BEHAVIOR_CONTINUE,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (r ReconcilerImplementation) ensureRole(
|
||||
ctx context.Context,
|
||||
cluster *cnpgv1.Cluster,
|
||||
barmanObjectName string,
|
||||
) error {
|
||||
var role rbacv1.Role
|
||||
if err := r.Client.Get(ctx, client.ObjectKey{
|
||||
Namespace: cluster.Namespace,
|
||||
Name: getRBACName(cluster.Name),
|
||||
}, &role); err != nil {
|
||||
if apierrs.IsNotFound(err) {
|
||||
return r.createRole(ctx, cluster, barmanObjectName)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: patch existing role
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r ReconcilerImplementation) ensureRoleBinding(
|
||||
ctx context.Context,
|
||||
cluster *cnpgv1.Cluster,
|
||||
) error {
|
||||
var role rbacv1.RoleBinding
|
||||
if err := r.Client.Get(ctx, client.ObjectKey{
|
||||
Namespace: cluster.Namespace,
|
||||
Name: getRBACName(cluster.Name),
|
||||
}, &role); err != nil {
|
||||
if apierrs.IsNotFound(err) {
|
||||
return r.createRoleBinding(ctx, cluster)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: patch existing role binding
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r ReconcilerImplementation) createRole(
|
||||
ctx context.Context,
|
||||
cluster *cnpgv1.Cluster,
|
||||
barmanObjectName string,
|
||||
) error {
|
||||
role := buildRole(cluster, barmanObjectName)
|
||||
if err := ctrl.SetControllerReference(cluster, role, r.Client.Scheme()); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.Client.Create(ctx, role)
|
||||
}
|
||||
|
||||
func (r ReconcilerImplementation) createRoleBinding(
|
||||
ctx context.Context,
|
||||
cluster *cnpgv1.Cluster,
|
||||
) error {
|
||||
roleBinding := buildRoleBinding(cluster)
|
||||
if err := ctrl.SetControllerReference(cluster, roleBinding, r.Client.Scheme()); err != nil {
|
||||
return err
|
||||
}
|
||||
return r.Client.Create(ctx, roleBinding)
|
||||
}
|
||||
|
||||
func buildRole(
|
||||
cluster *cnpgv1.Cluster,
|
||||
barmanObjectName string,
|
||||
) *rbacv1.Role {
|
||||
return &rbacv1.Role{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: cluster.Namespace,
|
||||
Name: getRBACName(cluster.Name),
|
||||
},
|
||||
|
||||
Rules: []rbacv1.PolicyRule{
|
||||
{
|
||||
APIGroups: []string{
|
||||
"barmancloud.cnpg.io",
|
||||
},
|
||||
Verbs: []string{
|
||||
"get",
|
||||
"watch",
|
||||
"list",
|
||||
},
|
||||
Resources: []string{
|
||||
"objectstores",
|
||||
},
|
||||
ResourceNames: []string{
|
||||
barmanObjectName,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func buildRoleBinding(
|
||||
cluster *cnpgv1.Cluster,
|
||||
) *rbacv1.RoleBinding {
|
||||
return &rbacv1.RoleBinding{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Namespace: cluster.Namespace,
|
||||
Name: getRBACName(cluster.Name),
|
||||
},
|
||||
Subjects: []rbacv1.Subject{
|
||||
{
|
||||
Kind: "ServiceAccount",
|
||||
APIGroup: "",
|
||||
Name: cluster.Name,
|
||||
Namespace: cluster.Namespace,
|
||||
},
|
||||
},
|
||||
RoleRef: rbacv1.RoleRef{
|
||||
APIGroup: "rbac.authorization.k8s.io",
|
||||
Kind: "Role",
|
||||
Name: getRBACName(cluster.Name),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// getRBACName returns the name of the RBAC entities for the
|
||||
// barman cloud plugin
|
||||
func getRBACName(clusterName string) string {
|
||||
return fmt.Sprintf("%s-barman", clusterName)
|
||||
}
|
||||
|
||||
@ -1,21 +1,45 @@
|
||||
package operator
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/cloudnative-pg/cnpg-i-machinery/pkg/pluginhelper/http"
|
||||
"github.com/cloudnative-pg/cnpg-i/pkg/lifecycle"
|
||||
"github.com/cloudnative-pg/cnpg-i/pkg/reconciler"
|
||||
"github.com/spf13/cobra"
|
||||
"google.golang.org/grpc"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
)
|
||||
|
||||
// NewCommand creates the command to start the GRPC server
|
||||
// CNPGI is the implementation of the CNPG-i server
|
||||
type CNPGI struct {
|
||||
Client client.Client
|
||||
PluginPath string
|
||||
ServerCertPath string
|
||||
ServerKeyPath string
|
||||
ClientCertPath string
|
||||
ServerAddress string
|
||||
}
|
||||
|
||||
// Start starts the GRPC server
|
||||
// of the operator plugin
|
||||
func NewCommand() *cobra.Command {
|
||||
cmd := http.CreateMainCmd(IdentityImplementation{}, func(server *grpc.Server) error {
|
||||
reconciler.RegisterReconcilerHooksServer(server, ReconcilerImplementation{})
|
||||
func (c *CNPGI) Start(ctx context.Context) error {
|
||||
enrich := func(server *grpc.Server) error {
|
||||
reconciler.RegisterReconcilerHooksServer(server, ReconcilerImplementation{
|
||||
Client: c.Client,
|
||||
})
|
||||
lifecycle.RegisterOperatorLifecycleServer(server, LifecycleImplementation{})
|
||||
return nil
|
||||
})
|
||||
cmd.Use = "plugin"
|
||||
return cmd
|
||||
}
|
||||
|
||||
srv := http.Server{
|
||||
IdentityImpl: IdentityImplementation{},
|
||||
Enrichers: []http.ServerEnricher{enrich},
|
||||
PluginPath: c.PluginPath,
|
||||
ServerCertPath: c.ServerCertPath,
|
||||
ServerKeyPath: c.ServerKeyPath,
|
||||
ClientCertPath: c.ClientCertPath,
|
||||
ServerAddress: c.ServerAddress,
|
||||
}
|
||||
|
||||
return srv.Start(ctx)
|
||||
}
|
||||
|
||||
@ -34,6 +34,8 @@ type ObjectStoreReconciler struct {
|
||||
Scheme *runtime.Scheme
|
||||
}
|
||||
|
||||
// +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=rolebindings,verbs=create;patch;update;get;list;watch
|
||||
// +kubebuilder:rbac:groups=rbac.authorization.k8s.io,resources=roles,verbs=create;patch;update;get;list;watch
|
||||
// +kubebuilder:rbac:groups=barmancloud.cnpg.io,resources=objectstores,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups=barmancloud.cnpg.io,resources=objectstores/status,verbs=get;update;patch
|
||||
// +kubebuilder:rbac:groups=barmancloud.cnpg.io,resources=objectstores/finalizers,verbs=update
|
||||
@ -61,7 +61,7 @@ var _ = BeforeSuite(func() {
|
||||
|
||||
By("bootstrapping test environment")
|
||||
testEnv = &envtest.Environment{
|
||||
CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")},
|
||||
CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")},
|
||||
ErrorIfCRDPathMissing: true,
|
||||
|
||||
// The BinaryAssetsDirectory is only required if you want to run the tests directly
|
||||
@ -29,7 +29,6 @@ spec:
|
||||
key: SIDECAR_IMAGE
|
||||
name: plugin-barman-cloud
|
||||
args:
|
||||
- operator
|
||||
- --server-cert=/server/tls.crt
|
||||
- --server-key=/server/tls.key
|
||||
- --client-cert=/client/tls.crt
|
||||
|
||||
@ -12,8 +12,8 @@ resources:
|
||||
images:
|
||||
- name: plugin-barman-cloud
|
||||
newName: kind.local/github.com/cloudnative-pg/plugin-barman-cloud/cmd/operator
|
||||
newTag: 634ab82d4f8b68ffada6033b7dff817bbed61a2fec8e05f9ebf74f5bedafb0dd
|
||||
newTag: 7e901b38eaf33b047dcf2eb044c9c8ca85535d8041a3144d25f7e1a4690ea071
|
||||
secretGenerator:
|
||||
- literals:
|
||||
- SIDECAR_IMAGE=kind.local/github.com/cloudnative-pg/plugin-barman-cloud/cmd/instance:7173a2dcf3ce74e982ca3d35053de40fcec67b31d607a0d12547b4f9d09c535c
|
||||
- SIDECAR_IMAGE=kind.local/github.com/cloudnative-pg/plugin-barman-cloud/cmd/instance:ca1fd58413940a247bc52cdb44f4a6909192d781b1767dc7ee9625368ee9d7e2
|
||||
name: plugin-barman-cloud
|
||||
|
||||
Loading…
Reference in New Issue
Block a user