Files
weave-scope/report/backcompat.go
Bryan Boreham c03aeb5d43 Move Counters into Latest
The only place Counters are used is in rendering, for the number of
nodes under a topology, so the overhead of holding a unique data
structure in every Node is unwarranted.

Counters are not set in the probe, so we don't need any
backwards-compatibility in report decoding. Similarly they are not set
until after all nodes are merged, so we don't need that logic.
2020-03-10 12:30:05 +00:00

159 lines
4.6 KiB
Go

package report
// Backwards-compatibility: code to read older reports and convert
import (
"strings"
"time"
"github.com/ugorji/go/codec"
)
// For backwards-compatibility with probes that sent a map of latestControls data
type bcNode struct {
Node
LatestControls map[string]nodeControlDataLatestEntry `json:"latestControls,omitempty"`
Counters map[string]int `json:"counters,omitempty"`
}
type nodeControlDataLatestEntry struct {
Timestamp time.Time `json:"timestamp"`
Value nodeControlData `json:"value"`
}
type nodeControlData struct {
Dead bool `json:"dead"`
}
// CodecDecodeSelf implements codec.Selfer
func (n *Node) CodecDecodeSelf(decoder *codec.Decoder) {
var in bcNode
decoder.Decode(&in)
*n = in.Node
if len(in.LatestControls) > 0 {
// Convert the map into a delimited string
cs := make([]string, 0, len(in.LatestControls))
var ts time.Time
for name, v := range in.LatestControls {
if !v.Value.Dead {
cs = append(cs, name)
// Pull out the newest timestamp to use for the whole set
if ts.Before(v.Timestamp) {
ts = v.Timestamp
}
}
}
n.Latest = n.Latest.Set(NodeActiveControls, ts, strings.Join(cs, ScopeDelim))
}
// Counters were not generated in the Scope probe, but decode them here in case a plugin used them.
for k, v := range in.Counters {
*n = n.AddCounter(k, v)
}
}
type _Node Node // just so we don't recurse inside CodecEncodeSelf
// CodecEncodeSelf implements codec.Selfer
func (n *Node) CodecEncodeSelf(encoder *codec.Encoder) {
encoder.Encode((*_Node)(n))
}
// Upgrade returns a new report based on a report received from the old probe.
//
func (r Report) Upgrade() Report {
return r.upgradePodNodes().upgradeNamespaces().upgradeDNSRecords()
}
func (r Report) upgradePodNodes() Report {
// At the same time the probe stopped reporting replicasets,
// it also started reporting deployments as pods' parents
if len(r.ReplicaSet.Nodes) == 0 {
return r
}
// For each pod, we check for any replica sets, and merge any deployments they point to
// into a replacement Parents value.
nodes := Nodes{}
for podID, pod := range r.Pod.Nodes {
if replicaSetIDs, ok := pod.Parents.Lookup(ReplicaSet); ok {
newParents := pod.Parents.Delete(ReplicaSet)
for _, replicaSetID := range replicaSetIDs {
if replicaSet, ok := r.ReplicaSet.Nodes[replicaSetID]; ok {
if deploymentIDs, ok := replicaSet.Parents.Lookup(Deployment); ok {
newParents = newParents.Add(Deployment, deploymentIDs)
}
}
}
// newParents contains a copy of the current parents without replicasets,
// PruneParents().WithParents() ensures replicasets are actually deleted
pod = pod.PruneParents().WithParents(newParents)
}
nodes[podID] = pod
}
r.Pod.Nodes = nodes
return r
}
func (r Report) upgradeNamespaces() Report {
if len(r.Namespace.Nodes) > 0 {
return r
}
namespaces := map[string]struct{}{}
for _, t := range []Topology{r.Pod, r.Service, r.Deployment, r.DaemonSet, r.StatefulSet, r.CronJob} {
for _, n := range t.Nodes {
if state, ok := n.Latest.Lookup(KubernetesState); ok && state == "deleted" {
continue
}
if namespace, ok := n.Latest.Lookup(KubernetesNamespace); ok {
namespaces[namespace] = struct{}{}
}
}
}
nodes := make(Nodes, len(namespaces))
for ns := range namespaces {
// Namespace ID:
// Probes did not use to report namespace ids, but since creating a report node requires an id,
// the namespace name, which is unique, is passed to `MakeNamespaceNodeID`
namespaceID := MakeNamespaceNodeID(ns)
nodes[namespaceID] = MakeNodeWith(namespaceID, map[string]string{KubernetesName: ns})
}
r.Namespace.Nodes = nodes
return r
}
func (r Report) upgradeDNSRecords() Report {
// For release 1.11.6, probes accidentally sent DNS records labeled "nodes".
// Translate the incorrect version here. Accident was in commit 951629a.
if len(r.BugDNS) > 0 {
r.DNS = r.BugDNS
r.BugDNS = nil
}
if len(r.DNS) > 0 {
return r
}
dns := make(DNSRecords)
for endpointID, endpoint := range r.Endpoint.Nodes {
_, addr, _, ok := ParseEndpointNodeID(endpointID)
snoopedNames, foundS := endpoint.Sets.Lookup(SnoopedDNSNames)
reverseNames, foundR := endpoint.Sets.Lookup(ReverseDNSNames)
if ok && (foundS || foundR) {
// Add address and names to report-level map
if existing, found := dns[addr]; found {
var sUnchanged, rUnchanged bool
snoopedNames, sUnchanged = snoopedNames.Merge(existing.Forward)
reverseNames, rUnchanged = reverseNames.Merge(existing.Reverse)
if sUnchanged && rUnchanged {
continue
}
}
dns[addr] = DNSRecord{Forward: snoopedNames, Reverse: reverseNames}
}
}
r.DNS = dns
return r
}