From fc52025490d357e79c38a7bfefcb02f3a193b7f6 Mon Sep 17 00:00:00 2001 From: Andrey Smirnov Date: Wed, 4 Dec 2019 21:57:10 +0300 Subject: [PATCH] fix: provide peer remote address for 'NODE': as default in osctl This change is pretty mechanical, just wrap every API so that remote peer address is used as default for `resp.Metadata.Hostname`. This makes `NODE:` non-empty in all the API calls. Signed-off-by: Andrey Smirnov --- cmd/osctl/cmd/containers.go | 14 ++- cmd/osctl/cmd/interfaces.go | 14 ++- cmd/osctl/cmd/memory.go | 25 +++-- cmd/osctl/cmd/mounts.go | 17 ++- cmd/osctl/cmd/processes.go | 10 +- cmd/osctl/cmd/root.go | 10 +- cmd/osctl/cmd/routes.go | 13 ++- cmd/osctl/cmd/service.go | 87 ++++++++------- cmd/osctl/cmd/stats.go | 15 ++- cmd/osctl/cmd/time.go | 16 ++- cmd/osctl/cmd/upgrade.go | 13 ++- cmd/osctl/cmd/version.go | 15 ++- cmd/osctl/pkg/client/client.go | 192 ++++++++++++++++++++++++++------- 13 files changed, 325 insertions(+), 116 deletions(-) diff --git a/cmd/osctl/cmd/containers.go b/cmd/osctl/cmd/containers.go index 42e4c9c0f..68e742f38 100644 --- a/cmd/osctl/cmd/containers.go +++ b/cmd/osctl/cmd/containers.go @@ -14,7 +14,9 @@ import ( criconstants "github.com/containerd/cri/pkg/constants" "github.com/spf13/cobra" + "google.golang.org/grpc" "google.golang.org/grpc/metadata" + "google.golang.org/grpc/peer" "github.com/talos-systems/talos/api/common" osapi "github.com/talos-systems/talos/api/os" @@ -50,20 +52,24 @@ var containersCmd = &cobra.Command{ md := metadata.New(make(map[string]string)) md.Set("targets", target...) - reply, err := c.Containers(metadata.NewOutgoingContext(globalCtx, md), namespace, driver) + var remotePeer peer.Peer + + reply, err := c.Containers(metadata.NewOutgoingContext(globalCtx, md), namespace, driver, grpc.Peer(&remotePeer)) if err != nil { helpers.Fatalf("error getting process list: %s", err) } - containerRender(reply) + containerRender(&remotePeer, reply) }) }, } -func containerRender(reply *osapi.ContainersReply) { +func containerRender(remotePeer *peer.Peer, reply *osapi.ContainersReply) { w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) fmt.Fprintln(w, "NODE\tNAMESPACE\tID\tIMAGE\tPID\tSTATUS") + defaultNode := addrFromPeer(remotePeer) + for _, rep := range reply.Response { resp := rep sort.Slice(resp.Containers, @@ -78,7 +84,7 @@ func containerRender(reply *osapi.ContainersReply) { display = "└─ " + display } - node := "" + node := defaultNode if resp.Metadata != nil { node = resp.Metadata.Hostname diff --git a/cmd/osctl/cmd/interfaces.go b/cmd/osctl/cmd/interfaces.go index 45c621e68..9323780e8 100644 --- a/cmd/osctl/cmd/interfaces.go +++ b/cmd/osctl/cmd/interfaces.go @@ -11,6 +11,8 @@ import ( "text/tabwriter" "github.com/spf13/cobra" + "google.golang.org/grpc" + "google.golang.org/grpc/peer" networkapi "github.com/talos-systems/talos/api/network" "github.com/talos-systems/talos/cmd/osctl/pkg/client" @@ -29,22 +31,26 @@ var interfacesCmd = &cobra.Command{ } setupClient(func(c *client.Client) { - reply, err := c.Interfaces(globalCtx) + var remotePeer peer.Peer + + reply, err := c.Interfaces(globalCtx, grpc.Peer(&remotePeer)) if err != nil { helpers.Fatalf("error getting interfaces: %s", err) } - intersRender(reply) + intersRender(&remotePeer, reply) }) }, } -func intersRender(reply *networkapi.InterfacesReply) { +func intersRender(remotePeer *peer.Peer, reply *networkapi.InterfacesReply) { w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) fmt.Fprintln(w, "NODE\tINDEX\tNAME\tMAC\tMTU\tADDRESS") + defaultNode := addrFromPeer(remotePeer) + for _, resp := range reply.Response { - node := "" + node := defaultNode if resp.Metadata != nil { node = resp.Metadata.Hostname diff --git a/cmd/osctl/cmd/memory.go b/cmd/osctl/cmd/memory.go index 07491dc96..dcc849a2d 100644 --- a/cmd/osctl/cmd/memory.go +++ b/cmd/osctl/cmd/memory.go @@ -11,6 +11,8 @@ import ( "text/tabwriter" "github.com/spf13/cobra" + "google.golang.org/grpc" + "google.golang.org/grpc/peer" osapi "github.com/talos-systems/talos/api/os" "github.com/talos-systems/talos/cmd/osctl/pkg/client" @@ -32,26 +34,30 @@ var memoryCmd = &cobra.Command{ } setupClient(func(c *client.Client) { - reply, err := c.Memory(globalCtx) + var remotePeer peer.Peer + + reply, err := c.Memory(globalCtx, grpc.Peer(&remotePeer)) if err != nil { helpers.Fatalf("error getting memory stats: %s", err) } if verbose { - verboseRender(reply) + verboseRender(&remotePeer, reply) } else { - briefRender(reply) + briefRender(&remotePeer, reply) } }) }, } -func briefRender(reply *osapi.MemInfoReply) { +func briefRender(remotePeer *peer.Peer, reply *osapi.MemInfoReply) { w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) fmt.Fprintln(w, "NODE\tTOTAL\tUSED\tFREE\tSHARED\tBUFFERS\tCACHE\tAVAILABLE") + defaultNode := addrFromPeer(remotePeer) + for _, resp := range reply.Response { - node := "" + node := defaultNode if resp.Metadata != nil { node = resp.Metadata.Hostname @@ -73,13 +79,18 @@ func briefRender(reply *osapi.MemInfoReply) { helpers.Should(w.Flush()) } -func verboseRender(reply *osapi.MemInfoReply) { +func verboseRender(remotePeer *peer.Peer, reply *osapi.MemInfoReply) { + defaultNode := addrFromPeer(remotePeer) + // Dump as /proc/meminfo for _, resp := range reply.Response { + node := defaultNode + if resp.Metadata != nil { - fmt.Printf("%s: %s\n", "NODE", resp.Metadata.Hostname) + node = resp.Metadata.Hostname } + fmt.Printf("%s: %s\n", "NODE", node) fmt.Printf("%s: %d %s\n", "MemTotal", resp.Meminfo.Memtotal, "kB") fmt.Printf("%s: %d %s\n", "MemFree", resp.Meminfo.Memfree, "kB") fmt.Printf("%s: %d %s\n", "MemAvailable", resp.Meminfo.Memavailable, "kB") diff --git a/cmd/osctl/cmd/mounts.go b/cmd/osctl/cmd/mounts.go index 0aa436149..bcb636bec 100644 --- a/cmd/osctl/cmd/mounts.go +++ b/cmd/osctl/cmd/mounts.go @@ -12,6 +12,8 @@ import ( "text/tabwriter" "github.com/spf13/cobra" + "google.golang.org/grpc" + "google.golang.org/grpc/peer" machineapi "github.com/talos-systems/talos/api/machine" "github.com/talos-systems/talos/cmd/osctl/pkg/client" @@ -31,15 +33,24 @@ var mountsCmd = &cobra.Command{ } setupClient(func(c *client.Client) { - mountsRender(c.Mounts(globalCtx)) + var remotePeer peer.Peer + + reply, err := c.Mounts(globalCtx, grpc.Peer(&remotePeer)) + if err != nil { + helpers.Fatalf("error getting interfaces: %s", err) + } + + mountsRender(&remotePeer, reply) }) }, } -func mountsRender(reply *machineapi.MountsReply, err error) { +func mountsRender(remotePeer *peer.Peer, reply *machineapi.MountsReply) { w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) fmt.Fprintln(w, "NODE\tFILESYSTEM\tSIZE(GB)\tUSED(GB)\tAVAILABLE(GB)\tPERCENT USED\tMOUNTED ON") + defaultNode := addrFromPeer(remotePeer) + for _, resp := range reply.Response { for _, r := range resp.Stats { percentAvailable := 100.0 - 100.0*(float64(r.Available)/float64(r.Size)) @@ -48,7 +59,7 @@ func mountsRender(reply *machineapi.MountsReply, err error) { continue } - node := "" + node := defaultNode if resp.Metadata != nil { node = resp.Metadata.Hostname diff --git a/cmd/osctl/cmd/processes.go b/cmd/osctl/cmd/processes.go index cb479ec97..b9d27acf1 100644 --- a/cmd/osctl/cmd/processes.go +++ b/cmd/osctl/cmd/processes.go @@ -20,7 +20,9 @@ import ( "github.com/ryanuber/columnize" "github.com/spf13/cobra" "golang.org/x/crypto/ssh/terminal" + "google.golang.org/grpc" "google.golang.org/grpc/metadata" + "google.golang.org/grpc/peer" osapi "github.com/talos-systems/talos/api/os" "github.com/talos-systems/talos/cmd/osctl/pkg/client" @@ -182,7 +184,9 @@ var cpu = func(p1, p2 *osapi.Process) bool { //nolint: gocyclo func processesOutput(ctx context.Context, c *client.Client) (output string, err error) { - reply, err := c.Processes(ctx) + var remotePeer peer.Peer + + reply, err := c.Processes(ctx, grpc.Peer(&remotePeer)) if err != nil { // TODO: Figure out how to expose errors to client without messing // up display @@ -191,6 +195,8 @@ func processesOutput(ctx context.Context, c *client.Client) (output string, err return output, nil } + defaultNode := addrFromPeer(&remotePeer) + s := []string{} s = append(s, "NODE | PID | STATE | THREADS | CPU-TIME | VIRTMEM | RESMEM | COMMAND") @@ -217,7 +223,7 @@ func processesOutput(ctx context.Context, c *client.Client) (output string, err args = p.Args } - node := "" + node := defaultNode if resp.Metadata != nil { node = resp.Metadata.Hostname diff --git a/cmd/osctl/cmd/root.go b/cmd/osctl/cmd/root.go index 177ce27f0..39a7e4d9d 100644 --- a/cmd/osctl/cmd/root.go +++ b/cmd/osctl/cmd/root.go @@ -200,9 +200,15 @@ func remotePeer(ctx context.Context) (peerHost string) { remote, ok := peer.FromContext(ctx) if ok { - peerHost = remote.Addr.String() - peerHost, _, _ = net.SplitHostPort(peerHost) //nolint: errcheck + peerHost = addrFromPeer(remote) } return } + +func addrFromPeer(remote *peer.Peer) (peerHost string) { + peerHost = remote.Addr.String() + peerHost, _, _ = net.SplitHostPort(peerHost) //nolint: errcheck + + return peerHost +} diff --git a/cmd/osctl/cmd/routes.go b/cmd/osctl/cmd/routes.go index 9ef535e3f..39d89be7f 100644 --- a/cmd/osctl/cmd/routes.go +++ b/cmd/osctl/cmd/routes.go @@ -11,6 +11,8 @@ import ( "text/tabwriter" "github.com/spf13/cobra" + "google.golang.org/grpc" + "google.golang.org/grpc/peer" networkapi "github.com/talos-systems/talos/api/network" "github.com/talos-systems/talos/cmd/osctl/pkg/client" @@ -29,22 +31,25 @@ var routesCmd = &cobra.Command{ } setupClient(func(c *client.Client) { - reply, err := c.Routes(globalCtx) + var remotePeer peer.Peer + reply, err := c.Routes(globalCtx, grpc.Peer(&remotePeer)) if err != nil { helpers.Fatalf("error getting routes: %s", err) } - routesRender(reply) + routesRender(&remotePeer, reply) }) }, } -func routesRender(reply *networkapi.RoutesReply) { +func routesRender(remotePeer *peer.Peer, reply *networkapi.RoutesReply) { w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) fmt.Fprintln(w, "NODE\tINTERFACE\tDESTINATION\tGATEWAY\tMETRIC") + defaultNode := addrFromPeer(remotePeer) + for _, resp := range reply.Response { - var node string + node := defaultNode if resp.Metadata != nil { node = resp.Metadata.Hostname diff --git a/cmd/osctl/cmd/service.go b/cmd/osctl/cmd/service.go index 730eb388d..90be52a07 100644 --- a/cmd/osctl/cmd/service.go +++ b/cmd/osctl/cmd/service.go @@ -12,6 +12,8 @@ import ( "github.com/golang/protobuf/ptypes" "github.com/spf13/cobra" + "google.golang.org/grpc" + "google.golang.org/grpc/peer" machineapi "github.com/talos-systems/talos/api/machine" "github.com/talos-systems/talos/cmd/osctl/pkg/client" @@ -63,7 +65,9 @@ With actions 'start', 'stop', 'restart', service state is updated respectively.` } func serviceList(c *client.Client) { - reply, err := c.ServiceList(globalCtx) + var remotePeer peer.Peer + + reply, err := c.ServiceList(globalCtx, grpc.Peer(&remotePeer)) if err != nil { helpers.Fatalf("error listing services: %s", err) } @@ -71,11 +75,13 @@ func serviceList(c *client.Client) { w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) fmt.Fprintln(w, "NODE\tSERVICE\tSTATE\tHEALTH\tLAST CHANGE\tLAST EVENT") + defaultNode := addrFromPeer(&remotePeer) + for _, resp := range reply.Response { for _, s := range resp.Services { svc := serviceInfoWrapper{s} - node := "" + node := defaultNode if resp.Metadata != nil { node = resp.Metadata.Hostname @@ -89,49 +95,44 @@ func serviceList(c *client.Client) { } func serviceInfo(c *client.Client, id string) { - reply, err := c.ServiceInfo(globalCtx, id) + var remotePeer peer.Peer + + services, err := c.ServiceInfo(globalCtx, id, grpc.Peer(&remotePeer)) if err != nil { helpers.Fatalf("error listing services: %s", err) } w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) - services := make([]*machineapi.ServiceInfo, 0, len(reply.Response)) + defaultNode := addrFromPeer(&remotePeer) - for _, resp := range reply.Response { - for _, svc := range resp.Services { - if svc.Id == id { - services = append(services, svc) - for _, s := range services { - node := "" + for _, s := range services { + node := defaultNode - if resp.Metadata != nil { - node = resp.Metadata.Hostname - } + if s.Metadata != nil { + node = s.Metadata.Hostname + } - fmt.Fprintf(w, "NODE\t%s\n", node) + fmt.Fprintf(w, "NODE\t%s\n", node) - svc := serviceInfoWrapper{s} - fmt.Fprintf(w, "ID\t%s\n", svc.Id) - fmt.Fprintf(w, "STATE\t%s\n", svc.State) - fmt.Fprintf(w, "HEALTH\t%s\n", svc.HealthStatus()) + svc := serviceInfoWrapper{s.Service} + fmt.Fprintf(w, "ID\t%s\n", svc.Id) + fmt.Fprintf(w, "STATE\t%s\n", svc.State) + fmt.Fprintf(w, "HEALTH\t%s\n", svc.HealthStatus()) - if svc.Health.LastMessage != "" { - fmt.Fprintf(w, "LAST HEALTH MESSAGE\t%s\n", svc.Health.LastMessage) - } + if svc.Health.LastMessage != "" { + fmt.Fprintf(w, "LAST HEALTH MESSAGE\t%s\n", svc.Health.LastMessage) + } - label := "EVENTS" + label := "EVENTS" - for i := range svc.Events.Events { - event := svc.Events.Events[len(svc.Events.Events)-1-i] + for i := range svc.Events.Events { + event := svc.Events.Events[len(svc.Events.Events)-1-i] - // nolint: errcheck - ts, _ := ptypes.Timestamp(event.Ts) - fmt.Fprintf(w, "%s\t[%s]: %s (%s ago)\n", label, event.State, event.Msg, time.Since(ts).Round(time.Second)) - label = "" - } - } - } + // nolint: errcheck + ts, _ := ptypes.Timestamp(event.Ts) + fmt.Fprintf(w, "%s\t[%s]: %s (%s ago)\n", label, event.State, event.Msg, time.Since(ts).Round(time.Second)) + label = "" } } @@ -143,16 +144,20 @@ func serviceInfo(c *client.Client, id string) { } func serviceStart(c *client.Client, id string) { - reply, err := c.ServiceStart(globalCtx, id) + var remotePeer peer.Peer + + reply, err := c.ServiceStart(globalCtx, id, grpc.Peer(&remotePeer)) if err != nil { helpers.Fatalf("error starting service: %s", err) } + defaultNode := addrFromPeer(&remotePeer) + w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) fmt.Fprintln(w, "NODE\tRESPONSE") for _, resp := range reply.Response { - node := "" + node := defaultNode if resp.Metadata != nil { node = resp.Metadata.Hostname @@ -165,16 +170,20 @@ func serviceStart(c *client.Client, id string) { } func serviceStop(c *client.Client, id string) { - reply, err := c.ServiceStop(globalCtx, id) + var remotePeer peer.Peer + + reply, err := c.ServiceStop(globalCtx, id, grpc.Peer(&remotePeer)) if err != nil { helpers.Fatalf("error starting service: %s", err) } + defaultNode := addrFromPeer(&remotePeer) + w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) fmt.Fprintln(w, "NODE\tRESPONSE") for _, resp := range reply.Response { - node := "" + node := defaultNode if resp.Metadata != nil { node = resp.Metadata.Hostname @@ -187,16 +196,20 @@ func serviceStop(c *client.Client, id string) { } func serviceRestart(c *client.Client, id string) { - reply, err := c.ServiceRestart(globalCtx, id) + var remotePeer peer.Peer + + reply, err := c.ServiceRestart(globalCtx, id, grpc.Peer(&remotePeer)) if err != nil { helpers.Fatalf("error starting service: %s", err) } + defaultNode := addrFromPeer(&remotePeer) + w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) fmt.Fprintln(w, "NODE\tRESPONSE") for _, resp := range reply.Response { - node := "" + node := defaultNode if resp.Metadata != nil { node = resp.Metadata.Hostname diff --git a/cmd/osctl/cmd/stats.go b/cmd/osctl/cmd/stats.go index 19d51a12c..fec60c9f0 100644 --- a/cmd/osctl/cmd/stats.go +++ b/cmd/osctl/cmd/stats.go @@ -14,7 +14,9 @@ import ( criconstants "github.com/containerd/cri/pkg/constants" "github.com/spf13/cobra" + "google.golang.org/grpc" "google.golang.org/grpc/metadata" + "google.golang.org/grpc/peer" "github.com/talos-systems/talos/api/common" osapi "github.com/talos-systems/talos/api/os" @@ -47,21 +49,26 @@ var statsCmd = &cobra.Command{ } md := metadata.New(make(map[string]string)) md.Set("targets", target...) - reply, err := c.Stats(metadata.NewOutgoingContext(globalCtx, md), namespace, driver) + + var remotePeer peer.Peer + + reply, err := c.Stats(metadata.NewOutgoingContext(globalCtx, md), namespace, driver, grpc.Peer(&remotePeer)) if err != nil { helpers.Fatalf("error getting stats: %s", err) } - statsRender(reply) + statsRender(&remotePeer, reply) }) }, } -func statsRender(reply *osapi.StatsReply) { +func statsRender(remotePeer *peer.Peer, reply *osapi.StatsReply) { w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) fmt.Fprintln(w, "NODE\tNAMESPACE\tID\tMEMORY(MB)\tCPU") + defaultNode := addrFromPeer(remotePeer) + for _, rep := range reply.Response { resp := rep sort.Slice(resp.Stats, @@ -76,7 +83,7 @@ func statsRender(reply *osapi.StatsReply) { display = "└─ " + display } - node := "" + node := defaultNode if resp.Metadata != nil { node = resp.Metadata.Hostname diff --git a/cmd/osctl/cmd/time.go b/cmd/osctl/cmd/time.go index c36c26fea..df42a428a 100644 --- a/cmd/osctl/cmd/time.go +++ b/cmd/osctl/cmd/time.go @@ -12,6 +12,8 @@ import ( "github.com/golang/protobuf/ptypes" "github.com/spf13/cobra" + "google.golang.org/grpc" + "google.golang.org/grpc/peer" timeapi "github.com/talos-systems/talos/api/time" "github.com/talos-systems/talos/cmd/osctl/pkg/client" @@ -30,14 +32,18 @@ var timeCmd = &cobra.Command{ helpers.Fatalf("failed to parse check flag: %w", err) } - var reply *timeapi.TimeReply + var ( + reply *timeapi.TimeReply + remotePeer peer.Peer + ) + if server == "" { - reply, err = c.Time(globalCtx) + reply, err = c.Time(globalCtx, grpc.Peer(&remotePeer)) if err != nil { helpers.Fatalf("error fetching time: %s", err) } } else { - reply, err = c.TimeCheck(globalCtx, server) + reply, err = c.TimeCheck(globalCtx, server, grpc.Peer(&remotePeer)) if err != nil { helpers.Fatalf("error fetching time: %s", err) } @@ -46,9 +52,11 @@ var timeCmd = &cobra.Command{ w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) fmt.Fprintln(w, "NODE\tNTP-SERVER\tLOCAL-TIME\tREMOTE-TIME") + defaultNode := addrFromPeer(&remotePeer) + var localtime, remotetime time.Time for _, resp := range reply.Response { - node := "" + node := defaultNode if resp.Metadata != nil { node = resp.Metadata.Hostname diff --git a/cmd/osctl/cmd/upgrade.go b/cmd/osctl/cmd/upgrade.go index 8d76a23c5..5aa5a7007 100644 --- a/cmd/osctl/cmd/upgrade.go +++ b/cmd/osctl/cmd/upgrade.go @@ -12,6 +12,8 @@ import ( "time" "github.com/spf13/cobra" + "google.golang.org/grpc" + "google.golang.org/grpc/peer" machineapi "github.com/talos-systems/talos/api/machine" "github.com/talos-systems/talos/cmd/osctl/pkg/client" @@ -37,14 +39,15 @@ func init() { func upgrade() { var ( - err error - reply *machineapi.UpgradeReply + err error + reply *machineapi.UpgradeReply + remotePeer peer.Peer ) setupClient(func(c *client.Client) { // TODO: See if we can validate version and prevent starting upgrades to // an unknown version - reply, err = c.Upgrade(globalCtx, upgradeImage) + reply, err = c.Upgrade(globalCtx, upgradeImage, grpc.Peer(&remotePeer)) }) if err != nil { @@ -54,8 +57,10 @@ func upgrade() { w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) fmt.Fprintln(w, "NODE\tACK\tSTARTED") + defaultNode := addrFromPeer(&remotePeer) + for _, resp := range reply.Response { - node := "" + node := defaultNode if resp.Metadata != nil { node = resp.Metadata.Hostname diff --git a/cmd/osctl/cmd/version.go b/cmd/osctl/cmd/version.go index 8ba06e787..b50d43bdf 100644 --- a/cmd/osctl/cmd/version.go +++ b/cmd/osctl/cmd/version.go @@ -9,6 +9,8 @@ import ( "os" "github.com/spf13/cobra" + "google.golang.org/grpc" + "google.golang.org/grpc/peer" "github.com/talos-systems/talos/cmd/osctl/pkg/client" "github.com/talos-systems/talos/cmd/osctl/pkg/helpers" @@ -45,15 +47,24 @@ var versionCmd = &cobra.Command{ fmt.Println("Server:") setupClient(func(c *client.Client) { - reply, err := c.Version(globalCtx) + var remotePeer peer.Peer + + reply, err := c.Version(globalCtx, grpc.Peer(&remotePeer)) if err != nil { helpers.Fatalf("error getting version: %s", err) } + + defaultNode := addrFromPeer(&remotePeer) + for _, resp := range reply.Response { + node := defaultNode + if resp.Metadata != nil { - fmt.Printf("\t%s: %s\n", "NODE", resp.Metadata.Hostname) + node = resp.Metadata.Hostname } + fmt.Printf("\t%s: %s\n", "NODE", node) + version.PrintLongVersionFromExisting(resp.Version) } }) diff --git a/cmd/osctl/pkg/client/client.go b/cmd/osctl/pkg/client/client.go index abc940f45..af50093f0 100644 --- a/cmd/osctl/pkg/client/client.go +++ b/cmd/osctl/pkg/client/client.go @@ -207,27 +207,34 @@ func (c *Client) Kubeconfig(ctx context.Context) ([]byte, error) { } // Stats implements the proto.OSClient interface. -func (c *Client) Stats(ctx context.Context, namespace string, driver common.ContainerDriver) (reply *osapi.StatsReply, err error) { - reply, err = c.client.Stats(ctx, &osapi.StatsRequest{ - Namespace: namespace, - Driver: driver, - }) +func (c *Client) Stats(ctx context.Context, namespace string, driver common.ContainerDriver, callOptions ...grpc.CallOption) (reply *osapi.StatsReply, err error) { + reply, err = c.client.Stats( + ctx, &osapi.StatsRequest{ + Namespace: namespace, + Driver: driver, + }, + callOptions..., + ) return } // Containers implements the proto.OSClient interface. -func (c *Client) Containers(ctx context.Context, namespace string, driver common.ContainerDriver) (reply *osapi.ContainersReply, err error) { - reply, err = c.client.Containers(ctx, &osapi.ContainersRequest{ - Namespace: namespace, - Driver: driver, - }) +func (c *Client) Containers(ctx context.Context, namespace string, driver common.ContainerDriver, callOptions ...grpc.CallOption) (reply *osapi.ContainersReply, err error) { + reply, err = c.client.Containers( + ctx, + &osapi.ContainersRequest{ + Namespace: namespace, + Driver: driver, + }, + callOptions..., + ) return } // Restart implements the proto.OSClient interface. -func (c *Client) Restart(ctx context.Context, namespace string, driver common.ContainerDriver, id string) (err error) { +func (c *Client) Restart(ctx context.Context, namespace string, driver common.ContainerDriver, id string, callOptions ...grpc.CallOption) (err error) { _, err = c.client.Restart(ctx, &osapi.RestartRequest{ Id: id, Namespace: namespace, @@ -272,33 +279,69 @@ func (c *Client) Logs(ctx context.Context, namespace string, driver common.Conta } // Version implements the proto.OSClient interface. -func (c *Client) Version(ctx context.Context) (*machineapi.VersionReply, error) { - return c.MachineClient.Version(ctx, &empty.Empty{}) +func (c *Client) Version(ctx context.Context, callOptions ...grpc.CallOption) (reply *machineapi.VersionReply, err error) { + reply, err = c.MachineClient.Version( + ctx, + &empty.Empty{}, + callOptions..., + ) + + return } // Routes implements the networkdproto.NetworkClient interface. -func (c *Client) Routes(ctx context.Context) (*networkapi.RoutesReply, error) { - return c.NetworkClient.Routes(ctx, &empty.Empty{}) +func (c *Client) Routes(ctx context.Context, callOptions ...grpc.CallOption) (reply *networkapi.RoutesReply, err error) { + reply, err = c.NetworkClient.Routes( + ctx, + &empty.Empty{}, + callOptions..., + ) + + return } // Interfaces implements the proto.OSClient interface. -func (c *Client) Interfaces(ctx context.Context) (*networkapi.InterfacesReply, error) { - return c.NetworkClient.Interfaces(ctx, &empty.Empty{}) +func (c *Client) Interfaces(ctx context.Context, callOptions ...grpc.CallOption) (reply *networkapi.InterfacesReply, err error) { + reply, err = c.NetworkClient.Interfaces( + ctx, + &empty.Empty{}, + callOptions..., + ) + + return } // Processes implements the proto.OSClient interface. -func (c *Client) Processes(ctx context.Context) (reply *osapi.ProcessesReply, err error) { - return c.client.Processes(ctx, &empty.Empty{}) +func (c *Client) Processes(ctx context.Context, callOptions ...grpc.CallOption) (reply *osapi.ProcessesReply, err error) { + reply, err = c.client.Processes( + ctx, + &empty.Empty{}, + callOptions..., + ) + + return } // Memory implements the proto.OSClient interface. -func (c *Client) Memory(ctx context.Context) (*osapi.MemInfoReply, error) { - return c.client.Memory(ctx, &empty.Empty{}) +func (c *Client) Memory(ctx context.Context, callOptions ...grpc.CallOption) (reply *osapi.MemInfoReply, err error) { + reply, err = c.client.Memory( + ctx, + &empty.Empty{}, + callOptions..., + ) + + return } // Mounts implements the proto.OSClient interface. -func (c *Client) Mounts(ctx context.Context) (*machineapi.MountsReply, error) { - return c.MachineClient.Mounts(ctx, &empty.Empty{}) +func (c *Client) Mounts(ctx context.Context, callOptions ...grpc.CallOption) (reply *machineapi.MountsReply, err error) { + reply, err = c.MachineClient.Mounts( + ctx, + &empty.Empty{}, + callOptions..., + ) + + return } // LS implements the proto.OSClient interface. @@ -320,46 +363,117 @@ func (c *Client) CopyOut(ctx context.Context, rootPath string) (io.Reader, <-cha // Upgrade initiates a Talos upgrade ... and implements the proto.OSClient // interface -func (c *Client) Upgrade(ctx context.Context, image string) (*machineapi.UpgradeReply, error) { - return c.MachineClient.Upgrade(ctx, &machineapi.UpgradeRequest{Image: image}) +func (c *Client) Upgrade(ctx context.Context, image string, callOptions ...grpc.CallOption) (reply *machineapi.UpgradeReply, err error) { + reply, err = c.MachineClient.Upgrade( + ctx, + &machineapi.UpgradeRequest{Image: image}, + callOptions..., + ) + + return } // ServiceList returns list of services with their state -func (c *Client) ServiceList(ctx context.Context) (*machineapi.ServiceListReply, error) { - return c.MachineClient.ServiceList(ctx, &empty.Empty{}) +func (c *Client) ServiceList(ctx context.Context, callOptions ...grpc.CallOption) (reply *machineapi.ServiceListReply, err error) { + reply, err = c.MachineClient.ServiceList( + ctx, + &empty.Empty{}, + callOptions..., + ) + + return +} + +// ServiceInfo provides info about a service and node metadata +type ServiceInfo struct { + Metadata *common.ResponseMetadata + Service *machineapi.ServiceInfo } // ServiceInfo returns info about a single service // // This is implemented via service list API, as we don't have many services // If service with given id is not registered, function returns nil -func (c *Client) ServiceInfo(ctx context.Context, id string) (*machineapi.ServiceListReply, error) { - return c.MachineClient.ServiceList(ctx, &empty.Empty{}) +func (c *Client) ServiceInfo(ctx context.Context, id string, callOptions ...grpc.CallOption) (services []ServiceInfo, err error) { + var reply *machineapi.ServiceListReply + + reply, err = c.MachineClient.ServiceList( + ctx, + &empty.Empty{}, + callOptions..., + ) + + if err != nil { + return + } + + for _, resp := range reply.Response { + for _, svc := range resp.Services { + if svc.Id == id { + services = append(services, ServiceInfo{ + Metadata: resp.Metadata, + Service: svc, + }) + } + } + } + + return } // ServiceStart starts a service. -func (c *Client) ServiceStart(ctx context.Context, id string) (*machineapi.ServiceStartReply, error) { - return c.MachineClient.ServiceStart(ctx, &machineapi.ServiceStartRequest{Id: id}) +func (c *Client) ServiceStart(ctx context.Context, id string, callOptions ...grpc.CallOption) (reply *machineapi.ServiceStartReply, err error) { + reply, err = c.MachineClient.ServiceStart( + ctx, + &machineapi.ServiceStartRequest{Id: id}, + callOptions..., + ) + + return } // ServiceStop stops a service. -func (c *Client) ServiceStop(ctx context.Context, id string) (*machineapi.ServiceStopReply, error) { - return c.MachineClient.ServiceStop(ctx, &machineapi.ServiceStopRequest{Id: id}) +func (c *Client) ServiceStop(ctx context.Context, id string, callOptions ...grpc.CallOption) (reply *machineapi.ServiceStopReply, err error) { + reply, err = c.MachineClient.ServiceStop( + ctx, + &machineapi.ServiceStopRequest{Id: id}, + callOptions..., + ) + + return } // ServiceRestart restarts a service. -func (c *Client) ServiceRestart(ctx context.Context, id string) (*machineapi.ServiceRestartReply, error) { - return c.MachineClient.ServiceRestart(ctx, &machineapi.ServiceRestartRequest{Id: id}) +func (c *Client) ServiceRestart(ctx context.Context, id string, callOptions ...grpc.CallOption) (reply *machineapi.ServiceRestartReply, err error) { + reply, err = c.MachineClient.ServiceRestart( + ctx, + &machineapi.ServiceRestartRequest{Id: id}, + callOptions..., + ) + + return } // Time returns the time -func (c *Client) Time(ctx context.Context) (*timeapi.TimeReply, error) { - return c.TimeClient.Time(ctx, &empty.Empty{}) +func (c *Client) Time(ctx context.Context, callOptions ...grpc.CallOption) (reply *timeapi.TimeReply, err error) { + reply, err = c.TimeClient.Time( + ctx, + &empty.Empty{}, + callOptions..., + ) + + return } // TimeCheck returns the time compared to the specified ntp server -func (c *Client) TimeCheck(ctx context.Context, server string) (*timeapi.TimeReply, error) { - return c.TimeClient.TimeCheck(ctx, &timeapi.TimeRequest{Server: server}) +func (c *Client) TimeCheck(ctx context.Context, server string, callOptions ...grpc.CallOption) (reply *timeapi.TimeReply, err error) { + reply, err = c.TimeClient.TimeCheck( + ctx, + &timeapi.TimeRequest{Server: server}, + callOptions..., + ) + + return } // Read reads a file.