diff --git a/internal/cmd/skupper/link/kube/link_status.go b/internal/cmd/skupper/link/kube/link_status.go index dffa58d57..5fb413da7 100644 --- a/internal/cmd/skupper/link/kube/link_status.go +++ b/internal/cmd/skupper/link/kube/link_status.go @@ -24,6 +24,7 @@ type CmdLinkStatus struct { Namespace string output string linkName string + siteName string } func NewCmdLinkStatus() *CmdLinkStatus { @@ -47,8 +48,9 @@ func (cmd *CmdLinkStatus) ValidateInput(args []string) error { if siteList == nil || len(siteList.Items) == 0 || err != nil { validationErrors = append(validationErrors, fmt.Errorf("there is no skupper site available")) + } else if len(siteList.Items) == 1 { + cmd.siteName = siteList.Items[0].Name } - if len(args) > 1 { validationErrors = append(validationErrors, fmt.Errorf("this command only accepts one argument")) } @@ -70,58 +72,74 @@ func (cmd *CmdLinkStatus) ValidateInput(args []string) error { func (cmd *CmdLinkStatus) InputToOptions() { cmd.output = cmd.Flags.Output } + func (cmd *CmdLinkStatus) Run() error { if cmd.linkName != "" { - + // display outgoing links selectedLink, err := cmd.Client.Links(cmd.Namespace).Get(context.TODO(), cmd.linkName, metav1.GetOptions{}) - if err != nil { - return err + if err == nil { + if cmd.output != "" { + return printEncodedOutput(cmd.output, selectedLink) + } else { + displaySingleLink(selectedLink) + } } - - if cmd.output != "" { - return printEncodedOuptut(cmd.output, selectedLink) - } else { - displaySingleLink(selectedLink) + // display incoming links + currentSite, err := cmd.Client.Sites(cmd.Namespace).Get(context.TODO(), cmd.siteName, metav1.GetOptions{}) + if err == nil && currentSite != nil { + if cmd.output != "" { + printEncodedIncomingLink(currentSite, cmd.siteName, cmd.linkName, cmd.output) + } else { + displaySingleIncomingLink(currentSite, cmd.siteName, cmd.linkName) + } } } else { - + // display outgoing links linkList, err := cmd.Client.Links(cmd.Namespace).List(context.TODO(), metav1.ListOptions{}) if err != nil { return err } if linkList != nil && len(linkList.Items) == 0 { - fmt.Println("There are no link resources in the namespace") - return nil - } - - if cmd.output != "" { - for _, link := range linkList.Items { - err := printEncodedOuptut(cmd.output, &link) + fmt.Println("There are no outgoing link resources in the namespace") + } else { + if cmd.output != "" { + for _, link := range linkList.Items { + err := printEncodedOutput(cmd.output, &link) - if err != nil { - return err + if err != nil { + return err + } } + } else { + displayLinkList(linkList.Items) } - } else { - displayLinkList(linkList.Items) } + // display incoming links + currentSite, err := cmd.Client.Sites(cmd.Namespace).Get(context.TODO(), cmd.siteName, metav1.GetOptions{}) + if err == nil && currentSite != nil { + if cmd.output != "" { + printEncodedIncomingLink(currentSite, cmd.siteName, "", cmd.output) + } else { + displayIncomingLink(currentSite, cmd.siteName) + } + } } - return nil } func (cmd *CmdLinkStatus) WaitUntil() error { return nil } -func printEncodedOuptut(outputType string, link *v2alpha1.Link) error { +func printEncodedOutput(outputType string, link *v2alpha1.Link) error { encodedOutput, err := utils.Encode(outputType, link) fmt.Println(encodedOutput) return err } func displaySingleLink(link *v2alpha1.Link) { + fmt.Println("Outgoing link from this site:") fmt.Printf("%s\t: %s\n", "Name", link.Name) fmt.Printf("%s\t: %s\n", "Status", link.Status.StatusType) fmt.Printf("%s\t: %d\n", "Cost", link.Spec.Cost) @@ -130,6 +148,7 @@ func displaySingleLink(link *v2alpha1.Link) { func displayLinkList(linkList []v2alpha1.Link) { writer := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', tabwriter.AlignRight) + fmt.Println("Outgoing link from this site:") fmt.Fprintln(writer, "NAME\tSTATUS\tCOST\tMESSAGE") for _, link := range linkList { @@ -139,3 +158,61 @@ func displayLinkList(linkList []v2alpha1.Link) { writer.Flush() } + +func displayIncomingLink(currentSite *v2alpha1.Site, localSiteName string) { + incomingLinksFound := false + writer := tabwriter.NewWriter(os.Stdout, 0, 8, 1, '\t', tabwriter.AlignRight) + fmt.Fprintln(writer, "\nIncoming Links from remote sites: ") + for _, links := range currentSite.Status.Network { + for _, link := range links.Links { + if link.RemoteSiteName == localSiteName { + if !incomingLinksFound { + fmt.Fprintln(writer, "NAME\tSTATUS\tREMOTE SITE") + } + status := "Error" + if link.Operational { + status = "Ready" + } + fmt.Fprintf(writer, "%s\t%s\t%s", link.Name, status, links.Name) + fmt.Fprintln(writer) + incomingLinksFound = true + } + } + } + + if !incomingLinksFound { + fmt.Fprintln(writer, "There are no incoming link resources in the namespace") + } + + writer.Flush() +} + +func displaySingleIncomingLink(currentSite *v2alpha1.Site, localSiteName string, linkName string) { + fmt.Println("\nIncoming Links from remote sites: ") + for _, links := range currentSite.Status.Network { + for _, link := range links.Links { + if link.RemoteSiteName == localSiteName && link.Name == linkName { + status := "Error" + if link.Operational { + status = "Ready" + } + fmt.Printf("%s\t: %s\n", "Name", link.Name) + fmt.Printf("%s\t: %s\n", "Status", status) + fmt.Printf("%s\t: %s\n", "Remote", links.Name) + } + } + } +} + +func printEncodedIncomingLink(currentSite *v2alpha1.Site, localSiteName string, linkName string, outputType string) { + for _, links := range currentSite.Status.Network { + for _, link := range links.Links { + if link.RemoteSiteName == localSiteName { + if linkName == "" || link.Name == linkName { + encodedOutput, _ := utils.Encode(outputType, links) + fmt.Println(encodedOutput) + } + } + } + } +} diff --git a/internal/cmd/skupper/link/kube/link_status_test.go b/internal/cmd/skupper/link/kube/link_status_test.go index f5c7d9200..1b92f4930 100644 --- a/internal/cmd/skupper/link/kube/link_status_test.go +++ b/internal/cmd/skupper/link/kube/link_status_test.go @@ -110,6 +110,7 @@ func TestCmdLinkStatus_Run(t *testing.T) { errorMessage string linkName string output string + site string } testTable := []test{ @@ -231,6 +232,178 @@ func TestCmdLinkStatus_Run(t *testing.T) { output: "unsupported", errorMessage: "format unsupported not supported", }, + { + name: "runs ok no incoming links", + skupperObjects: []runtime.Object{ + &v2alpha1.Site{ + ObjectMeta: v1.ObjectMeta{ + Name: "public1", + Namespace: "test", + }, + Status: v2alpha1.SiteStatus{ + Status: v2alpha1.Status{ + Message: "OK", + }, + }, + }, + }, + site: "public1", + }, + { + name: "runs ok shows incoming links", + skupperObjects: []runtime.Object{ + &v2alpha1.Site{ + ObjectMeta: v1.ObjectMeta{ + Name: "public1", + Namespace: "test", + }, + Status: v2alpha1.SiteStatus{ + Status: v2alpha1.Status{ + Message: "OK", + }, + Network: []v2alpha1.SiteRecord{ + { + Id: "08b068e0-31d2-4739-8291-d168230b527b", + Links: []v2alpha1.LinkRecord{ + { + Name: "public1-d72cbb23-d98d-4cf7-a943-a56d0d447498", + Operational: true, + RemoteSiteId: "bb96fff0-2f25-4259-830a-b4c15e5b3f80", + RemoteSiteName: "public1", + }, + }, + Name: "public2", + Namespace: "public2", + Platform: "kubernetes", + Version: "2.0.0", + }, + }, + }, + }, + }, + site: "public1", + }, + { + name: "runs ok shows selected incoming link", + skupperObjects: []runtime.Object{ + &v2alpha1.Site{ + ObjectMeta: v1.ObjectMeta{ + Name: "public1", + Namespace: "test", + }, + Status: v2alpha1.SiteStatus{ + Status: v2alpha1.Status{ + Message: "OK", + }, + Network: []v2alpha1.SiteRecord{ + { + Id: "08b068e0-31d2-4739-8291-d168230b527b", + Links: []v2alpha1.LinkRecord{ + { + Name: "public1-d72cbb23-d98d-4cf7-a943-a56d0d447498", + Operational: true, + RemoteSiteId: "bb96fff0-2f25-4259-830a-b4c15e5b3f80", + RemoteSiteName: "public1", + }, + }, + Name: "public2", + Namespace: "public2", + Platform: "kubernetes", + Version: "2.0.0", + }, + }, + }, + }, + }, + linkName: "public1-d72cbb23-d98d-4cf7-a943-a56d0d447498", + site: "public1", + }, + { + name: "runs ok shows selected incoming link in yaml form", + skupperObjects: []runtime.Object{ + &v2alpha1.Site{ + ObjectMeta: v1.ObjectMeta{ + Name: "public1", + Namespace: "test", + }, + Status: v2alpha1.SiteStatus{ + Status: v2alpha1.Status{ + Message: "OK", + }, + Network: []v2alpha1.SiteRecord{ + { + Id: "08b068e0-31d2-4739-8291-d168230b527b", + Links: []v2alpha1.LinkRecord{ + { + Name: "public1-d72cbb23-d98d-4cf7-a943-a56d0d447498", + Operational: true, + RemoteSiteId: "bb96fff0-2f25-4259-830a-b4c15e5b3f80", + RemoteSiteName: "public1", + }, + }, + Name: "public2", + Namespace: "public2", + Platform: "kubernetes", + Version: "2.0.0", + }, + }, + }, + }, + }, + linkName: "public1-d72cbb23-d98d-4cf7-a943-a56d0d447498", + site: "public1", + output: "yaml", + }, + { + name: "runs ok shows incoming links in json", + skupperObjects: []runtime.Object{ + &v2alpha1.Site{ + ObjectMeta: v1.ObjectMeta{ + Name: "public1", + Namespace: "test", + }, + Status: v2alpha1.SiteStatus{ + Status: v2alpha1.Status{ + Message: "OK", + }, + Network: []v2alpha1.SiteRecord{ + { + Id: "08b068e0-31d2-4739-8291-d168230b527b", + Links: []v2alpha1.LinkRecord{ + { + Name: "public1-d72cbb23-d98d-4cf7-a943-a56d0d447498", + Operational: true, + RemoteSiteId: "bb96fff0-2f25-4259-830a-b4c15e5b3f80", + RemoteSiteName: "public1", + }, + }, + Name: "public2", + Namespace: "public2", + Platform: "kubernetes", + Version: "2.0.0", + }, + { + Id: "65816e8e-cf73-4ba7-91e9-e16a9c0b6ea4", + Links: []v2alpha1.LinkRecord{ + { + Name: "public1-d72cbb23-d98d-4cf7-a943-a56d0d447498", + Operational: true, + RemoteSiteId: "bb96fff0-2f25-4259-830a-b4c15e5b3f80", + RemoteSiteName: "public1", + }, + }, + Name: "private1", + Namespace: "private1", + Platform: "kubernetes", + Version: "2.0.0", + }, + }, + }, + }, + }, + site: "public1", + output: "yaml", + }, } for _, test := range testTable { @@ -238,6 +411,7 @@ func TestCmdLinkStatus_Run(t *testing.T) { assert.Assert(t, err) cmd.linkName = test.linkName cmd.output = test.output + cmd.siteName = test.site t.Run(test.name, func(t *testing.T) {