Skip to content

Commit

Permalink
Add nonkube connector status CLI command and unit test
Browse files Browse the repository at this point in the history
  • Loading branch information
lynnemorrison committed Oct 18, 2024
1 parent d39bbc3 commit 261a052
Show file tree
Hide file tree
Showing 5 changed files with 437 additions and 14 deletions.
125 changes: 117 additions & 8 deletions internal/cmd/skupper/connector/nonkube/connector_status.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,137 @@ package nonkube

import (
"fmt"
"os"
"text/tabwriter"

"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/utils/validator"
"github.com/spf13/cobra"
)

type CmdConnectorStatus struct {
CobraCmd *cobra.Command
Flags *common.CommandConnectorStatusFlags
Namespace string
siteName string
connectorHandler *fs.ConnectorHandler
CobraCmd *cobra.Command
Flags *common.CommandConnectorStatusFlags
namespace string
connectorName string
output string
}

func NewCmdConnectorStatus() *CmdConnectorStatus {
return &CmdConnectorStatus{}
}

func (cmd *CmdConnectorStatus) NewClient(cobraCommand *cobra.Command, args []string) {
//TODO
if cmd.CobraCmd != nil && cmd.CobraCmd.Flag(common.FlagNameNamespace) != nil && cmd.CobraCmd.Flag(common.FlagNameNamespace).Value.String() != "" {
cmd.namespace = cmd.CobraCmd.Flag(common.FlagNameNamespace).Value.String()
}

cmd.connectorHandler = fs.NewConnectorHandler(cmd.namespace)
}

func (cmd *CmdConnectorStatus) ValidateInput(args []string) []error {
var validationErrors []error
resourceStringValidator := validator.NewResourceStringValidator()
outputTypeValidator := validator.NewOptionValidator(common.OutputTypes)

// Validate arguments name if specified
if len(args) > 1 {
validationErrors = append(validationErrors, fmt.Errorf("only one argument is allowed for this command"))
} else if len(args) == 1 {
if args[0] == "" {
validationErrors = append(validationErrors, fmt.Errorf("connector name must not be empty"))
} else {
ok, err := resourceStringValidator.Evaluate(args[0])
if !ok {
validationErrors = append(validationErrors, fmt.Errorf("connector name is not valid: %s", err))
} else {
cmd.connectorName = args[0]
}
}
}
// Validate that there is a connector with this name in the namespace
if cmd.connectorName != "" {
connector, err := cmd.connectorHandler.Get(cmd.connectorName)
if connector == nil || err != nil {
validationErrors = append(validationErrors, fmt.Errorf("connector %s does not exist in namespace %s", cmd.connectorName, cmd.namespace))
}
}

if cmd.Flags.Output != "" {
ok, err := outputTypeValidator.Evaluate(cmd.Flags.Output)
if !ok {
validationErrors = append(validationErrors, fmt.Errorf("output type is not valid: %s", err))
} else {
cmd.output = cmd.Flags.Output
}
}

return validationErrors
}

func (cmd *CmdConnectorStatus) ValidateInput(args []string) []error { return nil }
func (cmd *CmdConnectorStatus) InputToOptions() {}
func (cmd *CmdConnectorStatus) Run() error {
return fmt.Errorf("command not supported by the selected platform")
if cmd.connectorName == "" {
_, connectors, err := cmd.connectorHandler.GetRuntime("")
if err != nil {
fmt.Println("failed getting directory")
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
}
encodedOutput, err := utils.Encode(cmd.output, connector)
if err != nil {
fmt.Println("failed encoding connector output")
return err
}
fmt.Println(encodedOutput)
}
} else {
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
}
fmt.Fprintln(tw, fmt.Sprintf("%s\t%s\t%s\t%s\t%d",
connector.Name, connector.Status.Conditions[0].Type, connector.Spec.RoutingKey,
connector.Spec.Host, connector.Spec.Port))
}
_ = tw.Flush()
}
} else {
connector, _, err := cmd.connectorHandler.GetRuntime(cmd.connectorName + ".yaml")
if connector == nil || err != nil {
fmt.Println("No connectors found:", err)
return err
}
if cmd.output != "" {
encodedOutput, err := utils.Encode(cmd.output, connector)
if err != nil {
return err
}
fmt.Println(encodedOutput)
} else {
tw := tabwriter.NewWriter(os.Stdout, 8, 8, 1, '\t', tabwriter.TabIndent)
fmt.Fprintln(tw, fmt.Sprintf("Name:\t%s\nStatus:\t%s\nRouting key:\t%s\nHost:\t%s\nPort:\t%d\n",
connector.Name, connector.Status.Conditions[0].Type, connector.Spec.RoutingKey,
connector.Spec.Host, connector.Spec.Port))
_ = tw.Flush()
}
}
return nil
}

func (cmd *CmdConnectorStatus) InputToOptions() {}
func (cmd *CmdConnectorStatus) WaitUntil() error { return nil }
249 changes: 249 additions & 0 deletions internal/cmd/skupper/connector/nonkube/connector_status_test.go
Original file line number Diff line number Diff line change
@@ -1 +1,250 @@
package nonkube

import (
"os"
"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"
"gotest.tools/assert"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
)

