Files
Raj Chaudhuri c1d3107418 Changed module name, edited default manifests in prog/weave-kube
* Module name changed to `github.com/rajch/weave`
* Added environment variable `CHECKPOINT_DISABLE=1` to default manifests, to bypass weaveworks telemetry
2024-03-01 18:05:19 +05:30

377 lines
11 KiB
Go

package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"strings"
"text/template"
"time"
"github.com/gorilla/mux"
"github.com/weaveworks/mesh"
"github.com/rajch/weave/common"
"github.com/rajch/weave/ipam"
"github.com/rajch/weave/nameserver"
"github.com/rajch/weave/net/address"
"github.com/rajch/weave/plugin"
"github.com/rajch/weave/proxy"
weave "github.com/rajch/weave/router"
"github.com/weaveworks/go-checkpoint"
)
var allConnectionStates = []string{"established", "pending", "retrying", "failed", "connecting"}
var rootTemplate = template.New("root").Funcs(map[string]interface{}{
"countDNSEntries": countDNSEntries,
"printList": func(list []string) string {
if len(list) == 0 {
return "none"
}
return strings.Join(list, ", ")
},
"printIPAMRanges": func(router weave.NetworkRouterStatus, status ipam.Status) string {
var buffer bytes.Buffer
peerStats := summariseIpamStats(&status)
printOwned := func(name string, nickName string, info string, ips uint32) {
percentageRanges := float32(ips) * 100.0 / float32(status.RangeNumIPs)
displayName := name + "(" + nickName + ")"
fmt.Fprintf(&buffer, "%-37v %8d IPs (%04.1f%% of total) %s\n",
displayName, ips, percentageRanges, info)
}
// print the local info first
if ourStats := peerStats[router.Name]; ourStats != nil {
activeStr := fmt.Sprintf("(%d active)", status.ActiveIPs)
printOwned(router.Name, ourStats.nickname, activeStr, ourStats.ips)
}
// and then the rest
for peer, stats := range peerStats {
if peer != router.Name {
reachableStr := ""
if !stats.reachable {
reachableStr = "- unreachable!"
}
printOwned(peer, stats.nickname, reachableStr, stats.ips)
}
}
return buffer.String()
},
"allIPAMOwnersUnreachable": func(status ipam.Status) bool {
for _, entry := range status.Entries {
if entry.Size > 0 && entry.IsKnownPeer {
return false
}
}
return true
},
"printConnectionCounts": func(conns []mesh.LocalConnectionStatus) string {
counts := make(map[string]int)
for _, conn := range conns {
counts[conn.State]++
}
return printCounts(counts, allConnectionStates)
},
"printPeerConnectionCounts": func(peers []mesh.PeerStatus) string {
counts := make(map[string]int)
for _, peer := range peers {
for _, conn := range peer.Connections {
if conn.Established {
counts["established"]++
} else {
counts["pending"]++
}
}
}
return printCounts(counts, []string{"established", "pending"})
},
"printState": func(enabled bool) string {
if enabled {
return "enabled"
}
return "disabled"
},
"trimSuffix": strings.TrimSuffix,
})
type ipamStats struct {
ips uint32
nickname string
reachable bool
}
func summariseIpamStats(status *ipam.Status) map[string]*ipamStats {
peerStats := make(map[string]*ipamStats)
for _, entry := range status.Entries {
s, found := peerStats[entry.Peer]
if !found {
s = &ipamStats{nickname: entry.Nickname, reachable: entry.IsKnownPeer}
peerStats[entry.Peer] = s
}
s.ips += entry.Size
}
return peerStats
}
func countDNSEntries(entries []nameserver.EntryStatus) int {
count := 0
for _, entry := range entries {
if entry.Tombstone == 0 {
count++
}
}
return count
}
func countDNSEntriesForPeer(peername string, entries []nameserver.EntryStatus) int {
count := 0
for _, entry := range entries {
if entry.Tombstone == 0 && entry.Origin == peername {
count++
}
}
return count
}
// Print counts in a specified order
func printCounts(counts map[string]int, keys []string) string {
var stringCounts []string
for _, key := range keys {
if count, ok := counts[key]; ok {
stringCounts = append(stringCounts, fmt.Sprintf("%d %s", count, key))
}
}
return strings.Join(stringCounts, ", ")
}
// Strip escaped newlines from template
func escape(template string) string {
return strings.Replace(template, "\\\n", "", -1)
}
// Define a named template panicking on error
func defTemplate(name string, text string) *template.Template {
return template.Must(rootTemplate.New(name).Parse(escape(text)))
}
var statusTemplate = defTemplate("status", `\
Version: {{.Version}} ({{.VersionCheck}})
Service: router
Protocol: {{.Router.Protocol}} \
{{if eq .Router.ProtocolMinVersion .Router.ProtocolMaxVersion}}\
{{.Router.ProtocolMaxVersion}}\
{{else}}\
{{.Router.ProtocolMinVersion}}..{{.Router.ProtocolMaxVersion}}\
{{end}}
Name: {{.Router.Name}}({{.Router.NickName}})
Encryption: {{printState .Router.Encryption}}
PeerDiscovery: {{printState .Router.PeerDiscovery}}
Targets: {{len .Router.Targets}}
Connections: {{len .Router.Connections}}{{with printConnectionCounts .Router.Connections}} ({{.}}){{end}}
Peers: {{len .Router.Peers}}{{with printPeerConnectionCounts .Router.Peers}} (with {{.}} connections){{end}}
TrustedSubnets: {{printList .Router.TrustedSubnets}}
{{if .IPAM}}\
Service: ipam
{{if .IPAM.Entries}}\
{{if allIPAMOwnersUnreachable .IPAM}}\
Status: all IP ranges owned by unreachable peers - use 'rmpeer' if they are dead
{{else if len .IPAM.PendingAllocates}}\
Status: waiting for IP(s) to become available
{{else}}\
Status: ready
{{end}}\
{{else if .IPAM.Paxos}}\
{{if .IPAM.Paxos.Elector}}\
Status: awaiting consensus (quorum: {{.IPAM.Paxos.Quorum}}, known: {{.IPAM.Paxos.KnownNodes}})
{{else}}\
Status: priming
{{end}}\
{{else}}\
Status: idle
{{end}}\
Range: {{.IPAM.Range}}
DefaultSubnet: {{.IPAM.DefaultSubnet}}
{{end}}\
{{if .DNS}}\
Service: dns
Domain: {{.DNS.Domain}}
Upstream: {{printList .DNS.Upstream}}
TTL: {{.DNS.TTL}}
Entries: {{countDNSEntries .DNS.Entries}}
{{end}}\
{{if .Proxy}}\
Service: proxy
Address: {{printList .Proxy.Addresses}}
{{end}}\
{{if .Plugin}}\
{{if .Plugin.Config.EnableV2}}\
Service: plugin (v2)
{{else}}\
Service: plugin (legacy)
DriverName: {{.Plugin.DriverName}}
{{end}}\
{{end}}\
`)
var targetsTemplate = defTemplate("targetsTemplate", `\
{{range .Router.Targets}}{{.}}
{{end}}\
`)
var connectionsTemplate = defTemplate("connectionsTemplate", `\
{{range .Router.Connections}}\
{{if .Outbound}}->{{else}}<-{{end}} {{printf "%-21v" .Address}} {{printf "%-11v" .State}} {{.Info}} {{range $key,$element := .Attrs}}{{if ne $key "name"}}{{$key}}={{$element}}{{end}}{{end}}
{{end}}\
`)
var peersTemplate = defTemplate("peers", `\
{{range .Router.Peers}}\
{{.Name}}({{.NickName}})
{{range .Connections}}\
{{if .Outbound}}->{{else}}<-{{end}} {{printf "%-21v" .Address}} \
{{$nameNickName := printf "%v(%v)" .Name .NickName}}{{printf "%-37v" $nameNickName}} \
{{if .Established}}established{{else}}pending{{end}}
{{end}}\
{{end}}\
`)
var dnsEntriesTemplate = defTemplate("dnsEntries", `\
{{$domain := printf ".%v" .DNS.Domain}}\
{{range .DNS.Entries}}\
{{if eq .Tombstone 0}}\
{{$hostname := trimSuffix .Hostname $domain}}\
{{printf "%-12v" $hostname}} {{printf "%-15v" .Address}} {{printf "%12.12v" .ContainerID}} {{.Origin}}
{{end}}\
{{end}}\
`)
var ipamTemplate = defTemplate("ipamTemplate", `{{printIPAMRanges .Router .IPAM}}`)
type VersionCheck struct {
Enabled bool
Success bool
NewVersion string
NextCheckAt time.Time
}
func versionCheck() *VersionCheck {
v := &VersionCheck{}
if checkpoint.IsCheckDisabled() {
return v
}
v.Enabled = true
v.Success = success.Load().(bool)
v.NewVersion = newVersion.Load().(string)
v.NextCheckAt = checker.NextCheckAt()
return v
}
func (v *VersionCheck) String() string {
switch {
case !v.Enabled:
return "version check update disabled"
case !v.Success:
return fmt.Sprintf("failed to check latest version - see logs; next check at %s", v.NextCheckAt.Format("2006/01/02 15:04:05"))
case v.NewVersion != "":
return fmt.Sprintf("version %s available - please upgrade!", v.NewVersion)
default:
return fmt.Sprintf("up to date; next check at %s", v.NextCheckAt.Format("2006/01/02 15:04:05"))
}
}
type WeaveStatus struct {
Ready bool
Version string
VersionCheck *VersionCheck `json:"VersionCheck,omitempty"`
Router *weave.NetworkRouterStatus `json:"Router,omitempty"`
IPAM *ipam.Status `json:"IPAM,omitempty"`
DNS *nameserver.Status `json:"DNS,omitempty"`
Proxy *proxy.Status `json:"Proxy,omitempty"`
Plugin *plugin.Status `json:"Plugin,omitempty"`
}
// Read-only functions, suitable for exposing on an unprotected socket
func HandleHTTP(muxRouter *mux.Router, version string, router *weave.NetworkRouter, allocator *ipam.Allocator, defaultSubnet address.CIDR, ns *nameserver.Nameserver, dnsserver *nameserver.DNSServer, prxy *proxy.Proxy, plugin *plugin.Plugin, waitReady *common.WaitGroup) {
status := func() WeaveStatus {
return WeaveStatus{
waitReady.IsDone(),
version,
versionCheck(),
weave.NewNetworkRouterStatus(router),
ipam.NewStatus(allocator, defaultSubnet),
nameserver.NewStatus(ns, dnsserver),
proxy.NewStatus(prxy),
plugin.NewStatus(),
}
}
muxRouter.Methods("GET").Path("/report").Headers("Accept", "application/json").HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
json, err := json.MarshalIndent(status(), "", " ")
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
Log.Error("Error during report marshalling: ", err)
return
}
w.Header().Set("Content-Type", "application/json")
w.Write(json)
})
muxRouter.Methods("GET").Path("/report").HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
funcs := template.FuncMap{
"json": func(v interface{}) string {
a, _ := json.Marshal(v)
return string(a)
},
}
format, ok := r.URL.Query()["format"]
if !ok || len(format) != 1 {
http.Error(w, "error: must supply one format parameter", http.StatusBadRequest)
return
}
formatTemplate, err := template.New("format").Funcs(funcs).Parse(format[0])
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
if err := formatTemplate.Execute(w, status()); err != nil {
http.Error(w, "error during template execution", http.StatusInternalServerError)
Log.Error(err)
}
})
defHandler := func(path string, template *template.Template) {
muxRouter.Methods("GET").Path(path).HandlerFunc(
func(w http.ResponseWriter, r *http.Request) {
s := status()
if !s.Ready {
w.WriteHeader(http.StatusServiceUnavailable)
}
if err := template.Execute(w, s); err != nil {
http.Error(w, "error during template execution", http.StatusInternalServerError)
Log.Error(err)
}
})
}
defHandler("/status", statusTemplate)
defHandler("/status/targets", targetsTemplate)
defHandler("/status/connections", connectionsTemplate)
defHandler("/status/peers", peersTemplate)
defHandler("/status/dns", dnsEntriesTemplate)
defHandler("/status/ipam", ipamTemplate)
}