Skip to content

Commit aad0403

Browse files
Simplify status compression (#93)
- Eliminate all "hash" fields. - Allow omitting the sub-messages of AgentToServer message. When omitted it is implied that previously reported value of the sub-message is current (unchanged). - To detect lost messages have one auto-incremented sequence_num field AgentToServer message. Server can easily detect losses by just keeping the last sequence_num (as opposed to keeping 4 different hashes). Implements spec change open-telemetry/opamp-spec#101
1 parent 301ef45 commit aad0403

File tree

15 files changed

+518
-864
lines changed

15 files changed

+518
-864
lines changed

client/client.go

-3
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,6 @@ type OpAMPClient interface {
4949
// their AgentDescription to change dynamically while the OpAMPClient is started.
5050
// May be also called from OnMessage handler.
5151
//
52-
// The Hash field will be calculated and updated from the content of the rest of
53-
// the fields.
54-
//
5552
// nil values are not allowed and will return an error.
5653
SetAgentDescription(descr *protobufs.AgentDescription) error
5754

client/clientimpl_test.go

+27-38
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ func TestFirstStatusReport(t *testing.T) {
311311
// Start a Server.
312312
srv := internal.StartMockServer(t)
313313
srv.OnMessage = func(msg *protobufs.AgentToServer) *protobufs.ServerToAgent {
314+
assert.EqualValues(t, 0, msg.SequenceNum)
314315
return &protobufs.ServerToAgent{
315316
InstanceUid: msg.InstanceUid,
316317
RemoteConfig: remoteConfig,
@@ -353,8 +354,13 @@ func TestFirstStatusReport(t *testing.T) {
353354
func TestIncludesDetailsOnReconnect(t *testing.T) {
354355
srv := internal.StartMockServer(t)
355356

357+
seqNum := 0
358+
356359
var receivedDetails int64
357360
srv.OnMessage = func(msg *protobufs.AgentToServer) *protobufs.ServerToAgent {
361+
assert.EqualValues(t, seqNum, msg.SequenceNum)
362+
seqNum++
363+
358364
// Track when we receive AgentDescription
359365
if msg.AgentDescription != nil {
360366
atomic.AddInt64(&receivedDetails, 1)
@@ -687,6 +693,7 @@ func TestReportAgentDescription(t *testing.T) {
687693

688694
// ---> Server
689695
srv.Expect(func(msg *protobufs.AgentToServer) *protobufs.ServerToAgent {
696+
assert.EqualValues(t, 0, msg.SequenceNum)
690697
// The first status report after Start must have full AgentDescription.
691698
assert.True(t, proto.Equal(client.AgentDescription(), msg.AgentDescription))
692699
return &protobufs.ServerToAgent{InstanceUid: msg.InstanceUid}
@@ -699,25 +706,22 @@ func TestReportAgentDescription(t *testing.T) {
699706
// ---> Server
700707
srv.Expect(func(msg *protobufs.AgentToServer) *protobufs.ServerToAgent {
701708
// The status report must have compressed AgentDescription.
702-
descr := msg.AgentDescription
703-
assert.Nil(t, descr.IdentifyingAttributes)
704-
assert.Nil(t, descr.NonIdentifyingAttributes)
709+
assert.Nil(t, msg.AgentDescription)
705710

706-
// The Hash field must be present and unchanged.
707-
assert.NotNil(t, descr.Hash)
708-
assert.EqualValues(t, client.AgentDescription().Hash, descr.Hash)
711+
assert.EqualValues(t, 1, msg.SequenceNum)
709712

710713
// Ask client for full AgentDescription.
711714
return &protobufs.ServerToAgent{
712715
InstanceUid: msg.InstanceUid,
713-
Flags: protobufs.ServerToAgent_ReportAgentDescription,
716+
Flags: protobufs.ServerToAgent_ReportFullState,
714717
}
715718
})
716719

717720
// Server has requested the client to report, so there will be another message
718721
// coming to the Server.
719722
// ---> Server
720723
srv.Expect(func(msg *protobufs.AgentToServer) *protobufs.ServerToAgent {
724+
assert.EqualValues(t, 2, msg.SequenceNum)
721725
// The status report must again have full AgentDescription
722726
// because the Server asked for it.
723727
assert.True(t, proto.Equal(client.AgentDescription(), msg.AgentDescription))
@@ -758,6 +762,7 @@ func TestReportEffectiveConfig(t *testing.T) {
758762

759763
// ---> Server
760764
srv.Expect(func(msg *protobufs.AgentToServer) *protobufs.ServerToAgent {
765+
assert.EqualValues(t, 0, msg.SequenceNum)
761766
// The first status report after Start must have full EffectiveConfig.
762767
assert.True(t, proto.Equal(clientEffectiveConfig, msg.EffectiveConfig))
763768
return &protobufs.ServerToAgent{InstanceUid: msg.InstanceUid}
@@ -770,23 +775,21 @@ func TestReportEffectiveConfig(t *testing.T) {
770775
// ---> Server
771776
srv.Expect(func(msg *protobufs.AgentToServer) *protobufs.ServerToAgent {
772777
// The status report must have compressed EffectiveConfig.
773-
cfg := msg.EffectiveConfig
774-
assert.Nil(t, cfg.ConfigMap)
778+
assert.Nil(t, msg.EffectiveConfig)
775779

776-
// Hash must be present and unchanged.
777-
assert.NotNil(t, cfg.Hash)
778-
assert.EqualValues(t, clientEffectiveConfig.Hash, cfg.Hash)
780+
assert.EqualValues(t, 1, msg.SequenceNum)
779781

780782
// Ask client for full AgentDescription.
781783
return &protobufs.ServerToAgent{
782784
InstanceUid: msg.InstanceUid,
783-
Flags: protobufs.ServerToAgent_ReportEffectiveConfig,
785+
Flags: protobufs.ServerToAgent_ReportFullState,
784786
}
785787
})
786788

787789
// Server has requested the client to report, so there will be another message.
788790
// ---> Server
789791
srv.Expect(func(msg *protobufs.AgentToServer) *protobufs.ServerToAgent {
792+
assert.EqualValues(t, 2, msg.SequenceNum)
790793
// The status report must again have full EffectiveConfig
791794
// because Server asked for it.
792795
assert.True(t, proto.Equal(clientEffectiveConfig, msg.EffectiveConfig))
@@ -841,6 +844,7 @@ func verifyRemoteConfigUpdate(t *testing.T, successCase bool, expectStatus *prot
841844
remoteCfg := createRemoteConfig()
842845
// ---> Server
843846
srv.Expect(func(msg *protobufs.AgentToServer) *protobufs.ServerToAgent {
847+
assert.EqualValues(t, 0, msg.SequenceNum)
844848
// Send the remote config to the Agent.
845849
return &protobufs.ServerToAgent{
846850
InstanceUid: msg.InstanceUid,
@@ -855,12 +859,12 @@ func verifyRemoteConfigUpdate(t *testing.T, successCase bool, expectStatus *prot
855859

856860
// ---> Server
857861
srv.Expect(func(msg *protobufs.AgentToServer) *protobufs.ServerToAgent {
862+
assert.EqualValues(t, 1, msg.SequenceNum)
858863
// Verify that the remote config status is as expected.
859864
status := msg.RemoteConfigStatus
860865
assert.EqualValues(t, expectStatus.Status, status.Status)
861866
assert.Equal(t, expectStatus.ErrorMessage, status.ErrorMessage)
862867
assert.EqualValues(t, remoteCfg.ConfigHash, status.LastRemoteConfigHash)
863-
assert.NotNil(t, status.Hash)
864868

865869
firstConfigStatus = proto.Clone(status).(*protobufs.RemoteConfigStatus)
866870

@@ -873,24 +877,21 @@ func verifyRemoteConfigUpdate(t *testing.T, successCase bool, expectStatus *prot
873877

874878
// ---> Server
875879
srv.Expect(func(msg *protobufs.AgentToServer) *protobufs.ServerToAgent {
876-
// This time all fields except Hash must be unset. This is expected
880+
// This time the RemoteConfigStatus field must be unset. This is expected
877881
// as compression in OpAMP.
878-
status := msg.RemoteConfigStatus
879-
require.NotNil(t, status)
880-
assert.EqualValues(t, firstConfigStatus.Hash, status.Hash)
881-
assert.EqualValues(t, protobufs.RemoteConfigStatus_UNSET, status.Status)
882-
assert.EqualValues(t, "", status.ErrorMessage)
883-
assert.Nil(t, status.LastRemoteConfigHash)
882+
require.Nil(t, msg.RemoteConfigStatus)
883+
assert.EqualValues(t, 2, msg.SequenceNum)
884884

885885
return &protobufs.ServerToAgent{
886886
InstanceUid: msg.InstanceUid,
887887
// Ask client to report full status.
888-
Flags: protobufs.ServerToAgent_ReportRemoteConfigStatus,
888+
Flags: protobufs.ServerToAgent_ReportFullState,
889889
}
890890
})
891891

892892
// ---> Server
893893
srv.Expect(func(msg *protobufs.AgentToServer) *protobufs.ServerToAgent {
894+
assert.EqualValues(t, 3, msg.SequenceNum)
894895
// Exact same full status must be present again.
895896
status := msg.RemoteConfigStatus
896897
assert.True(t, proto.Equal(status, firstConfigStatus))
@@ -992,6 +993,7 @@ func verifyUpdatePackages(t *testing.T, testCase packageTestCase) {
992993

993994
// ---> Server
994995
srv.Expect(func(msg *protobufs.AgentToServer) *protobufs.ServerToAgent {
996+
assert.EqualValues(t, 0, msg.SequenceNum)
995997
// Send the packages to the Agent.
996998
return &protobufs.ServerToAgent{
997999
InstanceUid: msg.InstanceUid,
@@ -1002,8 +1004,6 @@ func verifyUpdatePackages(t *testing.T, testCase packageTestCase) {
10021004
// The Agent will try to install the packages and will send the status
10031005
// report about it back to the Server.
10041006

1005-
var lastStatusHash []byte
1006-
10071007
// ---> Server
10081008
// Wait for the expected package statuses to be received.
10091009
srv.EventuallyExpect("full PackageStatuses",
@@ -1013,7 +1013,6 @@ func verifyUpdatePackages(t *testing.T, testCase packageTestCase) {
10131013
status := msg.PackageStatuses
10141014
require.NotNil(t, status)
10151015
assert.EqualValues(t, testCase.expectedStatus.ServerProvidedAllPackagesHash, status.ServerProvidedAllPackagesHash)
1016-
lastStatusHash = status.Hash
10171016

10181017
if testCase.expectedError != "" {
10191018
assert.EqualValues(t, testCase.expectedError, status.ErrorMessage)
@@ -1046,7 +1045,6 @@ func verifyUpdatePackages(t *testing.T, testCase packageTestCase) {
10461045
assert.Len(t, status.Packages, len(testCase.available.Packages))
10471046
}
10481047
}
1049-
assert.NotNil(t, status.Hash)
10501048

10511049
return &protobufs.ServerToAgent{InstanceUid: msg.InstanceUid}, expectedStatusReceived
10521050
})
@@ -1069,21 +1067,13 @@ func verifyUpdatePackages(t *testing.T, testCase packageTestCase) {
10691067
srv.EventuallyExpect("compressed PackageStatuses",
10701068
func(msg *protobufs.AgentToServer) (*protobufs.ServerToAgent, bool) {
10711069
// Ensure that compressed status is received.
1072-
status := msg.PackageStatuses
1073-
require.NotNil(t, status)
1074-
compressedReceived := status.ServerProvidedAllPackagesHash == nil
1075-
if compressedReceived {
1076-
assert.Nil(t, status.ServerProvidedAllPackagesHash)
1077-
assert.Nil(t, status.Packages)
1078-
}
1079-
assert.NotNil(t, status.Hash)
1080-
assert.Equal(t, lastStatusHash, status.Hash)
1070+
compressedReceived := msg.PackageStatuses == nil
10811071

10821072
response := &protobufs.ServerToAgent{InstanceUid: msg.InstanceUid}
10831073

10841074
if compressedReceived {
10851075
// Ask for full report again.
1086-
response.Flags = protobufs.ServerToAgent_ReportPackageStatuses
1076+
response.Flags = protobufs.ServerToAgent_ReportFullState
10871077
} else {
10881078
// Keep triggering status report by setting AgentDescription
10891079
// until the compressed PackageStatuses arrives.
@@ -1114,8 +1104,7 @@ func createDownloadSrv(t *testing.T) *httptest.Server {
11141104
w.WriteHeader(http.StatusOK)
11151105
_, err := w.Write(packageFileContent)
11161106
assert.NoError(t, err)
1117-
},
1118-
)
1107+
})
11191108

11201109
srv := httptest.NewServer(m)
11211110

client/httpclient_test.go

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ func TestHTTPPolling(t *testing.T) {
1717
srv := internal.StartMockServer(t)
1818
var rcvCounter int64
1919
srv.OnMessage = func(msg *protobufs.AgentToServer) *protobufs.ServerToAgent {
20+
assert.EqualValues(t, rcvCounter, msg.SequenceNum)
2021
if msg != nil {
2122
atomic.AddInt64(&rcvCounter, 1)
2223
}

client/internal/clientcommon.go

+6-47
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,9 @@
11
package internal
22

33
import (
4-
"bytes"
54
"context"
6-
"crypto/sha256"
75
"errors"
86
"fmt"
9-
"sort"
107
"sync"
118

129
"github.com/open-telemetry/opamp-go/client/types"
@@ -175,9 +172,6 @@ func (c *ClientCommon) PrepareFirstMessage(ctx context.Context) error {
175172
if err != nil {
176173
return err
177174
}
178-
if cfg != nil {
179-
calcHashEffectiveConfig(cfg)
180-
}
181175

182176
c.sender.NextMessage().Update(
183177
func(msg *protobufs.AgentToServer) {
@@ -219,36 +213,6 @@ func (c *ClientCommon) SetAgentDescription(descr *protobufs.AgentDescription) er
219213
return nil
220214
}
221215

222-
// calcHashEffectiveConfig calculates and sets the Hash field from the rest of the
223-
// fields in the message.
224-
func calcHashEffectiveConfig(msg *protobufs.EffectiveConfig) {
225-
cfgMap := msg.GetConfigMap().GetConfigMap()
226-
227-
// Construct hash
228-
h := sha256.New()
229-
230-
// If the config is empty don't attemp to add more to the hash
231-
if len(cfgMap) > 0 {
232-
// Sort keys of configMap to make deterministic hash
233-
keys := make([]string, 0, len(cfgMap))
234-
for k := range cfgMap {
235-
keys = append(keys, k)
236-
}
237-
238-
sort.Strings(keys)
239-
240-
if msg.ConfigMap != nil {
241-
for _, k := range keys {
242-
v := cfgMap[k]
243-
h.Write([]byte(k))
244-
h.Write(v.Body)
245-
h.Write([]byte(v.ContentType))
246-
}
247-
}
248-
}
249-
msg.Hash = h.Sum(nil)
250-
}
251-
252216
// UpdateEffectiveConfig fetches the current local effective config using
253217
// GetEffectiveConfig callback and sends it to the Server using provided Sender.
254218
func (c *ClientCommon) UpdateEffectiveConfig(ctx context.Context) error {
@@ -257,9 +221,7 @@ func (c *ClientCommon) UpdateEffectiveConfig(ctx context.Context) error {
257221
if err != nil {
258222
return fmt.Errorf("GetEffectiveConfig failed: %w", err)
259223
}
260-
if cfg != nil {
261-
calcHashEffectiveConfig(cfg)
262-
}
224+
263225
// Send it to the Server.
264226
c.sender.NextMessage().Update(
265227
func(msg *protobufs.AgentToServer) {
@@ -281,16 +243,14 @@ func (c *ClientCommon) SetRemoteConfigStatus(status *protobufs.RemoteConfigStatu
281243
return errLastRemoteConfigHashNil
282244
}
283245

284-
// Get the hash of the status before we update it.
285-
prevHash := c.ClientSyncedState.RemoteConfigStatus().GetHash()
246+
statusChanged := !proto.Equal(c.ClientSyncedState.RemoteConfigStatus(), status)
286247

287248
// Remember the new status.
288249
if err := c.ClientSyncedState.SetRemoteConfigStatus(status); err != nil {
289250
return err
290251
}
291252

292-
// Check if the new status is different from the previous by comparing the hashes.
293-
if !bytes.Equal(prevHash, status.Hash) {
253+
if statusChanged {
294254
// Let the Server know about the new status.
295255
c.sender.NextMessage().Update(
296256
func(msg *protobufs.AgentToServer) {
@@ -310,15 +270,14 @@ func (c *ClientCommon) SetPackageStatuses(statuses *protobufs.PackageStatuses) e
310270
return errServerProvidedAllPackagesHashNil
311271
}
312272

313-
// Get the hash of the status before we update it.
314-
prevHash := c.ClientSyncedState.PackageStatuses().GetHash()
273+
statusChanged := !proto.Equal(c.ClientSyncedState.PackageStatuses(), statuses)
315274

316275
if err := c.ClientSyncedState.SetPackageStatuses(statuses); err != nil {
317276
return err
318277
}
319278

320-
// Check if the new status is different from the previous by comparing the hashes.
321-
if !bytes.Equal(prevHash, statuses.Hash) {
279+
// Check if the new status is different from the previous.
280+
if statusChanged {
322281
// Let the Server know about the new status.
323282

324283
c.sender.NextMessage().Update(

client/internal/clientcommon_test.go

-57
This file was deleted.

0 commit comments

Comments
 (0)