diff --git a/pkg/agent/config.go b/pkg/agent/config.go index fed4c799..ff0bb4c9 100644 --- a/pkg/agent/config.go +++ b/pkg/agent/config.go @@ -1,6 +1,7 @@ package agent import ( + "errors" "fmt" "io" "net/url" @@ -10,7 +11,6 @@ import ( "github.com/go-logr/logr" "github.com/hashicorp/go-multierror" - "github.com/pkg/errors" "github.com/spf13/cobra" "gopkg.in/yaml.v3" "k8s.io/client-go/rest" @@ -39,7 +39,7 @@ type Config struct { // https://preflight.jetstack.io in Jetstack Secure OAuth and Jetstack // Secure API Token modes, and https://api.venafi.cloud in Venafi Cloud Key // Pair Service Account mode. It is ignored in Venafi Cloud VenafiConnection - // mode. + // mode and in MachineHub mode. Server string `yaml:"server"` // OrganizationID is only used in Jetstack Secure OAuth and Jetstack Secure @@ -62,6 +62,9 @@ type Config struct { ExcludeAnnotationKeysRegex []string `yaml:"exclude-annotation-keys-regex"` // Skips label keys that match the given set of regular expressions. ExcludeLabelKeysRegex []string `yaml:"exclude-label-keys-regex"` + + // MachineHub holds config specific to MachineHub mode. + MachineHub MachineHubConfig `yaml:"machineHub"` } type Endpoint struct { @@ -89,6 +92,33 @@ type VenafiCloudConfig struct { UploadPath string `yaml:"upload_path,omitempty"` } +// MachineHubConfig holds configuration values specific to the CyberArk Machine Hub integration +type MachineHubConfig struct { + // Subdomain is the subdomain indicating where data should be pushed. Used + // for querying the Service Discovery Service to discover the Identity API + // URL. + Subdomain string `yaml:"subdomain"` + + // CredentialsSecretName is the name of a Kubernetes Secret in the same + // namespace as the agent, which will be watched for a username and password + // to send to CyberArk Identity for authentication. + CredentialsSecretName string `yaml:"credentialsSecretName"` +} + +func (mhc MachineHubConfig) Validate() error { + var errs []error + + if mhc.Subdomain == "" { + errs = append(errs, fmt.Errorf("subdomain must not be empty in MachineHub mode")) + } + + if mhc.CredentialsSecretName == "" { + errs = append(errs, fmt.Errorf("credentialsSecretName must not be empty in MachineHub mode")) + } + + return errors.Join(errs...) +} + type AgentCmdFlags struct { // ConfigFilePath (--config-file, -c) is the path to the agent configuration // YAML file. @@ -103,6 +133,9 @@ type AgentCmdFlags struct { // --credentials-file. VenafiCloudMode bool + // MachineHubMode configures the agent to send data to CyberArk Machine Hub. + MachineHubMode bool + // ClientID (--client-id) is the clientID in case of Venafi Cloud Key Pair // Service Account mode. ClientID string @@ -303,30 +336,48 @@ func InitAgentCmdFlags(c *cobra.Command, cfg *AgentCmdFlags) { ) c.PersistentFlags().MarkDeprecated("disable-compression", "no longer has an effect") + // This is a hidden feature flag we use to build the "Machine Hub" feature + // gradually without impacting customers. Once the feature is GA, we will + // turn this flag "on" by default. + c.PersistentFlags().BoolVar( + &cfg.MachineHubMode, + "machine-hub", + false, + "Enables the MachineHub mode. The agent will push data to CyberArk MachineHub.", + ) + c.PersistentFlags().MarkHidden("machine-hub") + } -type AuthMode string +// TLSPKMode controls how to authenticate to TLSPK / Jetstack Secure. Only one +// TLSPKMode may be provided if using using those backends. +type TLSPKMode string const ( - JetstackSecureOAuth AuthMode = "Jetstack Secure OAuth" - JetstackSecureAPIToken AuthMode = "Jetstack Secure API Token" - VenafiCloudKeypair AuthMode = "Venafi Cloud Key Pair Service Account" - VenafiCloudVenafiConnection AuthMode = "Venafi Cloud VenafiConnection" + JetstackSecureOAuth TLSPKMode = "Jetstack Secure OAuth" + JetstackSecureAPIToken TLSPKMode = "Jetstack Secure API Token" + VenafiCloudKeypair TLSPKMode = "Venafi Cloud Key Pair Service Account" + VenafiCloudVenafiConnection TLSPKMode = "Venafi Cloud VenafiConnection" + + // It is possible to push to both MachineHub and TLSPK. With this mode, the + // agent will only push to MachineHub and not to TLSPK. + Off TLSPKMode = "MachineHub only" ) // The command-line flags and the config file are combined into this struct by // ValidateAndCombineConfig. type CombinedConfig struct { - AuthMode AuthMode - - // Used by all modes. - ClusterID string DataGatherers []DataGatherer Period time.Duration BackoffMaxTime time.Duration + InstallNS string StrictMode bool OneShot bool - InstallNS string + + TLSPKMode TLSPKMode + + // Used by all TLSPK modes. + ClusterID string // Used by JetstackSecureOAuth, JetstackSecureAPIToken, and // VenafiCloudKeypair. Ignored in VenafiCloudVenafiConnection mode. @@ -351,6 +402,11 @@ type CombinedConfig struct { // Only used for testing purposes. OutputPath string InputPath string + + // MachineHub-related settings. + MachineHubMode bool + MachineHubSubdomain string + MachineHubCredentialsSecretName string } // ValidateAndCombineConfig combines and validates the input configuration with @@ -366,9 +422,23 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags) res := CombinedConfig{} var errs error + if flags.MachineHubMode { + err := cfg.MachineHub.Validate() + if err != nil { + return CombinedConfig{}, nil, fmt.Errorf("invalid MachineHub config provided: %w", err) + } + + res.MachineHubMode = true + res.MachineHubSubdomain = cfg.MachineHub.Subdomain + res.MachineHubCredentialsSecretName = cfg.MachineHub.CredentialsSecretName + + keysAndValues := []any{"credentialsSecretName", res.MachineHubCredentialsSecretName} + log.V(logs.Info).Info("Will push to CyberArk MachineHub using a username and password loaded from a Kubernetes Secret", keysAndValues...) + } + { var ( - mode AuthMode + mode TLSPKMode reason string keysAndValues []any ) @@ -396,19 +466,30 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags) mode = JetstackSecureOAuth reason = "--credentials-file was specified without --venafi-cloud" default: - return CombinedConfig{}, nil, fmt.Errorf("no auth mode specified. You can use one of four auth modes:\n" + - " - Use (--venafi-cloud with --credentials-file) or (--client-id with --private-key-path) to use the " + string(VenafiCloudKeypair) + " mode.\n" + - " - Use --venafi-connection for the " + string(VenafiCloudVenafiConnection) + " mode.\n" + - " - Use --credentials-file alone if you want to use the " + string(JetstackSecureOAuth) + " mode.\n" + - " - Use --api-token if you want to use the " + string(JetstackSecureAPIToken) + " mode.\n") + if !flags.MachineHubMode { + return CombinedConfig{}, nil, fmt.Errorf("no TLSPK mode specified and MachineHub mode is disabled. You must either enable the MachineHub mode (using --machine-hub), or enable one of the TLSPK modes.\n" + + "To enable one of the TLSPK modes, you can:\n" + + " - Use (--venafi-cloud with --credentials-file) or (--client-id with --private-key-path) to use the " + string(VenafiCloudKeypair) + " mode.\n" + + " - Use --venafi-connection for the " + string(VenafiCloudVenafiConnection) + " mode.\n" + + " - Use --credentials-file alone if you want to use the " + string(JetstackSecureOAuth) + " mode.\n" + + " - Use --api-token if you want to use the " + string(JetstackSecureAPIToken) + " mode.\n" + + "Note that it is possible to use one of the TLSPK modes along with the MachineHub mode (--machine-hub).") + } + + mode = Off } - res.AuthMode = mode + keysAndValues = append(keysAndValues, "mode", mode, "reason", reason) - log.V(logs.Debug).Info("Authentication mode", keysAndValues...) + if mode != Off { + log.V(logs.Debug).Info("Configured to push to Venafi", keysAndValues...) + } + + res.TLSPKMode = mode } // Validation and defaulting of `server` and the deprecated `endpoint.path`. - { + if res.TLSPKMode != Off { + // Only relevant if using TLSPK backends hasEndpointField := cfg.Endpoint.Host != "" && cfg.Endpoint.Path != "" hasServerField := cfg.Server != "" var server string @@ -430,7 +511,7 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags) endpointPath = cfg.Endpoint.Path case !hasServerField && !hasEndpointField: server = "https://preflight.jetstack.io" - if res.AuthMode == VenafiCloudKeypair { + if res.TLSPKMode == VenafiCloudKeypair { // The VenafiCloudVenafiConnection mode doesn't need a server. server = client.VenafiCloudProdURL } @@ -439,7 +520,7 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags) if urlErr != nil || url.Hostname() == "" { errs = multierror.Append(errs, fmt.Errorf("server %q is not a valid URL", server)) } - if res.AuthMode == VenafiCloudVenafiConnection && server != "" { + if res.TLSPKMode == VenafiCloudVenafiConnection && server != "" { log.Info(fmt.Sprintf("ignoring the server field specified in the config file. In %s mode, this field is not needed.", VenafiCloudVenafiConnection)) server = "" } @@ -451,9 +532,9 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags) { var uploadPath string switch { - case res.AuthMode == VenafiCloudKeypair: + case res.TLSPKMode == VenafiCloudKeypair: if cfg.VenafiCloud == nil || cfg.VenafiCloud.UploadPath == "" { - errs = multierror.Append(errs, fmt.Errorf("the venafi-cloud.upload_path field is required when using the %s mode", res.AuthMode)) + errs = multierror.Append(errs, fmt.Errorf("the venafi-cloud.upload_path field is required when using the %s mode", res.TLSPKMode)) break // Skip to the end of the switch statement. } _, urlErr := url.Parse(cfg.VenafiCloud.UploadPath) @@ -463,14 +544,14 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags) } uploadPath = cfg.VenafiCloud.UploadPath - case res.AuthMode == VenafiCloudVenafiConnection: + case res.TLSPKMode == VenafiCloudVenafiConnection: // The venafi-cloud.upload_path was initially meant to let users // configure HTTP proxies, but it has never been used since HTTP // proxies don't rewrite paths. Thus, we've disabled the ability to // change this value with the new --venafi-connection flag, and this // field is simply ignored. if cfg.VenafiCloud != nil && cfg.VenafiCloud.UploadPath != "" { - log.Info(fmt.Sprintf(`ignoring the venafi-cloud.upload_path field in the config file. In %s mode, this field is not needed.`, res.AuthMode)) + log.Info(fmt.Sprintf(`ignoring the venafi-cloud.upload_path field in the config file. In %s mode, this field is not needed.`, res.TLSPKMode)) } uploadPath = "" } @@ -488,26 +569,26 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags) // https://venafi.atlassian.net/browse/VC-35385 is done. { if cfg.VenafiCloud != nil && cfg.VenafiCloud.UploaderID != "" { - log.Info(fmt.Sprintf(`ignoring the venafi-cloud.uploader_id field in the config file. This field is not needed in %s mode.`, res.AuthMode)) + log.Info(fmt.Sprintf(`ignoring the venafi-cloud.uploader_id field in the config file. This field is not needed in %s mode.`, res.TLSPKMode)) } } // Validation of `cluster_id` and `organization_id`. - { + if res.TLSPKMode != Off { var clusterID string var organizationID string // Only used by the old jetstack-secure mode. switch { - case res.AuthMode == VenafiCloudKeypair: + case res.TLSPKMode == VenafiCloudKeypair: if cfg.ClusterID == "" { - errs = multierror.Append(errs, fmt.Errorf("cluster_id is required in %s mode", res.AuthMode)) + errs = multierror.Append(errs, fmt.Errorf("cluster_id is required in %s mode", res.TLSPKMode)) } clusterID = cfg.ClusterID - case res.AuthMode == VenafiCloudVenafiConnection: + case res.TLSPKMode == VenafiCloudVenafiConnection: if cfg.ClusterID == "" { - errs = multierror.Append(errs, fmt.Errorf("cluster_id is required in %s mode", res.AuthMode)) + errs = multierror.Append(errs, fmt.Errorf("cluster_id is required in %s mode", res.TLSPKMode)) } clusterID = cfg.ClusterID - case res.AuthMode == JetstackSecureOAuth || res.AuthMode == JetstackSecureAPIToken: + case res.TLSPKMode == JetstackSecureOAuth || res.TLSPKMode == JetstackSecureAPIToken: if cfg.OrganizationID == "" { errs = multierror.Append(errs, fmt.Errorf("organization_id is required")) } @@ -567,7 +648,7 @@ func ValidateAndCombineConfig(log logr.Logger, cfg Config, flags AgentCmdFlags) res.InstallNS = installNS // Validation of --venafi-connection and --venafi-connection-namespace. - if res.AuthMode == VenafiCloudVenafiConnection { + if res.TLSPKMode == VenafiCloudVenafiConnection { res.VenConnName = flags.VenConnName var venConnNS string = flags.VenConnNS if flags.VenConnNS == "" { @@ -634,7 +715,7 @@ func validateCredsAndCreateClient(log logr.Logger, flagCredentialsPath, flagClie var preflightClient client.Client metadata := &api.AgentMetadata{Version: version.PreflightVersion, ClusterID: cfg.ClusterID} switch { - case cfg.AuthMode == JetstackSecureOAuth: + case cfg.TLSPKMode == JetstackSecureOAuth: // Note that there are no command line flags to configure the // JetstackSecureOAuth mode. credsBytes, err := readCredentialsFile(flagCredentialsPath) @@ -653,7 +734,7 @@ func validateCredsAndCreateClient(log logr.Logger, flagCredentialsPath, flagClie if err != nil { errs = multierror.Append(errs, err) } - case cfg.AuthMode == VenafiCloudKeypair: + case cfg.TLSPKMode == VenafiCloudKeypair: var creds client.Credentials if flagClientID != "" && flagCredentialsPath != "" { @@ -696,7 +777,7 @@ func validateCredsAndCreateClient(log logr.Logger, flagCredentialsPath, flagClie if err != nil { errs = multierror.Append(errs, err) } - case cfg.AuthMode == VenafiCloudVenafiConnection: + case cfg.TLSPKMode == VenafiCloudVenafiConnection: var restCfg *rest.Config restCfg, err := kubeconfig.LoadRESTConfig("") if err != nil { @@ -708,18 +789,20 @@ func validateCredsAndCreateClient(log logr.Logger, flagCredentialsPath, flagClie if err != nil { errs = multierror.Append(errs, err) } - case cfg.AuthMode == JetstackSecureAPIToken: + case cfg.TLSPKMode == JetstackSecureAPIToken: var err error preflightClient, err = client.NewAPITokenClient(metadata, flagAPIToken, cfg.Server) if err != nil { errs = multierror.Append(errs, err) } + case cfg.TLSPKMode == Off: + // No client needed in this mode. default: - panic(fmt.Errorf("programmer mistake: auth mode not implemented: %s", cfg.AuthMode)) + panic(fmt.Errorf("programmer mistake: auth mode not implemented: %s", cfg.TLSPKMode)) } if errs != nil { - return nil, fmt.Errorf("failed loading config using the %s mode: %w", cfg.AuthMode, errs) + return nil, fmt.Errorf("failed loading config using the %s mode: %w", cfg.TLSPKMode, errs) } return preflightClient, nil @@ -756,7 +839,7 @@ func createCredentialClient(log logr.Logger, credentials client.Credentials, cfg uploaderID := "no" var uploadPath string - if cfg.AuthMode == VenafiCloudKeypair { + if cfg.TLSPKMode == VenafiCloudKeypair { // We don't do this for the VenafiCloudVenafiConnection mode because // the upload_path field is ignored in that mode. log.Info("Loading upload_path from \"venafi-cloud\" configuration.") @@ -846,7 +929,7 @@ func (c *Config) Dump() (string, error) { d, err := yaml.Marshal(&c) if err != nil { - return "", errors.Wrap(err, "failed to generate YAML dump of config") + return "", fmt.Errorf("failed to generate YAML dump of config: %w", err) } return string(d), nil diff --git a/pkg/agent/config_test.go b/pkg/agent/config_test.go index 414c1590..c07310b8 100644 --- a/pkg/agent/config_test.go +++ b/pkg/agent/config_test.go @@ -96,7 +96,7 @@ func Test_ValidateAndCombineConfig(t *testing.T) { withCmdLineFlags("--period", "99m", "--credentials-file", fakeCredsPath)) require.NoError(t, err) assert.Equal(t, testutil.Undent(` - INFO Authentication mode mode="Jetstack Secure OAuth" reason="--credentials-file was specified without --venafi-cloud" + INFO Configured to push to Venafi mode="Jetstack Secure OAuth" reason="--credentials-file was specified without --venafi-cloud" INFO Both the 'period' field and --period are set. Using the value provided with --period. `), gotLogs.String()) assert.Equal(t, 99*time.Minute, got.Period) @@ -178,7 +178,7 @@ func Test_ValidateAndCombineConfig(t *testing.T) { // The log line printed by pflag is not captured by the log recorder. assert.Equal(t, testutil.Undent(` - INFO Authentication mode mode="Jetstack Secure OAuth" reason="--credentials-file was specified without --venafi-cloud" + INFO Configured to push to Venafi mode="Jetstack Secure OAuth" reason="--credentials-file was specified without --venafi-cloud" INFO Using period from config period="1h0m0s" `), b.String()) }) @@ -194,12 +194,13 @@ func Test_ValidateAndCombineConfig(t *testing.T) { withoutCmdLineFlags(), ) assert.EqualError(t, err, testutil.Undent(` - no auth mode specified. You can use one of four auth modes: + no TLSPK mode specified and MachineHub mode is disabled. You must either enable the MachineHub mode (using --machine-hub), or enable one of the TLSPK modes. + To enable one of the TLSPK modes, you can: - Use (--venafi-cloud with --credentials-file) or (--client-id with --private-key-path) to use the Venafi Cloud Key Pair Service Account mode. - Use --venafi-connection for the Venafi Cloud VenafiConnection mode. - Use --credentials-file alone if you want to use the Jetstack Secure OAuth mode. - Use --api-token if you want to use the Jetstack Secure API Token mode. - `)) + Note that it is possible to use one of the TLSPK modes along with the MachineHub mode (--machine-hub).`)) assert.Nil(t, cl) }) @@ -227,7 +228,7 @@ func Test_ValidateAndCombineConfig(t *testing.T) { withCmdLineFlags("--credentials-file", credsPath), ) expect := CombinedConfig{ - AuthMode: "Jetstack Secure OAuth", + TLSPKMode: "Jetstack Secure OAuth", ClusterID: "example-cluster", DataGatherers: []DataGatherer{{Kind: "dummy", Name: "d1", @@ -276,7 +277,7 @@ func Test_ValidateAndCombineConfig(t *testing.T) { InputPath: "/home", OutputPath: "/nothome", UploadPath: "/testing/path", - AuthMode: VenafiCloudKeypair, + TLSPKMode: VenafiCloudKeypair, ClusterID: "the cluster name", BackoffMaxTime: 99 * time.Minute, InstallNS: "venafi", @@ -300,7 +301,7 @@ func Test_ValidateAndCombineConfig(t *testing.T) { withCmdLineFlags("--client-id", "5bc7d07c-45da-11ef-a878-523f1e1d7de1", "--private-key-path", privKeyPath), ) require.NoError(t, err) - assert.Equal(t, VenafiCloudKeypair, got.AuthMode) + assert.Equal(t, VenafiCloudKeypair, got.TLSPKMode) assert.IsType(t, &client.VenafiCloudClient{}, cl) }) @@ -389,7 +390,7 @@ func Test_ValidateAndCombineConfig(t *testing.T) { `)), withCmdLineFlags("--credentials-file", path)) require.NoError(t, err) - assert.Equal(t, CombinedConfig{Server: "https://api.venafi.eu", Period: time.Hour, OrganizationID: "foo", ClusterID: "bar", AuthMode: JetstackSecureOAuth, BackoffMaxTime: 10 * time.Minute, InstallNS: "venafi"}, got) + assert.Equal(t, CombinedConfig{Server: "https://api.venafi.eu", Period: time.Hour, OrganizationID: "foo", ClusterID: "bar", TLSPKMode: JetstackSecureOAuth, BackoffMaxTime: 10 * time.Minute, InstallNS: "venafi"}, got) assert.IsType(t, &client.OAuthClient{}, cl) }) @@ -468,7 +469,7 @@ func Test_ValidateAndCombineConfig(t *testing.T) { `)), withCmdLineFlags("--client-id", "5bc7d07c-45da-11ef-a878-523f1e1d7de1", "--private-key-path", path)) require.NoError(t, err) - assert.Equal(t, CombinedConfig{Server: "https://api.venafi.eu", Period: time.Hour, AuthMode: VenafiCloudKeypair, ClusterID: "the cluster name", UploadPath: "/foo/bar", BackoffMaxTime: 10 * time.Minute, InstallNS: "venafi"}, got) + assert.Equal(t, CombinedConfig{Server: "https://api.venafi.eu", Period: time.Hour, TLSPKMode: VenafiCloudKeypair, ClusterID: "the cluster name", UploadPath: "/foo/bar", BackoffMaxTime: 10 * time.Minute, InstallNS: "venafi"}, got) assert.IsType(t, &client.VenafiCloudClient{}, cl) }) @@ -490,7 +491,7 @@ func Test_ValidateAndCombineConfig(t *testing.T) { `)), withCmdLineFlags("--venafi-cloud", "--credentials-file", credsPath)) require.NoError(t, err) - assert.Equal(t, CombinedConfig{Server: "https://api.venafi.eu", Period: time.Hour, AuthMode: VenafiCloudKeypair, ClusterID: "the cluster name", UploadPath: "/foo/bar", BackoffMaxTime: 10 * time.Minute, InstallNS: "venafi"}, got) + assert.Equal(t, CombinedConfig{Server: "https://api.venafi.eu", Period: time.Hour, TLSPKMode: VenafiCloudKeypair, ClusterID: "the cluster name", UploadPath: "/foo/bar", BackoffMaxTime: 10 * time.Minute, InstallNS: "venafi"}, got) }) t.Run("venafi-cloud-keypair-auth: venafi-cloud.upload_path field is required", func(t *testing.T) { @@ -567,7 +568,7 @@ func Test_ValidateAndCombineConfig(t *testing.T) { assert.Equal(t, CombinedConfig{ Period: 1 * time.Hour, ClusterID: "the cluster name", - AuthMode: VenafiCloudVenafiConnection, + TLSPKMode: VenafiCloudVenafiConnection, VenConnName: "venafi-components", VenConnNS: "venafi", InstallNS: "venafi", @@ -593,13 +594,13 @@ func Test_ValidateAndCombineConfig(t *testing.T) { ) require.NoError(t, err) assert.Equal(t, testutil.Undent(` - INFO Authentication mode venConnName="venafi-components" mode="Venafi Cloud VenafiConnection" reason="--venafi-connection was specified" + INFO Configured to push to Venafi venConnName="venafi-components" mode="Venafi Cloud VenafiConnection" reason="--venafi-connection was specified" INFO ignoring the server field specified in the config file. In Venafi Cloud VenafiConnection mode, this field is not needed. INFO ignoring the venafi-cloud.upload_path field in the config file. In Venafi Cloud VenafiConnection mode, this field is not needed. INFO ignoring the venafi-cloud.uploader_id field in the config file. This field is not needed in Venafi Cloud VenafiConnection mode. INFO Using period from config period="1h0m0s" `), gotLogs.String()) - assert.Equal(t, VenafiCloudVenafiConnection, got.AuthMode) + assert.Equal(t, VenafiCloudVenafiConnection, got.TLSPKMode) assert.IsType(t, &client.VenConnClient{}, gotCl) }) @@ -614,7 +615,43 @@ func Test_ValidateAndCombineConfig(t *testing.T) { `)), withCmdLineFlags("--venafi-connection", "venafi-components")) require.NoError(t, err) - assert.Equal(t, VenafiCloudVenafiConnection, got.AuthMode) + assert.Equal(t, VenafiCloudVenafiConnection, got.TLSPKMode) + }) + + t.Run("machinehub only: username and password", func(t *testing.T) { + t.Setenv("POD_NAMESPACE", "venafi") + t.Setenv("KUBECONFIG", withFile(t, fakeKubeconfig)) + got, _, err := ValidateAndCombineConfig(discardLogs(), + withConfig(testutil.Undent(` + machineHub: + subdomain: foo + credentialsSecretName: secret-1 + period: 1h + `)), + withCmdLineFlags("--machine-hub")) + require.NoError(t, err) + assert.Equal(t, Off, got.TLSPKMode) + assert.Equal(t, true, got.MachineHubMode) + }) + + t.Run("machinehub + venafi-cloud-keypair-auth should work simultanously", func(t *testing.T) { + t.Setenv("POD_NAMESPACE", "venafi") + t.Setenv("KUBECONFIG", withFile(t, fakeKubeconfig)) + privKeyPath := withFile(t, fakePrivKeyPEM) + got, _, err := ValidateAndCombineConfig(discardLogs(), + withConfig(testutil.Undent(` + machineHub: + subdomain: foo + credentialsSecretName: secret-1 + period: 1h + venafi-cloud: + upload_path: /v1/tlspk/upload/clusterdata + cluster_id: foo + `)), + withCmdLineFlags("--machine-hub", "--venafi-cloud", "--client-id", "5bc7d07c-45da-11ef-a878-523f1e1d7de1", "--private-key-path", privKeyPath)) + require.NoError(t, err) + assert.Equal(t, VenafiCloudKeypair, got.TLSPKMode) + assert.Equal(t, true, got.MachineHubMode) }) } @@ -653,7 +690,7 @@ func Test_ValidateAndCombineConfig_VenafiCloudKeyPair(t *testing.T) { ) require.NoError(t, err) testutil.TrustCA(t, cl, cert) - assert.Equal(t, VenafiCloudKeypair, got.AuthMode) + assert.Equal(t, VenafiCloudKeypair, got.TLSPKMode) err = cl.PostDataReadingsWithOptions(ctx, nil, client.Options{ClusterName: "test cluster name"}) require.NoError(t, err) diff --git a/pkg/agent/run.go b/pkg/agent/run.go index 59cc79dd..69f8528a 100644 --- a/pkg/agent/run.go +++ b/pkg/agent/run.go @@ -327,22 +327,43 @@ func gatherAndOutputData(ctx context.Context, eventf Eventf, config CombinedConf } log.Info("Data saved to local file", "outputPath", config.OutputPath) } else { + group, ctx := errgroup.WithContext(ctx) + backOff := backoff.NewExponentialBackOff() backOff.InitialInterval = 30 * time.Second backOff.MaxInterval = 3 * time.Minute - post := func() (any, error) { - return struct{}{}, postData(klog.NewContext(ctx, log), config, preflightClient, readings) - } - notificationFunc := backoff.Notify(func(err error, t time.Duration) { eventf("Warning", "PushingErr", "retrying in %v after error: %s", t, err) log.Info("Warning: PushingErr: retrying", "in", t, "reason", err) }) - _, err := backoff.Retry(ctx, post, backoff.WithBackOff(backOff), backoff.WithNotify(notificationFunc), backoff.WithMaxElapsedTime(config.BackoffMaxTime)) - if err != nil { - return fmt.Errorf("Exiting due to fatal error uploading: %v", err) + if config.MachineHubMode { + post := func() (any, error) { + log.Info("machine hub mode not yet implemented") + return struct{}{}, nil + } + + group.Go(func() error { + _, err := backoff.Retry(ctx, post, backoff.WithBackOff(backOff), backoff.WithNotify(notificationFunc), backoff.WithMaxElapsedTime(config.BackoffMaxTime)) + return err + }) + } + + if config.TLSPKMode != Off { + post := func() (any, error) { + return struct{}{}, postData(klog.NewContext(ctx, log), config, preflightClient, readings) + } + + group.Go(func() error { + _, err := backoff.Retry(ctx, post, backoff.WithBackOff(backOff), backoff.WithNotify(notificationFunc), backoff.WithMaxElapsedTime(config.BackoffMaxTime)) + return err + }) + } + + groupErr := group.Wait() + if groupErr != nil { + return fmt.Errorf("got a fatal error from one or more upload actions: %s", groupErr) } } return nil @@ -404,7 +425,7 @@ func postData(ctx context.Context, config CombinedConfig, preflightClient client log.V(logs.Debug).Info("Posting data", "baseURL", baseURL) - if config.AuthMode == VenafiCloudKeypair || config.AuthMode == VenafiCloudVenafiConnection { + if config.TLSPKMode == VenafiCloudKeypair || config.TLSPKMode == VenafiCloudVenafiConnection { // orgID and clusterID are not required for Venafi Cloud auth err := preflightClient.PostDataReadingsWithOptions(ctx, readings, client.Options{ ClusterName: config.ClusterID,