diff --git a/cmd/cofidectl/cmd/federation/federation.go b/cmd/cofidectl/cmd/federation/federation.go index a030b5fd..883bdbc6 100644 --- a/cmd/cofidectl/cmd/federation/federation.go +++ b/cmd/cofidectl/cmd/federation/federation.go @@ -12,6 +12,7 @@ import ( trust_zone_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/trust_zone/v1alpha1" "github.com/cofide/cofidectl/internal/pkg/trustzone" cmdcontext "github.com/cofide/cofidectl/pkg/cmd/context" + "github.com/cofide/cofidectl/pkg/plugin/datasource" kubeutil "github.com/cofide/cofidectl/pkg/kube" "github.com/cofide/cofidectl/pkg/provider/helm" @@ -94,7 +95,7 @@ func (c *FederationCommand) GetListCommand() *cobra.Command { return err } - status, reason, err := checkFederationStatus(cmd.Context(), kubeConfig, from, to) + status, reason, err := checkFederationStatus(cmd.Context(), ds, kubeConfig, from, to) if err != nil { return err } @@ -126,11 +127,11 @@ type bundles struct { // checkFederationStatus builds a comparison map between two trust domains, retrieves there server CA bundle and any federated bundles available // locally from the SPIRE server, and then compares the bundles on each to verify SPIRE has the correct bundles on each side of the federation -func checkFederationStatus(ctx context.Context, kubeConfig string, from *trust_zone_proto.TrustZone, to *trust_zone_proto.TrustZone) (string, string, error) { +func checkFederationStatus(ctx context.Context, ds datasource.DataSource, kubeConfig string, from *trust_zone_proto.TrustZone, to *trust_zone_proto.TrustZone) (string, string, error) { compare := make(map[*trust_zone_proto.TrustZone]bundles) for _, tz := range []*trust_zone_proto.TrustZone{from, to} { - cluster, err := trustzone.GetClusterFromTrustZone(tz) + cluster, err := trustzone.GetClusterFromTrustZone(tz, ds) if err != nil { return "", "", err } diff --git a/cmd/cofidectl/cmd/trustzone/helm/helm.go b/cmd/cofidectl/cmd/trustzone/helm/helm.go index bbefd9de..e8f1ba6a 100644 --- a/cmd/cofidectl/cmd/trustzone/helm/helm.go +++ b/cmd/cofidectl/cmd/trustzone/helm/helm.go @@ -102,7 +102,7 @@ func (c *HelmCommand) overrideValues(ds datasource.DataSource, tzName string, va return err } - cluster, err := trustzone.GetClusterFromTrustZone(trustZone) + cluster, err := trustzone.GetClusterFromTrustZone(trustZone, ds) if err != nil { return err } @@ -118,7 +118,8 @@ func (c *HelmCommand) overrideValues(ds datasource.DataSource, tzName string, va return err } - return ds.UpdateTrustZone(trustZone) + _, err = ds.UpdateCluster(cluster) + return err } // readValues reads values in YAML format from the specified reader. @@ -189,7 +190,7 @@ func (c *HelmCommand) getValues(ds datasource.DataSource, tzName string) (map[st return nil, err } - cluster, err := trustzone.GetClusterFromTrustZone(trustZone) + cluster, err := trustzone.GetClusterFromTrustZone(trustZone, ds) if err != nil { return nil, err } diff --git a/cmd/cofidectl/cmd/trustzone/trustzone.go b/cmd/cofidectl/cmd/trustzone/trustzone.go index 9bef52d3..683474a2 100644 --- a/cmd/cofidectl/cmd/trustzone/trustzone.go +++ b/cmd/cofidectl/cmd/trustzone/trustzone.go @@ -86,7 +86,7 @@ func (c *TrustZoneCommand) GetListCommand() *cobra.Command { data := make([][]string, len(trustZones)) for i, trustZone := range trustZones { - cluster, err := trustzone.GetClusterFromTrustZone(trustZone) + cluster, err := trustzone.GetClusterFromTrustZone(trustZone, ds) if err != nil { return err } @@ -156,6 +156,18 @@ func (c *TrustZoneCommand) GetAddCommand() *cobra.Command { bundleEndpointProfile := trust_zone_proto.BundleEndpointProfile_BUNDLE_ENDPOINT_PROFILE_HTTPS_SPIFFE + newTrustZone := &trust_zone_proto.TrustZone{ + Name: opts.name, + TrustDomain: opts.trustDomain, + JwtIssuer: &opts.jwtIssuer, + BundleEndpointProfile: &bundleEndpointProfile, + } + + _, err = ds.AddTrustZone(newTrustZone) + if err != nil { + return fmt.Errorf("failed to create trust zone %s: %w", newTrustZone.Name, err) + } + newCluster := &clusterpb.Cluster{ Name: &opts.kubernetesCluster, TrustZone: &opts.name, @@ -164,17 +176,10 @@ func (c *TrustZoneCommand) GetAddCommand() *cobra.Command { Profile: &opts.profile, ExternalServer: &opts.externalServer, } - newTrustZone := &trust_zone_proto.TrustZone{ - Name: opts.name, - TrustDomain: opts.trustDomain, - JwtIssuer: &opts.jwtIssuer, - BundleEndpointProfile: &bundleEndpointProfile, - Clusters: []*clusterpb.Cluster{newCluster}, - } - _, err = ds.AddTrustZone(newTrustZone) + _, err = ds.AddCluster(newCluster) if err != nil { - return fmt.Errorf("failed to create trust zone %s: %s", newTrustZone.Name, err) + return fmt.Errorf("failed to create cluster %s: %w", newCluster.GetName(), err) } return nil @@ -230,7 +235,7 @@ func (c *TrustZoneCommand) status(ctx context.Context, source datasource.DataSou return err } - cluster, err := trustzone.GetClusterFromTrustZone(trustZone) + cluster, err := trustzone.GetClusterFromTrustZone(trustZone, source) if err != nil { return err } diff --git a/cmd/cofidectl/cmd/workload/workload.go b/cmd/cofidectl/cmd/workload/workload.go index 70e08bb0..01929c9d 100644 --- a/cmd/cofidectl/cmd/workload/workload.go +++ b/cmd/cofidectl/cmd/workload/workload.go @@ -16,6 +16,7 @@ import ( "github.com/cofide/cofidectl/internal/pkg/workload" cmdcontext "github.com/cofide/cofidectl/pkg/cmd/context" kubeutil "github.com/cofide/cofidectl/pkg/kube" + "github.com/cofide/cofidectl/pkg/plugin/datasource" "github.com/cofide/cofidectl/pkg/provider/helm" "github.com/olekukonko/tablewriter" "github.com/spf13/cobra" @@ -99,7 +100,7 @@ func (w *WorkloadCommand) GetListCommand() *cobra.Command { return fmt.Errorf("failed to retrieve the kubeconfig file location") } - err = renderRegisteredWorkloads(cmd.Context(), kubeConfig, trustZones) + err = renderRegisteredWorkloads(cmd.Context(), ds, kubeConfig, trustZones) if err != nil { return err } @@ -132,12 +133,17 @@ func (w *WorkloadCommand) GetStatusCommand() *cobra.Command { Long: workloadStatusCmdDesc, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { + ds, err := w.cmdCtx.PluginManager.GetDataSource(cmd.Context()) + if err != nil { + return err + } + kubeConfig, err := cmd.Flags().GetString("kube-config") if err != nil { return fmt.Errorf("failed to retrieve the kubeconfig file location") } - return w.status(cmd.Context(), kubeConfig, opts) + return w.status(cmd.Context(), ds, kubeConfig, opts) }, } @@ -153,18 +159,13 @@ func (w *WorkloadCommand) GetStatusCommand() *cobra.Command { return cmd } -func (w *WorkloadCommand) status(ctx context.Context, kubeConfig string, opts StatusOpts) error { - ds, err := w.cmdCtx.PluginManager.GetDataSource(ctx) - if err != nil { - return err - } - +func (w *WorkloadCommand) status(ctx context.Context, ds datasource.DataSource, kubeConfig string, opts StatusOpts) error { trustZone, err := ds.GetTrustZone(opts.trustZone) if err != nil { return err } - cluster, err := trustzone.GetClusterFromTrustZone(trustZone) + cluster, err := trustzone.GetClusterFromTrustZone(trustZone, ds) if err != nil { return err } @@ -190,11 +191,11 @@ func (w *WorkloadCommand) status(ctx context.Context, kubeConfig string, opts St return nil } -func renderRegisteredWorkloads(ctx context.Context, kubeConfig string, trustZones []*trust_zone_proto.TrustZone) error { +func renderRegisteredWorkloads(ctx context.Context, ds datasource.DataSource, kubeConfig string, trustZones []*trust_zone_proto.TrustZone) error { data := make([][]string, 0, len(trustZones)) for _, trustZone := range trustZones { - cluster, err := trustzone.GetClusterFromTrustZone(trustZone) + cluster, err := trustzone.GetClusterFromTrustZone(trustZone, ds) if err != nil { return err } @@ -292,7 +293,7 @@ func (w *WorkloadCommand) GetDiscoverCommand() *cobra.Command { return fmt.Errorf("failed to retrieve the kubeconfig file location") } - err = renderUnregisteredWorkloads(cmd.Context(), kubeConfig, trustZones, opts.includeSecrets) + err = renderUnregisteredWorkloads(cmd.Context(), ds, kubeConfig, trustZones, opts.includeSecrets) if err != nil { return err } @@ -308,11 +309,11 @@ func (w *WorkloadCommand) GetDiscoverCommand() *cobra.Command { return cmd } -func renderUnregisteredWorkloads(ctx context.Context, kubeConfig string, trustZones []*trust_zone_proto.TrustZone, includeSecrets bool) error { +func renderUnregisteredWorkloads(ctx context.Context, ds datasource.DataSource, kubeConfig string, trustZones []*trust_zone_proto.TrustZone, includeSecrets bool) error { data := make([][]string, 0, len(trustZones)) for _, trustZone := range trustZones { - cluster, err := trustzone.GetClusterFromTrustZone(trustZone) + cluster, err := trustzone.GetClusterFromTrustZone(trustZone, ds) if err != nil { return err } diff --git a/go.mod b/go.mod index cc77cb07..d259c85e 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ toolchain go1.23.4 require ( buf.build/go/protoyaml v0.3.1 cuelang.org/go v0.10.1 - github.com/cofide/cofide-api-sdk v0.5.2-0.20250117165051-3f40e0c57dc0 + github.com/cofide/cofide-api-sdk v0.5.2-0.20250130134925-4714836e1d96 github.com/fatih/color v1.18.0 github.com/gofrs/flock v0.12.1 github.com/google/go-cmp v0.6.0 @@ -17,7 +17,7 @@ require ( github.com/spiffe/go-spiffe/v2 v2.4.0 github.com/spiffe/spire-api-sdk v1.11.1 github.com/stretchr/testify v1.10.0 - google.golang.org/grpc v1.69.4 + google.golang.org/grpc v1.70.0 google.golang.org/protobuf v1.36.4 gopkg.in/yaml.v3 v3.0.1 helm.sh/helm/v3 v3.17.0 @@ -25,7 +25,7 @@ require ( require ( buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.0-20241127180247-a33202765966.1 // indirect - cel.dev/expr v0.18.0 // indirect + cel.dev/expr v0.19.0 // indirect dario.cat/mergo v1.0.1 // indirect github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect @@ -139,23 +139,23 @@ require ( github.com/xeipuuv/gojsonschema v1.2.0 // indirect github.com/xlab/treeprint v1.2.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect - go.opentelemetry.io/otel v1.31.0 // indirect + go.opentelemetry.io/otel v1.32.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.22.0 // indirect go.opentelemetry.io/otel/exporters/prometheus v0.49.0 // indirect - go.opentelemetry.io/otel/metric v1.31.0 // indirect - go.opentelemetry.io/otel/trace v1.31.0 // indirect + go.opentelemetry.io/otel/metric v1.32.0 // indirect + go.opentelemetry.io/otel/trace v1.32.0 // indirect golang.org/x/crypto v0.32.0 // indirect golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect golang.org/x/mod v0.21.0 // indirect golang.org/x/net v0.34.0 // indirect - golang.org/x/oauth2 v0.23.0 // indirect + golang.org/x/oauth2 v0.24.0 // indirect golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.29.0 // indirect golang.org/x/term v0.28.0 // indirect golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.7.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gotest.tools/v3 v3.5.0 // indirect diff --git a/go.sum b/go.sum index b5aef4fe..599b0ebd 100644 --- a/go.sum +++ b/go.sum @@ -2,8 +2,8 @@ buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.0-2024112718024 buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.0-20241127180247-a33202765966.1/go.mod h1:AxRT+qTj5PJCz2nyQzsR/qxAcveW5USRhJTt/edTO5w= buf.build/go/protoyaml v0.3.1 h1:ucyzE7DRnjX+mQ6AH4JzN0Kg50ByHHu+yrSKbgQn2D4= buf.build/go/protoyaml v0.3.1/go.mod h1:0TzNpFQDXhwbkXb/ajLvxIijqbve+vMQvWY/b3/Dzxg= -cel.dev/expr v0.18.0 h1:CJ6drgk+Hf96lkLikr4rFf19WrU0BOWEihyZnI2TAzo= -cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= +cel.dev/expr v0.19.0 h1:lXuo+nDhpyJSpWxpPVi5cPUwzKb+dsdOiw6IreM5yt0= +cel.dev/expr v0.19.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cuelabs.dev/go/oci/ociregistry v0.0.0-20240807094312-a32ad29eed79 h1:EceZITBGET3qHneD5xowSTY/YHbNybvMWGh62K2fG/M= @@ -86,8 +86,8 @@ github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEaIwg= github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc= -github.com/cofide/cofide-api-sdk v0.5.2-0.20250117165051-3f40e0c57dc0 h1:UBmQrMgsH5C2AUpzJKDAd/izBM/N7FFPfM/I6rpo3AY= -github.com/cofide/cofide-api-sdk v0.5.2-0.20250117165051-3f40e0c57dc0/go.mod h1:cU46gp7I0XxyqSiPzS17P9zWwU1agzxbexfZjRG5l94= +github.com/cofide/cofide-api-sdk v0.5.2-0.20250130134925-4714836e1d96 h1:kVt0YBy2+XDDbeWXY0VC1Bxk6lvPip41gNDwLLq+Z8o= +github.com/cofide/cofide-api-sdk v0.5.2-0.20250130134925-4714836e1d96/go.mod h1:Px3XPSfjqSDz4ypi5Eg2JHKoubbkpG8wWGKVwAxrGR8= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= github.com/containerd/containerd v1.7.24 h1:zxszGrGjrra1yYJW/6rhm9cJ1ZQ8rkKBR48brqsa7nA= @@ -476,8 +476,8 @@ go.opentelemetry.io/contrib/exporters/autoexport v0.46.1 h1:ysCfPZB9AjUlMa1UHYup go.opentelemetry.io/contrib/exporters/autoexport v0.46.1/go.mod h1:ha0aiYm+DOPsLHjh0zoQ8W8sLT+LJ58J3j47lGpSLrU= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= -go.opentelemetry.io/otel v1.31.0 h1:NsJcKPIW0D0H3NgzPDHmo0WW6SptzPdqg/L1zsIm2hY= -go.opentelemetry.io/otel v1.31.0/go.mod h1:O0C14Yl9FgkjqcCZAsE053C13OaddMYr/hz6clDkEJE= +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/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= @@ -494,14 +494,14 @@ go.opentelemetry.io/otel/exporters/stdout/stdoutmetric v0.44.0 h1:dEZWPjVN22urgY 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.31.0 h1:FSErL0ATQAmYHUIzSezZibnyVlft1ybhy4ozRPcF2fE= -go.opentelemetry.io/otel/metric v1.31.0/go.mod h1:C3dEloVbLuYoX41KpmAhOqNriGbA+qqH6PQ5E5mUfnY= -go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk= -go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0= -go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc= -go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8= -go.opentelemetry.io/otel/trace v1.31.0 h1:ffjsj1aRouKewfr85U2aGagJ46+MvodynlQ1HYdmJys= -go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06FJAsO1tJg480A= +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/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 v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= @@ -539,8 +539,8 @@ golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0= golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= -golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= -golang.org/x/oauth2 v0.23.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= @@ -599,10 +599,10 @@ google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoA google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= -google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53 h1:fVoAXEKA4+yufmbdVYv+SE73+cPZbbbe8paLsHfkK+U= -google.golang.org/genproto/googleapis/api v0.0.0-20241015192408-796eee8c2d53/go.mod h1:riSXTwQ4+nqmPGtobMFyW5FqVAmIs0St6VPp4Ug7CE4= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 h1:X58yt85/IXCx0Y3ZwN6sEIKZzQtDEYaBWrDvErdXrRE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI= +google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a h1:OAiGFfOiA0v9MRYsSidp3ubZaBnteRUyn3xB2ZQ5G/E= +google.golang.org/genproto/googleapis/api v0.0.0-20241202173237-19429a94021a/go.mod h1:jehYqy3+AhJU9ve55aNOaSml7wUXjF9x6z2LcCfpAhY= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a h1:hgh8P4EuoxpsuKMXX/To36nOFD7vixReXgn8lPGnt+o= +google.golang.org/genproto/googleapis/rpc v0.0.0-20241202173237-19429a94021a/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= @@ -610,8 +610,8 @@ google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8 google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= google.golang.org/grpc v1.48.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= -google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A= -google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= +google.golang.org/grpc v1.70.0 h1:pWFv03aZoHzlRKHWicjsZytKAiYCtNS0dHbXnIdq7jQ= +google.golang.org/grpc v1.70.0/go.mod h1:ofIJqVKDXx/JiXrwr2IG4/zwdH9txy3IlF40RmcJSQw= 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/internal/pkg/config/config.go b/internal/pkg/config/config.go index e28325d2..90040e28 100644 --- a/internal/pkg/config/config.go +++ b/internal/pkg/config/config.go @@ -6,6 +6,7 @@ package config import ( "buf.build/go/protoyaml" attestation_policy_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/attestation_policy/v1alpha1" + clusterpb "github.com/cofide/cofide-api-sdk/gen/go/proto/cluster/v1alpha1" config_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/config/v1alpha1" pluginspb "github.com/cofide/cofide-api-sdk/gen/go/proto/plugins/v1alpha1" trust_zone_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/trust_zone/v1alpha1" @@ -15,6 +16,7 @@ import ( // Config describes the cofide.yaml configuration file format. type Config struct { TrustZones []*trust_zone_proto.TrustZone + Clusters []*clusterpb.Cluster AttestationPolicies []*attestation_policy_proto.AttestationPolicy PluginConfig map[string]*structpb.Struct Plugins *pluginspb.Plugins @@ -23,6 +25,7 @@ type Config struct { func NewConfig() *Config { return &Config{ TrustZones: []*trust_zone_proto.TrustZone{}, + Clusters: []*clusterpb.Cluster{}, AttestationPolicies: []*attestation_policy_proto.AttestationPolicy{}, PluginConfig: map[string]*structpb.Struct{}, Plugins: &pluginspb.Plugins{}, @@ -36,6 +39,7 @@ func newConfigFromProto(proto *config_proto.Config) *Config { } return &Config{ TrustZones: proto.TrustZones, + Clusters: proto.Clusters, AttestationPolicies: proto.AttestationPolicies, PluginConfig: proto.PluginConfig, Plugins: plugins, @@ -45,6 +49,7 @@ func newConfigFromProto(proto *config_proto.Config) *Config { func (c *Config) toProto() *config_proto.Config { return &config_proto.Config{ TrustZones: c.TrustZones, + Clusters: c.Clusters, AttestationPolicies: c.AttestationPolicies, PluginConfig: c.PluginConfig, Plugins: c.Plugins, @@ -62,6 +67,7 @@ func (c *Config) marshalYAML() ([]byte, error) { func unmarshalYAML(data []byte) (*Config, error) { proto := config_proto.Config{ TrustZones: []*trust_zone_proto.TrustZone{}, + Clusters: []*clusterpb.Cluster{}, AttestationPolicies: []*attestation_policy_proto.AttestationPolicy{}, PluginConfig: map[string]*structpb.Struct{}, } @@ -81,6 +87,25 @@ func (c *Config) GetTrustZoneByName(name string) (*trust_zone_proto.TrustZone, b return nil, false } +func (c *Config) GetClusterByName(name, trustZone string) (*clusterpb.Cluster, bool) { + for _, cluster := range c.Clusters { + if cluster.GetName() == name && cluster.GetTrustZone() == trustZone { + return cluster, true + } + } + return nil, false +} + +func (c *Config) GetClustersByTrustZone(trustZone string) []*clusterpb.Cluster { + clusters := []*clusterpb.Cluster{} + for _, cluster := range c.Clusters { + if cluster.GetTrustZone() == trustZone { + clusters = append(clusters, cluster) + } + } + return clusters +} + func (c *Config) GetAttestationPolicyByName(name string) (*attestation_policy_proto.AttestationPolicy, bool) { for _, ap := range c.AttestationPolicies { if ap.Name == name { diff --git a/internal/pkg/config/config_test.go b/internal/pkg/config/config_test.go index ca221aa3..e6cacba7 100644 --- a/internal/pkg/config/config_test.go +++ b/internal/pkg/config/config_test.go @@ -7,6 +7,7 @@ import ( "testing" attestation_policy_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/attestation_policy/v1alpha1" + clusterpb "github.com/cofide/cofide-api-sdk/gen/go/proto/cluster/v1alpha1" pluginspb "github.com/cofide/cofide-api-sdk/gen/go/proto/plugins/v1alpha1" trust_zone_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/trust_zone/v1alpha1" "github.com/cofide/cofidectl/internal/pkg/test/fixtures" @@ -36,6 +37,10 @@ func TestConfig_YAMLMarshall(t *testing.T) { fixtures.TrustZone("tz1"), fixtures.TrustZone("tz2"), }, + Clusters: []*clusterpb.Cluster{ + fixtures.Cluster("local1"), + fixtures.Cluster("local2"), + }, AttestationPolicies: []*attestation_policy_proto.AttestationPolicy{ fixtures.AttestationPolicy("ap1"), fixtures.AttestationPolicy("ap2"), @@ -74,6 +79,7 @@ func TestConfig_YAMLUnmarshall(t *testing.T) { file: "default.yaml", want: &Config{ TrustZones: []*trust_zone_proto.TrustZone{}, + Clusters: []*clusterpb.Cluster{}, AttestationPolicies: []*attestation_policy_proto.AttestationPolicy{}, PluginConfig: map[string]*structpb.Struct{}, Plugins: &pluginspb.Plugins{}, @@ -87,6 +93,10 @@ func TestConfig_YAMLUnmarshall(t *testing.T) { fixtures.TrustZone("tz1"), fixtures.TrustZone("tz2"), }, + Clusters: []*clusterpb.Cluster{ + fixtures.Cluster("local1"), + fixtures.Cluster("local2"), + }, AttestationPolicies: []*attestation_policy_proto.AttestationPolicy{ fixtures.AttestationPolicy("ap1"), fixtures.AttestationPolicy("ap2"), @@ -155,6 +165,125 @@ func TestConfig_GetTrustZoneByName(t *testing.T) { } } +func TestConfig_GetClusterByName(t *testing.T) { + tests := []struct { + name string + clusters []*clusterpb.Cluster + cluster string + trustZone string + wantCluster *clusterpb.Cluster + wantOk bool + }{ + { + name: "found", + clusters: []*clusterpb.Cluster{ + fixtures.Cluster("local1"), + fixtures.Cluster("local2"), + }, + cluster: "local2", + trustZone: "tz2", + wantCluster: fixtures.Cluster("local2"), + wantOk: true, + }, + { + name: "not found", + clusters: []*clusterpb.Cluster{}, + cluster: "local1", + trustZone: "tz1", + wantCluster: nil, + wantOk: false, + }, + { + name: "trust zone scoped", + clusters: []*clusterpb.Cluster{ + fixtures.Cluster("local1"), + }, + cluster: "local1", + trustZone: "tz2", + wantCluster: nil, + wantOk: false, + }, + { + name: "nil list", + clusters: nil, + cluster: "local1", + trustZone: "tz1", + wantCluster: nil, + wantOk: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Config{ + Clusters: tt.clusters, + } + gotCluster, gotOk := c.GetClusterByName(tt.cluster, tt.trustZone) + assert.EqualExportedValues(t, tt.wantCluster, gotCluster) + assert.Equal(t, tt.wantOk, gotOk) + }) + } +} + +func TestConfig_GetClustersByTrustZone(t *testing.T) { + tests := []struct { + name string + clusters []*clusterpb.Cluster + trustZone string + wantClusters []*clusterpb.Cluster + }{ + { + name: "found", + clusters: []*clusterpb.Cluster{ + fixtures.Cluster("local1"), + fixtures.Cluster("local2"), + }, + trustZone: "tz2", + wantClusters: []*clusterpb.Cluster{fixtures.Cluster("local2")}, + }, + { + name: "found multiple", + clusters: []*clusterpb.Cluster{ + fixtures.Cluster("local1"), + fixtures.Cluster("local1"), + }, + trustZone: "tz1", + wantClusters: []*clusterpb.Cluster{ + fixtures.Cluster("local1"), + fixtures.Cluster("local1"), + }, + }, + { + name: "not found", + clusters: []*clusterpb.Cluster{}, + trustZone: "tz1", + wantClusters: []*clusterpb.Cluster{}, + }, + { + name: "trust zone scoped", + clusters: []*clusterpb.Cluster{ + fixtures.Cluster("local1"), + }, + trustZone: "tz2", + wantClusters: []*clusterpb.Cluster{}, + }, + { + name: "nil list", + clusters: nil, + trustZone: "tz1", + wantClusters: []*clusterpb.Cluster{}, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + c := &Config{ + Clusters: tt.clusters, + } + gotClusters := c.GetClustersByTrustZone(tt.trustZone) + assert.EqualExportedValues(t, tt.wantClusters, gotClusters) + }) + } +} + func TestConfig_GetAttestationPolicyByName(t *testing.T) { tests := []struct { name string diff --git a/internal/pkg/config/schema.cue b/internal/pkg/config/schema.cue index a2a8abe0..697cc849 100644 --- a/internal/pkg/config/schema.cue +++ b/internal/pkg/config/schema.cue @@ -9,7 +9,6 @@ attestation_policies: [...#APBinding] jwt_issuer?: string bundle_endpoint_profile?: #BundleEndpointProfile - clusters: [#Cluster] } #Cluster: { @@ -79,6 +78,7 @@ #Config: { trust_zones: [...#TrustZone] + clusters: [...#Cluster] attestation_policies: [...#AttestationPolicy] plugin_config?: #PluginConfig plugins!: #Plugins diff --git a/internal/pkg/config/testdata/config/empty_trust_zone_clusters.yaml b/internal/pkg/config/testdata/config/empty_trust_zone_clusters.yaml deleted file mode 100644 index ffe76f95..00000000 --- a/internal/pkg/config/testdata/config/empty_trust_zone_clusters.yaml +++ /dev/null @@ -1,10 +0,0 @@ -trust_zones: - - name: tz1 - trust_domain: td1 - bundle_endpoint_url: 127.0.0.1 - bundle: "" - federations: - - from: tz1 - to: tz2 - attestation_policies: [] - clusters: [] diff --git a/internal/pkg/config/testdata/config/full.yaml b/internal/pkg/config/testdata/config/full.yaml index 1debe0c4..1221ca0f 100644 --- a/internal/pkg/config/testdata/config/full.yaml +++ b/internal/pkg/config/testdata/config/full.yaml @@ -12,23 +12,6 @@ trust_zones: - tz2 jwt_issuer: https://tz1.example.com bundle_endpoint_profile: BUNDLE_ENDPOINT_PROFILE_HTTPS_SPIFFE - clusters: - - name: local1 - trust_zone: tz1 - kubernetes_context: kind-local1 - trust_provider: - kind: kubernetes - extra_helm_values: - global: - spire: - caSubject: - commonName: cn.example.com - organization: acme-org - spire-server: - logLevel: INFO - nameOverride: custom-server-name - profile: kubernetes - external_server: false - name: tz2 trust_domain: td2 bundle_endpoint_url: 127.0.0.2 @@ -42,14 +25,30 @@ trust_zones: - tz1 jwt_issuer: https://tz2.example.com bundle_endpoint_profile: BUNDLE_ENDPOINT_PROFILE_HTTPS_WEB - clusters: - - name: local2 - trust_zone: tz2 - kubernetes_context: kind-local2 - trust_provider: - kind: kubernetes - profile: kubernetes - external_server: false +clusters: + - name: local1 + trust_zone: tz1 + kubernetes_context: kind-local1 + trust_provider: + kind: kubernetes + extra_helm_values: + global: + spire: + caSubject: + commonName: cn.example.com + organization: acme-org + spire-server: + logLevel: INFO + nameOverride: custom-server-name + profile: kubernetes + external_server: false + - name: local2 + trust_zone: tz2 + kubernetes_context: kind-local2 + trust_provider: + kind: kubernetes + profile: kubernetes + external_server: false attestation_policies: - name: ap1 kubernetes: diff --git a/internal/pkg/config/testdata/config/missing_cluster_field.yaml b/internal/pkg/config/testdata/config/missing_cluster_field.yaml new file mode 100644 index 00000000..4abcf994 --- /dev/null +++ b/internal/pkg/config/testdata/config/missing_cluster_field.yaml @@ -0,0 +1,7 @@ +clusters: + - trust_zone: tz1 + kubernetes_context: kind-local1 + trust_provider: + kind: kubernetes + profile: kubernetes +plugins: {} diff --git a/internal/pkg/config/testdata/config/missing_trust_zone_field.yaml b/internal/pkg/config/testdata/config/missing_trust_zone_field.yaml index aa6ed63e..be5d628b 100644 --- a/internal/pkg/config/testdata/config/missing_trust_zone_field.yaml +++ b/internal/pkg/config/testdata/config/missing_trust_zone_field.yaml @@ -6,11 +6,3 @@ trust_zones: - from: tz1 to: tz2 attestation_policies: [] - clusters: - - name: local1 - trust_zone: tz1 - kubernetes_context: kind-local1 - trust_provider: - name: "" - kind: kubernetes - profile: kubernetes diff --git a/internal/pkg/config/testdata/config/multiple_trust_zone_clusters.yaml b/internal/pkg/config/testdata/config/multiple_trust_zone_clusters.yaml deleted file mode 100644 index 4a72bf68..00000000 --- a/internal/pkg/config/testdata/config/multiple_trust_zone_clusters.yaml +++ /dev/null @@ -1,24 +0,0 @@ -trust_zones: - - name: tz1 - trust_domain: td1 - bundle_endpoint_url: 127.0.0.1 - bundle: "" - federations: - - from: tz1 - to: tz2 - attestation_policies: [] - clusters: - - name: local1 - trust_zone: tz1 - kubernetes_context: kind-local1 - trust_provider: - name: "" - kind: kubernetes - profile: kubernetes - - name: local2 - trust_zone: tz1 - kubernetes_context: kind-local2 - trust_provider: - name: "" - kind: kubernetes - profile: kubernetes diff --git a/internal/pkg/config/validator_test.go b/internal/pkg/config/validator_test.go index 10efc35b..6e3edd7e 100644 --- a/internal/pkg/config/validator_test.go +++ b/internal/pkg/config/validator_test.go @@ -51,6 +51,11 @@ func TestValidator_ValidateInvalid(t *testing.T) { data: "trust_zones: \"not-a-list\"", wantErr: "trust_zones: conflicting values \"not-a-list\" and [...#TrustZone]", }, + { + name: "clusters not a list", + data: "clusters: \"not-a-list\"", + wantErr: "clusters: conflicting values \"not-a-list\" and [...#Cluster]", + }, { name: "attestation policies not a list", data: "attestation_policies: \"not-a-list\"", @@ -66,6 +71,11 @@ func TestValidator_ValidateInvalid(t *testing.T) { data: "trust_zones: [foo: bar]", wantErr: "trust_zones.0.foo: field not allowed", }, + { + name: "unexpected cluster field", + data: "clusters: [foo: bar]", + wantErr: "clusters.0.foo: field not allowed", + }, { name: "unexpected attestation policy field", data: "attestation_policies: [foo: bar]", @@ -76,21 +86,16 @@ func TestValidator_ValidateInvalid(t *testing.T) { data: string(readTestConfig(t, "missing_trust_zone_field.yaml")), wantErr: "trust_zones.0.name: field is required but not present", }, + { + name: "missing cluster field", + data: string(readTestConfig(t, "missing_cluster_field.yaml")), + wantErr: "clusters.0.name: field is required but not present", + }, { name: "missing attestation policy field", data: string(readTestConfig(t, "missing_attestation_policy_field.yaml")), wantErr: "attestation_policies.0.kubernetes: field is required but not present", }, - { - name: "empty trust zone clusters list", - data: string(readTestConfig(t, "empty_trust_zone_clusters.yaml")), - wantErr: "trust_zones.0.clusters: incompatible list lengths (0 and 1)", - }, - { - name: "multiple trust zone clusters list", - data: string(readTestConfig(t, "multiple_trust_zone_clusters.yaml")), - wantErr: "trust_zones.0.clusters: incompatible list lengths (1 and 2)", - }, { name: "plugins not a map", data: "plugins: 123", diff --git a/internal/pkg/proto/proto.go b/internal/pkg/proto/proto.go index b9d2eaa7..a6bcbedd 100644 --- a/internal/pkg/proto/proto.go +++ b/internal/pkg/proto/proto.go @@ -11,6 +11,7 @@ import ( ap_binding_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/ap_binding/v1alpha1" attestation_policy_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/attestation_policy/v1alpha1" + clusterpb "github.com/cofide/cofide-api-sdk/gen/go/proto/cluster/v1alpha1" federation_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/federation/v1alpha1" pluginspb "github.com/cofide/cofide-api-sdk/gen/go/proto/plugins/v1alpha1" trust_zone_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/trust_zone/v1alpha1" @@ -27,6 +28,17 @@ func CloneTrustZone(trustZone *trust_zone_proto.TrustZone) (*trust_zone_proto.Tr } } +func CloneCluster(cluster *clusterpb.Cluster) (*clusterpb.Cluster, error) { + if clone, ok := proto.Clone(cluster).(*clusterpb.Cluster); !ok { + return nil, fmt.Errorf("bug: type assertion failed for cluster %s", cluster.GetName()) + } else { + if clone == cluster { + return nil, fmt.Errorf("bug: cluster %s clones point to same address", cluster.GetName()) + } + return clone, nil + } +} + func CloneAttestationPolicy(policy *attestation_policy_proto.AttestationPolicy) (*attestation_policy_proto.AttestationPolicy, error) { if clone, ok := proto.Clone(policy).(*attestation_policy_proto.AttestationPolicy); !ok { return nil, fmt.Errorf("bug: type assertion failed for attestation policy %s", policy.Name) diff --git a/internal/pkg/test/fixtures/fixtures.go b/internal/pkg/test/fixtures/fixtures.go index df0f18f2..e2a51e23 100644 --- a/internal/pkg/test/fixtures/fixtures.go +++ b/internal/pkg/test/fixtures/fixtures.go @@ -38,42 +38,6 @@ var trustZoneFixtures map[string]*trust_zone_proto.TrustZone = map[string]*trust }, JwtIssuer: StringPtr("https://tz1.example.com"), BundleEndpointProfile: trust_zone_proto.BundleEndpointProfile_BUNDLE_ENDPOINT_PROFILE_HTTPS_SPIFFE.Enum(), - Clusters: []*clusterpb.Cluster{ - { - Name: StringPtr("local1"), - TrustZone: StringPtr("tz1"), - KubernetesContext: StringPtr("kind-local1"), - TrustProvider: &trust_provider_proto.TrustProvider{ - Kind: StringPtr("kubernetes"), - }, - Profile: StringPtr("kubernetes"), - ExtraHelmValues: func() *structpb.Struct { - ev := map[string]any{ - "global": map[string]any{ - "spire": map[string]any{ - // Modify multiple values in the same map. - "caSubject": map[string]any{ - "organization": "acme-org", - "commonName": "cn.example.com", - }, - }, - }, - "spire-server": map[string]any{ - // Modify an existing value. - "logLevel": "INFO", - // Customise a new value. - "nameOverride": "custom-server-name", - }, - } - value, err := structpb.NewStruct(ev) - if err != nil { - panic(err) - } - return value - }(), - ExternalServer: BoolPtr(false), - }, - }, }, "tz2": { Name: "tz2", @@ -94,18 +58,6 @@ var trustZoneFixtures map[string]*trust_zone_proto.TrustZone = map[string]*trust }, JwtIssuer: StringPtr("https://tz2.example.com"), BundleEndpointProfile: trust_zone_proto.BundleEndpointProfile_BUNDLE_ENDPOINT_PROFILE_HTTPS_WEB.Enum(), - Clusters: []*clusterpb.Cluster{ - { - Name: StringPtr("local2"), - TrustZone: StringPtr("tz2"), - KubernetesContext: StringPtr("kind-local2"), - TrustProvider: &trust_provider_proto.TrustProvider{ - Kind: StringPtr("kubernetes"), - }, - Profile: StringPtr("kubernetes"), - ExternalServer: BoolPtr(false), - }, - }, }, // tz3 has no federations or bound attestation policies. "tz3": { @@ -115,17 +67,6 @@ var trustZoneFixtures map[string]*trust_zone_proto.TrustZone = map[string]*trust Federations: []*federation_proto.Federation{}, AttestationPolicies: []*ap_binding_proto.APBinding{}, BundleEndpointProfile: trust_zone_proto.BundleEndpointProfile_BUNDLE_ENDPOINT_PROFILE_HTTPS_SPIFFE.Enum(), - Clusters: []*clusterpb.Cluster{ - { - Name: StringPtr("local3"), - TrustZone: StringPtr("tz3"), - KubernetesContext: StringPtr("kind-local3"), - TrustProvider: &trust_provider_proto.TrustProvider{ - Kind: StringPtr("kubernetes"), - }, - Profile: StringPtr("kubernetes"), - }, - }, }, // tz4 has no federations or bound attestation policies and uses the istio profile. "tz4": { @@ -134,17 +75,6 @@ var trustZoneFixtures map[string]*trust_zone_proto.TrustZone = map[string]*trust BundleEndpointUrl: StringPtr("127.0.0.4"), Federations: []*federation_proto.Federation{}, AttestationPolicies: []*ap_binding_proto.APBinding{}, - Clusters: []*clusterpb.Cluster{ - { - Name: StringPtr("local4"), - TrustZone: StringPtr("tz4"), - KubernetesContext: StringPtr("kind-local4"), - TrustProvider: &trust_provider_proto.TrustProvider{ - Kind: StringPtr("kubernetes"), - }, - Profile: StringPtr("istio"), - }, - }, }, // tz5 has no federations or bound attestation policies and has an external SPIRE server. "tz5": { @@ -153,18 +83,81 @@ var trustZoneFixtures map[string]*trust_zone_proto.TrustZone = map[string]*trust BundleEndpointUrl: StringPtr("127.0.0.5"), Federations: []*federation_proto.Federation{}, AttestationPolicies: []*ap_binding_proto.APBinding{}, - Clusters: []*clusterpb.Cluster{ - { - Name: StringPtr("local5"), - TrustZone: StringPtr("tz5"), - KubernetesContext: StringPtr("kind-local5"), - TrustProvider: &trust_provider_proto.TrustProvider{ - Kind: StringPtr("kubernetes"), + }, +} + +var clusterFixtures map[string]*clusterpb.Cluster = map[string]*clusterpb.Cluster{ + "local1": { + Name: StringPtr("local1"), + TrustZone: StringPtr("tz1"), + KubernetesContext: StringPtr("kind-local1"), + TrustProvider: &trust_provider_proto.TrustProvider{ + Kind: StringPtr("kubernetes"), + }, + Profile: StringPtr("kubernetes"), + ExtraHelmValues: func() *structpb.Struct { + ev := map[string]any{ + "global": map[string]any{ + "spire": map[string]any{ + // Modify multiple values in the same map. + "caSubject": map[string]any{ + "organization": "acme-org", + "commonName": "cn.example.com", + }, + }, }, - Profile: StringPtr("kubernetes"), - ExternalServer: BoolPtr(true), - }, + "spire-server": map[string]any{ + // Modify an existing value. + "logLevel": "INFO", + // Customise a new value. + "nameOverride": "custom-server-name", + }, + } + value, err := structpb.NewStruct(ev) + if err != nil { + panic(err) + } + return value + }(), + ExternalServer: BoolPtr(false), + }, + "local2": { + Name: StringPtr("local2"), + TrustZone: StringPtr("tz2"), + KubernetesContext: StringPtr("kind-local2"), + TrustProvider: &trust_provider_proto.TrustProvider{ + Kind: StringPtr("kubernetes"), + }, + Profile: StringPtr("kubernetes"), + ExternalServer: BoolPtr(false), + }, + "local3": { + Name: StringPtr("local3"), + TrustZone: StringPtr("tz3"), + KubernetesContext: StringPtr("kind-local3"), + TrustProvider: &trust_provider_proto.TrustProvider{ + Kind: StringPtr("kubernetes"), }, + Profile: StringPtr("kubernetes"), + }, + "local4": { + Name: StringPtr("local4"), + TrustZone: StringPtr("tz4"), + KubernetesContext: StringPtr("kind-local4"), + TrustProvider: &trust_provider_proto.TrustProvider{ + Kind: StringPtr("kubernetes"), + }, + Profile: StringPtr("istio"), + }, + "local5": { + Name: StringPtr("local5"), + TrustZone: StringPtr("tz5"), + KubernetesContext: StringPtr("kind-local5"), + TrustProvider: &trust_provider_proto.TrustProvider{ + Kind: StringPtr("kubernetes"), + }, + Profile: StringPtr("kubernetes"), + ExternalServer: BoolPtr(true), }, } @@ -284,6 +277,18 @@ func TrustZone(name string) *trust_zone_proto.TrustZone { return tz } +func Cluster(name string) *clusterpb.Cluster { + cluster, ok := clusterFixtures[name] + if !ok { + panic(fmt.Sprintf("invalid cluster fixture %s", name)) + } + cluster, err := proto.CloneCluster(cluster) + if err != nil { + panic(fmt.Sprintf("failed to clone cluster: %s", err)) + } + return cluster +} + func AttestationPolicy(name string) *attestation_policy_proto.AttestationPolicy { ap, ok := attestationPolicyFixtures[name] if !ok { diff --git a/internal/pkg/trustprovider/trustprovider.go b/internal/pkg/trustprovider/trustprovider.go index 4d562116..f49f498c 100644 --- a/internal/pkg/trustprovider/trustprovider.go +++ b/internal/pkg/trustprovider/trustprovider.go @@ -4,6 +4,7 @@ package trustprovider import ( + "errors" "fmt" trust_provider_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/trust_provider/v1alpha1" @@ -15,24 +16,26 @@ const ( ) type TrustProvider struct { - Name string Kind string AgentConfig TrustProviderAgentConfig ServerConfig TrustProviderServerConfig - Proto *trust_provider_proto.TrustProvider } -func NewTrustProvider(kind string) (*TrustProvider, error) { +func NewTrustProvider(tpp *trust_provider_proto.TrustProvider) (*TrustProvider, error) { + if tpp == nil { + return nil, errors.New("trust provider cannot be nil") + } + tp := &TrustProvider{ - Kind: kind, + Kind: tpp.GetKind(), } - if err := tp.GetValues(); err != nil { + if err := tp.getValues(); err != nil { return nil, err } return tp, nil } -func (tp *TrustProvider) GetValues() error { +func (tp *TrustProvider) getValues() error { switch tp.Kind { case "kubernetes": tp.AgentConfig = TrustProviderAgentConfig{ diff --git a/internal/pkg/trustzone/trustzone.go b/internal/pkg/trustzone/trustzone.go index 48443ec1..1936c612 100644 --- a/internal/pkg/trustzone/trustzone.go +++ b/internal/pkg/trustzone/trustzone.go @@ -8,36 +8,17 @@ import ( clusterpb "github.com/cofide/cofide-api-sdk/gen/go/proto/cluster/v1alpha1" trust_zone_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/trust_zone/v1alpha1" - "github.com/cofide/cofidectl/internal/pkg/trustprovider" + "github.com/cofide/cofidectl/pkg/plugin/datasource" ) -type TrustZone struct { - TrustZoneProto *trust_zone_proto.TrustZone -} - -func NewTrustZone(trustZone *trust_zone_proto.TrustZone) *TrustZone { - return &TrustZone{ - TrustZoneProto: trustZone, - } -} - -func (tz *TrustZone) GetTrustProvider() (*trustprovider.TrustProvider, error) { - cluster, err := GetClusterFromTrustZone(tz.TrustZoneProto) +// GetClusterFromTrustZone returns a cluster from a trust zone. +// For now there should be exactly one cluster per trust zone. +func GetClusterFromTrustZone(trustZone *trust_zone_proto.TrustZone, ds datasource.DataSource) (*clusterpb.Cluster, error) { + clusters, err := ds.ListClusters(trustZone.Name) if err != nil { return nil, err } - trustProviderProto := cluster.GetTrustProvider() - if trustProviderProto == nil { - return nil, fmt.Errorf("no trust provider for trust zone %s", tz.TrustZoneProto.Name) - } - return trustprovider.NewTrustProvider(trustProviderProto.GetKind()) -} - -// GetClusterFromTrustZone returns a cluster from a trust zone. -// For now there should be exactly one cluster per trust zone. -func GetClusterFromTrustZone(trustZone *trust_zone_proto.TrustZone) (*clusterpb.Cluster, error) { - clusters := trustZone.GetClusters() if clusters == nil || len(clusters) != 1 { return nil, fmt.Errorf("expected exactly one cluster per trust zone") } diff --git a/pkg/plugin/datasource/interface.go b/pkg/plugin/datasource/interface.go index 9f92848a..860ffee1 100644 --- a/pkg/plugin/datasource/interface.go +++ b/pkg/plugin/datasource/interface.go @@ -6,6 +6,7 @@ package datasource import ( ap_binding_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/ap_binding/v1alpha1" attestation_policy_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/attestation_policy/v1alpha1" + clusterpb "github.com/cofide/cofide-api-sdk/gen/go/proto/cluster/v1alpha1" federation_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/federation/v1alpha1" trust_zone_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/trust_zone/v1alpha1" "github.com/cofide/cofidectl/pkg/plugin/validator" @@ -15,10 +16,15 @@ import ( type DataSource interface { validator.Validator + AddTrustZone(*trust_zone_proto.TrustZone) (*trust_zone_proto.TrustZone, error) GetTrustZone(string) (*trust_zone_proto.TrustZone, error) ListTrustZones() ([]*trust_zone_proto.TrustZone, error) - AddTrustZone(*trust_zone_proto.TrustZone) (*trust_zone_proto.TrustZone, error) - UpdateTrustZone(*trust_zone_proto.TrustZone) error + UpdateTrustZone(*trust_zone_proto.TrustZone) (*trust_zone_proto.TrustZone, error) + + AddCluster(*clusterpb.Cluster) (*clusterpb.Cluster, error) + GetCluster(string, string) (*clusterpb.Cluster, error) + ListClusters(string) ([]*clusterpb.Cluster, error) + UpdateCluster(*clusterpb.Cluster) (*clusterpb.Cluster, error) AddAttestationPolicy(*attestation_policy_proto.AttestationPolicy) (*attestation_policy_proto.AttestationPolicy, error) GetAttestationPolicy(string) (*attestation_policy_proto.AttestationPolicy, error) diff --git a/pkg/plugin/datasource/plugin.go b/pkg/plugin/datasource/plugin.go index 951b579e..ec02278c 100644 --- a/pkg/plugin/datasource/plugin.go +++ b/pkg/plugin/datasource/plugin.go @@ -8,6 +8,7 @@ import ( ap_binding_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/ap_binding/v1alpha1" attestation_policy_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/attestation_policy/v1alpha1" + clusterpb "github.com/cofide/cofide-api-sdk/gen/go/proto/cluster/v1alpha1" cofidectl_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/cofidectl_plugin/v1alpha1" federation_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/federation/v1alpha1" trust_zone_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/trust_zone/v1alpha1" @@ -55,6 +56,15 @@ func (c *DataSourcePluginClientGRPC) Validate(ctx context.Context) error { return err } +func (c *DataSourcePluginClientGRPC) AddTrustZone(trustZone *trust_zone_proto.TrustZone) (*trust_zone_proto.TrustZone, error) { + resp, err := c.client.AddTrustZone(c.ctx, &cofidectl_proto.AddTrustZoneRequest{TrustZone: trustZone}) + if err != nil { + return nil, err + } + + return resp.TrustZone, nil +} + func (c *DataSourcePluginClientGRPC) GetTrustZone(name string) (*trust_zone_proto.TrustZone, error) { resp, err := c.client.GetTrustZone(c.ctx, &cofidectl_proto.GetTrustZoneRequest{Name: &name}) if err != nil { @@ -73,8 +83,8 @@ func (c *DataSourcePluginClientGRPC) ListTrustZones() ([]*trust_zone_proto.Trust return resp.TrustZones, nil } -func (c *DataSourcePluginClientGRPC) AddTrustZone(trustZone *trust_zone_proto.TrustZone) (*trust_zone_proto.TrustZone, error) { - resp, err := c.client.AddTrustZone(c.ctx, &cofidectl_proto.AddTrustZoneRequest{TrustZone: trustZone}) +func (c *DataSourcePluginClientGRPC) UpdateTrustZone(trustZone *trust_zone_proto.TrustZone) (*trust_zone_proto.TrustZone, error) { + resp, err := c.client.UpdateTrustZone(c.ctx, &cofidectl_proto.UpdateTrustZoneRequest{TrustZone: trustZone}) if err != nil { return nil, err } @@ -82,9 +92,40 @@ func (c *DataSourcePluginClientGRPC) AddTrustZone(trustZone *trust_zone_proto.Tr return resp.TrustZone, nil } -func (c *DataSourcePluginClientGRPC) UpdateTrustZone(trustZone *trust_zone_proto.TrustZone) error { - _, err := c.client.UpdateTrustZone(c.ctx, &cofidectl_proto.UpdateTrustZoneRequest{TrustZone: trustZone}) - return err +func (c *DataSourcePluginClientGRPC) AddCluster(cluster *clusterpb.Cluster) (*clusterpb.Cluster, error) { + resp, err := c.client.AddCluster(c.ctx, &cofidectl_proto.AddClusterRequest{Cluster: cluster}) + if err != nil { + return nil, err + } + + return resp.Cluster, nil +} + +func (c *DataSourcePluginClientGRPC) GetCluster(name, trustZone string) (*clusterpb.Cluster, error) { + resp, err := c.client.GetCluster(c.ctx, &cofidectl_proto.GetClusterRequest{Name: &name, TrustZone: &trustZone}) + if err != nil { + return nil, err + } + + return resp.Cluster, nil +} + +func (c *DataSourcePluginClientGRPC) ListClusters(trustZone string) ([]*clusterpb.Cluster, error) { + resp, err := c.client.ListClusters(c.ctx, &cofidectl_proto.ListClustersRequest{TrustZone: &trustZone}) + if err != nil { + return nil, err + } + + return resp.Clusters, nil +} + +func (c *DataSourcePluginClientGRPC) UpdateCluster(cluster *clusterpb.Cluster) (*clusterpb.Cluster, error) { + resp, err := c.client.UpdateCluster(c.ctx, &cofidectl_proto.UpdateClusterRequest{Cluster: cluster}) + if err != nil { + return nil, err + } + + return resp.Cluster, nil } func (c *DataSourcePluginClientGRPC) AddAttestationPolicy(policy *attestation_policy_proto.AttestationPolicy) (*attestation_policy_proto.AttestationPolicy, error) { @@ -167,6 +208,14 @@ func (s *GRPCServer) Validate(ctx context.Context, req *cofidectl_proto.Validate return &cofidectl_proto.ValidateResponse{}, nil } +func (s *GRPCServer) AddTrustZone(_ context.Context, req *cofidectl_proto.AddTrustZoneRequest) (*cofidectl_proto.AddTrustZoneResponse, error) { + trustZone, err := s.Impl.AddTrustZone(req.TrustZone) + if err != nil { + return nil, err + } + return &cofidectl_proto.AddTrustZoneResponse{TrustZone: trustZone}, nil +} + func (s *GRPCServer) GetTrustZone(_ context.Context, req *cofidectl_proto.GetTrustZoneRequest) (*cofidectl_proto.GetTrustZoneResponse, error) { trustZone, err := s.Impl.GetTrustZone(req.GetName()) if err != nil { @@ -183,20 +232,44 @@ func (s *GRPCServer) ListTrustZones(_ context.Context, req *cofidectl_proto.List return &cofidectl_proto.ListTrustZonesResponse{TrustZones: trustZones}, nil } -func (s *GRPCServer) AddTrustZone(_ context.Context, req *cofidectl_proto.AddTrustZoneRequest) (*cofidectl_proto.AddTrustZoneResponse, error) { - trustZone, err := s.Impl.AddTrustZone(req.TrustZone) +func (s *GRPCServer) UpdateTrustZone(_ context.Context, req *cofidectl_proto.UpdateTrustZoneRequest) (*cofidectl_proto.UpdateTrustZoneResponse, error) { + trustZone, err := s.Impl.UpdateTrustZone(req.TrustZone) if err != nil { return nil, err } - return &cofidectl_proto.AddTrustZoneResponse{TrustZone: trustZone}, nil + return &cofidectl_proto.UpdateTrustZoneResponse{TrustZone: trustZone}, nil } -func (s *GRPCServer) UpdateTrustZone(_ context.Context, req *cofidectl_proto.UpdateTrustZoneRequest) (*cofidectl_proto.UpdateTrustZoneResponse, error) { - err := s.Impl.UpdateTrustZone(req.TrustZone) +func (s *GRPCServer) AddCluster(_ context.Context, req *cofidectl_proto.AddClusterRequest) (*cofidectl_proto.AddClusterResponse, error) { + cluster, err := s.Impl.AddCluster(req.Cluster) + if err != nil { + return nil, err + } + return &cofidectl_proto.AddClusterResponse{Cluster: cluster}, nil +} + +func (s *GRPCServer) GetCluster(_ context.Context, req *cofidectl_proto.GetClusterRequest) (*cofidectl_proto.GetClusterResponse, error) { + cluster, err := s.Impl.GetCluster(req.GetName(), req.GetTrustZone()) + if err != nil { + return nil, err + } + return &cofidectl_proto.GetClusterResponse{Cluster: cluster}, nil +} + +func (s *GRPCServer) ListClusters(_ context.Context, req *cofidectl_proto.ListClustersRequest) (*cofidectl_proto.ListClustersResponse, error) { + clusters, err := s.Impl.ListClusters(req.GetTrustZone()) + if err != nil { + return nil, err + } + return &cofidectl_proto.ListClustersResponse{Clusters: clusters}, nil +} + +func (s *GRPCServer) UpdateCluster(_ context.Context, req *cofidectl_proto.UpdateClusterRequest) (*cofidectl_proto.UpdateClusterResponse, error) { + cluster, err := s.Impl.UpdateCluster(req.Cluster) if err != nil { return nil, err } - return &cofidectl_proto.UpdateTrustZoneResponse{}, nil + return &cofidectl_proto.UpdateClusterResponse{Cluster: cluster}, nil } func (s *GRPCServer) AddAttestationPolicy(_ context.Context, req *cofidectl_proto.AddAttestationPolicyRequest) (*cofidectl_proto.AddAttestationPolicyResponse, error) { diff --git a/pkg/plugin/local/local.go b/pkg/plugin/local/local.go index 33ec5af5..c7632cb8 100644 --- a/pkg/plugin/local/local.go +++ b/pkg/plugin/local/local.go @@ -10,12 +10,12 @@ import ( ap_binding_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/ap_binding/v1alpha1" attestation_policy_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/attestation_policy/v1alpha1" + clusterpb "github.com/cofide/cofide-api-sdk/gen/go/proto/cluster/v1alpha1" federation_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/federation/v1alpha1" trust_provider_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/trust_provider/v1alpha1" trust_zone_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/trust_zone/v1alpha1" "github.com/cofide/cofidectl/internal/pkg/config" "github.com/cofide/cofidectl/internal/pkg/proto" - "github.com/cofide/cofidectl/internal/pkg/trustzone" ) type LocalDataSource struct { @@ -90,29 +90,41 @@ func (lds *LocalDataSource) GetTrustZone(id string) (*trust_zone_proto.TrustZone return proto.CloneTrustZone(trustZone) } -func (lds *LocalDataSource) UpdateTrustZone(trustZone *trust_zone_proto.TrustZone) error { +func (lds *LocalDataSource) ListTrustZones() ([]*trust_zone_proto.TrustZone, error) { + trustZones := []*trust_zone_proto.TrustZone{} + for _, trustZone := range lds.config.TrustZones { + trustZone, err := proto.CloneTrustZone(trustZone) + if err != nil { + return nil, err + } + trustZones = append(trustZones, trustZone) + } + return trustZones, nil +} + +func (lds *LocalDataSource) UpdateTrustZone(trustZone *trust_zone_proto.TrustZone) (*trust_zone_proto.TrustZone, error) { for i, current := range lds.config.TrustZones { if current.Name == trustZone.Name { if err := validateTrustZoneUpdate(current, trustZone); err != nil { - return err + return nil, err } trustZone, err := proto.CloneTrustZone(trustZone) if err != nil { - return err + return nil, err } lds.config.TrustZones[i] = trustZone if err := lds.updateDataFile(); err != nil { - return fmt.Errorf("failed to update trust zone %s in local config: %s", trustZone.Name, err) + return nil, fmt.Errorf("failed to update trust zone %s in local config: %s", trustZone.Name, err) } - return nil + return proto.CloneTrustZone(trustZone) } } - return fmt.Errorf("failed to find trust zone %s in local config", trustZone.Name) + return nil, fmt.Errorf("failed to find trust zone %s in local config", trustZone.Name) } func validateTrustZoneUpdate(current, new *trust_zone_proto.TrustZone) error { @@ -122,17 +134,6 @@ func validateTrustZoneUpdate(current, new *trust_zone_proto.TrustZone) error { if new.TrustDomain != current.TrustDomain { return fmt.Errorf("cannot update trust domain for existing trust zone %s", current.Name) } - currentCluster, err := trustzone.GetClusterFromTrustZone(current) - if err != nil { - return err - } - newCluster, err := trustzone.GetClusterFromTrustZone(new) - if err != nil { - return err - } - if err := validateTrustProviderUpdate(current.Name, currentCluster.TrustProvider, newCluster.TrustProvider); err != nil { - return err - } // The following should be updated though other means. if !slices.EqualFunc(new.Federations, current.Federations, proto.FederationsEqual) { return fmt.Errorf("cannot update federations for existing trust zone %s", current.Name) @@ -143,29 +144,113 @@ func validateTrustZoneUpdate(current, new *trust_zone_proto.TrustZone) error { return nil } -func validateTrustProviderUpdate(tzName string, current, new *trust_provider_proto.TrustProvider) error { - if current == nil { - return fmt.Errorf("no trust provider in existing trust zone %s", tzName) +func (lds *LocalDataSource) AddCluster(cluster *clusterpb.Cluster) (*clusterpb.Cluster, error) { + name := cluster.GetName() + trustZone := cluster.GetTrustZone() + + if _, ok := lds.config.GetClusterByName(name, trustZone); ok { + return nil, fmt.Errorf("cluster %s already exists in trust zone %s in local config", name, trustZone) } - if new == nil { - return fmt.Errorf("cannot remove trust provider for trust zone %s", tzName) + + if len(lds.config.GetClustersByTrustZone(trustZone)) != 0 { + return nil, fmt.Errorf("trust zone %s already has a cluster", trustZone) } - if new.GetKind() != current.GetKind() { - return fmt.Errorf("cannot update trust provider kind for existing trust zone %s", tzName) + + cluster, err := proto.CloneCluster(cluster) + if err != nil { + return nil, err } - return nil + + lds.config.Clusters = append(lds.config.Clusters, cluster) + if err := lds.updateDataFile(); err != nil { + return nil, fmt.Errorf("failed to add cluster %s in trust zone %s to local config: %s", name, trustZone, err) + } + return cluster, nil } -func (lds *LocalDataSource) ListTrustZones() ([]*trust_zone_proto.TrustZone, error) { - trustZones := []*trust_zone_proto.TrustZone{} - for _, trustZone := range lds.config.TrustZones { - trustZone, err := proto.CloneTrustZone(trustZone) +func (lds *LocalDataSource) GetCluster(name, trustZone string) (*clusterpb.Cluster, error) { + cluster, ok := lds.config.GetClusterByName(name, trustZone) + if !ok { + return nil, fmt.Errorf("failed to find cluster %s in trust zone %s in local config", name, trustZone) + } + + return proto.CloneCluster(cluster) +} + +func (lds *LocalDataSource) ListClusters(trustZone string) ([]*clusterpb.Cluster, error) { + clusters := []*clusterpb.Cluster{} + for _, cluster := range lds.config.GetClustersByTrustZone(trustZone) { + cluster, err := proto.CloneCluster(cluster) if err != nil { return nil, err } - trustZones = append(trustZones, trustZone) + clusters = append(clusters, cluster) } - return trustZones, nil + return clusters, nil +} + +func (lds *LocalDataSource) UpdateCluster(cluster *clusterpb.Cluster) (*clusterpb.Cluster, error) { + name := cluster.GetName() + trustZone := cluster.GetTrustZone() + + for i, current := range lds.config.Clusters { + if current.GetName() == name { + if err := validateClusterUpdate(current, cluster); err != nil { + return nil, err + } + + cluster, err := proto.CloneCluster(cluster) + if err != nil { + return nil, err + } + + lds.config.Clusters[i] = cluster + + if err := lds.updateDataFile(); err != nil { + return nil, fmt.Errorf("failed to update cluster %s in trust zone %s in local config: %s", name, trustZone, err) + } + + return proto.CloneCluster(cluster) + } + } + + return nil, fmt.Errorf("failed to find cluster %s in trust zone %s in local config", name, trustZone) +} + +func validateClusterUpdate(current, new *clusterpb.Cluster) error { + name := current.GetName() + trustZone := current.GetTrustZone() + + if new.GetName() != current.GetName() { + return fmt.Errorf("cannot update name for existing cluster %s in trust zone %s", name, trustZone) + } + + if new.GetTrustZone() != current.GetTrustZone() { + return fmt.Errorf("cannot update trust zone for existing cluster %s in trust zone %s", name, trustZone) + } + + if new.GetProfile() != current.GetProfile() { + return fmt.Errorf("cannot update profile for existing cluster %s in trust zone %s", name, trustZone) + } + + if err := validateTrustProviderUpdate(current.GetName(), current.GetTrustZone(), current.TrustProvider, new.TrustProvider); err != nil { + return err + } + + return nil +} + +func validateTrustProviderUpdate(cluster, tzName string, current, new *trust_provider_proto.TrustProvider) error { + if current == nil { + return fmt.Errorf("no trust provider in existing cluster %s in trust zone %s", cluster, tzName) + } + if new == nil { + return fmt.Errorf("cannot remove trust provider for cluster %s in trust zone %s", cluster, tzName) + } + if new.GetKind() != current.GetKind() { + return fmt.Errorf("cannot update trust provider kind for existing cluster %s in trust zone %s", cluster, tzName) + } + return nil } func (lds *LocalDataSource) AddAttestationPolicy(policy *attestation_policy_proto.AttestationPolicy) (*attestation_policy_proto.AttestationPolicy, error) { diff --git a/pkg/plugin/local/local_test.go b/pkg/plugin/local/local_test.go index 9958f39a..7ecf61f6 100644 --- a/pkg/plugin/local/local_test.go +++ b/pkg/plugin/local/local_test.go @@ -55,6 +55,7 @@ func TestNewLocalDataSource(t *testing.T) { }, wantConfig: &config.Config{ TrustZones: []*trust_zone_proto.TrustZone{}, + Clusters: []*clusterpb.Cluster{}, AttestationPolicies: []*attestation_policy_proto.AttestationPolicy{}, PluginConfig: map[string]*structpb.Struct{}, Plugins: fixtures.Plugins("plugins1"), @@ -196,8 +197,6 @@ func TestLocalDataSource_UpdateTrustZone(t *testing.T) { tz := fixtures.TrustZone("tz1") tz.Bundle = fixtures.StringPtr("new bundle") tz.BundleEndpointUrl = fixtures.StringPtr("http://new.bundle") - tz.Clusters[0].Name = fixtures.StringPtr("new-cluster") - tz.Clusters[0].KubernetesContext = fixtures.StringPtr("new-context") return tz }(), wantErr: false, @@ -218,26 +217,6 @@ func TestLocalDataSource_UpdateTrustZone(t *testing.T) { wantErr: true, wantErrString: "cannot update trust domain for existing trust zone tz1", }, - { - name: "disallowed nil trust provider", - trustZone: func() *trust_zone_proto.TrustZone { - tz := fixtures.TrustZone("tz1") - tz.Clusters[0].TrustProvider = nil - return tz - }(), - wantErr: true, - wantErrString: "cannot remove trust provider for trust zone tz1", - }, - { - name: "disallowed trust provider kind", - trustZone: func() *trust_zone_proto.TrustZone { - tz := fixtures.TrustZone("tz1") - tz.Clusters[0].TrustProvider.Kind = fixtures.StringPtr("invalid") - return tz - }(), - wantErr: true, - wantErrString: "cannot update trust provider kind for existing trust zone tz1", - }, { name: "disallowed federation", trustZone: func() *trust_zone_proto.TrustZone { @@ -262,39 +241,6 @@ func TestLocalDataSource_UpdateTrustZone(t *testing.T) { wantErr: true, wantErrString: "cannot update federations for existing trust zone tz1", }, - { - name: "empty clusters list", - trustZone: func() *trust_zone_proto.TrustZone { - tz := fixtures.TrustZone("tz1") - tz.Clusters = []*clusterpb.Cluster{} - return tz - }(), - wantErr: true, - wantErrString: "expected exactly one cluster per trust zone", - }, - { - name: "nil clusters list", - trustZone: func() *trust_zone_proto.TrustZone { - tz := fixtures.TrustZone("tz1") - tz.Clusters = nil - return tz - }(), - wantErr: true, - wantErrString: "expected exactly one cluster per trust zone", - }, - { - name: "multiple clusters list", - trustZone: func() *trust_zone_proto.TrustZone { - tz := fixtures.TrustZone("tz1") - tz.Clusters = []*clusterpb.Cluster{ - tz.Clusters[0], - tz.Clusters[0], - } - return tz - }(), - wantErr: true, - wantErrString: "expected exactly one cluster per trust zone", - }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -302,16 +248,20 @@ func TestLocalDataSource_UpdateTrustZone(t *testing.T) { TrustZones: []*trust_zone_proto.TrustZone{ fixtures.TrustZone("tz1"), }, + Clusters: []*clusterpb.Cluster{ + fixtures.Cluster("local1"), + }, Plugins: fixtures.Plugins("plugins1"), } lds, loader := buildLocalDataSource(t, cfg) - err := lds.UpdateTrustZone(tt.trustZone) + trustZone, err := lds.UpdateTrustZone(tt.trustZone) if tt.wantErr { require.Error(t, err) assert.ErrorContains(t, err, tt.wantErrString) } else { require.Nil(t, err) + assert.EqualExportedValues(t, tt.trustZone, trustZone) assert.EqualExportedValues(t, tt.trustZone, lds.config.TrustZones[0]) assert.False(t, slices.Contains(lds.config.TrustZones, tt.trustZone), "Pointer to trust zone stored in config") // Check that the trust zone was persisted. @@ -368,6 +318,290 @@ func TestLocalDataSource_ListTrustZones(t *testing.T) { } } +func TestLocalDataSource_AddCluster(t *testing.T) { + t.Parallel() + tests := []struct { + name string + config *config.Config + cluster *clusterpb.Cluster + wantErr bool + wantErrString string + }{ + { + name: "success", + config: config.NewConfig(), + cluster: fixtures.Cluster("local1"), + wantErr: false, + }, + { + name: "duplicate", + config: &config.Config{ + Clusters: []*clusterpb.Cluster{ + fixtures.Cluster("local1"), + }, + Plugins: fixtures.Plugins("plugins1"), + }, + cluster: fixtures.Cluster("local1"), + wantErr: true, + wantErrString: "cluster local1 already exists in trust zone tz1 in local config", + }, + { + name: "one cluster per trust zone", + config: &config.Config{ + Clusters: []*clusterpb.Cluster{ + fixtures.Cluster("local1"), + }, + Plugins: fixtures.Plugins("plugins1"), + }, + cluster: func() *clusterpb.Cluster { + cluster := fixtures.Cluster("local1") + name := "local2" + cluster.Name = &name + return cluster + }(), + wantErr: true, + wantErrString: "trust zone tz1 already has a cluster", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + lds, loader := buildLocalDataSource(t, tt.config) + + got, err := lds.AddCluster(tt.cluster) + if tt.wantErr { + require.Error(t, err) + assert.ErrorContains(t, err, tt.wantErrString) + } else { + assert.EqualExportedValues(t, tt.cluster, got) + assert.False(t, slices.Contains(lds.config.Clusters, tt.cluster), "Pointer to cluster stored in config") + // Check that the trust zone was persisted. + gotConfig := readConfig(t, loader) + gotCluster, ok := gotConfig.GetClusterByName(tt.cluster.GetName(), tt.cluster.GetTrustZone()) + assert.True(t, ok) + assert.EqualExportedValues(t, tt.cluster, gotCluster) + } + }) + } +} + +func TestLocalDataSource_GetCluster(t *testing.T) { + t.Parallel() + tests := []struct { + name string + cluster string + trustZone string + wantErr bool + wantErrString string + }{ + { + name: "success", + cluster: "local1", + trustZone: "tz1", + wantErr: false, + }, + { + name: "non-existent", + cluster: "local2", + trustZone: "tz2", + wantErr: true, + wantErrString: "failed to find cluster local2 in trust zone tz2 in local config", + }, + { + name: "scoped to trust zone", + cluster: "local1", + trustZone: "tz2", + wantErr: true, + wantErrString: "failed to find cluster local1 in trust zone tz2 in local config", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cfg := &config.Config{ + Clusters: []*clusterpb.Cluster{ + fixtures.Cluster("local1"), + }, + Plugins: fixtures.Plugins("plugins1"), + } + lds, _ := buildLocalDataSource(t, cfg) + + got, err := lds.GetCluster(tt.cluster, tt.trustZone) + if tt.wantErr { + require.Error(t, err) + assert.ErrorContains(t, err, tt.wantErrString) + } else { + require.Nil(t, err) + assert.EqualExportedValues(t, cfg.Clusters[0], got) + assert.False(t, slices.Contains(lds.config.Clusters, got), "Pointer to cluster in config returned") + } + }) + } +} + +func TestLocalDataSource_ListClusters(t *testing.T) { + t.Parallel() + tests := []struct { + name string + config *config.Config + wantClusters []*clusterpb.Cluster + wantErr bool + }{ + { + name: "none", + config: config.NewConfig(), + wantClusters: []*clusterpb.Cluster{}, + wantErr: false, + }, + { + name: "two", + config: &config.Config{ + Clusters: []*clusterpb.Cluster{ + fixtures.Cluster("local1"), + fixtures.Cluster("local1"), + }, + Plugins: fixtures.Plugins("plugins1"), + }, + wantClusters: []*clusterpb.Cluster{ + fixtures.Cluster("local1"), + fixtures.Cluster("local1"), + }, + wantErr: false, + }, + { + name: "scoped to trust zone", + config: &config.Config{ + Clusters: []*clusterpb.Cluster{ + fixtures.Cluster("local1"), + fixtures.Cluster("local2"), + }, + Plugins: fixtures.Plugins("plugins1"), + }, + wantClusters: []*clusterpb.Cluster{ + fixtures.Cluster("local1"), + }, + wantErr: false, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + lds, _ := buildLocalDataSource(t, tt.config) + got, err := lds.ListClusters("tz1") + if tt.wantErr { + require.Error(t, err) + } else { + require.Nil(t, err) + if diff := cmp.Diff(got, tt.wantClusters, protocmp.Transform()); diff != "" { + t.Errorf("LocalDataSource.ListClusters() mismatch (-want,+got):\n%s", diff) + } + for _, gotCluster := range got { + assert.False(t, slices.Contains(lds.config.Clusters, gotCluster), "Pointer to cluster in config returned") + } + } + }) + } +} + +func TestLocalDataSource_UpdateCluster(t *testing.T) { + t.Parallel() + tests := []struct { + name string + cluster *clusterpb.Cluster + wantErr bool + wantErrString string + }{ + { + name: "no changes", + cluster: fixtures.Cluster("local1"), + wantErr: false, + }, + { + name: "allowed changes", + cluster: func() *clusterpb.Cluster { + cluster := fixtures.Cluster("local1") + cluster.KubernetesContext = fixtures.StringPtr("new-context") + cluster.ExtraHelmValues = nil + return cluster + }(), + wantErr: false, + }, + { + name: "non-existent", + cluster: fixtures.Cluster("local2"), + wantErr: true, + wantErrString: "failed to find cluster local2 in trust zone tz2 in local config", + }, + { + name: "disallowed trust zone", + cluster: func() *clusterpb.Cluster { + cluster := fixtures.Cluster("local1") + cluster.TrustZone = fixtures.StringPtr("tz2") + return cluster + }(), + wantErr: true, + wantErrString: "cannot update trust zone for existing cluster local1 in trust zone tz1", + }, + { + name: "disallowed nil trust provider", + cluster: func() *clusterpb.Cluster { + cluster := fixtures.Cluster("local1") + cluster.TrustProvider = nil + return cluster + }(), + wantErr: true, + wantErrString: "cannot remove trust provider for cluster local1 in trust zone tz1", + }, + { + name: "disallowed trust provider kind", + cluster: func() *clusterpb.Cluster { + cluster := fixtures.Cluster("local1") + cluster.TrustProvider.Kind = fixtures.StringPtr("invalid") + return cluster + }(), + wantErr: true, + wantErrString: "cannot update trust provider kind for existing cluster local1 in trust zone tz1", + }, + { + name: "disallowed profile", + cluster: func() *clusterpb.Cluster { + cluster := fixtures.Cluster("local1") + cluster.Profile = fixtures.StringPtr("istio") + return cluster + }(), + wantErr: true, + wantErrString: "cannot update profile for existing cluster local1 in trust zone tz1", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cfg := &config.Config{ + TrustZones: []*trust_zone_proto.TrustZone{ + fixtures.TrustZone("tz1"), + }, + Clusters: []*clusterpb.Cluster{ + fixtures.Cluster("local1"), + }, + Plugins: fixtures.Plugins("plugins1"), + } + lds, loader := buildLocalDataSource(t, cfg) + + cluster, err := lds.UpdateCluster(tt.cluster) + if tt.wantErr { + require.Error(t, err) + assert.ErrorContains(t, err, tt.wantErrString) + } else { + require.Nil(t, err) + assert.EqualExportedValues(t, tt.cluster, cluster) + assert.EqualExportedValues(t, tt.cluster, lds.config.Clusters[0]) + assert.False(t, slices.Contains(lds.config.Clusters, tt.cluster), "Pointer to cluster stored in config") + // Check that the cluster was persisted. + gotConfig := readConfig(t, loader) + gotCluster, ok := gotConfig.GetClusterByName(tt.cluster.GetName(), tt.cluster.GetTrustZone()) + assert.True(t, ok) + assert.EqualExportedValues(t, tt.cluster, gotCluster) + } + }) + } +} + func TestLocalDataSource_AddAttestationPolicy(t *testing.T) { t.Parallel() tests := []struct { diff --git a/pkg/plugin/provision/spirehelm/spirehelm.go b/pkg/plugin/provision/spirehelm/spirehelm.go index fc6bba55..714c1a8c 100644 --- a/pkg/plugin/provision/spirehelm/spirehelm.go +++ b/pkg/plugin/provision/spirehelm/spirehelm.go @@ -6,7 +6,6 @@ package spirehelm import ( "context" "fmt" - "iter" clusterpb "github.com/cofide/cofide-api-sdk/gen/go/proto/cluster/v1alpha1" provisionpb "github.com/cofide/cofide-api-sdk/gen/go/proto/provision_plugin/v1alpha1" @@ -31,6 +30,12 @@ type SpireHelm struct { spireAPIFactory SPIREAPIFactory } +// TrustZoneCluster provides a container for a trust zone and a cluster associated with it. +type TrustZoneCluster struct { + TrustZone *trust_zone_proto.TrustZone + Cluster *clusterpb.Cluster +} + func NewSpireHelm(providerFactory ProviderFactory, spireAPIFactory SPIREAPIFactory) *SpireHelm { if providerFactory == nil { providerFactory = &HelmSPIREProviderFactory{} @@ -70,7 +75,7 @@ func (h *SpireHelm) TearDown(ctx context.Context, ds datasource.DataSource, kube } func (h *SpireHelm) deploy(ctx context.Context, ds datasource.DataSource, kubeCfgFile string, statusCh chan<- *provisionpb.Status) error { - trustZones, err := h.ListTrustZones(ds) + trustZoneClusters, err := h.ListTrustZoneClusters(ds) if err != nil { statusCh <- provision.StatusError("Deploying", "Failed listing trust zones", err) return err @@ -80,20 +85,20 @@ func (h *SpireHelm) deploy(ctx context.Context, ds datasource.DataSource, kubeCf return err } - if err := h.InstallSPIREStack(ctx, ds, trustZones, statusCh); err != nil { + if err := h.InstallSPIREStack(ctx, ds, trustZoneClusters, statusCh); err != nil { return err } - if err := h.WatchAndConfigure(ctx, ds, trustZones, kubeCfgFile, statusCh); err != nil { + if err := h.WatchAndConfigure(ctx, ds, trustZoneClusters, kubeCfgFile, statusCh); err != nil { return err } - if err := h.ApplyPostInstallHelmConfig(ctx, ds, trustZones, statusCh); err != nil { + if err := h.ApplyPostInstallHelmConfig(ctx, ds, trustZoneClusters, statusCh); err != nil { return err } // Wait for spire-server to be ready again. - if err := h.WatchAndConfigure(ctx, ds, trustZones, kubeCfgFile, statusCh); err != nil { + if err := h.WatchAndConfigure(ctx, ds, trustZoneClusters, kubeCfgFile, statusCh); err != nil { return err } @@ -101,20 +106,20 @@ func (h *SpireHelm) deploy(ctx context.Context, ds datasource.DataSource, kubeCf } func (h *SpireHelm) tearDown(ctx context.Context, ds datasource.DataSource, statusCh chan<- *provisionpb.Status) error { - trustZones, err := h.ListTrustZones(ds) + trustZoneClusters, err := h.ListTrustZoneClusters(ds) if err != nil { statusCh <- provision.StatusError("Uninstalling", "Failed listing trust zones", err) return err } - if err := h.UninstallSPIREStack(ctx, trustZones, statusCh); err != nil { + if err := h.UninstallSPIREStack(ctx, ds, trustZoneClusters, statusCh); err != nil { return err } return nil } -// ListTrustZones returns a list of all trust zones. If no trust zones exist, it returns an error. -func (h *SpireHelm) ListTrustZones(ds datasource.DataSource) ([]*trust_zone_proto.TrustZone, error) { +// ListTrustZoneClusters returns a slice of TrustZoneClusters. If no trust zones exist, it returns an error. +func (h *SpireHelm) ListTrustZoneClusters(ds datasource.DataSource) ([]TrustZoneCluster, error) { trustZones, err := ds.ListTrustZones() if err != nil { return nil, err @@ -124,13 +129,18 @@ func (h *SpireHelm) ListTrustZones(ds datasource.DataSource) ([]*trust_zone_prot return nil, fmt.Errorf("no trust zones have been configured") } - // Sanity check that all trust zones have exactly one cluster. + trustZoneClusters := make([]TrustZoneCluster, 0, len(trustZones)) + for _, trustZone := range trustZones { - if _, err := trustzone.GetClusterFromTrustZone(trustZone); err != nil { + // Sanity check that the trust zone has exactly one cluster. + cluster, err := trustzone.GetClusterFromTrustZone(trustZone, ds) + if err != nil { return nil, err } + trustZoneCluster := TrustZoneCluster{TrustZone: trustZone, Cluster: cluster} + trustZoneClusters = append(trustZoneClusters, trustZoneCluster) } - return trustZones, nil + return trustZoneClusters, nil } func (h *SpireHelm) AddSPIRERepository(ctx context.Context, statusCh chan<- *provisionpb.Status) error { @@ -143,8 +153,11 @@ func (h *SpireHelm) AddSPIRERepository(ctx context.Context, statusCh chan<- *pro return prov.AddRepository(statusCh) } -func (h *SpireHelm) InstallSPIREStack(ctx context.Context, ds datasource.DataSource, trustZones []*trust_zone_proto.TrustZone, statusCh chan<- *provisionpb.Status) error { - for trustZone, cluster := range iterTZClusters(trustZones) { +func (h *SpireHelm) InstallSPIREStack(ctx context.Context, ds datasource.DataSource, trustZoneClusters []TrustZoneCluster, statusCh chan<- *provisionpb.Status) error { + for _, tzc := range trustZoneClusters { + trustZone := tzc.TrustZone + cluster := tzc.Cluster + prov, err := h.providerFactory.Build(ctx, ds, trustZone, cluster, true) if err != nil { sb := provision.NewStatusBuilder(trustZone.Name, cluster.GetName()) @@ -159,9 +172,12 @@ func (h *SpireHelm) InstallSPIREStack(ctx context.Context, ds datasource.DataSou return nil } -func (h *SpireHelm) WatchAndConfigure(ctx context.Context, ds datasource.DataSource, trustZones []*trust_zone_proto.TrustZone, kubeCfgFile string, statusCh chan<- *provisionpb.Status) error { +func (h *SpireHelm) WatchAndConfigure(ctx context.Context, ds datasource.DataSource, trustZoneClusters []TrustZoneCluster, kubeCfgFile string, statusCh chan<- *provisionpb.Status) error { // Wait for SPIRE servers to be available and update status before applying federation(s) - for trustZone, cluster := range iterTZClusters(trustZones) { + for _, tzc := range trustZoneClusters { + trustZone := tzc.TrustZone + cluster := tzc.Cluster + if cluster.GetExternalServer() { sb := provision.NewStatusBuilder(trustZone.Name, cluster.GetName()) statusCh <- sb.Done("Ready", "Skipped waiting for external SPIRE server pod and service") @@ -212,7 +228,7 @@ func (h *SpireHelm) GetBundleAndEndpoint( trustZone.Bundle = &bundle - if err := ds.UpdateTrustZone(trustZone); err != nil { + if _, err := ds.UpdateTrustZone(trustZone); err != nil { msg := fmt.Sprintf("Failed updating trust zone %s", trustZone.Name) statusCh <- provision.StatusError("Waiting", msg, err) return err @@ -223,8 +239,11 @@ func (h *SpireHelm) GetBundleAndEndpoint( return nil } -func (h *SpireHelm) ApplyPostInstallHelmConfig(ctx context.Context, ds datasource.DataSource, trustZones []*trust_zone_proto.TrustZone, statusCh chan<- *provisionpb.Status) error { - for trustZone, cluster := range iterTZClusters(trustZones) { +func (h *SpireHelm) ApplyPostInstallHelmConfig(ctx context.Context, ds datasource.DataSource, trustZoneClusters []TrustZoneCluster, statusCh chan<- *provisionpb.Status) error { + for _, tzc := range trustZoneClusters { + trustZone := tzc.TrustZone + cluster := tzc.Cluster + prov, err := h.providerFactory.Build(ctx, ds, trustZone, cluster, true) if err != nil { sb := provision.NewStatusBuilder(trustZone.Name, cluster.GetName()) @@ -240,8 +259,11 @@ func (h *SpireHelm) ApplyPostInstallHelmConfig(ctx context.Context, ds datasourc return nil } -func (h *SpireHelm) UninstallSPIREStack(ctx context.Context, trustZones []*trust_zone_proto.TrustZone, statusCh chan<- *provisionpb.Status) error { - for trustZone, cluster := range iterTZClusters(trustZones) { +func (h *SpireHelm) UninstallSPIREStack(ctx context.Context, ds datasource.DataSource, trustZoneClusters []TrustZoneCluster, statusCh chan<- *provisionpb.Status) error { + for _, tzc := range trustZoneClusters { + trustZone := tzc.TrustZone + cluster := tzc.Cluster + prov, err := h.providerFactory.Build(ctx, nil, trustZone, cluster, false) if err != nil { sb := provision.NewStatusBuilder(trustZone.Name, cluster.GetName()) @@ -255,16 +277,3 @@ func (h *SpireHelm) UninstallSPIREStack(ctx context.Context, trustZones []*trust } return nil } - -// iterTZClusters returns an iterator that iterates over all of the clusters for a slice of trust zones. -func iterTZClusters(trustZones []*trust_zone_proto.TrustZone) iter.Seq2[*trust_zone_proto.TrustZone, *clusterpb.Cluster] { - return func(yield func(*trust_zone_proto.TrustZone, *clusterpb.Cluster) bool) { - for _, trustZone := range trustZones { - for _, cluster := range trustZone.GetClusters() { - if !yield(trustZone, cluster) { - return - } - } - } - } -} diff --git a/pkg/plugin/provision/spirehelm/spirehelm_test.go b/pkg/plugin/provision/spirehelm/spirehelm_test.go index e5bcf959..556dda28 100644 --- a/pkg/plugin/provision/spirehelm/spirehelm_test.go +++ b/pkg/plugin/provision/spirehelm/spirehelm_test.go @@ -65,6 +65,9 @@ func TestSpireHelm_Deploy_ExternalServer(t *testing.T) { TrustZones: []*trust_zone_proto.TrustZone{ fixtures.TrustZone("tz5"), }, + Clusters: []*clusterpb.Cluster{ + fixtures.Cluster("local5"), + }, AttestationPolicies: []*attestation_policy_proto.AttestationPolicy{}, Plugins: fixtures.Plugins("plugins1"), } @@ -220,6 +223,10 @@ func defaultConfig() *config.Config { fixtures.TrustZone("tz1"), fixtures.TrustZone("tz2"), }, + Clusters: []*clusterpb.Cluster{ + fixtures.Cluster("local1"), + fixtures.Cluster("local2"), + }, AttestationPolicies: []*attestation_policy_proto.AttestationPolicy{ fixtures.AttestationPolicy("ap1"), fixtures.AttestationPolicy("ap2"), diff --git a/pkg/provider/helm/values.go b/pkg/provider/helm/values.go index f90352a6..c16a831b 100644 --- a/pkg/provider/helm/values.go +++ b/pkg/provider/helm/values.go @@ -11,7 +11,6 @@ import ( "github.com/cofide/cofidectl/internal/pkg/attestationpolicy" "github.com/cofide/cofidectl/internal/pkg/federation" "github.com/cofide/cofidectl/internal/pkg/trustprovider" - "github.com/cofide/cofidectl/internal/pkg/trustzone" "github.com/cofide/cofidectl/pkg/plugin/datasource" ) @@ -75,8 +74,7 @@ func NewHelmValuesGenerator(trustZone *trust_zone_proto.TrustZone, cluster *clus } func (g *HelmValuesGenerator) GenerateValues() (map[string]any, error) { - tz := trustzone.NewTrustZone(g.trustZone) - tp, err := tz.GetTrustProvider() + tp, err := trustprovider.NewTrustProvider(g.cluster.GetTrustProvider()) if err != nil { return nil, err } diff --git a/pkg/provider/helm/values_test.go b/pkg/provider/helm/values_test.go index 3e00cd2d..2d14719f 100644 --- a/pkg/provider/helm/values_test.go +++ b/pkg/provider/helm/values_test.go @@ -7,6 +7,7 @@ import ( "testing" attestation_policy_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/attestation_policy/v1alpha1" + clusterpb "github.com/cofide/cofide-api-sdk/gen/go/proto/cluster/v1alpha1" trust_zone_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/trust_zone/v1alpha1" "github.com/cofide/cofidectl/internal/pkg/config" "github.com/cofide/cofidectl/internal/pkg/test/fixtures" @@ -23,6 +24,7 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { tests := []struct { name string trustZone *trust_zone_proto.TrustZone + cluster *clusterpb.Cluster want Values }{ { @@ -34,9 +36,13 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { tz.BundleEndpointUrl = nil tz.Federations = nil tz.JwtIssuer = nil - tz.Clusters[0].ExtraHelmValues = nil return tz }(), + cluster: func() *clusterpb.Cluster { + cluster := fixtures.Cluster("local1") + cluster.ExtraHelmValues = nil + return cluster + }(), want: Values{ "global": Values{ "deleteHooks": Values{ @@ -119,6 +125,7 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { { name: "tz1", trustZone: fixtures.TrustZone("tz1"), + cluster: fixtures.Cluster("local1"), want: Values{ "global": Values{ "deleteHooks": Values{ @@ -226,6 +233,7 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { { name: "tz4 using the istio profile", trustZone: fixtures.TrustZone("tz4"), + cluster: fixtures.Cluster("local4"), want: Values{ "global": Values{ "deleteHooks": Values{ @@ -310,7 +318,7 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { t.Run(tt.name, func(t *testing.T) { cfg := defaultConfig() source := newFakeDataSource(t, cfg) - g := NewHelmValuesGenerator(tt.trustZone, tt.trustZone.Clusters[0], source, nil) + g := NewHelmValuesGenerator(tt.trustZone, tt.cluster, source, nil) got, err := g.GenerateValues() require.Nil(t, err, err) @@ -323,6 +331,7 @@ func TestHelmValuesGenerator_GenerateValues_AdditionalValues(t *testing.T) { tests := []struct { name string trustZone *trust_zone_proto.TrustZone + cluster *clusterpb.Cluster values Values want Values }{ @@ -335,9 +344,13 @@ func TestHelmValuesGenerator_GenerateValues_AdditionalValues(t *testing.T) { tz.BundleEndpointUrl = nil tz.Federations = nil tz.JwtIssuer = nil - tz.Clusters[0].ExtraHelmValues = nil return tz }(), + cluster: func() *clusterpb.Cluster { + cluster := fixtures.Cluster("local1") + cluster.ExtraHelmValues = nil + return cluster + }(), values: Values{ "spire-server": Values{ "controllerManager": Values{ @@ -448,7 +461,7 @@ func TestHelmValuesGenerator_GenerateValues_AdditionalValues(t *testing.T) { t.Run(tt.name, func(t *testing.T) { cfg := defaultConfig() source := newFakeDataSource(t, cfg) - g := NewHelmValuesGenerator(tt.trustZone, tt.trustZone.Clusters[0], source, tt.values) + g := NewHelmValuesGenerator(tt.trustZone, tt.cluster, source, tt.values) got, err := g.GenerateValues() require.Nil(t, err, err) @@ -461,23 +474,26 @@ func TestHelmValuesGenerator_GenerateValues_failure(t *testing.T) { tests := []struct { name string trustZone *trust_zone_proto.TrustZone + cluster *clusterpb.Cluster wantErrString string }{ { - name: "no trust provider", - trustZone: func() *trust_zone_proto.TrustZone { - tz := fixtures.TrustZone("tz1") - tz.Clusters[0].TrustProvider = nil - return tz + name: "no trust provider", + trustZone: fixtures.TrustZone("tz1"), + cluster: func() *clusterpb.Cluster { + cluster := fixtures.Cluster("local1") + cluster.TrustProvider = nil + return cluster }(), - wantErrString: "no trust provider for trust zone tz1", + wantErrString: "trust provider cannot be nil", }, { - name: "invalid trust provider kind", - trustZone: func() *trust_zone_proto.TrustZone { - tz := fixtures.TrustZone("tz1") - tz.Clusters[0].TrustProvider.Kind = fixtures.StringPtr("invalid-tp") - return tz + name: "invalid trust provider kind", + trustZone: fixtures.TrustZone("tz1"), + cluster: func() *clusterpb.Cluster { + cluster := fixtures.Cluster("local1") + cluster.TrustProvider.Kind = fixtures.StringPtr("invalid-tp") + return cluster }(), wantErrString: "an unknown trust provider kind was specified: invalid-tp", }, @@ -488,6 +504,7 @@ func TestHelmValuesGenerator_GenerateValues_failure(t *testing.T) { tz.AttestationPolicies[0].Policy = "invalid-ap" return tz }(), + cluster: fixtures.Cluster("local1"), wantErrString: "failed to find attestation policy invalid-ap in local config", }, { @@ -497,6 +514,7 @@ func TestHelmValuesGenerator_GenerateValues_failure(t *testing.T) { tz.Federations[0].To = "invalid-tz" return tz }(), + cluster: fixtures.Cluster("local1"), wantErrString: "failed to find trust zone invalid-tz in local config", }, } @@ -504,7 +522,7 @@ func TestHelmValuesGenerator_GenerateValues_failure(t *testing.T) { t.Run(tt.name, func(t *testing.T) { cfg := defaultConfig() source := newFakeDataSource(t, cfg) - g := NewHelmValuesGenerator(tt.trustZone, tt.trustZone.Clusters[0], source, nil) + g := NewHelmValuesGenerator(tt.trustZone, tt.cluster, source, nil) _, err := g.GenerateValues() require.Error(t, err) @@ -543,7 +561,7 @@ func TestHelmValuesGenerator_GenerateValues_federationFailure(t *testing.T) { cfg := defaultConfig() cfg.TrustZones[1] = tt.destTrustZone source := newFakeDataSource(t, cfg) - g := NewHelmValuesGenerator(cfg.TrustZones[0], cfg.TrustZones[0].Clusters[0], source, nil) + g := NewHelmValuesGenerator(cfg.TrustZones[0], cfg.Clusters[0], source, nil) _, err := g.GenerateValues() require.Error(t, err) @@ -1477,6 +1495,11 @@ func defaultConfig() *config.Config { fixtures.TrustZone("tz1"), fixtures.TrustZone("tz2"), }, + Clusters: []*clusterpb.Cluster{ + fixtures.Cluster("local1"), + fixtures.Cluster("local2"), + fixtures.Cluster("local4"), + }, AttestationPolicies: []*attestation_policy_proto.AttestationPolicy{ fixtures.AttestationPolicy("ap1"), fixtures.AttestationPolicy("ap2"), diff --git a/tests/integration/single-trust-zone/test.sh b/tests/integration/single-trust-zone/test.sh index 490d5e7c..34252edd 100755 --- a/tests/integration/single-trust-zone/test.sh +++ b/tests/integration/single-trust-zone/test.sh @@ -37,6 +37,18 @@ function configure() { ./cofidectl attestation-policy add kubernetes --name pod-label --pod-label $POD_POLICY_POD_LABEL ./cofidectl attestation-policy-binding add --trust-zone $TRUST_ZONE --attestation-policy namespace ./cofidectl attestation-policy-binding add --trust-zone $TRUST_ZONE --attestation-policy pod-label + override_helm_values +} + +function override_helm_values() { + cat << EOF > values.yaml +tornjak-frontend: + enabled: false +upstream-spire-agent: + upstream: false +EOF + ./cofidectl trust-zone helm override $TRUST_ZONE --input-file values.yaml + rm -f values.yaml } function up() { @@ -105,6 +117,22 @@ function show_workload_status() { echo "cofidectl workload status successful" } +function check_overridden_values() { + echo "Generated Helm values:" + ./cofidectl trust-zone helm values $TRUST_ZONE --output-file - + + check_overridden_value '."tornjak-frontend".enabled' "false" + check_overridden_value '."upstream-spire-agent".upstream' "false" +} + +function check_overridden_value() { + value=$(helm --kube-context $K8S_CLUSTER_CONTEXT get values spire | yq $1) + if [[ $value != $2 ]]; then + echo "Error: Did not find expected overridden Helm value $1: expected $2, actual $value" + return 1 + fi +} + function down() { ./cofidectl down } @@ -119,6 +147,7 @@ function main() { show_status run_tests show_workload_status + check_overridden_values down echo "Success!" }