From 0b08f47d3444e72176ff3a8ed2078b23d7025c09 Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Fri, 6 Dec 2024 13:47:30 +0000 Subject: [PATCH 01/48] Remove unnecessary types in values tests Stops vscode complaining --- pkg/provider/helm/values_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkg/provider/helm/values_test.go b/pkg/provider/helm/values_test.go index 45d26f4..a4d195c 100644 --- a/pkg/provider/helm/values_test.go +++ b/pkg/provider/helm/values_test.go @@ -801,10 +801,10 @@ func TestShallowMerge(t *testing.T) { { name: "valid slice of maps", maps: []map[string]any{ - map[string]any{ + { "foo": "bar", }, - map[string]any{ + { "fizz": "buzz", }, }, @@ -821,8 +821,8 @@ func TestShallowMerge(t *testing.T) { { name: "slice of empty maps", maps: []map[string]any{ - map[string]any{}, - map[string]any{}, + {}, + {}, }, want: map[string]any{}, }, From bfaaf5e5c5a6836971bf280c0a97a2c07c1b58bd Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Fri, 6 Dec 2024 16:55:08 +0000 Subject: [PATCH 02/48] Export helm.MergeMaps --- pkg/provider/helm/values.go | 10 +++++----- pkg/provider/helm/values_test.go | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pkg/provider/helm/values.go b/pkg/provider/helm/values.go index 158b546..a6d8204 100644 --- a/pkg/provider/helm/values.go +++ b/pkg/provider/helm/values.go @@ -214,7 +214,7 @@ func (g *HelmValuesGenerator) GenerateValues() (map[string]any, error) { combinedValues := shallowMerge(valuesMaps) if g.values != nil { - combinedValues, err = mergeMaps(combinedValues, g.values) + combinedValues, err = MergeMaps(combinedValues, g.values) if err != nil { return nil, err } @@ -223,7 +223,7 @@ func (g *HelmValuesGenerator) GenerateValues() (map[string]any, error) { if g.trustZone.ExtraHelmValues != nil { // TODO: Potentially retrieve Helm values as map[string]any directly. extraHelmValues := g.trustZone.ExtraHelmValues.AsMap() - combinedValues, err = mergeMaps(combinedValues, extraHelmValues) + combinedValues, err = MergeMaps(combinedValues, extraHelmValues) if err != nil { return nil, err } @@ -433,8 +433,8 @@ func getOrCreateNestedMap(m map[string]any, key string) (map[string]any, error) return newMap, nil } -// mergeMaps merges the source map into the destination map, returning a new merged map. -func mergeMaps(dest, src map[string]any) (map[string]any, error) { +// MergeMaps merges the source map into the destination map, returning a new merged map. +func MergeMaps(dest, src map[string]any) (map[string]any, error) { if src == nil { return nil, fmt.Errorf("source map is nil") } @@ -446,7 +446,7 @@ func mergeMaps(dest, src map[string]any) (map[string]any, error) { for key, value := range src { if srcMap, isSrcMap := value.(map[string]any); isSrcMap { if destMap, isDestMap := dest[key].(map[string]any); isDestMap { - merged, err := mergeMaps(destMap, srcMap) + merged, err := MergeMaps(destMap, srcMap) if err != nil { return nil, err } diff --git a/pkg/provider/helm/values_test.go b/pkg/provider/helm/values_test.go index a4d195c..28a2afb 100644 --- a/pkg/provider/helm/values_test.go +++ b/pkg/provider/helm/values_test.go @@ -780,7 +780,7 @@ func TestMergeMaps(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - resp, err := mergeMaps(tt.dest, tt.src) + resp, err := MergeMaps(tt.dest, tt.src) if tt.wantErr { assert.Equal(t, tt.errString, err.Error()) return From 1e4b857fa3cb19292e67c4f1178e586c7d7a7efe Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Thu, 5 Dec 2024 14:16:18 +0000 Subject: [PATCH 03/48] Implement gRPC provision plugin stubs with data source host plugin Working up & down! Fixes: #56 --- cmd/cofidectl/cmd/down.go | 5 +- cmd/cofidectl/cmd/init.go | 11 +- cmd/cofidectl/cmd/up.go | 5 +- go.mod | 2 +- go.sum | 4 +- internal/pkg/config/config.go | 4 + internal/pkg/config/config_test.go | 3 + internal/pkg/config/schema.cue | 4 + .../pkg/config/testdata/config/default.yaml | 1 + internal/pkg/config/testdata/config/full.yaml | 1 + pkg/plugin/datasource/interface.go | 31 +++ pkg/plugin/grpc.go | 4 + pkg/plugin/interface.go | 26 +- pkg/plugin/local/local_test.go | 4 +- pkg/plugin/manager/manager.go | 142 ++++++++--- pkg/plugin/manager/manager_test.go | 126 ++++++---- pkg/plugin/plugin.go | 7 +- pkg/plugin/provision/interface.go | 9 +- pkg/plugin/provision/plugin.go | 235 ++++++++++++++++++ pkg/plugin/provision/spirehelm/spirehelm.go | 4 + 20 files changed, 508 insertions(+), 120 deletions(-) create mode 100644 pkg/plugin/datasource/interface.go create mode 100644 pkg/plugin/provision/plugin.go diff --git a/cmd/cofidectl/cmd/down.go b/cmd/cofidectl/cmd/down.go index aca8c3d..093a3ef 100644 --- a/cmd/cofidectl/cmd/down.go +++ b/cmd/cofidectl/cmd/down.go @@ -34,7 +34,10 @@ func (d *DownCommand) DownCmd() *cobra.Command { return err } - provision := d.cmdCtx.PluginManager.GetProvision() + provision, err := d.cmdCtx.PluginManager.GetProvision() + if err != nil { + return err + } statusCh, err := provision.TearDown(cmd.Context(), ds) if err != nil { return err diff --git a/cmd/cofidectl/cmd/init.go b/cmd/cofidectl/cmd/init.go index ba24a52..338ca0c 100644 --- a/cmd/cofidectl/cmd/init.go +++ b/cmd/cofidectl/cmd/init.go @@ -44,20 +44,23 @@ func (i *InitCommand) GetRootCommand() *cobra.Command { Long: initRootCmdDesc, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - var pluginName string + var dsName string + var provisionName string if opts.enableConnect { if ok, _ := plugin.PluginExists(connectPluginName); ok { - pluginName = connectPluginName + dsName = connectPluginName + provisionName = connectPluginName } else { fmt.Println("👀 get in touch with us at hello@cofide.io to find out more") os.Exit(1) } } else { // Default to the local file data source. - pluginName = manager.LocalPluginName + dsName = manager.LocalPluginName + provisionName = manager.SpireHelmProvisionPluginName } - _, err := i.cmdCtx.PluginManager.Init(pluginName, nil) + _, err := i.cmdCtx.PluginManager.Init(dsName, provisionName, nil) return err }, } diff --git a/cmd/cofidectl/cmd/up.go b/cmd/cofidectl/cmd/up.go index b190f84..b39928c 100644 --- a/cmd/cofidectl/cmd/up.go +++ b/cmd/cofidectl/cmd/up.go @@ -35,7 +35,10 @@ func (u *UpCommand) UpCmd() *cobra.Command { return err } - provision := u.cmdCtx.PluginManager.GetProvision() + provision, err := u.cmdCtx.PluginManager.GetProvision() + if err != nil { + return err + } statusCh, err := provision.Deploy(cmd.Context(), ds, kubeCfgFile) if err != nil { return err diff --git a/go.mod b/go.mod index 8b48707..842158a 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.22.7 require ( buf.build/go/protoyaml v0.2.0 cuelang.org/go v0.10.1 - github.com/cofide/cofide-api-sdk v0.3.1-0.20241209124727-c60451a4ba77 + github.com/cofide/cofide-api-sdk v0.3.1-0.20241211085315-6c76f01e47d2 github.com/fatih/color v1.18.0 github.com/gofrs/flock v0.12.1 github.com/google/go-cmp v0.6.0 diff --git a/go.sum b/go.sum index e87ce2a..7c8867f 100644 --- a/go.sum +++ b/go.sum @@ -84,8 +84,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.3.1-0.20241209124727-c60451a4ba77 h1:Uq2GS7GLwqjGWnnEEi/dfoc0+D9/yhmjEwNqLd7e8J0= -github.com/cofide/cofide-api-sdk v0.3.1-0.20241209124727-c60451a4ba77/go.mod h1:yKMfhL3qCIVJcKvgZsPZC1o60/8co6/0NsCaJtrUoFY= +github.com/cofide/cofide-api-sdk v0.3.1-0.20241211085315-6c76f01e47d2 h1:EEKw2gyPke/JHMqOSW8xJTd0D0R0hj7I9/FEXqcekEQ= +github.com/cofide/cofide-api-sdk v0.3.1-0.20241211085315-6c76f01e47d2/go.mod h1:u2iATR4IbZm9ruIBN734UjVuO3XQKPAFViIY3Xr6kTA= 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.23 h1:H2CClyUkmpKAGlhQp95g2WXHfLYc7whAuvZGBNYOOwQ= diff --git a/internal/pkg/config/config.go b/internal/pkg/config/config.go index 5709b01..025c7b7 100644 --- a/internal/pkg/config/config.go +++ b/internal/pkg/config/config.go @@ -17,6 +17,7 @@ type Config struct { TrustZones []*trust_zone_proto.TrustZone AttestationPolicies []*attestation_policy_proto.AttestationPolicy PluginConfig map[string]*structpb.Struct + ProvisionPlugin string } func NewConfig() *Config { @@ -25,6 +26,7 @@ func NewConfig() *Config { TrustZones: []*trust_zone_proto.TrustZone{}, AttestationPolicies: []*attestation_policy_proto.AttestationPolicy{}, PluginConfig: map[string]*structpb.Struct{}, + ProvisionPlugin: "", } } @@ -34,6 +36,7 @@ func newConfigFromProto(proto *config_proto.Config) *Config { TrustZones: proto.TrustZones, AttestationPolicies: proto.AttestationPolicies, PluginConfig: proto.PluginConfig, + ProvisionPlugin: proto.GetProvisionPlugin(), } } @@ -43,6 +46,7 @@ func (c *Config) toProto() *config_proto.Config { TrustZones: c.TrustZones, AttestationPolicies: c.AttestationPolicies, PluginConfig: c.PluginConfig, + ProvisionPlugin: &c.ProvisionPlugin, } } diff --git a/internal/pkg/config/config_test.go b/internal/pkg/config/config_test.go index cdf6157..77abc1c 100644 --- a/internal/pkg/config/config_test.go +++ b/internal/pkg/config/config_test.go @@ -46,6 +46,7 @@ func TestConfig_YAMLMarshall(t *testing.T) { "plugin1": fixtures.PluginConfig("plugin1"), "plugin2": fixtures.PluginConfig("plugin2"), }, + ProvisionPlugin: "fake-provision-plugin", }, wantFile: "full.yaml", }, @@ -77,6 +78,7 @@ func TestConfig_YAMLUnmarshall(t *testing.T) { TrustZones: []*trust_zone_proto.TrustZone{}, AttestationPolicies: []*attestation_policy_proto.AttestationPolicy{}, PluginConfig: map[string]*structpb.Struct{}, + ProvisionPlugin: "", }, }, { @@ -97,6 +99,7 @@ func TestConfig_YAMLUnmarshall(t *testing.T) { "plugin1": fixtures.PluginConfig("plugin1"), "plugin2": fixtures.PluginConfig("plugin2"), }, + ProvisionPlugin: "fake-provision-plugin", }, }, } diff --git a/internal/pkg/config/schema.cue b/internal/pkg/config/schema.cue index f58a86c..76a150f 100644 --- a/internal/pkg/config/schema.cue +++ b/internal/pkg/config/schema.cue @@ -65,11 +65,15 @@ [string]: _ } +#ProvisionPlugin: string + #Config: { data_source!: #DataSource trust_zones: [...#TrustZone] attestation_policies: [...#AttestationPolicy] plugin_config?: #PluginConfig + // FIXME: make required? + provision_plugin?: #ProvisionPlugin } #Config diff --git a/internal/pkg/config/testdata/config/default.yaml b/internal/pkg/config/testdata/config/default.yaml index 968eb5d..0fffaa3 100644 --- a/internal/pkg/config/testdata/config/default.yaml +++ b/internal/pkg/config/testdata/config/default.yaml @@ -1 +1,2 @@ data_source: local +provision_plugin: "" diff --git a/internal/pkg/config/testdata/config/full.yaml b/internal/pkg/config/testdata/config/full.yaml index baee7ad..695b799 100644 --- a/internal/pkg/config/testdata/config/full.yaml +++ b/internal/pkg/config/testdata/config/full.yaml @@ -90,3 +90,4 @@ plugin_config: plugin2: number-cfg: 123 string-cfg: fake-string +provision_plugin: fake-provision-plugin diff --git a/pkg/plugin/datasource/interface.go b/pkg/plugin/datasource/interface.go new file mode 100644 index 0000000..352f4a2 --- /dev/null +++ b/pkg/plugin/datasource/interface.go @@ -0,0 +1,31 @@ +// Copyright 2024 Cofide Limited. +// SPDX-License-Identifier: Apache-2.0 + +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" + 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" +) + +// DataSource is the interface data source plugins have to implement. +type DataSource interface { + Validate() 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 + + AddAttestationPolicy(*attestation_policy_proto.AttestationPolicy) (*attestation_policy_proto.AttestationPolicy, error) + GetAttestationPolicy(string) (*attestation_policy_proto.AttestationPolicy, error) + ListAttestationPolicies() ([]*attestation_policy_proto.AttestationPolicy, error) + + AddAPBinding(*ap_binding_proto.APBinding) (*ap_binding_proto.APBinding, error) + DestroyAPBinding(*ap_binding_proto.APBinding) error + + AddFederation(*federation_proto.Federation) (*federation_proto.Federation, error) + ListFederations() ([]*federation_proto.Federation, error) + ListFederationsByTrustZone(string) ([]*federation_proto.Federation, error) +} diff --git a/pkg/plugin/grpc.go b/pkg/plugin/grpc.go index 9d958e9..0826018 100644 --- a/pkg/plugin/grpc.go +++ b/pkg/plugin/grpc.go @@ -23,6 +23,10 @@ type DataSourcePluginClientGRPC struct { client cofidectl_proto.DataSourcePluginServiceClient } +func NewDataSourcePluginClientGRPC(ctx context.Context, client cofidectl_proto.DataSourcePluginServiceClient) *DataSourcePluginClientGRPC { + return &DataSourcePluginClientGRPC{ctx: ctx, client: client} +} + func (c *DataSourcePluginClientGRPC) Validate() error { _, err := c.client.Validate(c.ctx, &cofidectl_proto.ValidateRequest{}) return err diff --git a/pkg/plugin/interface.go b/pkg/plugin/interface.go index 5576047..03e17ba 100644 --- a/pkg/plugin/interface.go +++ b/pkg/plugin/interface.go @@ -4,28 +4,8 @@ package plugin 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" - 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/datasource" ) -// DataSource is the interface data source plugins have to implement. -type DataSource interface { - Validate() 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 - - AddAttestationPolicy(*attestation_policy_proto.AttestationPolicy) (*attestation_policy_proto.AttestationPolicy, error) - GetAttestationPolicy(string) (*attestation_policy_proto.AttestationPolicy, error) - ListAttestationPolicies() ([]*attestation_policy_proto.AttestationPolicy, error) - - AddAPBinding(*ap_binding_proto.APBinding) (*ap_binding_proto.APBinding, error) - DestroyAPBinding(*ap_binding_proto.APBinding) error - - AddFederation(*federation_proto.Federation) (*federation_proto.Federation, error) - ListFederations() ([]*federation_proto.Federation, error) - ListFederationsByTrustZone(string) ([]*federation_proto.Federation, error) -} +// Alias DataSource in the plugin package while transitioning to a new package. +type DataSource = datasource.DataSource diff --git a/pkg/plugin/local/local_test.go b/pkg/plugin/local/local_test.go index 6396421..906f892 100644 --- a/pkg/plugin/local/local_test.go +++ b/pkg/plugin/local/local_test.go @@ -49,13 +49,15 @@ func TestNewLocalDataSource(t *testing.T) { { name: "non-default config", config: &config.Config{ - DataSource: "test-plugin", + DataSource: "test-plugin", + ProvisionPlugin: "test-provision-plugin", }, wantConfig: &config.Config{ DataSource: "test-plugin", TrustZones: []*trust_zone_proto.TrustZone{}, AttestationPolicies: []*attestation_policy_proto.AttestationPolicy{}, PluginConfig: map[string]*structpb.Struct{}, + ProvisionPlugin: "test-provision-plugin", }, }, } diff --git a/pkg/plugin/manager/manager.go b/pkg/plugin/manager/manager.go index 71cdf33..a5a2dc4 100644 --- a/pkg/plugin/manager/manager.go +++ b/pkg/plugin/manager/manager.go @@ -4,6 +4,7 @@ package manager import ( + "context" "errors" "fmt" "maps" @@ -23,26 +24,29 @@ import ( ) const ( - LocalPluginName = "local" + LocalPluginName = "local" + SpireHelmProvisionPluginName = "spire-helm" ) // PluginManager provides an interface for loading and managing `DataSource` plugins based on configuration. type PluginManager struct { configLoader config.Loader - loadGrpcPlugin func(hclog.Logger, string) (*go_plugin.Client, cofidectl_plugin.DataSource, error) + loadGrpcPlugin func(hclog.Logger, string) (*go_plugin.Client, cofidectl_plugin.DataSource, provision.Provision, error) source cofidectl_plugin.DataSource - client *go_plugin.Client + provision provision.Provision + clients map[string]*go_plugin.Client } func NewManager(configLoader config.Loader) *PluginManager { return &PluginManager{ configLoader: configLoader, loadGrpcPlugin: loadGrpcPlugin, + clients: map[string]*go_plugin.Client{}, } } // Init initialises the configuration for the specified data source plugin. -func (pm *PluginManager) Init(dsName string, pluginConfig map[string]*structpb.Struct) (cofidectl_plugin.DataSource, error) { +func (pm *PluginManager) Init(dsName string, provisionName string, pluginConfig map[string]*structpb.Struct) (cofidectl_plugin.DataSource, error) { if exists, _ := pm.configLoader.Exists(); exists { // Check that existing plugin config matches. cfg, err := pm.configLoader.Read() @@ -50,7 +54,10 @@ func (pm *PluginManager) Init(dsName string, pluginConfig map[string]*structpb.S return nil, err } if cfg.DataSource != dsName { - return nil, fmt.Errorf("existing config file uses a different plugin: %s vs %s", cfg.DataSource, dsName) + return nil, fmt.Errorf("existing config file uses a different data source plugin: %s vs %s", cfg.DataSource, dsName) + } + if cfg.ProvisionPlugin != provisionName { + return nil, fmt.Errorf("existing config file uses a different provision plugin: %s vs %s", cfg.ProvisionPlugin, provisionName) } if !maps.EqualFunc(cfg.PluginConfig, pluginConfig, proto.StructsEqual) { return nil, fmt.Errorf("existing config file has different plugin config:\n%v\nvs\n\n%v", cfg.PluginConfig, pluginConfig) @@ -59,6 +66,7 @@ func (pm *PluginManager) Init(dsName string, pluginConfig map[string]*structpb.S } else { cfg := config.NewConfig() cfg.DataSource = dsName + cfg.ProvisionPlugin = provisionName if pluginConfig != nil { cfg.PluginConfig = pluginConfig } @@ -83,16 +91,7 @@ func (pm *PluginManager) loadDataSource() (cofidectl_plugin.DataSource, error) { return nil, errors.New("data source has already been loaded") } - exists, err := pm.configLoader.Exists() - if err != nil { - return nil, err - } - - if !exists { - return nil, fmt.Errorf("the config file doesn't exist. Please run cofidectl init") - } - - cfg, err := pm.configLoader.Read() + cfg, err := pm.readConfig() if err != nil { return nil, err } @@ -116,7 +115,7 @@ func (pm *PluginManager) loadDataSource() (cofidectl_plugin.DataSource, error) { Level: hclog.Error, }) - client, ds, err = pm.loadGrpcPlugin(logger, cfg.DataSource) + client, ds, _, err = pm.loadGrpcPlugin(logger, cfg.DataSource) if err != nil { return nil, err } @@ -129,14 +128,77 @@ func (pm *PluginManager) loadDataSource() (cofidectl_plugin.DataSource, error) { return nil, err } pm.source = ds - pm.client = client + pm.clients[cfg.DataSource] = client return ds, nil } -func loadGrpcPlugin(logger hclog.Logger, pluginName string) (*go_plugin.Client, cofidectl_plugin.DataSource, error) { +func (pm *PluginManager) GetProvision() (provision.Provision, error) { + if pm.provision != nil { + return pm.provision, nil + } + return pm.loadProvision() +} + +func (pm *PluginManager) loadProvision() (provision.Provision, error) { + if pm.provision != nil { + return nil, errors.New("provision plugin has already been loaded") + } + + cfg, err := pm.readConfig() + if err != nil { + return nil, err + } + + if cfg.ProvisionPlugin == "" { + return nil, errors.New("provision plugin name cannot be empty") + } + + var provision provision.Provision + var client *go_plugin.Client + switch cfg.ProvisionPlugin { + case SpireHelmProvisionPluginName: + return spirehelm.NewSpireHelm(nil), nil + default: + logger := hclog.New(&hclog.LoggerOptions{ + Name: "plugin", + Output: os.Stdout, + Level: hclog.Error, + }) + + client, _, provision, err = pm.loadGrpcPlugin(logger, cfg.ProvisionPlugin) + if err != nil { + return nil, err + } + } + + if err := provision.Validate(context.TODO()); err != nil { + if client != nil { + client.Kill() + } + return nil, err + } + pm.provision = provision + pm.clients[cfg.ProvisionPlugin] = client + return provision, nil +} + +func (pm *PluginManager) readConfig() (*config.Config, error) { + exists, err := pm.configLoader.Exists() + if err != nil { + return nil, err + } + + if !exists { + return nil, fmt.Errorf("the config file doesn't exist. Please run cofidectl init") + } + + return pm.configLoader.Read() +} + +func loadGrpcPlugin(logger hclog.Logger, pluginName string) (*go_plugin.Client, cofidectl_plugin.DataSource, provision.Provision, error) { pluginPath, err := cofidectl_plugin.GetPluginPath(pluginName) if err != nil { - return nil, nil, err + return nil, nil, nil, err } cmd := exec.Command(pluginPath, cofidectl_plugin.DataSourcePluginArgs...) @@ -145,45 +207,58 @@ func loadGrpcPlugin(logger hclog.Logger, pluginName string) (*go_plugin.Client, HandshakeConfig: cofidectl_plugin.HandshakeConfig, Plugins: map[string]go_plugin.Plugin{ cofidectl_plugin.DataSourcePluginName: &cofidectl_plugin.DataSourcePlugin{}, + provision.ProvisionPluginName: &provision.ProvisionPlugin{}, }, AllowedProtocols: []go_plugin.Protocol{go_plugin.ProtocolGRPC}, Logger: logger, }) - source, err := startGrpcPlugin(client, pluginName) + source, provision, err := startGrpcPlugin(client, pluginName) if err != nil { client.Kill() - return nil, nil, err + return nil, nil, nil, err } - return client, source, nil + return client, source, provision, nil } -func startGrpcPlugin(client *go_plugin.Client, pluginName string) (cofidectl_plugin.DataSource, error) { +func startGrpcPlugin(client *go_plugin.Client, pluginName string) (cofidectl_plugin.DataSource, provision.Provision, error) { grpcClient, err := client.Client() if err != nil { - return nil, fmt.Errorf("cannot create interface to plugin: %w", err) + return nil, nil, fmt.Errorf("cannot create interface to plugin: %w", err) } if err = grpcClient.Ping(); err != nil { - return nil, fmt.Errorf("failed to ping the gRPC client: %w", err) + return nil, nil, fmt.Errorf("failed to ping the gRPC client: %w", err) } raw, err := grpcClient.Dispense(cofidectl_plugin.DataSourcePluginName) if err != nil { - return nil, fmt.Errorf("failed to dispense an instance of the plugin: %w", err) + return nil, nil, fmt.Errorf("failed to dispense an instance of the plugin: %w", err) } source, ok := raw.(cofidectl_plugin.DataSource) if !ok { - return nil, fmt.Errorf("gRPC data source plugin %s does not implement plugin interface", pluginName) + return nil, nil, fmt.Errorf("gRPC data source plugin %s does not implement plugin interface", pluginName) + } + + raw, err = grpcClient.Dispense(provision.ProvisionPluginName) + if err != nil { + return nil, nil, fmt.Errorf("failed to dispense an instance of the plugin: %w", err) + } + + provision, ok := raw.(provision.Provision) + if !ok { + return nil, nil, fmt.Errorf("gRPC data source plugin %s does not implement plugin interface", pluginName) } - return source, nil + return source, provision, nil } func (pm *PluginManager) Shutdown() { - if pm.client != nil { - pm.client.Kill() - pm.client = nil + for name, client := range pm.clients { + if client != nil { + client.Kill() + } + delete(pm.clients, name) } pm.source = nil } @@ -218,8 +293,3 @@ func (pm *PluginManager) SetPluginConfig(pluginName string, pluginConfig *struct cfg.PluginConfig[pluginName] = pluginConfig return pm.configLoader.Write(cfg) } - -// GetProvision returns the provision plugin. -func (pm *PluginManager) GetProvision() provision.Provision { - return spirehelm.NewSpireHelm(nil) -} diff --git a/pkg/plugin/manager/manager_test.go b/pkg/plugin/manager/manager_test.go index 507ad62..900b6f3 100644 --- a/pkg/plugin/manager/manager_test.go +++ b/pkg/plugin/manager/manager_test.go @@ -10,6 +10,8 @@ import ( "github.com/cofide/cofidectl/internal/pkg/config" cofidectl_plugin "github.com/cofide/cofidectl/pkg/plugin" "github.com/cofide/cofidectl/pkg/plugin/local" + "github.com/cofide/cofidectl/pkg/plugin/provision" + "github.com/cofide/cofidectl/pkg/plugin/provision/spirehelm" hclog "github.com/hashicorp/go-hclog" go_plugin "github.com/hashicorp/go-plugin" "github.com/stretchr/testify/assert" @@ -27,18 +29,28 @@ func newFakeGrpcDataSource(t *testing.T, configLoader config.Loader) *fakeGrpcDa return &fakeGrpcDataSource{LocalDataSource: *lds} } +type fakeGrpcProvision struct { + spirehelm.SpireHelm +} + +func newFakeGrpcProvision() *fakeGrpcProvision { + return &fakeGrpcProvision{} +} + func TestManager_Init_success(t *testing.T) { tests := []struct { - name string - config *config.Config - pluginName string - pluginConfig map[string]*structpb.Struct - want func(config.Loader) cofidectl_plugin.DataSource + name string + config *config.Config + dsName string + provisionName string + pluginConfig map[string]*structpb.Struct + want func(config.Loader) cofidectl_plugin.DataSource }{ { - name: "local", - config: nil, - pluginName: LocalPluginName, + name: "local", + config: nil, + dsName: LocalPluginName, + provisionName: SpireHelmProvisionPluginName, want: func(cl config.Loader) cofidectl_plugin.DataSource { lds, err := local.NewLocalDataSource(cl) assert.Nil(t, err) @@ -46,19 +58,21 @@ func TestManager_Init_success(t *testing.T) { }, }, { - name: "gRPC", - config: nil, - pluginName: "fake-plugin", + name: "gRPC", + config: nil, + dsName: "fake-plugin", + provisionName: "fake-provision-plugin", want: func(cl config.Loader) cofidectl_plugin.DataSource { fcds := newFakeGrpcDataSource(t, cl) return fcds }, }, { - name: "local with config", - config: nil, - pluginName: LocalPluginName, - pluginConfig: fakePluginConfig(t), + name: "local with config", + config: nil, + dsName: LocalPluginName, + provisionName: SpireHelmProvisionPluginName, + pluginConfig: fakePluginConfig(t), want: func(cl config.Loader) cofidectl_plugin.DataSource { lds, err := local.NewLocalDataSource(cl) assert.Nil(t, err) @@ -66,9 +80,10 @@ func TestManager_Init_success(t *testing.T) { }, }, { - name: "existing local", - config: &config.Config{DataSource: LocalPluginName}, - pluginName: LocalPluginName, + name: "existing local", + config: &config.Config{DataSource: LocalPluginName, ProvisionPlugin: SpireHelmProvisionPluginName}, + dsName: LocalPluginName, + provisionName: SpireHelmProvisionPluginName, want: func(cl config.Loader) cofidectl_plugin.DataSource { lds, err := local.NewLocalDataSource(cl) assert.Nil(t, err) @@ -76,19 +91,21 @@ func TestManager_Init_success(t *testing.T) { }, }, { - name: "existing gRPC", - config: &config.Config{DataSource: "fake-plugin"}, - pluginName: "fake-plugin", + name: "existing gRPC", + config: &config.Config{DataSource: "fake-plugin", ProvisionPlugin: "fake-provision-plugin"}, + dsName: "fake-plugin", + provisionName: "fake-provision-plugin", want: func(cl config.Loader) cofidectl_plugin.DataSource { fcds := newFakeGrpcDataSource(t, cl) return fcds }, }, { - name: "existing local with config", - config: &config.Config{DataSource: LocalPluginName, PluginConfig: fakePluginConfig(t)}, - pluginName: LocalPluginName, - pluginConfig: fakePluginConfig(t), + name: "existing local with config", + config: &config.Config{DataSource: LocalPluginName, ProvisionPlugin: SpireHelmProvisionPluginName, PluginConfig: fakePluginConfig(t)}, + dsName: LocalPluginName, + provisionName: SpireHelmProvisionPluginName, + pluginConfig: fakePluginConfig(t), want: func(cl config.Loader) cofidectl_plugin.DataSource { lds, err := local.NewLocalDataSource(cl) assert.Nil(t, err) @@ -103,11 +120,11 @@ func TestManager_Init_success(t *testing.T) { m := NewManager(configLoader) // Mock out the Connect plugin loader function. - m.loadGrpcPlugin = func(logger hclog.Logger, _ string) (*go_plugin.Client, cofidectl_plugin.DataSource, error) { - return nil, newFakeGrpcDataSource(t, configLoader), nil + m.loadGrpcPlugin = func(logger hclog.Logger, _ string) (*go_plugin.Client, cofidectl_plugin.DataSource, provision.Provision, error) { + return nil, newFakeGrpcDataSource(t, configLoader), newFakeGrpcProvision(), nil } - got, err := m.Init(tt.pluginName, tt.pluginConfig) + got, err := m.Init(tt.dsName, tt.provisionName, tt.pluginConfig) require.Nil(t, err) want := tt.want(configLoader) @@ -115,7 +132,8 @@ func TestManager_Init_success(t *testing.T) { config, err := configLoader.Read() assert.Nil(t, err) - assert.Equal(t, tt.pluginName, config.DataSource) + assert.Equal(t, tt.dsName, config.DataSource) + assert.Equal(t, tt.provisionName, config.ProvisionPlugin) expectedConfig := tt.pluginConfig if expectedConfig == nil { @@ -129,6 +147,10 @@ func TestManager_Init_success(t *testing.T) { got2, err := m.GetDataSource() require.Nil(t, err) assert.Same(t, got, got2, "GetDataSource() should return a cached copy") + + // provision, err := m.GetProvision() + // require.Nil(t, err) + // assert.Same(t, m.provision, provision, "GetProvision() should return a cached copy") }) } } @@ -137,21 +159,31 @@ func TestManager_Init_failure(t *testing.T) { tests := []struct { name string config *config.Config - pluginName string + dsName string + provisionName string pluginConfig map[string]*structpb.Struct want func(config.Loader) cofidectl_plugin.DataSource wantErrMessage string }{ { - name: "existing different plugin", + name: "existing different data source", config: &config.Config{DataSource: "fake-plugin"}, - pluginName: LocalPluginName, - wantErrMessage: "existing config file uses a different plugin: fake-plugin vs local", + dsName: LocalPluginName, + provisionName: SpireHelmProvisionPluginName, + wantErrMessage: "existing config file uses a different data source plugin: fake-plugin vs local", + }, + { + name: "existing different provision plugin", + config: &config.Config{DataSource: LocalPluginName, ProvisionPlugin: "fake-provision-plugin"}, + dsName: LocalPluginName, + provisionName: SpireHelmProvisionPluginName, + wantErrMessage: "existing config file uses a different provision plugin: fake-provision-plugin vs spire-helm", }, { name: "existing different plugin config", - config: &config.Config{DataSource: "local", PluginConfig: fakePluginConfig(t)}, - pluginName: LocalPluginName, + config: &config.Config{DataSource: "local", ProvisionPlugin: SpireHelmProvisionPluginName, PluginConfig: fakePluginConfig(t)}, + dsName: LocalPluginName, + provisionName: SpireHelmProvisionPluginName, wantErrMessage: "existing config file has different plugin config:", }, } @@ -162,7 +194,7 @@ func TestManager_Init_failure(t *testing.T) { m := NewManager(configLoader) - _, err = m.Init(tt.pluginName, tt.pluginConfig) + _, err = m.Init(tt.dsName, tt.provisionName, tt.pluginConfig) require.Error(t, err) assert.ErrorContains(t, err, tt.wantErrMessage) @@ -171,7 +203,8 @@ func TestManager_Init_failure(t *testing.T) { assert.Equal(t, config.DataSource, tt.config.DataSource, "config should not be updated") assert.Nil(t, m.source, "cached data source should be nil") - assert.Nil(t, m.client, "cached client should be nil") + assert.Nil(t, m.provision, "cached data source should be nil") + assert.Empty(t, m.clients, "cached clients should be empty") }) } } @@ -211,9 +244,9 @@ func TestManager_GetDataSource_success(t *testing.T) { var client *go_plugin.Client if tt.config.DataSource != LocalPluginName { client = &go_plugin.Client{} - m.loadGrpcPlugin = func(logger hclog.Logger, _ string) (*go_plugin.Client, cofidectl_plugin.DataSource, error) { + m.loadGrpcPlugin = func(logger hclog.Logger, _ string) (*go_plugin.Client, cofidectl_plugin.DataSource, provision.Provision, error) { ds := newFakeGrpcDataSource(t, configLoader) - return client, ds, nil + return client, ds, nil, nil } } @@ -222,7 +255,7 @@ func TestManager_GetDataSource_success(t *testing.T) { want := tt.want(configLoader) assert.Equal(t, want, got) - assert.Same(t, client, m.client) + assert.Same(t, client, m.clients[tt.config.DataSource]) got2, err := m.GetDataSource() require.Nil(t, err) @@ -255,15 +288,15 @@ func TestManager_GetDataSource_failure(t *testing.T) { m := NewManager(configLoader) // Mock out the Connect plugin loader function, and inject a load failure. - m.loadGrpcPlugin = func(logger hclog.Logger, _ string) (*go_plugin.Client, cofidectl_plugin.DataSource, error) { - return nil, nil, errors.New("failed to create connect plugin") + m.loadGrpcPlugin = func(logger hclog.Logger, _ string) (*go_plugin.Client, cofidectl_plugin.DataSource, provision.Provision, error) { + return nil, nil, nil, errors.New("failed to create connect plugin") } _, err = m.GetDataSource() require.Error(t, err) assert.ErrorContains(t, err, tt.wantErr) assert.Nil(t, m.source, "failed GetDataSource should not cache") - assert.Nil(t, m.client, "failed GetDataSource should not cache") + assert.Empty(t, m.clients, "failed GetDataSource should not cache") }) } } @@ -291,9 +324,9 @@ func TestManager_Shutdown(t *testing.T) { m := NewManager(configLoader) // Mock out the Connect plugin loader function. client := &go_plugin.Client{} - m.loadGrpcPlugin = func(logger hclog.Logger, _ string) (*go_plugin.Client, cofidectl_plugin.DataSource, error) { + m.loadGrpcPlugin = func(logger hclog.Logger, _ string) (*go_plugin.Client, cofidectl_plugin.DataSource, provision.Provision, error) { ds := newFakeGrpcDataSource(t, configLoader) - return client, ds, nil + return client, ds, nil, nil } _, err = m.GetDataSource() @@ -301,7 +334,8 @@ func TestManager_Shutdown(t *testing.T) { m.Shutdown() assert.Nil(t, m.source) - assert.Nil(t, m.client) + assert.Nil(t, m.provision) + assert.Empty(t, m.clients) }) } } diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go index b7543fe..99e4a5f 100644 --- a/pkg/plugin/plugin.go +++ b/pkg/plugin/plugin.go @@ -12,10 +12,13 @@ import ( "google.golang.org/grpc" ) -// DataSourcePluginName is the name that should be used in the plugin map. -const DataSourcePluginName = "data_source" +const ( + // DataSourcePluginName is the name that should be used in the plugin map. + DataSourcePluginName = "data_source" +) // DataSourcePluginArgs contains the arguments passed to plugins when executing them as a data source. +// TODO: change to plugin serve var DataSourcePluginArgs []string = []string{"data-source", "serve"} // IsDataSourceServeCmd returns whether the provided command line arguments indicate that a plugin should serve a data source. diff --git a/pkg/plugin/provision/interface.go b/pkg/plugin/provision/interface.go index f68b970..e1362f8 100644 --- a/pkg/plugin/provision/interface.go +++ b/pkg/plugin/provision/interface.go @@ -7,18 +7,21 @@ import ( "context" provisionpb "github.com/cofide/cofide-api-sdk/gen/go/proto/provision_plugin/v1alpha1" - "github.com/cofide/cofidectl/pkg/plugin" + "github.com/cofide/cofidectl/pkg/plugin/datasource" ) // Provision is the interface that provision plugins have to implement. type Provision interface { + // Validate checks whether the plugin is configured correctly. + Validate(ctx context.Context) error + // Deploy deploys the workload identity configuration to the clusters in the system. // The method is asynchronous, returning a channel over which Status messages are sent // describing the various stages of deployment and their outcomes. - Deploy(ctx context.Context, ds plugin.DataSource, kubeCfgFile string) (<-chan *provisionpb.Status, error) + Deploy(ctx context.Context, ds datasource.DataSource, kubeCfgFile string) (<-chan *provisionpb.Status, error) // TearDown tears down the workload identity configuration from the clusters in the system. // The method is asynchronous, returning a channel over which Status messages are sent // describing the various stages of tear down and their outcomes. - TearDown(ctx context.Context, ds plugin.DataSource) (<-chan *provisionpb.Status, error) + TearDown(ctx context.Context, ds datasource.DataSource) (<-chan *provisionpb.Status, error) } diff --git a/pkg/plugin/provision/plugin.go b/pkg/plugin/provision/plugin.go new file mode 100644 index 0000000..6366a31 --- /dev/null +++ b/pkg/plugin/provision/plugin.go @@ -0,0 +1,235 @@ +// Copyright 2024 Cofide Limited. +// SPDX-License-Identifier: Apache-2.0 + +package provision + +import ( + "context" + "fmt" + "io" + + cofidectl_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/cofidectl_plugin/v1alpha1" + provisionpb "github.com/cofide/cofide-api-sdk/gen/go/proto/provision_plugin/v1alpha1" + "github.com/cofide/cofidectl/pkg/plugin" + "github.com/cofide/cofidectl/pkg/plugin/datasource" + go_plugin "github.com/hashicorp/go-plugin" + "google.golang.org/grpc" + "google.golang.org/grpc/status" +) + +// ProvisionPluginName is the name that should be used in the plugin map. +const ProvisionPluginName = "provision" + +// ProvisionPlugin implements the plugin.Plugin interface to provide the GRPC +// server or client back to the plugin machinery. The server side should +// provide the Impl field with a concrete implementation of the ProvisionPlugin +// interface. +type ProvisionPlugin struct { + go_plugin.Plugin + Impl Provision +} + +func (pp *ProvisionPlugin) GRPCClient(ctx context.Context, broker *go_plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) { + return &ProvisionPluginClientGRPC{client: provisionpb.NewProvisionPluginServiceClient(c), broker: broker}, nil +} + +func (pp *ProvisionPlugin) GRPCServer(broker *go_plugin.GRPCBroker, s *grpc.Server) error { + provisionpb.RegisterProvisionPluginServiceServer(s, &GRPCServer{impl: pp.Impl, broker: broker}) + return nil +} + +// Type check to ensure that ProvisionPluginClientGRPC implements the Provision interface. +var _ Provision = &ProvisionPluginClientGRPC{} + +// ProvisionPluginClientGRPC is used by clients (main application) to translate the +// Provision interface of plugins to GRPC calls. +type ProvisionPluginClientGRPC struct { + broker *go_plugin.GRPCBroker + client provisionpb.ProvisionPluginServiceClient +} + +func (c *ProvisionPluginClientGRPC) Validate(ctx context.Context) error { + _, err := c.client.Validate(ctx, &provisionpb.ValidateRequest{}) + return wrapError(err) +} + +func (c *ProvisionPluginClientGRPC) Deploy(ctx context.Context, source datasource.DataSource, kubeCfgFile string) (<-chan *provisionpb.Status, error) { + server, brokerID := c.startDataSourceServer(source) + + req := provisionpb.DeployRequest{DataSource: &brokerID, KubeCfgFile: &kubeCfgFile} + stream, err := c.client.Deploy(ctx, &req) + if err != nil { + err := wrapError(err) + return nil, err + } + + statusCh := make(chan *provisionpb.Status) + go func() { + defer close(statusCh) + defer server.Stop() + for { + resp, err := stream.Recv() + if err != nil { + if err != io.EOF { + err := wrapError(err) + statusCh <- StatusError("Deploying", "Error", err) + } + return + } + statusCh <- resp.GetStatus() + } + }() + + return statusCh, nil +} + +func (c *ProvisionPluginClientGRPC) TearDown(ctx context.Context, source datasource.DataSource) (<-chan *provisionpb.Status, error) { + server, brokerID := c.startDataSourceServer(source) + + req := provisionpb.TearDownRequest{DataSource: &brokerID} + stream, err := c.client.TearDown(ctx, &req) + if err != nil { + err := wrapError(err) + return nil, err + } + + statusCh := make(chan *provisionpb.Status) + go func() { + defer close(statusCh) + defer server.Stop() + for { + resp, err := stream.Recv() + if err != nil { + if err != io.EOF { + err := wrapError(err) + statusCh <- StatusError("Tearing down", "Error", err) + } + return + } + statusCh <- resp.GetStatus() + } + }() + + return statusCh, nil +} + +// startDataSourceServer returns a grpc.Server and associated broker ID, allowing for bidirectional +// plugin communication. +// The provided DataSource is used as the server's data source implementation. +// The returned server should be shut down when no longer required. +// This uses the bidirectional communication feature of go-plugin. See +// https://pkg.go.dev/github.com/hashicorp/go-plugin/examples/bidirectional for an example. +func (c *ProvisionPluginClientGRPC) startDataSourceServer(source datasource.DataSource) (*grpc.Server, uint32) { + dsServer := &plugin.GRPCServer{Impl: source} + + serverCh := make(chan *grpc.Server) + serverFunc := func(opts []grpc.ServerOption) *grpc.Server { + server := grpc.NewServer(opts...) + cofidectl_proto.RegisterDataSourcePluginServiceServer(server, dsServer) + serverCh <- server + return server + } + + brokerID := c.broker.NextId() + go c.broker.AcceptAndServe(brokerID, serverFunc) + + // Wait for the accept goroutine to create and register the server. + server := <-serverCh + return server, brokerID +} + +// clientError wraps a gRPC Status, reformatting the error message. +type clientError struct { + status *status.Status +} + +// wrapError returns a clientError if the provided error is a gRPC status, or the original error otherwise. +func wrapError(err error) error { + if err == nil { + return nil + } + if status, ok := status.FromError(err); ok { + return &clientError{status: status} + } else { + return err + } +} + +func (ce *clientError) Error() string { + return fmt.Sprintf("provision plugin error: %s: %s", ce.status.Code(), ce.status.Message()) +} + +// GRPCServer implements provisionpb.ProvisionPluginServiceServer, translating gRPC calls to +// impl, the Provision implementation. +type GRPCServer struct { + impl Provision + broker *go_plugin.GRPCBroker +} + +func (s *GRPCServer) Validate(ctx context.Context, req *provisionpb.ValidateRequest) (*provisionpb.ValidateResponse, error) { + err := s.impl.Validate(ctx) + if err != nil { + return nil, err + } + return &provisionpb.ValidateResponse{}, nil +} + +func (s *GRPCServer) Deploy(req *provisionpb.DeployRequest, stream grpc.ServerStreamingServer[provisionpb.DeployResponse]) error { + client, conn, err := s.getDataSourceClient(stream.Context(), req.GetDataSource()) + if err != nil { + return err + } + defer conn.Close() + + statusCh, err := s.impl.Deploy(stream.Context(), client, req.GetKubeCfgFile()) + if err != nil { + return err + } + + // Read Status messages from the channel and stream back to the client. + for status := range statusCh { + resp := provisionpb.DeployResponse{Status: status} + if err := stream.Send(&resp); err != nil { + return err + } + } + return nil +} + +func (s *GRPCServer) TearDown(req *provisionpb.TearDownRequest, stream grpc.ServerStreamingServer[provisionpb.TearDownResponse]) error { + client, conn, err := s.getDataSourceClient(stream.Context(), req.GetDataSource()) + if err != nil { + return err + } + defer conn.Close() + + statusCh, err := s.impl.TearDown(stream.Context(), client) + if err != nil { + return err + } + + // Read Status messages from the channel and stream back to the client. + for status := range statusCh { + resp := provisionpb.TearDownResponse{Status: status} + if err := stream.Send(&resp); err != nil { + return err + } + } + return nil +} + +// getDataSourceClient returns a DataSource and associated gRPC connection, allowing for +// bidirectional plugin communication. +// The returned DataSource can be passed to the server's Provision implementation methods. +// The returned client should be closed when no longer required. +// This uses the bidirectional communication feature of go-plugin. See +// https://pkg.go.dev/github.com/hashicorp/go-plugin/examples/bidirectional for an example. +func (s *GRPCServer) getDataSourceClient(ctx context.Context, dataSourceID uint32) (datasource.DataSource, *grpc.ClientConn, error) { + conn, err := s.broker.Dial(dataSourceID) + if err != nil { + return nil, nil, err + } + + client := plugin.NewDataSourcePluginClientGRPC(ctx, cofidectl_proto.NewDataSourcePluginServiceClient(conn)) + return client, conn, nil +} diff --git a/pkg/plugin/provision/spirehelm/spirehelm.go b/pkg/plugin/provision/spirehelm/spirehelm.go index d01c5f7..0064996 100644 --- a/pkg/plugin/provision/spirehelm/spirehelm.go +++ b/pkg/plugin/provision/spirehelm/spirehelm.go @@ -36,6 +36,10 @@ func NewSpireHelm(providerFactory ProviderFactory) *SpireHelm { return &SpireHelm{providerFactory: providerFactory} } +func (h *SpireHelm) Validate(_ context.Context) error { + return nil +} + func (h *SpireHelm) Deploy(ctx context.Context, ds plugin.DataSource, kubeCfgFile string) (<-chan *provisionpb.Status, error) { statusCh := make(chan *provisionpb.Status) From 8415615ca36a613e832193e65a860b9513dc6ebf Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Wed, 11 Dec 2024 08:57:44 +0000 Subject: [PATCH 04/48] Refactor plugins configuration --- cmd/cofidectl/cmd/init.go | 14 +- go.mod | 2 +- go.sum | 4 +- internal/pkg/config/config.go | 17 +- internal/pkg/config/config_test.go | 39 ++--- internal/pkg/config/loader_test.go | 133 +++++----------- internal/pkg/config/schema.cue | 9 +- .../pkg/config/testdata/config/default.yaml | 3 +- internal/pkg/config/testdata/config/full.yaml | 5 +- internal/pkg/config/validator_test.go | 10 +- internal/pkg/proto/proto.go | 34 ++-- internal/pkg/test/fixtures/fixtures.go | 26 ++++ pkg/plugin/local/local_test.go | 26 ++-- pkg/plugin/manager/manager.go | 57 ++++--- pkg/plugin/manager/manager_test.go | 147 ++++++++++-------- .../provision/spirehelm/spirehelm_test.go | 1 + pkg/provider/helm/values_test.go | 1 + 17 files changed, 266 insertions(+), 262 deletions(-) diff --git a/cmd/cofidectl/cmd/init.go b/cmd/cofidectl/cmd/init.go index 338ca0c..cb1185e 100644 --- a/cmd/cofidectl/cmd/init.go +++ b/cmd/cofidectl/cmd/init.go @@ -44,23 +44,17 @@ func (i *InitCommand) GetRootCommand() *cobra.Command { Long: initRootCmdDesc, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - var dsName string - var provisionName string + plugins := manager.GetDefaultPlugins() if opts.enableConnect { if ok, _ := plugin.PluginExists(connectPluginName); ok { - dsName = connectPluginName - provisionName = connectPluginName + fmt.Println(`Please run "cofidectl connect init"`) } else { fmt.Println("👀 get in touch with us at hello@cofide.io to find out more") - os.Exit(1) } - } else { - // Default to the local file data source. - dsName = manager.LocalPluginName - provisionName = manager.SpireHelmProvisionPluginName + os.Exit(1) } - _, err := i.cmdCtx.PluginManager.Init(dsName, provisionName, nil) + _, err := i.cmdCtx.PluginManager.Init(plugins, nil) return err }, } diff --git a/go.mod b/go.mod index 842158a..d0bd568 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.22.7 require ( buf.build/go/protoyaml v0.2.0 cuelang.org/go v0.10.1 - github.com/cofide/cofide-api-sdk v0.3.1-0.20241211085315-6c76f01e47d2 + github.com/cofide/cofide-api-sdk v0.3.1-0.20241211174622-3ef03dc9b6dc github.com/fatih/color v1.18.0 github.com/gofrs/flock v0.12.1 github.com/google/go-cmp v0.6.0 diff --git a/go.sum b/go.sum index 7c8867f..7a9cc68 100644 --- a/go.sum +++ b/go.sum @@ -84,8 +84,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.3.1-0.20241211085315-6c76f01e47d2 h1:EEKw2gyPke/JHMqOSW8xJTd0D0R0hj7I9/FEXqcekEQ= -github.com/cofide/cofide-api-sdk v0.3.1-0.20241211085315-6c76f01e47d2/go.mod h1:u2iATR4IbZm9ruIBN734UjVuO3XQKPAFViIY3Xr6kTA= +github.com/cofide/cofide-api-sdk v0.3.1-0.20241211174622-3ef03dc9b6dc h1:iioGLZKrECKzCN9SQ3DNpG1acO+StWU1ZUyOz6+P8Eg= +github.com/cofide/cofide-api-sdk v0.3.1-0.20241211174622-3ef03dc9b6dc/go.mod h1:u2iATR4IbZm9ruIBN734UjVuO3XQKPAFViIY3Xr6kTA= 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.23 h1:H2CClyUkmpKAGlhQp95g2WXHfLYc7whAuvZGBNYOOwQ= diff --git a/internal/pkg/config/config.go b/internal/pkg/config/config.go index 025c7b7..e28325d 100644 --- a/internal/pkg/config/config.go +++ b/internal/pkg/config/config.go @@ -7,46 +7,47 @@ import ( "buf.build/go/protoyaml" attestation_policy_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/attestation_policy/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" "google.golang.org/protobuf/types/known/structpb" ) // Config describes the cofide.yaml configuration file format. type Config struct { - DataSource string TrustZones []*trust_zone_proto.TrustZone AttestationPolicies []*attestation_policy_proto.AttestationPolicy PluginConfig map[string]*structpb.Struct - ProvisionPlugin string + Plugins *pluginspb.Plugins } func NewConfig() *Config { return &Config{ - DataSource: "", TrustZones: []*trust_zone_proto.TrustZone{}, AttestationPolicies: []*attestation_policy_proto.AttestationPolicy{}, PluginConfig: map[string]*structpb.Struct{}, - ProvisionPlugin: "", + Plugins: &pluginspb.Plugins{}, } } func newConfigFromProto(proto *config_proto.Config) *Config { + plugins := proto.GetPlugins() + if plugins == nil { + plugins = &pluginspb.Plugins{} + } return &Config{ - DataSource: proto.GetDataSource(), TrustZones: proto.TrustZones, AttestationPolicies: proto.AttestationPolicies, PluginConfig: proto.PluginConfig, - ProvisionPlugin: proto.GetProvisionPlugin(), + Plugins: plugins, } } func (c *Config) toProto() *config_proto.Config { return &config_proto.Config{ - DataSource: &c.DataSource, TrustZones: c.TrustZones, AttestationPolicies: c.AttestationPolicies, PluginConfig: c.PluginConfig, - ProvisionPlugin: &c.ProvisionPlugin, + Plugins: c.Plugins, } } diff --git a/internal/pkg/config/config_test.go b/internal/pkg/config/config_test.go index 77abc1c..ca221aa 100644 --- a/internal/pkg/config/config_test.go +++ b/internal/pkg/config/config_test.go @@ -7,11 +7,11 @@ import ( "testing" attestation_policy_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/attestation_policy/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" - "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" - "google.golang.org/protobuf/testing/protocmp" + "github.com/stretchr/testify/require" "google.golang.org/protobuf/types/known/structpb" ) @@ -25,14 +25,13 @@ func TestConfig_YAMLMarshall(t *testing.T) { { name: "default", config: &Config{ - DataSource: "local", + Plugins: &pluginspb.Plugins{}, }, wantFile: "default.yaml", }, { name: "full", config: &Config{ - DataSource: "fake-plugin", TrustZones: []*trust_zone_proto.TrustZone{ fixtures.TrustZone("tz1"), fixtures.TrustZone("tz2"), @@ -46,7 +45,7 @@ func TestConfig_YAMLMarshall(t *testing.T) { "plugin1": fixtures.PluginConfig("plugin1"), "plugin2": fixtures.PluginConfig("plugin2"), }, - ProvisionPlugin: "fake-provision-plugin", + Plugins: fixtures.Plugins("plugins1"), }, wantFile: "full.yaml", }, @@ -74,18 +73,16 @@ func TestConfig_YAMLUnmarshall(t *testing.T) { name: "default", file: "default.yaml", want: &Config{ - DataSource: "local", TrustZones: []*trust_zone_proto.TrustZone{}, AttestationPolicies: []*attestation_policy_proto.AttestationPolicy{}, PluginConfig: map[string]*structpb.Struct{}, - ProvisionPlugin: "", + Plugins: &pluginspb.Plugins{}, }, }, { name: "full", file: "full.yaml", want: &Config{ - DataSource: "fake-plugin", TrustZones: []*trust_zone_proto.TrustZone{ fixtures.TrustZone("tz1"), fixtures.TrustZone("tz2"), @@ -99,7 +96,7 @@ func TestConfig_YAMLUnmarshall(t *testing.T) { "plugin1": fixtures.PluginConfig("plugin1"), "plugin2": fixtures.PluginConfig("plugin2"), }, - ProvisionPlugin: "fake-provision-plugin", + Plugins: fixtures.Plugins("plugins1"), }, }, } @@ -107,12 +104,8 @@ func TestConfig_YAMLUnmarshall(t *testing.T) { t.Run(tt.name, func(t *testing.T) { yamlConfig := readTestConfig(t, tt.file) got, err := unmarshalYAML(yamlConfig) - if err != nil { - t.Fatalf("error unmarshalling configuration from YAML: %v", err) - } - if diff := cmp.Diff(got, tt.want, protocmp.Transform()); diff != "" { - t.Errorf("yaml.Unmarshall() mismatch (-want,+got):\n%s", diff) - } + require.NoError(t, err, err) + assert.EqualExportedValues(t, tt.want, got) }) } } @@ -156,12 +149,8 @@ func TestConfig_GetTrustZoneByName(t *testing.T) { TrustZones: tt.trustZones, } gotTz, gotOk := c.GetTrustZoneByName(tt.trustZone) - if diff := cmp.Diff(tt.wantTz, gotTz, protocmp.Transform()); diff != "" { - t.Errorf("Config.GetTrustZoneByName() mismatch (-want,+got):\n%s", diff) - } - if gotOk != tt.wantOk { - t.Errorf("Config.GetTrustZoneByName() got1 = %v, want %v", gotOk, tt.wantOk) - } + assert.EqualExportedValues(t, tt.wantTz, gotTz) + assert.Equal(t, tt.wantOk, gotOk) }) } } @@ -205,12 +194,8 @@ func TestConfig_GetAttestationPolicyByName(t *testing.T) { AttestationPolicies: tt.policies, } gotAp, gotOk := c.GetAttestationPolicyByName(tt.policy) - if diff := cmp.Diff(tt.wantAp, gotAp, protocmp.Transform()); diff != "" { - t.Errorf("Config.GetAttestationPolicyByName() mismatch (-want,+got):\n%s", diff) - } - if gotOk != tt.wantOk { - t.Errorf("Config.GetAttestationPolicyByName() got1 = %v, want %v", gotOk, tt.wantOk) - } + assert.EqualExportedValues(t, tt.wantAp, gotAp) + assert.Equal(t, tt.wantOk, gotOk) }) } } diff --git a/internal/pkg/config/loader_test.go b/internal/pkg/config/loader_test.go index 2596f5c..6d96cae 100644 --- a/internal/pkg/config/loader_test.go +++ b/internal/pkg/config/loader_test.go @@ -6,12 +6,10 @@ package config import ( "os" "path/filepath" - "strings" "testing" - "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" - "google.golang.org/protobuf/testing/protocmp" + "github.com/stretchr/testify/require" attestation_policy_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/attestation_policy/v1alpha1" trust_zone_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/trust_zone/v1alpha1" @@ -29,21 +27,14 @@ func TestFileLoaderNonExistentConfig(t *testing.T) { loader := NewFileLoader(filepath.Join(tempDir, "non-existent.yaml")) gotExists, err := loader.Exists() - if err != nil { - t.Errorf("FileLoader.Exists() error = %v", err) - } else if gotExists { - t.Errorf("FileLoader.Exists() returned true") - } + require.NoError(t, err, err) + assert.False(t, gotExists, "FileLoader.Exists() returned true") _, gotErr := loader.Read() - if gotErr == nil { - t.Fatal("FileLoader.Read() did not return error") - } + require.Error(t, gotErr, "FileLoader.Read() did not return error") wantErr := "non-existent.yaml: no such file or directory" - if !strings.Contains(gotErr.Error(), wantErr) { - t.Fatalf("FileLoader.Read() err = %v, want %v", gotErr.Error(), wantErr) - } + assert.ErrorContains(t, gotErr, wantErr) } func TestFileLoaderWriteEmptyConfig(t *testing.T) { @@ -53,26 +44,17 @@ func TestFileLoaderWriteEmptyConfig(t *testing.T) { config := NewConfig() err := loader.Write(config) - if err != nil { - t.Fatalf("FileLoader.Write() returned error: %v", err) - } + require.NoError(t, err, err) gotExists, err := loader.Exists() - if err != nil { - t.Errorf("FileLoader.Exists() error = %v", err) - } else if !gotExists { - t.Errorf("FileLoader.Exists() returned false") - } + require.NoError(t, err, err) + assert.True(t, gotExists, "FileLoader.Exists() returned false") got, err := loader.Read() - if err != nil { - t.Fatalf("FileLoader.Read() returned error: %v", err) - } + require.NoError(t, err, err) want := NewConfig() - if diff := cmp.Diff(want, got); diff != "" { - t.Errorf("FileLoader.Read() mismatch (-want,+got):\n%s", diff) - } + assert.EqualExportedValues(t, want, got) } func TestFileLoaderNonEmptyConfig(t *testing.T) { @@ -81,7 +63,6 @@ func TestFileLoaderNonEmptyConfig(t *testing.T) { loader := NewFileLoader(filepath.Join(tempDir, "config.yaml")) config := NewConfig() - config.DataSource = "fake-plugin" config.TrustZones = []*trust_zone_proto.TrustZone{ fixtures.TrustZone("tz1"), fixtures.TrustZone("tz2"), @@ -90,21 +71,16 @@ func TestFileLoaderNonEmptyConfig(t *testing.T) { fixtures.AttestationPolicy("ap1"), fixtures.AttestationPolicy("ap2"), } + config.Plugins = fixtures.Plugins("plugins1") err := loader.Write(config) - if err != nil { - t.Fatalf("FileLoader.Write() returned error: %v", err) - } + require.NoError(t, err, err) got, err := loader.Read() - if err != nil { - t.Fatalf("FileLoader.Read() returned error: %v", err) - } + require.NoError(t, err, err) want := config - if diff := cmp.Diff(want, got, protocmp.Transform()); diff != "" { - t.Errorf("FileLoader.Read() mismatch (-want,+got):\n%s", diff) - } + assert.EqualExportedValues(t, want, got) } func TestFileLoaderReadInvalid(t *testing.T) { @@ -112,16 +88,13 @@ func TestFileLoaderReadInvalid(t *testing.T) { tempFile := filepath.Join(t.TempDir(), "config.yaml") loader := NewFileLoader(tempFile) - if err := os.WriteFile(tempFile, []byte(`data_source: 123`), 0o600); err != nil { - t.Fatalf("Failed to write to temp file: %v", err) - } + err := os.WriteFile(tempFile, []byte(`plugins: 123`), 0o600) + require.NoError(t, err, err) _, gotErr := loader.Read() - if gotErr == nil { - t.Fatalf("FileLoader.Read() did not return error") - } + require.Error(t, gotErr, "FileLoader.Read() did not return error") - wantErr := `error validating configuration YAML: data_source: conflicting values 123 and string (mismatched types int and string)` + wantErr := `error validating configuration YAML: plugins: conflicting values 123 and` assert.ErrorContains(t, gotErr, wantErr) } @@ -133,68 +106,45 @@ func TestMemoryLoaderImplementsLoader(t *testing.T) { func TestMemoryLoaderReadEmptyConfig(t *testing.T) { // First read without a write should return an error. loader, err := NewMemoryLoader(nil) - if err != nil { - t.Fatalf("NewMemoryLoader() returned error: %v", err) - } + require.NoError(t, err, err) gotExists, err := loader.Exists() - if err != nil { - t.Errorf("MemoryLoader.Exists() error = %v", err) - } else if gotExists { - t.Errorf("MemoryLoader.Exists() returned true") - } + require.NoError(t, err, err) + assert.False(t, gotExists, "MemoryLoader.Exists() returned true") _, gotErr := loader.Read() - if gotErr == nil { - t.Fatal("MemoryLoader.Read() did not return error") - } + require.Error(t, gotErr, gotErr) wantErr := "in-memory configuration does not exist" - if gotErr.Error() != wantErr { - t.Fatalf("MemoryLoader.Read() err = %v, want %v", gotErr.Error(), wantErr) - } + assert.ErrorContains(t, gotErr, wantErr) } func TestMemoryLoaderWriteEmptyConfig(t *testing.T) { // Reading after writing an empty Config should return an identical empty Config. loader, err := NewMemoryLoader(nil) - if err != nil { - t.Fatalf("NewMemoryLoader() returned error: %v", err) - } + require.NoError(t, err, err) config := NewConfig() err = loader.Write(config) - if err != nil { - t.Fatalf("MemoryLoader.Write() returned error: %v", err) - } + require.NoError(t, err, err) gotExists, err := loader.Exists() - if err != nil { - t.Errorf("MemoryLoader.Exists() error = %v", err) - } else if !gotExists { - t.Errorf("MemoryLoader.Exists() returned false") - } + require.NoError(t, err, err) + assert.True(t, gotExists, "MemoryLoader.Exists() returned false") got, err := loader.Read() - if err != nil { - t.Fatalf("MemoryLoader.Read() returned error: %v", err) - } + require.NoError(t, err, err) want := NewConfig() - if diff := cmp.Diff(want, got); diff != "" { - t.Errorf("MemoryLoader.Read() mismatch (-want,+got):\n%s", diff) - } + assert.EqualExportedValues(t, want, got) } func TestMemoryLoaderNonEmptyConfig(t *testing.T) { // Reading after writing a non-empty Config should return an identical non-empty Config. loader, err := NewMemoryLoader(nil) - if err != nil { - t.Fatalf("NewMemoryLoader() returned error: %v", err) - } + require.NoError(t, err, err) config := NewConfig() - config.DataSource = "fake-plugin" config.TrustZones = []*trust_zone_proto.TrustZone{ fixtures.TrustZone("tz1"), fixtures.TrustZone("tz2"), @@ -203,27 +153,21 @@ func TestMemoryLoaderNonEmptyConfig(t *testing.T) { fixtures.AttestationPolicy("ap1"), fixtures.AttestationPolicy("ap2"), } + config.Plugins = fixtures.Plugins("plugins1") err = loader.Write(config) - if err != nil { - t.Fatalf("MemoryLoader.Write() returned error: %v", err) - } + require.NoError(t, err, err) got, err := loader.Read() - if err != nil { - t.Fatalf("MemoryLoader.Read() returned error: %v", err) - } + require.NoError(t, err, err) want := config - if diff := cmp.Diff(want, got, protocmp.Transform()); diff != "" { - t.Errorf("MemoryLoader.Read() mismatch (-want,+got):\n%s", diff) - } + assert.EqualExportedValues(t, want, got) } func TestMemoryLoaderInitialConfig(t *testing.T) { // Creating a MemoryLoader with an initial Config should return an identical Config on Read. config := NewConfig() - config.DataSource = "fake-plugin" config.TrustZones = []*trust_zone_proto.TrustZone{ fixtures.TrustZone("tz1"), fixtures.TrustZone("tz2"), @@ -232,6 +176,7 @@ func TestMemoryLoaderInitialConfig(t *testing.T) { fixtures.AttestationPolicy("ap1"), fixtures.AttestationPolicy("ap2"), } + config.Plugins = fixtures.Plugins("plugins1") loader, err := NewMemoryLoader(config) if err != nil { @@ -251,9 +196,7 @@ func TestMemoryLoaderInitialConfig(t *testing.T) { } want := config - if diff := cmp.Diff(want, got, protocmp.Transform()); diff != "" { - t.Errorf("MemoryLoader.Read() mismatch (-want,+got):\n%s", diff) - } + assert.EqualExportedValues(t, want, got) } func TestMemoryLoaderReadInvalid(t *testing.T) { @@ -263,7 +206,7 @@ func TestMemoryLoaderReadInvalid(t *testing.T) { t.Fatalf("NewMemoryLoader() returned error: %v", err) } - loader.data = []byte(`data_source: 123`) + loader.data = []byte(`plugins: 123`) loader.exists = true _, gotErr := loader.Read() @@ -271,6 +214,6 @@ func TestMemoryLoaderReadInvalid(t *testing.T) { t.Fatalf("MemoryLoader.Read() did not return error") } - wantErr := `error validating configuration YAML: data_source: conflicting values 123 and string (mismatched types int and string)` + wantErr := `error validating configuration YAML: plugins: conflicting values 123 and` assert.ErrorContains(t, gotErr, wantErr) } diff --git a/internal/pkg/config/schema.cue b/internal/pkg/config/schema.cue index 76a150f..a9a13f6 100644 --- a/internal/pkg/config/schema.cue +++ b/internal/pkg/config/schema.cue @@ -65,15 +65,16 @@ [string]: _ } -#ProvisionPlugin: string +#Plugins: { + data_source?: string + provision?: string +} #Config: { - data_source!: #DataSource trust_zones: [...#TrustZone] attestation_policies: [...#AttestationPolicy] plugin_config?: #PluginConfig - // FIXME: make required? - provision_plugin?: #ProvisionPlugin + plugins!: #Plugins } #Config diff --git a/internal/pkg/config/testdata/config/default.yaml b/internal/pkg/config/testdata/config/default.yaml index 0fffaa3..c3f0cd9 100644 --- a/internal/pkg/config/testdata/config/default.yaml +++ b/internal/pkg/config/testdata/config/default.yaml @@ -1,2 +1 @@ -data_source: local -provision_plugin: "" +plugins: {} diff --git a/internal/pkg/config/testdata/config/full.yaml b/internal/pkg/config/testdata/config/full.yaml index 695b799..b93f1a5 100644 --- a/internal/pkg/config/testdata/config/full.yaml +++ b/internal/pkg/config/testdata/config/full.yaml @@ -1,4 +1,3 @@ -data_source: fake-plugin trust_zones: - name: tz1 trust_domain: td1 @@ -90,4 +89,6 @@ plugin_config: plugin2: number-cfg: 123 string-cfg: fake-string -provision_plugin: fake-provision-plugin +plugins: + data_source: fake-datasource + provision: fake-provision diff --git a/internal/pkg/config/validator_test.go b/internal/pkg/config/validator_test.go index 05fb172..1950cb1 100644 --- a/internal/pkg/config/validator_test.go +++ b/internal/pkg/config/validator_test.go @@ -46,11 +46,6 @@ func TestValidator_ValidateInvalid(t *testing.T) { data: "foo: bar", wantErr: "foo: field not allowed", }, - { - name: "data source not a string", - data: "data_source: 123", - wantErr: "data_source: conflicting values 123 and string", - }, { name: "trust zones not a list", data: "trust_zones: \"not-a-list\"", @@ -86,6 +81,11 @@ func TestValidator_ValidateInvalid(t *testing.T) { data: string(readTestConfig(t, "missing_attestation_policy_field.yaml")), wantErr: "attestation_policies.0.kubernetes: field is required but not present", }, + { + name: "plugins not a map", + data: "plugins: 123", + wantErr: "plugins: conflicting values 123 and ", + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/internal/pkg/proto/proto.go b/internal/pkg/proto/proto.go index 68b4fa9..55580de 100644 --- a/internal/pkg/proto/proto.go +++ b/internal/pkg/proto/proto.go @@ -12,6 +12,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" 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" ) @@ -48,6 +49,28 @@ func CloneAPBinding(binding *ap_binding_proto.APBinding) (*ap_binding_proto.APBi } } +func CloneFederation(federation *federation_proto.Federation) (*federation_proto.Federation, error) { + if clone, ok := proto.Clone(federation).(*federation_proto.Federation); !ok { + return nil, fmt.Errorf("bug: type assertion failed for federation %s-%s", federation.From, federation.To) + } else { + if clone == federation { + return nil, fmt.Errorf("bug: federation %s-%s clones are the same", federation.To, federation.To) + } + return clone, nil + } +} + +func ClonePlugins(p *pluginspb.Plugins) (*pluginspb.Plugins, error) { + if clone, ok := proto.Clone(p).(*pluginspb.Plugins); !ok { + return nil, fmt.Errorf("bug: type assertion failed for plugins %s", p) + } else { + if clone == p { + return nil, fmt.Errorf("bug: plugins %s clones are the same", p) + } + return clone, nil + } +} + func CloneStruct(spb *structpb.Struct) (*structpb.Struct, error) { if clone, ok := proto.Clone(spb).(*structpb.Struct); !ok { return nil, fmt.Errorf("bug: type assertion failed for struct %s", spb) @@ -63,17 +86,6 @@ func APBindingsEqual(apb1, apb2 *ap_binding_proto.APBinding) bool { return proto.Equal(apb1, apb2) } -func CloneFederation(federation *federation_proto.Federation) (*federation_proto.Federation, error) { - if clone, ok := proto.Clone(federation).(*federation_proto.Federation); !ok { - return nil, fmt.Errorf("bug: type assertion failed for federation %s-%s", federation.From, federation.To) - } else { - if clone == federation { - return nil, fmt.Errorf("bug: federation %s-%s clones are the same", federation.To, federation.To) - } - return clone, nil - } -} - func FederationsEqual(f1, f2 *federation_proto.Federation) bool { return proto.Equal(f1, f2) } diff --git a/internal/pkg/test/fixtures/fixtures.go b/internal/pkg/test/fixtures/fixtures.go index 85b82d6..512625e 100644 --- a/internal/pkg/test/fixtures/fixtures.go +++ b/internal/pkg/test/fixtures/fixtures.go @@ -9,6 +9,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" 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_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/proto" @@ -193,6 +194,19 @@ var pluginConfigFixtures map[string]*structpb.Struct = map[string]*structpb.Stru }(), } +var pluginsFixtures map[string]*pluginspb.Plugins = map[string]*pluginspb.Plugins{ + // Data source and provision use different plugins. + "plugins1": { + DataSource: StringPtr("fake-datasource"), + Provision: StringPtr("fake-provision"), + }, + // Data source and provision use the same plugin. + "plugins2": { + DataSource: StringPtr("fake-plugin"), + Provision: StringPtr("fake-plugin"), + }, +} + func TrustZone(name string) *trust_zone_proto.TrustZone { tz, ok := trustZoneFixtures[name] if !ok { @@ -229,6 +243,18 @@ func PluginConfig(name string) *structpb.Struct { return pc } +func Plugins(name string) *pluginspb.Plugins { + p, ok := pluginsFixtures[name] + if !ok { + panic(fmt.Sprintf("invalid plugins fixture %s", name)) + } + p, err := proto.ClonePlugins(p) + if err != nil { + panic(fmt.Sprintf("failed to clone plugins: %s", err)) + } + return p +} + func StringPtr(s string) *string { return &s } diff --git a/pkg/plugin/local/local_test.go b/pkg/plugin/local/local_test.go index 906f892..d8848b8 100644 --- a/pkg/plugin/local/local_test.go +++ b/pkg/plugin/local/local_test.go @@ -49,15 +49,13 @@ func TestNewLocalDataSource(t *testing.T) { { name: "non-default config", config: &config.Config{ - DataSource: "test-plugin", - ProvisionPlugin: "test-provision-plugin", + Plugins: fixtures.Plugins("plugins1"), }, wantConfig: &config.Config{ - DataSource: "test-plugin", TrustZones: []*trust_zone_proto.TrustZone{}, AttestationPolicies: []*attestation_policy_proto.AttestationPolicy{}, PluginConfig: map[string]*structpb.Struct{}, - ProvisionPlugin: "test-provision-plugin", + Plugins: fixtures.Plugins("plugins1"), }, }, } @@ -71,11 +69,8 @@ func TestNewLocalDataSource(t *testing.T) { require.Error(t, err) } else { require.Nil(t, err) - want := &LocalDataSource{ - loader: loader, - config: tt.wantConfig, - } - assert.Equal(t, want, got) + assert.Same(t, loader, got.loader) + assert.EqualExportedValues(t, tt.wantConfig, got.config) } }) } @@ -109,6 +104,7 @@ func TestLocalDataSource_AddTrustZone(t *testing.T) { TrustZones: []*trust_zone_proto.TrustZone{ fixtures.TrustZone("tz1"), }, + Plugins: fixtures.Plugins("plugins1"), }, trustZone: fixtures.TrustZone("tz1"), wantErr: true, @@ -162,6 +158,7 @@ func TestLocalDataSource_GetTrustZone(t *testing.T) { TrustZones: []*trust_zone_proto.TrustZone{ fixtures.TrustZone("tz1"), }, + Plugins: fixtures.Plugins("plugins1"), } lds, _ := buildLocalDataSource(t, cfg) @@ -270,6 +267,7 @@ func TestLocalDataSource_UpdateTrustZone(t *testing.T) { TrustZones: []*trust_zone_proto.TrustZone{ fixtures.TrustZone("tz1"), }, + Plugins: fixtures.Plugins("plugins1"), } lds, loader := buildLocalDataSource(t, cfg) @@ -310,6 +308,7 @@ func TestLocalDataSource_ListTrustZones(t *testing.T) { fixtures.TrustZone("tz1"), fixtures.TrustZone("tz2"), }, + Plugins: fixtures.Plugins("plugins1"), }, wantErr: false, }, @@ -355,6 +354,7 @@ func TestLocalDataSource_AddAttestationPolicy(t *testing.T) { AttestationPolicies: []*attestation_policy_proto.AttestationPolicy{ fixtures.AttestationPolicy("ap1"), }, + Plugins: fixtures.Plugins("plugins1"), }, policy: fixtures.AttestationPolicy("ap1"), wantErr: true, @@ -408,6 +408,7 @@ func TestLocalDataSource_GetAttestationPolicy(t *testing.T) { AttestationPolicies: []*attestation_policy_proto.AttestationPolicy{ fixtures.AttestationPolicy("ap1"), }, + Plugins: fixtures.Plugins("plugins1"), } lds, _ := buildLocalDataSource(t, cfg) @@ -443,6 +444,7 @@ func TestLocalDataSource_ListAttestationPolicies(t *testing.T) { fixtures.AttestationPolicy("ap1"), fixtures.AttestationPolicy("ap2"), }, + Plugins: fixtures.Plugins("plugins1"), }, wantErr: false, }, @@ -553,6 +555,7 @@ func TestLocalDataSource_AddAPBinding(t *testing.T) { fixtures.AttestationPolicy("ap1"), fixtures.AttestationPolicy("ap2"), }, + Plugins: fixtures.Plugins("plugins1"), } lds, loader := buildLocalDataSource(t, cfg) got, err := lds.AddAPBinding(tt.binding) @@ -616,6 +619,7 @@ func TestLocalDataSource_DestroyAPBinding(t *testing.T) { AttestationPolicies: []*attestation_policy_proto.AttestationPolicy{ fixtures.AttestationPolicy("ap1"), }, + Plugins: fixtures.Plugins("plugins1"), } lds, loader := buildLocalDataSource(t, cfg) err := lds.DestroyAPBinding(tt.binding) @@ -665,6 +669,7 @@ func TestLocalDataSource_ListAPBindingsByTrustZone(t *testing.T) { fixtures.TrustZone("tz1"), fixtures.TrustZone("tz3"), }, + Plugins: fixtures.Plugins("plugins1"), } lds, _ := buildLocalDataSource(t, cfg) got, err := lds.ListAPBindingsByTrustZone(tt.trustZone) @@ -747,6 +752,7 @@ func TestLocalDataSource_AddFederation(t *testing.T) { fixtures.TrustZone("tz2"), fixtures.TrustZone("tz3"), }, + Plugins: fixtures.Plugins("plugins1"), } lds, loader := buildLocalDataSource(t, cfg) got, err := lds.AddFederation(tt.federation) @@ -785,6 +791,7 @@ func TestLocalDataSource_ListFederations(t *testing.T) { fixtures.TrustZone("tz1"), fixtures.TrustZone("tz2"), }, + Plugins: fixtures.Plugins("plugins1"), }, wantErr: false, }, @@ -846,6 +853,7 @@ func TestLocalDataSource_ListFederationsByTrustZone(t *testing.T) { fixtures.TrustZone("tz1"), fixtures.TrustZone("tz3"), }, + Plugins: fixtures.Plugins("plugins1"), } lds, _ := buildLocalDataSource(t, cfg) got, err := lds.ListFederationsByTrustZone(tt.trustZone) diff --git a/pkg/plugin/manager/manager.go b/pkg/plugin/manager/manager.go index a5a2dc4..0d50e63 100644 --- a/pkg/plugin/manager/manager.go +++ b/pkg/plugin/manager/manager.go @@ -11,6 +11,7 @@ import ( "os" "os/exec" + pluginspb "github.com/cofide/cofide-api-sdk/gen/go/proto/plugins/v1alpha1" "github.com/cofide/cofidectl/internal/pkg/config" "github.com/cofide/cofidectl/internal/pkg/proto" cofidectl_plugin "github.com/cofide/cofidectl/pkg/plugin" @@ -24,7 +25,7 @@ import ( ) const ( - LocalPluginName = "local" + LocalDSPluginName = "local" SpireHelmProvisionPluginName = "spire-helm" ) @@ -45,19 +46,25 @@ func NewManager(configLoader config.Loader) *PluginManager { } } -// Init initialises the configuration for the specified data source plugin. -func (pm *PluginManager) Init(dsName string, provisionName string, pluginConfig map[string]*structpb.Struct) (cofidectl_plugin.DataSource, error) { +// Init initialises the configuration for the specified plugins. +func (pm *PluginManager) Init(plugins *pluginspb.Plugins, pluginConfig map[string]*structpb.Struct) (cofidectl_plugin.DataSource, error) { + if plugins == nil { + plugins = GetDefaultPlugins() + } + if exists, _ := pm.configLoader.Exists(); exists { // Check that existing plugin config matches. cfg, err := pm.configLoader.Read() if err != nil { return nil, err } - if cfg.DataSource != dsName { - return nil, fmt.Errorf("existing config file uses a different data source plugin: %s vs %s", cfg.DataSource, dsName) + ds := plugins.GetDataSource() + provision := plugins.GetProvision() + if ds != cfg.Plugins.GetDataSource() { + return nil, fmt.Errorf("existing config file uses a different data source plugin: %s vs %s", cfg.Plugins.GetDataSource(), ds) } - if cfg.ProvisionPlugin != provisionName { - return nil, fmt.Errorf("existing config file uses a different provision plugin: %s vs %s", cfg.ProvisionPlugin, provisionName) + if cfg.Plugins.GetProvision() != provision { + return nil, fmt.Errorf("existing config file uses a different provision plugin: %s vs %s", cfg.Plugins.GetProvision(), provision) } if !maps.EqualFunc(cfg.PluginConfig, pluginConfig, proto.StructsEqual) { return nil, fmt.Errorf("existing config file has different plugin config:\n%v\nvs\n\n%v", cfg.PluginConfig, pluginConfig) @@ -65,8 +72,11 @@ func (pm *PluginManager) Init(dsName string, provisionName string, pluginConfig fmt.Println("the config file already exists") } else { cfg := config.NewConfig() - cfg.DataSource = dsName - cfg.ProvisionPlugin = provisionName + plugins, err := proto.ClonePlugins(plugins) + if err != nil { + return nil, err + } + cfg.Plugins = plugins if pluginConfig != nil { cfg.PluginConfig = pluginConfig } @@ -96,14 +106,15 @@ func (pm *PluginManager) loadDataSource() (cofidectl_plugin.DataSource, error) { return nil, err } - if cfg.DataSource == "" { + dsName := cfg.Plugins.GetDataSource() + if dsName == "" { return nil, errors.New("plugin name cannot be empty") } var ds cofidectl_plugin.DataSource var client *go_plugin.Client - switch cfg.DataSource { - case LocalPluginName: + switch dsName { + case LocalDSPluginName: ds, err = local.NewLocalDataSource(pm.configLoader) if err != nil { return nil, err @@ -115,7 +126,7 @@ func (pm *PluginManager) loadDataSource() (cofidectl_plugin.DataSource, error) { Level: hclog.Error, }) - client, ds, _, err = pm.loadGrpcPlugin(logger, cfg.DataSource) + client, ds, _, err = pm.loadGrpcPlugin(logger, dsName) if err != nil { return nil, err } @@ -128,7 +139,7 @@ func (pm *PluginManager) loadDataSource() (cofidectl_plugin.DataSource, error) { return nil, err } pm.source = ds - pm.clients[cfg.DataSource] = client + pm.clients[dsName] = client return ds, nil } @@ -149,13 +160,14 @@ func (pm *PluginManager) loadProvision() (provision.Provision, error) { return nil, err } - if cfg.ProvisionPlugin == "" { + provisionName := cfg.Plugins.GetProvision() + if provisionName == "" { return nil, errors.New("provision plugin name cannot be empty") } var provision provision.Provision var client *go_plugin.Client - switch cfg.ProvisionPlugin { + switch provisionName { case SpireHelmProvisionPluginName: return spirehelm.NewSpireHelm(nil), nil default: @@ -165,7 +177,7 @@ func (pm *PluginManager) loadProvision() (provision.Provision, error) { Level: hclog.Error, }) - client, _, provision, err = pm.loadGrpcPlugin(logger, cfg.ProvisionPlugin) + client, _, provision, err = pm.loadGrpcPlugin(logger, provisionName) if err != nil { return nil, err } @@ -178,7 +190,7 @@ func (pm *PluginManager) loadProvision() (provision.Provision, error) { return nil, err } pm.provision = provision - pm.clients[cfg.ProvisionPlugin] = client + pm.clients[provisionName] = client return provision, nil } @@ -293,3 +305,12 @@ func (pm *PluginManager) SetPluginConfig(pluginName string, pluginConfig *struct cfg.PluginConfig[pluginName] = pluginConfig return pm.configLoader.Write(cfg) } + +func GetDefaultPlugins() *pluginspb.Plugins { + ds := LocalDSPluginName + provision := SpireHelmProvisionPluginName + return &pluginspb.Plugins{ + DataSource: &ds, + Provision: &provision, + } +} diff --git a/pkg/plugin/manager/manager_test.go b/pkg/plugin/manager/manager_test.go index 900b6f3..855d146 100644 --- a/pkg/plugin/manager/manager_test.go +++ b/pkg/plugin/manager/manager_test.go @@ -7,7 +7,9 @@ import ( "errors" "testing" + pluginspb "github.com/cofide/cofide-api-sdk/gen/go/proto/plugins/v1alpha1" "github.com/cofide/cofidectl/internal/pkg/config" + "github.com/cofide/cofidectl/internal/pkg/test/fixtures" cofidectl_plugin "github.com/cofide/cofidectl/pkg/plugin" "github.com/cofide/cofidectl/pkg/plugin/local" "github.com/cofide/cofidectl/pkg/plugin/provision" @@ -39,18 +41,26 @@ func newFakeGrpcProvision() *fakeGrpcProvision { func TestManager_Init_success(t *testing.T) { tests := []struct { - name string - config *config.Config - dsName string - provisionName string - pluginConfig map[string]*structpb.Struct - want func(config.Loader) cofidectl_plugin.DataSource + name string + config *config.Config + plugins *pluginspb.Plugins + pluginConfig map[string]*structpb.Struct + want func(config.Loader) cofidectl_plugin.DataSource }{ { - name: "local", - config: nil, - dsName: LocalPluginName, - provisionName: SpireHelmProvisionPluginName, + name: "defaults", + config: nil, + plugins: GetDefaultPlugins(), + want: func(cl config.Loader) cofidectl_plugin.DataSource { + lds, err := local.NewLocalDataSource(cl) + assert.Nil(t, err) + return lds + }, + }, + { + name: "nil plugins", + config: nil, + plugins: nil, want: func(cl config.Loader) cofidectl_plugin.DataSource { lds, err := local.NewLocalDataSource(cl) assert.Nil(t, err) @@ -58,21 +68,19 @@ func TestManager_Init_success(t *testing.T) { }, }, { - name: "gRPC", - config: nil, - dsName: "fake-plugin", - provisionName: "fake-provision-plugin", + name: "gRPC", + config: nil, + plugins: fixtures.Plugins("plugins1"), want: func(cl config.Loader) cofidectl_plugin.DataSource { fcds := newFakeGrpcDataSource(t, cl) return fcds }, }, { - name: "local with config", - config: nil, - dsName: LocalPluginName, - provisionName: SpireHelmProvisionPluginName, - pluginConfig: fakePluginConfig(t), + name: "defaults with config", + config: nil, + plugins: GetDefaultPlugins(), + pluginConfig: fakePluginConfig(t), want: func(cl config.Loader) cofidectl_plugin.DataSource { lds, err := local.NewLocalDataSource(cl) assert.Nil(t, err) @@ -80,10 +88,9 @@ func TestManager_Init_success(t *testing.T) { }, }, { - name: "existing local", - config: &config.Config{DataSource: LocalPluginName, ProvisionPlugin: SpireHelmProvisionPluginName}, - dsName: LocalPluginName, - provisionName: SpireHelmProvisionPluginName, + name: "existing defaults", + config: &config.Config{Plugins: GetDefaultPlugins()}, + plugins: GetDefaultPlugins(), want: func(cl config.Loader) cofidectl_plugin.DataSource { lds, err := local.NewLocalDataSource(cl) assert.Nil(t, err) @@ -91,21 +98,19 @@ func TestManager_Init_success(t *testing.T) { }, }, { - name: "existing gRPC", - config: &config.Config{DataSource: "fake-plugin", ProvisionPlugin: "fake-provision-plugin"}, - dsName: "fake-plugin", - provisionName: "fake-provision-plugin", + name: "existing gRPC", + config: &config.Config{Plugins: fixtures.Plugins("plugins1")}, + plugins: fixtures.Plugins("plugins1"), want: func(cl config.Loader) cofidectl_plugin.DataSource { fcds := newFakeGrpcDataSource(t, cl) return fcds }, }, { - name: "existing local with config", - config: &config.Config{DataSource: LocalPluginName, ProvisionPlugin: SpireHelmProvisionPluginName, PluginConfig: fakePluginConfig(t)}, - dsName: LocalPluginName, - provisionName: SpireHelmProvisionPluginName, - pluginConfig: fakePluginConfig(t), + name: "existing defaults with config", + config: &config.Config{Plugins: GetDefaultPlugins(), PluginConfig: fakePluginConfig(t)}, + plugins: GetDefaultPlugins(), + pluginConfig: fakePluginConfig(t), want: func(cl config.Loader) cofidectl_plugin.DataSource { lds, err := local.NewLocalDataSource(cl) assert.Nil(t, err) @@ -119,12 +124,12 @@ func TestManager_Init_success(t *testing.T) { require.Nil(t, err) m := NewManager(configLoader) - // Mock out the Connect plugin loader function. + // Mock out the gRPC plugin loader function. m.loadGrpcPlugin = func(logger hclog.Logger, _ string) (*go_plugin.Client, cofidectl_plugin.DataSource, provision.Provision, error) { return nil, newFakeGrpcDataSource(t, configLoader), newFakeGrpcProvision(), nil } - got, err := m.Init(tt.dsName, tt.provisionName, tt.pluginConfig) + got, err := m.Init(tt.plugins, tt.pluginConfig) require.Nil(t, err) want := tt.want(configLoader) @@ -132,8 +137,12 @@ func TestManager_Init_success(t *testing.T) { config, err := configLoader.Read() assert.Nil(t, err) - assert.Equal(t, tt.dsName, config.DataSource) - assert.Equal(t, tt.provisionName, config.ProvisionPlugin) + wantPlugins := tt.plugins + if wantPlugins == nil { + wantPlugins = GetDefaultPlugins() + } + assert.Equal(t, wantPlugins.GetDataSource(), config.Plugins.GetDataSource()) + assert.Equal(t, wantPlugins.GetProvision(), config.Plugins.GetProvision()) expectedConfig := tt.pluginConfig if expectedConfig == nil { @@ -167,22 +176,22 @@ func TestManager_Init_failure(t *testing.T) { }{ { name: "existing different data source", - config: &config.Config{DataSource: "fake-plugin"}, - dsName: LocalPluginName, + config: &config.Config{Plugins: fixtures.Plugins("plugins1")}, + dsName: LocalDSPluginName, provisionName: SpireHelmProvisionPluginName, - wantErrMessage: "existing config file uses a different data source plugin: fake-plugin vs local", + wantErrMessage: "existing config file uses a different data source plugin: fake-datasource vs local", }, { name: "existing different provision plugin", - config: &config.Config{DataSource: LocalPluginName, ProvisionPlugin: "fake-provision-plugin"}, - dsName: LocalPluginName, + config: &config.Config{Plugins: fixtures.Plugins("plugins1")}, + dsName: "fake-datasource", provisionName: SpireHelmProvisionPluginName, - wantErrMessage: "existing config file uses a different provision plugin: fake-provision-plugin vs spire-helm", + wantErrMessage: "existing config file uses a different provision plugin: fake-provision vs spire-helm", }, { name: "existing different plugin config", - config: &config.Config{DataSource: "local", ProvisionPlugin: SpireHelmProvisionPluginName, PluginConfig: fakePluginConfig(t)}, - dsName: LocalPluginName, + config: &config.Config{Plugins: GetDefaultPlugins(), PluginConfig: fakePluginConfig(t)}, + dsName: LocalDSPluginName, provisionName: SpireHelmProvisionPluginName, wantErrMessage: "existing config file has different plugin config:", }, @@ -194,16 +203,18 @@ func TestManager_Init_failure(t *testing.T) { m := NewManager(configLoader) - _, err = m.Init(tt.dsName, tt.provisionName, tt.pluginConfig) + plugins := &pluginspb.Plugins{DataSource: &tt.dsName, Provision: &tt.provisionName} + _, err = m.Init(plugins, tt.pluginConfig) require.Error(t, err) assert.ErrorContains(t, err, tt.wantErrMessage) config, err := configLoader.Read() assert.Nil(t, err) - assert.Equal(t, config.DataSource, tt.config.DataSource, "config should not be updated") + assert.Equal(t, config.Plugins.GetDataSource(), tt.config.Plugins.GetDataSource(), "config should not be updated") + assert.Equal(t, config.Plugins.GetProvision(), tt.config.Plugins.GetProvision(), "config should not be updated") assert.Nil(t, m.source, "cached data source should be nil") - assert.Nil(t, m.provision, "cached data source should be nil") + assert.Nil(t, m.provision, "cached provision plugin should be nil") assert.Empty(t, m.clients, "cached clients should be empty") }) } @@ -216,8 +227,8 @@ func TestManager_GetDataSource_success(t *testing.T) { want func(config.Loader) cofidectl_plugin.DataSource }{ { - name: "local", - config: config.Config{DataSource: LocalPluginName}, + name: "defaults", + config: config.Config{Plugins: GetDefaultPlugins()}, want: func(cl config.Loader) cofidectl_plugin.DataSource { lds, err := local.NewLocalDataSource(cl) assert.Nil(t, err) @@ -226,7 +237,7 @@ func TestManager_GetDataSource_success(t *testing.T) { }, { name: "gRPC", - config: config.Config{DataSource: "fake-plugin"}, + config: config.Config{Plugins: fixtures.Plugins("plugins1")}, want: func(cl config.Loader) cofidectl_plugin.DataSource { fcds := newFakeGrpcDataSource(t, cl) return fcds @@ -240,9 +251,9 @@ func TestManager_GetDataSource_success(t *testing.T) { m := NewManager(configLoader) - // Mock out the Connect plugin loader function. + // Mock out the gRPC plugin loader function. var client *go_plugin.Client - if tt.config.DataSource != LocalPluginName { + if tt.config.Plugins.GetDataSource() != LocalDSPluginName { client = &go_plugin.Client{} m.loadGrpcPlugin = func(logger hclog.Logger, _ string) (*go_plugin.Client, cofidectl_plugin.DataSource, provision.Provision, error) { ds := newFakeGrpcDataSource(t, configLoader) @@ -255,7 +266,7 @@ func TestManager_GetDataSource_success(t *testing.T) { want := tt.want(configLoader) assert.Equal(t, want, got) - assert.Same(t, client, m.clients[tt.config.DataSource]) + assert.Same(t, client, m.clients[tt.config.Plugins.GetDataSource()]) got2, err := m.GetDataSource() require.Nil(t, err) @@ -272,13 +283,13 @@ func TestManager_GetDataSource_failure(t *testing.T) { }{ { name: "empty", - config: config.Config{DataSource: ""}, + config: config.Config{Plugins: &pluginspb.Plugins{DataSource: fixtures.StringPtr("")}}, wantErr: "plugin name cannot be empty", }, { - name: "connect plugin load failure", - config: config.Config{DataSource: "fake-plugin"}, - wantErr: "failed to create connect plugin", + name: "plugin load failure", + config: config.Config{Plugins: fixtures.Plugins("plugins1")}, + wantErr: "failed to create plugin", }, } for _, tt := range tests { @@ -287,9 +298,9 @@ func TestManager_GetDataSource_failure(t *testing.T) { require.Nil(t, err) m := NewManager(configLoader) - // Mock out the Connect plugin loader function, and inject a load failure. + // Mock out the gRPC plugin loader function, and inject a load failure. m.loadGrpcPlugin = func(logger hclog.Logger, _ string) (*go_plugin.Client, cofidectl_plugin.DataSource, provision.Provision, error) { - return nil, nil, nil, errors.New("failed to create connect plugin") + return nil, nil, nil, errors.New("failed to create plugin") } _, err = m.GetDataSource() @@ -308,12 +319,12 @@ func TestManager_Shutdown(t *testing.T) { want func(config.Loader) cofidectl_plugin.DataSource }{ { - name: "local", - config: config.Config{DataSource: LocalPluginName}, + name: "defaults", + config: config.Config{Plugins: GetDefaultPlugins()}, }, { name: "gRPC", - config: config.Config{DataSource: "fake-plugin"}, + config: config.Config{Plugins: fixtures.Plugins("plugins1")}, }, } for _, tt := range tests { @@ -322,7 +333,7 @@ func TestManager_Shutdown(t *testing.T) { require.Nil(t, err) m := NewManager(configLoader) - // Mock out the Connect plugin loader function. + // Mock out the gRPC plugin loader function. client := &go_plugin.Client{} m.loadGrpcPlugin = func(logger hclog.Logger, _ string) (*go_plugin.Client, cofidectl_plugin.DataSource, provision.Provision, error) { ds := newFakeGrpcDataSource(t, configLoader) @@ -351,13 +362,13 @@ func TestManager_GetPluginConfig(t *testing.T) { }{ { name: "success", - config: &config.Config{PluginConfig: fakePluginConfig(t)}, + config: &config.Config{Plugins: GetDefaultPlugins(), PluginConfig: fakePluginConfig(t)}, pluginName: "local", want: fakeLocalPluginConfig(t), }, { name: "non-existent plugin", - config: &config.Config{PluginConfig: fakePluginConfig(t)}, + config: &config.Config{Plugins: GetDefaultPlugins(), PluginConfig: fakePluginConfig(t)}, pluginName: "non-existent-plugin", want: fakeLocalPluginConfig(t), wantErr: true, @@ -365,7 +376,7 @@ func TestManager_GetPluginConfig(t *testing.T) { }, { name: "no plugin config", - config: &config.Config{}, + config: &config.Config{Plugins: GetDefaultPlugins()}, pluginName: "non-existent-plugin", want: fakeLocalPluginConfig(t), wantErr: true, @@ -406,13 +417,13 @@ func TestManager_SetPluginConfig(t *testing.T) { }{ { name: "success", - config: &config.Config{}, + config: &config.Config{Plugins: GetDefaultPlugins()}, pluginName: "local", pluginConfig: fakeLocalPluginConfig(t), }, { name: "overwrite", - config: &config.Config{PluginConfig: fakePluginConfig(t)}, + config: &config.Config{Plugins: GetDefaultPlugins(), PluginConfig: fakePluginConfig(t)}, pluginName: "local", pluginConfig: fakeLocalPluginConfig(t), }, diff --git a/pkg/plugin/provision/spirehelm/spirehelm_test.go b/pkg/plugin/provision/spirehelm/spirehelm_test.go index 139204a..9ed134f 100644 --- a/pkg/plugin/provision/spirehelm/spirehelm_test.go +++ b/pkg/plugin/provision/spirehelm/spirehelm_test.go @@ -146,5 +146,6 @@ func defaultConfig() *config.Config { fixtures.AttestationPolicy("ap1"), fixtures.AttestationPolicy("ap2"), }, + Plugins: fixtures.Plugins("plugins1"), } } diff --git a/pkg/provider/helm/values_test.go b/pkg/provider/helm/values_test.go index 28a2afb..142317d 100644 --- a/pkg/provider/helm/values_test.go +++ b/pkg/provider/helm/values_test.go @@ -1289,5 +1289,6 @@ func defaultConfig() *config.Config { fixtures.AttestationPolicy("ap1"), fixtures.AttestationPolicy("ap2"), }, + Plugins: fixtures.Plugins("plugins1"), } } From d647e30ea3be266c70ea6e5baaea51774181db5d Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Wed, 11 Dec 2024 16:36:52 +0000 Subject: [PATCH 05/48] Rework gRPC plugin loading --- cmd/cofidectl/cmd/apbinding/apbinding.go | 6 +- .../attestationpolicy/attestationpolicy.go | 4 +- cmd/cofidectl/cmd/down.go | 4 +- cmd/cofidectl/cmd/federation/federation.go | 6 +- cmd/cofidectl/cmd/init.go | 3 +- cmd/cofidectl/cmd/trustzone/helm/helm.go | 4 +- cmd/cofidectl/cmd/trustzone/trustzone.go | 8 +- cmd/cofidectl/cmd/up.go | 4 +- cmd/cofidectl/cmd/workload/workload.go | 4 +- pkg/plugin/datasource/interface.go | 4 +- pkg/plugin/grpc.go | 4 +- pkg/plugin/local/local.go | 3 +- pkg/plugin/local/local_test.go | 3 +- pkg/plugin/manager/manager.go | 238 +++++++++++------- pkg/plugin/manager/manager_test.go | 205 ++++++++++----- pkg/plugin/plugin.go | 4 +- pkg/plugin/provision/interface.go | 4 +- pkg/plugin/provision/spirehelm/spirehelm.go | 5 +- pkg/plugin/validator/validator.go | 12 + 19 files changed, 334 insertions(+), 191 deletions(-) create mode 100644 pkg/plugin/validator/validator.go diff --git a/cmd/cofidectl/cmd/apbinding/apbinding.go b/cmd/cofidectl/cmd/apbinding/apbinding.go index 647fa20..e8eaca8 100644 --- a/cmd/cofidectl/cmd/apbinding/apbinding.go +++ b/cmd/cofidectl/cmd/apbinding/apbinding.go @@ -64,7 +64,7 @@ func (c *APBindingCommand) GetListCommand() *cobra.Command { Long: apBindingListCmdDesc, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - ds, err := c.cmdCtx.PluginManager.GetDataSource() + ds, err := c.cmdCtx.PluginManager.GetDataSource(cmd.Context()) if err != nil { return err } @@ -149,7 +149,7 @@ func (c *APBindingCommand) GetAddCommand() *cobra.Command { Long: apBindingAddCmdDesc, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - ds, err := c.cmdCtx.PluginManager.GetDataSource() + ds, err := c.cmdCtx.PluginManager.GetDataSource(cmd.Context()) if err != nil { return err } @@ -191,7 +191,7 @@ func (c *APBindingCommand) GetDelCommand() *cobra.Command { Long: apBindingDelCmdDesc, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - ds, err := c.cmdCtx.PluginManager.GetDataSource() + ds, err := c.cmdCtx.PluginManager.GetDataSource(cmd.Context()) if err != nil { return err } diff --git a/cmd/cofidectl/cmd/attestationpolicy/attestationpolicy.go b/cmd/cofidectl/cmd/attestationpolicy/attestationpolicy.go index dcf6c79..e0cef96 100644 --- a/cmd/cofidectl/cmd/attestationpolicy/attestationpolicy.go +++ b/cmd/cofidectl/cmd/attestationpolicy/attestationpolicy.go @@ -53,7 +53,7 @@ func (c *AttestationPolicyCommand) GetListCommand() *cobra.Command { Long: attestationPolicyListCmdDesc, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - ds, err := c.cmdCtx.PluginManager.GetDataSource() + ds, err := c.cmdCtx.PluginManager.GetDataSource(cmd.Context()) if err != nil { return err } @@ -162,7 +162,7 @@ func (c *AttestationPolicyCommand) GetAddK8sCommand() *cobra.Command { Long: attestationPolicyAddK8sCmdDesc, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - ds, err := c.cmdCtx.PluginManager.GetDataSource() + ds, err := c.cmdCtx.PluginManager.GetDataSource(cmd.Context()) if err != nil { return err } diff --git a/cmd/cofidectl/cmd/down.go b/cmd/cofidectl/cmd/down.go index 093a3ef..b4f3552 100644 --- a/cmd/cofidectl/cmd/down.go +++ b/cmd/cofidectl/cmd/down.go @@ -29,12 +29,12 @@ func (d *DownCommand) DownCmd() *cobra.Command { Long: downCmdDesc, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - ds, err := d.cmdCtx.PluginManager.GetDataSource() + ds, err := d.cmdCtx.PluginManager.GetDataSource(cmd.Context()) if err != nil { return err } - provision, err := d.cmdCtx.PluginManager.GetProvision() + provision, err := d.cmdCtx.PluginManager.GetProvision(cmd.Context()) if err != nil { return err } diff --git a/cmd/cofidectl/cmd/federation/federation.go b/cmd/cofidectl/cmd/federation/federation.go index f5d7b12..5848c36 100644 --- a/cmd/cofidectl/cmd/federation/federation.go +++ b/cmd/cofidectl/cmd/federation/federation.go @@ -11,9 +11,9 @@ import ( trust_zone_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/trust_zone/v1alpha1" cmdcontext "github.com/cofide/cofidectl/pkg/cmd/context" - "github.com/cofide/cofidectl/pkg/spire" kubeutil "github.com/cofide/cofidectl/pkg/kube" "github.com/cofide/cofidectl/pkg/provider/helm" + "github.com/cofide/cofidectl/pkg/spire" "github.com/olekukonko/tablewriter" "github.com/spf13/cobra" ) @@ -57,7 +57,7 @@ func (c *FederationCommand) GetListCommand() *cobra.Command { Long: federationListCmdDesc, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - ds, err := c.cmdCtx.PluginManager.GetDataSource() + ds, err := c.cmdCtx.PluginManager.GetDataSource(cmd.Context()) if err != nil { return err } @@ -181,7 +181,7 @@ func (c *FederationCommand) GetAddCommand() *cobra.Command { Long: federationAddCmdDesc, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - ds, err := c.cmdCtx.PluginManager.GetDataSource() + ds, err := c.cmdCtx.PluginManager.GetDataSource(cmd.Context()) if err != nil { return err } diff --git a/cmd/cofidectl/cmd/init.go b/cmd/cofidectl/cmd/init.go index cb1185e..a68a65c 100644 --- a/cmd/cofidectl/cmd/init.go +++ b/cmd/cofidectl/cmd/init.go @@ -54,8 +54,7 @@ func (i *InitCommand) GetRootCommand() *cobra.Command { os.Exit(1) } - _, err := i.cmdCtx.PluginManager.Init(plugins, nil) - return err + return i.cmdCtx.PluginManager.Init(cmd.Context(), plugins, nil) }, } diff --git a/cmd/cofidectl/cmd/trustzone/helm/helm.go b/cmd/cofidectl/cmd/trustzone/helm/helm.go index f90be25..f9f1162 100644 --- a/cmd/cofidectl/cmd/trustzone/helm/helm.go +++ b/cmd/cofidectl/cmd/trustzone/helm/helm.go @@ -63,7 +63,7 @@ func (c *HelmCommand) GetOverrideCommand() *cobra.Command { Long: helmOverrideCmdDesc, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - ds, err := c.cmdCtx.PluginManager.GetDataSource() + ds, err := c.cmdCtx.PluginManager.GetDataSource(cmd.Context()) if err != nil { return err } @@ -139,7 +139,7 @@ func (c *HelmCommand) GetValuesCommand() *cobra.Command { Long: helmValuesCmdDesc, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - ds, err := c.cmdCtx.PluginManager.GetDataSource() + ds, err := c.cmdCtx.PluginManager.GetDataSource(cmd.Context()) if err != nil { return err } diff --git a/cmd/cofidectl/cmd/trustzone/trustzone.go b/cmd/cofidectl/cmd/trustzone/trustzone.go index 3b89c9e..31c372b 100644 --- a/cmd/cofidectl/cmd/trustzone/trustzone.go +++ b/cmd/cofidectl/cmd/trustzone/trustzone.go @@ -17,10 +17,10 @@ import ( 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/pkg/spire" kubeutil "github.com/cofide/cofidectl/pkg/kube" cofidectl_plugin "github.com/cofide/cofidectl/pkg/plugin" helmprovider "github.com/cofide/cofidectl/pkg/provider/helm" + "github.com/cofide/cofidectl/pkg/spire" "github.com/olekukonko/tablewriter" "github.com/spf13/cobra" "github.com/spiffe/go-spiffe/v2/spiffeid" @@ -71,7 +71,7 @@ func (c *TrustZoneCommand) GetListCommand() *cobra.Command { Long: trustZoneListCmdDesc, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - ds, err := c.cmdCtx.PluginManager.GetDataSource() + ds, err := c.cmdCtx.PluginManager.GetDataSource(cmd.Context()) if err != nil { return err } @@ -130,7 +130,7 @@ func (c *TrustZoneCommand) GetAddCommand() *cobra.Command { return nil }, RunE: func(cmd *cobra.Command, args []string) error { - ds, err := c.cmdCtx.PluginManager.GetDataSource() + ds, err := c.cmdCtx.PluginManager.GetDataSource(cmd.Context()) if err != nil { return err } @@ -186,7 +186,7 @@ func (c *TrustZoneCommand) GetStatusCommand() *cobra.Command { Long: trustZoneStatusCmdDesc, Args: cobra.ExactArgs(1), RunE: func(cmd *cobra.Command, args []string) error { - ds, err := c.cmdCtx.PluginManager.GetDataSource() + ds, err := c.cmdCtx.PluginManager.GetDataSource(cmd.Context()) if err != nil { return err } diff --git a/cmd/cofidectl/cmd/up.go b/cmd/cofidectl/cmd/up.go index b39928c..71678d0 100644 --- a/cmd/cofidectl/cmd/up.go +++ b/cmd/cofidectl/cmd/up.go @@ -30,12 +30,12 @@ func (u *UpCommand) UpCmd() *cobra.Command { Long: upCmdDesc, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - ds, err := u.cmdCtx.PluginManager.GetDataSource() + ds, err := u.cmdCtx.PluginManager.GetDataSource(cmd.Context()) if err != nil { return err } - provision, err := u.cmdCtx.PluginManager.GetProvision() + provision, err := u.cmdCtx.PluginManager.GetProvision(cmd.Context()) if err != nil { return err } diff --git a/cmd/cofidectl/cmd/workload/workload.go b/cmd/cofidectl/cmd/workload/workload.go index b024f3c..400594d 100644 --- a/cmd/cofidectl/cmd/workload/workload.go +++ b/cmd/cofidectl/cmd/workload/workload.go @@ -63,7 +63,7 @@ func (w *WorkloadCommand) GetListCommand() *cobra.Command { Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { var err error - ds, err := w.cmdCtx.PluginManager.GetDataSource() + ds, err := w.cmdCtx.PluginManager.GetDataSource(cmd.Context()) if err != nil { return err } @@ -162,7 +162,7 @@ func (w *WorkloadCommand) GetDiscoverCommand() *cobra.Command { Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { var err error - ds, err := w.cmdCtx.PluginManager.GetDataSource() + ds, err := w.cmdCtx.PluginManager.GetDataSource(cmd.Context()) if err != nil { return err } diff --git a/pkg/plugin/datasource/interface.go b/pkg/plugin/datasource/interface.go index 352f4a2..9f92848 100644 --- a/pkg/plugin/datasource/interface.go +++ b/pkg/plugin/datasource/interface.go @@ -8,11 +8,13 @@ import ( attestation_policy_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/attestation_policy/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" ) // DataSource is the interface data source plugins have to implement. type DataSource interface { - Validate() error + validator.Validator + GetTrustZone(string) (*trust_zone_proto.TrustZone, error) ListTrustZones() ([]*trust_zone_proto.TrustZone, error) AddTrustZone(*trust_zone_proto.TrustZone) (*trust_zone_proto.TrustZone, error) diff --git a/pkg/plugin/grpc.go b/pkg/plugin/grpc.go index 0826018..1b46091 100644 --- a/pkg/plugin/grpc.go +++ b/pkg/plugin/grpc.go @@ -27,8 +27,8 @@ func NewDataSourcePluginClientGRPC(ctx context.Context, client cofidectl_proto.D return &DataSourcePluginClientGRPC{ctx: ctx, client: client} } -func (c *DataSourcePluginClientGRPC) Validate() error { - _, err := c.client.Validate(c.ctx, &cofidectl_proto.ValidateRequest{}) +func (c *DataSourcePluginClientGRPC) Validate(ctx context.Context) error { + _, err := c.client.Validate(ctx, &cofidectl_proto.ValidateRequest{}) return err } diff --git a/pkg/plugin/local/local.go b/pkg/plugin/local/local.go index 6d443a9..5ca8334 100644 --- a/pkg/plugin/local/local.go +++ b/pkg/plugin/local/local.go @@ -4,6 +4,7 @@ package local import ( + "context" "fmt" "slices" @@ -36,7 +37,7 @@ func NewLocalDataSource(loader config.Loader) (*LocalDataSource, error) { return lds, nil } -func (lds *LocalDataSource) Validate() error { +func (lds *LocalDataSource) Validate(_ context.Context) error { return nil } diff --git a/pkg/plugin/local/local_test.go b/pkg/plugin/local/local_test.go index d8848b8..ac00eed 100644 --- a/pkg/plugin/local/local_test.go +++ b/pkg/plugin/local/local_test.go @@ -4,6 +4,7 @@ package local import ( + "context" "slices" "testing" @@ -79,7 +80,7 @@ func TestNewLocalDataSource(t *testing.T) { func TestLocalDataSource_Validate(t *testing.T) { lds, _ := buildLocalDataSource(t, config.NewConfig()) - err := lds.Validate() + err := lds.Validate(context.Background()) require.Nil(t, err) } diff --git a/pkg/plugin/manager/manager.go b/pkg/plugin/manager/manager.go index 0d50e63..3f4dd82 100644 --- a/pkg/plugin/manager/manager.go +++ b/pkg/plugin/manager/manager.go @@ -15,9 +15,11 @@ import ( "github.com/cofide/cofidectl/internal/pkg/config" "github.com/cofide/cofidectl/internal/pkg/proto" cofidectl_plugin "github.com/cofide/cofidectl/pkg/plugin" + "github.com/cofide/cofidectl/pkg/plugin/datasource" "github.com/cofide/cofidectl/pkg/plugin/local" "github.com/cofide/cofidectl/pkg/plugin/provision" "github.com/cofide/cofidectl/pkg/plugin/provision/spirehelm" + "github.com/cofide/cofidectl/pkg/plugin/validator" "google.golang.org/protobuf/types/known/structpb" hclog "github.com/hashicorp/go-hclog" @@ -31,23 +33,38 @@ const ( // PluginManager provides an interface for loading and managing `DataSource` plugins based on configuration. type PluginManager struct { - configLoader config.Loader - loadGrpcPlugin func(hclog.Logger, string) (*go_plugin.Client, cofidectl_plugin.DataSource, provision.Provision, error) - source cofidectl_plugin.DataSource - provision provision.Provision - clients map[string]*go_plugin.Client + configLoader config.Loader + grpcPluginLoader grpcPluginLoader + source datasource.DataSource + provision provision.Provision + clients map[string]*go_plugin.Client +} + +// grpcPluginLoader is a function that loads a gRPC plugin. The function should load a single +// plugin that implements all cofidectl plugins with the specified name in pluginCfg. +// All cofidectl plugins in the returned grpcPlugin object should be validated using the +// Validate RPC before returing. +// It is primarily used for mocking in unit tests. +type grpcPluginLoader func(ctx context.Context, logger hclog.Logger, pluginName string, pluginCfg *pluginspb.Plugins) (*grpcPlugin, error) + +// grpcPlugin is used when loading loading gRPC plugins. It collects the client and any cofidectl +// plugins that the gRPC plugin implements. +type grpcPlugin struct { + client *go_plugin.Client + source datasource.DataSource + provision provision.Provision } func NewManager(configLoader config.Loader) *PluginManager { return &PluginManager{ - configLoader: configLoader, - loadGrpcPlugin: loadGrpcPlugin, - clients: map[string]*go_plugin.Client{}, + configLoader: configLoader, + grpcPluginLoader: loadGrpcPlugin, + clients: map[string]*go_plugin.Client{}, } } // Init initialises the configuration for the specified plugins. -func (pm *PluginManager) Init(plugins *pluginspb.Plugins, pluginConfig map[string]*structpb.Struct) (cofidectl_plugin.DataSource, error) { +func (pm *PluginManager) Init(ctx context.Context, plugins *pluginspb.Plugins, pluginConfig map[string]*structpb.Struct) error { if plugins == nil { plugins = GetDefaultPlugins() } @@ -56,47 +73,48 @@ func (pm *PluginManager) Init(plugins *pluginspb.Plugins, pluginConfig map[strin // Check that existing plugin config matches. cfg, err := pm.configLoader.Read() if err != nil { - return nil, err + return err } ds := plugins.GetDataSource() - provision := plugins.GetProvision() if ds != cfg.Plugins.GetDataSource() { - return nil, fmt.Errorf("existing config file uses a different data source plugin: %s vs %s", cfg.Plugins.GetDataSource(), ds) + return fmt.Errorf("existing config file uses a different data source plugin: %s vs %s", cfg.Plugins.GetDataSource(), ds) } + provision := plugins.GetProvision() if cfg.Plugins.GetProvision() != provision { - return nil, fmt.Errorf("existing config file uses a different provision plugin: %s vs %s", cfg.Plugins.GetProvision(), provision) + return fmt.Errorf("existing config file uses a different provision plugin: %s vs %s", cfg.Plugins.GetProvision(), provision) } if !maps.EqualFunc(cfg.PluginConfig, pluginConfig, proto.StructsEqual) { - return nil, fmt.Errorf("existing config file has different plugin config:\n%v\nvs\n\n%v", cfg.PluginConfig, pluginConfig) + return fmt.Errorf("existing config file has different plugin config:\n%v\nvs\n\n%v", cfg.PluginConfig, pluginConfig) } fmt.Println("the config file already exists") } else { cfg := config.NewConfig() plugins, err := proto.ClonePlugins(plugins) if err != nil { - return nil, err + return err } cfg.Plugins = plugins if pluginConfig != nil { cfg.PluginConfig = pluginConfig } if err := pm.configLoader.Write(cfg); err != nil { - return nil, err + return err } } - return pm.loadDataSource() + return nil } -func (pm *PluginManager) GetDataSource() (cofidectl_plugin.DataSource, error) { +// GetDataSource returns the data source plugin, loading it if necessary. +func (pm *PluginManager) GetDataSource(ctx context.Context) (cofidectl_plugin.DataSource, error) { if pm.source != nil { return pm.source, nil } - - return pm.loadDataSource() + return pm.loadDataSource(ctx) } -func (pm *PluginManager) loadDataSource() (cofidectl_plugin.DataSource, error) { +// loadDataSource loads the data source plugin, which may be an in-process or gRPC plugin. +func (pm *PluginManager) loadDataSource(ctx context.Context) (cofidectl_plugin.DataSource, error) { if pm.source != nil { return nil, errors.New("data source has already been loaded") } @@ -111,46 +129,35 @@ func (pm *PluginManager) loadDataSource() (cofidectl_plugin.DataSource, error) { return nil, errors.New("plugin name cannot be empty") } - var ds cofidectl_plugin.DataSource - var client *go_plugin.Client - switch dsName { - case LocalDSPluginName: - ds, err = local.NewLocalDataSource(pm.configLoader) + // Check if an in-process data source implementation has been requested. + if dsName == LocalDSPluginName { + ds, err := local.NewLocalDataSource(pm.configLoader) if err != nil { return nil, err } - default: - logger := hclog.New(&hclog.LoggerOptions{ - Name: "plugin", - Output: os.Stdout, - Level: hclog.Error, - }) - - client, ds, _, err = pm.loadGrpcPlugin(logger, dsName) - if err != nil { + if err := ds.Validate(ctx); err != nil { return nil, err } + pm.source = ds + return pm.source, nil } - if err := ds.Validate(); err != nil { - if client != nil { - client.Kill() - } + if err := pm.loadGrpcPlugin(ctx, dsName, cfg.Plugins); err != nil { return nil, err } - pm.source = ds - pm.clients[dsName] = client - return ds, nil + return pm.source, nil } -func (pm *PluginManager) GetProvision() (provision.Provision, error) { +// GetProvision returns the provision plugin, loading it if necessary. +func (pm *PluginManager) GetProvision(ctx context.Context) (provision.Provision, error) { if pm.provision != nil { return pm.provision, nil } - return pm.loadProvision() + return pm.loadProvision(ctx) } -func (pm *PluginManager) loadProvision() (provision.Provision, error) { +// loadProvision loads the provision plugin, which may be an in-process or gRPC plugin. +func (pm *PluginManager) loadProvision(ctx context.Context) (provision.Provision, error) { if pm.provision != nil { return nil, errors.New("provision plugin has already been loaded") } @@ -165,104 +172,139 @@ func (pm *PluginManager) loadProvision() (provision.Provision, error) { return nil, errors.New("provision plugin name cannot be empty") } - var provision provision.Provision - var client *go_plugin.Client - switch provisionName { - case SpireHelmProvisionPluginName: - return spirehelm.NewSpireHelm(nil), nil - default: - logger := hclog.New(&hclog.LoggerOptions{ - Name: "plugin", - Output: os.Stdout, - Level: hclog.Error, - }) - - client, _, provision, err = pm.loadGrpcPlugin(logger, provisionName) - if err != nil { + // Check if an in-process provision implementation has been requested. + if provisionName == SpireHelmProvisionPluginName { + spireHelm := spirehelm.NewSpireHelm(nil) + if err := spireHelm.Validate(ctx); err != nil { return nil, err } + pm.provision = spireHelm + return pm.provision, nil } - if err := provision.Validate(context.TODO()); err != nil { - if client != nil { - client.Kill() - } + if err := pm.loadGrpcPlugin(ctx, provisionName, cfg.Plugins); err != nil { return nil, err } - pm.provision = provision - pm.clients[provisionName] = client - return provision, nil + return pm.provision, nil } -func (pm *PluginManager) readConfig() (*config.Config, error) { - exists, err := pm.configLoader.Exists() +// loadGrpcPlugin loads a gRPC plugin. +// The gRPC plugin may provide one or more cofidectl plugins (e.g. data source, provision), and +// all cofidectl plugins configured to use this gRPC plugin will be loaded in a single plugin +// client and server process. +func (pm *PluginManager) loadGrpcPlugin(ctx context.Context, pluginName string, pluginCfg *pluginspb.Plugins) error { + logger := hclog.New(&hclog.LoggerOptions{ + Name: "plugin", + Output: os.Stdout, + Level: hclog.Error, + }) + + grpcPlugin, err := pm.grpcPluginLoader(ctx, logger, pluginName, pluginCfg) if err != nil { - return nil, err + return err } - if !exists { - return nil, fmt.Errorf("the config file doesn't exist. Please run cofidectl init") + if grpcPlugin.source != nil { + pm.source = grpcPlugin.source } - - return pm.configLoader.Read() + if grpcPlugin.provision != nil { + pm.provision = grpcPlugin.provision + } + pm.clients[pluginName] = grpcPlugin.client + return nil } -func loadGrpcPlugin(logger hclog.Logger, pluginName string) (*go_plugin.Client, cofidectl_plugin.DataSource, provision.Provision, error) { +// loadGrpcPlugin is the default grpcPluginLoader. +func loadGrpcPlugin(ctx context.Context, logger hclog.Logger, pluginName string, plugins *pluginspb.Plugins) (*grpcPlugin, error) { pluginPath, err := cofidectl_plugin.GetPluginPath(pluginName) if err != nil { - return nil, nil, nil, err + return nil, err + } + + pluginSet := map[string]go_plugin.Plugin{} + if plugins.GetDataSource() == pluginName { + pluginSet[cofidectl_plugin.DataSourcePluginName] = &cofidectl_plugin.DataSourcePlugin{} + } + if plugins.GetProvision() == pluginName { + pluginSet[provision.ProvisionPluginName] = &provision.ProvisionPlugin{} } cmd := exec.Command(pluginPath, cofidectl_plugin.DataSourcePluginArgs...) client := go_plugin.NewClient(&go_plugin.ClientConfig{ - Cmd: cmd, - HandshakeConfig: cofidectl_plugin.HandshakeConfig, - Plugins: map[string]go_plugin.Plugin{ - cofidectl_plugin.DataSourcePluginName: &cofidectl_plugin.DataSourcePlugin{}, - provision.ProvisionPluginName: &provision.ProvisionPlugin{}, - }, + Cmd: cmd, + HandshakeConfig: cofidectl_plugin.HandshakeConfig, + Plugins: pluginSet, AllowedProtocols: []go_plugin.Protocol{go_plugin.ProtocolGRPC}, Logger: logger, }) - source, provision, err := startGrpcPlugin(client, pluginName) + grpcPlugin, err := startGrpcPlugin(ctx, client, pluginName, plugins) if err != nil { client.Kill() - return nil, nil, nil, err + return nil, err } - return client, source, provision, nil + return grpcPlugin, nil } -func startGrpcPlugin(client *go_plugin.Client, pluginName string) (cofidectl_plugin.DataSource, provision.Provision, error) { +func startGrpcPlugin(ctx context.Context, client *go_plugin.Client, pluginName string, plugins *pluginspb.Plugins) (*grpcPlugin, error) { grpcClient, err := client.Client() if err != nil { - return nil, nil, fmt.Errorf("cannot create interface to plugin: %w", err) + return nil, fmt.Errorf("cannot create interface to plugin: %w", err) } if err = grpcClient.Ping(); err != nil { - return nil, nil, fmt.Errorf("failed to ping the gRPC client: %w", err) + return nil, fmt.Errorf("failed to ping the gRPC client: %w", err) + } + + grpcPlugin := &grpcPlugin{client: client} + if plugins.GetDataSource() == pluginName { + source, err := dispensePlugin[datasource.DataSource](ctx, grpcClient, cofidectl_plugin.DataSourcePluginName) + if err != nil { + return nil, err + } + grpcPlugin.source = source + } + + if plugins.GetProvision() == pluginName { + provision, err := dispensePlugin[provision.Provision](ctx, grpcClient, provision.ProvisionPluginName) + if err != nil { + return nil, err + } + grpcPlugin.provision = provision } + return grpcPlugin, nil +} - raw, err := grpcClient.Dispense(cofidectl_plugin.DataSourcePluginName) +// dispensePlugin dispenses a gRPC plugin from a client, ensuring that it implements the specified interface T. +func dispensePlugin[T validator.Validator](ctx context.Context, grpcClient go_plugin.ClientProtocol, name string) (T, error) { + var zero T + raw, err := grpcClient.Dispense(name) if err != nil { - return nil, nil, fmt.Errorf("failed to dispense an instance of the plugin: %w", err) + return zero, fmt.Errorf("failed to dispense an instance of the gRPC %s plugin: %w", name, err) } - source, ok := raw.(cofidectl_plugin.DataSource) + plugin, ok := raw.(T) if !ok { - return nil, nil, fmt.Errorf("gRPC data source plugin %s does not implement plugin interface", pluginName) + return zero, fmt.Errorf("gRPC %s plugin (%T) does not implement plugin interface ", name, plugin) } - raw, err = grpcClient.Dispense(provision.ProvisionPluginName) + if err := plugin.Validate(ctx); err != nil { + return zero, err + } + return plugin, nil +} + +func (pm *PluginManager) readConfig() (*config.Config, error) { + exists, err := pm.configLoader.Exists() if err != nil { - return nil, nil, fmt.Errorf("failed to dispense an instance of the plugin: %w", err) + return nil, err } - provision, ok := raw.(provision.Provision) - if !ok { - return nil, nil, fmt.Errorf("gRPC data source plugin %s does not implement plugin interface", pluginName) + if !exists { + return nil, fmt.Errorf("the config file doesn't exist. Please run cofidectl init") } - return source, provision, nil + + return pm.configLoader.Read() } func (pm *PluginManager) Shutdown() { @@ -273,6 +315,7 @@ func (pm *PluginManager) Shutdown() { delete(pm.clients, name) } pm.source = nil + pm.provision = nil } // GetPluginConfig returns a `Struct` message containing per-plugin configuration from the config file. @@ -306,6 +349,7 @@ func (pm *PluginManager) SetPluginConfig(pluginName string, pluginConfig *struct return pm.configLoader.Write(cfg) } +// GetDefaultPlugins returns a `Plugins` message containing the default plugins. func GetDefaultPlugins() *pluginspb.Plugins { ds := LocalDSPluginName provision := SpireHelmProvisionPluginName diff --git a/pkg/plugin/manager/manager_test.go b/pkg/plugin/manager/manager_test.go index 855d146..f9718f0 100644 --- a/pkg/plugin/manager/manager_test.go +++ b/pkg/plugin/manager/manager_test.go @@ -4,6 +4,7 @@ package manager import ( + "context" "errors" "testing" @@ -11,6 +12,7 @@ import ( "github.com/cofide/cofidectl/internal/pkg/config" "github.com/cofide/cofidectl/internal/pkg/test/fixtures" cofidectl_plugin "github.com/cofide/cofidectl/pkg/plugin" + "github.com/cofide/cofidectl/pkg/plugin/datasource" "github.com/cofide/cofidectl/pkg/plugin/local" "github.com/cofide/cofidectl/pkg/plugin/provision" "github.com/cofide/cofidectl/pkg/plugin/provision/spirehelm" @@ -45,77 +47,43 @@ func TestManager_Init_success(t *testing.T) { config *config.Config plugins *pluginspb.Plugins pluginConfig map[string]*structpb.Struct - want func(config.Loader) cofidectl_plugin.DataSource }{ { name: "defaults", config: nil, plugins: GetDefaultPlugins(), - want: func(cl config.Loader) cofidectl_plugin.DataSource { - lds, err := local.NewLocalDataSource(cl) - assert.Nil(t, err) - return lds - }, }, { name: "nil plugins", config: nil, plugins: nil, - want: func(cl config.Loader) cofidectl_plugin.DataSource { - lds, err := local.NewLocalDataSource(cl) - assert.Nil(t, err) - return lds - }, }, { name: "gRPC", config: nil, plugins: fixtures.Plugins("plugins1"), - want: func(cl config.Loader) cofidectl_plugin.DataSource { - fcds := newFakeGrpcDataSource(t, cl) - return fcds - }, }, { name: "defaults with config", config: nil, plugins: GetDefaultPlugins(), pluginConfig: fakePluginConfig(t), - want: func(cl config.Loader) cofidectl_plugin.DataSource { - lds, err := local.NewLocalDataSource(cl) - assert.Nil(t, err) - return lds - }, }, { name: "existing defaults", config: &config.Config{Plugins: GetDefaultPlugins()}, plugins: GetDefaultPlugins(), - want: func(cl config.Loader) cofidectl_plugin.DataSource { - lds, err := local.NewLocalDataSource(cl) - assert.Nil(t, err) - return lds - }, }, { name: "existing gRPC", config: &config.Config{Plugins: fixtures.Plugins("plugins1")}, plugins: fixtures.Plugins("plugins1"), - want: func(cl config.Loader) cofidectl_plugin.DataSource { - fcds := newFakeGrpcDataSource(t, cl) - return fcds - }, }, { name: "existing defaults with config", config: &config.Config{Plugins: GetDefaultPlugins(), PluginConfig: fakePluginConfig(t)}, plugins: GetDefaultPlugins(), pluginConfig: fakePluginConfig(t), - want: func(cl config.Loader) cofidectl_plugin.DataSource { - lds, err := local.NewLocalDataSource(cl) - assert.Nil(t, err) - return lds - }, }, } for _, tt := range tests { @@ -125,16 +93,13 @@ func TestManager_Init_success(t *testing.T) { m := NewManager(configLoader) // Mock out the gRPC plugin loader function. - m.loadGrpcPlugin = func(logger hclog.Logger, _ string) (*go_plugin.Client, cofidectl_plugin.DataSource, provision.Provision, error) { - return nil, newFakeGrpcDataSource(t, configLoader), newFakeGrpcProvision(), nil + m.grpcPluginLoader = func(_ context.Context, _ hclog.Logger, _ string, _ *pluginspb.Plugins) (*grpcPlugin, error) { + return &grpcPlugin{nil, newFakeGrpcDataSource(t, configLoader), newFakeGrpcProvision()}, nil } - got, err := m.Init(tt.plugins, tt.pluginConfig) + err = m.Init(context.Background(), tt.plugins, tt.pluginConfig) require.Nil(t, err) - want := tt.want(configLoader) - assert.Equal(t, want, got) - config, err := configLoader.Read() assert.Nil(t, err) wantPlugins := tt.plugins @@ -153,13 +118,9 @@ func TestManager_Init_success(t *testing.T) { assert.NotSame(t, value, config.PluginConfig[pluginName], "pointer to plugin config stored in config") } - got2, err := m.GetDataSource() - require.Nil(t, err) - assert.Same(t, got, got2, "GetDataSource() should return a cached copy") - - // provision, err := m.GetProvision() - // require.Nil(t, err) - // assert.Same(t, m.provision, provision, "GetProvision() should return a cached copy") + assert.Nil(t, m.source, "cached data source should be nil") + assert.Nil(t, m.provision, "cached provision plugin should be nil") + assert.Empty(t, m.clients, "cached clients should be empty") }) } } @@ -204,7 +165,7 @@ func TestManager_Init_failure(t *testing.T) { m := NewManager(configLoader) plugins := &pluginspb.Plugins{DataSource: &tt.dsName, Provision: &tt.provisionName} - _, err = m.Init(plugins, tt.pluginConfig) + err = m.Init(context.Background(), plugins, tt.pluginConfig) require.Error(t, err) assert.ErrorContains(t, err, tt.wantErrMessage) @@ -243,6 +204,14 @@ func TestManager_GetDataSource_success(t *testing.T) { return fcds }, }, + { + name: "gRPC with provision", + config: config.Config{Plugins: fixtures.Plugins("plugins2")}, + want: func(cl config.Loader) cofidectl_plugin.DataSource { + fcds := newFakeGrpcDataSource(t, cl) + return fcds + }, + }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { @@ -253,24 +222,36 @@ func TestManager_GetDataSource_success(t *testing.T) { // Mock out the gRPC plugin loader function. var client *go_plugin.Client + var provision provision.Provision if tt.config.Plugins.GetDataSource() != LocalDSPluginName { client = &go_plugin.Client{} - m.loadGrpcPlugin = func(logger hclog.Logger, _ string) (*go_plugin.Client, cofidectl_plugin.DataSource, provision.Provision, error) { + m.grpcPluginLoader = func(_ context.Context, _ hclog.Logger, _ string, _ *pluginspb.Plugins) (*grpcPlugin, error) { ds := newFakeGrpcDataSource(t, configLoader) - return client, ds, nil, nil + if tt.config.Plugins.GetProvision() == tt.config.Plugins.GetDataSource() { + provision = newFakeGrpcProvision() + } + return &grpcPlugin{client, ds, provision}, nil } } - got, err := m.GetDataSource() + got, err := m.GetDataSource(context.Background()) require.Nil(t, err) want := tt.want(configLoader) assert.Equal(t, want, got) + assert.Equal(t, want, m.source) assert.Same(t, client, m.clients[tt.config.Plugins.GetDataSource()]) - got2, err := m.GetDataSource() + got2, err := m.GetDataSource(context.Background()) require.Nil(t, err) assert.Same(t, got, got2, "second GetDataSource() should return a cached copy") + + if tt.config.Plugins.GetProvision() == tt.config.Plugins.GetDataSource() { + got, err := m.GetProvision(context.Background()) + require.Nil(t, err, err) + assert.Equal(t, provision, got) + assert.Equal(t, provision, m.provision) + } }) } } @@ -299,15 +280,118 @@ func TestManager_GetDataSource_failure(t *testing.T) { m := NewManager(configLoader) // Mock out the gRPC plugin loader function, and inject a load failure. - m.loadGrpcPlugin = func(logger hclog.Logger, _ string) (*go_plugin.Client, cofidectl_plugin.DataSource, provision.Provision, error) { - return nil, nil, nil, errors.New("failed to create plugin") + m.grpcPluginLoader = func(_ context.Context, _ hclog.Logger, _ string, _ *pluginspb.Plugins) (*grpcPlugin, error) { + return nil, errors.New("failed to create plugin") + } + + _, err = m.GetDataSource(context.Background()) + require.Error(t, err) + assert.ErrorContains(t, err, tt.wantErr) + assert.Nil(t, m.source, "failed GetDataSource should not cache data source") + assert.Nil(t, m.provision, "failed GetDataSource should not cache provision") + assert.Empty(t, m.clients, "failed GetDataSource should not cache clients") + }) + } +} + +func TestManager_GetProvision_success(t *testing.T) { + tests := []struct { + name string + config config.Config + want provision.Provision + }{ + { + name: "defaults", + config: config.Config{Plugins: GetDefaultPlugins()}, + want: spirehelm.NewSpireHelm(nil), + }, + { + name: "gRPC", + config: config.Config{Plugins: fixtures.Plugins("plugins1")}, + want: newFakeGrpcProvision(), + }, + { + name: "gRPC with provision", + config: config.Config{Plugins: fixtures.Plugins("plugins2")}, + want: newFakeGrpcProvision(), + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + configLoader, err := config.NewMemoryLoader(&tt.config) + require.Nil(t, err) + + m := NewManager(configLoader) + + // Mock out the gRPC plugin loader function. + var client *go_plugin.Client + var source datasource.DataSource + if tt.config.Plugins.GetDataSource() != LocalDSPluginName { + client = &go_plugin.Client{} + m.grpcPluginLoader = func(_ context.Context, _ hclog.Logger, _ string, _ *pluginspb.Plugins) (*grpcPlugin, error) { + provision := newFakeGrpcProvision() + if tt.config.Plugins.GetDataSource() == tt.config.Plugins.GetProvision() { + source = newFakeGrpcDataSource(t, configLoader) + } + return &grpcPlugin{client, source, provision}, nil + } + } + + got, err := m.GetProvision(context.Background()) + require.Nil(t, err) + + assert.Equal(t, tt.want, got) + assert.Equal(t, tt.want, m.provision) + assert.Same(t, client, m.clients[tt.config.Plugins.GetProvision()]) + + got2, err := m.GetProvision(context.Background()) + require.Nil(t, err) + assert.Same(t, got, got2, "second GetProvision() should return a cached copy") + + if tt.config.Plugins.GetDataSource() == tt.config.Plugins.GetProvision() { + got, err := m.GetDataSource(context.Background()) + require.Nil(t, err, err) + assert.Equal(t, source, got) + assert.Equal(t, source, m.source) + } + }) + } +} + +func TestManager_GetProvision_failure(t *testing.T) { + tests := []struct { + name string + config config.Config + wantErr string + }{ + { + name: "empty", + config: config.Config{Plugins: &pluginspb.Plugins{DataSource: fixtures.StringPtr("")}}, + wantErr: "plugin name cannot be empty", + }, + { + name: "plugin load failure", + config: config.Config{Plugins: fixtures.Plugins("plugins1")}, + wantErr: "failed to create plugin", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + configLoader, err := config.NewMemoryLoader(&tt.config) + require.Nil(t, err) + + m := NewManager(configLoader) + // Mock out the gRPC plugin loader function, and inject a load failure. + m.grpcPluginLoader = func(_ context.Context, _ hclog.Logger, _ string, _ *pluginspb.Plugins) (*grpcPlugin, error) { + return nil, errors.New("failed to create plugin") } - _, err = m.GetDataSource() + _, err = m.GetProvision(context.Background()) require.Error(t, err) assert.ErrorContains(t, err, tt.wantErr) - assert.Nil(t, m.source, "failed GetDataSource should not cache") - assert.Empty(t, m.clients, "failed GetDataSource should not cache") + assert.Nil(t, m.source, "failed GetProvision should not cache data source") + assert.Nil(t, m.provision, "failed GetProvision should not cache provision") + assert.Empty(t, m.clients, "failed GetProvision should not cache clients") }) } } @@ -335,12 +419,11 @@ func TestManager_Shutdown(t *testing.T) { m := NewManager(configLoader) // Mock out the gRPC plugin loader function. client := &go_plugin.Client{} - m.loadGrpcPlugin = func(logger hclog.Logger, _ string) (*go_plugin.Client, cofidectl_plugin.DataSource, provision.Provision, error) { - ds := newFakeGrpcDataSource(t, configLoader) - return client, ds, nil, nil + m.grpcPluginLoader = func(_ context.Context, _ hclog.Logger, _ string, _ *pluginspb.Plugins) (*grpcPlugin, error) { + return &grpcPlugin{client, newFakeGrpcDataSource(t, configLoader), nil}, nil } - _, err = m.GetDataSource() + _, err = m.GetDataSource(context.Background()) require.Nil(t, err) m.Shutdown() diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go index 99e4a5f..92f0456 100644 --- a/pkg/plugin/plugin.go +++ b/pkg/plugin/plugin.go @@ -48,8 +48,8 @@ type GRPCServer struct { Impl DataSource } -func (s *GRPCServer) Validate(_ context.Context, req *cofidectl_proto.ValidateRequest) (*cofidectl_proto.ValidateResponse, error) { - err := s.Impl.Validate() +func (s *GRPCServer) Validate(ctx context.Context, req *cofidectl_proto.ValidateRequest) (*cofidectl_proto.ValidateResponse, error) { + err := s.Impl.Validate(ctx) if err != nil { return nil, err } diff --git a/pkg/plugin/provision/interface.go b/pkg/plugin/provision/interface.go index e1362f8..139a38e 100644 --- a/pkg/plugin/provision/interface.go +++ b/pkg/plugin/provision/interface.go @@ -8,12 +8,12 @@ import ( provisionpb "github.com/cofide/cofide-api-sdk/gen/go/proto/provision_plugin/v1alpha1" "github.com/cofide/cofidectl/pkg/plugin/datasource" + "github.com/cofide/cofidectl/pkg/plugin/validator" ) // Provision is the interface that provision plugins have to implement. type Provision interface { - // Validate checks whether the plugin is configured correctly. - Validate(ctx context.Context) error + validator.Validator // Deploy deploys the workload identity configuration to the clusters in the system. // The method is asynchronous, returning a channel over which Status messages are sent diff --git a/pkg/plugin/provision/spirehelm/spirehelm.go b/pkg/plugin/provision/spirehelm/spirehelm.go index 0064996..8d8b697 100644 --- a/pkg/plugin/provision/spirehelm/spirehelm.go +++ b/pkg/plugin/provision/spirehelm/spirehelm.go @@ -12,6 +12,7 @@ import ( kubeutil "github.com/cofide/cofidectl/pkg/kube" "github.com/cofide/cofidectl/pkg/plugin" + "github.com/cofide/cofidectl/pkg/plugin/datasource" "github.com/cofide/cofidectl/pkg/plugin/provision" "github.com/cofide/cofidectl/pkg/spire" ) @@ -40,7 +41,7 @@ func (h *SpireHelm) Validate(_ context.Context) error { return nil } -func (h *SpireHelm) Deploy(ctx context.Context, ds plugin.DataSource, kubeCfgFile string) (<-chan *provisionpb.Status, error) { +func (h *SpireHelm) Deploy(ctx context.Context, ds datasource.DataSource, kubeCfgFile string) (<-chan *provisionpb.Status, error) { statusCh := make(chan *provisionpb.Status) go func() { @@ -52,7 +53,7 @@ func (h *SpireHelm) Deploy(ctx context.Context, ds plugin.DataSource, kubeCfgFil return statusCh, nil } -func (h *SpireHelm) TearDown(ctx context.Context, ds plugin.DataSource) (<-chan *provisionpb.Status, error) { +func (h *SpireHelm) TearDown(ctx context.Context, ds datasource.DataSource) (<-chan *provisionpb.Status, error) { statusCh := make(chan *provisionpb.Status) go func() { diff --git a/pkg/plugin/validator/validator.go b/pkg/plugin/validator/validator.go new file mode 100644 index 0000000..e576d50 --- /dev/null +++ b/pkg/plugin/validator/validator.go @@ -0,0 +1,12 @@ +// Copyright 2024 Cofide Limited. +// SPDX-License-Identifier: Apache-2.0 + +package validator + +import "context" + +// Validator is a common interface embedded into other plugin interfaces. +type Validator interface { + // Validate checks whether the plugin is configured correctly. + Validate(context.Context) error +} From 7851450748f7b82d614f817cb5e00aa49f9f6530 Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Wed, 11 Dec 2024 17:01:06 +0000 Subject: [PATCH 06/48] Rename gRPC plugin serving arguments From 'data-source serve' to 'plugin serve'. --- pkg/plugin/manager/manager.go | 2 +- pkg/plugin/plugin.go | 12 ++++++------ pkg/plugin/plugin_test.go | 16 ++++++++-------- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/pkg/plugin/manager/manager.go b/pkg/plugin/manager/manager.go index 3f4dd82..5e10e9e 100644 --- a/pkg/plugin/manager/manager.go +++ b/pkg/plugin/manager/manager.go @@ -229,7 +229,7 @@ func loadGrpcPlugin(ctx context.Context, logger hclog.Logger, pluginName string, pluginSet[provision.ProvisionPluginName] = &provision.ProvisionPlugin{} } - cmd := exec.Command(pluginPath, cofidectl_plugin.DataSourcePluginArgs...) + cmd := exec.Command(pluginPath, cofidectl_plugin.PluginServeArgs...) client := go_plugin.NewClient(&go_plugin.ClientConfig{ Cmd: cmd, HandshakeConfig: cofidectl_plugin.HandshakeConfig, diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go index 92f0456..d1c9c1f 100644 --- a/pkg/plugin/plugin.go +++ b/pkg/plugin/plugin.go @@ -17,13 +17,13 @@ const ( DataSourcePluginName = "data_source" ) -// DataSourcePluginArgs contains the arguments passed to plugins when executing them as a data source. -// TODO: change to plugin serve -var DataSourcePluginArgs []string = []string{"data-source", "serve"} +// PluginServeArgs contains the arguments passed to plugins when executing them as a gRPC plugin. +var PluginServeArgs []string = []string{"plugin", "serve"} -// IsDataSourceServeCmd returns whether the provided command line arguments indicate that a plugin should serve a data source. -func IsDataSourceServeCmd(args []string) bool { - return slices.Equal(args, DataSourcePluginArgs) +// IsPluginServeCmd returns whether the provided command line arguments indicate that a plugin +// should serve gRPC plugins. +func IsPluginServeCmd(args []string) bool { + return slices.Equal(args, PluginServeArgs) } // DataSourcePlugin implements the plugin.Plugin interface to provide the GRPC diff --git a/pkg/plugin/plugin_test.go b/pkg/plugin/plugin_test.go index 9ec1a4f..9074c4f 100644 --- a/pkg/plugin/plugin_test.go +++ b/pkg/plugin/plugin_test.go @@ -9,12 +9,12 @@ import ( "github.com/stretchr/testify/assert" ) -func TestIsDataSourceServeCmd(t *testing.T) { - assert.True(t, IsDataSourceServeCmd([]string{"data-source", "serve"})) - assert.False(t, IsDataSourceServeCmd([]string{"trust-zone", "list"})) - assert.False(t, IsDataSourceServeCmd([]string{})) - assert.False(t, IsDataSourceServeCmd([]string{"data-source"})) - assert.False(t, IsDataSourceServeCmd([]string{"DATA-SOURCE", "serve"})) - assert.False(t, IsDataSourceServeCmd([]string{"data-source", "serve", "extra"})) - assert.False(t, IsDataSourceServeCmd(nil)) +func TestIsPluginServeCmd(t *testing.T) { + assert.True(t, IsPluginServeCmd([]string{"plugin", "serve"})) + assert.False(t, IsPluginServeCmd([]string{"trust-zone", "list"})) + assert.False(t, IsPluginServeCmd([]string{})) + assert.False(t, IsPluginServeCmd([]string{"plugin"})) + assert.False(t, IsPluginServeCmd([]string{"PLUGIN", "serve"})) + assert.False(t, IsPluginServeCmd([]string{"plugin", "serve", "extra"})) + assert.False(t, IsPluginServeCmd(nil)) } From f82fca6e4346af20dc8cde5c07a34a825c1d64b3 Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Wed, 11 Dec 2024 13:50:23 +0000 Subject: [PATCH 07/48] Support specifying data source and provision plugins in init command --- cmd/cofidectl/cmd/init.go | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/cmd/cofidectl/cmd/init.go b/cmd/cofidectl/cmd/init.go index a68a65c..6181fc8 100644 --- a/cmd/cofidectl/cmd/init.go +++ b/cmd/cofidectl/cmd/init.go @@ -7,6 +7,7 @@ import ( "fmt" "os" + pluginspb "github.com/cofide/cofide-api-sdk/gen/go/proto/plugins/v1alpha1" "github.com/cofide/cofidectl/pkg/cmd/context" "github.com/cofide/cofidectl/pkg/plugin" "github.com/cofide/cofidectl/pkg/plugin/manager" @@ -33,7 +34,9 @@ directory ` type Opts struct { - enableConnect bool + enableConnect bool + dataSourcePlugin string + provisionPlugin string } func (i *InitCommand) GetRootCommand() *cobra.Command { @@ -44,7 +47,6 @@ func (i *InitCommand) GetRootCommand() *cobra.Command { Long: initRootCmdDesc, Args: cobra.NoArgs, RunE: func(cmd *cobra.Command, args []string) error { - plugins := manager.GetDefaultPlugins() if opts.enableConnect { if ok, _ := plugin.PluginExists(connectPluginName); ok { fmt.Println(`Please run "cofidectl connect init"`) @@ -54,12 +56,19 @@ func (i *InitCommand) GetRootCommand() *cobra.Command { os.Exit(1) } + plugins := &pluginspb.Plugins{ + DataSource: &opts.dataSourcePlugin, + Provision: &opts.provisionPlugin, + } return i.cmdCtx.PluginManager.Init(cmd.Context(), plugins, nil) }, } + defaultPlugins := manager.GetDefaultPlugins() f := cmd.Flags() f.BoolVar(&opts.enableConnect, "enable-connect", false, "Enables Cofide Connect") + f.StringVar(&opts.dataSourcePlugin, "data-source-plugin", defaultPlugins.GetDataSource(), "Data source plugin") + f.StringVar(&opts.provisionPlugin, "provision-plugin", defaultPlugins.GetProvision(), "Provision plugin") return cmd } From 92c6d2e75f34b88d03ca021f65bdfb1289dfdb47 Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Wed, 11 Dec 2024 16:51:57 +0000 Subject: [PATCH 08/48] Refactor data source plugin into a datasource package This provides a cleaner separation between the different plugin types now that we have multiple types of plugins (since adding provision plugins). --- cmd/cofidectl/cmd/apbinding/apbinding.go | 4 +- cmd/cofidectl/cmd/trustzone/helm/helm.go | 6 +- cmd/cofidectl/cmd/trustzone/trustzone.go | 4 +- .../attestationpolicy/attestationpolicy.go | 4 +- pkg/plugin/datasource/plugin.go | 264 ++++++++++++++++++ pkg/plugin/grpc.go | 133 --------- pkg/plugin/grpc_test.go | 13 - pkg/plugin/interface.go | 11 - pkg/plugin/local/local_test.go | 4 +- pkg/plugin/manager/manager.go | 16 +- pkg/plugin/manager/manager_test.go | 13 +- pkg/plugin/plugin.go | 136 --------- pkg/plugin/provision/plugin.go | 5 +- .../provision/spirehelm/providerfactory.go | 6 +- pkg/plugin/provision/spirehelm/spirehelm.go | 15 +- .../provision/spirehelm/spirehelm_test.go | 6 +- pkg/provider/helm/values.go | 6 +- pkg/provider/helm/values_test.go | 4 +- 18 files changed, 309 insertions(+), 341 deletions(-) create mode 100644 pkg/plugin/datasource/plugin.go delete mode 100644 pkg/plugin/grpc.go delete mode 100644 pkg/plugin/grpc_test.go delete mode 100644 pkg/plugin/interface.go diff --git a/cmd/cofidectl/cmd/apbinding/apbinding.go b/cmd/cofidectl/cmd/apbinding/apbinding.go index e8eaca8..fb276fd 100644 --- a/cmd/cofidectl/cmd/apbinding/apbinding.go +++ b/cmd/cofidectl/cmd/apbinding/apbinding.go @@ -10,7 +10,7 @@ import ( ap_binding_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/ap_binding/v1alpha1" trust_zone_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/trust_zone/v1alpha1" cmdcontext "github.com/cofide/cofidectl/pkg/cmd/context" - cofidectl_plugin "github.com/cofide/cofidectl/pkg/plugin" + "github.com/cofide/cofidectl/pkg/plugin/datasource" "github.com/olekukonko/tablewriter" "github.com/spf13/cobra" @@ -85,7 +85,7 @@ func (c *APBindingCommand) GetListCommand() *cobra.Command { return cmd } -func (c *APBindingCommand) list(source cofidectl_plugin.DataSource, opts ListOpts) ([]*ap_binding_proto.APBinding, error) { +func (c *APBindingCommand) list(source datasource.DataSource, opts ListOpts) ([]*ap_binding_proto.APBinding, error) { var err error var trustZones []*trust_zone_proto.TrustZone diff --git a/cmd/cofidectl/cmd/trustzone/helm/helm.go b/cmd/cofidectl/cmd/trustzone/helm/helm.go index f9f1162..978eb6a 100644 --- a/cmd/cofidectl/cmd/trustzone/helm/helm.go +++ b/cmd/cofidectl/cmd/trustzone/helm/helm.go @@ -13,7 +13,7 @@ import ( "gopkg.in/yaml.v3" cmdcontext "github.com/cofide/cofidectl/pkg/cmd/context" - "github.com/cofide/cofidectl/pkg/plugin" + "github.com/cofide/cofidectl/pkg/plugin/datasource" "github.com/cofide/cofidectl/pkg/provider/helm" ) @@ -95,7 +95,7 @@ func (c *HelmCommand) GetOverrideCommand() *cobra.Command { } // overrideValues overrides Helm values for a trust zone. -func (c *HelmCommand) overrideValues(ds plugin.DataSource, tzName string, values map[string]any) error { +func (c *HelmCommand) overrideValues(ds datasource.DataSource, tzName string, values map[string]any) error { trustZone, err := ds.GetTrustZone(tzName) if err != nil { return err @@ -177,7 +177,7 @@ func (c *HelmCommand) GetValuesCommand() *cobra.Command { } // getValues returns the Helm values for a trust zone. -func (c *HelmCommand) getValues(ds plugin.DataSource, tzName string) (map[string]any, error) { +func (c *HelmCommand) getValues(ds datasource.DataSource, tzName string) (map[string]any, error) { trustZone, err := ds.GetTrustZone(tzName) if err != nil { return nil, err diff --git a/cmd/cofidectl/cmd/trustzone/trustzone.go b/cmd/cofidectl/cmd/trustzone/trustzone.go index 31c372b..32697d5 100644 --- a/cmd/cofidectl/cmd/trustzone/trustzone.go +++ b/cmd/cofidectl/cmd/trustzone/trustzone.go @@ -18,7 +18,7 @@ import ( 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" kubeutil "github.com/cofide/cofidectl/pkg/kube" - cofidectl_plugin "github.com/cofide/cofidectl/pkg/plugin" + "github.com/cofide/cofidectl/pkg/plugin/datasource" helmprovider "github.com/cofide/cofidectl/pkg/provider/helm" "github.com/cofide/cofidectl/pkg/spire" "github.com/olekukonko/tablewriter" @@ -202,7 +202,7 @@ func (c *TrustZoneCommand) GetStatusCommand() *cobra.Command { return cmd } -func (c *TrustZoneCommand) status(ctx context.Context, source cofidectl_plugin.DataSource, kubeConfig, tzName string) error { +func (c *TrustZoneCommand) status(ctx context.Context, source datasource.DataSource, kubeConfig, tzName string) error { trustZone, err := source.GetTrustZone(tzName) if err != nil { return err diff --git a/internal/pkg/attestationpolicy/attestationpolicy.go b/internal/pkg/attestationpolicy/attestationpolicy.go index 6ccb932..518781f 100644 --- a/internal/pkg/attestationpolicy/attestationpolicy.go +++ b/internal/pkg/attestationpolicy/attestationpolicy.go @@ -8,7 +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" - cofidectl_plugin "github.com/cofide/cofidectl/pkg/plugin" + "github.com/cofide/cofidectl/pkg/plugin/datasource" ) type AttestationPolicy struct { @@ -28,7 +28,7 @@ func NewAttestationPolicy(attestationPolicy *attestation_policy_proto.Attestatio } } -func (ap *AttestationPolicy) GetHelmConfig(source cofidectl_plugin.DataSource, binding *ap_binding_proto.APBinding) (map[string]any, error) { +func (ap *AttestationPolicy) GetHelmConfig(source datasource.DataSource, binding *ap_binding_proto.APBinding) (map[string]any, error) { var clusterSPIFFEID = make(map[string]any) switch policy := ap.AttestationPolicyProto.Policy.(type) { case *attestation_policy_proto.AttestationPolicy_Kubernetes: diff --git a/pkg/plugin/datasource/plugin.go b/pkg/plugin/datasource/plugin.go new file mode 100644 index 0000000..951b579 --- /dev/null +++ b/pkg/plugin/datasource/plugin.go @@ -0,0 +1,264 @@ +// Copyright 2024 Cofide Limited. +// SPDX-License-Identifier: Apache-2.0 + +package datasource + +import ( + "context" + + 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" + 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" + go_plugin "github.com/hashicorp/go-plugin" + "google.golang.org/grpc" +) + +// DataSourcePluginName is the name that should be used in the plugin map. +const DataSourcePluginName = "data_source" + +// DataSourcePlugin implements the plugin.Plugin interface to provide the GRPC +// server or client back to the plugin machinery. The server side should +// proved the Impl field with a concrete implementation of the DataSource +// interface. +type DataSourcePlugin struct { + go_plugin.Plugin + Impl DataSource +} + +func (dsp *DataSourcePlugin) GRPCClient(ctx context.Context, broker *go_plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) { + return &DataSourcePluginClientGRPC{ctx: ctx, client: cofidectl_proto.NewDataSourcePluginServiceClient(c)}, nil +} + +func (dsp *DataSourcePlugin) GRPCServer(broker *go_plugin.GRPCBroker, s *grpc.Server) error { + cofidectl_proto.RegisterDataSourcePluginServiceServer(s, &GRPCServer{Impl: dsp.Impl}) + return nil +} + +// Type check to ensure DataSourcePluginClientGRPC implements DataSource. +var _ DataSource = &DataSourcePluginClientGRPC{} + +// DataSourcePluginClientGRPC is used by clients (main application) to translate the +// DataSource interface of plugins to GRPC calls. +type DataSourcePluginClientGRPC struct { + ctx context.Context + client cofidectl_proto.DataSourcePluginServiceClient +} + +func NewDataSourcePluginClientGRPC(ctx context.Context, client cofidectl_proto.DataSourcePluginServiceClient) *DataSourcePluginClientGRPC { + return &DataSourcePluginClientGRPC{ctx: ctx, client: client} +} + +func (c *DataSourcePluginClientGRPC) Validate(ctx context.Context) error { + _, err := c.client.Validate(ctx, &cofidectl_proto.ValidateRequest{}) + return err +} + +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 { + return nil, err + } + + return resp.TrustZone, nil +} + +func (c *DataSourcePluginClientGRPC) ListTrustZones() ([]*trust_zone_proto.TrustZone, error) { + resp, err := c.client.ListTrustZones(c.ctx, &cofidectl_proto.ListTrustZonesRequest{}) + if err != nil { + return nil, err + } + + 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}) + if err != nil { + return nil, err + } + + 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) AddAttestationPolicy(policy *attestation_policy_proto.AttestationPolicy) (*attestation_policy_proto.AttestationPolicy, error) { + resp, err := c.client.AddAttestationPolicy(c.ctx, &cofidectl_proto.AddAttestationPolicyRequest{Policy: policy}) + if err != nil { + return nil, err + } + + return resp.Policy, nil +} + +func (c *DataSourcePluginClientGRPC) GetAttestationPolicy(name string) (*attestation_policy_proto.AttestationPolicy, error) { + resp, err := c.client.GetAttestationPolicy(c.ctx, &cofidectl_proto.GetAttestationPolicyRequest{Name: &name}) + if err != nil { + return nil, err + } + + return resp.Policy, nil +} + +func (c *DataSourcePluginClientGRPC) ListAttestationPolicies() ([]*attestation_policy_proto.AttestationPolicy, error) { + resp, err := c.client.ListAttestationPolicies(c.ctx, &cofidectl_proto.ListAttestationPoliciesRequest{}) + if err != nil { + return nil, err + } + + return resp.Policies, nil +} + +func (c *DataSourcePluginClientGRPC) AddAPBinding(binding *ap_binding_proto.APBinding) (*ap_binding_proto.APBinding, error) { + resp, err := c.client.AddAPBinding(c.ctx, &cofidectl_proto.AddAPBindingRequest{Binding: binding}) + if err != nil { + return nil, err + } + + return resp.Binding, nil +} + +func (c *DataSourcePluginClientGRPC) DestroyAPBinding(binding *ap_binding_proto.APBinding) error { + _, err := c.client.DestroyAPBinding(c.ctx, &cofidectl_proto.DestroyAPBindingRequest{Binding: binding}) + return err +} + +func (c *DataSourcePluginClientGRPC) AddFederation(federation *federation_proto.Federation) (*federation_proto.Federation, error) { + resp, err := c.client.AddFederation(c.ctx, &cofidectl_proto.AddFederationRequest{Federation: federation}) + if err != nil { + return nil, err + } + + return resp.Federation, nil +} + +func (c *DataSourcePluginClientGRPC) ListFederations() ([]*federation_proto.Federation, error) { + resp, err := c.client.ListFederations(c.ctx, &cofidectl_proto.ListFederationsRequest{}) + if err != nil { + return nil, err + } + + return resp.Federations, nil +} + +func (c *DataSourcePluginClientGRPC) ListFederationsByTrustZone(string) ([]*federation_proto.Federation, error) { + resp, err := c.client.ListFederationsByTrustZone(c.ctx, &cofidectl_proto.ListFederationsByTrustZoneRequest{}) + if err != nil { + return nil, err + } + + return resp.Federations, nil +} + +type GRPCServer struct { + Impl DataSource +} + +func (s *GRPCServer) Validate(ctx context.Context, req *cofidectl_proto.ValidateRequest) (*cofidectl_proto.ValidateResponse, error) { + err := s.Impl.Validate(ctx) + if err != nil { + return nil, err + } + return &cofidectl_proto.ValidateResponse{}, 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 { + return nil, err + } + return &cofidectl_proto.GetTrustZoneResponse{TrustZone: trustZone}, nil +} + +func (s *GRPCServer) ListTrustZones(_ context.Context, req *cofidectl_proto.ListTrustZonesRequest) (*cofidectl_proto.ListTrustZonesResponse, error) { + trustZones, err := s.Impl.ListTrustZones() + if err != nil { + return nil, err + } + 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) + if err != nil { + return nil, err + } + return &cofidectl_proto.AddTrustZoneResponse{TrustZone: trustZone}, nil +} + +func (s *GRPCServer) UpdateTrustZone(_ context.Context, req *cofidectl_proto.UpdateTrustZoneRequest) (*cofidectl_proto.UpdateTrustZoneResponse, error) { + err := s.Impl.UpdateTrustZone(req.TrustZone) + if err != nil { + return nil, err + } + return &cofidectl_proto.UpdateTrustZoneResponse{}, nil +} + +func (s *GRPCServer) AddAttestationPolicy(_ context.Context, req *cofidectl_proto.AddAttestationPolicyRequest) (*cofidectl_proto.AddAttestationPolicyResponse, error) { + policy, err := s.Impl.AddAttestationPolicy(req.Policy) + if err != nil { + return nil, err + } + return &cofidectl_proto.AddAttestationPolicyResponse{Policy: policy}, nil +} + +func (s *GRPCServer) GetAttestationPolicy(_ context.Context, req *cofidectl_proto.GetAttestationPolicyRequest) (*cofidectl_proto.GetAttestationPolicyResponse, error) { + resp, err := s.Impl.GetAttestationPolicy(req.GetName()) + if err != nil { + return nil, err + } + return &cofidectl_proto.GetAttestationPolicyResponse{Policy: resp}, nil +} + +func (s *GRPCServer) ListAttestationPolicies(_ context.Context, req *cofidectl_proto.ListAttestationPoliciesRequest) (*cofidectl_proto.ListAttestationPoliciesResponse, error) { + policies, err := s.Impl.ListAttestationPolicies() + if err != nil { + return nil, err + } + return &cofidectl_proto.ListAttestationPoliciesResponse{Policies: policies}, nil +} + +func (s *GRPCServer) AddAPBinding(_ context.Context, req *cofidectl_proto.AddAPBindingRequest) (*cofidectl_proto.AddAPBindingResponse, error) { + binding, err := s.Impl.AddAPBinding(req.Binding) + if err != nil { + return nil, err + } + return &cofidectl_proto.AddAPBindingResponse{Binding: binding}, nil +} + +func (s *GRPCServer) DestroyAPBinding(_ context.Context, req *cofidectl_proto.DestroyAPBindingRequest) (*cofidectl_proto.DestroyAPBindingResponse, error) { + err := s.Impl.DestroyAPBinding(req.Binding) + if err != nil { + return nil, err + } + return &cofidectl_proto.DestroyAPBindingResponse{}, nil +} + +func (s *GRPCServer) AddFederation(_ context.Context, req *cofidectl_proto.AddFederationRequest) (*cofidectl_proto.AddFederationResponse, error) { + federation, err := s.Impl.AddFederation(req.Federation) + if err != nil { + return nil, err + } + return &cofidectl_proto.AddFederationResponse{Federation: federation}, nil +} + +func (s *GRPCServer) ListFederations(_ context.Context, req *cofidectl_proto.ListFederationsRequest) (*cofidectl_proto.ListFederationsResponse, error) { + federations, err := s.Impl.ListFederations() + if err != nil { + return nil, err + } + return &cofidectl_proto.ListFederationsResponse{Federations: federations}, nil +} + +func (s *GRPCServer) ListFederationsByTrustZone(_ context.Context, req *cofidectl_proto.ListFederationsByTrustZoneRequest) (*cofidectl_proto.ListFederationsByTrustZoneResponse, error) { + federations, err := s.Impl.ListFederationsByTrustZone(req.GetTrustZoneName()) + if err != nil { + return nil, err + } + return &cofidectl_proto.ListFederationsByTrustZoneResponse{Federations: federations}, nil +} diff --git a/pkg/plugin/grpc.go b/pkg/plugin/grpc.go deleted file mode 100644 index 1b46091..0000000 --- a/pkg/plugin/grpc.go +++ /dev/null @@ -1,133 +0,0 @@ -// Copyright 2024 Cofide Limited. -// SPDX-License-Identifier: Apache-2.0 - -package plugin - -import ( - "context" - - 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" - 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" -) - -// Type check to ensure DataSourcePluginClientGRPC implements DataSource. -var _ DataSource = &DataSourcePluginClientGRPC{} - -// DataSourcePluginClientGRPC is used by clients (main application) to translate the -// DataSource interface of plugins to GRPC calls. -type DataSourcePluginClientGRPC struct { - ctx context.Context - client cofidectl_proto.DataSourcePluginServiceClient -} - -func NewDataSourcePluginClientGRPC(ctx context.Context, client cofidectl_proto.DataSourcePluginServiceClient) *DataSourcePluginClientGRPC { - return &DataSourcePluginClientGRPC{ctx: ctx, client: client} -} - -func (c *DataSourcePluginClientGRPC) Validate(ctx context.Context) error { - _, err := c.client.Validate(ctx, &cofidectl_proto.ValidateRequest{}) - return err -} - -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 { - return nil, err - } - - return resp.TrustZone, nil -} - -func (c *DataSourcePluginClientGRPC) ListTrustZones() ([]*trust_zone_proto.TrustZone, error) { - resp, err := c.client.ListTrustZones(c.ctx, &cofidectl_proto.ListTrustZonesRequest{}) - if err != nil { - return nil, err - } - - 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}) - if err != nil { - return nil, err - } - - 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) AddAttestationPolicy(policy *attestation_policy_proto.AttestationPolicy) (*attestation_policy_proto.AttestationPolicy, error) { - resp, err := c.client.AddAttestationPolicy(c.ctx, &cofidectl_proto.AddAttestationPolicyRequest{Policy: policy}) - if err != nil { - return nil, err - } - - return resp.Policy, nil -} - -func (c *DataSourcePluginClientGRPC) GetAttestationPolicy(name string) (*attestation_policy_proto.AttestationPolicy, error) { - resp, err := c.client.GetAttestationPolicy(c.ctx, &cofidectl_proto.GetAttestationPolicyRequest{Name: &name}) - if err != nil { - return nil, err - } - - return resp.Policy, nil -} - -func (c *DataSourcePluginClientGRPC) ListAttestationPolicies() ([]*attestation_policy_proto.AttestationPolicy, error) { - resp, err := c.client.ListAttestationPolicies(c.ctx, &cofidectl_proto.ListAttestationPoliciesRequest{}) - if err != nil { - return nil, err - } - - return resp.Policies, nil -} - -func (c *DataSourcePluginClientGRPC) AddAPBinding(binding *ap_binding_proto.APBinding) (*ap_binding_proto.APBinding, error) { - resp, err := c.client.AddAPBinding(c.ctx, &cofidectl_proto.AddAPBindingRequest{Binding: binding}) - if err != nil { - return nil, err - } - - return resp.Binding, nil -} - -func (c *DataSourcePluginClientGRPC) DestroyAPBinding(binding *ap_binding_proto.APBinding) error { - _, err := c.client.DestroyAPBinding(c.ctx, &cofidectl_proto.DestroyAPBindingRequest{Binding: binding}) - return err -} - -func (c *DataSourcePluginClientGRPC) AddFederation(federation *federation_proto.Federation) (*federation_proto.Federation, error) { - resp, err := c.client.AddFederation(c.ctx, &cofidectl_proto.AddFederationRequest{Federation: federation}) - if err != nil { - return nil, err - } - - return resp.Federation, nil -} - -func (c *DataSourcePluginClientGRPC) ListFederations() ([]*federation_proto.Federation, error) { - resp, err := c.client.ListFederations(c.ctx, &cofidectl_proto.ListFederationsRequest{}) - if err != nil { - return nil, err - } - - return resp.Federations, nil -} - -func (c *DataSourcePluginClientGRPC) ListFederationsByTrustZone(string) ([]*federation_proto.Federation, error) { - resp, err := c.client.ListFederationsByTrustZone(c.ctx, &cofidectl_proto.ListFederationsByTrustZoneRequest{}) - if err != nil { - return nil, err - } - - return resp.Federations, nil -} diff --git a/pkg/plugin/grpc_test.go b/pkg/plugin/grpc_test.go deleted file mode 100644 index 2c3ab8e..0000000 --- a/pkg/plugin/grpc_test.go +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2024 Cofide Limited. -// SPDX-License-Identifier: Apache-2.0 - -package plugin - -import ( - "testing" -) - -func TestDataSourcePluginClientGRPC_ImplementsDataSource(t *testing.T) { - client := DataSourcePluginClientGRPC{} - var _ DataSource = &client -} diff --git a/pkg/plugin/interface.go b/pkg/plugin/interface.go deleted file mode 100644 index 03e17ba..0000000 --- a/pkg/plugin/interface.go +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright 2024 Cofide Limited. -// SPDX-License-Identifier: Apache-2.0 - -package plugin - -import ( - "github.com/cofide/cofidectl/pkg/plugin/datasource" -) - -// Alias DataSource in the plugin package while transitioning to a new package. -type DataSource = datasource.DataSource diff --git a/pkg/plugin/local/local_test.go b/pkg/plugin/local/local_test.go index ac00eed..550732d 100644 --- a/pkg/plugin/local/local_test.go +++ b/pkg/plugin/local/local_test.go @@ -8,7 +8,7 @@ import ( "slices" "testing" - "github.com/cofide/cofidectl/pkg/plugin" + "github.com/cofide/cofidectl/pkg/plugin/datasource" "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -25,7 +25,7 @@ import ( func TestLocalDataSource_ImplementsDataSource(t *testing.T) { local := LocalDataSource{} - var _ plugin.DataSource = &local + var _ datasource.DataSource = &local } func TestNewLocalDataSource(t *testing.T) { diff --git a/pkg/plugin/manager/manager.go b/pkg/plugin/manager/manager.go index 5e10e9e..8d744cd 100644 --- a/pkg/plugin/manager/manager.go +++ b/pkg/plugin/manager/manager.go @@ -14,7 +14,7 @@ import ( pluginspb "github.com/cofide/cofide-api-sdk/gen/go/proto/plugins/v1alpha1" "github.com/cofide/cofidectl/internal/pkg/config" "github.com/cofide/cofidectl/internal/pkg/proto" - cofidectl_plugin "github.com/cofide/cofidectl/pkg/plugin" + "github.com/cofide/cofidectl/pkg/plugin" "github.com/cofide/cofidectl/pkg/plugin/datasource" "github.com/cofide/cofidectl/pkg/plugin/local" "github.com/cofide/cofidectl/pkg/plugin/provision" @@ -106,7 +106,7 @@ func (pm *PluginManager) Init(ctx context.Context, plugins *pluginspb.Plugins, p } // GetDataSource returns the data source plugin, loading it if necessary. -func (pm *PluginManager) GetDataSource(ctx context.Context) (cofidectl_plugin.DataSource, error) { +func (pm *PluginManager) GetDataSource(ctx context.Context) (datasource.DataSource, error) { if pm.source != nil { return pm.source, nil } @@ -114,7 +114,7 @@ func (pm *PluginManager) GetDataSource(ctx context.Context) (cofidectl_plugin.Da } // loadDataSource loads the data source plugin, which may be an in-process or gRPC plugin. -func (pm *PluginManager) loadDataSource(ctx context.Context) (cofidectl_plugin.DataSource, error) { +func (pm *PluginManager) loadDataSource(ctx context.Context) (datasource.DataSource, error) { if pm.source != nil { return nil, errors.New("data source has already been loaded") } @@ -216,23 +216,23 @@ func (pm *PluginManager) loadGrpcPlugin(ctx context.Context, pluginName string, // loadGrpcPlugin is the default grpcPluginLoader. func loadGrpcPlugin(ctx context.Context, logger hclog.Logger, pluginName string, plugins *pluginspb.Plugins) (*grpcPlugin, error) { - pluginPath, err := cofidectl_plugin.GetPluginPath(pluginName) + pluginPath, err := plugin.GetPluginPath(pluginName) if err != nil { return nil, err } pluginSet := map[string]go_plugin.Plugin{} if plugins.GetDataSource() == pluginName { - pluginSet[cofidectl_plugin.DataSourcePluginName] = &cofidectl_plugin.DataSourcePlugin{} + pluginSet[datasource.DataSourcePluginName] = &datasource.DataSourcePlugin{} } if plugins.GetProvision() == pluginName { pluginSet[provision.ProvisionPluginName] = &provision.ProvisionPlugin{} } - cmd := exec.Command(pluginPath, cofidectl_plugin.PluginServeArgs...) + cmd := exec.Command(pluginPath, plugin.PluginServeArgs...) client := go_plugin.NewClient(&go_plugin.ClientConfig{ Cmd: cmd, - HandshakeConfig: cofidectl_plugin.HandshakeConfig, + HandshakeConfig: plugin.HandshakeConfig, Plugins: pluginSet, AllowedProtocols: []go_plugin.Protocol{go_plugin.ProtocolGRPC}, Logger: logger, @@ -258,7 +258,7 @@ func startGrpcPlugin(ctx context.Context, client *go_plugin.Client, pluginName s grpcPlugin := &grpcPlugin{client: client} if plugins.GetDataSource() == pluginName { - source, err := dispensePlugin[datasource.DataSource](ctx, grpcClient, cofidectl_plugin.DataSourcePluginName) + source, err := dispensePlugin[datasource.DataSource](ctx, grpcClient, datasource.DataSourcePluginName) if err != nil { return nil, err } diff --git a/pkg/plugin/manager/manager_test.go b/pkg/plugin/manager/manager_test.go index f9718f0..d28f295 100644 --- a/pkg/plugin/manager/manager_test.go +++ b/pkg/plugin/manager/manager_test.go @@ -11,7 +11,6 @@ import ( pluginspb "github.com/cofide/cofide-api-sdk/gen/go/proto/plugins/v1alpha1" "github.com/cofide/cofidectl/internal/pkg/config" "github.com/cofide/cofidectl/internal/pkg/test/fixtures" - cofidectl_plugin "github.com/cofide/cofidectl/pkg/plugin" "github.com/cofide/cofidectl/pkg/plugin/datasource" "github.com/cofide/cofidectl/pkg/plugin/local" "github.com/cofide/cofidectl/pkg/plugin/provision" @@ -132,7 +131,7 @@ func TestManager_Init_failure(t *testing.T) { dsName string provisionName string pluginConfig map[string]*structpb.Struct - want func(config.Loader) cofidectl_plugin.DataSource + want func(config.Loader) datasource.DataSource wantErrMessage string }{ { @@ -185,12 +184,12 @@ func TestManager_GetDataSource_success(t *testing.T) { tests := []struct { name string config config.Config - want func(config.Loader) cofidectl_plugin.DataSource + want func(config.Loader) datasource.DataSource }{ { name: "defaults", config: config.Config{Plugins: GetDefaultPlugins()}, - want: func(cl config.Loader) cofidectl_plugin.DataSource { + want: func(cl config.Loader) datasource.DataSource { lds, err := local.NewLocalDataSource(cl) assert.Nil(t, err) return lds @@ -199,7 +198,7 @@ func TestManager_GetDataSource_success(t *testing.T) { { name: "gRPC", config: config.Config{Plugins: fixtures.Plugins("plugins1")}, - want: func(cl config.Loader) cofidectl_plugin.DataSource { + want: func(cl config.Loader) datasource.DataSource { fcds := newFakeGrpcDataSource(t, cl) return fcds }, @@ -207,7 +206,7 @@ func TestManager_GetDataSource_success(t *testing.T) { { name: "gRPC with provision", config: config.Config{Plugins: fixtures.Plugins("plugins2")}, - want: func(cl config.Loader) cofidectl_plugin.DataSource { + want: func(cl config.Loader) datasource.DataSource { fcds := newFakeGrpcDataSource(t, cl) return fcds }, @@ -400,7 +399,7 @@ func TestManager_Shutdown(t *testing.T) { tests := []struct { name string config config.Config - want func(config.Loader) cofidectl_plugin.DataSource + want func(config.Loader) datasource.DataSource }{ { name: "defaults", diff --git a/pkg/plugin/plugin.go b/pkg/plugin/plugin.go index d1c9c1f..745dfe25 100644 --- a/pkg/plugin/plugin.go +++ b/pkg/plugin/plugin.go @@ -4,17 +4,7 @@ package plugin import ( - "context" "slices" - - cofidectl_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/cofidectl_plugin/v1alpha1" - go_plugin "github.com/hashicorp/go-plugin" - "google.golang.org/grpc" -) - -const ( - // DataSourcePluginName is the name that should be used in the plugin map. - DataSourcePluginName = "data_source" ) // PluginServeArgs contains the arguments passed to plugins when executing them as a gRPC plugin. @@ -25,129 +15,3 @@ var PluginServeArgs []string = []string{"plugin", "serve"} func IsPluginServeCmd(args []string) bool { return slices.Equal(args, PluginServeArgs) } - -// DataSourcePlugin implements the plugin.Plugin interface to provide the GRPC -// server or client back to the plugin machinery. The server side should -// proved the Impl field with a concrete implementation of the DataSource -// interface. -type DataSourcePlugin struct { - go_plugin.Plugin - Impl DataSource -} - -func (dsp *DataSourcePlugin) GRPCClient(ctx context.Context, broker *go_plugin.GRPCBroker, c *grpc.ClientConn) (interface{}, error) { - return &DataSourcePluginClientGRPC{ctx: ctx, client: cofidectl_proto.NewDataSourcePluginServiceClient(c)}, nil -} - -func (dsp *DataSourcePlugin) GRPCServer(broker *go_plugin.GRPCBroker, s *grpc.Server) error { - cofidectl_proto.RegisterDataSourcePluginServiceServer(s, &GRPCServer{Impl: dsp.Impl}) - return nil -} - -type GRPCServer struct { - Impl DataSource -} - -func (s *GRPCServer) Validate(ctx context.Context, req *cofidectl_proto.ValidateRequest) (*cofidectl_proto.ValidateResponse, error) { - err := s.Impl.Validate(ctx) - if err != nil { - return nil, err - } - return &cofidectl_proto.ValidateResponse{}, 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 { - return nil, err - } - return &cofidectl_proto.GetTrustZoneResponse{TrustZone: trustZone}, nil -} - -func (s *GRPCServer) ListTrustZones(_ context.Context, req *cofidectl_proto.ListTrustZonesRequest) (*cofidectl_proto.ListTrustZonesResponse, error) { - trustZones, err := s.Impl.ListTrustZones() - if err != nil { - return nil, err - } - 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) - if err != nil { - return nil, err - } - return &cofidectl_proto.AddTrustZoneResponse{TrustZone: trustZone}, nil -} - -func (s *GRPCServer) UpdateTrustZone(_ context.Context, req *cofidectl_proto.UpdateTrustZoneRequest) (*cofidectl_proto.UpdateTrustZoneResponse, error) { - err := s.Impl.UpdateTrustZone(req.TrustZone) - if err != nil { - return nil, err - } - return &cofidectl_proto.UpdateTrustZoneResponse{}, nil -} - -func (s *GRPCServer) AddAttestationPolicy(_ context.Context, req *cofidectl_proto.AddAttestationPolicyRequest) (*cofidectl_proto.AddAttestationPolicyResponse, error) { - policy, err := s.Impl.AddAttestationPolicy(req.Policy) - if err != nil { - return nil, err - } - return &cofidectl_proto.AddAttestationPolicyResponse{Policy: policy}, nil -} - -func (s *GRPCServer) GetAttestationPolicy(_ context.Context, req *cofidectl_proto.GetAttestationPolicyRequest) (*cofidectl_proto.GetAttestationPolicyResponse, error) { - resp, err := s.Impl.GetAttestationPolicy(req.GetName()) - if err != nil { - return nil, err - } - return &cofidectl_proto.GetAttestationPolicyResponse{Policy: resp}, nil -} - -func (s *GRPCServer) ListAttestationPolicies(_ context.Context, req *cofidectl_proto.ListAttestationPoliciesRequest) (*cofidectl_proto.ListAttestationPoliciesResponse, error) { - policies, err := s.Impl.ListAttestationPolicies() - if err != nil { - return nil, err - } - return &cofidectl_proto.ListAttestationPoliciesResponse{Policies: policies}, nil -} - -func (s *GRPCServer) AddAPBinding(_ context.Context, req *cofidectl_proto.AddAPBindingRequest) (*cofidectl_proto.AddAPBindingResponse, error) { - binding, err := s.Impl.AddAPBinding(req.Binding) - if err != nil { - return nil, err - } - return &cofidectl_proto.AddAPBindingResponse{Binding: binding}, nil -} - -func (s *GRPCServer) DestroyAPBinding(_ context.Context, req *cofidectl_proto.DestroyAPBindingRequest) (*cofidectl_proto.DestroyAPBindingResponse, error) { - err := s.Impl.DestroyAPBinding(req.Binding) - if err != nil { - return nil, err - } - return &cofidectl_proto.DestroyAPBindingResponse{}, nil -} - -func (s *GRPCServer) AddFederation(_ context.Context, req *cofidectl_proto.AddFederationRequest) (*cofidectl_proto.AddFederationResponse, error) { - federation, err := s.Impl.AddFederation(req.Federation) - if err != nil { - return nil, err - } - return &cofidectl_proto.AddFederationResponse{Federation: federation}, nil -} - -func (s *GRPCServer) ListFederations(_ context.Context, req *cofidectl_proto.ListFederationsRequest) (*cofidectl_proto.ListFederationsResponse, error) { - federations, err := s.Impl.ListFederations() - if err != nil { - return nil, err - } - return &cofidectl_proto.ListFederationsResponse{Federations: federations}, nil -} - -func (s *GRPCServer) ListFederationsByTrustZone(_ context.Context, req *cofidectl_proto.ListFederationsByTrustZoneRequest) (*cofidectl_proto.ListFederationsByTrustZoneResponse, error) { - federations, err := s.Impl.ListFederationsByTrustZone(req.GetTrustZoneName()) - if err != nil { - return nil, err - } - return &cofidectl_proto.ListFederationsByTrustZoneResponse{Federations: federations}, nil -} diff --git a/pkg/plugin/provision/plugin.go b/pkg/plugin/provision/plugin.go index 6366a31..ea57460 100644 --- a/pkg/plugin/provision/plugin.go +++ b/pkg/plugin/provision/plugin.go @@ -10,7 +10,6 @@ import ( cofidectl_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/cofidectl_plugin/v1alpha1" provisionpb "github.com/cofide/cofide-api-sdk/gen/go/proto/provision_plugin/v1alpha1" - "github.com/cofide/cofidectl/pkg/plugin" "github.com/cofide/cofidectl/pkg/plugin/datasource" go_plugin "github.com/hashicorp/go-plugin" "google.golang.org/grpc" @@ -120,7 +119,7 @@ func (c *ProvisionPluginClientGRPC) TearDown(ctx context.Context, source datasou // This uses the bidirectional communication feature of go-plugin. See // https://pkg.go.dev/github.com/hashicorp/go-plugin/examples/bidirectional for an example. func (c *ProvisionPluginClientGRPC) startDataSourceServer(source datasource.DataSource) (*grpc.Server, uint32) { - dsServer := &plugin.GRPCServer{Impl: source} + dsServer := &datasource.GRPCServer{Impl: source} serverCh := make(chan *grpc.Server) serverFunc := func(opts []grpc.ServerOption) *grpc.Server { @@ -230,6 +229,6 @@ func (s *GRPCServer) getDataSourceClient(ctx context.Context, dataSourceID uint3 return nil, nil, err } - client := plugin.NewDataSourcePluginClientGRPC(ctx, cofidectl_proto.NewDataSourcePluginServiceClient(conn)) + client := datasource.NewDataSourcePluginClientGRPC(ctx, cofidectl_proto.NewDataSourcePluginServiceClient(conn)) return client, conn, nil } diff --git a/pkg/plugin/provision/spirehelm/providerfactory.go b/pkg/plugin/provision/spirehelm/providerfactory.go index 89296b2..48c5574 100644 --- a/pkg/plugin/provision/spirehelm/providerfactory.go +++ b/pkg/plugin/provision/spirehelm/providerfactory.go @@ -8,7 +8,7 @@ import ( trust_zone_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/trust_zone/v1alpha1" - "github.com/cofide/cofidectl/pkg/plugin" + "github.com/cofide/cofidectl/pkg/plugin/datasource" "github.com/cofide/cofidectl/pkg/provider/helm" ) @@ -18,14 +18,14 @@ var _ ProviderFactory = &HelmSPIREProviderFactory{} // ProviderFactory is an interface that abstracts the construction of helm.Provider objects. type ProviderFactory interface { // Build returns a helm.Provider configured with values for an install/upgrade. - Build(ctx context.Context, ds plugin.DataSource, trustZone *trust_zone_proto.TrustZone, genValues bool) (helm.Provider, error) + Build(ctx context.Context, ds datasource.DataSource, trustZone *trust_zone_proto.TrustZone, genValues bool) (helm.Provider, error) } // HelmSPIREProviderFactory implements the ProviderFactory interface, building a HelmSPIREProvider // using the default values generator. type HelmSPIREProviderFactory struct{} -func (f *HelmSPIREProviderFactory) Build(ctx context.Context, ds plugin.DataSource, trustZone *trust_zone_proto.TrustZone, genValues bool) (helm.Provider, error) { +func (f *HelmSPIREProviderFactory) Build(ctx context.Context, ds datasource.DataSource, trustZone *trust_zone_proto.TrustZone, genValues bool) (helm.Provider, error) { spireValues := map[string]any{} var err error if genValues { diff --git a/pkg/plugin/provision/spirehelm/spirehelm.go b/pkg/plugin/provision/spirehelm/spirehelm.go index 8d8b697..78564f4 100644 --- a/pkg/plugin/provision/spirehelm/spirehelm.go +++ b/pkg/plugin/provision/spirehelm/spirehelm.go @@ -11,7 +11,6 @@ import ( trust_zone_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/trust_zone/v1alpha1" kubeutil "github.com/cofide/cofidectl/pkg/kube" - "github.com/cofide/cofidectl/pkg/plugin" "github.com/cofide/cofidectl/pkg/plugin/datasource" "github.com/cofide/cofidectl/pkg/plugin/provision" "github.com/cofide/cofidectl/pkg/spire" @@ -65,7 +64,7 @@ func (h *SpireHelm) TearDown(ctx context.Context, ds datasource.DataSource) (<-c return statusCh, nil } -func (h *SpireHelm) deploy(ctx context.Context, ds plugin.DataSource, kubeCfgFile string, statusCh chan<- *provisionpb.Status) error { +func (h *SpireHelm) deploy(ctx context.Context, ds datasource.DataSource, kubeCfgFile string, statusCh chan<- *provisionpb.Status) error { trustZones, err := h.ListTrustZones(ds) if err != nil { statusCh <- provision.StatusError("Deploying", "Failed listing trust zones", err) @@ -96,7 +95,7 @@ func (h *SpireHelm) deploy(ctx context.Context, ds plugin.DataSource, kubeCfgFil return nil } -func (h *SpireHelm) tearDown(ctx context.Context, ds plugin.DataSource, statusCh chan<- *provisionpb.Status) error { +func (h *SpireHelm) tearDown(ctx context.Context, ds datasource.DataSource, statusCh chan<- *provisionpb.Status) error { trustZones, err := h.ListTrustZones(ds) if err != nil { statusCh <- provision.StatusError("Uninstalling", "Failed listing trust zones", err) @@ -110,7 +109,7 @@ func (h *SpireHelm) tearDown(ctx context.Context, ds plugin.DataSource, statusCh } // ListTrustZones returns a list of all trust zones. If no trust zones exist, it returns an error. -func (h *SpireHelm) ListTrustZones(ds plugin.DataSource) ([]*trust_zone_proto.TrustZone, error) { +func (h *SpireHelm) ListTrustZones(ds datasource.DataSource) ([]*trust_zone_proto.TrustZone, error) { trustZones, err := ds.ListTrustZones() if err != nil { return nil, err @@ -132,7 +131,7 @@ func (h *SpireHelm) AddSPIRERepository(ctx context.Context, statusCh chan<- *pro return prov.AddRepository(statusCh) } -func (h *SpireHelm) InstallSPIREStack(ctx context.Context, ds plugin.DataSource, trustZones []*trust_zone_proto.TrustZone, statusCh chan<- *provisionpb.Status) error { +func (h *SpireHelm) InstallSPIREStack(ctx context.Context, ds datasource.DataSource, trustZones []*trust_zone_proto.TrustZone, statusCh chan<- *provisionpb.Status) error { for _, trustZone := range trustZones { prov, err := h.providerFactory.Build(ctx, ds, trustZone, true) if err != nil { @@ -148,7 +147,7 @@ func (h *SpireHelm) InstallSPIREStack(ctx context.Context, ds plugin.DataSource, return nil } -func (h *SpireHelm) WatchAndConfigure(ctx context.Context, ds plugin.DataSource, trustZones []*trust_zone_proto.TrustZone, kubeCfgFile string, statusCh chan<- *provisionpb.Status) error { +func (h *SpireHelm) WatchAndConfigure(ctx context.Context, ds datasource.DataSource, trustZones []*trust_zone_proto.TrustZone, kubeCfgFile string, statusCh chan<- *provisionpb.Status) error { // Wait for SPIRE servers to be available and update status before applying federation(s) for _, trustZone := range trustZones { if err := h.GetBundleAndEndpoint(ctx, statusCh, ds, trustZone, kubeCfgFile); err != nil { @@ -158,7 +157,7 @@ func (h *SpireHelm) WatchAndConfigure(ctx context.Context, ds plugin.DataSource, return nil } -func (h *SpireHelm) GetBundleAndEndpoint(ctx context.Context, statusCh chan<- *provisionpb.Status, ds plugin.DataSource, trustZone *trust_zone_proto.TrustZone, kubeCfgFile string) error { +func (h *SpireHelm) GetBundleAndEndpoint(ctx context.Context, statusCh chan<- *provisionpb.Status, ds datasource.DataSource, trustZone *trust_zone_proto.TrustZone, kubeCfgFile string) error { sb := provision.NewStatusBuilder(trustZone.Name, trustZone.GetKubernetesCluster()) statusCh <- sb.Ok("Waiting", "Waiting for SPIRE server pod and service") @@ -198,7 +197,7 @@ func (h *SpireHelm) GetBundleAndEndpoint(ctx context.Context, statusCh chan<- *p return nil } -func (h *SpireHelm) ApplyPostInstallHelmConfig(ctx context.Context, ds plugin.DataSource, trustZones []*trust_zone_proto.TrustZone, statusCh chan<- *provisionpb.Status) error { +func (h *SpireHelm) ApplyPostInstallHelmConfig(ctx context.Context, ds datasource.DataSource, trustZones []*trust_zone_proto.TrustZone, statusCh chan<- *provisionpb.Status) error { for _, trustZone := range trustZones { prov, err := h.providerFactory.Build(ctx, ds, trustZone, true) if err != nil { diff --git a/pkg/plugin/provision/spirehelm/spirehelm_test.go b/pkg/plugin/provision/spirehelm/spirehelm_test.go index 9ed134f..74c8465 100644 --- a/pkg/plugin/provision/spirehelm/spirehelm_test.go +++ b/pkg/plugin/provision/spirehelm/spirehelm_test.go @@ -13,7 +13,7 @@ import ( 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" - "github.com/cofide/cofidectl/pkg/plugin" + "github.com/cofide/cofidectl/pkg/plugin/datasource" "github.com/cofide/cofidectl/pkg/plugin/local" "github.com/cofide/cofidectl/pkg/plugin/provision" "github.com/cofide/cofidectl/pkg/provider/helm" @@ -82,7 +82,7 @@ func newFakeHelmSPIREProviderFactory() *fakeHelmSPIREProviderFactory { return &fakeHelmSPIREProviderFactory{} } -func (f *fakeHelmSPIREProviderFactory) Build(ctx context.Context, ds plugin.DataSource, trustZone *trust_zone_proto.TrustZone, genValues bool) (helm.Provider, error) { +func (f *fakeHelmSPIREProviderFactory) Build(ctx context.Context, ds datasource.DataSource, trustZone *trust_zone_proto.TrustZone, genValues bool) (helm.Provider, error) { return newFakeHelmSPIREProvider(trustZone), nil } @@ -128,7 +128,7 @@ func (p *fakeHelmSPIREProvider) CheckIfAlreadyInstalled() (bool, error) { return false, nil } -func newFakeDataSource(t *testing.T, cfg *config.Config) plugin.DataSource { +func newFakeDataSource(t *testing.T, cfg *config.Config) datasource.DataSource { configLoader, err := config.NewMemoryLoader(cfg) require.Nil(t, err) lds, err := local.NewLocalDataSource(configLoader) diff --git a/pkg/provider/helm/values.go b/pkg/provider/helm/values.go index a6d8204..d70c581 100644 --- a/pkg/provider/helm/values.go +++ b/pkg/provider/helm/values.go @@ -11,11 +11,11 @@ import ( "github.com/cofide/cofidectl/internal/pkg/federation" "github.com/cofide/cofidectl/internal/pkg/trustprovider" "github.com/cofide/cofidectl/internal/pkg/trustzone" - cofidectl_plugin "github.com/cofide/cofidectl/pkg/plugin" + "github.com/cofide/cofidectl/pkg/plugin/datasource" ) type HelmValuesGenerator struct { - source cofidectl_plugin.DataSource + source datasource.DataSource trustZone *trust_zone_proto.TrustZone values map[string]any } @@ -54,7 +54,7 @@ type spiffeCSIDriverValues struct { fullnameOverride string } -func NewHelmValuesGenerator(trustZone *trust_zone_proto.TrustZone, source cofidectl_plugin.DataSource, values map[string]any) *HelmValuesGenerator { +func NewHelmValuesGenerator(trustZone *trust_zone_proto.TrustZone, source datasource.DataSource, values map[string]any) *HelmValuesGenerator { return &HelmValuesGenerator{ trustZone: trustZone, source: source, diff --git a/pkg/provider/helm/values_test.go b/pkg/provider/helm/values_test.go index 142317d..c7dc14d 100644 --- a/pkg/provider/helm/values_test.go +++ b/pkg/provider/helm/values_test.go @@ -11,7 +11,7 @@ import ( "github.com/cofide/cofidectl/internal/pkg/config" "github.com/cofide/cofidectl/internal/pkg/test/fixtures" "github.com/cofide/cofidectl/internal/pkg/trustprovider" - "github.com/cofide/cofidectl/pkg/plugin" + "github.com/cofide/cofidectl/pkg/plugin/datasource" "github.com/cofide/cofidectl/pkg/plugin/local" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -1271,7 +1271,7 @@ func TestSpiffeCSIDriverValues_GenerateValues(t *testing.T) { } } -func newFakeDataSource(t *testing.T, cfg *config.Config) plugin.DataSource { +func newFakeDataSource(t *testing.T, cfg *config.Config) datasource.DataSource { configLoader, err := config.NewMemoryLoader(cfg) require.Nil(t, err) lds, err := local.NewLocalDataSource(configLoader) From 638a8437a80f679fd7e4eb79a17d223ff90492f4 Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Wed, 11 Dec 2024 17:42:57 +0000 Subject: [PATCH 09/48] Add cofidectl-test-plugin This gRPC plugin implements data source and provision plugins using the default local and spire-helm plugins respectively. It allows for integration testing of the gRPC plugin mechanism. Fixes: #4 --- .gitignore | 1 + Justfile | 7 +++ cmd/cofidectl-test-plugin/main.go | 74 +++++++++++++++++++++++++++++++ 3 files changed, 82 insertions(+) create mode 100644 cmd/cofidectl-test-plugin/main.go diff --git a/.gitignore b/.gitignore index a698b80..2b6ac55 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ cofide.yaml # Plugins cofidectl-connect +cofidectl-test-plugin # cofide-demos repository demos/cofide-demos/ diff --git a/Justfile b/Justfile index 74f0b17..adadefa 100644 --- a/Justfile +++ b/Justfile @@ -1,6 +1,13 @@ build: test go build -o cofidectl ./cmd/cofidectl/main.go +build-test-plugin: + go build -o cofidectl-test-plugin ./cmd/cofidectl-test-plugin/main.go + +install-test-plugin: build-test-plugin + mkdir -p ~/.cofide/plugins + cp cofidectl-test-plugin ~/.cofide/plugins + test: go run gotest.tools/gotestsum@latest --format github-actions ./... diff --git a/cmd/cofidectl-test-plugin/main.go b/cmd/cofidectl-test-plugin/main.go new file mode 100644 index 0000000..1c73bb7 --- /dev/null +++ b/cmd/cofidectl-test-plugin/main.go @@ -0,0 +1,74 @@ +// Copyright 2024 Cofide Limited. +// SPDX-License-Identifier: Apache-2.0 + +package main + +import ( + "fmt" + "log" + "os" + + "github.com/cofide/cofidectl/internal/pkg/config" + cmdcontext "github.com/cofide/cofidectl/pkg/cmd/context" + "github.com/cofide/cofidectl/pkg/plugin" + "github.com/cofide/cofidectl/pkg/plugin/datasource" + "github.com/cofide/cofidectl/pkg/plugin/local" + "github.com/cofide/cofidectl/pkg/plugin/provision" + "github.com/cofide/cofidectl/pkg/plugin/provision/spirehelm" + "github.com/hashicorp/go-hclog" + go_plugin "github.com/hashicorp/go-plugin" +) + +const ( + cofideConfigFile = "cofide.yaml" +) + +func main() { + log.SetFlags(0) + if err := run(); err != nil { + // This should be the only place that calls os.Exit, to ensure proper clean up. + // This includes functions that call os.Exit, e.g. cobra.CheckErr, log.Fatal + os.Exit(1) + } +} + +func run() error { + cmdCtx := cmdcontext.NewCommandContext(cofideConfigFile) + defer cmdCtx.Shutdown() + go cmdCtx.HandleSignals() + + // If the CLI is invoked with arguments plugin serve, the gRPC plugins are served. + if plugin.IsPluginServeCmd(os.Args[1:]) { + return serveDataSource() + } + return fmt.Errorf("unexpected command arguments: %v", os.Args[1:]) +} + +func serveDataSource() error { + // go-plugin client expects logs on stdout. + log.SetOutput(os.Stdout) + + logger := hclog.New(&hclog.LoggerOptions{ + Output: os.Stderr, + // Log at trace level in the plugin, it will be filtered in the host. + Level: hclog.Trace, + JSONFormat: true, + }) + + lds, err := local.NewLocalDataSource(config.NewFileLoader(cofideConfigFile)) + if err != nil { + return err + } + spireHelm := spirehelm.NewSpireHelm(nil) + + go_plugin.Serve(&go_plugin.ServeConfig{ + HandshakeConfig: plugin.HandshakeConfig, + Plugins: map[string]go_plugin.Plugin{ + datasource.DataSourcePluginName: &datasource.DataSourcePlugin{Impl: lds}, + provision.ProvisionPluginName: &provision.ProvisionPlugin{Impl: spireHelm}, + }, + Logger: logger, + GRPCServer: go_plugin.DefaultGRPCServer, + }) + return nil +} From fc6eff190d2c78ba871ac5f0d8fa454609355524 Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Wed, 11 Dec 2024 17:44:06 +0000 Subject: [PATCH 10/48] Add a CI test that uses cofidectl-test-plugin in the federation scenario --- .github/workflows/integration.yml | 13 +++++++++++++ tests/integration/federation/test.sh | 18 ++++++++++++++++-- tests/integration/single-trust-zone/test.sh | 18 ++++++++++++++++-- 3 files changed, 45 insertions(+), 4 deletions(-) diff --git a/.github/workflows/integration.yml b/.github/workflows/integration.yml index 329cde4..5354542 100644 --- a/.github/workflows/integration.yml +++ b/.github/workflows/integration.yml @@ -41,6 +41,12 @@ jobs: federation: name: federation runs-on: ubuntu-latest + strategy: + matrix: + # Repeat the test with in-process plugins and the test gRPC plugin. + plugin: + - "" + - "cofidectl-test-plugin" steps: - name: Checkout uses: actions/checkout@v4 @@ -56,6 +62,10 @@ jobs: - name: Build and run tests run: just build + - name: Build and install test plugin + run: just install-test-plugin + if: ${{ matrix.plugin == 'cofidectl-test-plugin' }} + - name: Install kind run: just install-kind @@ -69,3 +79,6 @@ jobs: - name: Test run: just integration-test federation + env: + DATA_SOURCE_PLUGIN: ${{ matrix.plugin }} + PROVISION_PLUGIN: ${{ matrix.plugin }} diff --git a/tests/integration/federation/test.sh b/tests/integration/federation/test.sh index d89bad1..93299da 100755 --- a/tests/integration/federation/test.sh +++ b/tests/integration/federation/test.sh @@ -5,6 +5,9 @@ set -euxo pipefail +DATA_SOURCE_PLUGIN=${DATA_SOURCE_PLUGIN:-} +PROVISION_PLUGIN=${PROVISION_PLUGIN:-} + K8S_CLUSTER_1_NAME=${K8S_CLUSTER_1_NAME:-local1} K8S_CLUSTER_1_CONTEXT=${K8S_CLUSTER_1_CONTEXT:-kind-$K8S_CLUSTER_1_NAME} @@ -20,9 +23,19 @@ TRUST_DOMAIN_2=${TRUST_DOMAIN_2:-td2} NAMESPACE_POLICY_NAMESPACE=${NAMESPACE_POLICY_NAMESPACE:-demo} POD_POLICY_POD_LABEL=${POD_POLICY_POD_LABEL:-"foo=bar"} -function configure() { +function init() { rm -f cofide.yaml - ./cofidectl init + args="" + if [[ -n "$DATA_SOURCE_PLUGIN" ]]; then + args="$args --data-source-plugin $DATA_SOURCE_PLUGIN" + fi + if [[ -n "$PROVISION_PLUGIN" ]]; then + args="$args --provision-plugin $PROVISION_PLUGIN" + fi + ./cofidectl init $args +} + +function configure() { ./cofidectl trust-zone add $TRUST_ZONE_1 --trust-domain $TRUST_DOMAIN_1 --kubernetes-context $K8S_CLUSTER_1_CONTEXT --kubernetes-cluster $K8S_CLUSTER_1_NAME --profile kubernetes ./cofidectl trust-zone add $TRUST_ZONE_2 --trust-domain $TRUST_DOMAIN_2 --kubernetes-context $K8S_CLUSTER_2_CONTEXT --kubernetes-cluster $K8S_CLUSTER_2_NAME --profile kubernetes ./cofidectl federation add --from $TRUST_ZONE_1 --to $TRUST_ZONE_2 @@ -91,6 +104,7 @@ function down() { } function main() { + init configure up list_resources diff --git a/tests/integration/single-trust-zone/test.sh b/tests/integration/single-trust-zone/test.sh index 9012040..5737c96 100755 --- a/tests/integration/single-trust-zone/test.sh +++ b/tests/integration/single-trust-zone/test.sh @@ -5,6 +5,9 @@ set -euxo pipefail +DATA_SOURCE_PLUGIN=${DATA_SOURCE_PLUGIN:-} +PROVISION_PLUGIN=${PROVISION_PLUGIN:-} + K8S_CLUSTER_NAME=${K8S_CLUSTER_NAME:-local1} K8S_CLUSTER_CONTEXT=${K8S_CLUSTER_CONTEXT:-kind-$K8S_CLUSTER_NAME} @@ -14,9 +17,19 @@ TRUST_DOMAIN=${TRUST_DOMAIN:-td1} NAMESPACE_POLICY_NAMESPACE=${NAMESPACE_POLICY_NAMESPACE:-demo} POD_POLICY_POD_LABEL=${POD_POLICY_POD_LABEL:-"foo=bar"} -function configure() { +function init() { rm -f cofide.yaml - ./cofidectl init + args="" + if [[ -n "$DATA_SOURCE_PLUGIN" ]]; then + args="$args --data-source-plugin $DATA_SOURCE_PLUGIN" + fi + if [[ -n "$PROVISION_PLUGIN" ]]; then + args="$args --provision-plugin $PROVISION_PLUGIN" + fi + ./cofidectl init $args +} + +function configure() { ./cofidectl trust-zone add $TRUST_ZONE --trust-domain $TRUST_DOMAIN --kubernetes-context $K8S_CLUSTER_CONTEXT --kubernetes-cluster $K8S_CLUSTER_NAME --profile kubernetes ./cofidectl attestation-policy add kubernetes --name namespace --namespace $NAMESPACE_POLICY_NAMESPACE ./cofidectl attestation-policy add kubernetes --name pod-label --pod-label $POD_POLICY_POD_LABEL @@ -72,6 +85,7 @@ function down() { } function main() { + init configure up list_resources From cfd673157b62f0f9fe282a94d9b5147510b747af Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Fri, 13 Dec 2024 10:43:31 +0000 Subject: [PATCH 11/48] Fix defaultSvidName Helm value --- pkg/provider/helm/values.go | 4 ++-- pkg/provider/helm/values_test.go | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/pkg/provider/helm/values.go b/pkg/provider/helm/values.go index 2e80e2b..5f68be6 100644 --- a/pkg/provider/helm/values.go +++ b/pkg/provider/helm/values.go @@ -497,7 +497,7 @@ func getSDSConfig(profile string) (map[string]any, error) { // https://istio.io/latest/docs/ops/integrations/spire/#spiffe-federation return map[string]any{ "enabled": true, - "defaultSVIDName": "default", + "defaultSvidName": "default", "defaultBundleName": "null", "defaultAllBundlesName": "ROOTCA", }, nil @@ -505,7 +505,7 @@ func getSDSConfig(profile string) (map[string]any, error) { // https://github.com/spiffe/spire/blob/main/doc/spire_agent.md#sds-configuration return map[string]any{ "enabled": true, - "defaultSVIDName": "default", + "defaultSvidName": "default", "defaultBundleName": "ROOTCA", "defaultAllBundlesName": "ALL", }, nil diff --git a/pkg/provider/helm/values_test.go b/pkg/provider/helm/values_test.go index 3ff0b99..5250158 100644 --- a/pkg/provider/helm/values_test.go +++ b/pkg/provider/helm/values_test.go @@ -69,7 +69,7 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { }, "sds": map[string]any{ "enabled": true, - "defaultSVIDName": "default", + "defaultSvidName": "default", "defaultBundleName": "ROOTCA", "defaultAllBundlesName": "ALL", }, @@ -159,7 +159,7 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { }, "sds": map[string]any{ "enabled": true, - "defaultSVIDName": "default", + "defaultSvidName": "default", "defaultBundleName": "ROOTCA", "defaultAllBundlesName": "ALL", }, @@ -268,7 +268,7 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { }, "sds": map[string]any{ "enabled": true, - "defaultSVIDName": "default", + "defaultSvidName": "default", "defaultBundleName": "null", "defaultAllBundlesName": "ROOTCA", }, @@ -405,7 +405,7 @@ func TestHelmValuesGenerator_GenerateValues_AdditionalValues(t *testing.T) { }, "sds": map[string]any{ "enabled": true, - "defaultSVIDName": "default", + "defaultSvidName": "default", "defaultBundleName": "ROOTCA", "defaultAllBundlesName": "ALL", }, @@ -1087,7 +1087,7 @@ func TestSpireAgentValues_GenerateValues(t *testing.T) { }, sdsConfig: map[string]any{ "enabled": true, - "defaultSVIDName": "default", + "defaultSvidName": "default", "defaultBundleName": "ROOTCA", "defaultAllBundlesName": "ALL", }, @@ -1104,7 +1104,7 @@ func TestSpireAgentValues_GenerateValues(t *testing.T) { }, "sds": map[string]any{ "enabled": true, - "defaultSVIDName": "default", + "defaultSvidName": "default", "defaultBundleName": "ROOTCA", "defaultAllBundlesName": "ALL", }, @@ -1143,7 +1143,7 @@ func TestSpireAgentValues_GenerateValues(t *testing.T) { }, sdsConfig: map[string]any{ "enabled": true, - "defaultSVIDName": "default", + "defaultSvidName": "default", "defaultBundleName": "ROOTCA", "defaultAllBundlesName": "ALL", }, @@ -1167,7 +1167,7 @@ func TestSpireAgentValues_GenerateValues(t *testing.T) { }, sdsConfig: map[string]any{ "enabled": true, - "defaultSVIDName": "default", + "defaultSvidName": "default", "defaultBundleName": "ROOTCA", "defaultAllBundlesName": "ALL", }, @@ -1197,7 +1197,7 @@ func TestSpireAgentValues_GenerateValues(t *testing.T) { }, sdsConfig: map[string]any{ "enabled": true, - "defaultSVIDName": "default", + "defaultSvidName": "default", "defaultBundleName": "ROOTCA", "defaultAllBundlesName": "ALL", }, @@ -1468,7 +1468,7 @@ func TestGetSDSConfig(t *testing.T) { profile: "kubernetes", want: map[string]any{ "enabled": true, - "defaultSVIDName": "default", + "defaultSvidName": "default", "defaultBundleName": "ROOTCA", "defaultAllBundlesName": "ALL", }, @@ -1479,7 +1479,7 @@ func TestGetSDSConfig(t *testing.T) { profile: "istio", want: map[string]any{ "enabled": true, - "defaultSVIDName": "default", + "defaultSvidName": "default", "defaultBundleName": "null", "defaultAllBundlesName": "ROOTCA", }, From f43e4751cd66d689d73ff754e0a4919da0d4e4c5 Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Fri, 13 Dec 2024 11:28:43 +0000 Subject: [PATCH 12/48] Add kubeCfgFile to TearDown Provision interface method This is required by some external provision plugins. --- cmd/cofidectl/cmd/down.go | 2 +- go.mod | 2 +- go.sum | 4 ++-- pkg/plugin/provision/interface.go | 2 +- pkg/plugin/provision/plugin.go | 6 +++--- pkg/plugin/provision/spirehelm/spirehelm.go | 2 +- pkg/plugin/provision/spirehelm/spirehelm_test.go | 2 +- 7 files changed, 10 insertions(+), 10 deletions(-) diff --git a/cmd/cofidectl/cmd/down.go b/cmd/cofidectl/cmd/down.go index 623453f..b8adc17 100644 --- a/cmd/cofidectl/cmd/down.go +++ b/cmd/cofidectl/cmd/down.go @@ -38,7 +38,7 @@ func (d *DownCommand) DownCmd() *cobra.Command { if err != nil { return err } - statusCh, err := provision.TearDown(cmd.Context(), ds) + statusCh, err := provision.TearDown(cmd.Context(), ds, kubeCfgFile) if err != nil { return err } diff --git a/go.mod b/go.mod index 46e6610..5366660 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.22.7 require ( buf.build/go/protoyaml v0.2.0 cuelang.org/go v0.10.1 - github.com/cofide/cofide-api-sdk v0.4.1-0.20241212134830-527e2164f012 + github.com/cofide/cofide-api-sdk v0.4.1-0.20241213113045-864bfb581c91 github.com/fatih/color v1.18.0 github.com/gofrs/flock v0.12.1 github.com/google/go-cmp v0.6.0 diff --git a/go.sum b/go.sum index 4b04b27..e40800c 100644 --- a/go.sum +++ b/go.sum @@ -84,8 +84,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.4.1-0.20241212134830-527e2164f012 h1:XJe+gZeK8YFYULInSQ10+BApaO872hPxZSp7ofJjyoU= -github.com/cofide/cofide-api-sdk v0.4.1-0.20241212134830-527e2164f012/go.mod h1:u2iATR4IbZm9ruIBN734UjVuO3XQKPAFViIY3Xr6kTA= +github.com/cofide/cofide-api-sdk v0.4.1-0.20241213113045-864bfb581c91 h1:We5sn+o74dce2gkO63ola8RFSD92jsud3pAIwVbzAq8= +github.com/cofide/cofide-api-sdk v0.4.1-0.20241213113045-864bfb581c91/go.mod h1:u2iATR4IbZm9ruIBN734UjVuO3XQKPAFViIY3Xr6kTA= 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.23 h1:H2CClyUkmpKAGlhQp95g2WXHfLYc7whAuvZGBNYOOwQ= diff --git a/pkg/plugin/provision/interface.go b/pkg/plugin/provision/interface.go index 139a38e..aae5897 100644 --- a/pkg/plugin/provision/interface.go +++ b/pkg/plugin/provision/interface.go @@ -23,5 +23,5 @@ type Provision interface { // TearDown tears down the workload identity configuration from the clusters in the system. // The method is asynchronous, returning a channel over which Status messages are sent // describing the various stages of tear down and their outcomes. - TearDown(ctx context.Context, ds datasource.DataSource) (<-chan *provisionpb.Status, error) + TearDown(ctx context.Context, ds datasource.DataSource, kubeCfgFile string) (<-chan *provisionpb.Status, error) } diff --git a/pkg/plugin/provision/plugin.go b/pkg/plugin/provision/plugin.go index ea57460..f03ea24 100644 --- a/pkg/plugin/provision/plugin.go +++ b/pkg/plugin/provision/plugin.go @@ -82,10 +82,10 @@ func (c *ProvisionPluginClientGRPC) Deploy(ctx context.Context, source datasourc return statusCh, nil } -func (c *ProvisionPluginClientGRPC) TearDown(ctx context.Context, source datasource.DataSource) (<-chan *provisionpb.Status, error) { +func (c *ProvisionPluginClientGRPC) TearDown(ctx context.Context, source datasource.DataSource, kubeCfgFile string) (<-chan *provisionpb.Status, error) { server, brokerID := c.startDataSourceServer(source) - req := provisionpb.TearDownRequest{DataSource: &brokerID} + req := provisionpb.TearDownRequest{DataSource: &brokerID, KubeCfgFile: &kubeCfgFile} stream, err := c.client.TearDown(ctx, &req) if err != nil { err := wrapError(err) @@ -202,7 +202,7 @@ func (s *GRPCServer) TearDown(req *provisionpb.TearDownRequest, stream grpc.Serv } defer conn.Close() - statusCh, err := s.impl.TearDown(stream.Context(), client) + statusCh, err := s.impl.TearDown(stream.Context(), client, req.GetKubeCfgFile()) if err != nil { return err } diff --git a/pkg/plugin/provision/spirehelm/spirehelm.go b/pkg/plugin/provision/spirehelm/spirehelm.go index 78564f4..648aaa3 100644 --- a/pkg/plugin/provision/spirehelm/spirehelm.go +++ b/pkg/plugin/provision/spirehelm/spirehelm.go @@ -52,7 +52,7 @@ func (h *SpireHelm) Deploy(ctx context.Context, ds datasource.DataSource, kubeCf return statusCh, nil } -func (h *SpireHelm) TearDown(ctx context.Context, ds datasource.DataSource) (<-chan *provisionpb.Status, error) { +func (h *SpireHelm) TearDown(ctx context.Context, ds datasource.DataSource, kubeCfgFile string) (<-chan *provisionpb.Status, error) { statusCh := make(chan *provisionpb.Status) go func() { diff --git a/pkg/plugin/provision/spirehelm/spirehelm_test.go b/pkg/plugin/provision/spirehelm/spirehelm_test.go index 74c8465..b539b9e 100644 --- a/pkg/plugin/provision/spirehelm/spirehelm_test.go +++ b/pkg/plugin/provision/spirehelm/spirehelm_test.go @@ -55,7 +55,7 @@ func TestSpireHelm_TearDown(t *testing.T) { spireHelm := NewSpireHelm(providerFactory) ds := newFakeDataSource(t, defaultConfig()) - statusCh, err := spireHelm.TearDown(context.Background(), ds) + statusCh, err := spireHelm.TearDown(context.Background(), ds, "fake-kube.cfg") require.NoError(t, err, err) statuses := collectStatuses(statusCh) From bdfb4aa3ebbf09df82d8f64677c702bbc8d79578 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2024 01:25:25 +0000 Subject: [PATCH 13/48] Bump github.com/spiffe/spire-api-sdk from 1.11.0 to 1.11.1 Bumps [github.com/spiffe/spire-api-sdk](https://github.com/spiffe/spire-api-sdk) from 1.11.0 to 1.11.1. - [Commits](https://github.com/spiffe/spire-api-sdk/compare/v1.11.0...v1.11.1) --- updated-dependencies: - dependency-name: github.com/spiffe/spire-api-sdk dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index 9557bd5..e9cfe03 100644 --- a/go.mod +++ b/go.mod @@ -13,7 +13,7 @@ require ( github.com/manifoldco/promptui v0.9.0 github.com/spf13/cobra v1.8.1 github.com/spiffe/go-spiffe/v2 v2.4.0 - github.com/spiffe/spire-api-sdk v1.11.0 + github.com/spiffe/spire-api-sdk v1.11.1 github.com/stretchr/testify v1.10.0 google.golang.org/grpc v1.68.1 google.golang.org/protobuf v1.35.2 diff --git a/go.sum b/go.sum index 5fc3b34..0eab82e 100644 --- a/go.sum +++ b/go.sum @@ -436,8 +436,8 @@ github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spiffe/go-spiffe/v2 v2.4.0 h1:j/FynG7hi2azrBG5cvjRcnQ4sux/VNj8FAVc99Fl66c= github.com/spiffe/go-spiffe/v2 v2.4.0/go.mod h1:m5qJ1hGzjxjtrkGHZupoXHo/FDWwCB1MdSyBzfHugx0= -github.com/spiffe/spire-api-sdk v1.11.0 h1:9t46NLWGEaOKwWb95nhOaezAUSTBJqW5Lx7C3uK8L4M= -github.com/spiffe/spire-api-sdk v1.11.0/go.mod h1:4uuhFlN6KBWjACRP3xXwrOTNnvaLp1zJs8Lribtr4fI= +github.com/spiffe/spire-api-sdk v1.11.1 h1:s5RWwBszfMYsRQTGeB6p93NfYBuwHP0tjHFEj5LHun0= +github.com/spiffe/spire-api-sdk v1.11.1/go.mod h1:4uuhFlN6KBWjACRP3xXwrOTNnvaLp1zJs8Lribtr4fI= github.com/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= From 440afc49bf2a406d91280a67f8f8727483dc1845 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2024 01:25:35 +0000 Subject: [PATCH 14/48] Bump buf.build/go/protoyaml from 0.2.0 to 0.3.0 Bumps [buf.build/go/protoyaml](https://github.com/bufbuild/protoyaml-go) from 0.2.0 to 0.3.0. - [Release notes](https://github.com/bufbuild/protoyaml-go/releases) - [Commits](https://github.com/bufbuild/protoyaml-go/compare/v0.2.0...v0.3.0) --- updated-dependencies: - dependency-name: buf.build/go/protoyaml dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 10 ++++++---- go.sum | 18 ++++++++++-------- 2 files changed, 16 insertions(+), 12 deletions(-) diff --git a/go.mod b/go.mod index 9557bd5..224a613 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/cofide/cofidectl go 1.22.7 require ( - buf.build/go/protoyaml v0.2.0 + buf.build/go/protoyaml v0.3.0 cuelang.org/go v0.10.1 github.com/cofide/cofide-api-sdk v0.3.1-0.20241211140859-1388b27cdec8 github.com/fatih/color v1.18.0 @@ -21,11 +21,13 @@ require ( helm.sh/helm/v3 v3.16.3 ) +require cel.dev/expr v0.18.0 // indirect + // Uncomment the following for development with local Cofide API SDK changes: //replace github.com/cofide/cofide-api-sdk => ../cofide-api-sdk require ( - buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.34.2-20240717164558-a6c49f84cc0f.2 // indirect + buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.35.2-20241127180247-a33202765966.1 // 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 @@ -40,7 +42,7 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/briandowns/spinner v1.23.1 - github.com/bufbuild/protovalidate-go v0.6.3 // indirect + github.com/bufbuild/protovalidate-go v0.8.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/chai2010/gettext-go v1.0.2 // indirect github.com/chzyer/readline v1.5.1 // indirect @@ -74,7 +76,7 @@ require ( github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/protobuf v1.5.4 // indirect github.com/google/btree v1.0.1 // indirect - github.com/google/cel-go v0.21.0 // indirect + github.com/google/cel-go v0.22.1 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect diff --git a/go.sum b/go.sum index 5fc3b34..82745e5 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,9 @@ -buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.34.2-20240717164558-a6c49f84cc0f.2 h1:SZRVx928rbYZ6hEKUIN+vtGDkl7uotABRWGY4OAg5gM= -buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.34.2-20240717164558-a6c49f84cc0f.2/go.mod h1:ylS4c28ACSI59oJrOdW4pHS4n0Hw4TgSPHn8rpHl4Yw= -buf.build/go/protoyaml v0.2.0 h1:2g3OHjtLDqXBREIOjpZGHmQ+U/4mkN1YiQjxNB68Ip8= -buf.build/go/protoyaml v0.2.0/go.mod h1:L/9QvTDkTWcDTzAL6HMfN+mYC6CmZRm2KnsUA054iL0= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.35.2-20241127180247-a33202765966.1 h1:jLd96rDDNJ+zIJxvV/L855VEtrjR0G4aePVDlCpf6kw= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.35.2-20241127180247-a33202765966.1/go.mod h1:mnHCFccv4HwuIAOHNGdiIc5ZYbBCvbTWZcodLN5wITI= +buf.build/go/protoyaml v0.3.0 h1:NF/C1r0ezdUm8J3ABJsZqbsOVpT/Igrg2b//QPAIieo= +buf.build/go/protoyaml v0.3.0/go.mod h1:gaDlo8lOhxsvsBqblrvMFaVOcKVS1RQya8SGFB5XIxU= +cel.dev/expr v0.18.0 h1:CJ6drgk+Hf96lkLikr4rFf19WrU0BOWEihyZnI2TAzo= +cel.dev/expr v0.18.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= @@ -56,8 +58,8 @@ github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuP github.com/bshuster-repo/logrus-logstash-hook v1.0.0/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= github.com/bufbuild/protocompile v0.4.0 h1:LbFKd2XowZvQ/kajzguUp2DC9UEIQhIq77fZZlaQsNA= github.com/bufbuild/protocompile v0.4.0/go.mod h1:3v93+mbWn/v3xzN+31nwkJfrEpAUwp+BagBSZWx+TP8= -github.com/bufbuild/protovalidate-go v0.6.3 h1:wxQyzW035zM16Binbaz/nWAzS12dRIXhZdSUWRY7Fv0= -github.com/bufbuild/protovalidate-go v0.6.3/go.mod h1:J4PtwP9Z2YAGgB0+o+tTWEDtLtXvz/gfhFZD8pbzM/U= +github.com/bufbuild/protovalidate-go v0.8.0 h1:Xs3kCLCJ4tQiogJ0iOXm+ClKw/KviW3nLAryCGW2I3Y= +github.com/bufbuild/protovalidate-go v0.8.0/go.mod h1:JPWZInGm2y2NBg3vKDKdDIkvDjyLv31J3hLH5GIFc/Q= 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= @@ -213,8 +215,8 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -github.com/google/cel-go v0.21.0 h1:cl6uW/gxN+Hy50tNYvI691+sXxioCnstFzLp2WO4GCI= -github.com/google/cel-go v0.21.0/go.mod h1:rHUlWCcBKgyEk+eV03RPdZUekPp6YcJwV0FxuUksYxc= +github.com/google/cel-go v0.22.1 h1:AfVXx3chM2qwoSbM7Da8g8hX8OVSkBFwX+rz2+PcK40= +github.com/google/cel-go v0.22.1/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= From d71264b54fe42947de3836dde69261ac760f1f80 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2024 09:50:55 +0000 Subject: [PATCH 15/48] Bump google.golang.org/grpc from 1.68.1 to 1.69.0 Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.68.1 to 1.69.0. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.68.1...v1.69.0) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 13 ++++++------- go.sum | 32 ++++++++++++++++---------------- 2 files changed, 22 insertions(+), 23 deletions(-) diff --git a/go.mod b/go.mod index 98661ba..346d55f 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,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.68.1 + google.golang.org/grpc v1.69.0 google.golang.org/protobuf v1.35.2 gopkg.in/yaml.v3 v3.0.1 helm.sh/helm/v3 v3.16.3 @@ -142,12 +142,11 @@ 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.28.0 // indirect + go.opentelemetry.io/otel v1.31.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.28.0 // indirect - go.opentelemetry.io/otel/sdk/metric v1.27.0 // indirect - go.opentelemetry.io/otel/trace v1.28.0 // indirect + go.opentelemetry.io/otel/metric v1.31.0 // indirect + go.opentelemetry.io/otel/trace v1.31.0 // indirect go.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect golang.org/x/crypto v0.31.0 // indirect golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect @@ -159,8 +158,8 @@ require ( golang.org/x/term v0.27.0 // indirect golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.3.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // 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 gopkg.in/evanphx/json-patch.v4 v4.12.0 // 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 d85dfd8..0b910d4 100644 --- a/go.sum +++ b/go.sum @@ -478,8 +478,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.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= -go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +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/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= @@ -496,14 +496,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.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= -go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= -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.27.0 h1:5uGNOlpXi+Hbo/DRoI31BSb1v+OGcpv2NemcCrOL8gI= -go.opentelemetry.io/otel/sdk/metric v1.27.0/go.mod h1:we7jJVrYN2kh3mVBlswtPU22K0SA+769l93J6bsyvqw= -go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= -go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= +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/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= @@ -605,10 +605,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-20240903143218-8af14fe29dc1 h1:hjSy6tcFQZ171igDaN5QHOw2n6vx40juYbC/x67CEhc= -google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= +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/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= @@ -616,8 +616,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.68.1 h1:oI5oTa11+ng8r8XMMN7jAOmWfPZWbYpCFaMUTACxkM0= -google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= +google.golang.org/grpc v1.69.0 h1:quSiOM1GJPmPH5XtU+BCoVXcDVJJAzNcoyfC2cCjGkI= +google.golang.org/grpc v1.69.0/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= 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= From 9dedc4df72468bc0abb2ffff612c06984e6cb48c Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 16 Dec 2024 09:58:27 +0000 Subject: [PATCH 16/48] Bump k8s.io/client-go from 0.31.3 to 0.32.0 Bumps [k8s.io/client-go](https://github.com/kubernetes/client-go) from 0.31.3 to 0.32.0. - [Changelog](https://github.com/kubernetes/client-go/blob/master/CHANGELOG.md) - [Commits](https://github.com/kubernetes/client-go/compare/v0.31.3...v0.32.0) --- updated-dependencies: - dependency-name: k8s.io/client-go dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 22 +++++++++++----------- go.sum | 59 ++++++++++++++++++++++++++++------------------------------ 2 files changed, 39 insertions(+), 42 deletions(-) diff --git a/go.mod b/go.mod index 346d55f..37cde58 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,7 @@ module github.com/cofide/cofidectl go 1.22.7 +toolchain go1.23.4 require ( buf.build/go/protoyaml v0.3.0 @@ -84,13 +85,12 @@ require ( github.com/gorilla/mux v1.8.1 // indirect github.com/gorilla/websocket v1.5.0 // indirect github.com/gosuri/uitable v0.0.4 // indirect - github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 // indirect + github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-hclog v1.6.3 github.com/hashicorp/go-multierror v1.1.1 // indirect github.com/hashicorp/yamux v0.1.1 // indirect github.com/huandu/xstrings v1.5.0 // indirect - github.com/imdario/mergo v0.3.16 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/jmoiron/sqlx v1.4.0 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -109,7 +109,7 @@ require ( github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect github.com/moby/locker v1.0.1 // indirect - github.com/moby/spdystream v0.4.0 // indirect + github.com/moby/spdystream v0.5.0 // indirect github.com/moby/term v0.5.0 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect @@ -157,28 +157,28 @@ require ( golang.org/x/sys v0.28.0 // indirect golang.org/x/term v0.27.0 // indirect golang.org/x/text v0.21.0 // indirect - golang.org/x/time v0.3.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 gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/yaml.v2 v2.4.0 // indirect gotest.tools/v3 v3.5.0 // indirect - k8s.io/api v0.31.3 + k8s.io/api v0.32.0 k8s.io/apiextensions-apiserver v0.31.1 // indirect - k8s.io/apimachinery v0.31.3 + k8s.io/apimachinery v0.32.0 k8s.io/apiserver v0.31.1 // indirect k8s.io/cli-runtime v0.31.3 // indirect - k8s.io/client-go v0.31.3 + k8s.io/client-go v0.32.0 k8s.io/component-base v0.31.3 // indirect k8s.io/klog/v2 v2.130.1 // indirect - k8s.io/kube-openapi v0.0.0-20240423202451-8948a665c108 // indirect + k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect k8s.io/kubectl v0.31.3 - k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 // indirect + k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect oras.land/oras-go v1.2.6 // indirect - sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect + sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect sigs.k8s.io/kustomize/api v0.17.2 // indirect sigs.k8s.io/kustomize/kyaml v0.17.1 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect + sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index 0b910d4..5174d59 100644 --- a/go.sum +++ b/go.sum @@ -233,8 +233,8 @@ github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= -github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af h1:kmjWCqn2qkEml422C2Rrd27c3VGxi6a/6HNq8QmHRKM= -github.com/google/pprof v0.0.0-20240525223248-4bfdf5a9a2af/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo= +github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= @@ -248,8 +248,8 @@ github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWm github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= 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-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +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 v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= @@ -272,8 +272,6 @@ github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ= github.com/huandu/xstrings v1.5.0 h1:2ag3IFq9ZDANvthTwTiqSSZLjDc+BedvHPAp5tJy2TI= github.com/huandu/xstrings v1.5.0/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq4ovT0aE= -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/jhump/protoreflect v1.15.1 h1:HUMERORf3I3ZdX05WaQ6MIpd/NJ434hTp5YiKgfCL6c= @@ -337,8 +335,8 @@ github.com/mitchellh/reflectwalk v1.0.2 h1:G2LzWKi524PWgd3mLHV8Y5k7s6XUvT0Gef6zx github.com/mitchellh/reflectwalk v1.0.2/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw= 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/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8= -github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= +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/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= @@ -363,10 +361,10 @@ github.com/oklog/run v1.1.0 h1:GEenZ1cK0+q0+wsJew9qUg/DyD8k3JzYsZAi5gYi2mA= github.com/oklog/run v1.1.0/go.mod h1:sVPdnTZT1zYwAJeCMu2Th4T21pA3FPOQRfWjQlk7DVU= github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= -github.com/onsi/ginkgo/v2 v2.19.0 h1:9Cnnf7UHo57Hy3k6/m5k3dRfGTMXGvxhHFvkDTCTpvA= -github.com/onsi/ginkgo/v2 v2.19.0/go.mod h1:rlwLi9PilAFJ8jCg9UE1QP6VBpd6/xj3SRC0d6TU0To= -github.com/onsi/gomega v1.33.1 h1:dsYjIxxSR755MDmKVsaFQTE22ChNBcuuTWgkUDSubOk= -github.com/onsi/gomega v1.33.1/go.mod h1:U4R44UsT+9eLIaYRB2a5qajjtQYn0hauxvRm16AVYg0= +github.com/onsi/ginkgo/v2 v2.21.0 h1:7rg/4f3rB88pb5obDgNZrNHrQ4e6WpjonchcpuBRnZM= +github.com/onsi/ginkgo/v2 v2.21.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= +github.com/onsi/gomega v1.35.1 h1:Cwbd75ZBPxFSuZ6T+rN/WCb/gOc6YgFBXLlZLhC7Ds4= +github.com/onsi/gomega v1.35.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= 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= @@ -583,8 +581,8 @@ golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo= golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ= +golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= @@ -593,8 +591,8 @@ golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= -golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= +golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ= +golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= 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= @@ -645,7 +643,6 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= @@ -657,37 +654,37 @@ helm.sh/helm/v3 v3.16.3 h1:kb8bSxMeRJ+knsK/ovvlaVPfdis0X3/ZhYCSFRP+YmY= helm.sh/helm/v3 v3.16.3/go.mod h1:zeVWGDR4JJgiRbT3AnNsjYaX8OTJlIE9zC+Q7F7iUSU= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= -k8s.io/api v0.31.3 h1:umzm5o8lFbdN/hIXbrK9oRpOproJO62CV1zqxXrLgk8= -k8s.io/api v0.31.3/go.mod h1:UJrkIp9pnMOI9K2nlL6vwpxRzzEX5sWgn8kGQe92kCE= +k8s.io/api v0.32.0 h1:OL9JpbvAU5ny9ga2fb24X8H6xQlVp+aJMFlgtQjR9CE= +k8s.io/api v0.32.0/go.mod h1:4LEwHZEf6Q/cG96F3dqR965sYOfmPM7rq81BLgsE0p0= k8s.io/apiextensions-apiserver v0.31.1 h1:L+hwULvXx+nvTYX/MKM3kKMZyei+UiSXQWciX/N6E40= k8s.io/apiextensions-apiserver v0.31.1/go.mod h1:tWMPR3sgW+jsl2xm9v7lAyRF1rYEK71i9G5dRtkknoQ= -k8s.io/apimachinery v0.31.3 h1:6l0WhcYgasZ/wk9ktLq5vLaoXJJr5ts6lkaQzgeYPq4= -k8s.io/apimachinery v0.31.3/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo= +k8s.io/apimachinery v0.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg= +k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= k8s.io/apiserver v0.31.1 h1:Sars5ejQDCRBY5f7R3QFHdqN3s61nhkpaX8/k1iEw1c= k8s.io/apiserver v0.31.1/go.mod h1:lzDhpeToamVZJmmFlaLwdYZwd7zB+WYRYIboqA1kGxM= k8s.io/cli-runtime v0.31.3 h1:fEQD9Xokir78y7pVK/fCJN090/iYNrLHpFbGU4ul9TI= k8s.io/cli-runtime v0.31.3/go.mod h1:Q2jkyTpl+f6AtodQvgDI8io3jrfr+Z0LyQBPJJ2Btq8= -k8s.io/client-go v0.31.3 h1:CAlZuM+PH2cm+86LOBemaJI/lQ5linJ6UFxKX/SoG+4= -k8s.io/client-go v0.31.3/go.mod h1:2CgjPUTpv3fE5dNygAr2NcM8nhHzXvxB8KL5gYc3kJs= +k8s.io/client-go v0.32.0 h1:DimtMcnN/JIKZcrSrstiwvvZvLjG0aSxy8PxN8IChp8= +k8s.io/client-go v0.32.0/go.mod h1:boDWvdM1Drk4NJj/VddSLnx59X3OPgwrOo0vGbtq9+8= k8s.io/component-base v0.31.3 h1:DMCXXVx546Rfvhj+3cOm2EUxhS+EyztH423j+8sOwhQ= k8s.io/component-base v0.31.3/go.mod h1:xME6BHfUOafRgT0rGVBGl7TuSg8Z9/deT7qq6w7qjIU= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= -k8s.io/kube-openapi v0.0.0-20240423202451-8948a665c108 h1:Q8Z7VlGhcJgBHJHYugJ/K/7iB8a2eSxCyxdVjJp+lLY= -k8s.io/kube-openapi v0.0.0-20240423202451-8948a665c108/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98= +k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= +k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4= k8s.io/kubectl v0.31.3 h1:3r111pCjPsvnR98oLLxDMwAeM6OPGmPty6gSKaLTQes= k8s.io/kubectl v0.31.3/go.mod h1:lhMECDCbJN8He12qcKqs2QfmVo9Pue30geovBVpH5fs= -k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A= -k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= +k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= oras.land/oras-go v1.2.6 h1:z8cmxQXBU8yZ4mkytWqXfo6tZcamPwjsuxYU81xJ8Lk= oras.land/oras-go v1.2.6/go.mod h1:OVPc1PegSEe/K8YiLfosrlqlqTN9PUyFvOw5Y9gwrT8= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo= -sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= +sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= sigs.k8s.io/kustomize/api v0.17.2 h1:E7/Fjk7V5fboiuijoZHgs4aHuexi5Y2loXlVOAVAG5g= sigs.k8s.io/kustomize/api v0.17.2/go.mod h1:UWTz9Ct+MvoeQsHcJ5e+vziRRkwimm3HytpZgIYqye0= sigs.k8s.io/kustomize/kyaml v0.17.1 h1:TnxYQxFXzbmNG6gOINgGWQt09GghzgTP6mIurOgrLCQ= sigs.k8s.io/kustomize/kyaml v0.17.1/go.mod h1:9V0mCjIEYjlXuCdYsSXvyoy2BTsLESH7TlGV81S282U= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4= -sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08= +sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA= +sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= From b6aceb8fecd66e63e48bbacd9647aa9af5fb4586 Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Mon, 16 Dec 2024 10:03:15 +0000 Subject: [PATCH 17/48] Bump minimum go version to 1.23.0 in go.mod This is the result of 'go mod tidy' after bumping k8s client-go to 0.32.0. --- go.mod | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 37cde58..f091d5f 100644 --- a/go.mod +++ b/go.mod @@ -1,6 +1,7 @@ module github.com/cofide/cofidectl -go 1.22.7 +go 1.23.0 + toolchain go1.23.4 require ( From 164ae6c0a9a3cc762fb9d90fa9e853c154c70661 Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Mon, 16 Dec 2024 11:41:01 +0000 Subject: [PATCH 18/48] Make ping pong success check more strict Fixes: #90 --- tests/integration/federation/test.sh | 2 +- tests/integration/single-trust-zone/test.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/integration/federation/test.sh b/tests/integration/federation/test.sh index d89bad1..45d48fc 100755 --- a/tests/integration/federation/test.sh +++ b/tests/integration/federation/test.sh @@ -71,7 +71,7 @@ function run_tests() { function wait_for_pong() { for i in $(seq 30); do - if kubectl --context $K8S_CLUSTER_1_CONTEXT logs -n demo deployments/ping-pong-client | grep pong; then + if kubectl --context $K8S_CLUSTER_1_CONTEXT logs -n demo deployments/ping-pong-client | grep '\.\.\.pong'; then return 0 fi sleep 2 diff --git a/tests/integration/single-trust-zone/test.sh b/tests/integration/single-trust-zone/test.sh index 1d27030..51575e2 100755 --- a/tests/integration/single-trust-zone/test.sh +++ b/tests/integration/single-trust-zone/test.sh @@ -59,7 +59,7 @@ function run_tests() { function wait_for_pong() { for i in $(seq 30); do - if kubectl --context $K8S_CLUSTER_CONTEXT logs -n demo deployments/ping-pong-client | grep pong; then + if kubectl --context $K8S_CLUSTER_CONTEXT logs -n demo deployments/ping-pong-client | grep '\.\.\.pong'; then return 0 fi sleep 2 From 9680e7a96b5cdf753949755020fa7acac943d4a3 Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Mon, 16 Dec 2024 13:43:18 +0000 Subject: [PATCH 19/48] Address code review feedback --- cmd/cofidectl/cmd/down.go | 1 + cmd/cofidectl/cmd/up.go | 1 + internal/pkg/proto/proto.go | 2 +- pkg/plugin/provision/plugin.go | 3 +-- 4 files changed, 4 insertions(+), 3 deletions(-) diff --git a/cmd/cofidectl/cmd/down.go b/cmd/cofidectl/cmd/down.go index 623453f..6f8537d 100644 --- a/cmd/cofidectl/cmd/down.go +++ b/cmd/cofidectl/cmd/down.go @@ -38,6 +38,7 @@ func (d *DownCommand) DownCmd() *cobra.Command { if err != nil { return err } + statusCh, err := provision.TearDown(cmd.Context(), ds) if err != nil { return err diff --git a/cmd/cofidectl/cmd/up.go b/cmd/cofidectl/cmd/up.go index 171adcc..83f0f85 100644 --- a/cmd/cofidectl/cmd/up.go +++ b/cmd/cofidectl/cmd/up.go @@ -44,6 +44,7 @@ func (u *UpCommand) UpCmd() *cobra.Command { if err != nil { return err } + statusCh, err := provision.Deploy(cmd.Context(), ds, kubeCfgFile) if err != nil { return err diff --git a/internal/pkg/proto/proto.go b/internal/pkg/proto/proto.go index 55580de..b9d2eaa 100644 --- a/internal/pkg/proto/proto.go +++ b/internal/pkg/proto/proto.go @@ -54,7 +54,7 @@ func CloneFederation(federation *federation_proto.Federation) (*federation_proto return nil, fmt.Errorf("bug: type assertion failed for federation %s-%s", federation.From, federation.To) } else { if clone == federation { - return nil, fmt.Errorf("bug: federation %s-%s clones are the same", federation.To, federation.To) + return nil, fmt.Errorf("bug: federation %s-%s clones are the same", federation.From, federation.To) } return clone, nil } diff --git a/pkg/plugin/provision/plugin.go b/pkg/plugin/provision/plugin.go index 6366a31..285a107 100644 --- a/pkg/plugin/provision/plugin.go +++ b/pkg/plugin/provision/plugin.go @@ -150,9 +150,8 @@ func wrapError(err error) error { } if status, ok := status.FromError(err); ok { return &clientError{status: status} - } else { - return err } + return err } func (ce *clientError) Error() string { From 977f457bbeef1cac9270d0e1c68bdfc6f151d642 Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Mon, 16 Dec 2024 13:45:55 +0000 Subject: [PATCH 20/48] Address code review feedback - gRPC capitalisation --- pkg/plugin/manager/manager.go | 18 ++++++++--------- pkg/plugin/manager/manager_test.go | 32 +++++++++++++++--------------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/pkg/plugin/manager/manager.go b/pkg/plugin/manager/manager.go index 5e10e9e..f1a0725 100644 --- a/pkg/plugin/manager/manager.go +++ b/pkg/plugin/manager/manager.go @@ -58,7 +58,7 @@ type grpcPlugin struct { func NewManager(configLoader config.Loader) *PluginManager { return &PluginManager{ configLoader: configLoader, - grpcPluginLoader: loadGrpcPlugin, + grpcPluginLoader: loadGRPCPlugin, clients: map[string]*go_plugin.Client{}, } } @@ -142,7 +142,7 @@ func (pm *PluginManager) loadDataSource(ctx context.Context) (cofidectl_plugin.D return pm.source, nil } - if err := pm.loadGrpcPlugin(ctx, dsName, cfg.Plugins); err != nil { + if err := pm.loadGRPCPlugin(ctx, dsName, cfg.Plugins); err != nil { return nil, err } return pm.source, nil @@ -182,17 +182,17 @@ func (pm *PluginManager) loadProvision(ctx context.Context) (provision.Provision return pm.provision, nil } - if err := pm.loadGrpcPlugin(ctx, provisionName, cfg.Plugins); err != nil { + if err := pm.loadGRPCPlugin(ctx, provisionName, cfg.Plugins); err != nil { return nil, err } return pm.provision, nil } -// loadGrpcPlugin loads a gRPC plugin. +// loadGRPCPlugin loads a gRPC plugin. // The gRPC plugin may provide one or more cofidectl plugins (e.g. data source, provision), and // all cofidectl plugins configured to use this gRPC plugin will be loaded in a single plugin // client and server process. -func (pm *PluginManager) loadGrpcPlugin(ctx context.Context, pluginName string, pluginCfg *pluginspb.Plugins) error { +func (pm *PluginManager) loadGRPCPlugin(ctx context.Context, pluginName string, pluginCfg *pluginspb.Plugins) error { logger := hclog.New(&hclog.LoggerOptions{ Name: "plugin", Output: os.Stdout, @@ -214,8 +214,8 @@ func (pm *PluginManager) loadGrpcPlugin(ctx context.Context, pluginName string, return nil } -// loadGrpcPlugin is the default grpcPluginLoader. -func loadGrpcPlugin(ctx context.Context, logger hclog.Logger, pluginName string, plugins *pluginspb.Plugins) (*grpcPlugin, error) { +// loadGRPCPlugin is the default grpcPluginLoader. +func loadGRPCPlugin(ctx context.Context, logger hclog.Logger, pluginName string, plugins *pluginspb.Plugins) (*grpcPlugin, error) { pluginPath, err := cofidectl_plugin.GetPluginPath(pluginName) if err != nil { return nil, err @@ -238,7 +238,7 @@ func loadGrpcPlugin(ctx context.Context, logger hclog.Logger, pluginName string, Logger: logger, }) - grpcPlugin, err := startGrpcPlugin(ctx, client, pluginName, plugins) + grpcPlugin, err := startGRPCPlugin(ctx, client, pluginName, plugins) if err != nil { client.Kill() return nil, err @@ -246,7 +246,7 @@ func loadGrpcPlugin(ctx context.Context, logger hclog.Logger, pluginName string, return grpcPlugin, nil } -func startGrpcPlugin(ctx context.Context, client *go_plugin.Client, pluginName string, plugins *pluginspb.Plugins) (*grpcPlugin, error) { +func startGRPCPlugin(ctx context.Context, client *go_plugin.Client, pluginName string, plugins *pluginspb.Plugins) (*grpcPlugin, error) { grpcClient, err := client.Client() if err != nil { return nil, fmt.Errorf("cannot create interface to plugin: %w", err) diff --git a/pkg/plugin/manager/manager_test.go b/pkg/plugin/manager/manager_test.go index f9718f0..8824006 100644 --- a/pkg/plugin/manager/manager_test.go +++ b/pkg/plugin/manager/manager_test.go @@ -23,22 +23,22 @@ import ( "google.golang.org/protobuf/types/known/structpb" ) -type fakeGrpcDataSource struct { +type fakeGRPCDataSource struct { local.LocalDataSource } -func newFakeGrpcDataSource(t *testing.T, configLoader config.Loader) *fakeGrpcDataSource { +func newFakeGRPCDataSource(t *testing.T, configLoader config.Loader) *fakeGRPCDataSource { lds, err := local.NewLocalDataSource(configLoader) assert.Nil(t, err) - return &fakeGrpcDataSource{LocalDataSource: *lds} + return &fakeGRPCDataSource{LocalDataSource: *lds} } -type fakeGrpcProvision struct { +type fakeGRPCProvision struct { spirehelm.SpireHelm } -func newFakeGrpcProvision() *fakeGrpcProvision { - return &fakeGrpcProvision{} +func newFakeGRPCProvision() *fakeGRPCProvision { + return &fakeGRPCProvision{} } func TestManager_Init_success(t *testing.T) { @@ -94,7 +94,7 @@ func TestManager_Init_success(t *testing.T) { m := NewManager(configLoader) // Mock out the gRPC plugin loader function. m.grpcPluginLoader = func(_ context.Context, _ hclog.Logger, _ string, _ *pluginspb.Plugins) (*grpcPlugin, error) { - return &grpcPlugin{nil, newFakeGrpcDataSource(t, configLoader), newFakeGrpcProvision()}, nil + return &grpcPlugin{nil, newFakeGRPCDataSource(t, configLoader), newFakeGRPCProvision()}, nil } err = m.Init(context.Background(), tt.plugins, tt.pluginConfig) @@ -200,7 +200,7 @@ func TestManager_GetDataSource_success(t *testing.T) { name: "gRPC", config: config.Config{Plugins: fixtures.Plugins("plugins1")}, want: func(cl config.Loader) cofidectl_plugin.DataSource { - fcds := newFakeGrpcDataSource(t, cl) + fcds := newFakeGRPCDataSource(t, cl) return fcds }, }, @@ -208,7 +208,7 @@ func TestManager_GetDataSource_success(t *testing.T) { name: "gRPC with provision", config: config.Config{Plugins: fixtures.Plugins("plugins2")}, want: func(cl config.Loader) cofidectl_plugin.DataSource { - fcds := newFakeGrpcDataSource(t, cl) + fcds := newFakeGRPCDataSource(t, cl) return fcds }, }, @@ -226,9 +226,9 @@ func TestManager_GetDataSource_success(t *testing.T) { if tt.config.Plugins.GetDataSource() != LocalDSPluginName { client = &go_plugin.Client{} m.grpcPluginLoader = func(_ context.Context, _ hclog.Logger, _ string, _ *pluginspb.Plugins) (*grpcPlugin, error) { - ds := newFakeGrpcDataSource(t, configLoader) + ds := newFakeGRPCDataSource(t, configLoader) if tt.config.Plugins.GetProvision() == tt.config.Plugins.GetDataSource() { - provision = newFakeGrpcProvision() + provision = newFakeGRPCProvision() } return &grpcPlugin{client, ds, provision}, nil } @@ -308,12 +308,12 @@ func TestManager_GetProvision_success(t *testing.T) { { name: "gRPC", config: config.Config{Plugins: fixtures.Plugins("plugins1")}, - want: newFakeGrpcProvision(), + want: newFakeGRPCProvision(), }, { name: "gRPC with provision", config: config.Config{Plugins: fixtures.Plugins("plugins2")}, - want: newFakeGrpcProvision(), + want: newFakeGRPCProvision(), }, } for _, tt := range tests { @@ -329,9 +329,9 @@ func TestManager_GetProvision_success(t *testing.T) { if tt.config.Plugins.GetDataSource() != LocalDSPluginName { client = &go_plugin.Client{} m.grpcPluginLoader = func(_ context.Context, _ hclog.Logger, _ string, _ *pluginspb.Plugins) (*grpcPlugin, error) { - provision := newFakeGrpcProvision() + provision := newFakeGRPCProvision() if tt.config.Plugins.GetDataSource() == tt.config.Plugins.GetProvision() { - source = newFakeGrpcDataSource(t, configLoader) + source = newFakeGRPCDataSource(t, configLoader) } return &grpcPlugin{client, source, provision}, nil } @@ -420,7 +420,7 @@ func TestManager_Shutdown(t *testing.T) { // Mock out the gRPC plugin loader function. client := &go_plugin.Client{} m.grpcPluginLoader = func(_ context.Context, _ hclog.Logger, _ string, _ *pluginspb.Plugins) (*grpcPlugin, error) { - return &grpcPlugin{client, newFakeGrpcDataSource(t, configLoader), nil}, nil + return &grpcPlugin{client, newFakeGRPCDataSource(t, configLoader), nil}, nil } _, err = m.GetDataSource(context.Background()) From 38e171f40c35283fef070bba750cdcf7fc7e352b Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Mon, 16 Dec 2024 13:46:46 +0000 Subject: [PATCH 21/48] Address code review feedback - fix spelling --- pkg/plugin/manager/manager.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkg/plugin/manager/manager.go b/pkg/plugin/manager/manager.go index f1a0725..c4448fa 100644 --- a/pkg/plugin/manager/manager.go +++ b/pkg/plugin/manager/manager.go @@ -43,7 +43,7 @@ type PluginManager struct { // grpcPluginLoader is a function that loads a gRPC plugin. The function should load a single // plugin that implements all cofidectl plugins with the specified name in pluginCfg. // All cofidectl plugins in the returned grpcPlugin object should be validated using the -// Validate RPC before returing. +// Validate RPC before returning. // It is primarily used for mocking in unit tests. type grpcPluginLoader func(ctx context.Context, logger hclog.Logger, pluginName string, pluginCfg *pluginspb.Plugins) (*grpcPlugin, error) From 252cd2603eaad6274b4d5eae066fda8736c729d2 Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Mon, 16 Dec 2024 14:08:29 +0000 Subject: [PATCH 22/48] Check plugin configuration in federation integration test --- tests/integration/federation/test.sh | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/integration/federation/test.sh b/tests/integration/federation/test.sh index 93299da..18871e0 100755 --- a/tests/integration/federation/test.sh +++ b/tests/integration/federation/test.sh @@ -35,6 +35,19 @@ function init() { ./cofidectl init $args } +function check_init() { + data_source_plugin="$(yq '.plugins.data_source' cofide.yaml -r)" + provision_plugin="$(yq '.plugins.provision' cofide.yaml -r)" + if [[ "$data_source_plugin" != "${DATA_SOURCE_PLUGIN:-local}" ]]; then + echo "Unexpected data source plugin in cofide.yaml: $data_source_plugin vs ${DATA_SOURCE_PLUGIN:-local}" + exit 1 + fi + if [[ "$provision_plugin" != "${PROVISION_PLUGIN:-spire-helm}" ]]; then + echo "Unexpected provision plugin in cofide.yaml: $provision_plugin vs ${PROVISION_PLUGIN:-spire-helm}" + exit 1 + fi +} + function configure() { ./cofidectl trust-zone add $TRUST_ZONE_1 --trust-domain $TRUST_DOMAIN_1 --kubernetes-context $K8S_CLUSTER_1_CONTEXT --kubernetes-cluster $K8S_CLUSTER_1_NAME --profile kubernetes ./cofidectl trust-zone add $TRUST_ZONE_2 --trust-domain $TRUST_DOMAIN_2 --kubernetes-context $K8S_CLUSTER_2_CONTEXT --kubernetes-cluster $K8S_CLUSTER_2_NAME --profile kubernetes @@ -105,6 +118,7 @@ function down() { function main() { init + check_init configure up list_resources From a4c3211e41bf3c493d6049d707c79b0f46ce2157 Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Mon, 16 Dec 2024 14:09:17 +0000 Subject: [PATCH 23/48] Move replace to bottom of go.mod This works better with 'go mod tidy' and avoids it creating new require sections. --- go.mod | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 224948d..7c61117 100644 --- a/go.mod +++ b/go.mod @@ -23,13 +23,9 @@ require ( helm.sh/helm/v3 v3.16.3 ) -require cel.dev/expr v0.18.0 // indirect - -// Uncomment the following for development with local Cofide API SDK changes: -//replace github.com/cofide/cofide-api-sdk => ../cofide-api-sdk - require ( buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.35.2-20241127180247-a33202765966.1 // indirect + cel.dev/expr v0.18.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 @@ -183,3 +179,6 @@ require ( sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) + +// Uncomment the following for development with local Cofide API SDK changes: +// replace github.com/cofide/cofide-api-sdk => ../cofide-api-sdk From 3eaa125d9ba7b4a2d4703200c9125afabb7942dc Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Mon, 16 Dec 2024 16:35:02 +0000 Subject: [PATCH 24/48] Disable the default clusterSPIFFEID If you create a trust zone with no attestation policies bound to it, we previously enabled the default clusterSPIFFEID. This resource matches any pod in a non-spire namespace. This behaviour is surprising, and we should instead rely on an explicit catch-all attestation policy being bound to the trust zone: cofidectl attestation-policy add kubernetes --name catch-all This commit fixes the issue by disabling the default clusterSPIFFEID. Fixes: #92 --- pkg/provider/helm/values.go | 47 ++++++++++++-------------------- pkg/provider/helm/values_test.go | 6 ++-- 2 files changed, 21 insertions(+), 32 deletions(-) diff --git a/pkg/provider/helm/values.go b/pkg/provider/helm/values.go index 522328d..1b08263 100644 --- a/pkg/provider/helm/values.go +++ b/pkg/provider/helm/values.go @@ -125,45 +125,34 @@ func (g *HelmValuesGenerator) GenerateValues() (map[string]any, error) { return nil, fmt.Errorf("failed to get controllerManager map from spireServer: %w", err) } - // Enables the default ClusterSPIFFEID CR by default. - controllerManager["identities"] = map[string]any{ - "clusterSPIFFEIDs": map[string]any{ - "default": map[string]any{ - "enabled": true, - }, - }, - } - identities, err := getOrCreateNestedMap(controllerManager, "identities") if err != nil { return nil, fmt.Errorf("failed to get identities map from controllerManager: %w", err) } - if len(g.trustZone.AttestationPolicies) > 0 { - csids, err := getOrCreateNestedMap(identities, "clusterSPIFFEIDs") + csids, err := getOrCreateNestedMap(identities, "clusterSPIFFEIDs") + if err != nil { + return nil, fmt.Errorf("failed to get clusterSPIFFEIDs map from identities: %w", err) + } + + // Disables the default ClusterSPIFFEID CR. + csids["default"] = map[string]any{ + "enabled": false, + } + + // Adds the attestation policies as ClusterSPIFFEID CRs to be reconciled by the spire-controller-manager. + for _, binding := range g.trustZone.AttestationPolicies { + policy, err := g.source.GetAttestationPolicy(binding.Policy) if err != nil { - return nil, fmt.Errorf("failed to get clusterSPIFFEIDs map from identities: %w", err) + return nil, err } - // Disables the default ClusterSPIFFEID CR. - csids["default"] = map[string]any{ - "enabled": false, + clusterSPIFFEIDs, err := attestationpolicy.NewAttestationPolicy(policy).GetHelmConfig(g.source, binding) + if err != nil { + return nil, err } - // Adds the attestation policies as ClusterSPIFFEID CRs to be reconciled by the spire-controller-manager. - for _, binding := range g.trustZone.AttestationPolicies { - policy, err := g.source.GetAttestationPolicy(binding.Policy) - if err != nil { - return nil, err - } - - clusterSPIFFEIDs, err := attestationpolicy.NewAttestationPolicy(policy).GetHelmConfig(g.source, binding) - if err != nil { - return nil, err - } - - csids[policy.Name] = clusterSPIFFEIDs - } + csids[policy.Name] = clusterSPIFFEIDs } // Adds the federations as ClusterFederatedTrustDomain CRs to be reconciled by the spire-controller-manager. diff --git a/pkg/provider/helm/values_test.go b/pkg/provider/helm/values_test.go index 3ccba16..b7f3c89 100644 --- a/pkg/provider/helm/values_test.go +++ b/pkg/provider/helm/values_test.go @@ -94,7 +94,7 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { "identities": Values{ "clusterSPIFFEIDs": Values{ "default": Values{ - "enabled": true, + "enabled": false, }, }, }, @@ -293,7 +293,7 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { "identities": Values{ "clusterSPIFFEIDs": Values{ "default": Values{ - "enabled": true, + "enabled": false, }, }, }, @@ -430,7 +430,7 @@ func TestHelmValuesGenerator_GenerateValues_AdditionalValues(t *testing.T) { "identities": Values{ "clusterSPIFFEIDs": Values{ "default": Values{ - "enabled": true, + "enabled": false, }, }, "clusterFederatedTrustDomains": Values{ From b2953df346f15ebf208a84f641b4836244ae988c Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Thu, 19 Dec 2024 16:12:01 +0000 Subject: [PATCH 25/48] Support custom SPIRE Helm chart location --- pkg/provider/helm/helm.go | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/pkg/provider/helm/helm.go b/pkg/provider/helm/helm.go index 0ef6b82..cca52f9 100644 --- a/pkg/provider/helm/helm.go +++ b/pkg/provider/helm/helm.go @@ -286,7 +286,7 @@ func installChart(ctx context.Context, cfg *action.Configuration, client *action } options, err := client.ChartPathOptions.LocateChart( - fmt.Sprintf("%s/%s", SPIRERepositoryName, chartName), + getChartRef(chartName), settings, ) if err != nil { @@ -325,7 +325,7 @@ func upgradeChart(ctx context.Context, cfg *action.Configuration, client *action } options, err := client.ChartPathOptions.LocateChart( - fmt.Sprintf("%s/%s", SPIRERepositoryName, chartName), + getChartRef(chartName), settings, ) if err != nil { @@ -340,6 +340,16 @@ func upgradeChart(ctx context.Context, cfg *action.Configuration, client *action return client.RunWithContext(ctx, chartName, chart, values) } +func getChartRef(chartName string) string { + // TODO: Make this conditional on an environment variable and use it for the repoPath. + if true { + repoPath := "/home/mark/src/spiffe/helm-charts-hardened/charts" + return fmt.Sprintf("%s/%s", repoPath, chartName) + } else { + return fmt.Sprintf("%s/%s", SPIRERepositoryName, chartName) + } +} + func newUninstall(cfg *action.Configuration) *action.Uninstall { uninstall := action.NewUninstall(cfg) return uninstall From b03222ba26dd0227e84656339cac8b1d41cfdd20 Mon Sep 17 00:00:00 2001 From: Nial Daly <34862917+nialdaly@users.noreply.github.com> Date: Thu, 19 Dec 2024 17:07:09 +0000 Subject: [PATCH 26/48] feat: Add an `--external-server` flag to the `cofidectl trust-zone add` command (#94) --- cmd/cofidectl/cmd/trustzone/trustzone.go | 3 ++ go.mod | 4 +-- go.sum | 2 ++ internal/pkg/config/schema.cue | 1 + internal/pkg/config/testdata/config/full.yaml | 2 ++ internal/pkg/test/fixtures/fixtures.go | 21 +++++++++++++ pkg/plugin/provision/spirehelm/spirehelm.go | 7 +++++ .../provision/spirehelm/spirehelm_test.go | 30 +++++++++++++++++++ pkg/provider/helm/values.go | 13 ++++++++ pkg/provider/helm/values_test.go | 22 ++++++++++++++ 10 files changed, 103 insertions(+), 2 deletions(-) diff --git a/cmd/cofidectl/cmd/trustzone/trustzone.go b/cmd/cofidectl/cmd/trustzone/trustzone.go index d857bd8..f272bdd 100644 --- a/cmd/cofidectl/cmd/trustzone/trustzone.go +++ b/cmd/cofidectl/cmd/trustzone/trustzone.go @@ -114,6 +114,7 @@ type addOpts struct { context string profile string jwtIssuer string + externalServer bool } func (c *TrustZoneCommand) GetAddCommand() *cobra.Command { @@ -156,6 +157,7 @@ func (c *TrustZoneCommand) GetAddCommand() *cobra.Command { Profile: &opts.profile, JwtIssuer: &opts.jwtIssuer, BundleEndpointProfile: &bundleEndpointProfile, + ExternalServer: &opts.externalServer, } _, err = ds.AddTrustZone(newTrustZone) @@ -173,6 +175,7 @@ func (c *TrustZoneCommand) GetAddCommand() *cobra.Command { f.StringVar(&opts.context, "kubernetes-context", "", "Kubernetes context to use for this trust zone") f.StringVar(&opts.profile, "profile", "kubernetes", "Cofide profile used in the installation (e.g. kubernetes, istio)") f.StringVar(&opts.jwtIssuer, "jwt-issuer", "", "JWT issuer to use for this trust zone") + f.BoolVar(&opts.externalServer, "external-server", false, "If the SPIRE server runs externally") cobra.CheckErr(cmd.MarkFlagRequired("trust-domain")) cobra.CheckErr(cmd.MarkFlagRequired("kubernetes-cluster")) diff --git a/go.mod b/go.mod index cdc9b32..0b93a7f 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ toolchain go1.23.4 require ( buf.build/go/protoyaml v0.3.0 cuelang.org/go v0.10.1 - github.com/cofide/cofide-api-sdk v0.4.1-0.20241213113045-864bfb581c91 + github.com/cofide/cofide-api-sdk v0.4.1-0.20241218164200-fd9d4ce5088c github.com/fatih/color v1.18.0 github.com/gofrs/flock v0.12.1 github.com/google/go-cmp v0.6.0 @@ -25,7 +25,7 @@ require ( require ( buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.35.2-20241127180247-a33202765966.1 // indirect - cel.dev/expr v0.18.0 // indirect + cel.dev/expr v0.18.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 diff --git a/go.sum b/go.sum index 6b711e7..c00376d 100644 --- a/go.sum +++ b/go.sum @@ -88,6 +88,8 @@ github.com/cockroachdb/apd/v3 v3.2.1 h1:U+8j7t0axsIgvQUqthuNm82HIrYXodOV2iWLWtEa github.com/cockroachdb/apd/v3 v3.2.1/go.mod h1:klXJcjp+FffLTHlhIG69tezTDvdP065naDsHzKhYSqc= github.com/cofide/cofide-api-sdk v0.4.1-0.20241213113045-864bfb581c91 h1:We5sn+o74dce2gkO63ola8RFSD92jsud3pAIwVbzAq8= github.com/cofide/cofide-api-sdk v0.4.1-0.20241213113045-864bfb581c91/go.mod h1:u2iATR4IbZm9ruIBN734UjVuO3XQKPAFViIY3Xr6kTA= +github.com/cofide/cofide-api-sdk v0.4.1-0.20241218164200-fd9d4ce5088c h1:nEdnzITNrjaBjP6VRCEsCyFF5nUI5M0nBDB98JZrZEI= +github.com/cofide/cofide-api-sdk v0.4.1-0.20241218164200-fd9d4ce5088c/go.mod h1:uChrsD6JvNDSCsAxXil3gGqTHxee0VJ1MbCT7xFZg6g= 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.23 h1:H2CClyUkmpKAGlhQp95g2WXHfLYc7whAuvZGBNYOOwQ= diff --git a/internal/pkg/config/schema.cue b/internal/pkg/config/schema.cue index 1900609..d67d511 100644 --- a/internal/pkg/config/schema.cue +++ b/internal/pkg/config/schema.cue @@ -14,6 +14,7 @@ jwt_issuer?: string extra_helm_values?: #HelmValues bundle_endpoint_profile?: #BundleEndpointProfile + external_server?: bool } #TrustProvider: { diff --git a/internal/pkg/config/testdata/config/full.yaml b/internal/pkg/config/testdata/config/full.yaml index 265649d..8e6626e 100644 --- a/internal/pkg/config/testdata/config/full.yaml +++ b/internal/pkg/config/testdata/config/full.yaml @@ -24,6 +24,7 @@ trust_zones: logLevel: INFO bundle_endpoint_profile: BUNDLE_ENDPOINT_PROFILE_HTTPS_SPIFFE profile: kubernetes + external_server: false - name: tz2 trust_domain: td2 kubernetes_cluster: local2 @@ -42,6 +43,7 @@ trust_zones: jwt_issuer: https://tz2.example.com bundle_endpoint_profile: BUNDLE_ENDPOINT_PROFILE_HTTPS_WEB profile: kubernetes + external_server: false attestation_policies: - name: ap1 kubernetes: diff --git a/internal/pkg/test/fixtures/fixtures.go b/internal/pkg/test/fixtures/fixtures.go index fbdffcc..a75b6cb 100644 --- a/internal/pkg/test/fixtures/fixtures.go +++ b/internal/pkg/test/fixtures/fixtures.go @@ -62,6 +62,7 @@ var trustZoneFixtures map[string]*trust_zone_proto.TrustZone = map[string]*trust return value }(), BundleEndpointProfile: trust_zone_proto.BundleEndpointProfile_BUNDLE_ENDPOINT_PROFILE_HTTPS_SPIFFE.Enum(), + ExternalServer: BoolPtr(false), }, "tz2": { Name: "tz2", @@ -88,6 +89,7 @@ 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(), + ExternalServer: BoolPtr(false), }, // tz3 has no federations or bound attestation policies. "tz3": { @@ -118,6 +120,21 @@ var trustZoneFixtures map[string]*trust_zone_proto.TrustZone = map[string]*trust Federations: []*federation_proto.Federation{}, AttestationPolicies: []*ap_binding_proto.APBinding{}, }, + // tz5 has no federations or bound attestation policies and has an external SPIRE server. + "tz5": { + Name: "tz5", + TrustDomain: "td5", + KubernetesCluster: StringPtr("local5"), + KubernetesContext: StringPtr("kind-local5"), + TrustProvider: &trust_provider_proto.TrustProvider{ + Kind: StringPtr("kubernetes"), + }, + Profile: StringPtr("kubernetes"), + BundleEndpointUrl: StringPtr("127.0.0.5"), + Federations: []*federation_proto.Federation{}, + AttestationPolicies: []*ap_binding_proto.APBinding{}, + ExternalServer: BoolPtr(true), + }, } var attestationPolicyFixtures map[string]*attestation_policy_proto.AttestationPolicy = map[string]*attestation_policy_proto.AttestationPolicy{ @@ -275,3 +292,7 @@ func Plugins(name string) *pluginspb.Plugins { func StringPtr(s string) *string { return &s } + +func BoolPtr(b bool) *bool { + return &b +} diff --git a/pkg/plugin/provision/spirehelm/spirehelm.go b/pkg/plugin/provision/spirehelm/spirehelm.go index 648aaa3..f86aa5b 100644 --- a/pkg/plugin/provision/spirehelm/spirehelm.go +++ b/pkg/plugin/provision/spirehelm/spirehelm.go @@ -150,10 +150,17 @@ func (h *SpireHelm) InstallSPIREStack(ctx context.Context, ds datasource.DataSou func (h *SpireHelm) WatchAndConfigure(ctx context.Context, ds datasource.DataSource, trustZones []*trust_zone_proto.TrustZone, kubeCfgFile string, statusCh chan<- *provisionpb.Status) error { // Wait for SPIRE servers to be available and update status before applying federation(s) for _, trustZone := range trustZones { + if trustZone.GetExternalServer() { + sb := provision.NewStatusBuilder(trustZone.Name, trustZone.GetKubernetesCluster()) + statusCh <- sb.Done("Ready", "Skipped waiting for external SPIRE server pod and service") + continue + } + if err := h.GetBundleAndEndpoint(ctx, statusCh, ds, trustZone, kubeCfgFile); err != nil { return err } } + return nil } diff --git a/pkg/plugin/provision/spirehelm/spirehelm_test.go b/pkg/plugin/provision/spirehelm/spirehelm_test.go index b539b9e..0127a8e 100644 --- a/pkg/plugin/provision/spirehelm/spirehelm_test.go +++ b/pkg/plugin/provision/spirehelm/spirehelm_test.go @@ -50,6 +50,36 @@ func TestSpireHelm_Deploy(t *testing.T) { assert.EqualExportedValues(t, want, statuses) } +func TestSpireHelm_Deploy_ExternalServer(t *testing.T) { + providerFactory := newFakeHelmSPIREProviderFactory() + spireHelm := NewSpireHelm(providerFactory) + + config := &config.Config{ + TrustZones: []*trust_zone_proto.TrustZone{ + fixtures.TrustZone("tz5"), + }, + AttestationPolicies: []*attestation_policy_proto.AttestationPolicy{}, + Plugins: fixtures.Plugins("plugins1"), + } + + ds := newFakeDataSource(t, config) + + statusCh, err := spireHelm.Deploy(context.Background(), ds, "fake-kube.cfg") + require.NoError(t, err, err) + + statuses := collectStatuses(statusCh) + want := []*provisionpb.Status{ + provision.StatusOk("Preparing", "Adding SPIRE Helm repo"), + provision.StatusDone("Prepared", "Added SPIRE Helm repo"), + provision.StatusOk("Installing", "Installing SPIRE CRDs for local5 in tz5"), + provision.StatusOk("Installing", "Installing SPIRE chart for local5 in tz5"), + provision.StatusDone("Installed", "Installation completed for local5 in tz5"), + provision.StatusDone("Ready", "Skipped waiting for external SPIRE server pod and service for local5 in tz5"), + provision.StatusDone("Ready", "Skipped waiting for external SPIRE server pod and service for local5 in tz5"), + } + assert.EqualExportedValues(t, want, statuses) +} + func TestSpireHelm_TearDown(t *testing.T) { providerFactory := newFakeHelmSPIREProviderFactory() spireHelm := NewSpireHelm(providerFactory) diff --git a/pkg/provider/helm/values.go b/pkg/provider/helm/values.go index d43c324..e7f537c 100644 --- a/pkg/provider/helm/values.go +++ b/pkg/provider/helm/values.go @@ -41,6 +41,7 @@ type spireServerValues struct { caKeyType string caTTL string controllerManagerEnabled bool + enabled bool fullnameOverride string logLevel string serverConfig trustprovider.TrustProviderServerConfig @@ -101,10 +102,13 @@ func (g *HelmValuesGenerator) GenerateValues() (map[string]any, error) { return nil, err } + spireServerEnabled := !g.trustZone.GetExternalServer() + ssv := spireServerValues{ caKeyType: "rsa-2048", caTTL: "12h", controllerManagerEnabled: true, + enabled: spireServerEnabled, fullnameOverride: "spire-server", logLevel: "DEBUG", serverConfig: tp.ServerConfig, @@ -330,6 +334,14 @@ func (s *spireAgentValues) generateValues() (map[string]any, error) { // generateValues generates the spire-server Helm values map. func (s *spireServerValues) generateValues() (map[string]any, error) { + if !s.enabled { + return map[string]any{ + "spire-server": map[string]any{ + "enabled": s.enabled, + }, + }, nil + } + if s.caKeyType == "" { return nil, fmt.Errorf("caKeyType value is empty") } @@ -364,6 +376,7 @@ func (s *spireServerValues) generateValues() (map[string]any, error) { return map[string]any{ "spire-server": map[string]any{ + "enabled": s.enabled, "caKeyType": s.caKeyType, "caTTL": s.caTTL, "controllerManager": map[string]any{ diff --git a/pkg/provider/helm/values_test.go b/pkg/provider/helm/values_test.go index c35af9e..2ecd30c 100644 --- a/pkg/provider/helm/values_test.go +++ b/pkg/provider/helm/values_test.go @@ -99,6 +99,7 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { }, }, }, + "enabled": true, "fullnameOverride": "spire-server", "logLevel": "DEBUG", "nodeAttestor": Values{ @@ -209,6 +210,7 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { }, }, }, + "enabled": true, "federation": Values{ "enabled": true, }, @@ -298,6 +300,7 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { }, }, }, + "enabled": true, "fullnameOverride": "spire-server", "logLevel": "DEBUG", "nodeAttestor": Values{ @@ -444,6 +447,7 @@ func TestHelmValuesGenerator_GenerateValues_AdditionalValues(t *testing.T) { }, }, }, + "enabled": true, "fullnameOverride": "spire-server", "logLevel": "DEBUG", "nodeAttestor": Values{ @@ -840,6 +844,7 @@ func TestMergeMaps(t *testing.T) { }, }, }, + "enabled": true, }, }, want: map[string]any{ @@ -864,6 +869,7 @@ func TestMergeMaps(t *testing.T) { }, }, }, + "enabled": true, }, }, }, @@ -1286,6 +1292,7 @@ func TestSpireServerValues_GenerateValues(t *testing.T) { caKeyType: "rsa-2048", caTTL: "12h", controllerManagerEnabled: true, + enabled: true, fullnameOverride: "spire-server", logLevel: "DEBUG", serverConfig: trustprovider.TrustProviderServerConfig{ @@ -1308,6 +1315,7 @@ func TestSpireServerValues_GenerateValues(t *testing.T) { "controllerManager": map[string]any{ "enabled": true, }, + "enabled": true, "fullnameOverride": "spire-server", "logLevel": "DEBUG", "nodeAttestor": Values{ @@ -1330,12 +1338,25 @@ func TestSpireServerValues_GenerateValues(t *testing.T) { }, wantErr: false, }, + { + name: "valid SPIRE server values, enabled set to false", + input: spireServerValues{ + enabled: false, + }, + want: map[string]any{ + "spire-server": map[string]any{ + "enabled": false, + }, + }, + wantErr: false, + }, { name: "invalid SPIRE server values, empty serviceType value", input: spireServerValues{ caKeyType: "rsa-2048", caTTL: "12h", controllerManagerEnabled: true, + enabled: true, fullnameOverride: "spire-server", logLevel: "DEBUG", serverConfig: trustprovider.TrustProviderServerConfig{ @@ -1361,6 +1382,7 @@ func TestSpireServerValues_GenerateValues(t *testing.T) { caKeyType: "rsa-2048", caTTL: "12h", controllerManagerEnabled: true, + enabled: true, fullnameOverride: "spire-server", logLevel: "DEBUG", serverConfig: trustprovider.TrustProviderServerConfig{ From b54bcc72498ab44b92176cd5def035a270079f15 Mon Sep 17 00:00:00 2001 From: nialdaly Date: Thu, 19 Dec 2024 18:59:36 +0000 Subject: [PATCH 27/48] Updated the getChartRef method --- pkg/provider/helm/helm.go | 38 +++++++++++++++++++++++++++++--------- 1 file changed, 29 insertions(+), 9 deletions(-) diff --git a/pkg/provider/helm/helm.go b/pkg/provider/helm/helm.go index cca52f9..f31b597 100644 --- a/pkg/provider/helm/helm.go +++ b/pkg/provider/helm/helm.go @@ -285,8 +285,13 @@ func installChart(ctx context.Context, cfg *action.Configuration, client *action return nil, nil } + chartRef, err := getChartRef(chartName) + if err != nil { + return nil, err + } + options, err := client.ChartPathOptions.LocateChart( - getChartRef(chartName), + chartRef, settings, ) if err != nil { @@ -324,8 +329,13 @@ func upgradeChart(ctx context.Context, cfg *action.Configuration, client *action return nil, fmt.Errorf("%v not installed", chartName) } + chartRef, err := getChartRef(chartName) + if err != nil { + return nil, err + } + options, err := client.ChartPathOptions.LocateChart( - getChartRef(chartName), + chartRef, settings, ) if err != nil { @@ -340,14 +350,24 @@ func upgradeChart(ctx context.Context, cfg *action.Configuration, client *action return client.RunWithContext(ctx, chartName, chart, values) } -func getChartRef(chartName string) string { - // TODO: Make this conditional on an environment variable and use it for the repoPath. - if true { - repoPath := "/home/mark/src/spiffe/helm-charts-hardened/charts" - return fmt.Sprintf("%s/%s", repoPath, chartName) - } else { - return fmt.Sprintf("%s/%s", SPIRERepositoryName, chartName) +// getChartRef returns the full chart reference using either a custom repository path +// from HELM_REPO_PATH environment variable or the default SPIRE repository. +func getChartRef(chartName string) (string, error) { + if chartName == "" { + return "", fmt.Errorf("chart name cannot be empty") } + + repoPath, exists := os.LookupEnv("HELM_REPO_PATH") + if exists { + if repoPath == "" { + return "", fmt.Errorf("HELM_REPO_PATH environment variable is set but empty") + } + + repoPath = strings.TrimRight(repoPath, "/") + return fmt.Sprintf("%s/%s", repoPath, chartName), nil + } + + return fmt.Sprintf("%s/%s", SPIRERepositoryName, chartName), nil } func newUninstall(cfg *action.Configuration) *action.Uninstall { From c7577189c166155e5e070f685e5251b48b89c6c3 Mon Sep 17 00:00:00 2001 From: nialdaly Date: Thu, 19 Dec 2024 19:00:39 +0000 Subject: [PATCH 28/48] Unit test added for getChartRef --- pkg/provider/helm/helm_test.go | 92 ++++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/pkg/provider/helm/helm_test.go b/pkg/provider/helm/helm_test.go index 806c3bc..4d95177 100644 --- a/pkg/provider/helm/helm_test.go +++ b/pkg/provider/helm/helm_test.go @@ -5,6 +5,7 @@ package helm import ( "context" + "os" "testing" trust_zone_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/trust_zone/v1alpha1" @@ -22,3 +23,94 @@ func TestHelmSPIREProvider(t *testing.T) { assert.Equal(t, p.SPIRECRDsVersion, "0.4.0") assert.Equal(t, trustZoneProto.TrustDomain, p.trustZone.TrustDomain) } + +func TestGetChartRef(t *testing.T) { + originalPath := os.Getenv("HELM_REPO_PATH") + defer os.Setenv("HELM_REPO_PATH", originalPath) + + tests := []struct { + name string + helmRepoPath string + helmRepoPathSet bool + chartName string + want string + wantErr bool + errString string + }{ + { + name: "with HELM_REPO_PATH set", + helmRepoPathSet: true, + helmRepoPath: "spire-local", + chartName: "spire", + want: "spire-local/spire", + wantErr: false, + }, + { + name: "with HELM_REPO_PATH containing trailing slash", + helmRepoPathSet: true, + helmRepoPath: "custom-repo/", + chartName: "spire", + want: "custom-repo/spire", + wantErr: false, + }, + { + name: "with HELM_REPO_PATH containing trailing slashes", + helmRepoPathSet: true, + helmRepoPath: "custom-repo//", + chartName: "spire", + want: "custom-repo/spire", + wantErr: false, + }, + { + name: "with empty HELM_REPO_PATH", + helmRepoPathSet: true, + helmRepoPath: "", + chartName: "spire", + want: "spire/spire", + wantErr: true, + errString: "HELM_REPO_PATH environment variable is set but empty", + }, + { + name: "with empty chart name", + helmRepoPathSet: false, + helmRepoPath: "spire-local", + chartName: "", + wantErr: true, + errString: "chart name cannot be empty", + }, + { + name: "with HELM_REPO_PATH not set", + helmRepoPathSet: false, + chartName: "spire", + want: "spire/spire", + wantErr: false, + }, + { + name: "with empty HELM_REPO_PATH and an empty chart name", + helmRepoPathSet: false, + chartName: "", + wantErr: true, + errString: "chart name cannot be empty", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if tt.helmRepoPathSet { + os.Setenv("HELM_REPO_PATH", tt.helmRepoPath) + } else { + os.Unsetenv("HELM_REPO_PATH") + } + + got, err := getChartRef(tt.chartName) + if tt.wantErr { + assert.Error(t, err) + assert.Contains(t, err.Error(), tt.errString) + return + } + + assert.NoError(t, err) + assert.Equal(t, tt.want, got) + }) + } +} From 6679503a858e5462caf23a5e0226a159a045a147 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Dec 2024 01:30:21 +0000 Subject: [PATCH 29/48] Bump helm.sh/helm/v3 from 3.16.3 to 3.16.4 Bumps [helm.sh/helm/v3](https://github.com/helm/helm) from 3.16.3 to 3.16.4. - [Release notes](https://github.com/helm/helm/releases) - [Commits](https://github.com/helm/helm/compare/v3.16.3...v3.16.4) --- updated-dependencies: - dependency-name: helm.sh/helm/v3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 6 +++--- go.sum | 14 ++++++-------- 2 files changed, 9 insertions(+), 11 deletions(-) diff --git a/go.mod b/go.mod index 0b93a7f..fe6b4fc 100644 --- a/go.mod +++ b/go.mod @@ -20,7 +20,7 @@ require ( google.golang.org/grpc v1.69.0 google.golang.org/protobuf v1.35.2 gopkg.in/yaml.v3 v3.0.1 - helm.sh/helm/v3 v3.16.3 + helm.sh/helm/v3 v3.16.4 ) require ( @@ -162,9 +162,9 @@ require ( gopkg.in/yaml.v2 v2.4.0 // indirect gotest.tools/v3 v3.5.0 // indirect k8s.io/api v0.32.0 - k8s.io/apiextensions-apiserver v0.31.1 // indirect + k8s.io/apiextensions-apiserver v0.31.3 // indirect k8s.io/apimachinery v0.32.0 - k8s.io/apiserver v0.31.1 // indirect + k8s.io/apiserver v0.31.3 // indirect k8s.io/cli-runtime v0.31.3 // indirect k8s.io/client-go v0.32.0 k8s.io/component-base v0.31.3 // indirect diff --git a/go.sum b/go.sum index c00376d..09e951b 100644 --- a/go.sum +++ b/go.sum @@ -86,8 +86,6 @@ 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.4.1-0.20241213113045-864bfb581c91 h1:We5sn+o74dce2gkO63ola8RFSD92jsud3pAIwVbzAq8= -github.com/cofide/cofide-api-sdk v0.4.1-0.20241213113045-864bfb581c91/go.mod h1:u2iATR4IbZm9ruIBN734UjVuO3XQKPAFViIY3Xr6kTA= github.com/cofide/cofide-api-sdk v0.4.1-0.20241218164200-fd9d4ce5088c h1:nEdnzITNrjaBjP6VRCEsCyFF5nUI5M0nBDB98JZrZEI= github.com/cofide/cofide-api-sdk v0.4.1-0.20241218164200-fd9d4ce5088c/go.mod h1:uChrsD6JvNDSCsAxXil3gGqTHxee0VJ1MbCT7xFZg6g= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= @@ -652,18 +650,18 @@ gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY= gotest.tools/v3 v3.5.0/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU= -helm.sh/helm/v3 v3.16.3 h1:kb8bSxMeRJ+knsK/ovvlaVPfdis0X3/ZhYCSFRP+YmY= -helm.sh/helm/v3 v3.16.3/go.mod h1:zeVWGDR4JJgiRbT3AnNsjYaX8OTJlIE9zC+Q7F7iUSU= +helm.sh/helm/v3 v3.16.4 h1:rBn/h9MACw+QlhxQTjpl8Ifx+VTWaYsw3rguGBYBzr0= +helm.sh/helm/v3 v3.16.4/go.mod h1:k8QPotUt57wWbi90w3LNmg3/MWcLPigVv+0/X4B8BzA= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= k8s.io/api v0.32.0 h1:OL9JpbvAU5ny9ga2fb24X8H6xQlVp+aJMFlgtQjR9CE= k8s.io/api v0.32.0/go.mod h1:4LEwHZEf6Q/cG96F3dqR965sYOfmPM7rq81BLgsE0p0= -k8s.io/apiextensions-apiserver v0.31.1 h1:L+hwULvXx+nvTYX/MKM3kKMZyei+UiSXQWciX/N6E40= -k8s.io/apiextensions-apiserver v0.31.1/go.mod h1:tWMPR3sgW+jsl2xm9v7lAyRF1rYEK71i9G5dRtkknoQ= +k8s.io/apiextensions-apiserver v0.31.3 h1:+GFGj2qFiU7rGCsA5o+p/rul1OQIq6oYpQw4+u+nciE= +k8s.io/apiextensions-apiserver v0.31.3/go.mod h1:2DSpFhUZZJmn/cr/RweH1cEVVbzFw9YBu4T+U3mf1e4= k8s.io/apimachinery v0.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg= k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= -k8s.io/apiserver v0.31.1 h1:Sars5ejQDCRBY5f7R3QFHdqN3s61nhkpaX8/k1iEw1c= -k8s.io/apiserver v0.31.1/go.mod h1:lzDhpeToamVZJmmFlaLwdYZwd7zB+WYRYIboqA1kGxM= +k8s.io/apiserver v0.31.3 h1:+1oHTtCB+OheqFEz375D0IlzHZ5VeQKX1KGXnx+TTuY= +k8s.io/apiserver v0.31.3/go.mod h1:PrxVbebxrxQPFhJk4powDISIROkNMKHibTg9lTRQ0Qg= k8s.io/cli-runtime v0.31.3 h1:fEQD9Xokir78y7pVK/fCJN090/iYNrLHpFbGU4ul9TI= k8s.io/cli-runtime v0.31.3/go.mod h1:Q2jkyTpl+f6AtodQvgDI8io3jrfr+Z0LyQBPJJ2Btq8= k8s.io/client-go v0.32.0 h1:DimtMcnN/JIKZcrSrstiwvvZvLjG0aSxy8PxN8IChp8= From ed9f732506198593898d9fa910525f0c821057ea Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Dec 2024 01:30:24 +0000 Subject: [PATCH 30/48] Bump google.golang.org/grpc from 1.69.0 to 1.69.2 Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.69.0 to 1.69.2. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](https://github.com/grpc/grpc-go/compare/v1.69.0...v1.69.2) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index 0b93a7f..2bbd953 100644 --- a/go.mod +++ b/go.mod @@ -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.0 + google.golang.org/grpc v1.69.2 google.golang.org/protobuf v1.35.2 gopkg.in/yaml.v3 v3.0.1 helm.sh/helm/v3 v3.16.3 diff --git a/go.sum b/go.sum index c00376d..591ab3c 100644 --- a/go.sum +++ b/go.sum @@ -86,8 +86,6 @@ 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.4.1-0.20241213113045-864bfb581c91 h1:We5sn+o74dce2gkO63ola8RFSD92jsud3pAIwVbzAq8= -github.com/cofide/cofide-api-sdk v0.4.1-0.20241213113045-864bfb581c91/go.mod h1:u2iATR4IbZm9ruIBN734UjVuO3XQKPAFViIY3Xr6kTA= github.com/cofide/cofide-api-sdk v0.4.1-0.20241218164200-fd9d4ce5088c h1:nEdnzITNrjaBjP6VRCEsCyFF5nUI5M0nBDB98JZrZEI= github.com/cofide/cofide-api-sdk v0.4.1-0.20241218164200-fd9d4ce5088c/go.mod h1:uChrsD6JvNDSCsAxXil3gGqTHxee0VJ1MbCT7xFZg6g= github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= @@ -616,8 +614,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.0 h1:quSiOM1GJPmPH5XtU+BCoVXcDVJJAzNcoyfC2cCjGkI= -google.golang.org/grpc v1.69.0/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= +google.golang.org/grpc v1.69.2 h1:U3S9QEtbXC0bYNvRtcoklF3xGtLViumSYxWykJS+7AU= +google.golang.org/grpc v1.69.2/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4= 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= From a5f5dc5ff892734d260575b5bc7349edade688a1 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Dec 2024 09:33:56 +0000 Subject: [PATCH 31/48] Bump buf.build/go/protoyaml from 0.3.0 to 0.3.1 Bumps [buf.build/go/protoyaml](https://github.com/bufbuild/protoyaml-go) from 0.3.0 to 0.3.1. - [Release notes](https://github.com/bufbuild/protoyaml-go/releases) - [Commits](https://github.com/bufbuild/protoyaml-go/compare/v0.3.0...v0.3.1) --- updated-dependencies: - dependency-name: buf.build/go/protoyaml dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 6 +++--- go.sum | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/go.mod b/go.mod index 6579102..06b70aa 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.23.0 toolchain go1.23.4 require ( - buf.build/go/protoyaml v0.3.0 + buf.build/go/protoyaml v0.3.1 cuelang.org/go v0.10.1 github.com/cofide/cofide-api-sdk v0.4.1-0.20241218164200-fd9d4ce5088c github.com/fatih/color v1.18.0 @@ -18,13 +18,13 @@ require ( github.com/spiffe/spire-api-sdk v1.11.1 github.com/stretchr/testify v1.10.0 google.golang.org/grpc v1.69.2 - google.golang.org/protobuf v1.35.2 + google.golang.org/protobuf v1.36.0 gopkg.in/yaml.v3 v3.0.1 helm.sh/helm/v3 v3.16.4 ) require ( - buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.35.2-20241127180247-a33202765966.1 // indirect + buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.0-20241127180247-a33202765966.1 // indirect cel.dev/expr v0.18.0 // indirect dario.cat/mergo v1.0.1 // indirect github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 // indirect diff --git a/go.sum b/go.sum index 327182c..31c74d9 100644 --- a/go.sum +++ b/go.sum @@ -1,7 +1,7 @@ -buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.35.2-20241127180247-a33202765966.1 h1:jLd96rDDNJ+zIJxvV/L855VEtrjR0G4aePVDlCpf6kw= -buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.35.2-20241127180247-a33202765966.1/go.mod h1:mnHCFccv4HwuIAOHNGdiIc5ZYbBCvbTWZcodLN5wITI= -buf.build/go/protoyaml v0.3.0 h1:NF/C1r0ezdUm8J3ABJsZqbsOVpT/Igrg2b//QPAIieo= -buf.build/go/protoyaml v0.3.0/go.mod h1:gaDlo8lOhxsvsBqblrvMFaVOcKVS1RQya8SGFB5XIxU= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.0-20241127180247-a33202765966.1 h1:ntAj16eF7AtUyzOOAFk5gvbAO52QmUKPKk7GmsIEORo= +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= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= @@ -629,8 +629,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= -google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ= +google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From 3be801616c0036e72508926705e8cfd92f126fc3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 23 Dec 2024 09:34:42 +0000 Subject: [PATCH 32/48] Bump k8s.io/kubectl from 0.31.3 to 0.32.0 Bumps [k8s.io/kubectl](https://github.com/kubernetes/kubectl) from 0.31.3 to 0.32.0. - [Commits](https://github.com/kubernetes/kubectl/compare/v0.31.3...v0.32.0) --- updated-dependencies: - dependency-name: k8s.io/kubectl dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 14 ++++++-------- go.sum | 30 +++++++++++++----------------- 2 files changed, 19 insertions(+), 25 deletions(-) diff --git a/go.mod b/go.mod index 6579102..9bf505b 100644 --- a/go.mod +++ b/go.mod @@ -60,7 +60,7 @@ require ( github.com/docker/go-metrics v0.0.1 // indirect github.com/emicklei/go-restful/v3 v3.12.0 // indirect github.com/evanphx/json-patch v5.9.0+incompatible // indirect - github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d // indirect + github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fxamacker/cbor/v2 v2.7.0 // indirect github.com/go-errors/errors v1.4.2 // indirect @@ -144,7 +144,6 @@ require ( 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.starlark.net v0.0.0-20230525235612-a134d8f9ddca // indirect golang.org/x/crypto v0.31.0 // indirect golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect golang.org/x/mod v0.20.0 // indirect @@ -159,23 +158,22 @@ require ( google.golang.org/genproto/googleapis/rpc v0.0.0-20241015192408-796eee8c2d53 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gotest.tools/v3 v3.5.0 // indirect k8s.io/api v0.32.0 k8s.io/apiextensions-apiserver v0.31.3 // indirect k8s.io/apimachinery v0.32.0 k8s.io/apiserver v0.31.3 // indirect - k8s.io/cli-runtime v0.31.3 // indirect + k8s.io/cli-runtime v0.32.0 // indirect k8s.io/client-go v0.32.0 - k8s.io/component-base v0.31.3 // indirect + k8s.io/component-base v0.32.0 // indirect k8s.io/klog/v2 v2.130.1 // indirect k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f // indirect - k8s.io/kubectl v0.31.3 + k8s.io/kubectl v0.32.0 k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect oras.land/oras-go v1.2.6 // indirect sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect - sigs.k8s.io/kustomize/api v0.17.2 // indirect - sigs.k8s.io/kustomize/kyaml v0.17.1 // indirect + sigs.k8s.io/kustomize/api v0.18.0 // indirect + sigs.k8s.io/kustomize/kyaml v0.18.1 // indirect sigs.k8s.io/structured-merge-diff/v4 v4.4.2 // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) diff --git a/go.sum b/go.sum index 327182c..d3d74ba 100644 --- a/go.sum +++ b/go.sum @@ -147,8 +147,8 @@ github.com/envoyproxy/protoc-gen-validate v1.1.0 h1:tntQDh69XqOCOZsDz0lVJQez/2L6 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/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d h1:105gxyaGwCFad8crR9dcMQWvV9Hvulu6hwUh4tWPJnM= -github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZMPRZwes7CROmyNKgQzC3XPs6L/G2EJLHddWejkmf4= +github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f h1:Wl78ApPPB2Wvf/TIe2xdyJxTlb6obmF18d8QdkxNDu4= +github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc= github.com/fatih/color v1.13.0/go.mod h1:kLAiJbzzSOZDVNGyDpeOxJ47H46qBXwg5ILebYFFOfk= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= @@ -224,7 +224,6 @@ github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMyw github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= 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.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/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= @@ -450,6 +449,7 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals= @@ -505,8 +505,6 @@ go.opentelemetry.io/otel/trace v1.31.0/go.mod h1:TXZkRk7SM2ZQLtR6eoAWQFIHPvzQ06F 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= -go.starlark.net v0.0.0-20230525235612-a134d8f9ddca h1:VdD38733bfYv5tUZwEIskMM93VanwNIi5bIKnDrJdEY= -go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= @@ -563,7 +561,6 @@ golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/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-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -574,7 +571,6 @@ golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/term v0.0.0-20220526004731-065cf7ba2467/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q= golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -662,28 +658,28 @@ k8s.io/apimachinery v0.32.0 h1:cFSE7N3rmEEtv4ei5X6DaJPHHX0C+upp+v5lVPiEwpg= k8s.io/apimachinery v0.32.0/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= k8s.io/apiserver v0.31.3 h1:+1oHTtCB+OheqFEz375D0IlzHZ5VeQKX1KGXnx+TTuY= k8s.io/apiserver v0.31.3/go.mod h1:PrxVbebxrxQPFhJk4powDISIROkNMKHibTg9lTRQ0Qg= -k8s.io/cli-runtime v0.31.3 h1:fEQD9Xokir78y7pVK/fCJN090/iYNrLHpFbGU4ul9TI= -k8s.io/cli-runtime v0.31.3/go.mod h1:Q2jkyTpl+f6AtodQvgDI8io3jrfr+Z0LyQBPJJ2Btq8= +k8s.io/cli-runtime v0.32.0 h1:dP+OZqs7zHPpGQMCGAhectbHU2SNCuZtIimRKTv2T1c= +k8s.io/cli-runtime v0.32.0/go.mod h1:Mai8ht2+esoDRK5hr861KRy6z0zHsSTYttNVJXgP3YQ= k8s.io/client-go v0.32.0 h1:DimtMcnN/JIKZcrSrstiwvvZvLjG0aSxy8PxN8IChp8= k8s.io/client-go v0.32.0/go.mod h1:boDWvdM1Drk4NJj/VddSLnx59X3OPgwrOo0vGbtq9+8= -k8s.io/component-base v0.31.3 h1:DMCXXVx546Rfvhj+3cOm2EUxhS+EyztH423j+8sOwhQ= -k8s.io/component-base v0.31.3/go.mod h1:xME6BHfUOafRgT0rGVBGl7TuSg8Z9/deT7qq6w7qjIU= +k8s.io/component-base v0.32.0 h1:d6cWHZkCiiep41ObYQS6IcgzOUQUNpywm39KVYaUqzU= +k8s.io/component-base v0.32.0/go.mod h1:JLG2W5TUxUu5uDyKiH2R/7NnxJo1HlPoRIIbVLkK5eM= k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk= k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE= k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f h1:GA7//TjRY9yWGy1poLzYYJJ4JRdzg3+O6e8I+e+8T5Y= k8s.io/kube-openapi v0.0.0-20241105132330-32ad38e42d3f/go.mod h1:R/HEjbvWI0qdfb8viZUeVZm0X6IZnxAydC7YU42CMw4= -k8s.io/kubectl v0.31.3 h1:3r111pCjPsvnR98oLLxDMwAeM6OPGmPty6gSKaLTQes= -k8s.io/kubectl v0.31.3/go.mod h1:lhMECDCbJN8He12qcKqs2QfmVo9Pue30geovBVpH5fs= +k8s.io/kubectl v0.32.0 h1:rpxl+ng9qeG79YA4Em9tLSfX0G8W0vfaiPVrc/WR7Xw= +k8s.io/kubectl v0.32.0/go.mod h1:qIjSX+QgPQUgdy8ps6eKsYNF+YmFOAO3WygfucIqFiE= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 h1:M3sRQVHv7vB20Xc2ybTt7ODCeFj6JSWYFzOFnYeS6Ro= k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= oras.land/oras-go v1.2.6 h1:z8cmxQXBU8yZ4mkytWqXfo6tZcamPwjsuxYU81xJ8Lk= oras.land/oras-go v1.2.6/go.mod h1:OVPc1PegSEe/K8YiLfosrlqlqTN9PUyFvOw5Y9gwrT8= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 h1:/Rv+M11QRah1itp8VhT6HoVx1Ray9eB4DBr+K+/sCJ8= sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3/go.mod h1:18nIHnGi6636UCz6m8i4DhaJ65T6EruyzmoQqI2BVDo= -sigs.k8s.io/kustomize/api v0.17.2 h1:E7/Fjk7V5fboiuijoZHgs4aHuexi5Y2loXlVOAVAG5g= -sigs.k8s.io/kustomize/api v0.17.2/go.mod h1:UWTz9Ct+MvoeQsHcJ5e+vziRRkwimm3HytpZgIYqye0= -sigs.k8s.io/kustomize/kyaml v0.17.1 h1:TnxYQxFXzbmNG6gOINgGWQt09GghzgTP6mIurOgrLCQ= -sigs.k8s.io/kustomize/kyaml v0.17.1/go.mod h1:9V0mCjIEYjlXuCdYsSXvyoy2BTsLESH7TlGV81S282U= +sigs.k8s.io/kustomize/api v0.18.0 h1:hTzp67k+3NEVInwz5BHyzc9rGxIauoXferXyjv5lWPo= +sigs.k8s.io/kustomize/api v0.18.0/go.mod h1:f8isXnX+8b+SGLHQ6yO4JG1rdkZlvhaCf/uZbLVMb0U= +sigs.k8s.io/kustomize/kyaml v0.18.1 h1:WvBo56Wzw3fjS+7vBjN6TeivvpbW9GmRaWZ9CIVmt4E= +sigs.k8s.io/kustomize/kyaml v0.18.1/go.mod h1:C3L2BFVU1jgcddNBE1TxuVLgS46TjObMwW5FT9FcjYo= sigs.k8s.io/structured-merge-diff/v4 v4.4.2 h1:MdmvkGuXi/8io6ixD5wud3vOLwc1rj0aNqRlpuvjmwA= sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= From 8287ec5d054402f20e85c081bddb188e6bdf4dbf Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Mon, 23 Dec 2024 14:02:00 +0000 Subject: [PATCH 33/48] Add workload status command This change adds a 'cofidectl workload status' command. The command accepts a trust zone, pod name and namespace, and deploys an ephemeral debug container to the pod. This container emits diagnostic information about the SVIDs provided to the workload. This requires us to set disableContainerSelectors=true in the SPIRE Helm configuration, to allow the debug container to obtain an ID. Fixes: #14 Co-Authored-By: Matt Bates Co-Authored-By: Maartje Eyskens --- cmd/cofidectl/cmd/workload/workload.go | 92 ++++++++++++- internal/pkg/trustprovider/trustprovider.go | 2 +- internal/pkg/workload/workload.go | 140 +++++++++++++++++++- pkg/provider/helm/values_test.go | 20 +-- 4 files changed, 240 insertions(+), 14 deletions(-) diff --git a/cmd/cofidectl/cmd/workload/workload.go b/cmd/cofidectl/cmd/workload/workload.go index 400594d..68662da 100644 --- a/cmd/cofidectl/cmd/workload/workload.go +++ b/cmd/cofidectl/cmd/workload/workload.go @@ -8,9 +8,12 @@ import ( "fmt" "os" + provisionpb "github.com/cofide/cofide-api-sdk/gen/go/proto/provision_plugin/v1alpha1" trust_zone_proto "github.com/cofide/cofide-api-sdk/gen/go/proto/trust_zone/v1alpha1" + "github.com/cofide/cofidectl/cmd/cofidectl/cmd/statusspinner" "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/provider/helm" "github.com/olekukonko/tablewriter" "github.com/spf13/cobra" @@ -32,13 +35,14 @@ This command consists of multiple sub-commands to interact with workloads. func (c *WorkloadCommand) GetRootCommand() *cobra.Command { cmd := &cobra.Command{ - Use: "workload list|discover [ARGS]", - Short: "List workloads in a trust zone or discover candidate workloads", + Use: "workload list|discover|status [ARGS]", + Short: "List or introspect the status of workloads in a trust zone or discover candidate workloads", Long: workloadRootCmdDesc, Args: cobra.NoArgs, } cmd.AddCommand( + c.GetStatusCommand(), c.GetListCommand(), c.GetDiscoverCommand(), ) @@ -108,6 +112,77 @@ func (w *WorkloadCommand) GetListCommand() *cobra.Command { return cmd } +var workloadStatusCmdDesc = ` +This command will display the status of workloads in the Cofide configuration state. +` + +type StatusOpts struct { + podName string + namespace string + trustZone string +} + +func (w *WorkloadCommand) GetStatusCommand() *cobra.Command { + opts := StatusOpts{} + cmd := &cobra.Command{ + Use: "status [ARGS]", + Short: "Display workload status", + Long: workloadStatusCmdDesc, + Args: cobra.NoArgs, + RunE: func(cmd *cobra.Command, args []string) error { + 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) + }, + } + + f := cmd.Flags() + f.StringVar(&opts.podName, "pod-name", "", "Pod name for the workload") + f.StringVar(&opts.namespace, "namespace", "", "Namespace for the workload") + f.StringVar(&opts.trustZone, "trust-zone", "", "Trust zone for the workload") + + cobra.CheckErr(cmd.MarkFlagRequired("pod-name")) + cobra.CheckErr(cmd.MarkFlagRequired("namespace")) + cobra.CheckErr(cmd.MarkFlagRequired("trust-zone")) + + 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 + } + + trustZone, err := ds.GetTrustZone(opts.trustZone) + if err != nil { + return err + } + + client, err := kubeutil.NewKubeClientFromSpecifiedContext(kubeConfig, *trustZone.KubernetesContext) + if err != nil { + return err + } + + statusCh, dataCh := getWorkloadStatus(ctx, client, opts.podName, opts.namespace) + + // Create a spinner to display whilst the debug container is created and executed and logs retrieved + if err := statusspinner.WatchProvisionStatus(ctx, statusCh, false); err != nil { + return fmt.Errorf("retrieving workload status failed: %w", err) + } + + result := <-dataCh + if result == "" { + return fmt.Errorf("retrieving workload status failed") + } + + fmt.Println(result) + return nil +} + func renderRegisteredWorkloads(ctx context.Context, kubeConfig string, trustZones []*trust_zone_proto.TrustZone) error { data := make([][]string, 0, len(trustZones)) @@ -144,6 +219,19 @@ func renderRegisteredWorkloads(ctx context.Context, kubeConfig string, trustZone return nil } +func getWorkloadStatus(ctx context.Context, client *kubeutil.Client, podName string, namespace string) (<-chan *provisionpb.Status, chan string) { + statusCh := make(chan *provisionpb.Status) + dataCh := make(chan string, 1) + + go func() { + defer close(statusCh) + defer close(dataCh) + workload.GetStatus(ctx, statusCh, dataCh, client, podName, namespace) + }() + + return statusCh, dataCh +} + var workloadDiscoverCmdDesc = ` This command will discover all of the unregistered workloads. ` diff --git a/internal/pkg/trustprovider/trustprovider.go b/internal/pkg/trustprovider/trustprovider.go index 5c543ee..3a460a0 100644 --- a/internal/pkg/trustprovider/trustprovider.go +++ b/internal/pkg/trustprovider/trustprovider.go @@ -41,7 +41,7 @@ func (tp *TrustProvider) GetValues() error { WorkloadAttestorConfig: map[string]any{ "enabled": true, "skipKubeletVerification": true, - "disableContainerSelectors": false, + "disableContainerSelectors": true, "useNewContainerLocator": false, "verboseContainerLocatorLogs": false, }, diff --git a/internal/pkg/workload/workload.go b/internal/pkg/workload/workload.go index 1224ea3..ef7ab4a 100644 --- a/internal/pkg/workload/workload.go +++ b/internal/pkg/workload/workload.go @@ -4,16 +4,24 @@ package workload import ( + "bytes" "context" "fmt" + "io" "time" - "github.com/cofide/cofidectl/pkg/spire" + provisionpb "github.com/cofide/cofide-api-sdk/gen/go/proto/provision_plugin/v1alpha1" kubeutil "github.com/cofide/cofidectl/pkg/kube" + "github.com/cofide/cofidectl/pkg/plugin/provision" + "github.com/cofide/cofidectl/pkg/spire" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/util/rand" ) +const debugContainerNamePrefix = "cofidectl-debug" +const debugContainerImage = "ghcr.io/cofide/cofidectl-debug-container/cmd:v0.1.0" + type Workload struct { Name string Namespace string @@ -134,6 +142,136 @@ func GetUnregisteredWorkloads(ctx context.Context, kubeCfgFile string, kubeConte return unregisteredWorkloads, nil } +func GetStatus(ctx context.Context, statusCh chan<- *provisionpb.Status, dataCh chan string, client *kubeutil.Client, podName string, namespace string) { + debugContainerName := fmt.Sprintf("%s-%s", debugContainerNamePrefix, rand.String(5)) + + statusCh <- provision.StatusOk( + "Creating", + fmt.Sprintf("Waiting for ephemeral debug container to be created in %s", podName), + ) + + if err := createDebugContainer(ctx, client, podName, namespace, debugContainerName); err != nil { + statusCh <- provision.StatusError( + "Creating", + fmt.Sprintf("Failed waiting for ephemeral debug container to be created in %s", podName), + err, + ) + return + } + + statusCh <- provision.StatusOk( + "Waiting", + "Waiting for ephemeral debug container to complete", + ) + + if err := waitForDebugContainer(ctx, client, podName, namespace, debugContainerName); err != nil { + statusCh <- provision.StatusError( + "Waiting", + "Error waiting for ephemeral debug container to complete", + err, + ) + return + } + + logs, err := getDebugContainerLogs(ctx, client, podName, namespace, debugContainerName) + if err != nil { + statusCh <- provision.StatusError( + "Waiting", + "Error waiting for ephemeral debug container logs", + err, + ) + return + } + + dataCh <- logs + statusCh <- provision.StatusDone( + "Complete", + fmt.Sprintf("Successfully executed emphemeral debug container in %s", podName), + ) +} + +func createDebugContainer(ctx context.Context, client *kubeutil.Client, podName string, namespace string, debugContainerName string) error { + pod, err := client.Clientset.CoreV1().Pods(namespace).Get(ctx, podName, metav1.GetOptions{}) + if err != nil { + return err + } + + debugContainer := v1.EphemeralContainer{ + EphemeralContainerCommon: v1.EphemeralContainerCommon{ + Name: debugContainerName, + Image: debugContainerImage, + ImagePullPolicy: v1.PullIfNotPresent, + TTY: true, + Stdin: true, + VolumeMounts: []v1.VolumeMount{ + { + ReadOnly: true, + Name: "spiffe-workload-api", + MountPath: "/spiffe-workload-api", + }}, + }, + TargetContainerName: pod.Spec.Containers[0].Name, + } + + pod.Spec.EphemeralContainers = append(pod.Spec.EphemeralContainers, debugContainer) + + _, err = client.Clientset.CoreV1().Pods(namespace).UpdateEphemeralContainers( + ctx, + pod.Name, + pod, + metav1.UpdateOptions{}, + ) + if err != nil { + return err + } + return nil +} + +func waitForDebugContainer(ctx context.Context, client *kubeutil.Client, podName string, namespace string, debugContainerName string) error { + waitCtx, cancel := context.WithTimeout(ctx, 5*time.Minute) + defer cancel() + + for { + pod, err := client.Clientset.CoreV1().Pods(namespace).Get(ctx, podName, metav1.GetOptions{}) + if err != nil { + return err + } + + for _, status := range pod.Status.EphemeralContainerStatuses { + if status.Name == debugContainerName && status.State.Terminated != nil { + return nil + } + } + + select { + case <-waitCtx.Done(): + return err + default: + time.Sleep(time.Second) + continue + } + } +} + +func getDebugContainerLogs(ctx context.Context, client *kubeutil.Client, podName string, namespace string, debugContainerName string) (string, error) { + logs, err := client.Clientset.CoreV1().Pods(namespace).GetLogs(podName, &v1.PodLogOptions{ + Container: debugContainerName, + }).Stream(ctx) + if err != nil { + return "", err + } + defer logs.Close() + + // Read the logs + buf := new(bytes.Buffer) + _, err = io.Copy(buf, logs) + if err != nil { + return "", err + } + + return buf.String(), nil +} + func isAtRisk(creationTS time.Time) (time.Duration, bool) { // Consider secrets older than 30 days as long-lived and a source for potential risk age := time.Since(creationTS) diff --git a/pkg/provider/helm/values_test.go b/pkg/provider/helm/values_test.go index 2ecd30c..58296e4 100644 --- a/pkg/provider/helm/values_test.go +++ b/pkg/provider/helm/values_test.go @@ -78,7 +78,7 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { }, "workloadAttestors": Values{ "k8s": Values{ - "disableContainerSelectors": false, + "disableContainerSelectors": true, "enabled": true, "skipKubeletVerification": true, "useNewContainerLocator": false, @@ -169,7 +169,7 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { }, "workloadAttestors": Values{ "k8s": Values{ - "disableContainerSelectors": false, + "disableContainerSelectors": true, "enabled": true, "skipKubeletVerification": true, "useNewContainerLocator": false, @@ -279,7 +279,7 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { }, "workloadAttestors": Values{ "k8s": Values{ - "disableContainerSelectors": false, + "disableContainerSelectors": true, "enabled": true, "skipKubeletVerification": true, "useNewContainerLocator": false, @@ -417,7 +417,7 @@ func TestHelmValuesGenerator_GenerateValues_AdditionalValues(t *testing.T) { }, "workloadAttestors": Values{ "k8s": Values{ - "disableContainerSelectors": false, + "disableContainerSelectors": true, "enabled": true, "skipKubeletVerification": true, "useNewContainerLocator": false, @@ -1084,7 +1084,7 @@ func TestSpireAgentValues_GenerateValues(t *testing.T) { WorkloadAttestorConfig: map[string]any{ "enabled": true, "skipKubeletVerification": true, - "disableContainerSelectors": false, + "disableContainerSelectors": true, "useNewContainerLocator": false, "verboseContainerLocatorLogs": false, }, @@ -1121,7 +1121,7 @@ func TestSpireAgentValues_GenerateValues(t *testing.T) { "k8s": map[string]any{ "enabled": true, "skipKubeletVerification": true, - "disableContainerSelectors": false, + "disableContainerSelectors": true, "useNewContainerLocator": false, "verboseContainerLocatorLogs": false, }, @@ -1140,7 +1140,7 @@ func TestSpireAgentValues_GenerateValues(t *testing.T) { WorkloadAttestorConfig: map[string]any{ "enabled": true, "skipKubeletVerification": true, - "disableContainerSelectors": false, + "disableContainerSelectors": true, "useNewContainerLocator": false, "verboseContainerLocatorLogs": false, }, @@ -1194,7 +1194,7 @@ func TestSpireAgentValues_GenerateValues(t *testing.T) { WorkloadAttestorConfig: map[string]any{ "enabled": true, "skipKubeletVerification": true, - "disableContainerSelectors": false, + "disableContainerSelectors": true, "useNewContainerLocator": false, "verboseContainerLocatorLogs": false, }, @@ -1224,7 +1224,7 @@ func TestSpireAgentValues_GenerateValues(t *testing.T) { WorkloadAttestorConfig: map[string]any{ "enabled": true, "skipKubeletVerification": true, - "disableContainerSelectors": false, + "disableContainerSelectors": true, "useNewContainerLocator": false, "verboseContainerLocatorLogs": false, }, @@ -1249,7 +1249,7 @@ func TestSpireAgentValues_GenerateValues(t *testing.T) { WorkloadAttestorConfig: map[string]any{ "enabled": true, "skipKubeletVerification": true, - "disableContainerSelectors": false, + "disableContainerSelectors": true, "useNewContainerLocator": false, "verboseContainerLocatorLogs": false, }, From 4ee0f79558a394582a8fbcc90c535f19ccfd8dc9 Mon Sep 17 00:00:00 2001 From: Nial Daly <34862917+nialdaly@users.noreply.github.com> Date: Tue, 24 Dec 2024 09:15:55 +0000 Subject: [PATCH 34/48] cofidectl-debug-container image renamed to remove cmd suffix (#108) --- internal/pkg/workload/workload.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/pkg/workload/workload.go b/internal/pkg/workload/workload.go index ef7ab4a..d008da4 100644 --- a/internal/pkg/workload/workload.go +++ b/internal/pkg/workload/workload.go @@ -20,7 +20,7 @@ import ( ) const debugContainerNamePrefix = "cofidectl-debug" -const debugContainerImage = "ghcr.io/cofide/cofidectl-debug-container/cmd:v0.1.0" +const debugContainerImage = "ghcr.io/cofide/cofidectl-debug-container:v0.1.0" type Workload struct { Name string From 029180dde3582044d403463595a5d46710894fde Mon Sep 17 00:00:00 2001 From: Nial Daly <34862917+nialdaly@users.noreply.github.com> Date: Tue, 24 Dec 2024 14:50:04 +0000 Subject: [PATCH 35/48] Bumped golang.org/x/net to v0.33.0 (#110) --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index af313e8..a7264e5 100644 --- a/go.mod +++ b/go.mod @@ -147,7 +147,7 @@ require ( golang.org/x/crypto v0.31.0 // indirect golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 // indirect golang.org/x/mod v0.20.0 // indirect - golang.org/x/net v0.30.0 // indirect + golang.org/x/net v0.33.0 // indirect golang.org/x/oauth2 v0.23.0 // indirect golang.org/x/sync v0.10.0 // indirect golang.org/x/sys v0.28.0 // indirect diff --git a/go.sum b/go.sum index a31f2bd..5c7d714 100644 --- a/go.sum +++ b/go.sum @@ -535,8 +535,8 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= -golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4= -golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= +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.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= From 86586bf41e12947dc844b0f63c49efef6db449b8 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Dec 2024 02:09:43 +0000 Subject: [PATCH 36/48] Bump google.golang.org/protobuf from 1.36.0 to 1.36.1 Bumps google.golang.org/protobuf from 1.36.0 to 1.36.1. --- updated-dependencies: - dependency-name: google.golang.org/protobuf dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index a7264e5..93cb330 100644 --- a/go.mod +++ b/go.mod @@ -18,7 +18,7 @@ require ( github.com/spiffe/spire-api-sdk v1.11.1 github.com/stretchr/testify v1.10.0 google.golang.org/grpc v1.69.2 - google.golang.org/protobuf v1.36.0 + google.golang.org/protobuf v1.36.1 gopkg.in/yaml.v3 v3.0.1 helm.sh/helm/v3 v3.16.4 ) diff --git a/go.sum b/go.sum index 5c7d714..add910d 100644 --- a/go.sum +++ b/go.sum @@ -625,8 +625,8 @@ google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp0 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= -google.golang.org/protobuf v1.36.0 h1:mjIs9gYtt56AzC4ZaffQuh88TZurBGhIJMBZGSxNerQ= -google.golang.org/protobuf v1.36.0/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +google.golang.org/protobuf v1.36.1 h1:yBPeRvTftaleIgM3PZ/WBIZ7XM/eEYAaEyCwvyjq/gk= +google.golang.org/protobuf v1.36.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= From ed298fc8ea94414ee70d0f9f199d291c39f02ef9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 30 Dec 2024 02:09:55 +0000 Subject: [PATCH 37/48] Bump github.com/cofide/cofide-api-sdk Bumps [github.com/cofide/cofide-api-sdk](https://github.com/cofide/cofide-api-sdk) from 0.4.1-0.20241218164200-fd9d4ce5088c to 0.5.0. - [Release notes](https://github.com/cofide/cofide-api-sdk/releases) - [Commits](https://github.com/cofide/cofide-api-sdk/commits/v0.5.0) --- updated-dependencies: - dependency-name: github.com/cofide/cofide-api-sdk dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- go.mod | 2 +- go.sum | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/go.mod b/go.mod index a7264e5..6d27199 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.4.1-0.20241218164200-fd9d4ce5088c + github.com/cofide/cofide-api-sdk v0.5.0 github.com/fatih/color v1.18.0 github.com/gofrs/flock v0.12.1 github.com/google/go-cmp v0.6.0 diff --git a/go.sum b/go.sum index 5c7d714..ab3f8ab 100644 --- a/go.sum +++ b/go.sum @@ -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.4.1-0.20241218164200-fd9d4ce5088c h1:nEdnzITNrjaBjP6VRCEsCyFF5nUI5M0nBDB98JZrZEI= -github.com/cofide/cofide-api-sdk v0.4.1-0.20241218164200-fd9d4ce5088c/go.mod h1:uChrsD6JvNDSCsAxXil3gGqTHxee0VJ1MbCT7xFZg6g= +github.com/cofide/cofide-api-sdk v0.5.0 h1:7lXelqgsay6szdzmEzqoqs4E9+HHO7K7gCJhT4LFR6s= +github.com/cofide/cofide-api-sdk v0.5.0/go.mod h1:uChrsD6JvNDSCsAxXil3gGqTHxee0VJ1MbCT7xFZg6g= 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.23 h1:H2CClyUkmpKAGlhQp95g2WXHfLYc7whAuvZGBNYOOwQ= From a225c06d01607d5011ecc4d39cb2dc7b1b860294 Mon Sep 17 00:00:00 2001 From: Nial Daly <34862917+nialdaly@users.noreply.github.com> Date: Thu, 2 Jan 2025 12:27:28 +0000 Subject: [PATCH 38/48] fix: Update `goheader` linter setting to support 2024 and onwards (#115) --- .golangci.yaml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.golangci.yaml b/.golangci.yaml index 6edecbb..fd6afcb 100644 --- a/.golangci.yaml +++ b/.golangci.yaml @@ -7,7 +7,9 @@ linters-settings: values: const: COMPANY: Cofide Limited + regexp: + VALID_YEAR: 202[4-9]|20[3-9][0-9]|2[1-9][0-9][0-9] # Require Cofide copyright and SPDX license in all source files. - template: | - Copyright {{ MOD-YEAR }} {{ COMPANY }}. - SPDX-License-Identifier: Apache-2.0 \ No newline at end of file + template: |- + Copyright {{ VALID_YEAR }} {{ COMPANY }}. + SPDX-License-Identifier: Apache-2.0 From bde84465f0ef0f60374ad563fc6a90522b722e39 Mon Sep 17 00:00:00 2001 From: Nial Daly <34862917+nialdaly@users.noreply.github.com> Date: Thu, 2 Jan 2025 15:59:40 +0000 Subject: [PATCH 39/48] feat: `cofidectl workload status` command added to each integration test (#109) --- tests/integration/federation/test.sh | 19 +++++++++++++++++++ tests/integration/single-trust-zone/test.sh | 19 +++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/tests/integration/federation/test.sh b/tests/integration/federation/test.sh index 6a90d2e..c37ae53 100755 --- a/tests/integration/federation/test.sh +++ b/tests/integration/federation/test.sh @@ -112,6 +112,24 @@ function post_deploy() { fi } +function show_workload_status() { + POD_NAME=$(kubectl get pods -l app=ping-pong-client \ + -n $NAMESPACE_POLICY_NAMESPACE \ + -o jsonpath='{.items[0].metadata.name}' \ + --context $K8S_CLUSTER_1_CONTEXT) + WORKLOAD_STATUS_RESPONSE=$(./cofidectl workload status --namespace $NAMESPACE_POLICY_NAMESPACE \ + --pod-name $POD_NAME \ + --trust-zone $TRUST_ZONE_1) + + if [[ $WORKLOAD_STATUS_RESPONSE != *"SVID verified against trust bundle"* ]]; then + echo "cofidectl workload status unsuccessful" + exit 1 + fi + + echo "cofidectl workload status successful" + exit 0 +} + function down() { ./cofidectl down } @@ -126,6 +144,7 @@ function main() { show_status run_tests post_deploy + show_workload_status down echo "Success!" } diff --git a/tests/integration/single-trust-zone/test.sh b/tests/integration/single-trust-zone/test.sh index 6391ea7..4a501ab 100755 --- a/tests/integration/single-trust-zone/test.sh +++ b/tests/integration/single-trust-zone/test.sh @@ -80,6 +80,24 @@ function wait_for_pong() { return 1 } +function show_workload_status() { + POD_NAME=$(kubectl get pods -l app=ping-pong-client \ + -n $NAMESPACE_POLICY_NAMESPACE \ + -o jsonpath='{.items[0].metadata.name}' \ + --context $K8S_CLUSTER_CONTEXT) + WORKLOAD_STATUS_RESPONSE=$(./cofidectl workload status --namespace $NAMESPACE_POLICY_NAMESPACE \ + --pod-name $POD_NAME \ + --trust-zone $TRUST_ZONE) + + if [[ $WORKLOAD_STATUS_RESPONSE != *"SVID verified against trust bundle"* ]]; then + echo "cofidectl workload status unsuccessful" + exit 1 + fi + + echo "cofidectl workload status successful" + exit 0 +} + function down() { ./cofidectl down } @@ -92,6 +110,7 @@ function main() { show_config show_status run_tests + show_workload_status down echo "Success!" } From 212260f33cec31aa89bd916b6535eb38bd384851 Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Thu, 2 Jan 2025 15:36:37 +0000 Subject: [PATCH 40/48] Enable Helm chart recommendations, use 3-namespace deployment Setting the Helm chart recommendations enabled flag applies various changes to the configuration, as described in [1]: - 2 namespace layout (spire-server & spire-system) - Namespace pod security standards - Priority class name - Prometheus - Strict mode - Security contexts We also use a 3rd namespace for the Helm chart state and CRDs as described in [2]. [1] https://spiffe.io/docs/latest/spire-helm-charts-hardened-about/recommendations/ [2] https://spiffe.io/docs/latest/spire-helm-charts-hardened-about/namespaces/ Fixes: #113 Fixes: #114 --- internal/pkg/config/testdata/config/full.yaml | 6 ++- internal/pkg/test/fixtures/fixtures.go | 6 ++- internal/pkg/workload/workload.go | 15 +++++--- pkg/provider/helm/helm.go | 7 ++-- pkg/provider/helm/values.go | 11 ++++-- pkg/provider/helm/values_test.go | 37 +++++++++++++++---- pkg/spire/spire.go | 18 +++++---- pkg/spire/spire_server_cli.go | 2 +- pkg/spire/spire_server_cli_test.go | 2 +- pkg/spire/spire_test.go | 10 ++--- 10 files changed, 76 insertions(+), 38 deletions(-) diff --git a/internal/pkg/config/testdata/config/full.yaml b/internal/pkg/config/testdata/config/full.yaml index 8e6626e..e597269 100644 --- a/internal/pkg/config/testdata/config/full.yaml +++ b/internal/pkg/config/testdata/config/full.yaml @@ -18,8 +18,10 @@ trust_zones: extra_helm_values: global: spire: - namespaces: - create: true + caSubject: + commonName: cn.example.com + country: UK + organization: acme-org spire-server: logLevel: INFO bundle_endpoint_profile: BUNDLE_ENDPOINT_PROFILE_HTTPS_SPIFFE diff --git a/internal/pkg/test/fixtures/fixtures.go b/internal/pkg/test/fixtures/fixtures.go index a75b6cb..8728db7 100644 --- a/internal/pkg/test/fixtures/fixtures.go +++ b/internal/pkg/test/fixtures/fixtures.go @@ -46,8 +46,10 @@ var trustZoneFixtures map[string]*trust_zone_proto.TrustZone = map[string]*trust ev := map[string]any{ "global": map[string]any{ "spire": map[string]any{ - "namespaces": map[string]any{ - "create": true, + "caSubject": map[string]any{ + "country": "UK", + "organization": "acme-org", + "commonName": "cn.example.com", }, }, }, diff --git a/internal/pkg/workload/workload.go b/internal/pkg/workload/workload.go index d008da4..67cacd0 100644 --- a/internal/pkg/workload/workload.go +++ b/internal/pkg/workload/workload.go @@ -77,12 +77,15 @@ func GetRegisteredWorkloads(ctx context.Context, kubeConfig string, kubeContext // GetUnregisteredWorkloads will discover workloads in a Kubernetes cluster that are not (yet) registered func GetUnregisteredWorkloads(ctx context.Context, kubeCfgFile string, kubeContext string, secretDiscovery bool, checkSpire bool) ([]Workload, error) { // Includes the initial Kubernetes namespaces. - ignoredNamespaces := map[string]int{ - "kube-node-lease": 1, - "kube-public": 2, - "kube-system": 3, - "local-path-storage": 4, - "spire": 5, + ignoredNamespaces := map[string]bool{ + "kube-node-lease": true, + "kube-public": true, + "kube-system": true, + "local-path-storage": true, + "spire": true, + "spire-server": true, + "spire-system": true, + "spire-mgmt": true, } client, err := kubeutil.NewKubeClientFromSpecifiedContext(kubeCfgFile, kubeContext) diff --git a/pkg/provider/helm/helm.go b/pkg/provider/helm/helm.go index f31b597..28bbf27 100644 --- a/pkg/provider/helm/helm.go +++ b/pkg/provider/helm/helm.go @@ -34,7 +34,8 @@ const ( SPIRECRDsChartName = "spire-crds" SPIRECRDsChartVersion = "0.4.0" - SPIRENamespace = "spire" + // Kubernetes namespace in which Helm charts and CRDs will be installed. + SPIREManagementNamespace = "spire-mgmt" ) // Type assertion that HelmSPIREProvider implements the Provider interface. @@ -261,7 +262,7 @@ func newInstall(cfg *action.Configuration, chart string, version string) *action install := action.NewInstall(cfg) install.Version = version install.ReleaseName = chart - install.Namespace = SPIRENamespace + install.Namespace = SPIREManagementNamespace install.CreateNamespace = true return install } @@ -308,7 +309,7 @@ func installChart(ctx context.Context, cfg *action.Configuration, client *action func newUpgrade(cfg *action.Configuration, version string) *action.Upgrade { upgrade := action.NewUpgrade(cfg) - upgrade.Namespace = SPIRENamespace + upgrade.Namespace = SPIREManagementNamespace upgrade.Version = version upgrade.ReuseValues = true return upgrade diff --git a/pkg/provider/helm/values.go b/pkg/provider/helm/values.go index e7f537c..30de5e2 100644 --- a/pkg/provider/helm/values.go +++ b/pkg/provider/helm/values.go @@ -24,8 +24,9 @@ type globalValues struct { deleteHooks bool installAndUpgradeHooksEnabled bool spireClusterName string - spireCreateRecommendations bool spireJwtIssuer string + spireNamespacesCreate bool + spireRecommendationsEnabled bool spireTrustDomain string } @@ -73,8 +74,9 @@ func (g *HelmValuesGenerator) GenerateValues() (map[string]any, error) { gv := globalValues{ spireClusterName: g.trustZone.GetKubernetesCluster(), - spireCreateRecommendations: true, spireJwtIssuer: g.trustZone.GetJwtIssuer(), + spireNamespacesCreate: true, + spireRecommendationsEnabled: true, spireTrustDomain: g.trustZone.TrustDomain, installAndUpgradeHooksEnabled: false, deleteHooks: false, @@ -243,8 +245,11 @@ func (g *globalValues) generateValues() (map[string]any, error) { "global": map[string]any{ "spire": map[string]any{ "clusterName": g.spireClusterName, + "namespaces": map[string]any{ + "create": g.spireNamespacesCreate, + }, "recommendations": map[string]any{ - "create": g.spireCreateRecommendations, + "enabled": g.spireRecommendationsEnabled, }, "trustDomain": g.spireTrustDomain, }, diff --git a/pkg/provider/helm/values_test.go b/pkg/provider/helm/values_test.go index 58296e4..dc7299a 100644 --- a/pkg/provider/helm/values_test.go +++ b/pkg/provider/helm/values_test.go @@ -47,9 +47,12 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { }, "spire": Values{ "clusterName": "local1", - "recommendations": Values{ + "namespaces": Values{ "create": true, }, + "recommendations": Values{ + "enabled": true, + }, "trustDomain": "td1", }, }, @@ -133,13 +136,18 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { "enabled": false, }, "spire": Values{ + "caSubject": Values{ + "country": "UK", + "organization": "acme-org", + "commonName": "cn.example.com", + }, "clusterName": "local1", "jwtIssuer": "https://tz1.example.com", "namespaces": Values{ "create": true, }, "recommendations": Values{ - "create": true, + "enabled": true, }, "trustDomain": "td1", }, @@ -248,9 +256,12 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { }, "spire": Values{ "clusterName": "local4", - "recommendations": Values{ + "namespaces": Values{ "create": true, }, + "recommendations": Values{ + "enabled": true, + }, "trustDomain": "td4", }, }, @@ -386,9 +397,12 @@ func TestHelmValuesGenerator_GenerateValues_AdditionalValues(t *testing.T) { }, "spire": Values{ "clusterName": "local1", - "recommendations": Values{ + "namespaces": Values{ "create": true, }, + "recommendations": Values{ + "enabled": true, + }, "trustDomain": "td1", }, }, @@ -973,9 +987,12 @@ func TestGlobalValues_GenerateValues(t *testing.T) { "global": map[string]any{ "spire": map[string]any{ "clusterName": "local1", - "recommendations": map[string]any{ + "namespaces": Values{ "create": false, }, + "recommendations": map[string]any{ + "enabled": false, + }, "trustDomain": "td1", }, "installAndUpgradeHooks": map[string]any{ @@ -999,9 +1016,12 @@ func TestGlobalValues_GenerateValues(t *testing.T) { "global": map[string]any{ "spire": map[string]any{ "clusterName": "local1", - "recommendations": map[string]any{ + "namespaces": Values{ "create": false, }, + "recommendations": map[string]any{ + "enabled": false, + }, "trustDomain": "td1", }, "installAndUpgradeHooks": map[string]any{ @@ -1026,9 +1046,12 @@ func TestGlobalValues_GenerateValues(t *testing.T) { "spire": map[string]any{ "clusterName": "local1", "jwtIssuer": "https://tz1.example.com", - "recommendations": map[string]any{ + "namespaces": Values{ "create": false, }, + "recommendations": map[string]any{ + "enabled": false, + }, "trustDomain": "td1", }, "installAndUpgradeHooks": map[string]any{ diff --git a/pkg/spire/spire.go b/pkg/spire/spire.go index 52c2848..7e1d011 100644 --- a/pkg/spire/spire.go +++ b/pkg/spire/spire.go @@ -24,14 +24,16 @@ import ( ) const ( - namespace = "spire" + serverNamespace = "spire-server" serverStatefulsetName = "spire-server" serverPodName = "spire-server-0" serverContainerName = "spire-server" serverServiceName = "spire-server" serverExecutable = "/opt/spire/bin/spire-server" scmContainerName = "spire-controller-manager" - agentDaemonSetName = "spire-agent" + + agentNamespace = "spire-system" + agentDaemonSetName = "spire-agent" ) // ServerStatus contains status information about a running SPIRE server cluster. @@ -80,7 +82,7 @@ func GetServerStatus(ctx context.Context, client *kubeutil.Client) (*ServerStatu func getServerStatefulSet(ctx context.Context, client *kubeutil.Client) (*appsv1.StatefulSet, error) { return client.Clientset.AppsV1(). - StatefulSets(namespace). + StatefulSets(serverNamespace). Get(ctx, serverStatefulsetName, metav1.GetOptions{}) } @@ -88,7 +90,7 @@ func getPodsForStatefulSet(ctx context.Context, client *kubeutil.Client, statefu set := labels.Set(statefulset.Spec.Selector.MatchLabels) listOptions := metav1.ListOptions{LabelSelector: set.AsSelector().String()} return client.Clientset.CoreV1(). - Pods(namespace). + Pods(serverNamespace). List(ctx, listOptions) } @@ -210,7 +212,7 @@ func addAgentK8sStatus(ctx context.Context, client *kubeutil.Client, agents []Ag func getAgentDaemonSet(ctx context.Context, client *kubeutil.Client) (*appsv1.DaemonSet, error) { return client.Clientset.AppsV1(). - DaemonSets(namespace). + DaemonSets(agentNamespace). Get(ctx, agentDaemonSetName, metav1.GetOptions{}) } @@ -218,7 +220,7 @@ func getPodsforDaemonSet(ctx context.Context, client *kubeutil.Client, daemonset set := labels.Set(daemonset.Spec.Selector.MatchLabels) listOptions := metav1.ListOptions{LabelSelector: set.AsSelector().String()} return client.Clientset.CoreV1(). - Pods(namespace). + Pods(agentNamespace). List(ctx, listOptions) } @@ -343,7 +345,7 @@ func GetBundle(ctx context.Context, client *kubeutil.Client) (string, error) { func createPodWatcher(ctx context.Context, client *kubeutil.Client) (watch.Interface, error) { watchFunc := func(opts metav1.ListOptions) (watch.Interface, error) { timeout := int64(120) - return client.Clientset.CoreV1().Pods(namespace).Watch(ctx, metav1.ListOptions{ + return client.Clientset.CoreV1().Pods(serverNamespace).Watch(ctx, metav1.ListOptions{ FieldSelector: fmt.Sprintf("metadata.name=%s", serverPodName), TimeoutSeconds: &timeout, }) @@ -360,7 +362,7 @@ func createPodWatcher(ctx context.Context, client *kubeutil.Client) (watch.Inter func createServiceWatcher(ctx context.Context, client *kubeutil.Client) (watch.Interface, error) { watchFunc := func(opts metav1.ListOptions) (watch.Interface, error) { timeout := int64(120) - return client.Clientset.CoreV1().Services(namespace).Watch(ctx, metav1.ListOptions{ + return client.Clientset.CoreV1().Services(serverNamespace).Watch(ctx, metav1.ListOptions{ FieldSelector: fmt.Sprintf("metadata.name=%s", serverServiceName), TimeoutSeconds: &timeout, }) diff --git a/pkg/spire/spire_server_cli.go b/pkg/spire/spire_server_cli.go index eb6293d..1dd9580 100644 --- a/pkg/spire/spire_server_cli.go +++ b/pkg/spire/spire_server_cli.go @@ -35,7 +35,7 @@ func execInServerContainer(ctx context.Context, client *kubeutil.Client, command client.Clientset, client.RestConfig, serverPodName, - namespace, + serverNamespace, serverContainerName, command, stdin, diff --git a/pkg/spire/spire_server_cli_test.go b/pkg/spire/spire_server_cli_test.go index 8074422..074f076 100644 --- a/pkg/spire/spire_server_cli_test.go +++ b/pkg/spire/spire_server_cli_test.go @@ -35,7 +35,7 @@ var oneAgentList = `{ }, { "type": "k8s_psat", - "value": "agent_ns:spire" + "value": "agent_ns:spire-system" }, { "type": "k8s_psat", diff --git a/pkg/spire/spire_test.go b/pkg/spire/spire_test.go index 3f36cc1..1fde911 100644 --- a/pkg/spire/spire_test.go +++ b/pkg/spire/spire_test.go @@ -36,7 +36,7 @@ func TestGetServerStatus(t *testing.T) { ), ) _, err := clientSet.AppsV1(). - StatefulSets("spire"). + StatefulSets("spire-server"). Apply(ctx, ssConfig, metav1.ApplyOptions{}) if err != nil { t.Fatalf("failed to create statefulset: %v", err) @@ -59,7 +59,7 @@ func TestGetServerStatus(t *testing.T) { ), ) _, err = clientSet.CoreV1(). - Pods("spire"). + Pods("spire-server"). Apply(ctx, podConfig, metav1.ApplyOptions{}) if err != nil { t.Fatalf("failed to create pod: %v", err) @@ -104,7 +104,7 @@ func Test_addAgentK8sStatus(t *testing.T) { WithNumberReady(1), ) _, err := clientSet.AppsV1(). - DaemonSets("spire"). + DaemonSets("spire-system"). Apply(ctx, dsConfig, metav1.ApplyOptions{}) if err != nil { t.Fatalf("failed to create daemonset: %v", err) @@ -125,7 +125,7 @@ func Test_addAgentK8sStatus(t *testing.T) { ), ) _, err = clientSet.CoreV1(). - Pods("spire"). + Pods("spire-system"). Apply(ctx, podConfig, metav1.ApplyOptions{}) if err != nil { t.Fatalf("failed to create pod: %v", err) @@ -146,7 +146,7 @@ func Test_addAgentK8sStatus(t *testing.T) { ), ) _, err = clientSet.CoreV1(). - Pods("spire"). + Pods("spire-system"). Apply(ctx, podConfig, metav1.ApplyOptions{}) if err != nil { t.Fatalf("failed to create pod: %v", err) From b376c14bdefefba468335c1a52405e74bc9438d1 Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Fri, 3 Jan 2025 11:39:49 +0000 Subject: [PATCH 41/48] Add --log-level argument to root command This allows setting the log level threshold. The default is ERROR. Currently we do not do much logging in cofidectl, but this may change now that we can set the level. The main benefit for now is that gRPC plugin log level is also set based on this argument, giving more insight into plugin issues. Fixes: #82 --- cmd/cofidectl/cmd/root.go | 39 ++++++++++++++++++++++++++-- cmd/cofidectl/cmd/root_test.go | 46 ++++++++++++++++++++++++++++++++++ pkg/cmd/context/context.go | 14 ++++++++++- pkg/plugin/manager/manager.go | 9 ++++++- 4 files changed, 104 insertions(+), 4 deletions(-) create mode 100644 cmd/cofidectl/cmd/root_test.go diff --git a/cmd/cofidectl/cmd/root.go b/cmd/cofidectl/cmd/root.go index 35e6cce..62c2774 100644 --- a/cmd/cofidectl/cmd/root.go +++ b/cmd/cofidectl/cmd/root.go @@ -4,15 +4,19 @@ package cmd import ( + "fmt" + "log/slog" "os" "path" + "slices" + "strings" "github.com/cofide/cofidectl/cmd/cofidectl/cmd/apbinding" "github.com/cofide/cofidectl/cmd/cofidectl/cmd/attestationpolicy" - cmdcontext "github.com/cofide/cofidectl/pkg/cmd/context" "github.com/cofide/cofidectl/cmd/cofidectl/cmd/federation" "github.com/cofide/cofidectl/cmd/cofidectl/cmd/trustzone" "github.com/cofide/cofidectl/cmd/cofidectl/cmd/workload" + cmdcontext "github.com/cofide/cofidectl/pkg/cmd/context" "github.com/spf13/cobra" ) @@ -32,11 +36,24 @@ func NewRootCommand(cmdCtx *cmdcontext.CommandContext) *RootCommand { var rootCmdDesc = `cofidectl - Workload identity for hybrid and multi-cloud security` func (r *RootCommand) GetRootCommand() (*cobra.Command, error) { + var logLevel string + cmd := &cobra.Command{ Use: "cofidectl", Short: "Cofide CLI", Long: rootCmdDesc, SilenceUsage: true, + // This runs before any subcommand. + PersistentPreRunE: func(cmd *cobra.Command, args []string) error { + slogLevel, err := slogLevelFromString(logLevel) + if err != nil { + return err + } + + r.cmdCtx.SetLogLevel(slogLevel) + slog.Debug("Set slog level", slog.String("level", slogLevel.String())) + return nil + }, } home, err := os.UserHomeDir() @@ -44,7 +61,9 @@ func (r *RootCommand) GetRootCommand() (*cobra.Command, error) { return nil, err } - cmd.PersistentFlags().StringVar(&kubeCfgFile, "kube-config", path.Join(home, ".kube/config"), "kubeconfig file location") + pf := cmd.PersistentFlags() + pf.StringVar(&kubeCfgFile, "kube-config", path.Join(home, ".kube/config"), "kubeconfig file location") + pf.StringVar(&logLevel, "log-level", "ERROR", "log level") initCmd := NewInitCommand(r.cmdCtx) upCmd := NewUpCommand(r.cmdCtx) @@ -68,3 +87,19 @@ func (r *RootCommand) GetRootCommand() (*cobra.Command, error) { return cmd, nil } + +// slogLevelFromString returns an slog.Level from a string log level. +// The string level is case-insensitive. +func slogLevelFromString(level string) (slog.Level, error) { + var slogLevel slog.Level + // slog accepts funky inputs like INFO-3, so restrict what we accept. + validLevels := []string{"debug", "warn", "info", "error"} + if !slices.Contains(validLevels, strings.ToLower(level)) { + return slogLevel, fmt.Errorf("unexpected log level %s, valid levels: %s", level, strings.Join(validLevels, ", ")) + } + + if err := slogLevel.UnmarshalText([]byte(level)); err != nil { + return slogLevel, fmt.Errorf("unexpected log level %s: %w", level, err) + } + return slogLevel, nil +} diff --git a/cmd/cofidectl/cmd/root_test.go b/cmd/cofidectl/cmd/root_test.go new file mode 100644 index 0000000..967e29c --- /dev/null +++ b/cmd/cofidectl/cmd/root_test.go @@ -0,0 +1,46 @@ +// Copyright 2025 Cofide Limited. +// SPDX-License-Identifier: Apache-2.0 + +package cmd + +import ( + "log/slog" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_slogLevelFromString(t *testing.T) { + t.Parallel() + + tests := []struct { + name string + level string + want slog.Level + wantErr bool + }{ + {name: "debug", level: "debug", want: slog.LevelDebug}, + {name: "warn", level: "warn", want: slog.LevelWarn}, + {name: "info", level: "info", want: slog.LevelInfo}, + {name: "error", level: "error", want: slog.LevelError}, + {name: "debug upper", level: "DEBUG", want: slog.LevelDebug}, + {name: "warn upper", level: "WARN", want: slog.LevelWarn}, + {name: "info upper", level: "INFO", want: slog.LevelInfo}, + {name: "error upper", level: "ERROR", want: slog.LevelError}, + {name: "invalid", level: "invalid level", wantErr: true}, + {name: "warning", level: "warning", wantErr: true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := slogLevelFromString(tt.level) + + if tt.wantErr { + require.Error(t, err, err) + assert.ErrorContains(t, err, "unexpected log level") + } else { + assert.Equal(t, got, tt.want) + } + }) + } +} diff --git a/pkg/cmd/context/context.go b/pkg/cmd/context/context.go index cf5fe15..8334ad9 100644 --- a/pkg/cmd/context/context.go +++ b/pkg/cmd/context/context.go @@ -7,6 +7,7 @@ import ( "context" "errors" "fmt" + "log/slog" "os" "os/signal" "syscall" @@ -22,14 +23,19 @@ type CommandContext struct { Ctx context.Context cancel context.CancelCauseFunc PluginManager *manager.PluginManager + logLevel *slog.LevelVar } // NewCommandContext returns a command context wired up with a config loader and plugin manager. func NewCommandContext(cofideConfigFile string) *CommandContext { + logLevel := &slog.LevelVar{} + handler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{Level: logLevel}) + logger := slog.New(handler) + slog.SetDefault(logger) ctx, cancel := context.WithCancelCause(context.Background()) configLoader := config.NewFileLoader(cofideConfigFile) pluginManager := manager.NewManager(configLoader) - return &CommandContext{Ctx: ctx, cancel: cancel, PluginManager: pluginManager} + return &CommandContext{Ctx: ctx, cancel: cancel, PluginManager: pluginManager, logLevel: logLevel} } func (cc *CommandContext) Shutdown() { @@ -54,3 +60,9 @@ func (cc *CommandContext) HandleSignals() { fmt.Println("Timed out waiting for shutdown") os.Exit(1) } + +// SetLogLevel sets the log level of the default handler and gRPC plugins. +func (cc *CommandContext) SetLogLevel(level slog.Level) { + cc.logLevel.Set(level) + cc.PluginManager.SetLogLevel(level) +} diff --git a/pkg/plugin/manager/manager.go b/pkg/plugin/manager/manager.go index 02edeef..b7d1e65 100644 --- a/pkg/plugin/manager/manager.go +++ b/pkg/plugin/manager/manager.go @@ -7,6 +7,7 @@ import ( "context" "errors" "fmt" + "log/slog" "maps" "os" "os/exec" @@ -38,6 +39,7 @@ type PluginManager struct { source datasource.DataSource provision provision.Provision clients map[string]*go_plugin.Client + logLevel hclog.Level } // grpcPluginLoader is a function that loads a gRPC plugin. The function should load a single @@ -196,7 +198,7 @@ func (pm *PluginManager) loadGRPCPlugin(ctx context.Context, pluginName string, logger := hclog.New(&hclog.LoggerOptions{ Name: "plugin", Output: os.Stdout, - Level: hclog.Error, + Level: pm.logLevel, }) grpcPlugin, err := pm.grpcPluginLoader(ctx, logger, pluginName, pluginCfg) @@ -349,6 +351,11 @@ func (pm *PluginManager) SetPluginConfig(pluginName string, pluginConfig *struct return pm.configLoader.Write(cfg) } +// SetLogLevel sets the log level for gRPC plugins. +func (pm *PluginManager) SetLogLevel(level slog.Level) { + pm.logLevel = hclog.LevelFromString(level.String()) +} + // GetDefaultPlugins returns a `Plugins` message containing the default plugins. func GetDefaultPlugins() *pluginspb.Plugins { ds := LocalDSPluginName From f9dd2d511e605ec546089a7885f89d9c2b1ae26a Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Fri, 3 Jan 2025 15:17:08 +0000 Subject: [PATCH 42/48] Update Helm value overrides for 3 namespace setup Arguably we could avoid customising these values and let Helm handle them. --- internal/pkg/trustprovider/trustprovider.go | 5 +-- pkg/provider/helm/values.go | 2 +- pkg/provider/helm/values_test.go | 37 +++++++++++---------- 3 files changed, 23 insertions(+), 21 deletions(-) diff --git a/internal/pkg/trustprovider/trustprovider.go b/internal/pkg/trustprovider/trustprovider.go index 3a460a0..7467186 100644 --- a/internal/pkg/trustprovider/trustprovider.go +++ b/internal/pkg/trustprovider/trustprovider.go @@ -52,8 +52,9 @@ func (tp *TrustProvider) GetValues() error { NodeAttestor: kubernetesPsat, NodeAttestorEnabled: true, NodeAttestorConfig: map[string]any{ - "enabled": true, - "serviceAccountAllowList": []string{"spire:spire-agent"}, + "enabled": true, + // Rely on defaults? + "serviceAccountAllowList": []string{"spire-system:spire-agent"}, "audience": []string{"spire-server"}, "allowedNodeLabelKeys": []string{}, "allowedPodLabelKeys": []string{}, diff --git a/pkg/provider/helm/values.go b/pkg/provider/helm/values.go index 30de5e2..9b0af32 100644 --- a/pkg/provider/helm/values.go +++ b/pkg/provider/helm/values.go @@ -97,7 +97,7 @@ func (g *HelmValuesGenerator) GenerateValues() (map[string]any, error) { logLevel: "DEBUG", agentConfig: tp.AgentConfig, sdsConfig: sdsConfig, - spireServerAddress: "spire-server.spire", + spireServerAddress: "spire-server.spire-server", } spireAgentValues, err := sav.generateValues() if err != nil { diff --git a/pkg/provider/helm/values_test.go b/pkg/provider/helm/values_test.go index dc7299a..88a08ed 100644 --- a/pkg/provider/helm/values_test.go +++ b/pkg/provider/helm/values_test.go @@ -77,7 +77,7 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { "defaultAllBundlesName": "ALL", }, "server": Values{ - "address": "spire-server.spire", + "address": "spire-server.spire-server", }, "workloadAttestors": Values{ "k8s": Values{ @@ -114,7 +114,7 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { }, "enabled": true, "serviceAccountAllowList": []string{ - "spire:spire-agent", + "spire-system:spire-agent", }, }, }, @@ -173,7 +173,7 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { "defaultAllBundlesName": "ALL", }, "server": Values{ - "address": "spire-server.spire", + "address": "spire-server.spire-server", }, "workloadAttestors": Values{ "k8s": Values{ @@ -224,6 +224,7 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { }, "fullnameOverride": "spire-server", "logLevel": "INFO", + "nameOverride": "custom-server-name", "nodeAttestor": Values{ "k8sPsat": Values{ "allowedNodeLabelKeys": []string{}, @@ -233,7 +234,7 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { }, "enabled": true, "serviceAccountAllowList": []string{ - "spire:spire-agent", + "spire-system:spire-agent", }, }, }, @@ -286,7 +287,7 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { "defaultAllBundlesName": "ROOTCA", }, "server": Values{ - "address": "spire-server.spire", + "address": "spire-server.spire-server", }, "workloadAttestors": Values{ "k8s": Values{ @@ -323,7 +324,7 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { }, "enabled": true, "serviceAccountAllowList": []string{ - "spire:spire-agent", + "spire-system:spire-agent", }, }, }, @@ -427,7 +428,7 @@ func TestHelmValuesGenerator_GenerateValues_AdditionalValues(t *testing.T) { "defaultAllBundlesName": "ALL", }, "server": Values{ - "address": "spire-server.spire", + "address": "spire-server.spire-server", }, "workloadAttestors": Values{ "k8s": Values{ @@ -473,7 +474,7 @@ func TestHelmValuesGenerator_GenerateValues_AdditionalValues(t *testing.T) { }, "enabled": true, "serviceAccountAllowList": []string{ - "spire:spire-agent", + "spire-system:spire-agent", }, }, }, @@ -1120,7 +1121,7 @@ func TestSpireAgentValues_GenerateValues(t *testing.T) { "defaultBundleName": "ROOTCA", "defaultAllBundlesName": "ALL", }, - spireServerAddress: "spire-server.spire", + spireServerAddress: "spire-server.spire-server", }, want: map[string]any{ "spire-agent": map[string]any{ @@ -1138,7 +1139,7 @@ func TestSpireAgentValues_GenerateValues(t *testing.T) { "defaultAllBundlesName": "ALL", }, "server": map[string]any{ - "address": "spire-server.spire", + "address": "spire-server.spire-server", }, "workloadAttestors": map[string]any{ "k8s": map[string]any{ @@ -1176,7 +1177,7 @@ func TestSpireAgentValues_GenerateValues(t *testing.T) { "defaultBundleName": "ROOTCA", "defaultAllBundlesName": "ALL", }, - spireServerAddress: "spire-server.spire", + spireServerAddress: "spire-server.spire-server", }, want: nil, wantErr: true, @@ -1200,7 +1201,7 @@ func TestSpireAgentValues_GenerateValues(t *testing.T) { "defaultBundleName": "ROOTCA", "defaultAllBundlesName": "ALL", }, - spireServerAddress: "spire-server.spire", + spireServerAddress: "spire-server.spire-server", }, want: nil, wantErr: true, @@ -1230,7 +1231,7 @@ func TestSpireAgentValues_GenerateValues(t *testing.T) { "defaultBundleName": "ROOTCA", "defaultAllBundlesName": "ALL", }, - spireServerAddress: "spire-server.spire", + spireServerAddress: "spire-server.spire-server", }, want: nil, wantErr: true, @@ -1255,7 +1256,7 @@ func TestSpireAgentValues_GenerateValues(t *testing.T) { NodeAttestorEnabled: true, }, sdsConfig: map[string]any{}, - spireServerAddress: "spire-server.spire", + spireServerAddress: "spire-server.spire-server", }, want: nil, wantErr: true, @@ -1280,7 +1281,7 @@ func TestSpireAgentValues_GenerateValues(t *testing.T) { NodeAttestorEnabled: true, }, sdsConfig: nil, - spireServerAddress: "spire-server.spire", + spireServerAddress: "spire-server.spire-server", }, want: nil, wantErr: true, @@ -1323,7 +1324,7 @@ func TestSpireServerValues_GenerateValues(t *testing.T) { NodeAttestorEnabled: true, NodeAttestorConfig: map[string]any{ "enabled": true, - "serviceAccountAllowList": []string{"spire:spire-agent"}, + "serviceAccountAllowList": []string{"spire-system:spire-agent"}, "audience": []string{"spire-server"}, "allowedNodeLabelKeys": []string{}, "allowedPodLabelKeys": []string{}, @@ -1350,7 +1351,7 @@ func TestSpireServerValues_GenerateValues(t *testing.T) { }, "enabled": true, "serviceAccountAllowList": []string{ - "spire:spire-agent", + "spire-system:spire-agent", }, }, }, @@ -1387,7 +1388,7 @@ func TestSpireServerValues_GenerateValues(t *testing.T) { NodeAttestorEnabled: true, NodeAttestorConfig: map[string]any{ "enabled": true, - "serviceAccountAllowList": []string{"spire:spire-agent"}, + "serviceAccountAllowList": []string{"spire-system:spire-agent"}, "audience": []string{"spire-server"}, "allowedNodeLabelKeys": []string{}, "allowedPodLabelKeys": []string{}, From b58b48f8b9c650f83aa22e31bc0383da70fd8c39 Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Fri, 3 Jan 2025 15:15:41 +0000 Subject: [PATCH 43/48] Add CA subject to Helm values This is required by Helm recommendations. We provide a default CA subject, and this can be overridden using custom Helm values. --- internal/pkg/config/testdata/config/full.yaml | 2 +- internal/pkg/test/fixtures/fixtures.go | 5 +++- pkg/provider/helm/values.go | 22 ++++++++++++++ pkg/provider/helm/values_test.go | 30 +++++++++++++++++++ 4 files changed, 57 insertions(+), 2 deletions(-) diff --git a/internal/pkg/config/testdata/config/full.yaml b/internal/pkg/config/testdata/config/full.yaml index e597269..d58d97b 100644 --- a/internal/pkg/config/testdata/config/full.yaml +++ b/internal/pkg/config/testdata/config/full.yaml @@ -20,10 +20,10 @@ trust_zones: spire: caSubject: commonName: cn.example.com - country: UK organization: acme-org spire-server: logLevel: INFO + nameOverride: custom-server-name bundle_endpoint_profile: BUNDLE_ENDPOINT_PROFILE_HTTPS_SPIFFE profile: kubernetes external_server: false diff --git a/internal/pkg/test/fixtures/fixtures.go b/internal/pkg/test/fixtures/fixtures.go index 8728db7..482167e 100644 --- a/internal/pkg/test/fixtures/fixtures.go +++ b/internal/pkg/test/fixtures/fixtures.go @@ -46,15 +46,18 @@ var trustZoneFixtures map[string]*trust_zone_proto.TrustZone = map[string]*trust ev := map[string]any{ "global": map[string]any{ "spire": map[string]any{ + // Modify multiple values in the same map. "caSubject": map[string]any{ - "country": "UK", "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) diff --git a/pkg/provider/helm/values.go b/pkg/provider/helm/values.go index 9b0af32..9ff1902 100644 --- a/pkg/provider/helm/values.go +++ b/pkg/provider/helm/values.go @@ -23,6 +23,7 @@ type HelmValuesGenerator struct { type globalValues struct { deleteHooks bool installAndUpgradeHooksEnabled bool + spireCASubject caSubject spireClusterName string spireJwtIssuer string spireNamespacesCreate bool @@ -30,6 +31,12 @@ type globalValues struct { spireTrustDomain string } +type caSubject struct { + commonName string + country string + organization string +} + type spireAgentValues struct { agentConfig trustprovider.TrustProviderAgentConfig fullnameOverride string @@ -73,6 +80,11 @@ func (g *HelmValuesGenerator) GenerateValues() (map[string]any, error) { } gv := globalValues{ + spireCASubject: caSubject{ + commonName: "cofide.io", + country: "UK", + organization: "Cofide", + }, spireClusterName: g.trustZone.GetKubernetesCluster(), spireJwtIssuer: g.trustZone.GetJwtIssuer(), spireNamespacesCreate: true, @@ -244,6 +256,7 @@ func (g *globalValues) generateValues() (map[string]any, error) { values := map[string]any{ "global": map[string]any{ "spire": map[string]any{ + "caSubject": g.spireCASubject.generateValues(), "clusterName": g.spireClusterName, "namespaces": map[string]any{ "create": g.spireNamespacesCreate, @@ -279,6 +292,15 @@ func (g *globalValues) generateValues() (map[string]any, error) { return values, nil } +// generateValues generates the global.spire.caSubject Helm values map. +func (c *caSubject) generateValues() map[string]any { + return map[string]any{ + "country": c.country, + "organization": c.organization, + "commonName": c.commonName, + } +} + // generateValues generates the spire-agent Helm values map. func (s *spireAgentValues) generateValues() (map[string]any, error) { if s.fullnameOverride == "" { diff --git a/pkg/provider/helm/values_test.go b/pkg/provider/helm/values_test.go index 88a08ed..8dc2502 100644 --- a/pkg/provider/helm/values_test.go +++ b/pkg/provider/helm/values_test.go @@ -46,6 +46,11 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { "enabled": false, }, "spire": Values{ + "caSubject": Values{ + "commonName": "cofide.io", + "country": "UK", + "organization": "Cofide", + }, "clusterName": "local1", "namespaces": Values{ "create": true, @@ -256,6 +261,11 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { "enabled": false, }, "spire": Values{ + "caSubject": Values{ + "commonName": "cofide.io", + "country": "UK", + "organization": "Cofide", + }, "clusterName": "local4", "namespaces": Values{ "create": true, @@ -397,6 +407,11 @@ func TestHelmValuesGenerator_GenerateValues_AdditionalValues(t *testing.T) { "enabled": false, }, "spire": Values{ + "caSubject": Values{ + "commonName": "cofide.io", + "country": "UK", + "organization": "Cofide", + }, "clusterName": "local1", "namespaces": Values{ "create": true, @@ -987,6 +1002,11 @@ func TestGlobalValues_GenerateValues(t *testing.T) { want: map[string]any{ "global": map[string]any{ "spire": map[string]any{ + "caSubject": Values{ + "commonName": "", + "country": "", + "organization": "", + }, "clusterName": "local1", "namespaces": Values{ "create": false, @@ -1016,6 +1036,11 @@ func TestGlobalValues_GenerateValues(t *testing.T) { want: map[string]any{ "global": map[string]any{ "spire": map[string]any{ + "caSubject": Values{ + "commonName": "", + "country": "", + "organization": "", + }, "clusterName": "local1", "namespaces": Values{ "create": false, @@ -1045,6 +1070,11 @@ func TestGlobalValues_GenerateValues(t *testing.T) { want: map[string]any{ "global": map[string]any{ "spire": map[string]any{ + "caSubject": Values{ + "commonName": "", + "country": "", + "organization": "", + }, "clusterName": "local1", "jwtIssuer": "https://tz1.example.com", "namespaces": Values{ From 6a7a72242346bd4d666690d67c8b8a7f60c6ec06 Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Mon, 11 Nov 2024 15:21:05 +0000 Subject: [PATCH 44/48] Enable automatic mTLS negotiation for data source plugins --- pkg/plugin/manager/manager.go | 1 + 1 file changed, 1 insertion(+) diff --git a/pkg/plugin/manager/manager.go b/pkg/plugin/manager/manager.go index b7d1e65..ab18e79 100644 --- a/pkg/plugin/manager/manager.go +++ b/pkg/plugin/manager/manager.go @@ -238,6 +238,7 @@ func loadGRPCPlugin(ctx context.Context, logger hclog.Logger, pluginName string, Plugins: pluginSet, AllowedProtocols: []go_plugin.Protocol{go_plugin.ProtocolGRPC}, Logger: logger, + AutoMTLS: true, }) grpcPlugin, err := startGRPCPlugin(ctx, client, pluginName, plugins) From 74e5c5d8f5e1f23ae4b1ff2d77ad1ca1473da1da Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Mon, 6 Jan 2025 09:58:01 +0000 Subject: [PATCH 45/48] Slim down Helm value overrides Try to avoid hard coding Helm values where the chart can provide a sensible default or compute a value from other values. For example, some values depend on the namespaces used, such as the server address, spire agent service account allow list, etc. --- internal/pkg/trustprovider/trustprovider.go | 37 ++-- pkg/provider/helm/values.go | 27 +-- pkg/provider/helm/values_test.go | 225 ++++++-------------- 3 files changed, 81 insertions(+), 208 deletions(-) diff --git a/internal/pkg/trustprovider/trustprovider.go b/internal/pkg/trustprovider/trustprovider.go index 7467186..4d56211 100644 --- a/internal/pkg/trustprovider/trustprovider.go +++ b/internal/pkg/trustprovider/trustprovider.go @@ -36,28 +36,18 @@ func (tp *TrustProvider) GetValues() error { switch tp.Kind { case "kubernetes": tp.AgentConfig = TrustProviderAgentConfig{ - WorkloadAttestor: KubernetesTrustProvider, - WorkloadAttestorEnabled: true, + WorkloadAttestor: KubernetesTrustProvider, WorkloadAttestorConfig: map[string]any{ - "enabled": true, - "skipKubeletVerification": true, - "disableContainerSelectors": true, - "useNewContainerLocator": false, - "verboseContainerLocatorLogs": false, + "enabled": true, + "disableContainerSelectors": true, }, - NodeAttestor: kubernetesPsat, - NodeAttestorEnabled: true, + NodeAttestor: kubernetesPsat, } tp.ServerConfig = TrustProviderServerConfig{ - NodeAttestor: kubernetesPsat, - NodeAttestorEnabled: true, + NodeAttestor: kubernetesPsat, NodeAttestorConfig: map[string]any{ - "enabled": true, - // Rely on defaults? - "serviceAccountAllowList": []string{"spire-system:spire-agent"}, - "audience": []string{"spire-server"}, - "allowedNodeLabelKeys": []string{}, - "allowedPodLabelKeys": []string{}, + "enabled": true, + "audience": []string{"spire-server"}, }, } default: @@ -67,17 +57,14 @@ func (tp *TrustProvider) GetValues() error { } type TrustProviderAgentConfig struct { - WorkloadAttestor string `yaml:"workloadAttestor"` - WorkloadAttestorEnabled bool `yaml:"workloadAttestorEnabled"` - WorkloadAttestorConfig map[string]any `yaml:"workloadAttestorConfig"` - NodeAttestor string `yaml:"nodeAttestor"` - NodeAttestorEnabled bool `yaml:"nodeAttestorEnabled"` + WorkloadAttestor string + WorkloadAttestorConfig map[string]any + NodeAttestor string } type TrustProviderServerConfig struct { - NodeAttestor string `yaml:"nodeAttestor"` - NodeAttestorEnabled bool `yaml:"nodeAttestorEnabled"` - NodeAttestorConfig map[string]any `yaml:"nodeAttestorConfig"` + NodeAttestor string + NodeAttestorConfig map[string]any } // GetTrustProviderKindFromProfile returns the valid kind of trust provider for the diff --git a/pkg/provider/helm/values.go b/pkg/provider/helm/values.go index 9ff1902..bf05131 100644 --- a/pkg/provider/helm/values.go +++ b/pkg/provider/helm/values.go @@ -38,11 +38,10 @@ type caSubject struct { } type spireAgentValues struct { - agentConfig trustprovider.TrustProviderAgentConfig - fullnameOverride string - logLevel string - sdsConfig map[string]any - spireServerAddress string + agentConfig trustprovider.TrustProviderAgentConfig + fullnameOverride string + logLevel string + sdsConfig map[string]any } type spireServerValues struct { @@ -105,11 +104,10 @@ func (g *HelmValuesGenerator) GenerateValues() (map[string]any, error) { } sav := spireAgentValues{ - fullnameOverride: "spire-agent", - logLevel: "DEBUG", - agentConfig: tp.AgentConfig, - sdsConfig: sdsConfig, - spireServerAddress: "spire-server.spire-server", + fullnameOverride: "spire-agent", + logLevel: "DEBUG", + agentConfig: tp.AgentConfig, + sdsConfig: sdsConfig, } spireAgentValues, err := sav.generateValues() if err != nil { @@ -335,23 +333,16 @@ func (s *spireAgentValues) generateValues() (map[string]any, error) { return nil, fmt.Errorf("agentConfig.WorkloadAttestorConfig value is empty") } - if s.spireServerAddress == "" { - return nil, fmt.Errorf("spireServerAddress value is empty") - } - return map[string]any{ "spire-agent": map[string]any{ "fullnameOverride": s.fullnameOverride, "logLevel": s.logLevel, "nodeAttestor": map[string]any{ s.agentConfig.NodeAttestor: map[string]any{ - "enabled": s.agentConfig.NodeAttestorEnabled, + "enabled": true, }, }, "sds": s.sdsConfig, - "server": map[string]any{ - "address": s.spireServerAddress, - }, "workloadAttestors": map[string]any{ s.agentConfig.WorkloadAttestor: s.agentConfig.WorkloadAttestorConfig, }, diff --git a/pkg/provider/helm/values_test.go b/pkg/provider/helm/values_test.go index 8dc2502..e2e0133 100644 --- a/pkg/provider/helm/values_test.go +++ b/pkg/provider/helm/values_test.go @@ -81,16 +81,10 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { "defaultBundleName": "ROOTCA", "defaultAllBundlesName": "ALL", }, - "server": Values{ - "address": "spire-server.spire-server", - }, "workloadAttestors": Values{ "k8s": Values{ - "disableContainerSelectors": true, - "enabled": true, - "skipKubeletVerification": true, - "useNewContainerLocator": false, - "verboseContainerLocatorLogs": false, + "disableContainerSelectors": true, + "enabled": true, }, }, }, @@ -112,15 +106,8 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { "logLevel": "DEBUG", "nodeAttestor": Values{ "k8sPsat": Values{ - "allowedNodeLabelKeys": []string{}, - "allowedPodLabelKeys": []string{}, - "audience": []string{ - "spire-server", - }, - "enabled": true, - "serviceAccountAllowList": []string{ - "spire-system:spire-agent", - }, + "audience": []string{"spire-server"}, + "enabled": true, }, }, "service": Values{ @@ -177,16 +164,10 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { "defaultBundleName": "ROOTCA", "defaultAllBundlesName": "ALL", }, - "server": Values{ - "address": "spire-server.spire-server", - }, "workloadAttestors": Values{ "k8s": Values{ - "disableContainerSelectors": true, - "enabled": true, - "skipKubeletVerification": true, - "useNewContainerLocator": false, - "verboseContainerLocatorLogs": false, + "disableContainerSelectors": true, + "enabled": true, }, }, }, @@ -232,15 +213,8 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { "nameOverride": "custom-server-name", "nodeAttestor": Values{ "k8sPsat": Values{ - "allowedNodeLabelKeys": []string{}, - "allowedPodLabelKeys": []string{}, - "audience": []string{ - "spire-server", - }, - "enabled": true, - "serviceAccountAllowList": []string{ - "spire-system:spire-agent", - }, + "audience": []string{"spire-server"}, + "enabled": true, }, }, "service": Values{ @@ -296,16 +270,10 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { "defaultBundleName": "null", "defaultAllBundlesName": "ROOTCA", }, - "server": Values{ - "address": "spire-server.spire-server", - }, "workloadAttestors": Values{ "k8s": Values{ - "disableContainerSelectors": true, - "enabled": true, - "skipKubeletVerification": true, - "useNewContainerLocator": false, - "verboseContainerLocatorLogs": false, + "disableContainerSelectors": true, + "enabled": true, }, }, }, @@ -327,15 +295,8 @@ func TestHelmValuesGenerator_GenerateValues_success(t *testing.T) { "logLevel": "DEBUG", "nodeAttestor": Values{ "k8sPsat": Values{ - "allowedNodeLabelKeys": []string{}, - "allowedPodLabelKeys": []string{}, - "audience": []string{ - "spire-server", - }, - "enabled": true, - "serviceAccountAllowList": []string{ - "spire-system:spire-agent", - }, + "audience": []string{"spire-server"}, + "enabled": true, }, }, "service": Values{ @@ -442,16 +403,10 @@ func TestHelmValuesGenerator_GenerateValues_AdditionalValues(t *testing.T) { "defaultBundleName": "ROOTCA", "defaultAllBundlesName": "ALL", }, - "server": Values{ - "address": "spire-server.spire-server", - }, "workloadAttestors": Values{ "k8s": Values{ - "disableContainerSelectors": true, - "enabled": true, - "skipKubeletVerification": true, - "useNewContainerLocator": false, - "verboseContainerLocatorLogs": false, + "disableContainerSelectors": true, + "enabled": true, }, }, }, @@ -482,15 +437,8 @@ func TestHelmValuesGenerator_GenerateValues_AdditionalValues(t *testing.T) { "logLevel": "DEBUG", "nodeAttestor": Values{ "k8sPsat": Values{ - "allowedNodeLabelKeys": []string{}, - "allowedPodLabelKeys": []string{}, - "audience": []string{ - "spire-server", - }, - "enabled": true, - "serviceAccountAllowList": []string{ - "spire-system:spire-agent", - }, + "audience": []string{"spire-server"}, + "enabled": true, }, }, "service": Values{ @@ -1133,17 +1081,12 @@ func TestSpireAgentValues_GenerateValues(t *testing.T) { fullnameOverride: "spire-agent", logLevel: "DEBUG", agentConfig: trustprovider.TrustProviderAgentConfig{ - WorkloadAttestor: "k8s", - WorkloadAttestorEnabled: true, + WorkloadAttestor: "k8s", WorkloadAttestorConfig: map[string]any{ - "enabled": true, - "skipKubeletVerification": true, - "disableContainerSelectors": true, - "useNewContainerLocator": false, - "verboseContainerLocatorLogs": false, - }, - NodeAttestor: "k8sPsat", - NodeAttestorEnabled: true, + "enabled": true, + "disableContainerSelectors": true, + }, + NodeAttestor: "k8sPsat", }, sdsConfig: map[string]any{ "enabled": true, @@ -1151,7 +1094,6 @@ func TestSpireAgentValues_GenerateValues(t *testing.T) { "defaultBundleName": "ROOTCA", "defaultAllBundlesName": "ALL", }, - spireServerAddress: "spire-server.spire-server", }, want: map[string]any{ "spire-agent": map[string]any{ @@ -1168,16 +1110,10 @@ func TestSpireAgentValues_GenerateValues(t *testing.T) { "defaultBundleName": "ROOTCA", "defaultAllBundlesName": "ALL", }, - "server": map[string]any{ - "address": "spire-server.spire-server", - }, "workloadAttestors": map[string]any{ "k8s": map[string]any{ - "enabled": true, - "skipKubeletVerification": true, - "disableContainerSelectors": true, - "useNewContainerLocator": false, - "verboseContainerLocatorLogs": false, + "enabled": true, + "disableContainerSelectors": true, }, }, }, @@ -1189,17 +1125,12 @@ func TestSpireAgentValues_GenerateValues(t *testing.T) { input: spireAgentValues{ fullnameOverride: "spire-agent", agentConfig: trustprovider.TrustProviderAgentConfig{ - WorkloadAttestor: "k8s", - WorkloadAttestorEnabled: true, + WorkloadAttestor: "k8s", WorkloadAttestorConfig: map[string]any{ - "enabled": true, - "skipKubeletVerification": true, - "disableContainerSelectors": true, - "useNewContainerLocator": false, - "verboseContainerLocatorLogs": false, - }, - NodeAttestor: "k8sPsat", - NodeAttestorEnabled: true, + "enabled": true, + "disableContainerSelectors": true, + }, + NodeAttestor: "k8sPsat", }, sdsConfig: map[string]any{ "enabled": true, @@ -1207,7 +1138,6 @@ func TestSpireAgentValues_GenerateValues(t *testing.T) { "defaultBundleName": "ROOTCA", "defaultAllBundlesName": "ALL", }, - spireServerAddress: "spire-server.spire-server", }, want: nil, wantErr: true, @@ -1219,11 +1149,9 @@ func TestSpireAgentValues_GenerateValues(t *testing.T) { fullnameOverride: "spire-agent", logLevel: "DEBUG", agentConfig: trustprovider.TrustProviderAgentConfig{ - WorkloadAttestor: "k8s", - WorkloadAttestorEnabled: true, - WorkloadAttestorConfig: map[string]any{}, - NodeAttestor: "k8sPsat", - NodeAttestorEnabled: true, + WorkloadAttestor: "k8s", + WorkloadAttestorConfig: map[string]any{}, + NodeAttestor: "k8sPsat", }, sdsConfig: map[string]any{ "enabled": true, @@ -1231,7 +1159,6 @@ func TestSpireAgentValues_GenerateValues(t *testing.T) { "defaultBundleName": "ROOTCA", "defaultAllBundlesName": "ALL", }, - spireServerAddress: "spire-server.spire-server", }, want: nil, wantErr: true, @@ -1243,17 +1170,12 @@ func TestSpireAgentValues_GenerateValues(t *testing.T) { fullnameOverride: "spire-agent", logLevel: "DEBUG", agentConfig: trustprovider.TrustProviderAgentConfig{ - WorkloadAttestor: "", - WorkloadAttestorEnabled: true, + WorkloadAttestor: "", WorkloadAttestorConfig: map[string]any{ - "enabled": true, - "skipKubeletVerification": true, - "disableContainerSelectors": true, - "useNewContainerLocator": false, - "verboseContainerLocatorLogs": false, - }, - NodeAttestor: "k8sPsat", - NodeAttestorEnabled: true, + "enabled": true, + "disableContainerSelectors": true, + }, + NodeAttestor: "k8sPsat", }, sdsConfig: map[string]any{ "enabled": true, @@ -1261,7 +1183,6 @@ func TestSpireAgentValues_GenerateValues(t *testing.T) { "defaultBundleName": "ROOTCA", "defaultAllBundlesName": "ALL", }, - spireServerAddress: "spire-server.spire-server", }, want: nil, wantErr: true, @@ -1273,20 +1194,14 @@ func TestSpireAgentValues_GenerateValues(t *testing.T) { fullnameOverride: "spire-agent", logLevel: "DEBUG", agentConfig: trustprovider.TrustProviderAgentConfig{ - WorkloadAttestor: "", - WorkloadAttestorEnabled: true, + WorkloadAttestor: "", WorkloadAttestorConfig: map[string]any{ - "enabled": true, - "skipKubeletVerification": true, - "disableContainerSelectors": true, - "useNewContainerLocator": false, - "verboseContainerLocatorLogs": false, - }, - NodeAttestor: "k8sPsat", - NodeAttestorEnabled: true, + "enabled": true, + "disableContainerSelectors": true, + }, + NodeAttestor: "k8sPsat", }, - sdsConfig: map[string]any{}, - spireServerAddress: "spire-server.spire-server", + sdsConfig: map[string]any{}, }, want: nil, wantErr: true, @@ -1298,20 +1213,14 @@ func TestSpireAgentValues_GenerateValues(t *testing.T) { fullnameOverride: "spire-agent", logLevel: "DEBUG", agentConfig: trustprovider.TrustProviderAgentConfig{ - WorkloadAttestor: "", - WorkloadAttestorEnabled: true, + WorkloadAttestor: "", WorkloadAttestorConfig: map[string]any{ - "enabled": true, - "skipKubeletVerification": true, - "disableContainerSelectors": true, - "useNewContainerLocator": false, - "verboseContainerLocatorLogs": false, - }, - NodeAttestor: "k8sPsat", - NodeAttestorEnabled: true, + "enabled": true, + "disableContainerSelectors": true, + }, + NodeAttestor: "k8sPsat", }, - sdsConfig: nil, - spireServerAddress: "spire-server.spire-server", + sdsConfig: nil, }, want: nil, wantErr: true, @@ -1350,14 +1259,10 @@ func TestSpireServerValues_GenerateValues(t *testing.T) { fullnameOverride: "spire-server", logLevel: "DEBUG", serverConfig: trustprovider.TrustProviderServerConfig{ - NodeAttestor: "k8sPsat", - NodeAttestorEnabled: true, + NodeAttestor: "k8sPsat", NodeAttestorConfig: map[string]any{ - "enabled": true, - "serviceAccountAllowList": []string{"spire-system:spire-agent"}, - "audience": []string{"spire-server"}, - "allowedNodeLabelKeys": []string{}, - "allowedPodLabelKeys": []string{}, + "enabled": true, + "audience": []string{"spire-server"}, }, }, serviceType: "LoadBalancer", @@ -1374,15 +1279,8 @@ func TestSpireServerValues_GenerateValues(t *testing.T) { "logLevel": "DEBUG", "nodeAttestor": Values{ "k8sPsat": Values{ - "allowedNodeLabelKeys": []string{}, - "allowedPodLabelKeys": []string{}, - "audience": []string{ - "spire-server", - }, - "enabled": true, - "serviceAccountAllowList": []string{ - "spire-system:spire-agent", - }, + "audience": []string{"spire-server"}, + "enabled": true, }, }, "service": map[string]any{ @@ -1414,14 +1312,11 @@ func TestSpireServerValues_GenerateValues(t *testing.T) { fullnameOverride: "spire-server", logLevel: "DEBUG", serverConfig: trustprovider.TrustProviderServerConfig{ - NodeAttestor: "k8sPsat", - NodeAttestorEnabled: true, + NodeAttestor: "k8sPsat", + //NodeAttestorEnabled: true, NodeAttestorConfig: map[string]any{ - "enabled": true, - "serviceAccountAllowList": []string{"spire-system:spire-agent"}, - "audience": []string{"spire-server"}, - "allowedNodeLabelKeys": []string{}, - "allowedPodLabelKeys": []string{}, + "enabled": true, + "audience": []string{"spire-server"}, }, }, serviceType: "", @@ -1440,9 +1335,9 @@ func TestSpireServerValues_GenerateValues(t *testing.T) { fullnameOverride: "spire-server", logLevel: "DEBUG", serverConfig: trustprovider.TrustProviderServerConfig{ - NodeAttestor: "k8sPsat", - NodeAttestorEnabled: true, - NodeAttestorConfig: map[string]any{}, + NodeAttestor: "k8sPsat", + //NodeAttestorEnabled: true, + NodeAttestorConfig: map[string]any{}, }, serviceType: "", }, From fdbbed136697f44326dd461c2e6ec6432461f326 Mon Sep 17 00:00:00 2001 From: Jason Costello Date: Tue, 7 Jan 2025 12:57:51 +0000 Subject: [PATCH 46/48] Adds Reason column to `federation list` output (#118) * Adds Reason column to federation list, integration test coverage for no bundle case * [From review] Adjust consts --- cmd/cofidectl/cmd/federation/federation.go | 29 ++++++++++++++------- tests/integration/federation/test.sh | 12 ++++++++- tests/integration/single-trust-zone/test.sh | 1 - 3 files changed, 30 insertions(+), 12 deletions(-) diff --git a/cmd/cofidectl/cmd/federation/federation.go b/cmd/cofidectl/cmd/federation/federation.go index 5848c36..5da0152 100644 --- a/cmd/cofidectl/cmd/federation/federation.go +++ b/cmd/cofidectl/cmd/federation/federation.go @@ -18,6 +18,14 @@ import ( "github.com/spf13/cobra" ) +const ( + FederationStatusHealthy string = "Healthy" + FederationStatusUnhealthy string = "Unhealthy" + + FederationStatusReasonNoBundleFound string = "No bundle found" + FederationStatusReasonBundlesDoNotMatch string = "Bundles do not match" +) + type FederationCommand struct { cmdCtx *cmdcontext.CommandContext } @@ -84,7 +92,7 @@ func (c *FederationCommand) GetListCommand() *cobra.Command { return err } - status, err := checkFederationStatus(cmd.Context(), kubeConfig, from, to) + status, reason, err := checkFederationStatus(cmd.Context(), kubeConfig, from, to) if err != nil { return err } @@ -93,11 +101,12 @@ func (c *FederationCommand) GetListCommand() *cobra.Command { federation.From, federation.To, status, + reason, } } table := tablewriter.NewWriter(os.Stdout) - table.SetHeader([]string{"From Trust Zone", "To Trust Zone", "Status"}) + table.SetHeader([]string{"From Trust Zone", "To Trust Zone", "Status", "Reason"}) table.SetBorder(false) table.AppendBulk(data) table.Render() @@ -115,24 +124,24 @@ 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, error) { +func checkFederationStatus(ctx context.Context, 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} { if deployed, err := isTrustZoneDeployed(ctx, tz); err != nil { - return "", err + return "", "", err } else if !deployed { - return "Inactive", nil + return "Inactive", "", nil } client, err := kubeutil.NewKubeClientFromSpecifiedContext(kubeConfig, tz.GetKubernetesContext()) if err != nil { - return "", err + return "", "", err } serverCABundle, federatedBundles, err := spire.GetServerCABundleAndFederatedBundles(ctx, client) if err != nil { - return "", err + return "", "", err } compare[tz] = bundles{ @@ -144,15 +153,15 @@ func checkFederationStatus(ctx context.Context, kubeConfig string, from *trust_z // Bundle does not exist at all on opposite trust domain _, ok := compare[from].federatedBundles[to.TrustDomain] if !ok { - return "Unhealthy", nil + return FederationStatusUnhealthy, FederationStatusReasonNoBundleFound, nil } // Bundle does not match entry on opposite trust domain if compare[from].federatedBundles[to.TrustDomain] != compare[to].serverCABundle { - return "Unhealthy", nil + return FederationStatusUnhealthy, FederationStatusReasonBundlesDoNotMatch, nil } - return "Healthy", nil + return FederationStatusHealthy, "", nil } // isTrustZoneDeployed returns whether a trust zone has been deployed, i.e. whether a SPIRE Helm release has been installed. diff --git a/tests/integration/federation/test.sh b/tests/integration/federation/test.sh index c37ae53..0158740 100755 --- a/tests/integration/federation/test.sh +++ b/tests/integration/federation/test.sh @@ -127,7 +127,16 @@ function show_workload_status() { fi echo "cofidectl workload status successful" - exit 0 +} + +function teardown_federation_and_verify() { + kubectl --context $K8S_CLUSTER_2_CONTEXT delete clusterspiffeids.spire.spiffe.io spire-spire-namespace + kubectl exec --context $K8S_CLUSTER_2_CONTEXT -n spire spire-server-0 -- /opt/spire/bin/spire-server federation delete -id td1 + kubectl exec --context $K8S_CLUSTER_2_CONTEXT -n spire spire-server-0 -- /opt/spire/bin/spire-server bundle delete -id td1 + federations=$(./cofidectl federation list) + if ! echo "$federations" | grep "Unhealthy | No bundle found" >/dev/null; then + return 1 + fi } function down() { @@ -145,6 +154,7 @@ function main() { run_tests post_deploy show_workload_status + teardown_federation_and_verify down echo "Success!" } diff --git a/tests/integration/single-trust-zone/test.sh b/tests/integration/single-trust-zone/test.sh index 4a501ab..8de910a 100755 --- a/tests/integration/single-trust-zone/test.sh +++ b/tests/integration/single-trust-zone/test.sh @@ -95,7 +95,6 @@ function show_workload_status() { fi echo "cofidectl workload status successful" - exit 0 } function down() { From f00327eec16bedbda03eb4193ce8540ca0cb5a95 Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Wed, 8 Jan 2025 09:19:36 +0000 Subject: [PATCH 47/48] Add basic SPIRE resource check in integration tests Currently performs a basic check that expected resources exist in the expected namespaces. --- tests/integration/federation/test.sh | 11 +++++++++ tests/integration/lib.sh | 25 +++++++++++++++++++++ tests/integration/single-trust-zone/test.sh | 9 ++++++++ 3 files changed, 45 insertions(+) create mode 100644 tests/integration/lib.sh diff --git a/tests/integration/federation/test.sh b/tests/integration/federation/test.sh index c37ae53..2737916 100755 --- a/tests/integration/federation/test.sh +++ b/tests/integration/federation/test.sh @@ -5,6 +5,8 @@ set -euxo pipefail +source $(dirname $(dirname $BASH_SOURCE))/lib.sh + DATA_SOURCE_PLUGIN=${DATA_SOURCE_PLUGIN:-} PROVISION_PLUGIN=${PROVISION_PLUGIN:-} @@ -65,6 +67,14 @@ function up() { ./cofidectl up } +function check_spire() { + for context in $K8S_CLUSTER_1_CONTEXT $K8S_CLUSTER_2_CONTEXT; do + check_spire_server $context + check_spire_agents $context + check_spire_csi_driver $context + done +} + function list_resources() { ./cofidectl trust-zone list ./cofidectl attestation-policy list @@ -139,6 +149,7 @@ function main() { check_init configure up + check_spire list_resources show_config show_status diff --git a/tests/integration/lib.sh b/tests/integration/lib.sh new file mode 100644 index 0000000..6bbe644 --- /dev/null +++ b/tests/integration/lib.sh @@ -0,0 +1,25 @@ +# This file provides common library functions for use by integration tests. + +function check_spire_server() { + local context=${1:?Spire server k8s context} + if ! kubectl --context $context -n spire-server get statefulsets spire-server; then + echo "Server statefulset not found" + return 1 + fi +} + +function check_spire_agents() { + local context=${1:?Spire agent k8s context} + if ! kubectl --context $context -n spire-system get daemonsets spire-agent; then + echo "Agent daemonset not found" + return 1 + fi +} + +function check_spire_csi_driver() { + local context=${1:?Spire CSI k8s context} + if ! kubectl --context $context -n spire-system get csidrivers.storage.k8s.io csi.spiffe.io; then + echo "CSI driver not found" + return 1 + fi +} diff --git a/tests/integration/single-trust-zone/test.sh b/tests/integration/single-trust-zone/test.sh index 4a501ab..61b4778 100755 --- a/tests/integration/single-trust-zone/test.sh +++ b/tests/integration/single-trust-zone/test.sh @@ -5,6 +5,8 @@ set -euxo pipefail +source $(dirname $(dirname $BASH_SOURCE))/lib.sh + DATA_SOURCE_PLUGIN=${DATA_SOURCE_PLUGIN:-} PROVISION_PLUGIN=${PROVISION_PLUGIN:-} @@ -41,6 +43,12 @@ function up() { ./cofidectl up --quiet } +function check_spire() { + check_spire_server $K8S_CLUSTER_CONTEXT + check_spire_agents $K8S_CLUSTER_CONTEXT + check_spire_csi_driver $K8S_CLUSTER_CONTEXT +} + function list_resources() { ./cofidectl trust-zone list ./cofidectl attestation-policy list @@ -106,6 +114,7 @@ function main() { init configure up + check_spire list_resources show_config show_status From 33fefeae0695bb062412f80256a7e11a8d55a91b Mon Sep 17 00:00:00 2001 From: Mark Goddard Date: Wed, 8 Jan 2025 09:39:40 +0000 Subject: [PATCH 48/48] Update clusterspiffeid names in integration test for 3 namespace change CRs are now created in the spire-mgmt namespace. --- tests/integration/federation/test.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/integration/federation/test.sh b/tests/integration/federation/test.sh index 3315986..a549f68 100755 --- a/tests/integration/federation/test.sh +++ b/tests/integration/federation/test.sh @@ -140,9 +140,9 @@ function show_workload_status() { } function teardown_federation_and_verify() { - kubectl --context $K8S_CLUSTER_2_CONTEXT delete clusterspiffeids.spire.spiffe.io spire-spire-namespace - kubectl exec --context $K8S_CLUSTER_2_CONTEXT -n spire spire-server-0 -- /opt/spire/bin/spire-server federation delete -id td1 - kubectl exec --context $K8S_CLUSTER_2_CONTEXT -n spire spire-server-0 -- /opt/spire/bin/spire-server bundle delete -id td1 + kubectl --context $K8S_CLUSTER_2_CONTEXT delete clusterspiffeids.spire.spiffe.io spire-mgmt-spire-namespace + kubectl exec --context $K8S_CLUSTER_2_CONTEXT -n spire-server spire-server-0 -- /opt/spire/bin/spire-server federation delete -id td1 + kubectl exec --context $K8S_CLUSTER_2_CONTEXT -n spire-server spire-server-0 -- /opt/spire/bin/spire-server bundle delete -id td1 federations=$(./cofidectl federation list) if ! echo "$federations" | grep "Unhealthy | No bundle found" >/dev/null; then return 1