feat(storage): finish initial basic implementation
- support both s3 & file storage backends - support imgproxy to scale images - manually tested with MinIO & local storage - fixed service discovery issue in APIGatey reconciler not detecting service changes - refactored defaults and env variable code to make it manageable again - add repo link to docs
This commit is contained in:
parent
604525de38
commit
0014927ca9
46 changed files with 16170 additions and 606 deletions
internal/controlplane
|
@ -19,9 +19,9 @@ package controlplane
|
|||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"hash/fnv"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -87,7 +87,7 @@ func (r *APIGatewayReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
|||
return ctrl.Result{}, fmt.Errorf("failed to prepare config hash: %w", err)
|
||||
}
|
||||
|
||||
serviceHash := sha256.New().Sum(rawServices)
|
||||
serviceHash := fnv.New64a().Sum(rawServices)
|
||||
if bytes.Equal(serviceHash, gateway.Status.Envoy.ResourceHash) {
|
||||
logger.Info("Resource hash did not change - skipping reconciliation")
|
||||
return ctrl.Result{}, nil
|
||||
|
|
|
@ -36,7 +36,9 @@ func (c *GoTrueCluster) Cluster(instance string) []*clusterv3.Cluster {
|
|||
return nil
|
||||
}
|
||||
|
||||
return []*clusterv3.Cluster{c.ServiceCluster.Cluster(fmt.Sprintf("auth@%s", instance), 9999)}
|
||||
serviceCfg := supabase.ServiceConfig.Auth
|
||||
|
||||
return []*clusterv3.Cluster{c.ServiceCluster.Cluster(fmt.Sprintf("%s@%s", serviceCfg.Name, instance), uint32(serviceCfg.Defaults.APIPort))}
|
||||
}
|
||||
|
||||
func (c *GoTrueCluster) Routes(instance string) []*routev3.Route {
|
||||
|
|
|
@ -34,8 +34,11 @@ func (c *PostgrestCluster) Cluster(instance string) []*clusterv3.Cluster {
|
|||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
serviceCfg := supabase.ServiceConfig.Postgrest
|
||||
|
||||
return []*clusterv3.Cluster{
|
||||
c.ServiceCluster.Cluster(fmt.Sprintf("%s@%s", supabase.ServiceConfig.Postgrest.Name, instance), 3000),
|
||||
c.ServiceCluster.Cluster(fmt.Sprintf("%s@%s", serviceCfg.Name, instance), uint32(serviceCfg.Defaults.ServerPort)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,6 +47,8 @@ func (c *PostgrestCluster) Routes(instance string) []*routev3.Route {
|
|||
return nil
|
||||
}
|
||||
|
||||
serviceCfg := supabase.ServiceConfig.Postgrest
|
||||
|
||||
return []*routev3.Route{
|
||||
{
|
||||
Name: "PostgREST: /rest/v1/* -> http://rest:3000/*",
|
||||
|
@ -55,7 +60,7 @@ func (c *PostgrestCluster) Routes(instance string) []*routev3.Route {
|
|||
Action: &routev3.Route_Route{
|
||||
Route: &routev3.RouteAction{
|
||||
ClusterSpecifier: &routev3.RouteAction_Cluster{
|
||||
Cluster: fmt.Sprintf("%s@%s", supabase.ServiceConfig.Postgrest.Name, instance),
|
||||
Cluster: fmt.Sprintf("%s@%s", serviceCfg.Name, instance),
|
||||
},
|
||||
PrefixRewrite: "/",
|
||||
},
|
||||
|
@ -71,7 +76,7 @@ func (c *PostgrestCluster) Routes(instance string) []*routev3.Route {
|
|||
Action: &routev3.Route_Route{
|
||||
Route: &routev3.RouteAction{
|
||||
ClusterSpecifier: &routev3.RouteAction_Cluster{
|
||||
Cluster: fmt.Sprintf("%s@%s", supabase.ServiceConfig.Postgrest.Name, instance),
|
||||
Cluster: fmt.Sprintf("%s@%s", serviceCfg.Name, instance),
|
||||
},
|
||||
PrefixRewrite: "/rpc/graphql",
|
||||
},
|
||||
|
|
|
@ -36,11 +36,12 @@ import (
|
|||
)
|
||||
|
||||
type EnvoyServices struct {
|
||||
ServiceLabelKey string `json:"-"`
|
||||
Postgrest *PostgrestCluster `json:"postgrest,omitempty"`
|
||||
GoTrue *GoTrueCluster `json:"auth,omitempty"`
|
||||
PGMeta *PGMetaCluster `json:"pgmeta,omitempty"`
|
||||
Studio *StudioCluster `json:"studio,omitempty"`
|
||||
ServiceLabelKey string `json:"-"`
|
||||
Postgrest *PostgrestCluster `json:"postgrest,omitempty"`
|
||||
GoTrue *GoTrueCluster `json:"auth,omitempty"`
|
||||
StorageApi *StorageApiCluster `json:"storageApi,omitempty"`
|
||||
PGMeta *PGMetaCluster `json:"pgmeta,omitempty"`
|
||||
Studio *StudioCluster `json:"studio,omitempty"`
|
||||
}
|
||||
|
||||
func (s *EnvoyServices) UpsertEndpointSlices(endpointSlices ...discoveryv1.EndpointSlice) {
|
||||
|
@ -56,6 +57,11 @@ func (s *EnvoyServices) UpsertEndpointSlices(endpointSlices ...discoveryv1.Endpo
|
|||
s.GoTrue = new(GoTrueCluster)
|
||||
}
|
||||
s.GoTrue.AddOrUpdateEndpoints(eps)
|
||||
case supabase.ServiceConfig.Storage.Name:
|
||||
if s.StorageApi == nil {
|
||||
s.StorageApi = new(StorageApiCluster)
|
||||
}
|
||||
s.StorageApi.AddOrUpdateEndpoints(eps)
|
||||
case supabase.ServiceConfig.PGMeta.Name:
|
||||
if s.PGMeta == nil {
|
||||
s.PGMeta = new(PGMetaCluster)
|
||||
|
@ -76,6 +82,10 @@ func (s EnvoyServices) Targets() map[string][]string {
|
|||
targets[supabase.ServiceConfig.Auth.Name] = s.GoTrue.Targets()
|
||||
}
|
||||
|
||||
if s.StorageApi != nil {
|
||||
targets[supabase.ServiceConfig.Storage.Name] = s.StorageApi.Targets()
|
||||
}
|
||||
|
||||
if s.PGMeta != nil {
|
||||
targets[supabase.ServiceConfig.PGMeta.Name] = s.PGMeta.Targets()
|
||||
}
|
||||
|
@ -179,6 +189,7 @@ func (s *EnvoyServices) snapshot(ctx context.Context, instance, version string)
|
|||
Routes: slices.Concat(
|
||||
s.Postgrest.Routes(instance),
|
||||
s.GoTrue.Routes(instance),
|
||||
s.StorageApi.Routes(instance),
|
||||
s.PGMeta.Routes(instance),
|
||||
),
|
||||
}},
|
||||
|
@ -252,6 +263,7 @@ func (s *EnvoyServices) snapshot(ctx context.Context, instance, version string)
|
|||
slices.Concat(
|
||||
s.Postgrest.Cluster(instance),
|
||||
s.GoTrue.Cluster(instance),
|
||||
s.StorageApi.Cluster(instance),
|
||||
s.PGMeta.Cluster(instance),
|
||||
)...),
|
||||
resource.RouteType: {apiRouteCfg},
|
||||
|
|
72
internal/controlplane/storage.go
Normal file
72
internal/controlplane/storage.go
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
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 controlplane
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
clusterv3 "github.com/envoyproxy/go-control-plane/envoy/config/cluster/v3"
|
||||
routev3 "github.com/envoyproxy/go-control-plane/envoy/config/route/v3"
|
||||
"google.golang.org/protobuf/types/known/anypb"
|
||||
|
||||
"code.icb4dc0.de/prskr/supabase-operator/internal/supabase"
|
||||
)
|
||||
|
||||
type StorageApiCluster struct {
|
||||
ServiceCluster
|
||||
}
|
||||
|
||||
func (c *StorageApiCluster) Cluster(instance string) []*clusterv3.Cluster {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
serviceCfg := supabase.ServiceConfig.Storage
|
||||
|
||||
return []*clusterv3.Cluster{
|
||||
c.ServiceCluster.Cluster(fmt.Sprintf("%s@%s", serviceCfg.Name, instance), uint32(serviceCfg.Defaults.ApiPort)),
|
||||
}
|
||||
}
|
||||
|
||||
func (c *StorageApiCluster) Routes(instance string) []*routev3.Route {
|
||||
if c == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
serviceCfg := supabase.ServiceConfig.Storage
|
||||
|
||||
return []*routev3.Route{{
|
||||
Name: "Storage: /storage/v1/* -> http://storage:5000/*",
|
||||
Match: &routev3.RouteMatch{
|
||||
PathSpecifier: &routev3.RouteMatch_Prefix{
|
||||
Prefix: "/storage/v1/",
|
||||
},
|
||||
},
|
||||
Action: &routev3.Route_Route{
|
||||
Route: &routev3.RouteAction{
|
||||
ClusterSpecifier: &routev3.RouteAction_Cluster{
|
||||
Cluster: fmt.Sprintf("%s@%s", serviceCfg.Name, instance),
|
||||
},
|
||||
PrefixRewrite: "/",
|
||||
},
|
||||
},
|
||||
TypedPerFilterConfig: map[string]*anypb.Any{
|
||||
FilterNameRBAC: MustAny(RBACPerRoute(RBACAllowAllConfig())),
|
||||
FilterNameJwtAuthn: MustAny(JWTAllowAll()),
|
||||
},
|
||||
}}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue