diff --git a/ziti/cmd/ziti/cmd/create_config_controller.go b/ziti/cmd/ziti/cmd/create_config_controller.go index 693b82749..2e5cc10dc 100644 --- a/ziti/cmd/ziti/cmd/create_config_controller.go +++ b/ziti/cmd/ziti/cmd/create_config_controller.go @@ -139,7 +139,6 @@ func (options *CreateConfigControllerOptions) run(data *ConfigTemplateValues) er // Check if the path exists, fail if it doesn't basePath := filepath.Dir(options.Output) + "/" if _, err := os.Stat(filepath.Dir(basePath)); os.IsNotExist(err) { - logrus.Fatalf("Provided path: [%s] does not exist\n", basePath) return err } diff --git a/ziti/cmd/ziti/cmd/create_config_controller_test.go b/ziti/cmd/ziti/cmd/create_config_controller_test.go new file mode 100644 index 000000000..af2804ed3 --- /dev/null +++ b/ziti/cmd/ziti/cmd/create_config_controller_test.go @@ -0,0 +1,42 @@ +package cmd + +import ( + "github.com/stretchr/testify/assert" + "testing" + "time" +) + +func TestControllerOutputPathDoesNotExist(t *testing.T) { + expectedErrorMsg := "stat /IDoNotExist: no such file or directory" + + // Create the options with both flags set to true + options := &CreateConfigControllerOptions{} + options.Output = "/IDoNotExist/MyController.yaml" + + err := options.run(&ConfigTemplateValues{}) + + assert.EqualError(t, err, expectedErrorMsg, "Error does not match, expected %s but got %s", expectedErrorMsg, err) +} + +func TestExecuteCreateConfigControllerHasNonBlankTemplateValues(t *testing.T) { + expectedNonEmptyStringFields := []string{".Controller.Name", ".ZitiHome", ".Controller.IdentityCert", ".Controller.IdentityServerCert", ".Controller.IdentityKey", ".Controller.IdentityCA", ".Controller.ListenerAddress", ".Controller.Port", ".Controller.Edge.AdvertisedHostPort", ".Controller.Edge.ZitiSigningCert", ".Controller.Edge.ZitiSigningKey", ".Controller.Edge.ListenerHostPort", ".Controller.Edge.IdentityCA", ".Controller.Edge.IdentityKey", ".Controller.Edge.IdentityServerCert", ".Controller.Edge.IdentityCert", ".Controller.WebListener.MinTLSVersion", ".Controller.WebListener.MaxTLSVersion"} + expectedNonEmptyStringValues := []*string{&data.Controller.Name, &data.ZitiHome, &data.Controller.IdentityCert, &data.Controller.IdentityServerCert, &data.Controller.IdentityKey, &data.Controller.IdentityCA, &data.Controller.ListenerAddress, &data.Controller.Port, &data.Controller.Edge.AdvertisedHostPort, &data.Controller.Edge.ZitiSigningCert, &data.Controller.Edge.ZitiSigningKey, &data.Controller.Edge.ListenerHostPort, &data.Controller.Edge.IdentityCA, &data.Controller.Edge.IdentityKey, &data.Controller.Edge.IdentityServerCert, &data.Controller.Edge.IdentityCert, &data.Controller.WebListener.MinTLSVersion, &data.Controller.WebListener.MaxTLSVersion} + expectedNonEmptyTimeFields := []string{".Controller.HealthCheck.Interval", ".Controller.HealthCheck.Timeout", ".Controller.HealthCheck.InitialDelay", ".Controller.Edge.APISessionTimeout", ".Controller.EdgeIdentityDuration", ".Controller.EdgeRouterDuration", ".Controller.WebListener.IdleTimeout", ".Controller.WebListener.ReadTimeout", ".Controller.WebListener.WriteTimeout"} + expectedNonEmptyTimeValues := []*time.Duration{&data.Controller.HealthCheck.Interval, &data.Controller.HealthCheck.Timeout, &data.Controller.HealthCheck.InitialDelay, &data.Controller.Edge.APISessionTimeout, &data.Controller.EdgeIdentityDuration, &data.Controller.EdgeRouterDuration, &data.Controller.WebListener.IdleTimeout, &data.Controller.WebListener.ReadTimeout, &data.Controller.WebListener.WriteTimeout} + + // Create and run the CLI command (capture output, otherwise config prints to stdout instead of test results) + cmd := NewCmdCreateConfigController() + _ = captureOutput(func() { + _ = cmd.Execute() + }) + + // Check that the expected string template values are not blank + for field, value := range expectedNonEmptyStringValues { + assert.NotEqualf(t, "", *value, expectedNonEmptyStringFields[field]+" should be a non-blank value") + } + + // Check that the expected time.Duration template values are not zero + for field, value := range expectedNonEmptyTimeValues { + assert.NotZero(t, *value, expectedNonEmptyTimeFields[field]+" should be a non-zero value") + } +} diff --git a/ziti/cmd/ziti/cmd/create_config_environment.go b/ziti/cmd/ziti/cmd/create_config_environment.go index 9a71f5a3e..b9984d859 100644 --- a/ziti/cmd/ziti/cmd/create_config_environment.go +++ b/ziti/cmd/ziti/cmd/create_config_environment.go @@ -101,6 +101,8 @@ var ( //go:embed config_templates/environment.yml var environmentConfigTemplate string +var environmentOptions *CreateConfigEnvironmentOptions + // CreateConfigEnvironmentOptions the options for the create environment command type CreateConfigEnvironmentOptions struct { CreateConfigOptions @@ -112,7 +114,7 @@ type CreateConfigEnvironmentOptions struct { // NewCmdCreateConfigEnvironment creates a command object for the "environment" command func NewCmdCreateConfigEnvironment() *cobra.Command { - environmentOptions := &CreateConfigEnvironmentOptions{ + environmentOptions = &CreateConfigEnvironmentOptions{ ConfigTemplateValues: *data, EnvVariableMetaData: EnvVariableMetaData{ OS: "Stuff", diff --git a/ziti/cmd/ziti/cmd/create_config_router.go b/ziti/cmd/ziti/cmd/create_config_router.go index 3fc7044b3..b0d9684f9 100644 --- a/ziti/cmd/ziti/cmd/create_config_router.go +++ b/ziti/cmd/ziti/cmd/create_config_router.go @@ -65,7 +65,7 @@ func NewCmdCreateConfigRouter() *cobra.Command { data.populateDefaults() // Update router data with options passed in - data.Router.Name = routerOptions.RouterName + data.Router.Name = validateRouterName(routerOptions.RouterName) SetZitiRouterIdentity(&data.Router, data.Router.Name) }, Run: func(cmd *cobra.Command, args []string) { diff --git a/ziti/cmd/ziti/cmd/create_config_router_common.go b/ziti/cmd/ziti/cmd/create_config_router_common.go index 4a2d35b3b..8ac190ee4 100644 --- a/ziti/cmd/ziti/cmd/create_config_router_common.go +++ b/ziti/cmd/ziti/cmd/create_config_router_common.go @@ -64,3 +64,12 @@ func SetZitiRouterIdentityCA(r *RouterTemplateValues, routerName string) { } r.IdentityCA = cmdhelper.NormalizePath(val) } + +func validateRouterName(name string) string { + // Currently, only worry about router name if it's blank + if name == "" { + hostname, _ := os.Hostname() + return hostname + } + return name +} diff --git a/ziti/cmd/ziti/cmd/create_config_router_edge.go b/ziti/cmd/ziti/cmd/create_config_router_edge.go index f6262514b..dc6ce81e6 100644 --- a/ziti/cmd/ziti/cmd/create_config_router_edge.go +++ b/ziti/cmd/ziti/cmd/create_config_router_edge.go @@ -93,8 +93,7 @@ func (options *CreateConfigRouterOptions) addEdgeFlags(cmd *cobra.Command) { func (options *CreateConfigRouterOptions) runEdgeRouter(data *ConfigTemplateValues) error { // Ensure private and wss are not both used if options.IsPrivate && options.WssEnabled { - logrus.Fatal("Flags for private and wss configs are mutually exclusive. You must choose private or wss, not both") - return errors.New("Flags for private and wss configs are mutually exclusive.") + return errors.New("Flags for private and wss configs are mutually exclusive. You must choose private or wss, not both") } tmpl, err := template.New("edge-router-config").Parse(routerConfigEdgeTemplate) @@ -107,7 +106,6 @@ func (options *CreateConfigRouterOptions) runEdgeRouter(data *ConfigTemplateValu // Check if the path exists, fail if it doesn't basePath := filepath.Dir(options.Output) + "/" if _, err := os.Stat(filepath.Dir(basePath)); os.IsNotExist(err) { - logrus.Fatalf("Provided path: [%s] does not exist\n", basePath) return err } diff --git a/ziti/cmd/ziti/cmd/create_config_router_fabric.go b/ziti/cmd/ziti/cmd/create_config_router_fabric.go index cdf376ff3..457fb8a31 100644 --- a/ziti/cmd/ziti/cmd/create_config_router_fabric.go +++ b/ziti/cmd/ziti/cmd/create_config_router_fabric.go @@ -90,7 +90,6 @@ func (options *CreateConfigRouterOptions) runFabricRouter(data *ConfigTemplateVa // Check if the path exists, fail if it doesn't basePath := filepath.Dir(options.Output) + "/" if _, err := os.Stat(filepath.Dir(basePath)); os.IsNotExist(err) { - logrus.Fatalf("Provided path: [%s] does not exist\n", basePath) return err } diff --git a/ziti/cmd/ziti/cmd/create_config_router_test.go b/ziti/cmd/ziti/cmd/create_config_router_test.go new file mode 100644 index 000000000..55075883b --- /dev/null +++ b/ziti/cmd/ziti/cmd/create_config_router_test.go @@ -0,0 +1,318 @@ +package cmd + +import ( + "github.com/openziti/ziti/ziti/cmd/ziti/constants" + "github.com/stretchr/testify/assert" + "os" + "testing" + "time" +) + +func clearOptionsAndTemplateData() { + routerOptions = CreateConfigRouterOptions{} + data = &ConfigTemplateValues{} +} + +func TestBlankEdgeRouterNameBecomesHostname(t *testing.T) { + hostname, _ := os.Hostname() + blank := "" + + // Setup options with blank router name + clearOptionsAndTemplateData() + routerOptions.Output = defaultOutput + routerOptions.RouterName = blank + + // Check that template values is a blank name + assert.Equal(t, blank, data.Router.Name, "Mismatch router name, expected %s but got %s", "", hostname) + + // Create and run the CLI command (capture output, otherwise config prints to stdout instead of test results) + cmd := NewCmdCreateConfigRouter() + cmd.SetArgs([]string{"edge", "--routerName", blank}) + _ = captureOutput(func() { + _ = cmd.Execute() + }) + + // Check that the blank name was replaced with hostname in the template values + assert.Equal(t, hostname, data.Router.Name, "Mismatch router name, expected %s but got %s", "", hostname) + +} + +func TestBlankFabricRouterNameBecomesHostname(t *testing.T) { + hostname, _ := os.Hostname() + blank := "" + + // Create the options with empty router name + clearOptionsAndTemplateData() + routerOptions.Output = defaultOutput + routerOptions.RouterName = blank + + // Check that template values is a blank name + assert.Equal(t, blank, data.Router.Name, "Mismatch router name, expected %s but got %s", "", hostname) + + // Create and run the CLI command (capture output, otherwise config prints to stdout instead of test results) + cmd := NewCmdCreateConfigRouter() + cmd.SetArgs([]string{"fabric", "--routerName", blank}) + _ = captureOutput(func() { + _ = cmd.Execute() + }) + + // Check that the blank name was replaced with hostname in the template values + assert.Equal(t, hostname, data.Router.Name, "Mismatch router name, expected %s but got %s", "", hostname) + +} + +func TestEdgeRouterCannotBeWSSAndPrivate(t *testing.T) { + expectedErrorMsg := "Flags for private and wss configs are mutually exclusive. You must choose private or wss, not both" + + // Create the options with both flags set to true + clearOptionsAndTemplateData() + routerOptions.Output = defaultOutput + routerOptions.IsPrivate = true + routerOptions.WssEnabled = true + + err := routerOptions.runEdgeRouter(&ConfigTemplateValues{}) + + assert.EqualError(t, err, expectedErrorMsg, "Error does not match, expected %s but got %s", expectedErrorMsg, err) +} + +func TestEdgeRouterOutputPathDoesNotExist(t *testing.T) { + expectedErrorMsg := "stat /IDoNotExist: no such file or directory" + + // Set the router options + clearOptionsAndTemplateData() + routerOptions.RouterName = "MyEdgeRouter" + routerOptions.Output = "/IDoNotExist/MyEdgeRouter.yaml" + + err := routerOptions.runEdgeRouter(&ConfigTemplateValues{}) + + assert.EqualError(t, err, expectedErrorMsg, "Error does not match, expected %s but got %s", expectedErrorMsg, err) +} + +func TestFabricRouterOutputPathDoesNotExist(t *testing.T) { + expectedErrorMsg := "stat /IDoNotExist: no such file or directory" + + // Set the router options + clearOptionsAndTemplateData() + routerOptions.RouterName = "MyFabricRouter" + routerOptions.Output = "/IDoNotExist/MyFabricRouter.yaml" + + err := routerOptions.runFabricRouter(&ConfigTemplateValues{}) + + assert.EqualError(t, err, expectedErrorMsg, "Error does not match, expected %s but got %s", expectedErrorMsg, err) +} + +func TestSetZitiRouterIdentityCertDefault(t *testing.T) { + // Ensure env variable is not set + _ = os.Setenv(constants.ZitiRouterIdentityCertVarName, "") + + routerName := "RouterTest" + expectedDefault := workingDir + "/" + routerName + ".cert" + rtv := &RouterTemplateValues{} + SetZitiRouterIdentityCert(rtv, routerName) + + // Check that the default is used + assert.Equal(t, expectedDefault, rtv.IdentityCert) +} + +func TestSetZitiRouterIdentityCertCustom(t *testing.T) { + expectedCustom := "My/Custom/Path/for/PKI/RouterTest.cert" + // Set the env variable which is used to populate this value + _ = os.Setenv(constants.ZitiRouterIdentityCertVarName, expectedCustom) + + rtv := &RouterTemplateValues{} + SetZitiRouterIdentityCert(rtv, "Irrelevant") + + // Check that the custom value is used + assert.Equal(t, expectedCustom, rtv.IdentityCert) +} + +func TestSetZitiRouterIdentityServerCertDefault(t *testing.T) { + // Ensure env variable is not set + _ = os.Setenv(constants.ZitiRouterIdentityServerCertVarName, "") + + routerName := "RouterTest" + expectedDefault := workingDir + "/" + routerName + ".server.chain.cert" + rtv := &RouterTemplateValues{} + SetZitiRouterIdentityServerCert(rtv, routerName) + + // Check that the default is used + assert.Equal(t, expectedDefault, rtv.IdentityServerCert) +} + +func TestSetZitiRouterIdentityServerCertCustom(t *testing.T) { + expectedCustom := "My/Custom/Path/for/PKI/RouterTest.server.chain.cert" + // Set the env variable which is used to populate this value + _ = os.Setenv(constants.ZitiRouterIdentityServerCertVarName, expectedCustom) + + rtv := &RouterTemplateValues{} + SetZitiRouterIdentityServerCert(rtv, "Irrelevant") + + // Check that the custom value is used + assert.Equal(t, expectedCustom, rtv.IdentityServerCert) +} + +func TestSetZitiRouterIdentityKeyCertDefault(t *testing.T) { + // Ensure env variable is not set + _ = os.Setenv(constants.ZitiRouterIdentityKeyVarDescription, "") + + routerName := "RouterTest" + expectedDefault := workingDir + "/" + routerName + ".key" + rtv := &RouterTemplateValues{} + SetZitiRouterIdentityKey(rtv, routerName) + + // Check that the default is used + assert.Equal(t, expectedDefault, rtv.IdentityKey) +} + +func TestSetZitiRouterIdentityKeyCustom(t *testing.T) { + expectedCustom := "My/Custom/Path/for/PKI/RouterTest.key" + // Set the env variable which is used to populate this value + _ = os.Setenv(constants.ZitiRouterIdentityKeyVarName, expectedCustom) + + rtv := &RouterTemplateValues{} + SetZitiRouterIdentityKey(rtv, "Irrelevant") + + // Check that the custom value is used + assert.Equal(t, expectedCustom, rtv.IdentityKey) +} + +func TestSetZitiRouterIdentityKeyCADefault(t *testing.T) { + // Ensure env variable is not set + _ = os.Setenv(constants.ZitiRouterIdentityCAVarName, "") + + routerName := "RouterTest" + expectedDefault := workingDir + "/" + routerName + ".cas" + rtv := &RouterTemplateValues{} + SetZitiRouterIdentityCA(rtv, routerName) + + // Check that the default is used + assert.Equal(t, expectedDefault, rtv.IdentityCA) +} + +func TestSetZitiRouterIdentityCACustom(t *testing.T) { + expectedCustom := "My/Custom/Path/for/PKI/RouterTest.cas" + // Set the env variable which is used to populate this value + _ = os.Setenv(constants.ZitiRouterIdentityCAVarName, expectedCustom) + + rtv := &RouterTemplateValues{} + SetZitiRouterIdentityCA(rtv, "Irrelevant") + + // Check that the custom value is used + assert.Equal(t, expectedCustom, rtv.IdentityCA) +} + +func TestSetZitiRouterIdentitySetsAllIdentitiesAndEdgeRouterRawName(t *testing.T) { + // Setup + expectedRawName := "MyEdgeRouterRawName" + blank := "" + rtv := &RouterTemplateValues{} + + // Check that they're all currently blank + assert.Equal(t, blank, rtv.Edge.Hostname) + assert.Equal(t, blank, rtv.IdentityCert) + assert.Equal(t, blank, rtv.IdentityServerCert) + assert.Equal(t, blank, rtv.IdentityKey) + assert.Equal(t, blank, rtv.IdentityCA) + + // Set the env variable + _ = os.Setenv(constants.ZitiEdgeRouterRawNameVarName, expectedRawName) + + SetZitiRouterIdentity(rtv, expectedRawName) + + // Check that the value matches + assert.Equal(t, expectedRawName, rtv.Edge.Hostname) + assert.NotEqualf(t, blank, rtv.IdentityCert, "Router.IdentityCert expected to have a value, instead it was blank") + assert.NotEqualf(t, blank, rtv.IdentityServerCert, "Router.IdentityCert expected to have a value, instead it was blank") + assert.NotEqualf(t, blank, rtv.IdentityKey, "Router.IdentityCert expected to have a value, instead it was blank") + assert.NotEqualf(t, blank, rtv.IdentityCA, "Router.IdentityCert expected to have a value, instead it was blank") +} + +func TestSetZitiRouterIdentitySetsAllIdentitiesAndEdgeRouterRawNameToHostWhenBlank(t *testing.T) { + // Setup + expectedRawName, _ := os.Hostname() + blank := "" + rtv := &RouterTemplateValues{} + + // Check that they're all currently blank + assert.Equal(t, blank, rtv.Edge.Hostname) + assert.Equal(t, blank, rtv.IdentityCert) + assert.Equal(t, blank, rtv.IdentityServerCert) + assert.Equal(t, blank, rtv.IdentityKey) + assert.Equal(t, blank, rtv.IdentityCA) + + // Set the env variable to an empty value + _ = os.Setenv(constants.ZitiEdgeRouterRawNameVarName, "") + + SetZitiRouterIdentity(rtv, expectedRawName) + + // Check that the value matches + assert.Equal(t, expectedRawName, rtv.Edge.Hostname) + assert.NotEqualf(t, blank, rtv.IdentityCert, "Router.IdentityCert expected to have a value, instead it was blank") + assert.NotEqualf(t, blank, rtv.IdentityServerCert, "Router.IdentityCert expected to have a value, instead it was blank") + assert.NotEqualf(t, blank, rtv.IdentityKey, "Router.IdentityCert expected to have a value, instead it was blank") + assert.NotEqualf(t, blank, rtv.IdentityCA, "Router.IdentityCert expected to have a value, instead it was blank") +} + +func TestExecuteCreateConfigRouterEdgeHasNonBlankTemplateValues(t *testing.T) { + routerName := "MyEdgeRouter" + expectedNonEmptyStringFields := []string{".ZitiHome", ".Hostname", ".Router.Name", ".Router.IdentityCert", ".Router.IdentityServerCert", ".Router.IdentityKey", ".Router.IdentityCA", ".Router.Edge.Hostname", ".Router.Edge.Port"} + expectedNonEmptyStringValues := []*string{&data.ZitiHome, &data.Hostname, &data.Router.Name, &data.Router.IdentityCert, &data.Router.IdentityServerCert, &data.Router.IdentityKey, &data.Router.IdentityCA, &data.Router.Edge.Hostname, &data.Router.Edge.Port} + expectedNonEmptyIntFields := []string{".Router.Listener.BindPort", ".Router.Listener.OutQueueSize", ".Router.Wss.ReadBufferSize", ".Router.Wss.WriteBufferSize", ".Router.Forwarder.XgressDialQueueLength", ".Router.Forwarder.XgressDialWorkerCount", ".Router.Forwarder.LinkDialQueueLength", ".Router.Forwarder.LinkDialWorkerCount"} + expectedNonEmptyIntValues := []*int{&data.Router.Listener.BindPort, &data.Router.Listener.OutQueueSize, &data.Router.Wss.ReadBufferSize, &data.Router.Wss.WriteBufferSize, &data.Router.Forwarder.XgressDialQueueLength, &data.Router.Forwarder.XgressDialWorkerCount, &data.Router.Forwarder.LinkDialQueueLength, &data.Router.Forwarder.LinkDialWorkerCount} + expectedNonEmptyTimeFields := []string{".Router.Listener.ConnectTimeout", "Router.Listener.GetSessionTimeout", ".Router.Wss.WriteTimeout", ".Router.Wss.ReadTimeout", ".Router.Wss.IdleTimeout", ".Router.Wss.PongTimeout", ".Router.Wss.PingInterval", ".Router.Wss.HandshakeTimeout", ".Router.Forwarder.LatencyProbeInterval"} + expectedNonEmptyTimeValues := []*time.Duration{&data.Router.Listener.ConnectTimeout, &data.Router.Listener.GetSessionTimeout, &data.Router.Wss.WriteTimeout, &data.Router.Wss.ReadTimeout, &data.Router.Wss.IdleTimeout, &data.Router.Wss.PongTimeout, &data.Router.Wss.PingInterval, &data.Router.Wss.HandshakeTimeout, &data.Router.Forwarder.LatencyProbeInterval} + + // Create and run the CLI command (capture output, otherwise config prints to stdout instead of test results) + cmd := NewCmdCreateConfigRouter() + cmd.SetArgs([]string{"edge", "--routerName", routerName}) + _ = captureOutput(func() { + _ = cmd.Execute() + }) + + // Check that the expected string template values are not blank + for field, value := range expectedNonEmptyStringValues { + assert.NotEqualf(t, "", *value, expectedNonEmptyStringFields[field]+" should be a non-blank value") + } + + // Check that the expected int template values are not zero + for field, value := range expectedNonEmptyIntValues { + assert.NotZero(t, *value, expectedNonEmptyIntFields[field]+" should be a non-zero value") + } + + // Check that the expected time.Duration template values are not zero + for field, value := range expectedNonEmptyTimeValues { + assert.NotZero(t, *value, expectedNonEmptyTimeFields[field]+" should be a non-zero value") + } +} + +func TestExecuteCreateConfigRouterFabricHasNonBlankTemplateValues(t *testing.T) { + routerName := "MyFabricRouter" + expectedNonEmptyStringFields := []string{".ZitiHome", ".Hostname", ".Router.Name", ".Router.IdentityCert", ".Router.IdentityServerCert", ".Router.IdentityKey", ".Router.IdentityCA", ".Router.Edge.Hostname", ".Router.Edge.Port"} + expectedNonEmptyStringValues := []*string{&data.ZitiHome, &data.Hostname, &data.Router.Name, &data.Router.IdentityCert, &data.Router.IdentityServerCert, &data.Router.IdentityKey, &data.Router.IdentityCA, &data.Router.Edge.Hostname, &data.Router.Edge.Port} + expectedNonEmptyIntFields := []string{".Router.Listener.BindPort", ".Router.Listener.OutQueueSize", ".Router.Wss.ReadBufferSize", ".Router.Wss.WriteBufferSize", ".Router.Forwarder.XgressDialQueueLength", ".Router.Forwarder.XgressDialWorkerCount", ".Router.Forwarder.LinkDialQueueLength", ".Router.Forwarder.LinkDialWorkerCount"} + expectedNonEmptyIntValues := []*int{&data.Router.Listener.BindPort, &data.Router.Listener.OutQueueSize, &data.Router.Wss.ReadBufferSize, &data.Router.Wss.WriteBufferSize, &data.Router.Forwarder.XgressDialQueueLength, &data.Router.Forwarder.XgressDialWorkerCount, &data.Router.Forwarder.LinkDialQueueLength, &data.Router.Forwarder.LinkDialWorkerCount} + expectedNonEmptyTimeFields := []string{".Router.Listener.ConnectTimeout", "Router.Listener.GetSessionTimeout", ".Router.Wss.WriteTimeout", ".Router.Wss.ReadTimeout", ".Router.Wss.IdleTimeout", ".Router.Wss.PongTimeout", ".Router.Wss.PingInterval", ".Router.Wss.HandshakeTimeout", ".Router.Forwarder.LatencyProbeInterval"} + expectedNonEmptyTimeValues := []*time.Duration{&data.Router.Listener.ConnectTimeout, &data.Router.Listener.GetSessionTimeout, &data.Router.Wss.WriteTimeout, &data.Router.Wss.ReadTimeout, &data.Router.Wss.IdleTimeout, &data.Router.Wss.PongTimeout, &data.Router.Wss.PingInterval, &data.Router.Wss.HandshakeTimeout, &data.Router.Forwarder.LatencyProbeInterval} + + // Create and run the CLI command (capture output, otherwise config prints to stdout instead of test results) + cmd := NewCmdCreateConfigRouter() + cmd.SetArgs([]string{"fabric", "--routerName", routerName}) + _ = captureOutput(func() { + _ = cmd.Execute() + }) + + // Check that the expected string template values are not blank + for field, value := range expectedNonEmptyStringValues { + assert.NotEqualf(t, "", *value, expectedNonEmptyStringFields[field]+" should be a non-blank value") + } + + // Check that the expected int template values are not zero + for field, value := range expectedNonEmptyIntValues { + assert.NotZero(t, *value, expectedNonEmptyIntFields[field]+" should be a non-zero value") + } + + // Check that the expected time.Duration template values are not zero + for field, value := range expectedNonEmptyTimeValues { + assert.NotZero(t, *value, expectedNonEmptyTimeFields[field]+" should be a non-zero value") + } +} diff --git a/ziti/cmd/ziti/cmd/create_config_test.go b/ziti/cmd/ziti/cmd/create_config_test.go new file mode 100644 index 000000000..0dfd007ed --- /dev/null +++ b/ziti/cmd/ziti/cmd/create_config_test.go @@ -0,0 +1,218 @@ +package cmd + +import ( + "bytes" + "fmt" + "github.com/stretchr/testify/assert" + "io" + "os" + "reflect" + "strings" + "testing" +) + +func getZitiEnvironmentVariables() []string { + return []string{ + "ZITI_HOME", + "ZITI_CONTROLLER_NAME", + "ZITI_CTRL_PORT", + "ZITI_EDGE_ROUTER_RAWNAME", + "ZITI_EDGE_ROUTER_PORT", + "ZITI_EDGE_CTRL_IDENTITY_CERT", + "ZITI_EDGE_CTRL_IDENTITY_SERVER_CERT", + "ZITI_EDGE_CTRL_IDENTITY_KEY", + "ZITI_EDGE_CTRL_IDENTITY_CA", + "ZITI_CTRL_IDENTITY_CERT", + "ZITI_CTRL_IDENTITY_SERVER_CERT", + "ZITI_CTRL_IDENTITY_KEY", + "ZITI_CTRL_IDENTITY_CA", + "ZITI_SIGNING_CERT", + "ZITI_SIGNING_KEY", + "ZITI_ROUTER_IDENTITY_CERT", + "ZITI_ROUTER_IDENTITY_SERVER_CERT", + "ZITI_ROUTER_IDENTITY_KEY", + "ZITI_ROUTER_IDENTITY_CA", + "ZITI_CTRL_LISTENER_ADDRESS", + "ZITI_CTRL_ADVERTISED_ADDRESS", + "ZITI_CTRL_EDGE_LISTENER_HOST_PORT", + "ZITI_EDGE_CTRL_ADVERTISED_HOST_PORT", + } +} + +// Test that all ZITI_* variables are included in the values for output +func TestThatNoNewUnknownTemplateEnvVariablesExist(t *testing.T) { + // Get the list of ZITI_* environment variables + allEnvVars := getZitiEnvironmentVariables() + + // Create a config environment command which will populate the env variable metadata + NewCmdCreateConfigEnvironment() + + values := reflect.ValueOf(environmentOptions.EnvVariableMetaData) + elements := make([]interface{}, values.NumField()) + + for i := 0; i < values.NumField(); i++ { + elements[i] = values.Field(i).Interface() + } + + // Check that every known environment variable is represented in the interface of values + var unknownValues []string + for _, value := range elements { + strValue := fmt.Sprintf("%v", value) + // If the value starts with ZITI_, make sure it's in the list of known environment variables + if strings.HasPrefix(strValue, "ZITI_") { + if !contains(allEnvVars, strValue) { + unknownValues = append(unknownValues, strValue) + } + } + } + + assert.Zero(t, len(unknownValues)) + for _, value := range unknownValues { + fmt.Printf("The variable %s was found in template values but is not a known variable. If necessary, update ZITI env variables list in test with the new variable\n", value) + } +} + +// Test that all known ZITI env variables are found in the output +func TestThatAllKnownEnvVariablesAreAccountedForInTemplate(t *testing.T) { + // Get the list of ZITI_* environment variables + allEnvVars := getZitiEnvironmentVariables() + + // Create a config environment command which will populate the env variable metadata + NewCmdCreateConfigEnvironment() + + values := reflect.ValueOf(environmentOptions.EnvVariableMetaData) + elements := make([]interface{}, values.NumField()) + + for i := 0; i < values.NumField(); i++ { + elements[i] = values.Field(i).Interface() + } + + // Check that the interface of values doesn't have any remaining variables unaccounted for here + var unfoundVariables []string + for _, variable := range allEnvVars { + found := false + for _, value := range elements { + strValue := fmt.Sprintf("%v", value) + if variable == strValue { + found = true + break + } + } + if !found { + unfoundVariables = append(unfoundVariables, variable) + } + } + + assert.Zero(t, len(unfoundVariables)) + for _, value := range unfoundVariables { + fmt.Printf("The variable %s was not found in template values. Update ZITI env variables list in test by removing this variable if necessary\n", value) + } +} + +// Test that all ZITI_* variables are included in the values for output +func TestThatNoNewUnknownOutputEnvVariablesExist(t *testing.T) { + // Get the list of ZITI_* environment variables + allEnvVars := getZitiEnvironmentVariables() + + // Create a config environment command which will populate the env variable metadata + NewCmdCreateConfigEnvironment() + + // Run the environment options command and capture stdout + cmd := NewCmdCreateConfigEnvironment() + cmd.SetArgs([]string{"-o", "stdout"}) + output := captureOutput(func() { + _ = cmd.Execute() + }) + + // Split the output on newlines + lines := strings.Split(output, "\n") + // Check that every known environment variable is represented in the env file output + prefix := "ZITI_" + var unknownValues []string + for _, line := range lines { + // Only look at lines with a ZITI_* env var + if !strings.Contains(line, prefix) { + continue + } + // Strip out the variable name and see if it's a known value + start := strings.Index(line, prefix) + end := strings.Index(line, "=") + if end < 0 { + // If there's no assignment, assume a variable name was referenced in a comment and ignore + continue + } + envVar := strings.TrimSpace(line[start:end]) + if !contains(allEnvVars, envVar) { + unknownValues = append(unknownValues, envVar) + } + } + + assert.Zero(t, len(unknownValues)) + for _, value := range unknownValues { + fmt.Printf("The variable %s was found in env file output but is not a known variable. If necessary, update ZITI env variables list in test with the new variable\n", value) + } +} + +// Test that all known ZITI_* variables are included in the env file +func TestThatAllKnownEnvVariablesAreAccountedForInOutput(t *testing.T) { + // Get the list of ZITI_* environment variables + allEnvVars := getZitiEnvironmentVariables() + + // Create a config environment command which will populate the env variable metadata + NewCmdCreateConfigEnvironment() + + // Run the environment options command and capture stdout + cmd := NewCmdCreateConfigEnvironment() + cmd.SetArgs([]string{"-o", "stdout"}) + output := captureOutput(func() { + _ = cmd.Execute() + }) + + // Split the output on newlines + lines := strings.Split(output, "\n") + // Check that every known environment variable is represented in the env file output + var unfoundVariables []string + for _, variable := range allEnvVars { + found := false + for _, line := range lines { + // Check if the line contains an assignment for this env variable + assignment := variable + "=" + if strings.Contains(line, assignment) { + found = true + break + } + } + if !found { + unfoundVariables = append(unfoundVariables, variable) + } + } + + assert.Zero(t, len(unfoundVariables)) + for _, value := range unfoundVariables { + fmt.Printf("The variable %s was not found in env file output. Update ZITI env variables list in test by removing this variable if necessary\n", value) + } +} + +func contains(s []string, str string) bool { + for _, v := range s { + if v == str { + return true + } + } + + return false +} + +func captureOutput(function func()) string { + var buffer bytes.Buffer + oldStdOut := os.Stdout + r, w, _ := os.Pipe() + os.Stdout = w + + function() + + _ = w.Close() + os.Stdout = oldStdOut + _, _ = io.Copy(&buffer, r) + return buffer.String() +} diff --git a/ziti/cmd/ziti/cmd/helpers/helpers_test.go b/ziti/cmd/ziti/cmd/helpers/helpers_test.go new file mode 100644 index 000000000..02060c2d6 --- /dev/null +++ b/ziti/cmd/ziti/cmd/helpers/helpers_test.go @@ -0,0 +1,277 @@ +package helpers + +import ( + "github.com/stretchr/testify/assert" + "os" + "strings" + "testing" +) + +func TestHomeDirHasNoWindowsSlashes(t *testing.T) { + + value := HomeDir() + + assert.Zero(t, strings.Count(value, "\\")) + +} + +func TestWorkingDirHasNoWindowsSlashes(t *testing.T) { + value, _ := WorkingDir() + + assert.Zero(t, strings.Count(value, "\\")) +} + +func TestGetZitiHomeWhenUnset(t *testing.T) { + // Setup + varName := "ZITI_HOME" + + // Ensure the variable is unset + _ = os.Unsetenv(varName) + wd, _ := os.Getwd() + expectedValue := NormalizePath(wd) + + // Check that the value matches + actualValue, _ := GetZitiHome() + assert.Equal(t, expectedValue, actualValue) + + // The env variable should be populated with the expected value + envValue := os.Getenv(varName) + assert.Equal(t, expectedValue, envValue) +} + +func TestGetZitiHomeWhenSet(t *testing.T) { + // Setup + varName := "ZITI_HOME" + expectedValue := "/path/to/ziti/home" + + // Set the env variable + _ = os.Setenv(varName, expectedValue) + + // Check that the value matches + actualValue, _ := GetZitiHome() + assert.Equal(t, expectedValue, actualValue) +} + +func TestGetZitiHomeWhenSetWithWindowsSlashes(t *testing.T) { + // Setup + varName := "ZITI_HOME" + expectedValue := "/path/to/ziti/home" + + // Set the env variable using windows backslash + _ = os.Setenv(varName, strings.ReplaceAll(expectedValue, "/", "\\")) + + // Check that the value matches + actualValue, _ := GetZitiHome() + assert.Equal(t, expectedValue, actualValue) +} + +func TestGetZitiCtrlAdvertisedAddressWhenUnset(t *testing.T) { + // Setup + varName := "ZITI_CTRL_ADVERTISED_ADDRESS" + + // Ensure the variable is unset + _ = os.Unsetenv(varName) + expectedValue, _ := os.Hostname() + + // Check that the value matches + actualValue, _ := GetZitiCtrlAdvertisedAddress() + assert.Equal(t, expectedValue, actualValue) + + // The env variable should be populated with the expected value + envValue := os.Getenv(varName) + assert.Equal(t, expectedValue, envValue) +} + +func TestGetZitiCtrlAdvertisedAddressWhenSet(t *testing.T) { + // Setup + varName := "ZITI_CTRL_ADVERTISED_ADDRESS" + expectedValue := "localhost" + + // Set the env variable + _ = os.Setenv(varName, expectedValue) + + // Check that the value matches + actualValue, _ := GetZitiCtrlAdvertisedAddress() + assert.Equal(t, expectedValue, actualValue) +} + +func TestGetZitiCtrlPortWhenUnset(t *testing.T) { + // Setup + varName := "ZITI_CTRL_PORT" + + // Ensure the variable is unset + _ = os.Unsetenv(varName) + expectedValue := "6262" + + // Check that the value matches + actualValue, _ := GetZitiCtrlPort() + assert.Equal(t, expectedValue, actualValue) + + // The env variable should be populated with the expected value + envValue := os.Getenv(varName) + assert.Equal(t, expectedValue, envValue) +} + +func TestGetZitiCtrlPortWhenSet(t *testing.T) { + // Setup + varName := "ZITI_CTRL_PORT" + expectedValue := "1234" + + // Set the env variable + _ = os.Setenv(varName, expectedValue) + + // Check that the value matches + actualValue, _ := GetZitiCtrlPort() + assert.Equal(t, expectedValue, actualValue) +} + +func TestGetZitiCtrlListenerAddressWhenUnset(t *testing.T) { + // Setup + varName := "ZITI_CTRL_LISTENER_ADDRESS" + + // Ensure the variable is unset + _ = os.Unsetenv(varName) + expectedValue := "0.0.0.0" + + // Check that the value matches + actualValue, _ := GetZitiCtrlListenerAddress() + assert.Equal(t, expectedValue, actualValue) + + // The env variable should be populated with the expected value + envValue := os.Getenv(varName) + assert.Equal(t, expectedValue, envValue) +} + +func TestGetZitiCtrlListenerAddressWhenSet(t *testing.T) { + // Setup + varName := "ZITI_CTRL_LISTENER_ADDRESS" + expectedValue := "localhost" + + // Set the env variable + _ = os.Setenv(varName, expectedValue) + + // Check that the value matches + actualValue, _ := GetZitiCtrlListenerAddress() + assert.Equal(t, expectedValue, actualValue) +} + +func TestGetZitiCtrlNameWhenUnset(t *testing.T) { + // Setup + varName := "ZITI_CONTROLLER_NAME" + + // Ensure the variable is unset + _ = os.Unsetenv(varName) + expectedValue := "controller" + + // Check that the value matches + actualValue, _ := GetZitiCtrlName() + assert.Equal(t, expectedValue, actualValue) + + // The env variable should be populated with the expected value + envValue := os.Getenv(varName) + assert.Equal(t, expectedValue, envValue) +} + +func TestGetZitiCtrlNameWhenSet(t *testing.T) { + // Setup + varName := "ZITI_CONTROLLER_NAME" + expectedValue := "MyController" + + // Set the env variable + _ = os.Setenv(varName, expectedValue) + + // Check that the value matches + actualValue, _ := GetZitiCtrlName() + assert.Equal(t, expectedValue, actualValue) +} + +func TestGetZitiEdgeRouterPortWhenUnset(t *testing.T) { + // Setup + varName := "ZITI_EDGE_ROUTER_PORT" + + // Ensure the variable is unset + _ = os.Unsetenv(varName) + expectedValue := "3022" + + // Check that the value matches + actualValue, _ := GetZitiEdgeRouterPort() + assert.Equal(t, expectedValue, actualValue) + + // The env variable should be populated with the expected value + envValue := os.Getenv(varName) + assert.Equal(t, expectedValue, envValue) +} + +func TestGetZitiEdgeRouterPortWhenSet(t *testing.T) { + // Setup + varName := "ZITI_EDGE_ROUTER_PORT" + expectedValue := "4321" + + // Set the env variable + _ = os.Setenv(varName, expectedValue) + + // Check that the value matches + actualValue, _ := GetZitiEdgeRouterPort() + assert.Equal(t, expectedValue, actualValue) +} + +func TestGetZitiEdgeCtrlListenerHostPortWhenUnset(t *testing.T) { + // Setup + varName := "ZITI_CTRL_EDGE_LISTENER_HOST_PORT" + + // Ensure the variable is unset + _ = os.Unsetenv(varName) + expectedValue := "0.0.0.0:1280" + + // Check that the value matches + actualValue, _ := GetZitiEdgeCtrlListenerHostPort() + assert.Equal(t, expectedValue, actualValue) + + // The env variable should be populated with the expected value + envValue := os.Getenv(varName) + assert.Equal(t, expectedValue, envValue) +} + +func TestGetZitiEdgeCtrlListenerHostPortWhenSet(t *testing.T) { + // Setup + varName := "ZITI_CTRL_EDGE_LISTENER_HOST_PORT" + expectedValue := "localhost:1234" + + // Set the env variable + _ = os.Setenv(varName, expectedValue) + + // Check that the value matches + actualValue, _ := GetZitiEdgeCtrlListenerHostPort() + assert.Equal(t, expectedValue, actualValue) +} + +func TestGetZitiEdgeCtrlAdvertisedHostPortWhenUnset(t *testing.T) { + // Setup + varName := "ZITI_EDGE_CTRL_ADVERTISED_HOST_PORT" + + // Ensure the variable is unset + _ = os.Unsetenv(varName) + hostname, _ := os.Hostname() + expectedValue := hostname + ":1280" + + // Check that the value matches + actualValue, _ := GetZitiEdgeCtrlAdvertisedHostPort() + assert.Equal(t, expectedValue, actualValue) + + // The env variable should be populated with the expected value + envValue := os.Getenv(varName) + assert.Equal(t, expectedValue, envValue) +} + +func TestGetZitiEdgeCtrlAdvertisedHostPortWhenSet(t *testing.T) { + // Setup + varName := "ZITI_EDGE_CTRL_ADVERTISED_HOST_PORT" + expectedValue := "localhost:4321" + + // Set the env variable + _ = os.Setenv(varName, expectedValue) + + // Check that the value matches + actualValue, _ := GetZitiEdgeCtrlAdvertisedHostPort() + assert.Equal(t, expectedValue, actualValue) +}