feat(storage): prepare custom resource for storage API
This commit is contained in:
parent
d02e2d4653
commit
b55afea477
34 changed files with 1110 additions and 369 deletions
4
PROJECT
4
PROJECT
|
@ -56,4 +56,8 @@ resources:
|
|||
kind: Storage
|
||||
path: code.icb4dc0.de/prskr/supabase-operator/api/v1alpha1
|
||||
version: v1alpha1
|
||||
webhooks:
|
||||
defaulting: true
|
||||
validation: true
|
||||
webhookVersion: v1
|
||||
version: "3"
|
||||
|
|
8
Tiltfile
8
Tiltfile
|
@ -78,3 +78,11 @@ k8s_resource(
|
|||
'supabase-controller-manager'
|
||||
],
|
||||
)
|
||||
|
||||
k8s_resource(
|
||||
objects=["core-sample:Storage:supabase-demo"],
|
||||
new_name='Storage',
|
||||
resource_deps=[
|
||||
'supabase-controller-manager'
|
||||
],
|
||||
)
|
||||
|
|
|
@ -22,6 +22,50 @@ import (
|
|||
corev1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
type JwtSpec struct {
|
||||
// SecretRef - object reference to the Secret where JWT values are stored
|
||||
SecretName string `json:"secretName,omitempty"`
|
||||
// SecretKey - key in secret where to read the JWT HMAC secret from
|
||||
// +kubebuilder:default=secret
|
||||
SecretKey string `json:"secretKey,omitempty"`
|
||||
// JwksKey - key in secret where to read the JWKS from
|
||||
// +kubebuilder:default=jwks.json
|
||||
JwksKey string `json:"jwksKey,omitempty"`
|
||||
// AnonKey - key in secret where to read the anon JWT from
|
||||
// +kubebuilder:default=anon_key
|
||||
AnonKey string `json:"anonKey,omitempty"`
|
||||
// ServiceKey - key in secret where to read the service JWT from
|
||||
// +kubebuilder:default=service_key
|
||||
ServiceKey string `json:"serviceKey,omitempty"`
|
||||
}
|
||||
|
||||
func (s JwtSpec) SecretKeySelector() *corev1.SecretKeySelector {
|
||||
return &corev1.SecretKeySelector{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: s.SecretName,
|
||||
},
|
||||
Key: s.SecretKey,
|
||||
}
|
||||
}
|
||||
|
||||
func (s JwtSpec) AnonKeySelector() *corev1.SecretKeySelector {
|
||||
return &corev1.SecretKeySelector{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: s.SecretName,
|
||||
},
|
||||
Key: s.AnonKey,
|
||||
}
|
||||
}
|
||||
|
||||
func (s JwtSpec) ServiceKeySelector() *corev1.SecretKeySelector {
|
||||
return &corev1.SecretKeySelector{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: s.SecretName,
|
||||
},
|
||||
Key: s.ServiceKey,
|
||||
}
|
||||
}
|
||||
|
||||
type ImageSpec struct {
|
||||
Image string `json:"image,omitempty"`
|
||||
PullPolicy corev1.PullPolicy `json:"pullPolicy,omitempty"`
|
||||
|
|
|
@ -41,11 +41,11 @@ func init() {
|
|||
var ErrNoSuchSecretValue = errors.New("no such secret value")
|
||||
|
||||
type DatabaseRolesSecrets struct {
|
||||
Admin *corev1.LocalObjectReference `json:"supabaseAdmin,omitempty"`
|
||||
Authenticator *corev1.LocalObjectReference `json:"authenticator,omitempty"`
|
||||
AuthAdmin *corev1.LocalObjectReference `json:"supabaseAuthAdmin,omitempty"`
|
||||
FunctionsAdmin *corev1.LocalObjectReference `json:"supabaseFunctionsAdmin,omitempty"`
|
||||
StorageAdmin *corev1.LocalObjectReference `json:"supabaseStorageAdmin,omitempty"`
|
||||
Admin string `json:"supabaseAdmin,omitempty"`
|
||||
Authenticator string `json:"authenticator,omitempty"`
|
||||
AuthAdmin string `json:"supabaseAuthAdmin,omitempty"`
|
||||
FunctionsAdmin string `json:"supabaseFunctionsAdmin,omitempty"`
|
||||
StorageAdmin string `json:"supabaseStorageAdmin,omitempty"`
|
||||
}
|
||||
|
||||
type DatabaseRoles struct {
|
||||
|
@ -91,23 +91,10 @@ func (d Database) DSNEnv(key string) corev1.EnvVar {
|
|||
}
|
||||
|
||||
type CoreJwtSpec struct {
|
||||
JwtSpec `json:",inline"`
|
||||
// Secret - JWT HMAC secret in plain text
|
||||
// This is WRITE-ONLY and will be copied to the SecretRef by the defaulter
|
||||
Secret *string `json:"secret,omitempty"`
|
||||
// SecretRef - object reference to the Secret where JWT values are stored
|
||||
SecretRef *corev1.LocalObjectReference `json:"secretRef,omitempty"`
|
||||
// SecretKey - key in secret where to read the JWT HMAC secret from
|
||||
// +kubebuilder:default=secret
|
||||
SecretKey string `json:"secretKey,omitempty"`
|
||||
// JwksKey - key in secret where to read the JWKS from
|
||||
// +kubebuilder:default=jwks.json
|
||||
JwksKey string `json:"jwksKey,omitempty"`
|
||||
// AnonKey - key in secret where to read the anon JWT from
|
||||
// +kubebuilder:default=anon_key
|
||||
AnonKey string `json:"anonKey,omitempty"`
|
||||
// ServiceKey - key in secret where to read the service JWT from
|
||||
// +kubebuilder:default=service_key
|
||||
ServiceKey string `json:"serviceKey,omitempty"`
|
||||
// Expiry - expiration time in seconds for JWTs
|
||||
// +kubebuilder:default=3600
|
||||
Expiry int `json:"expiry,omitempty"`
|
||||
|
@ -115,7 +102,7 @@ type CoreJwtSpec struct {
|
|||
|
||||
func (s CoreJwtSpec) GetJWTSecret(ctx context.Context, client client.Client) ([]byte, error) {
|
||||
var secret corev1.Secret
|
||||
if err := client.Get(ctx, types.NamespacedName{Name: s.SecretRef.Name}, &secret); err != nil {
|
||||
if err := client.Get(ctx, types.NamespacedName{Name: s.SecretName}, &secret); err != nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
|
@ -129,14 +116,18 @@ func (s CoreJwtSpec) GetJWTSecret(ctx context.Context, client client.Client) ([]
|
|||
|
||||
func (s CoreJwtSpec) SecretKeySelector() *corev1.SecretKeySelector {
|
||||
return &corev1.SecretKeySelector{
|
||||
LocalObjectReference: *s.SecretRef,
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: s.SecretName,
|
||||
},
|
||||
Key: s.SecretKey,
|
||||
}
|
||||
}
|
||||
|
||||
func (s CoreJwtSpec) JwksKeySelector() *corev1.SecretKeySelector {
|
||||
return &corev1.SecretKeySelector{
|
||||
LocalObjectReference: *s.SecretRef,
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: s.SecretName,
|
||||
},
|
||||
Key: s.JwksKey,
|
||||
}
|
||||
}
|
||||
|
@ -146,7 +137,9 @@ func (s CoreJwtSpec) SecretAsEnv(key string) corev1.EnvVar {
|
|||
Name: key,
|
||||
ValueFrom: &corev1.EnvVarSource{
|
||||
SecretKeyRef: &corev1.SecretKeySelector{
|
||||
LocalObjectReference: *s.SecretRef,
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: s.SecretName,
|
||||
},
|
||||
Key: s.SecretKey,
|
||||
},
|
||||
},
|
||||
|
@ -194,11 +187,21 @@ func (p *AuthProviderMeta) Vars(provider string) []corev1.EnvVar {
|
|||
}}
|
||||
}
|
||||
|
||||
type SmtpCredentialsReference struct {
|
||||
SecretName string `json:"secretName"`
|
||||
// UsernameKey
|
||||
// +kubebuilder:default="username"
|
||||
UsernameKey string `json:"usernameKey"`
|
||||
// PasswordKey
|
||||
// +kubebuilder:default="password"
|
||||
PasswordKey string `json:"passwordKey"`
|
||||
}
|
||||
|
||||
type EmailAuthSmtpSpec struct {
|
||||
Host string `json:"host"`
|
||||
Port uint16 `json:"port"`
|
||||
MaxFrequency *uint `json:"maxFrequency,omitempty"`
|
||||
CredentialsFrom *corev1.LocalObjectReference `json:"credentialsFrom"`
|
||||
CredentialsRef *SmtpCredentialsReference `json:"credentialsRef"`
|
||||
}
|
||||
|
||||
type EmailAuthProvider struct {
|
||||
|
@ -225,8 +228,10 @@ func (p *EmailAuthProvider) Vars(apiExternalURL string) []corev1.EnvVar {
|
|||
Name: "GOTRUE_SMTP_USER",
|
||||
ValueFrom: &corev1.EnvVarSource{
|
||||
SecretKeyRef: &corev1.SecretKeySelector{
|
||||
LocalObjectReference: *p.SmtpSpec.CredentialsFrom,
|
||||
Key: corev1.BasicAuthUsernameKey,
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: p.SmtpSpec.CredentialsRef.SecretName,
|
||||
},
|
||||
Key: p.SmtpSpec.CredentialsRef.UsernameKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -234,8 +239,10 @@ func (p *EmailAuthProvider) Vars(apiExternalURL string) []corev1.EnvVar {
|
|||
Name: "GOTRUE_SMTP_PASS",
|
||||
ValueFrom: &corev1.EnvVarSource{
|
||||
SecretKeyRef: &corev1.SecretKeySelector{
|
||||
LocalObjectReference: *p.SmtpSpec.CredentialsFrom,
|
||||
Key: corev1.BasicAuthPasswordKey,
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: p.SmtpSpec.CredentialsRef.SecretName,
|
||||
},
|
||||
Key: p.SmtpSpec.CredentialsRef.PasswordKey,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -21,43 +21,8 @@ import (
|
|||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
type DashboardJwtSpec struct {
|
||||
// SecretRef - object reference to the Secret where JWT values are stored
|
||||
SecretRef *corev1.LocalObjectReference `json:"secretRef,omitempty"`
|
||||
// SecretKey - key in secret where to read the JWT HMAC secret from
|
||||
// +kubebuilder:default=secret
|
||||
SecretKey string `json:"secretKey,omitempty"`
|
||||
// AnonKey - key in secret where to read the anon JWT from
|
||||
// +kubebuilder:default=anon_key
|
||||
AnonKey string `json:"anonKey,omitempty"`
|
||||
// ServiceKey - key in secret where to read the service JWT from
|
||||
// +kubebuilder:default=service_key
|
||||
ServiceKey string `json:"serviceKey,omitempty"`
|
||||
}
|
||||
|
||||
func (s DashboardJwtSpec) SecretKeySelector() *corev1.SecretKeySelector {
|
||||
return &corev1.SecretKeySelector{
|
||||
LocalObjectReference: *s.SecretRef,
|
||||
Key: s.SecretKey,
|
||||
}
|
||||
}
|
||||
|
||||
func (s DashboardJwtSpec) AnonKeySelector() *corev1.SecretKeySelector {
|
||||
return &corev1.SecretKeySelector{
|
||||
LocalObjectReference: *s.SecretRef,
|
||||
Key: s.AnonKey,
|
||||
}
|
||||
}
|
||||
|
||||
func (s DashboardJwtSpec) ServiceKeySelector() *corev1.SecretKeySelector {
|
||||
return &corev1.SecretKeySelector{
|
||||
LocalObjectReference: *s.SecretRef,
|
||||
Key: s.ServiceKey,
|
||||
}
|
||||
}
|
||||
|
||||
type StudioSpec struct {
|
||||
JWT *DashboardJwtSpec `json:"jwt,omitempty"`
|
||||
JWT *JwtSpec `json:"jwt,omitempty"`
|
||||
// WorkloadTemplate - customize the studio deployment
|
||||
WorkloadTemplate *WorkloadTemplate `json:"workloadTemplate,omitempty"`
|
||||
// GatewayServiceSelector - selector to find the service for the API gateway
|
||||
|
@ -75,6 +40,16 @@ type PGMetaSpec struct {
|
|||
WorkloadTemplate *WorkloadTemplate `json:"workloadTemplate,omitempty"`
|
||||
}
|
||||
|
||||
type DbCredentialsReference struct {
|
||||
SecretName string `json:"secretName"`
|
||||
// UsernameKey
|
||||
// +kubebuilder:default="username"
|
||||
UsernameKey string `json:"usernameKey,omitempty"`
|
||||
// PasswordKey
|
||||
// +kubebuilder:default="password"
|
||||
PasswordKey string `json:"passwordKey,omitempty"`
|
||||
}
|
||||
|
||||
type DashboardDbSpec struct {
|
||||
Host string `json:"host"`
|
||||
// Port - Database port, typically 5432
|
||||
|
@ -83,20 +58,24 @@ type DashboardDbSpec struct {
|
|||
DBName string `json:"dbName"`
|
||||
// DBCredentialsRef - reference to a Secret key where the DB credentials can be retrieved from
|
||||
// Credentials need to be stored in basic auth form
|
||||
DBCredentialsRef *corev1.LocalObjectReference `json:"dbCredentialsRef"`
|
||||
DBCredentialsRef *DbCredentialsReference `json:"dbCredentialsRef"`
|
||||
}
|
||||
|
||||
func (s DashboardDbSpec) UserRef() *corev1.SecretKeySelector {
|
||||
return &corev1.SecretKeySelector{
|
||||
LocalObjectReference: *s.DBCredentialsRef,
|
||||
Key: corev1.BasicAuthUsernameKey,
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: s.DBCredentialsRef.SecretName,
|
||||
},
|
||||
Key: s.DBCredentialsRef.UsernameKey,
|
||||
}
|
||||
}
|
||||
|
||||
func (s DashboardDbSpec) PasswordRef() *corev1.SecretKeySelector {
|
||||
return &corev1.SecretKeySelector{
|
||||
LocalObjectReference: *s.DBCredentialsRef,
|
||||
Key: corev1.BasicAuthPasswordKey,
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: s.DBCredentialsRef.SecretName,
|
||||
},
|
||||
Key: s.DBCredentialsRef.PasswordKey,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -17,26 +17,93 @@ limitations under the License.
|
|||
package v1alpha1
|
||||
|
||||
import (
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
|
||||
// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized.
|
||||
type StorageBackend string
|
||||
|
||||
const (
|
||||
StorageBackendFile StorageBackend = "file"
|
||||
StorageBackendS3 StorageBackend = "s3"
|
||||
)
|
||||
|
||||
type StorageApiDbSpec struct {
|
||||
Host string `json:"host"`
|
||||
// Port - Database port, typically 5432
|
||||
// +kubebuilder:default=5432
|
||||
Port int `json:"port,omitempty"`
|
||||
DBName string `json:"dbName"`
|
||||
// DBCredentialsRef - reference to a Secret key where the DB credentials can be retrieved from
|
||||
// Credentials need to be stored in basic auth form
|
||||
DBCredentialsRef *DbCredentialsReference `json:"dbCredentialsRef"`
|
||||
}
|
||||
|
||||
func (s StorageApiDbSpec) UserRef() *corev1.SecretKeySelector {
|
||||
return &corev1.SecretKeySelector{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: s.DBCredentialsRef.SecretName,
|
||||
},
|
||||
Key: s.DBCredentialsRef.UsernameKey,
|
||||
}
|
||||
}
|
||||
|
||||
func (s StorageApiDbSpec) PasswordRef() *corev1.SecretKeySelector {
|
||||
return &corev1.SecretKeySelector{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: s.DBCredentialsRef.SecretName,
|
||||
},
|
||||
Key: s.DBCredentialsRef.PasswordKey,
|
||||
}
|
||||
}
|
||||
|
||||
type S3CredentialsRef struct {
|
||||
SecretName string `json:"secretName"`
|
||||
// AccessKeyIdKey - key in Secret where access key id will be referenced from
|
||||
// +kubebuilder:default="accessKeyId"
|
||||
AccessKeyIdKey string `json:"accessKeyIdKey,omitempty"`
|
||||
// AccessSecretKeyKey - key in Secret where access secret key will be referenced from
|
||||
// +kubebuilder:default="secretAccessKey"
|
||||
AccessSecretKeyKey string `json:"accessSecretKeyKey,omitempty"`
|
||||
}
|
||||
|
||||
type S3ProtocolSpec struct {
|
||||
// Region - S3 region to use in the API
|
||||
// +kubebuilder:default="us-east-1"
|
||||
Region string `json:"region,omitempty"`
|
||||
|
||||
// AllowForwardedHeader
|
||||
// +kubebuilder:default=true
|
||||
AllowForwardedHeader bool `json:"allowForwardedHeader,omitempty"`
|
||||
|
||||
// CredentialsSecretRef - reference to the Secret where access key id and access secret key are stored
|
||||
CredentialsSecretRef *S3CredentialsRef `json:"credentialsSecretRef,omitempty"`
|
||||
}
|
||||
|
||||
// StorageSpec defines the desired state of Storage.
|
||||
type StorageSpec struct {
|
||||
// INSERT ADDITIONAL SPEC FIELDS - desired state of cluster
|
||||
// Important: Run "make" to regenerate code after modifying this file
|
||||
|
||||
// Foo is an example field of Storage. Edit storage_types.go to remove/update
|
||||
Foo string `json:"foo,omitempty"`
|
||||
// BackendType - backend storage type to use
|
||||
// +kubebuilder:validation:Enum={s3,file}
|
||||
BackendType StorageBackend `json:"backendType"`
|
||||
// FileSizeLimit - maximum file upload size in bytes
|
||||
// +kubebuilder:default=52428800
|
||||
FileSizeLimit uint64 `json:"fileSizeLimit,omitempty"`
|
||||
// JwtAuth - Configure the JWT authentication parameters.
|
||||
// This includes where to retrieve anon and service key from as well as JWT secret and JWKS references
|
||||
// needed to validate JWTs send to the API
|
||||
JwtAuth JwtSpec `json:"jwtAuth"`
|
||||
// DBSpec - Configure access to the Postgres database
|
||||
// In most cases this will reference the supabase-storage-admin credentials secret provided by the Core resource
|
||||
DBSpec StorageApiDbSpec `json:"db"`
|
||||
// S3 - Configure S3 protocol
|
||||
S3 *S3ProtocolSpec `json:"s3,omitempty"`
|
||||
// EnableImageTransformation - whether to deploy the image proxy
|
||||
// the image proxy scale images to lower resolutions on demand to reduce traffic for instance for mobile devices
|
||||
EnableImageTransformation bool `json:"enableImageTransformation,omitempty"`
|
||||
}
|
||||
|
||||
// StorageStatus defines the observed state of Storage.
|
||||
type StorageStatus struct {
|
||||
// INSERT ADDITIONAL STATUS FIELD - define observed state of cluster
|
||||
// Important: Run "make" to regenerate code after modifying this file
|
||||
}
|
||||
type StorageStatus struct{}
|
||||
|
||||
// +kubebuilder:object:root=true
|
||||
// +kubebuilder:subresource:status
|
||||
|
|
|
@ -21,7 +21,7 @@ limitations under the License.
|
|||
package v1alpha1
|
||||
|
||||
import (
|
||||
"k8s.io/api/core/v1"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
runtime "k8s.io/apimachinery/pkg/runtime"
|
||||
)
|
||||
|
@ -345,16 +345,12 @@ func (in *Core) DeepCopyObject() runtime.Object {
|
|||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *CoreJwtSpec) DeepCopyInto(out *CoreJwtSpec) {
|
||||
*out = *in
|
||||
out.JwtSpec = in.JwtSpec
|
||||
if in.Secret != nil {
|
||||
in, out := &in.Secret, &out.Secret
|
||||
*out = new(string)
|
||||
**out = **in
|
||||
}
|
||||
if in.SecretRef != nil {
|
||||
in, out := &in.SecretRef, &out.SecretRef
|
||||
*out = new(v1.LocalObjectReference)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new CoreJwtSpec.
|
||||
|
@ -474,7 +470,7 @@ func (in *DashboardDbSpec) DeepCopyInto(out *DashboardDbSpec) {
|
|||
*out = *in
|
||||
if in.DBCredentialsRef != nil {
|
||||
in, out := &in.DBCredentialsRef, &out.DBCredentialsRef
|
||||
*out = new(v1.LocalObjectReference)
|
||||
*out = new(DbCredentialsReference)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
@ -489,26 +485,6 @@ func (in *DashboardDbSpec) DeepCopy() *DashboardDbSpec {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DashboardJwtSpec) DeepCopyInto(out *DashboardJwtSpec) {
|
||||
*out = *in
|
||||
if in.SecretRef != nil {
|
||||
in, out := &in.SecretRef, &out.SecretRef
|
||||
*out = new(v1.LocalObjectReference)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DashboardJwtSpec.
|
||||
func (in *DashboardJwtSpec) DeepCopy() *DashboardJwtSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DashboardJwtSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DashboardList) DeepCopyInto(out *DashboardList) {
|
||||
*out = *in
|
||||
|
@ -599,7 +575,7 @@ func (in *Database) DeepCopyInto(out *Database) {
|
|||
*out = new(v1.SecretKeySelector)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
in.Roles.DeepCopyInto(&out.Roles)
|
||||
out.Roles = in.Roles
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Database.
|
||||
|
@ -615,7 +591,7 @@ func (in *Database) DeepCopy() *Database {
|
|||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DatabaseRoles) DeepCopyInto(out *DatabaseRoles) {
|
||||
*out = *in
|
||||
in.Secrets.DeepCopyInto(&out.Secrets)
|
||||
out.Secrets = in.Secrets
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseRoles.
|
||||
|
@ -631,31 +607,6 @@ func (in *DatabaseRoles) DeepCopy() *DatabaseRoles {
|
|||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DatabaseRolesSecrets) DeepCopyInto(out *DatabaseRolesSecrets) {
|
||||
*out = *in
|
||||
if in.Admin != nil {
|
||||
in, out := &in.Admin, &out.Admin
|
||||
*out = new(v1.LocalObjectReference)
|
||||
**out = **in
|
||||
}
|
||||
if in.Authenticator != nil {
|
||||
in, out := &in.Authenticator, &out.Authenticator
|
||||
*out = new(v1.LocalObjectReference)
|
||||
**out = **in
|
||||
}
|
||||
if in.AuthAdmin != nil {
|
||||
in, out := &in.AuthAdmin, &out.AuthAdmin
|
||||
*out = new(v1.LocalObjectReference)
|
||||
**out = **in
|
||||
}
|
||||
if in.FunctionsAdmin != nil {
|
||||
in, out := &in.FunctionsAdmin, &out.FunctionsAdmin
|
||||
*out = new(v1.LocalObjectReference)
|
||||
**out = **in
|
||||
}
|
||||
if in.StorageAdmin != nil {
|
||||
in, out := &in.StorageAdmin, &out.StorageAdmin
|
||||
*out = new(v1.LocalObjectReference)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DatabaseRolesSecrets.
|
||||
|
@ -706,6 +657,21 @@ func (in *DatabaseStatus) DeepCopy() *DatabaseStatus {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *DbCredentialsReference) DeepCopyInto(out *DbCredentialsReference) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new DbCredentialsReference.
|
||||
func (in *DbCredentialsReference) DeepCopy() *DbCredentialsReference {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(DbCredentialsReference)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *EmailAuthProvider) DeepCopyInto(out *EmailAuthProvider) {
|
||||
*out = *in
|
||||
|
@ -745,9 +711,9 @@ func (in *EmailAuthSmtpSpec) DeepCopyInto(out *EmailAuthSmtpSpec) {
|
|||
*out = new(uint)
|
||||
**out = **in
|
||||
}
|
||||
if in.CredentialsFrom != nil {
|
||||
in, out := &in.CredentialsFrom, &out.CredentialsFrom
|
||||
*out = new(v1.LocalObjectReference)
|
||||
if in.CredentialsRef != nil {
|
||||
in, out := &in.CredentialsRef, &out.CredentialsRef
|
||||
*out = new(SmtpCredentialsReference)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
@ -839,6 +805,21 @@ func (in *ImageSpec) DeepCopy() *ImageSpec {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *JwtSpec) DeepCopyInto(out *JwtSpec) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new JwtSpec.
|
||||
func (in *JwtSpec) DeepCopy() *JwtSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(JwtSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in MigrationStatus) DeepCopyInto(out *MigrationStatus) {
|
||||
{
|
||||
|
@ -946,12 +927,62 @@ func (in *PostgrestSpec) DeepCopy() *PostgrestSpec {
|
|||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *S3CredentialsRef) DeepCopyInto(out *S3CredentialsRef) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S3CredentialsRef.
|
||||
func (in *S3CredentialsRef) DeepCopy() *S3CredentialsRef {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(S3CredentialsRef)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *S3ProtocolSpec) DeepCopyInto(out *S3ProtocolSpec) {
|
||||
*out = *in
|
||||
if in.CredentialsSecretRef != nil {
|
||||
in, out := &in.CredentialsSecretRef, &out.CredentialsSecretRef
|
||||
*out = new(S3CredentialsRef)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new S3ProtocolSpec.
|
||||
func (in *S3ProtocolSpec) DeepCopy() *S3ProtocolSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(S3ProtocolSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *SmtpCredentialsReference) DeepCopyInto(out *SmtpCredentialsReference) {
|
||||
*out = *in
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new SmtpCredentialsReference.
|
||||
func (in *SmtpCredentialsReference) DeepCopy() *SmtpCredentialsReference {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(SmtpCredentialsReference)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Storage) DeepCopyInto(out *Storage) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
in.ObjectMeta.DeepCopyInto(&out.ObjectMeta)
|
||||
out.Spec = in.Spec
|
||||
in.Spec.DeepCopyInto(&out.Spec)
|
||||
out.Status = in.Status
|
||||
}
|
||||
|
||||
|
@ -973,6 +1004,26 @@ func (in *Storage) DeepCopyObject() runtime.Object {
|
|||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *StorageApiDbSpec) DeepCopyInto(out *StorageApiDbSpec) {
|
||||
*out = *in
|
||||
if in.DBCredentialsRef != nil {
|
||||
in, out := &in.DBCredentialsRef, &out.DBCredentialsRef
|
||||
*out = new(DbCredentialsReference)
|
||||
**out = **in
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StorageApiDbSpec.
|
||||
func (in *StorageApiDbSpec) DeepCopy() *StorageApiDbSpec {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(StorageApiDbSpec)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *StorageList) DeepCopyInto(out *StorageList) {
|
||||
*out = *in
|
||||
|
@ -1008,6 +1059,13 @@ func (in *StorageList) DeepCopyObject() runtime.Object {
|
|||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *StorageSpec) DeepCopyInto(out *StorageSpec) {
|
||||
*out = *in
|
||||
out.JwtAuth = in.JwtAuth
|
||||
in.DBSpec.DeepCopyInto(&out.DBSpec)
|
||||
if in.S3 != nil {
|
||||
in, out := &in.S3, &out.S3
|
||||
*out = new(S3ProtocolSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
}
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new StorageSpec.
|
||||
|
@ -1040,8 +1098,8 @@ func (in *StudioSpec) DeepCopyInto(out *StudioSpec) {
|
|||
*out = *in
|
||||
if in.JWT != nil {
|
||||
in, out := &in.JWT, &out.JWT
|
||||
*out = new(DashboardJwtSpec)
|
||||
(*in).DeepCopyInto(*out)
|
||||
*out = new(JwtSpec)
|
||||
**out = **in
|
||||
}
|
||||
if in.WorkloadTemplate != nil {
|
||||
in, out := &in.WorkloadTemplate, &out.WorkloadTemplate
|
||||
|
|
|
@ -156,13 +156,16 @@ func (m manager) Run(ctx context.Context) error {
|
|||
}
|
||||
|
||||
if err = webhooksupabasev1alpha1.SetupAPIGatewayWebhookWithManager(mgr, webhookConfig); err != nil {
|
||||
setupLog.Error(err, "unable to create webhook", "webhook", "APIGateway")
|
||||
os.Exit(1)
|
||||
return fmt.Errorf("unable to create webhook: %w", err)
|
||||
}
|
||||
|
||||
if err = webhooksupabasev1alpha1.SetupDashboardWebhookWithManager(mgr); err != nil {
|
||||
return fmt.Errorf("unable to create webhook: %w", err)
|
||||
}
|
||||
|
||||
if err = webhooksupabasev1alpha1.SetupStorageWebhookWithManager(mgr); err != nil {
|
||||
return fmt.Errorf("unable to create webhook: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// +kubebuilder:scaffold:builder
|
||||
|
|
20
config/certmanager/certificate-metrics.yaml
Normal file
20
config/certmanager/certificate-metrics.yaml
Normal file
|
@ -0,0 +1,20 @@
|
|||
# The following manifests contain a self-signed issuer CR and a metrics certificate CR.
|
||||
# More document can be found at https://docs.cert-manager.io
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: supabase-operator
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: metrics-certs # this name should match the one appeared in kustomizeconfig.yaml
|
||||
namespace: system
|
||||
spec:
|
||||
dnsNames:
|
||||
# SERVICE_NAME and SERVICE_NAMESPACE will be substituted by kustomize
|
||||
# replacements in the config/default/kustomization.yaml file.
|
||||
- SERVICE_NAME.SERVICE_NAMESPACE.svc
|
||||
- SERVICE_NAME.SERVICE_NAMESPACE.svc.cluster.local
|
||||
issuerRef:
|
||||
kind: Issuer
|
||||
name: selfsigned-issuer
|
||||
secretName: metrics-server-cert
|
20
config/certmanager/certificate-webhook.yaml
Normal file
20
config/certmanager/certificate-webhook.yaml
Normal file
|
@ -0,0 +1,20 @@
|
|||
# The following manifests contain a self-signed issuer CR and a certificate CR.
|
||||
# More document can be found at https://docs.cert-manager.io
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Certificate
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: supabase-operator
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: serving-cert # this name should match the one appeared in kustomizeconfig.yaml
|
||||
namespace: system
|
||||
spec:
|
||||
# SERVICE_NAME and SERVICE_NAMESPACE will be substituted by kustomize
|
||||
# replacements in the config/default/kustomization.yaml file.
|
||||
dnsNames:
|
||||
- SERVICE_NAME.SERVICE_NAMESPACE.svc
|
||||
- SERVICE_NAME.SERVICE_NAMESPACE.svc.cluster.local
|
||||
issuerRef:
|
||||
kind: Issuer
|
||||
name: selfsigned-issuer
|
||||
secretName: webhook-server-cert
|
13
config/certmanager/issuer.yaml
Normal file
13
config/certmanager/issuer.yaml
Normal file
|
@ -0,0 +1,13 @@
|
|||
# The following manifest contains a self-signed issuer CR.
|
||||
# More information can be found at https://docs.cert-manager.io
|
||||
# WARNING: Targets CertManager v1.0. Check https://cert-manager.io/docs/installation/upgrading/ for breaking changes.
|
||||
apiVersion: cert-manager.io/v1
|
||||
kind: Issuer
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: supabase-operator
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: selfsigned-issuer
|
||||
namespace: system
|
||||
spec:
|
||||
selfSigned: {}
|
|
@ -105,22 +105,23 @@ spec:
|
|||
type: string
|
||||
smtpSpec:
|
||||
properties:
|
||||
credentialsFrom:
|
||||
description: |-
|
||||
LocalObjectReference contains enough information to let you locate the
|
||||
referenced object inside the same namespace.
|
||||
credentialsRef:
|
||||
properties:
|
||||
name:
|
||||
default: ""
|
||||
description: |-
|
||||
Name of the referent.
|
||||
This field is effectively required, but due to backwards compatibility is
|
||||
allowed to be empty. Instances of this type with an empty value here are
|
||||
almost certainly wrong.
|
||||
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
passwordKey:
|
||||
default: password
|
||||
description: PasswordKey
|
||||
type: string
|
||||
secretName:
|
||||
type: string
|
||||
usernameKey:
|
||||
default: username
|
||||
description: UsernameKey
|
||||
type: string
|
||||
required:
|
||||
- passwordKey
|
||||
- secretName
|
||||
- usernameKey
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
host:
|
||||
type: string
|
||||
maxFrequency:
|
||||
|
@ -128,7 +129,7 @@ spec:
|
|||
port:
|
||||
type: integer
|
||||
required:
|
||||
- credentialsFrom
|
||||
- credentialsRef
|
||||
- host
|
||||
- port
|
||||
type: object
|
||||
|
@ -935,86 +936,16 @@ spec:
|
|||
role that Supabase needs
|
||||
properties:
|
||||
authenticator:
|
||||
description: |-
|
||||
LocalObjectReference contains enough information to let you locate the
|
||||
referenced object inside the same namespace.
|
||||
properties:
|
||||
name:
|
||||
default: ""
|
||||
description: |-
|
||||
Name of the referent.
|
||||
This field is effectively required, but due to backwards compatibility is
|
||||
allowed to be empty. Instances of this type with an empty value here are
|
||||
almost certainly wrong.
|
||||
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
type: string
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
supabaseAdmin:
|
||||
description: |-
|
||||
LocalObjectReference contains enough information to let you locate the
|
||||
referenced object inside the same namespace.
|
||||
properties:
|
||||
name:
|
||||
default: ""
|
||||
description: |-
|
||||
Name of the referent.
|
||||
This field is effectively required, but due to backwards compatibility is
|
||||
allowed to be empty. Instances of this type with an empty value here are
|
||||
almost certainly wrong.
|
||||
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
type: string
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
supabaseAuthAdmin:
|
||||
description: |-
|
||||
LocalObjectReference contains enough information to let you locate the
|
||||
referenced object inside the same namespace.
|
||||
properties:
|
||||
name:
|
||||
default: ""
|
||||
description: |-
|
||||
Name of the referent.
|
||||
This field is effectively required, but due to backwards compatibility is
|
||||
allowed to be empty. Instances of this type with an empty value here are
|
||||
almost certainly wrong.
|
||||
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
type: string
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
supabaseFunctionsAdmin:
|
||||
description: |-
|
||||
LocalObjectReference contains enough information to let you locate the
|
||||
referenced object inside the same namespace.
|
||||
properties:
|
||||
name:
|
||||
default: ""
|
||||
description: |-
|
||||
Name of the referent.
|
||||
This field is effectively required, but due to backwards compatibility is
|
||||
allowed to be empty. Instances of this type with an empty value here are
|
||||
almost certainly wrong.
|
||||
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
type: string
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
supabaseStorageAdmin:
|
||||
description: |-
|
||||
LocalObjectReference contains enough information to let you locate the
|
||||
referenced object inside the same namespace.
|
||||
properties:
|
||||
name:
|
||||
default: ""
|
||||
description: |-
|
||||
Name of the referent.
|
||||
This field is effectively required, but due to backwards compatibility is
|
||||
allowed to be empty. Instances of this type with an empty value here are
|
||||
almost certainly wrong.
|
||||
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
type: string
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
type: object
|
||||
selfManaged:
|
||||
description: |-
|
||||
SelfManaged - whether the database roles are managed externally
|
||||
|
@ -1053,21 +984,10 @@ spec:
|
|||
description: SecretKey - key in secret where to read the JWT HMAC
|
||||
secret from
|
||||
type: string
|
||||
secretRef:
|
||||
secretName:
|
||||
description: SecretRef - object reference to the Secret where
|
||||
JWT values are stored
|
||||
properties:
|
||||
name:
|
||||
default: ""
|
||||
description: |-
|
||||
Name of the referent.
|
||||
This field is effectively required, but due to backwards compatibility is
|
||||
allowed to be empty. Instances of this type with an empty value here are
|
||||
almost certainly wrong.
|
||||
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
type: string
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
serviceKey:
|
||||
default: service_key
|
||||
description: ServiceKey - key in secret where to read the service
|
||||
|
|
|
@ -46,17 +46,19 @@ spec:
|
|||
DBCredentialsRef - reference to a Secret key where the DB credentials can be retrieved from
|
||||
Credentials need to be stored in basic auth form
|
||||
properties:
|
||||
name:
|
||||
default: ""
|
||||
description: |-
|
||||
Name of the referent.
|
||||
This field is effectively required, but due to backwards compatibility is
|
||||
allowed to be empty. Instances of this type with an empty value here are
|
||||
almost certainly wrong.
|
||||
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
passwordKey:
|
||||
default: password
|
||||
description: PasswordKey
|
||||
type: string
|
||||
secretName:
|
||||
type: string
|
||||
usernameKey:
|
||||
default: username
|
||||
description: UsernameKey
|
||||
type: string
|
||||
required:
|
||||
- secretName
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
dbName:
|
||||
type: string
|
||||
host:
|
||||
|
@ -814,26 +816,20 @@ spec:
|
|||
description: AnonKey - key in secret where to read the anon
|
||||
JWT from
|
||||
type: string
|
||||
jwksKey:
|
||||
default: jwks.json
|
||||
description: JwksKey - key in secret where to read the JWKS
|
||||
from
|
||||
type: string
|
||||
secretKey:
|
||||
default: secret
|
||||
description: SecretKey - key in secret where to read the JWT
|
||||
HMAC secret from
|
||||
type: string
|
||||
secretRef:
|
||||
secretName:
|
||||
description: SecretRef - object reference to the Secret where
|
||||
JWT values are stored
|
||||
properties:
|
||||
name:
|
||||
default: ""
|
||||
description: |-
|
||||
Name of the referent.
|
||||
This field is effectively required, but due to backwards compatibility is
|
||||
allowed to be empty. Instances of this type with an empty value here are
|
||||
almost certainly wrong.
|
||||
More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names
|
||||
type: string
|
||||
type: object
|
||||
x-kubernetes-map-type: atomic
|
||||
serviceKey:
|
||||
default: service_key
|
||||
description: ServiceKey - key in secret where to read the
|
||||
|
|
|
@ -39,10 +39,123 @@ spec:
|
|||
spec:
|
||||
description: StorageSpec defines the desired state of Storage.
|
||||
properties:
|
||||
foo:
|
||||
description: Foo is an example field of Storage. Edit storage_types.go
|
||||
to remove/update
|
||||
backendType:
|
||||
description: BackendType - backend storage type to use
|
||||
enum:
|
||||
- s3
|
||||
- file
|
||||
type: string
|
||||
db:
|
||||
description: |-
|
||||
DBSpec - Configure access to the Postgres database
|
||||
In most cases this will reference the supabase-storage-admin credentials secret provided by the Core resource
|
||||
properties:
|
||||
dbCredentialsRef:
|
||||
description: |-
|
||||
DBCredentialsRef - reference to a Secret key where the DB credentials can be retrieved from
|
||||
Credentials need to be stored in basic auth form
|
||||
properties:
|
||||
passwordKey:
|
||||
default: password
|
||||
description: PasswordKey
|
||||
type: string
|
||||
secretName:
|
||||
type: string
|
||||
usernameKey:
|
||||
default: username
|
||||
description: UsernameKey
|
||||
type: string
|
||||
required:
|
||||
- secretName
|
||||
type: object
|
||||
dbName:
|
||||
type: string
|
||||
host:
|
||||
type: string
|
||||
port:
|
||||
default: 5432
|
||||
description: Port - Database port, typically 5432
|
||||
type: integer
|
||||
required:
|
||||
- dbCredentialsRef
|
||||
- dbName
|
||||
- host
|
||||
type: object
|
||||
enableImageTransformation:
|
||||
description: |-
|
||||
EnableImageTransformation - whether to deploy the image proxy
|
||||
the image proxy scale images to lower resolutions on demand to reduce traffic for instance for mobile devices
|
||||
type: boolean
|
||||
fileSizeLimit:
|
||||
default: 52428800
|
||||
description: FileSizeLimit - maximum file upload size in bytes
|
||||
format: int64
|
||||
type: integer
|
||||
jwtAuth:
|
||||
description: |-
|
||||
JwtAuth - Configure the JWT authentication parameters.
|
||||
This includes where to retrieve anon and service key from as well as JWT secret and JWKS references
|
||||
needed to validate JWTs send to the API
|
||||
properties:
|
||||
anonKey:
|
||||
default: anon_key
|
||||
description: AnonKey - key in secret where to read the anon JWT
|
||||
from
|
||||
type: string
|
||||
jwksKey:
|
||||
default: jwks.json
|
||||
description: JwksKey - key in secret where to read the JWKS from
|
||||
type: string
|
||||
secretKey:
|
||||
default: secret
|
||||
description: SecretKey - key in secret where to read the JWT HMAC
|
||||
secret from
|
||||
type: string
|
||||
secretName:
|
||||
description: SecretRef - object reference to the Secret where
|
||||
JWT values are stored
|
||||
type: string
|
||||
serviceKey:
|
||||
default: service_key
|
||||
description: ServiceKey - key in secret where to read the service
|
||||
JWT from
|
||||
type: string
|
||||
type: object
|
||||
s3:
|
||||
description: S3 - Configure S3 protocol
|
||||
properties:
|
||||
allowForwardedHeader:
|
||||
default: true
|
||||
description: AllowForwardedHeader
|
||||
type: boolean
|
||||
credentialsSecretRef:
|
||||
description: CredentialsSecretRef - reference to the Secret where
|
||||
access key id and access secret key are stored
|
||||
properties:
|
||||
accessKeyIdKey:
|
||||
default: accessKeyId
|
||||
description: AccessKeyIdKey - key in Secret where access key
|
||||
id will be referenced from
|
||||
type: string
|
||||
accessSecretKeyKey:
|
||||
default: secretAccessKey
|
||||
description: AccessSecretKeyKey - key in Secret where access
|
||||
secret key will be referenced from
|
||||
type: string
|
||||
secretName:
|
||||
type: string
|
||||
required:
|
||||
- secretName
|
||||
type: object
|
||||
region:
|
||||
default: us-east-1
|
||||
description: Region - S3 region to use in the API
|
||||
type: string
|
||||
type: object
|
||||
required:
|
||||
- backendType
|
||||
- db
|
||||
- jwtAuth
|
||||
type: object
|
||||
status:
|
||||
description: StorageStatus defines the observed state of Storage.
|
||||
|
|
|
@ -10,12 +10,11 @@ spec:
|
|||
host: cluster-example-rw.supabase-demo.svc
|
||||
dbName: app
|
||||
dbCredentialsRef:
|
||||
name: db-roles-creds-supabase-admin
|
||||
secretName: core-sample-db-creds-supabase-admin
|
||||
studio:
|
||||
externalUrl: http://localhost:8000
|
||||
jwt:
|
||||
anonKey: anon_key
|
||||
secretKey: secret
|
||||
secretRef:
|
||||
name: core-sample-jwt
|
||||
secretName: core-sample-jwt
|
||||
serviceKey: service_key
|
||||
|
|
|
@ -4,6 +4,14 @@ metadata:
|
|||
labels:
|
||||
app.kubernetes.io/name: supabase-operator
|
||||
app.kubernetes.io/managed-by: kustomize
|
||||
name: storage-sample
|
||||
name: core-sample
|
||||
spec:
|
||||
# TODO(user): Add fields here
|
||||
backendType: file
|
||||
db:
|
||||
host: cluster-example-rw.supabase-demo.svc
|
||||
dbName: app
|
||||
dbCredentialsRef:
|
||||
secretName: core-sample-db-creds-supabase-storage-admin
|
||||
enableImageTransformation: true
|
||||
jwtAuth:
|
||||
secretName: core-sample-jwt
|
||||
|
|
|
@ -64,6 +64,26 @@ webhooks:
|
|||
resources:
|
||||
- dashboards
|
||||
sideEffects: None
|
||||
- admissionReviewVersions:
|
||||
- v1
|
||||
clientConfig:
|
||||
service:
|
||||
name: webhook-service
|
||||
namespace: system
|
||||
path: /mutate-supabase-k8s-icb4dc0-de-v1alpha1-storage
|
||||
failurePolicy: Fail
|
||||
name: mstorage-v1alpha1.kb.io
|
||||
rules:
|
||||
- apiGroups:
|
||||
- supabase.k8s.icb4dc0.de
|
||||
apiVersions:
|
||||
- v1alpha1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
resources:
|
||||
- storages
|
||||
sideEffects: None
|
||||
---
|
||||
apiVersion: admissionregistration.k8s.io/v1
|
||||
kind: ValidatingWebhookConfiguration
|
||||
|
@ -130,3 +150,23 @@ webhooks:
|
|||
resources:
|
||||
- dashboards
|
||||
sideEffects: None
|
||||
- admissionReviewVersions:
|
||||
- v1
|
||||
clientConfig:
|
||||
service:
|
||||
name: webhook-service
|
||||
namespace: system
|
||||
path: /validate-supabase-k8s-icb4dc0-de-v1alpha1-storage
|
||||
failurePolicy: Fail
|
||||
name: vstorage-v1alpha1.kb.io
|
||||
rules:
|
||||
- apiGroups:
|
||||
- supabase.k8s.icb4dc0.de
|
||||
apiVersions:
|
||||
- v1alpha1
|
||||
operations:
|
||||
- CREATE
|
||||
- UPDATE
|
||||
resources:
|
||||
- storages
|
||||
sideEffects: None
|
||||
|
|
|
@ -229,12 +229,12 @@ _Appears in:_
|
|||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `secret` _string_ | Secret - JWT HMAC secret in plain text<br />This is WRITE-ONLY and will be copied to the SecretRef by the defaulter | | |
|
||||
| `secretRef` _[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#localobjectreference-v1-core)_ | SecretRef - object reference to the Secret where JWT values are stored | | |
|
||||
| `secretName` _string_ | SecretRef - object reference to the Secret where JWT values are stored | | |
|
||||
| `secretKey` _string_ | SecretKey - key in secret where to read the JWT HMAC secret from | secret | |
|
||||
| `jwksKey` _string_ | JwksKey - key in secret where to read the JWKS from | jwks.json | |
|
||||
| `anonKey` _string_ | AnonKey - key in secret where to read the anon JWT from | anon_key | |
|
||||
| `serviceKey` _string_ | ServiceKey - key in secret where to read the service JWT from | service_key | |
|
||||
| `secret` _string_ | Secret - JWT HMAC secret in plain text<br />This is WRITE-ONLY and will be copied to the SecretRef by the defaulter | | |
|
||||
| `expiry` _integer_ | Expiry - expiration time in seconds for JWTs | 3600 | |
|
||||
|
||||
|
||||
|
@ -314,26 +314,7 @@ _Appears in:_
|
|||
| `host` _string_ | | | |
|
||||
| `port` _integer_ | Port - Database port, typically 5432 | 5432 | |
|
||||
| `dbName` _string_ | | | |
|
||||
| `dbCredentialsRef` _[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#localobjectreference-v1-core)_ | DBCredentialsRef - reference to a Secret key where the DB credentials can be retrieved from<br />Credentials need to be stored in basic auth form | | |
|
||||
|
||||
|
||||
#### DashboardJwtSpec
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
_Appears in:_
|
||||
- [StudioSpec](#studiospec)
|
||||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `secretRef` _[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#localobjectreference-v1-core)_ | SecretRef - object reference to the Secret where JWT values are stored | | |
|
||||
| `secretKey` _string_ | SecretKey - key in secret where to read the JWT HMAC secret from | secret | |
|
||||
| `anonKey` _string_ | AnonKey - key in secret where to read the anon JWT from | anon_key | |
|
||||
| `serviceKey` _string_ | ServiceKey - key in secret where to read the service JWT from | service_key | |
|
||||
| `dbCredentialsRef` _[DbCredentialsReference](#dbcredentialsreference)_ | DBCredentialsRef - reference to a Secret key where the DB credentials can be retrieved from<br />Credentials need to be stored in basic auth form | | |
|
||||
|
||||
|
||||
#### DashboardList
|
||||
|
@ -422,11 +403,11 @@ _Appears in:_
|
|||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `supabaseAdmin` _[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#localobjectreference-v1-core)_ | | | |
|
||||
| `authenticator` _[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#localobjectreference-v1-core)_ | | | |
|
||||
| `supabaseAuthAdmin` _[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#localobjectreference-v1-core)_ | | | |
|
||||
| `supabaseFunctionsAdmin` _[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#localobjectreference-v1-core)_ | | | |
|
||||
| `supabaseStorageAdmin` _[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#localobjectreference-v1-core)_ | | | |
|
||||
| `supabaseAdmin` _string_ | | | |
|
||||
| `authenticator` _string_ | | | |
|
||||
| `supabaseAuthAdmin` _string_ | | | |
|
||||
| `supabaseFunctionsAdmin` _string_ | | | |
|
||||
| `supabaseStorageAdmin` _string_ | | | |
|
||||
|
||||
|
||||
#### DatabaseStatus
|
||||
|
@ -446,6 +427,25 @@ _Appears in:_
|
|||
| `roles` _object (keys:string, values:integer array)_ | | | |
|
||||
|
||||
|
||||
#### DbCredentialsReference
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
_Appears in:_
|
||||
- [DashboardDbSpec](#dashboarddbspec)
|
||||
- [StorageApiDbSpec](#storageapidbspec)
|
||||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `secretName` _string_ | | | |
|
||||
| `usernameKey` _string_ | UsernameKey | username | |
|
||||
| `passwordKey` _string_ | PasswordKey | password | |
|
||||
|
||||
|
||||
#### EmailAuthProvider
|
||||
|
||||
|
||||
|
@ -484,7 +484,7 @@ _Appears in:_
|
|||
| `host` _string_ | | | |
|
||||
| `port` _integer_ | | | |
|
||||
| `maxFrequency` _integer_ | | | |
|
||||
| `credentialsFrom` _[LocalObjectReference](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#localobjectreference-v1-core)_ | | | |
|
||||
| `credentialsRef` _[SmtpCredentialsReference](#smtpcredentialsreference)_ | | | |
|
||||
|
||||
|
||||
#### EnvoySpec
|
||||
|
@ -558,6 +558,28 @@ _Appears in:_
|
|||
| `pullPolicy` _[PullPolicy](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#pullpolicy-v1-core)_ | | | |
|
||||
|
||||
|
||||
#### JwtSpec
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
_Appears in:_
|
||||
- [CoreJwtSpec](#corejwtspec)
|
||||
- [StorageSpec](#storagespec)
|
||||
- [StudioSpec](#studiospec)
|
||||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `secretName` _string_ | SecretRef - object reference to the Secret where JWT values are stored | | |
|
||||
| `secretKey` _string_ | SecretKey - key in secret where to read the JWT HMAC secret from | secret | |
|
||||
| `jwksKey` _string_ | JwksKey - key in secret where to read the JWKS from | jwks.json | |
|
||||
| `anonKey` _string_ | AnonKey - key in secret where to read the anon JWT from | anon_key | |
|
||||
| `serviceKey` _string_ | ServiceKey - key in secret where to read the service JWT from | service_key | |
|
||||
|
||||
|
||||
#### MigrationStatus
|
||||
|
||||
_Underlying type:_ _[Time](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.30/#time-v1-meta)_
|
||||
|
@ -642,6 +664,60 @@ _Appears in:_
|
|||
| `workloadTemplate` _[WorkloadTemplate](#workloadtemplate)_ | WorkloadTemplate - customize the PostgREST workload | | |
|
||||
|
||||
|
||||
#### S3CredentialsRef
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
_Appears in:_
|
||||
- [S3ProtocolSpec](#s3protocolspec)
|
||||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `secretName` _string_ | | | |
|
||||
| `accessKeyIdKey` _string_ | AccessKeyIdKey - key in Secret where access key id will be referenced from | accessKeyId | |
|
||||
| `accessSecretKeyKey` _string_ | AccessSecretKeyKey - key in Secret where access secret key will be referenced from | secretAccessKey | |
|
||||
|
||||
|
||||
#### S3ProtocolSpec
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
_Appears in:_
|
||||
- [StorageSpec](#storagespec)
|
||||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `region` _string_ | Region - S3 region to use in the API | us-east-1 | |
|
||||
| `allowForwardedHeader` _boolean_ | AllowForwardedHeader | true | |
|
||||
| `credentialsSecretRef` _[S3CredentialsRef](#s3credentialsref)_ | CredentialsSecretRef - reference to the Secret where access key id and access secret key are stored | | |
|
||||
|
||||
|
||||
#### SmtpCredentialsReference
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
_Appears in:_
|
||||
- [EmailAuthSmtpSpec](#emailauthsmtpspec)
|
||||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `secretName` _string_ | | | |
|
||||
| `usernameKey` _string_ | UsernameKey | username | |
|
||||
| `passwordKey` _string_ | PasswordKey | password | |
|
||||
|
||||
|
||||
#### Storage
|
||||
|
||||
|
||||
|
@ -661,6 +737,42 @@ _Appears in:_
|
|||
| `spec` _[StorageSpec](#storagespec)_ | | | |
|
||||
|
||||
|
||||
#### StorageApiDbSpec
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
_Appears in:_
|
||||
- [StorageSpec](#storagespec)
|
||||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `host` _string_ | | | |
|
||||
| `port` _integer_ | Port - Database port, typically 5432 | 5432 | |
|
||||
| `dbName` _string_ | | | |
|
||||
| `dbCredentialsRef` _[DbCredentialsReference](#dbcredentialsreference)_ | DBCredentialsRef - reference to a Secret key where the DB credentials can be retrieved from<br />Credentials need to be stored in basic auth form | | |
|
||||
|
||||
|
||||
#### StorageBackend
|
||||
|
||||
_Underlying type:_ _string_
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
_Appears in:_
|
||||
- [StorageSpec](#storagespec)
|
||||
|
||||
| Field | Description |
|
||||
| --- | --- |
|
||||
| `file` | |
|
||||
| `s3` | |
|
||||
|
||||
|
||||
#### StorageList
|
||||
|
||||
|
||||
|
@ -692,7 +804,12 @@ _Appears in:_
|
|||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `foo` _string_ | Foo is an example field of Storage. Edit storage_types.go to remove/update | | |
|
||||
| `backendType` _[StorageBackend](#storagebackend)_ | BackendType - backend storage type to use | | Enum: [s3 file] <br /> |
|
||||
| `fileSizeLimit` _integer_ | FileSizeLimit - maximum file upload size in bytes | 52428800 | |
|
||||
| `jwtAuth` _[JwtSpec](#jwtspec)_ | JwtAuth - Configure the JWT authentication parameters.<br />This includes where to retrieve anon and service key from as well as JWT secret and JWKS references<br />needed to validate JWTs send to the API | | |
|
||||
| `db` _[StorageApiDbSpec](#storageapidbspec)_ | DBSpec - Configure access to the Postgres database<br />In most cases this will reference the supabase-storage-admin credentials secret provided by the Core resource | | |
|
||||
| `s3` _[S3ProtocolSpec](#s3protocolspec)_ | S3 - Configure S3 protocol | | |
|
||||
| `enableImageTransformation` _boolean_ | EnableImageTransformation - whether to deploy the image proxy<br />the image proxy scale images to lower resolutions on demand to reduce traffic for instance for mobile devices | | |
|
||||
|
||||
|
||||
|
||||
|
@ -710,7 +827,7 @@ _Appears in:_
|
|||
|
||||
| Field | Description | Default | Validation |
|
||||
| --- | --- | --- | --- |
|
||||
| `jwt` _[DashboardJwtSpec](#dashboardjwtspec)_ | | | |
|
||||
| `jwt` _[JwtSpec](#jwtspec)_ | | | |
|
||||
| `workloadTemplate` _[WorkloadTemplate](#workloadtemplate)_ | WorkloadTemplate - customize the studio deployment | | |
|
||||
| `gatewayServiceSelector` _object (keys:string, values:string)_ | GatewayServiceSelector - selector to find the service for the API gateway<br />Required to configure the API URL in the studio deployment<br />If you don't run multiple APIGateway instances in the same namespaces, the default will be fine | \{ app.kubernetes.io/component:api-gateway app.kubernetes.io/name:envoy \} | |
|
||||
| `externalUrl` _string_ | APIExternalURL is referring to the URL where Supabase API will be available<br />Typically this is the ingress of the API gateway | | |
|
||||
|
|
|
@ -38,6 +38,7 @@ import (
|
|||
"code.icb4dc0.de/prskr/supabase-operator/internal/db"
|
||||
"code.icb4dc0.de/prskr/supabase-operator/internal/errx"
|
||||
"code.icb4dc0.de/prskr/supabase-operator/internal/meta"
|
||||
"code.icb4dc0.de/prskr/supabase-operator/internal/pw"
|
||||
"code.icb4dc0.de/prskr/supabase-operator/internal/supabase"
|
||||
)
|
||||
|
||||
|
@ -161,11 +162,11 @@ func (r *CoreDbReconciler) ensureDbRolesSecrets(
|
|||
)
|
||||
|
||||
roles := map[string]supabase.DBRole{
|
||||
dbSpec.Roles.Secrets.Authenticator.Name: supabase.DBRoleAuthenticator,
|
||||
dbSpec.Roles.Secrets.AuthAdmin.Name: supabase.DBRoleAuthAdmin,
|
||||
dbSpec.Roles.Secrets.FunctionsAdmin.Name: supabase.DBRoleFunctionsAdmin,
|
||||
dbSpec.Roles.Secrets.StorageAdmin.Name: supabase.DBRoleStorageAdmin,
|
||||
dbSpec.Roles.Secrets.Admin.Name: supabase.DBRoleSupabaseAdmin,
|
||||
dbSpec.Roles.Secrets.Authenticator: supabase.DBRoleAuthenticator,
|
||||
dbSpec.Roles.Secrets.AuthAdmin: supabase.DBRoleAuthAdmin,
|
||||
dbSpec.Roles.Secrets.FunctionsAdmin: supabase.DBRoleFunctionsAdmin,
|
||||
dbSpec.Roles.Secrets.StorageAdmin: supabase.DBRoleStorageAdmin,
|
||||
dbSpec.Roles.Secrets.Admin: supabase.DBRoleSupabaseAdmin,
|
||||
}
|
||||
|
||||
if core.Status.Database.Roles == nil {
|
||||
|
@ -210,7 +211,7 @@ func (r *CoreDbReconciler) ensureDbRolesSecrets(
|
|||
if role.String() == dsnUser {
|
||||
credentialsSecret.Data[corev1.BasicAuthPasswordKey] = []byte(dsnPW)
|
||||
} else {
|
||||
credentialsSecret.Data[corev1.BasicAuthPasswordKey] = GeneratePW(24, nil)
|
||||
credentialsSecret.Data[corev1.BasicAuthPasswordKey] = pw.GeneratePW(24, nil)
|
||||
}
|
||||
|
||||
secretLogger.Info("Update database role to match secret credentials")
|
||||
|
|
|
@ -115,7 +115,7 @@ func (r *CoreAuthReconciler) reconcileAuthDeployment(
|
|||
ValueFrom: &corev1.EnvVarSource{
|
||||
SecretKeyRef: &corev1.SecretKeySelector{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: core.Spec.Database.Roles.Secrets.AuthAdmin.Name,
|
||||
Name: core.Spec.Database.Roles.Secrets.AuthAdmin,
|
||||
},
|
||||
Key: corev1.BasicAuthPasswordKey,
|
||||
},
|
||||
|
|
|
@ -57,7 +57,7 @@ func (r *CoreJwtReconciler) Reconcile(ctx context.Context, req ctrl.Request) (re
|
|||
}
|
||||
|
||||
jwtSecret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: core.Spec.JWT.SecretRef.Name, Namespace: core.Namespace},
|
||||
ObjectMeta: metav1.ObjectMeta{Name: core.Spec.JWT.SecretName, Namespace: core.Namespace},
|
||||
}
|
||||
|
||||
_, err = controllerutil.CreateOrUpdate(ctx, r.Client, jwtSecret, func() error {
|
||||
|
|
|
@ -129,7 +129,7 @@ func (r *CorePostgrestReconiler) reconilePostgrestDeployment(
|
|||
ValueFrom: &corev1.EnvVarSource{
|
||||
SecretKeyRef: &corev1.SecretKeySelector{
|
||||
LocalObjectReference: corev1.LocalObjectReference{
|
||||
Name: core.Spec.Database.Roles.Secrets.Authenticator.Name,
|
||||
Name: core.Spec.Database.Roles.Secrets.Authenticator,
|
||||
},
|
||||
Key: corev1.BasicAuthPasswordKey,
|
||||
},
|
||||
|
|
|
@ -90,7 +90,7 @@ func (r *DashboardPGMetaReconciler) reconcilePGMetaDeployment(
|
|||
|
||||
dsnSecret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: dashboard.Spec.DBSpec.DBCredentialsRef.Name,
|
||||
Name: dashboard.Spec.DBSpec.DBCredentialsRef.SecretName,
|
||||
Namespace: dashboard.Namespace,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -25,6 +25,9 @@ package controller
|
|||
// +kubebuilder:rbac:groups=supabase.k8s.icb4dc0.de,resources=dashboards,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups=supabase.k8s.icb4dc0.de,resources=dashboards/status,verbs=get;update;patch
|
||||
// +kubebuilder:rbac:groups=supabase.k8s.icb4dc0.de,resources=dashboards/finalizers,verbs=update
|
||||
// +kubebuilder:rbac:groups=supabase.k8s.icb4dc0.de,resources=storages,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups=supabase.k8s.icb4dc0.de,resources=storages/status,verbs=get;update;patch
|
||||
// +kubebuilder:rbac:groups=supabase.k8s.icb4dc0.de,resources=storages/finalizers,verbs=update
|
||||
// +kubebuilder:rbac:groups=apps,resources=deployments,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups="",resources=secrets;configmaps;services,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups="",resources=events,verbs=create
|
||||
|
|
|
@ -33,16 +33,8 @@ type StorageReconciler struct {
|
|||
Scheme *runtime.Scheme
|
||||
}
|
||||
|
||||
// +kubebuilder:rbac:groups=supabase.k8s.icb4dc0.de,resources=storages,verbs=get;list;watch;create;update;patch;delete
|
||||
// +kubebuilder:rbac:groups=supabase.k8s.icb4dc0.de,resources=storages/status,verbs=get;update;patch
|
||||
// +kubebuilder:rbac:groups=supabase.k8s.icb4dc0.de,resources=storages/finalizers,verbs=update
|
||||
|
||||
// Reconcile is part of the main kubernetes reconciliation loop which aims to
|
||||
// move the current state of the cluster closer to the desired state.
|
||||
// TODO(user): Modify the Reconcile function to compare the state specified by
|
||||
// the Storage object against the actual cluster state, and then
|
||||
// perform operations to make the cluster state reflect the state specified by
|
||||
// the user.
|
||||
//
|
||||
// For more details, check Reconcile and its Result here:
|
||||
// - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.19.4/pkg/reconcile
|
||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
|||
limitations under the License.
|
||||
*/
|
||||
|
||||
package controller
|
||||
package pw
|
||||
|
||||
import (
|
||||
"bytes"
|
|
@ -125,6 +125,30 @@ type studioDefaults struct {
|
|||
APIPort int32
|
||||
}
|
||||
|
||||
type storageEnvApiKeys struct {
|
||||
AnonKey secretEnv
|
||||
ServiceKey secretEnv
|
||||
JwtSecret secretEnv
|
||||
JwtJwks secretEnv
|
||||
DatabaseDSN stringEnv
|
||||
FileSizeLimit intEnv[uint64]
|
||||
UploadFileSizeLimit intEnv[uint64]
|
||||
UploadFileSizeLimitStandard intEnv[uint64]
|
||||
StorageBackend stringEnv
|
||||
TenantID fixedEnv
|
||||
StorageS3Region stringEnv
|
||||
GlobalS3Bucket fixedEnv
|
||||
EnableImaageTransformation boolEnv
|
||||
ImgProxyURL stringEnv
|
||||
TusUrlPath fixedEnv
|
||||
S3AccessKeyId secretEnv
|
||||
S3AccessKeySecret secretEnv
|
||||
S3ProtocolPrefix fixedEnv
|
||||
S3AllowForwardedHeader boolEnv
|
||||
}
|
||||
|
||||
type storageApiDefaults struct{}
|
||||
|
||||
type envoyDefaults struct {
|
||||
ConfigKey string
|
||||
UID, GID int64
|
||||
|
@ -160,6 +184,7 @@ var ServiceConfig = struct {
|
|||
Auth serviceConfig[authEnvKeys, authConfigDefaults]
|
||||
PGMeta serviceConfig[pgMetaEnvKeys, pgMetaDefaults]
|
||||
Studio serviceConfig[studioEnvKeys, studioDefaults]
|
||||
Storage serviceConfig[storageEnvApiKeys, storageApiDefaults]
|
||||
Envoy envoyServiceConfig
|
||||
JWT jwtConfig
|
||||
}{
|
||||
|
@ -259,6 +284,31 @@ var ServiceConfig = struct {
|
|||
APIPort: 3000,
|
||||
},
|
||||
},
|
||||
Storage: serviceConfig[storageEnvApiKeys, storageApiDefaults]{
|
||||
Name: "storage-api",
|
||||
EnvKeys: storageEnvApiKeys{
|
||||
AnonKey: "ANON_KEY",
|
||||
ServiceKey: "SERVICE_KEY",
|
||||
JwtSecret: "AUTH_JWT_SECRET",
|
||||
JwtJwks: "AUTH_JWT_JWKS",
|
||||
StorageBackend: "STORAGE_BACKEND",
|
||||
DatabaseDSN: "DATABASE_URL",
|
||||
FileSizeLimit: "FILE_SIZE_LIMIT",
|
||||
UploadFileSizeLimit: "UPLOAD_FILE_SIZE_LIMIT",
|
||||
UploadFileSizeLimitStandard: "UPLOAD_FILE_SIZE_LIMIT_STANDARD",
|
||||
TenantID: fixedEnvOf("TENANT_ID", "stub"),
|
||||
StorageS3Region: "STORAGE_S3_REGION",
|
||||
GlobalS3Bucket: fixedEnvOf("GLOBAL_S3_BUCKET", "stub"),
|
||||
EnableImaageTransformation: "ENABLE_IMAGE_TRANSFORMATION",
|
||||
ImgProxyURL: "IMGPROXY_URL",
|
||||
TusUrlPath: fixedEnvOf("TUS_URL_PATH", "/storage/v1/upload/resumable"),
|
||||
S3AccessKeyId: "S3_PROTOCOL_ACCESS_KEY_ID",
|
||||
S3AccessKeySecret: "S3_PROTOCOL_ACCESS_KEY_SECRET",
|
||||
S3ProtocolPrefix: fixedEnvOf("S3_PROTOCOL_PREFIX", "/storage/v1"),
|
||||
S3AllowForwardedHeader: "S3_ALLOW_FORWARDED_HEADER",
|
||||
},
|
||||
Defaults: storageApiDefaults{},
|
||||
},
|
||||
Envoy: envoyServiceConfig{
|
||||
Defaults: envoyDefaults{
|
||||
ConfigKey: "config.yaml",
|
||||
|
|
|
@ -63,44 +63,32 @@ func (d *CoreCustomDefaulter) Default(ctx context.Context, obj runtime.Object) e
|
|||
return fmt.Errorf("ensuring JWT secret: %w", err)
|
||||
}
|
||||
|
||||
// TODO copy plain text DSN to secret if present
|
||||
|
||||
corelog.Info("Defaulting database roles")
|
||||
if !core.Spec.Database.Roles.SelfManaged {
|
||||
const roleCredsSecretNameTemplate = "db-roles-creds-%s"
|
||||
if core.Spec.Database.Roles.Secrets.Admin == nil {
|
||||
const roleCredsSecretNameTemplate = "%s-db-creds-%s"
|
||||
if core.Spec.Database.Roles.Secrets.Admin == "" {
|
||||
corelog.Info("Defaulting role", "role_name", supabase.DBRoleSupabaseAdmin)
|
||||
core.Spec.Database.Roles.Secrets.Admin = &corev1.LocalObjectReference{
|
||||
Name: fmt.Sprintf(roleCredsSecretNameTemplate, supabase.DBRoleSupabaseAdmin.K8sString()),
|
||||
}
|
||||
core.Spec.Database.Roles.Secrets.Admin = fmt.Sprintf(roleCredsSecretNameTemplate, core.Name, supabase.DBRoleSupabaseAdmin.K8sString())
|
||||
}
|
||||
|
||||
if core.Spec.Database.Roles.Secrets.Authenticator == nil {
|
||||
if core.Spec.Database.Roles.Secrets.Authenticator == "" {
|
||||
corelog.Info("Defaulting role", "role_name", supabase.DBRoleAuthenticator)
|
||||
core.Spec.Database.Roles.Secrets.Authenticator = &corev1.LocalObjectReference{
|
||||
Name: fmt.Sprintf(roleCredsSecretNameTemplate, supabase.DBRoleAuthenticator.K8sString()),
|
||||
}
|
||||
core.Spec.Database.Roles.Secrets.Authenticator = fmt.Sprintf(roleCredsSecretNameTemplate, core.Name, supabase.DBRoleAuthenticator.K8sString())
|
||||
}
|
||||
|
||||
if core.Spec.Database.Roles.Secrets.AuthAdmin == nil {
|
||||
if core.Spec.Database.Roles.Secrets.AuthAdmin == "" {
|
||||
corelog.Info("Defaulting role", "role_name", supabase.DBRoleAuthAdmin)
|
||||
core.Spec.Database.Roles.Secrets.AuthAdmin = &corev1.LocalObjectReference{
|
||||
Name: fmt.Sprintf(roleCredsSecretNameTemplate, supabase.DBRoleAuthAdmin.K8sString()),
|
||||
}
|
||||
core.Spec.Database.Roles.Secrets.AuthAdmin = fmt.Sprintf(roleCredsSecretNameTemplate, core.Name, supabase.DBRoleAuthAdmin.K8sString())
|
||||
}
|
||||
|
||||
if core.Spec.Database.Roles.Secrets.FunctionsAdmin == nil {
|
||||
if core.Spec.Database.Roles.Secrets.FunctionsAdmin == "" {
|
||||
corelog.Info("Defaulting role", "role_name", supabase.DBRoleFunctionsAdmin)
|
||||
core.Spec.Database.Roles.Secrets.FunctionsAdmin = &corev1.LocalObjectReference{
|
||||
Name: fmt.Sprintf(roleCredsSecretNameTemplate, supabase.DBRoleFunctionsAdmin.K8sString()),
|
||||
}
|
||||
core.Spec.Database.Roles.Secrets.FunctionsAdmin = fmt.Sprintf(roleCredsSecretNameTemplate, core.Name, supabase.DBRoleFunctionsAdmin.K8sString())
|
||||
}
|
||||
|
||||
if core.Spec.Database.Roles.Secrets.StorageAdmin == nil {
|
||||
if core.Spec.Database.Roles.Secrets.StorageAdmin == "" {
|
||||
corelog.Info("Defaulting role", "role_name", supabase.DBRoleStorageAdmin)
|
||||
core.Spec.Database.Roles.Secrets.StorageAdmin = &corev1.LocalObjectReference{
|
||||
Name: fmt.Sprintf(roleCredsSecretNameTemplate, supabase.DBRoleStorageAdmin.K8sString()),
|
||||
}
|
||||
core.Spec.Database.Roles.Secrets.StorageAdmin = fmt.Sprintf(roleCredsSecretNameTemplate, core.Name, supabase.DBRoleStorageAdmin.K8sString())
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -114,10 +102,8 @@ func (d *CoreCustomDefaulter) defaultJWT(ctx context.Context, core *supabasev1al
|
|||
core.Spec.JWT = new(supabasev1alpha1.CoreJwtSpec)
|
||||
}
|
||||
|
||||
if core.Spec.JWT.SecretRef == nil {
|
||||
core.Spec.JWT.SecretRef = &corev1.LocalObjectReference{
|
||||
Name: supabase.ServiceConfig.JWT.ObjectName(core),
|
||||
}
|
||||
if core.Spec.JWT.SecretName == "" {
|
||||
core.Spec.JWT.SecretName = supabase.ServiceConfig.JWT.ObjectName(core)
|
||||
}
|
||||
|
||||
if core.Spec.JWT.SecretKey == "" {
|
||||
|
@ -138,7 +124,7 @@ func (d *CoreCustomDefaulter) defaultJWT(ctx context.Context, core *supabasev1al
|
|||
|
||||
jwtSecret := &corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: core.Spec.JWT.SecretRef.Name,
|
||||
Name: core.Spec.JWT.SecretName,
|
||||
Namespace: core.Namespace,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -127,47 +127,47 @@ func (v *CoreCustomValidator) validateDb(
|
|||
}
|
||||
}
|
||||
|
||||
if authenticator := dbSpec.Roles.Secrets.Authenticator; authenticator == nil {
|
||||
if authenticator := dbSpec.Roles.Secrets.Authenticator; authenticator == "" {
|
||||
return warnings, fmt.Errorf("%w: %s", ErrManagedCredentialsNotSpecified, supabase.DBRoleAuthenticator)
|
||||
} else {
|
||||
exists, err := doesSecretExists(ctx, authenticator.Name)
|
||||
exists, err := doesSecretExists(ctx, authenticator)
|
||||
if err != nil {
|
||||
return warnings, err
|
||||
} else if !exists {
|
||||
return warnings, fmt.Errorf("%w: %s", ErrManagedCredentialsSecretMissing, authenticator.Name)
|
||||
return warnings, fmt.Errorf("%w: %s", ErrManagedCredentialsSecretMissing, authenticator)
|
||||
}
|
||||
}
|
||||
|
||||
if authAdmin := dbSpec.Roles.Secrets.AuthAdmin; authAdmin == nil {
|
||||
if authAdmin := dbSpec.Roles.Secrets.AuthAdmin; authAdmin == "" {
|
||||
return warnings, fmt.Errorf("%w: %s", ErrManagedCredentialsNotSpecified, supabase.DBRoleAuthAdmin)
|
||||
} else {
|
||||
exists, err := doesSecretExists(ctx, authAdmin.Name)
|
||||
exists, err := doesSecretExists(ctx, authAdmin)
|
||||
if err != nil {
|
||||
return warnings, err
|
||||
} else if !exists {
|
||||
return warnings, fmt.Errorf("%w: %s", ErrManagedCredentialsSecretMissing, authAdmin.Name)
|
||||
return warnings, fmt.Errorf("%w: %s", ErrManagedCredentialsSecretMissing, authAdmin)
|
||||
}
|
||||
}
|
||||
|
||||
if functionsAdmin := dbSpec.Roles.Secrets.FunctionsAdmin; functionsAdmin == nil {
|
||||
if functionsAdmin := dbSpec.Roles.Secrets.FunctionsAdmin; functionsAdmin == "" {
|
||||
return warnings, fmt.Errorf("%w: %s", ErrManagedCredentialsNotSpecified, supabase.DBRoleFunctionsAdmin)
|
||||
} else {
|
||||
exists, err := doesSecretExists(ctx, functionsAdmin.Name)
|
||||
exists, err := doesSecretExists(ctx, functionsAdmin)
|
||||
if err != nil {
|
||||
return warnings, err
|
||||
} else if !exists {
|
||||
return warnings, fmt.Errorf("%w: %s", ErrManagedCredentialsSecretMissing, functionsAdmin.Name)
|
||||
return warnings, fmt.Errorf("%w: %s", ErrManagedCredentialsSecretMissing, functionsAdmin)
|
||||
}
|
||||
}
|
||||
|
||||
if storageAdmin := dbSpec.Roles.Secrets.StorageAdmin; storageAdmin == nil {
|
||||
if storageAdmin := dbSpec.Roles.Secrets.StorageAdmin; storageAdmin == "" {
|
||||
return warnings, fmt.Errorf("%w: %s", ErrManagedCredentialsNotSpecified, supabase.DBRoleStorageAdmin)
|
||||
} else {
|
||||
exists, err := doesSecretExists(ctx, storageAdmin.Name)
|
||||
exists, err := doesSecretExists(ctx, storageAdmin)
|
||||
if err != nil {
|
||||
return warnings, err
|
||||
} else if !exists {
|
||||
return warnings, fmt.Errorf("%w: %s", ErrManagedCredentialsSecretMissing, storageAdmin.Name)
|
||||
return warnings, fmt.Errorf("%w: %s", ErrManagedCredentialsSecretMissing, storageAdmin)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -53,3 +53,11 @@ func SetupDashboardWebhookWithManager(mgr ctrl.Manager) error {
|
|||
WithDefaulter(&DashboardCustomDefaulter{}).
|
||||
Complete()
|
||||
}
|
||||
|
||||
// SetupStorageWebhookWithManager registers the webhook for Storage in the manager.
|
||||
func SetupStorageWebhookWithManager(mgr ctrl.Manager) error {
|
||||
return ctrl.NewWebhookManagedBy(mgr).For(&supabasev1alpha1.Storage{}).
|
||||
WithValidator(&StorageCustomValidator{}).
|
||||
WithDefaulter(&StorageCustomDefaulter{Client: mgr.GetClient()}).
|
||||
Complete()
|
||||
}
|
||||
|
|
109
internal/webhook/v1alpha1/storage_webhook_defaulter.go
Normal file
109
internal/webhook/v1alpha1/storage_webhook_defaulter.go
Normal file
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
Copyright 2025 Peter Kurfer.
|
||||
|
||||
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 v1alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"maps"
|
||||
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client"
|
||||
"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
||||
|
||||
supabasev1alpha1 "code.icb4dc0.de/prskr/supabase-operator/api/v1alpha1"
|
||||
"code.icb4dc0.de/prskr/supabase-operator/internal/meta"
|
||||
"code.icb4dc0.de/prskr/supabase-operator/internal/pw"
|
||||
)
|
||||
|
||||
// +kubebuilder:webhook:path=/mutate-supabase-k8s-icb4dc0-de-v1alpha1-storage,mutating=true,failurePolicy=fail,sideEffects=None,groups=supabase.k8s.icb4dc0.de,resources=storages,verbs=create;update,versions=v1alpha1,name=mstorage-v1alpha1.kb.io,admissionReviewVersions=v1
|
||||
|
||||
// StorageCustomDefaulter struct is responsible for setting default values on the custom resource of the
|
||||
// Kind Storage when those are created or updated.
|
||||
//
|
||||
// NOTE: The +kubebuilder:object:generate=false marker prevents controller-gen from generating DeepCopy methods,
|
||||
// as it is used only for temporary operations and does not need to be deeply copied.
|
||||
type StorageCustomDefaulter struct {
|
||||
client.Client
|
||||
}
|
||||
|
||||
var _ webhook.CustomDefaulter = &StorageCustomDefaulter{}
|
||||
|
||||
// Default implements webhook.CustomDefaulter so a webhook will be registered for the Kind Storage.
|
||||
func (d *StorageCustomDefaulter) Default(ctx context.Context, obj runtime.Object) error {
|
||||
storage, ok := obj.(*supabasev1alpha1.Storage)
|
||||
|
||||
if !ok {
|
||||
return fmt.Errorf("expected an Storage object but got %T", obj)
|
||||
}
|
||||
storagelog.Info("Defaulting for Storage", "name", storage.GetName())
|
||||
|
||||
if err := d.defaultS3Protocol(ctx, storage); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (d *StorageCustomDefaulter) defaultS3Protocol(ctx context.Context, storage *supabasev1alpha1.Storage) error {
|
||||
if storage.Spec.S3 == nil {
|
||||
storage.Spec.S3 = new(supabasev1alpha1.S3ProtocolSpec)
|
||||
}
|
||||
|
||||
if storage.Spec.S3.CredentialsSecretRef == nil {
|
||||
storage.Spec.S3.CredentialsSecretRef = &supabasev1alpha1.S3CredentialsRef{
|
||||
AccessKeyIdKey: "accessKeyId",
|
||||
AccessSecretKeyKey: "secretAccessKey",
|
||||
SecretName: fmt.Sprintf("%s-storage-protocol-s3-credentials", storage.Name),
|
||||
}
|
||||
}
|
||||
|
||||
credentialsSecret := corev1.Secret{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
Name: storage.Spec.S3.CredentialsSecretRef.SecretName,
|
||||
Namespace: storage.Namespace,
|
||||
},
|
||||
}
|
||||
|
||||
_, err := controllerutil.CreateOrUpdate(ctx, d.Client, &credentialsSecret, func() error {
|
||||
credentialsSecret.Labels = maps.Clone(storage.Labels)
|
||||
if credentialsSecret.Labels == nil {
|
||||
credentialsSecret.Labels = make(map[string]string)
|
||||
}
|
||||
|
||||
credentialsSecret.Labels[meta.SupabaseLabel.Reload] = ""
|
||||
|
||||
if credentialsSecret.Data == nil {
|
||||
credentialsSecret.Data = make(map[string][]byte, 2)
|
||||
}
|
||||
|
||||
if _, ok := credentialsSecret.Data[storage.Spec.S3.CredentialsSecretRef.AccessKeyIdKey]; !ok {
|
||||
credentialsSecret.Data[storage.Spec.S3.CredentialsSecretRef.AccessKeyIdKey] = pw.GeneratePW(32, nil)
|
||||
}
|
||||
|
||||
if _, ok := credentialsSecret.Data[storage.Spec.S3.CredentialsSecretRef.AccessSecretKeyKey]; !ok {
|
||||
credentialsSecret.Data[storage.Spec.S3.CredentialsSecretRef.AccessSecretKeyKey] = pw.GeneratePW(64, nil)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
86
internal/webhook/v1alpha1/storage_webhook_test.go
Normal file
86
internal/webhook/v1alpha1/storage_webhook_test.go
Normal file
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
Copyright 2025 Peter Kurfer.
|
||||
|
||||
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 v1alpha1
|
||||
|
||||
import (
|
||||
. "github.com/onsi/ginkgo/v2"
|
||||
. "github.com/onsi/gomega"
|
||||
|
||||
supabasev1alpha1 "code.icb4dc0.de/prskr/supabase-operator/api/v1alpha1"
|
||||
// TODO (user): Add any additional imports if needed
|
||||
)
|
||||
|
||||
var _ = Describe("Storage Webhook", func() {
|
||||
var (
|
||||
obj *supabasev1alpha1.Storage
|
||||
oldObj *supabasev1alpha1.Storage
|
||||
validator StorageCustomValidator
|
||||
defaulter StorageCustomDefaulter
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
obj = &supabasev1alpha1.Storage{}
|
||||
oldObj = &supabasev1alpha1.Storage{}
|
||||
validator = StorageCustomValidator{}
|
||||
Expect(validator).NotTo(BeNil(), "Expected validator to be initialized")
|
||||
defaulter = StorageCustomDefaulter{}
|
||||
Expect(defaulter).NotTo(BeNil(), "Expected defaulter to be initialized")
|
||||
Expect(oldObj).NotTo(BeNil(), "Expected oldObj to be initialized")
|
||||
Expect(obj).NotTo(BeNil(), "Expected obj to be initialized")
|
||||
// TODO (user): Add any setup logic common to all tests
|
||||
})
|
||||
|
||||
AfterEach(func() {
|
||||
// TODO (user): Add any teardown logic common to all tests
|
||||
})
|
||||
|
||||
Context("When creating Storage under Defaulting Webhook", func() {
|
||||
// TODO (user): Add logic for defaulting webhooks
|
||||
// Example:
|
||||
// It("Should apply defaults when a required field is empty", func() {
|
||||
// By("simulating a scenario where defaults should be applied")
|
||||
// obj.SomeFieldWithDefault = ""
|
||||
// By("calling the Default method to apply defaults")
|
||||
// defaulter.Default(ctx, obj)
|
||||
// By("checking that the default values are set")
|
||||
// Expect(obj.SomeFieldWithDefault).To(Equal("default_value"))
|
||||
// })
|
||||
})
|
||||
|
||||
Context("When creating or updating Storage under Validating Webhook", func() {
|
||||
// TODO (user): Add logic for validating webhooks
|
||||
// Example:
|
||||
// It("Should deny creation if a required field is missing", func() {
|
||||
// By("simulating an invalid creation scenario")
|
||||
// obj.SomeRequiredField = ""
|
||||
// Expect(validator.ValidateCreate(ctx, obj)).Error().To(HaveOccurred())
|
||||
// })
|
||||
//
|
||||
// It("Should admit creation if all required fields are present", func() {
|
||||
// By("simulating an invalid creation scenario")
|
||||
// obj.SomeRequiredField = "valid_value"
|
||||
// Expect(validator.ValidateCreate(ctx, obj)).To(BeNil())
|
||||
// })
|
||||
//
|
||||
// It("Should validate updates correctly", func() {
|
||||
// By("simulating a valid update scenario")
|
||||
// oldObj.SomeRequiredField = "updated_value"
|
||||
// obj.SomeRequiredField = "updated_value"
|
||||
// Expect(validator.ValidateUpdate(ctx, oldObj, obj)).To(BeNil())
|
||||
// })
|
||||
})
|
||||
})
|
87
internal/webhook/v1alpha1/storage_webhook_validator.go
Normal file
87
internal/webhook/v1alpha1/storage_webhook_validator.go
Normal file
|
@ -0,0 +1,87 @@
|
|||
/*
|
||||
Copyright 2025 Peter Kurfer.
|
||||
|
||||
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 v1alpha1
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
logf "sigs.k8s.io/controller-runtime/pkg/log"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook"
|
||||
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
|
||||
|
||||
supabasev1alpha1 "code.icb4dc0.de/prskr/supabase-operator/api/v1alpha1"
|
||||
)
|
||||
|
||||
// nolint:unused
|
||||
// log is for logging in this package.
|
||||
var storagelog = logf.Log.WithName("storage-resource")
|
||||
|
||||
// NOTE: The 'path' attribute must follow a specific pattern and should not be modified directly here.
|
||||
// Modifying the path for an invalid path can cause API server errors; failing to locate the webhook.
|
||||
// +kubebuilder:webhook:path=/validate-supabase-k8s-icb4dc0-de-v1alpha1-storage,mutating=false,failurePolicy=fail,sideEffects=None,groups=supabase.k8s.icb4dc0.de,resources=storages,verbs=create;update,versions=v1alpha1,name=vstorage-v1alpha1.kb.io,admissionReviewVersions=v1
|
||||
|
||||
// StorageCustomValidator struct is responsible for validating the Storage resource
|
||||
// when it is created, updated, or deleted.
|
||||
//
|
||||
// NOTE: The +kubebuilder:object:generate=false marker prevents controller-gen from generating DeepCopy methods,
|
||||
// as this struct is used only for temporary operations and does not need to be deeply copied.
|
||||
type StorageCustomValidator struct {
|
||||
// TODO(user): Add more fields as needed for validation
|
||||
}
|
||||
|
||||
var _ webhook.CustomValidator = &StorageCustomValidator{}
|
||||
|
||||
// ValidateCreate implements webhook.CustomValidator so a webhook will be registered for the type Storage.
|
||||
func (v *StorageCustomValidator) ValidateCreate(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
|
||||
storage, ok := obj.(*supabasev1alpha1.Storage)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected a Storage object but got %T", obj)
|
||||
}
|
||||
storagelog.Info("Validation for Storage upon creation", "name", storage.GetName())
|
||||
|
||||
// TODO(user): fill in your validation logic upon object creation.
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// ValidateUpdate implements webhook.CustomValidator so a webhook will be registered for the type Storage.
|
||||
func (v *StorageCustomValidator) ValidateUpdate(ctx context.Context, oldObj, newObj runtime.Object) (admission.Warnings, error) {
|
||||
storage, ok := newObj.(*supabasev1alpha1.Storage)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected a Storage object for the newObj but got %T", newObj)
|
||||
}
|
||||
storagelog.Info("Validation for Storage upon update", "name", storage.GetName())
|
||||
|
||||
// TODO(user): fill in your validation logic upon object update.
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// ValidateDelete implements webhook.CustomValidator so a webhook will be registered for the type Storage.
|
||||
func (v *StorageCustomValidator) ValidateDelete(ctx context.Context, obj runtime.Object) (admission.Warnings, error) {
|
||||
storage, ok := obj.(*supabasev1alpha1.Storage)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("expected a Storage object but got %T", obj)
|
||||
}
|
||||
storagelog.Info("Validation for Storage upon deletion", "name", storage.GetName())
|
||||
|
||||
// TODO(user): fill in your validation logic upon object deletion.
|
||||
|
||||
return nil, nil
|
||||
}
|
|
@ -127,6 +127,9 @@ var _ = BeforeSuite(func() {
|
|||
err = SetupDashboardWebhookWithManager(mgr)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = SetupStorageWebhookWithManager(mgr)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
// +kubebuilder:scaffold:webhook
|
||||
|
||||
go func() {
|
||||
|
|
Loading…
Add table
Reference in a new issue