Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[cmd/opampsupervisor]: Implement PackagesAvailable for upgrading agent #35503

Open
wants to merge 53 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
53 commits
Select commit Hold shift + click to select a range
3d321f7
start working on processing agent packages
BinaryFissionGames Sep 3, 2024
c3d7863
implement (untested) outline for taking package
BinaryFissionGames Sep 10, 2024
509bd37
WIP state
BinaryFissionGames Sep 20, 2024
82bae9c
WIP managing using package manager
BinaryFissionGames Sep 25, 2024
6dd85b4
implement more of packageManager
BinaryFissionGames Sep 26, 2024
fe0290b
Fix mismatch interface
BinaryFissionGames Sep 26, 2024
1281e0d
implement update-content
BinaryFissionGames Sep 26, 2024
630dd4b
make signature verification configurable, spec out some of test
BinaryFissionGames Sep 27, 2024
fe7423d
add env var to comment
BinaryFissionGames Sep 27, 2024
de4701f
Remove duplicate todo
BinaryFissionGames Sep 27, 2024
e170f08
return error for creating verification options
BinaryFissionGames Sep 27, 2024
2b01e2a
add comment for singature values
BinaryFissionGames Sep 30, 2024
96cfcb1
Remove TODO
BinaryFissionGames Sep 30, 2024
bac146e
err shadowing
BinaryFissionGames Sep 30, 2024
4ac06a5
add some unit test
BinaryFissionGames Sep 30, 2024
2d9842b
iterate on e2e test
BinaryFissionGames Sep 30, 2024
80fa627
fix stop/starting collector
BinaryFissionGames Oct 1, 2024
131d6f0
fix nil not equalling nil
BinaryFissionGames Oct 1, 2024
acecb7e
fix copy file
BinaryFissionGames Oct 1, 2024
5a6dcfb
check agent description
BinaryFissionGames Oct 1, 2024
156751e
extract tarball
BinaryFissionGames Oct 1, 2024
0db2e29
fix e2e test
BinaryFissionGames Oct 1, 2024
71530c2
fix import order
BinaryFissionGames Oct 1, 2024
5937254
remove unnecesary else
BinaryFissionGames Oct 1, 2024
aa8015e
go.mod should use 1.22.0
BinaryFissionGames Oct 1, 2024
310bdde
comment grammar
BinaryFissionGames Oct 1, 2024
25584ca
re-add todo
BinaryFissionGames Oct 1, 2024
a0a6ede
remove commented options in CheckOpts
BinaryFissionGames Oct 1, 2024
1f5cdaa
remove replace
BinaryFissionGames Oct 1, 2024
72c41a5
tidy
BinaryFissionGames Oct 1, 2024
259a755
add chlog
BinaryFissionGames Oct 1, 2024
cd2325f
tidy
BinaryFissionGames Oct 1, 2024
bb257ad
tidy
BinaryFissionGames Oct 1, 2024
cff9db5
Use rekor package for client not cosign
BinaryFissionGames Oct 1, 2024
1eb2289
Calculate hash (hash differs from goos/goarch)
BinaryFissionGames Oct 1, 2024
d09b84a
start on documenting the upgrade process
BinaryFissionGames Oct 16, 2024
da80a31
fill out more information about signing
BinaryFissionGames Oct 17, 2024
90974b4
add block diagram
BinaryFissionGames Oct 22, 2024
4d40997
add info to README
BinaryFissionGames Oct 22, 2024
efd0bcc
add issue to comment for specifying root certs
BinaryFissionGames Oct 22, 2024
0ac7802
remove TODO
BinaryFissionGames Oct 22, 2024
825f43a
remove TODO wait for done in syncer
BinaryFissionGames Oct 22, 2024
86c5b0d
add docs to AgentSignatureIdentity
BinaryFissionGames Oct 22, 2024
418cb97
fix comment for verifyPackageSignature
BinaryFissionGames Oct 22, 2024
54c8669
close gzip reader
BinaryFissionGames Oct 22, 2024
9ce4c80
comment maxAgentBytes
BinaryFissionGames Oct 22, 2024
a7e1ea0
use persistent state instead of separate packages state
BinaryFissionGames Oct 23, 2024
578fd6e
fix signature mismatch
BinaryFissionGames Oct 23, 2024
4a22619
remove "v" prefix from version
BinaryFissionGames Oct 23, 2024
5e6b831
dont do healthcheck if healthchecker is nil
dpaasman00 Oct 31, 2024
d70b146
get opamp server port before writing initial cfg
dpaasman00 Oct 31, 2024
806985d
fix packages tests
dpaasman00 Oct 31, 2024
3750f52
fix ci
dpaasman00 Oct 31, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .chloggen/feat_supervisor-update-collector.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Use this changelog template to create an entry for release notes.

# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix'
change_type: enhancement

# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver)
component: opampsupervisor

# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`).
note: "Adds support for agent upgrades"

# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists.
issues: [34734, 33947]
182 changes: 181 additions & 1 deletion cmd/opampsupervisor/e2e_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1254,7 +1254,7 @@ func TestSupervisorWritesAgentFilesToStorageDir(t *testing.T) {
"storage_dir": storageDir,
})

require.Nil(t, s.Start())
require.NoError(t, s.Start())

waitForSupervisorConnection(server.supervisorConnected, true)

Expand Down Expand Up @@ -1633,6 +1633,148 @@ func TestSupervisorOpAmpServerPort(t *testing.T) {
}, 10*time.Second, 500*time.Millisecond, "Log never appeared in output")
}

func TestSupervisorUpgradesAgent(t *testing.T) {
tmpDir := t.TempDir()
storageDir := filepath.Join(tmpDir, "storage")

ext := ""
if runtime.GOOS == "windows" {
ext = ".exe"
}

agentFileName := fmt.Sprintf("otelcontribcol_%s_%s%s", runtime.GOOS, runtime.GOARCH, ext)

agentFilePath := filepath.Join("..", "..", "bin", agentFileName)
agentFileCopyPath := filepath.Join(tmpDir, agentFileName)

// Upgrading will overwrite the agent binary, so we'll copy to a new path to not affect other tests
copyFile(t, agentFilePath, agentFileCopyPath)

agentIDChan := make(chan []byte, 1)
agentDescriptionChan := make(chan *protobufs.AgentDescription, 1)
packageStatusesChan := make(chan *protobufs.PackageStatuses, 2)

server := newOpAMPServer(
t,
defaultConnectingHandler,
server.ConnectionCallbacksStruct{
OnMessageFunc: func(_ context.Context, _ types.Connection, message *protobufs.AgentToServer) *protobufs.ServerToAgent {
select {
case agentIDChan <- message.InstanceUid:
default:
}

if message.AgentDescription != nil {
select {
case agentDescriptionChan <- message.AgentDescription:
default:
}
}

if message.PackageStatuses != nil {
select {
case packageStatusesChan <- message.PackageStatuses:
default:
}
}

return &protobufs.ServerToAgent{}
},
},
)

s := newSupervisor(t, "upgrade", map[string]string{
"url": server.addr,
"storage_dir": storageDir,
"agent_path": agentFileCopyPath,
})

require.Nil(t, s.Start())
defer s.Shutdown()

waitForSupervisorConnection(server.supervisorConnected, true)

t.Logf("Supervisor connected")

agentVersion := "0.110.0"
agentName := fmt.Sprintf("otelcol-contrib_0.110.0_%s_%s.tar.gz", runtime.GOOS, runtime.GOARCH)
agentURL := fmt.Sprintf("https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v0.110.0/%s", agentName)
agentSigURL := fmt.Sprintf("https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v0.110.0/%s.sig", agentName)
agentCertURL := fmt.Sprintf("https://github.com/open-telemetry/opentelemetry-collector-releases/releases/download/v0.110.0/%s.pem", agentName)

agentHash := getHTTPBodyHash(t, agentURL)
cert := getHTTPBodyContents(t, agentCertURL)
sig := getHTTPBodyContents(t, agentSigURL)

signatureField := bytes.Join([][]byte{cert, sig}, []byte(" "))

<-packageStatusesChan
<-agentDescriptionChan
agentID := <-agentIDChan
server.sendToSupervisor(&protobufs.ServerToAgent{
InstanceUid: agentID,
PackagesAvailable: &protobufs.PackagesAvailable{
Packages: map[string]*protobufs.PackageAvailable{
"": {
Type: protobufs.PackageType_PackageType_TopLevel,
Version: agentVersion,
Hash: []byte{0x01, 0x02},
File: &protobufs.DownloadableFile{
DownloadUrl: agentURL,
ContentHash: agentHash,
Signature: signatureField,
},
},
},
AllPackagesHash: []byte{0x03, 0x04},
},
})

// Wait for new package statuses
ps := <-packageStatusesChan
require.Equal(t, &protobufs.PackageStatuses{
Packages: map[string]*protobufs.PackageStatus{
"": {
// TODO: Should initital version be filled in?
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be the version/hash of the Collector binary, right? I think the version can be the one obtained during bootstrapping.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think that makes sense. I think the mental hurdle I'm having here is the sort of disconnect between the artifact the agent needs to end up with and what is available from the releases.

Because the release artifact is a tarball, but the agent executable is not, it means that the artifact offered by the server and the one the agent ends up with have completely different hashes. This initial status is kind of a weird edge case.

// What about the hash?
Name: "",
AgentHasVersion: "",
AgentHasHash: nil,
ServerOfferedVersion: agentVersion,
ServerOfferedHash: []byte{0x01, 0x02},
Status: protobufs.PackageStatusEnum_PackageStatusEnum_Installing,
},
},
ServerProvidedAllPackagesHash: []byte{0x03, 0x04},
}, ps)

ps = <-packageStatusesChan
require.Equal(t, &protobufs.PackageStatuses{
Packages: map[string]*protobufs.PackageStatus{
"": {
Name: "",
AgentHasVersion: agentVersion,
AgentHasHash: []byte{0x01, 0x02},
ServerOfferedVersion: agentVersion,
ServerOfferedHash: []byte{0x01, 0x02},
Status: protobufs.PackageStatusEnum_PackageStatusEnum_Installed,
},
},
ServerProvidedAllPackagesHash: []byte{0x03, 0x04},
}, ps)

agentDesc := <-agentDescriptionChan
versionFound := false
for _, v := range agentDesc.IdentifyingAttributes {
if v.Key == semconv.AttributeServiceVersion {
versionFound = true
require.Equal(t, agentVersion, v.Value.GetStringValue())
break
}
}
require.True(t, versionFound, "Agent description after upgrade did not contain the agent version.")
}

func findRandomPort() (int, error) {
l, err := net.Listen("tcp", "localhost:0")

Expand All @@ -1650,3 +1792,41 @@ func findRandomPort() (int, error) {

return port, nil
}

func getHTTPBodyContents(t *testing.T, url string) []byte {
r, err := http.Get(url)
require.NoError(t, err)
defer r.Body.Close()

by, err := io.ReadAll(r.Body)
require.NoError(t, err)

return by
}

func getHTTPBodyHash(t *testing.T, url string) []byte {
resp, err := http.Get(url)
require.NoError(t, err)
defer resp.Body.Close()

hasher := sha256.New()
_, err = io.Copy(hasher, resp.Body)
require.NoError(t, err)
return hasher.Sum(nil)
}

func copyFile(t *testing.T, from, to string) {
fromFile, err := os.Open(from)
require.NoError(t, err)
defer fromFile.Close()

fi, err := fromFile.Stat()
require.NoError(t, err)

toFile, err := os.OpenFile(to, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, fi.Mode())
require.NoError(t, err)
defer toFile.Close()

_, err = io.Copy(toFile, fromFile)
require.NoError(t, err)
}
Loading