From e4660b081e5a786abd0375da8166820cb070997f Mon Sep 17 00:00:00 2001 From: Bryan Boreham Date: Mon, 14 Oct 2019 18:50:28 +0000 Subject: [PATCH] backwards-compatibility: unmarshall latestControls data from older probes With a test. --- report/backcompat.go | 51 ++++++++++++++++++++++++++++++++++++++++++ report/marshal_test.go | 50 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 101 insertions(+) diff --git a/report/backcompat.go b/report/backcompat.go index c74258ce6..0d7c8391e 100644 --- a/report/backcompat.go +++ b/report/backcompat.go @@ -2,6 +2,57 @@ 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"` +} + +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)) + } +} + +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 { diff --git a/report/marshal_test.go b/report/marshal_test.go index 08518a495..0275341c7 100644 --- a/report/marshal_test.go +++ b/report/marshal_test.go @@ -1,12 +1,14 @@ package report_test import ( + "bytes" "context" "reflect" "testing" "time" "github.com/weaveworks/common/mtime" + "github.com/weaveworks/common/test" "github.com/weaveworks/scope/report" s_reflect "github.com/weaveworks/scope/test/reflect" ) @@ -71,3 +73,51 @@ func TestBiggerRoundtrip(t *testing.T) { t.Errorf("%v != %v", r1, *r2) } } + +func TestControlsCompat(t *testing.T) { + testData := `{ + "Container": { + "nodes": { + "031d;": { + "id": "031d;", + "latest": { + "control_probe_id": { + "timestamp": "2019-10-14T14:36:01Z", + "value": "29b4f381044a89a3" + } + }, + "latestControls": { + "docker_attach_container": { + "timestamp": "2019-10-14T14:36:01Z", + "value": {"dead": true} + }, + "docker_remove_container": { + "timestamp": "2019-10-14T14:36:01Z", + "value": {"dead": false} + } + }, + "topology": "container" + } + }, + "shape": "hexagon" + } +}` + + nowTime := time.Date(2019, 10, 14, 14, 36, 1, 0, time.UTC) + mtime.NowForce(nowTime) + expected := report.MakeReport() + expected.Container.AddNode(report.MakeNode(report.MakeContainerNodeID("031d")). + WithTopology("container"). + WithLatestActiveControls("docker_remove_container"). + WithLatests(map[string]string{"control_probe_id": "29b4f381044a89a3"}), + ) + + buf := bytes.NewBufferString(testData) + rpt, err := report.MakeFromBinary(context.Background(), buf, false, false) + if err != nil { + t.Fatal(err) + } + if !s_reflect.DeepEqual(&expected, rpt) { + t.Error(test.Diff(&expected, rpt)) + } +}