func TestCmdConnectorStatus_ValidateInput(t *testing.T) {
type test struct {
name string
args []string
flags *common.CommandConnectorStatusFlags
cobraGenericFlags map[string]string
k8sObjects []runtime.Object
skupperObjects []runtime.Object
expectedErrors []string
}

testTable := []test{
{
name: "connector is not shown because connector does not exist in the namespace",
args: []string{"no-connector"},
flags: &common.CommandConnectorStatusFlags{},
expectedErrors: []string{"connector no-connector does not exist in namespace test"},
},
{
name: "connector name is nil",
args: []string{""},
flags: &common.CommandConnectorStatusFlags{},
expectedErrors: []string{"connector name must not be empty"},
},
{
name: "more than one argument is specified",
args: []string{"my", "connector"},
flags: &common.CommandConnectorStatusFlags{},
expectedErrors: []string{"only one argument is allowed for this command"},
},
{
name: "connector name is not valid.",
args: []string{"my new connector"},
flags: &common.CommandConnectorStatusFlags{},
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: "no args",
flags: &common.CommandConnectorStatusFlags{},
expectedErrors: []string{},
},
{
name: "bad output status",
args: []string{"my-connector"},
flags: &common.CommandConnectorStatusFlags{Output: "not-supported"},
expectedErrors: []string{"output type is not valid: value not-supported not allowed. It should be one of this options: [json yaml]"},
},
{
name: "good output status",
args: []string{"my-connector"},
flags: &common.CommandConnectorStatusFlags{Output: "json"},
expectedErrors: []string{},
},
}

//Add a temp file so connector exists for status tests
connectorResource := v1alpha1.Connector{
TypeMeta: metav1.TypeMeta{
APIVersion: "skupper.io/v1alpha1",
Kind: "Connector",
},
ObjectMeta: metav1.ObjectMeta{
Name: "my-connector",
Namespace: "test",
},
}

command := &CmdConnectorStatus{}
command.namespace = "test"
command.connectorHandler = fs2.NewConnectorHandler(command.namespace)

defer command.connectorHandler.Delete("my-connector")
err := command.connectorHandler.Add(connectorResource)
assert.Check(t, err == nil)

for _, test := range testTable {
t.Run(test.name, func(t *testing.T) {
command.connectorName = ""

if test.flags != nil {
command.Flags = test.flags
}

if test.cobraGenericFlags != nil && len(test.cobraGenericFlags) > 0 {
for name, value := range test.cobraGenericFlags {
command.CobraCmd.Flags().String(name, value, "")
}
}

actualErrors := command.ValidateInput(test.args)

actualErrorsMessages := utils.ErrorsToMessages(actualErrors)

assert.DeepEqual(t, actualErrorsMessages, test.expectedErrors)

})
}
}

func TestCmdConnectorStatus_Run(t *testing.T) {
type test struct {
name string
connectorName string
flags common.CommandConnectorStatusFlags
k8sObjects []runtime.Object
skupperObjects []runtime.Object
skupperErrorMessage string
errorMessage string
}

homeDir, err := os.UserHomeDir()
assert.Check(t, err == nil)

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",
},
{
name: "runs ok, returns 1 connectors",
connectorName: "my-connector",
},
{
name: "runs ok, returns 1 connectors yaml",
connectorName: "my-connector",
flags: common.CommandConnectorStatusFlags{Output: "yaml"},
},
{
name: "runs ok, returns all connectors",
},
{
name: "runs ok, returns all connectors json",
flags: common.CommandConnectorStatusFlags{Output: "json"},
},
{
name: "runs ok, returns all connectors output bad",
flags: common.CommandConnectorStatusFlags{Output: "bad-value"},
errorMessage: "format bad-value not supported",
},
{
name: "runs ok, returns 1 connectors bad output",
connectorName: "my-connector",
flags: common.CommandConnectorStatusFlags{Output: "bad-value"},
errorMessage: "format bad-value not supported",
},
}

//Add a temp file so connector exists for status tests
connectorResource1 := v1alpha1.Connector{
TypeMeta: metav1.TypeMeta{
APIVersion: "skupper.io/v1alpha1",
Kind: "Connector",
},
ObjectMeta: metav1.ObjectMeta{
Name: "my-connector",
Namespace: "test",
},
Spec: v1alpha1.ConnectorSpec{
Host: "1.2.3.4",
Port: 8080,
RoutingKey: "backend-8080",
},
Status: v1alpha1.ConnectorStatus{
Status: v1alpha1.Status{
Conditions: []metav1.Condition{
{
Type: "Configured",
Status: "True",
},
},
},
},
}
connectorResource2 := v1alpha1.Connector{
TypeMeta: metav1.TypeMeta{
APIVersion: "skupper.io/v1alpha1",
Kind: "Connector",
},
ObjectMeta: metav1.ObjectMeta{
Name: "my-connector2",
Namespace: "test",
},
Spec: v1alpha1.ConnectorSpec{
Host: "1.1.1.1",
Port: 9999,
RoutingKey: "test-9999",
},
Status: v1alpha1.ConnectorStatus{
Status: v1alpha1.Status{
Conditions: []metav1.Condition{
{
Type: "Configured",
Status: "True",
},
},
},
},
}

// add two connectors in runtime directory
command := &CmdConnectorStatus{}
command.namespace = "test"
command.connectorHandler = fs2.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")
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")
assert.Check(t, err == nil)

for _, test := range testTable {
command.connectorName = test.connectorName
command.Flags = &test.flags
command.output = command.Flags.Output
command.namespace = "test"

t.Run(test.name, func(t *testing.T) {

err := command.Run()
if err != nil {
assert.Check(t, test.errorMessage == err.Error())
} else {
assert.Check(t, err == nil)
}
})
}
}
Loading

0 comments on commit 261a052

Please sign in to comment.