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 55 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
504bc68
start working on processing agent packages
BinaryFissionGames Sep 3, 2024
f5f75c3
implement (untested) outline for taking package
BinaryFissionGames Sep 10, 2024
f25425c
WIP state
BinaryFissionGames Sep 20, 2024
1ad5d33
WIP managing using package manager
BinaryFissionGames Sep 25, 2024
4fbdc99
implement more of packageManager
BinaryFissionGames Sep 26, 2024
d087728
Fix mismatch interface
BinaryFissionGames Sep 26, 2024
301ebc4
implement update-content
BinaryFissionGames Sep 26, 2024
a1133bb
make signature verification configurable, spec out some of test
BinaryFissionGames Sep 27, 2024
f981d4b
add env var to comment
BinaryFissionGames Sep 27, 2024
efa226b
Remove duplicate todo
BinaryFissionGames Sep 27, 2024
4af3091
return error for creating verification options
BinaryFissionGames Sep 27, 2024
913f150
add comment for singature values
BinaryFissionGames Sep 30, 2024
9549385
Remove TODO
BinaryFissionGames Sep 30, 2024
72879ab
err shadowing
BinaryFissionGames Sep 30, 2024
6a9ff98
add some unit test
BinaryFissionGames Sep 30, 2024
ac63e9e
iterate on e2e test
BinaryFissionGames Sep 30, 2024
4d2c44e
fix stop/starting collector
BinaryFissionGames Oct 1, 2024
29addf4
fix nil not equalling nil
BinaryFissionGames Oct 1, 2024
f4b2ea6
fix copy file
BinaryFissionGames Oct 1, 2024
05ea57e
check agent description
BinaryFissionGames Oct 1, 2024
13a9bc0
extract tarball
BinaryFissionGames Oct 1, 2024
83d0cf5
fix e2e test
BinaryFissionGames Oct 1, 2024
1f19ca1
fix import order
BinaryFissionGames Oct 1, 2024
c5c48c4
remove unnecesary else
BinaryFissionGames Oct 1, 2024
b98a6fd
go.mod should use 1.22.0
BinaryFissionGames Oct 1, 2024
3895474
comment grammar
BinaryFissionGames Oct 1, 2024
9d6187d
re-add todo
BinaryFissionGames Oct 1, 2024
4dfd9e8
remove commented options in CheckOpts
BinaryFissionGames Oct 1, 2024
36d0773
add chlog
BinaryFissionGames Oct 1, 2024
fd5b504
tidy
BinaryFissionGames Oct 1, 2024
2682b4c
tidy
BinaryFissionGames Oct 1, 2024
118b7db
Use rekor package for client not cosign
BinaryFissionGames Oct 1, 2024
966372d
Calculate hash (hash differs from goos/goarch)
BinaryFissionGames Oct 1, 2024
f6e87b8
start on documenting the upgrade process
BinaryFissionGames Oct 16, 2024
179db69
fill out more information about signing
BinaryFissionGames Oct 17, 2024
17cb1b6
add block diagram
BinaryFissionGames Oct 22, 2024
e9f8252
add info to README
BinaryFissionGames Oct 22, 2024
f020c1a
add issue to comment for specifying root certs
BinaryFissionGames Oct 22, 2024
12e48b5
remove TODO
BinaryFissionGames Oct 22, 2024
e190a47
remove TODO wait for done in syncer
BinaryFissionGames Oct 22, 2024
ae4c81e
add docs to AgentSignatureIdentity
BinaryFissionGames Oct 22, 2024
09764c7
fix comment for verifyPackageSignature
BinaryFissionGames Oct 22, 2024
ed63804
close gzip reader
BinaryFissionGames Oct 22, 2024
c8afd02
comment maxAgentBytes
BinaryFissionGames Oct 22, 2024
68abd02
use persistent state instead of separate packages state
BinaryFissionGames Oct 23, 2024
2bea3f5
fix signature mismatch
BinaryFissionGames Oct 23, 2024
0eac913
remove "v" prefix from version
BinaryFissionGames Oct 23, 2024
64ab5bc
dont do healthcheck if healthchecker is nil
dpaasman00 Oct 31, 2024
1e70c9d
get opamp server port before writing initial cfg
dpaasman00 Oct 31, 2024
e9bbecc
fix packages tests
dpaasman00 Oct 31, 2024
c0cc631
fix ci
dpaasman00 Oct 31, 2024
e095324
fix e2e test
dpaasman00 Jan 21, 2025
c9329aa
remove duplicate opampclient mock func
dpaasman00 Jan 22, 2025
09cbad2
fix issue link, add better comments, use binpb, mv agent binaries ins…
dpaasman00 Jan 30, 2025
2dfd7cd
fix lint, e2e test, unittests
dpaasman00 Jan 30, 2025
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 @@ -1251,7 +1251,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 @@ -1647,6 +1647,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,
types.ConnectionCallbacks{
OnMessage: 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")
if err != nil {
Expand All @@ -1662,3 +1804,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