From 5f216e2367af6c1e8c203480556de04f8ebb1a3f Mon Sep 17 00:00:00 2001 From: lynnemorrison Date: Wed, 23 Oct 2024 08:56:59 -0400 Subject: [PATCH] code review fixes --- internal/cmd/skupper/common/constants.go | 6 ++ .../connector/nonkube/connector_create.go | 18 ++-- .../nonkube/connector_create_test.go | 40 +++++---- .../nonkube/connector_delete_test.go | 4 +- .../connector/nonkube/connector_status.go | 26 ++---- .../nonkube/connector_status_test.go | 54 +++++++----- .../connector/nonkube/connector_update.go | 19 +++-- .../nonkube/connector_update_test.go | 31 +++++-- .../nonkube/client/fs/connector_handler.go | 83 +++++++++++-------- .../client/fs/custom_resource_handler.go | 40 ++------- internal/nonkube/client/fs/path_provider.go | 24 ++---- internal/nonkube/client/fs/site_handler.go | 15 ++-- pkg/utils/validator/simple_validator.go | 11 +++ 13 files changed, 204 insertions(+), 167 deletions(-) diff --git a/internal/cmd/skupper/common/constants.go b/internal/cmd/skupper/common/constants.go index 4e9e31e795..5e70ff6761 100644 --- a/internal/cmd/skupper/common/constants.go +++ b/internal/cmd/skupper/common/constants.go @@ -7,3 +7,9 @@ var ( ConnectorTypes = []string{"tcp"} WorkloadTypes = []string{"deployment", "service", "daemonset", "statefulset"} ) + +const ( + Connectors string = "connectors" + Listeners string = "listeners" + Sites string = "sites" +) diff --git a/internal/cmd/skupper/connector/nonkube/connector_create.go b/internal/cmd/skupper/connector/nonkube/connector_create.go index 8b8e08f2d9..6e14cdadce 100644 --- a/internal/cmd/skupper/connector/nonkube/connector_create.go +++ b/internal/cmd/skupper/connector/nonkube/connector_create.go @@ -2,12 +2,13 @@ package nonkube import ( "fmt" + "net" "strconv" "github.com/skupperproject/skupper/internal/cmd/skupper/common" "github.com/skupperproject/skupper/internal/cmd/skupper/common/utils" "github.com/skupperproject/skupper/internal/nonkube/client/fs" - "github.com/skupperproject/skupper/pkg/apis/skupper/v1alpha1" + "github.com/skupperproject/skupper/pkg/apis/skupper/v2alpha1" "github.com/skupperproject/skupper/pkg/utils/validator" "github.com/spf13/cobra" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -54,6 +55,7 @@ func (cmd *CmdConnectorCreate) ValidateInput(args []string) []error { numberValidator := validator.NewNumberValidator() connectorTypeValidator := validator.NewOptionValidator(common.ConnectorTypes) outputTypeValidator := validator.NewOptionValidator(common.OutputTypes) + hostStringValidator := validator.NewHostStringValidator() // Validate arguments name and port if len(args) < 2 { @@ -101,7 +103,13 @@ func (cmd *CmdConnectorCreate) ValidateInput(args []string) []error { } } if cmd.Flags.Host != "" { - // TBD what is valid host + ip := net.ParseIP(cmd.Flags.Host) + ok, _ := hostStringValidator.Evaluate(cmd.Flags.Host) + if !ok || ip == nil { + validationErrors = append(validationErrors, fmt.Errorf("host is not valid: a valid IP address or hostname is expected")) + } + } else { + validationErrors = append(validationErrors, fmt.Errorf("host name must be configured: an IP address or hostname is expected")) } if cmd.Flags.TlsSecret != "" { // TBD what is valid TlsSecret @@ -129,16 +137,16 @@ func (cmd *CmdConnectorCreate) InputToOptions() { } func (cmd *CmdConnectorCreate) Run() error { - connectorResource := v1alpha1.Connector{ + connectorResource := v2alpha1.Connector{ TypeMeta: metav1.TypeMeta{ - APIVersion: "skupper.io/v1alpha1", + APIVersion: "skupper.io/v2alpha1", Kind: "Connector", }, ObjectMeta: metav1.ObjectMeta{ Name: cmd.connectorName, Namespace: cmd.namespace, }, - Spec: v1alpha1.ConnectorSpec{ + Spec: v2alpha1.ConnectorSpec{ Host: cmd.host, Port: cmd.port, RoutingKey: cmd.routingKey, diff --git a/internal/cmd/skupper/connector/nonkube/connector_create_test.go b/internal/cmd/skupper/connector/nonkube/connector_create_test.go index 8df6283de8..7b407551d1 100644 --- a/internal/cmd/skupper/connector/nonkube/connector_create_test.go +++ b/internal/cmd/skupper/connector/nonkube/connector_create_test.go @@ -5,7 +5,7 @@ import ( "github.com/skupperproject/skupper/internal/cmd/skupper/common" "github.com/skupperproject/skupper/internal/cmd/skupper/common/utils" - fs2 "github.com/skupperproject/skupper/internal/nonkube/client/fs" + "github.com/skupperproject/skupper/internal/nonkube/client/fs" "github.com/spf13/cobra" "gotest.tools/assert" @@ -27,67 +27,79 @@ func TestNonKubeCmdConnectorCreate_ValidateInput(t *testing.T) { { name: "Connector name and port are not specified", args: []string{}, - flags: &common.CommandConnectorCreateFlags{}, + flags: &common.CommandConnectorCreateFlags{Host: "1.2.3.4"}, expectedErrors: []string{"connector name and port must be configured"}, }, { name: "Connector name is not valid", args: []string{"my new Connector", "8080"}, - flags: &common.CommandConnectorCreateFlags{}, + flags: &common.CommandConnectorCreateFlags{Host: "1.2.3.4"}, expectedErrors: []string{"connector name is not valid: value does not match this regular expression: ^[a-z0-9]([-a-z0-9]*[a-z0-9])*(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])*)*$"}, }, { name: "Connector name is empty", args: []string{"", "1234"}, - flags: &common.CommandConnectorCreateFlags{}, + flags: &common.CommandConnectorCreateFlags{Host: "1.2.3.4"}, expectedErrors: []string{"connector name must not be empty"}, }, { name: "connector port empty", args: []string{"my-name-port-empty", ""}, - flags: &common.CommandConnectorCreateFlags{}, + flags: &common.CommandConnectorCreateFlags{Host: "1.2.3.4"}, expectedErrors: []string{"connector port must not be empty"}, }, { name: "port is not valid", args: []string{"my-connector-port", "abcd"}, - flags: &common.CommandConnectorCreateFlags{}, + flags: &common.CommandConnectorCreateFlags{Host: "1.2.3.4"}, expectedErrors: []string{"connector port is not valid: strconv.Atoi: parsing \"abcd\": invalid syntax"}, }, { name: "port not positive", args: []string{"my-port-positive", "-45"}, - flags: &common.CommandConnectorCreateFlags{}, + flags: &common.CommandConnectorCreateFlags{Host: "1.2.3.4"}, expectedErrors: []string{"connector port is not valid: value is not positive"}, }, { name: "more than two arguments was specified", args: []string{"my", "Connector", "test"}, - flags: &common.CommandConnectorCreateFlags{}, + flags: &common.CommandConnectorCreateFlags{Host: "1.2.3.4"}, expectedErrors: []string{"only two arguments are allowed for this command"}, }, { name: "type is not valid", args: []string{"my-connector", "8080"}, - flags: &common.CommandConnectorCreateFlags{ConnectorType: "not-valid"}, + flags: &common.CommandConnectorCreateFlags{ConnectorType: "not-valid", Host: "1.2.3.4"}, expectedErrors: []string{"connector type is not valid: value not-valid not allowed. It should be one of this options: [tcp]"}, }, { name: "routing key is not valid", args: []string{"my-connector-rk", "8080"}, - flags: &common.CommandConnectorCreateFlags{RoutingKey: "not-valid$"}, + flags: &common.CommandConnectorCreateFlags{RoutingKey: "not-valid$", Host: "1.2.3.4"}, expectedErrors: []string{"routing key is not valid: value does not match this regular expression: ^[a-z0-9]([-a-z0-9]*[a-z0-9])*(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])*)*$"}, }, + { + name: "host is not valid", + args: []string{"my-connector-host", "8080"}, + flags: &common.CommandConnectorCreateFlags{Host: "not-valid$"}, + expectedErrors: []string{"host is not valid: a valid IP address or hostname is expected"}, + }, + { + name: "host is not configued", + args: []string{"my-connector-host", "8080"}, + flags: &common.CommandConnectorCreateFlags{}, + expectedErrors: []string{"host name must be configured: an IP address or hostname is expected"}, + }, { name: "output format is not valid", args: []string{"my-connector", "8080"}, - flags: &common.CommandConnectorCreateFlags{Output: "not-valid"}, + flags: &common.CommandConnectorCreateFlags{Output: "not-valid", Host: "1.2.3.4"}, expectedErrors: []string{"output type is not valid: value not-valid not allowed. It should be one of this options: [json yaml]"}, }, { name: "kubernetes flags are not valid on this platform", args: []string{"my-connector", "8080"}, - flags: &common.CommandConnectorCreateFlags{}, + flags: &common.CommandConnectorCreateFlags{Host: "1.2.3.4"}, expectedErrors: []string{}, cobraGenericFlags: map[string]string{ common.FlagNameContext: "test", @@ -190,7 +202,7 @@ func TestNonKubeCmdConnectorCreate_InputToOptions(t *testing.T) { cmd.Flags = &test.flags cmd.connectorName = "my-Connector" cmd.namespace = test.namespace - cmd.connectorHandler = fs2.NewConnectorHandler(cmd.namespace) + cmd.connectorHandler = fs.NewConnectorHandler(cmd.namespace) cmd.InputToOptions() @@ -266,7 +278,7 @@ func TestNonKubeCmdConnectorCreate_Run(t *testing.T) { command.host = test.host command.connectorType = test.connectorType command.namespace = test.namespace - command.connectorHandler = fs2.NewConnectorHandler(command.namespace) + command.connectorHandler = fs.NewConnectorHandler(command.namespace) defer command.connectorHandler.Delete("test1") t.Run(test.name, func(t *testing.T) { diff --git a/internal/cmd/skupper/connector/nonkube/connector_delete_test.go b/internal/cmd/skupper/connector/nonkube/connector_delete_test.go index d4224cf312..521ef67ee5 100644 --- a/internal/cmd/skupper/connector/nonkube/connector_delete_test.go +++ b/internal/cmd/skupper/connector/nonkube/connector_delete_test.go @@ -5,7 +5,7 @@ import ( "github.com/skupperproject/skupper/internal/cmd/skupper/common" "github.com/skupperproject/skupper/internal/cmd/skupper/common/utils" - fs2 "github.com/skupperproject/skupper/internal/nonkube/client/fs" + "github.com/skupperproject/skupper/internal/nonkube/client/fs" "github.com/spf13/cobra" "gotest.tools/assert" @@ -119,7 +119,7 @@ func TestCmdConnectorDelete_Run(t *testing.T) { cmd.connectorName = test.deleteName cmd.namespace = test.namespace - cmd.connectorHandler = fs2.NewConnectorHandler(cmd.namespace) + cmd.connectorHandler = fs.NewConnectorHandler(cmd.namespace) cmd.InputToOptions() err := cmd.Run() diff --git a/internal/cmd/skupper/connector/nonkube/connector_status.go b/internal/cmd/skupper/connector/nonkube/connector_status.go index 730e66095a..7ea8f4878c 100644 --- a/internal/cmd/skupper/connector/nonkube/connector_status.go +++ b/internal/cmd/skupper/connector/nonkube/connector_status.go @@ -75,22 +75,15 @@ func (cmd *CmdConnectorStatus) ValidateInput(args []string) []error { func (cmd *CmdConnectorStatus) Run() error { if cmd.connectorName == "" { - _, connectors, err := cmd.connectorHandler.GetRuntime("") - if err != nil { - fmt.Println("failed getting directory") + connectors, err := cmd.connectorHandler.List() + if connectors == nil || err != nil { + fmt.Println("No connectors found:") return err } if cmd.output != "" { - for _, file := range connectors { - fmt.Println("file name:", file.Name()) - connector, _, err := cmd.connectorHandler.GetRuntime(file.Name()) - if err != nil { - fmt.Println("failed getting connector") - return err - } + for _, connector := range connectors { encodedOutput, err := utils.Encode(cmd.output, connector) if err != nil { - fmt.Println("failed encoding connector output") return err } fmt.Println(encodedOutput) @@ -99,12 +92,7 @@ func (cmd *CmdConnectorStatus) Run() error { tw := tabwriter.NewWriter(os.Stdout, 8, 8, 1, '\t', tabwriter.TabIndent) _, _ = fmt.Fprintln(tw, fmt.Sprintf("%s\t%s\t%s\t%s\t%s", "NAME", "STATUS", "ROUTING-KEY", "HOST", "PORT")) - for _, file := range connectors { - connector, _, err := cmd.connectorHandler.GetRuntime(file.Name()) - if err != nil { - fmt.Println("failed getting connector") - return err - } + for _, connector := range connectors { status := "Not Ready" if connector.IsConfigured() { status = "Ok" @@ -115,9 +103,9 @@ func (cmd *CmdConnectorStatus) Run() error { _ = tw.Flush() } } else { - connector, _, err := cmd.connectorHandler.GetRuntime(cmd.connectorName + ".yaml") + connector, err := cmd.connectorHandler.Get(cmd.connectorName) if connector == nil || err != nil { - fmt.Println("No connectors found:", err) + fmt.Println("No connectors found:") return err } if cmd.output != "" { diff --git a/internal/cmd/skupper/connector/nonkube/connector_status_test.go b/internal/cmd/skupper/connector/nonkube/connector_status_test.go index 2e36daeb1f..6ba60ac28b 100644 --- a/internal/cmd/skupper/connector/nonkube/connector_status_test.go +++ b/internal/cmd/skupper/connector/nonkube/connector_status_test.go @@ -2,12 +2,13 @@ package nonkube import ( "os" + "path/filepath" "testing" "github.com/skupperproject/skupper/internal/cmd/skupper/common" "github.com/skupperproject/skupper/internal/cmd/skupper/common/utils" - fs2 "github.com/skupperproject/skupper/internal/nonkube/client/fs" - "github.com/skupperproject/skupper/pkg/apis/skupper/v1alpha1" + "github.com/skupperproject/skupper/internal/nonkube/client/fs" + "github.com/skupperproject/skupper/pkg/apis/skupper/v2alpha1" "gotest.tools/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -24,6 +25,10 @@ func TestCmdConnectorStatus_ValidateInput(t *testing.T) { expectedErrors []string } + homeDir, err := os.UserHomeDir() + assert.Check(t, err == nil) + path := filepath.Join(homeDir, "/.local/share/skupper/namespaces/test/runtime/state") + testTable := []test{ { name: "connector is not shown because connector does not exist in the namespace", @@ -69,9 +74,9 @@ func TestCmdConnectorStatus_ValidateInput(t *testing.T) { } //Add a temp file so connector exists for status tests - connectorResource := v1alpha1.Connector{ + connectorResource := v2alpha1.Connector{ TypeMeta: metav1.TypeMeta{ - APIVersion: "skupper.io/v1alpha1", + APIVersion: "skupper.io/v2alpha1", Kind: "Connector", }, ObjectMeta: metav1.ObjectMeta{ @@ -82,10 +87,12 @@ func TestCmdConnectorStatus_ValidateInput(t *testing.T) { command := &CmdConnectorStatus{} command.namespace = "test" - command.connectorHandler = fs2.NewConnectorHandler(command.namespace) + command.connectorHandler = fs.NewConnectorHandler(command.namespace) defer command.connectorHandler.Delete("my-connector") - err := command.connectorHandler.Add(connectorResource) + content, err := command.connectorHandler.EncodeToYaml(connectorResource) + assert.Check(t, err == nil) + err = command.connectorHandler.WriteFile(path, "my-connector.yaml", content, common.Connectors) assert.Check(t, err == nil) for _, test := range testTable { @@ -125,13 +132,13 @@ func TestCmdConnectorStatus_Run(t *testing.T) { homeDir, err := os.UserHomeDir() assert.Check(t, err == nil) + path := filepath.Join(homeDir, "/.local/share/skupper/namespaces/test/") testTable := []test{ { name: "run fails connector doesn't exist", connectorName: "no-connector", - errorMessage: "failed to read file: open " + homeDir + "/.local/share/skupper/namespaces/test/runtime/state/connectors/no-connector.yaml: no such file or directory", - }, + errorMessage: "failed to read file: open " + path + "/input/sources/connectors/no-connector.yaml: no such file or directory"}, { name: "runs ok, returns 1 connectors", connectorName: "my-connector", @@ -162,22 +169,22 @@ func TestCmdConnectorStatus_Run(t *testing.T) { } //Add a temp file so connector exists for status tests - connectorResource1 := v1alpha1.Connector{ + connectorResource1 := v2alpha1.Connector{ TypeMeta: metav1.TypeMeta{ - APIVersion: "skupper.io/v1alpha1", + APIVersion: "skupper.io/v2alpha1", Kind: "Connector", }, ObjectMeta: metav1.ObjectMeta{ Name: "my-connector", Namespace: "test", }, - Spec: v1alpha1.ConnectorSpec{ + Spec: v2alpha1.ConnectorSpec{ Host: "1.2.3.4", Port: 8080, RoutingKey: "backend-8080", }, - Status: v1alpha1.ConnectorStatus{ - Status: v1alpha1.Status{ + Status: v2alpha1.ConnectorStatus{ + Status: v2alpha1.Status{ Conditions: []metav1.Condition{ { Type: "Configured", @@ -187,22 +194,22 @@ func TestCmdConnectorStatus_Run(t *testing.T) { }, }, } - connectorResource2 := v1alpha1.Connector{ + connectorResource2 := v2alpha1.Connector{ TypeMeta: metav1.TypeMeta{ - APIVersion: "skupper.io/v1alpha1", + APIVersion: "skupper.io/v2alpha1", Kind: "Connector", }, ObjectMeta: metav1.ObjectMeta{ Name: "my-connector2", Namespace: "test", }, - Spec: v1alpha1.ConnectorSpec{ + Spec: v2alpha1.ConnectorSpec{ Host: "1.1.1.1", Port: 9999, RoutingKey: "test-9999", }, - Status: v1alpha1.ConnectorStatus{ - Status: v1alpha1.Status{ + Status: v2alpha1.ConnectorStatus{ + Status: v2alpha1.Status{ Conditions: []metav1.Condition{ { Type: "Configured", @@ -216,19 +223,19 @@ func TestCmdConnectorStatus_Run(t *testing.T) { // add two connectors in runtime directory command := &CmdConnectorStatus{} command.namespace = "test" - command.connectorHandler = fs2.NewConnectorHandler(command.namespace) + command.connectorHandler = fs.NewConnectorHandler(command.namespace) defer command.connectorHandler.Delete("my-connector") defer command.connectorHandler.Delete("my-connector2") content, err := command.connectorHandler.EncodeToYaml(connectorResource1) assert.Check(t, err == nil) - err = command.connectorHandler.WriteFile(".local/share/skupper/namespaces/test/runtime/state", "my-connector.yaml", content, "connectors") + err = command.connectorHandler.WriteFile(path+"/runtime/state", "my-connector.yaml", content, common.Connectors) assert.Check(t, err == nil) content, err = command.connectorHandler.EncodeToYaml(connectorResource2) assert.Check(t, err == nil) - err = command.connectorHandler.WriteFile(".local/share/skupper/namespaces/test/runtime/state", "my-connector2.yaml", content, "connectors") + err = command.connectorHandler.WriteFile(path+"/runtime/state", "my-connector2.yaml", content, common.Connectors) assert.Check(t, err == nil) for _, test := range testTable { @@ -260,18 +267,19 @@ func TestCmdConnectorStatus_RunNoDirectory(t *testing.T) { homeDir, err := os.UserHomeDir() assert.Check(t, err == nil) + path := filepath.Join(homeDir, "/.local/share/skupper/namespaces/default/input/sources/connectors") testTable := []test{ { name: "runs fails no directory", - errorMessage: "failed to read directory: open " + homeDir + "/.local/share/skupper/namespaces/default/runtime/state/connectors: no such file or directory", + errorMessage: "failed to read directory: open " + path + ": no such file or directory", }, } for _, test := range testTable { command := &CmdConnectorStatus{} command.namespace = "default" - command.connectorHandler = fs2.NewConnectorHandler(command.namespace) + command.connectorHandler = fs.NewConnectorHandler(command.namespace) command.connectorName = test.connectorName command.Flags = &test.flags command.output = command.Flags.Output diff --git a/internal/cmd/skupper/connector/nonkube/connector_update.go b/internal/cmd/skupper/connector/nonkube/connector_update.go index 0d64eedf80..328ddc8850 100644 --- a/internal/cmd/skupper/connector/nonkube/connector_update.go +++ b/internal/cmd/skupper/connector/nonkube/connector_update.go @@ -2,11 +2,12 @@ package nonkube import ( "fmt" + "net" "github.com/skupperproject/skupper/internal/cmd/skupper/common" "github.com/skupperproject/skupper/internal/cmd/skupper/common/utils" "github.com/skupperproject/skupper/internal/nonkube/client/fs" - "github.com/skupperproject/skupper/pkg/apis/skupper/v1alpha1" + "github.com/skupperproject/skupper/pkg/apis/skupper/v2alpha1" "github.com/skupperproject/skupper/pkg/utils/validator" "github.com/spf13/cobra" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -47,6 +48,7 @@ func (cmd *CmdConnectorUpdate) ValidateInput(args []string) []error { numberValidator := validator.NewNumberValidator() connectorTypeValidator := validator.NewOptionValidator(common.ConnectorTypes) outputTypeValidator := validator.NewOptionValidator(common.OutputTypes) + hostStringValidator := validator.NewHostStringValidator() if cmd.CobraCmd != nil && cmd.CobraCmd.Flag(common.FlagNameContext) != nil && cmd.CobraCmd.Flag(common.FlagNameContext).Value.String() != "" { fmt.Println("Warning: --context flag is not supported on this platform") @@ -113,8 +115,13 @@ func (cmd *CmdConnectorUpdate) ValidateInput(args []string) []error { } } if cmd.Flags.Host != "" { - //TBD what characters are not allowed for host flag - cmd.newSettings.host = cmd.Flags.Host + ip := net.ParseIP(cmd.Flags.Host) + ok, _ := hostStringValidator.Evaluate(cmd.Flags.Host) + if !ok || ip == nil { + validationErrors = append(validationErrors, fmt.Errorf("host is not valid: a valid IP address or hostname is expected")) + } else { + cmd.newSettings.host = cmd.Flags.Host + } } if cmd.Flags.Output != "" { ok, err := outputTypeValidator.Evaluate(cmd.Flags.Output) @@ -135,16 +142,16 @@ func (cmd *CmdConnectorUpdate) InputToOptions() { } func (cmd *CmdConnectorUpdate) Run() error { - connectorResource := v1alpha1.Connector{ + connectorResource := v2alpha1.Connector{ TypeMeta: metav1.TypeMeta{ - APIVersion: "skupper.io/v1alpha1", + APIVersion: "skupper.io/v2alpha1", Kind: "Connector", }, ObjectMeta: metav1.ObjectMeta{ Name: cmd.connectorName, Namespace: cmd.namespace, }, - Spec: v1alpha1.ConnectorSpec{ + Spec: v2alpha1.ConnectorSpec{ Host: cmd.newSettings.host, Port: cmd.newSettings.port, RoutingKey: cmd.newSettings.routingKey, diff --git a/internal/cmd/skupper/connector/nonkube/connector_update_test.go b/internal/cmd/skupper/connector/nonkube/connector_update_test.go index 8e4f3412e8..d5e456eb28 100644 --- a/internal/cmd/skupper/connector/nonkube/connector_update_test.go +++ b/internal/cmd/skupper/connector/nonkube/connector_update_test.go @@ -1,12 +1,14 @@ package nonkube import ( + "os" + "path/filepath" "testing" "github.com/skupperproject/skupper/internal/cmd/skupper/common" "github.com/skupperproject/skupper/internal/cmd/skupper/common/utils" - fs2 "github.com/skupperproject/skupper/internal/nonkube/client/fs" - "github.com/skupperproject/skupper/pkg/apis/skupper/v1alpha1" + "github.com/skupperproject/skupper/internal/nonkube/client/fs" + "github.com/skupperproject/skupper/pkg/apis/skupper/v2alpha1" "github.com/spf13/cobra" "gotest.tools/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -24,6 +26,10 @@ func TestCmdConnectorUpdate_ValidateInput(t *testing.T) { expectedErrors []string } + homeDir, err := os.UserHomeDir() + assert.Check(t, err == nil) + path := filepath.Join(homeDir, "/.local/share/skupper/namespaces/test/runtime/state") + testTable := []test{ { name: "connector is not updated because get connector returned error", @@ -67,6 +73,12 @@ func TestCmdConnectorUpdate_ValidateInput(t *testing.T) { flags: &common.CommandConnectorUpdateFlags{RoutingKey: "not-valid$"}, expectedErrors: []string{"routing key is not valid: value does not match this regular expression: ^[a-z0-9]([-a-z0-9]*[a-z0-9])*(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])*)*$"}, }, + { + name: "host is not valid", + args: []string{"my-connector"}, + flags: &common.CommandConnectorUpdateFlags{Host: "not-valid$"}, + expectedErrors: []string{"host is not valid: a valid IP address or hostname is expected"}, + }, { name: "port is not valid", args: []string{"my-connector"}, @@ -98,15 +110,16 @@ func TestCmdConnectorUpdate_ValidateInput(t *testing.T) { Port: 1234, ConnectorType: "tcp", Output: "json", + Host: "1.2.3.4", }, expectedErrors: []string{}, }, } - //TBD add a temp file so connector exists for update tests will pass - connectorResource := v1alpha1.Connector{ + //Add a temp file so connector exists for update tests will pass + connectorResource := v2alpha1.Connector{ TypeMeta: metav1.TypeMeta{ - APIVersion: "skupper.io/v1alpha1", + APIVersion: "skupper.io/v2alpha1", Kind: "Connector", }, ObjectMeta: metav1.ObjectMeta{ @@ -118,10 +131,12 @@ func TestCmdConnectorUpdate_ValidateInput(t *testing.T) { command := &CmdConnectorUpdate{Flags: &common.CommandConnectorUpdateFlags{}} command.CobraCmd = &cobra.Command{Use: "test"} command.namespace = "test" - command.connectorHandler = fs2.NewConnectorHandler(command.namespace) + command.connectorHandler = fs.NewConnectorHandler(command.namespace) defer command.connectorHandler.Delete("my-connector") - err := command.connectorHandler.Add(connectorResource) + content, err := command.connectorHandler.EncodeToYaml(connectorResource) + assert.Check(t, err == nil) + err = command.connectorHandler.WriteFile(path, "my-connector.yaml", content, common.Connectors) assert.Check(t, err == nil) for _, test := range testTable { @@ -197,7 +212,7 @@ func TestCmdConnectorUpdate_Run(t *testing.T) { command.newSettings.routingKey = test.routingKey command.newSettings.tlsSecret = test.tlsSecret command.namespace = test.namespace - command.connectorHandler = fs2.NewConnectorHandler(command.namespace) + command.connectorHandler = fs.NewConnectorHandler(command.namespace) defer command.connectorHandler.Delete("my-connector") t.Run(test.name, func(t *testing.T) { command.InputToOptions() diff --git a/internal/nonkube/client/fs/connector_handler.go b/internal/nonkube/client/fs/connector_handler.go index 6e8a55fe1d..fb22ef3b41 100644 --- a/internal/nonkube/client/fs/connector_handler.go +++ b/internal/nonkube/client/fs/connector_handler.go @@ -2,9 +2,11 @@ package fs import ( "errors" + "fmt" "io/fs" - "github.com/skupperproject/skupper/pkg/apis/skupper/v1alpha1" + "github.com/skupperproject/skupper/internal/cmd/skupper/common" + "github.com/skupperproject/skupper/pkg/apis/skupper/v2alpha1" ) type ConnectorHandler struct { @@ -20,7 +22,7 @@ func NewConnectorHandler(namespace string) *ConnectorHandler { } } -func (s *ConnectorHandler) Add(resource v1alpha1.Connector) error { +func (s *ConnectorHandler) Add(resource v2alpha1.Connector) error { fileName := resource.Name + ".yaml" content, err := s.EncodeToYaml(resource) @@ -28,7 +30,7 @@ func (s *ConnectorHandler) Add(resource v1alpha1.Connector) error { return err } - err = s.WriteFile(s.pathProvider.GetNamespace(), fileName, content, "connectors") + err = s.WriteFile(s.pathProvider.GetNamespace(), fileName, content, common.Connectors) if err != nil { return err } @@ -36,55 +38,38 @@ func (s *ConnectorHandler) Add(resource v1alpha1.Connector) error { return nil } -func (s *ConnectorHandler) Get(name string) (*v1alpha1.Connector, error) { - var context v1alpha1.Connector +func (s *ConnectorHandler) Get(name string) (*v2alpha1.Connector, error) { + var context v2alpha1.Connector fileName := name + ".yaml" - err, file := s.ReadFile(s.pathProvider.GetNamespace(), fileName, "connectors") + // First read from runtime directory, where output is found after bootstrap + // has run. If no runtime connectors try and display configured connectors + err, file := s.ReadFile(s.pathProvider.GetRuntimeNamespace(), fileName, common.Connectors) if err != nil { - return nil, err + fmt.Println("Site not initialized yet") + err, file = s.ReadFile(s.pathProvider.GetNamespace(), fileName, common.Connectors) + if err != nil { + return nil, err + } } - if err = s.EncodeYaml(file, &context); err != nil { + if err = s.DecodeYaml(file, &context); err != nil { return nil, err } return &context, nil } -func (s *ConnectorHandler) GetRuntime(fileName string) (*v1alpha1.Connector, []fs.DirEntry, error) { - var context v1alpha1.Connector - - if fileName == "" { - err, files := s.ReadDir(s.pathProvider.GetRuntimeNamespace(), "connectors") - if err != nil { - return nil, nil, err - } - return nil, files, nil - } else { - err, file := s.ReadFile(s.pathProvider.GetRuntimeNamespace(), fileName, "connectors") - if err != nil { - return nil, nil, err - } - - if err = s.EncodeYaml(file, &context); err != nil { - return nil, nil, err - } - - return &context, nil, nil - } -} - func (s *ConnectorHandler) Delete(name string) error { fileName := name + ".yaml" - if err := s.DeleteFile(s.pathProvider.GetNamespace(), fileName, "connectors"); err != nil { + if err := s.DeleteFile(s.pathProvider.GetNamespace(), fileName, common.Connectors); err != nil { if !errors.Is(err, fs.ErrNotExist) { return err } } - if err := s.DeleteFile(s.pathProvider.GetRuntimeNamespace(), fileName, "connectors"); err != nil { + if err := s.DeleteFile(s.pathProvider.GetRuntimeNamespace(), fileName, common.Connectors); err != nil { if !errors.Is(err, fs.ErrNotExist) { return err } @@ -92,4 +77,34 @@ func (s *ConnectorHandler) Delete(name string) error { return nil } -func (s *ConnectorHandler) Update(resource v1alpha1.Site) error { return nil } +func (s *ConnectorHandler) List() ([]*v2alpha1.Connector, error) { + var connectors []*v2alpha1.Connector + + // First read from runtime directory, where output is found after bootstrap + // has run. If no runtime connectors try and display configured connectors + path := s.pathProvider.GetRuntimeNamespace() + err, files := s.ReadDir(path, common.Connectors) + if err != nil { + fmt.Println("Site not initialized yet") + path = s.pathProvider.GetNamespace() + err, files = s.ReadDir(path, common.Connectors) + if err != nil { + return nil, err + } + } + + for _, file := range files { + err, connector := s.ReadFile(path, file.Name(), common.Connectors) + if err != nil { + return nil, err + } + var context v2alpha1.Connector + if err = s.DecodeYaml(connector, &context); err != nil { + return nil, err + } + connectors = append(connectors, &context) + } + return connectors, nil +} + +func (s *ConnectorHandler) Update(resource v2alpha1.Site) error { return nil } diff --git a/internal/nonkube/client/fs/custom_resource_handler.go b/internal/nonkube/client/fs/custom_resource_handler.go index 2aa4749dd7..22d76d1b20 100644 --- a/internal/nonkube/client/fs/custom_resource_handler.go +++ b/internal/nonkube/client/fs/custom_resource_handler.go @@ -15,13 +15,13 @@ type CustomResourceHandler[T any] interface { Add(T) error Update(T) error Get(name string) (T, error) - GetRuntime(name string) (T, []fs.DirEntry, error) + List() ([]T, error) Delete(name string) error //Common methods EncodeToYaml(resource interface{}) (string, error) WriteFile(path string, name string, content string, kind string) error - EncodeYaml(content []byte, resource interface{}) (interface{}, error) + DecodeYaml(content []byte, resource interface{}) (interface{}, error) ReadFile(path string, name string, kind string) (error, []byte) DeleteFile(path string, name string, kind string) error ReadDir(path string, kind string) (error, []fs.DirEntry) @@ -34,8 +34,7 @@ func (b *BaseCustomResourceHandler) EncodeToYaml(resource interface{}) (string, return utils.Encode("yaml", resource) } -// TBD better name -func (b *BaseCustomResourceHandler) EncodeYaml(content []byte, resource interface{}) error { +func (b *BaseCustomResourceHandler) DecodeYaml(content []byte, resource interface{}) error { if err := yaml.Unmarshal(content, &resource); err != nil { return err @@ -45,17 +44,11 @@ func (b *BaseCustomResourceHandler) EncodeYaml(content []byte, resource interfac func (b *BaseCustomResourceHandler) WriteFile(path string, name string, content string, kind string) error { - // Resolve the home directory - homeDir, err := os.UserHomeDir() - if err != nil { - return err - } - - fullPath := filepath.Join(homeDir, path, kind) + fullPath := filepath.Join(path, kind) completeFilePath := filepath.Join(fullPath, name) // Create the directories recursively - err = os.MkdirAll(fullPath, 0775) + err := os.MkdirAll(fullPath, 0775) if err != nil { return fmt.Errorf("failed to create directories: %s", err) } @@ -80,13 +73,7 @@ func (b *BaseCustomResourceHandler) WriteFile(path string, name string, content func (b *BaseCustomResourceHandler) ReadFile(path string, name string, kind string) (error, []byte) { - // Resolve the home directory - homeDir, err := os.UserHomeDir() - if err != nil { - return err, nil - } - - fullPath := filepath.Join(homeDir, path, kind) + fullPath := filepath.Join(path, kind) completeFilePath := filepath.Join(fullPath, name) file, err := os.ReadFile(completeFilePath) @@ -100,13 +87,7 @@ func (b *BaseCustomResourceHandler) ReadFile(path string, name string, kind stri func (b *BaseCustomResourceHandler) DeleteFile(path string, name string, kind string) error { var completeFilePath string - // Resolve the home directory - homeDir, err := os.UserHomeDir() - if err != nil { - return err - } - - fullPath := filepath.Join(homeDir, path, kind) + fullPath := filepath.Join(path, kind) completeFilePath = filepath.Join(fullPath, name) if err := os.RemoveAll(completeFilePath); err != nil { @@ -117,13 +98,8 @@ func (b *BaseCustomResourceHandler) DeleteFile(path string, name string, kind st } func (b *BaseCustomResourceHandler) ReadDir(path string, kind string) (error, []fs.DirEntry) { - // Resolve the home directory - homeDir, err := os.UserHomeDir() - if err != nil { - return err, nil - } - fullPath := filepath.Join(homeDir, path, kind) + fullPath := filepath.Join(path, kind) files, err := os.ReadDir(fullPath) if err != nil { diff --git a/internal/nonkube/client/fs/path_provider.go b/internal/nonkube/client/fs/path_provider.go index dcda1c9ec5..844b07c56a 100644 --- a/internal/nonkube/client/fs/path_provider.go +++ b/internal/nonkube/client/fs/path_provider.go @@ -1,31 +1,17 @@ package fs -import "fmt" +import ( + "github.com/skupperproject/skupper/pkg/nonkube/api" +) type PathProvider struct { Namespace string } -func (p *PathProvider) getDefaultNamespace() string { - return ".local/share/skupper/namespaces/default/input/sources" -} - func (p *PathProvider) GetNamespace() string { - - if p.Namespace == "" { - return p.getDefaultNamespace() - } - return fmt.Sprintf(".local/share/skupper/namespaces/%s/input/sources", p.Namespace) -} - -func (p *PathProvider) getRuntimeDefaultNamespace() string { - return ".local/share/skupper/namespaces/default/runtime/state" + return api.GetHostNamespaceHome(p.Namespace) + "/input/sources" } func (p *PathProvider) GetRuntimeNamespace() string { - - if p.Namespace == "" { - return p.getRuntimeDefaultNamespace() - } - return fmt.Sprintf(".local/share/skupper/namespaces/%s/runtime/state", p.Namespace) + return api.GetHostNamespaceHome(p.Namespace) + "/runtime/state" } diff --git a/internal/nonkube/client/fs/site_handler.go b/internal/nonkube/client/fs/site_handler.go index b396fc7a43..e0e7691ca4 100644 --- a/internal/nonkube/client/fs/site_handler.go +++ b/internal/nonkube/client/fs/site_handler.go @@ -1,6 +1,9 @@ package fs -import "github.com/skupperproject/skupper/pkg/apis/skupper/v2alpha1" +import ( + "github.com/skupperproject/skupper/internal/cmd/skupper/common" + "github.com/skupperproject/skupper/pkg/apis/skupper/v2alpha1" +) type SiteHandler struct { BaseCustomResourceHandler @@ -23,13 +26,15 @@ func (s *SiteHandler) Add(resource v2alpha1.Site) error { return err } - err = s.WriteFile(s.pathProvider.GetNamespace(), fileName, content, "sites") + err = s.WriteFile(s.pathProvider.GetNamespace(), fileName, content, common.Sites) if err != nil { return err } return nil } -func (s *SiteHandler) Update(resource v2alpha1.Site) error { return nil } -func (s *SiteHandler) Get(name string) *v2alpha1.Site { return nil } -func (s *SiteHandler) Delete(name string) error { return nil } + +func (s *SiteHandler) Update(resource v2alpha1.Site) error { return nil } +func (s *SiteHandler) Get(name string) *v2alpha1.Site { return nil } +func (s *SiteHandler) Delete(name string) error { return nil } +func (s *SiteHandler) List([]*v2alpha1.Site, error) []*v2alpha1.Site { return nil } diff --git a/pkg/utils/validator/simple_validator.go b/pkg/utils/validator/simple_validator.go index 427eadddd4..1230e57ff2 100644 --- a/pkg/utils/validator/simple_validator.go +++ b/pkg/utils/validator/simple_validator.go @@ -28,6 +28,17 @@ func NewStringValidator() *StringValidator { } } +func NewHostStringValidator() *StringValidator { + re, err := regexp.Compile(`^[a-z0-9]+([-.]{1}[a-z0-9]+)*$`) + if err != nil { + fmt.Printf("Error compiling regex: %v", err) + return nil + } + return &StringValidator{ + Expression: re, + } +} + func NewResourceStringValidator() *StringValidator { re, err := regexp.Compile("^[a-z0-9]([-a-z0-9]*[a-z0-9])*(\\.[a-z0-9]([-a-z0-9]*[a-z0-9])*)*$") if err != nil {