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 <smirnov.andrey@gmail.com>
This commit is contained in:
Andrey Smirnov 2019-12-04 21:57:10 +03:00 committed by Andrey Smirnov
parent 43e6703b8b
commit fc52025490
13 changed files with 325 additions and 116 deletions

View File

@ -14,7 +14,9 @@ import (
criconstants "github.com/containerd/cri/pkg/constants" criconstants "github.com/containerd/cri/pkg/constants"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata" "google.golang.org/grpc/metadata"
"google.golang.org/grpc/peer"
"github.com/talos-systems/talos/api/common" "github.com/talos-systems/talos/api/common"
osapi "github.com/talos-systems/talos/api/os" osapi "github.com/talos-systems/talos/api/os"
@ -50,20 +52,24 @@ var containersCmd = &cobra.Command{
md := metadata.New(make(map[string]string)) md := metadata.New(make(map[string]string))
md.Set("targets", target...) 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 { if err != nil {
helpers.Fatalf("error getting process list: %s", err) 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) w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
fmt.Fprintln(w, "NODE\tNAMESPACE\tID\tIMAGE\tPID\tSTATUS") fmt.Fprintln(w, "NODE\tNAMESPACE\tID\tIMAGE\tPID\tSTATUS")
defaultNode := addrFromPeer(remotePeer)
for _, rep := range reply.Response { for _, rep := range reply.Response {
resp := rep resp := rep
sort.Slice(resp.Containers, sort.Slice(resp.Containers,
@ -78,7 +84,7 @@ func containerRender(reply *osapi.ContainersReply) {
display = "└─ " + display display = "└─ " + display
} }
node := "" node := defaultNode
if resp.Metadata != nil { if resp.Metadata != nil {
node = resp.Metadata.Hostname node = resp.Metadata.Hostname

View File

@ -11,6 +11,8 @@ import (
"text/tabwriter" "text/tabwriter"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"google.golang.org/grpc"
"google.golang.org/grpc/peer"
networkapi "github.com/talos-systems/talos/api/network" networkapi "github.com/talos-systems/talos/api/network"
"github.com/talos-systems/talos/cmd/osctl/pkg/client" "github.com/talos-systems/talos/cmd/osctl/pkg/client"
@ -29,22 +31,26 @@ var interfacesCmd = &cobra.Command{
} }
setupClient(func(c *client.Client) { 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 { if err != nil {
helpers.Fatalf("error getting interfaces: %s", err) 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) w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
fmt.Fprintln(w, "NODE\tINDEX\tNAME\tMAC\tMTU\tADDRESS") fmt.Fprintln(w, "NODE\tINDEX\tNAME\tMAC\tMTU\tADDRESS")
defaultNode := addrFromPeer(remotePeer)
for _, resp := range reply.Response { for _, resp := range reply.Response {
node := "" node := defaultNode
if resp.Metadata != nil { if resp.Metadata != nil {
node = resp.Metadata.Hostname node = resp.Metadata.Hostname

View File

@ -11,6 +11,8 @@ import (
"text/tabwriter" "text/tabwriter"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"google.golang.org/grpc"
"google.golang.org/grpc/peer"
osapi "github.com/talos-systems/talos/api/os" osapi "github.com/talos-systems/talos/api/os"
"github.com/talos-systems/talos/cmd/osctl/pkg/client" "github.com/talos-systems/talos/cmd/osctl/pkg/client"
@ -32,26 +34,30 @@ var memoryCmd = &cobra.Command{
} }
setupClient(func(c *client.Client) { 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 { if err != nil {
helpers.Fatalf("error getting memory stats: %s", err) helpers.Fatalf("error getting memory stats: %s", err)
} }
if verbose { if verbose {
verboseRender(reply) verboseRender(&remotePeer, reply)
} else { } 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) w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
fmt.Fprintln(w, "NODE\tTOTAL\tUSED\tFREE\tSHARED\tBUFFERS\tCACHE\tAVAILABLE") fmt.Fprintln(w, "NODE\tTOTAL\tUSED\tFREE\tSHARED\tBUFFERS\tCACHE\tAVAILABLE")
defaultNode := addrFromPeer(remotePeer)
for _, resp := range reply.Response { for _, resp := range reply.Response {
node := "" node := defaultNode
if resp.Metadata != nil { if resp.Metadata != nil {
node = resp.Metadata.Hostname node = resp.Metadata.Hostname
@ -73,13 +79,18 @@ func briefRender(reply *osapi.MemInfoReply) {
helpers.Should(w.Flush()) helpers.Should(w.Flush())
} }
func verboseRender(reply *osapi.MemInfoReply) { func verboseRender(remotePeer *peer.Peer, reply *osapi.MemInfoReply) {
defaultNode := addrFromPeer(remotePeer)
// Dump as /proc/meminfo // Dump as /proc/meminfo
for _, resp := range reply.Response { for _, resp := range reply.Response {
node := defaultNode
if resp.Metadata != nil { 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", "MemTotal", resp.Meminfo.Memtotal, "kB")
fmt.Printf("%s: %d %s\n", "MemFree", resp.Meminfo.Memfree, "kB") fmt.Printf("%s: %d %s\n", "MemFree", resp.Meminfo.Memfree, "kB")
fmt.Printf("%s: %d %s\n", "MemAvailable", resp.Meminfo.Memavailable, "kB") fmt.Printf("%s: %d %s\n", "MemAvailable", resp.Meminfo.Memavailable, "kB")

View File

@ -12,6 +12,8 @@ import (
"text/tabwriter" "text/tabwriter"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"google.golang.org/grpc"
"google.golang.org/grpc/peer"
machineapi "github.com/talos-systems/talos/api/machine" machineapi "github.com/talos-systems/talos/api/machine"
"github.com/talos-systems/talos/cmd/osctl/pkg/client" "github.com/talos-systems/talos/cmd/osctl/pkg/client"
@ -31,15 +33,24 @@ var mountsCmd = &cobra.Command{
} }
setupClient(func(c *client.Client) { 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) w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
fmt.Fprintln(w, "NODE\tFILESYSTEM\tSIZE(GB)\tUSED(GB)\tAVAILABLE(GB)\tPERCENT USED\tMOUNTED ON") fmt.Fprintln(w, "NODE\tFILESYSTEM\tSIZE(GB)\tUSED(GB)\tAVAILABLE(GB)\tPERCENT USED\tMOUNTED ON")
defaultNode := addrFromPeer(remotePeer)
for _, resp := range reply.Response { for _, resp := range reply.Response {
for _, r := range resp.Stats { for _, r := range resp.Stats {
percentAvailable := 100.0 - 100.0*(float64(r.Available)/float64(r.Size)) percentAvailable := 100.0 - 100.0*(float64(r.Available)/float64(r.Size))
@ -48,7 +59,7 @@ func mountsRender(reply *machineapi.MountsReply, err error) {
continue continue
} }
node := "" node := defaultNode
if resp.Metadata != nil { if resp.Metadata != nil {
node = resp.Metadata.Hostname node = resp.Metadata.Hostname

View File

@ -20,7 +20,9 @@ import (
"github.com/ryanuber/columnize" "github.com/ryanuber/columnize"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"golang.org/x/crypto/ssh/terminal" "golang.org/x/crypto/ssh/terminal"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata" "google.golang.org/grpc/metadata"
"google.golang.org/grpc/peer"
osapi "github.com/talos-systems/talos/api/os" osapi "github.com/talos-systems/talos/api/os"
"github.com/talos-systems/talos/cmd/osctl/pkg/client" "github.com/talos-systems/talos/cmd/osctl/pkg/client"
@ -182,7 +184,9 @@ var cpu = func(p1, p2 *osapi.Process) bool {
//nolint: gocyclo //nolint: gocyclo
func processesOutput(ctx context.Context, c *client.Client) (output string, err error) { 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 { if err != nil {
// TODO: Figure out how to expose errors to client without messing // TODO: Figure out how to expose errors to client without messing
// up display // up display
@ -191,6 +195,8 @@ func processesOutput(ctx context.Context, c *client.Client) (output string, err
return output, nil return output, nil
} }
defaultNode := addrFromPeer(&remotePeer)
s := []string{} s := []string{}
s = append(s, "NODE | PID | STATE | THREADS | CPU-TIME | VIRTMEM | RESMEM | COMMAND") 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 args = p.Args
} }
node := "" node := defaultNode
if resp.Metadata != nil { if resp.Metadata != nil {
node = resp.Metadata.Hostname node = resp.Metadata.Hostname

View File

@ -200,9 +200,15 @@ func remotePeer(ctx context.Context) (peerHost string) {
remote, ok := peer.FromContext(ctx) remote, ok := peer.FromContext(ctx)
if ok { if ok {
peerHost = remote.Addr.String() peerHost = addrFromPeer(remote)
peerHost, _, _ = net.SplitHostPort(peerHost) //nolint: errcheck
} }
return return
} }
func addrFromPeer(remote *peer.Peer) (peerHost string) {
peerHost = remote.Addr.String()
peerHost, _, _ = net.SplitHostPort(peerHost) //nolint: errcheck
return peerHost
}

View File

@ -11,6 +11,8 @@ import (
"text/tabwriter" "text/tabwriter"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"google.golang.org/grpc"
"google.golang.org/grpc/peer"
networkapi "github.com/talos-systems/talos/api/network" networkapi "github.com/talos-systems/talos/api/network"
"github.com/talos-systems/talos/cmd/osctl/pkg/client" "github.com/talos-systems/talos/cmd/osctl/pkg/client"
@ -29,22 +31,25 @@ var routesCmd = &cobra.Command{
} }
setupClient(func(c *client.Client) { 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 { if err != nil {
helpers.Fatalf("error getting routes: %s", err) 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) w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
fmt.Fprintln(w, "NODE\tINTERFACE\tDESTINATION\tGATEWAY\tMETRIC") fmt.Fprintln(w, "NODE\tINTERFACE\tDESTINATION\tGATEWAY\tMETRIC")
defaultNode := addrFromPeer(remotePeer)
for _, resp := range reply.Response { for _, resp := range reply.Response {
var node string node := defaultNode
if resp.Metadata != nil { if resp.Metadata != nil {
node = resp.Metadata.Hostname node = resp.Metadata.Hostname

View File

@ -12,6 +12,8 @@ import (
"github.com/golang/protobuf/ptypes" "github.com/golang/protobuf/ptypes"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"google.golang.org/grpc"
"google.golang.org/grpc/peer"
machineapi "github.com/talos-systems/talos/api/machine" machineapi "github.com/talos-systems/talos/api/machine"
"github.com/talos-systems/talos/cmd/osctl/pkg/client" "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) { 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 { if err != nil {
helpers.Fatalf("error listing services: %s", err) 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) w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
fmt.Fprintln(w, "NODE\tSERVICE\tSTATE\tHEALTH\tLAST CHANGE\tLAST EVENT") fmt.Fprintln(w, "NODE\tSERVICE\tSTATE\tHEALTH\tLAST CHANGE\tLAST EVENT")
defaultNode := addrFromPeer(&remotePeer)
for _, resp := range reply.Response { for _, resp := range reply.Response {
for _, s := range resp.Services { for _, s := range resp.Services {
svc := serviceInfoWrapper{s} svc := serviceInfoWrapper{s}
node := "" node := defaultNode
if resp.Metadata != nil { if resp.Metadata != nil {
node = resp.Metadata.Hostname node = resp.Metadata.Hostname
@ -89,29 +95,27 @@ func serviceList(c *client.Client) {
} }
func serviceInfo(c *client.Client, id string) { 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 { if err != nil {
helpers.Fatalf("error listing services: %s", err) helpers.Fatalf("error listing services: %s", err)
} }
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) 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 { for _, s := range services {
node := "" node := defaultNode
if resp.Metadata != nil { if s.Metadata != nil {
node = resp.Metadata.Hostname node = s.Metadata.Hostname
} }
fmt.Fprintf(w, "NODE\t%s\n", node) fmt.Fprintf(w, "NODE\t%s\n", node)
svc := serviceInfoWrapper{s} svc := serviceInfoWrapper{s.Service}
fmt.Fprintf(w, "ID\t%s\n", svc.Id) fmt.Fprintf(w, "ID\t%s\n", svc.Id)
fmt.Fprintf(w, "STATE\t%s\n", svc.State) fmt.Fprintf(w, "STATE\t%s\n", svc.State)
fmt.Fprintf(w, "HEALTH\t%s\n", svc.HealthStatus()) fmt.Fprintf(w, "HEALTH\t%s\n", svc.HealthStatus())
@ -131,9 +135,6 @@ func serviceInfo(c *client.Client, id string) {
label = "" label = ""
} }
} }
}
}
}
if len(services) == 0 { if len(services) == 0 {
helpers.Fatalf("service %q is not registered on any nodes", id) helpers.Fatalf("service %q is not registered on any nodes", id)
@ -143,16 +144,20 @@ func serviceInfo(c *client.Client, id string) {
} }
func serviceStart(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 { if err != nil {
helpers.Fatalf("error starting service: %s", err) helpers.Fatalf("error starting service: %s", err)
} }
defaultNode := addrFromPeer(&remotePeer)
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
fmt.Fprintln(w, "NODE\tRESPONSE") fmt.Fprintln(w, "NODE\tRESPONSE")
for _, resp := range reply.Response { for _, resp := range reply.Response {
node := "" node := defaultNode
if resp.Metadata != nil { if resp.Metadata != nil {
node = resp.Metadata.Hostname node = resp.Metadata.Hostname
@ -165,16 +170,20 @@ func serviceStart(c *client.Client, id string) {
} }
func serviceStop(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 { if err != nil {
helpers.Fatalf("error starting service: %s", err) helpers.Fatalf("error starting service: %s", err)
} }
defaultNode := addrFromPeer(&remotePeer)
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
fmt.Fprintln(w, "NODE\tRESPONSE") fmt.Fprintln(w, "NODE\tRESPONSE")
for _, resp := range reply.Response { for _, resp := range reply.Response {
node := "" node := defaultNode
if resp.Metadata != nil { if resp.Metadata != nil {
node = resp.Metadata.Hostname node = resp.Metadata.Hostname
@ -187,16 +196,20 @@ func serviceStop(c *client.Client, id string) {
} }
func serviceRestart(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 { if err != nil {
helpers.Fatalf("error starting service: %s", err) helpers.Fatalf("error starting service: %s", err)
} }
defaultNode := addrFromPeer(&remotePeer)
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
fmt.Fprintln(w, "NODE\tRESPONSE") fmt.Fprintln(w, "NODE\tRESPONSE")
for _, resp := range reply.Response { for _, resp := range reply.Response {
node := "" node := defaultNode
if resp.Metadata != nil { if resp.Metadata != nil {
node = resp.Metadata.Hostname node = resp.Metadata.Hostname

View File

@ -14,7 +14,9 @@ import (
criconstants "github.com/containerd/cri/pkg/constants" criconstants "github.com/containerd/cri/pkg/constants"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"google.golang.org/grpc"
"google.golang.org/grpc/metadata" "google.golang.org/grpc/metadata"
"google.golang.org/grpc/peer"
"github.com/talos-systems/talos/api/common" "github.com/talos-systems/talos/api/common"
osapi "github.com/talos-systems/talos/api/os" osapi "github.com/talos-systems/talos/api/os"
@ -47,21 +49,26 @@ var statsCmd = &cobra.Command{
} }
md := metadata.New(make(map[string]string)) md := metadata.New(make(map[string]string))
md.Set("targets", target...) 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 { if err != nil {
helpers.Fatalf("error getting stats: %s", err) 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) w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
fmt.Fprintln(w, "NODE\tNAMESPACE\tID\tMEMORY(MB)\tCPU") fmt.Fprintln(w, "NODE\tNAMESPACE\tID\tMEMORY(MB)\tCPU")
defaultNode := addrFromPeer(remotePeer)
for _, rep := range reply.Response { for _, rep := range reply.Response {
resp := rep resp := rep
sort.Slice(resp.Stats, sort.Slice(resp.Stats,
@ -76,7 +83,7 @@ func statsRender(reply *osapi.StatsReply) {
display = "└─ " + display display = "└─ " + display
} }
node := "" node := defaultNode
if resp.Metadata != nil { if resp.Metadata != nil {
node = resp.Metadata.Hostname node = resp.Metadata.Hostname

View File

@ -12,6 +12,8 @@ import (
"github.com/golang/protobuf/ptypes" "github.com/golang/protobuf/ptypes"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"google.golang.org/grpc"
"google.golang.org/grpc/peer"
timeapi "github.com/talos-systems/talos/api/time" timeapi "github.com/talos-systems/talos/api/time"
"github.com/talos-systems/talos/cmd/osctl/pkg/client" "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) helpers.Fatalf("failed to parse check flag: %w", err)
} }
var reply *timeapi.TimeReply var (
reply *timeapi.TimeReply
remotePeer peer.Peer
)
if server == "" { if server == "" {
reply, err = c.Time(globalCtx) reply, err = c.Time(globalCtx, grpc.Peer(&remotePeer))
if err != nil { if err != nil {
helpers.Fatalf("error fetching time: %s", err) helpers.Fatalf("error fetching time: %s", err)
} }
} else { } else {
reply, err = c.TimeCheck(globalCtx, server) reply, err = c.TimeCheck(globalCtx, server, grpc.Peer(&remotePeer))
if err != nil { if err != nil {
helpers.Fatalf("error fetching time: %s", err) helpers.Fatalf("error fetching time: %s", err)
} }
@ -46,9 +52,11 @@ var timeCmd = &cobra.Command{
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
fmt.Fprintln(w, "NODE\tNTP-SERVER\tLOCAL-TIME\tREMOTE-TIME") fmt.Fprintln(w, "NODE\tNTP-SERVER\tLOCAL-TIME\tREMOTE-TIME")
defaultNode := addrFromPeer(&remotePeer)
var localtime, remotetime time.Time var localtime, remotetime time.Time
for _, resp := range reply.Response { for _, resp := range reply.Response {
node := "" node := defaultNode
if resp.Metadata != nil { if resp.Metadata != nil {
node = resp.Metadata.Hostname node = resp.Metadata.Hostname

View File

@ -12,6 +12,8 @@ import (
"time" "time"
"github.com/spf13/cobra" "github.com/spf13/cobra"
"google.golang.org/grpc"
"google.golang.org/grpc/peer"
machineapi "github.com/talos-systems/talos/api/machine" machineapi "github.com/talos-systems/talos/api/machine"
"github.com/talos-systems/talos/cmd/osctl/pkg/client" "github.com/talos-systems/talos/cmd/osctl/pkg/client"
@ -39,12 +41,13 @@ func upgrade() {
var ( var (
err error err error
reply *machineapi.UpgradeReply reply *machineapi.UpgradeReply
remotePeer peer.Peer
) )
setupClient(func(c *client.Client) { setupClient(func(c *client.Client) {
// TODO: See if we can validate version and prevent starting upgrades to // TODO: See if we can validate version and prevent starting upgrades to
// an unknown version // an unknown version
reply, err = c.Upgrade(globalCtx, upgradeImage) reply, err = c.Upgrade(globalCtx, upgradeImage, grpc.Peer(&remotePeer))
}) })
if err != nil { if err != nil {
@ -54,8 +57,10 @@ func upgrade() {
w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0)
fmt.Fprintln(w, "NODE\tACK\tSTARTED") fmt.Fprintln(w, "NODE\tACK\tSTARTED")
defaultNode := addrFromPeer(&remotePeer)
for _, resp := range reply.Response { for _, resp := range reply.Response {
node := "" node := defaultNode
if resp.Metadata != nil { if resp.Metadata != nil {
node = resp.Metadata.Hostname node = resp.Metadata.Hostname

View File

@ -9,6 +9,8 @@ import (
"os" "os"
"github.com/spf13/cobra" "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/client"
"github.com/talos-systems/talos/cmd/osctl/pkg/helpers" "github.com/talos-systems/talos/cmd/osctl/pkg/helpers"
@ -45,15 +47,24 @@ var versionCmd = &cobra.Command{
fmt.Println("Server:") fmt.Println("Server:")
setupClient(func(c *client.Client) { 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 { if err != nil {
helpers.Fatalf("error getting version: %s", err) helpers.Fatalf("error getting version: %s", err)
} }
defaultNode := addrFromPeer(&remotePeer)
for _, resp := range reply.Response { for _, resp := range reply.Response {
node := defaultNode
if resp.Metadata != nil { 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) version.PrintLongVersionFromExisting(resp.Version)
} }
}) })

View File

@ -207,27 +207,34 @@ func (c *Client) Kubeconfig(ctx context.Context) ([]byte, error) {
} }
// Stats implements the proto.OSClient interface. // Stats implements the proto.OSClient interface.
func (c *Client) Stats(ctx context.Context, namespace string, driver common.ContainerDriver) (reply *osapi.StatsReply, err error) { 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{ reply, err = c.client.Stats(
ctx, &osapi.StatsRequest{
Namespace: namespace, Namespace: namespace,
Driver: driver, Driver: driver,
}) },
callOptions...,
)
return return
} }
// Containers implements the proto.OSClient interface. // Containers implements the proto.OSClient interface.
func (c *Client) Containers(ctx context.Context, namespace string, driver common.ContainerDriver) (reply *osapi.ContainersReply, err error) { 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{ reply, err = c.client.Containers(
ctx,
&osapi.ContainersRequest{
Namespace: namespace, Namespace: namespace,
Driver: driver, Driver: driver,
}) },
callOptions...,
)
return return
} }
// Restart implements the proto.OSClient interface. // 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{ _, err = c.client.Restart(ctx, &osapi.RestartRequest{
Id: id, Id: id,
Namespace: namespace, Namespace: namespace,
@ -272,33 +279,69 @@ func (c *Client) Logs(ctx context.Context, namespace string, driver common.Conta
} }
// Version implements the proto.OSClient interface. // Version implements the proto.OSClient interface.
func (c *Client) Version(ctx context.Context) (*machineapi.VersionReply, error) { func (c *Client) Version(ctx context.Context, callOptions ...grpc.CallOption) (reply *machineapi.VersionReply, err error) {
return c.MachineClient.Version(ctx, &empty.Empty{}) reply, err = c.MachineClient.Version(
ctx,
&empty.Empty{},
callOptions...,
)
return
} }
// Routes implements the networkdproto.NetworkClient interface. // Routes implements the networkdproto.NetworkClient interface.
func (c *Client) Routes(ctx context.Context) (*networkapi.RoutesReply, error) { func (c *Client) Routes(ctx context.Context, callOptions ...grpc.CallOption) (reply *networkapi.RoutesReply, err error) {
return c.NetworkClient.Routes(ctx, &empty.Empty{}) reply, err = c.NetworkClient.Routes(
ctx,
&empty.Empty{},
callOptions...,
)
return
} }
// Interfaces implements the proto.OSClient interface. // Interfaces implements the proto.OSClient interface.
func (c *Client) Interfaces(ctx context.Context) (*networkapi.InterfacesReply, error) { func (c *Client) Interfaces(ctx context.Context, callOptions ...grpc.CallOption) (reply *networkapi.InterfacesReply, err error) {
return c.NetworkClient.Interfaces(ctx, &empty.Empty{}) reply, err = c.NetworkClient.Interfaces(
ctx,
&empty.Empty{},
callOptions...,
)
return
} }
// Processes implements the proto.OSClient interface. // Processes implements the proto.OSClient interface.
func (c *Client) Processes(ctx context.Context) (reply *osapi.ProcessesReply, err error) { func (c *Client) Processes(ctx context.Context, callOptions ...grpc.CallOption) (reply *osapi.ProcessesReply, err error) {
return c.client.Processes(ctx, &empty.Empty{}) reply, err = c.client.Processes(
ctx,
&empty.Empty{},
callOptions...,
)
return
} }
// Memory implements the proto.OSClient interface. // Memory implements the proto.OSClient interface.
func (c *Client) Memory(ctx context.Context) (*osapi.MemInfoReply, error) { func (c *Client) Memory(ctx context.Context, callOptions ...grpc.CallOption) (reply *osapi.MemInfoReply, err error) {
return c.client.Memory(ctx, &empty.Empty{}) reply, err = c.client.Memory(
ctx,
&empty.Empty{},
callOptions...,
)
return
} }
// Mounts implements the proto.OSClient interface. // Mounts implements the proto.OSClient interface.
func (c *Client) Mounts(ctx context.Context) (*machineapi.MountsReply, error) { func (c *Client) Mounts(ctx context.Context, callOptions ...grpc.CallOption) (reply *machineapi.MountsReply, err error) {
return c.MachineClient.Mounts(ctx, &empty.Empty{}) reply, err = c.MachineClient.Mounts(
ctx,
&empty.Empty{},
callOptions...,
)
return
} }
// LS implements the proto.OSClient interface. // 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 // Upgrade initiates a Talos upgrade ... and implements the proto.OSClient
// interface // interface
func (c *Client) Upgrade(ctx context.Context, image string) (*machineapi.UpgradeReply, error) { func (c *Client) Upgrade(ctx context.Context, image string, callOptions ...grpc.CallOption) (reply *machineapi.UpgradeReply, err error) {
return c.MachineClient.Upgrade(ctx, &machineapi.UpgradeRequest{Image: image}) reply, err = c.MachineClient.Upgrade(
ctx,
&machineapi.UpgradeRequest{Image: image},
callOptions...,
)
return
} }
// ServiceList returns list of services with their state // ServiceList returns list of services with their state
func (c *Client) ServiceList(ctx context.Context) (*machineapi.ServiceListReply, error) { func (c *Client) ServiceList(ctx context.Context, callOptions ...grpc.CallOption) (reply *machineapi.ServiceListReply, err error) {
return c.MachineClient.ServiceList(ctx, &empty.Empty{}) 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 // ServiceInfo returns info about a single service
// //
// This is implemented via service list API, as we don't have many services // 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 // If service with given id is not registered, function returns nil
func (c *Client) ServiceInfo(ctx context.Context, id string) (*machineapi.ServiceListReply, error) { func (c *Client) ServiceInfo(ctx context.Context, id string, callOptions ...grpc.CallOption) (services []ServiceInfo, err error) {
return c.MachineClient.ServiceList(ctx, &empty.Empty{}) 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. // ServiceStart starts a service.
func (c *Client) ServiceStart(ctx context.Context, id string) (*machineapi.ServiceStartReply, error) { func (c *Client) ServiceStart(ctx context.Context, id string, callOptions ...grpc.CallOption) (reply *machineapi.ServiceStartReply, err error) {
return c.MachineClient.ServiceStart(ctx, &machineapi.ServiceStartRequest{Id: id}) reply, err = c.MachineClient.ServiceStart(
ctx,
&machineapi.ServiceStartRequest{Id: id},
callOptions...,
)
return
} }
// ServiceStop stops a service. // ServiceStop stops a service.
func (c *Client) ServiceStop(ctx context.Context, id string) (*machineapi.ServiceStopReply, error) { func (c *Client) ServiceStop(ctx context.Context, id string, callOptions ...grpc.CallOption) (reply *machineapi.ServiceStopReply, err error) {
return c.MachineClient.ServiceStop(ctx, &machineapi.ServiceStopRequest{Id: id}) reply, err = c.MachineClient.ServiceStop(
ctx,
&machineapi.ServiceStopRequest{Id: id},
callOptions...,
)
return
} }
// ServiceRestart restarts a service. // ServiceRestart restarts a service.
func (c *Client) ServiceRestart(ctx context.Context, id string) (*machineapi.ServiceRestartReply, error) { func (c *Client) ServiceRestart(ctx context.Context, id string, callOptions ...grpc.CallOption) (reply *machineapi.ServiceRestartReply, err error) {
return c.MachineClient.ServiceRestart(ctx, &machineapi.ServiceRestartRequest{Id: id}) reply, err = c.MachineClient.ServiceRestart(
ctx,
&machineapi.ServiceRestartRequest{Id: id},
callOptions...,
)
return
} }
// Time returns the time // Time returns the time
func (c *Client) Time(ctx context.Context) (*timeapi.TimeReply, error) { func (c *Client) Time(ctx context.Context, callOptions ...grpc.CallOption) (reply *timeapi.TimeReply, err error) {
return c.TimeClient.Time(ctx, &empty.Empty{}) reply, err = c.TimeClient.Time(
ctx,
&empty.Empty{},
callOptions...,
)
return
} }
// TimeCheck returns the time compared to the specified ntp server // TimeCheck returns the time compared to the specified ntp server
func (c *Client) TimeCheck(ctx context.Context, server string) (*timeapi.TimeReply, error) { func (c *Client) TimeCheck(ctx context.Context, server string, callOptions ...grpc.CallOption) (reply *timeapi.TimeReply, err error) {
return c.TimeClient.TimeCheck(ctx, &timeapi.TimeRequest{Server: server}) reply, err = c.TimeClient.TimeCheck(
ctx,
&timeapi.TimeRequest{Server: server},
callOptions...,
)
return
} }
// Read reads a file. // Read reads a file.