Skip to content
Merged
Show file tree
Hide file tree
Changes from 31 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
b217b00
feat: migrate to multicluster controller runtime
kekcleader Oct 1, 2025
eea2c95
feat: added api export provider
kekcleader Oct 1, 2025
a342f9b
feat: update WorkspaceTypeSubroutine and WorkspaceSubroutine to suppo…
kekcleader Oct 2, 2025
3fbc5f5
feat: continuation of migration
kekcleader Oct 3, 2025
3021ff1
Merge branch 'main' into feat/migrate-to-multi-cluster-controller-run…
kekcleader Oct 3, 2025
b7b8d2a
chore: tidy modules to add missing controller-runtime entries
kekcleader Oct 3, 2025
03def8f
fix: go.mod update
kekcleader Oct 3, 2025
9e0b391
fix: fix lint errors
kekcleader Oct 3, 2025
17e04e3
feat: some new unit tests
kekcleader Oct 3, 2025
703b482
fix: prevent nil-pointer panics and harden workspace retrieval
kekcleader Oct 3, 2025
37484e0
feat: new unit tests for account_controller
kekcleader Oct 5, 2025
5f0f2f1
fix: fixed deprecated Requeue usage
kekcleader Oct 5, 2025
3098382
refactor: adopt commons multicluster lifecycle; update Finalizers(ins…
kekcleader Oct 6, 2025
3a0b0d8
fix: remove unused helper and cleaned imports
kekcleader Oct 6, 2025
4f09aa2
feat: increasing coverage in pkg/subroutines
kekcleader Oct 6, 2025
4074319
feat: Added targeted unit tests across workspace, workspace_type, fga…
kekcleader Oct 6, 2025
0624477
fix: Updated accountinfo_subroutine_test.go to remove the empty if-br…
kekcleader Oct 6, 2025
351d610
fix: Prevent nil FGA client from being built into subroutines
kekcleader Oct 7, 2025
fbd10bd
fix: Restore MustGetClusteredName’s contract
kekcleader Oct 7, 2025
6494fae
fix: ServiceAccount validation bug + logging misuse
kekcleader Oct 7, 2025
0f762fc
fix: use the subroutine’s scheme
kekcleader Oct 7, 2025
418a859
Merge branch 'main' into feat/migrate-to-multi-cluster-controller-run…
kekcleader Oct 7, 2025
73a474b
fix: regenerated mocks
kekcleader Oct 7, 2025
a32b436
fix: openfga alias in mock
kekcleader Oct 7, 2025
39bb747
fix: Refine the mockery v3 go:generate directive
kekcleader Oct 7, 2025
babf4d4
fix: remove limiter helper functions, wsCtx
kekcleader Oct 8, 2025
a68912c
fix: tests
kekcleader Oct 8, 2025
da819e5
fix: tests work
kekcleader Oct 8, 2025
25914c7
Merge branch 'main' into feat/migrate-to-multi-cluster-controller-run…
kekcleader Oct 8, 2025
e823bb0
fix: increased coverage
kekcleader Oct 8, 2025
a654ba7
fix: update download-kcp.sh
kekcleader Oct 8, 2025
0ae8e54
fix: update download-kcp.sh (2)
kekcleader Oct 8, 2025
cb0f51b
fix: removed the redundant scheme field from WorkspaceSubroutine, upd…
kekcleader Oct 9, 2025
3a17962
fix: removed the scheme property from the controller since it’s redun…
kekcleader Oct 9, 2025
ac4e249
fix: reworked WorkspaceTypeSubroutine to avoid mutex-based lazy init …
kekcleader Oct 9, 2025
9f326ca
chore: refactor mcruntime migration (#49)
aaronschweig Oct 13, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@ go.work
*~
.DS_Store
kcp.log
coverage.html
coverage.html
.gocache/
13 changes: 13 additions & 0 deletions Taskfile.yml
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,19 @@ tasks:
- "{{.LOCAL_BIN}}/controller-gen object:headerFile=hack/boilerplate.go.txt paths=./..."
- "{{.LOCAL_BIN}}/apigen --input-dir {{.CRD_DIRECTORY}} --output-dir ./config/resources"
- "{{.LOCAL_BIN}}/apigen --input-dir {{.CRD_DIRECTORY}} --output-dir {{ .TEST_SETUP_DIRECTORY }}"
generate-mocks:
desc: Regenerate mocks and enforce OpenFGA import alias
cmds:
- GOTOOLCHAIN=go1.24.5+auto go generate ./pkg/subroutines
- |
bash -lc '
FILE="pkg/subroutines/mocks/mock_OpenFGAServiceClient.go";
if [ -f "$FILE" ]; then
# Only add alias if missing (import without alias on a dedicated line)
if grep -qE "^[[:space:]]*\"github.com/openfga/api/proto/openfga/v1\"$" "$FILE"; then
perl -0777 -i -pe "s/(^\s*)\"github.com\\/openfga\\/api\\/proto\\/openfga\\/v1\"/\${1}openfgav1 \"github.com\\/openfga\\/api\\/proto\\/openfga\\/v1\"/m" "$FILE";
fi;
fi'
build:
cmds:
- go build ./...
Expand Down
84 changes: 58 additions & 26 deletions cmd/operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,12 @@ package cmd
import (
"context"
"crypto/tls"
"fmt"
"net/http"
"strings"

apisv1alpha1 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha1"
"github.com/kcp-dev/multicluster-provider/apiexport"
openfgav1 "github.com/openfga/api/proto/openfga/v1"
platformmeshcontext "github.com/platform-mesh/golang-commons/context"
"github.com/platform-mesh/golang-commons/traces"
Expand All @@ -35,9 +37,9 @@ import (
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/healthz"
"sigs.k8s.io/controller-runtime/pkg/kcp"
metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server"
"sigs.k8s.io/controller-runtime/pkg/webhook"
mcmanager "sigs.k8s.io/multicluster-runtime/pkg/manager"

"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"

Expand Down Expand Up @@ -102,7 +104,25 @@ func RunController(_ *cobra.Command, _ []string) { // coverage-ignore
restCfg.Wrap(func(rt http.RoundTripper) http.RoundTripper {
return otelhttp.NewTransport(rt)
})
opts := ctrl.Options{

// Resolve virtual workspace endpoint for the APIExport provider, if configured
providerCfg := rest.CopyConfig(restCfg)
virtualWorkspaceURL, err := resolveVirtualWorkspaceURL(ctx, restCfg, operatorCfg.Kcp.ApiExportEndpointSliceName)
if err != nil {
log.Fatal().Err(err).Msg("unable to resolve APIExport endpoint")
}
if virtualWorkspaceURL != "" {
log.Info().Str("apiExportEndpoint", virtualWorkspaceURL).Msg("using APIExport virtual workspace endpoint")
providerCfg.Host = virtualWorkspaceURL
}

provider, err := apiexport.New(providerCfg, apiexport.Options{Scheme: scheme})
if err != nil {
log.Fatal().Err(err).Msg("unable to construct APIExport provider")
}

// Create multicluster manager with APIExport provider
mcOpts := mcmanager.Options{
Scheme: scheme,
Metrics: metricsserver.Options{
BindAddress: defaultCfg.Metrics.BindAddress,
Expand All @@ -117,28 +137,7 @@ func RunController(_ *cobra.Command, _ []string) { // coverage-ignore
LeaderElectionConfig: restCfg,
LeaderElectionReleaseOnCancel: true,
}
var mgr ctrl.Manager
mgrConfig := rest.CopyConfig(restCfg)
if len(operatorCfg.Kcp.ApiExportEndpointSliceName) > 0 {
// Lookup API Endpointslice
kclient, err := client.New(restCfg, client.Options{
Scheme: scheme,
})
if err != nil {
log.Fatal().Err(err).Msg("unable to create client")
}
es := &apisv1alpha1.APIExportEndpointSlice{}
err = kclient.Get(ctx, client.ObjectKey{Name: operatorCfg.Kcp.ApiExportEndpointSliceName}, es)
if err != nil {
log.Fatal().Err(err).Msg("unable to create client")
}
if len(es.Status.APIExportEndpoints) == 0 {
log.Fatal().Msg("no APIExportEndpoints found")
}
log.Info().Str("host", es.Status.APIExportEndpoints[0].URL).Msg("using host")
mgrConfig.Host = es.Status.APIExportEndpoints[0].URL
}
mgr, err = kcp.NewClusterAwareManager(mgrConfig, opts)
mgr, err := mcmanager.New(providerCfg, provider, mcOpts)
if err != nil {
log.Fatal().Err(err).Msg("unable to start manager")
}
Expand All @@ -151,7 +150,6 @@ func RunController(_ *cobra.Command, _ []string) { // coverage-ignore
grpc.WithStatsHandler(otelgrpc.NewClientHandler()),
)
if err != nil {

log.Fatal().Err(err).Msg("error when creating the grpc client")
}
log.Debug().Msg("FGA client created")
Expand All @@ -173,7 +171,7 @@ func RunController(_ *cobra.Command, _ []string) { // coverage-ignore
}
}
log.Info().Strs("deniedNames", denyList).Msg("webhooks are enabled")
if err := v1alpha1.SetupAccountWebhookWithManager(mgr, denyList); err != nil {
if err := v1alpha1.SetupAccountWebhookWithManager(mgr.GetLocalManager(), denyList); err != nil {
log.Fatal().Err(err).Str("webhook", "Account").Msg("unable to create webhook")
}
}
Expand All @@ -185,8 +183,42 @@ func RunController(_ *cobra.Command, _ []string) { // coverage-ignore
log.Fatal().Err(err).Msg("unable to set up ready check")
}

// Start APIExport provider in background goroutine (critical for multicluster runtime)
log.Info().Msg("starting APIExport provider")
go func() {
if err := provider.Run(ctx, mgr); err != nil {
log.Fatal().Err(err).Msg("problem running APIExport provider")
}
}()

log.Info().Msg("starting manager")
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
log.Fatal().Err(err).Msg("problem running manager")
}
}

func resolveVirtualWorkspaceURL(ctx context.Context, cfg *rest.Config, endpointSliceName string) (string, error) {
if endpointSliceName == "" {
return "", nil
}

lookupCfg := rest.CopyConfig(cfg)
// Remove multicluster round tripper to avoid enforcing cluster headers on discovery requests
lookupCfg.WrapTransport = nil

cl, err := client.New(lookupCfg, client.Options{Scheme: scheme})
if err != nil {
return "", fmt.Errorf("failed to create client for APIExportEndpointSlice lookup: %w", err)
}

var slice apisv1alpha1.APIExportEndpointSlice
if err := cl.Get(ctx, client.ObjectKey{Name: endpointSliceName}, &slice); err != nil {
return "", fmt.Errorf("failed to fetch APIExportEndpointSlice %q: %w", endpointSliceName, err)
}

if len(slice.Status.APIExportEndpoints) == 0 {
return "", fmt.Errorf("APIExportEndpointSlice %q has no endpoints", endpointSliceName)
}

return slice.Status.APIExportEndpoints[0].URL, nil
}
54 changes: 27 additions & 27 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ module github.com/platform-mesh/account-operator

go 1.24.5

replace sigs.k8s.io/controller-runtime => github.com/kcp-dev/controller-runtime v0.19.0-kcp.1

replace (
k8s.io/api => k8s.io/api v0.34.1
k8s.io/apimachinery => k8s.io/apimachinery v0.34.1
Expand All @@ -13,9 +11,10 @@ replace (
require (
github.com/kcp-dev/kcp/sdk v0.28.1-0.20250926104223-cec2e15f24c6
github.com/kcp-dev/logicalcluster/v3 v3.0.5
github.com/kcp-dev/multicluster-provider v0.0.0-20250827085327-2b5ca378b7b4
github.com/openfga/api/proto v0.0.0-20250909173124-0ac19aac54f2
github.com/otiai10/copy v1.14.1
github.com/platform-mesh/golang-commons v0.0.21
github.com/platform-mesh/golang-commons v0.5.4
github.com/rs/zerolog v1.34.0
github.com/spf13/cobra v1.10.1
github.com/spf13/viper v1.21.0
Expand All @@ -29,14 +28,14 @@ require (
k8s.io/apiextensions-apiserver v0.34.1
k8s.io/apimachinery v0.34.1
k8s.io/client-go v0.34.1
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4
sigs.k8s.io/controller-runtime v0.22.2
sigs.k8s.io/controller-runtime v0.22.1
sigs.k8s.io/multicluster-runtime v0.21.0-alpha.9
)

require (
github.com/99designs/gqlgen v0.17.76 // indirect
github.com/99designs/gqlgen v0.17.81 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/cenkalti/backoff/v5 v5.0.2 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
Expand All @@ -45,8 +44,8 @@ require (
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.9.0 // indirect
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
github.com/getsentry/sentry-go v0.34.0 // indirect
github.com/go-jose/go-jose/v4 v4.1.2 // indirect
github.com/getsentry/sentry-go v0.35.3 // indirect
github.com/go-jose/go-jose/v4 v4.1.3 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-logr/zerologr v1.2.3 // indirect
Expand All @@ -55,10 +54,11 @@ require (
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/btree v1.1.3 // indirect
github.com/google/gnostic-models v0.7.0 // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
Expand All @@ -70,14 +70,14 @@ require (
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/onsi/gomega v1.35.1 // indirect
github.com/onsi/gomega v1.36.2 // indirect
github.com/otiai10/mint v1.6.3 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.22.0 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.62.0 // indirect
github.com/prometheus/client_golang v1.23.2 // indirect
github.com/prometheus/client_model v0.6.2 // indirect
github.com/prometheus/common v0.66.1 // indirect
github.com/prometheus/procfs v0.16.1 // indirect
github.com/sagikazarmark/locafero v0.11.0 // indirect
github.com/sosodev/duration v1.3.1 // indirect
Expand All @@ -91,31 +91,31 @@ require (
github.com/x448/float16 v0.8.4 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/otel v1.38.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.37.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.37.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 // indirect
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.38.0 // indirect
go.opentelemetry.io/otel/metric v1.38.0 // indirect
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
go.opentelemetry.io/otel/trace v1.38.0 // indirect
go.opentelemetry.io/proto/otlp v1.7.0 // indirect
go.opentelemetry.io/proto/otlp v1.8.0 // indirect
go.yaml.in/yaml/v2 v2.4.2 // indirect
go.yaml.in/yaml/v3 v3.0.4 // indirect
golang.org/x/crypto v0.41.0 // indirect
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b // indirect
golang.org/x/net v0.43.0 // indirect
golang.org/x/oauth2 v0.30.0 // indirect
golang.org/x/sync v0.16.0 // indirect
golang.org/x/term v0.34.0 // indirect
golang.org/x/text v0.28.0 // indirect
golang.org/x/exp v0.0.0-20251002181428-27f1f14c8bb9 // indirect
golang.org/x/net v0.44.0 // indirect
golang.org/x/oauth2 v0.31.0 // indirect
golang.org/x/sync v0.17.0 // indirect
golang.org/x/term v0.35.0 // indirect
golang.org/x/text v0.29.0 // indirect
golang.org/x/time v0.11.0 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250804133106-a7a43d27e69b // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect
google.golang.org/protobuf v1.36.8 // indirect
google.golang.org/protobuf v1.36.9 // indirect
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect
k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
sigs.k8s.io/randfill v1.0.0 // indirect
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
Expand Down
Loading
Loading