From cd6b3e54770a4579b13593af6fab3148efd5590c Mon Sep 17 00:00:00 2001 From: David Gogl <1381862+kengou@users.noreply.github.com> Date: Fri, 10 Jan 2025 00:00:09 +0100 Subject: [PATCH 001/108] feat(dex): create connector and client to sql along with the CRDs --- cmd/greenhouse/controllers.go | 5 ++ cmd/greenhouse/main.go | 9 +++ go.mod | 3 + pkg/controllers/organization/dex.go | 64 +++++++++++++++++++ .../organization/organization_controller.go | 23 +++++++ pkg/idproxy/storage.go | 26 ++++++++ 6 files changed, 130 insertions(+) diff --git a/cmd/greenhouse/controllers.go b/cmd/greenhouse/controllers.go index 9803b9ee7..1f4809969 100644 --- a/cmd/greenhouse/controllers.go +++ b/cmd/greenhouse/controllers.go @@ -78,6 +78,11 @@ func startOrganizationReconciler(name string, mgr ctrl.Manager) error { } return (&organizationcontrollers.OrganizationReconciler{ Namespace: namespace, + PGDB: pgDB, + PGHost: pgHost, + PGPort: pgPort, + PGUser: pgUser, + PGPasswd: pgPasswd, }).SetupWithManager(name, mgr) } diff --git a/cmd/greenhouse/main.go b/cmd/greenhouse/main.go index 4b0994e38..137f30efa 100644 --- a/cmd/greenhouse/main.go +++ b/cmd/greenhouse/main.go @@ -58,6 +58,9 @@ var ( remoteClusterBearerTokenValidity, renewRemoteClusterBearerTokenAfter time.Duration kubeClientOpts clientutil.RuntimeOptions + // DB connection parameters + pgDB, pgHost, pgUser, pgPasswd string + pgPort uint16 ) func init() { @@ -91,6 +94,12 @@ func main() { flag.StringVar(&common.DNSDomain, "dns-domain", "", "The DNS domain to use for the Greenhouse central cluster") + flag.StringVar(&pgDB, "database", os.Getenv("DB_NAME"), "Database name") + flag.StringVar(&pgHost, "dbHost", os.Getenv("DB_HOST"), "Database host") + flag.Uint16Var(&pgPort, "dbPort", 5432, "Database port") + flag.StringVar(&pgUser, "dbUser", os.Getenv("DB_USER"), "Database user") + flag.StringVar(&pgPasswd, "dbPassword", os.Getenv("DB_PASSWORD"), "Database password") + opts := zap.Options{ Development: true, TimeEncoder: zapcore.RFC3339TimeEncoder, diff --git a/go.mod b/go.mod index cc4e450de..402e1b180 100644 --- a/go.mod +++ b/go.mod @@ -55,6 +55,7 @@ require ( cloud.google.com/go/auth v0.9.2 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect dario.cat/mergo v1.0.1 // indirect + filippo.io/edwards25519 v1.1.0 // indirect github.com/Microsoft/hcsshim v0.12.6 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/containerd/errdefs v0.1.0 // indirect @@ -64,11 +65,13 @@ require ( github.com/creack/pty v1.1.23 // indirect github.com/distribution/reference v0.6.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/go-sql-driver/mysql v1.8.1 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect + github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/miekg/dns v1.1.58 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect diff --git a/pkg/controllers/organization/dex.go b/pkg/controllers/organization/dex.go index b73c7c13d..5ef1b75ff 100644 --- a/pkg/controllers/organization/dex.go +++ b/pkg/controllers/organization/dex.go @@ -12,6 +12,7 @@ import ( "strings" "github.com/dexidp/dex/connector/oidc" + "github.com/dexidp/dex/storage" "github.com/pkg/errors" "golang.org/x/text/cases" "golang.org/x/text/language" @@ -88,6 +89,33 @@ func (r *OrganizationReconciler) reconcileDexConnector(ctx context.Context, org log.FromContext(ctx).Info("updated dex connector", "namespace", dexConnector.Namespace, "name", dexConnector.GetName()) r.recorder.Eventf(org, corev1.EventTypeNormal, "UpdatedDexConnector", "Updated dex connector %s/%s", dexConnector.Namespace, dexConnector.GetName()) } + + // Create the connectors also in SQL storage + var oidcConnector storage.Connector + if oidcConnector, err = r.sqlDexStorage.GetConnector(org.Name); err != nil { + if err = r.sqlDexStorage.CreateConnector(ctx, storage.Connector{ + ID: org.Name, + Type: dexConnectorTypeGreenhouse, + Name: cases.Title(language.English).String(org.Name), + Config: configByte, + }); err != nil { + return err + } + log.FromContext(ctx).Info("created dex connector in SQL storage", "name", org.Name) + r.recorder.Eventf(org, corev1.EventTypeNormal, "CreatedDexConnectorSQL", "Created dex connector in SQL storage %s", org.Name) + } + if err = r.sqlDexStorage.UpdateConnector(oidcConnector.ID, func(c storage.Connector) (storage.Connector, error) { + c.ID = org.Name + c.Type = dexConnectorTypeGreenhouse + c.Name = cases.Title(language.English).String(org.Name) + c.Config = configByte + return c, nil + }); err != nil { + return err + } + log.FromContext(ctx).Info("updated dex connector in SQL storage", "name", org.Name) + r.recorder.Eventf(org, corev1.EventTypeNormal, "UpdatedDexConnectorSQL", "Updated dex connector in SQL storage %s", org.Name) + return nil } @@ -118,6 +146,42 @@ func (r *OrganizationReconciler) discoverOIDCRedirectURL(ctx context.Context, or } func (r *OrganizationReconciler) reconcileOAuth2Client(ctx context.Context, org *greenhousesapv1alpha1.Organization) error { + var oAuthClient storage.Client + var err error + if oAuthClient, err = r.sqlDexStorage.GetClient(org.Name); err != nil { + if err = r.sqlDexStorage.CreateClient(ctx, storage.Client{ + Public: true, + ID: org.Name, + Name: org.Name, + RedirectURIs: []string{ + "https://dashboard." + common.DNSDomain, + fmt.Sprintf("https://%s.dashboard.%s", org.Name, common.DNSDomain), + }, + }); err != nil { + return err + } + log.FromContext(ctx).Info("created oauth2client", "name", org.Name) + r.recorder.Eventf(org, corev1.EventTypeNormal, "CreatedOAuth2Client", "Created oauth2client %s", org.Name) + return nil + } + + if err = r.sqlDexStorage.UpdateClient(oAuthClient.Name, func(authClient storage.Client) (storage.Client, error) { + authClient.Public = true + authClient.ID = org.Name + authClient.Name = org.Name + for _, requiredRedirectURL := range []string{ + "https://dashboard." + common.DNSDomain, + fmt.Sprintf("https://%s.dashboard.%s", org.Name, common.DNSDomain), + } { + authClient.RedirectURIs = util.AppendStringToSliceIfNotContains(requiredRedirectURL, authClient.RedirectURIs) + } + return authClient, nil + }); err != nil { + return err + } + log.FromContext(ctx).Info("updated oauth2client", "name", org.Name) + r.recorder.Eventf(org, corev1.EventTypeNormal, "UpdatedOAuth2Client", "Updated oauth2client %s", org.Name) + var oAuth2Client = new(dexapi.OAuth2Client) oAuth2Client.ObjectMeta.Name = encodedOAuth2ClientName(org.Name) oAuth2Client.ObjectMeta.Namespace = r.Namespace diff --git a/pkg/controllers/organization/organization_controller.go b/pkg/controllers/organization/organization_controller.go index fa3f90734..7a0469dd9 100644 --- a/pkg/controllers/organization/organization_controller.go +++ b/pkg/controllers/organization/organization_controller.go @@ -5,7 +5,12 @@ package organization import ( "context" + "log/slog" + "os" + "github.com/cloudoperators/greenhouse/pkg/idproxy" + "github.com/dexidp/dex/storage" + "github.com/dexidp/dex/storage/sql" "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" @@ -45,6 +50,10 @@ type OrganizationReconciler struct { client.Client recorder record.EventRecorder Namespace string + // Database related configuration + PGDB, PGHost, PGUser, PGPasswd string + PGPort uint16 + sqlDexStorage storage.Storage } //+kubebuilder:rbac:groups=greenhouse.sap,resources=organizations,verbs=get;list;watch;create;update;patch;delete @@ -64,6 +73,20 @@ type OrganizationReconciler struct { func (r *OrganizationReconciler) SetupWithManager(name string, mgr ctrl.Manager) error { r.Client = mgr.GetClient() r.recorder = mgr.GetEventRecorderFor(name) + + var err error + logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) + r.sqlDexStorage, err = idproxy.NewPostgresStorage(sql.SSL{Mode: "disable"}, sql.NetworkDB{ + Host: r.PGHost, + Port: r.PGPort, + User: r.PGUser, + Password: r.PGPasswd, + Database: r.PGDB, + }, logger.With("component", "storage")) + if err != nil { + return err + } + return ctrl.NewControllerManagedBy(mgr). Named(name). For(&greenhousesapv1alpha1.Organization{}). diff --git a/pkg/idproxy/storage.go b/pkg/idproxy/storage.go index a14e366e1..7bb4c66f3 100644 --- a/pkg/idproxy/storage.go +++ b/pkg/idproxy/storage.go @@ -11,9 +11,35 @@ import ( "github.com/dexidp/dex/storage" "github.com/dexidp/dex/storage/kubernetes" "github.com/dexidp/dex/storage/kubernetes/k8sapi" + "github.com/dexidp/dex/storage/sql" "github.com/ghodss/yaml" ) +// NewPostgresStorage creates a new Postgres storage. +func NewPostgresStorage(sslConfig sql.SSL, dbConfig sql.NetworkDB, logger *slog.Logger) (storage.Storage, error) { + sqlStorage, err := postgresStorage(sslConfig, sql.NetworkDB{ + Host: dbConfig.Host, + Port: dbConfig.Port, + User: dbConfig.User, + Password: dbConfig.Password, + Database: dbConfig.Database, + }, logger.With("component", "storage")) + if err != nil { + return nil, err + } + return sqlStorage, nil +} + +// postgresStorage creates a new Postgres storage. +func postgresStorage(sslConfig sql.SSL, dbConfig sql.NetworkDB, logger *slog.Logger) (storage.Storage, error) { + var storageConfig = sql.Postgres{ + SSL: sslConfig, + NetworkDB: dbConfig, + } + return storageConfig.Open(logger) +} + +// NewKubernetesStorage creates a new Kubernetes storage. func NewKubernetesStorage(kubeconfig, kubecontext, namespace string, logger *slog.Logger) (storage.Storage, error) { var storageConfig = kubernetes.Config{InCluster: true} if kubeconfig != "" { From b2edebf65f3c56040b26b1c35591d5863447e240 Mon Sep 17 00:00:00 2001 From: David Gogl <1381862+kengou@users.noreply.github.com> Date: Fri, 10 Jan 2025 00:01:44 +0100 Subject: [PATCH 002/108] feat(dex): add sql backend to idproxy commented out --- cmd/idproxy/main.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/cmd/idproxy/main.go b/cmd/idproxy/main.go index bb22b9df0..2ad81bed4 100644 --- a/cmd/idproxy/main.go +++ b/cmd/idproxy/main.go @@ -39,6 +39,9 @@ func main() { var idTokenValidity time.Duration var listenAddr, metricsAddr string var allowedOrigins []string + // DB connection parameters + var pgDB, pgHost, pgUser, pgPasswd string + var pgPort uint16 logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) // set default logger to be used by log slog.SetDefault(logger) @@ -48,6 +51,11 @@ func main() { flag.StringVar(&kubeconfig, "kubeconfig", os.Getenv("KUBECONFIG"), "Use kubeconfig for authentication") flag.StringVar(&kubecontext, "kubecontext", os.Getenv("KUBECONTEXT"), "Use context from kubeconfig") flag.StringVar(&kubenamespace, "kubenamespace", os.Getenv("KUBENAMESPACE"), "Use namespace") + flag.StringVar(&pgDB, "database", os.Getenv("DB_NAME"), "Database name") + flag.StringVar(&pgHost, "dbHost", os.Getenv("DB_HOST"), "Database host") + flag.Uint16Var(&pgPort, "dbPort", 5432, "Database port") + flag.StringVar(&pgUser, "dbUser", os.Getenv("DB_USER"), "Database user") + flag.StringVar(&pgPasswd, "dbPassword", os.Getenv("DB_PASSWORD"), "Database password") flag.StringVar(&issuer, "issuer", "", "Issuer URL") flag.StringVar(&listenAddr, "listen-addr", ":8080", "oidc listen address") flag.StringVar(&metricsAddr, "metrics-addr", ":6543", "bind address for metrics") @@ -58,7 +66,19 @@ func main() { if issuer == "" { log.Fatal("No --issuer given") } + /* + sqlDexStorage, err := idproxy.NewPostgresStorage(sql.SSL{Mode: "disable"}, sql.NetworkDB{ + Host: pgHost, + Port: pgPort, + User: pgUser, + Password: pgPasswd, + Database: pgDB, + }, logger.With("component", "storage")) + if err != nil { + log.Fatalf("Failed to initialize postgres storage: %s", err) + } + */ dexStorage, err := idproxy.NewKubernetesStorage(kubeconfig, kubecontext, kubenamespace, logger.With("component", "storage")) if err != nil { log.Fatalf("Failed to initialize kubernetes storage: %s", err) @@ -78,6 +98,7 @@ func main() { SkipApprovalScreen: true, Logger: logger.With("component", "server"), Storage: dexStorage, + // Storage: sqlDexStorage, AllowedOrigins: allowedOrigins, IDTokensValidFor: idTokenValidity, RefreshTokenPolicy: refreshPolicy, From df36be56a88b670a28f49b02c6a5de40f32683ac Mon Sep 17 00:00:00 2001 From: David Gogl <1381862+kengou@users.noreply.github.com> Date: Fri, 10 Jan 2025 00:00:09 +0100 Subject: [PATCH 003/108] feat(dex): create connector and client to sql along with the CRDs --- cmd/greenhouse/controllers.go | 5 ++ cmd/greenhouse/main.go | 9 +++ go.mod | 3 + pkg/controllers/organization/dex.go | 64 +++++++++++++++++++ .../organization/organization_controller.go | 23 +++++++ pkg/idproxy/storage.go | 26 ++++++++ 6 files changed, 130 insertions(+) diff --git a/cmd/greenhouse/controllers.go b/cmd/greenhouse/controllers.go index 9803b9ee7..1f4809969 100644 --- a/cmd/greenhouse/controllers.go +++ b/cmd/greenhouse/controllers.go @@ -78,6 +78,11 @@ func startOrganizationReconciler(name string, mgr ctrl.Manager) error { } return (&organizationcontrollers.OrganizationReconciler{ Namespace: namespace, + PGDB: pgDB, + PGHost: pgHost, + PGPort: pgPort, + PGUser: pgUser, + PGPasswd: pgPasswd, }).SetupWithManager(name, mgr) } diff --git a/cmd/greenhouse/main.go b/cmd/greenhouse/main.go index 4b0994e38..137f30efa 100644 --- a/cmd/greenhouse/main.go +++ b/cmd/greenhouse/main.go @@ -58,6 +58,9 @@ var ( remoteClusterBearerTokenValidity, renewRemoteClusterBearerTokenAfter time.Duration kubeClientOpts clientutil.RuntimeOptions + // DB connection parameters + pgDB, pgHost, pgUser, pgPasswd string + pgPort uint16 ) func init() { @@ -91,6 +94,12 @@ func main() { flag.StringVar(&common.DNSDomain, "dns-domain", "", "The DNS domain to use for the Greenhouse central cluster") + flag.StringVar(&pgDB, "database", os.Getenv("DB_NAME"), "Database name") + flag.StringVar(&pgHost, "dbHost", os.Getenv("DB_HOST"), "Database host") + flag.Uint16Var(&pgPort, "dbPort", 5432, "Database port") + flag.StringVar(&pgUser, "dbUser", os.Getenv("DB_USER"), "Database user") + flag.StringVar(&pgPasswd, "dbPassword", os.Getenv("DB_PASSWORD"), "Database password") + opts := zap.Options{ Development: true, TimeEncoder: zapcore.RFC3339TimeEncoder, diff --git a/go.mod b/go.mod index b5165487e..ef2f603e7 100644 --- a/go.mod +++ b/go.mod @@ -55,6 +55,7 @@ require ( cloud.google.com/go/auth v0.9.2 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect dario.cat/mergo v1.0.1 // indirect + filippo.io/edwards25519 v1.1.0 // indirect github.com/Microsoft/hcsshim v0.12.6 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/containerd/errdefs v0.3.0 // indirect @@ -64,11 +65,13 @@ require ( github.com/creack/pty v1.1.23 // indirect github.com/distribution/reference v0.6.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/go-sql-driver/mysql v1.8.1 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect + github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/miekg/dns v1.1.58 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/otiai10/mint v1.6.3 // indirect diff --git a/pkg/controllers/organization/dex.go b/pkg/controllers/organization/dex.go index b73c7c13d..5ef1b75ff 100644 --- a/pkg/controllers/organization/dex.go +++ b/pkg/controllers/organization/dex.go @@ -12,6 +12,7 @@ import ( "strings" "github.com/dexidp/dex/connector/oidc" + "github.com/dexidp/dex/storage" "github.com/pkg/errors" "golang.org/x/text/cases" "golang.org/x/text/language" @@ -88,6 +89,33 @@ func (r *OrganizationReconciler) reconcileDexConnector(ctx context.Context, org log.FromContext(ctx).Info("updated dex connector", "namespace", dexConnector.Namespace, "name", dexConnector.GetName()) r.recorder.Eventf(org, corev1.EventTypeNormal, "UpdatedDexConnector", "Updated dex connector %s/%s", dexConnector.Namespace, dexConnector.GetName()) } + + // Create the connectors also in SQL storage + var oidcConnector storage.Connector + if oidcConnector, err = r.sqlDexStorage.GetConnector(org.Name); err != nil { + if err = r.sqlDexStorage.CreateConnector(ctx, storage.Connector{ + ID: org.Name, + Type: dexConnectorTypeGreenhouse, + Name: cases.Title(language.English).String(org.Name), + Config: configByte, + }); err != nil { + return err + } + log.FromContext(ctx).Info("created dex connector in SQL storage", "name", org.Name) + r.recorder.Eventf(org, corev1.EventTypeNormal, "CreatedDexConnectorSQL", "Created dex connector in SQL storage %s", org.Name) + } + if err = r.sqlDexStorage.UpdateConnector(oidcConnector.ID, func(c storage.Connector) (storage.Connector, error) { + c.ID = org.Name + c.Type = dexConnectorTypeGreenhouse + c.Name = cases.Title(language.English).String(org.Name) + c.Config = configByte + return c, nil + }); err != nil { + return err + } + log.FromContext(ctx).Info("updated dex connector in SQL storage", "name", org.Name) + r.recorder.Eventf(org, corev1.EventTypeNormal, "UpdatedDexConnectorSQL", "Updated dex connector in SQL storage %s", org.Name) + return nil } @@ -118,6 +146,42 @@ func (r *OrganizationReconciler) discoverOIDCRedirectURL(ctx context.Context, or } func (r *OrganizationReconciler) reconcileOAuth2Client(ctx context.Context, org *greenhousesapv1alpha1.Organization) error { + var oAuthClient storage.Client + var err error + if oAuthClient, err = r.sqlDexStorage.GetClient(org.Name); err != nil { + if err = r.sqlDexStorage.CreateClient(ctx, storage.Client{ + Public: true, + ID: org.Name, + Name: org.Name, + RedirectURIs: []string{ + "https://dashboard." + common.DNSDomain, + fmt.Sprintf("https://%s.dashboard.%s", org.Name, common.DNSDomain), + }, + }); err != nil { + return err + } + log.FromContext(ctx).Info("created oauth2client", "name", org.Name) + r.recorder.Eventf(org, corev1.EventTypeNormal, "CreatedOAuth2Client", "Created oauth2client %s", org.Name) + return nil + } + + if err = r.sqlDexStorage.UpdateClient(oAuthClient.Name, func(authClient storage.Client) (storage.Client, error) { + authClient.Public = true + authClient.ID = org.Name + authClient.Name = org.Name + for _, requiredRedirectURL := range []string{ + "https://dashboard." + common.DNSDomain, + fmt.Sprintf("https://%s.dashboard.%s", org.Name, common.DNSDomain), + } { + authClient.RedirectURIs = util.AppendStringToSliceIfNotContains(requiredRedirectURL, authClient.RedirectURIs) + } + return authClient, nil + }); err != nil { + return err + } + log.FromContext(ctx).Info("updated oauth2client", "name", org.Name) + r.recorder.Eventf(org, corev1.EventTypeNormal, "UpdatedOAuth2Client", "Updated oauth2client %s", org.Name) + var oAuth2Client = new(dexapi.OAuth2Client) oAuth2Client.ObjectMeta.Name = encodedOAuth2ClientName(org.Name) oAuth2Client.ObjectMeta.Namespace = r.Namespace diff --git a/pkg/controllers/organization/organization_controller.go b/pkg/controllers/organization/organization_controller.go index fa3f90734..7a0469dd9 100644 --- a/pkg/controllers/organization/organization_controller.go +++ b/pkg/controllers/organization/organization_controller.go @@ -5,7 +5,12 @@ package organization import ( "context" + "log/slog" + "os" + "github.com/cloudoperators/greenhouse/pkg/idproxy" + "github.com/dexidp/dex/storage" + "github.com/dexidp/dex/storage/sql" "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" @@ -45,6 +50,10 @@ type OrganizationReconciler struct { client.Client recorder record.EventRecorder Namespace string + // Database related configuration + PGDB, PGHost, PGUser, PGPasswd string + PGPort uint16 + sqlDexStorage storage.Storage } //+kubebuilder:rbac:groups=greenhouse.sap,resources=organizations,verbs=get;list;watch;create;update;patch;delete @@ -64,6 +73,20 @@ type OrganizationReconciler struct { func (r *OrganizationReconciler) SetupWithManager(name string, mgr ctrl.Manager) error { r.Client = mgr.GetClient() r.recorder = mgr.GetEventRecorderFor(name) + + var err error + logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) + r.sqlDexStorage, err = idproxy.NewPostgresStorage(sql.SSL{Mode: "disable"}, sql.NetworkDB{ + Host: r.PGHost, + Port: r.PGPort, + User: r.PGUser, + Password: r.PGPasswd, + Database: r.PGDB, + }, logger.With("component", "storage")) + if err != nil { + return err + } + return ctrl.NewControllerManagedBy(mgr). Named(name). For(&greenhousesapv1alpha1.Organization{}). diff --git a/pkg/idproxy/storage.go b/pkg/idproxy/storage.go index a14e366e1..7bb4c66f3 100644 --- a/pkg/idproxy/storage.go +++ b/pkg/idproxy/storage.go @@ -11,9 +11,35 @@ import ( "github.com/dexidp/dex/storage" "github.com/dexidp/dex/storage/kubernetes" "github.com/dexidp/dex/storage/kubernetes/k8sapi" + "github.com/dexidp/dex/storage/sql" "github.com/ghodss/yaml" ) +// NewPostgresStorage creates a new Postgres storage. +func NewPostgresStorage(sslConfig sql.SSL, dbConfig sql.NetworkDB, logger *slog.Logger) (storage.Storage, error) { + sqlStorage, err := postgresStorage(sslConfig, sql.NetworkDB{ + Host: dbConfig.Host, + Port: dbConfig.Port, + User: dbConfig.User, + Password: dbConfig.Password, + Database: dbConfig.Database, + }, logger.With("component", "storage")) + if err != nil { + return nil, err + } + return sqlStorage, nil +} + +// postgresStorage creates a new Postgres storage. +func postgresStorage(sslConfig sql.SSL, dbConfig sql.NetworkDB, logger *slog.Logger) (storage.Storage, error) { + var storageConfig = sql.Postgres{ + SSL: sslConfig, + NetworkDB: dbConfig, + } + return storageConfig.Open(logger) +} + +// NewKubernetesStorage creates a new Kubernetes storage. func NewKubernetesStorage(kubeconfig, kubecontext, namespace string, logger *slog.Logger) (storage.Storage, error) { var storageConfig = kubernetes.Config{InCluster: true} if kubeconfig != "" { From 274eaa1869fd0bc779d1102302294c95c99e034d Mon Sep 17 00:00:00 2001 From: David Gogl <1381862+kengou@users.noreply.github.com> Date: Fri, 10 Jan 2025 00:01:44 +0100 Subject: [PATCH 004/108] feat(dex): add sql backend to idproxy commented out --- cmd/idproxy/main.go | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/cmd/idproxy/main.go b/cmd/idproxy/main.go index bb22b9df0..2ad81bed4 100644 --- a/cmd/idproxy/main.go +++ b/cmd/idproxy/main.go @@ -39,6 +39,9 @@ func main() { var idTokenValidity time.Duration var listenAddr, metricsAddr string var allowedOrigins []string + // DB connection parameters + var pgDB, pgHost, pgUser, pgPasswd string + var pgPort uint16 logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) // set default logger to be used by log slog.SetDefault(logger) @@ -48,6 +51,11 @@ func main() { flag.StringVar(&kubeconfig, "kubeconfig", os.Getenv("KUBECONFIG"), "Use kubeconfig for authentication") flag.StringVar(&kubecontext, "kubecontext", os.Getenv("KUBECONTEXT"), "Use context from kubeconfig") flag.StringVar(&kubenamespace, "kubenamespace", os.Getenv("KUBENAMESPACE"), "Use namespace") + flag.StringVar(&pgDB, "database", os.Getenv("DB_NAME"), "Database name") + flag.StringVar(&pgHost, "dbHost", os.Getenv("DB_HOST"), "Database host") + flag.Uint16Var(&pgPort, "dbPort", 5432, "Database port") + flag.StringVar(&pgUser, "dbUser", os.Getenv("DB_USER"), "Database user") + flag.StringVar(&pgPasswd, "dbPassword", os.Getenv("DB_PASSWORD"), "Database password") flag.StringVar(&issuer, "issuer", "", "Issuer URL") flag.StringVar(&listenAddr, "listen-addr", ":8080", "oidc listen address") flag.StringVar(&metricsAddr, "metrics-addr", ":6543", "bind address for metrics") @@ -58,7 +66,19 @@ func main() { if issuer == "" { log.Fatal("No --issuer given") } + /* + sqlDexStorage, err := idproxy.NewPostgresStorage(sql.SSL{Mode: "disable"}, sql.NetworkDB{ + Host: pgHost, + Port: pgPort, + User: pgUser, + Password: pgPasswd, + Database: pgDB, + }, logger.With("component", "storage")) + if err != nil { + log.Fatalf("Failed to initialize postgres storage: %s", err) + } + */ dexStorage, err := idproxy.NewKubernetesStorage(kubeconfig, kubecontext, kubenamespace, logger.With("component", "storage")) if err != nil { log.Fatalf("Failed to initialize kubernetes storage: %s", err) @@ -78,6 +98,7 @@ func main() { SkipApprovalScreen: true, Logger: logger.With("component", "server"), Storage: dexStorage, + // Storage: sqlDexStorage, AllowedOrigins: allowedOrigins, IDTokensValidFor: idTokenValidity, RefreshTokenPolicy: refreshPolicy, From c76613c94b212aaf095439918fb3347be30c90b3 Mon Sep 17 00:00:00 2001 From: David Gogl <1381862+kengou@users.noreply.github.com> Date: Fri, 10 Jan 2025 00:22:26 +0100 Subject: [PATCH 005/108] fix(controller) go fmt, fix imports --- pkg/controllers/organization/organization_controller.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/controllers/organization/organization_controller.go b/pkg/controllers/organization/organization_controller.go index 7a0469dd9..5c6634e77 100644 --- a/pkg/controllers/organization/organization_controller.go +++ b/pkg/controllers/organization/organization_controller.go @@ -8,7 +8,6 @@ import ( "log/slog" "os" - "github.com/cloudoperators/greenhouse/pkg/idproxy" "github.com/dexidp/dex/storage" "github.com/dexidp/dex/storage/sql" "github.com/pkg/errors" @@ -26,6 +25,7 @@ import ( greenhousesapv1alpha1 "github.com/cloudoperators/greenhouse/pkg/apis/greenhouse/v1alpha1" "github.com/cloudoperators/greenhouse/pkg/clientutil" dexapi "github.com/cloudoperators/greenhouse/pkg/dex/api" + "github.com/cloudoperators/greenhouse/pkg/idproxy" "github.com/cloudoperators/greenhouse/pkg/lifecycle" "github.com/cloudoperators/greenhouse/pkg/scim" ) From 9500d0d6acf0be7fccfd0e8ffe9ff919f08a45c4 Mon Sep 17 00:00:00 2001 From: David Gogl <1381862+kengou@users.noreply.github.com> Date: Fri, 10 Jan 2025 09:01:20 +0100 Subject: [PATCH 006/108] fix(dex) failing tests --- pkg/controllers/organization/dex.go | 47 ++++++++++++++++--- .../organization/organization_controller.go | 19 -------- 2 files changed, 40 insertions(+), 26 deletions(-) diff --git a/pkg/controllers/organization/dex.go b/pkg/controllers/organization/dex.go index 5ef1b75ff..90ffb02d2 100644 --- a/pkg/controllers/organization/dex.go +++ b/pkg/controllers/organization/dex.go @@ -9,8 +9,14 @@ import ( "encoding/json" "fmt" "hash/fnv" + "log/slog" + "os" "strings" + "github.com/dexidp/dex/storage/sql" + + "github.com/cloudoperators/greenhouse/pkg/idproxy" + "github.com/dexidp/dex/connector/oidc" "github.com/dexidp/dex/storage" "github.com/pkg/errors" @@ -90,10 +96,24 @@ func (r *OrganizationReconciler) reconcileDexConnector(ctx context.Context, org r.recorder.Eventf(org, corev1.EventTypeNormal, "UpdatedDexConnector", "Updated dex connector %s/%s", dexConnector.Namespace, dexConnector.GetName()) } + logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) + sqlDexStorage, err := idproxy.NewPostgresStorage(sql.SSL{Mode: "disable"}, sql.NetworkDB{ + Host: r.PGHost, + Port: r.PGPort, + User: r.PGUser, + Password: r.PGPasswd, + Database: r.PGDB, + }, logger.With("component", "storage")) + if err != nil { + return err + } + + defer sqlDexStorage.Close() + // Create the connectors also in SQL storage var oidcConnector storage.Connector - if oidcConnector, err = r.sqlDexStorage.GetConnector(org.Name); err != nil { - if err = r.sqlDexStorage.CreateConnector(ctx, storage.Connector{ + if oidcConnector, err = sqlDexStorage.GetConnector(org.Name); err != nil { + if err = sqlDexStorage.CreateConnector(ctx, storage.Connector{ ID: org.Name, Type: dexConnectorTypeGreenhouse, Name: cases.Title(language.English).String(org.Name), @@ -104,7 +124,7 @@ func (r *OrganizationReconciler) reconcileDexConnector(ctx context.Context, org log.FromContext(ctx).Info("created dex connector in SQL storage", "name", org.Name) r.recorder.Eventf(org, corev1.EventTypeNormal, "CreatedDexConnectorSQL", "Created dex connector in SQL storage %s", org.Name) } - if err = r.sqlDexStorage.UpdateConnector(oidcConnector.ID, func(c storage.Connector) (storage.Connector, error) { + if err = sqlDexStorage.UpdateConnector(oidcConnector.ID, func(c storage.Connector) (storage.Connector, error) { c.ID = org.Name c.Type = dexConnectorTypeGreenhouse c.Name = cases.Title(language.English).String(org.Name) @@ -146,10 +166,23 @@ func (r *OrganizationReconciler) discoverOIDCRedirectURL(ctx context.Context, or } func (r *OrganizationReconciler) reconcileOAuth2Client(ctx context.Context, org *greenhousesapv1alpha1.Organization) error { + logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) + sqlDexStorage, err := idproxy.NewPostgresStorage(sql.SSL{Mode: "disable"}, sql.NetworkDB{ + Host: r.PGHost, + Port: r.PGPort, + User: r.PGUser, + Password: r.PGPasswd, + Database: r.PGDB, + }, logger.With("component", "storage")) + if err != nil { + return err + } + + defer sqlDexStorage.Close() + var oAuthClient storage.Client - var err error - if oAuthClient, err = r.sqlDexStorage.GetClient(org.Name); err != nil { - if err = r.sqlDexStorage.CreateClient(ctx, storage.Client{ + if oAuthClient, err = sqlDexStorage.GetClient(org.Name); err != nil { + if err = sqlDexStorage.CreateClient(ctx, storage.Client{ Public: true, ID: org.Name, Name: org.Name, @@ -165,7 +198,7 @@ func (r *OrganizationReconciler) reconcileOAuth2Client(ctx context.Context, org return nil } - if err = r.sqlDexStorage.UpdateClient(oAuthClient.Name, func(authClient storage.Client) (storage.Client, error) { + if err = sqlDexStorage.UpdateClient(oAuthClient.Name, func(authClient storage.Client) (storage.Client, error) { authClient.Public = true authClient.ID = org.Name authClient.Name = org.Name diff --git a/pkg/controllers/organization/organization_controller.go b/pkg/controllers/organization/organization_controller.go index 5c6634e77..41a039f95 100644 --- a/pkg/controllers/organization/organization_controller.go +++ b/pkg/controllers/organization/organization_controller.go @@ -5,11 +5,7 @@ package organization import ( "context" - "log/slog" - "os" - "github.com/dexidp/dex/storage" - "github.com/dexidp/dex/storage/sql" "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" @@ -25,7 +21,6 @@ import ( greenhousesapv1alpha1 "github.com/cloudoperators/greenhouse/pkg/apis/greenhouse/v1alpha1" "github.com/cloudoperators/greenhouse/pkg/clientutil" dexapi "github.com/cloudoperators/greenhouse/pkg/dex/api" - "github.com/cloudoperators/greenhouse/pkg/idproxy" "github.com/cloudoperators/greenhouse/pkg/lifecycle" "github.com/cloudoperators/greenhouse/pkg/scim" ) @@ -53,7 +48,6 @@ type OrganizationReconciler struct { // Database related configuration PGDB, PGHost, PGUser, PGPasswd string PGPort uint16 - sqlDexStorage storage.Storage } //+kubebuilder:rbac:groups=greenhouse.sap,resources=organizations,verbs=get;list;watch;create;update;patch;delete @@ -74,19 +68,6 @@ func (r *OrganizationReconciler) SetupWithManager(name string, mgr ctrl.Manager) r.Client = mgr.GetClient() r.recorder = mgr.GetEventRecorderFor(name) - var err error - logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) - r.sqlDexStorage, err = idproxy.NewPostgresStorage(sql.SSL{Mode: "disable"}, sql.NetworkDB{ - Host: r.PGHost, - Port: r.PGPort, - User: r.PGUser, - Password: r.PGPasswd, - Database: r.PGDB, - }, logger.With("component", "storage")) - if err != nil { - return err - } - return ctrl.NewControllerManagedBy(mgr). Named(name). For(&greenhousesapv1alpha1.Organization{}). From e9fe394acd1ec64c8f3b8886753121b20148bdb4 Mon Sep 17 00:00:00 2001 From: David Gogl <1381862+kengou@users.noreply.github.com> Date: Wed, 15 Jan 2025 20:31:07 +0100 Subject: [PATCH 007/108] feat(dex) shared networkstorage, rm events --- cmd/greenhouse/controllers.go | 7 +-- cmd/greenhouse/main.go | 15 +++--- pkg/controllers/organization/dex.go | 51 +++---------------- .../organization/organization_controller.go | 17 ++++++- 4 files changed, 32 insertions(+), 58 deletions(-) diff --git a/cmd/greenhouse/controllers.go b/cmd/greenhouse/controllers.go index 1f4809969..f887ae42a 100644 --- a/cmd/greenhouse/controllers.go +++ b/cmd/greenhouse/controllers.go @@ -76,13 +76,10 @@ func startOrganizationReconciler(name string, mgr ctrl.Manager) error { if v, ok := os.LookupEnv("POD_NAMESPACE"); ok { namespace = v } + return (&organizationcontrollers.OrganizationReconciler{ Namespace: namespace, - PGDB: pgDB, - PGHost: pgHost, - PGPort: pgPort, - PGUser: pgUser, - PGPasswd: pgPasswd, + NetworkDB: postgresDB, }).SetupWithManager(name, mgr) } diff --git a/cmd/greenhouse/main.go b/cmd/greenhouse/main.go index 137f30efa..1d70c08f4 100644 --- a/cmd/greenhouse/main.go +++ b/cmd/greenhouse/main.go @@ -12,6 +12,8 @@ import ( "strings" "time" + "github.com/dexidp/dex/storage/sql" + "github.com/sapcc/go-bits/osext" flag "github.com/spf13/pflag" "go.uber.org/zap/zapcore" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" @@ -59,8 +61,7 @@ var ( renewRemoteClusterBearerTokenAfter time.Duration kubeClientOpts clientutil.RuntimeOptions // DB connection parameters - pgDB, pgHost, pgUser, pgPasswd string - pgPort uint16 + postgresDB sql.NetworkDB ) func init() { @@ -94,11 +95,11 @@ func main() { flag.StringVar(&common.DNSDomain, "dns-domain", "", "The DNS domain to use for the Greenhouse central cluster") - flag.StringVar(&pgDB, "database", os.Getenv("DB_NAME"), "Database name") - flag.StringVar(&pgHost, "dbHost", os.Getenv("DB_HOST"), "Database host") - flag.Uint16Var(&pgPort, "dbPort", 5432, "Database port") - flag.StringVar(&pgUser, "dbUser", os.Getenv("DB_USER"), "Database user") - flag.StringVar(&pgPasswd, "dbPassword", os.Getenv("DB_PASSWORD"), "Database password") + flag.StringVar(&postgresDB.Database, "database", osext.GetenvOrDefault("DEX_POSTGRES_DATABASE", "dex"), "Database name") + flag.StringVar(&postgresDB.Host, "dbHost", osext.GetenvOrDefault("DEX_POSTGRES_HOST", "localhost"), "Database host") + flag.Uint16Var(&postgresDB.Port, "dbPort", 5432, "Database port") + flag.StringVar(&postgresDB.User, "dbUser", osext.GetenvOrDefault("DEX_POSTGRES_USER", "dex"), "Database user") + flag.StringVar(&postgresDB.Password, "dbPassword", osext.GetenvOrDefault("DEX_POSTGRES_PASSWORD", "dex"), "Database password") opts := zap.Options{ Development: true, diff --git a/pkg/controllers/organization/dex.go b/pkg/controllers/organization/dex.go index 90ffb02d2..e9db885a7 100644 --- a/pkg/controllers/organization/dex.go +++ b/pkg/controllers/organization/dex.go @@ -9,14 +9,8 @@ import ( "encoding/json" "fmt" "hash/fnv" - "log/slog" - "os" "strings" - "github.com/dexidp/dex/storage/sql" - - "github.com/cloudoperators/greenhouse/pkg/idproxy" - "github.com/dexidp/dex/connector/oidc" "github.com/dexidp/dex/storage" "github.com/pkg/errors" @@ -96,24 +90,10 @@ func (r *OrganizationReconciler) reconcileDexConnector(ctx context.Context, org r.recorder.Eventf(org, corev1.EventTypeNormal, "UpdatedDexConnector", "Updated dex connector %s/%s", dexConnector.Namespace, dexConnector.GetName()) } - logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) - sqlDexStorage, err := idproxy.NewPostgresStorage(sql.SSL{Mode: "disable"}, sql.NetworkDB{ - Host: r.PGHost, - Port: r.PGPort, - User: r.PGUser, - Password: r.PGPasswd, - Database: r.PGDB, - }, logger.With("component", "storage")) - if err != nil { - return err - } - - defer sqlDexStorage.Close() - // Create the connectors also in SQL storage var oidcConnector storage.Connector - if oidcConnector, err = sqlDexStorage.GetConnector(org.Name); err != nil { - if err = sqlDexStorage.CreateConnector(ctx, storage.Connector{ + if oidcConnector, err = r.dexStorage.GetConnector(org.Name); err != nil { + if err = r.dexStorage.CreateConnector(ctx, storage.Connector{ ID: org.Name, Type: dexConnectorTypeGreenhouse, Name: cases.Title(language.English).String(org.Name), @@ -122,9 +102,8 @@ func (r *OrganizationReconciler) reconcileDexConnector(ctx context.Context, org return err } log.FromContext(ctx).Info("created dex connector in SQL storage", "name", org.Name) - r.recorder.Eventf(org, corev1.EventTypeNormal, "CreatedDexConnectorSQL", "Created dex connector in SQL storage %s", org.Name) } - if err = sqlDexStorage.UpdateConnector(oidcConnector.ID, func(c storage.Connector) (storage.Connector, error) { + if err = r.dexStorage.UpdateConnector(oidcConnector.ID, func(c storage.Connector) (storage.Connector, error) { c.ID = org.Name c.Type = dexConnectorTypeGreenhouse c.Name = cases.Title(language.English).String(org.Name) @@ -134,7 +113,6 @@ func (r *OrganizationReconciler) reconcileDexConnector(ctx context.Context, org return err } log.FromContext(ctx).Info("updated dex connector in SQL storage", "name", org.Name) - r.recorder.Eventf(org, corev1.EventTypeNormal, "UpdatedDexConnectorSQL", "Updated dex connector in SQL storage %s", org.Name) return nil } @@ -166,23 +144,10 @@ func (r *OrganizationReconciler) discoverOIDCRedirectURL(ctx context.Context, or } func (r *OrganizationReconciler) reconcileOAuth2Client(ctx context.Context, org *greenhousesapv1alpha1.Organization) error { - logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) - sqlDexStorage, err := idproxy.NewPostgresStorage(sql.SSL{Mode: "disable"}, sql.NetworkDB{ - Host: r.PGHost, - Port: r.PGPort, - User: r.PGUser, - Password: r.PGPasswd, - Database: r.PGDB, - }, logger.With("component", "storage")) - if err != nil { - return err - } - - defer sqlDexStorage.Close() - var oAuthClient storage.Client - if oAuthClient, err = sqlDexStorage.GetClient(org.Name); err != nil { - if err = sqlDexStorage.CreateClient(ctx, storage.Client{ + var err error + if oAuthClient, err = r.dexStorage.GetClient(org.Name); err != nil { + if err = r.dexStorage.CreateClient(ctx, storage.Client{ Public: true, ID: org.Name, Name: org.Name, @@ -194,11 +159,10 @@ func (r *OrganizationReconciler) reconcileOAuth2Client(ctx context.Context, org return err } log.FromContext(ctx).Info("created oauth2client", "name", org.Name) - r.recorder.Eventf(org, corev1.EventTypeNormal, "CreatedOAuth2Client", "Created oauth2client %s", org.Name) return nil } - if err = sqlDexStorage.UpdateClient(oAuthClient.Name, func(authClient storage.Client) (storage.Client, error) { + if err = r.dexStorage.UpdateClient(oAuthClient.Name, func(authClient storage.Client) (storage.Client, error) { authClient.Public = true authClient.ID = org.Name authClient.Name = org.Name @@ -213,7 +177,6 @@ func (r *OrganizationReconciler) reconcileOAuth2Client(ctx context.Context, org return err } log.FromContext(ctx).Info("updated oauth2client", "name", org.Name) - r.recorder.Eventf(org, corev1.EventTypeNormal, "UpdatedOAuth2Client", "Updated oauth2client %s", org.Name) var oAuth2Client = new(dexapi.OAuth2Client) oAuth2Client.ObjectMeta.Name = encodedOAuth2ClientName(org.Name) diff --git a/pkg/controllers/organization/organization_controller.go b/pkg/controllers/organization/organization_controller.go index 41a039f95..6e5bc63a4 100644 --- a/pkg/controllers/organization/organization_controller.go +++ b/pkg/controllers/organization/organization_controller.go @@ -5,7 +5,11 @@ package organization import ( "context" + "log/slog" + "os" + "github.com/dexidp/dex/storage" + "github.com/dexidp/dex/storage/sql" "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" @@ -21,6 +25,7 @@ import ( greenhousesapv1alpha1 "github.com/cloudoperators/greenhouse/pkg/apis/greenhouse/v1alpha1" "github.com/cloudoperators/greenhouse/pkg/clientutil" dexapi "github.com/cloudoperators/greenhouse/pkg/dex/api" + "github.com/cloudoperators/greenhouse/pkg/idproxy" "github.com/cloudoperators/greenhouse/pkg/lifecycle" "github.com/cloudoperators/greenhouse/pkg/scim" ) @@ -46,8 +51,8 @@ type OrganizationReconciler struct { recorder record.EventRecorder Namespace string // Database related configuration - PGDB, PGHost, PGUser, PGPasswd string - PGPort uint16 + NetworkDB sql.NetworkDB + dexStorage storage.Storage } //+kubebuilder:rbac:groups=greenhouse.sap,resources=organizations,verbs=get;list;watch;create;update;patch;delete @@ -68,6 +73,14 @@ func (r *OrganizationReconciler) SetupWithManager(name string, mgr ctrl.Manager) r.Client = mgr.GetClient() r.recorder = mgr.GetEventRecorderFor(name) + var err error + logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) + if r.dexStorage, err = idproxy.NewPostgresStorage(sql.SSL{Mode: "disable"}, r.NetworkDB, logger.With("component", "storage")); err != nil { + return errors.Wrap(err, "failed to initialize postgres storage") + } + + defer r.dexStorage.Close() + return ctrl.NewControllerManagedBy(mgr). Named(name). For(&greenhousesapv1alpha1.Organization{}). From 47e1cca284a8cebc22839acb4f1ca75c00a02385 Mon Sep 17 00:00:00 2001 From: David Gogl <1381862+kengou@users.noreply.github.com> Date: Thu, 16 Jan 2025 10:18:57 +0100 Subject: [PATCH 008/108] fix(dex) use clienutil instead of osext --- cmd/greenhouse/main.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/cmd/greenhouse/main.go b/cmd/greenhouse/main.go index 1d70c08f4..ebfe322ae 100644 --- a/cmd/greenhouse/main.go +++ b/cmd/greenhouse/main.go @@ -13,7 +13,6 @@ import ( "time" "github.com/dexidp/dex/storage/sql" - "github.com/sapcc/go-bits/osext" flag "github.com/spf13/pflag" "go.uber.org/zap/zapcore" apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1" @@ -95,11 +94,11 @@ func main() { flag.StringVar(&common.DNSDomain, "dns-domain", "", "The DNS domain to use for the Greenhouse central cluster") - flag.StringVar(&postgresDB.Database, "database", osext.GetenvOrDefault("DEX_POSTGRES_DATABASE", "dex"), "Database name") - flag.StringVar(&postgresDB.Host, "dbHost", osext.GetenvOrDefault("DEX_POSTGRES_HOST", "localhost"), "Database host") + flag.StringVar(&postgresDB.Database, "database", clientutil.GetEnvOrDefault("DEX_POSTGRES_DATABASE", "dex"), "Database name") + flag.StringVar(&postgresDB.Host, "dbHost", clientutil.GetEnvOrDefault("DEX_POSTGRES_HOST", "localhost"), "Database host") flag.Uint16Var(&postgresDB.Port, "dbPort", 5432, "Database port") - flag.StringVar(&postgresDB.User, "dbUser", osext.GetenvOrDefault("DEX_POSTGRES_USER", "dex"), "Database user") - flag.StringVar(&postgresDB.Password, "dbPassword", osext.GetenvOrDefault("DEX_POSTGRES_PASSWORD", "dex"), "Database password") + flag.StringVar(&postgresDB.User, "dbUser", clientutil.GetEnvOrDefault("DEX_POSTGRES_USER", "dex"), "Database user") + flag.StringVar(&postgresDB.Password, "dbPassword", clientutil.GetEnvOrDefault("DEX_POSTGRES_PASSWORD", "dex"), "Database password") opts := zap.Options{ Development: true, From 2c0feb7e5e7b73158b80be2750db293d340119e7 Mon Sep 17 00:00:00 2001 From: David Gogl <1381862+kengou@users.noreply.github.com> Date: Thu, 16 Jan 2025 13:09:44 +0100 Subject: [PATCH 009/108] feat(idproxy): use struct in idproxy as input --- cmd/idproxy/main.go | 23 +++++++++-------------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/cmd/idproxy/main.go b/cmd/idproxy/main.go index 2ad81bed4..624f7807b 100644 --- a/cmd/idproxy/main.go +++ b/cmd/idproxy/main.go @@ -16,6 +16,7 @@ import ( "time" "github.com/dexidp/dex/server" + "github.com/dexidp/dex/storage/sql" "github.com/go-logr/logr" "github.com/oklog/run" "github.com/prometheus/client_golang/prometheus" @@ -40,8 +41,8 @@ func main() { var listenAddr, metricsAddr string var allowedOrigins []string // DB connection parameters - var pgDB, pgHost, pgUser, pgPasswd string - var pgPort uint16 + var postgresDB sql.NetworkDB + logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) // set default logger to be used by log slog.SetDefault(logger) @@ -51,11 +52,11 @@ func main() { flag.StringVar(&kubeconfig, "kubeconfig", os.Getenv("KUBECONFIG"), "Use kubeconfig for authentication") flag.StringVar(&kubecontext, "kubecontext", os.Getenv("KUBECONTEXT"), "Use context from kubeconfig") flag.StringVar(&kubenamespace, "kubenamespace", os.Getenv("KUBENAMESPACE"), "Use namespace") - flag.StringVar(&pgDB, "database", os.Getenv("DB_NAME"), "Database name") - flag.StringVar(&pgHost, "dbHost", os.Getenv("DB_HOST"), "Database host") - flag.Uint16Var(&pgPort, "dbPort", 5432, "Database port") - flag.StringVar(&pgUser, "dbUser", os.Getenv("DB_USER"), "Database user") - flag.StringVar(&pgPasswd, "dbPassword", os.Getenv("DB_PASSWORD"), "Database password") + flag.StringVar(&postgresDB.Database, "database", os.Getenv("DB_NAME"), "Database name") + flag.StringVar(&postgresDB.Host, "dbHost", os.Getenv("DB_HOST"), "Database host") + flag.Uint16Var(&postgresDB.Port, "dbPort", 5432, "Database port") + flag.StringVar(&postgresDB.User, "dbUser", os.Getenv("DB_USER"), "Database user") + flag.StringVar(&postgresDB.Password, "dbPassword", os.Getenv("DB_PASSWORD"), "Database password") flag.StringVar(&issuer, "issuer", "", "Issuer URL") flag.StringVar(&listenAddr, "listen-addr", ":8080", "oidc listen address") flag.StringVar(&metricsAddr, "metrics-addr", ":6543", "bind address for metrics") @@ -67,13 +68,7 @@ func main() { log.Fatal("No --issuer given") } /* - sqlDexStorage, err := idproxy.NewPostgresStorage(sql.SSL{Mode: "disable"}, sql.NetworkDB{ - Host: pgHost, - Port: pgPort, - User: pgUser, - Password: pgPasswd, - Database: pgDB, - }, logger.With("component", "storage")) + sqlDexStorage, err := idproxy.NewPostgresStorage(sql.SSL{Mode: "disable"}, postgresDB, logger.With("component", "storage")) if err != nil { log.Fatalf("Failed to initialize postgres storage: %s", err) } From 1605920751a56bcb1faba46e8f82eeffbcd14e5f Mon Sep 17 00:00:00 2001 From: David Gogl <1381862+kengou@users.noreply.github.com> Date: Thu, 16 Jan 2025 14:20:54 +0100 Subject: [PATCH 010/108] feat(dex): add mock for dex Storage --- .mockery.yaml | 5 +- pkg/mocks/mock_Storage.go | 909 ++++++++++++++++++++++++++++++++++++++ pkg/test/env.go | 15 + 3 files changed, 928 insertions(+), 1 deletion(-) create mode 100644 pkg/mocks/mock_Storage.go diff --git a/.mockery.yaml b/.mockery.yaml index 2d6924e93..d1d2d7cc6 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -13,4 +13,7 @@ packages: sigs.k8s.io/controller-runtime/pkg/client: interfaces: Client: - SubResourceWriter: \ No newline at end of file + SubResourceWriter: + github.com/dexidp/dex/storage: + interfaces: + Storage: \ No newline at end of file diff --git a/pkg/mocks/mock_Storage.go b/pkg/mocks/mock_Storage.go new file mode 100644 index 000000000..d2f31847c --- /dev/null +++ b/pkg/mocks/mock_Storage.go @@ -0,0 +1,909 @@ +// Code generated by mockery v2.51.0. DO NOT EDIT. + +package mocks + +import ( + context "context" + + storage "github.com/dexidp/dex/storage" + mock "github.com/stretchr/testify/mock" + + time "time" +) + +// MockStorage is an autogenerated mock type for the Storage type +type MockStorage struct { + mock.Mock +} + +// Close provides a mock function with no fields +func (_m *MockStorage) Close() error { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Close") + } + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// CreateAuthCode provides a mock function with given fields: ctx, c +func (_m *MockStorage) CreateAuthCode(ctx context.Context, c storage.AuthCode) error { + ret := _m.Called(ctx, c) + + if len(ret) == 0 { + panic("no return value specified for CreateAuthCode") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, storage.AuthCode) error); ok { + r0 = rf(ctx, c) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// CreateAuthRequest provides a mock function with given fields: ctx, a +func (_m *MockStorage) CreateAuthRequest(ctx context.Context, a storage.AuthRequest) error { + ret := _m.Called(ctx, a) + + if len(ret) == 0 { + panic("no return value specified for CreateAuthRequest") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, storage.AuthRequest) error); ok { + r0 = rf(ctx, a) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// CreateClient provides a mock function with given fields: ctx, c +func (_m *MockStorage) CreateClient(ctx context.Context, c storage.Client) error { + ret := _m.Called(ctx, c) + + if len(ret) == 0 { + panic("no return value specified for CreateClient") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, storage.Client) error); ok { + r0 = rf(ctx, c) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// CreateConnector provides a mock function with given fields: ctx, c +func (_m *MockStorage) CreateConnector(ctx context.Context, c storage.Connector) error { + ret := _m.Called(ctx, c) + + if len(ret) == 0 { + panic("no return value specified for CreateConnector") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, storage.Connector) error); ok { + r0 = rf(ctx, c) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// CreateDeviceRequest provides a mock function with given fields: ctx, d +func (_m *MockStorage) CreateDeviceRequest(ctx context.Context, d storage.DeviceRequest) error { + ret := _m.Called(ctx, d) + + if len(ret) == 0 { + panic("no return value specified for CreateDeviceRequest") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, storage.DeviceRequest) error); ok { + r0 = rf(ctx, d) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// CreateDeviceToken provides a mock function with given fields: ctx, d +func (_m *MockStorage) CreateDeviceToken(ctx context.Context, d storage.DeviceToken) error { + ret := _m.Called(ctx, d) + + if len(ret) == 0 { + panic("no return value specified for CreateDeviceToken") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, storage.DeviceToken) error); ok { + r0 = rf(ctx, d) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// CreateOfflineSessions provides a mock function with given fields: ctx, s +func (_m *MockStorage) CreateOfflineSessions(ctx context.Context, s storage.OfflineSessions) error { + ret := _m.Called(ctx, s) + + if len(ret) == 0 { + panic("no return value specified for CreateOfflineSessions") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, storage.OfflineSessions) error); ok { + r0 = rf(ctx, s) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// CreatePassword provides a mock function with given fields: ctx, p +func (_m *MockStorage) CreatePassword(ctx context.Context, p storage.Password) error { + ret := _m.Called(ctx, p) + + if len(ret) == 0 { + panic("no return value specified for CreatePassword") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, storage.Password) error); ok { + r0 = rf(ctx, p) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// CreateRefresh provides a mock function with given fields: ctx, r +func (_m *MockStorage) CreateRefresh(ctx context.Context, r storage.RefreshToken) error { + ret := _m.Called(ctx, r) + + if len(ret) == 0 { + panic("no return value specified for CreateRefresh") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, storage.RefreshToken) error); ok { + r0 = rf(ctx, r) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DeleteAuthCode provides a mock function with given fields: code +func (_m *MockStorage) DeleteAuthCode(code string) error { + ret := _m.Called(code) + + if len(ret) == 0 { + panic("no return value specified for DeleteAuthCode") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string) error); ok { + r0 = rf(code) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DeleteAuthRequest provides a mock function with given fields: id +func (_m *MockStorage) DeleteAuthRequest(id string) error { + ret := _m.Called(id) + + if len(ret) == 0 { + panic("no return value specified for DeleteAuthRequest") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string) error); ok { + r0 = rf(id) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DeleteClient provides a mock function with given fields: id +func (_m *MockStorage) DeleteClient(id string) error { + ret := _m.Called(id) + + if len(ret) == 0 { + panic("no return value specified for DeleteClient") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string) error); ok { + r0 = rf(id) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DeleteConnector provides a mock function with given fields: id +func (_m *MockStorage) DeleteConnector(id string) error { + ret := _m.Called(id) + + if len(ret) == 0 { + panic("no return value specified for DeleteConnector") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string) error); ok { + r0 = rf(id) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DeleteOfflineSessions provides a mock function with given fields: userID, connID +func (_m *MockStorage) DeleteOfflineSessions(userID string, connID string) error { + ret := _m.Called(userID, connID) + + if len(ret) == 0 { + panic("no return value specified for DeleteOfflineSessions") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string, string) error); ok { + r0 = rf(userID, connID) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DeletePassword provides a mock function with given fields: email +func (_m *MockStorage) DeletePassword(email string) error { + ret := _m.Called(email) + + if len(ret) == 0 { + panic("no return value specified for DeletePassword") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string) error); ok { + r0 = rf(email) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// DeleteRefresh provides a mock function with given fields: id +func (_m *MockStorage) DeleteRefresh(id string) error { + ret := _m.Called(id) + + if len(ret) == 0 { + panic("no return value specified for DeleteRefresh") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string) error); ok { + r0 = rf(id) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// GarbageCollect provides a mock function with given fields: now +func (_m *MockStorage) GarbageCollect(now time.Time) (storage.GCResult, error) { + ret := _m.Called(now) + + if len(ret) == 0 { + panic("no return value specified for GarbageCollect") + } + + var r0 storage.GCResult + var r1 error + if rf, ok := ret.Get(0).(func(time.Time) (storage.GCResult, error)); ok { + return rf(now) + } + if rf, ok := ret.Get(0).(func(time.Time) storage.GCResult); ok { + r0 = rf(now) + } else { + r0 = ret.Get(0).(storage.GCResult) + } + + if rf, ok := ret.Get(1).(func(time.Time) error); ok { + r1 = rf(now) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetAuthCode provides a mock function with given fields: id +func (_m *MockStorage) GetAuthCode(id string) (storage.AuthCode, error) { + ret := _m.Called(id) + + if len(ret) == 0 { + panic("no return value specified for GetAuthCode") + } + + var r0 storage.AuthCode + var r1 error + if rf, ok := ret.Get(0).(func(string) (storage.AuthCode, error)); ok { + return rf(id) + } + if rf, ok := ret.Get(0).(func(string) storage.AuthCode); ok { + r0 = rf(id) + } else { + r0 = ret.Get(0).(storage.AuthCode) + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetAuthRequest provides a mock function with given fields: id +func (_m *MockStorage) GetAuthRequest(id string) (storage.AuthRequest, error) { + ret := _m.Called(id) + + if len(ret) == 0 { + panic("no return value specified for GetAuthRequest") + } + + var r0 storage.AuthRequest + var r1 error + if rf, ok := ret.Get(0).(func(string) (storage.AuthRequest, error)); ok { + return rf(id) + } + if rf, ok := ret.Get(0).(func(string) storage.AuthRequest); ok { + r0 = rf(id) + } else { + r0 = ret.Get(0).(storage.AuthRequest) + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetClient provides a mock function with given fields: id +func (_m *MockStorage) GetClient(id string) (storage.Client, error) { + ret := _m.Called(id) + + if len(ret) == 0 { + panic("no return value specified for GetClient") + } + + var r0 storage.Client + var r1 error + if rf, ok := ret.Get(0).(func(string) (storage.Client, error)); ok { + return rf(id) + } + if rf, ok := ret.Get(0).(func(string) storage.Client); ok { + r0 = rf(id) + } else { + r0 = ret.Get(0).(storage.Client) + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetConnector provides a mock function with given fields: id +func (_m *MockStorage) GetConnector(id string) (storage.Connector, error) { + ret := _m.Called(id) + + if len(ret) == 0 { + panic("no return value specified for GetConnector") + } + + var r0 storage.Connector + var r1 error + if rf, ok := ret.Get(0).(func(string) (storage.Connector, error)); ok { + return rf(id) + } + if rf, ok := ret.Get(0).(func(string) storage.Connector); ok { + r0 = rf(id) + } else { + r0 = ret.Get(0).(storage.Connector) + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetDeviceRequest provides a mock function with given fields: userCode +func (_m *MockStorage) GetDeviceRequest(userCode string) (storage.DeviceRequest, error) { + ret := _m.Called(userCode) + + if len(ret) == 0 { + panic("no return value specified for GetDeviceRequest") + } + + var r0 storage.DeviceRequest + var r1 error + if rf, ok := ret.Get(0).(func(string) (storage.DeviceRequest, error)); ok { + return rf(userCode) + } + if rf, ok := ret.Get(0).(func(string) storage.DeviceRequest); ok { + r0 = rf(userCode) + } else { + r0 = ret.Get(0).(storage.DeviceRequest) + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(userCode) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetDeviceToken provides a mock function with given fields: deviceCode +func (_m *MockStorage) GetDeviceToken(deviceCode string) (storage.DeviceToken, error) { + ret := _m.Called(deviceCode) + + if len(ret) == 0 { + panic("no return value specified for GetDeviceToken") + } + + var r0 storage.DeviceToken + var r1 error + if rf, ok := ret.Get(0).(func(string) (storage.DeviceToken, error)); ok { + return rf(deviceCode) + } + if rf, ok := ret.Get(0).(func(string) storage.DeviceToken); ok { + r0 = rf(deviceCode) + } else { + r0 = ret.Get(0).(storage.DeviceToken) + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(deviceCode) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetKeys provides a mock function with no fields +func (_m *MockStorage) GetKeys() (storage.Keys, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetKeys") + } + + var r0 storage.Keys + var r1 error + if rf, ok := ret.Get(0).(func() (storage.Keys, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() storage.Keys); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(storage.Keys) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetOfflineSessions provides a mock function with given fields: userID, connID +func (_m *MockStorage) GetOfflineSessions(userID string, connID string) (storage.OfflineSessions, error) { + ret := _m.Called(userID, connID) + + if len(ret) == 0 { + panic("no return value specified for GetOfflineSessions") + } + + var r0 storage.OfflineSessions + var r1 error + if rf, ok := ret.Get(0).(func(string, string) (storage.OfflineSessions, error)); ok { + return rf(userID, connID) + } + if rf, ok := ret.Get(0).(func(string, string) storage.OfflineSessions); ok { + r0 = rf(userID, connID) + } else { + r0 = ret.Get(0).(storage.OfflineSessions) + } + + if rf, ok := ret.Get(1).(func(string, string) error); ok { + r1 = rf(userID, connID) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetPassword provides a mock function with given fields: email +func (_m *MockStorage) GetPassword(email string) (storage.Password, error) { + ret := _m.Called(email) + + if len(ret) == 0 { + panic("no return value specified for GetPassword") + } + + var r0 storage.Password + var r1 error + if rf, ok := ret.Get(0).(func(string) (storage.Password, error)); ok { + return rf(email) + } + if rf, ok := ret.Get(0).(func(string) storage.Password); ok { + r0 = rf(email) + } else { + r0 = ret.Get(0).(storage.Password) + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(email) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetRefresh provides a mock function with given fields: id +func (_m *MockStorage) GetRefresh(id string) (storage.RefreshToken, error) { + ret := _m.Called(id) + + if len(ret) == 0 { + panic("no return value specified for GetRefresh") + } + + var r0 storage.RefreshToken + var r1 error + if rf, ok := ret.Get(0).(func(string) (storage.RefreshToken, error)); ok { + return rf(id) + } + if rf, ok := ret.Get(0).(func(string) storage.RefreshToken); ok { + r0 = rf(id) + } else { + r0 = ret.Get(0).(storage.RefreshToken) + } + + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(id) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ListClients provides a mock function with no fields +func (_m *MockStorage) ListClients() ([]storage.Client, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for ListClients") + } + + var r0 []storage.Client + var r1 error + if rf, ok := ret.Get(0).(func() ([]storage.Client, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() []storage.Client); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]storage.Client) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ListConnectors provides a mock function with no fields +func (_m *MockStorage) ListConnectors() ([]storage.Connector, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for ListConnectors") + } + + var r0 []storage.Connector + var r1 error + if rf, ok := ret.Get(0).(func() ([]storage.Connector, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() []storage.Connector); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]storage.Connector) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ListPasswords provides a mock function with no fields +func (_m *MockStorage) ListPasswords() ([]storage.Password, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for ListPasswords") + } + + var r0 []storage.Password + var r1 error + if rf, ok := ret.Get(0).(func() ([]storage.Password, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() []storage.Password); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]storage.Password) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ListRefreshTokens provides a mock function with no fields +func (_m *MockStorage) ListRefreshTokens() ([]storage.RefreshToken, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for ListRefreshTokens") + } + + var r0 []storage.RefreshToken + var r1 error + if rf, ok := ret.Get(0).(func() ([]storage.RefreshToken, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() []storage.RefreshToken); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]storage.RefreshToken) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// UpdateAuthRequest provides a mock function with given fields: id, updater +func (_m *MockStorage) UpdateAuthRequest(id string, updater func(storage.AuthRequest) (storage.AuthRequest, error)) error { + ret := _m.Called(id, updater) + + if len(ret) == 0 { + panic("no return value specified for UpdateAuthRequest") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string, func(storage.AuthRequest) (storage.AuthRequest, error)) error); ok { + r0 = rf(id, updater) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UpdateClient provides a mock function with given fields: id, updater +func (_m *MockStorage) UpdateClient(id string, updater func(storage.Client) (storage.Client, error)) error { + ret := _m.Called(id, updater) + + if len(ret) == 0 { + panic("no return value specified for UpdateClient") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string, func(storage.Client) (storage.Client, error)) error); ok { + r0 = rf(id, updater) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UpdateConnector provides a mock function with given fields: id, updater +func (_m *MockStorage) UpdateConnector(id string, updater func(storage.Connector) (storage.Connector, error)) error { + ret := _m.Called(id, updater) + + if len(ret) == 0 { + panic("no return value specified for UpdateConnector") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string, func(storage.Connector) (storage.Connector, error)) error); ok { + r0 = rf(id, updater) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UpdateDeviceToken provides a mock function with given fields: deviceCode, updater +func (_m *MockStorage) UpdateDeviceToken(deviceCode string, updater func(storage.DeviceToken) (storage.DeviceToken, error)) error { + ret := _m.Called(deviceCode, updater) + + if len(ret) == 0 { + panic("no return value specified for UpdateDeviceToken") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string, func(storage.DeviceToken) (storage.DeviceToken, error)) error); ok { + r0 = rf(deviceCode, updater) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UpdateKeys provides a mock function with given fields: updater +func (_m *MockStorage) UpdateKeys(updater func(storage.Keys) (storage.Keys, error)) error { + ret := _m.Called(updater) + + if len(ret) == 0 { + panic("no return value specified for UpdateKeys") + } + + var r0 error + if rf, ok := ret.Get(0).(func(func(storage.Keys) (storage.Keys, error)) error); ok { + r0 = rf(updater) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UpdateOfflineSessions provides a mock function with given fields: userID, connID, updater +func (_m *MockStorage) UpdateOfflineSessions(userID string, connID string, updater func(storage.OfflineSessions) (storage.OfflineSessions, error)) error { + ret := _m.Called(userID, connID, updater) + + if len(ret) == 0 { + panic("no return value specified for UpdateOfflineSessions") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string, string, func(storage.OfflineSessions) (storage.OfflineSessions, error)) error); ok { + r0 = rf(userID, connID, updater) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UpdatePassword provides a mock function with given fields: email, updater +func (_m *MockStorage) UpdatePassword(email string, updater func(storage.Password) (storage.Password, error)) error { + ret := _m.Called(email, updater) + + if len(ret) == 0 { + panic("no return value specified for UpdatePassword") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string, func(storage.Password) (storage.Password, error)) error); ok { + r0 = rf(email, updater) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// UpdateRefreshToken provides a mock function with given fields: id, updater +func (_m *MockStorage) UpdateRefreshToken(id string, updater func(storage.RefreshToken) (storage.RefreshToken, error)) error { + ret := _m.Called(id, updater) + + if len(ret) == 0 { + panic("no return value specified for UpdateRefreshToken") + } + + var r0 error + if rf, ok := ret.Get(0).(func(string, func(storage.RefreshToken) (storage.RefreshToken, error)) error); ok { + r0 = rf(id, updater) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// NewMockStorage creates a new instance of MockStorage. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockStorage(t interface { + mock.TestingT + Cleanup(func()) +}) *MockStorage { + mock := &MockStorage{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/pkg/test/env.go b/pkg/test/env.go index 50c1bc73c..fa3e9f9a8 100644 --- a/pkg/test/env.go +++ b/pkg/test/env.go @@ -12,6 +12,7 @@ import ( "path/filepath" "time" + "github.com/dexidp/dex/storage/sql" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" @@ -167,6 +168,20 @@ var ( Expect(registerFunc(K8sManager)).To(Succeed(), "there must be no error registering the webhook", "name", webhookName) } + // Start Postgres for testing + psqlCfg := &sql.Postgres{ + sql.NetworkDB{ + Database: "postgres", + User: "postgres", + Password: "postgres", + Host: "localhost", + Port: 5432, + }, + sql.SSL{ + Mode: "disable", + }, + } + // Register controllers. for controllerName, registerFunc := range allRegisterControllerFuncs { Expect(registerFunc(controllerName, K8sManager)). From f56f4d94a2fb6f75c37aaee0c83e346efca8d67b Mon Sep 17 00:00:00 2001 From: License Bot Date: Thu, 16 Jan 2025 13:21:48 +0000 Subject: [PATCH 011/108] Automatic application of license header --- pkg/mocks/mock_Storage.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/mocks/mock_Storage.go b/pkg/mocks/mock_Storage.go index d2f31847c..ad7edf9de 100644 --- a/pkg/mocks/mock_Storage.go +++ b/pkg/mocks/mock_Storage.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors +// SPDX-License-Identifier: Apache-2.0 + // Code generated by mockery v2.51.0. DO NOT EDIT. package mocks From 5078f0e9d388039a13b4a882bd4e645cadf58025 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Thu, 16 Jan 2025 16:26:22 +0100 Subject: [PATCH 012/108] (chore): removes unused vars --- pkg/test/env.go | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/pkg/test/env.go b/pkg/test/env.go index fa3e9f9a8..50c1bc73c 100644 --- a/pkg/test/env.go +++ b/pkg/test/env.go @@ -12,7 +12,6 @@ import ( "path/filepath" "time" - "github.com/dexidp/dex/storage/sql" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" corev1 "k8s.io/api/core/v1" @@ -168,20 +167,6 @@ var ( Expect(registerFunc(K8sManager)).To(Succeed(), "there must be no error registering the webhook", "name", webhookName) } - // Start Postgres for testing - psqlCfg := &sql.Postgres{ - sql.NetworkDB{ - Database: "postgres", - User: "postgres", - Password: "postgres", - Host: "localhost", - Port: 5432, - }, - sql.SSL{ - Mode: "disable", - }, - } - // Register controllers. for controllerName, registerFunc := range allRegisterControllerFuncs { Expect(registerFunc(controllerName, K8sManager)). From 7caec01658961f3415870a046f8a2796d2d88226 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Thu, 16 Jan 2025 16:27:13 +0100 Subject: [PATCH 013/108] (chore): spin up postgres test container for organization test --- pkg/controllers/organization/suite_test.go | 53 +++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/pkg/controllers/organization/suite_test.go b/pkg/controllers/organization/suite_test.go index 2e4b55efa..036771062 100644 --- a/pkg/controllers/organization/suite_test.go +++ b/pkg/controllers/organization/suite_test.go @@ -4,6 +4,10 @@ package organization_test import ( + "context" + "github.com/dexidp/dex/storage/sql" + "github.com/testcontainers/testcontainers-go" + "github.com/testcontainers/testcontainers-go/wait" "net/http/httptest" "testing" @@ -14,10 +18,20 @@ import ( organizationpkg "github.com/cloudoperators/greenhouse/pkg/controllers/organization" "github.com/cloudoperators/greenhouse/pkg/scim" "github.com/cloudoperators/greenhouse/pkg/test" + + "github.com/testcontainers/testcontainers-go/modules/postgres" +) + +const ( + mockDb = "mock" + mockUsr = "mock" + mockPwd = "mock_pwd" + mockPort = 5004 ) var ( groupsServer *httptest.Server + mockPgTc *postgres.PostgresContainer ) func TestOrganization(t *testing.T) { @@ -26,10 +40,29 @@ func TestOrganization(t *testing.T) { } var _ = BeforeSuite(func() { + var err error + ctx := context.Background() By("mocking SCIM server") groupsServer = scim.ReturnDefaultGroupResponseMockServer() - test.RegisterController("organizationController", (&organizationpkg.OrganizationReconciler{Namespace: "default"}).SetupWithManager) + mockPgTc, err = startPgTC(ctx) + Expect(err).NotTo(HaveOccurred()) + + host, err := mockPgTc.Host(ctx) + Expect(err).NotTo(HaveOccurred()) + + port, err := mockPgTc.MappedPort(ctx, "5432/tcp") + Expect(err).NotTo(HaveOccurred()) + + netDB := sql.NetworkDB{ + Host: host, + Port: uint16(port.Int()), + User: mockUsr, + Password: mockPwd, + Database: mockDb, + } + + test.RegisterController("organizationController", (&organizationpkg.OrganizationReconciler{Namespace: "default", NetworkDB: netDB}).SetupWithManager) test.RegisterWebhook("orgWebhook", admission.SetupOrganizationWebhookWithManager) test.RegisterWebhook("teamWebhook", admission.SetupTeamWebhookWithManager) test.RegisterWebhook("pluginDefinitionWebhook", admission.SetupPluginDefinitionWebhookWithManager) @@ -41,6 +74,24 @@ var _ = BeforeSuite(func() { var _ = AfterSuite(func() { By("tearing down the test environment") groupsServer.Close() + err := testcontainers.TerminateContainer(mockPgTc) + Expect(err).NotTo(HaveOccurred()) test.TestAfterSuite() }) + +func startPgTC(ctx context.Context) (*postgres.PostgresContainer, error) { + return postgres.Run(ctx, "postgres:16-alpine", + postgres.WithDatabase(mockDb), + postgres.WithUsername(mockUsr), + postgres.WithPassword(mockPwd), + testcontainers.WithWaitStrategy( + // First, we wait for the container to log readiness twice. + // This is because it will restart itself after the first startup. + wait.ForLog("database system is ready to accept connections").WithOccurrence(2), + // Then, we wait for docker to actually serve the port on localhost. + // For non-linux OSes like Mac and Windows, Docker or Rancher Desktop will have to start a separate proxy. + // Without this, the tests will be flaky on those OSes! + wait.ForListeningPort("5432/tcp"), + )) +} From 74f0bb47b7f9e8e5dd73ae5d86dc4753f8bfba2a Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Thu, 16 Jan 2025 16:27:23 +0100 Subject: [PATCH 014/108] (chore): tidy up! --- go.mod | 21 ++++++++++++++++++++- go.sum | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index ef2f603e7..b4df83c13 100644 --- a/go.mod +++ b/go.mod @@ -32,6 +32,8 @@ require ( github.com/spf13/cobra v1.8.1 github.com/spf13/pflag v1.0.5 github.com/stretchr/testify v1.10.0 + github.com/testcontainers/testcontainers-go v0.35.0 + github.com/testcontainers/testcontainers-go/modules/postgres v0.35.0 github.com/vladimirvivien/gexe v0.4.1 github.com/wI2L/jsondiff v0.6.1 go.uber.org/zap v1.27.0 @@ -56,37 +58,54 @@ require ( cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect dario.cat/mergo v1.0.1 // indirect filippo.io/edwards25519 v1.1.0 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/hcsshim v0.12.6 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/containerd/errdefs v0.3.0 // indirect github.com/containerd/log v0.1.0 // indirect github.com/containerd/platforms v0.2.1 // indirect + github.com/cpuguy83/dockercfg v0.3.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect github.com/creack/pty v1.1.23 // indirect github.com/distribution/reference v0.6.0 // indirect + github.com/docker/go-units v0.5.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-sql-driver/mysql v1.8.1 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/miekg/dns v1.1.58 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/patternmatcher v0.6.0 // indirect + github.com/moby/sys/sequential v0.5.0 // indirect + github.com/moby/sys/user v0.3.0 // indirect + github.com/moby/sys/userns v0.1.0 // indirect + github.com/morikuni/aec v1.0.0 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/otiai10/mint v1.6.3 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/sergi/go-diff v1.3.1 // indirect + github.com/shirou/gopsutil/v3 v3.23.12 // indirect + github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/tidwall/gjson v1.18.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/sjson v1.2.5 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect github.com/x448/float16 v0.8.4 // indirect + github.com/yusufpapurcu/wmi v1.2.3 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect - gotest.tools/v3 v3.5.1 // indirect ) require ( diff --git a/go.sum b/go.sum index c97417af5..ba53ad96c 100644 --- a/go.sum +++ b/go.sum @@ -83,6 +83,8 @@ github.com/coreos/go-oidc/v3 v3.11.0 h1:Ia3MxdwpSw702YW0xgfmP1GVCMA9aEFWu12XUZ3/ github.com/coreos/go-oidc/v3 v3.11.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= +github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -118,6 +120,8 @@ github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= @@ -167,6 +171,8 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= @@ -210,6 +216,7 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -262,6 +269,14 @@ github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.5.4 h1:Xp2aQS8uXButQdnCMWNmvx6UysWQQC+u1EoizjguY+8= +github.com/jackc/pgx/v5 v5.5.4/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= @@ -313,6 +328,10 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattermost/xml-roundtrip-validator v0.1.0 h1:RXbVD2UAl7A7nOTR4u7E3ILa4IbtvKBHw64LDsmu9hU= @@ -327,6 +346,8 @@ github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mdelapenya/tlscert v0.1.0 h1:YTpF579PYUX475eOL+6zyEO3ngLTOUWck78NBuJVXaM= +github.com/mdelapenya/tlscert v0.1.0/go.mod h1:wrbyM/DwbFCeCeqdPX/8c6hNOqQgbf0rUDErE1uD+64= github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= @@ -335,12 +356,20 @@ github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQ github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= +github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= +github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU= github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= +github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= +github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= +github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo= +github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= @@ -354,6 +383,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -385,6 +416,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY= github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -427,6 +460,12 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= +github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= +github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= +github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= +github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= +github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= +github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -453,8 +492,13 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/testcontainers/testcontainers-go v0.35.0 h1:uADsZpTKFAtp8SLK+hMwSaa+X+JiERHtd4sQAFmXeMo= +github.com/testcontainers/testcontainers-go v0.35.0/go.mod h1:oEVBj5zrfJTrgjwONs1SsRbnBtH9OKl+IGl3UMcr2B4= +github.com/testcontainers/testcontainers-go/modules/postgres v0.35.0 h1:eEGx9kYzZb2cNhRbBrNOCL/YPOM7+RMJiy3bB+ie0/I= +github.com/testcontainers/testcontainers-go/modules/postgres v0.35.0/go.mod h1:hfH71Mia/WWLBgMD2YctYcMlfsbnT0hflweL1dy8Q4s= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= @@ -465,6 +509,10 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/vladimirvivien/gexe v0.4.1 h1:W9gWkp8vSPjDoXDu04Yp4KljpVMaSt8IQuHswLDd5LY= github.com/vladimirvivien/gexe v0.4.1/go.mod h1:3gjgTqE2c0VyHnU5UOIwk7gyNzZDGulPb/DJPgcw64E= github.com/wI2L/jsondiff v0.6.1 h1:ISZb9oNWbP64LHnu4AUhsMF5W0FIj5Ok3Krip9Shqpw= @@ -483,6 +531,8 @@ github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= +github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib/exporters/autoexport v0.46.1 h1:ysCfPZB9AjUlMa1UHYup3c9dAOCMQX/6sxSfPBUoxHw= @@ -589,8 +639,10 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -600,6 +652,8 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= From 6c9e6f6e268d4d3d060bce5250f8b2d7a7758c82 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Thu, 16 Jan 2025 16:28:10 +0100 Subject: [PATCH 015/108] (chore): go fmt --- cmd/idproxy/main.go | 2 +- pkg/controllers/organization/suite_test.go | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/cmd/idproxy/main.go b/cmd/idproxy/main.go index 624f7807b..bc9b2286c 100644 --- a/cmd/idproxy/main.go +++ b/cmd/idproxy/main.go @@ -42,7 +42,7 @@ func main() { var allowedOrigins []string // DB connection parameters var postgresDB sql.NetworkDB - + logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) // set default logger to be used by log slog.SetDefault(logger) diff --git a/pkg/controllers/organization/suite_test.go b/pkg/controllers/organization/suite_test.go index 036771062..3054f6838 100644 --- a/pkg/controllers/organization/suite_test.go +++ b/pkg/controllers/organization/suite_test.go @@ -5,11 +5,12 @@ package organization_test import ( "context" + "net/http/httptest" + "testing" + "github.com/dexidp/dex/storage/sql" "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/wait" - "net/http/httptest" - "testing" . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -23,10 +24,9 @@ import ( ) const ( - mockDb = "mock" - mockUsr = "mock" - mockPwd = "mock_pwd" - mockPort = 5004 + mockDb = "mock" + mockUsr = "mock" + mockPwd = "mock_pwd" ) var ( From 18791f5ade99975f15613599eb0b87f89ed0381f Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Thu, 16 Jan 2025 16:34:56 +0100 Subject: [PATCH 016/108] (chore): lint fix --- pkg/controllers/organization/suite_test.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/pkg/controllers/organization/suite_test.go b/pkg/controllers/organization/suite_test.go index 3054f6838..d7856ad9a 100644 --- a/pkg/controllers/organization/suite_test.go +++ b/pkg/controllers/organization/suite_test.go @@ -24,9 +24,9 @@ import ( ) const ( - mockDb = "mock" - mockUsr = "mock" - mockPwd = "mock_pwd" + mockDB = "mock" + mockUSR = "mock" + mockPWD = "mock_pwd" ) var ( @@ -57,9 +57,9 @@ var _ = BeforeSuite(func() { netDB := sql.NetworkDB{ Host: host, Port: uint16(port.Int()), - User: mockUsr, - Password: mockPwd, - Database: mockDb, + User: mockUSR, + Password: mockPWD, + Database: mockDB, } test.RegisterController("organizationController", (&organizationpkg.OrganizationReconciler{Namespace: "default", NetworkDB: netDB}).SetupWithManager) @@ -82,9 +82,9 @@ var _ = AfterSuite(func() { func startPgTC(ctx context.Context) (*postgres.PostgresContainer, error) { return postgres.Run(ctx, "postgres:16-alpine", - postgres.WithDatabase(mockDb), - postgres.WithUsername(mockUsr), - postgres.WithPassword(mockPwd), + postgres.WithDatabase(mockDB), + postgres.WithUsername(mockUSR), + postgres.WithPassword(mockPWD), testcontainers.WithWaitStrategy( // First, we wait for the container to log readiness twice. // This is because it will restart itself after the first startup. From d3c5cced5a3c0a54f66416293242159a3f02f375 Mon Sep 17 00:00:00 2001 From: David Gogl <1381862+kengou@users.noreply.github.com> Date: Wed, 29 Jan 2025 14:35:09 +0100 Subject: [PATCH 017/108] feat(ci) add flags, featureflag to deployments --- charts/idproxy/templates/deployment.yaml | 19 +++++++++++++++++++ charts/manager/etc/feature_flags.yaml | 13 +++++++++++++ charts/manager/templates/deployment.yaml | 11 +++++++++++ .../templates/manager-featureflag.yaml | 8 ++++++++ 4 files changed, 51 insertions(+) create mode 100644 charts/manager/etc/feature_flags.yaml create mode 100644 charts/manager/templates/manager-featureflag.yaml diff --git a/charts/idproxy/templates/deployment.yaml b/charts/idproxy/templates/deployment.yaml index 143de5f0e..199a2ce7a 100644 --- a/charts/idproxy/templates/deployment.yaml +++ b/charts/idproxy/templates/deployment.yaml @@ -54,6 +54,25 @@ spec: {{- range $origin := .Values.corsAllowedOrigins | default (list "*") }} - --allowed-origins={{ $origin }} {{- end }} + env: + - name: DEX_POSTGRES_DATABASE + value: {{ .Values.postgresql.postgresqlDatabase }} + - name: DEX_POSTGRES_HOST + value: {{ .Values.postgresql.postgresqlHost }} + - name: DEX_POSTGRES_USER + value: {{ .Values.postgresql.postgresqlUsername }} + - name: DEX_POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + key: postgres-password + name: dex-postgres-pguser-dex + - name: FEATURE_FLAG_NAMESPACE + valueFrom: + fieldRef: + apiVersion: v1 + fieldPath: metadata.namespace + - name: FEATURE_FLAG_CONFIG_MAP_NAME + value: {{ include "manager.fullname" . }}-feature-flags ports: - name: oidc containerPort: 8080 diff --git a/charts/manager/etc/feature_flags.yaml b/charts/manager/etc/feature_flags.yaml new file mode 100644 index 000000000..2d0d58f0b --- /dev/null +++ b/charts/manager/etc/feature_flags.yaml @@ -0,0 +1,13 @@ +dex-sql-backend: + variations: + true: true + false: false + defaultRule: + variation: false + +dex-kubernetes-backend: + variations: + true: true + false: false + defaultRule: + variation: true \ No newline at end of file diff --git a/charts/manager/templates/deployment.yaml b/charts/manager/templates/deployment.yaml index cec2d1274..45c637773 100644 --- a/charts/manager/templates/deployment.yaml +++ b/charts/manager/templates/deployment.yaml @@ -35,6 +35,17 @@ spec: {{- include "manager.params" . | indent 8 }} {{- end }} env: + - name: DEX_POSTGRES_DATABASE + value: {{ .Values.postgresql.postgresqlDatabase }} + - name: DEX_POSTGRES_HOST + value: + - name: DEX_POSTGRES_USER + value: {{ .Values.postgresql.postgresqlUsername }} + - name: DEX_POSTGRES_PASSWORD + valueFrom: + secretKeyRef: + key: postgres-password + name: dex-postgres-pguser-dex - name: POD_NAMESPACE valueFrom: fieldRef: diff --git a/charts/manager/templates/manager-featureflag.yaml b/charts/manager/templates/manager-featureflag.yaml new file mode 100644 index 000000000..89b5bd2e0 --- /dev/null +++ b/charts/manager/templates/manager-featureflag.yaml @@ -0,0 +1,8 @@ +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ include "manager.fullname" . }}-feature-flags +data: + config.yaml: |- + {{- .Files.Get "etc/feature_flags.yaml" | nindent 4 }} \ No newline at end of file From 290bca1e8905f922d4aaa4dcec28ed626991096e Mon Sep 17 00:00:00 2001 From: David Gogl <1381862+kengou@users.noreply.github.com> Date: Wed, 29 Jan 2025 14:37:24 +0100 Subject: [PATCH 018/108] fix(ci) rm license header frmo mockery generated code --- .mockery.yaml | 3 --- pkg/mocks/mock_Client.go | 3 --- pkg/mocks/mock_Reconciler.go | 3 --- pkg/mocks/mock_Storage.go | 3 --- pkg/mocks/mock_SubResourceWriter.go | 3 --- 5 files changed, 15 deletions(-) diff --git a/.mockery.yaml b/.mockery.yaml index d1d2d7cc6..26364ec9e 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -1,6 +1,3 @@ -# SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors -# SPDX-License-Identifier: Apache-2.0 - # .mockery.yaml with-expecter: false filename: "mock_{{.InterfaceName}}.go" diff --git a/pkg/mocks/mock_Client.go b/pkg/mocks/mock_Client.go index 5e6a6839a..422469889 100644 --- a/pkg/mocks/mock_Client.go +++ b/pkg/mocks/mock_Client.go @@ -1,6 +1,3 @@ -// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors -// SPDX-License-Identifier: Apache-2.0 - // Code generated by mockery v2.50.0. DO NOT EDIT. package mocks diff --git a/pkg/mocks/mock_Reconciler.go b/pkg/mocks/mock_Reconciler.go index d51e81d9d..7e649589e 100644 --- a/pkg/mocks/mock_Reconciler.go +++ b/pkg/mocks/mock_Reconciler.go @@ -1,6 +1,3 @@ -// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors -// SPDX-License-Identifier: Apache-2.0 - // Code generated by mockery v2.50.0. DO NOT EDIT. package mocks diff --git a/pkg/mocks/mock_Storage.go b/pkg/mocks/mock_Storage.go index ad7edf9de..d2f31847c 100644 --- a/pkg/mocks/mock_Storage.go +++ b/pkg/mocks/mock_Storage.go @@ -1,6 +1,3 @@ -// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors -// SPDX-License-Identifier: Apache-2.0 - // Code generated by mockery v2.51.0. DO NOT EDIT. package mocks diff --git a/pkg/mocks/mock_SubResourceWriter.go b/pkg/mocks/mock_SubResourceWriter.go index cafe872c3..dabd28266 100644 --- a/pkg/mocks/mock_SubResourceWriter.go +++ b/pkg/mocks/mock_SubResourceWriter.go @@ -1,6 +1,3 @@ -// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors -// SPDX-License-Identifier: Apache-2.0 - // Code generated by mockery v2.50.0. DO NOT EDIT. package mocks From 109608229fcf8638a2ab004f12b32fdb235cc74c Mon Sep 17 00:00:00 2001 From: David Gogl <1381862+kengou@users.noreply.github.com> Date: Wed, 29 Jan 2025 14:37:44 +0100 Subject: [PATCH 019/108] add licenserc to skip mockery --- .github/licenserc.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/.github/licenserc.yaml b/.github/licenserc.yaml index cf5906d08..83f783d98 100644 --- a/.github/licenserc.yaml +++ b/.github/licenserc.yaml @@ -38,8 +38,11 @@ header: - 'pkg/idproxy/web/**' - 'pkg/apis/scheme_builder.go' # Belongs to the Kubernetes authors - '**/zz_generated.deepcopy.go' # Generated by Kubebuilder - - 'charts/**/templates/*.yaml' # license headers on helm templates are causing issues - + - 'charts/**/templates/*.yaml' # license headers on helm templates are causing issues + - '.mockery.yaml' # Mockery config file + - 'pkg/mocks/**' # Mockery generated files + + comment: on-failure # license-location-threshold specifies the index threshold where the license header can be located, From 59702e1ba58f813aa31c65c70039b694499d0aec Mon Sep 17 00:00:00 2001 From: David Gogl <1381862+kengou@users.noreply.github.com> Date: Wed, 29 Jan 2025 14:41:12 +0100 Subject: [PATCH 020/108] feat(idproxy) add featureflag to switch backend --- cmd/idproxy/main.go | 47 ++++++--- go.mod | 36 ++++--- go.sum | 221 ++++++++++++++++++++++++++++----------- pkg/features/features.go | 53 ++++++++++ 4 files changed, 270 insertions(+), 87 deletions(-) create mode 100644 pkg/features/features.go diff --git a/cmd/idproxy/main.go b/cmd/idproxy/main.go index bc9b2286c..8942d46bd 100644 --- a/cmd/idproxy/main.go +++ b/cmd/idproxy/main.go @@ -16,9 +16,11 @@ import ( "time" "github.com/dexidp/dex/server" + "github.com/dexidp/dex/storage" "github.com/dexidp/dex/storage/sql" "github.com/go-logr/logr" "github.com/oklog/run" + "github.com/open-feature/go-sdk/openfeature" "github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus/collectors" "github.com/prometheus/client_golang/prometheus/promhttp" @@ -30,6 +32,7 @@ import ( logk "sigs.k8s.io/controller-runtime/pkg/log" greenhousesapv1alpha1 "github.com/cloudoperators/greenhouse/pkg/apis/greenhouse/v1alpha1" + "github.com/cloudoperators/greenhouse/pkg/features" "github.com/cloudoperators/greenhouse/pkg/idproxy" "github.com/cloudoperators/greenhouse/pkg/idproxy/web" ) @@ -42,6 +45,8 @@ func main() { var allowedOrigins []string // DB connection parameters var postgresDB sql.NetworkDB + var dexStorage storage.Storage + var err error logger := slog.New(slog.NewJSONHandler(os.Stdout, nil)) // set default logger to be used by log @@ -52,11 +57,11 @@ func main() { flag.StringVar(&kubeconfig, "kubeconfig", os.Getenv("KUBECONFIG"), "Use kubeconfig for authentication") flag.StringVar(&kubecontext, "kubecontext", os.Getenv("KUBECONTEXT"), "Use context from kubeconfig") flag.StringVar(&kubenamespace, "kubenamespace", os.Getenv("KUBENAMESPACE"), "Use namespace") - flag.StringVar(&postgresDB.Database, "database", os.Getenv("DB_NAME"), "Database name") - flag.StringVar(&postgresDB.Host, "dbHost", os.Getenv("DB_HOST"), "Database host") + flag.StringVar(&postgresDB.Database, "database", os.Getenv("DEX_POSTGRES_DATABASE"), "Database name") + flag.StringVar(&postgresDB.Host, "dbHost", os.Getenv("DEX_POSTGRES_HOST"), "Database host") flag.Uint16Var(&postgresDB.Port, "dbPort", 5432, "Database port") - flag.StringVar(&postgresDB.User, "dbUser", os.Getenv("DB_USER"), "Database user") - flag.StringVar(&postgresDB.Password, "dbPassword", os.Getenv("DB_PASSWORD"), "Database password") + flag.StringVar(&postgresDB.User, "dbUser", os.Getenv("DEX_POSTGRES_USER"), "Database user") + flag.StringVar(&postgresDB.Password, "dbPassword", os.Getenv("DEX_POSTGRES_PASSWORD"), "Database password") flag.StringVar(&issuer, "issuer", "", "Issuer URL") flag.StringVar(&listenAddr, "listen-addr", ":8080", "oidc listen address") flag.StringVar(&metricsAddr, "metrics-addr", ":6543", "bind address for metrics") @@ -67,16 +72,35 @@ func main() { if issuer == "" { log.Fatal("No --issuer given") } - /* - sqlDexStorage, err := idproxy.NewPostgresStorage(sql.SSL{Mode: "disable"}, postgresDB, logger.With("component", "storage")) + + ofClient := features.GetOFClient("idproxy") + evalCtx := openfeature.NewEvaluationContext( + "backend", + map[string]interface{}{}, + ) + postgresBackend, err := ofClient.BooleanValue(context.TODO(), "dex-sql-backend", false, evalCtx) + if err != nil { + log.Fatalf("Failed to get dex-sql-backend value details: %s", err) + } + + k8sBackend, err := ofClient.BooleanValue(context.TODO(), "dex-kubernetes-backend", false, evalCtx) + if err != nil { + log.Fatalf("Failed to get dex-kubernetes-backend value details: %s", err) + } + + switch { + case postgresBackend: + dexStorage, err = idproxy.NewPostgresStorage(sql.SSL{Mode: "disable"}, postgresDB, logger.With("component", "storage")) if err != nil { log.Fatalf("Failed to initialize postgres storage: %s", err) } - - */ - dexStorage, err := idproxy.NewKubernetesStorage(kubeconfig, kubecontext, kubenamespace, logger.With("component", "storage")) - if err != nil { - log.Fatalf("Failed to initialize kubernetes storage: %s", err) + case k8sBackend: + dexStorage, err = idproxy.NewKubernetesStorage(kubeconfig, kubecontext, kubenamespace, logger.With("component", "storage")) + if err != nil { + log.Fatalf("Failed to initialize kubernetes storage: %s", err) + } + default: + log.Fatalf("Exactly one of dex-sql-backend or dex-kubernetes-backend must be true") } refreshPolicy, err := server.NewRefreshTokenPolicy(logger.With("component", "refreshtokenpolicy"), true, "24h", "24h", "5s") @@ -93,7 +117,6 @@ func main() { SkipApprovalScreen: true, Logger: logger.With("component", "server"), Storage: dexStorage, - // Storage: sqlDexStorage, AllowedOrigins: allowedOrigins, IDTokensValidFor: idTokenValidity, RefreshTokenPolicy: refreshPolicy, diff --git a/go.mod b/go.mod index b4df83c13..3cdf99498 100644 --- a/go.mod +++ b/go.mod @@ -26,6 +26,8 @@ require ( github.com/oklog/run v1.1.1-0.20240127200640-eee6e044b77c github.com/onsi/ginkgo/v2 v2.22.2 github.com/onsi/gomega v1.36.2 + github.com/open-feature/go-sdk v1.14.1 + github.com/open-feature/go-sdk-contrib/providers/go-feature-flag-in-process v0.1.0 github.com/pkg/errors v0.9.1 github.com/prometheus/client_golang v1.20.5 github.com/sirupsen/logrus v1.9.3 @@ -34,6 +36,7 @@ require ( github.com/stretchr/testify v1.10.0 github.com/testcontainers/testcontainers-go v0.35.0 github.com/testcontainers/testcontainers-go/modules/postgres v0.35.0 + github.com/thomaspoignant/go-feature-flag v1.40.0 github.com/vladimirvivien/gexe v0.4.1 github.com/wI2L/jsondiff v0.6.1 go.uber.org/zap v1.27.0 @@ -54,12 +57,14 @@ require ( ) require ( - cloud.google.com/go/auth v0.9.2 // indirect - cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect + cloud.google.com/go/auth v0.11.0 // indirect + cloud.google.com/go/auth/oauth2adapt v0.2.6 // indirect dario.cat/mergo v1.0.1 // indirect filippo.io/edwards25519 v1.1.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/hcsshim v0.12.6 // indirect + github.com/antlr4-go/antlr/v4 v4.13.0 // indirect + github.com/blang/semver v3.5.1+incompatible // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/containerd/errdefs v0.3.0 // indirect github.com/containerd/log v0.1.0 // indirect @@ -75,7 +80,6 @@ require ( github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect github.com/gorilla/websocket v1.5.3 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/magiconair/properties v1.8.7 // indirect @@ -88,6 +92,7 @@ require ( github.com/moby/sys/userns v0.1.0 // indirect github.com/morikuni/aec v1.0.0 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect + github.com/nikunjy/rules v1.5.0 // indirect github.com/otiai10/mint v1.6.3 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect @@ -104,12 +109,11 @@ require ( github.com/x448/float16 v0.8.4 // indirect github.com/yusufpapurcu/wmi v1.2.3 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect ) require ( - cloud.google.com/go/compute/metadata v0.5.0 // indirect + cloud.google.com/go/compute/metadata v0.5.2 // indirect github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 // indirect github.com/AppsFlyer/go-sundheit v0.6.0 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect @@ -164,8 +168,8 @@ require ( github.com/google/s2a-go v0.1.8 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/googleapis/enterprise-certificate-proxy v0.3.3 // indirect - github.com/googleapis/gax-go/v2 v2.13.0 // indirect + github.com/googleapis/enterprise-certificate-proxy v0.3.4 // indirect + github.com/googleapis/gax-go/v2 v2.14.0 // indirect github.com/gorilla/handlers v1.5.2 // indirect github.com/gorilla/mux v1.8.1 // indirect github.com/gosuri/uitable v0.0.4 // indirect @@ -179,7 +183,7 @@ require ( github.com/jonboulle/clockwork v0.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect github.com/json-iterator/go v1.1.12 // indirect - github.com/klauspost/compress v1.17.9 // indirect + github.com/klauspost/compress v1.17.11 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/lib/pq v1.10.9 // indirect @@ -204,7 +208,7 @@ require ( github.com/otiai10/copy v1.14.1 github.com/peterbourgon/diskv v2.0.1+incompatible // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.59.1 // indirect + github.com/prometheus/common v0.60.1 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/rivo/uniseg v0.4.7 // indirect github.com/rubenv/sql-migrate v1.7.0 // indirect @@ -217,24 +221,24 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xlab/treeprint v1.2.0 // indirect go.opencensus.io v0.24.0 // indirect - go.opentelemetry.io/otel v1.29.0 // indirect - go.opentelemetry.io/otel/metric v1.29.0 // indirect - go.opentelemetry.io/otel/trace v1.29.0 // indirect + go.opentelemetry.io/otel v1.32.0 // indirect + go.opentelemetry.io/otel/metric v1.32.0 // indirect + go.opentelemetry.io/otel/trace v1.32.0 // indirect go.starlark.net v0.0.0-20240725214946-42030a7cedce // indirect go.uber.org/multierr v1.11.0 // indirect golang.org/x/crypto v0.31.0 // indirect golang.org/x/exp v0.0.0-20240823005443-9b4947da3948 // indirect golang.org/x/net v0.33.0 // indirect - golang.org/x/oauth2 v0.22.0 // indirect + golang.org/x/oauth2 v0.24.0 // indirect golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.28.0 // indirect golang.org/x/term v0.27.0 // indirect golang.org/x/time v0.9.0 golang.org/x/tools v0.28.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/api v0.195.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed // indirect - google.golang.org/grpc v1.66.3 // indirect + google.golang.org/api v0.210.0 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697 // indirect + google.golang.org/grpc v1.68.1 // indirect google.golang.org/protobuf v1.36.1 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect diff --git a/go.sum b/go.sum index ba53ad96c..ef01ce846 100644 --- a/go.sum +++ b/go.sum @@ -1,10 +1,20 @@ +cel.dev/expr v0.16.1 h1:NR0+oFYzR1CqLFhTAqg3ql59G9VfN8fKq1TCHJ6gq1g= +cel.dev/expr v0.16.1/go.mod h1:AsGA5zb3WruAEQeQng1RZdGEXmBj0jvMWh6l5SnNuC8= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go/auth v0.9.2 h1:I+Rq388FYU8QdbVB1IiPd+6KNdrqtAPE/asiKHShBLM= -cloud.google.com/go/auth v0.9.2/go.mod h1:7z6VY+7h3KUdRov5F1i8NDP5ZzWKYmEPO842BgCsmTk= -cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY= -cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc= -cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= -cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= +cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE= +cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U= +cloud.google.com/go/auth v0.11.0 h1:Ic5SZz2lsvbYcWT5dfjNWgw6tTlGi2Wc8hyQSC9BstA= +cloud.google.com/go/auth v0.11.0/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI= +cloud.google.com/go/auth/oauth2adapt v0.2.6 h1:V6a6XDu2lTwPZWOawrAa9HUK+DB2zfJyTuciBG5hFkU= +cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8= +cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixAHn3fyqngqo= +cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k= +cloud.google.com/go/iam v1.2.2 h1:ozUSofHUGf/F4tCNy/mu9tHLTaxZFLOUiKzjcgWHGIA= +cloud.google.com/go/iam v1.2.2/go.mod h1:0Ys8ccaZHdI1dEUilwzqng/6ps2YB6vRsjIe00/+6JY= +cloud.google.com/go/monitoring v1.21.2 h1:FChwVtClH19E7pJ+e0xUhJPGksctZNVOk2UhMmblmdU= +cloud.google.com/go/monitoring v1.21.2/go.mod h1:hS3pXvaG8KgWTSz+dAdyzPrGUYmi2Q+WFX8g2hqVEZU= +cloud.google.com/go/storage v1.48.0 h1:FhBDHACbVtdPx7S/AbcKujPWiHvfO6F8OXGgCEbB2+o= +cloud.google.com/go/storage v1.48.0/go.mod h1:aFoDYNMAjv67lp+xcuZqjUKv/ctmplzQ3wJgodA7b+M= dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= @@ -22,6 +32,12 @@ github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/k github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.1 h1:pB2F2JKCj1Znmp2rwxxt1J0Fg0wezTMgWYk5Mpbi1kg= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.1/go.mod h1:itPGVDKf9cC/ov4MdvJ2QZ0khw4bfoo9jzwTJlaxy2k= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1 h1:UQ0AhxogsIRZDkElkblfnwjc3IaltCm2HUMvezQaL7s= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1/go.mod h1:jyqM3eLpJ3IbIFDTKVz2rF9T/xWGW0rIriGwnz8l9Tk= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1 h1:8nn+rsCvTq9axyEh382S0PFLBeaFwNsT43IrPWzctRU= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1/go.mod h1:viRWSEhtMZqz1rhwmOVKkWl6SwmVowfL9O2YR5gI2PE= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= @@ -42,10 +58,42 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI= github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= +github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= +github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= +github.com/apache/arrow/go/arrow v0.0.0-20200730104253-651201b0f516 h1:byKBBF2CKWBjjA4J1ZL2JXttJULvWSl50LegTyRZ728= +github.com/apache/arrow/go/arrow v0.0.0-20200730104253-651201b0f516/go.mod h1:QNYViu/X0HXDHw7m3KXzWSVXIbfUvJqBFe6Gj8/pYA0= +github.com/apache/thrift v0.14.2 h1:hY4rAyg7Eqbb27GB6gkhUKrRAuc8xRjlNtJq+LseKeY= +github.com/apache/thrift v0.14.2/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= +github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= +github.com/aws/aws-sdk-go-v2 v1.32.6 h1:7BokKRgRPuGmKkFMhEg/jSul+tB9VvXhcViILtfG8b4= +github.com/aws/aws-sdk-go-v2 v1.32.6/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwUGOZdsFFnQ5uLvR0hWqqhyE7Q9M8= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7/go.mod h1:QraP0UcVlQJsmHfioCrveWOC1nbiWUl3ej08h4mXWoc= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.43 h1:iLdpkYZ4cXIQMO7ud+cqMWR1xK5ESbt1rvN77tRi1BY= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.43/go.mod h1:OgbsKPAswXDd5kxnR4vZov69p3oYjbvUyIRBAAV0y9o= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25 h1:s/fF4+yDQDoElYhfIVvSNyeCydfbuTKzhxSXDXCPasU= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25/go.mod h1:IgPfDv5jqFIzQSNbUEMoitNooSMXjRSDkhXv8jiROvU= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25 h1:ZntTCl5EsYnhN/IygQEUugpdwbhdkom9uHcbCftiGgA= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25/go.mod h1:DBdPrgeocww+CSl1C8cEV8PN1mHMBhuCDLpXezyvWkE= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.25 h1:r67ps7oHCYnflpgDy2LZU0MAQtQbYIOqNNnqGO6xQkE= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.25/go.mod h1:GrGY+Q4fIokYLtjCVB/aFfCVL6hhGUFl8inD18fDalE= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.6 h1:HCpPsWqmYQieU7SS6E9HXfdAMSud0pteVXieJmcpIRI= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.6/go.mod h1:ngUiVRCco++u+soRRVBIvBZxSMMvOVMXA4PJ36JLfSw= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6 h1:50+XsN70RS7dwJ2CkVNXzj7U2L1HKP8nqTd3XWEXBN4= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6/go.mod h1:WqgLmwY7so32kG01zD8CPTJWVWM+TzJoOVHwTg4aPug= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.6 h1:BbGDtTi0T1DYlmjBiCr/le3wzhA37O8QTC5/Ab8+EXk= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.6/go.mod h1:hLMJt7Q8ePgViKupeymbqI0la+t9/iYFBjxQCFwuAwI= +github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0 h1:nyuzXooUNJexRT0Oy0UQY6AhOzxPxhtt4DcBIHyCnmw= +github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0/go.mod h1:sT/iQz8JK3u/5gZkT+Hmr7GzVZehUMkRZpOaAwYXeGY= +github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro= +github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= github.com/beevik/etree v1.4.1 h1:PmQJDDYahBGNKDcpdX8uPy1xRCwoCGVUiW669MEirVI= github.com/beevik/etree v1.4.1/go.mod h1:gPNJNaBGVZ9AwsidazFZyygnd+0pAU38N4D+WemwKNs= @@ -53,6 +101,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= +github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= @@ -60,12 +110,16 @@ github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= +github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v1.0.3 h1:9liNh8t+u26xl5ddmWLmsOsdNLwkdRTg5AG+JnTiM80= github.com/chai2010/gettext-go v1.0.3/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 h1:QVw89YDxXxEe+l8gU8ETbOasdwEV+avkR75ZzsVV9WI= +github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/cgroups/v3 v3.0.3 h1:S5ByHZ/h9PMe5IOQoN7E+nMc2UcLEM/V48DGDJ9kip0= github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0= @@ -129,7 +183,11 @@ github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRr github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/go-control-plane v0.13.0 h1:HzkeUz1Knt+3bK+8LG1bxOO/jzWZmdxpwC51i202les= +github.com/envoyproxy/go-control-plane v0.13.0/go.mod h1:GRaKG3dwvFoTg4nj7aXdZnvMg4d7nvT/wl9WgVXn3Q8= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM= +github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4= github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls= github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= @@ -194,6 +252,8 @@ github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4er github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -206,6 +266,8 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 h1:0VpGH+cDhbDtdcweoyCVsF3fhN8kejK6rFe/2FFX2nU= @@ -232,10 +294,10 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/googleapis/enterprise-certificate-proxy v0.3.3 h1:QRje2j5GZimBzlbhGA2V2QlGNgL8G6e+wGo/+/2bWI0= -github.com/googleapis/enterprise-certificate-proxy v0.3.3/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= -github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s= -github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A= +github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= +github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= +github.com/googleapis/gax-go/v2 v2.14.0 h1:f+jMrjBPl+DL9nI4IQzLUxMq7XrAqFYB7hBPqMNIe8o= +github.com/googleapis/gax-go/v2 v2.14.0/go.mod h1:lhBCnjdLrWRaPvLWhmc8IS24m9mr07qSYnHncrgo+zk= github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= @@ -248,8 +310,9 @@ github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 h1:ad0vkEBuk23VJzZR9nkLVG0YAoN9coASF1GusYX6AlU= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0/go.mod h1:igFoXX2ELCW06bol23DWPB5BEWfZISOzSP5K2sbLea0= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -258,7 +321,7 @@ github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9 github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= +github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= github.com/hashicorp/golang-lru/arc/v2 v2.0.5 h1:l2zaLDubNhW4XO3LnliVj0GXO3+/CGNJAg1dcN2Fpfw= github.com/hashicorp/golang-lru/arc/v2 v2.0.5/go.mod h1:ny6zBSQZi2JxIeYcv7kt2sH2PXJtirBN7RDhRpxPkxU= github.com/hashicorp/golang-lru/v2 v2.0.5 h1:wW7h1TG88eUIJ2i69gaE3uNVtEPIagzhGvHgwfx2Vm4= @@ -291,6 +354,8 @@ github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZ github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= github.com/jeremywohl/flatten/v2 v2.0.0-20211013061545-07e4a09fb8e4 h1:eA9wi6ZzpIRobvXkn/S2Lyw1hr2pc71zxzOPl7Xjs4w= github.com/jeremywohl/flatten/v2 v2.0.0-20211013061545-07e4a09fb8e4/go.mod h1:s9g9Dfls+aEgucKXKW+i8MRZuLXT2MrD/WjYpMnWfOw= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= @@ -305,8 +370,8 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= +github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -390,12 +455,18 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8m github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/nikunjy/rules v1.5.0 h1:KJDSLOsFhwt7kcXUyZqwkgrQg5YoUwj+TVu6ItCQShw= +github.com/nikunjy/rules v1.5.0/go.mod h1:TlZtZdBChrkqi8Lr2AXocme8Z7EsbxtFdDoKeI6neBQ= github.com/oklog/run v1.1.1-0.20240127200640-eee6e044b77c h1:UPP5+t0sCRHk8JklGdZTjqaGRlukvgitcKlw0/mrjr0= github.com/oklog/run v1.1.1-0.20240127200640-eee6e044b77c/go.mod h1:mgDbKRSwPhJfesJ4PntqFUbKQRZ50NgmZTSPlFA0YFk= github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU= github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk= github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= +github.com/open-feature/go-sdk v1.14.1 h1:jcxjCIG5Up3XkgYwWN5Y/WWfc6XobOhqrIwjyDBsoQo= +github.com/open-feature/go-sdk v1.14.1/go.mod h1:t337k0VB/t/YxJ9S0prT30ISUHwYmUd/jhUZgFcOvGg= +github.com/open-feature/go-sdk-contrib/providers/go-feature-flag-in-process v0.1.0 h1:EFIT5QBQ/T3lNVLmma69SNQbAWBgAl+EtcH0VfrdM7Y= +github.com/open-feature/go-sdk-contrib/providers/go-feature-flag-in-process v0.1.0/go.mod h1:DpptytCB+FbUIoRjTGtSDEA82aojWC4MIxL8GOK26Rs= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= @@ -408,11 +479,15 @@ github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+v github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= +github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= +github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= @@ -432,8 +507,8 @@ github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= -github.com/prometheus/common v0.59.1 h1:LXb1quJHWm1P6wq/U824uxYi4Sg0oGvNeUm1z5dJoX0= -github.com/prometheus/common v0.59.1/go.mod h1:GpWM7dewqmVYcd7SmRaiWVe9SSqjf0UrwnYnpEZNuT0= +github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc= +github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= @@ -443,8 +518,8 @@ github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 h1:EaDatTxkdHG+U3Bk4EUr+DZ7fO github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5/go.mod h1:fyalQWdtzDBECAQFBJuQe5bzQ02jGd5Qcbgb97Flm7U= github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 h1:EfpWLLCyXw8PSM2/XNJLjI3Pb27yVE+gIAfeqp8LUCc= github.com/redis/go-redis/extra/redisotel/v9 v9.0.5/go.mod h1:WZjPDy7VNzn77AAfnAfVjZNvfJTYfPetfZk5yoSTLaQ= -github.com/redis/go-redis/v9 v9.1.0 h1:137FnGdk+EQdCbye1FW+qOEcY5S+SpY9T0NiuqvtfMY= -github.com/redis/go-redis/v9 v9.1.0/go.mod h1:urWj3He21Dj5k4TK1y59xH8Uj6ATueP8AH1cY3lZl4c= +github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= +github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -499,6 +574,10 @@ github.com/testcontainers/testcontainers-go v0.35.0 h1:uADsZpTKFAtp8SLK+hMwSaa+X github.com/testcontainers/testcontainers-go v0.35.0/go.mod h1:oEVBj5zrfJTrgjwONs1SsRbnBtH9OKl+IGl3UMcr2B4= github.com/testcontainers/testcontainers-go/modules/postgres v0.35.0 h1:eEGx9kYzZb2cNhRbBrNOCL/YPOM7+RMJiy3bB+ie0/I= github.com/testcontainers/testcontainers-go/modules/postgres v0.35.0/go.mod h1:hfH71Mia/WWLBgMD2YctYcMlfsbnT0hflweL1dy8Q4s= +github.com/thejerf/slogassert v0.3.4 h1:VoTsXixRbXMrRSSxDjYTiEDCM4VWbsYPW5rB/hX24kM= +github.com/thejerf/slogassert v0.3.4/go.mod h1:0zn9ISLVKo1aPMTqcGfG1o6dWwt+Rk574GlUxHD4rs8= +github.com/thomaspoignant/go-feature-flag v1.40.0 h1:BKNPwA9C1ZI8z34YrPv0Q4AhXFX8nSWXLeDC0/RGfe0= +github.com/thomaspoignant/go-feature-flag v1.40.0/go.mod h1:/R0ehOI15+7inVQvqm8ZCcqVkOFcRtrDqrb0qxRE7HY= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= @@ -526,6 +605,10 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xitongsys/parquet-go v1.6.2 h1:MhCaXii4eqceKPu9BwrjLqyK10oX9WF+xGhwvwbw7xM= +github.com/xitongsys/parquet-go v1.6.2/go.mod h1:IulAQyalCm0rPiZVNnCgm/PCL64X2tdSVGMQ/UeKqWA= +github.com/xitongsys/parquet-go-source v0.0.0-20230830030807-0dd610dbff1d h1:VVWj8KWdzpebBaXpTVpOaQW32y2UCWy3JXJ5lVDa/e8= +github.com/xitongsys/parquet-go-source v0.0.0-20230830030807-0dd610dbff1d/go.mod h1:HaLl1OAA7RAuQURU3Enxn7aRAI9yezsPPaxiGrbzxW4= github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= @@ -535,38 +618,52 @@ github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFi github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/exporters/autoexport v0.46.1 h1:ysCfPZB9AjUlMa1UHYup3c9dAOCMQX/6sxSfPBUoxHw= -go.opentelemetry.io/contrib/exporters/autoexport v0.46.1/go.mod h1:ha0aiYm+DOPsLHjh0zoQ8W8sLT+LJ58J3j47lGpSLrU= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 h1:9G6E0TXzGFVfTnawRzrPl83iHOAV7L8NJiR8RSGYV1g= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74= +go.opentelemetry.io/contrib/bridges/prometheus v0.57.0 h1:UW0+QyeyBVhn+COBec3nGhfnFe5lwB0ic1JBVjzhk0w= +go.opentelemetry.io/contrib/bridges/prometheus v0.57.0/go.mod h1:ppciCHRLsyCio54qbzQv0E4Jyth/fLWDTJYfvWpcSVk= +go.opentelemetry.io/contrib/detectors/gcp v1.29.0 h1:TiaiXB4DpGD3sdzNlYQxruQngn5Apwzi1X0DRhuGvDQ= +go.opentelemetry.io/contrib/detectors/gcp v1.29.0/go.mod h1:GW2aWZNwR2ZxDLdv8OyC2G8zkRoQBuURgV7RPQgcPoU= +go.opentelemetry.io/contrib/exporters/autoexport v0.57.0 h1:jmTVJ86dP60C01K3slFQa2NQ/Aoi7zA+wy7vMOKD9H4= +go.opentelemetry.io/contrib/exporters/autoexport v0.57.0/go.mod h1:EJBheUMttD/lABFyLXhce47Wr6DPWYReCzaZiXadH7g= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= -go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= -go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0 h1:jd0+5t/YynESZqsSyPz+7PAFdEop0dlN0+PkyHYo8oI= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0/go.mod h1:U707O40ee1FpQGyhvqnzmCJm1Wh6OX6GGBVn0E6Uyyk= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0 h1:bflGWrfYyuulcdxf14V6n9+CoQcu5SAAdHmDPAJnlps= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0/go.mod h1:qcTO4xHAxZLaLxPd60TdE88rxtItPHgHWqOhOGRr0as= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0 h1:digkEZCJWobwBqMwC0cwCq8/wkkRy/OowZg5OArWZrM= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0/go.mod h1:/OpE/y70qVkndM0TrxT4KBoN3RsFZP0QaofcfYrj76I= -go.opentelemetry.io/otel/exporters/prometheus v0.44.0 h1:08qeJgaPC0YEBu2PQMbqU3rogTlyzpjhCI2b58Yn00w= -go.opentelemetry.io/otel/exporters/prometheus v0.44.0/go.mod h1:ERL2uIeBtg4TxZdojHUwzZfIFlUIjZtxubT5p4h1Gjg= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.44.0 h1:dEZWPjVN22urgYCza3PXRUGEyCB++y1sAqm6guWFesk= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.44.0/go.mod h1:sTt30Evb7hJB/gEk27qLb1+l9n4Tb8HvHkR0Wx3S6CU= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.21.0 h1:VhlEQAPp9R1ktYfrPk5SOryw1e9LDDTZCbIPFrho0ec= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.21.0/go.mod h1:kB3ufRbfU+CQ4MlUcqtW8Z7YEOBeK2DJ6CmR5rYYF3E= -go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc= -go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= -go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= -go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= -go.opentelemetry.io/otel/sdk/metric v1.21.0 h1:smhI5oD714d6jHE6Tie36fPx4WDFIg+Y6RfAY4ICcR0= -go.opentelemetry.io/otel/sdk/metric v1.21.0/go.mod h1:FJ8RAsoPGv/wYMgBdUJXOm+6pzFY3YdljnXtv1SBE8Q= -go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= -go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= +go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= +go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 h1:WzNab7hOOLzdDF/EoWCt4glhrbMPVMOO5JYTmpz36Ls= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0/go.mod h1:hKvJwTzJdp90Vh7p6q/9PAOd55dI6WA6sWj62a/JvSs= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0 h1:S+LdBGiQXtJdowoJoQPEtI52syEP/JYBUpjO49EQhV8= +go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0/go.mod h1:5KXybFvPGds3QinJWQT7pmXf+TN5YIa7CNYObWRkj50= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0 h1:j7ZSD+5yn+lo3sGV69nW04rRR0jhYnBwjuX3r0HvnK0= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0/go.mod h1:WXbYJTUaZXAbYd8lbgGuvih0yuCfOFC5RJoYnoLcGz8= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0 h1:t/Qur3vKSkUCcDVaSumWF2PKHt85pc7fRvFuoVT8qFU= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0/go.mod h1:Rl61tySSdcOJWoEgYZVtmnKdA0GeKrSqkHC1t+91CH8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 h1:IJFEoHiytixx8cMiVAO+GmHR6Frwu+u5Ur8njpFO6Ac= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0/go.mod h1:3rHrKNtLIoS0oZwkY2vxi+oJcwFRWdtUyRII+so45p8= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0 h1:9kV11HXBHZAvuPUZxmMWrH8hZn/6UnHX4K0mu36vNsU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0/go.mod h1:JyA0FHXe22E1NeNiHmVp7kFHglnexDQ7uRWDiiJ1hKQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0 h1:cMyu9O88joYEaI47CnQkxO1XZdpoTF9fEnW2duIddhw= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0/go.mod h1:6Am3rn7P9TVVeXYG+wtcGE7IE1tsQ+bP3AuWcKt/gOI= +go.opentelemetry.io/otel/exporters/prometheus v0.54.0 h1:rFwzp68QMgtzu9PgP3jm9XaMICI6TsofWWPcBDKwlsU= +go.opentelemetry.io/otel/exporters/prometheus v0.54.0/go.mod h1:QyjcV9qDP6VeK5qPyKETvNjmaaEc7+gqjh4SS0ZYzDU= +go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.8.0 h1:CHXNXwfKWfzS65yrlB2PVds1IBZcdsX8Vepy9of0iRU= +go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.8.0/go.mod h1:zKU4zUgKiaRxrdovSS2amdM5gOc59slmo/zJwGX+YBg= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.32.0 h1:SZmDnHcgp3zwlPBS2JX2urGYe/jBKEIT6ZedHRUyCz8= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.32.0/go.mod h1:fdWW0HtZJ7+jNpTKUR0GpMEDP69nR8YBJQxNiVCE3jk= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.32.0 h1:cC2yDI3IQd0Udsux7Qmq8ToKAx1XCilTQECZ0KDZyTw= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.32.0/go.mod h1:2PD5Ex6z8CFzDbTdOlwyNIUywRr1DN0ospafJM1wJ+s= +go.opentelemetry.io/otel/log v0.8.0 h1:egZ8vV5atrUWUbnSsHn6vB8R21G2wrKqNiDt3iWertk= +go.opentelemetry.io/otel/log v0.8.0/go.mod h1:M9qvDdUTRCopJcGRKg57+JSQ9LgLBrwwfC32epk5NX8= +go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= +go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= +go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= +go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= +go.opentelemetry.io/otel/sdk/log v0.8.0 h1:zg7GUYXqxk1jnGF/dTdLPrK06xJdrXgqgFLnI4Crxvs= +go.opentelemetry.io/otel/sdk/log v0.8.0/go.mod h1:50iXr0UVwQrYS45KbruFrEt4LvAdCaWWgIrsN3ZQggo= +go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= +go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= +go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= +go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.starlark.net v0.0.0-20240725214946-42030a7cedce h1:YyGqCjZtGZJ+mRPaenEiB87afEO2MFRzLiJNZ0Z0bPw= @@ -621,8 +718,8 @@ golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= -golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= -golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= +golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -692,26 +789,32 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= +golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/api v0.195.0 h1:Ude4N8FvTKnnQJHU48RFI40jOBgIrL8Zqr3/QeST6yU= -google.golang.org/api v0.195.0/go.mod h1:DOGRWuv3P8TU8Lnz7uQc4hyNqrBpMtD9ppW3wBJurgc= +google.golang.org/api v0.210.0 h1:HMNffZ57OoZCRYSbdWVRoqOa8V8NIHLL0CzdBPLztWk= +google.golang.org/api v0.210.0/go.mod h1:B9XDZGnx2NtyjzVkOVTGrFSAVZgPcbedzKg/gTLwqBs= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed h1:3RgNmBoI9MZhsj3QxC+AP/qQhNwpCLOvYDYYsFrhFt0= -google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed h1:J6izYgfBXAI3xTKLgxzTmUltdYaLsuBxFCgDHWJ/eXg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk= +google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc= +google.golang.org/genproto/googleapis/api v0.0.0-20241113202542-65e8d215514f h1:M65LEviCfuZTfrfzwwEoxVtgvfkFkBUbFnRbxCXuXhU= +google.golang.org/genproto/googleapis/api v0.0.0-20241113202542-65e8d215514f/go.mod h1:Yo94eF2nj7igQt+TiJ49KxjIH8ndLYPZMIRSiRcEbg0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697 h1:LWZqQOEjDyONlF1H6afSWpAL/znlREo2tHfLoe+8LMA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc v1.66.3 h1:TWlsh8Mv0QI/1sIbs1W36lqRclxrmF+eFJ4DbI0fuhA= -google.golang.org/grpc v1.66.3/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= +google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0= +google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= +google.golang.org/grpc/stats/opentelemetry v0.0.0-20240907200651-3ffb98b2c93a h1:UIpYSuWdWHSzjwcAFRLjKcPXFZVVLXGEM23W+NWqipw= +google.golang.org/grpc/stats/opentelemetry v0.0.0-20240907200651-3ffb98b2c93a/go.mod h1:9i1T9n4ZinTUZGgzENMi8MDDgbGC5mqTS75JAv6xN3A= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/pkg/features/features.go b/pkg/features/features.go new file mode 100644 index 000000000..81786c0bd --- /dev/null +++ b/pkg/features/features.go @@ -0,0 +1,53 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors +// SPDX-License-Identifier: Apache-2.0 + +package features + +import ( + "context" + "log" + "time" + + ofinprocess "github.com/open-feature/go-sdk-contrib/providers/go-feature-flag-in-process/pkg" + "github.com/open-feature/go-sdk/openfeature" + goffclient "github.com/thomaspoignant/go-feature-flag" + "github.com/thomaspoignant/go-feature-flag/retriever/k8sretriever" + "k8s.io/client-go/rest" + + "github.com/cloudoperators/greenhouse/pkg/clientutil" +) + +func GetOFClient(appName string) *openfeature.Client { + k8sInClusterConfig, err := rest.InClusterConfig() + if err != nil { + log.Fatalf("Failed to create in-cluster config: %s", err) + } + + goFeatureFlagConfig := &goffclient.Config{ + PollingInterval: 30 * time.Second, + Context: context.Background(), + Retriever: &k8sretriever.Retriever{ + Namespace: clientutil.GetEnvOrDefault("FEATURE_FLAG_NAMESPACE", "greenhouse"), + ConfigMapName: clientutil.GetEnvOrDefault("FEATURE_FLAG_CONFIG_MAP_NAME", "greenhouse-feature-flags"), + Key: clientutil.GetEnvOrDefault("FEATURE_FLAG_CONFIG_MAP_KEY", "config.yaml"), + ClientConfig: *k8sInClusterConfig, + }, + } + + options := ofinprocess.ProviderOptions{ + GOFeatureFlagConfig: goFeatureFlagConfig, + } + + provider, err := ofinprocess.NewProviderWithContext(context.Background(), options) + if err != nil { + log.Fatalf("Failed to create provider: %s", err) + } + if err = openfeature.SetNamedProvider(appName, provider); err != nil { + log.Fatalf("Failed to set provider: %s", err) + } + if err = goffclient.Init(*goFeatureFlagConfig); err != nil { + log.Fatalf("Failed to init provider: %s", err) + } + + return openfeature.NewClient(appName) +} From 5356ec92529e8f5fd3987485633491f307fbf95d Mon Sep 17 00:00:00 2001 From: David Gogl <1381862+kengou@users.noreply.github.com> Date: Wed, 29 Jan 2025 14:43:17 +0100 Subject: [PATCH 021/108] lint licenserc.yaml --- .github/licenserc.yaml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/licenserc.yaml b/.github/licenserc.yaml index 83f783d98..e1868cce2 100644 --- a/.github/licenserc.yaml +++ b/.github/licenserc.yaml @@ -42,7 +42,6 @@ header: - '.mockery.yaml' # Mockery config file - 'pkg/mocks/**' # Mockery generated files - comment: on-failure # license-location-threshold specifies the index threshold where the license header can be located, From 74769139e21c0d971151210773dadd82d57324b3 Mon Sep 17 00:00:00 2001 From: David Gogl <1381862+kengou@users.noreply.github.com> Date: Thu, 30 Jan 2025 11:31:51 +0100 Subject: [PATCH 022/108] fix(dex) only create when ErrNotFound is the error during fetch --- pkg/controllers/organization/dex.go | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/pkg/controllers/organization/dex.go b/pkg/controllers/organization/dex.go index e9db885a7..d7b877f4b 100644 --- a/pkg/controllers/organization/dex.go +++ b/pkg/controllers/organization/dex.go @@ -92,7 +92,7 @@ func (r *OrganizationReconciler) reconcileDexConnector(ctx context.Context, org // Create the connectors also in SQL storage var oidcConnector storage.Connector - if oidcConnector, err = r.dexStorage.GetConnector(org.Name); err != nil { + if oidcConnector, err = r.dexStorage.GetConnector(org.Name); err != nil && errors.Is(err, storage.ErrNotFound) { if err = r.dexStorage.CreateConnector(ctx, storage.Connector{ ID: org.Name, Type: dexConnectorTypeGreenhouse, @@ -103,6 +103,9 @@ func (r *OrganizationReconciler) reconcileDexConnector(ctx context.Context, org } log.FromContext(ctx).Info("created dex connector in SQL storage", "name", org.Name) } + if err != nil { + log.FromContext(ctx).Error(err, "failed to get dex connector in SQL storage", "name", org.Name) + } if err = r.dexStorage.UpdateConnector(oidcConnector.ID, func(c storage.Connector) (storage.Connector, error) { c.ID = org.Name c.Type = dexConnectorTypeGreenhouse @@ -146,7 +149,7 @@ func (r *OrganizationReconciler) discoverOIDCRedirectURL(ctx context.Context, or func (r *OrganizationReconciler) reconcileOAuth2Client(ctx context.Context, org *greenhousesapv1alpha1.Organization) error { var oAuthClient storage.Client var err error - if oAuthClient, err = r.dexStorage.GetClient(org.Name); err != nil { + if oAuthClient, err = r.dexStorage.GetClient(org.Name); err != nil && errors.Is(err, storage.ErrNotFound) { if err = r.dexStorage.CreateClient(ctx, storage.Client{ Public: true, ID: org.Name, @@ -161,6 +164,9 @@ func (r *OrganizationReconciler) reconcileOAuth2Client(ctx context.Context, org log.FromContext(ctx).Info("created oauth2client", "name", org.Name) return nil } + if err != nil { + log.FromContext(ctx).Error(err, "failed to get oauth2client", "name", org.Name) + } if err = r.dexStorage.UpdateClient(oAuthClient.Name, func(authClient storage.Client) (storage.Client, error) { authClient.Public = true From 42393b6523a2a77fd4987e959d4c8c0023cea95f Mon Sep 17 00:00:00 2001 From: David Gogl <1381862+kengou@users.noreply.github.com> Date: Thu, 30 Jan 2025 11:32:25 +0100 Subject: [PATCH 023/108] fix(feature) increase PollingInterval --- pkg/features/features.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/features/features.go b/pkg/features/features.go index 81786c0bd..2881f5153 100644 --- a/pkg/features/features.go +++ b/pkg/features/features.go @@ -24,7 +24,7 @@ func GetOFClient(appName string) *openfeature.Client { } goFeatureFlagConfig := &goffclient.Config{ - PollingInterval: 30 * time.Second, + PollingInterval: 30 * time.Minute, Context: context.Background(), Retriever: &k8sretriever.Retriever{ Namespace: clientutil.GetEnvOrDefault("FEATURE_FLAG_NAMESPACE", "greenhouse"), From 4baeee0906ce60627a45e9e085b92cbd28c517fa Mon Sep 17 00:00:00 2001 From: David Gogl <1381862+kengou@users.noreply.github.com> Date: Thu, 30 Jan 2025 11:35:23 +0100 Subject: [PATCH 024/108] fix(idproxy) add comments to the code --- cmd/idproxy/main.go | 6 ++++-- pkg/features/features.go | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cmd/idproxy/main.go b/cmd/idproxy/main.go index 8942d46bd..c79524741 100644 --- a/cmd/idproxy/main.go +++ b/cmd/idproxy/main.go @@ -73,11 +73,13 @@ func main() { log.Fatal("No --issuer given") } - ofClient := features.GetOFClient("idproxy") + // Initialize open-feature client + ofClient := features.NewOfClient("idproxy") evalCtx := openfeature.NewEvaluationContext( "backend", map[string]interface{}{}, ) + // Get the value of dex-sql-backend and dex-kubernetes-backend postgresBackend, err := ofClient.BooleanValue(context.TODO(), "dex-sql-backend", false, evalCtx) if err != nil { log.Fatalf("Failed to get dex-sql-backend value details: %s", err) @@ -87,7 +89,7 @@ func main() { if err != nil { log.Fatalf("Failed to get dex-kubernetes-backend value details: %s", err) } - + // Exactly one of dex-sql-backend or dex-kubernetes-backend must be true switch { case postgresBackend: dexStorage, err = idproxy.NewPostgresStorage(sql.SSL{Mode: "disable"}, postgresDB, logger.With("component", "storage")) diff --git a/pkg/features/features.go b/pkg/features/features.go index 2881f5153..d1b62af99 100644 --- a/pkg/features/features.go +++ b/pkg/features/features.go @@ -17,7 +17,8 @@ import ( "github.com/cloudoperators/greenhouse/pkg/clientutil" ) -func GetOFClient(appName string) *openfeature.Client { +// NewOfClient returns a open-feature client loading feature flags from a ConfigMap +func NewOfClient(appName string) *openfeature.Client { k8sInClusterConfig, err := rest.InClusterConfig() if err != nil { log.Fatalf("Failed to create in-cluster config: %s", err) From 4af3b6fecab9456965406574cb8f39181977f6f3 Mon Sep 17 00:00:00 2001 From: License Bot Date: Thu, 30 Jan 2025 10:41:12 +0000 Subject: [PATCH 025/108] Automatic application of license header --- charts/manager/etc/feature_flags.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/charts/manager/etc/feature_flags.yaml b/charts/manager/etc/feature_flags.yaml index 2d0d58f0b..519c04d65 100644 --- a/charts/manager/etc/feature_flags.yaml +++ b/charts/manager/etc/feature_flags.yaml @@ -1,3 +1,6 @@ +# SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors +# SPDX-License-Identifier: Apache-2.0 + dex-sql-backend: variations: true: true From 69449bb998b1144f2a4d1663fd1f15d588b8fba1 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Fri, 31 Jan 2025 15:32:57 +0100 Subject: [PATCH 026/108] (chore): move to dex pkg --- pkg/{idproxy => dex}/connector.go | 2 +- pkg/{idproxy => dex}/connector_test.go | 2 +- pkg/{idproxy => dex}/web/robots.txt | 0 .../web/static/img/atlassian-crowd-icon.svg | 0 .../web/static/img/bitbucket-icon.svg | 0 .../web/static/img/email-icon.svg | 0 .../web/static/img/gitea-icon.svg | 0 .../web/static/img/github-icon.svg | 0 .../web/static/img/gitlab-icon.svg | 0 .../web/static/img/google-icon.svg | 0 .../web/static/img/keystone-icon.svg | 0 .../web/static/img/ldap-icon.svg | 0 .../web/static/img/linkedin-icon.svg | 0 .../web/static/img/microsoft-icon.svg | 0 .../web/static/img/oidc-icon.svg | 0 .../web/static/img/saml-icon.svg | 0 pkg/{idproxy => dex}/web/static/main.css | 26 +++++++++--------- .../web/templates/approval.html | 0 .../web/templates/device.html | 0 .../web/templates/device_success.html | 0 pkg/{idproxy => dex}/web/templates/error.html | 0 .../web/templates/footer.html | 0 .../web/templates/header.html | 0 pkg/{idproxy => dex}/web/templates/login.html | 0 pkg/{idproxy => dex}/web/templates/oob.html | 0 .../web/templates/password.html | 0 .../web/themes/dark/favicon.png | Bin pkg/{idproxy => dex}/web/themes/dark/logo.png | Bin .../web/themes/dark/styles.css | 0 .../web/themes/light/favicon.png | Bin .../web/themes/light/logo.png | Bin .../web/themes/light/styles.css | 0 pkg/{idproxy => dex}/web/web.go | 0 33 files changed, 15 insertions(+), 15 deletions(-) rename pkg/{idproxy => dex}/connector.go (99%) rename pkg/{idproxy => dex}/connector_test.go (99%) rename pkg/{idproxy => dex}/web/robots.txt (100%) rename pkg/{idproxy => dex}/web/static/img/atlassian-crowd-icon.svg (100%) rename pkg/{idproxy => dex}/web/static/img/bitbucket-icon.svg (100%) rename pkg/{idproxy => dex}/web/static/img/email-icon.svg (100%) rename pkg/{idproxy => dex}/web/static/img/gitea-icon.svg (100%) rename pkg/{idproxy => dex}/web/static/img/github-icon.svg (100%) rename pkg/{idproxy => dex}/web/static/img/gitlab-icon.svg (100%) rename pkg/{idproxy => dex}/web/static/img/google-icon.svg (100%) rename pkg/{idproxy => dex}/web/static/img/keystone-icon.svg (100%) rename pkg/{idproxy => dex}/web/static/img/ldap-icon.svg (100%) rename pkg/{idproxy => dex}/web/static/img/linkedin-icon.svg (100%) rename pkg/{idproxy => dex}/web/static/img/microsoft-icon.svg (100%) rename pkg/{idproxy => dex}/web/static/img/oidc-icon.svg (100%) rename pkg/{idproxy => dex}/web/static/img/saml-icon.svg (100%) rename pkg/{idproxy => dex}/web/static/main.css (73%) rename pkg/{idproxy => dex}/web/templates/approval.html (100%) rename pkg/{idproxy => dex}/web/templates/device.html (100%) rename pkg/{idproxy => dex}/web/templates/device_success.html (100%) rename pkg/{idproxy => dex}/web/templates/error.html (100%) rename pkg/{idproxy => dex}/web/templates/footer.html (100%) rename pkg/{idproxy => dex}/web/templates/header.html (100%) rename pkg/{idproxy => dex}/web/templates/login.html (100%) rename pkg/{idproxy => dex}/web/templates/oob.html (100%) rename pkg/{idproxy => dex}/web/templates/password.html (100%) rename pkg/{idproxy => dex}/web/themes/dark/favicon.png (100%) rename pkg/{idproxy => dex}/web/themes/dark/logo.png (100%) rename pkg/{idproxy => dex}/web/themes/dark/styles.css (100%) rename pkg/{idproxy => dex}/web/themes/light/favicon.png (100%) rename pkg/{idproxy => dex}/web/themes/light/logo.png (100%) rename pkg/{idproxy => dex}/web/themes/light/styles.css (100%) rename pkg/{idproxy => dex}/web/web.go (100%) diff --git a/pkg/idproxy/connector.go b/pkg/dex/connector.go similarity index 99% rename from pkg/idproxy/connector.go rename to pkg/dex/connector.go index 6a99857ea..c342cad0e 100644 --- a/pkg/idproxy/connector.go +++ b/pkg/dex/connector.go @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors // SPDX-License-Identifier: Apache-2.0 -package idproxy +package dex import ( "context" diff --git a/pkg/idproxy/connector_test.go b/pkg/dex/connector_test.go similarity index 99% rename from pkg/idproxy/connector_test.go rename to pkg/dex/connector_test.go index 19e88a336..1a6533dcb 100644 --- a/pkg/idproxy/connector_test.go +++ b/pkg/dex/connector_test.go @@ -1,7 +1,7 @@ // SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors // SPDX-License-Identifier: Apache-2.0 -package idproxy +package dex import ( "context" diff --git a/pkg/idproxy/web/robots.txt b/pkg/dex/web/robots.txt similarity index 100% rename from pkg/idproxy/web/robots.txt rename to pkg/dex/web/robots.txt diff --git a/pkg/idproxy/web/static/img/atlassian-crowd-icon.svg b/pkg/dex/web/static/img/atlassian-crowd-icon.svg similarity index 100% rename from pkg/idproxy/web/static/img/atlassian-crowd-icon.svg rename to pkg/dex/web/static/img/atlassian-crowd-icon.svg diff --git a/pkg/idproxy/web/static/img/bitbucket-icon.svg b/pkg/dex/web/static/img/bitbucket-icon.svg similarity index 100% rename from pkg/idproxy/web/static/img/bitbucket-icon.svg rename to pkg/dex/web/static/img/bitbucket-icon.svg diff --git a/pkg/idproxy/web/static/img/email-icon.svg b/pkg/dex/web/static/img/email-icon.svg similarity index 100% rename from pkg/idproxy/web/static/img/email-icon.svg rename to pkg/dex/web/static/img/email-icon.svg diff --git a/pkg/idproxy/web/static/img/gitea-icon.svg b/pkg/dex/web/static/img/gitea-icon.svg similarity index 100% rename from pkg/idproxy/web/static/img/gitea-icon.svg rename to pkg/dex/web/static/img/gitea-icon.svg diff --git a/pkg/idproxy/web/static/img/github-icon.svg b/pkg/dex/web/static/img/github-icon.svg similarity index 100% rename from pkg/idproxy/web/static/img/github-icon.svg rename to pkg/dex/web/static/img/github-icon.svg diff --git a/pkg/idproxy/web/static/img/gitlab-icon.svg b/pkg/dex/web/static/img/gitlab-icon.svg similarity index 100% rename from pkg/idproxy/web/static/img/gitlab-icon.svg rename to pkg/dex/web/static/img/gitlab-icon.svg diff --git a/pkg/idproxy/web/static/img/google-icon.svg b/pkg/dex/web/static/img/google-icon.svg similarity index 100% rename from pkg/idproxy/web/static/img/google-icon.svg rename to pkg/dex/web/static/img/google-icon.svg diff --git a/pkg/idproxy/web/static/img/keystone-icon.svg b/pkg/dex/web/static/img/keystone-icon.svg similarity index 100% rename from pkg/idproxy/web/static/img/keystone-icon.svg rename to pkg/dex/web/static/img/keystone-icon.svg diff --git a/pkg/idproxy/web/static/img/ldap-icon.svg b/pkg/dex/web/static/img/ldap-icon.svg similarity index 100% rename from pkg/idproxy/web/static/img/ldap-icon.svg rename to pkg/dex/web/static/img/ldap-icon.svg diff --git a/pkg/idproxy/web/static/img/linkedin-icon.svg b/pkg/dex/web/static/img/linkedin-icon.svg similarity index 100% rename from pkg/idproxy/web/static/img/linkedin-icon.svg rename to pkg/dex/web/static/img/linkedin-icon.svg diff --git a/pkg/idproxy/web/static/img/microsoft-icon.svg b/pkg/dex/web/static/img/microsoft-icon.svg similarity index 100% rename from pkg/idproxy/web/static/img/microsoft-icon.svg rename to pkg/dex/web/static/img/microsoft-icon.svg diff --git a/pkg/idproxy/web/static/img/oidc-icon.svg b/pkg/dex/web/static/img/oidc-icon.svg similarity index 100% rename from pkg/idproxy/web/static/img/oidc-icon.svg rename to pkg/dex/web/static/img/oidc-icon.svg diff --git a/pkg/idproxy/web/static/img/saml-icon.svg b/pkg/dex/web/static/img/saml-icon.svg similarity index 100% rename from pkg/idproxy/web/static/img/saml-icon.svg rename to pkg/dex/web/static/img/saml-icon.svg diff --git a/pkg/idproxy/web/static/main.css b/pkg/dex/web/static/main.css similarity index 73% rename from pkg/idproxy/web/static/main.css rename to pkg/dex/web/static/main.css index 7ab0316fd..cf3a42448 100644 --- a/pkg/idproxy/web/static/main.css +++ b/pkg/dex/web/static/main.css @@ -45,69 +45,69 @@ body { .dex-btn-icon--google { background-color: #FFFFFF; - background-image: url(../static/img/google-icon.svg);; + background-image: url(img/google-icon.svg);; } .dex-btn-icon--local { background-color: #84B6EF; - background-image: url(../static/img/email-icon.svg); + background-image: url(img/email-icon.svg); } .dex-btn-icon--gitea { background-color: #F5F5F5; - background-image: url(../static/img/gitea-icon.svg); + background-image: url(img/gitea-icon.svg); } .dex-btn-icon--github { background-color: #F5F5F5; - background-image: url(../static/img/github-icon.svg); + background-image: url(img/github-icon.svg); } .dex-btn-icon--gitlab { background-color: #F5F5F5; - background-image: url(../static/img/gitlab-icon.svg); + background-image: url(img/gitlab-icon.svg); background-size: contain; } .dex-btn-icon--keystone { background-color: #F5F5F5; - background-image: url(../static/img/keystone-icon.svg); + background-image: url(img/keystone-icon.svg); background-size: contain; } .dex-btn-icon--oidc { background-color: #EBEBEE; - background-image: url(../static/img/oidc-icon.svg); + background-image: url(img/oidc-icon.svg); background-size: contain; } .dex-btn-icon--bitbucket-cloud { background-color: #205081; - background-image: url(../static/img/bitbucket-icon.svg); + background-image: url(img/bitbucket-icon.svg); } .dex-btn-icon--atlassian-crowd { background-color: #CFDCEA; - background-image: url(../static/img/atlassian-crowd-icon.svg); + background-image: url(img/atlassian-crowd-icon.svg); } .dex-btn-icon--ldap { background-color: #84B6EF; - background-image: url(../static/img/ldap-icon.svg); + background-image: url(img/ldap-icon.svg); } .dex-btn-icon--saml { background-color: #84B6EF; - background-image: url(../static/img/saml-icon.svg); + background-image: url(img/saml-icon.svg); } .dex-btn-icon--linkedin { - background-image: url(../static/img/linkedin-icon.svg); + background-image: url(img/linkedin-icon.svg); background-size: contain; } .dex-btn-icon--microsoft { - background-image: url(../static/img/microsoft-icon.svg); + background-image: url(img/microsoft-icon.svg); } .dex-btn-text { diff --git a/pkg/idproxy/web/templates/approval.html b/pkg/dex/web/templates/approval.html similarity index 100% rename from pkg/idproxy/web/templates/approval.html rename to pkg/dex/web/templates/approval.html diff --git a/pkg/idproxy/web/templates/device.html b/pkg/dex/web/templates/device.html similarity index 100% rename from pkg/idproxy/web/templates/device.html rename to pkg/dex/web/templates/device.html diff --git a/pkg/idproxy/web/templates/device_success.html b/pkg/dex/web/templates/device_success.html similarity index 100% rename from pkg/idproxy/web/templates/device_success.html rename to pkg/dex/web/templates/device_success.html diff --git a/pkg/idproxy/web/templates/error.html b/pkg/dex/web/templates/error.html similarity index 100% rename from pkg/idproxy/web/templates/error.html rename to pkg/dex/web/templates/error.html diff --git a/pkg/idproxy/web/templates/footer.html b/pkg/dex/web/templates/footer.html similarity index 100% rename from pkg/idproxy/web/templates/footer.html rename to pkg/dex/web/templates/footer.html diff --git a/pkg/idproxy/web/templates/header.html b/pkg/dex/web/templates/header.html similarity index 100% rename from pkg/idproxy/web/templates/header.html rename to pkg/dex/web/templates/header.html diff --git a/pkg/idproxy/web/templates/login.html b/pkg/dex/web/templates/login.html similarity index 100% rename from pkg/idproxy/web/templates/login.html rename to pkg/dex/web/templates/login.html diff --git a/pkg/idproxy/web/templates/oob.html b/pkg/dex/web/templates/oob.html similarity index 100% rename from pkg/idproxy/web/templates/oob.html rename to pkg/dex/web/templates/oob.html diff --git a/pkg/idproxy/web/templates/password.html b/pkg/dex/web/templates/password.html similarity index 100% rename from pkg/idproxy/web/templates/password.html rename to pkg/dex/web/templates/password.html diff --git a/pkg/idproxy/web/themes/dark/favicon.png b/pkg/dex/web/themes/dark/favicon.png similarity index 100% rename from pkg/idproxy/web/themes/dark/favicon.png rename to pkg/dex/web/themes/dark/favicon.png diff --git a/pkg/idproxy/web/themes/dark/logo.png b/pkg/dex/web/themes/dark/logo.png similarity index 100% rename from pkg/idproxy/web/themes/dark/logo.png rename to pkg/dex/web/themes/dark/logo.png diff --git a/pkg/idproxy/web/themes/dark/styles.css b/pkg/dex/web/themes/dark/styles.css similarity index 100% rename from pkg/idproxy/web/themes/dark/styles.css rename to pkg/dex/web/themes/dark/styles.css diff --git a/pkg/idproxy/web/themes/light/favicon.png b/pkg/dex/web/themes/light/favicon.png similarity index 100% rename from pkg/idproxy/web/themes/light/favicon.png rename to pkg/dex/web/themes/light/favicon.png diff --git a/pkg/idproxy/web/themes/light/logo.png b/pkg/dex/web/themes/light/logo.png similarity index 100% rename from pkg/idproxy/web/themes/light/logo.png rename to pkg/dex/web/themes/light/logo.png diff --git a/pkg/idproxy/web/themes/light/styles.css b/pkg/dex/web/themes/light/styles.css similarity index 100% rename from pkg/idproxy/web/themes/light/styles.css rename to pkg/dex/web/themes/light/styles.css diff --git a/pkg/idproxy/web/web.go b/pkg/dex/web/web.go similarity index 100% rename from pkg/idproxy/web/web.go rename to pkg/dex/web/web.go From 3e496e7e94986caf8ac88f78482da77330a66ded Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Fri, 31 Jan 2025 15:33:34 +0100 Subject: [PATCH 027/108] (chore): adds GetEnv with error return --- pkg/clientutil/env.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pkg/clientutil/env.go b/pkg/clientutil/env.go index fa6017f06..18d18bafe 100644 --- a/pkg/clientutil/env.go +++ b/pkg/clientutil/env.go @@ -4,6 +4,7 @@ package clientutil import ( + "errors" "os" "strconv" ) @@ -24,3 +25,10 @@ func GetIntEnvWithDefault(envKey string, def int) int { } return i } + +func GetEnv(envKey string) (string, error) { + if v, ok := os.LookupEnv(envKey); ok { + return v, nil + } + return "", errors.New("environment variable not set") +} From 6f786dff79b5a0cfac14fecf081874b021501426 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Fri, 31 Jan 2025 15:34:10 +0100 Subject: [PATCH 028/108] (chore): adds feature resolver --- pkg/features/features.go | 82 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 82 insertions(+) create mode 100644 pkg/features/features.go diff --git a/pkg/features/features.go b/pkg/features/features.go new file mode 100644 index 000000000..f9fab4a29 --- /dev/null +++ b/pkg/features/features.go @@ -0,0 +1,82 @@ +package features + +import ( + "context" + "errors" + "sync" + + "gopkg.in/yaml.v3" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/cloudoperators/greenhouse/pkg/clientutil" +) + +const ( + DexFeatureKey = "dex" + featureConfigMapName = "greenhouse-feature-flags" + featureConfigMapNamespace = "greenhouse" +) + +type features struct { + m sync.Mutex + k8sClient client.Client + raw map[string]string + dex *dexFeatures `yaml:"dex"` +} + +type dexFeatures struct { + Storage string `yaml:"storage"` +} + +type Features interface { + GetDexStorageType(ctx context.Context) *string +} + +func NewFeatures(ctx context.Context, k8sClient client.Client) (Features, error) { + featureMap := &corev1.ConfigMap{} + if err := k8sClient.Get(ctx, types.NamespacedName{Name: featureConfigMapName, Namespace: featureConfigMapNamespace}, featureMap); err != nil { + return nil, err + } + return &features{ + k8sClient: k8sClient, + raw: featureMap.Data, + }, nil +} + +func (f *features) resolveDexFeatures() error { + f.m.Lock() + defer f.m.Unlock() + + // Extract the `dex` key from the ConfigMap + dexRaw, exists := f.raw[DexFeatureKey] + if !exists { + return errors.New("dex feature not found in ConfigMap") + } + + // Unmarshal the `dex` YAML string into the struct + dex := &dexFeatures{} + err := yaml.Unmarshal([]byte(dexRaw), dex) + if err != nil { + return err + } + + f.dex = dex + return nil +} + +func (f *features) GetDexStorageType(ctx context.Context) *string { + if f.dex != nil { + return clientutil.Ptr(f.dex.Storage) + } + if err := f.resolveDexFeatures(); err != nil { + ctrl.LoggerFrom(ctx).Error(err, "failed to resolve dex features") + return clientutil.Ptr("") + } + if f.dex.Storage == "" { + return nil + } + return clientutil.Ptr(f.dex.Storage) +} From c9aa70d0ca5202af84b89df23eda18ebec376754 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Fri, 31 Jan 2025 15:34:35 +0100 Subject: [PATCH 029/108] (chore): adds generic helper to return ptr types --- pkg/clientutil/util.go | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/pkg/clientutil/util.go b/pkg/clientutil/util.go index 94c411edf..6b07f5b32 100644 --- a/pkg/clientutil/util.go +++ b/pkg/clientutil/util.go @@ -132,3 +132,9 @@ func ParseDateTime(t time.Time) (time.Time, error) { layout := t.Format(time.DateTime) return time.Parse(time.DateTime, layout) } + +// Ptr - is a helper func that allocates a new T value +// to store v and returns a pointer to it. +func Ptr[T any](v T) *T { + return &v +} From 366832e3d1d8551a0c12bb4693a64d4b52570354 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Fri, 31 Jan 2025 15:35:17 +0100 Subject: [PATCH 030/108] (chore): adds dex storage resolver to idproxy --- cmd/idproxy/main.go | 50 +++++++++++++++++++++------------------------ go.mod | 3 +++ 2 files changed, 26 insertions(+), 27 deletions(-) diff --git a/cmd/idproxy/main.go b/cmd/idproxy/main.go index bb22b9df0..83a90ba8a 100644 --- a/cmd/idproxy/main.go +++ b/cmd/idproxy/main.go @@ -22,19 +22,18 @@ import ( "github.com/prometheus/client_golang/prometheus/collectors" "github.com/prometheus/client_golang/prometheus/promhttp" flag "github.com/spf13/pflag" - "k8s.io/apimachinery/pkg/runtime" _ "k8s.io/client-go/plugin/pkg/client/auth/oidc" - "sigs.k8s.io/controller-runtime/pkg/client" ctrl "sigs.k8s.io/controller-runtime/pkg/client/config" logk "sigs.k8s.io/controller-runtime/pkg/log" - greenhousesapv1alpha1 "github.com/cloudoperators/greenhouse/pkg/apis/greenhouse/v1alpha1" - "github.com/cloudoperators/greenhouse/pkg/idproxy" - "github.com/cloudoperators/greenhouse/pkg/idproxy/web" + "github.com/cloudoperators/greenhouse/pkg/clientutil" + "github.com/cloudoperators/greenhouse/pkg/dex" + dexstore "github.com/cloudoperators/greenhouse/pkg/dex/store" + "github.com/cloudoperators/greenhouse/pkg/dex/web" + "github.com/cloudoperators/greenhouse/pkg/features" ) func main() { - var kubeconfig, kubecontext, kubenamespace string var issuer string var idTokenValidity time.Duration var listenAddr, metricsAddr string @@ -45,9 +44,6 @@ func main() { // set default deferred logger to be used by controller-runtime logk.SetLogger(logr.FromSlogHandler(logger.Handler())) - flag.StringVar(&kubeconfig, "kubeconfig", os.Getenv("KUBECONFIG"), "Use kubeconfig for authentication") - flag.StringVar(&kubecontext, "kubecontext", os.Getenv("KUBECONTEXT"), "Use context from kubeconfig") - flag.StringVar(&kubenamespace, "kubenamespace", os.Getenv("KUBENAMESPACE"), "Use namespace") flag.StringVar(&issuer, "issuer", "", "Issuer URL") flag.StringVar(&listenAddr, "listen-addr", ":8080", "oidc listen address") flag.StringVar(&metricsAddr, "metrics-addr", ":6543", "bind address for metrics") @@ -59,10 +55,25 @@ func main() { log.Fatal("No --issuer given") } - dexStorage, err := idproxy.NewKubernetesStorage(kubeconfig, kubecontext, kubenamespace, logger.With("component", "storage")) + restCfg := ctrl.GetConfigOrDie() + ctx := context.Background() + k8sClient, err := clientutil.NewK8sClient(restCfg) if err != nil { - log.Fatalf("Failed to initialize kubernetes storage: %s", err) + log.Fatalf("failed to create k8s client: %s", err) } + ghFeatures, err := features.NewFeatures(ctx, k8sClient) + if err != nil { + log.Fatalf("failed to get greenhouse features: %s", err) + } + backend := ghFeatures.GetDexStorageType(ctx) + if backend == nil { + log.Fatalf("failed to get dex storage type") + } + dexter, err := dexstore.NewDexStorageFactory(nil, logger, *backend) + if err != nil { + log.Fatalf("failed to create dex storage interface: %s", err) + } + dexStorage := dexter.GetStorage() refreshPolicy, err := server.NewRefreshTokenPolicy(logger.With("component", "refreshtokenpolicy"), true, "24h", "24h", "5s") if err != nil { @@ -88,22 +99,7 @@ func main() { } server.ConnectorsConfig["greenhouse-oidc"] = func() server.ConnectorConfig { - k8sConfig, err := ctrl.GetConfigWithContext(kubecontext) - if err != nil { - log.Fatalf(`Failed to create k8s config: %s`, err) - } - - scheme := runtime.NewScheme() - err = greenhousesapv1alpha1.AddToScheme(scheme) - if err != nil { - log.Fatalf(`Failed to create scheme: %s`, err) - } - k8sClient, err := client.New(k8sConfig, client.Options{Scheme: scheme}) - if err != nil { - log.Fatalf(`Failed to create k8s client: %s`, err) - } - - oidcConfig := new(idproxy.OIDCConfig) + oidcConfig := new(dex.OIDCConfig) oidcConfig.AddClient(k8sClient) oidcConfig.AddRedirectURI(issuer + "/callback") diff --git a/go.mod b/go.mod index 09486aaf2..89d80b31e 100644 --- a/go.mod +++ b/go.mod @@ -55,6 +55,7 @@ require ( cloud.google.com/go/auth v0.9.2 // indirect cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect dario.cat/mergo v1.0.1 // indirect + filippo.io/edwards25519 v1.1.0 // indirect github.com/Microsoft/hcsshim v0.12.6 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/containerd/errdefs v0.3.0 // indirect @@ -65,11 +66,13 @@ require ( github.com/distribution/reference v0.6.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/go-sql-driver/mysql v1.8.1 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect + github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/miekg/dns v1.1.58 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/otiai10/mint v1.6.3 // indirect From df1b96a78b8e7bdc85e311700d67b0592625fb84 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Sat, 1 Feb 2025 00:21:27 +0100 Subject: [PATCH 031/108] (chore): adds license headers --- pkg/dex/web/static/img/atlassian-crowd-icon.svg | 5 +++++ pkg/dex/web/static/img/bitbucket-icon.svg | 5 +++++ pkg/dex/web/static/img/email-icon.svg | 5 +++++ pkg/dex/web/static/img/gitea-icon.svg | 5 +++++ pkg/dex/web/static/img/github-icon.svg | 5 +++++ pkg/dex/web/static/img/gitlab-icon.svg | 5 +++++ pkg/dex/web/static/img/google-icon.svg | 5 +++++ pkg/dex/web/static/img/keystone-icon.svg | 5 +++++ pkg/dex/web/static/img/ldap-icon.svg | 5 +++++ pkg/dex/web/static/img/linkedin-icon.svg | 5 +++++ pkg/dex/web/static/img/microsoft-icon.svg | 5 +++++ pkg/dex/web/static/img/oidc-icon.svg | 5 +++++ pkg/dex/web/static/img/saml-icon.svg | 5 +++++ pkg/dex/web/static/main.css | 5 +++++ pkg/dex/web/templates/approval.html | 5 +++++ pkg/dex/web/templates/device.html | 5 +++++ pkg/dex/web/templates/device_success.html | 5 +++++ pkg/dex/web/templates/error.html | 5 +++++ pkg/dex/web/templates/footer.html | 6 +++++- pkg/dex/web/templates/header.html | 5 +++++ pkg/dex/web/templates/login.html | 5 +++++ pkg/dex/web/templates/oob.html | 5 +++++ pkg/dex/web/templates/password.html | 5 +++++ pkg/dex/web/themes/dark/styles.css | 5 +++++ pkg/dex/web/themes/light/styles.css | 5 +++++ pkg/dex/web/web.go | 3 +++ 26 files changed, 128 insertions(+), 1 deletion(-) diff --git a/pkg/dex/web/static/img/atlassian-crowd-icon.svg b/pkg/dex/web/static/img/atlassian-crowd-icon.svg index cd94e3005..729e4e806 100644 --- a/pkg/dex/web/static/img/atlassian-crowd-icon.svg +++ b/pkg/dex/web/static/img/atlassian-crowd-icon.svg @@ -1,4 +1,9 @@ + + + + diff --git a/pkg/dex/web/static/img/email-icon.svg b/pkg/dex/web/static/img/email-icon.svg index 10f0d8d0c..0d74df9e0 100644 --- a/pkg/dex/web/static/img/email-icon.svg +++ b/pkg/dex/web/static/img/email-icon.svg @@ -1,4 +1,9 @@ + + Shape diff --git a/pkg/dex/web/static/img/gitea-icon.svg b/pkg/dex/web/static/img/gitea-icon.svg index afeeacb77..941ea9bbb 100644 --- a/pkg/dex/web/static/img/gitea-icon.svg +++ b/pkg/dex/web/static/img/gitea-icon.svg @@ -1 +1,6 @@ + + \ No newline at end of file diff --git a/pkg/dex/web/static/img/github-icon.svg b/pkg/dex/web/static/img/github-icon.svg index 5d6072823..e56db2376 100644 --- a/pkg/dex/web/static/img/github-icon.svg +++ b/pkg/dex/web/static/img/github-icon.svg @@ -1,4 +1,9 @@ + + diff --git a/pkg/dex/web/static/img/gitlab-icon.svg b/pkg/dex/web/static/img/gitlab-icon.svg index e8d408fa2..0836a691d 100644 --- a/pkg/dex/web/static/img/gitlab-icon.svg +++ b/pkg/dex/web/static/img/gitlab-icon.svg @@ -1,4 +1,9 @@ + + logo-square diff --git a/pkg/dex/web/static/img/google-icon.svg b/pkg/dex/web/static/img/google-icon.svg index d667afdf5..7115861d5 100644 --- a/pkg/dex/web/static/img/google-icon.svg +++ b/pkg/dex/web/static/img/google-icon.svg @@ -1,4 +1,9 @@ + + logo_googleg_48dp diff --git a/pkg/dex/web/static/img/keystone-icon.svg b/pkg/dex/web/static/img/keystone-icon.svg index 7a30aba1f..8b79fbd48 100644 --- a/pkg/dex/web/static/img/keystone-icon.svg +++ b/pkg/dex/web/static/img/keystone-icon.svg @@ -1,3 +1,8 @@ + + diff --git a/pkg/dex/web/static/img/ldap-icon.svg b/pkg/dex/web/static/img/ldap-icon.svg index 506dadc08..69aafe2ea 100644 --- a/pkg/dex/web/static/img/ldap-icon.svg +++ b/pkg/dex/web/static/img/ldap-icon.svg @@ -1,4 +1,9 @@ + + Combined-Shape diff --git a/pkg/dex/web/static/img/linkedin-icon.svg b/pkg/dex/web/static/img/linkedin-icon.svg index 409bad5e5..4d3411d6a 100644 --- a/pkg/dex/web/static/img/linkedin-icon.svg +++ b/pkg/dex/web/static/img/linkedin-icon.svg @@ -1 +1,6 @@ + + \ No newline at end of file diff --git a/pkg/dex/web/static/img/microsoft-icon.svg b/pkg/dex/web/static/img/microsoft-icon.svg index 739c395ab..6c5c2f0ce 100644 --- a/pkg/dex/web/static/img/microsoft-icon.svg +++ b/pkg/dex/web/static/img/microsoft-icon.svg @@ -1,4 +1,9 @@ + + diff --git a/pkg/dex/web/static/img/oidc-icon.svg b/pkg/dex/web/static/img/oidc-icon.svg index e2817b0a1..8f0fd7b05 100644 --- a/pkg/dex/web/static/img/oidc-icon.svg +++ b/pkg/dex/web/static/img/oidc-icon.svg @@ -1,4 +1,9 @@ + + + + Combined-Shape diff --git a/pkg/dex/web/static/main.css b/pkg/dex/web/static/main.css index cf3a42448..df7556d4c 100644 --- a/pkg/dex/web/static/main.css +++ b/pkg/dex/web/static/main.css @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors + * SPDX-License-Identifier: Apache-2.0 + */ + * { box-sizing: border-box; } diff --git a/pkg/dex/web/templates/approval.html b/pkg/dex/web/templates/approval.html index 1c037d2d2..46745e121 100644 --- a/pkg/dex/web/templates/approval.html +++ b/pkg/dex/web/templates/approval.html @@ -1,3 +1,8 @@ + + {{ template "header.html" . }}
diff --git a/pkg/dex/web/templates/device.html b/pkg/dex/web/templates/device.html index 674cbdc32..d52622f7b 100644 --- a/pkg/dex/web/templates/device.html +++ b/pkg/dex/web/templates/device.html @@ -1,3 +1,8 @@ + + {{ template "header.html" . }}
diff --git a/pkg/dex/web/templates/device_success.html b/pkg/dex/web/templates/device_success.html index 53b09ce58..88f73c17a 100644 --- a/pkg/dex/web/templates/device_success.html +++ b/pkg/dex/web/templates/device_success.html @@ -1,3 +1,8 @@ + + {{ template "header.html" . }}
diff --git a/pkg/dex/web/templates/error.html b/pkg/dex/web/templates/error.html index 418f76fbd..5bba25651 100644 --- a/pkg/dex/web/templates/error.html +++ b/pkg/dex/web/templates/error.html @@ -1,3 +1,8 @@ + + {{ template "header.html" . }}
diff --git a/pkg/dex/web/templates/footer.html b/pkg/dex/web/templates/footer.html index faef8e5fe..08d8ae213 100644 --- a/pkg/dex/web/templates/footer.html +++ b/pkg/dex/web/templates/footer.html @@ -1,4 +1,8 @@ + -
+
diff --git a/pkg/dex/web/templates/header.html b/pkg/dex/web/templates/header.html index 3304dbfbc..edc85f4bd 100644 --- a/pkg/dex/web/templates/header.html +++ b/pkg/dex/web/templates/header.html @@ -1,3 +1,8 @@ + + diff --git a/pkg/dex/web/templates/login.html b/pkg/dex/web/templates/login.html index f432dd009..b38b2faf4 100644 --- a/pkg/dex/web/templates/login.html +++ b/pkg/dex/web/templates/login.html @@ -1,3 +1,8 @@ + + {{ template "header.html" . }}
diff --git a/pkg/dex/web/templates/oob.html b/pkg/dex/web/templates/oob.html index ba84d8173..be544a35f 100644 --- a/pkg/dex/web/templates/oob.html +++ b/pkg/dex/web/templates/oob.html @@ -1,3 +1,8 @@ + + {{ template "header.html" . }}
diff --git a/pkg/dex/web/templates/password.html b/pkg/dex/web/templates/password.html index 8c77b26e9..24dc07813 100644 --- a/pkg/dex/web/templates/password.html +++ b/pkg/dex/web/templates/password.html @@ -1,3 +1,8 @@ + + {{ template "header.html" . }}
diff --git a/pkg/dex/web/themes/dark/styles.css b/pkg/dex/web/themes/dark/styles.css index edf30412f..4a1a6db67 100644 --- a/pkg/dex/web/themes/dark/styles.css +++ b/pkg/dex/web/themes/dark/styles.css @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors + * SPDX-License-Identifier: Apache-2.0 + */ + .theme-body { background-color: #0f1218; color: #c8d1d9; diff --git a/pkg/dex/web/themes/light/styles.css b/pkg/dex/web/themes/light/styles.css index 2d9205711..8ba7f2635 100644 --- a/pkg/dex/web/themes/light/styles.css +++ b/pkg/dex/web/themes/light/styles.css @@ -1,3 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors + * SPDX-License-Identifier: Apache-2.0 + */ + .theme-body { background-color: #efefef; color: #333; diff --git a/pkg/dex/web/web.go b/pkg/dex/web/web.go index 0c7e9873a..3e30480bc 100644 --- a/pkg/dex/web/web.go +++ b/pkg/dex/web/web.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors +// SPDX-License-Identifier: Apache-2.0 + package web import ( From 3f3e4fd7e42aac68e2189cceed6aa3792ee17da7 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Sat, 1 Feb 2025 00:22:09 +0100 Subject: [PATCH 032/108] (chore): resolve feature flags from configmap --- pkg/features/features.go | 21 ++++--- pkg/features/features_test.go | 101 ++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+), 8 deletions(-) create mode 100644 pkg/features/features_test.go diff --git a/pkg/features/features.go b/pkg/features/features.go index f9fab4a29..aa510de52 100644 --- a/pkg/features/features.go +++ b/pkg/features/features.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors +// SPDX-License-Identifier: Apache-2.0 + package features import ( @@ -7,6 +10,7 @@ import ( "gopkg.in/yaml.v3" corev1 "k8s.io/api/core/v1" + kerrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" @@ -21,10 +25,9 @@ const ( ) type features struct { - m sync.Mutex - k8sClient client.Client - raw map[string]string - dex *dexFeatures `yaml:"dex"` + m sync.Mutex + raw map[string]string + dex *dexFeatures `yaml:"dex"` } type dexFeatures struct { @@ -35,14 +38,16 @@ type Features interface { GetDexStorageType(ctx context.Context) *string } -func NewFeatures(ctx context.Context, k8sClient client.Client) (Features, error) { +func NewFeatures(ctx context.Context, k8sClient client.Reader) (Features, error) { featureMap := &corev1.ConfigMap{} if err := k8sClient.Get(ctx, types.NamespacedName{Name: featureConfigMapName, Namespace: featureConfigMapNamespace}, featureMap); err != nil { + if kerrors.IsNotFound(err) { + return nil, nil + } return nil, err } return &features{ - k8sClient: k8sClient, - raw: featureMap.Data, + raw: featureMap.Data, }, nil } @@ -73,7 +78,7 @@ func (f *features) GetDexStorageType(ctx context.Context) *string { } if err := f.resolveDexFeatures(); err != nil { ctrl.LoggerFrom(ctx).Error(err, "failed to resolve dex features") - return clientutil.Ptr("") + return nil } if f.dex.Storage == "" { return nil diff --git a/pkg/features/features_test.go b/pkg/features/features_test.go new file mode 100644 index 000000000..4f062e3e4 --- /dev/null +++ b/pkg/features/features_test.go @@ -0,0 +1,101 @@ +package features + +import ( + "context" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + corev1 "k8s.io/api/core/v1" + kerrors "k8s.io/apimachinery/pkg/api/errors" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + + "github.com/cloudoperators/greenhouse/pkg/clientutil" + "github.com/cloudoperators/greenhouse/pkg/mocks" +) + +// Test_DexFeatures - test dex storage type feature gate +func Test_DexFeatures(t *testing.T) { + type testCase struct { + name string + configMapData map[string]string + getError error + expectedValue *string + } + + testCases := []testCase{ + { + name: "it should return kubernetes as storage type from feature-flags cm", + configMapData: map[string]string{DexFeatureKey: "storage: kubernetes\n"}, + expectedValue: clientutil.Ptr("kubernetes"), + }, + { + name: "it should return postgres as storage type from feature-flags cm", + configMapData: map[string]string{DexFeatureKey: "storage: postgres\n"}, + expectedValue: clientutil.Ptr("postgres"), + }, + { + name: "it should return nil when storage type is not found in feature-flags cm", + configMapData: map[string]string{"someOtherKey": "value\n"}, + expectedValue: nil, // should return nil since `dex` is missing + }, + { + name: "it should return nil when storage type is empty in feature-flags cm", + configMapData: map[string]string{DexFeatureKey: "storage: "}, + expectedValue: nil, + }, + { + name: "it should return a nil instance of features when feature-flags cm is not found", + getError: kerrors.NewNotFound(schema.GroupResource{}, "configmap not found"), + expectedValue: nil, // should return nil since ConfigMap is not found + }, + { + name: "it should return nil when flag is malformed in feature-flags cm", + configMapData: map[string]string{DexFeatureKey: "storage:: invalid_yaml"}, + expectedValue: nil, // should return an empty string and log an error + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + ctx := context.Background() + ctx = log.IntoContext(ctx, log.Log) + + mockK8sClient := &mocks.MockClient{} + configMap := &corev1.ConfigMap{} + + if tc.getError != nil { + mockK8sClient.On("Get", ctx, types.NamespacedName{ + Name: featureConfigMapName, Namespace: featureConfigMapNamespace, + }, mock.Anything).Return(tc.getError) + } else { + configMap.Data = tc.configMapData + mockK8sClient.On("Get", ctx, types.NamespacedName{ + Name: featureConfigMapName, Namespace: featureConfigMapNamespace, + }, mock.Anything).Run(func(args mock.Arguments) { + arg := args.Get(2).(*corev1.ConfigMap) + *arg = *configMap + }).Return(nil) + } + + // Create Features instance + featuresInstance, err := NewFeatures(ctx, mockK8sClient) + if tc.getError != nil && client.IgnoreNotFound(tc.getError) == nil { + assert.NoError(t, client.IgnoreNotFound(err)) + assert.Nil(t, featuresInstance, "Expected nil when ConfigMap is missing") + return + } + assert.NoError(t, err) + + // Get Dex storage type + value := featuresInstance.GetDexStorageType(ctx) + + // Assert expected value + assert.Equal(t, tc.expectedValue, value) + mockK8sClient.AssertExpectations(t) + }) + } +} From 13e0fed292b6f007604b32b14e9667fb81916ec6 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Sat, 1 Feb 2025 00:23:09 +0100 Subject: [PATCH 033/108] (chore): adds dex storage adapter interface --- pkg/dex/store/storage.go | 90 +++++++++++++++++++++++++++++++ pkg/idproxy/storage.go | 112 --------------------------------------- 2 files changed, 90 insertions(+), 112 deletions(-) create mode 100644 pkg/dex/store/storage.go delete mode 100644 pkg/idproxy/storage.go diff --git a/pkg/dex/store/storage.go b/pkg/dex/store/storage.go new file mode 100644 index 000000000..64ee4b4b6 --- /dev/null +++ b/pkg/dex/store/storage.go @@ -0,0 +1,90 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors +// SPDX-License-Identifier: Apache-2.0 + +package store + +import ( + "context" + "crypto/hmac" + "crypto/sha256" + "encoding/hex" + "fmt" + "log/slog" + + "github.com/dexidp/dex/storage" + corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + + greenhouseapisv1alpha1 "github.com/cloudoperators/greenhouse/pkg/apis/greenhouse/v1alpha1" + "github.com/cloudoperators/greenhouse/pkg/clientutil" +) + +const ( + Postgres = "postgres" + K8s = "kubernetes" + dexConnectorTypeGreenhouse = "greenhouse-oidc" + clientIDKey = "clientID" + clientSecretKey = "clientSecret" +) + +type Dexter interface { + CreateUpdateConnector(ctx context.Context, k8sClient client.Client, org *greenhouseapisv1alpha1.Organization, configByte []byte, namespace string) error + CreateUpdateOauth2Client(ctx context.Context, k8sClient client.Client, org *greenhouseapisv1alpha1.Organization, namespace string) error + GetStorage() storage.Storage + GetBackend() string + Close() error +} + +func NewDexStorageFactory(logger *slog.Logger, backend string) (Dexter, error) { + switch backend { + case Postgres: + dexStorage, err := newPostgresStore(logger) + if err != nil { + return nil, err + } + return &pgDex{storage: dexStorage, backend: backend}, nil + case K8s: + dexStorage, err := newKubernetesStore(logger) + if err != nil { + return nil, err + } + return &k8sDex{storage: dexStorage, backend: backend}, nil + default: + return nil, fmt.Errorf("unknown dexStorage backend: %s", backend) + } +} + +func getDeterministicSecret(clientID, version string, secretKey types.UID) string { + h := hmac.New(sha256.New, []byte(secretKey)) + h.Write([]byte(version + clientID)) + return hex.EncodeToString(h.Sum(nil))[:32] +} + +func prepareClientSecret(namespace, clientID, clientSecret string) *corev1.Secret { + secret := new(corev1.Secret) + secret.SetName(clientID + "-dex-secrets") + secret.SetNamespace(namespace) + secret.Data = map[string][]byte{ + clientIDKey: []byte(clientID), + clientSecretKey: []byte(clientSecret), + } + return secret +} + +func writeCredentialsToNamespace(ctx context.Context, cl client.Client, secret *corev1.Secret) error { + result, err := clientutil.CreateOrPatch(ctx, cl, secret, func() error { + return nil + }) + if err != nil { + log.FromContext(ctx).Error(err, "unable to create dex client credentials", "name", secret.Name, "namespace", secret.Namespace) + } + switch result { + case clientutil.OperationResultCreated: + log.FromContext(ctx).Info("created oauth2client secrets", "name", secret.Name, "namespace", secret.Namespace) + case clientutil.OperationResultUpdated: + log.FromContext(ctx).Info("updated oauth2client secrets", "name", secret.Name, "namespace", secret.Namespace) + } + return nil +} diff --git a/pkg/idproxy/storage.go b/pkg/idproxy/storage.go deleted file mode 100644 index a14e366e1..000000000 --- a/pkg/idproxy/storage.go +++ /dev/null @@ -1,112 +0,0 @@ -// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors -// SPDX-License-Identifier: Apache-2.0 - -package idproxy - -import ( - "fmt" - "log/slog" - "os" - - "github.com/dexidp/dex/storage" - "github.com/dexidp/dex/storage/kubernetes" - "github.com/dexidp/dex/storage/kubernetes/k8sapi" - "github.com/ghodss/yaml" -) - -func NewKubernetesStorage(kubeconfig, kubecontext, namespace string, logger *slog.Logger) (storage.Storage, error) { - var storageConfig = kubernetes.Config{InCluster: true} - if kubeconfig != "" { - k, err := generateKubeConfig(kubeconfig, kubecontext, namespace) - if err != nil { - return nil, fmt.Errorf("failed to generate kubeconfig: %w", err) - } - file, err := os.CreateTemp("", "idproxy") - if err != nil { - return nil, fmt.Errorf("failed ot create temp file: %w", err) - } - _, err = file.Write(k) - file.Close() - defer os.Remove(file.Name()) - if err != nil { - return nil, fmt.Errorf("failed to write temporary kubeconfig %s: %w", file.Name(), err) - } - storageConfig.InCluster = false - storageConfig.KubeConfigFile = file.Name() - } - - return storageConfig.Open(logger) -} - -func generateKubeConfig(kubeconfig, kubecontext, namespace string) ([]byte, error) { - data, err := os.ReadFile(kubeconfig) - - if err != nil { - return nil, err - } - - var config k8sapi.Config - if err := yaml.Unmarshal(data, &config); err != nil { - return nil, fmt.Errorf("unmarshal %s: %w", kubeconfig, err) - } - context, err := getContext(&config, kubecontext) - if err != nil { - return nil, err - } - if namespace != "" { - context.Context.Namespace = namespace - } - authinfo, err := getAuthInfo(&config, context.Context.AuthInfo) - if err != nil { - return nil, err - } - cluster, err := getCluster(&config, context.Context.Cluster) - if err != nil { - return nil, err - } - - newConfig := k8sapi.Config{ - CurrentContext: context.Name, - Contexts: []k8sapi.NamedContext{context}, - Clusters: []k8sapi.NamedCluster{cluster}, - AuthInfos: []k8sapi.NamedAuthInfo{authinfo}, - } - - return yaml.Marshal(newConfig) -} - -func getContext(c *k8sapi.Config, context string) (k8sapi.NamedContext, error) { - if context == "" { - context = c.CurrentContext - } - for _, c := range c.Contexts { - if c.Name == context { - return c, nil - } - } - return k8sapi.NamedContext{}, fmt.Errorf("context %#v not found", context) -} - -func getAuthInfo(c *k8sapi.Config, name string) (k8sapi.NamedAuthInfo, error) { - for _, a := range c.AuthInfos { - if a.Name == name { - if a.AuthInfo.AuthProvider != nil { - if a.AuthInfo.AuthProvider.Name == "oidc" { - a.AuthInfo.Token = a.AuthInfo.AuthProvider.Config["id-token"] - } - a.AuthInfo.AuthProvider = nil - } - return a, nil - } - } - return k8sapi.NamedAuthInfo{}, fmt.Errorf("authinfo %#v not found", name) -} - -func getCluster(c *k8sapi.Config, name string) (k8sapi.NamedCluster, error) { - for _, c := range c.Clusters { - if c.Name == name { - return c, nil - } - } - return k8sapi.NamedCluster{}, fmt.Errorf("cluster %#v not found", name) -} From cf7b2d8a5a89c0497426e6fa023bf6a54b7782fe Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Sat, 1 Feb 2025 00:24:32 +0100 Subject: [PATCH 034/108] (chore): adds dex postgres storage adapter additionally while creating OAuth2Client in postgres it also generates client secret in the organization namespace --- pkg/dex/store/pg_store.go | 156 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 156 insertions(+) create mode 100644 pkg/dex/store/pg_store.go diff --git a/pkg/dex/store/pg_store.go b/pkg/dex/store/pg_store.go new file mode 100644 index 000000000..7620dc39d --- /dev/null +++ b/pkg/dex/store/pg_store.go @@ -0,0 +1,156 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors +// SPDX-License-Identifier: Apache-2.0 + +package store + +import ( + "context" + "errors" + "fmt" + "log/slog" + + "github.com/dexidp/dex/storage" + "github.com/dexidp/dex/storage/sql" + "golang.org/x/text/cases" + "golang.org/x/text/language" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" + + greenhouseapisv1alpha1 "github.com/cloudoperators/greenhouse/pkg/apis/greenhouse/v1alpha1" + "github.com/cloudoperators/greenhouse/pkg/clientutil" + "github.com/cloudoperators/greenhouse/pkg/common" + "github.com/cloudoperators/greenhouse/pkg/util" +) + +type pgDex struct { + storage storage.Storage + backend string +} + +const ( + hostEnv = "DB_HOST" + portEnv = "DB_PORT" + userEnv = "DB_USER" + passEnv = "DB_PASSWORD" + dbNameEnv = "DB_DATABASE" +) + +func newPostgresStore(logger *slog.Logger) (storage.Storage, error) { + var host, user, pass, database string + var port int + var err error + database = clientutil.GetEnvOrDefault(dbNameEnv, "postgres") + user = clientutil.GetEnvOrDefault(userEnv, "postgres") + port = clientutil.GetIntEnvWithDefault(portEnv, 5432) + if host, err = clientutil.GetEnv(hostEnv); err != nil { + return nil, err + } + if pass, err = clientutil.GetEnv(passEnv); err != nil { + return nil, err + } + cfg := &sql.Postgres{ + SSL: sql.SSL{Mode: "disable"}, + NetworkDB: sql.NetworkDB{ + Host: host, + Port: uint16(port), + User: user, + Password: pass, + Database: database, + }, + } + return cfg.Open(logger) +} + +func (p *pgDex) GetBackend() string { + return p.backend +} + +func (p *pgDex) CreateUpdateConnector(ctx context.Context, _ client.Client, org *greenhouseapisv1alpha1.Organization, configByte []byte, _ string) error { + oidcConnector, err := p.storage.GetConnector(org.Name) + if err != nil && errors.Is(err, storage.ErrNotFound) { + if err = p.storage.CreateConnector(ctx, storage.Connector{ + ID: org.Name, + Type: dexConnectorTypeGreenhouse, + Name: cases.Title(language.English).String(org.Name), + Config: configByte, + }); err != nil { + return err + } + log.FromContext(ctx).Info("created dex connector in SQL storage", "name", org.Name) + } + if err != nil { + log.FromContext(ctx).Error(err, "failed to get dex connector in SQL storage", "name", org.Name) + } + err = p.storage.UpdateConnector(oidcConnector.ID, func(c storage.Connector) (storage.Connector, error) { + c.ID = org.Name + c.Type = dexConnectorTypeGreenhouse + c.Name = cases.Title(language.English).String(org.Name) + c.Config = configByte + return c, nil + }) + + if err != nil { + return err + } + log.FromContext(ctx).Info("updated dex connector in SQL storage", "name", org.Name) + return nil +} + +func (p *pgDex) CreateUpdateOauth2Client(ctx context.Context, k8sClient client.Client, org *greenhouseapisv1alpha1.Organization, namespace string) error { + generatedClientSecret := getDeterministicSecret(org.Name, org.GetResourceVersion(), org.GetUID()) + oAuthClient, err := p.storage.GetClient(org.Name) + if err != nil && errors.Is(err, storage.ErrNotFound) { + if err = p.storage.CreateClient(ctx, storage.Client{ + Public: true, + ID: org.Name, + Secret: generatedClientSecret, + Name: org.Name, + RedirectURIs: []string{ + "http://localhost:8085", + "http://localhost:8000", + "https://dashboard." + common.DNSDomain, + fmt.Sprintf("https://%s.dashboard.%s", org.Name, common.DNSDomain), + }, + }); err != nil { + return err + } + log.FromContext(ctx).Info("created oauth2client", "name", org.Name) + return nil + } + if err != nil { + log.FromContext(ctx).Error(err, "failed to get oauth2client", "name", org.Name) + } + err = p.storage.UpdateClient(oAuthClient.Name, func(authClient storage.Client) (storage.Client, error) { + authClient.Public = true + authClient.ID = org.Name + authClient.Secret = generatedClientSecret + authClient.Name = org.Name + for _, requiredRedirectURL := range []string{ + "http://localhost:8085", + "http://localhost:8000", + "https://dashboard." + common.DNSDomain, + fmt.Sprintf("https://%s.dashboard.%s", org.Name, common.DNSDomain), + } { + authClient.RedirectURIs = util.AppendStringToSliceIfNotContains(requiredRedirectURL, authClient.RedirectURIs) + } + return authClient, nil + }) + if err != nil { + return err + } + secret := prepareClientSecret(namespace, org.Name, generatedClientSecret) + err = writeCredentialsToNamespace(ctx, k8sClient, secret) + if err != nil { + return err + } + log.FromContext(ctx).Info("updated oauth2client", "name", org.Name) + return nil +} + +func (p *pgDex) GetStorage() storage.Storage { + return p.storage +} + +func (p *pgDex) Close() error { + return p.storage.Close() +} From 41cf6ffbc3a6433d2fe38c0f35e6059f858710ab Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Sat, 1 Feb 2025 00:24:42 +0100 Subject: [PATCH 035/108] (chore): adds dex kubernetes storage adapter additionally while creating OAuth2Client in postgres it also generates client secret in the organization namespace --- pkg/dex/store/k8s_store.go | 131 +++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) create mode 100644 pkg/dex/store/k8s_store.go diff --git a/pkg/dex/store/k8s_store.go b/pkg/dex/store/k8s_store.go new file mode 100644 index 000000000..ebcd523fb --- /dev/null +++ b/pkg/dex/store/k8s_store.go @@ -0,0 +1,131 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors +// SPDX-License-Identifier: Apache-2.0 + +package store + +import ( + "context" + "encoding/base32" + "fmt" + "hash/fnv" + "log/slog" + "strings" + + "github.com/dexidp/dex/storage" + "github.com/dexidp/dex/storage/kubernetes" + "golang.org/x/text/cases" + "golang.org/x/text/language" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" + "sigs.k8s.io/controller-runtime/pkg/log" + + greenhouseapisv1alpha1 "github.com/cloudoperators/greenhouse/pkg/apis/greenhouse/v1alpha1" + "github.com/cloudoperators/greenhouse/pkg/clientutil" + "github.com/cloudoperators/greenhouse/pkg/common" + dexapi "github.com/cloudoperators/greenhouse/pkg/dex/api" + "github.com/cloudoperators/greenhouse/pkg/util" +) + +type k8sDex struct { + storage storage.Storage + backend string +} + +const encoding = "abcdefghijklmnopqrstuvwxyz234567" + +func newKubernetesStore(logger *slog.Logger) (storage.Storage, error) { + cfg := kubernetes.Config{InCluster: true} + kEnv := clientutil.GetEnvOrDefault("KUBECONFIG", "") + if strings.TrimSpace(kEnv) != "" { + cfg.InCluster = false + cfg.KubeConfigFile = kEnv + } + dexStorage, err := cfg.Open(logger) + if err != nil { + return nil, err + } + return dexStorage, nil +} + +func (k *k8sDex) GetBackend() string { + return k.backend +} + +func (k *k8sDex) CreateUpdateConnector(ctx context.Context, k8sClient client.Client, org *greenhouseapisv1alpha1.Organization, configByte []byte, namespace string) (err error) { + var result clientutil.OperationResult + var dexConnector = new(dexapi.Connector) + dexConnector.Namespace = namespace + dexConnector.ObjectMeta.Name = org.GetName() + result, err = clientutil.CreateOrPatch(ctx, k8sClient, dexConnector, func() error { + dexConnector.DexConnector.Type = dexConnectorTypeGreenhouse + dexConnector.DexConnector.Name = cases.Title(language.English).String(org.Name) + dexConnector.DexConnector.ID = org.GetName() + dexConnector.DexConnector.Config = configByte + return controllerutil.SetControllerReference(org, dexConnector, k8sClient.Scheme()) + }) + if err != nil { + return + } + switch result { + case clientutil.OperationResultCreated: + log.FromContext(ctx).Info("created dex connector", "namespace", dexConnector.Namespace, "name", dexConnector.GetName()) + case clientutil.OperationResultUpdated: + log.FromContext(ctx).Info("updated dex connector", "namespace", dexConnector.Namespace, "name", dexConnector.GetName()) + } + return +} + +func (k *k8sDex) CreateUpdateOauth2Client(ctx context.Context, k8sClient client.Client, org *greenhouseapisv1alpha1.Organization, namespace string) error { + var oAuth2Client = new(dexapi.OAuth2Client) + oAuth2Client.ObjectMeta.Name = encodedOAuth2ClientName(org.Name) + oAuth2Client.ObjectMeta.Namespace = namespace + generatedClientSecret := getDeterministicSecret(org.Name, org.GetResourceVersion(), org.GetUID()) + + result, err := clientutil.CreateOrPatch(ctx, k8sClient, oAuth2Client, func() error { + oAuth2Client.Client.Public = true + oAuth2Client.Client.ID = org.Name + oAuth2Client.Secret = generatedClientSecret + oAuth2Client.Client.Name = org.Name + for _, requiredRedirectURL := range []string{ + "http://localhost:8085", + "http://localhost:8000", + "https://dashboard." + common.DNSDomain, + fmt.Sprintf("https://%s.dashboard.%s", org.Name, common.DNSDomain), + } { + oAuth2Client.Client.RedirectURIs = util.AppendStringToSliceIfNotContains(requiredRedirectURL, oAuth2Client.RedirectURIs) + } + return controllerutil.SetControllerReference(org, oAuth2Client, k8sClient.Scheme()) + }) + if err != nil { + return err + } + switch result { + case clientutil.OperationResultCreated: + log.FromContext(ctx).Info("created oauth2client", "namespace", oAuth2Client.Namespace, "name", oAuth2Client.GetName()) + case clientutil.OperationResultUpdated: + log.FromContext(ctx).Info("updated oauth2client", "namespace", oAuth2Client.Namespace, "name", oAuth2Client.GetName()) + } + + secret := prepareClientSecret(namespace, org.Name, generatedClientSecret) + err = writeCredentialsToNamespace(ctx, k8sClient, secret) + if err != nil { + return err + } + return nil +} + +func encodedOAuth2ClientName(orgName string) string { + // See https://github.com/dexidp/dex/issues/1606 for encoding. + return strings.TrimRight(base32. + NewEncoding(encoding). + EncodeToString(fnv.New64().Sum([]byte(orgName))), "=", + ) +} + +func (k *k8sDex) GetStorage() storage.Storage { + return k.storage +} + +func (k *k8sDex) Close() error { + return k.storage.Close() +} From 76bfbae383714c05aa33b23f37f7e3f7954fe6f1 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Sat, 1 Feb 2025 00:25:11 +0100 Subject: [PATCH 036/108] (chore): adds dex storage adapter interface mocks --- .mockery.yaml | 3 + pkg/mocks/mock_Dexter.go | 129 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 pkg/mocks/mock_Dexter.go diff --git a/.mockery.yaml b/.mockery.yaml index 2d6924e93..379c8b913 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -7,6 +7,9 @@ filename: "mock_{{.InterfaceName}}.go" outpkg: mocks dir: pkg/mocks packages: + github.com/cloudoperators/greenhouse/pkg/dex/store: + interfaces: + Dexter: github.com/cloudoperators/greenhouse/pkg/lifecycle: interfaces: Reconciler: diff --git a/pkg/mocks/mock_Dexter.go b/pkg/mocks/mock_Dexter.go new file mode 100644 index 000000000..dbf9af290 --- /dev/null +++ b/pkg/mocks/mock_Dexter.go @@ -0,0 +1,129 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors +// SPDX-License-Identifier: Apache-2.0 + +// Code generated by mockery v2.50.0. DO NOT EDIT. + +package mocks + +import ( + context "context" + + client "sigs.k8s.io/controller-runtime/pkg/client" + + mock "github.com/stretchr/testify/mock" + + storage "github.com/dexidp/dex/storage" + + v1alpha1 "github.com/cloudoperators/greenhouse/pkg/apis/greenhouse/v1alpha1" +) + +// MockDexter is an autogenerated mock type for the Dexter type +type MockDexter struct { + mock.Mock +} + +// Close provides a mock function with no fields +func (_m *MockDexter) Close() error { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Close") + } + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// CreateUpdateConnector provides a mock function with given fields: ctx, k8sClient, org, configByte, namespace +func (_m *MockDexter) CreateUpdateConnector(ctx context.Context, k8sClient client.Client, org *v1alpha1.Organization, configByte []byte, namespace string) error { + ret := _m.Called(ctx, k8sClient, org, configByte, namespace) + + if len(ret) == 0 { + panic("no return value specified for CreateUpdateConnector") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, client.Client, *v1alpha1.Organization, []byte, string) error); ok { + r0 = rf(ctx, k8sClient, org, configByte, namespace) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// CreateUpdateOauth2Client provides a mock function with given fields: ctx, k8sClient, org, namespace +func (_m *MockDexter) CreateUpdateOauth2Client(ctx context.Context, k8sClient client.Client, org *v1alpha1.Organization, namespace string) error { + ret := _m.Called(ctx, k8sClient, org, namespace) + + if len(ret) == 0 { + panic("no return value specified for CreateUpdateOauth2Client") + } + + var r0 error + if rf, ok := ret.Get(0).(func(context.Context, client.Client, *v1alpha1.Organization, string) error); ok { + r0 = rf(ctx, k8sClient, org, namespace) + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// GetBackend provides a mock function with no fields +func (_m *MockDexter) GetBackend() string { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetBackend") + } + + var r0 string + if rf, ok := ret.Get(0).(func() string); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(string) + } + + return r0 +} + +// GetStorage provides a mock function with no fields +func (_m *MockDexter) GetStorage() storage.Storage { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for GetStorage") + } + + var r0 storage.Storage + if rf, ok := ret.Get(0).(func() storage.Storage); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(storage.Storage) + } + } + + return r0 +} + +// NewMockDexter creates a new instance of MockDexter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewMockDexter(t interface { + mock.TestingT + Cleanup(func()) +}) *MockDexter { + mock := &MockDexter{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} From 91759fc9b6a885912f4e867e2ca4df9d640ea24d Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Sat, 1 Feb 2025 00:26:29 +0100 Subject: [PATCH 037/108] (chore): adds dex storage adapter interface to reconciler removes dependency to dex CRDs if the storage backend is postgres --- .../organization/organization_controller.go | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/pkg/controllers/organization/organization_controller.go b/pkg/controllers/organization/organization_controller.go index fa3f90734..49ff94f07 100644 --- a/pkg/controllers/organization/organization_controller.go +++ b/pkg/controllers/organization/organization_controller.go @@ -21,6 +21,7 @@ import ( greenhousesapv1alpha1 "github.com/cloudoperators/greenhouse/pkg/apis/greenhouse/v1alpha1" "github.com/cloudoperators/greenhouse/pkg/clientutil" dexapi "github.com/cloudoperators/greenhouse/pkg/dex/api" + dexstore "github.com/cloudoperators/greenhouse/pkg/dex/store" "github.com/cloudoperators/greenhouse/pkg/lifecycle" "github.com/cloudoperators/greenhouse/pkg/scim" ) @@ -44,6 +45,7 @@ var ( type OrganizationReconciler struct { client.Client recorder record.EventRecorder + Dexter dexstore.Dexter Namespace string } @@ -64,7 +66,7 @@ type OrganizationReconciler struct { func (r *OrganizationReconciler) SetupWithManager(name string, mgr ctrl.Manager) error { r.Client = mgr.GetClient() r.recorder = mgr.GetEventRecorderFor(name) - return ctrl.NewControllerManagedBy(mgr). + b := ctrl.NewControllerManagedBy(mgr). Named(name). For(&greenhousesapv1alpha1.Organization{}). Owns(&corev1.Namespace{}). @@ -75,8 +77,6 @@ func (r *OrganizationReconciler) SetupWithManager(name string, mgr ctrl.Manager) Owns(&rbacv1.RoleBinding{}). Owns(&rbacv1.ClusterRole{}). Owns(&rbacv1.ClusterRoleBinding{}). - Owns(&dexapi.Connector{}). - Owns(&dexapi.OAuth2Client{}). Watches(&corev1.Secret{}, handler.EnqueueRequestsFromMapFunc(r.enqueueOrganizationForReferencedSecret), builder.WithPredicates(clientutil.PredicateHasOICDConfigured())). @@ -85,8 +85,12 @@ func (r *OrganizationReconciler) SetupWithManager(name string, mgr ctrl.Manager) builder.WithPredicates(predicate.And( clientutil.PredicateByName(serviceProxyName), predicate.GenerationChangedPredicate{}, - ))). - Complete(r) + ))) + if r.Dexter.GetBackend() == dexstore.K8s { + b.Owns(&dexapi.Connector{}). + Owns(&dexapi.OAuth2Client{}) + } + return b.Complete(r) } func (r *OrganizationReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { From e4ba7b253ad5c5bff394334fbe49ee93db3873e6 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Sat, 1 Feb 2025 00:27:15 +0100 Subject: [PATCH 038/108] (chore): creates dex resources in organization namespace --- pkg/controllers/organization/dex.go | 82 +++-------------------------- 1 file changed, 6 insertions(+), 76 deletions(-) diff --git a/pkg/controllers/organization/dex.go b/pkg/controllers/organization/dex.go index b73c7c13d..7d5ff624b 100644 --- a/pkg/controllers/organization/dex.go +++ b/pkg/controllers/organization/dex.go @@ -5,33 +5,20 @@ package organization import ( "context" - "encoding/base32" "encoding/json" - "fmt" - "hash/fnv" "strings" "github.com/dexidp/dex/connector/oidc" "github.com/pkg/errors" - "golang.org/x/text/cases" - "golang.org/x/text/language" - corev1 "k8s.io/api/core/v1" networkingv1 "k8s.io/api/networking/v1" "k8s.io/apimachinery/pkg/types" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - "sigs.k8s.io/controller-runtime/pkg/log" greenhousesapv1alpha1 "github.com/cloudoperators/greenhouse/pkg/apis/greenhouse/v1alpha1" "github.com/cloudoperators/greenhouse/pkg/clientutil" - "github.com/cloudoperators/greenhouse/pkg/common" - dexapi "github.com/cloudoperators/greenhouse/pkg/dex/api" - "github.com/cloudoperators/greenhouse/pkg/util" ) -const dexConnectorTypeGreenhouse = "greenhouse-oidc" - //+kubebuilder:rbac:groups=greenhouse.sap,resources=organizations,verbs=get;list;watch;create;update;patch;delete //+kubebuilder:rbac:groups=greenhouse.sap,resources=organizations/status,verbs=get;update;patch //+kubebuilder:rbac:groups=greenhouse.sap,resources=organizations/finalizers,verbs=update @@ -41,6 +28,10 @@ const dexConnectorTypeGreenhouse = "greenhouse-oidc" //+kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch func (r *OrganizationReconciler) reconcileDexConnector(ctx context.Context, org *greenhousesapv1alpha1.Organization) error { + if r.Dexter == nil { + ctrl.LoggerFrom(ctx).Error(errors.New("dex interface not initialized"), "dex storage feature") + return errors.New("dex interface not initialized") + } clientID, err := clientutil.GetSecretKeyFromSecretKeyReference(ctx, r.Client, org.Name, org.Spec.Authentication.OIDCConfig.ClientIDReference) if err != nil { return err @@ -67,28 +58,7 @@ func (r *OrganizationReconciler) reconcileDexConnector(ctx context.Context, org if err != nil { return err } - var dexConnector = new(dexapi.Connector) - dexConnector.Namespace = r.Namespace - dexConnector.ObjectMeta.Name = org.Name - result, err := clientutil.CreateOrPatch(ctx, r.Client, dexConnector, func() error { - dexConnector.DexConnector.Type = dexConnectorTypeGreenhouse - dexConnector.DexConnector.Name = cases.Title(language.English).String(org.Name) - dexConnector.DexConnector.ID = org.Name - dexConnector.DexConnector.Config = configByte - return controllerutil.SetControllerReference(org, dexConnector, r.Scheme()) - }) - if err != nil { - return err - } - switch result { - case clientutil.OperationResultCreated: - log.FromContext(ctx).Info("created dex connector", "namespace", dexConnector.Namespace, "name", dexConnector.GetName()) - r.recorder.Eventf(org, corev1.EventTypeNormal, "CreatedDexConnector", "Created dex connector %s/%s", dexConnector.Namespace, dexConnector.GetName()) - case clientutil.OperationResultUpdated: - log.FromContext(ctx).Info("updated dex connector", "namespace", dexConnector.Namespace, "name", dexConnector.GetName()) - r.recorder.Eventf(org, corev1.EventTypeNormal, "UpdatedDexConnector", "Updated dex connector %s/%s", dexConnector.Namespace, dexConnector.GetName()) - } - return nil + return r.Dexter.CreateUpdateConnector(ctx, r.Client, org, configByte, org.Name) } func (r *OrganizationReconciler) enqueueOrganizationForReferencedSecret(_ context.Context, o client.Object) []ctrl.Request { @@ -118,39 +88,7 @@ func (r *OrganizationReconciler) discoverOIDCRedirectURL(ctx context.Context, or } func (r *OrganizationReconciler) reconcileOAuth2Client(ctx context.Context, org *greenhousesapv1alpha1.Organization) error { - var oAuth2Client = new(dexapi.OAuth2Client) - oAuth2Client.ObjectMeta.Name = encodedOAuth2ClientName(org.Name) - oAuth2Client.ObjectMeta.Namespace = r.Namespace - - result, err := clientutil.CreateOrPatch(ctx, r.Client, oAuth2Client, func() error { - oAuth2Client.Client.Public = true - oAuth2Client.Client.ID = org.Name - oAuth2Client.Client.Name = org.Name - if oAuth2Client.RedirectURIs == nil { - oAuth2Client.RedirectURIs = make([]string, 2) - } - // Ensure the required redirect URLs are present. - // Additional ones can be added by the user. - for _, requiredRedirectURL := range []string{ - "https://dashboard." + common.DNSDomain, - fmt.Sprintf("https://%s.dashboard.%s", org.Name, common.DNSDomain), - } { - oAuth2Client.Client.RedirectURIs = util.AppendStringToSliceIfNotContains(requiredRedirectURL, oAuth2Client.RedirectURIs) - } - return controllerutil.SetControllerReference(org, oAuth2Client, r.Scheme()) - }) - if err != nil { - return err - } - switch result { - case clientutil.OperationResultCreated: - log.FromContext(ctx).Info("created oauth2client", "namespace", oAuth2Client.Namespace, "name", oAuth2Client.GetName()) - r.recorder.Eventf(org, corev1.EventTypeNormal, "CreatedOAuth2Client", "Created oauth2client %s/%s", oAuth2Client.Namespace, oAuth2Client.GetName()) - case clientutil.OperationResultUpdated: - log.FromContext(ctx).Info("updated oauth2client", "namespace", oAuth2Client.Namespace, "name", oAuth2Client.GetName()) - r.recorder.Eventf(org, corev1.EventTypeNormal, "UpdatedOAuth2Client", "Updated oauth2client %s/%s", oAuth2Client.Namespace, oAuth2Client.GetName()) - } - return nil + return r.Dexter.CreateUpdateOauth2Client(ctx, r.Client, org, org.Name) } func ensureCallbackURL(url string) string { @@ -164,11 +102,3 @@ func ensureCallbackURL(url string) string { } return url } - -func encodedOAuth2ClientName(orgName string) string { - // See https://github.com/dexidp/dex/issues/1606 for encoding. - return strings.TrimRight(base32. - NewEncoding("abcdefghijklmnopqrstuvwxyz234567"). - EncodeToString(fnv.New64().Sum([]byte(orgName))), "=", - ) -} From d4b72df2b5cc3464d970af874dbb0a6b5b6a2677 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Sat, 1 Feb 2025 00:28:56 +0100 Subject: [PATCH 039/108] (chore): setup dex storage adapter interface on operator start --- cmd/greenhouse/controllers.go | 16 ++++++++++++++++ cmd/greenhouse/main.go | 9 +++++++++ 2 files changed, 25 insertions(+) diff --git a/cmd/greenhouse/controllers.go b/cmd/greenhouse/controllers.go index 9803b9ee7..dbb01797e 100644 --- a/cmd/greenhouse/controllers.go +++ b/cmd/greenhouse/controllers.go @@ -4,17 +4,21 @@ package main import ( + "context" + "log/slog" "os" "sort" ctrl "sigs.k8s.io/controller-runtime" + "github.com/cloudoperators/greenhouse/pkg/clientutil" clustercontrollers "github.com/cloudoperators/greenhouse/pkg/controllers/cluster" organizationcontrollers "github.com/cloudoperators/greenhouse/pkg/controllers/organization" plugincontrollers "github.com/cloudoperators/greenhouse/pkg/controllers/plugin" teamcontrollers "github.com/cloudoperators/greenhouse/pkg/controllers/team" teammembershipcontrollers "github.com/cloudoperators/greenhouse/pkg/controllers/teammembership" teamrbaccontrollers "github.com/cloudoperators/greenhouse/pkg/controllers/teamrbac" + dexstore "github.com/cloudoperators/greenhouse/pkg/dex/store" ) // knownControllers contains all controllers to be registered when starting the operator. @@ -76,7 +80,19 @@ func startOrganizationReconciler(name string, mgr ctrl.Manager) error { if v, ok := os.LookupEnv("POD_NAMESPACE"); ok { namespace = v } + var dexter dexstore.Dexter + var err error + backend := clientutil.Ptr("kubernetes") + l := slog.New(slog.NewJSONHandler(os.Stdout, nil)) + if f != nil { + backend = f.GetDexStorageType(context.Background()) + } + dexter, err = dexstore.NewDexStorageFactory(l.With("component", "storage"), *backend) + if err != nil { + return err + } return (&organizationcontrollers.OrganizationReconciler{ + Dexter: dexter, Namespace: namespace, }).SetupWithManager(name, mgr) } diff --git a/cmd/greenhouse/main.go b/cmd/greenhouse/main.go index 4b0994e38..9f5a73a9f 100644 --- a/cmd/greenhouse/main.go +++ b/cmd/greenhouse/main.go @@ -4,6 +4,7 @@ package main import ( + "context" "errors" goflag "flag" "fmt" @@ -28,6 +29,7 @@ import ( "github.com/cloudoperators/greenhouse/pkg/clientutil" "github.com/cloudoperators/greenhouse/pkg/common" dexapi "github.com/cloudoperators/greenhouse/pkg/dex/api" + "github.com/cloudoperators/greenhouse/pkg/features" "github.com/cloudoperators/greenhouse/pkg/helm" "github.com/cloudoperators/greenhouse/pkg/version" ) @@ -58,6 +60,7 @@ var ( remoteClusterBearerTokenValidity, renewRemoteClusterBearerTokenAfter time.Duration kubeClientOpts clientutil.RuntimeOptions + f features.Features ) func init() { @@ -130,6 +133,12 @@ func main() { }) handleError(err, "unable to start manager") + k8sClient := mgr.GetAPIReader() + f, err = features.NewFeatures(context.TODO(), k8sClient) + if err != nil { + handleError(err, "unable to get features") + } + mode, err := calculateManagerMode() if err != nil { handleError(err, "unable to calculate manager mode") From 6d86ff7e2c182de1adbd28908f48692ffa9b49d6 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Sat, 1 Feb 2025 00:29:10 +0100 Subject: [PATCH 040/108] (chore): setup dex storage adapter interface on idproxy start --- cmd/idproxy/main.go | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/cmd/idproxy/main.go b/cmd/idproxy/main.go index 83a90ba8a..ae9b02a24 100644 --- a/cmd/idproxy/main.go +++ b/cmd/idproxy/main.go @@ -56,23 +56,24 @@ func main() { } restCfg := ctrl.GetConfigOrDie() - ctx := context.Background() + ctx := context.TODO() k8sClient, err := clientutil.NewK8sClient(restCfg) if err != nil { log.Fatalf("failed to create k8s client: %s", err) } + backend := clientutil.Ptr("kubernetes") ghFeatures, err := features.NewFeatures(ctx, k8sClient) if err != nil { log.Fatalf("failed to get greenhouse features: %s", err) } - backend := ghFeatures.GetDexStorageType(ctx) - if backend == nil { - log.Fatalf("failed to get dex storage type") + if ghFeatures != nil { + backend = ghFeatures.GetDexStorageType(ctx) } - dexter, err := dexstore.NewDexStorageFactory(nil, logger, *backend) + dexter, err := dexstore.NewDexStorageFactory(logger.With("component", "storage"), *backend) if err != nil { log.Fatalf("failed to create dex storage interface: %s", err) } + logger.Info("using dex storage - ", "type", *backend) dexStorage := dexter.GetStorage() refreshPolicy, err := server.NewRefreshTokenPolicy(logger.With("component", "refreshtokenpolicy"), true, "24h", "24h", "5s") From a3f1211b7edd4c73ff355fb538106393b15c352e Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Sat, 1 Feb 2025 00:29:44 +0100 Subject: [PATCH 041/108] (chore): mock dex storage interface in unit tests --- pkg/controllers/organization/suite_test.go | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/pkg/controllers/organization/suite_test.go b/pkg/controllers/organization/suite_test.go index 2e4b55efa..13712b4f7 100644 --- a/pkg/controllers/organization/suite_test.go +++ b/pkg/controllers/organization/suite_test.go @@ -9,9 +9,12 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + "github.com/stretchr/testify/mock" "github.com/cloudoperators/greenhouse/pkg/admission" organizationpkg "github.com/cloudoperators/greenhouse/pkg/controllers/organization" + dexstore "github.com/cloudoperators/greenhouse/pkg/dex/store" + "github.com/cloudoperators/greenhouse/pkg/mocks" "github.com/cloudoperators/greenhouse/pkg/scim" "github.com/cloudoperators/greenhouse/pkg/test" ) @@ -28,8 +31,8 @@ func TestOrganization(t *testing.T) { var _ = BeforeSuite(func() { By("mocking SCIM server") groupsServer = scim.ReturnDefaultGroupResponseMockServer() - - test.RegisterController("organizationController", (&organizationpkg.OrganizationReconciler{Namespace: "default"}).SetupWithManager) + dexter := dexMocks() + test.RegisterController("organizationController", (&organizationpkg.OrganizationReconciler{Namespace: "default", Dexter: dexter}).SetupWithManager) test.RegisterWebhook("orgWebhook", admission.SetupOrganizationWebhookWithManager) test.RegisterWebhook("teamWebhook", admission.SetupTeamWebhookWithManager) test.RegisterWebhook("pluginDefinitionWebhook", admission.SetupPluginDefinitionWebhookWithManager) @@ -44,3 +47,11 @@ var _ = AfterSuite(func() { test.TestAfterSuite() }) + +func dexMocks() dexstore.Dexter { + dexter := &mocks.MockDexter{} + dexter.On("CreateUpdateConnector", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) + dexter.On("CreateUpdateOauth2Client", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) + dexter.On("GetBackend").Return(dexstore.K8s) + return dexter +} From 53825657dcc8a1bcb75696cdcf4ecb4432e40f6c Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Sat, 1 Feb 2025 00:52:03 +0100 Subject: [PATCH 042/108] (chore): adds inline code comments --- cmd/greenhouse/controllers.go | 3 + cmd/greenhouse/main.go | 3 + cmd/idproxy/main.go | 5 + go.mod | 2 +- go.sum | 259 ++++++---------------------------- pkg/dex/store/k8s_store.go | 10 +- pkg/dex/store/pg_store.go | 7 +- pkg/dex/store/storage.go | 10 +- 8 files changed, 77 insertions(+), 222 deletions(-) diff --git a/cmd/greenhouse/controllers.go b/cmd/greenhouse/controllers.go index dbb01797e..a877224e9 100644 --- a/cmd/greenhouse/controllers.go +++ b/cmd/greenhouse/controllers.go @@ -75,6 +75,9 @@ func isControllerEnabled(controllerName string) bool { return false } +// startOrganizationReconciler - setup the organization reconciler +// resolves dex storage backend from greenhouse-feature-flags +// initializes the dex storage adapter interface in the organization reconciler func startOrganizationReconciler(name string, mgr ctrl.Manager) error { namespace := "greenhouse" if v, ok := os.LookupEnv("POD_NAMESPACE"); ok { diff --git a/cmd/greenhouse/main.go b/cmd/greenhouse/main.go index 9f5a73a9f..1cfdb5d60 100644 --- a/cmd/greenhouse/main.go +++ b/cmd/greenhouse/main.go @@ -133,7 +133,10 @@ func main() { }) handleError(err, "unable to start manager") + // extract the manager API Client Reader + // Note: mgr.GetClient() will fail here because the cache is not ready yet k8sClient := mgr.GetAPIReader() + // Initialize the feature gates from feature-flags config map f, err = features.NewFeatures(context.TODO(), k8sClient) if err != nil { handleError(err, "unable to get features") diff --git a/cmd/idproxy/main.go b/cmd/idproxy/main.go index ae9b02a24..81209aa01 100644 --- a/cmd/idproxy/main.go +++ b/cmd/idproxy/main.go @@ -55,12 +55,15 @@ func main() { log.Fatal("No --issuer given") } + // ctrl.GetConfigOrDie() is used to get the k8s client config depending on the environment + // In Cluster config is used when running in a k8s cluster else uses the kubeconfig file specified by the KUBECONFIG env variable restCfg := ctrl.GetConfigOrDie() ctx := context.TODO() k8sClient, err := clientutil.NewK8sClient(restCfg) if err != nil { log.Fatalf("failed to create k8s client: %s", err) } + // default to kubernetes storage backend backend := clientutil.Ptr("kubernetes") ghFeatures, err := features.NewFeatures(ctx, k8sClient) if err != nil { @@ -69,11 +72,13 @@ func main() { if ghFeatures != nil { backend = ghFeatures.GetDexStorageType(ctx) } + // initialize dex storage adapter interface dexter, err := dexstore.NewDexStorageFactory(logger.With("component", "storage"), *backend) if err != nil { log.Fatalf("failed to create dex storage interface: %s", err) } logger.Info("using dex storage - ", "type", *backend) + // get the underlying dex storage interface dexStorage := dexter.GetStorage() refreshPolicy, err := server.NewRefreshTokenPolicy(logger.With("component", "refreshtokenpolicy"), true, "24h", "24h", "5s") diff --git a/go.mod b/go.mod index 89d80b31e..6241c262a 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,6 @@ replace ( require ( github.com/cenkalti/backoff/v4 v4.3.0 github.com/dexidp/dex v0.0.0-20240807174518-43956db7fd75 - github.com/ghodss/yaml v1.0.0 github.com/go-jose/go-jose/v4 v4.0.4 github.com/jeremywohl/flatten/v2 v2.0.0-20211013061545-07e4a09fb8e4 github.com/oklog/run v1.1.1-0.20240127200640-eee6e044b77c @@ -66,6 +65,7 @@ require ( github.com/distribution/reference v0.6.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/ghodss/yaml v1.0.0 // indirect github.com/go-sql-driver/mysql v1.8.1 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect diff --git a/go.sum b/go.sum index ee273c92a..21e62330d 100644 --- a/go.sum +++ b/go.sum @@ -1,23 +1,10 @@ -cel.dev/expr v0.16.1 h1:NR0+oFYzR1CqLFhTAqg3ql59G9VfN8fKq1TCHJ6gq1g= -cel.dev/expr v0.16.1/go.mod h1:AsGA5zb3WruAEQeQng1RZdGEXmBj0jvMWh6l5SnNuC8= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= -cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE= -cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U= +cloud.google.com/go/auth v0.9.2 h1:I+Rq388FYU8QdbVB1IiPd+6KNdrqtAPE/asiKHShBLM= cloud.google.com/go/auth v0.9.2/go.mod h1:7z6VY+7h3KUdRov5F1i8NDP5ZzWKYmEPO842BgCsmTk= -cloud.google.com/go/auth v0.11.0 h1:Ic5SZz2lsvbYcWT5dfjNWgw6tTlGi2Wc8hyQSC9BstA= -cloud.google.com/go/auth v0.11.0/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI= +cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY= cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc= -cloud.google.com/go/auth/oauth2adapt v0.2.6 h1:V6a6XDu2lTwPZWOawrAa9HUK+DB2zfJyTuciBG5hFkU= -cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8= +cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY= cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY= -cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixAHn3fyqngqo= -cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k= -cloud.google.com/go/iam v1.2.2 h1:ozUSofHUGf/F4tCNy/mu9tHLTaxZFLOUiKzjcgWHGIA= -cloud.google.com/go/iam v1.2.2/go.mod h1:0Ys8ccaZHdI1dEUilwzqng/6ps2YB6vRsjIe00/+6JY= -cloud.google.com/go/monitoring v1.21.2 h1:FChwVtClH19E7pJ+e0xUhJPGksctZNVOk2UhMmblmdU= -cloud.google.com/go/monitoring v1.21.2/go.mod h1:hS3pXvaG8KgWTSz+dAdyzPrGUYmi2Q+WFX8g2hqVEZU= -cloud.google.com/go/storage v1.48.0 h1:FhBDHACbVtdPx7S/AbcKujPWiHvfO6F8OXGgCEbB2+o= -cloud.google.com/go/storage v1.48.0/go.mod h1:aFoDYNMAjv67lp+xcuZqjUKv/ctmplzQ3wJgodA7b+M= dario.cat/mergo v1.0.1 h1:Ra4+bf83h2ztPIQYNP99R6m+Y7KfnARDfID+a+vLl4s= dario.cat/mergo v1.0.1/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk= filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= @@ -35,12 +22,6 @@ github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/k github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/DATA-DOG/go-sqlmock v1.5.2 h1:OcvFkGmslmlZibjAjaHm3L//6LiuBgolP7OputlJIzU= github.com/DATA-DOG/go-sqlmock v1.5.2/go.mod h1:88MAG/4G7SMwSE3CeA0ZKzrT5CiOU3OJ+JlNzwDqpNU= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.1 h1:pB2F2JKCj1Znmp2rwxxt1J0Fg0wezTMgWYk5Mpbi1kg= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.24.1/go.mod h1:itPGVDKf9cC/ov4MdvJ2QZ0khw4bfoo9jzwTJlaxy2k= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1 h1:UQ0AhxogsIRZDkElkblfnwjc3IaltCm2HUMvezQaL7s= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1/go.mod h1:jyqM3eLpJ3IbIFDTKVz2rF9T/xWGW0rIriGwnz8l9Tk= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1 h1:8nn+rsCvTq9axyEh382S0PFLBeaFwNsT43IrPWzctRU= -github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1/go.mod h1:viRWSEhtMZqz1rhwmOVKkWl6SwmVowfL9O2YR5gI2PE= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Masterminds/goutils v1.1.1 h1:5nUrii3FMTL5diU80unEVvNevw1nH4+ZV4DSLVJLSYI= @@ -61,42 +42,10 @@ github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuy github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI= github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4= -github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= -github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= -github.com/apache/arrow/go/arrow v0.0.0-20200730104253-651201b0f516 h1:byKBBF2CKWBjjA4J1ZL2JXttJULvWSl50LegTyRZ728= -github.com/apache/arrow/go/arrow v0.0.0-20200730104253-651201b0f516/go.mod h1:QNYViu/X0HXDHw7m3KXzWSVXIbfUvJqBFe6Gj8/pYA0= -github.com/apache/thrift v0.14.2 h1:hY4rAyg7Eqbb27GB6gkhUKrRAuc8xRjlNtJq+LseKeY= -github.com/apache/thrift v0.14.2/go.mod h1:cp2SuWMxlEZw2r+iP2GNCdIi4C1qmUzdZFSVb+bacwQ= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= -github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU= -github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU= -github.com/aws/aws-sdk-go-v2 v1.32.6 h1:7BokKRgRPuGmKkFMhEg/jSul+tB9VvXhcViILtfG8b4= -github.com/aws/aws-sdk-go-v2 v1.32.6/go.mod h1:P5WJBrYqqbWVaOxgH0X/FYYD47/nooaPOZPlQdmiN2U= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7 h1:lL7IfaFzngfx0ZwUGOZdsFFnQ5uLvR0hWqqhyE7Q9M8= -github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.7/go.mod h1:QraP0UcVlQJsmHfioCrveWOC1nbiWUl3ej08h4mXWoc= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.43 h1:iLdpkYZ4cXIQMO7ud+cqMWR1xK5ESbt1rvN77tRi1BY= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.43/go.mod h1:OgbsKPAswXDd5kxnR4vZov69p3oYjbvUyIRBAAV0y9o= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25 h1:s/fF4+yDQDoElYhfIVvSNyeCydfbuTKzhxSXDXCPasU= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.25/go.mod h1:IgPfDv5jqFIzQSNbUEMoitNooSMXjRSDkhXv8jiROvU= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25 h1:ZntTCl5EsYnhN/IygQEUugpdwbhdkom9uHcbCftiGgA= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.25/go.mod h1:DBdPrgeocww+CSl1C8cEV8PN1mHMBhuCDLpXezyvWkE= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.25 h1:r67ps7oHCYnflpgDy2LZU0MAQtQbYIOqNNnqGO6xQkE= -github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.25/go.mod h1:GrGY+Q4fIokYLtjCVB/aFfCVL6hhGUFl8inD18fDalE= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1 h1:iXtILhvDxB6kPvEXgsDhGaZCSC6LQET5ZHSdJozeI0Y= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.1/go.mod h1:9nu0fVANtYiAePIBh2/pFUSwtJ402hLnp854CNoDOeE= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.6 h1:HCpPsWqmYQieU7SS6E9HXfdAMSud0pteVXieJmcpIRI= -github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.4.6/go.mod h1:ngUiVRCco++u+soRRVBIvBZxSMMvOVMXA4PJ36JLfSw= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6 h1:50+XsN70RS7dwJ2CkVNXzj7U2L1HKP8nqTd3XWEXBN4= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.6/go.mod h1:WqgLmwY7so32kG01zD8CPTJWVWM+TzJoOVHwTg4aPug= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.6 h1:BbGDtTi0T1DYlmjBiCr/le3wzhA37O8QTC5/Ab8+EXk= -github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.6/go.mod h1:hLMJt7Q8ePgViKupeymbqI0la+t9/iYFBjxQCFwuAwI= -github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0 h1:nyuzXooUNJexRT0Oy0UQY6AhOzxPxhtt4DcBIHyCnmw= -github.com/aws/aws-sdk-go-v2/service/s3 v1.71.0/go.mod h1:sT/iQz8JK3u/5gZkT+Hmr7GzVZehUMkRZpOaAwYXeGY= -github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro= -github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/beevik/etree v1.1.0/go.mod h1:r8Aw8JqVegEf0w2fDnATrX9VpkMcyFeM0FhwO62wh+A= github.com/beevik/etree v1.4.1 h1:PmQJDDYahBGNKDcpdX8uPy1xRCwoCGVUiW669MEirVI= github.com/beevik/etree v1.4.1/go.mod h1:gPNJNaBGVZ9AwsidazFZyygnd+0pAU38N4D+WemwKNs= @@ -104,8 +53,6 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= -github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= @@ -113,16 +60,12 @@ github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= -github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v1.0.3 h1:9liNh8t+u26xl5ddmWLmsOsdNLwkdRTg5AG+JnTiM80= github.com/chai2010/gettext-go v1.0.3/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 h1:QVw89YDxXxEe+l8gU8ETbOasdwEV+avkR75ZzsVV9WI= -github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/cgroups/v3 v3.0.3 h1:S5ByHZ/h9PMe5IOQoN7E+nMc2UcLEM/V48DGDJ9kip0= github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0= @@ -140,8 +83,6 @@ github.com/coreos/go-oidc/v3 v3.11.0 h1:Ia3MxdwpSw702YW0xgfmP1GVCMA9aEFWu12XUZ3/ github.com/coreos/go-oidc/v3 v3.11.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= -github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -177,8 +118,6 @@ github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= -github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= -github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= @@ -186,11 +125,7 @@ github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRr github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= -github.com/envoyproxy/go-control-plane v0.13.0 h1:HzkeUz1Knt+3bK+8LG1bxOO/jzWZmdxpwC51i202les= -github.com/envoyproxy/go-control-plane v0.13.0/go.mod h1:GRaKG3dwvFoTg4nj7aXdZnvMg4d7nvT/wl9WgVXn3Q8= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6Uu2PdjCQwWCJ3bM= -github.com/envoyproxy/protoc-gen-validate v1.1.0/go.mod h1:sXRDRVmzEbkM7CVcM06s9shE/m23dg3wzjl0UWqJ2q4= github.com/evanphx/json-patch v5.9.0+incompatible h1:fBXyNpNMuTTDdquAq/uisOr2lShz4oaXpDTX2bLe7ls= github.com/evanphx/json-patch v5.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= @@ -234,8 +169,6 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= -github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= -github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= @@ -257,8 +190,6 @@ github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4er github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= -github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc= -github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -271,8 +202,6 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 h1:0VpGH+cDhbDtdcweoyCVsF3fhN8kejK6rFe/2FFX2nU= @@ -283,7 +212,6 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -299,12 +227,10 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3 github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/googleapis/enterprise-certificate-proxy v0.3.3 h1:QRje2j5GZimBzlbhGA2V2QlGNgL8G6e+wGo/+/2bWI0= github.com/googleapis/enterprise-certificate-proxy v0.3.3/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= -github.com/googleapis/enterprise-certificate-proxy v0.3.4 h1:XYIDZApgAnrN1c855gTgghdIA6Stxb52D5RnLI1SLyw= -github.com/googleapis/enterprise-certificate-proxy v0.3.4/go.mod h1:YKe7cfqYXjKGpGvmSg28/fFvhNzinZQm8DGnaburhGA= +github.com/googleapis/gax-go/v2 v2.13.0 h1:yitjD5f7jQHhyDsnhKEBU52NdvvdSeGzlAnDPT0hH1s= github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS338j1Is2S+B7A= -github.com/googleapis/gax-go/v2 v2.14.0 h1:f+jMrjBPl+DL9nI4IQzLUxMq7XrAqFYB7hBPqMNIe8o= -github.com/googleapis/gax-go/v2 v2.14.0/go.mod h1:lhBCnjdLrWRaPvLWhmc8IS24m9mr07qSYnHncrgo+zk= github.com/gorilla/handlers v1.5.2 h1:cLTUSsNkgcwhgRqvCNmdbRWG0A3N4F+M2nWKdScwyEE= github.com/gorilla/handlers v1.5.2/go.mod h1:dX+xVpaxdSw+q0Qek8SSsl3dfMk3jNddUkMzo0GtH0w= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= @@ -317,10 +243,8 @@ github.com/gosuri/uitable v0.0.4 h1:IG2xLKRvErL3uhY6e1BylFzG+aJiwQviDDTfOKeKTpY= github.com/gosuri/uitable v0.0.4/go.mod h1:tKR86bXuXPZazfOTG1FIzvjIdXzd0mo4Vtn16vt0PJo= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 h1:+ngKgrYPPJrOjhax5N+uePQ0Fh1Z7PheYoUI/0nzkPA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= -github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0 h1:ad0vkEBuk23VJzZR9nkLVG0YAoN9coASF1GusYX6AlU= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.23.0/go.mod h1:igFoXX2ELCW06bol23DWPB5BEWfZISOzSP5K2sbLea0= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -329,7 +253,7 @@ github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9 github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8= github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro= -github.com/hashicorp/golang-lru v1.0.2 h1:dV3g9Z/unq5DpblPpw+Oqcv4dU/1omnb4Ok8iPY6p1c= +github.com/hashicorp/golang-lru v0.5.4 h1:YDjusn29QI/Das2iO9M0BHnIbxPeyuCHsjMW+lJfyTc= github.com/hashicorp/golang-lru/arc/v2 v2.0.5 h1:l2zaLDubNhW4XO3LnliVj0GXO3+/CGNJAg1dcN2Fpfw= github.com/hashicorp/golang-lru/arc/v2 v2.0.5/go.mod h1:ny6zBSQZi2JxIeYcv7kt2sH2PXJtirBN7RDhRpxPkxU= github.com/hashicorp/golang-lru/v2 v2.0.5 h1:wW7h1TG88eUIJ2i69gaE3uNVtEPIagzhGvHgwfx2Vm4= @@ -340,14 +264,6 @@ github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= -github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.5.4 h1:Xp2aQS8uXButQdnCMWNmvx6UysWQQC+u1EoizjguY+8= -github.com/jackc/pgx/v5 v5.5.4/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= -github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= -github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= @@ -362,8 +278,6 @@ github.com/jcmturner/rpc/v2 v2.0.3 h1:7FXXj8Ti1IaVFpSAziCZWNzbNuZmnvw/i6CqLNdWfZ github.com/jcmturner/rpc/v2 v2.0.3/go.mod h1:VUJYCIDm3PVOEHw8sgt091/20OJjskO/YJki3ELg/Hc= github.com/jeremywohl/flatten/v2 v2.0.0-20211013061545-07e4a09fb8e4 h1:eA9wi6ZzpIRobvXkn/S2Lyw1hr2pc71zxzOPl7Xjs4w= github.com/jeremywohl/flatten/v2 v2.0.0-20211013061545-07e4a09fb8e4/go.mod h1:s9g9Dfls+aEgucKXKW+i8MRZuLXT2MrD/WjYpMnWfOw= -github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= -github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= @@ -378,9 +292,8 @@ github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHm github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= -github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc= -github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -402,10 +315,6 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= -github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= -github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattermost/xml-roundtrip-validator v0.1.0 h1:RXbVD2UAl7A7nOTR4u7E3ILa4IbtvKBHw64LDsmu9hU= @@ -420,8 +329,6 @@ github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mdelapenya/tlscert v0.1.0 h1:YTpF579PYUX475eOL+6zyEO3ngLTOUWck78NBuJVXaM= -github.com/mdelapenya/tlscert v0.1.0/go.mod h1:wrbyM/DwbFCeCeqdPX/8c6hNOqQgbf0rUDErE1uD+64= github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= @@ -430,20 +337,12 @@ github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQ github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= -github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= -github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= -github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU= github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= -github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= -github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= -github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo= -github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= @@ -457,25 +356,17 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= -github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/nikunjy/rules v1.5.0 h1:KJDSLOsFhwt7kcXUyZqwkgrQg5YoUwj+TVu6ItCQShw= -github.com/nikunjy/rules v1.5.0/go.mod h1:TlZtZdBChrkqi8Lr2AXocme8Z7EsbxtFdDoKeI6neBQ= github.com/oklog/run v1.1.1-0.20240127200640-eee6e044b77c h1:UPP5+t0sCRHk8JklGdZTjqaGRlukvgitcKlw0/mrjr0= github.com/oklog/run v1.1.1-0.20240127200640-eee6e044b77c/go.mod h1:mgDbKRSwPhJfesJ4PntqFUbKQRZ50NgmZTSPlFA0YFk= github.com/onsi/ginkgo/v2 v2.22.2 h1:/3X8Panh8/WwhU/3Ssa6rCKqPLuAkVY2I0RoyDLySlU= github.com/onsi/ginkgo/v2 v2.22.2/go.mod h1:oeMosUL+8LtarXBHu/c0bx2D/K9zyQ6uX3cTyztHwsk= github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= -github.com/open-feature/go-sdk v1.14.1 h1:jcxjCIG5Up3XkgYwWN5Y/WWfc6XobOhqrIwjyDBsoQo= -github.com/open-feature/go-sdk v1.14.1/go.mod h1:t337k0VB/t/YxJ9S0prT30ISUHwYmUd/jhUZgFcOvGg= -github.com/open-feature/go-sdk-contrib/providers/go-feature-flag-in-process v0.1.0 h1:EFIT5QBQ/T3lNVLmma69SNQbAWBgAl+EtcH0VfrdM7Y= -github.com/open-feature/go-sdk-contrib/providers/go-feature-flag-in-process v0.1.0/go.mod h1:DpptytCB+FbUIoRjTGtSDEA82aojWC4MIxL8GOK26Rs= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= @@ -488,20 +379,14 @@ github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+v github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= -github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ= -github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= -github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= -github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY= github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -516,9 +401,8 @@ github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY= github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= +github.com/prometheus/common v0.59.1 h1:LXb1quJHWm1P6wq/U824uxYi4Sg0oGvNeUm1z5dJoX0= github.com/prometheus/common v0.59.1/go.mod h1:GpWM7dewqmVYcd7SmRaiWVe9SSqjf0UrwnYnpEZNuT0= -github.com/prometheus/common v0.60.1 h1:FUas6GcOw66yB/73KC+BOZoFJmbo/1pojoILArPAaSc= -github.com/prometheus/common v0.60.1/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= @@ -528,8 +412,8 @@ github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5 h1:EaDatTxkdHG+U3Bk4EUr+DZ7fO github.com/redis/go-redis/extra/rediscmd/v9 v9.0.5/go.mod h1:fyalQWdtzDBECAQFBJuQe5bzQ02jGd5Qcbgb97Flm7U= github.com/redis/go-redis/extra/redisotel/v9 v9.0.5 h1:EfpWLLCyXw8PSM2/XNJLjI3Pb27yVE+gIAfeqp8LUCc= github.com/redis/go-redis/extra/redisotel/v9 v9.0.5/go.mod h1:WZjPDy7VNzn77AAfnAfVjZNvfJTYfPetfZk5yoSTLaQ= -github.com/redis/go-redis/v9 v9.7.0 h1:HhLSs+B6O021gwzl+locl0zEDnyNkxMtf/Z3NNBMa9E= -github.com/redis/go-redis/v9 v9.7.0/go.mod h1:f6zhXITC7JUJIlPEiBOTXxJgPLdZcA93GewI7inzyWw= +github.com/redis/go-redis/v9 v9.1.0 h1:137FnGdk+EQdCbye1FW+qOEcY5S+SpY9T0NiuqvtfMY= +github.com/redis/go-redis/v9 v9.1.0/go.mod h1:urWj3He21Dj5k4TK1y59xH8Uj6ATueP8AH1cY3lZl4c= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -545,12 +429,6 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= -github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= -github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= -github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= -github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= -github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= -github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -577,17 +455,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/testcontainers/testcontainers-go v0.35.0 h1:uADsZpTKFAtp8SLK+hMwSaa+X+JiERHtd4sQAFmXeMo= -github.com/testcontainers/testcontainers-go v0.35.0/go.mod h1:oEVBj5zrfJTrgjwONs1SsRbnBtH9OKl+IGl3UMcr2B4= -github.com/testcontainers/testcontainers-go/modules/postgres v0.35.0 h1:eEGx9kYzZb2cNhRbBrNOCL/YPOM7+RMJiy3bB+ie0/I= -github.com/testcontainers/testcontainers-go/modules/postgres v0.35.0/go.mod h1:hfH71Mia/WWLBgMD2YctYcMlfsbnT0hflweL1dy8Q4s= -github.com/thejerf/slogassert v0.3.4 h1:VoTsXixRbXMrRSSxDjYTiEDCM4VWbsYPW5rB/hX24kM= -github.com/thejerf/slogassert v0.3.4/go.mod h1:0zn9ISLVKo1aPMTqcGfG1o6dWwt+Rk574GlUxHD4rs8= -github.com/thomaspoignant/go-feature-flag v1.40.0 h1:BKNPwA9C1ZI8z34YrPv0Q4AhXFX8nSWXLeDC0/RGfe0= -github.com/thomaspoignant/go-feature-flag v1.40.0/go.mod h1:/R0ehOI15+7inVQvqm8ZCcqVkOFcRtrDqrb0qxRE7HY= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= @@ -598,10 +467,6 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= -github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= -github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= -github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= -github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/vladimirvivien/gexe v0.4.1 h1:W9gWkp8vSPjDoXDu04Yp4KljpVMaSt8IQuHswLDd5LY= github.com/vladimirvivien/gexe v0.4.1/go.mod h1:3gjgTqE2c0VyHnU5UOIwk7gyNzZDGulPb/DJPgcw64E= github.com/wI2L/jsondiff v0.6.1 h1:ISZb9oNWbP64LHnu4AUhsMF5W0FIj5Ok3Krip9Shqpw= @@ -615,68 +480,45 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHo github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= -github.com/xitongsys/parquet-go v1.6.2 h1:MhCaXii4eqceKPu9BwrjLqyK10oX9WF+xGhwvwbw7xM= -github.com/xitongsys/parquet-go v1.6.2/go.mod h1:IulAQyalCm0rPiZVNnCgm/PCL64X2tdSVGMQ/UeKqWA= -github.com/xitongsys/parquet-go-source v0.0.0-20230830030807-0dd610dbff1d h1:VVWj8KWdzpebBaXpTVpOaQW32y2UCWy3JXJ5lVDa/e8= -github.com/xitongsys/parquet-go-source v0.0.0-20230830030807-0dd610dbff1d/go.mod h1:HaLl1OAA7RAuQURU3Enxn7aRAI9yezsPPaxiGrbzxW4= github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= -github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/contrib/bridges/prometheus v0.57.0 h1:UW0+QyeyBVhn+COBec3nGhfnFe5lwB0ic1JBVjzhk0w= -go.opentelemetry.io/contrib/bridges/prometheus v0.57.0/go.mod h1:ppciCHRLsyCio54qbzQv0E4Jyth/fLWDTJYfvWpcSVk= -go.opentelemetry.io/contrib/detectors/gcp v1.29.0 h1:TiaiXB4DpGD3sdzNlYQxruQngn5Apwzi1X0DRhuGvDQ= -go.opentelemetry.io/contrib/detectors/gcp v1.29.0/go.mod h1:GW2aWZNwR2ZxDLdv8OyC2G8zkRoQBuURgV7RPQgcPoU= -go.opentelemetry.io/contrib/exporters/autoexport v0.57.0 h1:jmTVJ86dP60C01K3slFQa2NQ/Aoi7zA+wy7vMOKD9H4= -go.opentelemetry.io/contrib/exporters/autoexport v0.57.0/go.mod h1:EJBheUMttD/lABFyLXhce47Wr6DPWYReCzaZiXadH7g= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI= +go.opentelemetry.io/contrib/exporters/autoexport v0.46.1 h1:ysCfPZB9AjUlMa1UHYup3c9dAOCMQX/6sxSfPBUoxHw= +go.opentelemetry.io/contrib/exporters/autoexport v0.46.1/go.mod h1:ha0aiYm+DOPsLHjh0zoQ8W8sLT+LJ58J3j47lGpSLrU= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 h1:9G6E0TXzGFVfTnawRzrPl83iHOAV7L8NJiR8RSGYV1g= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8= +go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw= go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8= -go.opentelemetry.io/otel v1.32.0 h1:WnBN+Xjcteh0zdk01SVqV55d/m62NJLJdIyb4y/WO5U= -go.opentelemetry.io/otel v1.32.0/go.mod h1:00DCVSB0RQcnzlwyTfqtxSm+DRr9hpYrHjNGiBHVQIg= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0 h1:WzNab7hOOLzdDF/EoWCt4glhrbMPVMOO5JYTmpz36Ls= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.8.0/go.mod h1:hKvJwTzJdp90Vh7p6q/9PAOd55dI6WA6sWj62a/JvSs= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0 h1:S+LdBGiQXtJdowoJoQPEtI52syEP/JYBUpjO49EQhV8= -go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.8.0/go.mod h1:5KXybFvPGds3QinJWQT7pmXf+TN5YIa7CNYObWRkj50= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0 h1:j7ZSD+5yn+lo3sGV69nW04rRR0jhYnBwjuX3r0HvnK0= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.32.0/go.mod h1:WXbYJTUaZXAbYd8lbgGuvih0yuCfOFC5RJoYnoLcGz8= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0 h1:t/Qur3vKSkUCcDVaSumWF2PKHt85pc7fRvFuoVT8qFU= -go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.32.0/go.mod h1:Rl61tySSdcOJWoEgYZVtmnKdA0GeKrSqkHC1t+91CH8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0 h1:IJFEoHiytixx8cMiVAO+GmHR6Frwu+u5Ur8njpFO6Ac= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.32.0/go.mod h1:3rHrKNtLIoS0oZwkY2vxi+oJcwFRWdtUyRII+so45p8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0 h1:9kV11HXBHZAvuPUZxmMWrH8hZn/6UnHX4K0mu36vNsU= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.32.0/go.mod h1:JyA0FHXe22E1NeNiHmVp7kFHglnexDQ7uRWDiiJ1hKQ= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0 h1:cMyu9O88joYEaI47CnQkxO1XZdpoTF9fEnW2duIddhw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.32.0/go.mod h1:6Am3rn7P9TVVeXYG+wtcGE7IE1tsQ+bP3AuWcKt/gOI= -go.opentelemetry.io/otel/exporters/prometheus v0.54.0 h1:rFwzp68QMgtzu9PgP3jm9XaMICI6TsofWWPcBDKwlsU= -go.opentelemetry.io/otel/exporters/prometheus v0.54.0/go.mod h1:QyjcV9qDP6VeK5qPyKETvNjmaaEc7+gqjh4SS0ZYzDU= -go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.8.0 h1:CHXNXwfKWfzS65yrlB2PVds1IBZcdsX8Vepy9of0iRU= -go.opentelemetry.io/otel/exporters/stdout/stdoutlog v0.8.0/go.mod h1:zKU4zUgKiaRxrdovSS2amdM5gOc59slmo/zJwGX+YBg= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.32.0 h1:SZmDnHcgp3zwlPBS2JX2urGYe/jBKEIT6ZedHRUyCz8= -go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v1.32.0/go.mod h1:fdWW0HtZJ7+jNpTKUR0GpMEDP69nR8YBJQxNiVCE3jk= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.32.0 h1:cC2yDI3IQd0Udsux7Qmq8ToKAx1XCilTQECZ0KDZyTw= -go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.32.0/go.mod h1:2PD5Ex6z8CFzDbTdOlwyNIUywRr1DN0ospafJM1wJ+s= -go.opentelemetry.io/otel/log v0.8.0 h1:egZ8vV5atrUWUbnSsHn6vB8R21G2wrKqNiDt3iWertk= -go.opentelemetry.io/otel/log v0.8.0/go.mod h1:M9qvDdUTRCopJcGRKg57+JSQ9LgLBrwwfC32epk5NX8= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0 h1:jd0+5t/YynESZqsSyPz+7PAFdEop0dlN0+PkyHYo8oI= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.44.0/go.mod h1:U707O40ee1FpQGyhvqnzmCJm1Wh6OX6GGBVn0E6Uyyk= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0 h1:bflGWrfYyuulcdxf14V6n9+CoQcu5SAAdHmDPAJnlps= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v0.44.0/go.mod h1:qcTO4xHAxZLaLxPd60TdE88rxtItPHgHWqOhOGRr0as= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0 h1:digkEZCJWobwBqMwC0cwCq8/wkkRy/OowZg5OArWZrM= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.21.0/go.mod h1:/OpE/y70qVkndM0TrxT4KBoN3RsFZP0QaofcfYrj76I= +go.opentelemetry.io/otel/exporters/prometheus v0.44.0 h1:08qeJgaPC0YEBu2PQMbqU3rogTlyzpjhCI2b58Yn00w= +go.opentelemetry.io/otel/exporters/prometheus v0.44.0/go.mod h1:ERL2uIeBtg4TxZdojHUwzZfIFlUIjZtxubT5p4h1Gjg= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.44.0 h1:dEZWPjVN22urgYCza3PXRUGEyCB++y1sAqm6guWFesk= +go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.44.0/go.mod h1:sTt30Evb7hJB/gEk27qLb1+l9n4Tb8HvHkR0Wx3S6CU= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.21.0 h1:VhlEQAPp9R1ktYfrPk5SOryw1e9LDDTZCbIPFrho0ec= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.21.0/go.mod h1:kB3ufRbfU+CQ4MlUcqtW8Z7YEOBeK2DJ6CmR5rYYF3E= +go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc= go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8= -go.opentelemetry.io/otel/metric v1.32.0 h1:xV2umtmNcThh2/a/aCP+h64Xx5wsj8qqnkYZktzNa0M= -go.opentelemetry.io/otel/metric v1.32.0/go.mod h1:jH7CIbbK6SH2V2wE16W05BHCtIDzauciCRLoc/SyMv8= -go.opentelemetry.io/otel/sdk v1.32.0 h1:RNxepc9vK59A8XsgZQouW8ue8Gkb4jpWtJm9ge5lEG4= -go.opentelemetry.io/otel/sdk v1.32.0/go.mod h1:LqgegDBjKMmb2GC6/PrTnteJG39I8/vJCAP9LlJXEjU= -go.opentelemetry.io/otel/sdk/log v0.8.0 h1:zg7GUYXqxk1jnGF/dTdLPrK06xJdrXgqgFLnI4Crxvs= -go.opentelemetry.io/otel/sdk/log v0.8.0/go.mod h1:50iXr0UVwQrYS45KbruFrEt4LvAdCaWWgIrsN3ZQggo= -go.opentelemetry.io/otel/sdk/metric v1.32.0 h1:rZvFnvmvawYb0alrYkjraqJq0Z4ZUJAiyYCU9snn1CU= -go.opentelemetry.io/otel/sdk/metric v1.32.0/go.mod h1:PWeZlq0zt9YkYAp3gjKZ0eicRYvOh1Gd+X99x6GHpCQ= +go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= +go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= +go.opentelemetry.io/otel/sdk/metric v1.21.0 h1:smhI5oD714d6jHE6Tie36fPx4WDFIg+Y6RfAY4ICcR0= +go.opentelemetry.io/otel/sdk/metric v1.21.0/go.mod h1:FJ8RAsoPGv/wYMgBdUJXOm+6pzFY3YdljnXtv1SBE8Q= +go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4= go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ= -go.opentelemetry.io/otel/trace v1.32.0 h1:WIC9mYrXf8TmY/EXuULKc8hR17vE+Hjv2cssQDe03fM= -go.opentelemetry.io/otel/trace v1.32.0/go.mod h1:+i4rkvCraA+tG6AzwloGaCtkx53Fa+L+V8e9a7YvhT8= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.starlark.net v0.0.0-20240725214946-42030a7cedce h1:YyGqCjZtGZJ+mRPaenEiB87afEO2MFRzLiJNZ0Z0bPw= @@ -731,9 +573,8 @@ golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= golang.org/x/net v0.33.0 h1:74SYHlV8BIgHIFC/LrYkOGIwL19eTYXQ5wc6TBuO36I= golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.22.0 h1:BzDx2FehcG7jJwgWLELCdmLuxk2i+x9UDpSiss2u0ZA= golang.org/x/oauth2 v0.22.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= -golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -750,10 +591,8 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -763,8 +602,6 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= @@ -803,36 +640,26 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= -golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= +google.golang.org/api v0.195.0 h1:Ude4N8FvTKnnQJHU48RFI40jOBgIrL8Zqr3/QeST6yU= google.golang.org/api v0.195.0/go.mod h1:DOGRWuv3P8TU8Lnz7uQc4hyNqrBpMtD9ppW3wBJurgc= -google.golang.org/api v0.210.0 h1:HMNffZ57OoZCRYSbdWVRoqOa8V8NIHLL0CzdBPLztWk= -google.golang.org/api v0.210.0/go.mod h1:B9XDZGnx2NtyjzVkOVTGrFSAVZgPcbedzKg/gTLwqBs= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk= -google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc= +google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed h1:3RgNmBoI9MZhsj3QxC+AP/qQhNwpCLOvYDYYsFrhFt0= google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo= -google.golang.org/genproto/googleapis/api v0.0.0-20241113202542-65e8d215514f h1:M65LEviCfuZTfrfzwwEoxVtgvfkFkBUbFnRbxCXuXhU= -google.golang.org/genproto/googleapis/api v0.0.0-20241113202542-65e8d215514f/go.mod h1:Yo94eF2nj7igQt+TiJ49KxjIH8ndLYPZMIRSiRcEbg0= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed h1:J6izYgfBXAI3xTKLgxzTmUltdYaLsuBxFCgDHWJ/eXg= google.golang.org/genproto/googleapis/rpc v0.0.0-20240827150818-7e3bb234dfed/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697 h1:LWZqQOEjDyONlF1H6afSWpAL/znlREo2tHfLoe+8LMA= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.66.3 h1:TWlsh8Mv0QI/1sIbs1W36lqRclxrmF+eFJ4DbI0fuhA= google.golang.org/grpc v1.66.3/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y= -google.golang.org/grpc v1.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0= -google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= -google.golang.org/grpc/stats/opentelemetry v0.0.0-20240907200651-3ffb98b2c93a h1:UIpYSuWdWHSzjwcAFRLjKcPXFZVVLXGEM23W+NWqipw= -google.golang.org/grpc/stats/opentelemetry v0.0.0-20240907200651-3ffb98b2c93a/go.mod h1:9i1T9n4ZinTUZGgzENMi8MDDgbGC5mqTS75JAv6xN3A= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= diff --git a/pkg/dex/store/k8s_store.go b/pkg/dex/store/k8s_store.go index ebcd523fb..a0ec4d114 100644 --- a/pkg/dex/store/k8s_store.go +++ b/pkg/dex/store/k8s_store.go @@ -33,6 +33,7 @@ type k8sDex struct { const encoding = "abcdefghijklmnopqrstuvwxyz234567" +// newKubernetesStore - creates a new kubernetes storage backend for dex func newKubernetesStore(logger *slog.Logger) (storage.Storage, error) { cfg := kubernetes.Config{InCluster: true} kEnv := clientutil.GetEnvOrDefault("KUBECONFIG", "") @@ -51,6 +52,7 @@ func (k *k8sDex) GetBackend() string { return k.backend } +// CreateUpdateConnector - creates or updates a dex connector in dex kubernetes storage backend func (k *k8sDex) CreateUpdateConnector(ctx context.Context, k8sClient client.Client, org *greenhouseapisv1alpha1.Organization, configByte []byte, namespace string) (err error) { var result clientutil.OperationResult var dexConnector = new(dexapi.Connector) @@ -75,11 +77,12 @@ func (k *k8sDex) CreateUpdateConnector(ctx context.Context, k8sClient client.Cli return } +// CreateUpdateOauth2Client - creates or updates an oauth2 client in dex kubernetes storage backend func (k *k8sDex) CreateUpdateOauth2Client(ctx context.Context, k8sClient client.Client, org *greenhouseapisv1alpha1.Organization, namespace string) error { var oAuth2Client = new(dexapi.OAuth2Client) oAuth2Client.ObjectMeta.Name = encodedOAuth2ClientName(org.Name) oAuth2Client.ObjectMeta.Namespace = namespace - generatedClientSecret := getDeterministicSecret(org.Name, org.GetResourceVersion(), org.GetUID()) + generatedClientSecret := getDeterministicSecret(org.Name, org.GetUID()) result, err := clientutil.CreateOrPatch(ctx, k8sClient, oAuth2Client, func() error { oAuth2Client.Client.Public = true @@ -114,14 +117,17 @@ func (k *k8sDex) CreateUpdateOauth2Client(ctx context.Context, k8sClient client. return nil } +// encodedOAuth2ClientName - encodes the org name to a base32 string +// for kubernetes backend storage we need to encode the name OAuth2Client CR name +// See https://github.com/dexidp/dex/issues/1606 for encoding func encodedOAuth2ClientName(orgName string) string { - // See https://github.com/dexidp/dex/issues/1606 for encoding. return strings.TrimRight(base32. NewEncoding(encoding). EncodeToString(fnv.New64().Sum([]byte(orgName))), "=", ) } +// GetStorage - returns the underlying dex storage interface func (k *k8sDex) GetStorage() storage.Storage { return k.storage } diff --git a/pkg/dex/store/pg_store.go b/pkg/dex/store/pg_store.go index 7620dc39d..1df6bc1f9 100644 --- a/pkg/dex/store/pg_store.go +++ b/pkg/dex/store/pg_store.go @@ -35,6 +35,7 @@ const ( dbNameEnv = "DB_DATABASE" ) +// newPostgresStore - creates a new postgres storage backend for dex func newPostgresStore(logger *slog.Logger) (storage.Storage, error) { var host, user, pass, database string var port int @@ -65,6 +66,7 @@ func (p *pgDex) GetBackend() string { return p.backend } +// CreateUpdateConnector - creates or updates a dex connector in dex postgres storage backend func (p *pgDex) CreateUpdateConnector(ctx context.Context, _ client.Client, org *greenhouseapisv1alpha1.Organization, configByte []byte, _ string) error { oidcConnector, err := p.storage.GetConnector(org.Name) if err != nil && errors.Is(err, storage.ErrNotFound) { @@ -96,8 +98,9 @@ func (p *pgDex) CreateUpdateConnector(ctx context.Context, _ client.Client, org return nil } +// CreateUpdateOauth2Client - creates or updates an oauth2 client in dex postgres storage backend func (p *pgDex) CreateUpdateOauth2Client(ctx context.Context, k8sClient client.Client, org *greenhouseapisv1alpha1.Organization, namespace string) error { - generatedClientSecret := getDeterministicSecret(org.Name, org.GetResourceVersion(), org.GetUID()) + generatedClientSecret := getDeterministicSecret(org.Name, org.GetUID()) oAuthClient, err := p.storage.GetClient(org.Name) if err != nil && errors.Is(err, storage.ErrNotFound) { if err = p.storage.CreateClient(ctx, storage.Client{ @@ -138,6 +141,7 @@ func (p *pgDex) CreateUpdateOauth2Client(ctx context.Context, k8sClient client.C if err != nil { return err } + // write the client credentials to the organization namespace secret := prepareClientSecret(namespace, org.Name, generatedClientSecret) err = writeCredentialsToNamespace(ctx, k8sClient, secret) if err != nil { @@ -147,6 +151,7 @@ func (p *pgDex) CreateUpdateOauth2Client(ctx context.Context, k8sClient client.C return nil } +// GetStorage - returns the underlying dex storage interface func (p *pgDex) GetStorage() storage.Storage { return p.storage } diff --git a/pkg/dex/store/storage.go b/pkg/dex/store/storage.go index 64ee4b4b6..52429ba83 100644 --- a/pkg/dex/store/storage.go +++ b/pkg/dex/store/storage.go @@ -29,6 +29,8 @@ const ( clientSecretKey = "clientSecret" ) +// Dexter - dex storage adapter interface +// Supported backends: Postgres, K8s type Dexter interface { CreateUpdateConnector(ctx context.Context, k8sClient client.Client, org *greenhouseapisv1alpha1.Organization, configByte []byte, namespace string) error CreateUpdateOauth2Client(ctx context.Context, k8sClient client.Client, org *greenhouseapisv1alpha1.Organization, namespace string) error @@ -37,6 +39,7 @@ type Dexter interface { Close() error } +// NewDexStorageFactory - create a new dex storage adapter depending on the backend func NewDexStorageFactory(logger *slog.Logger, backend string) (Dexter, error) { switch backend { case Postgres: @@ -56,12 +59,14 @@ func NewDexStorageFactory(logger *slog.Logger, backend string) (Dexter, error) { } } -func getDeterministicSecret(clientID, version string, secretKey types.UID) string { +// getDeterministicSecret - generate a deterministic secret based on the clientID and secretKey +func getDeterministicSecret(clientID string, secretKey types.UID) string { h := hmac.New(sha256.New, []byte(secretKey)) - h.Write([]byte(version + clientID)) + h.Write([]byte(clientID)) return hex.EncodeToString(h.Sum(nil))[:32] } +// prepareClientSecret - create a coreV1 secret with the clientID and secret func prepareClientSecret(namespace, clientID, clientSecret string) *corev1.Secret { secret := new(corev1.Secret) secret.SetName(clientID + "-dex-secrets") @@ -73,6 +78,7 @@ func prepareClientSecret(namespace, clientID, clientSecret string) *corev1.Secre return secret } +// writeCredentialsToNamespace - write the client credentials to the organization namespace func writeCredentialsToNamespace(ctx context.Context, cl client.Client, secret *corev1.Secret) error { result, err := clientutil.CreateOrPatch(ctx, cl, secret, func() error { return nil From b3857ffff361324aecf1febe4f0918f695dd34c6 Mon Sep 17 00:00:00 2001 From: License Bot Date: Fri, 31 Jan 2025 23:53:58 +0000 Subject: [PATCH 043/108] Automatic application of license header --- pkg/features/features_test.go | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkg/features/features_test.go b/pkg/features/features_test.go index 4f062e3e4..b08f0ab33 100644 --- a/pkg/features/features_test.go +++ b/pkg/features/features_test.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors +// SPDX-License-Identifier: Apache-2.0 + package features import ( From 4a9fdcb4bc9d93ae7f596ea2eb488b039d98a470 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Sat, 1 Feb 2025 02:21:35 +0100 Subject: [PATCH 044/108] (chore): use create update for writing oauth2client secrets --- pkg/dex/store/k8s_store.go | 2 +- pkg/dex/store/pg_store.go | 12 +++++------ pkg/dex/store/storage.go | 44 +++++++++++++++++++++++++++++--------- 3 files changed, 41 insertions(+), 17 deletions(-) diff --git a/pkg/dex/store/k8s_store.go b/pkg/dex/store/k8s_store.go index a0ec4d114..0c8749b2f 100644 --- a/pkg/dex/store/k8s_store.go +++ b/pkg/dex/store/k8s_store.go @@ -110,7 +110,7 @@ func (k *k8sDex) CreateUpdateOauth2Client(ctx context.Context, k8sClient client. } secret := prepareClientSecret(namespace, org.Name, generatedClientSecret) - err = writeCredentialsToNamespace(ctx, k8sClient, secret) + err = writeCredentialsToNamespace(ctx, k8sClient, org, secret) if err != nil { return err } diff --git a/pkg/dex/store/pg_store.go b/pkg/dex/store/pg_store.go index 1df6bc1f9..481a50c41 100644 --- a/pkg/dex/store/pg_store.go +++ b/pkg/dex/store/pg_store.go @@ -28,11 +28,11 @@ type pgDex struct { } const ( - hostEnv = "DB_HOST" - portEnv = "DB_PORT" - userEnv = "DB_USER" - passEnv = "DB_PASSWORD" - dbNameEnv = "DB_DATABASE" + hostEnv = "PG_HOST" + portEnv = "PG_PORT" + userEnv = "PG_USER" + passEnv = "PG_PASSWORD" + dbNameEnv = "PG_DATABASE" ) // newPostgresStore - creates a new postgres storage backend for dex @@ -143,7 +143,7 @@ func (p *pgDex) CreateUpdateOauth2Client(ctx context.Context, k8sClient client.C } // write the client credentials to the organization namespace secret := prepareClientSecret(namespace, org.Name, generatedClientSecret) - err = writeCredentialsToNamespace(ctx, k8sClient, secret) + err = writeCredentialsToNamespace(ctx, k8sClient, org, secret) if err != nil { return err } diff --git a/pkg/dex/store/storage.go b/pkg/dex/store/storage.go index 52429ba83..161dbd48f 100644 --- a/pkg/dex/store/storage.go +++ b/pkg/dex/store/storage.go @@ -14,11 +14,11 @@ import ( "github.com/dexidp/dex/storage" corev1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/types" + controllerruntime "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/log" greenhouseapisv1alpha1 "github.com/cloudoperators/greenhouse/pkg/apis/greenhouse/v1alpha1" - "github.com/cloudoperators/greenhouse/pkg/clientutil" ) const ( @@ -79,18 +79,42 @@ func prepareClientSecret(namespace, clientID, clientSecret string) *corev1.Secre } // writeCredentialsToNamespace - write the client credentials to the organization namespace -func writeCredentialsToNamespace(ctx context.Context, cl client.Client, secret *corev1.Secret) error { - result, err := clientutil.CreateOrPatch(ctx, cl, secret, func() error { +// if the secret already exists, update the secret +// set organization as the owner reference for the secret +func writeCredentialsToNamespace(ctx context.Context, cl client.Client, org *greenhouseapisv1alpha1.Organization, secret *corev1.Secret) error { + existing := &corev1.Secret{} + err := cl.Get(ctx, types.NamespacedName{Name: secret.Name, Namespace: secret.Namespace}, existing) + if err != nil { + if client.IgnoreNotFound(err) != nil { + log.FromContext(ctx).Error(err, "unable to get dex client credentials", "name", secret.Name, "namespace", secret.Namespace) + return err + } + // if not found, create the secret with owner reference + // set the owner reference + err = controllerruntime.SetControllerReference(org, secret, cl.Scheme()) + if err != nil { + log.FromContext(ctx).Error(err, "unable to set controller reference for dex client credentials", "name", secret.Name, "namespace", secret.Namespace) + return err + } + err = cl.Create(ctx, secret) + if err != nil { + log.FromContext(ctx).Error(err, "unable to create dex client credentials", "name", secret.Name, "namespace", secret.Namespace) + return err + } return nil - }) + } + + existing.Data[clientIDKey] = secret.Data[clientIDKey] + existing.Data[clientSecretKey] = secret.Data[clientSecretKey] + err = controllerruntime.SetControllerReference(org, existing, cl.Scheme()) if err != nil { - log.FromContext(ctx).Error(err, "unable to create dex client credentials", "name", secret.Name, "namespace", secret.Namespace) + log.FromContext(ctx).Error(err, "unable to create / patch dex client credentials", "name", secret.Name, "namespace", secret.Namespace) + return err } - switch result { - case clientutil.OperationResultCreated: - log.FromContext(ctx).Info("created oauth2client secrets", "name", secret.Name, "namespace", secret.Namespace) - case clientutil.OperationResultUpdated: - log.FromContext(ctx).Info("updated oauth2client secrets", "name", secret.Name, "namespace", secret.Namespace) + err = cl.Update(ctx, existing) + if err != nil { + log.FromContext(ctx).Error(err, "unable to update dex client credentials", "name", secret.Name, "namespace", secret.Namespace) + return err } return nil } From faba477dc6a0dabeb43062a99c31874f04facdc9 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Sat, 1 Feb 2025 02:50:37 +0100 Subject: [PATCH 045/108] (chore): adds docs to run dex locally --- cmd/greenhouse/README.md | 30 ++++++++++++++++++++++++ cmd/idproxy/README.md | 50 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 80 insertions(+) create mode 100644 cmd/greenhouse/README.md create mode 100644 cmd/idproxy/README.md diff --git a/cmd/greenhouse/README.md b/cmd/greenhouse/README.md new file mode 100644 index 000000000..8e8e63afc --- /dev/null +++ b/cmd/greenhouse/README.md @@ -0,0 +1,30 @@ +## Running Greenhouse Operator Locally + +An extensive guide is available in the [localenv](../../dev-env/localenv/README.md) documentation. + +This is a quick note on running the operator with Dex storage backend configuration + +### Using `kubernetes` as the Dex storage backend + +If you are using `kubernetes` as the dex storage backend, you need to set the following environment variables: + +- `KUBECONFIG=` + +> [!NOTE] +> If your kube configs (for KinD) are merged in the default ~/.kube/config path +> then you can set the `KUBECONFIG` environment variable to `~/.kube/config` and set the current context to +> kind-greenhouse-admin +> `KUBECONFIG` is needed because dex will revert to InCluster mode if KUBECONFIG is not set + +### Using `postgres` as the Dex storage backend + +If you are using `postgres` as the dex storage backend, you need to set the following environment variables: + +- `PG_DATABASE=` ex: `postgres` (defaults to `postgres` if not set) +- `PG_PORT=` ex: `5432` (defaults to `5432` if not set) +- `PG_USER=` ex: `postgres` (defaults to `postgres` if not set) +- `PG_HOST=` ex: `localhost` (required) +- `PG_PASSWORD=` ex: `password` (required) + +> [!NOTE] +> Explicitly setting `KUBECONFIG` is not required when using `postgres` as the dex storage backend \ No newline at end of file diff --git a/cmd/idproxy/README.md b/cmd/idproxy/README.md new file mode 100644 index 000000000..e8c68dc9f --- /dev/null +++ b/cmd/idproxy/README.md @@ -0,0 +1,50 @@ +## Running `idprox` service locally + +You can run the `idproxy` service locally by setting the following arguments and environment variables - + +## Prerequisites + +- Greenhouse Organization with `oidc` authentication in `spec.authentication` + +> [!NOTE] +> If you do not have a proper IDP to authenticate with, you can run `keycloak` locally in a docker container +> Or you can use a mock oauth2 server like [ghcr.io/navikt/mock-oauth2-server](https://github.com/navikt/mock-oauth2-server) +> Disclaimer: `keycloak` and `mock-oauth2-server` are not tested with `idproxy` service + +Arguments: + +- `--listen-addr=:` ex: `--listen-addr=:8085` (defaults to `:8080` if not set) +- `--issuer=` ex: `--issuer=http://localhost:` (required) + +Environment Variables: + +- If you are using `kubernetes` as the dex storage backend, set the following environment variables: + - `KUBECONFIG=` + +- If you are using `postgres` as the dex storage backend, set the following environment variables: + - `PG_DATABASE=` ex: `postgres` (defaults to `postgres` if not set) + - `PG_PORT=` ex: `5432` (defaults to `5432` if not set) + - `PG_USER=` ex: `postgres` (defaults to `postgres` if not set) + - `PG_HOST=` ex: `localhost` (required) + - `PG_PASSWORD=` ex: `password` (required) + +> [!NOTE] +> There should be a configured `Connector` and `OAuth2Client` for the `idproxy` service to work properly. +> The `Connector` and `OAuth2Client` is created when you create an `Organization` + +## Testing `idproxy` service locally + +You can test the `idproxy` service locally by initiating an `oauth2` flow with grant type `authorization_code` - + +- Visit the well-known endpoint `http://localhost:/.well-known/openid-configuration` to get the `authorization_endpoint` and `token_endpoint` +- Use `insomnia` or `postman` tool +- Create an empty http request +- Set `Auth` type to `OAuth 2.0` +- Set `Authorization` and `Token` endpoint to the values obtained from the well-known endpoint +- Set the `client id` and `client secret` (available as `-dex-secrets` secret in the `` namespace) +- Set the redirect uri to `http://localhost:8000` +- Minimum scope required is `openid` (Additionally you can set `email` `groups` `offline_access`) +- Send the request and you will be redirected to the `dex` login page +- Choose the provider you want to authenticate with +- You may be asked to enter the `username` and `password` for the provider +- Once authenticated you should see the `id_token` and `access_token` automatically populated in `insomnia` or `postman` tool \ No newline at end of file From 43a284b7db6f44564197cd67da1772ae52c59c14 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Wed, 5 Feb 2025 17:22:40 +0100 Subject: [PATCH 046/108] (chore): auto determine kubernetes automatically determines the kubernetes client mode without explicitly specifying any env variable --- cmd/greenhouse/README.md | 17 +++++------------ cmd/idproxy/README.md | 6 ++++-- cmd/idproxy/main.go | 2 +- pkg/dex/store/k8s_store.go | 24 +++++++++++++++++++++--- 4 files changed, 31 insertions(+), 18 deletions(-) diff --git a/cmd/greenhouse/README.md b/cmd/greenhouse/README.md index 8e8e63afc..061b6766e 100644 --- a/cmd/greenhouse/README.md +++ b/cmd/greenhouse/README.md @@ -6,15 +6,11 @@ This is a quick note on running the operator with Dex storage backend configurat ### Using `kubernetes` as the Dex storage backend -If you are using `kubernetes` as the dex storage backend, you need to set the following environment variables: +When running the operator locally the kubernetes mode is automatically detected: -- `KUBECONFIG=` - -> [!NOTE] -> If your kube configs (for KinD) are merged in the default ~/.kube/config path -> then you can set the `KUBECONFIG` environment variable to `~/.kube/config` and set the current context to -> kind-greenhouse-admin -> `KUBECONFIG` is needed because dex will revert to InCluster mode if KUBECONFIG is not set +1. `KUBECONFIG` environment variable - example - `export KUBECONFIG=/path/to/config` (Priority 1) +2. kube config from the recommended dir and file - example - `$HOME/.kube/config` (Priority 2) +3. Running inside a kubernetes cluster, `in-cluster` mode is used. (Priority 3) ### Using `postgres` as the Dex storage backend @@ -24,7 +20,4 @@ If you are using `postgres` as the dex storage backend, you need to set the foll - `PG_PORT=` ex: `5432` (defaults to `5432` if not set) - `PG_USER=` ex: `postgres` (defaults to `postgres` if not set) - `PG_HOST=` ex: `localhost` (required) -- `PG_PASSWORD=` ex: `password` (required) - -> [!NOTE] -> Explicitly setting `KUBECONFIG` is not required when using `postgres` as the dex storage backend \ No newline at end of file +- `PG_PASSWORD=` ex: `password` (required) \ No newline at end of file diff --git a/cmd/idproxy/README.md b/cmd/idproxy/README.md index e8c68dc9f..3283623a7 100644 --- a/cmd/idproxy/README.md +++ b/cmd/idproxy/README.md @@ -18,8 +18,10 @@ Arguments: Environment Variables: -- If you are using `kubernetes` as the dex storage backend, set the following environment variables: - - `KUBECONFIG=` +- If you are using `kubernetes` as the dex storage backend, kubernetes mode is automatically detected + 1. `KUBECONFIG` environment variable - example - `export KUBECONFIG=/path/to/config` (Priority 1) + 2. kube config from the recommended dir and file - example - `$HOME/.kube/config` (Priority 2) + 3. Running inside a kubernetes cluster, `in-cluster` mode is used. (Priority 3) - If you are using `postgres` as the dex storage backend, set the following environment variables: - `PG_DATABASE=` ex: `postgres` (defaults to `postgres` if not set) diff --git a/cmd/idproxy/main.go b/cmd/idproxy/main.go index 81209aa01..cedf07f52 100644 --- a/cmd/idproxy/main.go +++ b/cmd/idproxy/main.go @@ -56,7 +56,7 @@ func main() { } // ctrl.GetConfigOrDie() is used to get the k8s client config depending on the environment - // In Cluster config is used when running in a k8s cluster else uses the kubeconfig file specified by the KUBECONFIG env variable + // In-Cluster config is used when running in a k8s cluster else uses the kubeconfig file specified by the KUBECONFIG env variable restCfg := ctrl.GetConfigOrDie() ctx := context.TODO() k8sClient, err := clientutil.NewK8sClient(restCfg) diff --git a/pkg/dex/store/k8s_store.go b/pkg/dex/store/k8s_store.go index 0c8749b2f..cabdf975e 100644 --- a/pkg/dex/store/k8s_store.go +++ b/pkg/dex/store/k8s_store.go @@ -9,12 +9,17 @@ import ( "fmt" "hash/fnv" "log/slog" + "os" + "path/filepath" "strings" "github.com/dexidp/dex/storage" "github.com/dexidp/dex/storage/kubernetes" "golang.org/x/text/cases" "golang.org/x/text/language" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/util/homedir" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/log" @@ -36,10 +41,10 @@ const encoding = "abcdefghijklmnopqrstuvwxyz234567" // newKubernetesStore - creates a new kubernetes storage backend for dex func newKubernetesStore(logger *slog.Logger) (storage.Storage, error) { cfg := kubernetes.Config{InCluster: true} - kEnv := clientutil.GetEnvOrDefault("KUBECONFIG", "") - if strings.TrimSpace(kEnv) != "" { + cfgPath := determineKubeMode() + if strings.TrimSpace(cfgPath) != "" { cfg.InCluster = false - cfg.KubeConfigFile = kEnv + cfg.KubeConfigFile = cfgPath } dexStorage, err := cfg.Open(logger) if err != nil { @@ -48,6 +53,19 @@ func newKubernetesStore(logger *slog.Logger) (storage.Storage, error) { return dexStorage, nil } +func determineKubeMode() string { + cfgPath := os.Getenv(clientcmd.RecommendedConfigPathEnvVar) + if len(cfgPath) > 0 { + return cfgPath + } + _, err := rest.InClusterConfig() + if err == nil { + return "" + } + home := homedir.HomeDir() + return filepath.Join(home, ".kube", "config") +} + func (k *k8sDex) GetBackend() string { return k.backend } From cdc76eb1cf659817ccded6ba0a34736702fe20b2 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Wed, 5 Feb 2025 17:33:54 +0100 Subject: [PATCH 047/108] (chore): uses org name as dex resource namespace --- pkg/controllers/organization/dex.go | 4 +- pkg/dex/store/k8s_store.go | 21 ++++----- pkg/dex/store/pg_store.go | 17 ++----- pkg/dex/store/storage.go | 73 +---------------------------- 4 files changed, 15 insertions(+), 100 deletions(-) diff --git a/pkg/controllers/organization/dex.go b/pkg/controllers/organization/dex.go index 7d5ff624b..1a983e192 100644 --- a/pkg/controllers/organization/dex.go +++ b/pkg/controllers/organization/dex.go @@ -58,7 +58,7 @@ func (r *OrganizationReconciler) reconcileDexConnector(ctx context.Context, org if err != nil { return err } - return r.Dexter.CreateUpdateConnector(ctx, r.Client, org, configByte, org.Name) + return r.Dexter.CreateUpdateConnector(ctx, r.Client, org, configByte) } func (r *OrganizationReconciler) enqueueOrganizationForReferencedSecret(_ context.Context, o client.Object) []ctrl.Request { @@ -88,7 +88,7 @@ func (r *OrganizationReconciler) discoverOIDCRedirectURL(ctx context.Context, or } func (r *OrganizationReconciler) reconcileOAuth2Client(ctx context.Context, org *greenhousesapv1alpha1.Organization) error { - return r.Dexter.CreateUpdateOauth2Client(ctx, r.Client, org, org.Name) + return r.Dexter.CreateUpdateOauth2Client(ctx, r.Client, org) } func ensureCallbackURL(url string) string { diff --git a/pkg/dex/store/k8s_store.go b/pkg/dex/store/k8s_store.go index cabdf975e..30930bd63 100644 --- a/pkg/dex/store/k8s_store.go +++ b/pkg/dex/store/k8s_store.go @@ -71,11 +71,12 @@ func (k *k8sDex) GetBackend() string { } // CreateUpdateConnector - creates or updates a dex connector in dex kubernetes storage backend -func (k *k8sDex) CreateUpdateConnector(ctx context.Context, k8sClient client.Client, org *greenhouseapisv1alpha1.Organization, configByte []byte, namespace string) (err error) { +func (k *k8sDex) CreateUpdateConnector(ctx context.Context, k8sClient client.Client, org *greenhouseapisv1alpha1.Organization, configByte []byte) (err error) { var result clientutil.OperationResult var dexConnector = new(dexapi.Connector) - dexConnector.Namespace = namespace - dexConnector.ObjectMeta.Name = org.GetName() + namespaceName := org.GetName() + dexConnector.SetName(namespaceName) + dexConnector.SetNamespace(namespaceName) result, err = clientutil.CreateOrPatch(ctx, k8sClient, dexConnector, func() error { dexConnector.DexConnector.Type = dexConnectorTypeGreenhouse dexConnector.DexConnector.Name = cases.Title(language.English).String(org.Name) @@ -96,16 +97,15 @@ func (k *k8sDex) CreateUpdateConnector(ctx context.Context, k8sClient client.Cli } // CreateUpdateOauth2Client - creates or updates an oauth2 client in dex kubernetes storage backend -func (k *k8sDex) CreateUpdateOauth2Client(ctx context.Context, k8sClient client.Client, org *greenhouseapisv1alpha1.Organization, namespace string) error { +func (k *k8sDex) CreateUpdateOauth2Client(ctx context.Context, k8sClient client.Client, org *greenhouseapisv1alpha1.Organization) error { var oAuth2Client = new(dexapi.OAuth2Client) - oAuth2Client.ObjectMeta.Name = encodedOAuth2ClientName(org.Name) - oAuth2Client.ObjectMeta.Namespace = namespace - generatedClientSecret := getDeterministicSecret(org.Name, org.GetUID()) + namespaceName := org.GetName() + oAuth2Client.SetName(encodedOAuth2ClientName(namespaceName)) + oAuth2Client.SetNamespace(namespaceName) result, err := clientutil.CreateOrPatch(ctx, k8sClient, oAuth2Client, func() error { oAuth2Client.Client.Public = true oAuth2Client.Client.ID = org.Name - oAuth2Client.Secret = generatedClientSecret oAuth2Client.Client.Name = org.Name for _, requiredRedirectURL := range []string{ "http://localhost:8085", @@ -127,11 +127,6 @@ func (k *k8sDex) CreateUpdateOauth2Client(ctx context.Context, k8sClient client. log.FromContext(ctx).Info("updated oauth2client", "namespace", oAuth2Client.Namespace, "name", oAuth2Client.GetName()) } - secret := prepareClientSecret(namespace, org.Name, generatedClientSecret) - err = writeCredentialsToNamespace(ctx, k8sClient, org, secret) - if err != nil { - return err - } return nil } diff --git a/pkg/dex/store/pg_store.go b/pkg/dex/store/pg_store.go index 481a50c41..be6474f6c 100644 --- a/pkg/dex/store/pg_store.go +++ b/pkg/dex/store/pg_store.go @@ -67,7 +67,7 @@ func (p *pgDex) GetBackend() string { } // CreateUpdateConnector - creates or updates a dex connector in dex postgres storage backend -func (p *pgDex) CreateUpdateConnector(ctx context.Context, _ client.Client, org *greenhouseapisv1alpha1.Organization, configByte []byte, _ string) error { +func (p *pgDex) CreateUpdateConnector(ctx context.Context, _ client.Client, org *greenhouseapisv1alpha1.Organization, configByte []byte) error { oidcConnector, err := p.storage.GetConnector(org.Name) if err != nil && errors.Is(err, storage.ErrNotFound) { if err = p.storage.CreateConnector(ctx, storage.Connector{ @@ -99,18 +99,15 @@ func (p *pgDex) CreateUpdateConnector(ctx context.Context, _ client.Client, org } // CreateUpdateOauth2Client - creates or updates an oauth2 client in dex postgres storage backend -func (p *pgDex) CreateUpdateOauth2Client(ctx context.Context, k8sClient client.Client, org *greenhouseapisv1alpha1.Organization, namespace string) error { - generatedClientSecret := getDeterministicSecret(org.Name, org.GetUID()) +func (p *pgDex) CreateUpdateOauth2Client(ctx context.Context, _ client.Client, org *greenhouseapisv1alpha1.Organization) error { oAuthClient, err := p.storage.GetClient(org.Name) if err != nil && errors.Is(err, storage.ErrNotFound) { if err = p.storage.CreateClient(ctx, storage.Client{ Public: true, ID: org.Name, - Secret: generatedClientSecret, Name: org.Name, RedirectURIs: []string{ "http://localhost:8085", - "http://localhost:8000", "https://dashboard." + common.DNSDomain, fmt.Sprintf("https://%s.dashboard.%s", org.Name, common.DNSDomain), }, @@ -123,14 +120,13 @@ func (p *pgDex) CreateUpdateOauth2Client(ctx context.Context, k8sClient client.C if err != nil { log.FromContext(ctx).Error(err, "failed to get oauth2client", "name", org.Name) } + err = p.storage.UpdateClient(oAuthClient.Name, func(authClient storage.Client) (storage.Client, error) { authClient.Public = true authClient.ID = org.Name - authClient.Secret = generatedClientSecret authClient.Name = org.Name for _, requiredRedirectURL := range []string{ "http://localhost:8085", - "http://localhost:8000", "https://dashboard." + common.DNSDomain, fmt.Sprintf("https://%s.dashboard.%s", org.Name, common.DNSDomain), } { @@ -141,13 +137,6 @@ func (p *pgDex) CreateUpdateOauth2Client(ctx context.Context, k8sClient client.C if err != nil { return err } - // write the client credentials to the organization namespace - secret := prepareClientSecret(namespace, org.Name, generatedClientSecret) - err = writeCredentialsToNamespace(ctx, k8sClient, org, secret) - if err != nil { - return err - } - log.FromContext(ctx).Info("updated oauth2client", "name", org.Name) return nil } diff --git a/pkg/dex/store/storage.go b/pkg/dex/store/storage.go index 161dbd48f..4490cfb43 100644 --- a/pkg/dex/store/storage.go +++ b/pkg/dex/store/storage.go @@ -5,18 +5,11 @@ package store import ( "context" - "crypto/hmac" - "crypto/sha256" - "encoding/hex" "fmt" "log/slog" "github.com/dexidp/dex/storage" - corev1 "k8s.io/api/core/v1" - "k8s.io/apimachinery/pkg/types" - controllerruntime "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/log" greenhouseapisv1alpha1 "github.com/cloudoperators/greenhouse/pkg/apis/greenhouse/v1alpha1" ) @@ -25,15 +18,13 @@ const ( Postgres = "postgres" K8s = "kubernetes" dexConnectorTypeGreenhouse = "greenhouse-oidc" - clientIDKey = "clientID" - clientSecretKey = "clientSecret" ) // Dexter - dex storage adapter interface // Supported backends: Postgres, K8s type Dexter interface { - CreateUpdateConnector(ctx context.Context, k8sClient client.Client, org *greenhouseapisv1alpha1.Organization, configByte []byte, namespace string) error - CreateUpdateOauth2Client(ctx context.Context, k8sClient client.Client, org *greenhouseapisv1alpha1.Organization, namespace string) error + CreateUpdateConnector(ctx context.Context, k8sClient client.Client, org *greenhouseapisv1alpha1.Organization, configByte []byte) error + CreateUpdateOauth2Client(ctx context.Context, k8sClient client.Client, org *greenhouseapisv1alpha1.Organization) error GetStorage() storage.Storage GetBackend() string Close() error @@ -58,63 +49,3 @@ func NewDexStorageFactory(logger *slog.Logger, backend string) (Dexter, error) { return nil, fmt.Errorf("unknown dexStorage backend: %s", backend) } } - -// getDeterministicSecret - generate a deterministic secret based on the clientID and secretKey -func getDeterministicSecret(clientID string, secretKey types.UID) string { - h := hmac.New(sha256.New, []byte(secretKey)) - h.Write([]byte(clientID)) - return hex.EncodeToString(h.Sum(nil))[:32] -} - -// prepareClientSecret - create a coreV1 secret with the clientID and secret -func prepareClientSecret(namespace, clientID, clientSecret string) *corev1.Secret { - secret := new(corev1.Secret) - secret.SetName(clientID + "-dex-secrets") - secret.SetNamespace(namespace) - secret.Data = map[string][]byte{ - clientIDKey: []byte(clientID), - clientSecretKey: []byte(clientSecret), - } - return secret -} - -// writeCredentialsToNamespace - write the client credentials to the organization namespace -// if the secret already exists, update the secret -// set organization as the owner reference for the secret -func writeCredentialsToNamespace(ctx context.Context, cl client.Client, org *greenhouseapisv1alpha1.Organization, secret *corev1.Secret) error { - existing := &corev1.Secret{} - err := cl.Get(ctx, types.NamespacedName{Name: secret.Name, Namespace: secret.Namespace}, existing) - if err != nil { - if client.IgnoreNotFound(err) != nil { - log.FromContext(ctx).Error(err, "unable to get dex client credentials", "name", secret.Name, "namespace", secret.Namespace) - return err - } - // if not found, create the secret with owner reference - // set the owner reference - err = controllerruntime.SetControllerReference(org, secret, cl.Scheme()) - if err != nil { - log.FromContext(ctx).Error(err, "unable to set controller reference for dex client credentials", "name", secret.Name, "namespace", secret.Namespace) - return err - } - err = cl.Create(ctx, secret) - if err != nil { - log.FromContext(ctx).Error(err, "unable to create dex client credentials", "name", secret.Name, "namespace", secret.Namespace) - return err - } - return nil - } - - existing.Data[clientIDKey] = secret.Data[clientIDKey] - existing.Data[clientSecretKey] = secret.Data[clientSecretKey] - err = controllerruntime.SetControllerReference(org, existing, cl.Scheme()) - if err != nil { - log.FromContext(ctx).Error(err, "unable to create / patch dex client credentials", "name", secret.Name, "namespace", secret.Namespace) - return err - } - err = cl.Update(ctx, existing) - if err != nil { - log.FromContext(ctx).Error(err, "unable to update dex client credentials", "name", secret.Name, "namespace", secret.Namespace) - return err - } - return nil -} From 726dc2fdfbec9b2a43e6d36a27a812fc26de32fc Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Wed, 5 Feb 2025 17:45:43 +0100 Subject: [PATCH 048/108] (chore): regenerate dexter mocks --- pkg/controllers/organization/suite_test.go | 4 ++-- pkg/mocks/mock_Dexter.go | 23 ++++++++++------------ pkg/mocks/mock_Reconciler.go | 3 +-- pkg/mocks/mock_Storage.go | 2 +- 4 files changed, 14 insertions(+), 18 deletions(-) diff --git a/pkg/controllers/organization/suite_test.go b/pkg/controllers/organization/suite_test.go index 13712b4f7..b32b59905 100644 --- a/pkg/controllers/organization/suite_test.go +++ b/pkg/controllers/organization/suite_test.go @@ -50,8 +50,8 @@ var _ = AfterSuite(func() { func dexMocks() dexstore.Dexter { dexter := &mocks.MockDexter{} - dexter.On("CreateUpdateConnector", mock.Anything, mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) - dexter.On("CreateUpdateOauth2Client", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) + dexter.On("CreateUpdateConnector", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) + dexter.On("CreateUpdateOauth2Client", mock.Anything, mock.Anything, mock.Anything).Return(nil) dexter.On("GetBackend").Return(dexstore.K8s) return dexter } diff --git a/pkg/mocks/mock_Dexter.go b/pkg/mocks/mock_Dexter.go index dbf9af290..9bd8ef9a5 100644 --- a/pkg/mocks/mock_Dexter.go +++ b/pkg/mocks/mock_Dexter.go @@ -1,6 +1,3 @@ -// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors -// SPDX-License-Identifier: Apache-2.0 - // Code generated by mockery v2.50.0. DO NOT EDIT. package mocks @@ -40,17 +37,17 @@ func (_m *MockDexter) Close() error { return r0 } -// CreateUpdateConnector provides a mock function with given fields: ctx, k8sClient, org, configByte, namespace -func (_m *MockDexter) CreateUpdateConnector(ctx context.Context, k8sClient client.Client, org *v1alpha1.Organization, configByte []byte, namespace string) error { - ret := _m.Called(ctx, k8sClient, org, configByte, namespace) +// CreateUpdateConnector provides a mock function with given fields: ctx, k8sClient, org, configByte +func (_m *MockDexter) CreateUpdateConnector(ctx context.Context, k8sClient client.Client, org *v1alpha1.Organization, configByte []byte) error { + ret := _m.Called(ctx, k8sClient, org, configByte) if len(ret) == 0 { panic("no return value specified for CreateUpdateConnector") } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, client.Client, *v1alpha1.Organization, []byte, string) error); ok { - r0 = rf(ctx, k8sClient, org, configByte, namespace) + if rf, ok := ret.Get(0).(func(context.Context, client.Client, *v1alpha1.Organization, []byte) error); ok { + r0 = rf(ctx, k8sClient, org, configByte) } else { r0 = ret.Error(0) } @@ -58,17 +55,17 @@ func (_m *MockDexter) CreateUpdateConnector(ctx context.Context, k8sClient clien return r0 } -// CreateUpdateOauth2Client provides a mock function with given fields: ctx, k8sClient, org, namespace -func (_m *MockDexter) CreateUpdateOauth2Client(ctx context.Context, k8sClient client.Client, org *v1alpha1.Organization, namespace string) error { - ret := _m.Called(ctx, k8sClient, org, namespace) +// CreateUpdateOauth2Client provides a mock function with given fields: ctx, k8sClient, org +func (_m *MockDexter) CreateUpdateOauth2Client(ctx context.Context, k8sClient client.Client, org *v1alpha1.Organization) error { + ret := _m.Called(ctx, k8sClient, org) if len(ret) == 0 { panic("no return value specified for CreateUpdateOauth2Client") } var r0 error - if rf, ok := ret.Get(0).(func(context.Context, client.Client, *v1alpha1.Organization, string) error); ok { - r0 = rf(ctx, k8sClient, org, namespace) + if rf, ok := ret.Get(0).(func(context.Context, client.Client, *v1alpha1.Organization) error); ok { + r0 = rf(ctx, k8sClient, org) } else { r0 = ret.Error(0) } diff --git a/pkg/mocks/mock_Reconciler.go b/pkg/mocks/mock_Reconciler.go index 7e649589e..a381e1065 100644 --- a/pkg/mocks/mock_Reconciler.go +++ b/pkg/mocks/mock_Reconciler.go @@ -5,9 +5,8 @@ package mocks import ( context "context" - mock "github.com/stretchr/testify/mock" - lifecycle "github.com/cloudoperators/greenhouse/pkg/lifecycle" + mock "github.com/stretchr/testify/mock" reconcile "sigs.k8s.io/controller-runtime/pkg/reconcile" ) diff --git a/pkg/mocks/mock_Storage.go b/pkg/mocks/mock_Storage.go index d2f31847c..2982301d6 100644 --- a/pkg/mocks/mock_Storage.go +++ b/pkg/mocks/mock_Storage.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.51.0. DO NOT EDIT. +// Code generated by mockery v2.50.0. DO NOT EDIT. package mocks From 6c879117ae84aca1520caffa692e385adcf8ac1f Mon Sep 17 00:00:00 2001 From: David Gogl <1381862+kengou@users.noreply.github.com> Date: Tue, 11 Feb 2025 13:19:56 +0100 Subject: [PATCH 049/108] feat(dex): add postgresql helm chart to greenhouse, idproxy, manager charts --- charts/greenhouse/Chart.lock | 7 +++-- charts/greenhouse/Chart.yaml | 6 +++- charts/greenhouse/templates/_helpers.tpl | 10 +++++++ charts/greenhouse/values.yaml | 22 +++++++++++++++ charts/idproxy/.gitignore | 2 ++ charts/idproxy/Chart.yaml | 2 +- charts/idproxy/templates/_helpers.tpl | 10 +++++++ charts/idproxy/templates/deployment.yaml | 28 +++++++++++-------- charts/manager/Chart.yaml | 2 +- charts/manager/etc/feature_flags.yaml | 16 ----------- charts/manager/templates/_helpers.tpl | 10 +++++++ charts/manager/templates/deployment.yaml | 22 +++++++++------ .../templates/manager-featureflag.yaml | 15 ++++++++-- cmd/greenhouse/main.go | 2 +- cmd/idproxy/main.go | 2 +- pkg/features/features.go | 9 +++--- pkg/features/features_test.go | 6 ++-- 17 files changed, 119 insertions(+), 52 deletions(-) create mode 100644 charts/idproxy/.gitignore delete mode 100644 charts/manager/etc/feature_flags.yaml diff --git a/charts/greenhouse/Chart.lock b/charts/greenhouse/Chart.lock index 7170159da..3879d2447 100644 --- a/charts/greenhouse/Chart.lock +++ b/charts/greenhouse/Chart.lock @@ -14,5 +14,8 @@ dependencies: - name: demo repository: file://../demo version: 0.1.1 -digest: sha256:59b7daf16ea2e2f9a77650b46e1c26be926f1f1e756b17315b00785d128edc54 -generated: "2024-12-17T15:10:31.85777+01:00" +- name: postgresql-ng + repository: oci://ghcr.io/sapcc/helm-charts + version: 1.2.7 +digest: sha256:463596443f340831929b4c116522cbad3e8f09849be5de42d830eeadbed73a86 +generated: "2025-02-10T11:35:06.90304+01:00" diff --git a/charts/greenhouse/Chart.yaml b/charts/greenhouse/Chart.yaml index 5ee101861..057600565 100644 --- a/charts/greenhouse/Chart.yaml +++ b/charts/greenhouse/Chart.yaml @@ -5,7 +5,7 @@ apiVersion: v2 name: greenhouse description: A Helm chart for deploying greenhouse type: application -version: 0.7.2 +version: 0.7.3 appVersion: "0.1.0" dependencies: @@ -28,3 +28,7 @@ dependencies: version: 0.1.1 repository: "file://../demo" condition: demo.enabled + - name: postgresql-ng + version: 1.2.7 + repository: "oci://ghcr.io/sapcc/helm-charts" + condition: postgresql.enabled diff --git a/charts/greenhouse/templates/_helpers.tpl b/charts/greenhouse/templates/_helpers.tpl index 1ffa41161..74e364638 100644 --- a/charts/greenhouse/templates/_helpers.tpl +++ b/charts/greenhouse/templates/_helpers.tpl @@ -60,3 +60,13 @@ Create the name of the service account to use {{- default "default" .Values.serviceAccount.name }} {{- end }} {{- end }} + +{{/* +Check if global.dex.backend is postgres then the postgressql part need to be enabled as well +*/}} +{{- if .Values.global.dex.backend == "postgres" && eq .Values.postgresql.enabled "false" }} +{{- fail "dex.backend: Setting the dex.backend to postgres requires that you enable and configure postgresql" }} +{{- end }} +{{- if .Values.global.dex.backend == "kubernetes" && eq .Values.postgresql.enabled "true" }} +{{- fail "dex.backend: Setting the dex.backend to kubernetes does not require postgresql enabled" }} +{{- end }} diff --git a/charts/greenhouse/values.yaml b/charts/greenhouse/values.yaml index 7cabfc181..d0a0be15c 100644 --- a/charts/greenhouse/values.yaml +++ b/charts/greenhouse/values.yaml @@ -11,6 +11,28 @@ global: redirectURL: clientID: clientSecret: + # DEX configuration for Greenhouse. + dex: + backend: postgres # postgres or kubernetes + postgres: + postgresqlDatabase: + postgresqlHost: + postgresqlPort: + postgresqlUsername: + +postgresql: + enabled: true + global: + linkerd_enabled: false + region: greenhouse + registry: # The registry where the image is stored + resources: {} + postgresDatabase: # The database that will be created in the database + users: + #dex: # The user that will be created in the database + # grant: # The grants that will be given to the user + # - 'GRANT ALL PRIVILEGES ON DATABASE "%PGDATABASE%"' + tableOwner: # The owner of the tables in the database # Organization & IDProxy should be enabled only after the initial install of greenhouse organization: diff --git a/charts/idproxy/.gitignore b/charts/idproxy/.gitignore new file mode 100644 index 000000000..8fa23da4d --- /dev/null +++ b/charts/idproxy/.gitignore @@ -0,0 +1,2 @@ +charts/ +local.yaml diff --git a/charts/idproxy/Chart.yaml b/charts/idproxy/Chart.yaml index c39aa3669..db10df16d 100644 --- a/charts/idproxy/Chart.yaml +++ b/charts/idproxy/Chart.yaml @@ -18,7 +18,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.2.0 +version: 0.2.1 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to diff --git a/charts/idproxy/templates/_helpers.tpl b/charts/idproxy/templates/_helpers.tpl index cef408889..266a2dcc9 100644 --- a/charts/idproxy/templates/_helpers.tpl +++ b/charts/idproxy/templates/_helpers.tpl @@ -65,3 +65,13 @@ Create the name of the service account to use {{- define "idproxy.auth.hostname" -}} {{- printf "%s.%s" "auth" (required "global.dnsDomain missing" .Values.global.dnsDomain) }} {{- end }} + +{{/* +Define postgresql helpers +*/}} +{{- define "postgres.fullname" -}} + {{- printf "%s-postgresql" .Release.Name | trunc 48 | replace "_" "-" -}} +{{- end -}} +{{- denife "feature-flag.fullname"-}} + {{- printf "%s-feature-flag" .Release.Name | trunc 48 | replace "_" "-" -}} +{{- end -}} diff --git a/charts/idproxy/templates/deployment.yaml b/charts/idproxy/templates/deployment.yaml index 199a2ce7a..3f4d998fa 100644 --- a/charts/idproxy/templates/deployment.yaml +++ b/charts/idproxy/templates/deployment.yaml @@ -55,24 +55,30 @@ spec: - --allowed-origins={{ $origin }} {{- end }} env: - - name: DEX_POSTGRES_DATABASE - value: {{ .Values.postgresql.postgresqlDatabase }} - - name: DEX_POSTGRES_HOST - value: {{ .Values.postgresql.postgresqlHost }} - - name: DEX_POSTGRES_USER - value: {{ .Values.postgresql.postgresqlUsername }} - - name: DEX_POSTGRES_PASSWORD + - name: FEATURE_FLAGS + value: {{ feature-flag.fullname }} + {{- if .Values.global.dex.backend == "postgres" }} + - name: PG_DATABASE + value: {{ .Values.global.dex.postgresql.postgresqlDatabase }} + - name: PG_HOST + value: {{ .Values.global.dex.postgresql.postgresqlHost }} + - name: PG_PORT + value: {{ .Values.global.dex.postgresql.postgresqlPort }} + - name: PG_USER + value: {{ .Values.global.dex.postgresql.postgresqlUsername }} + - name: PG_PASSWORD valueFrom: secretKeyRef: key: postgres-password - name: dex-postgres-pguser-dex - - name: FEATURE_FLAG_NAMESPACE + name: {{ .Release.Name }}-pguser-{{ .Values.global.dex.postgresql.postgresqlUsername }} + {{- end }} + - name: POD_NAMESPACE valueFrom: fieldRef: apiVersion: v1 fieldPath: metadata.namespace - - name: FEATURE_FLAG_CONFIG_MAP_NAME - value: {{ include "manager.fullname" . }}-feature-flags + - name: FEATURE_FLAGS + value: {{ feature-flag.fullname }} ports: - name: oidc containerPort: 8080 diff --git a/charts/manager/Chart.yaml b/charts/manager/Chart.yaml index d8b1d3251..f7f9c40bf 100644 --- a/charts/manager/Chart.yaml +++ b/charts/manager/Chart.yaml @@ -16,7 +16,7 @@ type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) -version: 0.1.6 +version: 0.1.7 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. diff --git a/charts/manager/etc/feature_flags.yaml b/charts/manager/etc/feature_flags.yaml deleted file mode 100644 index 519c04d65..000000000 --- a/charts/manager/etc/feature_flags.yaml +++ /dev/null @@ -1,16 +0,0 @@ -# SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors -# SPDX-License-Identifier: Apache-2.0 - -dex-sql-backend: - variations: - true: true - false: false - defaultRule: - variation: false - -dex-kubernetes-backend: - variations: - true: true - false: false - defaultRule: - variation: true \ No newline at end of file diff --git a/charts/manager/templates/_helpers.tpl b/charts/manager/templates/_helpers.tpl index a6d3ded74..a8005f5b3 100644 --- a/charts/manager/templates/_helpers.tpl +++ b/charts/manager/templates/_helpers.tpl @@ -68,3 +68,13 @@ Create the name of the service account to use {{- default "default" .Values.serviceAccount.name }} {{- end }} {{- end }} + +{{/* +Define postgresql helpers +*/}} +{{- define "postgres.fullname" -}} + {{- printf "%s-postgresql" .Release.Name | trunc 48 | replace "_" "-" -}} +{{- end -}} +{{- denife "feature-flag.fullname"-}} + {{- printf "%s-feature-flag" .Release.Name | trunc 48 | replace "_" "-" -}} +{{- end -}} \ No newline at end of file diff --git a/charts/manager/templates/deployment.yaml b/charts/manager/templates/deployment.yaml index 45c637773..ca4892071 100644 --- a/charts/manager/templates/deployment.yaml +++ b/charts/manager/templates/deployment.yaml @@ -35,17 +35,23 @@ spec: {{- include "manager.params" . | indent 8 }} {{- end }} env: - - name: DEX_POSTGRES_DATABASE - value: {{ .Values.postgresql.postgresqlDatabase }} - - name: DEX_POSTGRES_HOST - value: - - name: DEX_POSTGRES_USER - value: {{ .Values.postgresql.postgresqlUsername }} - - name: DEX_POSTGRES_PASSWORD + - name: FEATURE_FLAGS + value: {{ feature-flag.fullname }} + {{- if .Values.global.dex.backend == "postgres" }} + - name: PG_DATABASE + value: {{ .Values.global.dex.postgresql.postgresqlDatabase }} + - name: PG_HOST + value: {{ .Values.global.dex.postgresql.postgresqlHost }} + - name: PG_PORT + value: {{ .Values.global.dex.postgresql.postgresqlPort }} + - name: PG_USER + value: {{ .Values.global.dex.postgresql.postgresqlUsername }} + - name: PG_PASSWORD valueFrom: secretKeyRef: key: postgres-password - name: dex-postgres-pguser-dex + name: {{ .Release.Name }}-pguser-{{ .Values.global.dex.postgresql.postgresqlUsername }} + {{- end }} - name: POD_NAMESPACE valueFrom: fieldRef: diff --git a/charts/manager/templates/manager-featureflag.yaml b/charts/manager/templates/manager-featureflag.yaml index 89b5bd2e0..260cf54fa 100644 --- a/charts/manager/templates/manager-featureflag.yaml +++ b/charts/manager/templates/manager-featureflag.yaml @@ -4,5 +4,16 @@ kind: ConfigMap metadata: name: {{ include "manager.fullname" . }}-feature-flags data: - config.yaml: |- - {{- .Files.Get "etc/feature_flags.yaml" | nindent 4 }} \ No newline at end of file + _example: > + ################################ + # # + # EXAMPLE CONFIGURATION # + # # + ################################ + # enable dex features + # storage allows you to choose what kind of dex storage backend you would like to choose + # Greenhouse gives the choice of using kubernetes as the storage backend or to use postgresSQL as the backend + dex: | + storage: kubernetes / postgres + dex: | + storage: {{ .Values.dex.backend }} \ No newline at end of file diff --git a/cmd/greenhouse/main.go b/cmd/greenhouse/main.go index 1cfdb5d60..e2c68ae54 100644 --- a/cmd/greenhouse/main.go +++ b/cmd/greenhouse/main.go @@ -137,7 +137,7 @@ func main() { // Note: mgr.GetClient() will fail here because the cache is not ready yet k8sClient := mgr.GetAPIReader() // Initialize the feature gates from feature-flags config map - f, err = features.NewFeatures(context.TODO(), k8sClient) + f, err = features.NewFeatures(context.TODO(), k8sClient, clientutil.GetEnvOrDefault("FEATURE_FLAGS", "greenhouse-feature-flags"), clientutil.GetEnvOrDefault("POD_NAMESPACE", "greenhouse")) if err != nil { handleError(err, "unable to get features") } diff --git a/cmd/idproxy/main.go b/cmd/idproxy/main.go index cedf07f52..a41e05fac 100644 --- a/cmd/idproxy/main.go +++ b/cmd/idproxy/main.go @@ -65,7 +65,7 @@ func main() { } // default to kubernetes storage backend backend := clientutil.Ptr("kubernetes") - ghFeatures, err := features.NewFeatures(ctx, k8sClient) + ghFeatures, err := features.NewFeatures(ctx, k8sClient, clientutil.GetEnvOrDefault("FEATURE_FLAGS", "greenhouse-feature-flags"), clientutil.GetEnvOrDefault("POD_NAMESPACE", "greenhouse")) if err != nil { log.Fatalf("failed to get greenhouse features: %s", err) } diff --git a/pkg/features/features.go b/pkg/features/features.go index aa510de52..c533f0ae7 100644 --- a/pkg/features/features.go +++ b/pkg/features/features.go @@ -19,9 +19,8 @@ import ( ) const ( - DexFeatureKey = "dex" - featureConfigMapName = "greenhouse-feature-flags" - featureConfigMapNamespace = "greenhouse" + DexFeatureKey = "dex" + featureConfigMapName = "greenhouse-feature-flags" ) type features struct { @@ -38,9 +37,9 @@ type Features interface { GetDexStorageType(ctx context.Context) *string } -func NewFeatures(ctx context.Context, k8sClient client.Reader) (Features, error) { +func NewFeatures(ctx context.Context, k8sClient client.Reader, configMapName, podNameSpace string) (Features, error) { featureMap := &corev1.ConfigMap{} - if err := k8sClient.Get(ctx, types.NamespacedName{Name: featureConfigMapName, Namespace: featureConfigMapNamespace}, featureMap); err != nil { + if err := k8sClient.Get(ctx, types.NamespacedName{Name: configMapName, Namespace: podNameSpace}, featureMap); err != nil { if kerrors.IsNotFound(err) { return nil, nil } diff --git a/pkg/features/features_test.go b/pkg/features/features_test.go index b08f0ab33..41f327fd5 100644 --- a/pkg/features/features_test.go +++ b/pkg/features/features_test.go @@ -72,12 +72,12 @@ func Test_DexFeatures(t *testing.T) { if tc.getError != nil { mockK8sClient.On("Get", ctx, types.NamespacedName{ - Name: featureConfigMapName, Namespace: featureConfigMapNamespace, + Name: clientutil.GetEnvOrDefault("FEATURE_FLAGS", "greenhouse-feature-flags"), Namespace: clientutil.GetEnvOrDefault("POD_NAMESPACE", "greenhouse"), }, mock.Anything).Return(tc.getError) } else { configMap.Data = tc.configMapData mockK8sClient.On("Get", ctx, types.NamespacedName{ - Name: featureConfigMapName, Namespace: featureConfigMapNamespace, + Name: clientutil.GetEnvOrDefault("FEATURE_FLAGS", "greenhouse-feature-flags"), Namespace: clientutil.GetEnvOrDefault("POD_NAMESPACE", "greenhouse"), }, mock.Anything).Run(func(args mock.Arguments) { arg := args.Get(2).(*corev1.ConfigMap) *arg = *configMap @@ -85,7 +85,7 @@ func Test_DexFeatures(t *testing.T) { } // Create Features instance - featuresInstance, err := NewFeatures(ctx, mockK8sClient) + featuresInstance, err := NewFeatures(ctx, mockK8sClient, clientutil.GetEnvOrDefault("FEATURE_FLAGS", "greenhouse-feature-flags"), clientutil.GetEnvOrDefault("POD_NAMESPACE", "greenhouse")) if tc.getError != nil && client.IgnoreNotFound(tc.getError) == nil { assert.NoError(t, client.IgnoreNotFound(err)) assert.Nil(t, featuresInstance, "Expected nil when ConfigMap is missing") From adf07c4652397c659b9e74b124ee4f5de47e7559 Mon Sep 17 00:00:00 2001 From: David Gogl <1381862+kengou@users.noreply.github.com> Date: Tue, 11 Feb 2025 13:23:20 +0100 Subject: [PATCH 050/108] fix(charts): rm .gitignore from idproxy chart --- charts/idproxy/.gitignore | 2 -- 1 file changed, 2 deletions(-) delete mode 100644 charts/idproxy/.gitignore diff --git a/charts/idproxy/.gitignore b/charts/idproxy/.gitignore deleted file mode 100644 index 8fa23da4d..000000000 --- a/charts/idproxy/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -charts/ -local.yaml From 7d75177ed3e095d70f921f32ea68393cfc79df5c Mon Sep 17 00:00:00 2001 From: David Gogl <1381862+kengou@users.noreply.github.com> Date: Tue, 11 Feb 2025 13:24:57 +0100 Subject: [PATCH 051/108] fix fmt --- charts/manager/templates/_helpers.tpl | 2 +- charts/manager/templates/manager-featureflag.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/manager/templates/_helpers.tpl b/charts/manager/templates/_helpers.tpl index a8005f5b3..8d6d83ee0 100644 --- a/charts/manager/templates/_helpers.tpl +++ b/charts/manager/templates/_helpers.tpl @@ -77,4 +77,4 @@ Define postgresql helpers {{- end -}} {{- denife "feature-flag.fullname"-}} {{- printf "%s-feature-flag" .Release.Name | trunc 48 | replace "_" "-" -}} -{{- end -}} \ No newline at end of file +{{- end -}} diff --git a/charts/manager/templates/manager-featureflag.yaml b/charts/manager/templates/manager-featureflag.yaml index 260cf54fa..b38e9cc3a 100644 --- a/charts/manager/templates/manager-featureflag.yaml +++ b/charts/manager/templates/manager-featureflag.yaml @@ -16,4 +16,4 @@ data: dex: | storage: kubernetes / postgres dex: | - storage: {{ .Values.dex.backend }} \ No newline at end of file + storage: {{ .Values.dex.backend }} From cfe3fbb96178dd11a55cfc4d21c709dac9fdeb08 Mon Sep 17 00:00:00 2001 From: David Gogl <1381862+kengou@users.noreply.github.com> Date: Tue, 11 Feb 2025 13:31:13 +0100 Subject: [PATCH 052/108] fix(charts): helm lint, update umbrellachart versions --- charts/greenhouse/Chart.lock | 8 ++++---- charts/greenhouse/Chart.yaml | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/charts/greenhouse/Chart.lock b/charts/greenhouse/Chart.lock index 3879d2447..fc0d001d3 100644 --- a/charts/greenhouse/Chart.lock +++ b/charts/greenhouse/Chart.lock @@ -1,13 +1,13 @@ dependencies: - name: idproxy repository: file://../idproxy - version: 0.2.0 + version: 0.2.1 - name: cors-proxy repository: file://../cors-proxy version: 0.2.0 - name: manager repository: file://../manager - version: 0.1.6 + version: 0.1.7 - name: dashboard repository: file://../dashboard version: 0.1.0 @@ -17,5 +17,5 @@ dependencies: - name: postgresql-ng repository: oci://ghcr.io/sapcc/helm-charts version: 1.2.7 -digest: sha256:463596443f340831929b4c116522cbad3e8f09849be5de42d830eeadbed73a86 -generated: "2025-02-10T11:35:06.90304+01:00" +digest: sha256:d39c88a67fdf87f9098058d0fbf33eb43a315bc9011da5134c64a67718c94b8a +generated: "2025-02-11T13:30:08.243315+01:00" diff --git a/charts/greenhouse/Chart.yaml b/charts/greenhouse/Chart.yaml index 057600565..9d4c25a86 100644 --- a/charts/greenhouse/Chart.yaml +++ b/charts/greenhouse/Chart.yaml @@ -12,13 +12,13 @@ dependencies: - condition: idproxy.enabled name: idproxy repository: "file://../idproxy" - version: 0.2.0 + version: 0.2.1 - condition: cors-proxy.enabled name: cors-proxy repository: "file://../cors-proxy" version: 0.2.0 - name: manager - version: 0.1.6 + version: 0.1.7 repository: "file://../manager" - condition: dashboard.enabled name: dashboard From aa24488156c6747e2cb6c2baee39defb2ffcb359 Mon Sep 17 00:00:00 2001 From: Abhijith Ravindra <137736216+abhijith-darshan@users.noreply.github.com> Date: Thu, 13 Feb 2025 09:11:16 +0100 Subject: [PATCH 053/108] Apply suggestions from code review Co-authored-by: IvoGoman --- cmd/idproxy/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cmd/idproxy/README.md b/cmd/idproxy/README.md index 3283623a7..f414f6aba 100644 --- a/cmd/idproxy/README.md +++ b/cmd/idproxy/README.md @@ -1,4 +1,4 @@ -## Running `idprox` service locally +## Running `idproxy` service locally You can run the `idproxy` service locally by setting the following arguments and environment variables - @@ -18,12 +18,12 @@ Arguments: Environment Variables: -- If you are using `kubernetes` as the dex storage backend, kubernetes mode is automatically detected +- If you want to use `kubernetes` as the dex storage backend, the mode is determined by the following settings: 1. `KUBECONFIG` environment variable - example - `export KUBECONFIG=/path/to/config` (Priority 1) 2. kube config from the recommended dir and file - example - `$HOME/.kube/config` (Priority 2) 3. Running inside a kubernetes cluster, `in-cluster` mode is used. (Priority 3) -- If you are using `postgres` as the dex storage backend, set the following environment variables: +- If want to use `postgres` as the dex storage backend, set the following environment variables: - `PG_DATABASE=` ex: `postgres` (defaults to `postgres` if not set) - `PG_PORT=` ex: `5432` (defaults to `5432` if not set) - `PG_USER=` ex: `postgres` (defaults to `postgres` if not set) From 014640113fc2cb1d3d8b0f1416b6cef0eac2431b Mon Sep 17 00:00:00 2001 From: Abhijith Ravindra <137736216+abhijith-darshan@users.noreply.github.com> Date: Thu, 13 Feb 2025 09:13:33 +0100 Subject: [PATCH 054/108] Apply suggestions from code review Co-authored-by: IvoGoman --- pkg/clientutil/env.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/clientutil/env.go b/pkg/clientutil/env.go index 18d18bafe..f95b1c2b1 100644 --- a/pkg/clientutil/env.go +++ b/pkg/clientutil/env.go @@ -30,5 +30,5 @@ func GetEnv(envKey string) (string, error) { if v, ok := os.LookupEnv(envKey); ok { return v, nil } - return "", errors.New("environment variable not set") + return "", fmt.Errorf("environment variable '%s' not set", envKey) } From e782dda9a3c9c32a858c168b1efc98b65b3a72bd Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Thu, 13 Feb 2025 09:25:27 +0100 Subject: [PATCH 055/108] (chore): uses descriptive var names and const --- cmd/greenhouse/controllers.go | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/cmd/greenhouse/controllers.go b/cmd/greenhouse/controllers.go index 47aa863cb..aa927800e 100644 --- a/cmd/greenhouse/controllers.go +++ b/cmd/greenhouse/controllers.go @@ -9,6 +9,7 @@ import ( "os" "sort" + "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" "github.com/cloudoperators/greenhouse/pkg/clientutil" @@ -21,6 +22,10 @@ import ( dexstore "github.com/cloudoperators/greenhouse/pkg/dex/store" ) +const ( + defaultIDProxyStorageType = "kubernetes" +) + // knownControllers contains all controllers to be registered when starting the operator. var knownControllers = map[string]func(controllerName string, mgr ctrl.Manager) error{ // Organization controllers. @@ -67,20 +72,17 @@ func isControllerEnabled(controllerName string) bool { return false } -// startOrganizationReconciler - setup the organization reconciler +// startOrganizationReconciler - initializes the organization reconciler // resolves dex storage backend from greenhouse-feature-flags // initializes the dex storage adapter interface in the organization reconciler func startOrganizationReconciler(name string, mgr ctrl.Manager) error { - namespace := "greenhouse" - if v, ok := os.LookupEnv("POD_NAMESPACE"); ok { - namespace = v - } + namespace := clientutil.GetEnvOrDefault(podNamespaceEnv, defaultPodNamespace) var dexter dexstore.Dexter var err error - backend := clientutil.Ptr("kubernetes") + backend := ptr.To(defaultIDProxyStorageType) l := slog.New(slog.NewJSONHandler(os.Stdout, nil)) - if f != nil { - backend = f.GetDexStorageType(context.Background()) + if featureFlags != nil { + backend = featureFlags.GetDexStorageType(context.Background()) } dexter, err = dexstore.NewDexStorageFactory(l.With("component", "storage"), *backend) if err != nil { From 2e343e69f31c2461364f8c62cd4a3c815055b0d1 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Thu, 13 Feb 2025 09:26:01 +0100 Subject: [PATCH 056/108] (chore): remove mutex and use k8s ptr.To --- pkg/features/features.go | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/pkg/features/features.go b/pkg/features/features.go index c533f0ae7..7ee8c37c1 100644 --- a/pkg/features/features.go +++ b/pkg/features/features.go @@ -6,25 +6,21 @@ package features import ( "context" "errors" - "sync" "gopkg.in/yaml.v3" corev1 "k8s.io/api/core/v1" kerrors "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - - "github.com/cloudoperators/greenhouse/pkg/clientutil" ) const ( - DexFeatureKey = "dex" - featureConfigMapName = "greenhouse-feature-flags" + DexFeatureKey = "dex" ) type features struct { - m sync.Mutex raw map[string]string dex *dexFeatures `yaml:"dex"` } @@ -37,9 +33,9 @@ type Features interface { GetDexStorageType(ctx context.Context) *string } -func NewFeatures(ctx context.Context, k8sClient client.Reader, configMapName, podNameSpace string) (Features, error) { +func NewFeatures(ctx context.Context, k8sClient client.Reader, configMapName, namespace string) (Features, error) { featureMap := &corev1.ConfigMap{} - if err := k8sClient.Get(ctx, types.NamespacedName{Name: configMapName, Namespace: podNameSpace}, featureMap); err != nil { + if err := k8sClient.Get(ctx, types.NamespacedName{Name: configMapName, Namespace: namespace}, featureMap); err != nil { if kerrors.IsNotFound(err) { return nil, nil } @@ -51,9 +47,6 @@ func NewFeatures(ctx context.Context, k8sClient client.Reader, configMapName, po } func (f *features) resolveDexFeatures() error { - f.m.Lock() - defer f.m.Unlock() - // Extract the `dex` key from the ConfigMap dexRaw, exists := f.raw[DexFeatureKey] if !exists { @@ -73,7 +66,7 @@ func (f *features) resolveDexFeatures() error { func (f *features) GetDexStorageType(ctx context.Context) *string { if f.dex != nil { - return clientutil.Ptr(f.dex.Storage) + return ptr.To(f.dex.Storage) } if err := f.resolveDexFeatures(); err != nil { ctrl.LoggerFrom(ctx).Error(err, "failed to resolve dex features") @@ -82,5 +75,5 @@ func (f *features) GetDexStorageType(ctx context.Context) *string { if f.dex.Storage == "" { return nil } - return clientutil.Ptr(f.dex.Storage) + return ptr.To(f.dex.Storage) } From 153bdcd711ce1ee85463713f37d18c4e238c28a6 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Thu, 13 Feb 2025 09:26:15 +0100 Subject: [PATCH 057/108] (chore): inline homeDir usage --- pkg/dex/store/k8s_store.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkg/dex/store/k8s_store.go b/pkg/dex/store/k8s_store.go index 30930bd63..6b6f329e0 100644 --- a/pkg/dex/store/k8s_store.go +++ b/pkg/dex/store/k8s_store.go @@ -62,8 +62,7 @@ func determineKubeMode() string { if err == nil { return "" } - home := homedir.HomeDir() - return filepath.Join(home, ".kube", "config") + return filepath.Join(homedir.HomeDir(), ".kube", "config") } func (k *k8sDex) GetBackend() string { From 2565083399f994cd1a013146647c964d9783482a Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Thu, 13 Feb 2025 09:27:00 +0100 Subject: [PATCH 058/108] (chore): uses descriptive var names and const --- cmd/greenhouse/main.go | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/cmd/greenhouse/main.go b/cmd/greenhouse/main.go index e2c68ae54..a1610539c 100644 --- a/cmd/greenhouse/main.go +++ b/cmd/greenhouse/main.go @@ -48,8 +48,12 @@ const ( const ( defaultRemoteClusterBearerTokenValidity = 24 * time.Hour defaultRenewRemoteClusterBearerTokenAfter = 20 * time.Hour - disableControllersEnv = "WEBHOOK_ONLY" // used to deploy the operator in webhook only mode no controllers will run in this mode. - disableWebhookEnv = "CONTROLLERS_ONLY" // used to disable webhooks when running locally or in debug mode. + disableControllersEnv = "WEBHOOK_ONLY" // used to deploy the operator in webhook only mode no controllers will run in this mode. + disableWebhookEnv = "CONTROLLERS_ONLY" // used to disable webhooks when running locally or in debug mode. + podNamespaceEnv = "POD_NAMESPACE" // used to read the pod namespace from the environment. + defaultPodNamespace = "greenhouse" // default pod namespace. + featureFlagsEnv = "FEATURE_FLAGS" // used to read the feature flags configMap name from the environment. + defaultFeatureFlagConfigMapName = "greenhouse-feature-flags" // default feature flags configMap name. ) var ( @@ -60,7 +64,7 @@ var ( remoteClusterBearerTokenValidity, renewRemoteClusterBearerTokenAfter time.Duration kubeClientOpts clientutil.RuntimeOptions - f features.Features + featureFlags features.Features ) func init() { @@ -117,7 +121,7 @@ func main() { // Disable leader election if not run within a cluster. isEnableLeaderElection := true - if _, ok := os.LookupEnv("POD_NAMESPACE"); !ok { + if _, ok := os.LookupEnv(podNamespaceEnv); !ok { isEnableLeaderElection = false } @@ -137,7 +141,12 @@ func main() { // Note: mgr.GetClient() will fail here because the cache is not ready yet k8sClient := mgr.GetAPIReader() // Initialize the feature gates from feature-flags config map - f, err = features.NewFeatures(context.TODO(), k8sClient, clientutil.GetEnvOrDefault("FEATURE_FLAGS", "greenhouse-feature-flags"), clientutil.GetEnvOrDefault("POD_NAMESPACE", "greenhouse")) + featureFlags, err = features.NewFeatures( + context.TODO(), + k8sClient, + clientutil.GetEnvOrDefault(featureFlagsEnv, defaultFeatureFlagConfigMapName), + clientutil.GetEnvOrDefault(podNamespaceEnv, defaultPodNamespace), + ) if err != nil { handleError(err, "unable to get features") } From d05bc49347aa46548fa1d6e619149341c0b0d713 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Thu, 13 Feb 2025 09:27:21 +0100 Subject: [PATCH 059/108] (chore): adds logging --- pkg/dex/store/pg_store.go | 32 +++++++++++++++++--------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/pkg/dex/store/pg_store.go b/pkg/dex/store/pg_store.go index be6474f6c..1df8b9c42 100644 --- a/pkg/dex/store/pg_store.go +++ b/pkg/dex/store/pg_store.go @@ -69,29 +69,31 @@ func (p *pgDex) GetBackend() string { // CreateUpdateConnector - creates or updates a dex connector in dex postgres storage backend func (p *pgDex) CreateUpdateConnector(ctx context.Context, _ client.Client, org *greenhouseapisv1alpha1.Organization, configByte []byte) error { oidcConnector, err := p.storage.GetConnector(org.Name) - if err != nil && errors.Is(err, storage.ErrNotFound) { - if err = p.storage.CreateConnector(ctx, storage.Connector{ - ID: org.Name, - Type: dexConnectorTypeGreenhouse, - Name: cases.Title(language.English).String(org.Name), - Config: configByte, - }); err != nil { - return err - } - log.FromContext(ctx).Info("created dex connector in SQL storage", "name", org.Name) - } if err != nil { + if errors.Is(err, storage.ErrNotFound) { + err = p.storage.CreateConnector(ctx, storage.Connector{ + ID: org.Name, + Type: dexConnectorTypeGreenhouse, + Name: cases.Title(language.English).String(org.Name), + Config: configByte, + }) + if err != nil { + return err + } + log.FromContext(ctx).Info("created dex connector in SQL storage", "name", org.Name) + return nil + } log.FromContext(ctx).Error(err, "failed to get dex connector in SQL storage", "name", org.Name) + return err } - err = p.storage.UpdateConnector(oidcConnector.ID, func(c storage.Connector) (storage.Connector, error) { + if err = p.storage.UpdateConnector(oidcConnector.ID, func(c storage.Connector) (storage.Connector, error) { c.ID = org.Name c.Type = dexConnectorTypeGreenhouse c.Name = cases.Title(language.English).String(org.Name) c.Config = configByte return c, nil - }) - - if err != nil { + }); err != nil { + log.FromContext(ctx).Error(err, "failed to update dex connector in SQL storage", "name", org.Name) return err } log.FromContext(ctx).Info("updated dex connector in SQL storage", "name", org.Name) From e7fa9aa965960632572a168944fe25289716ecd6 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Thu, 13 Feb 2025 11:25:53 +0100 Subject: [PATCH 060/108] (chore): adds common redirect uri logic --- pkg/dex/store/k8s_store.go | 14 +++-------- pkg/dex/store/pg_store.go | 51 ++++++++++++++------------------------ pkg/dex/store/storage.go | 18 ++++++++++++++ pkg/util/slices.go | 14 ----------- 4 files changed, 40 insertions(+), 57 deletions(-) delete mode 100644 pkg/util/slices.go diff --git a/pkg/dex/store/k8s_store.go b/pkg/dex/store/k8s_store.go index 6b6f329e0..ec43c1689 100644 --- a/pkg/dex/store/k8s_store.go +++ b/pkg/dex/store/k8s_store.go @@ -6,7 +6,6 @@ package store import ( "context" "encoding/base32" - "fmt" "hash/fnv" "log/slog" "os" @@ -26,9 +25,7 @@ import ( greenhouseapisv1alpha1 "github.com/cloudoperators/greenhouse/pkg/apis/greenhouse/v1alpha1" "github.com/cloudoperators/greenhouse/pkg/clientutil" - "github.com/cloudoperators/greenhouse/pkg/common" dexapi "github.com/cloudoperators/greenhouse/pkg/dex/api" - "github.com/cloudoperators/greenhouse/pkg/util" ) type k8sDex struct { @@ -84,6 +81,7 @@ func (k *k8sDex) CreateUpdateConnector(ctx context.Context, k8sClient client.Cli return controllerutil.SetControllerReference(org, dexConnector, k8sClient.Scheme()) }) if err != nil { + log.FromContext(ctx).Error(err, "failed to create/update dex connector", "namespace", dexConnector.Namespace, "name", dexConnector.GetName()) return } switch result { @@ -106,17 +104,11 @@ func (k *k8sDex) CreateUpdateOauth2Client(ctx context.Context, k8sClient client. oAuth2Client.Client.Public = true oAuth2Client.Client.ID = org.Name oAuth2Client.Client.Name = org.Name - for _, requiredRedirectURL := range []string{ - "http://localhost:8085", - "http://localhost:8000", - "https://dashboard." + common.DNSDomain, - fmt.Sprintf("https://%s.dashboard.%s", org.Name, common.DNSDomain), - } { - oAuth2Client.Client.RedirectURIs = util.AppendStringToSliceIfNotContains(requiredRedirectURL, oAuth2Client.RedirectURIs) - } + oAuth2Client.RedirectURIs = getRedirects(org, oAuth2Client.RedirectURIs) return controllerutil.SetControllerReference(org, oAuth2Client, k8sClient.Scheme()) }) if err != nil { + log.FromContext(ctx).Error(err, "failed to create/update oauth2client", "namespace", oAuth2Client.Namespace, "name", oAuth2Client.GetName()) return err } switch result { diff --git a/pkg/dex/store/pg_store.go b/pkg/dex/store/pg_store.go index 1df8b9c42..4f6320d17 100644 --- a/pkg/dex/store/pg_store.go +++ b/pkg/dex/store/pg_store.go @@ -6,7 +6,6 @@ package store import ( "context" "errors" - "fmt" "log/slog" "github.com/dexidp/dex/storage" @@ -18,8 +17,6 @@ import ( greenhouseapisv1alpha1 "github.com/cloudoperators/greenhouse/pkg/apis/greenhouse/v1alpha1" "github.com/cloudoperators/greenhouse/pkg/clientutil" - "github.com/cloudoperators/greenhouse/pkg/common" - "github.com/cloudoperators/greenhouse/pkg/util" ) type pgDex struct { @@ -71,13 +68,13 @@ func (p *pgDex) CreateUpdateConnector(ctx context.Context, _ client.Client, org oidcConnector, err := p.storage.GetConnector(org.Name) if err != nil { if errors.Is(err, storage.ErrNotFound) { - err = p.storage.CreateConnector(ctx, storage.Connector{ + if err = p.storage.CreateConnector(ctx, storage.Connector{ ID: org.Name, Type: dexConnectorTypeGreenhouse, Name: cases.Title(language.English).String(org.Name), Config: configByte, - }) - if err != nil { + }); err != nil { + log.FromContext(ctx).Error(err, "failed to create dex connector in SQL storage", "name", org.Name) return err } log.FromContext(ctx).Info("created dex connector in SQL storage", "name", org.Name) @@ -103,42 +100,32 @@ func (p *pgDex) CreateUpdateConnector(ctx context.Context, _ client.Client, org // CreateUpdateOauth2Client - creates or updates an oauth2 client in dex postgres storage backend func (p *pgDex) CreateUpdateOauth2Client(ctx context.Context, _ client.Client, org *greenhouseapisv1alpha1.Organization) error { oAuthClient, err := p.storage.GetClient(org.Name) - if err != nil && errors.Is(err, storage.ErrNotFound) { - if err = p.storage.CreateClient(ctx, storage.Client{ - Public: true, - ID: org.Name, - Name: org.Name, - RedirectURIs: []string{ - "http://localhost:8085", - "https://dashboard." + common.DNSDomain, - fmt.Sprintf("https://%s.dashboard.%s", org.Name, common.DNSDomain), - }, - }); err != nil { - return err + if err != nil { + if errors.Is(err, storage.ErrNotFound) { + if err = p.storage.CreateClient(ctx, storage.Client{ + Public: true, + ID: org.Name, + Name: org.Name, + RedirectURIs: getRedirects(org, nil), + }); err != nil { + log.FromContext(ctx).Error(err, "failed to create oauth2client", "name", org.Name) + return err + } } log.FromContext(ctx).Info("created oauth2client", "name", org.Name) return nil } - if err != nil { - log.FromContext(ctx).Error(err, "failed to get oauth2client", "name", org.Name) - } - - err = p.storage.UpdateClient(oAuthClient.Name, func(authClient storage.Client) (storage.Client, error) { + if err = p.storage.UpdateClient(oAuthClient.Name, func(authClient storage.Client) (storage.Client, error) { authClient.Public = true authClient.ID = org.Name authClient.Name = org.Name - for _, requiredRedirectURL := range []string{ - "http://localhost:8085", - "https://dashboard." + common.DNSDomain, - fmt.Sprintf("https://%s.dashboard.%s", org.Name, common.DNSDomain), - } { - authClient.RedirectURIs = util.AppendStringToSliceIfNotContains(requiredRedirectURL, authClient.RedirectURIs) - } + authClient.RedirectURIs = getRedirects(org, authClient.RedirectURIs) return authClient, nil - }) - if err != nil { + }); err != nil { + log.FromContext(ctx).Error(err, "failed to update oauth2client", "name", org.Name) return err } + log.FromContext(ctx).Info("updated oauth2client", "name", org.Name) return nil } diff --git a/pkg/dex/store/storage.go b/pkg/dex/store/storage.go index 4490cfb43..d3305de63 100644 --- a/pkg/dex/store/storage.go +++ b/pkg/dex/store/storage.go @@ -7,11 +7,13 @@ import ( "context" "fmt" "log/slog" + "slices" "github.com/dexidp/dex/storage" "sigs.k8s.io/controller-runtime/pkg/client" greenhouseapisv1alpha1 "github.com/cloudoperators/greenhouse/pkg/apis/greenhouse/v1alpha1" + "github.com/cloudoperators/greenhouse/pkg/common" ) const ( @@ -49,3 +51,19 @@ func NewDexStorageFactory(logger *slog.Logger, backend string) (Dexter, error) { return nil, fmt.Errorf("unknown dexStorage backend: %s", backend) } } + +func getRedirects(org *greenhouseapisv1alpha1.Organization, redirectURIs []string) []string { + redirects := []string{ + "http://localhost:8085", + "https://dashboard." + common.DNSDomain, + fmt.Sprintf("https://%s.dashboard.%s", org.Name, common.DNSDomain), + } + + for _, r := range redirects { + if !slices.Contains(redirectURIs, r) { + redirectURIs = append(redirectURIs, r) + } + } + + return redirects +} diff --git a/pkg/util/slices.go b/pkg/util/slices.go deleted file mode 100644 index a977344a7..000000000 --- a/pkg/util/slices.go +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors -// SPDX-License-Identifier: Apache-2.0 - -package util - -import "slices" - -// AppendStringToSliceIfNotContains appends a string to a slice if it is not already present. -func AppendStringToSliceIfNotContains(theString string, theSlice []string) []string { - if !slices.Contains(theSlice, theString) { - theSlice = append(theSlice, theString) - } - return theSlice -} From a30ae0b2bb5837b50b828e8bb9bae6c43f91327c Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Thu, 13 Feb 2025 11:46:41 +0100 Subject: [PATCH 061/108] (chore): features as struct methods --- cmd/greenhouse/main.go | 2 +- pkg/features/features.go | 14 +++++--------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/cmd/greenhouse/main.go b/cmd/greenhouse/main.go index a1610539c..e2df3ad5c 100644 --- a/cmd/greenhouse/main.go +++ b/cmd/greenhouse/main.go @@ -64,7 +64,7 @@ var ( remoteClusterBearerTokenValidity, renewRemoteClusterBearerTokenAfter time.Duration kubeClientOpts clientutil.RuntimeOptions - featureFlags features.Features + featureFlags *features.Features ) func init() { diff --git a/pkg/features/features.go b/pkg/features/features.go index 7ee8c37c1..8cb94385d 100644 --- a/pkg/features/features.go +++ b/pkg/features/features.go @@ -20,7 +20,7 @@ const ( DexFeatureKey = "dex" ) -type features struct { +type Features struct { raw map[string]string dex *dexFeatures `yaml:"dex"` } @@ -29,11 +29,7 @@ type dexFeatures struct { Storage string `yaml:"storage"` } -type Features interface { - GetDexStorageType(ctx context.Context) *string -} - -func NewFeatures(ctx context.Context, k8sClient client.Reader, configMapName, namespace string) (Features, error) { +func NewFeatures(ctx context.Context, k8sClient client.Reader, configMapName, namespace string) (*Features, error) { featureMap := &corev1.ConfigMap{} if err := k8sClient.Get(ctx, types.NamespacedName{Name: configMapName, Namespace: namespace}, featureMap); err != nil { if kerrors.IsNotFound(err) { @@ -41,12 +37,12 @@ func NewFeatures(ctx context.Context, k8sClient client.Reader, configMapName, na } return nil, err } - return &features{ + return &Features{ raw: featureMap.Data, }, nil } -func (f *features) resolveDexFeatures() error { +func (f *Features) resolveDexFeatures() error { // Extract the `dex` key from the ConfigMap dexRaw, exists := f.raw[DexFeatureKey] if !exists { @@ -64,7 +60,7 @@ func (f *features) resolveDexFeatures() error { return nil } -func (f *features) GetDexStorageType(ctx context.Context) *string { +func (f *Features) GetDexStorageType(ctx context.Context) *string { if f.dex != nil { return ptr.To(f.dex.Storage) } From 727be1e6286666d755f24aa6126f83ec5540775a Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Thu, 13 Feb 2025 11:50:51 +0100 Subject: [PATCH 062/108] (chore): adds inline comments --- pkg/clientutil/env.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/clientutil/env.go b/pkg/clientutil/env.go index f95b1c2b1..11db87376 100644 --- a/pkg/clientutil/env.go +++ b/pkg/clientutil/env.go @@ -4,7 +4,7 @@ package clientutil import ( - "errors" + "fmt" "os" "strconv" ) @@ -17,6 +17,7 @@ func GetEnvOrDefault(envKey, defaultValue string) string { return defaultValue } +// GetIntEnvWithDefault returns the integer value of the environment variable or the default value. func GetIntEnvWithDefault(envKey string, def int) int { s := os.Getenv(envKey) i, err := strconv.Atoi(s) @@ -26,6 +27,7 @@ func GetIntEnvWithDefault(envKey string, def int) int { return i } +// GetEnv returns the value of the environment variable or an error if it is not set func GetEnv(envKey string) (string, error) { if v, ok := os.LookupEnv(envKey); ok { return v, nil From 02f09ba90e3800bf11fa87784737fbaab4c1e640 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Thu, 13 Feb 2025 11:51:49 +0100 Subject: [PATCH 063/108] (chore): use k8s ptr.To --- cmd/idproxy/main.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cmd/idproxy/main.go b/cmd/idproxy/main.go index a41e05fac..ce6012930 100644 --- a/cmd/idproxy/main.go +++ b/cmd/idproxy/main.go @@ -23,6 +23,7 @@ import ( "github.com/prometheus/client_golang/prometheus/promhttp" flag "github.com/spf13/pflag" _ "k8s.io/client-go/plugin/pkg/client/auth/oidc" + "k8s.io/utils/ptr" ctrl "sigs.k8s.io/controller-runtime/pkg/client/config" logk "sigs.k8s.io/controller-runtime/pkg/log" @@ -64,7 +65,7 @@ func main() { log.Fatalf("failed to create k8s client: %s", err) } // default to kubernetes storage backend - backend := clientutil.Ptr("kubernetes") + backend := ptr.To("kubernetes") ghFeatures, err := features.NewFeatures(ctx, k8sClient, clientutil.GetEnvOrDefault("FEATURE_FLAGS", "greenhouse-feature-flags"), clientutil.GetEnvOrDefault("POD_NAMESPACE", "greenhouse")) if err != nil { log.Fatalf("failed to get greenhouse features: %s", err) From e9a32039e967631a1b775eb534e25f2d7bd8f44e Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Thu, 13 Feb 2025 12:06:07 +0100 Subject: [PATCH 064/108] (chore): fix lints --- pkg/dex/store/k8s_store.go | 2 +- pkg/dex/store/pg_store.go | 2 +- pkg/features/features_test.go | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/dex/store/k8s_store.go b/pkg/dex/store/k8s_store.go index ec43c1689..364670846 100644 --- a/pkg/dex/store/k8s_store.go +++ b/pkg/dex/store/k8s_store.go @@ -52,7 +52,7 @@ func newKubernetesStore(logger *slog.Logger) (storage.Storage, error) { func determineKubeMode() string { cfgPath := os.Getenv(clientcmd.RecommendedConfigPathEnvVar) - if len(cfgPath) > 0 { + if strings.TrimSpace(cfgPath) != "" { return cfgPath } _, err := rest.InClusterConfig() diff --git a/pkg/dex/store/pg_store.go b/pkg/dex/store/pg_store.go index 4f6320d17..318d03d94 100644 --- a/pkg/dex/store/pg_store.go +++ b/pkg/dex/store/pg_store.go @@ -50,7 +50,7 @@ func newPostgresStore(logger *slog.Logger) (storage.Storage, error) { SSL: sql.SSL{Mode: "disable"}, NetworkDB: sql.NetworkDB{ Host: host, - Port: uint16(port), + Port: uint16(port), //nolint:gosec User: user, Password: pass, Database: database, diff --git a/pkg/features/features_test.go b/pkg/features/features_test.go index 41f327fd5..6f7f50e1d 100644 --- a/pkg/features/features_test.go +++ b/pkg/features/features_test.go @@ -79,7 +79,7 @@ func Test_DexFeatures(t *testing.T) { mockK8sClient.On("Get", ctx, types.NamespacedName{ Name: clientutil.GetEnvOrDefault("FEATURE_FLAGS", "greenhouse-feature-flags"), Namespace: clientutil.GetEnvOrDefault("POD_NAMESPACE", "greenhouse"), }, mock.Anything).Run(func(args mock.Arguments) { - arg := args.Get(2).(*corev1.ConfigMap) + arg := args.Get(2).(*corev1.ConfigMap) //nolint:errcheck *arg = *configMap }).Return(nil) } From b3ec876d943087988a6bd38064933faa85083919 Mon Sep 17 00:00:00 2001 From: David Gogl <1381862+kengou@users.noreply.github.com> Date: Thu, 13 Feb 2025 14:53:53 +0100 Subject: [PATCH 065/108] fix(postgres-ng) make chart working --- charts/greenhouse/Chart.lock | 4 ++-- charts/greenhouse/Chart.yaml | 2 +- charts/greenhouse/ci/test-values.yaml | 24 +++++++++++++++++++ charts/greenhouse/templates/_helpers.tpl | 4 ++-- charts/greenhouse/values.yaml | 21 ++++++++-------- charts/idproxy/ci/test-values.yaml | 7 ++++++ charts/idproxy/templates/_helpers.tpl | 9 +++++-- charts/idproxy/templates/deployment.yaml | 8 +++---- charts/manager/ci/test-values.yaml | 6 +++++ charts/manager/templates/_helpers.tpl | 8 +++++-- charts/manager/templates/deployment.yaml | 6 ++--- .../templates/manager-featureflag.yaml | 2 +- 12 files changed, 74 insertions(+), 27 deletions(-) diff --git a/charts/greenhouse/Chart.lock b/charts/greenhouse/Chart.lock index fc0d001d3..1b5547e42 100644 --- a/charts/greenhouse/Chart.lock +++ b/charts/greenhouse/Chart.lock @@ -17,5 +17,5 @@ dependencies: - name: postgresql-ng repository: oci://ghcr.io/sapcc/helm-charts version: 1.2.7 -digest: sha256:d39c88a67fdf87f9098058d0fbf33eb43a315bc9011da5134c64a67718c94b8a -generated: "2025-02-11T13:30:08.243315+01:00" +digest: sha256:ed5f9a31d629834b1337d8af26be4fd178f2bd8d9624495942ed8e4542100798 +generated: "2025-02-13T14:50:22.07126+01:00" diff --git a/charts/greenhouse/Chart.yaml b/charts/greenhouse/Chart.yaml index 9d4c25a86..174da887d 100644 --- a/charts/greenhouse/Chart.yaml +++ b/charts/greenhouse/Chart.yaml @@ -31,4 +31,4 @@ dependencies: - name: postgresql-ng version: 1.2.7 repository: "oci://ghcr.io/sapcc/helm-charts" - condition: postgresql.enabled + condition: postgresql-ng.enabled diff --git a/charts/greenhouse/ci/test-values.yaml b/charts/greenhouse/ci/test-values.yaml index e581ca0cf..547da87f1 100644 --- a/charts/greenhouse/ci/test-values.yaml +++ b/charts/greenhouse/ci/test-values.yaml @@ -9,6 +9,30 @@ global: redirectURL: https://top.secret/redirect clientID: topSecret! clientSecret: topSecret! + # DEX configuration for Greenhouse. + dex: + backend: postgres # postgres or kubernetesa + postgres: + postgresqlDatabase: dex + postgresqlPort: 5432 + postgresqlUsername: dex + linkerd_enabled: false + region: greenhouse + registry: keppel.eu-nl-1.cloud.sap/ccloud + +postgresql-ng: + enabled: true + resources: {} + # The database that will be created in the database + postgresDatabase: dex + tableOwner: acme-user + users: + acme-user: + +tableOwner: acme-user +users: + acme-user: + plugins: enabled: true diff --git a/charts/greenhouse/templates/_helpers.tpl b/charts/greenhouse/templates/_helpers.tpl index 74e364638..a8c065ad4 100644 --- a/charts/greenhouse/templates/_helpers.tpl +++ b/charts/greenhouse/templates/_helpers.tpl @@ -64,9 +64,9 @@ Create the name of the service account to use {{/* Check if global.dex.backend is postgres then the postgressql part need to be enabled as well */}} -{{- if .Values.global.dex.backend == "postgres" && eq .Values.postgresql.enabled "false" }} +{{- if and (eq .Values.global.dex.backend "postgres") (eq .Values.postgresql.enabled "false") }} {{- fail "dex.backend: Setting the dex.backend to postgres requires that you enable and configure postgresql" }} {{- end }} -{{- if .Values.global.dex.backend == "kubernetes" && eq .Values.postgresql.enabled "true" }} +{{- if and (eq .Values.global.dex.backend "kubernetes") (eq .Values.postgresql.enabled "true") }} {{- fail "dex.backend: Setting the dex.backend to kubernetes does not require postgresql enabled" }} {{- end }} diff --git a/charts/greenhouse/values.yaml b/charts/greenhouse/values.yaml index d0a0be15c..f9701f4fd 100644 --- a/charts/greenhouse/values.yaml +++ b/charts/greenhouse/values.yaml @@ -13,26 +13,27 @@ global: clientSecret: # DEX configuration for Greenhouse. dex: - backend: postgres # postgres or kubernetes - postgres: + backend: postgres # postgres or kubernetes + postgresql: postgresqlDatabase: - postgresqlHost: postgresqlPort: postgresqlUsername: -postgresql: +postgresql-ng: enabled: true global: linkerd_enabled: false region: greenhouse - registry: # The registry where the image is stored + # The registry where the image is stored + registry: resources: {} - postgresDatabase: # The database that will be created in the database + # The database that will be created in the database + postgresDatabase: dex users: - #dex: # The user that will be created in the database - # grant: # The grants that will be given to the user - # - 'GRANT ALL PRIVILEGES ON DATABASE "%PGDATABASE%"' - tableOwner: # The owner of the tables in the database + dex: # The user that will be created in the database + grant: # The grants that will be given to the user + - 'GRANT ALL PRIVILEGES ON DATABASE "%PGDATABASE%"' + tableOwner: # The owner of the tables in the database # Organization & IDProxy should be enabled only after the initial install of greenhouse organization: diff --git a/charts/idproxy/ci/test-values.yaml b/charts/idproxy/ci/test-values.yaml index 059e64978..5e014e617 100644 --- a/charts/idproxy/ci/test-values.yaml +++ b/charts/idproxy/ci/test-values.yaml @@ -2,6 +2,13 @@ # SPDX-License-Identifier: Apache-2.0 global: dnsDomain: example.com + dex: + backend: postgres + postgresql: + postgresqlDatabase: dex + postgresqlPort: 5432 + postgresqlUsername: dex + ingress: enabled: true diff --git a/charts/idproxy/templates/_helpers.tpl b/charts/idproxy/templates/_helpers.tpl index 266a2dcc9..5160635b0 100644 --- a/charts/idproxy/templates/_helpers.tpl +++ b/charts/idproxy/templates/_helpers.tpl @@ -72,6 +72,11 @@ Define postgresql helpers {{- define "postgres.fullname" -}} {{- printf "%s-postgresql" .Release.Name | trunc 48 | replace "_" "-" -}} {{- end -}} -{{- denife "feature-flag.fullname"-}} - {{- printf "%s-feature-flag" .Release.Name | trunc 48 | replace "_" "-" -}} +{{- define "featureFlag.fullname" -}} + {{- printf "%s-feature-flag" .Release.Name | trunc 48 | replace "_" "-" -}} {{- end -}} +{{/* Render the backend */}} +{{- define "dex.backend" -}} + {{- printf "%s" (required "global.dex.backend missing" .Values.global.dex.backend) }} +{{- end }} + diff --git a/charts/idproxy/templates/deployment.yaml b/charts/idproxy/templates/deployment.yaml index 3f4d998fa..f5b20e937 100644 --- a/charts/idproxy/templates/deployment.yaml +++ b/charts/idproxy/templates/deployment.yaml @@ -56,12 +56,12 @@ spec: {{- end }} env: - name: FEATURE_FLAGS - value: {{ feature-flag.fullname }} - {{- if .Values.global.dex.backend == "postgres" }} + value: {{ template "featureFlag.fullname" . }} + {{- if eq (include "dex.backend" $) "postgres" }} - name: PG_DATABASE value: {{ .Values.global.dex.postgresql.postgresqlDatabase }} - name: PG_HOST - value: {{ .Values.global.dex.postgresql.postgresqlHost }} + value: {{- printf "%s-postgresql" .Release.Name | trunc 48 | replace "_" "-" -}} - name: PG_PORT value: {{ .Values.global.dex.postgresql.postgresqlPort }} - name: PG_USER @@ -78,7 +78,7 @@ spec: apiVersion: v1 fieldPath: metadata.namespace - name: FEATURE_FLAGS - value: {{ feature-flag.fullname }} + value: {{ template "featureFlag.fullname" . }} ports: - name: oidc containerPort: 8080 diff --git a/charts/manager/ci/test-values.yaml b/charts/manager/ci/test-values.yaml index a84e67637..e83327030 100644 --- a/charts/manager/ci/test-values.yaml +++ b/charts/manager/ci/test-values.yaml @@ -3,3 +3,9 @@ global: dnsDomain: green.house + dex: + backend: postgres + postgresql: + postgresqlDatabase: dex + postgresqlPort: 5432 + postgresqlUsername: dex diff --git a/charts/manager/templates/_helpers.tpl b/charts/manager/templates/_helpers.tpl index 8d6d83ee0..9fd186b15 100644 --- a/charts/manager/templates/_helpers.tpl +++ b/charts/manager/templates/_helpers.tpl @@ -75,6 +75,10 @@ Define postgresql helpers {{- define "postgres.fullname" -}} {{- printf "%s-postgresql" .Release.Name | trunc 48 | replace "_" "-" -}} {{- end -}} -{{- denife "feature-flag.fullname"-}} - {{- printf "%s-feature-flag" .Release.Name | trunc 48 | replace "_" "-" -}} +{{- define "featureFlag.fullname" -}} + {{- printf "%s-feature-flag" .Release.Name | trunc 48 | replace "_" "-" -}} {{- end -}} +{{/* Render the backend */}} +{{- define "dex.backend" -}} + {{- printf "%s" (required "global.dex.backend missing" .Values.global.dex.backend) }} +{{- end }} \ No newline at end of file diff --git a/charts/manager/templates/deployment.yaml b/charts/manager/templates/deployment.yaml index ca4892071..0310a548b 100644 --- a/charts/manager/templates/deployment.yaml +++ b/charts/manager/templates/deployment.yaml @@ -36,12 +36,12 @@ spec: {{- end }} env: - name: FEATURE_FLAGS - value: {{ feature-flag.fullname }} - {{- if .Values.global.dex.backend == "postgres" }} + value: {{ template "featureFlag.fullname" . }} + {{- if eq .Values.global.dex.backend "postgres" }} - name: PG_DATABASE value: {{ .Values.global.dex.postgresql.postgresqlDatabase }} - name: PG_HOST - value: {{ .Values.global.dex.postgresql.postgresqlHost }} + value: {{- printf "%s-postgresql" .Release.Name | trunc 48 | replace "_" "-" -}} - name: PG_PORT value: {{ .Values.global.dex.postgresql.postgresqlPort }} - name: PG_USER diff --git a/charts/manager/templates/manager-featureflag.yaml b/charts/manager/templates/manager-featureflag.yaml index b38e9cc3a..fb21bf6f3 100644 --- a/charts/manager/templates/manager-featureflag.yaml +++ b/charts/manager/templates/manager-featureflag.yaml @@ -16,4 +16,4 @@ data: dex: | storage: kubernetes / postgres dex: | - storage: {{ .Values.dex.backend }} + storage: {{ include "dex.backend" $ }} From 3ed7b18ad5e0a726e409d0957f71efea2a050ddc Mon Sep 17 00:00:00 2001 From: David Gogl <1381862+kengou@users.noreply.github.com> Date: Thu, 13 Feb 2025 14:55:22 +0100 Subject: [PATCH 066/108] bump umbrella chart version --- charts/greenhouse/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/greenhouse/Chart.yaml b/charts/greenhouse/Chart.yaml index 0a0ffe142..e41419db5 100644 --- a/charts/greenhouse/Chart.yaml +++ b/charts/greenhouse/Chart.yaml @@ -5,7 +5,7 @@ apiVersion: v2 name: greenhouse description: A Helm chart for deploying greenhouse type: application -version: 0.8.1 +version: 0.8.2 appVersion: "0.1.0" dependencies: From 6b86e16592189a812e802b6a7b0591d325e6feed Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Thu, 13 Feb 2025 23:35:51 +0100 Subject: [PATCH 067/108] (chore): simplifies dex storage usage --- .mockery.yaml | 3 - pkg/dex/storage.go | 105 +++++++++++++++++++++++++++ pkg/dex/store/k8s_store.go | 141 ------------------------------------- pkg/dex/store/pg_store.go | 139 ------------------------------------ pkg/dex/store/storage.go | 69 ------------------ pkg/mocks/mock_Dexter.go | 126 --------------------------------- 6 files changed, 105 insertions(+), 478 deletions(-) create mode 100644 pkg/dex/storage.go delete mode 100644 pkg/dex/store/k8s_store.go delete mode 100644 pkg/dex/store/pg_store.go delete mode 100644 pkg/dex/store/storage.go delete mode 100644 pkg/mocks/mock_Dexter.go diff --git a/.mockery.yaml b/.mockery.yaml index 50cb92190..26364ec9e 100644 --- a/.mockery.yaml +++ b/.mockery.yaml @@ -4,9 +4,6 @@ filename: "mock_{{.InterfaceName}}.go" outpkg: mocks dir: pkg/mocks packages: - github.com/cloudoperators/greenhouse/pkg/dex/store: - interfaces: - Dexter: github.com/cloudoperators/greenhouse/pkg/lifecycle: interfaces: Reconciler: diff --git a/pkg/dex/storage.go b/pkg/dex/storage.go new file mode 100644 index 000000000..8d677ed11 --- /dev/null +++ b/pkg/dex/storage.go @@ -0,0 +1,105 @@ +// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors +// SPDX-License-Identifier: Apache-2.0 + +package dex + +import ( + "fmt" + "log/slog" + "os" + "path/filepath" + "strings" + + "github.com/dexidp/dex/storage" + "github.com/dexidp/dex/storage/kubernetes" + "github.com/dexidp/dex/storage/sql" + "k8s.io/client-go/rest" + "k8s.io/client-go/tools/clientcmd" + "k8s.io/client-go/util/homedir" + + "github.com/cloudoperators/greenhouse/pkg/clientutil" +) + +const ( + Postgres = "postgres" + K8s = "kubernetes" +) + +const ( + hostEnv = "PG_HOST" + portEnv = "PG_PORT" + userEnv = "PG_USER" + passEnv = "PG_PASSWORD" + dbNameEnv = "PG_DATABASE" +) + +type Dexter struct { + storage storage.Storage + backend string +} + +// newKubernetesStore - creates a new kubernetes storage backend for dex +func newKubernetesStore(logger *slog.Logger) (storage.Storage, error) { + cfg := kubernetes.Config{InCluster: true} + cfgPath := determineKubeMode() + if strings.TrimSpace(cfgPath) != "" { + cfg.InCluster = false + cfg.KubeConfigFile = cfgPath + } + dexStorage, err := cfg.Open(logger) + if err != nil { + return nil, err + } + return dexStorage, nil +} + +func determineKubeMode() string { + cfgPath := os.Getenv(clientcmd.RecommendedConfigPathEnvVar) + if strings.TrimSpace(cfgPath) != "" { + return cfgPath + } + _, err := rest.InClusterConfig() + if err == nil { + return "" + } + return filepath.Join(homedir.HomeDir(), ".kube", "config") +} + +// newPostgresStore - creates a new postgres storage backend for dex +func newPostgresStore(logger *slog.Logger) (storage.Storage, error) { + var host, user, pass, database string + var port int + var err error + database = clientutil.GetEnvOrDefault(dbNameEnv, "postgres") + user = clientutil.GetEnvOrDefault(userEnv, "postgres") + port = clientutil.GetIntEnvWithDefault(portEnv, 5432) + if host, err = clientutil.GetEnv(hostEnv); err != nil { + return nil, err + } + if pass, err = clientutil.GetEnv(passEnv); err != nil { + return nil, err + } + cfg := &sql.Postgres{ + SSL: sql.SSL{Mode: "disable"}, + NetworkDB: sql.NetworkDB{ + Host: host, + Port: uint16(port), //nolint:gosec + User: user, + Password: pass, + Database: database, + }, + } + return cfg.Open(logger) +} + +// NewDexStorage - create a new dex storage adapter depending on the backend +func NewDexStorage(logger *slog.Logger, backend string) (storage.Storage, error) { + switch backend { + case Postgres: + return newPostgresStore(logger) + case K8s: + return newKubernetesStore(logger) + default: + return nil, fmt.Errorf("unknown dexStorage backend: %s", backend) + } +} diff --git a/pkg/dex/store/k8s_store.go b/pkg/dex/store/k8s_store.go deleted file mode 100644 index 364670846..000000000 --- a/pkg/dex/store/k8s_store.go +++ /dev/null @@ -1,141 +0,0 @@ -// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors -// SPDX-License-Identifier: Apache-2.0 - -package store - -import ( - "context" - "encoding/base32" - "hash/fnv" - "log/slog" - "os" - "path/filepath" - "strings" - - "github.com/dexidp/dex/storage" - "github.com/dexidp/dex/storage/kubernetes" - "golang.org/x/text/cases" - "golang.org/x/text/language" - "k8s.io/client-go/rest" - "k8s.io/client-go/tools/clientcmd" - "k8s.io/client-go/util/homedir" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" - "sigs.k8s.io/controller-runtime/pkg/log" - - greenhouseapisv1alpha1 "github.com/cloudoperators/greenhouse/pkg/apis/greenhouse/v1alpha1" - "github.com/cloudoperators/greenhouse/pkg/clientutil" - dexapi "github.com/cloudoperators/greenhouse/pkg/dex/api" -) - -type k8sDex struct { - storage storage.Storage - backend string -} - -const encoding = "abcdefghijklmnopqrstuvwxyz234567" - -// newKubernetesStore - creates a new kubernetes storage backend for dex -func newKubernetesStore(logger *slog.Logger) (storage.Storage, error) { - cfg := kubernetes.Config{InCluster: true} - cfgPath := determineKubeMode() - if strings.TrimSpace(cfgPath) != "" { - cfg.InCluster = false - cfg.KubeConfigFile = cfgPath - } - dexStorage, err := cfg.Open(logger) - if err != nil { - return nil, err - } - return dexStorage, nil -} - -func determineKubeMode() string { - cfgPath := os.Getenv(clientcmd.RecommendedConfigPathEnvVar) - if strings.TrimSpace(cfgPath) != "" { - return cfgPath - } - _, err := rest.InClusterConfig() - if err == nil { - return "" - } - return filepath.Join(homedir.HomeDir(), ".kube", "config") -} - -func (k *k8sDex) GetBackend() string { - return k.backend -} - -// CreateUpdateConnector - creates or updates a dex connector in dex kubernetes storage backend -func (k *k8sDex) CreateUpdateConnector(ctx context.Context, k8sClient client.Client, org *greenhouseapisv1alpha1.Organization, configByte []byte) (err error) { - var result clientutil.OperationResult - var dexConnector = new(dexapi.Connector) - namespaceName := org.GetName() - dexConnector.SetName(namespaceName) - dexConnector.SetNamespace(namespaceName) - result, err = clientutil.CreateOrPatch(ctx, k8sClient, dexConnector, func() error { - dexConnector.DexConnector.Type = dexConnectorTypeGreenhouse - dexConnector.DexConnector.Name = cases.Title(language.English).String(org.Name) - dexConnector.DexConnector.ID = org.GetName() - dexConnector.DexConnector.Config = configByte - return controllerutil.SetControllerReference(org, dexConnector, k8sClient.Scheme()) - }) - if err != nil { - log.FromContext(ctx).Error(err, "failed to create/update dex connector", "namespace", dexConnector.Namespace, "name", dexConnector.GetName()) - return - } - switch result { - case clientutil.OperationResultCreated: - log.FromContext(ctx).Info("created dex connector", "namespace", dexConnector.Namespace, "name", dexConnector.GetName()) - case clientutil.OperationResultUpdated: - log.FromContext(ctx).Info("updated dex connector", "namespace", dexConnector.Namespace, "name", dexConnector.GetName()) - } - return -} - -// CreateUpdateOauth2Client - creates or updates an oauth2 client in dex kubernetes storage backend -func (k *k8sDex) CreateUpdateOauth2Client(ctx context.Context, k8sClient client.Client, org *greenhouseapisv1alpha1.Organization) error { - var oAuth2Client = new(dexapi.OAuth2Client) - namespaceName := org.GetName() - oAuth2Client.SetName(encodedOAuth2ClientName(namespaceName)) - oAuth2Client.SetNamespace(namespaceName) - - result, err := clientutil.CreateOrPatch(ctx, k8sClient, oAuth2Client, func() error { - oAuth2Client.Client.Public = true - oAuth2Client.Client.ID = org.Name - oAuth2Client.Client.Name = org.Name - oAuth2Client.RedirectURIs = getRedirects(org, oAuth2Client.RedirectURIs) - return controllerutil.SetControllerReference(org, oAuth2Client, k8sClient.Scheme()) - }) - if err != nil { - log.FromContext(ctx).Error(err, "failed to create/update oauth2client", "namespace", oAuth2Client.Namespace, "name", oAuth2Client.GetName()) - return err - } - switch result { - case clientutil.OperationResultCreated: - log.FromContext(ctx).Info("created oauth2client", "namespace", oAuth2Client.Namespace, "name", oAuth2Client.GetName()) - case clientutil.OperationResultUpdated: - log.FromContext(ctx).Info("updated oauth2client", "namespace", oAuth2Client.Namespace, "name", oAuth2Client.GetName()) - } - - return nil -} - -// encodedOAuth2ClientName - encodes the org name to a base32 string -// for kubernetes backend storage we need to encode the name OAuth2Client CR name -// See https://github.com/dexidp/dex/issues/1606 for encoding -func encodedOAuth2ClientName(orgName string) string { - return strings.TrimRight(base32. - NewEncoding(encoding). - EncodeToString(fnv.New64().Sum([]byte(orgName))), "=", - ) -} - -// GetStorage - returns the underlying dex storage interface -func (k *k8sDex) GetStorage() storage.Storage { - return k.storage -} - -func (k *k8sDex) Close() error { - return k.storage.Close() -} diff --git a/pkg/dex/store/pg_store.go b/pkg/dex/store/pg_store.go deleted file mode 100644 index 318d03d94..000000000 --- a/pkg/dex/store/pg_store.go +++ /dev/null @@ -1,139 +0,0 @@ -// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors -// SPDX-License-Identifier: Apache-2.0 - -package store - -import ( - "context" - "errors" - "log/slog" - - "github.com/dexidp/dex/storage" - "github.com/dexidp/dex/storage/sql" - "golang.org/x/text/cases" - "golang.org/x/text/language" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/log" - - greenhouseapisv1alpha1 "github.com/cloudoperators/greenhouse/pkg/apis/greenhouse/v1alpha1" - "github.com/cloudoperators/greenhouse/pkg/clientutil" -) - -type pgDex struct { - storage storage.Storage - backend string -} - -const ( - hostEnv = "PG_HOST" - portEnv = "PG_PORT" - userEnv = "PG_USER" - passEnv = "PG_PASSWORD" - dbNameEnv = "PG_DATABASE" -) - -// newPostgresStore - creates a new postgres storage backend for dex -func newPostgresStore(logger *slog.Logger) (storage.Storage, error) { - var host, user, pass, database string - var port int - var err error - database = clientutil.GetEnvOrDefault(dbNameEnv, "postgres") - user = clientutil.GetEnvOrDefault(userEnv, "postgres") - port = clientutil.GetIntEnvWithDefault(portEnv, 5432) - if host, err = clientutil.GetEnv(hostEnv); err != nil { - return nil, err - } - if pass, err = clientutil.GetEnv(passEnv); err != nil { - return nil, err - } - cfg := &sql.Postgres{ - SSL: sql.SSL{Mode: "disable"}, - NetworkDB: sql.NetworkDB{ - Host: host, - Port: uint16(port), //nolint:gosec - User: user, - Password: pass, - Database: database, - }, - } - return cfg.Open(logger) -} - -func (p *pgDex) GetBackend() string { - return p.backend -} - -// CreateUpdateConnector - creates or updates a dex connector in dex postgres storage backend -func (p *pgDex) CreateUpdateConnector(ctx context.Context, _ client.Client, org *greenhouseapisv1alpha1.Organization, configByte []byte) error { - oidcConnector, err := p.storage.GetConnector(org.Name) - if err != nil { - if errors.Is(err, storage.ErrNotFound) { - if err = p.storage.CreateConnector(ctx, storage.Connector{ - ID: org.Name, - Type: dexConnectorTypeGreenhouse, - Name: cases.Title(language.English).String(org.Name), - Config: configByte, - }); err != nil { - log.FromContext(ctx).Error(err, "failed to create dex connector in SQL storage", "name", org.Name) - return err - } - log.FromContext(ctx).Info("created dex connector in SQL storage", "name", org.Name) - return nil - } - log.FromContext(ctx).Error(err, "failed to get dex connector in SQL storage", "name", org.Name) - return err - } - if err = p.storage.UpdateConnector(oidcConnector.ID, func(c storage.Connector) (storage.Connector, error) { - c.ID = org.Name - c.Type = dexConnectorTypeGreenhouse - c.Name = cases.Title(language.English).String(org.Name) - c.Config = configByte - return c, nil - }); err != nil { - log.FromContext(ctx).Error(err, "failed to update dex connector in SQL storage", "name", org.Name) - return err - } - log.FromContext(ctx).Info("updated dex connector in SQL storage", "name", org.Name) - return nil -} - -// CreateUpdateOauth2Client - creates or updates an oauth2 client in dex postgres storage backend -func (p *pgDex) CreateUpdateOauth2Client(ctx context.Context, _ client.Client, org *greenhouseapisv1alpha1.Organization) error { - oAuthClient, err := p.storage.GetClient(org.Name) - if err != nil { - if errors.Is(err, storage.ErrNotFound) { - if err = p.storage.CreateClient(ctx, storage.Client{ - Public: true, - ID: org.Name, - Name: org.Name, - RedirectURIs: getRedirects(org, nil), - }); err != nil { - log.FromContext(ctx).Error(err, "failed to create oauth2client", "name", org.Name) - return err - } - } - log.FromContext(ctx).Info("created oauth2client", "name", org.Name) - return nil - } - if err = p.storage.UpdateClient(oAuthClient.Name, func(authClient storage.Client) (storage.Client, error) { - authClient.Public = true - authClient.ID = org.Name - authClient.Name = org.Name - authClient.RedirectURIs = getRedirects(org, authClient.RedirectURIs) - return authClient, nil - }); err != nil { - log.FromContext(ctx).Error(err, "failed to update oauth2client", "name", org.Name) - return err - } - log.FromContext(ctx).Info("updated oauth2client", "name", org.Name) - return nil -} - -// GetStorage - returns the underlying dex storage interface -func (p *pgDex) GetStorage() storage.Storage { - return p.storage -} - -func (p *pgDex) Close() error { - return p.storage.Close() -} diff --git a/pkg/dex/store/storage.go b/pkg/dex/store/storage.go deleted file mode 100644 index d3305de63..000000000 --- a/pkg/dex/store/storage.go +++ /dev/null @@ -1,69 +0,0 @@ -// SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors -// SPDX-License-Identifier: Apache-2.0 - -package store - -import ( - "context" - "fmt" - "log/slog" - "slices" - - "github.com/dexidp/dex/storage" - "sigs.k8s.io/controller-runtime/pkg/client" - - greenhouseapisv1alpha1 "github.com/cloudoperators/greenhouse/pkg/apis/greenhouse/v1alpha1" - "github.com/cloudoperators/greenhouse/pkg/common" -) - -const ( - Postgres = "postgres" - K8s = "kubernetes" - dexConnectorTypeGreenhouse = "greenhouse-oidc" -) - -// Dexter - dex storage adapter interface -// Supported backends: Postgres, K8s -type Dexter interface { - CreateUpdateConnector(ctx context.Context, k8sClient client.Client, org *greenhouseapisv1alpha1.Organization, configByte []byte) error - CreateUpdateOauth2Client(ctx context.Context, k8sClient client.Client, org *greenhouseapisv1alpha1.Organization) error - GetStorage() storage.Storage - GetBackend() string - Close() error -} - -// NewDexStorageFactory - create a new dex storage adapter depending on the backend -func NewDexStorageFactory(logger *slog.Logger, backend string) (Dexter, error) { - switch backend { - case Postgres: - dexStorage, err := newPostgresStore(logger) - if err != nil { - return nil, err - } - return &pgDex{storage: dexStorage, backend: backend}, nil - case K8s: - dexStorage, err := newKubernetesStore(logger) - if err != nil { - return nil, err - } - return &k8sDex{storage: dexStorage, backend: backend}, nil - default: - return nil, fmt.Errorf("unknown dexStorage backend: %s", backend) - } -} - -func getRedirects(org *greenhouseapisv1alpha1.Organization, redirectURIs []string) []string { - redirects := []string{ - "http://localhost:8085", - "https://dashboard." + common.DNSDomain, - fmt.Sprintf("https://%s.dashboard.%s", org.Name, common.DNSDomain), - } - - for _, r := range redirects { - if !slices.Contains(redirectURIs, r) { - redirectURIs = append(redirectURIs, r) - } - } - - return redirects -} diff --git a/pkg/mocks/mock_Dexter.go b/pkg/mocks/mock_Dexter.go deleted file mode 100644 index 9bd8ef9a5..000000000 --- a/pkg/mocks/mock_Dexter.go +++ /dev/null @@ -1,126 +0,0 @@ -// Code generated by mockery v2.50.0. DO NOT EDIT. - -package mocks - -import ( - context "context" - - client "sigs.k8s.io/controller-runtime/pkg/client" - - mock "github.com/stretchr/testify/mock" - - storage "github.com/dexidp/dex/storage" - - v1alpha1 "github.com/cloudoperators/greenhouse/pkg/apis/greenhouse/v1alpha1" -) - -// MockDexter is an autogenerated mock type for the Dexter type -type MockDexter struct { - mock.Mock -} - -// Close provides a mock function with no fields -func (_m *MockDexter) Close() error { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for Close") - } - - var r0 error - if rf, ok := ret.Get(0).(func() error); ok { - r0 = rf() - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// CreateUpdateConnector provides a mock function with given fields: ctx, k8sClient, org, configByte -func (_m *MockDexter) CreateUpdateConnector(ctx context.Context, k8sClient client.Client, org *v1alpha1.Organization, configByte []byte) error { - ret := _m.Called(ctx, k8sClient, org, configByte) - - if len(ret) == 0 { - panic("no return value specified for CreateUpdateConnector") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, client.Client, *v1alpha1.Organization, []byte) error); ok { - r0 = rf(ctx, k8sClient, org, configByte) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// CreateUpdateOauth2Client provides a mock function with given fields: ctx, k8sClient, org -func (_m *MockDexter) CreateUpdateOauth2Client(ctx context.Context, k8sClient client.Client, org *v1alpha1.Organization) error { - ret := _m.Called(ctx, k8sClient, org) - - if len(ret) == 0 { - panic("no return value specified for CreateUpdateOauth2Client") - } - - var r0 error - if rf, ok := ret.Get(0).(func(context.Context, client.Client, *v1alpha1.Organization) error); ok { - r0 = rf(ctx, k8sClient, org) - } else { - r0 = ret.Error(0) - } - - return r0 -} - -// GetBackend provides a mock function with no fields -func (_m *MockDexter) GetBackend() string { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetBackend") - } - - var r0 string - if rf, ok := ret.Get(0).(func() string); ok { - r0 = rf() - } else { - r0 = ret.Get(0).(string) - } - - return r0 -} - -// GetStorage provides a mock function with no fields -func (_m *MockDexter) GetStorage() storage.Storage { - ret := _m.Called() - - if len(ret) == 0 { - panic("no return value specified for GetStorage") - } - - var r0 storage.Storage - if rf, ok := ret.Get(0).(func() storage.Storage); ok { - r0 = rf() - } else { - if ret.Get(0) != nil { - r0 = ret.Get(0).(storage.Storage) - } - } - - return r0 -} - -// NewMockDexter creates a new instance of MockDexter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. -// The first argument is typically a *testing.T value. -func NewMockDexter(t interface { - mock.TestingT - Cleanup(func()) -}) *MockDexter { - mock := &MockDexter{} - mock.Mock.Test(t) - - t.Cleanup(func() { mock.AssertExpectations(t) }) - - return mock -} From 608c4c8afaed37325f07488c0f1b0040f1f6a296 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Thu, 13 Feb 2025 23:36:55 +0100 Subject: [PATCH 068/108] (chore): initialize dex backend for organization reconciler --- cmd/greenhouse/controllers.go | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/cmd/greenhouse/controllers.go b/cmd/greenhouse/controllers.go index aa927800e..f481b0083 100644 --- a/cmd/greenhouse/controllers.go +++ b/cmd/greenhouse/controllers.go @@ -5,8 +5,6 @@ package main import ( "context" - "log/slog" - "os" "sort" "k8s.io/utils/ptr" @@ -19,11 +17,7 @@ import ( teamcontrollers "github.com/cloudoperators/greenhouse/pkg/controllers/team" teammembershipcontrollers "github.com/cloudoperators/greenhouse/pkg/controllers/teammembership" teamrbaccontrollers "github.com/cloudoperators/greenhouse/pkg/controllers/teamrbac" - dexstore "github.com/cloudoperators/greenhouse/pkg/dex/store" -) - -const ( - defaultIDProxyStorageType = "kubernetes" + dexstore "github.com/cloudoperators/greenhouse/pkg/dex" ) // knownControllers contains all controllers to be registered when starting the operator. @@ -77,20 +71,13 @@ func isControllerEnabled(controllerName string) bool { // initializes the dex storage adapter interface in the organization reconciler func startOrganizationReconciler(name string, mgr ctrl.Manager) error { namespace := clientutil.GetEnvOrDefault(podNamespaceEnv, defaultPodNamespace) - var dexter dexstore.Dexter - var err error - backend := ptr.To(defaultIDProxyStorageType) - l := slog.New(slog.NewJSONHandler(os.Stdout, nil)) + backend := ptr.To(dexstore.K8s) if featureFlags != nil { backend = featureFlags.GetDexStorageType(context.Background()) } - dexter, err = dexstore.NewDexStorageFactory(l.With("component", "storage"), *backend) - if err != nil { - return err - } return (&organizationcontrollers.OrganizationReconciler{ - Dexter: dexter, - Namespace: namespace, + Namespace: namespace, + DexStorageType: *backend, }).SetupWithManager(name, mgr) } From 2a86e58e302fd8a671877b406060ef2910f6b651 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Thu, 13 Feb 2025 23:38:14 +0100 Subject: [PATCH 069/108] (chore): regenerate mocks --- pkg/mocks/mock_Client.go | 2 +- pkg/mocks/mock_Reconciler.go | 2 +- pkg/mocks/mock_Storage.go | 2 +- pkg/mocks/mock_SubResourceWriter.go | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/mocks/mock_Client.go b/pkg/mocks/mock_Client.go index 422469889..f8773bed6 100644 --- a/pkg/mocks/mock_Client.go +++ b/pkg/mocks/mock_Client.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.50.0. DO NOT EDIT. +// Code generated by mockery v2.52.1. DO NOT EDIT. package mocks diff --git a/pkg/mocks/mock_Reconciler.go b/pkg/mocks/mock_Reconciler.go index a381e1065..b5f1f42b2 100644 --- a/pkg/mocks/mock_Reconciler.go +++ b/pkg/mocks/mock_Reconciler.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.50.0. DO NOT EDIT. +// Code generated by mockery v2.52.1. DO NOT EDIT. package mocks diff --git a/pkg/mocks/mock_Storage.go b/pkg/mocks/mock_Storage.go index 2982301d6..835ad0a9a 100644 --- a/pkg/mocks/mock_Storage.go +++ b/pkg/mocks/mock_Storage.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.50.0. DO NOT EDIT. +// Code generated by mockery v2.52.1. DO NOT EDIT. package mocks diff --git a/pkg/mocks/mock_SubResourceWriter.go b/pkg/mocks/mock_SubResourceWriter.go index dabd28266..141ff18a3 100644 --- a/pkg/mocks/mock_SubResourceWriter.go +++ b/pkg/mocks/mock_SubResourceWriter.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.50.0. DO NOT EDIT. +// Code generated by mockery v2.52.1. DO NOT EDIT. package mocks From 962106c1eac8219a6315d5272c72e487e9e520ed Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Thu, 13 Feb 2025 23:39:18 +0100 Subject: [PATCH 070/108] (chore): refactors dex reconciliation appends new organization redirects into default connector's redirects --- pkg/controllers/organization/dex.go | 160 ++++++++++++++---- .../organization/organization_controller.go | 37 +++- 2 files changed, 156 insertions(+), 41 deletions(-) diff --git a/pkg/controllers/organization/dex.go b/pkg/controllers/organization/dex.go index 1a983e192..2dcc18673 100644 --- a/pkg/controllers/organization/dex.go +++ b/pkg/controllers/organization/dex.go @@ -6,32 +6,45 @@ package organization import ( "context" "encoding/json" + "fmt" + "slices" "strings" "github.com/dexidp/dex/connector/oidc" + "github.com/dexidp/dex/storage" "github.com/pkg/errors" + "golang.org/x/text/cases" + "golang.org/x/text/language" networkingv1 "k8s.io/api/networking/v1" - "k8s.io/apimachinery/pkg/types" - ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/log" greenhousesapv1alpha1 "github.com/cloudoperators/greenhouse/pkg/apis/greenhouse/v1alpha1" "github.com/cloudoperators/greenhouse/pkg/clientutil" + "github.com/cloudoperators/greenhouse/pkg/common" ) -//+kubebuilder:rbac:groups=greenhouse.sap,resources=organizations,verbs=get;list;watch;create;update;patch;delete -//+kubebuilder:rbac:groups=greenhouse.sap,resources=organizations/status,verbs=get;update;patch -//+kubebuilder:rbac:groups=greenhouse.sap,resources=organizations/finalizers,verbs=update -//+kubebuilder:rbac:groups="",resources=events,verbs=get;list;watch;create;update;patch -//+kubebuilder:rbac:groups="",resources=namespaces,verbs=get;list;watch;create;update;patch -//+kubebuilder:rbac:groups=dex.coreos.com,resources=connectors;oauth2clients,verbs=get;list;watch;create;update;patch -//+kubebuilder:rbac:groups="",resources=secrets,verbs=get;list;watch +const dexConnectorTypeGreenhouse = "greenhouse-oidc" -func (r *OrganizationReconciler) reconcileDexConnector(ctx context.Context, org *greenhousesapv1alpha1.Organization) error { - if r.Dexter == nil { - ctrl.LoggerFrom(ctx).Error(errors.New("dex interface not initialized"), "dex storage feature") - return errors.New("dex interface not initialized") +func (r *OrganizationReconciler) discoverOIDCRedirectURL(ctx context.Context, org *greenhousesapv1alpha1.Organization) (string, error) { + if r := org.Spec.Authentication.OIDCConfig.RedirectURI; r != "" { + return r, nil + } + var ingressList = new(networkingv1.IngressList) + if err := r.List(ctx, ingressList, client.InNamespace(r.Namespace), client.MatchingLabels{"app.kubernetes.io/name": "idproxy"}); err != nil { + return "", err } + for _, ing := range ingressList.Items { + for _, rule := range ing.Spec.Rules { + if rule.Host != "" { + return ensureCallbackURL(rule.Host), nil + } + } + } + return "", errors.New("oidc redirect URL not provided and cannot be discovered") +} + +func (r *OrganizationReconciler) reconcileDexConnector(ctx context.Context, org *greenhousesapv1alpha1.Organization) error { clientID, err := clientutil.GetSecretKeyFromSecretKeyReference(ctx, r.Client, org.Name, org.Spec.Authentication.OIDCConfig.ClientIDReference) if err != nil { return err @@ -58,37 +71,94 @@ func (r *OrganizationReconciler) reconcileDexConnector(ctx context.Context, org if err != nil { return err } - return r.Dexter.CreateUpdateConnector(ctx, r.Client, org, configByte) -} - -func (r *OrganizationReconciler) enqueueOrganizationForReferencedSecret(_ context.Context, o client.Object) []ctrl.Request { - var org = new(greenhousesapv1alpha1.Organization) - if err := r.Get(context.Background(), types.NamespacedName{Namespace: "", Name: o.GetNamespace()}, org); err != nil { - return nil + oidcConnector, err := r.dex.GetConnector(org.Name) + if err != nil { + if errors.Is(err, storage.ErrNotFound) { + if err = r.dex.CreateConnector(ctx, storage.Connector{ + ID: org.Name, + Type: dexConnectorTypeGreenhouse, + Name: cases.Title(language.English).String(org.Name), + Config: configByte, + }); err != nil { + log.FromContext(ctx).Error(err, "failed to create dex connector", "name", org.Name) + return err + } + log.FromContext(ctx).Info("successfully created dex connector", "name", org.Name) + return nil + } + log.FromContext(ctx).Error(err, "failed to get dex connector", "name", org.Name) + return err + } + if err = r.dex.UpdateConnector(oidcConnector.ID, func(c storage.Connector) (storage.Connector, error) { + c.ID = org.Name + c.Type = dexConnectorTypeGreenhouse + c.Name = cases.Title(language.English).String(org.Name) + c.Config = configByte + return c, nil + }); err != nil { + log.FromContext(ctx).Error(err, "failed to update dex connector", "name", org.Name) + return err } - return []ctrl.Request{{NamespacedName: client.ObjectKeyFromObject(org)}} + log.FromContext(ctx).Info("successfully updated dex connector", "name", org.Name) + return nil } -func (r *OrganizationReconciler) discoverOIDCRedirectURL(ctx context.Context, org *greenhousesapv1alpha1.Organization) (string, error) { - if r := org.Spec.Authentication.OIDCConfig.RedirectURI; r != "" { - return r, nil - } - var ingressList = new(networkingv1.IngressList) - if err := r.List(ctx, ingressList, client.InNamespace(r.Namespace), client.MatchingLabels{"app.kubernetes.io/name": "idproxy"}); err != nil { - return "", err - } - for _, ing := range ingressList.Items { - for _, rule := range ing.Spec.Rules { - if rule.Host != "" { - return ensureCallbackURL(rule.Host), nil +func (r *OrganizationReconciler) reconcileOAuth2Client(ctx context.Context, org *greenhousesapv1alpha1.Organization) error { + oAuthClient, err := r.dex.GetClient(org.Name) + if err != nil { + if errors.Is(err, storage.ErrNotFound) { + redirectURIs := getRedirects(org, nil) + if err = r.dex.CreateClient(ctx, storage.Client{ + Public: true, + ID: org.Name, + Name: org.Name, + RedirectURIs: redirectURIs, + }); err != nil { + log.FromContext(ctx).Error(err, "failed to create oauth2client", "name", org.Name) + return err } + log.FromContext(ctx).Info("successfully created oauth2client", "name", org.Name) + return nil } + log.FromContext(ctx).Error(err, "failed to get oauth2client", "name", org.Name) + return err } - return "", errors.New("oidc redirect URL not provided and cannot be discovered") + if err = r.dex.UpdateClient(oAuthClient.ID, func(authClient storage.Client) (storage.Client, error) { + authClient.Public = true + authClient.ID = org.Name + authClient.Name = org.Name + authClient.RedirectURIs = getRedirects(org, authClient.RedirectURIs) + return authClient, nil + }); err != nil { + log.FromContext(ctx).Error(err, "failed to update oauth2client", "name", org.Name) + return err + } + log.FromContext(ctx).Info("successfully updated oauth2client", "name", org.Name) + return nil } -func (r *OrganizationReconciler) reconcileOAuth2Client(ctx context.Context, org *greenhousesapv1alpha1.Organization) error { - return r.Dexter.CreateUpdateOauth2Client(ctx, r.Client, org) +func (r *OrganizationReconciler) appendRedirectsToDefaultConnector(ctx context.Context, defaultOAuthClientID string, newOAuthClientID string) error { + defaultOAuthClient, err := r.dex.GetClient(defaultOAuthClientID) + if err != nil { + log.FromContext(ctx).Error(err, "failed to get default connector's oauth2client", "ID", defaultOAuthClientID) + return err + } + newOAuthClient, err := r.dex.GetClient(newOAuthClientID) + if err != nil { + log.FromContext(ctx).Error(err, "failed to get oauth2client to append default connector's redirects", "ID", newOAuthClientID) + return err + } + err = r.dex.UpdateClient(defaultOAuthClient.Name, func(authClient storage.Client) (storage.Client, error) { + appendedRedirects := appendRedirects(authClient.RedirectURIs, newOAuthClient.RedirectURIs...) + authClient.RedirectURIs = appendedRedirects + return authClient, nil + }) + if err != nil { + log.FromContext(ctx).Error(err, "failed to update default connector's oauth2client redirects", "ID", defaultOAuthClientID) + return err + } + log.FromContext(ctx).Info("successfully updated default connector's oauth2client redirects", "ID", defaultOAuthClientID) + return nil } func ensureCallbackURL(url string) string { @@ -102,3 +172,21 @@ func ensureCallbackURL(url string) string { } return url } + +func getRedirects(org *greenhousesapv1alpha1.Organization, redirectURIs []string) []string { + defaultRedirects := []string{ + "http://localhost:8085", // allowing local development of idproxy url + "https://dashboard." + common.DNSDomain, + fmt.Sprintf("https://%s.dashboard.%s", org.Name, common.DNSDomain), + } + return appendRedirects(defaultRedirects, redirectURIs...) +} + +func appendRedirects(redirects []string, newRedirects ...string) []string { + for _, r := range newRedirects { + if !slices.Contains(redirects, r) { + redirects = append(redirects, r) + } + } + return redirects +} diff --git a/pkg/controllers/organization/organization_controller.go b/pkg/controllers/organization/organization_controller.go index 49ff94f07..f917c061d 100644 --- a/pkg/controllers/organization/organization_controller.go +++ b/pkg/controllers/organization/organization_controller.go @@ -5,10 +5,14 @@ package organization import ( "context" + "log/slog" + "os" + "github.com/dexidp/dex/storage" "github.com/pkg/errors" corev1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" + "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/tools/record" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/builder" @@ -20,8 +24,8 @@ import ( greenhousesapv1alpha1 "github.com/cloudoperators/greenhouse/pkg/apis/greenhouse/v1alpha1" "github.com/cloudoperators/greenhouse/pkg/clientutil" + dexstore "github.com/cloudoperators/greenhouse/pkg/dex" dexapi "github.com/cloudoperators/greenhouse/pkg/dex/api" - dexstore "github.com/cloudoperators/greenhouse/pkg/dex/store" "github.com/cloudoperators/greenhouse/pkg/lifecycle" "github.com/cloudoperators/greenhouse/pkg/scim" ) @@ -41,12 +45,15 @@ var ( } ) +const defaultGreenhouseConnectorID = "greenhouse" + // OrganizationReconciler reconciles an Organization object type OrganizationReconciler struct { client.Client - recorder record.EventRecorder - Dexter dexstore.Dexter - Namespace string + recorder record.EventRecorder + DexStorageType string + dex storage.Storage + Namespace string } //+kubebuilder:rbac:groups=greenhouse.sap,resources=organizations,verbs=get;list;watch;create;update;patch;delete @@ -66,6 +73,12 @@ type OrganizationReconciler struct { func (r *OrganizationReconciler) SetupWithManager(name string, mgr ctrl.Manager) error { r.Client = mgr.GetClient() r.recorder = mgr.GetEventRecorderFor(name) + l := slog.New(slog.NewJSONHandler(os.Stdout, nil)) + dexter, err := dexstore.NewDexStorage(l.With("component", "storage"), r.DexStorageType) + if err != nil { + return err + } + r.dex = dexter b := ctrl.NewControllerManagedBy(mgr). Named(name). For(&greenhousesapv1alpha1.Organization{}). @@ -86,7 +99,7 @@ func (r *OrganizationReconciler) SetupWithManager(name string, mgr ctrl.Manager) clientutil.PredicateByName(serviceProxyName), predicate.GenerationChangedPredicate{}, ))) - if r.Dexter.GetBackend() == dexstore.K8s { + if r.DexStorageType == dexstore.K8s { b.Owns(&dexapi.Connector{}). Owns(&dexapi.OAuth2Client{}) } @@ -143,6 +156,12 @@ func (r *OrganizationReconciler) EnsureCreated(ctx context.Context, object lifec org.SetCondition(greenhousesapv1alpha1.FalseCondition(greenhousesapv1alpha1.OrganizationOICDConfigured, greenhousesapv1alpha1.OAuthOICDFailed, err.Error())) return ctrl.Result{}, lifecycle.Failed, err } + if org.Name != defaultGreenhouseConnectorID { + if err := r.appendRedirectsToDefaultConnector(ctx, defaultGreenhouseConnectorID, org.Name); err != nil { + org.SetCondition(greenhousesapv1alpha1.FalseCondition(greenhousesapv1alpha1.DefaultConnectorRedirectsConfigured, greenhousesapv1alpha1.DefaultConnectorRedirectsFailed, err.Error())) + return ctrl.Result{}, lifecycle.Failed, err + } + } org.SetCondition(greenhousesapv1alpha1.TrueCondition(greenhousesapv1alpha1.OrganizationOICDConfigured, "", "")) } @@ -329,3 +348,11 @@ func (r *OrganizationReconciler) setStatus() lifecycle.Conditioner { org.Status.SetConditions(scimAPIAvailableCondition, readyCondition) } } + +func (r *OrganizationReconciler) enqueueOrganizationForReferencedSecret(_ context.Context, o client.Object) []ctrl.Request { + var org = new(greenhousesapv1alpha1.Organization) + if err := r.Get(context.Background(), types.NamespacedName{Namespace: "", Name: o.GetNamespace()}, org); err != nil { + return nil + } + return []ctrl.Request{{NamespacedName: client.ObjectKeyFromObject(org)}} +} From abbe59f06e7b2cc5992c9bb67f6cbed5ef4b23a9 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Thu, 13 Feb 2025 23:46:27 +0100 Subject: [PATCH 071/108] (chore): add go docs --- pkg/controllers/organization/dex.go | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/pkg/controllers/organization/dex.go b/pkg/controllers/organization/dex.go index 2dcc18673..9ecc4b17b 100644 --- a/pkg/controllers/organization/dex.go +++ b/pkg/controllers/organization/dex.go @@ -44,6 +44,7 @@ func (r *OrganizationReconciler) discoverOIDCRedirectURL(ctx context.Context, or return "", errors.New("oidc redirect URL not provided and cannot be discovered") } +// reconcileDexConnector - creates or updates dex connector func (r *OrganizationReconciler) reconcileDexConnector(ctx context.Context, org *greenhousesapv1alpha1.Organization) error { clientID, err := clientutil.GetSecretKeyFromSecretKeyReference(ctx, r.Client, org.Name, org.Spec.Authentication.OIDCConfig.ClientIDReference) if err != nil { @@ -103,6 +104,7 @@ func (r *OrganizationReconciler) reconcileDexConnector(ctx context.Context, org return nil } +// reconcileOAuth2Client - creates or updates oauth2client func (r *OrganizationReconciler) reconcileOAuth2Client(ctx context.Context, org *greenhousesapv1alpha1.Organization) error { oAuthClient, err := r.dex.GetClient(org.Name) if err != nil { @@ -137,6 +139,10 @@ func (r *OrganizationReconciler) reconcileOAuth2Client(ctx context.Context, org return nil } +// appendRedirectsToDefaultConnector - appends new organization's OAuth2Client redirect URIs into the default OAuth2Client redirect URIs +// NOTE: this has to be separate and should not be used with in any dex.UpdateClient transaction as it does not support concurrent updates +// It is also not safe when using MaxConcurrentReconciles > 1 as the default connector's redirect URIs can be updated concurrently and +// the last update will win func (r *OrganizationReconciler) appendRedirectsToDefaultConnector(ctx context.Context, defaultOAuthClientID string, newOAuthClientID string) error { defaultOAuthClient, err := r.dex.GetClient(defaultOAuthClientID) if err != nil { @@ -173,6 +179,10 @@ func ensureCallbackURL(url string) string { return url } +// getRedirects - returns the list of default redirect URIs for the reconciling OAuth2Client +// and merges with the provided redirect URIs +// this is needed when the default connector is being reconciled as it should not overwrite +// any appended redirect URIs from other organizations func getRedirects(org *greenhousesapv1alpha1.Organization, redirectURIs []string) []string { defaultRedirects := []string{ "http://localhost:8085", // allowing local development of idproxy url @@ -182,6 +192,7 @@ func getRedirects(org *greenhousesapv1alpha1.Organization, redirectURIs []string return appendRedirects(defaultRedirects, redirectURIs...) } +// appendRedirects - appends newRedirects to the redirects slice if it does not exist func appendRedirects(redirects []string, newRedirects ...string) []string { for _, r := range newRedirects { if !slices.Contains(redirects, r) { From 2a89556c2dcf3946db89597249b73b94d2a49f24 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Thu, 13 Feb 2025 23:46:48 +0100 Subject: [PATCH 072/108] (chore): update org test suite --- pkg/controllers/organization/suite_test.go | 15 ++------------- 1 file changed, 2 insertions(+), 13 deletions(-) diff --git a/pkg/controllers/organization/suite_test.go b/pkg/controllers/organization/suite_test.go index b32b59905..a89660240 100644 --- a/pkg/controllers/organization/suite_test.go +++ b/pkg/controllers/organization/suite_test.go @@ -9,12 +9,10 @@ import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" - "github.com/stretchr/testify/mock" "github.com/cloudoperators/greenhouse/pkg/admission" organizationpkg "github.com/cloudoperators/greenhouse/pkg/controllers/organization" - dexstore "github.com/cloudoperators/greenhouse/pkg/dex/store" - "github.com/cloudoperators/greenhouse/pkg/mocks" + "github.com/cloudoperators/greenhouse/pkg/dex" "github.com/cloudoperators/greenhouse/pkg/scim" "github.com/cloudoperators/greenhouse/pkg/test" ) @@ -31,8 +29,7 @@ func TestOrganization(t *testing.T) { var _ = BeforeSuite(func() { By("mocking SCIM server") groupsServer = scim.ReturnDefaultGroupResponseMockServer() - dexter := dexMocks() - test.RegisterController("organizationController", (&organizationpkg.OrganizationReconciler{Namespace: "default", Dexter: dexter}).SetupWithManager) + test.RegisterController("organizationController", (&organizationpkg.OrganizationReconciler{Namespace: "default", DexStorageType: dex.K8s}).SetupWithManager) test.RegisterWebhook("orgWebhook", admission.SetupOrganizationWebhookWithManager) test.RegisterWebhook("teamWebhook", admission.SetupTeamWebhookWithManager) test.RegisterWebhook("pluginDefinitionWebhook", admission.SetupPluginDefinitionWebhookWithManager) @@ -47,11 +44,3 @@ var _ = AfterSuite(func() { test.TestAfterSuite() }) - -func dexMocks() dexstore.Dexter { - dexter := &mocks.MockDexter{} - dexter.On("CreateUpdateConnector", mock.Anything, mock.Anything, mock.Anything, mock.Anything).Return(nil) - dexter.On("CreateUpdateOauth2Client", mock.Anything, mock.Anything, mock.Anything).Return(nil) - dexter.On("GetBackend").Return(dexstore.K8s) - return dexter -} From 085d567af16b4b697eb94ae6d05b7e2cc6e0d76f Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Thu, 13 Feb 2025 23:47:21 +0100 Subject: [PATCH 073/108] (chore): adds new conditions for appending redirects to default connector --- pkg/apis/greenhouse/v1alpha1/organization_types.go | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkg/apis/greenhouse/v1alpha1/organization_types.go b/pkg/apis/greenhouse/v1alpha1/organization_types.go index 0fb58cc71..8afaef608 100644 --- a/pkg/apis/greenhouse/v1alpha1/organization_types.go +++ b/pkg/apis/greenhouse/v1alpha1/organization_types.go @@ -27,10 +27,14 @@ const ( ServiceProxyProvisioned ConditionType = "ServiceProxyProvisioned" // OrganizationOICDConfigured is set when the OICD is configured OrganizationOICDConfigured ConditionType = "OrganizationOICDConfigured" + // DefaultConnectorRedirectsConfigured is set when the default connector redirects are appended with redirects URIs from new organizations + DefaultConnectorRedirectsConfigured ConditionType = "DefaultConnectorRedirectsConfigured" // DexReconcileFailed is set when dex reconcile step has failed DexReconcileFailed ConditionReason = "DexReconcileFailed" // OAuthOICDFailed is set when OAuth reconciler has failed OAuthOICDFailed ConditionReason = "OAuthOICDFailed" + // DefaultConnectorRedirectsFailed is set when the default connector redirects are not updated with new organization redirect URIs + DefaultConnectorRedirectsFailed ConditionReason = "DefaultConnectorRedirectsFailed" // OrganizationAdminTeamConfigured is set when the admin team is configured for organization OrganizationAdminTeamConfigured ConditionType = "OrganizationAdminTeamConfigured" ) From 29fe0868dc152b82aa460d2d3adafc151a7363b0 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Thu, 13 Feb 2025 23:47:55 +0100 Subject: [PATCH 074/108] (chore): uses simplified dex storage initialization --- cmd/idproxy/main.go | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/cmd/idproxy/main.go b/cmd/idproxy/main.go index ce6012930..fe6a3d2ab 100644 --- a/cmd/idproxy/main.go +++ b/cmd/idproxy/main.go @@ -29,7 +29,6 @@ import ( "github.com/cloudoperators/greenhouse/pkg/clientutil" "github.com/cloudoperators/greenhouse/pkg/dex" - dexstore "github.com/cloudoperators/greenhouse/pkg/dex/store" "github.com/cloudoperators/greenhouse/pkg/dex/web" "github.com/cloudoperators/greenhouse/pkg/features" ) @@ -65,7 +64,7 @@ func main() { log.Fatalf("failed to create k8s client: %s", err) } // default to kubernetes storage backend - backend := ptr.To("kubernetes") + backend := ptr.To[string](dex.K8s) ghFeatures, err := features.NewFeatures(ctx, k8sClient, clientutil.GetEnvOrDefault("FEATURE_FLAGS", "greenhouse-feature-flags"), clientutil.GetEnvOrDefault("POD_NAMESPACE", "greenhouse")) if err != nil { log.Fatalf("failed to get greenhouse features: %s", err) @@ -74,13 +73,11 @@ func main() { backend = ghFeatures.GetDexStorageType(ctx) } // initialize dex storage adapter interface - dexter, err := dexstore.NewDexStorageFactory(logger.With("component", "storage"), *backend) + dexter, err := dex.NewDexStorage(logger.With("component", "storage"), *backend) if err != nil { log.Fatalf("failed to create dex storage interface: %s", err) } logger.Info("using dex storage - ", "type", *backend) - // get the underlying dex storage interface - dexStorage := dexter.GetStorage() refreshPolicy, err := server.NewRefreshTokenPolicy(logger.With("component", "refreshtokenpolicy"), true, "24h", "24h", "5s") if err != nil { @@ -95,7 +92,7 @@ func main() { Issuer: issuer, SkipApprovalScreen: true, Logger: logger.With("component", "server"), - Storage: dexStorage, + Storage: dexter, AllowedOrigins: allowedOrigins, IDTokensValidFor: idTokenValidity, RefreshTokenPolicy: refreshPolicy, From dc82fb576c720d78573bdd7f5765dfe1342d2ed9 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Thu, 13 Feb 2025 23:48:14 +0100 Subject: [PATCH 075/108] (chore): adds .dockerignore --- .dockerignore | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 .dockerignore diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 000000000..fd7449369 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,4 @@ +# SPDX-FileCopyrightText: 2024 SAP SE or an SAP affiliate company and Greenhouse contributors +# SPDX-License-Identifier: Apache-2.0 + +bin \ No newline at end of file From d1e5fbfc708ffd8fd2f955d32d4e0229e6a3acde Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Thu, 13 Feb 2025 23:53:16 +0100 Subject: [PATCH 076/108] (chore): removes unused vars --- pkg/controllers/organization/dex.go | 2 +- pkg/dex/storage.go | 5 ----- 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/pkg/controllers/organization/dex.go b/pkg/controllers/organization/dex.go index 9ecc4b17b..bef130c31 100644 --- a/pkg/controllers/organization/dex.go +++ b/pkg/controllers/organization/dex.go @@ -143,7 +143,7 @@ func (r *OrganizationReconciler) reconcileOAuth2Client(ctx context.Context, org // NOTE: this has to be separate and should not be used with in any dex.UpdateClient transaction as it does not support concurrent updates // It is also not safe when using MaxConcurrentReconciles > 1 as the default connector's redirect URIs can be updated concurrently and // the last update will win -func (r *OrganizationReconciler) appendRedirectsToDefaultConnector(ctx context.Context, defaultOAuthClientID string, newOAuthClientID string) error { +func (r *OrganizationReconciler) appendRedirectsToDefaultConnector(ctx context.Context, defaultOAuthClientID, newOAuthClientID string) error { defaultOAuthClient, err := r.dex.GetClient(defaultOAuthClientID) if err != nil { log.FromContext(ctx).Error(err, "failed to get default connector's oauth2client", "ID", defaultOAuthClientID) diff --git a/pkg/dex/storage.go b/pkg/dex/storage.go index 8d677ed11..e6892c2d5 100644 --- a/pkg/dex/storage.go +++ b/pkg/dex/storage.go @@ -33,11 +33,6 @@ const ( dbNameEnv = "PG_DATABASE" ) -type Dexter struct { - storage storage.Storage - backend string -} - // newKubernetesStore - creates a new kubernetes storage backend for dex func newKubernetesStore(logger *slog.Logger) (storage.Storage, error) { cfg := kubernetes.Config{InCluster: true} From dba8cf6e8a3b9a33c1c9e7b14723075e4390a035 Mon Sep 17 00:00:00 2001 From: Cloud Operator <169066274+cloud-operator@users.noreply.github.com> Date: Thu, 13 Feb 2025 22:58:00 +0000 Subject: [PATCH 077/108] Automatic generation of CRD API Docs --- docs/reference/api/openapi.yaml | 1414 +++++++++++++++---------------- 1 file changed, 707 insertions(+), 707 deletions(-) diff --git a/docs/reference/api/openapi.yaml b/docs/reference/api/openapi.yaml index f5ae5faeb..ec258d8e3 100755 --- a/docs/reference/api/openapi.yaml +++ b/docs/reference/api/openapi.yaml @@ -4,64 +4,64 @@ info: version: main description: PlusOne operations platform paths: - /ClusterKubeconfig: + /Team: post: responses: default: - description: ClusterKubeconfig - /PluginDefinition: + description: Team + /TeamRole: post: responses: default: - description: PluginDefinition - /Plugin: + description: TeamRole + /TeamMembership: post: responses: default: - description: Plugin - /Team: + description: TeamMembership + /Organization: post: responses: default: - description: Team - /TeamMembership: + description: Organization + /Cluster: post: responses: default: - description: TeamMembership - /TeamRoleBinding: + description: Cluster + /Plugin: post: responses: default: - description: TeamRoleBinding - /Organization: + description: Plugin + /PluginDefinition: post: responses: default: - description: Organization - /PluginPreset: + description: PluginDefinition + /ClusterKubeconfig: post: responses: default: - description: PluginPreset - /TeamRole: + description: ClusterKubeconfig + /TeamRoleBinding: post: responses: default: - description: TeamRole - /Cluster: + description: TeamRoleBinding + /PluginPreset: post: responses: default: - description: Cluster + description: PluginPreset components: schemas: - ClusterKubeconfig: + Team: xml: name: greenhouse.sap namespace: v1alpha1 - title: ClusterKubeconfig - description: ClusterKubeconfig is the Schema for the clusterkubeconfigs API\nObjectMeta.OwnerReferences is used to link the ClusterKubeconfig to the Cluster\nObjectMeta.Generation is used to detect changes in the ClusterKubeconfig and sync local kubeconfig files\nObjectMeta.Name is designed to be the same with the Cluster name + title: Team + description: Team is the Schema for the teams API properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' @@ -72,92 +72,44 @@ components: metadata: type: object spec: - description: ClusterKubeconfigSpec stores the kubeconfig data for the cluster\nThe idea is to use kubeconfig data locally with minimum effort (with local tools or plain kubectl):\nkubectl get cluster-kubeconfig $NAME -o yaml | yq -y .spec.kubeconfig + description: TeamSpec defines the desired state of Team properties: - kubeconfig: - description: 'ClusterKubeconfigData stores the kubeconfig data ready to use kubectl or other local tooling\nIt is a simplified version of clientcmdapi.Config: https://pkg.go.dev/k8s.io/client-go/tools/clientcmd/api#Config' - properties: - apiVersion: - type: string - clusters: - items: - properties: - cluster: - properties: - certificate-authority-data: - format: byte - type: string - server: - type: string - type: object - name: - type: string - required: - - cluster - - name - type: object - type: array - contexts: - items: - properties: - context: - properties: - cluster: - type: string - namespace: - type: string - user: - type: string - required: - - cluster - - user - type: object - name: - type: string - required: - - name - type: object - type: array - current-context: - type: string - kind: - type: string - preferences: - type: object - users: - items: - properties: - name: - type: string - user: - properties: - auth-provider: - description: AuthProviderConfig holds the configuration for a specified auth provider. - properties: - config: - additionalProperties: - type: string - type: object - name: - type: string - required: - - name - type: object - client-certificate-data: - format: byte - type: string - client-key-data: - format: byte - type: string - type: object - required: - - name - type: object - type: array - type: object + description: + description: Description provides additional details of the team. + type: string + joinUrl: + description: URL to join the IdP group. + type: string + mappedIdPGroup: + description: IdP group id matching team. + type: string type: object status: + description: TeamStatus defines the observed state of Team properties: + members: + items: + description: User specifies a human person. + properties: + email: + description: Email of the user. + type: string + firstName: + description: FirstName of the user. + type: string + id: + description: ID is the unique identifier of the user. + type: string + lastName: + description: LastName of the user. + type: string + required: + - email + - firstName + - id + - lastName + type: object + type: array statusConditions: description: A StatusConditions contains a list of conditions.\nOnly one condition of a given type may exist in the list. properties: @@ -191,14 +143,16 @@ components: - type x-kubernetes-list-type: map type: object + required: + - statusConditions type: object type: object - PluginDefinition: + TeamRole: xml: name: greenhouse.sap namespace: v1alpha1 - title: PluginDefinition - description: PluginDefinition is the Schema for the PluginDefinitions API + title: TeamRole + description: TeamRole is the Schema for the TeamRoles API properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' @@ -209,103 +163,464 @@ components: metadata: type: object spec: - description: PluginDefinitionSpec defines the desired state of PluginDefinitionSpec + description: TeamRoleSpec defines the desired state of a TeamRole properties: - description: - description: Description provides additional details of the pluginDefinition. - type: string - displayName: - description: DisplayName provides a human-readable label for the pluginDefinition. - type: string - docMarkDownUrl: - description: DocMarkDownUrl specifies the URL to the markdown documentation file for this plugin.\nSource needs to allow all CORS origins. - type: string - helmChart: - description: HelmChart specifies where the Helm Chart for this pluginDefinition can be found. + aggregationRule: + description: AggregationRule describes how to locate ClusterRoles to aggregate into the ClusterRole on the remote cluster properties: - name: - description: Name of the HelmChart chart. - type: string - repository: - description: Repository of the HelmChart chart. - type: string - version: - description: Version of the HelmChart chart. - type: string - required: - - name - - repository - - version + clusterRoleSelectors: + description: ClusterRoleSelectors holds a list of selectors which will be used to find ClusterRoles and create the rules.\nIf any of the selectors match, then the ClusterRole's permissions will be added + items: + description: A label selector is a label query over a set of resources. The result of matchLabels and\nmatchExpressions are ANDed. An empty label selector matches all objects. A null\nlabel selector matches no objects. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that\nrelates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: operator represents a key's relationship to a set of values.\nValid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. If the operator is In or NotIn,\nthe values array must be non-empty. If the operator is Exists or DoesNotExist,\nthe values array must be empty. This array is replaced during a strategic\nmerge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels\nmap is equivalent to an element of matchExpressions, whose key field is "key", the\noperator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic type: object - icon: - description: 'Icon specifies the icon to be used for this plugin in the Greenhouse UI.\nIcons can be either:\n- A string representing a juno icon in camel case from this list: https://github.com/sapcc/juno/blob/main/libs/juno-ui-components/src/components/Icon/Icon.component.js#L6-L52\n- A publicly accessible image reference to a .png file. Will be displayed 100x100px' - type: string - options: - description: RequiredValues is a list of values required to create an instance of this PluginDefinition. + labels: + additionalProperties: + type: string + description: Labels are applied to the ClusterRole created on the remote cluster.\nThis allows using TeamRoles as part of AggregationRules by other TeamRoles + type: object + rules: + description: Rules is a list of rbacv1.PolicyRules used on a managed RBAC (Cluster)Role items: + description: PolicyRule holds information that describes a policy rule, but does not contain information\nabout who the rule applies to or which namespace the rule applies to. properties: - default: - description: Default provides a default value for the option - x-kubernetes-preserve-unknown-fields: true - description: - description: Description provides a human-readable text for the value as shown in the UI. - type: string - displayName: - description: DisplayName provides a human-readable label for the configuration option + apiGroups: + description: APIGroups is the name of the APIGroup that contains the resources. If multiple API groups are specified, any action requested against one of\nthe enumerated resources in any API group will be allowed. "" represents the core API group and "*" represents all API groups. + items: + type: string + type: array + x-kubernetes-list-type: atomic + nonResourceURLs: + description: NonResourceURLs is a set of partial urls that a user should have access to. *s are allowed, but only as the full, final step in the path\nSince non-resource URLs are not namespaced, this field is only applicable for ClusterRoles referenced from a ClusterRoleBinding.\nRules can either apply to API resources (such as "pods" or "secrets") or non-resource URL paths (such as "/api"), but not both. + items: + type: string + type: array + x-kubernetes-list-type: atomic + resourceNames: + description: ResourceNames is an optional white list of names that the rule applies to. An empty set means that everything is allowed. + items: + type: string + type: array + x-kubernetes-list-type: atomic + resources: + description: Resources is a list of resources this rule applies to. '*' represents all resources. + items: + type: string + type: array + x-kubernetes-list-type: atomic + verbs: + description: Verbs is a list of Verbs that apply to ALL the ResourceKinds contained in this rule. '*' represents all verbs. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - verbs + type: object + type: array + type: object + status: + description: TeamRoleStatus defines the observed state of a TeamRole + type: object + type: object + TeamMembership: + xml: + name: greenhouse.sap + namespace: v1alpha1 + title: TeamMembership + description: TeamMembership is the Schema for the teammemberships API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents.\nServers may infer this from the endpoint the client submits requests to.\nCannot be updated.\nIn CamelCase.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: TeamMembershipSpec defines the desired state of TeamMembership + properties: + members: + description: Members list users that are part of a team. + items: + description: User specifies a human person. + properties: + email: + description: Email of the user. type: string - name: - description: Name/Key of the config option. + firstName: + description: FirstName of the user. type: string - regex: - description: Regex specifies a match rule for validating configuration options. + id: + description: ID is the unique identifier of the user. type: string - required: - description: Required indicates that this config option is required - type: boolean - type: - description: Type of this configuration option. - enum: - - string - - secret - - bool - - int - - list - - map + lastName: + description: LastName of the user. type: string required: - - name - - required - - type + - email + - firstName + - id + - lastName type: object type: array - uiApplication: - description: UIApplication specifies a reference to a UI application + type: object + status: + description: TeamMembershipStatus defines the observed state of TeamMembership + properties: + lastSyncedTime: + description: LastSyncedTime is the information when was the last time the membership was synced + format: date-time + type: string + lastUpdateTime: + description: LastChangedTime is the information when was the last time the membership was actually changed + format: date-time + type: string + statusConditions: + description: StatusConditions contain the different conditions that constitute the status of the TeamMembership. properties: - name: - description: Name of the UI application. - type: string - url: - description: URL specifies the url to a built javascript asset.\nBy default, assets are loaded from the Juno asset server using the provided name and version. - type: string - version: - description: Version of the frontend application. - type: string - required: - - name - - version + conditions: + items: + description: Condition contains additional information on the state of a resource. + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition transitioned from one status to another. + format: date-time + type: string + message: + description: Message is an optional human readable message indicating details about the last transition. + type: string + reason: + description: Reason is a one-word, CamelCase reason for the condition's last transition. + type: string + status: + description: Status of the condition. + type: string + type: + description: Type of the condition. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + type: object + type: object + Organization: + xml: + name: greenhouse.sap + namespace: v1alpha1 + title: Organization + description: Organization is the Schema for the organizations API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents.\nServers may infer this from the endpoint the client submits requests to.\nCannot be updated.\nIn CamelCase.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: OrganizationSpec defines the desired state of Organization + properties: + authentication: + description: Authentication configures the organizations authentication mechanism. + properties: + oidc: + description: OIDConfig configures the OIDC provider. + properties: + clientIDReference: + description: ClientIDReference references the Kubernetes secret containing the client id. + properties: + key: + description: Key in the secret to select the value from. + type: string + name: + description: Name of the secret in the same namespace. + type: string + required: + - key + - name + type: object + clientSecretReference: + description: ClientSecretReference references the Kubernetes secret containing the client secret. + properties: + key: + description: Key in the secret to select the value from. + type: string + name: + description: Name of the secret in the same namespace. + type: string + required: + - key + - name + type: object + issuer: + description: Issuer is the URL of the identity service. + type: string + redirectURI: + description: RedirectURI is the redirect URI.\nIf none is specified, the Greenhouse ID proxy will be used. + type: string + required: + - clientIDReference + - clientSecretReference + - issuer + type: object + scim: + description: SCIMConfig configures the SCIM client. + properties: + baseURL: + description: URL to the SCIM server. + type: string + basicAuthPw: + description: Password to be used for basic authentication. + properties: + secret: + description: Secret references the secret containing the value. + properties: + key: + description: Key in the secret to select the value from. + type: string + name: + description: Name of the secret in the same namespace. + type: string + required: + - key + - name + type: object + type: object + basicAuthUser: + description: User to be used for basic authentication. + properties: + secret: + description: Secret references the secret containing the value. + properties: + key: + description: Key in the secret to select the value from. + type: string + name: + description: Name of the secret in the same namespace. + type: string + required: + - key + - name + type: object + type: object + required: + - baseURL + - basicAuthPw + - basicAuthUser + type: object + type: object + description: + description: Description provides additional details of the organization. + type: string + displayName: + description: DisplayName is an optional name for the organization to be displayed in the Greenhouse UI.\nDefaults to a normalized version of metadata.name. + type: string + mappedOrgAdminIdPGroup: + description: MappedOrgAdminIDPGroup is the IDP group ID identifying org admins + type: string + type: object + status: + description: OrganizationStatus defines the observed state of an Organization + properties: + statusConditions: + description: StatusConditions contain the different conditions that constitute the status of the Organization. + properties: + conditions: + items: + description: Condition contains additional information on the state of a resource. + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition transitioned from one status to another. + format: date-time + type: string + message: + description: Message is an optional human readable message indicating details about the last transition. + type: string + reason: + description: Reason is a one-word, CamelCase reason for the condition's last transition. + type: string + status: + description: Status of the condition. + type: string + type: + description: Type of the condition. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + type: object + type: object + Cluster: + xml: + name: greenhouse.sap + namespace: v1alpha1 + title: Cluster + description: Cluster is the Schema for the clusters API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents.\nServers may infer this from the endpoint the client submits requests to.\nCannot be updated.\nIn CamelCase.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ClusterSpec defines the desired state of the Cluster. + properties: + accessMode: + description: AccessMode configures how the cluster is accessed from the Greenhouse operator. + enum: + - direct + type: string + kubeConfig: + description: KubeConfig contains specific values for `KubeConfig` for the cluster. + properties: + maxTokenValidity: + default: 72 + description: MaxTokenValidity specifies the maximum duration for which a token remains valid in hours. + format: int32 + maximum: 72 + minimum: 24 + type: integer + type: object + required: + - accessMode + type: object + status: + description: ClusterStatus defines the observed state of Cluster + properties: + bearerTokenExpirationTimestamp: + description: BearerTokenExpirationTimestamp reflects the expiration timestamp of the bearer token used to access the cluster. + format: date-time + type: string + kubernetesVersion: + description: KubernetesVersion reflects the detected Kubernetes version of the cluster. + type: string + nodes: + additionalProperties: + properties: + ready: + description: Fast track to the node ready condition. + type: boolean + statusConditions: + description: We mirror the node conditions here for faster reference + properties: + conditions: + items: + description: Condition contains additional information on the state of a resource. + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition transitioned from one status to another. + format: date-time + type: string + message: + description: Message is an optional human readable message indicating details about the last transition. + type: string + reason: + description: Reason is a one-word, CamelCase reason for the condition's last transition. + type: string + status: + description: Status of the condition. + type: string + type: + description: Type of the condition. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + type: object + description: Nodes provides a map of cluster node names to node statuses + type: object + statusConditions: + description: StatusConditions contain the different conditions that constitute the status of the Cluster. + properties: + conditions: + items: + description: Condition contains additional information on the state of a resource. + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition transitioned from one status to another. + format: date-time + type: string + message: + description: Message is an optional human readable message indicating details about the last transition. + type: string + reason: + description: Reason is a one-word, CamelCase reason for the condition's last transition. + type: string + status: + description: Status of the condition. + type: string + type: + description: Type of the condition. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map type: object - version: - description: Version of this pluginDefinition - type: string - weight: - description: Weight configures the order in which Plugins are shown in the Greenhouse UI.\nDefaults to alphabetical sorting if not provided or on conflict. - format: int32 - type: integer - required: - - version - type: object - status: - description: PluginDefinitionStatus defines the observed state of PluginDefinition type: object type: object Plugin: @@ -502,12 +817,12 @@ components: type: integer type: object type: object - Team: + PluginDefinition: xml: name: greenhouse.sap namespace: v1alpha1 - title: Team - description: Team is the Schema for the teams API + title: PluginDefinition + description: PluginDefinition is the Schema for the PluginDefinitions API properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' @@ -518,137 +833,209 @@ components: metadata: type: object spec: - description: TeamSpec defines the desired state of Team + description: PluginDefinitionSpec defines the desired state of PluginDefinitionSpec properties: description: - description: Description provides additional details of the team. + description: Description provides additional details of the pluginDefinition. type: string - joinUrl: - description: URL to join the IdP group. + displayName: + description: DisplayName provides a human-readable label for the pluginDefinition. type: string - mappedIdPGroup: - description: IdP group id matching team. + docMarkDownUrl: + description: DocMarkDownUrl specifies the URL to the markdown documentation file for this plugin.\nSource needs to allow all CORS origins. type: string - type: object - status: - description: TeamStatus defines the observed state of Team - properties: - members: + helmChart: + description: HelmChart specifies where the Helm Chart for this pluginDefinition can be found. + properties: + name: + description: Name of the HelmChart chart. + type: string + repository: + description: Repository of the HelmChart chart. + type: string + version: + description: Version of the HelmChart chart. + type: string + required: + - name + - repository + - version + type: object + icon: + description: 'Icon specifies the icon to be used for this plugin in the Greenhouse UI.\nIcons can be either:\n- A string representing a juno icon in camel case from this list: https://github.com/sapcc/juno/blob/main/libs/juno-ui-components/src/components/Icon/Icon.component.js#L6-L52\n- A publicly accessible image reference to a .png file. Will be displayed 100x100px' + type: string + options: + description: RequiredValues is a list of values required to create an instance of this PluginDefinition. items: - description: User specifies a human person. properties: - email: - description: Email of the user. + default: + description: Default provides a default value for the option + x-kubernetes-preserve-unknown-fields: true + description: + description: Description provides a human-readable text for the value as shown in the UI. type: string - firstName: - description: FirstName of the user. + displayName: + description: DisplayName provides a human-readable label for the configuration option type: string - id: - description: ID is the unique identifier of the user. + name: + description: Name/Key of the config option. type: string - lastName: - description: LastName of the user. + regex: + description: Regex specifies a match rule for validating configuration options. + type: string + required: + description: Required indicates that this config option is required + type: boolean + type: + description: Type of this configuration option. + enum: + - string + - secret + - bool + - int + - list + - map type: string required: - - email - - firstName - - id - - lastName + - name + - required + - type type: object type: array - statusConditions: - description: A StatusConditions contains a list of conditions.\nOnly one condition of a given type may exist in the list. + uiApplication: + description: UIApplication specifies a reference to a UI application properties: - conditions: + name: + description: Name of the UI application. + type: string + url: + description: URL specifies the url to a built javascript asset.\nBy default, assets are loaded from the Juno asset server using the provided name and version. + type: string + version: + description: Version of the frontend application. + type: string + required: + - name + - version + type: object + version: + description: Version of this pluginDefinition + type: string + weight: + description: Weight configures the order in which Plugins are shown in the Greenhouse UI.\nDefaults to alphabetical sorting if not provided or on conflict. + format: int32 + type: integer + required: + - version + type: object + status: + description: PluginDefinitionStatus defines the observed state of PluginDefinition + type: object + type: object + ClusterKubeconfig: + xml: + name: greenhouse.sap + namespace: v1alpha1 + title: ClusterKubeconfig + description: ClusterKubeconfig is the Schema for the clusterkubeconfigs API\nObjectMeta.OwnerReferences is used to link the ClusterKubeconfig to the Cluster\nObjectMeta.Generation is used to detect changes in the ClusterKubeconfig and sync local kubeconfig files\nObjectMeta.Name is designed to be the same with the Cluster name + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents.\nServers may infer this from the endpoint the client submits requests to.\nCannot be updated.\nIn CamelCase.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ClusterKubeconfigSpec stores the kubeconfig data for the cluster\nThe idea is to use kubeconfig data locally with minimum effort (with local tools or plain kubectl):\nkubectl get cluster-kubeconfig $NAME -o yaml | yq -y .spec.kubeconfig + properties: + kubeconfig: + description: 'ClusterKubeconfigData stores the kubeconfig data ready to use kubectl or other local tooling\nIt is a simplified version of clientcmdapi.Config: https://pkg.go.dev/k8s.io/client-go/tools/clientcmd/api#Config' + properties: + apiVersion: + type: string + clusters: items: - description: Condition contains additional information on the state of a resource. properties: - lastTransitionTime: - description: LastTransitionTime is the last time the condition transitioned from one status to another. - format: date-time - type: string - message: - description: Message is an optional human readable message indicating details about the last transition. - type: string - reason: - description: Reason is a one-word, CamelCase reason for the condition's last transition. + cluster: + properties: + certificate-authority-data: + format: byte + type: string + server: + type: string + type: object + name: type: string - status: - description: Status of the condition. + required: + - cluster + - name + type: object + type: array + contexts: + items: + properties: + context: + properties: + cluster: + type: string + namespace: + type: string + user: + type: string + required: + - cluster + - user + type: object + name: type: string - type: - description: Type of the condition. + required: + - name + type: object + type: array + current-context: + type: string + kind: + type: string + preferences: + type: object + users: + items: + properties: + name: type: string + user: + properties: + auth-provider: + description: AuthProviderConfig holds the configuration for a specified auth provider. + properties: + config: + additionalProperties: + type: string + type: object + name: + type: string + required: + - name + type: object + client-certificate-data: + format: byte + type: string + client-key-data: + format: byte + type: string + type: object required: - - lastTransitionTime - - status - - type + - name type: object type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map type: object - required: - - statusConditions - type: object - type: object - TeamMembership: - xml: - name: greenhouse.sap - namespace: v1alpha1 - title: TeamMembership - description: TeamMembership is the Schema for the teammemberships API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents.\nServers may infer this from the endpoint the client submits requests to.\nCannot be updated.\nIn CamelCase.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: TeamMembershipSpec defines the desired state of TeamMembership - properties: - members: - description: Members list users that are part of a team. - items: - description: User specifies a human person. - properties: - email: - description: Email of the user. - type: string - firstName: - description: FirstName of the user. - type: string - id: - description: ID is the unique identifier of the user. - type: string - lastName: - description: LastName of the user. - type: string - required: - - email - - firstName - - id - - lastName - type: object - type: array type: object status: - description: TeamMembershipStatus defines the observed state of TeamMembership properties: - lastSyncedTime: - description: LastSyncedTime is the information when was the last time the membership was synced - format: date-time - type: string - lastUpdateTime: - description: LastChangedTime is the information when was the last time the membership was actually changed - format: date-time - type: string statusConditions: - description: StatusConditions contain the different conditions that constitute the status of the TeamMembership. + description: A StatusConditions contains a list of conditions.\nOnly one condition of a given type may exist in the list. properties: conditions: items: @@ -768,185 +1155,30 @@ components: type: string message: description: Message is an optional human readable message indicating details about the last transition. - type: string - reason: - description: Reason is a one-word, CamelCase reason for the condition's last transition. - type: string - status: - description: Status of the condition. - type: string - type: - description: Type of the condition. - type: string - required: - - lastTransitionTime - - status - - type - type: object - required: - - clusterName - type: object - type: array - x-kubernetes-list-map-keys: - - clusterName - x-kubernetes-list-type: map - statusConditions: - description: StatusConditions contain the different conditions that constitute the status of the TeamRoleBinding. - properties: - conditions: - items: - description: Condition contains additional information on the state of a resource. - properties: - lastTransitionTime: - description: LastTransitionTime is the last time the condition transitioned from one status to another. - format: date-time - type: string - message: - description: Message is an optional human readable message indicating details about the last transition. - type: string - reason: - description: Reason is a one-word, CamelCase reason for the condition's last transition. - type: string - status: - description: Status of the condition. - type: string - type: - description: Type of the condition. - type: string - required: - - lastTransitionTime - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - type: object - type: object - type: object - Organization: - xml: - name: greenhouse.sap - namespace: v1alpha1 - title: Organization - description: Organization is the Schema for the organizations API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents.\nServers may infer this from the endpoint the client submits requests to.\nCannot be updated.\nIn CamelCase.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: OrganizationSpec defines the desired state of Organization - properties: - authentication: - description: Authentication configures the organizations authentication mechanism. - properties: - oidc: - description: OIDConfig configures the OIDC provider. - properties: - clientIDReference: - description: ClientIDReference references the Kubernetes secret containing the client id. - properties: - key: - description: Key in the secret to select the value from. - type: string - name: - description: Name of the secret in the same namespace. - type: string - required: - - key - - name - type: object - clientSecretReference: - description: ClientSecretReference references the Kubernetes secret containing the client secret. - properties: - key: - description: Key in the secret to select the value from. - type: string - name: - description: Name of the secret in the same namespace. - type: string - required: - - key - - name - type: object - issuer: - description: Issuer is the URL of the identity service. - type: string - redirectURI: - description: RedirectURI is the redirect URI.\nIf none is specified, the Greenhouse ID proxy will be used. - type: string - required: - - clientIDReference - - clientSecretReference - - issuer - type: object - scim: - description: SCIMConfig configures the SCIM client. - properties: - baseURL: - description: URL to the SCIM server. - type: string - basicAuthPw: - description: Password to be used for basic authentication. - properties: - secret: - description: Secret references the secret containing the value. - properties: - key: - description: Key in the secret to select the value from. - type: string - name: - description: Name of the secret in the same namespace. - type: string - required: - - key - - name - type: object - type: object - basicAuthUser: - description: User to be used for basic authentication. - properties: - secret: - description: Secret references the secret containing the value. - properties: - key: - description: Key in the secret to select the value from. - type: string - name: - description: Name of the secret in the same namespace. - type: string - required: - - key - - name - type: object - type: object - required: - - baseURL - - basicAuthPw - - basicAuthUser - type: object - type: object - description: - description: Description provides additional details of the organization. - type: string - displayName: - description: DisplayName is an optional name for the organization to be displayed in the Greenhouse UI.\nDefaults to a normalized version of metadata.name. - type: string - mappedOrgAdminIdPGroup: - description: MappedOrgAdminIDPGroup is the IDP group ID identifying org admins - type: string - type: object - status: - description: OrganizationStatus defines the observed state of an Organization - properties: + type: string + reason: + description: Reason is a one-word, CamelCase reason for the condition's last transition. + type: string + status: + description: Status of the condition. + type: string + type: + description: Type of the condition. + type: string + required: + - lastTransitionTime + - status + - type + type: object + required: + - clusterName + type: object + type: array + x-kubernetes-list-map-keys: + - clusterName + x-kubernetes-list-type: map statusConditions: - description: StatusConditions contain the different conditions that constitute the status of the Organization. + description: StatusConditions contain the different conditions that constitute the status of the TeamRoleBinding. properties: conditions: items: @@ -1170,235 +1402,3 @@ components: type: object type: object type: object - TeamRole: - xml: - name: greenhouse.sap - namespace: v1alpha1 - title: TeamRole - description: TeamRole is the Schema for the TeamRoles API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents.\nServers may infer this from the endpoint the client submits requests to.\nCannot be updated.\nIn CamelCase.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: TeamRoleSpec defines the desired state of a TeamRole - properties: - aggregationRule: - description: AggregationRule describes how to locate ClusterRoles to aggregate into the ClusterRole on the remote cluster - properties: - clusterRoleSelectors: - description: ClusterRoleSelectors holds a list of selectors which will be used to find ClusterRoles and create the rules.\nIf any of the selectors match, then the ClusterRole's permissions will be added - items: - description: A label selector is a label query over a set of resources. The result of matchLabels and\nmatchExpressions are ANDed. An empty label selector matches all objects. A null\nlabel selector matches no objects. - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - items: - description: A label selector requirement is a selector that contains values, a key, and an operator that\nrelates the key and values. - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: operator represents a key's relationship to a set of values.\nValid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array of string values. If the operator is In or NotIn,\nthe values array must be non-empty. If the operator is Exists or DoesNotExist,\nthe values array must be empty. This array is replaced during a strategic\nmerge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels\nmap is equivalent to an element of matchExpressions, whose key field is "key", the\noperator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - type: array - x-kubernetes-list-type: atomic - type: object - labels: - additionalProperties: - type: string - description: Labels are applied to the ClusterRole created on the remote cluster.\nThis allows using TeamRoles as part of AggregationRules by other TeamRoles - type: object - rules: - description: Rules is a list of rbacv1.PolicyRules used on a managed RBAC (Cluster)Role - items: - description: PolicyRule holds information that describes a policy rule, but does not contain information\nabout who the rule applies to or which namespace the rule applies to. - properties: - apiGroups: - description: APIGroups is the name of the APIGroup that contains the resources. If multiple API groups are specified, any action requested against one of\nthe enumerated resources in any API group will be allowed. "" represents the core API group and "*" represents all API groups. - items: - type: string - type: array - x-kubernetes-list-type: atomic - nonResourceURLs: - description: NonResourceURLs is a set of partial urls that a user should have access to. *s are allowed, but only as the full, final step in the path\nSince non-resource URLs are not namespaced, this field is only applicable for ClusterRoles referenced from a ClusterRoleBinding.\nRules can either apply to API resources (such as "pods" or "secrets") or non-resource URL paths (such as "/api"), but not both. - items: - type: string - type: array - x-kubernetes-list-type: atomic - resourceNames: - description: ResourceNames is an optional white list of names that the rule applies to. An empty set means that everything is allowed. - items: - type: string - type: array - x-kubernetes-list-type: atomic - resources: - description: Resources is a list of resources this rule applies to. '*' represents all resources. - items: - type: string - type: array - x-kubernetes-list-type: atomic - verbs: - description: Verbs is a list of Verbs that apply to ALL the ResourceKinds contained in this rule. '*' represents all verbs. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - verbs - type: object - type: array - type: object - status: - description: TeamRoleStatus defines the observed state of a TeamRole - type: object - type: object - Cluster: - xml: - name: greenhouse.sap - namespace: v1alpha1 - title: Cluster - description: Cluster is the Schema for the clusters API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents.\nServers may infer this from the endpoint the client submits requests to.\nCannot be updated.\nIn CamelCase.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: ClusterSpec defines the desired state of the Cluster. - properties: - accessMode: - description: AccessMode configures how the cluster is accessed from the Greenhouse operator. - enum: - - direct - type: string - kubeConfig: - description: KubeConfig contains specific values for `KubeConfig` for the cluster. - properties: - maxTokenValidity: - default: 72 - description: MaxTokenValidity specifies the maximum duration for which a token remains valid in hours. - format: int32 - maximum: 72 - minimum: 24 - type: integer - type: object - required: - - accessMode - type: object - status: - description: ClusterStatus defines the observed state of Cluster - properties: - bearerTokenExpirationTimestamp: - description: BearerTokenExpirationTimestamp reflects the expiration timestamp of the bearer token used to access the cluster. - format: date-time - type: string - kubernetesVersion: - description: KubernetesVersion reflects the detected Kubernetes version of the cluster. - type: string - nodes: - additionalProperties: - properties: - ready: - description: Fast track to the node ready condition. - type: boolean - statusConditions: - description: We mirror the node conditions here for faster reference - properties: - conditions: - items: - description: Condition contains additional information on the state of a resource. - properties: - lastTransitionTime: - description: LastTransitionTime is the last time the condition transitioned from one status to another. - format: date-time - type: string - message: - description: Message is an optional human readable message indicating details about the last transition. - type: string - reason: - description: Reason is a one-word, CamelCase reason for the condition's last transition. - type: string - status: - description: Status of the condition. - type: string - type: - description: Type of the condition. - type: string - required: - - lastTransitionTime - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - type: object - type: object - description: Nodes provides a map of cluster node names to node statuses - type: object - statusConditions: - description: StatusConditions contain the different conditions that constitute the status of the Cluster. - properties: - conditions: - items: - description: Condition contains additional information on the state of a resource. - properties: - lastTransitionTime: - description: LastTransitionTime is the last time the condition transitioned from one status to another. - format: date-time - type: string - message: - description: Message is an optional human readable message indicating details about the last transition. - type: string - reason: - description: Reason is a one-word, CamelCase reason for the condition's last transition. - type: string - status: - description: Status of the condition. - type: string - type: - description: Type of the condition. - type: string - required: - - lastTransitionTime - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - type: object - type: object - type: object From 09be246091c82cdcb473a84ff27abc27cc3bb62a Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Fri, 14 Feb 2025 00:06:32 +0100 Subject: [PATCH 078/108] (chore): fixes out of bounds issue for postgres port --- pkg/dex/storage.go | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/pkg/dex/storage.go b/pkg/dex/storage.go index e6892c2d5..9472ada6b 100644 --- a/pkg/dex/storage.go +++ b/pkg/dex/storage.go @@ -77,8 +77,13 @@ func newPostgresStore(logger *slog.Logger) (storage.Storage, error) { cfg := &sql.Postgres{ SSL: sql.SSL{Mode: "disable"}, NetworkDB: sql.NetworkDB{ - Host: host, - Port: uint16(port), //nolint:gosec + Host: host, + Port: func() uint16 { + if port < 0 || port > 65535 { + return 5432 // default port value or handle error as needed + } + return uint16(port) + }(), User: user, Password: pass, Database: database, From 06bae36b23caa1ca3535106a0b2ce10a3563d65b Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Fri, 14 Feb 2025 00:07:37 +0100 Subject: [PATCH 079/108] (chore): provide dex values for e2e --- dev-env/localenv/dev.values.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/dev-env/localenv/dev.values.yaml b/dev-env/localenv/dev.values.yaml index 835c1e681..c46ff05f0 100644 --- a/dev-env/localenv/dev.values.yaml +++ b/dev-env/localenv/dev.values.yaml @@ -3,5 +3,7 @@ global: dnsDomain: green.house + dex: + backend: kubernetes alerts: enabled: false \ No newline at end of file From e06e4459472b1aa961c5946ae7e080c4bdab5d78 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Fri, 14 Feb 2025 00:17:19 +0100 Subject: [PATCH 080/108] (chore): allow dex api to create CRDs in local and e2e cluster --- dev-env/localenv/dev.config.yaml | 4 ---- e2e/config.yaml | 4 ---- 2 files changed, 8 deletions(-) diff --git a/dev-env/localenv/dev.config.yaml b/dev-env/localenv/dev.config.yaml index a868198d3..383c743ad 100644 --- a/dev-env/localenv/dev.config.yaml +++ b/dev-env/localenv/dev.config.yaml @@ -8,10 +8,6 @@ config: name: greenhouse-admin namespace: greenhouse dependencies: - - manifest: - release: greenhouse - chartPath: charts/idproxy - crdOnly: true - manifest: release: greenhouse chartPath: charts/manager diff --git a/e2e/config.yaml b/e2e/config.yaml index 556e414fc..cc4d51879 100644 --- a/e2e/config.yaml +++ b/e2e/config.yaml @@ -8,10 +8,6 @@ config: name: greenhouse-admin namespace: greenhouse dependencies: - - manifest: - release: greenhouse - chartPath: charts/idproxy - crdOnly: true - manifest: release: greenhouse chartPath: charts/manager From 066221195c3c78ee23c577fef0bea48d8354ec1a Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Fri, 14 Feb 2025 00:57:05 +0100 Subject: [PATCH 081/108] (chore): uses postgres testcontainers for integration tests --- go.mod | 21 ++++++++- go.sum | 54 ++++++++++++++++++++++ pkg/controllers/organization/suite_test.go | 49 +++++++++++++++++++- 3 files changed, 121 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index fc854819e..74c511eae 100644 --- a/go.mod +++ b/go.mod @@ -31,6 +31,8 @@ require ( github.com/spf13/cobra v1.8.1 github.com/spf13/pflag v1.0.6 github.com/stretchr/testify v1.10.0 + github.com/testcontainers/testcontainers-go v0.35.0 + github.com/testcontainers/testcontainers-go/modules/postgres v0.35.0 github.com/vladimirvivien/gexe v0.4.1 github.com/wI2L/jsondiff v0.6.1 go.uber.org/zap v1.27.0 @@ -54,39 +56,56 @@ require ( cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect dario.cat/mergo v1.0.1 // indirect filippo.io/edwards25519 v1.1.0 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/hcsshim v0.12.6 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/containerd/errdefs v0.3.0 // indirect github.com/containerd/log v0.1.0 // indirect github.com/containerd/platforms v0.2.1 // indirect + github.com/cpuguy83/dockercfg v0.3.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect github.com/creack/pty v1.1.23 // indirect github.com/distribution/reference v0.6.0 // indirect + github.com/docker/go-units v0.5.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-sql-driver/mysql v1.8.1 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/miekg/dns v1.1.58 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/patternmatcher v0.6.0 // indirect + github.com/moby/sys/sequential v0.5.0 // indirect + github.com/moby/sys/user v0.3.0 // indirect + github.com/moby/sys/userns v0.1.0 // indirect + github.com/morikuni/aec v1.0.0 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/otiai10/mint v1.6.3 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/sergi/go-diff v1.3.1 // indirect + github.com/shirou/gopsutil/v3 v3.23.12 // indirect + github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/tidwall/gjson v1.18.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/sjson v1.2.5 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect github.com/x448/float16 v0.8.4 // indirect + github.com/yusufpapurcu/wmi v1.2.3 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect - gotest.tools/v3 v3.5.1 // indirect ) require ( diff --git a/go.sum b/go.sum index b89ed1341..f84e3b3e2 100644 --- a/go.sum +++ b/go.sum @@ -83,6 +83,8 @@ github.com/coreos/go-oidc/v3 v3.11.0 h1:Ia3MxdwpSw702YW0xgfmP1GVCMA9aEFWu12XUZ3/ github.com/coreos/go-oidc/v3 v3.11.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= +github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -118,6 +120,8 @@ github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= @@ -169,6 +173,8 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= @@ -212,6 +218,7 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -264,6 +271,14 @@ github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgx/v5 v5.5.4 h1:Xp2aQS8uXButQdnCMWNmvx6UysWQQC+u1EoizjguY+8= +github.com/jackc/pgx/v5 v5.5.4/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= +github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= +github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= @@ -315,6 +330,10 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattermost/xml-roundtrip-validator v0.1.0 h1:RXbVD2UAl7A7nOTR4u7E3ILa4IbtvKBHw64LDsmu9hU= @@ -329,6 +348,8 @@ github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/mdelapenya/tlscert v0.1.0 h1:YTpF579PYUX475eOL+6zyEO3ngLTOUWck78NBuJVXaM= +github.com/mdelapenya/tlscert v0.1.0/go.mod h1:wrbyM/DwbFCeCeqdPX/8c6hNOqQgbf0rUDErE1uD+64= github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= @@ -337,12 +358,20 @@ github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQ github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= +github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= +github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU= github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= +github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= +github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= +github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo= +github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= @@ -356,6 +385,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -387,6 +418,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY= github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -429,6 +462,12 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= +github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= +github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= +github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= +github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= +github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= +github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -456,8 +495,13 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/testcontainers/testcontainers-go v0.35.0 h1:uADsZpTKFAtp8SLK+hMwSaa+X+JiERHtd4sQAFmXeMo= +github.com/testcontainers/testcontainers-go v0.35.0/go.mod h1:oEVBj5zrfJTrgjwONs1SsRbnBtH9OKl+IGl3UMcr2B4= +github.com/testcontainers/testcontainers-go/modules/postgres v0.35.0 h1:eEGx9kYzZb2cNhRbBrNOCL/YPOM7+RMJiy3bB+ie0/I= +github.com/testcontainers/testcontainers-go/modules/postgres v0.35.0/go.mod h1:hfH71Mia/WWLBgMD2YctYcMlfsbnT0hflweL1dy8Q4s= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= @@ -468,6 +512,10 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/vladimirvivien/gexe v0.4.1 h1:W9gWkp8vSPjDoXDu04Yp4KljpVMaSt8IQuHswLDd5LY= github.com/vladimirvivien/gexe v0.4.1/go.mod h1:3gjgTqE2c0VyHnU5UOIwk7gyNzZDGulPb/DJPgcw64E= github.com/wI2L/jsondiff v0.6.1 h1:ISZb9oNWbP64LHnu4AUhsMF5W0FIj5Ok3Krip9Shqpw= @@ -486,6 +534,8 @@ github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= +github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib/exporters/autoexport v0.46.1 h1:ysCfPZB9AjUlMa1UHYup3c9dAOCMQX/6sxSfPBUoxHw= @@ -592,8 +642,10 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -603,6 +655,8 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= diff --git a/pkg/controllers/organization/suite_test.go b/pkg/controllers/organization/suite_test.go index a89660240..3826de063 100644 --- a/pkg/controllers/organization/suite_test.go +++ b/pkg/controllers/organization/suite_test.go @@ -4,7 +4,11 @@ package organization_test import ( + "context" + "github.com/testcontainers/testcontainers-go" + "github.com/testcontainers/testcontainers-go/wait" "net/http/httptest" + "os" "testing" . "github.com/onsi/ginkgo/v2" @@ -15,10 +19,19 @@ import ( "github.com/cloudoperators/greenhouse/pkg/dex" "github.com/cloudoperators/greenhouse/pkg/scim" "github.com/cloudoperators/greenhouse/pkg/test" + + "github.com/testcontainers/testcontainers-go/modules/postgres" +) + +const ( + mockDb = "mock" + mockUsr = "mock" + mockPwd = "mock_pwd" ) var ( groupsServer *httptest.Server + mockPgTc *postgres.PostgresContainer ) func TestOrganization(t *testing.T) { @@ -27,9 +40,24 @@ func TestOrganization(t *testing.T) { } var _ = BeforeSuite(func() { + var err error + ctx := context.Background() By("mocking SCIM server") groupsServer = scim.ReturnDefaultGroupResponseMockServer() - test.RegisterController("organizationController", (&organizationpkg.OrganizationReconciler{Namespace: "default", DexStorageType: dex.K8s}).SetupWithManager) + + mockPgTc, err = startPgTC(ctx) + Expect(err).NotTo(HaveOccurred()) + host, err := mockPgTc.Host(ctx) + Expect(err).NotTo(HaveOccurred()) + Expect(os.Setenv("PG_HOST", host)).ToNot(HaveOccurred()) + port, err := mockPgTc.MappedPort(ctx, "5432/tcp") + Expect(err).NotTo(HaveOccurred()) + Expect(os.Setenv("PG_PORT", port.Port())).ToNot(HaveOccurred()) + Expect(os.Setenv("PG_USER", mockUsr)).ToNot(HaveOccurred()) + Expect(os.Setenv("PG_PASSWORD", mockPwd)).ToNot(HaveOccurred()) + Expect(os.Setenv("PG_DATABASE", mockDb)).ToNot(HaveOccurred()) + + test.RegisterController("organizationController", (&organizationpkg.OrganizationReconciler{Namespace: "default", DexStorageType: dex.Postgres}).SetupWithManager) test.RegisterWebhook("orgWebhook", admission.SetupOrganizationWebhookWithManager) test.RegisterWebhook("teamWebhook", admission.SetupTeamWebhookWithManager) test.RegisterWebhook("pluginDefinitionWebhook", admission.SetupPluginDefinitionWebhookWithManager) @@ -41,6 +69,23 @@ var _ = BeforeSuite(func() { var _ = AfterSuite(func() { By("tearing down the test environment") groupsServer.Close() - + err := testcontainers.TerminateContainer(mockPgTc) + Expect(err).NotTo(HaveOccurred()) test.TestAfterSuite() }) + +func startPgTC(ctx context.Context) (*postgres.PostgresContainer, error) { + return postgres.Run(ctx, "postgres:16-alpine", + postgres.WithDatabase(mockDb), + postgres.WithUsername(mockUsr), + postgres.WithPassword(mockPwd), + testcontainers.WithWaitStrategy( + // First, we wait for the container to log readiness twice. + // This is because it will restart itself after the first startup. + wait.ForLog("database system is ready to accept connections").WithOccurrence(2), + // Then, we wait for docker to actually serve the port on localhost. + // For non-linux OSes like Mac and Windows, Docker or Rancher Desktop will have to start a separate proxy. + // Without this, the tests will be flaky on those OSes! + wait.ForListeningPort("5432/tcp"), + )) +} From aaa9a563f8fd06463ee1961d086fa933f97a41d0 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Fri, 14 Feb 2025 01:01:51 +0100 Subject: [PATCH 082/108] (chore): increase timeout for plugin preset test --- pkg/controllers/plugin/pluginpreset_controller_test.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkg/controllers/plugin/pluginpreset_controller_test.go b/pkg/controllers/plugin/pluginpreset_controller_test.go index 98c9ae927..0c9ec1f7e 100644 --- a/pkg/controllers/plugin/pluginpreset_controller_test.go +++ b/pkg/controllers/plugin/pluginpreset_controller_test.go @@ -4,6 +4,8 @@ package plugin import ( + "time" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/onsi/gomega/format" @@ -254,7 +256,7 @@ var _ = Describe("PluginPreset Controller Lifecycle", Ordered, func() { err = test.K8sClient.List(test.Ctx, pluginList, client.InNamespace(cluster.GetNamespace()), client.MatchingLabels{greenhouseapis.LabelKeyPluginPreset: pluginPresetName}) g.Expect(err).NotTo(HaveOccurred(), "failed to list Plugins") g.Expect(pluginList.Items).To(HaveLen(1), "there should be only one Plugin") - }).Should(Succeed(), "the PluginPreset should have removed the Plugin for the deleted Cluster") + }, 2*time.Minute).Should(Succeed(), "the PluginPreset should have removed the Plugin for the deleted Cluster") }) It("should set the Status NotReady if ClusterSelector does not match", func() { From 6ec8254270d6e38616d7468f79e8c6c30f9a4aa5 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Fri, 14 Feb 2025 01:14:06 +0100 Subject: [PATCH 083/108] (chore): go fmt! --- pkg/controllers/organization/suite_test.go | 11 ++++++----- pkg/mocks/mock_Reconciler.go | 3 ++- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/pkg/controllers/organization/suite_test.go b/pkg/controllers/organization/suite_test.go index 3826de063..af16bd2c3 100644 --- a/pkg/controllers/organization/suite_test.go +++ b/pkg/controllers/organization/suite_test.go @@ -5,12 +5,13 @@ package organization_test import ( "context" - "github.com/testcontainers/testcontainers-go" - "github.com/testcontainers/testcontainers-go/wait" "net/http/httptest" "os" "testing" + "github.com/testcontainers/testcontainers-go" + "github.com/testcontainers/testcontainers-go/wait" + . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -24,7 +25,7 @@ import ( ) const ( - mockDb = "mock" + mockDB = "mock" mockUsr = "mock" mockPwd = "mock_pwd" ) @@ -55,7 +56,7 @@ var _ = BeforeSuite(func() { Expect(os.Setenv("PG_PORT", port.Port())).ToNot(HaveOccurred()) Expect(os.Setenv("PG_USER", mockUsr)).ToNot(HaveOccurred()) Expect(os.Setenv("PG_PASSWORD", mockPwd)).ToNot(HaveOccurred()) - Expect(os.Setenv("PG_DATABASE", mockDb)).ToNot(HaveOccurred()) + Expect(os.Setenv("PG_DATABASE", mockDB)).ToNot(HaveOccurred()) test.RegisterController("organizationController", (&organizationpkg.OrganizationReconciler{Namespace: "default", DexStorageType: dex.Postgres}).SetupWithManager) test.RegisterWebhook("orgWebhook", admission.SetupOrganizationWebhookWithManager) @@ -76,7 +77,7 @@ var _ = AfterSuite(func() { func startPgTC(ctx context.Context) (*postgres.PostgresContainer, error) { return postgres.Run(ctx, "postgres:16-alpine", - postgres.WithDatabase(mockDb), + postgres.WithDatabase(mockDB), postgres.WithUsername(mockUsr), postgres.WithPassword(mockPwd), testcontainers.WithWaitStrategy( diff --git a/pkg/mocks/mock_Reconciler.go b/pkg/mocks/mock_Reconciler.go index b5f1f42b2..fed456274 100644 --- a/pkg/mocks/mock_Reconciler.go +++ b/pkg/mocks/mock_Reconciler.go @@ -5,9 +5,10 @@ package mocks import ( context "context" - lifecycle "github.com/cloudoperators/greenhouse/pkg/lifecycle" mock "github.com/stretchr/testify/mock" + lifecycle "github.com/cloudoperators/greenhouse/pkg/lifecycle" + reconcile "sigs.k8s.io/controller-runtime/pkg/reconcile" ) From 30f11318a0adc1b8bf2a854c7e6391c8bf87af5c Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Mon, 17 Feb 2025 10:56:51 +0100 Subject: [PATCH 084/108] (chore): uses dex k8s backend for integration test --- go.mod | 21 +-------- go.sum | 54 ---------------------- pkg/controllers/organization/suite_test.go | 48 +------------------ pkg/test/env.go | 18 ++++---- 4 files changed, 11 insertions(+), 130 deletions(-) diff --git a/go.mod b/go.mod index 74c511eae..fc854819e 100644 --- a/go.mod +++ b/go.mod @@ -31,8 +31,6 @@ require ( github.com/spf13/cobra v1.8.1 github.com/spf13/pflag v1.0.6 github.com/stretchr/testify v1.10.0 - github.com/testcontainers/testcontainers-go v0.35.0 - github.com/testcontainers/testcontainers-go/modules/postgres v0.35.0 github.com/vladimirvivien/gexe v0.4.1 github.com/wI2L/jsondiff v0.6.1 go.uber.org/zap v1.27.0 @@ -56,56 +54,39 @@ require ( cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect dario.cat/mergo v1.0.1 // indirect filippo.io/edwards25519 v1.1.0 // indirect - github.com/Microsoft/go-winio v0.6.2 // indirect github.com/Microsoft/hcsshim v0.12.6 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/containerd/errdefs v0.3.0 // indirect github.com/containerd/log v0.1.0 // indirect github.com/containerd/platforms v0.2.1 // indirect - github.com/cpuguy83/dockercfg v0.3.2 // indirect github.com/cpuguy83/go-md2man/v2 v2.0.4 // indirect github.com/creack/pty v1.1.23 // indirect github.com/distribution/reference v0.6.0 // indirect - github.com/docker/go-units v0.5.0 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/ghodss/yaml v1.0.0 // indirect - github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-sql-driver/mysql v1.8.1 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/google/gnostic-models v0.6.9-0.20230804172637-c7be7c783f49 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect github.com/kylelemons/godebug v1.1.0 // indirect - github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect - github.com/magiconair/properties v1.8.7 // indirect github.com/mattn/go-sqlite3 v1.14.22 // indirect github.com/miekg/dns v1.1.58 // indirect - github.com/moby/docker-image-spec v1.3.1 // indirect - github.com/moby/patternmatcher v0.6.0 // indirect - github.com/moby/sys/sequential v0.5.0 // indirect - github.com/moby/sys/user v0.3.0 // indirect - github.com/moby/sys/userns v0.1.0 // indirect - github.com/morikuni/aec v1.0.0 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/otiai10/mint v1.6.3 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect - github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/sergi/go-diff v1.3.1 // indirect - github.com/shirou/gopsutil/v3 v3.23.12 // indirect - github.com/shoenig/go-m1cpu v0.1.6 // indirect github.com/stretchr/objx v0.5.2 // indirect github.com/tidwall/gjson v1.18.0 // indirect github.com/tidwall/match v1.1.1 // indirect github.com/tidwall/pretty v1.2.1 // indirect github.com/tidwall/sjson v1.2.5 // indirect - github.com/tklauser/go-sysconf v0.3.12 // indirect - github.com/tklauser/numcpus v0.6.1 // indirect github.com/x448/float16 v0.8.4 // indirect - github.com/yusufpapurcu/wmi v1.2.3 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect + gotest.tools/v3 v3.5.1 // indirect ) require ( diff --git a/go.sum b/go.sum index f84e3b3e2..b89ed1341 100644 --- a/go.sum +++ b/go.sum @@ -83,8 +83,6 @@ github.com/coreos/go-oidc/v3 v3.11.0 h1:Ia3MxdwpSw702YW0xgfmP1GVCMA9aEFWu12XUZ3/ github.com/coreos/go-oidc/v3 v3.11.0/go.mod h1:gE3LgjOgFoHi9a4ce4/tJczr0Ai2/BoDhf0r5lltWI0= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= -github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= -github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= github.com/cpuguy83/go-md2man/v2 v2.0.4 h1:wfIWP927BUkWJb2NmU/kNDYIBTh/ziUX91+lVfRxZq4= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= @@ -120,8 +118,6 @@ github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= -github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= -github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU= @@ -173,8 +169,6 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= -github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= -github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ= github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY= github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= @@ -218,7 +212,6 @@ github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= @@ -271,14 +264,6 @@ github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= -github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= -github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.5.4 h1:Xp2aQS8uXButQdnCMWNmvx6UysWQQC+u1EoizjguY+8= -github.com/jackc/pgx/v5 v5.5.4/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A= -github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk= -github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4= github.com/jcmturner/aescts/v2 v2.0.0 h1:9YKLH6ey7H4eDBXW8khjYslgyqG2xZikXP0EQFKrle8= github.com/jcmturner/aescts/v2 v2.0.0/go.mod h1:AiaICIRyfYg35RUkr8yESTqvSy7csK90qZ5xfvvsoNs= github.com/jcmturner/dnsutils/v2 v2.0.0 h1:lltnkeZGL0wILNvrNiVCR6Ro5PGU/SeBvVO/8c/iPbo= @@ -330,10 +315,6 @@ github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= -github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= -github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattermost/xml-roundtrip-validator v0.1.0 h1:RXbVD2UAl7A7nOTR4u7E3ILa4IbtvKBHw64LDsmu9hU= @@ -348,8 +329,6 @@ github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU= github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= -github.com/mdelapenya/tlscert v0.1.0 h1:YTpF579PYUX475eOL+6zyEO3ngLTOUWck78NBuJVXaM= -github.com/mdelapenya/tlscert v0.1.0/go.mod h1:wrbyM/DwbFCeCeqdPX/8c6hNOqQgbf0rUDErE1uD+64= github.com/miekg/dns v1.1.58 h1:ca2Hdkz+cDg/7eNF6V56jjzuZ4aCAE+DbVkILdQWG/4= github.com/miekg/dns v1.1.58/go.mod h1:Ypv+3b/KadlvW9vJfXOTf300O4UqaHFzFCuHz+rPkBY= github.com/mitchellh/copystructure v1.2.0 h1:vpKXTN4ewci03Vljg/q9QvCGUDttBOGBIa15WveJJGw= @@ -358,20 +337,12 @@ github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQ github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zxSIeXaQ= github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= -github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= -github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= -github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= -github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU= github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= -github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= -github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo= -github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo= -github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= @@ -385,8 +356,6 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00 h1:n6/2gBQ3RWajuToeY6ZtZTIKv2v7ThUy5KKusIT0yc0= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= -github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= @@ -418,8 +387,6 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= -github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/poy/onpar v1.1.2 h1:QaNrNiZx0+Nar5dLgTVp5mXkyoVFIbepjyEoGSnhbAY= github.com/poy/onpar v1.1.2/go.mod h1:6X8FLNoxyr9kkmnlqpK6LSoiOtrO6MICtWwEuWkLjzg= github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= @@ -462,12 +429,6 @@ github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8= github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I= -github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= -github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= -github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= -github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= -github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= -github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k= github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= @@ -495,13 +456,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/testcontainers/testcontainers-go v0.35.0 h1:uADsZpTKFAtp8SLK+hMwSaa+X+JiERHtd4sQAFmXeMo= -github.com/testcontainers/testcontainers-go v0.35.0/go.mod h1:oEVBj5zrfJTrgjwONs1SsRbnBtH9OKl+IGl3UMcr2B4= -github.com/testcontainers/testcontainers-go/modules/postgres v0.35.0 h1:eEGx9kYzZb2cNhRbBrNOCL/YPOM7+RMJiy3bB+ie0/I= -github.com/testcontainers/testcontainers-go/modules/postgres v0.35.0/go.mod h1:hfH71Mia/WWLBgMD2YctYcMlfsbnT0hflweL1dy8Q4s= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= @@ -512,10 +468,6 @@ github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4= github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= -github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= -github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= -github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= -github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= github.com/vladimirvivien/gexe v0.4.1 h1:W9gWkp8vSPjDoXDu04Yp4KljpVMaSt8IQuHswLDd5LY= github.com/vladimirvivien/gexe v0.4.1/go.mod h1:3gjgTqE2c0VyHnU5UOIwk7gyNzZDGulPb/DJPgcw64E= github.com/wI2L/jsondiff v0.6.1 h1:ISZb9oNWbP64LHnu4AUhsMF5W0FIj5Ok3Krip9Shqpw= @@ -534,8 +486,6 @@ github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -github.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw= -github.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/contrib/exporters/autoexport v0.46.1 h1:ysCfPZB9AjUlMa1UHYup3c9dAOCMQX/6sxSfPBUoxHw= @@ -642,10 +592,8 @@ golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5h golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -655,8 +603,6 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= diff --git a/pkg/controllers/organization/suite_test.go b/pkg/controllers/organization/suite_test.go index af16bd2c3..a91940ae8 100644 --- a/pkg/controllers/organization/suite_test.go +++ b/pkg/controllers/organization/suite_test.go @@ -4,14 +4,9 @@ package organization_test import ( - "context" "net/http/httptest" - "os" "testing" - "github.com/testcontainers/testcontainers-go" - "github.com/testcontainers/testcontainers-go/wait" - . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" @@ -20,19 +15,10 @@ import ( "github.com/cloudoperators/greenhouse/pkg/dex" "github.com/cloudoperators/greenhouse/pkg/scim" "github.com/cloudoperators/greenhouse/pkg/test" - - "github.com/testcontainers/testcontainers-go/modules/postgres" -) - -const ( - mockDB = "mock" - mockUsr = "mock" - mockPwd = "mock_pwd" ) var ( groupsServer *httptest.Server - mockPgTc *postgres.PostgresContainer ) func TestOrganization(t *testing.T) { @@ -41,24 +27,10 @@ func TestOrganization(t *testing.T) { } var _ = BeforeSuite(func() { - var err error - ctx := context.Background() By("mocking SCIM server") groupsServer = scim.ReturnDefaultGroupResponseMockServer() - mockPgTc, err = startPgTC(ctx) - Expect(err).NotTo(HaveOccurred()) - host, err := mockPgTc.Host(ctx) - Expect(err).NotTo(HaveOccurred()) - Expect(os.Setenv("PG_HOST", host)).ToNot(HaveOccurred()) - port, err := mockPgTc.MappedPort(ctx, "5432/tcp") - Expect(err).NotTo(HaveOccurred()) - Expect(os.Setenv("PG_PORT", port.Port())).ToNot(HaveOccurred()) - Expect(os.Setenv("PG_USER", mockUsr)).ToNot(HaveOccurred()) - Expect(os.Setenv("PG_PASSWORD", mockPwd)).ToNot(HaveOccurred()) - Expect(os.Setenv("PG_DATABASE", mockDB)).ToNot(HaveOccurred()) - - test.RegisterController("organizationController", (&organizationpkg.OrganizationReconciler{Namespace: "default", DexStorageType: dex.Postgres}).SetupWithManager) + test.RegisterController("organizationController", (&organizationpkg.OrganizationReconciler{Namespace: "default", DexStorageType: dex.K8s}).SetupWithManager) test.RegisterWebhook("orgWebhook", admission.SetupOrganizationWebhookWithManager) test.RegisterWebhook("teamWebhook", admission.SetupTeamWebhookWithManager) test.RegisterWebhook("pluginDefinitionWebhook", admission.SetupPluginDefinitionWebhookWithManager) @@ -70,23 +42,5 @@ var _ = BeforeSuite(func() { var _ = AfterSuite(func() { By("tearing down the test environment") groupsServer.Close() - err := testcontainers.TerminateContainer(mockPgTc) - Expect(err).NotTo(HaveOccurred()) test.TestAfterSuite() }) - -func startPgTC(ctx context.Context) (*postgres.PostgresContainer, error) { - return postgres.Run(ctx, "postgres:16-alpine", - postgres.WithDatabase(mockDB), - postgres.WithUsername(mockUsr), - postgres.WithPassword(mockPwd), - testcontainers.WithWaitStrategy( - // First, we wait for the container to log readiness twice. - // This is because it will restart itself after the first startup. - wait.ForLog("database system is ready to accept connections").WithOccurrence(2), - // Then, we wait for docker to actually serve the port on localhost. - // For non-linux OSes like Mac and Windows, Docker or Rancher Desktop will have to start a separate proxy. - // Without this, the tests will be flaky on those OSes! - wait.ForListeningPort("5432/tcp"), - )) -} diff --git a/pkg/test/env.go b/pkg/test/env.go index 50c1bc73c..3cc66fccb 100644 --- a/pkg/test/env.go +++ b/pkg/test/env.go @@ -282,15 +282,15 @@ func StartControlPlane(port string, installCRDs, installWebhooks bool) (*rest.Co } // utility to export kubeconfig and use it e.g. on a breakpoint to inspect resources during testing - if os.Getenv("TEST_EXPORT_KUBECONFIG") == "true" { - dir := GinkgoT().TempDir() - kubeCfgFile, err := os.CreateTemp(dir, "*-kubeconfig.yaml") - Expect(err).NotTo(HaveOccurred()) - _, err = kubeCfgFile.Write(kubeConfig) - Expect(err).NotTo(HaveOccurred()) - fmt.Printf("export KUBECONFIG=%s\n", kubeCfgFile.Name()) - Expect(err).NotTo(HaveOccurred()) - } + + dir := GinkgoT().TempDir() + kubeCfgFile, err := os.CreateTemp(dir, "*-kubeconfig.yaml") + Expect(err).NotTo(HaveOccurred()) + _, err = kubeCfgFile.Write(kubeConfig) + Expect(err).NotTo(HaveOccurred()) + err = os.Setenv("KUBECONFIG", kubeCfgFile.Name()) + Expect(err).NotTo(HaveOccurred()) + fmt.Printf("export KUBECONFIG=%s\n", kubeCfgFile.Name()) return cfg, k8sClient, testEnv, kubeConfig } From 1f177218a107e944fccc7529830796d07aae3ba4 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Mon, 17 Feb 2025 11:01:26 +0100 Subject: [PATCH 085/108] (chore): uses global const --- pkg/controllers/organization/dex.go | 10 +++++----- .../organization/organization_controller.go | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/controllers/organization/dex.go b/pkg/controllers/organization/dex.go index bef130c31..9fbb93709 100644 --- a/pkg/controllers/organization/dex.go +++ b/pkg/controllers/organization/dex.go @@ -143,10 +143,10 @@ func (r *OrganizationReconciler) reconcileOAuth2Client(ctx context.Context, org // NOTE: this has to be separate and should not be used with in any dex.UpdateClient transaction as it does not support concurrent updates // It is also not safe when using MaxConcurrentReconciles > 1 as the default connector's redirect URIs can be updated concurrently and // the last update will win -func (r *OrganizationReconciler) appendRedirectsToDefaultConnector(ctx context.Context, defaultOAuthClientID, newOAuthClientID string) error { - defaultOAuthClient, err := r.dex.GetClient(defaultOAuthClientID) +func (r *OrganizationReconciler) appendRedirectsToDefaultConnector(ctx context.Context, newOAuthClientID string) error { + defaultOAuthClient, err := r.dex.GetClient(defaultGreenhouseConnectorID) if err != nil { - log.FromContext(ctx).Error(err, "failed to get default connector's oauth2client", "ID", defaultOAuthClientID) + log.FromContext(ctx).Error(err, "failed to get default connector's oauth2client", "ID", defaultGreenhouseConnectorID) return err } newOAuthClient, err := r.dex.GetClient(newOAuthClientID) @@ -160,10 +160,10 @@ func (r *OrganizationReconciler) appendRedirectsToDefaultConnector(ctx context.C return authClient, nil }) if err != nil { - log.FromContext(ctx).Error(err, "failed to update default connector's oauth2client redirects", "ID", defaultOAuthClientID) + log.FromContext(ctx).Error(err, "failed to update default connector's oauth2client redirects", "ID", defaultGreenhouseConnectorID) return err } - log.FromContext(ctx).Info("successfully updated default connector's oauth2client redirects", "ID", defaultOAuthClientID) + log.FromContext(ctx).Info("successfully updated default connector's oauth2client redirects", "ID", defaultGreenhouseConnectorID) return nil } diff --git a/pkg/controllers/organization/organization_controller.go b/pkg/controllers/organization/organization_controller.go index f917c061d..7f0a583de 100644 --- a/pkg/controllers/organization/organization_controller.go +++ b/pkg/controllers/organization/organization_controller.go @@ -157,7 +157,7 @@ func (r *OrganizationReconciler) EnsureCreated(ctx context.Context, object lifec return ctrl.Result{}, lifecycle.Failed, err } if org.Name != defaultGreenhouseConnectorID { - if err := r.appendRedirectsToDefaultConnector(ctx, defaultGreenhouseConnectorID, org.Name); err != nil { + if err := r.appendRedirectsToDefaultConnector(ctx, org.Name); err != nil { org.SetCondition(greenhousesapv1alpha1.FalseCondition(greenhousesapv1alpha1.DefaultConnectorRedirectsConfigured, greenhousesapv1alpha1.DefaultConnectorRedirectsFailed, err.Error())) return ctrl.Result{}, lifecycle.Failed, err } From 2d8c9b6512d70e2b1b07e8478ac83c25cce400fa Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Mon, 17 Feb 2025 11:11:15 +0100 Subject: [PATCH 086/108] (chore): increase timeout on cluster delete --- pkg/test/util.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/test/util.go b/pkg/test/util.go index b4166f628..a030834fd 100644 --- a/pkg/test/util.go +++ b/pkg/test/util.go @@ -56,7 +56,7 @@ func MustDeleteCluster(ctx context.Context, c client.Client, id client.ObjectKey Eventually(func() bool { err := c.Get(ctx, client.ObjectKeyFromObject(cluster), cluster) return apierrors.IsNotFound(err) - }).Should(BeTrue(), "the object should be deleted eventually") + }, 3*time.Minute).Should(BeTrue(), "the object should be deleted eventually") } // SetClusterReadyCondition sets the ready condition of the cluster resource. From eca6f2d814a039c84705836339e53b4d64871866 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Mon, 17 Feb 2025 11:57:45 +0100 Subject: [PATCH 087/108] (chore): removes timeout on preset test --- pkg/test/util.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/test/util.go b/pkg/test/util.go index a030834fd..0ab072a90 100644 --- a/pkg/test/util.go +++ b/pkg/test/util.go @@ -56,7 +56,7 @@ func MustDeleteCluster(ctx context.Context, c client.Client, id client.ObjectKey Eventually(func() bool { err := c.Get(ctx, client.ObjectKeyFromObject(cluster), cluster) return apierrors.IsNotFound(err) - }, 3*time.Minute).Should(BeTrue(), "the object should be deleted eventually") + }, 5*time.Minute).Should(BeTrue(), "the object should be deleted eventually") } // SetClusterReadyCondition sets the ready condition of the cluster resource. From b1c608bdad861fc09109921a275621152002d559 Mon Sep 17 00:00:00 2001 From: David Gogl <1381862+kengou@users.noreply.github.com> Date: Mon, 17 Feb 2025 16:46:03 +0100 Subject: [PATCH 088/108] remove postgresng tableowner from root --- charts/greenhouse/ci/test-values.yaml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/charts/greenhouse/ci/test-values.yaml b/charts/greenhouse/ci/test-values.yaml index 6adf87b14..496f4daef 100644 --- a/charts/greenhouse/ci/test-values.yaml +++ b/charts/greenhouse/ci/test-values.yaml @@ -29,10 +29,6 @@ postgresql-ng: users: acme-user: -tableOwner: acme-user -users: - acme-user: - plugins: enabled: true From 7cb0aa02a2799a0e1a5aab2b9e21bb8c7ee284f1 Mon Sep 17 00:00:00 2001 From: David Gogl <1381862+kengou@users.noreply.github.com> Date: Tue, 18 Feb 2025 12:59:19 +0100 Subject: [PATCH 089/108] disable validation for kubernetes backend and postgres --- charts/greenhouse/templates/_helpers.tpl | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/charts/greenhouse/templates/_helpers.tpl b/charts/greenhouse/templates/_helpers.tpl index a8c065ad4..4b4532f82 100644 --- a/charts/greenhouse/templates/_helpers.tpl +++ b/charts/greenhouse/templates/_helpers.tpl @@ -64,9 +64,13 @@ Create the name of the service account to use {{/* Check if global.dex.backend is postgres then the postgressql part need to be enabled as well */}} -{{- if and (eq .Values.global.dex.backend "postgres") (eq .Values.postgresql.enabled "false") }} +{{- if and (eq .Values.global.dex.backend "postgres") (eq .Values.postgresql-ng.enabled "false") }} {{- fail "dex.backend: Setting the dex.backend to postgres requires that you enable and configure postgresql" }} {{- end }} -{{- if and (eq .Values.global.dex.backend "kubernetes") (eq .Values.postgresql.enabled "true") }} +{{/* +Comment out for now to support the rollout of the new dex backend +{{- if and (eq .Values.global.dex.backend "kubernetes") (eq .Values.postgresql-ng.enabled "true") }} {{- fail "dex.backend: Setting the dex.backend to kubernetes does not require postgresql enabled" }} {{- end }} +*/}} + From 8237ce54d4410be60ffbdf18a39e36517de43043 Mon Sep 17 00:00:00 2001 From: David Gogl <1381862+kengou@users.noreply.github.com> Date: Tue, 18 Feb 2025 13:20:31 +0100 Subject: [PATCH 090/108] rename variable to avoid failing lint --- charts/greenhouse/Chart.lock | 4 ++-- charts/greenhouse/Chart.yaml | 2 +- charts/greenhouse/ci/test-values.yaml | 2 +- charts/greenhouse/templates/_helpers.tpl | 4 ++-- charts/greenhouse/values.yaml | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/charts/greenhouse/Chart.lock b/charts/greenhouse/Chart.lock index 1b5547e42..4d70c4231 100644 --- a/charts/greenhouse/Chart.lock +++ b/charts/greenhouse/Chart.lock @@ -17,5 +17,5 @@ dependencies: - name: postgresql-ng repository: oci://ghcr.io/sapcc/helm-charts version: 1.2.7 -digest: sha256:ed5f9a31d629834b1337d8af26be4fd178f2bd8d9624495942ed8e4542100798 -generated: "2025-02-13T14:50:22.07126+01:00" +digest: sha256:2d62ccab5fac6ef205e389e21eb4ba9dbbc32e72b6f615660c6855cbef3fcf7b +generated: "2025-02-18T13:19:17.04322+01:00" diff --git a/charts/greenhouse/Chart.yaml b/charts/greenhouse/Chart.yaml index e41419db5..851fb7b8b 100644 --- a/charts/greenhouse/Chart.yaml +++ b/charts/greenhouse/Chart.yaml @@ -31,4 +31,4 @@ dependencies: - name: postgresql-ng version: 1.2.7 repository: "oci://ghcr.io/sapcc/helm-charts" - condition: postgresql-ng.enabled + condition: postgresqlnq.enabled diff --git a/charts/greenhouse/ci/test-values.yaml b/charts/greenhouse/ci/test-values.yaml index 496f4daef..7fcc1cd40 100644 --- a/charts/greenhouse/ci/test-values.yaml +++ b/charts/greenhouse/ci/test-values.yaml @@ -20,7 +20,7 @@ global: region: greenhouse registry: keppel.eu-nl-1.cloud.sap/ccloud -postgresql-ng: +postgresqlnq: enabled: true resources: {} # The database that will be created in the database diff --git a/charts/greenhouse/templates/_helpers.tpl b/charts/greenhouse/templates/_helpers.tpl index 4b4532f82..358c2ff65 100644 --- a/charts/greenhouse/templates/_helpers.tpl +++ b/charts/greenhouse/templates/_helpers.tpl @@ -64,12 +64,12 @@ Create the name of the service account to use {{/* Check if global.dex.backend is postgres then the postgressql part need to be enabled as well */}} -{{- if and (eq .Values.global.dex.backend "postgres") (eq .Values.postgresql-ng.enabled "false") }} +{{- if and (eq .Values.global.dex.backend "postgres") (eq .Values.postgresqlnq.enabled "false") }} {{- fail "dex.backend: Setting the dex.backend to postgres requires that you enable and configure postgresql" }} {{- end }} {{/* Comment out for now to support the rollout of the new dex backend -{{- if and (eq .Values.global.dex.backend "kubernetes") (eq .Values.postgresql-ng.enabled "true") }} +{{- if and (eq .Values.global.dex.backend "kubernetes") (eq .Values.postgresqlnq.enabled "true") }} {{- fail "dex.backend: Setting the dex.backend to kubernetes does not require postgresql enabled" }} {{- end }} */}} diff --git a/charts/greenhouse/values.yaml b/charts/greenhouse/values.yaml index 25b31fd9a..e68907fcd 100644 --- a/charts/greenhouse/values.yaml +++ b/charts/greenhouse/values.yaml @@ -19,7 +19,7 @@ global: postgresqlPort: postgresqlUsername: -postgresql-ng: +postgresqlnq: enabled: true global: linkerd_enabled: false From 01d7e8d40bfe66bde32483b548cd8210effc8819 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Tue, 18 Feb 2025 13:58:39 +0100 Subject: [PATCH 091/108] (chore): removes explicit timeouts in tests --- pkg/controllers/plugin/pluginpreset_controller_test.go | 4 +--- pkg/test/util.go | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/pkg/controllers/plugin/pluginpreset_controller_test.go b/pkg/controllers/plugin/pluginpreset_controller_test.go index 9e01836d5..dfeff1ce6 100644 --- a/pkg/controllers/plugin/pluginpreset_controller_test.go +++ b/pkg/controllers/plugin/pluginpreset_controller_test.go @@ -4,8 +4,6 @@ package plugin import ( - "time" - . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" "github.com/onsi/gomega/format" @@ -256,7 +254,7 @@ var _ = Describe("PluginPreset Controller Lifecycle", Ordered, func() { err = test.K8sClient.List(test.Ctx, pluginList, client.InNamespace(cluster.GetNamespace()), client.MatchingLabels{greenhouseapis.LabelKeyPluginPreset: pluginPresetName}) g.Expect(err).NotTo(HaveOccurred(), "failed to list Plugins") g.Expect(pluginList.Items).To(HaveLen(1), "there should be only one Plugin") - }, 2*time.Minute).Should(Succeed(), "the PluginPreset should have removed the Plugin for the deleted Cluster") + }).Should(Succeed(), "the PluginPreset should have removed the Plugin for the deleted Cluster") }) It("should set the Status NotReady if ClusterSelector does not match", func() { diff --git a/pkg/test/util.go b/pkg/test/util.go index 0ab072a90..b4166f628 100644 --- a/pkg/test/util.go +++ b/pkg/test/util.go @@ -56,7 +56,7 @@ func MustDeleteCluster(ctx context.Context, c client.Client, id client.ObjectKey Eventually(func() bool { err := c.Get(ctx, client.ObjectKeyFromObject(cluster), cluster) return apierrors.IsNotFound(err) - }, 5*time.Minute).Should(BeTrue(), "the object should be deleted eventually") + }).Should(BeTrue(), "the object should be deleted eventually") } // SetClusterReadyCondition sets the ready condition of the cluster resource. From c472e8a70656efcb716ee394cbb88446156be8dd Mon Sep 17 00:00:00 2001 From: David Gogl <1381862+kengou@users.noreply.github.com> Date: Tue, 18 Feb 2025 14:17:12 +0100 Subject: [PATCH 092/108] use alias for chart to fix lint --- charts/greenhouse/Chart.lock | 4 ++-- charts/greenhouse/Chart.yaml | 3 ++- charts/greenhouse/ci/test-values.yaml | 5 ++--- charts/greenhouse/templates/_helpers.tpl | 4 ++-- charts/greenhouse/values.yaml | 2 +- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/charts/greenhouse/Chart.lock b/charts/greenhouse/Chart.lock index 4d70c4231..8769f74a6 100644 --- a/charts/greenhouse/Chart.lock +++ b/charts/greenhouse/Chart.lock @@ -17,5 +17,5 @@ dependencies: - name: postgresql-ng repository: oci://ghcr.io/sapcc/helm-charts version: 1.2.7 -digest: sha256:2d62ccab5fac6ef205e389e21eb4ba9dbbc32e72b6f615660c6855cbef3fcf7b -generated: "2025-02-18T13:19:17.04322+01:00" +digest: sha256:b981d54ea5cd56e389f250a4745b8cb3f5f7f340c383623d29c76970d8746a05 +generated: "2025-02-18T14:15:55.146316+01:00" diff --git a/charts/greenhouse/Chart.yaml b/charts/greenhouse/Chart.yaml index 851fb7b8b..f5092d295 100644 --- a/charts/greenhouse/Chart.yaml +++ b/charts/greenhouse/Chart.yaml @@ -29,6 +29,7 @@ dependencies: repository: "file://../demo" condition: demo.enabled - name: postgresql-ng + alias: postgresqlng version: 1.2.7 repository: "oci://ghcr.io/sapcc/helm-charts" - condition: postgresqlnq.enabled + condition: postgresqlng.enabled diff --git a/charts/greenhouse/ci/test-values.yaml b/charts/greenhouse/ci/test-values.yaml index 7fcc1cd40..1e160c39d 100644 --- a/charts/greenhouse/ci/test-values.yaml +++ b/charts/greenhouse/ci/test-values.yaml @@ -20,15 +20,14 @@ global: region: greenhouse registry: keppel.eu-nl-1.cloud.sap/ccloud -postgresqlnq: +postgresqlng: enabled: true resources: {} # The database that will be created in the database postgresDatabase: dex - tableOwner: acme-user + tableOwner: dex users: acme-user: - plugins: enabled: true diff --git a/charts/greenhouse/templates/_helpers.tpl b/charts/greenhouse/templates/_helpers.tpl index 358c2ff65..4064e3e4c 100644 --- a/charts/greenhouse/templates/_helpers.tpl +++ b/charts/greenhouse/templates/_helpers.tpl @@ -64,12 +64,12 @@ Create the name of the service account to use {{/* Check if global.dex.backend is postgres then the postgressql part need to be enabled as well */}} -{{- if and (eq .Values.global.dex.backend "postgres") (eq .Values.postgresqlnq.enabled "false") }} +{{- if and (eq .Values.global.dex.backend "postgres") (eq .Values.postgresqlng.enabled "false") }} {{- fail "dex.backend: Setting the dex.backend to postgres requires that you enable and configure postgresql" }} {{- end }} {{/* Comment out for now to support the rollout of the new dex backend -{{- if and (eq .Values.global.dex.backend "kubernetes") (eq .Values.postgresqlnq.enabled "true") }} +{{- if and (eq .Values.global.dex.backend "kubernetes") (eq .Values.postgresqlng.enabled "true") }} {{- fail "dex.backend: Setting the dex.backend to kubernetes does not require postgresql enabled" }} {{- end }} */}} diff --git a/charts/greenhouse/values.yaml b/charts/greenhouse/values.yaml index e68907fcd..be8ade54c 100644 --- a/charts/greenhouse/values.yaml +++ b/charts/greenhouse/values.yaml @@ -19,7 +19,7 @@ global: postgresqlPort: postgresqlUsername: -postgresqlnq: +postgresqlng: enabled: true global: linkerd_enabled: false From f360f9c4443eb865f62689c4e61ceaccf76b52ac Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Wed, 19 Feb 2025 13:55:00 +0100 Subject: [PATCH 093/108] (chore): enable owner ref on dex resources --- pkg/controllers/organization/dex.go | 79 +++++++++++++++++++++++++++-- 1 file changed, 75 insertions(+), 4 deletions(-) diff --git a/pkg/controllers/organization/dex.go b/pkg/controllers/organization/dex.go index 9fbb93709..8dc141eec 100644 --- a/pkg/controllers/organization/dex.go +++ b/pkg/controllers/organization/dex.go @@ -17,11 +17,14 @@ import ( "golang.org/x/text/language" networkingv1 "k8s.io/api/networking/v1" "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/log" greenhousesapv1alpha1 "github.com/cloudoperators/greenhouse/pkg/apis/greenhouse/v1alpha1" "github.com/cloudoperators/greenhouse/pkg/clientutil" "github.com/cloudoperators/greenhouse/pkg/common" + "github.com/cloudoperators/greenhouse/pkg/dex" + dexapi "github.com/cloudoperators/greenhouse/pkg/dex/api" ) const dexConnectorTypeGreenhouse = "greenhouse-oidc" @@ -44,6 +47,30 @@ func (r *OrganizationReconciler) discoverOIDCRedirectURL(ctx context.Context, or return "", errors.New("oidc redirect URL not provided and cannot be discovered") } +// removeAuthRedirectFromDefaultConnector - removes oauth redirects of the org being deleted +// in the default connector's OAuth2Client +func (r *OrganizationReconciler) removeAuthRedirectFromDefaultConnector(ctx context.Context, org *greenhousesapv1alpha1.Organization) error { + defaultClient, err := r.dex.GetClient(defaultGreenhouseConnectorID) + if err != nil { + log.FromContext(ctx).Error(err, "failed to get default oauth2client", "name", defaultGreenhouseConnectorID) + return err + } + err = r.dex.UpdateClient(defaultClient.Name, func(authClient storage.Client) (storage.Client, error) { + orgRedirect := getRedirectForOrg(org.Name) + updatedRedirects := slices.DeleteFunc(authClient.RedirectURIs, func(s string) bool { + return s == orgRedirect + }) + authClient.RedirectURIs = updatedRedirects + return authClient, nil + }) + if err != nil { + log.FromContext(ctx).Error(err, "failed to update default connector's oauth2client redirects", "ID", defaultGreenhouseConnectorID) + return err + } + log.FromContext(ctx).Info("successfully removed redirects from default connector's oauth2client redirects", "ID", defaultGreenhouseConnectorID) + return nil +} + // reconcileDexConnector - creates or updates dex connector func (r *OrganizationReconciler) reconcileDexConnector(ctx context.Context, org *greenhousesapv1alpha1.Organization) error { clientID, err := clientutil.GetSecretKeyFromSecretKeyReference(ctx, r.Client, org.Name, org.Spec.Authentication.OIDCConfig.ClientIDReference) @@ -109,7 +136,7 @@ func (r *OrganizationReconciler) reconcileOAuth2Client(ctx context.Context, org oAuthClient, err := r.dex.GetClient(org.Name) if err != nil { if errors.Is(err, storage.ErrNotFound) { - redirectURIs := getRedirects(org, nil) + redirectURIs := getRedirects(org.Name, nil) if err = r.dex.CreateClient(ctx, storage.Client{ Public: true, ID: org.Name, @@ -129,7 +156,7 @@ func (r *OrganizationReconciler) reconcileOAuth2Client(ctx context.Context, org authClient.Public = true authClient.ID = org.Name authClient.Name = org.Name - authClient.RedirectURIs = getRedirects(org, authClient.RedirectURIs) + authClient.RedirectURIs = getRedirects(org.Name, authClient.RedirectURIs) return authClient, nil }); err != nil { log.FromContext(ctx).Error(err, "failed to update oauth2client", "name", org.Name) @@ -167,6 +194,46 @@ func (r *OrganizationReconciler) appendRedirectsToDefaultConnector(ctx context.C return nil } +func (r *OrganizationReconciler) setDexOwnerReferences(ctx context.Context, org *greenhousesapv1alpha1.Organization) error { + if r.DexStorageType == dex.K8s { + connector := &dexapi.ConnectorList{} + if err := r.Client.List(ctx, connector); err != nil { + log.FromContext(ctx).Error(err, "failed to list dex connectors") + return err + } + for _, c := range connector.Items { + if c.ID == org.Name { + _, err := clientutil.CreateOrPatch(ctx, r.Client, &c, func() error { + return controllerutil.SetOwnerReference(org, &c, r.Scheme()) + }) + if err != nil { + log.FromContext(ctx).Error(err, "failed to set owner reference for dex connector", "name", c.ID) + return err + } + break + } + } + oauthClients := &dexapi.OAuth2ClientList{} + if err := r.Client.List(ctx, oauthClients); err != nil { + log.FromContext(ctx).Error(err, "failed to list dex oauth2clients") + return err + } + for _, c := range oauthClients.Items { + if c.ID == org.Name { + _, err := clientutil.CreateOrPatch(ctx, r.Client, &c, func() error { + return controllerutil.SetOwnerReference(org, &c, r.Scheme()) + }) + if err != nil { + log.FromContext(ctx).Error(err, "failed to set owner reference for dex oauth2client", "name", c.ID) + return err + } + break + } + } + } + return nil +} + func ensureCallbackURL(url string) string { prefix := "https://" if !strings.HasPrefix(url, prefix) { @@ -183,15 +250,19 @@ func ensureCallbackURL(url string) string { // and merges with the provided redirect URIs // this is needed when the default connector is being reconciled as it should not overwrite // any appended redirect URIs from other organizations -func getRedirects(org *greenhousesapv1alpha1.Organization, redirectURIs []string) []string { +func getRedirects(orgName string, redirectURIs []string) []string { defaultRedirects := []string{ "http://localhost:8085", // allowing local development of idproxy url "https://dashboard." + common.DNSDomain, - fmt.Sprintf("https://%s.dashboard.%s", org.Name, common.DNSDomain), + getRedirectForOrg(orgName), } return appendRedirects(defaultRedirects, redirectURIs...) } +func getRedirectForOrg(orgName string) string { + return fmt.Sprintf("https://%s.%s", orgName, common.DNSDomain) +} + // appendRedirects - appends newRedirects to the redirects slice if it does not exist func appendRedirects(redirects []string, newRedirects ...string) []string { for _, r := range newRedirects { From 0a3c2f4c73daae0039e5f53bdda58341111c3278 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Wed, 19 Feb 2025 13:55:08 +0100 Subject: [PATCH 094/108] (chore): handle deletion of org and dex resources --- .../organization/organization_controller.go | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/pkg/controllers/organization/organization_controller.go b/pkg/controllers/organization/organization_controller.go index 765d6a5cd..84961794e 100644 --- a/pkg/controllers/organization/organization_controller.go +++ b/pkg/controllers/organization/organization_controller.go @@ -111,7 +111,19 @@ func (r *OrganizationReconciler) Reconcile(ctx context.Context, req ctrl.Request return lifecycle.Reconcile(ctx, r.Client, req.NamespacedName, &greenhousesapv1alpha1.Organization{}, r, r.setStatus()) } -func (r *OrganizationReconciler) EnsureDeleted(_ context.Context, _ lifecycle.RuntimeObject) (ctrl.Result, lifecycle.ReconcileResult, error) { +func (r *OrganizationReconciler) EnsureDeleted(ctx context.Context, obj lifecycle.RuntimeObject) (ctrl.Result, lifecycle.ReconcileResult, error) { + org, ok := obj.(*greenhousesapv1alpha1.Organization) + if !ok { + return ctrl.Result{}, lifecycle.Success, nil + } + + if org.Spec.Authentication != nil && org.Spec.Authentication.OIDCConfig != nil { + // delete org oauth redirects from default connector + if err := r.removeAuthRedirectFromDefaultConnector(ctx, org); err != nil { + return ctrl.Result{}, lifecycle.Failed, err + } + } + // if dex storage type is kubernetes then deletion of org connector, org oauth2client will be handled automatically by owner reference return ctrl.Result{}, lifecycle.Success, nil // nothing to do in that case } @@ -163,6 +175,11 @@ func (r *OrganizationReconciler) EnsureCreated(ctx context.Context, object lifec return ctrl.Result{}, lifecycle.Failed, err } } + + if err := r.setDexOwnerReferences(ctx, org); err != nil { + return ctrl.Result{}, lifecycle.Failed, err + } + org.SetCondition(greenhousesapv1alpha1.TrueCondition(greenhousesapv1alpha1.OrganizationOICDConfigured, "", "")) } From d0ab9e9d935ceed5c3d32a67498c937f62a6a0ac Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Wed, 19 Feb 2025 13:55:24 +0100 Subject: [PATCH 095/108] (chore): add tests for dex resources --- .../organization_controller_test.go | 110 ++++++++++++++++++ pkg/controllers/organization/suite_test.go | 11 +- 2 files changed, 119 insertions(+), 2 deletions(-) diff --git a/pkg/controllers/organization/organization_controller_test.go b/pkg/controllers/organization/organization_controller_test.go index e6e0cb6b5..312aa2a62 100644 --- a/pkg/controllers/organization/organization_controller_test.go +++ b/pkg/controllers/organization/organization_controller_test.go @@ -6,6 +6,7 @@ package organization_test import ( . "github.com/onsi/ginkgo/v2" . "github.com/onsi/gomega" + "golang.org/x/exp/slices" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" @@ -13,6 +14,8 @@ import ( greenhousev1alpha1 "github.com/cloudoperators/greenhouse/pkg/apis/greenhouse/v1alpha1" "github.com/cloudoperators/greenhouse/pkg/clientutil" + "github.com/cloudoperators/greenhouse/pkg/dex" + dexapi "github.com/cloudoperators/greenhouse/pkg/dex/api" "github.com/cloudoperators/greenhouse/pkg/scim" "github.com/cloudoperators/greenhouse/pkg/test" ) @@ -296,9 +299,116 @@ var _ = Describe("Test Organization reconciliation", Ordered, func() { g.Expect(readyCondition.IsTrue()).To(BeTrue(), "ReadyCondition should be True on Organization") }).Should(Succeed(), "Organization should have set correct status condition") }) + + It("should create dex resources if oidc is enabled", func() { + By("creating greenhouse organization with OIDC config") + greenhouseOrgName := "greenhouse" + setup.CreateOrganization(test.Ctx, greenhouseOrgName, func(org *greenhousev1alpha1.Organization) { + org.Spec.MappedOrgAdminIDPGroup = validIdpGroupName + }) + test.EventuallyCreated(test.Ctx, test.K8sClient, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: greenhouseOrgName}}) + + By("creating a test organization for OIDC") + oidcOrgName := "test-oidc-org" + setup.CreateOrganization(test.Ctx, oidcOrgName, func(org *greenhousev1alpha1.Organization) { + org.Spec.MappedOrgAdminIDPGroup = validIdpGroupName + }) + test.EventuallyCreated(test.Ctx, test.K8sClient, &corev1.Namespace{ObjectMeta: metav1.ObjectMeta{Name: oidcOrgName}}) + + By("creating a secret for OIDC config") + createSecretForOIDCConfig(greenhouseOrgName) + createSecretForOIDCConfig(oidcOrgName) + + By("updating the organization with OIDC config") + updateOrgWithOIDC(greenhouseOrgName) + updateOrgWithOIDC(oidcOrgName) + + By("checking Organization status") + checkOrganizationReadyStatus(greenhouseOrgName) + checkOrganizationReadyStatus(oidcOrgName) + + if DexStorageType == dex.K8s { + By("checking dex connector resource") + connectors := &dexapi.ConnectorList{} + oAuthClients := &dexapi.OAuth2ClientList{} + + err := setup.List(test.Ctx, connectors) + Expect(err).ToNot(HaveOccurred(), "there should be no error listing dex connectors") + Expect(len(connectors.Items)).To(BeNumerically(">", 1), "there should be at least one dex connector") + err = setup.List(test.Ctx, oAuthClients) + Expect(err).ToNot(HaveOccurred(), "there should be no error listing dex oauth clients") + Expect(len(oAuthClients.Items)).To(BeNumerically(">", 1), "there should be at least one dex oauth client") + + filteredOrgConnector := slices.DeleteFunc(connectors.Items, func(c dexapi.Connector) bool { + return c.ID != oidcOrgName + }) + Expect(filteredOrgConnector).To(HaveLen(1), "there should be one dex connector after filtering") + ownerRef := clientutil.GetOwnerReference(&filteredOrgConnector[0], "Organization") + Expect(ownerRef).ToNot(BeNil(), "there should be an owner reference for the dex connector") + Expect(ownerRef.Name).To(Equal(oidcOrgName), "the owner reference should have the correct name") + + By("checking dex oauth client resource") + filteredOrgClient := slices.DeleteFunc(oAuthClients.Items, func(c dexapi.OAuth2Client) bool { + return c.ID != oidcOrgName + }) + Expect(filteredOrgClient).To(HaveLen(1), "there should be one dex oauth client after filtering") + ownerRef = clientutil.GetOwnerReference(&filteredOrgClient[0], "Organization") + Expect(ownerRef).ToNot(BeNil(), "there should be an owner reference for the dex oauth client") + Expect(ownerRef.Name).To(Equal(oidcOrgName), "the owner reference should have the correct name") + } + }) }) }) +func checkOrganizationReadyStatus(orgName string) { + By("checking Organization status") + Eventually(func(g Gomega) { + org := &greenhousev1alpha1.Organization{} + err := test.K8sClient.Get(test.Ctx, types.NamespacedName{Name: orgName}, org) + g.Expect(err).ToNot(HaveOccurred(), "there should be no error getting the Organization") + readyCondition := org.Status.GetConditionByType(greenhousev1alpha1.ReadyCondition) + g.Expect(readyCondition).ToNot(BeNil(), "ReadyCondition should be set on Organization") + g.Expect(readyCondition.IsTrue()).To(BeTrue(), "ReadyCondition should be True on Organization") + oidcCondition := org.Status.GetConditionByType(greenhousev1alpha1.OrganizationOICDConfigured) + g.Expect(oidcCondition).ToNot(BeNil(), "OrganizationOICDConfigured should be set on Organization") + g.Expect(oidcCondition.IsTrue()).To(BeTrue(), "OrganizationOICDConfigured should be True on Organization") + }).Should(Succeed(), "Organization should have set correct status condition") +} + +func createSecretForOIDCConfig(namespace string) { + oidcSecret := &corev1.Secret{} + oidcSecret.SetName("test-oidc-secret") + oidcSecret.SetNamespace(namespace) + oidcSecret.Data = map[string][]byte{ + "clientId": []byte("test-client-id"), + "clientSecret": []byte("test-client-secret"), + } + err := test.K8sClient.Create(test.Ctx, oidcSecret) + Expect(err).ToNot(HaveOccurred(), "there should be no error creating the secret") +} + +func updateOrgWithOIDC(orgName string) { + org := &greenhousev1alpha1.Organization{} + err := test.K8sClient.Get(test.Ctx, types.NamespacedName{Name: orgName}, org) + Expect(err).ToNot(HaveOccurred(), "there should be no error getting the organization") + org.Spec.Authentication = &greenhousev1alpha1.Authentication{ + OIDCConfig: &greenhousev1alpha1.OIDCConfig{ + Issuer: "https://example.com", + RedirectURI: "https://example.com/callback", + ClientIDReference: greenhousev1alpha1.SecretKeyReference{ + Name: "test-oidc-secret", + Key: "clientId", + }, + ClientSecretReference: greenhousev1alpha1.SecretKeyReference{ + Name: "test-oidc-secret", + Key: "clientSecret", + }, + }, + } + err = test.K8sClient.Update(test.Ctx, org) + Expect(err).ToNot(HaveOccurred(), "there should be no error updating the organization with OIDC config") +} + func createSecretForSCIMConfig(namespace string) { testSecret := corev1.Secret{ TypeMeta: metav1.TypeMeta{ diff --git a/pkg/controllers/organization/suite_test.go b/pkg/controllers/organization/suite_test.go index a91940ae8..c3e648477 100644 --- a/pkg/controllers/organization/suite_test.go +++ b/pkg/controllers/organization/suite_test.go @@ -18,7 +18,8 @@ import ( ) var ( - groupsServer *httptest.Server + DexStorageType string + groupsServer *httptest.Server ) func TestOrganization(t *testing.T) { @@ -30,7 +31,13 @@ var _ = BeforeSuite(func() { By("mocking SCIM server") groupsServer = scim.ReturnDefaultGroupResponseMockServer() - test.RegisterController("organizationController", (&organizationpkg.OrganizationReconciler{Namespace: "default", DexStorageType: dex.K8s}).SetupWithManager) + By("setting the dex storage type") + // here we could set the dex storage type to be used in the tests to expect the right behavior + // via environment variables in the future + // for postgres we can start a postgres testcontainer + DexStorageType = dex.K8s + + test.RegisterController("organizationController", (&organizationpkg.OrganizationReconciler{Namespace: "default", DexStorageType: DexStorageType}).SetupWithManager) test.RegisterWebhook("orgWebhook", admission.SetupOrganizationWebhookWithManager) test.RegisterWebhook("teamWebhook", admission.SetupTeamWebhookWithManager) test.RegisterWebhook("pluginDefinitionWebhook", admission.SetupPluginDefinitionWebhookWithManager) From 8befc6cf7759e8c2a8145d05ac1035294c007075 Mon Sep 17 00:00:00 2001 From: David Gogl <1381862+kengou@users.noreply.github.com> Date: Wed, 19 Feb 2025 14:03:31 +0100 Subject: [PATCH 096/108] bump chart version --- charts/greenhouse/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/greenhouse/Chart.yaml b/charts/greenhouse/Chart.yaml index f5092d295..8ec6c1d4a 100644 --- a/charts/greenhouse/Chart.yaml +++ b/charts/greenhouse/Chart.yaml @@ -5,7 +5,7 @@ apiVersion: v2 name: greenhouse description: A Helm chart for deploying greenhouse type: application -version: 0.8.2 +version: 0.8.3 appVersion: "0.1.0" dependencies: From 364c3ff77414ad3b7ea0e7da73389c28a886142c Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Mon, 24 Feb 2025 13:58:30 +0100 Subject: [PATCH 097/108] (chore): rebuild chart dependencies --- charts/greenhouse/Chart.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/charts/greenhouse/Chart.lock b/charts/greenhouse/Chart.lock index 0ce85947c..752b9fafe 100644 --- a/charts/greenhouse/Chart.lock +++ b/charts/greenhouse/Chart.lock @@ -17,5 +17,5 @@ dependencies: - name: postgresql-ng repository: oci://ghcr.io/sapcc/helm-charts version: 1.2.7 -digest: sha256:b981d54ea5cd56e389f250a4745b8cb3f5f7f340c383623d29c76970d8746a05 -generated: "2025-02-18T14:15:55.146316+01:00" +digest: sha256:e9fcb4f95fecdabf4f0f8dd19ff3d4d32028b5368181831fee932486f0845887 +generated: "2025-02-24T13:56:52.338605+01:00" From da3e6eb28b2c1aebcb1d45b3b1b436ded11ee427 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Mon, 24 Feb 2025 14:01:31 +0100 Subject: [PATCH 098/108] (chore): bump chart version --- charts/greenhouse/Chart.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/charts/greenhouse/Chart.yaml b/charts/greenhouse/Chart.yaml index d351c6836..016c09fe8 100644 --- a/charts/greenhouse/Chart.yaml +++ b/charts/greenhouse/Chart.yaml @@ -5,7 +5,7 @@ apiVersion: v2 name: greenhouse description: A Helm chart for deploying greenhouse type: application -version: 0.8.3 +version: 0.8.4 appVersion: "0.1.0" dependencies: From 0e52eeb811dc42be150f2da4ad007dbe1a421f14 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Mon, 24 Feb 2025 14:07:42 +0100 Subject: [PATCH 099/108] (chore): update docs --- cmd/idproxy/README.md | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/cmd/idproxy/README.md b/cmd/idproxy/README.md index f414f6aba..80cabc1f4 100644 --- a/cmd/idproxy/README.md +++ b/cmd/idproxy/README.md @@ -32,21 +32,4 @@ Environment Variables: > [!NOTE] > There should be a configured `Connector` and `OAuth2Client` for the `idproxy` service to work properly. -> The `Connector` and `OAuth2Client` is created when you create an `Organization` - -## Testing `idproxy` service locally - -You can test the `idproxy` service locally by initiating an `oauth2` flow with grant type `authorization_code` - - -- Visit the well-known endpoint `http://localhost:/.well-known/openid-configuration` to get the `authorization_endpoint` and `token_endpoint` -- Use `insomnia` or `postman` tool -- Create an empty http request -- Set `Auth` type to `OAuth 2.0` -- Set `Authorization` and `Token` endpoint to the values obtained from the well-known endpoint -- Set the `client id` and `client secret` (available as `-dex-secrets` secret in the `` namespace) -- Set the redirect uri to `http://localhost:8000` -- Minimum scope required is `openid` (Additionally you can set `email` `groups` `offline_access`) -- Send the request and you will be redirected to the `dex` login page -- Choose the provider you want to authenticate with -- You may be asked to enter the `username` and `password` for the provider -- Once authenticated you should see the `id_token` and `access_token` automatically populated in `insomnia` or `postman` tool \ No newline at end of file +> The `Connector` and `OAuth2Client` is created when you create an `Organization` \ No newline at end of file From 50a8f9c6a64eadc8b0e92aa8b964140ee9e0bf6e Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Mon, 24 Feb 2025 14:53:42 +0100 Subject: [PATCH 100/108] (chore): adds connector and client deletion --- pkg/controllers/organization/dex.go | 26 +++++++++++++++++++ .../organization/organization_controller.go | 16 +++++++++--- 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/pkg/controllers/organization/dex.go b/pkg/controllers/organization/dex.go index 8dc141eec..c3857cafd 100644 --- a/pkg/controllers/organization/dex.go +++ b/pkg/controllers/organization/dex.go @@ -71,6 +71,32 @@ func (r *OrganizationReconciler) removeAuthRedirectFromDefaultConnector(ctx cont return nil } +func (r *OrganizationReconciler) deleteDexConnector(ctx context.Context, org *greenhousesapv1alpha1.Organization) error { + if err := r.dex.DeleteConnector(org.Name); err != nil { + if errors.Is(err, storage.ErrNotFound) { + log.FromContext(ctx).Info("dex connector not found", "name", org.Name) + return nil + } + log.FromContext(ctx).Error(err, "failed to delete dex connector", "name", org.Name) + return err + } + log.FromContext(ctx).Info("successfully deleted dex connector", "name", org.Name) + return nil +} + +func (r *OrganizationReconciler) deleteOAuth2Client(ctx context.Context, org *greenhousesapv1alpha1.Organization) error { + if err := r.dex.DeleteClient(org.Name); err != nil { + if errors.Is(err, storage.ErrNotFound) { + log.FromContext(ctx).Info("oauth2client not found", "name", org.Name) + return nil + } + log.FromContext(ctx).Error(err, "failed to delete oauth2client", "name", org.Name) + return err + } + log.FromContext(ctx).Info("successfully deleted oauth2client", "name", org.Name) + return nil +} + // reconcileDexConnector - creates or updates dex connector func (r *OrganizationReconciler) reconcileDexConnector(ctx context.Context, org *greenhousesapv1alpha1.Organization) error { clientID, err := clientutil.GetSecretKeyFromSecretKeyReference(ctx, r.Client, org.Name, org.Spec.Authentication.OIDCConfig.ClientIDReference) diff --git a/pkg/controllers/organization/organization_controller.go b/pkg/controllers/organization/organization_controller.go index 84961794e..4729d2f9a 100644 --- a/pkg/controllers/organization/organization_controller.go +++ b/pkg/controllers/organization/organization_controller.go @@ -122,8 +122,16 @@ func (r *OrganizationReconciler) EnsureDeleted(ctx context.Context, obj lifecycl if err := r.removeAuthRedirectFromDefaultConnector(ctx, org); err != nil { return ctrl.Result{}, lifecycle.Failed, err } + // if dex storage type is kubernetes then deletion of org connector, org oauth2client will be handled automatically by owner reference + if r.DexStorageType == dexstore.Postgres { + if err := r.deleteDexConnector(ctx, org); err != nil { + return ctrl.Result{}, lifecycle.Failed, err + } + if err := r.deleteOAuth2Client(ctx, org); err != nil { + return ctrl.Result{}, lifecycle.Failed, err + } + } } - // if dex storage type is kubernetes then deletion of org connector, org oauth2client will be handled automatically by owner reference return ctrl.Result{}, lifecycle.Success, nil // nothing to do in that case } @@ -176,8 +184,10 @@ func (r *OrganizationReconciler) EnsureCreated(ctx context.Context, object lifec } } - if err := r.setDexOwnerReferences(ctx, org); err != nil { - return ctrl.Result{}, lifecycle.Failed, err + if r.DexStorageType == dexstore.K8s { + if err := r.setDexOwnerReferences(ctx, org); err != nil { + return ctrl.Result{}, lifecycle.Failed, err + } } org.SetCondition(greenhousesapv1alpha1.TrueCondition(greenhousesapv1alpha1.OrganizationOICDConfigured, "", "")) From 8d259a94d53589e96f8f98695e359209fc53854b Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Mon, 24 Feb 2025 15:01:57 +0100 Subject: [PATCH 101/108] (chore): remove unused org condition type --- pkg/apis/greenhouse/v1alpha1/organization_types.go | 2 -- pkg/controllers/organization/organization_controller.go | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/pkg/apis/greenhouse/v1alpha1/organization_types.go b/pkg/apis/greenhouse/v1alpha1/organization_types.go index 504ed3871..7ebc323cb 100644 --- a/pkg/apis/greenhouse/v1alpha1/organization_types.go +++ b/pkg/apis/greenhouse/v1alpha1/organization_types.go @@ -29,8 +29,6 @@ const ( ServiceProxyProvisioned ConditionType = "ServiceProxyProvisioned" // OrganizationOICDConfigured is set when the OICD is configured OrganizationOICDConfigured ConditionType = "OrganizationOICDConfigured" - // DefaultConnectorRedirectsConfigured is set when the default connector redirects are appended with redirects URIs from new organizations - DefaultConnectorRedirectsConfigured ConditionType = "DefaultConnectorRedirectsConfigured" // DexReconcileFailed is set when dex reconcile step has failed DexReconcileFailed ConditionReason = "DexReconcileFailed" // OAuthOICDFailed is set when OAuth reconciler has failed diff --git a/pkg/controllers/organization/organization_controller.go b/pkg/controllers/organization/organization_controller.go index 4729d2f9a..557d5afdd 100644 --- a/pkg/controllers/organization/organization_controller.go +++ b/pkg/controllers/organization/organization_controller.go @@ -179,13 +179,14 @@ func (r *OrganizationReconciler) EnsureCreated(ctx context.Context, object lifec } if org.Name != defaultGreenhouseConnectorID { if err := r.appendRedirectsToDefaultConnector(ctx, org.Name); err != nil { - org.SetCondition(greenhousesapv1alpha1.FalseCondition(greenhousesapv1alpha1.DefaultConnectorRedirectsConfigured, greenhousesapv1alpha1.DefaultConnectorRedirectsFailed, err.Error())) + org.SetCondition(greenhousesapv1alpha1.FalseCondition(greenhousesapv1alpha1.OrganizationOICDConfigured, greenhousesapv1alpha1.DefaultConnectorRedirectsFailed, err.Error())) return ctrl.Result{}, lifecycle.Failed, err } } if r.DexStorageType == dexstore.K8s { if err := r.setDexOwnerReferences(ctx, org); err != nil { + org.SetCondition(greenhousesapv1alpha1.FalseCondition(greenhousesapv1alpha1.OrganizationOICDConfigured, "", err.Error())) return ctrl.Result{}, lifecycle.Failed, err } } From faec442d883e369df66fc830ee9e49f6c8ca49a3 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Mon, 24 Feb 2025 15:43:41 +0100 Subject: [PATCH 102/108] (chore): regenerate manifests --- charts/manager/templates/manager-role.yaml | 3 - docs/reference/api/openapi.yaml | 1648 ++++++++++---------- types/typescript/schema.d.ts | 28 + 3 files changed, 852 insertions(+), 827 deletions(-) diff --git a/charts/manager/templates/manager-role.yaml b/charts/manager/templates/manager-role.yaml index 7b7308707..76c4dc36a 100644 --- a/charts/manager/templates/manager-role.yaml +++ b/charts/manager/templates/manager-role.yaml @@ -82,10 +82,8 @@ rules: resources: - clusters/finalizers - organizations/finalizers - - plugindefinitions/finalizers - pluginpresets/finalizers - plugins/finalizers - - teammemberships/finalizers - teamrolebindings/finalizers - teams/finalizers verbs: @@ -95,7 +93,6 @@ rules: resources: - clusters/status - organizations/status - - plugindefinitions/status - pluginpresets/status - plugins/status - teammemberships/status diff --git a/docs/reference/api/openapi.yaml b/docs/reference/api/openapi.yaml index d7884cace..824b101d1 100755 --- a/docs/reference/api/openapi.yaml +++ b/docs/reference/api/openapi.yaml @@ -4,64 +4,64 @@ info: version: main description: PlusOne operations platform paths: - /Team: + /Organization: post: responses: default: - description: Team - /TeamRole: + description: Organization + /ClusterKubeconfig: post: responses: default: - description: TeamRole - /TeamMembership: + description: ClusterKubeconfig + /Cluster: post: responses: default: - description: TeamMembership - /Organization: + description: Cluster + /TeamRole: post: responses: default: - description: Organization - /Cluster: + description: TeamRole + /TeamMembership: post: responses: default: - description: Cluster - /Plugin: + description: TeamMembership + /PluginPreset: post: responses: default: - description: Plugin - /PluginDefinition: + description: PluginPreset + /Team: post: responses: default: - description: PluginDefinition - /ClusterKubeconfig: + description: Team + /TeamRoleBinding: post: responses: default: - description: ClusterKubeconfig - /TeamRoleBinding: + description: TeamRoleBinding + /Plugin: post: responses: default: - description: TeamRoleBinding - /PluginPreset: + description: Plugin + /PluginDefinition: post: responses: default: - description: PluginPreset + description: PluginDefinition components: schemas: - Team: + Organization: xml: name: greenhouse.sap namespace: v1alpha1 - title: Team - description: Team is the Schema for the teams API + title: Organization + description: Organization is the Schema for the organizations API properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' @@ -72,46 +72,145 @@ components: metadata: type: object spec: - description: TeamSpec defines the desired state of Team + description: OrganizationSpec defines the desired state of Organization properties: + authentication: + description: Authentication configures the organizations authentication mechanism. + properties: + oidc: + description: OIDConfig configures the OIDC provider. + properties: + clientIDReference: + description: ClientIDReference references the Kubernetes secret containing the client id. + properties: + key: + description: Key in the secret to select the value from. + type: string + name: + description: Name of the secret in the same namespace. + type: string + required: + - key + - name + type: object + clientSecretReference: + description: ClientSecretReference references the Kubernetes secret containing the client secret. + properties: + key: + description: Key in the secret to select the value from. + type: string + name: + description: Name of the secret in the same namespace. + type: string + required: + - key + - name + type: object + issuer: + description: Issuer is the URL of the identity service. + type: string + redirectURI: + description: RedirectURI is the redirect URI.\nIf none is specified, the Greenhouse ID proxy will be used. + type: string + required: + - clientIDReference + - clientSecretReference + - issuer + type: object + scim: + description: SCIMConfig configures the SCIM client. + properties: + authType: + default: basic + description: AuthType defined possible authentication type + enum: + - basic + - token + type: string + baseURL: + description: URL to the SCIM server. + type: string + basicAuthPw: + description: Password to be used for basic authentication. + properties: + secret: + description: Secret references the secret containing the value. + properties: + key: + description: Key in the secret to select the value from. + type: string + name: + description: Name of the secret in the same namespace. + type: string + required: + - key + - name + type: object + type: object + basicAuthUser: + description: User to be used for basic authentication. + properties: + secret: + description: Secret references the secret containing the value. + properties: + key: + description: Key in the secret to select the value from. + type: string + name: + description: Name of the secret in the same namespace. + type: string + required: + - key + - name + type: object + type: object + bearerHeader: + default: Authorization + description: BearerHeader to be used to defined bearer token header + type: string + bearerPrefix: + default: Bearer + description: BearerPrefix to be used to defined bearer token prefix + type: string + bearerToken: + description: BearerToken to be used for bearer token authorization + properties: + secret: + description: Secret references the secret containing the value. + properties: + key: + description: Key in the secret to select the value from. + type: string + name: + description: Name of the secret in the same namespace. + type: string + required: + - key + - name + type: object + type: object + required: + - baseURL + - basicAuthPw + - basicAuthUser + - bearerToken + type: object + type: object description: - description: Description provides additional details of the team. + description: Description provides additional details of the organization. type: string - joinUrl: - description: URL to join the IdP group. + displayName: + description: DisplayName is an optional name for the organization to be displayed in the Greenhouse UI.\nDefaults to a normalized version of metadata.name. type: string - mappedIdPGroup: - description: IdP group id matching team. + mappedOrgAdminIdPGroup: + description: MappedOrgAdminIDPGroup is the IDP group ID identifying org admins type: string type: object status: - description: TeamStatus defines the observed state of Team + description: OrganizationStatus defines the observed state of an Organization properties: - members: - items: - description: User specifies a human person. - properties: - email: - description: Email of the user. - type: string - firstName: - description: FirstName of the user. - type: string - id: - description: ID is the unique identifier of the user. - type: string - lastName: - description: LastName of the user. - type: string - required: - - email - - firstName - - id - - lastName - type: object - type: array statusConditions: - description: A StatusConditions contains a list of conditions.\nOnly one condition of a given type may exist in the list. + description: StatusConditions contain the different conditions that constitute the status of the Organization. properties: conditions: items: @@ -143,16 +242,14 @@ components: - type x-kubernetes-list-type: map type: object - required: - - statusConditions type: object type: object - TeamRole: + ClusterKubeconfig: xml: name: greenhouse.sap namespace: v1alpha1 - title: TeamRole - description: TeamRole is the Schema for the TeamRoles API + title: ClusterKubeconfig + description: ClusterKubeconfig is the Schema for the clusterkubeconfigs API\nObjectMeta.OwnerReferences is used to link the ClusterKubeconfig to the Cluster\nObjectMeta.Generation is used to detect changes in the ClusterKubeconfig and sync local kubeconfig files\nObjectMeta.Name is designed to be the same with the Cluster name properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' @@ -163,154 +260,94 @@ components: metadata: type: object spec: - description: TeamRoleSpec defines the desired state of a TeamRole + description: ClusterKubeconfigSpec stores the kubeconfig data for the cluster\nThe idea is to use kubeconfig data locally with minimum effort (with local tools or plain kubectl):\nkubectl get cluster-kubeconfig $NAME -o yaml | yq -y .spec.kubeconfig properties: - aggregationRule: - description: AggregationRule describes how to locate ClusterRoles to aggregate into the ClusterRole on the remote cluster + kubeconfig: + description: 'ClusterKubeconfigData stores the kubeconfig data ready to use kubectl or other local tooling\nIt is a simplified version of clientcmdapi.Config: https://pkg.go.dev/k8s.io/client-go/tools/clientcmd/api#Config' properties: - clusterRoleSelectors: - description: ClusterRoleSelectors holds a list of selectors which will be used to find ClusterRoles and create the rules.\nIf any of the selectors match, then the ClusterRole's permissions will be added + apiVersion: + type: string + clusters: items: - description: A label selector is a label query over a set of resources. The result of matchLabels and\nmatchExpressions are ANDed. An empty label selector matches all objects. A null\nlabel selector matches no objects. properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - items: - description: A label selector requirement is a selector that contains values, a key, and an operator that\nrelates the key and values. - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: operator represents a key's relationship to a set of values.\nValid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array of string values. If the operator is In or NotIn,\nthe values array must be non-empty. If the operator is Exists or DoesNotExist,\nthe values array must be empty. This array is replaced during a strategic\nmerge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels\nmap is equivalent to an element of matchExpressions, whose key field is "key", the\noperator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic + cluster: + properties: + certificate-authority-data: + format: byte + type: string + server: + type: string + type: object + name: + type: string + required: + - cluster + - name + type: object + type: array + contexts: + items: + properties: + context: + properties: + cluster: + type: string + namespace: + type: string + user: + type: string + required: + - cluster + - user + type: object + name: + type: string + required: + - name + type: object + type: array + current-context: + type: string + kind: + type: string + preferences: + type: object + users: + items: + properties: + name: + type: string + user: + properties: + auth-provider: + description: AuthProviderConfig holds the configuration for a specified auth provider. + properties: + config: + additionalProperties: + type: string + type: object + name: + type: string + required: + - name + type: object + client-certificate-data: + format: byte + type: string + client-key-data: + format: byte + type: string + type: object + required: + - name + type: object type: array - x-kubernetes-list-type: atomic - type: object - labels: - additionalProperties: - type: string - description: Labels are applied to the ClusterRole created on the remote cluster.\nThis allows using TeamRoles as part of AggregationRules by other TeamRoles type: object - rules: - description: Rules is a list of rbacv1.PolicyRules used on a managed RBAC (Cluster)Role - items: - description: PolicyRule holds information that describes a policy rule, but does not contain information\nabout who the rule applies to or which namespace the rule applies to. - properties: - apiGroups: - description: APIGroups is the name of the APIGroup that contains the resources. If multiple API groups are specified, any action requested against one of\nthe enumerated resources in any API group will be allowed. "" represents the core API group and "*" represents all API groups. - items: - type: string - type: array - x-kubernetes-list-type: atomic - nonResourceURLs: - description: NonResourceURLs is a set of partial urls that a user should have access to. *s are allowed, but only as the full, final step in the path\nSince non-resource URLs are not namespaced, this field is only applicable for ClusterRoles referenced from a ClusterRoleBinding.\nRules can either apply to API resources (such as "pods" or "secrets") or non-resource URL paths (such as "/api"), but not both. - items: - type: string - type: array - x-kubernetes-list-type: atomic - resourceNames: - description: ResourceNames is an optional white list of names that the rule applies to. An empty set means that everything is allowed. - items: - type: string - type: array - x-kubernetes-list-type: atomic - resources: - description: Resources is a list of resources this rule applies to. '*' represents all resources. - items: - type: string - type: array - x-kubernetes-list-type: atomic - verbs: - description: Verbs is a list of Verbs that apply to ALL the ResourceKinds contained in this rule. '*' represents all verbs. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - verbs - type: object - type: array - type: object - status: - description: TeamRoleStatus defines the observed state of a TeamRole - type: object - type: object - TeamMembership: - xml: - name: greenhouse.sap - namespace: v1alpha1 - title: TeamMembership - description: TeamMembership is the Schema for the teammemberships API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents.\nServers may infer this from the endpoint the client submits requests to.\nCannot be updated.\nIn CamelCase.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: TeamMembershipSpec defines the desired state of TeamMembership - properties: - members: - description: Members list users that are part of a team. - items: - description: User specifies a human person. - properties: - email: - description: Email of the user. - type: string - firstName: - description: FirstName of the user. - type: string - id: - description: ID is the unique identifier of the user. - type: string - lastName: - description: LastName of the user. - type: string - required: - - email - - firstName - - id - - lastName - type: object - type: array type: object status: - description: TeamMembershipStatus defines the observed state of TeamMembership properties: - lastSyncedTime: - description: LastSyncedTime is the information when was the last time the membership was synced - format: date-time - type: string - lastUpdateTime: - description: LastChangedTime is the information when was the last time the membership was actually changed - format: date-time - type: string statusConditions: - description: StatusConditions contain the different conditions that constitute the status of the TeamMembership. + description: A StatusConditions contains a list of conditions.\nOnly one condition of a given type may exist in the list. properties: conditions: items: @@ -344,12 +381,12 @@ components: type: object type: object type: object - Organization: + Cluster: xml: name: greenhouse.sap namespace: v1alpha1 - title: Organization - description: Organization is the Schema for the organizations API + title: Cluster + description: Cluster is the Schema for the clusters API properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' @@ -360,203 +397,15 @@ components: metadata: type: object spec: - description: OrganizationSpec defines the desired state of Organization + description: ClusterSpec defines the desired state of the Cluster. properties: - authentication: - description: Authentication configures the organizations authentication mechanism. - properties: - oidc: - description: OIDConfig configures the OIDC provider. - properties: - clientIDReference: - description: ClientIDReference references the Kubernetes secret containing the client id. - properties: - key: - description: Key in the secret to select the value from. - type: string - name: - description: Name of the secret in the same namespace. - type: string - required: - - key - - name - type: object - clientSecretReference: - description: ClientSecretReference references the Kubernetes secret containing the client secret. - properties: - key: - description: Key in the secret to select the value from. - type: string - name: - description: Name of the secret in the same namespace. - type: string - required: - - key - - name - type: object - issuer: - description: Issuer is the URL of the identity service. - type: string - redirectURI: - description: RedirectURI is the redirect URI.\nIf none is specified, the Greenhouse ID proxy will be used. - type: string - required: - - clientIDReference - - clientSecretReference - - issuer - type: object - scim: - description: SCIMConfig configures the SCIM client. - properties: - authType: - default: basic - description: AuthType defined possible authentication type - enum: - - basic - - token - type: string - baseURL: - description: URL to the SCIM server. - type: string - basicAuthPw: - description: Password to be used for basic authentication. - properties: - secret: - description: Secret references the secret containing the value. - properties: - key: - description: Key in the secret to select the value from. - type: string - name: - description: Name of the secret in the same namespace. - type: string - required: - - key - - name - type: object - type: object - basicAuthUser: - description: User to be used for basic authentication. - properties: - secret: - description: Secret references the secret containing the value. - properties: - key: - description: Key in the secret to select the value from. - type: string - name: - description: Name of the secret in the same namespace. - type: string - required: - - key - - name - type: object - type: object - bearerHeader: - default: Authorization - description: BearerHeader to be used to defined bearer token header - type: string - bearerPrefix: - default: Bearer - description: BearerPrefix to be used to defined bearer token prefix - type: string - bearerToken: - description: BearerToken to be used for bearer token authorization - properties: - secret: - description: Secret references the secret containing the value. - properties: - key: - description: Key in the secret to select the value from. - type: string - name: - description: Name of the secret in the same namespace. - type: string - required: - - key - - name - type: object - type: object - required: - - baseURL - - basicAuthPw - - basicAuthUser - - bearerToken - type: object - type: object - description: - description: Description provides additional details of the organization. - type: string - displayName: - description: DisplayName is an optional name for the organization to be displayed in the Greenhouse UI.\nDefaults to a normalized version of metadata.name. - type: string - mappedOrgAdminIdPGroup: - description: MappedOrgAdminIDPGroup is the IDP group ID identifying org admins - type: string - type: object - status: - description: OrganizationStatus defines the observed state of an Organization - properties: - statusConditions: - description: StatusConditions contain the different conditions that constitute the status of the Organization. - properties: - conditions: - items: - description: Condition contains additional information on the state of a resource. - properties: - lastTransitionTime: - description: LastTransitionTime is the last time the condition transitioned from one status to another. - format: date-time - type: string - message: - description: Message is an optional human readable message indicating details about the last transition. - type: string - reason: - description: Reason is a one-word, CamelCase reason for the condition's last transition. - type: string - status: - description: Status of the condition. - type: string - type: - description: Type of the condition. - type: string - required: - - lastTransitionTime - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - type: object - type: object - type: object - Cluster: - xml: - name: greenhouse.sap - namespace: v1alpha1 - title: Cluster - description: Cluster is the Schema for the clusters API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents.\nServers may infer this from the endpoint the client submits requests to.\nCannot be updated.\nIn CamelCase.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: ClusterSpec defines the desired state of the Cluster. - properties: - accessMode: - description: AccessMode configures how the cluster is accessed from the Greenhouse operator. - enum: - - direct - type: string - kubeConfig: - description: KubeConfig contains specific values for `KubeConfig` for the cluster. + accessMode: + description: AccessMode configures how the cluster is accessed from the Greenhouse operator. + enum: + - direct + type: string + kubeConfig: + description: KubeConfig contains specific values for `KubeConfig` for the cluster. properties: maxTokenValidity: default: 72 @@ -656,12 +505,12 @@ components: type: object type: object type: object - Plugin: + TeamRole: xml: name: greenhouse.sap namespace: v1alpha1 - title: Plugin - description: Plugin is the Schema for the plugins API + title: TeamRole + description: TeamRole is the Schema for the TeamRoles API properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' @@ -672,131 +521,154 @@ components: metadata: type: object spec: - description: PluginSpec defines the desired state of Plugin + description: TeamRoleSpec defines the desired state of a TeamRole properties: - clusterName: - description: ClusterName is the name of the cluster the plugin is deployed to. If not set, the plugin is deployed to the greenhouse cluster. - type: string - disabled: - description: Disabled indicates that the plugin is administratively disabled. - type: boolean - displayName: - description: DisplayName is an optional name for the Plugin to be displayed in the Greenhouse UI.\nThis is especially helpful to distinguish multiple instances of a PluginDefinition in the same context.\nDefaults to a normalized version of metadata.name. - type: string - optionValues: - description: Values are the values for a PluginDefinition instance. - items: - description: PluginOptionValue is the value for a PluginOption. - properties: - name: - description: Name of the values. - type: string - value: - description: Value is the actual value in plain text. - x-kubernetes-preserve-unknown-fields: true - valueFrom: - description: ValueFrom references a potentially confidential value in another source. + aggregationRule: + description: AggregationRule describes how to locate ClusterRoles to aggregate into the ClusterRole on the remote cluster + properties: + clusterRoleSelectors: + description: ClusterRoleSelectors holds a list of selectors which will be used to find ClusterRoles and create the rules.\nIf any of the selectors match, then the ClusterRole's permissions will be added + items: + description: A label selector is a label query over a set of resources. The result of matchLabels and\nmatchExpressions are ANDed. An empty label selector matches all objects. A null\nlabel selector matches no objects. properties: - secret: - description: Secret references the secret containing the value. - properties: - key: - description: Key in the secret to select the value from. - type: string - name: - description: Name of the secret in the same namespace. - type: string - required: - - key - - name + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that\nrelates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: operator represents a key's relationship to a set of values.\nValid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. If the operator is In or NotIn,\nthe values array must be non-empty. If the operator is Exists or DoesNotExist,\nthe values array must be empty. This array is replaced during a strategic\nmerge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels\nmap is equivalent to an element of matchExpressions, whose key field is "key", the\noperator is "In", and the values array contains only "value". The requirements are ANDed. type: object type: object + x-kubernetes-map-type: atomic + type: array + x-kubernetes-list-type: atomic + type: object + labels: + additionalProperties: + type: string + description: Labels are applied to the ClusterRole created on the remote cluster.\nThis allows using TeamRoles as part of AggregationRules by other TeamRoles + type: object + rules: + description: Rules is a list of rbacv1.PolicyRules used on a managed RBAC (Cluster)Role + items: + description: PolicyRule holds information that describes a policy rule, but does not contain information\nabout who the rule applies to or which namespace the rule applies to. + properties: + apiGroups: + description: APIGroups is the name of the APIGroup that contains the resources. If multiple API groups are specified, any action requested against one of\nthe enumerated resources in any API group will be allowed. "" represents the core API group and "*" represents all API groups. + items: + type: string + type: array + x-kubernetes-list-type: atomic + nonResourceURLs: + description: NonResourceURLs is a set of partial urls that a user should have access to. *s are allowed, but only as the full, final step in the path\nSince non-resource URLs are not namespaced, this field is only applicable for ClusterRoles referenced from a ClusterRoleBinding.\nRules can either apply to API resources (such as "pods" or "secrets") or non-resource URL paths (such as "/api"), but not both. + items: + type: string + type: array + x-kubernetes-list-type: atomic + resourceNames: + description: ResourceNames is an optional white list of names that the rule applies to. An empty set means that everything is allowed. + items: + type: string + type: array + x-kubernetes-list-type: atomic + resources: + description: Resources is a list of resources this rule applies to. '*' represents all resources. + items: + type: string + type: array + x-kubernetes-list-type: atomic + verbs: + description: Verbs is a list of Verbs that apply to ALL the ResourceKinds contained in this rule. '*' represents all verbs. + items: + type: string + type: array + x-kubernetes-list-type: atomic required: - - name + - verbs type: object type: array - pluginDefinition: - description: PluginDefinition is the name of the PluginDefinition this instance is for. - type: string - releaseNamespace: - description: ReleaseNamespace is the namespace in the remote cluster to which the backend is deployed.\nDefaults to the Greenhouse managed namespace if not set. - type: string - required: - - disabled - - pluginDefinition type: object status: - description: PluginStatus defines the observed state of Plugin + description: TeamRoleStatus defines the observed state of a TeamRole + type: object + type: object + TeamMembership: + xml: + name: greenhouse.sap + namespace: v1alpha1 + title: TeamMembership + description: TeamMembership is the Schema for the teammemberships API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents.\nServers may infer this from the endpoint the client submits requests to.\nCannot be updated.\nIn CamelCase.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: TeamMembershipSpec defines the desired state of TeamMembership properties: - description: - description: Description provides additional details of the plugin. - type: string - exposedServices: - additionalProperties: - description: Service references a Kubernetes service of a Plugin. + members: + description: Members list users that are part of a team. + items: + description: User specifies a human person. properties: - name: - description: Name is the name of the service in the target cluster. + email: + description: Email of the user. type: string - namespace: - description: Namespace is the namespace of the service in the target cluster. + firstName: + description: FirstName of the user. type: string - port: - description: Port is the port of the service. - format: int32 - type: integer - protocol: - description: Protocol is the protocol of the service. + id: + description: ID is the unique identifier of the user. + type: string + lastName: + description: LastName of the user. type: string required: - - name - - namespace - - port + - email + - firstName + - id + - lastName type: object - description: ExposedServices provides an overview of the Plugins services that are centrally exposed.\nIt maps the exposed URL to the service found in the manifest. - type: object - helmChart: - description: HelmChart contains a reference the helm chart used for the deployed pluginDefinition version. - properties: - name: - description: Name of the HelmChart chart. - type: string - repository: - description: Repository of the HelmChart chart. - type: string - version: - description: Version of the HelmChart chart. - type: string - required: - - name - - repository - - version - type: object - helmReleaseStatus: - description: HelmReleaseStatus reflects the status of the latest HelmChart release.\nThis is only configured if the pluginDefinition is backed by HelmChart. - properties: - diff: - description: Diff contains the difference between the deployed helm chart and the helm chart in the last reconciliation - type: string - firstDeployed: - description: FirstDeployed is the timestamp of the first deployment of the release. - format: date-time - type: string - lastDeployed: - description: LastDeployed is the timestamp of the last deployment of the release. - format: date-time - type: string - pluginOptionChecksum: - description: PluginOptionChecksum is the checksum of plugin option values. - type: string - status: - description: Status is the status of a HelmChart release. - type: string - required: - - status - type: object + type: array + type: object + status: + description: TeamMembershipStatus defines the observed state of TeamMembership + properties: + lastSyncedTime: + description: LastSyncedTime is the information when was the last time the membership was synced + format: date-time + type: string + lastUpdateTime: + description: LastChangedTime is the information when was the last time the membership was actually changed + format: date-time + type: string statusConditions: - description: StatusConditions contain the different conditions that constitute the status of the Plugin. + description: StatusConditions contain the different conditions that constitute the status of the TeamMembership. properties: conditions: items: @@ -828,37 +700,14 @@ components: - type x-kubernetes-list-type: map type: object - uiApplication: - description: UIApplication contains a reference to the frontend that is used for the deployed pluginDefinition version. - properties: - name: - description: Name of the UI application. - type: string - url: - description: URL specifies the url to a built javascript asset.\nBy default, assets are loaded from the Juno asset server using the provided name and version. - type: string - version: - description: Version of the frontend application. - type: string - required: - - name - - version - type: object - version: - description: Version contains the latest pluginDefinition version the config was last applied with successfully. - type: string - weight: - description: Weight configures the order in which Plugins are shown in the Greenhouse UI. - format: int32 - type: integer type: object type: object - PluginDefinition: + PluginPreset: xml: name: greenhouse.sap namespace: v1alpha1 - title: PluginDefinition - description: PluginDefinition is the Schema for the PluginDefinitions API + title: PluginPreset + description: PluginPreset is the Schema for the PluginPresets API properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' @@ -869,207 +718,234 @@ components: metadata: type: object spec: - description: PluginDefinitionSpec defines the desired state of PluginDefinitionSpec + description: PluginPresetSpec defines the desired state of PluginPreset properties: - description: - description: Description provides additional details of the pluginDefinition. - type: string - displayName: - description: DisplayName provides a human-readable label for the pluginDefinition. - type: string - docMarkDownUrl: - description: DocMarkDownUrl specifies the URL to the markdown documentation file for this plugin.\nSource needs to allow all CORS origins. - type: string - helmChart: - description: HelmChart specifies where the Helm Chart for this pluginDefinition can be found. - properties: - name: - description: Name of the HelmChart chart. - type: string - repository: - description: Repository of the HelmChart chart. - type: string - version: - description: Version of the HelmChart chart. - type: string - required: - - name - - repository - - version - type: object - icon: - description: 'Icon specifies the icon to be used for this plugin in the Greenhouse UI.\nIcons can be either:\n- A string representing a juno icon in camel case from this list: https://github.com/sapcc/juno/blob/main/libs/juno-ui-components/src/components/Icon/Icon.component.js#L6-L52\n- A publicly accessible image reference to a .png file. Will be displayed 100x100px' - type: string - options: - description: RequiredValues is a list of values required to create an instance of this PluginDefinition. + clusterOptionOverrides: + description: ClusterOptionOverrides define plugin option values to override by the PluginPreset items: + description: ClusterOptionOverride defines which plugin option should be override in which cluster properties: - default: - description: Default provides a default value for the option - x-kubernetes-preserve-unknown-fields: true - description: - description: Description provides a human-readable text for the value as shown in the UI. - type: string - displayName: - description: DisplayName provides a human-readable label for the configuration option - type: string - name: - description: Name/Key of the config option. - type: string - regex: - description: Regex specifies a match rule for validating configuration options. - type: string - required: - description: Required indicates that this config option is required - type: boolean - type: - description: Type of this configuration option. - enum: - - string - - secret - - bool - - int - - list - - map + clusterName: type: string + overrides: + items: + description: PluginOptionValue is the value for a PluginOption. + properties: + name: + description: Name of the values. + type: string + value: + description: Value is the actual value in plain text. + x-kubernetes-preserve-unknown-fields: true + valueFrom: + description: ValueFrom references a potentially confidential value in another source. + properties: + secret: + description: Secret references the secret containing the value. + properties: + key: + description: Key in the secret to select the value from. + type: string + name: + description: Name of the secret in the same namespace. + type: string + required: + - key + - name + type: object + type: object + required: + - name + type: object + type: array required: - - name - - required - - type + - clusterName + - overrides type: object type: array - uiApplication: - description: UIApplication specifies a reference to a UI application + clusterSelector: + description: ClusterSelector is a label selector to select the clusters the plugin bundle should be deployed to. properties: - name: - description: Name of the UI application. - type: string - url: - description: URL specifies the url to a built javascript asset.\nBy default, assets are loaded from the Juno asset server using the provided name and version. - type: string - version: - description: Version of the frontend application. - type: string - required: - - name - - version - type: object - version: - description: Version of this pluginDefinition - type: string - weight: - description: Weight configures the order in which Plugins are shown in the Greenhouse UI.\nDefaults to alphabetical sorting if not provided or on conflict. - format: int32 - type: integer - required: - - version - type: object - status: - description: PluginDefinitionStatus defines the observed state of PluginDefinition - type: object - type: object - ClusterKubeconfig: - xml: - name: greenhouse.sap - namespace: v1alpha1 - title: ClusterKubeconfig - description: ClusterKubeconfig is the Schema for the clusterkubeconfigs API\nObjectMeta.OwnerReferences is used to link the ClusterKubeconfig to the Cluster\nObjectMeta.Generation is used to detect changes in the ClusterKubeconfig and sync local kubeconfig files\nObjectMeta.Name is designed to be the same with the Cluster name - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents.\nServers may infer this from the endpoint the client submits requests to.\nCannot be updated.\nIn CamelCase.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: ClusterKubeconfigSpec stores the kubeconfig data for the cluster\nThe idea is to use kubeconfig data locally with minimum effort (with local tools or plain kubectl):\nkubectl get cluster-kubeconfig $NAME -o yaml | yq -y .spec.kubeconfig - properties: - kubeconfig: - description: 'ClusterKubeconfigData stores the kubeconfig data ready to use kubectl or other local tooling\nIt is a simplified version of clientcmdapi.Config: https://pkg.go.dev/k8s.io/client-go/tools/clientcmd/api#Config' - properties: - apiVersion: - type: string - clusters: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. items: + description: A label selector requirement is a selector that contains values, a key, and an operator that\nrelates the key and values. properties: - cluster: - properties: - certificate-authority-data: - format: byte - type: string - server: - type: string - type: object - name: + key: + description: key is the label key that the selector applies to. type: string - required: - - cluster - - name - type: object - type: array - contexts: - items: - properties: - context: - properties: - cluster: - type: string - namespace: - type: string - user: - type: string - required: - - cluster - - user - type: object - name: + operator: + description: operator represents a key's relationship to a set of values.\nValid operators are In, NotIn, Exists and DoesNotExist. type: string + values: + description: values is an array of string values. If the operator is In or NotIn,\nthe values array must be non-empty. If the operator is Exists or DoesNotExist,\nthe values array must be empty. This array is replaced during a strategic\nmerge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic required: - - name + - key + - operator type: object type: array - current-context: + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels\nmap is equivalent to an element of matchExpressions, whose key field is "key", the\noperator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + plugin: + description: PluginSpec is the spec of the plugin to be deployed by the PluginPreset. + properties: + clusterName: + description: ClusterName is the name of the cluster the plugin is deployed to. If not set, the plugin is deployed to the greenhouse cluster. type: string - kind: + disabled: + description: Disabled indicates that the plugin is administratively disabled. + type: boolean + displayName: + description: DisplayName is an optional name for the Plugin to be displayed in the Greenhouse UI.\nThis is especially helpful to distinguish multiple instances of a PluginDefinition in the same context.\nDefaults to a normalized version of metadata.name. type: string - preferences: - type: object - users: + optionValues: + description: Values are the values for a PluginDefinition instance. items: + description: PluginOptionValue is the value for a PluginOption. properties: name: + description: Name of the values. type: string - user: + value: + description: Value is the actual value in plain text. + x-kubernetes-preserve-unknown-fields: true + valueFrom: + description: ValueFrom references a potentially confidential value in another source. properties: - auth-provider: - description: AuthProviderConfig holds the configuration for a specified auth provider. + secret: + description: Secret references the secret containing the value. properties: - config: - additionalProperties: - type: string - type: object + key: + description: Key in the secret to select the value from. + type: string name: + description: Name of the secret in the same namespace. type: string required: + - key - name type: object - client-certificate-data: - format: byte - type: string - client-key-data: - format: byte - type: string type: object required: - name type: object type: array + pluginDefinition: + description: PluginDefinition is the name of the PluginDefinition this instance is for. + type: string + releaseNamespace: + description: ReleaseNamespace is the namespace in the remote cluster to which the backend is deployed.\nDefaults to the Greenhouse managed namespace if not set. + type: string + required: + - disabled + - pluginDefinition + type: object + required: + - clusterSelector + - plugin + type: object + status: + description: PluginPresetStatus defines the observed state of PluginPreset + properties: + statusConditions: + description: StatusConditions contain the different conditions that constitute the status of the PluginPreset. + properties: + conditions: + items: + description: Condition contains additional information on the state of a resource. + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition transitioned from one status to another. + format: date-time + type: string + message: + description: Message is an optional human readable message indicating details about the last transition. + type: string + reason: + description: Reason is a one-word, CamelCase reason for the condition's last transition. + type: string + status: + description: Status of the condition. + type: string + type: + description: Type of the condition. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map type: object type: object + type: object + Team: + xml: + name: greenhouse.sap + namespace: v1alpha1 + title: Team + description: Team is the Schema for the teams API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents.\nServers may infer this from the endpoint the client submits requests to.\nCannot be updated.\nIn CamelCase.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: TeamSpec defines the desired state of Team + properties: + description: + description: Description provides additional details of the team. + type: string + joinUrl: + description: URL to join the IdP group. + type: string + mappedIdPGroup: + description: IdP group id matching team. + type: string + type: object status: + description: TeamStatus defines the observed state of Team properties: + members: + items: + description: User specifies a human person. + properties: + email: + description: Email of the user. + type: string + firstName: + description: FirstName of the user. + type: string + id: + description: ID is the unique identifier of the user. + type: string + lastName: + description: LastName of the user. + type: string + required: + - email + - firstName + - id + - lastName + type: object + type: array statusConditions: description: A StatusConditions contains a list of conditions.\nOnly one condition of a given type may exist in the list. properties: @@ -1103,6 +979,8 @@ components: - type x-kubernetes-list-type: map type: object + required: + - statusConditions type: object type: object TeamRoleBinding: @@ -1248,12 +1126,12 @@ components: type: object type: object type: object - PluginPreset: + Plugin: xml: name: greenhouse.sap namespace: v1alpha1 - title: PluginPreset - description: PluginPreset is the Schema for the PluginPresets API + title: Plugin + description: Plugin is the Schema for the plugins API properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' @@ -1264,147 +1142,131 @@ components: metadata: type: object spec: - description: PluginPresetSpec defines the desired state of PluginPreset + description: PluginSpec defines the desired state of Plugin properties: - clusterOptionOverrides: - description: ClusterOptionOverrides define plugin option values to override by the PluginPreset + clusterName: + description: ClusterName is the name of the cluster the plugin is deployed to. If not set, the plugin is deployed to the greenhouse cluster. + type: string + disabled: + description: Disabled indicates that the plugin is administratively disabled. + type: boolean + displayName: + description: DisplayName is an optional name for the Plugin to be displayed in the Greenhouse UI.\nThis is especially helpful to distinguish multiple instances of a PluginDefinition in the same context.\nDefaults to a normalized version of metadata.name. + type: string + optionValues: + description: Values are the values for a PluginDefinition instance. items: - description: ClusterOptionOverride defines which plugin option should be override in which cluster + description: PluginOptionValue is the value for a PluginOption. properties: - clusterName: + name: + description: Name of the values. type: string - overrides: - items: - description: PluginOptionValue is the value for a PluginOption. - properties: - name: - description: Name of the values. - type: string - value: - description: Value is the actual value in plain text. - x-kubernetes-preserve-unknown-fields: true - valueFrom: - description: ValueFrom references a potentially confidential value in another source. - properties: - secret: - description: Secret references the secret containing the value. - properties: - key: - description: Key in the secret to select the value from. - type: string - name: - description: Name of the secret in the same namespace. - type: string - required: - - key - - name - type: object - type: object - required: - - name - type: object - type: array + value: + description: Value is the actual value in plain text. + x-kubernetes-preserve-unknown-fields: true + valueFrom: + description: ValueFrom references a potentially confidential value in another source. + properties: + secret: + description: Secret references the secret containing the value. + properties: + key: + description: Key in the secret to select the value from. + type: string + name: + description: Name of the secret in the same namespace. + type: string + required: + - key + - name + type: object + type: object required: - - clusterName - - overrides + - name type: object type: array - clusterSelector: - description: ClusterSelector is a label selector to select the clusters the plugin bundle should be deployed to. - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - items: - description: A label selector requirement is a selector that contains values, a key, and an operator that\nrelates the key and values. - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: operator represents a key's relationship to a set of values.\nValid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array of string values. If the operator is In or NotIn,\nthe values array must be non-empty. If the operator is Exists or DoesNotExist,\nthe values array must be empty. This array is replaced during a strategic\nmerge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: + pluginDefinition: + description: PluginDefinition is the name of the PluginDefinition this instance is for. + type: string + releaseNamespace: + description: ReleaseNamespace is the namespace in the remote cluster to which the backend is deployed.\nDefaults to the Greenhouse managed namespace if not set. + type: string + required: + - disabled + - pluginDefinition + type: object + status: + description: PluginStatus defines the observed state of Plugin + properties: + description: + description: Description provides additional details of the plugin. + type: string + exposedServices: + additionalProperties: + description: Service references a Kubernetes service of a Plugin. + properties: + name: + description: Name is the name of the service in the target cluster. type: string - description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels\nmap is equivalent to an element of matchExpressions, whose key field is "key", the\noperator is "In", and the values array contains only "value". The requirements are ANDed. - type: object + namespace: + description: Namespace is the namespace of the service in the target cluster. + type: string + port: + description: Port is the port of the service. + format: int32 + type: integer + protocol: + description: Protocol is the protocol of the service. + type: string + required: + - name + - namespace + - port + type: object + description: ExposedServices provides an overview of the Plugins services that are centrally exposed.\nIt maps the exposed URL to the service found in the manifest. type: object - x-kubernetes-map-type: atomic - plugin: - description: PluginSpec is the spec of the plugin to be deployed by the PluginPreset. + helmChart: + description: HelmChart contains a reference the helm chart used for the deployed pluginDefinition version. properties: - clusterName: - description: ClusterName is the name of the cluster the plugin is deployed to. If not set, the plugin is deployed to the greenhouse cluster. + name: + description: Name of the HelmChart chart. type: string - disabled: - description: Disabled indicates that the plugin is administratively disabled. - type: boolean - displayName: - description: DisplayName is an optional name for the Plugin to be displayed in the Greenhouse UI.\nThis is especially helpful to distinguish multiple instances of a PluginDefinition in the same context.\nDefaults to a normalized version of metadata.name. + repository: + description: Repository of the HelmChart chart. type: string - optionValues: - description: Values are the values for a PluginDefinition instance. - items: - description: PluginOptionValue is the value for a PluginOption. - properties: - name: - description: Name of the values. - type: string - value: - description: Value is the actual value in plain text. - x-kubernetes-preserve-unknown-fields: true - valueFrom: - description: ValueFrom references a potentially confidential value in another source. - properties: - secret: - description: Secret references the secret containing the value. - properties: - key: - description: Key in the secret to select the value from. - type: string - name: - description: Name of the secret in the same namespace. - type: string - required: - - key - - name - type: object - type: object - required: - - name - type: object - type: array - pluginDefinition: - description: PluginDefinition is the name of the PluginDefinition this instance is for. + version: + description: Version of the HelmChart chart. type: string - releaseNamespace: - description: ReleaseNamespace is the namespace in the remote cluster to which the backend is deployed.\nDefaults to the Greenhouse managed namespace if not set. + required: + - name + - repository + - version + type: object + helmReleaseStatus: + description: HelmReleaseStatus reflects the status of the latest HelmChart release.\nThis is only configured if the pluginDefinition is backed by HelmChart. + properties: + diff: + description: Diff contains the difference between the deployed helm chart and the helm chart in the last reconciliation + type: string + firstDeployed: + description: FirstDeployed is the timestamp of the first deployment of the release. + format: date-time + type: string + lastDeployed: + description: LastDeployed is the timestamp of the last deployment of the release. + format: date-time + type: string + pluginOptionChecksum: + description: PluginOptionChecksum is the checksum of plugin option values. + type: string + status: + description: Status is the status of a HelmChart release. type: string required: - - disabled - - pluginDefinition + - status type: object - required: - - clusterSelector - - plugin - type: object - status: - description: PluginPresetStatus defines the observed state of PluginPreset - properties: statusConditions: - description: StatusConditions contain the different conditions that constitute the status of the PluginPreset. + description: StatusConditions contain the different conditions that constitute the status of the Plugin. properties: conditions: items: @@ -1436,5 +1298,143 @@ components: - type x-kubernetes-list-type: map type: object + uiApplication: + description: UIApplication contains a reference to the frontend that is used for the deployed pluginDefinition version. + properties: + name: + description: Name of the UI application. + type: string + url: + description: URL specifies the url to a built javascript asset.\nBy default, assets are loaded from the Juno asset server using the provided name and version. + type: string + version: + description: Version of the frontend application. + type: string + required: + - name + - version + type: object + version: + description: Version contains the latest pluginDefinition version the config was last applied with successfully. + type: string + weight: + description: Weight configures the order in which Plugins are shown in the Greenhouse UI. + format: int32 + type: integer + type: object + type: object + PluginDefinition: + xml: + name: greenhouse.sap + namespace: v1alpha1 + title: PluginDefinition + description: PluginDefinition is the Schema for the PluginDefinitions API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents.\nServers may infer this from the endpoint the client submits requests to.\nCannot be updated.\nIn CamelCase.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: PluginDefinitionSpec defines the desired state of PluginDefinitionSpec + properties: + description: + description: Description provides additional details of the pluginDefinition. + type: string + displayName: + description: DisplayName provides a human-readable label for the pluginDefinition. + type: string + docMarkDownUrl: + description: DocMarkDownUrl specifies the URL to the markdown documentation file for this plugin.\nSource needs to allow all CORS origins. + type: string + helmChart: + description: HelmChart specifies where the Helm Chart for this pluginDefinition can be found. + properties: + name: + description: Name of the HelmChart chart. + type: string + repository: + description: Repository of the HelmChart chart. + type: string + version: + description: Version of the HelmChart chart. + type: string + required: + - name + - repository + - version + type: object + icon: + description: 'Icon specifies the icon to be used for this plugin in the Greenhouse UI.\nIcons can be either:\n- A string representing a juno icon in camel case from this list: https://github.com/sapcc/juno/blob/main/libs/juno-ui-components/src/components/Icon/Icon.component.js#L6-L52\n- A publicly accessible image reference to a .png file. Will be displayed 100x100px' + type: string + options: + description: RequiredValues is a list of values required to create an instance of this PluginDefinition. + items: + properties: + default: + description: Default provides a default value for the option + x-kubernetes-preserve-unknown-fields: true + description: + description: Description provides a human-readable text for the value as shown in the UI. + type: string + displayName: + description: DisplayName provides a human-readable label for the configuration option + type: string + name: + description: Name/Key of the config option. + type: string + regex: + description: Regex specifies a match rule for validating configuration options. + type: string + required: + description: Required indicates that this config option is required + type: boolean + type: + description: Type of this configuration option. + enum: + - string + - secret + - bool + - int + - list + - map + type: string + required: + - name + - required + - type + type: object + type: array + uiApplication: + description: UIApplication specifies a reference to a UI application + properties: + name: + description: Name of the UI application. + type: string + url: + description: URL specifies the url to a built javascript asset.\nBy default, assets are loaded from the Juno asset server using the provided name and version. + type: string + version: + description: Version of the frontend application. + type: string + required: + - name + - version + type: object + version: + description: Version of this pluginDefinition + type: string + weight: + description: Weight configures the order in which Plugins are shown in the Greenhouse UI.\nDefaults to alphabetical sorting if not provided or on conflict. + format: int32 + type: integer + required: + - version + type: object + status: + description: PluginDefinitionStatus defines the observed state of PluginDefinition type: object type: object diff --git a/types/typescript/schema.d.ts b/types/typescript/schema.d.ts index 9c9286f87..84bfef2d5 100644 --- a/types/typescript/schema.d.ts +++ b/types/typescript/schema.d.ts @@ -396,6 +396,12 @@ export interface components { }; /** @description SCIMConfig configures the SCIM client. */ scim?: { + /** + * @description AuthType defined possible authentication type + * @default basic + * @enum {string} + */ + authType: "basic" | "token"; /** @description URL to the SCIM server. */ baseURL: string; /** @description Password to be used for basic authentication. */ @@ -418,6 +424,26 @@ export interface components { name: string; }; }; + /** + * @description BearerHeader to be used to defined bearer token header + * @default Authorization + */ + bearerHeader: string; + /** + * @description BearerPrefix to be used to defined bearer token prefix + * @default Bearer + */ + bearerPrefix: string; + /** @description BearerToken to be used for bearer token authorization */ + bearerToken: { + /** @description Secret references the secret containing the value. */ + secret?: { + /** @description Key in the secret to select the value from. */ + key: string; + /** @description Name of the secret in the same namespace. */ + name: string; + }; + }; }; }; /** @description Description provides additional details of the organization. */ @@ -1142,6 +1168,8 @@ export interface components { }; /** @description HelmReleaseStatus reflects the status of the latest HelmChart release.\nThis is only configured if the pluginDefinition is backed by HelmChart. */ helmReleaseStatus?: { + /** @description Diff contains the difference between the deployed helm chart and the helm chart in the last reconciliation */ + diff?: string; /** * Format: date-time * @description FirstDeployed is the timestamp of the first deployment of the release. From 104e5e8cb0f972211a1d96519d2f902ec8037599 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Mon, 24 Feb 2025 15:44:26 +0100 Subject: [PATCH 103/108] (chore): org deletion test --- .../organization/organization_controller_test.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/pkg/controllers/organization/organization_controller_test.go b/pkg/controllers/organization/organization_controller_test.go index 312aa2a62..9e4c303ac 100644 --- a/pkg/controllers/organization/organization_controller_test.go +++ b/pkg/controllers/organization/organization_controller_test.go @@ -355,6 +355,14 @@ var _ = Describe("Test Organization reconciliation", Ordered, func() { ownerRef = clientutil.GetOwnerReference(&filteredOrgClient[0], "Organization") Expect(ownerRef).ToNot(BeNil(), "there should be an owner reference for the dex oauth client") Expect(ownerRef.Name).To(Equal(oidcOrgName), "the owner reference should have the correct name") + + By("deleting the organizations") + test.EventuallyDeleted(test.Ctx, test.K8sClient, &greenhousev1alpha1.Organization{ObjectMeta: metav1.ObjectMeta{Name: oidcOrgName}}) + test.EventuallyDeleted(test.Ctx, test.K8sClient, &greenhousev1alpha1.Organization{ObjectMeta: metav1.ObjectMeta{Name: greenhouseOrgName}}) + + // we cannot test the deletion of dex resources because of the local of controller-manager in the test environment + // since we assert the ownerReferences exist in the above test, it is safe to assume that the dex resources will be deleted + // controller-runtime issue: https://github.com/kubernetes-sigs/controller-runtime/issues/626#issuecomment-538529534 } }) }) From 94cbbd14c7de03765add35adf0485f7628976a89 Mon Sep 17 00:00:00 2001 From: Cloud Operator <169066274+cloud-operator@users.noreply.github.com> Date: Mon, 24 Feb 2025 14:46:06 +0000 Subject: [PATCH 104/108] Automatic generation of CRD API Docs --- docs/reference/api/openapi.yaml | 1460 +++++++++++++++---------------- 1 file changed, 730 insertions(+), 730 deletions(-) diff --git a/docs/reference/api/openapi.yaml b/docs/reference/api/openapi.yaml index 824b101d1..d7884cace 100755 --- a/docs/reference/api/openapi.yaml +++ b/docs/reference/api/openapi.yaml @@ -4,64 +4,64 @@ info: version: main description: PlusOne operations platform paths: - /Organization: + /Team: post: responses: default: - description: Organization - /ClusterKubeconfig: + description: Team + /TeamRole: post: responses: default: - description: ClusterKubeconfig - /Cluster: + description: TeamRole + /TeamMembership: post: responses: default: - description: Cluster - /TeamRole: + description: TeamMembership + /Organization: post: responses: default: - description: TeamRole - /TeamMembership: + description: Organization + /Cluster: post: responses: default: - description: TeamMembership - /PluginPreset: + description: Cluster + /Plugin: post: responses: default: - description: PluginPreset - /Team: + description: Plugin + /PluginDefinition: post: responses: default: - description: Team - /TeamRoleBinding: + description: PluginDefinition + /ClusterKubeconfig: post: responses: default: - description: TeamRoleBinding - /Plugin: + description: ClusterKubeconfig + /TeamRoleBinding: post: responses: default: - description: Plugin - /PluginDefinition: + description: TeamRoleBinding + /PluginPreset: post: responses: default: - description: PluginDefinition + description: PluginPreset components: schemas: - Organization: + Team: xml: name: greenhouse.sap namespace: v1alpha1 - title: Organization - description: Organization is the Schema for the organizations API + title: Team + description: Team is the Schema for the teams API properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' @@ -72,145 +72,46 @@ components: metadata: type: object spec: - description: OrganizationSpec defines the desired state of Organization + description: TeamSpec defines the desired state of Team properties: - authentication: - description: Authentication configures the organizations authentication mechanism. - properties: - oidc: - description: OIDConfig configures the OIDC provider. - properties: - clientIDReference: - description: ClientIDReference references the Kubernetes secret containing the client id. - properties: - key: - description: Key in the secret to select the value from. - type: string - name: - description: Name of the secret in the same namespace. - type: string - required: - - key - - name - type: object - clientSecretReference: - description: ClientSecretReference references the Kubernetes secret containing the client secret. - properties: - key: - description: Key in the secret to select the value from. - type: string - name: - description: Name of the secret in the same namespace. - type: string - required: - - key - - name - type: object - issuer: - description: Issuer is the URL of the identity service. - type: string - redirectURI: - description: RedirectURI is the redirect URI.\nIf none is specified, the Greenhouse ID proxy will be used. - type: string - required: - - clientIDReference - - clientSecretReference - - issuer - type: object - scim: - description: SCIMConfig configures the SCIM client. - properties: - authType: - default: basic - description: AuthType defined possible authentication type - enum: - - basic - - token - type: string - baseURL: - description: URL to the SCIM server. - type: string - basicAuthPw: - description: Password to be used for basic authentication. - properties: - secret: - description: Secret references the secret containing the value. - properties: - key: - description: Key in the secret to select the value from. - type: string - name: - description: Name of the secret in the same namespace. - type: string - required: - - key - - name - type: object - type: object - basicAuthUser: - description: User to be used for basic authentication. - properties: - secret: - description: Secret references the secret containing the value. - properties: - key: - description: Key in the secret to select the value from. - type: string - name: - description: Name of the secret in the same namespace. - type: string - required: - - key - - name - type: object - type: object - bearerHeader: - default: Authorization - description: BearerHeader to be used to defined bearer token header - type: string - bearerPrefix: - default: Bearer - description: BearerPrefix to be used to defined bearer token prefix - type: string - bearerToken: - description: BearerToken to be used for bearer token authorization - properties: - secret: - description: Secret references the secret containing the value. - properties: - key: - description: Key in the secret to select the value from. - type: string - name: - description: Name of the secret in the same namespace. - type: string - required: - - key - - name - type: object - type: object - required: - - baseURL - - basicAuthPw - - basicAuthUser - - bearerToken - type: object - type: object description: - description: Description provides additional details of the organization. + description: Description provides additional details of the team. type: string - displayName: - description: DisplayName is an optional name for the organization to be displayed in the Greenhouse UI.\nDefaults to a normalized version of metadata.name. + joinUrl: + description: URL to join the IdP group. type: string - mappedOrgAdminIdPGroup: - description: MappedOrgAdminIDPGroup is the IDP group ID identifying org admins + mappedIdPGroup: + description: IdP group id matching team. type: string type: object status: - description: OrganizationStatus defines the observed state of an Organization + description: TeamStatus defines the observed state of Team properties: + members: + items: + description: User specifies a human person. + properties: + email: + description: Email of the user. + type: string + firstName: + description: FirstName of the user. + type: string + id: + description: ID is the unique identifier of the user. + type: string + lastName: + description: LastName of the user. + type: string + required: + - email + - firstName + - id + - lastName + type: object + type: array statusConditions: - description: StatusConditions contain the different conditions that constitute the status of the Organization. + description: A StatusConditions contains a list of conditions.\nOnly one condition of a given type may exist in the list. properties: conditions: items: @@ -242,14 +143,16 @@ components: - type x-kubernetes-list-type: map type: object + required: + - statusConditions type: object type: object - ClusterKubeconfig: + TeamRole: xml: name: greenhouse.sap namespace: v1alpha1 - title: ClusterKubeconfig - description: ClusterKubeconfig is the Schema for the clusterkubeconfigs API\nObjectMeta.OwnerReferences is used to link the ClusterKubeconfig to the Cluster\nObjectMeta.Generation is used to detect changes in the ClusterKubeconfig and sync local kubeconfig files\nObjectMeta.Name is designed to be the same with the Cluster name + title: TeamRole + description: TeamRole is the Schema for the TeamRoles API properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' @@ -260,276 +163,15 @@ components: metadata: type: object spec: - description: ClusterKubeconfigSpec stores the kubeconfig data for the cluster\nThe idea is to use kubeconfig data locally with minimum effort (with local tools or plain kubectl):\nkubectl get cluster-kubeconfig $NAME -o yaml | yq -y .spec.kubeconfig + description: TeamRoleSpec defines the desired state of a TeamRole properties: - kubeconfig: - description: 'ClusterKubeconfigData stores the kubeconfig data ready to use kubectl or other local tooling\nIt is a simplified version of clientcmdapi.Config: https://pkg.go.dev/k8s.io/client-go/tools/clientcmd/api#Config' + aggregationRule: + description: AggregationRule describes how to locate ClusterRoles to aggregate into the ClusterRole on the remote cluster properties: - apiVersion: - type: string - clusters: - items: - properties: - cluster: - properties: - certificate-authority-data: - format: byte - type: string - server: - type: string - type: object - name: - type: string - required: - - cluster - - name - type: object - type: array - contexts: + clusterRoleSelectors: + description: ClusterRoleSelectors holds a list of selectors which will be used to find ClusterRoles and create the rules.\nIf any of the selectors match, then the ClusterRole's permissions will be added items: - properties: - context: - properties: - cluster: - type: string - namespace: - type: string - user: - type: string - required: - - cluster - - user - type: object - name: - type: string - required: - - name - type: object - type: array - current-context: - type: string - kind: - type: string - preferences: - type: object - users: - items: - properties: - name: - type: string - user: - properties: - auth-provider: - description: AuthProviderConfig holds the configuration for a specified auth provider. - properties: - config: - additionalProperties: - type: string - type: object - name: - type: string - required: - - name - type: object - client-certificate-data: - format: byte - type: string - client-key-data: - format: byte - type: string - type: object - required: - - name - type: object - type: array - type: object - type: object - status: - properties: - statusConditions: - description: A StatusConditions contains a list of conditions.\nOnly one condition of a given type may exist in the list. - properties: - conditions: - items: - description: Condition contains additional information on the state of a resource. - properties: - lastTransitionTime: - description: LastTransitionTime is the last time the condition transitioned from one status to another. - format: date-time - type: string - message: - description: Message is an optional human readable message indicating details about the last transition. - type: string - reason: - description: Reason is a one-word, CamelCase reason for the condition's last transition. - type: string - status: - description: Status of the condition. - type: string - type: - description: Type of the condition. - type: string - required: - - lastTransitionTime - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - type: object - type: object - type: object - Cluster: - xml: - name: greenhouse.sap - namespace: v1alpha1 - title: Cluster - description: Cluster is the Schema for the clusters API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents.\nServers may infer this from the endpoint the client submits requests to.\nCannot be updated.\nIn CamelCase.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: ClusterSpec defines the desired state of the Cluster. - properties: - accessMode: - description: AccessMode configures how the cluster is accessed from the Greenhouse operator. - enum: - - direct - type: string - kubeConfig: - description: KubeConfig contains specific values for `KubeConfig` for the cluster. - properties: - maxTokenValidity: - default: 72 - description: MaxTokenValidity specifies the maximum duration for which a token remains valid in hours. - format: int32 - maximum: 72 - minimum: 24 - type: integer - type: object - required: - - accessMode - type: object - status: - description: ClusterStatus defines the observed state of Cluster - properties: - bearerTokenExpirationTimestamp: - description: BearerTokenExpirationTimestamp reflects the expiration timestamp of the bearer token used to access the cluster. - format: date-time - type: string - kubernetesVersion: - description: KubernetesVersion reflects the detected Kubernetes version of the cluster. - type: string - nodes: - additionalProperties: - properties: - ready: - description: Fast track to the node ready condition. - type: boolean - statusConditions: - description: We mirror the node conditions here for faster reference - properties: - conditions: - items: - description: Condition contains additional information on the state of a resource. - properties: - lastTransitionTime: - description: LastTransitionTime is the last time the condition transitioned from one status to another. - format: date-time - type: string - message: - description: Message is an optional human readable message indicating details about the last transition. - type: string - reason: - description: Reason is a one-word, CamelCase reason for the condition's last transition. - type: string - status: - description: Status of the condition. - type: string - type: - description: Type of the condition. - type: string - required: - - lastTransitionTime - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - type: object - type: object - description: Nodes provides a map of cluster node names to node statuses - type: object - statusConditions: - description: StatusConditions contain the different conditions that constitute the status of the Cluster. - properties: - conditions: - items: - description: Condition contains additional information on the state of a resource. - properties: - lastTransitionTime: - description: LastTransitionTime is the last time the condition transitioned from one status to another. - format: date-time - type: string - message: - description: Message is an optional human readable message indicating details about the last transition. - type: string - reason: - description: Reason is a one-word, CamelCase reason for the condition's last transition. - type: string - status: - description: Status of the condition. - type: string - type: - description: Type of the condition. - type: string - required: - - lastTransitionTime - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map - type: object - type: object - type: object - TeamRole: - xml: - name: greenhouse.sap - namespace: v1alpha1 - title: TeamRole - description: TeamRole is the Schema for the TeamRoles API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents.\nServers may infer this from the endpoint the client submits requests to.\nCannot be updated.\nIn CamelCase.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: - type: object - spec: - description: TeamRoleSpec defines the desired state of a TeamRole - properties: - aggregationRule: - description: AggregationRule describes how to locate ClusterRoles to aggregate into the ClusterRole on the remote cluster - properties: - clusterRoleSelectors: - description: ClusterRoleSelectors holds a list of selectors which will be used to find ClusterRoles and create the rules.\nIf any of the selectors match, then the ClusterRole's permissions will be added - items: - description: A label selector is a label query over a set of resources. The result of matchLabels and\nmatchExpressions are ANDed. An empty label selector matches all objects. A null\nlabel selector matches no objects. + description: A label selector is a label query over a set of resources. The result of matchLabels and\nmatchExpressions are ANDed. An empty label selector matches all objects. A null\nlabel selector matches no objects. properties: matchExpressions: description: matchExpressions is a list of label selector requirements. The requirements are ANDed. @@ -702,12 +344,12 @@ components: type: object type: object type: object - PluginPreset: + Organization: xml: name: greenhouse.sap namespace: v1alpha1 - title: PluginPreset - description: PluginPreset is the Schema for the PluginPresets API + title: Organization + description: Organization is the Schema for the organizations API properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' @@ -718,147 +360,145 @@ components: metadata: type: object spec: - description: PluginPresetSpec defines the desired state of PluginPreset + description: OrganizationSpec defines the desired state of Organization properties: - clusterOptionOverrides: - description: ClusterOptionOverrides define plugin option values to override by the PluginPreset - items: - description: ClusterOptionOverride defines which plugin option should be override in which cluster - properties: - clusterName: - type: string - overrides: - items: - description: PluginOptionValue is the value for a PluginOption. - properties: - name: - description: Name of the values. - type: string - value: - description: Value is the actual value in plain text. - x-kubernetes-preserve-unknown-fields: true - valueFrom: - description: ValueFrom references a potentially confidential value in another source. - properties: - secret: - description: Secret references the secret containing the value. - properties: - key: - description: Key in the secret to select the value from. - type: string - name: - description: Name of the secret in the same namespace. - type: string - required: - - key - - name - type: object - type: object + authentication: + description: Authentication configures the organizations authentication mechanism. + properties: + oidc: + description: OIDConfig configures the OIDC provider. + properties: + clientIDReference: + description: ClientIDReference references the Kubernetes secret containing the client id. + properties: + key: + description: Key in the secret to select the value from. + type: string + name: + description: Name of the secret in the same namespace. + type: string required: + - key - name type: object - type: array - required: - - clusterName - - overrides - type: object - type: array - clusterSelector: - description: ClusterSelector is a label selector to select the clusters the plugin bundle should be deployed to. - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - items: - description: A label selector requirement is a selector that contains values, a key, and an operator that\nrelates the key and values. - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: operator represents a key's relationship to a set of values.\nValid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array of string values. If the operator is In or NotIn,\nthe values array must be non-empty. If the operator is Exists or DoesNotExist,\nthe values array must be empty. This array is replaced during a strategic\nmerge patch. - items: + clientSecretReference: + description: ClientSecretReference references the Kubernetes secret containing the client secret. + properties: + key: + description: Key in the secret to select the value from. type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels\nmap is equivalent to an element of matchExpressions, whose key field is "key", the\noperator is "In", and the values array contains only "value". The requirements are ANDed. + name: + description: Name of the secret in the same namespace. + type: string + required: + - key + - name + type: object + issuer: + description: Issuer is the URL of the identity service. + type: string + redirectURI: + description: RedirectURI is the redirect URI.\nIf none is specified, the Greenhouse ID proxy will be used. + type: string + required: + - clientIDReference + - clientSecretReference + - issuer + type: object + scim: + description: SCIMConfig configures the SCIM client. + properties: + authType: + default: basic + description: AuthType defined possible authentication type + enum: + - basic + - token + type: string + baseURL: + description: URL to the SCIM server. + type: string + basicAuthPw: + description: Password to be used for basic authentication. + properties: + secret: + description: Secret references the secret containing the value. + properties: + key: + description: Key in the secret to select the value from. + type: string + name: + description: Name of the secret in the same namespace. + type: string + required: + - key + - name + type: object + type: object + basicAuthUser: + description: User to be used for basic authentication. + properties: + secret: + description: Secret references the secret containing the value. + properties: + key: + description: Key in the secret to select the value from. + type: string + name: + description: Name of the secret in the same namespace. + type: string + required: + - key + - name + type: object + type: object + bearerHeader: + default: Authorization + description: BearerHeader to be used to defined bearer token header + type: string + bearerPrefix: + default: Bearer + description: BearerPrefix to be used to defined bearer token prefix + type: string + bearerToken: + description: BearerToken to be used for bearer token authorization + properties: + secret: + description: Secret references the secret containing the value. + properties: + key: + description: Key in the secret to select the value from. + type: string + name: + description: Name of the secret in the same namespace. + type: string + required: + - key + - name + type: object + type: object + required: + - baseURL + - basicAuthPw + - basicAuthUser + - bearerToken type: object type: object - x-kubernetes-map-type: atomic - plugin: - description: PluginSpec is the spec of the plugin to be deployed by the PluginPreset. - properties: - clusterName: - description: ClusterName is the name of the cluster the plugin is deployed to. If not set, the plugin is deployed to the greenhouse cluster. - type: string - disabled: - description: Disabled indicates that the plugin is administratively disabled. - type: boolean - displayName: - description: DisplayName is an optional name for the Plugin to be displayed in the Greenhouse UI.\nThis is especially helpful to distinguish multiple instances of a PluginDefinition in the same context.\nDefaults to a normalized version of metadata.name. - type: string - optionValues: - description: Values are the values for a PluginDefinition instance. - items: - description: PluginOptionValue is the value for a PluginOption. - properties: - name: - description: Name of the values. - type: string - value: - description: Value is the actual value in plain text. - x-kubernetes-preserve-unknown-fields: true - valueFrom: - description: ValueFrom references a potentially confidential value in another source. - properties: - secret: - description: Secret references the secret containing the value. - properties: - key: - description: Key in the secret to select the value from. - type: string - name: - description: Name of the secret in the same namespace. - type: string - required: - - key - - name - type: object - type: object - required: - - name - type: object - type: array - pluginDefinition: - description: PluginDefinition is the name of the PluginDefinition this instance is for. - type: string - releaseNamespace: - description: ReleaseNamespace is the namespace in the remote cluster to which the backend is deployed.\nDefaults to the Greenhouse managed namespace if not set. - type: string - required: - - disabled - - pluginDefinition - type: object - required: - - clusterSelector - - plugin + description: + description: Description provides additional details of the organization. + type: string + displayName: + description: DisplayName is an optional name for the organization to be displayed in the Greenhouse UI.\nDefaults to a normalized version of metadata.name. + type: string + mappedOrgAdminIdPGroup: + description: MappedOrgAdminIDPGroup is the IDP group ID identifying org admins + type: string type: object status: - description: PluginPresetStatus defines the observed state of PluginPreset + description: OrganizationStatus defines the observed state of an Organization properties: statusConditions: - description: StatusConditions contain the different conditions that constitute the status of the PluginPreset. + description: StatusConditions contain the different conditions that constitute the status of the Organization. properties: conditions: items: @@ -892,12 +532,12 @@ components: type: object type: object type: object - Team: + Cluster: xml: name: greenhouse.sap namespace: v1alpha1 - title: Team - description: Team is the Schema for the teams API + title: Cluster + description: Cluster is the Schema for the clusters API properties: apiVersion: description: 'APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' @@ -908,191 +548,81 @@ components: metadata: type: object spec: - description: TeamSpec defines the desired state of Team + description: ClusterSpec defines the desired state of the Cluster. properties: - description: - description: Description provides additional details of the team. + accessMode: + description: AccessMode configures how the cluster is accessed from the Greenhouse operator. + enum: + - direct type: string - joinUrl: - description: URL to join the IdP group. - type: string - mappedIdPGroup: - description: IdP group id matching team. - type: string - type: object - status: - description: TeamStatus defines the observed state of Team - properties: - members: - items: - description: User specifies a human person. - properties: - email: - description: Email of the user. - type: string - firstName: - description: FirstName of the user. - type: string - id: - description: ID is the unique identifier of the user. - type: string - lastName: - description: LastName of the user. - type: string - required: - - email - - firstName - - id - - lastName - type: object - type: array - statusConditions: - description: A StatusConditions contains a list of conditions.\nOnly one condition of a given type may exist in the list. + kubeConfig: + description: KubeConfig contains specific values for `KubeConfig` for the cluster. properties: - conditions: - items: - description: Condition contains additional information on the state of a resource. - properties: - lastTransitionTime: - description: LastTransitionTime is the last time the condition transitioned from one status to another. - format: date-time - type: string - message: - description: Message is an optional human readable message indicating details about the last transition. - type: string - reason: - description: Reason is a one-word, CamelCase reason for the condition's last transition. - type: string - status: - description: Status of the condition. - type: string - type: - description: Type of the condition. - type: string - required: - - lastTransitionTime - - status - - type - type: object - type: array - x-kubernetes-list-map-keys: - - type - x-kubernetes-list-type: map + maxTokenValidity: + default: 72 + description: MaxTokenValidity specifies the maximum duration for which a token remains valid in hours. + format: int32 + maximum: 72 + minimum: 24 + type: integer type: object required: - - statusConditions - type: object - type: object - TeamRoleBinding: - xml: - name: greenhouse.sap - namespace: v1alpha1 - title: TeamRoleBinding - description: TeamRoleBinding is the Schema for the rolebindings API - properties: - apiVersion: - description: 'APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' - type: string - kind: - description: 'Kind is a string value representing the REST resource this object represents.\nServers may infer this from the endpoint the client submits requests to.\nCannot be updated.\nIn CamelCase.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' - type: string - metadata: + - accessMode type: object - spec: - description: TeamRoleBindingSpec defines the desired state of a TeamRoleBinding + status: + description: ClusterStatus defines the observed state of Cluster properties: - clusterName: - description: ClusterName is the name of the cluster the rbacv1 resources are created on. - type: string - clusterSelector: - description: ClusterSelector is a label selector to select the Clusters the TeamRoleBinding should be deployed to. - properties: - matchExpressions: - description: matchExpressions is a list of label selector requirements. The requirements are ANDed. - items: - description: A label selector requirement is a selector that contains values, a key, and an operator that\nrelates the key and values. - properties: - key: - description: key is the label key that the selector applies to. - type: string - operator: - description: operator represents a key's relationship to a set of values.\nValid operators are In, NotIn, Exists and DoesNotExist. - type: string - values: - description: values is an array of string values. If the operator is In or NotIn,\nthe values array must be non-empty. If the operator is Exists or DoesNotExist,\nthe values array must be empty. This array is replaced during a strategic\nmerge patch. - items: - type: string - type: array - x-kubernetes-list-type: atomic - required: - - key - - operator - type: object - type: array - x-kubernetes-list-type: atomic - matchLabels: - additionalProperties: - type: string - description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels\nmap is equivalent to an element of matchExpressions, whose key field is "key", the\noperator is "In", and the values array contains only "value". The requirements are ANDed. - type: object - type: object - x-kubernetes-map-type: atomic - namespaces: - description: Namespaces is a list of namespaces in the Greenhouse Clusters to apply the RoleBinding to.\nIf empty, a ClusterRoleBinding will be created on the remote cluster, otherwise a RoleBinding per namespace. - items: - type: string - type: array - teamRef: - description: TeamRef references a Greenhouse Team by name + bearerTokenExpirationTimestamp: + description: BearerTokenExpirationTimestamp reflects the expiration timestamp of the bearer token used to access the cluster. + format: date-time type: string - teamRoleRef: - description: TeamRoleRef references a Greenhouse TeamRole by name + kubernetesVersion: + description: KubernetesVersion reflects the detected Kubernetes version of the cluster. type: string - type: object - status: - description: TeamRoleBindingStatus defines the observed state of the TeamRoleBinding - properties: - clusters: - description: PropagationStatus is the list of clusters the TeamRoleBinding is applied to - items: - description: PropagationStatus defines the observed state of the TeamRoleBinding's associated rbacv1 resources on a Cluster + nodes: + additionalProperties: properties: - clusterName: - description: ClusterName is the name of the cluster the rbacv1 resources are created on. - type: string - condition: - description: Condition is the overall Status of the rbacv1 resources created on the cluster + ready: + description: Fast track to the node ready condition. + type: boolean + statusConditions: + description: We mirror the node conditions here for faster reference properties: - lastTransitionTime: - description: LastTransitionTime is the last time the condition transitioned from one status to another. - format: date-time - type: string - message: - description: Message is an optional human readable message indicating details about the last transition. - type: string - reason: - description: Reason is a one-word, CamelCase reason for the condition's last transition. - type: string - status: - description: Status of the condition. - type: string - type: - description: Type of the condition. - type: string - required: - - lastTransitionTime - - status - - type + conditions: + items: + description: Condition contains additional information on the state of a resource. + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition transitioned from one status to another. + format: date-time + type: string + message: + description: Message is an optional human readable message indicating details about the last transition. + type: string + reason: + description: Reason is a one-word, CamelCase reason for the condition's last transition. + type: string + status: + description: Status of the condition. + type: string + type: + description: Type of the condition. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map type: object - required: - - clusterName type: object - type: array - x-kubernetes-list-map-keys: - - clusterName - x-kubernetes-list-type: map + description: Nodes provides a map of cluster node names to node statuses + type: object statusConditions: - description: StatusConditions contain the different conditions that constitute the status of the TeamRoleBinding. + description: StatusConditions contain the different conditions that constitute the status of the Cluster. properties: conditions: items: @@ -1438,3 +968,473 @@ components: description: PluginDefinitionStatus defines the observed state of PluginDefinition type: object type: object + ClusterKubeconfig: + xml: + name: greenhouse.sap + namespace: v1alpha1 + title: ClusterKubeconfig + description: ClusterKubeconfig is the Schema for the clusterkubeconfigs API\nObjectMeta.OwnerReferences is used to link the ClusterKubeconfig to the Cluster\nObjectMeta.Generation is used to detect changes in the ClusterKubeconfig and sync local kubeconfig files\nObjectMeta.Name is designed to be the same with the Cluster name + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents.\nServers may infer this from the endpoint the client submits requests to.\nCannot be updated.\nIn CamelCase.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ClusterKubeconfigSpec stores the kubeconfig data for the cluster\nThe idea is to use kubeconfig data locally with minimum effort (with local tools or plain kubectl):\nkubectl get cluster-kubeconfig $NAME -o yaml | yq -y .spec.kubeconfig + properties: + kubeconfig: + description: 'ClusterKubeconfigData stores the kubeconfig data ready to use kubectl or other local tooling\nIt is a simplified version of clientcmdapi.Config: https://pkg.go.dev/k8s.io/client-go/tools/clientcmd/api#Config' + properties: + apiVersion: + type: string + clusters: + items: + properties: + cluster: + properties: + certificate-authority-data: + format: byte + type: string + server: + type: string + type: object + name: + type: string + required: + - cluster + - name + type: object + type: array + contexts: + items: + properties: + context: + properties: + cluster: + type: string + namespace: + type: string + user: + type: string + required: + - cluster + - user + type: object + name: + type: string + required: + - name + type: object + type: array + current-context: + type: string + kind: + type: string + preferences: + type: object + users: + items: + properties: + name: + type: string + user: + properties: + auth-provider: + description: AuthProviderConfig holds the configuration for a specified auth provider. + properties: + config: + additionalProperties: + type: string + type: object + name: + type: string + required: + - name + type: object + client-certificate-data: + format: byte + type: string + client-key-data: + format: byte + type: string + type: object + required: + - name + type: object + type: array + type: object + type: object + status: + properties: + statusConditions: + description: A StatusConditions contains a list of conditions.\nOnly one condition of a given type may exist in the list. + properties: + conditions: + items: + description: Condition contains additional information on the state of a resource. + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition transitioned from one status to another. + format: date-time + type: string + message: + description: Message is an optional human readable message indicating details about the last transition. + type: string + reason: + description: Reason is a one-word, CamelCase reason for the condition's last transition. + type: string + status: + description: Status of the condition. + type: string + type: + description: Type of the condition. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + type: object + type: object + TeamRoleBinding: + xml: + name: greenhouse.sap + namespace: v1alpha1 + title: TeamRoleBinding + description: TeamRoleBinding is the Schema for the rolebindings API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents.\nServers may infer this from the endpoint the client submits requests to.\nCannot be updated.\nIn CamelCase.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: TeamRoleBindingSpec defines the desired state of a TeamRoleBinding + properties: + clusterName: + description: ClusterName is the name of the cluster the rbacv1 resources are created on. + type: string + clusterSelector: + description: ClusterSelector is a label selector to select the Clusters the TeamRoleBinding should be deployed to. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that\nrelates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: operator represents a key's relationship to a set of values.\nValid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. If the operator is In or NotIn,\nthe values array must be non-empty. If the operator is Exists or DoesNotExist,\nthe values array must be empty. This array is replaced during a strategic\nmerge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels\nmap is equivalent to an element of matchExpressions, whose key field is "key", the\noperator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + namespaces: + description: Namespaces is a list of namespaces in the Greenhouse Clusters to apply the RoleBinding to.\nIf empty, a ClusterRoleBinding will be created on the remote cluster, otherwise a RoleBinding per namespace. + items: + type: string + type: array + teamRef: + description: TeamRef references a Greenhouse Team by name + type: string + teamRoleRef: + description: TeamRoleRef references a Greenhouse TeamRole by name + type: string + type: object + status: + description: TeamRoleBindingStatus defines the observed state of the TeamRoleBinding + properties: + clusters: + description: PropagationStatus is the list of clusters the TeamRoleBinding is applied to + items: + description: PropagationStatus defines the observed state of the TeamRoleBinding's associated rbacv1 resources on a Cluster + properties: + clusterName: + description: ClusterName is the name of the cluster the rbacv1 resources are created on. + type: string + condition: + description: Condition is the overall Status of the rbacv1 resources created on the cluster + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition transitioned from one status to another. + format: date-time + type: string + message: + description: Message is an optional human readable message indicating details about the last transition. + type: string + reason: + description: Reason is a one-word, CamelCase reason for the condition's last transition. + type: string + status: + description: Status of the condition. + type: string + type: + description: Type of the condition. + type: string + required: + - lastTransitionTime + - status + - type + type: object + required: + - clusterName + type: object + type: array + x-kubernetes-list-map-keys: + - clusterName + x-kubernetes-list-type: map + statusConditions: + description: StatusConditions contain the different conditions that constitute the status of the TeamRoleBinding. + properties: + conditions: + items: + description: Condition contains additional information on the state of a resource. + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition transitioned from one status to another. + format: date-time + type: string + message: + description: Message is an optional human readable message indicating details about the last transition. + type: string + reason: + description: Reason is a one-word, CamelCase reason for the condition's last transition. + type: string + status: + description: Status of the condition. + type: string + type: + description: Type of the condition. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + type: object + type: object + PluginPreset: + xml: + name: greenhouse.sap + namespace: v1alpha1 + title: PluginPreset + description: PluginPreset is the Schema for the PluginPresets API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation of an object.\nServers should convert recognized schemas to the latest internal value, and\nmay reject unrecognized values.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this object represents.\nServers may infer this from the endpoint the client submits requests to.\nCannot be updated.\nIn CamelCase.\nMore info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: PluginPresetSpec defines the desired state of PluginPreset + properties: + clusterOptionOverrides: + description: ClusterOptionOverrides define plugin option values to override by the PluginPreset + items: + description: ClusterOptionOverride defines which plugin option should be override in which cluster + properties: + clusterName: + type: string + overrides: + items: + description: PluginOptionValue is the value for a PluginOption. + properties: + name: + description: Name of the values. + type: string + value: + description: Value is the actual value in plain text. + x-kubernetes-preserve-unknown-fields: true + valueFrom: + description: ValueFrom references a potentially confidential value in another source. + properties: + secret: + description: Secret references the secret containing the value. + properties: + key: + description: Key in the secret to select the value from. + type: string + name: + description: Name of the secret in the same namespace. + type: string + required: + - key + - name + type: object + type: object + required: + - name + type: object + type: array + required: + - clusterName + - overrides + type: object + type: array + clusterSelector: + description: ClusterSelector is a label selector to select the clusters the plugin bundle should be deployed to. + properties: + matchExpressions: + description: matchExpressions is a list of label selector requirements. The requirements are ANDed. + items: + description: A label selector requirement is a selector that contains values, a key, and an operator that\nrelates the key and values. + properties: + key: + description: key is the label key that the selector applies to. + type: string + operator: + description: operator represents a key's relationship to a set of values.\nValid operators are In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array of string values. If the operator is In or NotIn,\nthe values array must be non-empty. If the operator is Exists or DoesNotExist,\nthe values array must be empty. This array is replaced during a strategic\nmerge patch. + items: + type: string + type: array + x-kubernetes-list-type: atomic + required: + - key + - operator + type: object + type: array + x-kubernetes-list-type: atomic + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels\nmap is equivalent to an element of matchExpressions, whose key field is "key", the\noperator is "In", and the values array contains only "value". The requirements are ANDed. + type: object + type: object + x-kubernetes-map-type: atomic + plugin: + description: PluginSpec is the spec of the plugin to be deployed by the PluginPreset. + properties: + clusterName: + description: ClusterName is the name of the cluster the plugin is deployed to. If not set, the plugin is deployed to the greenhouse cluster. + type: string + disabled: + description: Disabled indicates that the plugin is administratively disabled. + type: boolean + displayName: + description: DisplayName is an optional name for the Plugin to be displayed in the Greenhouse UI.\nThis is especially helpful to distinguish multiple instances of a PluginDefinition in the same context.\nDefaults to a normalized version of metadata.name. + type: string + optionValues: + description: Values are the values for a PluginDefinition instance. + items: + description: PluginOptionValue is the value for a PluginOption. + properties: + name: + description: Name of the values. + type: string + value: + description: Value is the actual value in plain text. + x-kubernetes-preserve-unknown-fields: true + valueFrom: + description: ValueFrom references a potentially confidential value in another source. + properties: + secret: + description: Secret references the secret containing the value. + properties: + key: + description: Key in the secret to select the value from. + type: string + name: + description: Name of the secret in the same namespace. + type: string + required: + - key + - name + type: object + type: object + required: + - name + type: object + type: array + pluginDefinition: + description: PluginDefinition is the name of the PluginDefinition this instance is for. + type: string + releaseNamespace: + description: ReleaseNamespace is the namespace in the remote cluster to which the backend is deployed.\nDefaults to the Greenhouse managed namespace if not set. + type: string + required: + - disabled + - pluginDefinition + type: object + required: + - clusterSelector + - plugin + type: object + status: + description: PluginPresetStatus defines the observed state of PluginPreset + properties: + statusConditions: + description: StatusConditions contain the different conditions that constitute the status of the PluginPreset. + properties: + conditions: + items: + description: Condition contains additional information on the state of a resource. + properties: + lastTransitionTime: + description: LastTransitionTime is the last time the condition transitioned from one status to another. + format: date-time + type: string + message: + description: Message is an optional human readable message indicating details about the last transition. + type: string + reason: + description: Reason is a one-word, CamelCase reason for the condition's last transition. + type: string + status: + description: Status of the condition. + type: string + type: + description: Type of the condition. + type: string + required: + - lastTransitionTime + - status + - type + type: object + type: array + x-kubernetes-list-map-keys: + - type + x-kubernetes-list-type: map + type: object + type: object + type: object From 37cb1548d9f24dd1e25e090306022122e4718e14 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Mon, 24 Feb 2025 15:46:18 +0100 Subject: [PATCH 105/108] (chore): removes unused finalizer const --- pkg/apis/well_known.go | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/pkg/apis/well_known.go b/pkg/apis/well_known.go index d5890e111..2899cbc57 100644 --- a/pkg/apis/well_known.go +++ b/pkg/apis/well_known.go @@ -11,16 +11,6 @@ const ( // GroupName for greenhouse API resources. GroupName = "greenhouse.sap" - // FinalizerCleanupHelmRelease is used to invoke the Helm release cleanup logic. - FinalizerCleanupHelmRelease = "greenhouse.sap/helm" - - // FinalizerCleanupPluginPreset is used to invoke the PluginPreset cleanup logic. - FinalizerCleanupPluginPreset = "greenhouse.sap/pluginpreset" - - // FinalizerCleanupPropagatedResource is used to invoke the cleanup of remote resources. - // TODO: Remove this finalizer after standardization is complete - FinalizerCleanupPropagatedResource = "greenhouse.sap/propagatedResource" - // SecretTypeKubeConfig specifies a secret containing the kubeconfig for a cluster. SecretTypeKubeConfig corev1.SecretType = "greenhouse.sap/kubeconfig" From 21c7bd141d4960d88b11ef1939ee37aaf9274d02 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Tue, 25 Feb 2025 01:24:31 +0100 Subject: [PATCH 106/108] (chore): updates organization deletion phase and tests --- pkg/controllers/organization/dex.go | 43 ------------------- .../organization/organization_controller.go | 20 +++------ .../organization_controller_test.go | 16 +++---- 3 files changed, 12 insertions(+), 67 deletions(-) diff --git a/pkg/controllers/organization/dex.go b/pkg/controllers/organization/dex.go index c3857cafd..0d0d7207e 100644 --- a/pkg/controllers/organization/dex.go +++ b/pkg/controllers/organization/dex.go @@ -17,14 +17,11 @@ import ( "golang.org/x/text/language" networkingv1 "k8s.io/api/networking/v1" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" "sigs.k8s.io/controller-runtime/pkg/log" greenhousesapv1alpha1 "github.com/cloudoperators/greenhouse/pkg/apis/greenhouse/v1alpha1" "github.com/cloudoperators/greenhouse/pkg/clientutil" "github.com/cloudoperators/greenhouse/pkg/common" - "github.com/cloudoperators/greenhouse/pkg/dex" - dexapi "github.com/cloudoperators/greenhouse/pkg/dex/api" ) const dexConnectorTypeGreenhouse = "greenhouse-oidc" @@ -220,46 +217,6 @@ func (r *OrganizationReconciler) appendRedirectsToDefaultConnector(ctx context.C return nil } -func (r *OrganizationReconciler) setDexOwnerReferences(ctx context.Context, org *greenhousesapv1alpha1.Organization) error { - if r.DexStorageType == dex.K8s { - connector := &dexapi.ConnectorList{} - if err := r.Client.List(ctx, connector); err != nil { - log.FromContext(ctx).Error(err, "failed to list dex connectors") - return err - } - for _, c := range connector.Items { - if c.ID == org.Name { - _, err := clientutil.CreateOrPatch(ctx, r.Client, &c, func() error { - return controllerutil.SetOwnerReference(org, &c, r.Scheme()) - }) - if err != nil { - log.FromContext(ctx).Error(err, "failed to set owner reference for dex connector", "name", c.ID) - return err - } - break - } - } - oauthClients := &dexapi.OAuth2ClientList{} - if err := r.Client.List(ctx, oauthClients); err != nil { - log.FromContext(ctx).Error(err, "failed to list dex oauth2clients") - return err - } - for _, c := range oauthClients.Items { - if c.ID == org.Name { - _, err := clientutil.CreateOrPatch(ctx, r.Client, &c, func() error { - return controllerutil.SetOwnerReference(org, &c, r.Scheme()) - }) - if err != nil { - log.FromContext(ctx).Error(err, "failed to set owner reference for dex oauth2client", "name", c.ID) - return err - } - break - } - } - } - return nil -} - func ensureCallbackURL(url string) string { prefix := "https://" if !strings.HasPrefix(url, prefix) { diff --git a/pkg/controllers/organization/organization_controller.go b/pkg/controllers/organization/organization_controller.go index 557d5afdd..0566ef11a 100644 --- a/pkg/controllers/organization/organization_controller.go +++ b/pkg/controllers/organization/organization_controller.go @@ -122,14 +122,11 @@ func (r *OrganizationReconciler) EnsureDeleted(ctx context.Context, obj lifecycl if err := r.removeAuthRedirectFromDefaultConnector(ctx, org); err != nil { return ctrl.Result{}, lifecycle.Failed, err } - // if dex storage type is kubernetes then deletion of org connector, org oauth2client will be handled automatically by owner reference - if r.DexStorageType == dexstore.Postgres { - if err := r.deleteDexConnector(ctx, org); err != nil { - return ctrl.Result{}, lifecycle.Failed, err - } - if err := r.deleteOAuth2Client(ctx, org); err != nil { - return ctrl.Result{}, lifecycle.Failed, err - } + if err := r.deleteDexConnector(ctx, org); err != nil { + return ctrl.Result{}, lifecycle.Failed, err + } + if err := r.deleteOAuth2Client(ctx, org); err != nil { + return ctrl.Result{}, lifecycle.Failed, err } } return ctrl.Result{}, lifecycle.Success, nil // nothing to do in that case @@ -184,13 +181,6 @@ func (r *OrganizationReconciler) EnsureCreated(ctx context.Context, object lifec } } - if r.DexStorageType == dexstore.K8s { - if err := r.setDexOwnerReferences(ctx, org); err != nil { - org.SetCondition(greenhousesapv1alpha1.FalseCondition(greenhousesapv1alpha1.OrganizationOICDConfigured, "", err.Error())) - return ctrl.Result{}, lifecycle.Failed, err - } - } - org.SetCondition(greenhousesapv1alpha1.TrueCondition(greenhousesapv1alpha1.OrganizationOICDConfigured, "", "")) } diff --git a/pkg/controllers/organization/organization_controller_test.go b/pkg/controllers/organization/organization_controller_test.go index 9e4c303ac..a6074a979 100644 --- a/pkg/controllers/organization/organization_controller_test.go +++ b/pkg/controllers/organization/organization_controller_test.go @@ -343,26 +343,24 @@ var _ = Describe("Test Organization reconciliation", Ordered, func() { return c.ID != oidcOrgName }) Expect(filteredOrgConnector).To(HaveLen(1), "there should be one dex connector after filtering") - ownerRef := clientutil.GetOwnerReference(&filteredOrgConnector[0], "Organization") - Expect(ownerRef).ToNot(BeNil(), "there should be an owner reference for the dex connector") - Expect(ownerRef.Name).To(Equal(oidcOrgName), "the owner reference should have the correct name") + Expect(filteredOrgConnector[0].ID).To(Equal(oidcOrgName), "the connector ID should be equal to organization name") By("checking dex oauth client resource") filteredOrgClient := slices.DeleteFunc(oAuthClients.Items, func(c dexapi.OAuth2Client) bool { return c.ID != oidcOrgName }) Expect(filteredOrgClient).To(HaveLen(1), "there should be one dex oauth client after filtering") - ownerRef = clientutil.GetOwnerReference(&filteredOrgClient[0], "Organization") - Expect(ownerRef).ToNot(BeNil(), "there should be an owner reference for the dex oauth client") - Expect(ownerRef.Name).To(Equal(oidcOrgName), "the owner reference should have the correct name") + Expect(filteredOrgClient[0].ID).To(Equal(oidcOrgName), "the oauth client ID should be equal to organization name") By("deleting the organizations") test.EventuallyDeleted(test.Ctx, test.K8sClient, &greenhousev1alpha1.Organization{ObjectMeta: metav1.ObjectMeta{Name: oidcOrgName}}) test.EventuallyDeleted(test.Ctx, test.K8sClient, &greenhousev1alpha1.Organization{ObjectMeta: metav1.ObjectMeta{Name: greenhouseOrgName}}) - // we cannot test the deletion of dex resources because of the local of controller-manager in the test environment - // since we assert the ownerReferences exist in the above test, it is safe to assume that the dex resources will be deleted - // controller-runtime issue: https://github.com/kubernetes-sigs/controller-runtime/issues/626#issuecomment-538529534 + By("checking if the dex resources are deleted") + err = setup.List(test.Ctx, connectors) + Expect(len(connectors.Items)).To(Equal(0), "there should be no dex connector resources") + err = setup.List(test.Ctx, oAuthClients) + Expect(len(oAuthClients.Items)).To(Equal(0), "there should be no dex oauth clients resources") } }) }) From f23d3fcd9fe2fa746af144c2e0322060b191afc4 Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Tue, 25 Feb 2025 01:35:46 +0100 Subject: [PATCH 107/108] (chore): fixes lint errors --- .../organization/organization_controller_test.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/controllers/organization/organization_controller_test.go b/pkg/controllers/organization/organization_controller_test.go index a6074a979..7cefd5626 100644 --- a/pkg/controllers/organization/organization_controller_test.go +++ b/pkg/controllers/organization/organization_controller_test.go @@ -358,9 +358,11 @@ var _ = Describe("Test Organization reconciliation", Ordered, func() { By("checking if the dex resources are deleted") err = setup.List(test.Ctx, connectors) - Expect(len(connectors.Items)).To(Equal(0), "there should be no dex connector resources") + Expect(err).ToNot(HaveOccurred(), "there should be no error listing dex connectors") + Expect(len(connectors.Items)).To(BeEmpty(), "there should be no dex connector resources") err = setup.List(test.Ctx, oAuthClients) - Expect(len(oAuthClients.Items)).To(Equal(0), "there should be no dex oauth clients resources") + Expect(err).ToNot(HaveOccurred(), "there should be no error listing dex oauth clients") + Expect(len(oAuthClients.Items)).To(BeEmpty(), "there should be no dex oauth clients resources") } }) }) From 097584207a5b40a926d360394bdc96a2542cbb6b Mon Sep 17 00:00:00 2001 From: abhijith-darshan Date: Tue, 25 Feb 2025 01:54:46 +0100 Subject: [PATCH 108/108] (chore): fixes org test --- pkg/controllers/organization/organization_controller_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkg/controllers/organization/organization_controller_test.go b/pkg/controllers/organization/organization_controller_test.go index 7cefd5626..54efad4d6 100644 --- a/pkg/controllers/organization/organization_controller_test.go +++ b/pkg/controllers/organization/organization_controller_test.go @@ -359,10 +359,10 @@ var _ = Describe("Test Organization reconciliation", Ordered, func() { By("checking if the dex resources are deleted") err = setup.List(test.Ctx, connectors) Expect(err).ToNot(HaveOccurred(), "there should be no error listing dex connectors") - Expect(len(connectors.Items)).To(BeEmpty(), "there should be no dex connector resources") + Expect(connectors.Items).To(BeEmpty(), "there should be no dex connector resources") err = setup.List(test.Ctx, oAuthClients) Expect(err).ToNot(HaveOccurred(), "there should be no error listing dex oauth clients") - Expect(len(oAuthClients.Items)).To(BeEmpty(), "there should be no dex oauth clients resources") + Expect(oAuthClients.Items).To(BeEmpty(), "there should be no dex oauth clients resources") } }) })