2023-08-11 13:12:13 +00:00
|
|
|
// Copyright (c) HashiCorp, Inc.
|
|
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
|
2023-07-17 22:15:22 +00:00
|
|
|
package sprawl
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"fmt"
|
|
|
|
"sort"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"text/tabwriter"
|
|
|
|
)
|
|
|
|
|
|
|
|
// PrintDetails will dump relevant addressing and naming data to the logger for
|
|
|
|
// human interaction purposes.
|
|
|
|
func (s *Sprawl) PrintDetails() error {
|
|
|
|
det := logDetails{
|
|
|
|
TopologyID: s.topology.ID,
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, cluster := range s.topology.Clusters {
|
|
|
|
client := s.clients[cluster.Name]
|
|
|
|
|
|
|
|
cfg, err := client.Operator().RaftGetConfiguration(nil)
|
|
|
|
if err != nil {
|
|
|
|
return fmt.Errorf("could not get raft config for cluster %q: %w", cluster.Name, err)
|
|
|
|
}
|
|
|
|
|
|
|
|
var leaderNode string
|
|
|
|
for _, svr := range cfg.Servers {
|
|
|
|
if svr.Leader {
|
|
|
|
leaderNode = strings.TrimSuffix(svr.Node, "-pod")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
cd := clusterDetails{
|
|
|
|
Name: cluster.Name,
|
|
|
|
Leader: leaderNode,
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, node := range cluster.Nodes {
|
|
|
|
if node.Disabled {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
var addrs []string
|
|
|
|
for _, addr := range node.Addresses {
|
|
|
|
addrs = append(addrs, addr.Network+"="+addr.IPAddress)
|
|
|
|
}
|
|
|
|
sort.Strings(addrs)
|
|
|
|
|
|
|
|
if node.IsServer() {
|
|
|
|
cd.Apps = append(cd.Apps, appDetail{
|
|
|
|
Type: "server",
|
|
|
|
Container: node.DockerName(),
|
|
|
|
Addresses: addrs,
|
|
|
|
ExposedPort: node.ExposedPort(8500),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2023-11-10 19:40:55 +00:00
|
|
|
for _, wrk := range node.Workloads {
|
|
|
|
if wrk.IsMeshGateway {
|
2023-07-17 22:15:22 +00:00
|
|
|
cd.Apps = append(cd.Apps, appDetail{
|
|
|
|
Type: "mesh-gateway",
|
|
|
|
Container: node.DockerName(),
|
2023-11-10 19:40:55 +00:00
|
|
|
ExposedPort: node.ExposedPort(wrk.Port),
|
|
|
|
ExposedEnvoyAdminPort: node.ExposedPort(wrk.EnvoyAdminPort),
|
2023-07-17 22:15:22 +00:00
|
|
|
Addresses: addrs,
|
2023-11-10 19:40:55 +00:00
|
|
|
Service: wrk.ID.String(),
|
2023-07-17 22:15:22 +00:00
|
|
|
})
|
|
|
|
} else {
|
2023-11-02 19:53:41 +00:00
|
|
|
ports := make(map[string]int)
|
2023-11-10 19:40:55 +00:00
|
|
|
for name, port := range wrk.Ports {
|
2023-11-02 21:49:15 +00:00
|
|
|
ports[name] = node.ExposedPort(port.Number)
|
2023-11-02 19:53:41 +00:00
|
|
|
}
|
2023-07-17 22:15:22 +00:00
|
|
|
cd.Apps = append(cd.Apps, appDetail{
|
|
|
|
Type: "app",
|
|
|
|
Container: node.DockerName(),
|
2023-11-10 19:40:55 +00:00
|
|
|
ExposedPort: node.ExposedPort(wrk.Port),
|
2023-11-02 19:53:41 +00:00
|
|
|
ExposedPorts: ports,
|
2023-11-10 19:40:55 +00:00
|
|
|
ExposedEnvoyAdminPort: node.ExposedPort(wrk.EnvoyAdminPort),
|
2023-07-17 22:15:22 +00:00
|
|
|
Addresses: addrs,
|
2023-11-10 19:40:55 +00:00
|
|
|
Service: wrk.ID.String(),
|
2023-07-17 22:15:22 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
det.Clusters = append(det.Clusters, cd)
|
|
|
|
}
|
|
|
|
|
|
|
|
var buf bytes.Buffer
|
|
|
|
w := tabwriter.NewWriter(&buf, 0, 0, 3, ' ', tabwriter.Debug)
|
|
|
|
|
|
|
|
score := map[string]int{
|
|
|
|
"server": 0,
|
|
|
|
"mesh-gateway": 1,
|
|
|
|
"app": 2,
|
|
|
|
}
|
|
|
|
|
|
|
|
for _, cluster := range det.Clusters {
|
|
|
|
fmt.Fprintf(w, "CLUSTER\tTYPE\tCONTAINER\tNAME\tADDRS\tPORTS\t\n")
|
|
|
|
sort.Slice(cluster.Apps, func(i, j int) bool {
|
|
|
|
a := cluster.Apps[i]
|
|
|
|
b := cluster.Apps[j]
|
|
|
|
|
|
|
|
asc := score[a.Type]
|
|
|
|
bsc := score[b.Type]
|
|
|
|
|
|
|
|
if asc < bsc {
|
|
|
|
return true
|
|
|
|
} else if asc > bsc {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
|
|
|
if a.Container < b.Container {
|
|
|
|
return true
|
|
|
|
} else if a.Container > b.Container {
|
|
|
|
return false
|
|
|
|
}
|
|
|
|
|
2023-11-02 19:53:41 +00:00
|
|
|
return a.Service < b.Service
|
2023-07-17 22:15:22 +00:00
|
|
|
})
|
|
|
|
for _, d := range cluster.Apps {
|
|
|
|
if d.Type == "server" && d.Container == cluster.Leader {
|
|
|
|
d.Type = "leader"
|
|
|
|
}
|
2023-11-02 19:53:41 +00:00
|
|
|
var portStr string
|
|
|
|
if len(d.ExposedPorts) > 0 {
|
|
|
|
var out []string
|
|
|
|
for name, exposed := range d.ExposedPorts {
|
|
|
|
out = append(out, fmt.Sprintf("app:%s=%d", name, exposed))
|
|
|
|
}
|
|
|
|
sort.Strings(out)
|
|
|
|
portStr = strings.Join(out, " ")
|
|
|
|
} else {
|
|
|
|
portStr = "app=" + strconv.Itoa(d.ExposedPort)
|
|
|
|
}
|
2023-07-17 22:15:22 +00:00
|
|
|
if d.ExposedEnvoyAdminPort > 0 {
|
|
|
|
portStr += " envoy=" + strconv.Itoa(d.ExposedEnvoyAdminPort)
|
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "%s\t%s\t%s\t%s\t%s\t%s\t\n",
|
|
|
|
cluster.Name,
|
|
|
|
d.Type,
|
|
|
|
d.Container,
|
|
|
|
d.Service,
|
|
|
|
strings.Join(d.Addresses, ", "),
|
|
|
|
portStr,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
fmt.Fprintf(w, "\t\t\t\t\t\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
w.Flush()
|
|
|
|
|
2023-10-03 15:06:50 +00:00
|
|
|
s.logger.Debug("CURRENT SPRAWL DETAILS", "details", buf.String())
|
2023-07-17 22:15:22 +00:00
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type logDetails struct {
|
|
|
|
TopologyID string
|
|
|
|
Clusters []clusterDetails
|
|
|
|
}
|
|
|
|
|
|
|
|
type clusterDetails struct {
|
|
|
|
Name string
|
|
|
|
|
|
|
|
Leader string
|
|
|
|
Apps []appDetail
|
|
|
|
}
|
|
|
|
|
|
|
|
type appDetail struct {
|
|
|
|
Type string // server|mesh-gateway|app
|
|
|
|
Container string
|
|
|
|
Addresses []string
|
2023-11-02 19:53:41 +00:00
|
|
|
ExposedPort int `json:",omitempty"`
|
|
|
|
ExposedPorts map[string]int `json:",omitempty"`
|
|
|
|
ExposedEnvoyAdminPort int `json:",omitempty"`
|
2023-07-17 22:15:22 +00:00
|
|
|
// just services
|
|
|
|
Service string `json:",omitempty"`
|
|
|
|
}
|