Peter Kurfer
647f602c79
- added Core CRD to manage DB migrations & configuration, PostgREST and GoTrue (auth) - added APIGateway CRD to manage Envoy proxy - added Dashboard CRD to manage (so far) pg-meta and (soon) studio deployments - implemented basic Envoy control plane based on K8s watcher
145 lines
3.1 KiB
Go
145 lines
3.1 KiB
Go
package controller
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"errors"
|
|
"io"
|
|
"maps"
|
|
"reflect"
|
|
|
|
corev1 "k8s.io/api/core/v1"
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
"k8s.io/apimachinery/pkg/fields"
|
|
"k8s.io/apimachinery/pkg/types"
|
|
"sigs.k8s.io/controller-runtime/pkg/client"
|
|
"sigs.k8s.io/controller-runtime/pkg/handler"
|
|
"sigs.k8s.io/controller-runtime/pkg/log"
|
|
"sigs.k8s.io/controller-runtime/pkg/reconcile"
|
|
|
|
"code.icb4dc0.de/prskr/supabase-operator/api"
|
|
)
|
|
|
|
func Close(closer io.Closer, err *error) {
|
|
*err = errors.Join(*err, closer.Close())
|
|
}
|
|
|
|
func CloseCtx(ctx context.Context, closable interface {
|
|
Close(ctx context.Context) error
|
|
}, err *error,
|
|
) {
|
|
*err = errors.Join(*err, closable.Close(ctx))
|
|
}
|
|
|
|
func ptrOf[T any](val T) *T {
|
|
return &val
|
|
}
|
|
|
|
func boolValueOf(ptr *bool) bool {
|
|
if ptr == nil {
|
|
return false
|
|
}
|
|
|
|
return *ptr
|
|
}
|
|
|
|
func MergeLabels(source map[string]string, toAppend ...map[string]string) map[string]string {
|
|
result := make(map[string]string, len(source)+len(toAppend))
|
|
maps.Copy(result, source)
|
|
|
|
for _, additionalLabels := range toAppend {
|
|
for k, v := range additionalLabels {
|
|
if _, exists := result[k]; exists {
|
|
continue
|
|
}
|
|
|
|
result[k] = v
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
func MergeEnv(source []corev1.EnvVar, toAppend ...corev1.EnvVar) []corev1.EnvVar {
|
|
existingKeys := make(map[string]bool, len(source)+len(toAppend))
|
|
|
|
merged := append(make([]corev1.EnvVar, 0, len(source)+len(toAppend)), source...)
|
|
|
|
for _, v := range source {
|
|
existingKeys[v.Name] = true
|
|
}
|
|
|
|
for _, v := range toAppend {
|
|
if _, alreadyPresent := existingKeys[v.Name]; alreadyPresent {
|
|
continue
|
|
}
|
|
merged = append(merged, v)
|
|
existingKeys[v.Name] = true
|
|
}
|
|
|
|
return merged
|
|
}
|
|
|
|
func ValueOrFallback[T any](value, fallback T) T {
|
|
rval := reflect.ValueOf(value)
|
|
if rval.IsZero() {
|
|
return fallback
|
|
}
|
|
|
|
return value
|
|
}
|
|
|
|
func HashStrings(vals ...string) []byte {
|
|
h := sha256.New()
|
|
|
|
for _, v := range vals {
|
|
h.Write([]byte(v))
|
|
}
|
|
|
|
return h.Sum(nil)
|
|
}
|
|
|
|
func HashBytes(vals ...[]byte) []byte {
|
|
h := sha256.New()
|
|
|
|
for _, v := range vals {
|
|
h.Write(v)
|
|
}
|
|
|
|
return h.Sum(nil)
|
|
}
|
|
|
|
func FieldSelectorEventHandler[TItem metav1.Object, TList api.ObjectList[TItem]](
|
|
cli client.Client,
|
|
fieldSelector string,
|
|
) handler.TypedEventHandler[client.Object, reconcile.Request] {
|
|
return handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, obj client.Object) []reconcile.Request {
|
|
var (
|
|
list TList
|
|
selectorInstance = client.MatchingFieldsSelector{
|
|
Selector: fields.OneTermEqualSelector(fieldSelector, obj.GetName()),
|
|
}
|
|
logger = log.FromContext(ctx, "object", obj.GetName(), "namespace", obj.GetNamespace())
|
|
)
|
|
|
|
list = reflect.New(reflect.TypeOf(list).Elem()).Interface().(TList)
|
|
|
|
if err := cli.List(ctx, list, selectorInstance, client.InNamespace(obj.GetNamespace())); err != nil {
|
|
logger.Error(err, "could not list items for field selector event handler")
|
|
return nil
|
|
}
|
|
|
|
requests := make([]reconcile.Request, 0)
|
|
|
|
for item := range list.Iter() {
|
|
requests = append(requests, reconcile.Request{
|
|
NamespacedName: types.NamespacedName{
|
|
Name: item.GetName(),
|
|
Namespace: obj.GetNamespace(),
|
|
},
|
|
})
|
|
}
|
|
|
|
return requests
|
|
})
|
|
}
|