From f25124a20a366409c0f2aad8441d9ebbdf9cba63 Mon Sep 17 00:00:00 2001 From: Denis Date: Thu, 9 May 2024 16:44:44 +0200 Subject: [PATCH] Don't use artifact API for snapshot downloads (#4693) * The Elastic Agent upgrade to a snapshot is now using the more reliable snapshot API * The artifact fetcher in testing is now using the same snapshot API The artifact API is now used only in a single integration test-case `TestStandaloneDowngradeToSpecificSnapshotBuild`. --- .../1715158488-use-snapshot-api.yaml | 5 + .../artifact/download/snapshot/downloader.go | 68 ++++----- .../download/snapshot/downloader_test.go | 138 +++-------------- .../snapshot/testdata/latest-snapshot.json | 6 + pkg/testing/fetcher_artifact.go | 139 +++++------------- pkg/testing/fetcher_artifact_test.go | 10 +- 6 files changed, 106 insertions(+), 260 deletions(-) create mode 100644 changelog/fragments/1715158488-use-snapshot-api.yaml create mode 100644 internal/pkg/agent/application/upgrade/artifact/download/snapshot/testdata/latest-snapshot.json diff --git a/changelog/fragments/1715158488-use-snapshot-api.yaml b/changelog/fragments/1715158488-use-snapshot-api.yaml new file mode 100644 index 00000000000..41ae3d3bd0d --- /dev/null +++ b/changelog/fragments/1715158488-use-snapshot-api.yaml @@ -0,0 +1,5 @@ +kind: enhancement +summary: Use more stable snapshot API for upgrades to snapshot versions +component: "elastic-agent" +pr: https://github.com/elastic/elastic-agent/pull/4693 +issue: https://github.com/elastic/elastic-agent/issues/4458 diff --git a/internal/pkg/agent/application/upgrade/artifact/download/snapshot/downloader.go b/internal/pkg/agent/application/upgrade/artifact/download/snapshot/downloader.go index 5c417531304..34d38bb5749 100644 --- a/internal/pkg/agent/application/upgrade/artifact/download/snapshot/downloader.go +++ b/internal/pkg/agent/application/upgrade/artifact/download/snapshot/downloader.go @@ -124,60 +124,50 @@ func snapshotURI(ctx context.Context, client *gohttp.Client, versionOverride *ag version = versionOverride.CoreVersion() } - artifactsURI := fmt.Sprintf("https://artifacts-api.elastic.co/v1/search/%s-SNAPSHOT/elastic-agent", version) - request, err := gohttp.NewRequestWithContext(ctx, gohttp.MethodGet, artifactsURI, nil) + // otherwise, if we don't know the exact build and we're trying to find the latest snapshot build + buildID, err := findLatestSnapshot(ctx, client, version) if err != nil { - return "", fmt.Errorf("creating request to artifact api: %w", err) + return "", fmt.Errorf("failed to find snapshot information for version %q: %w", version, err) } - resp, err := client.Do(request) + return fmt.Sprintf(snapshotURIFormat, version, buildID), nil +} + +func findLatestSnapshot(ctx context.Context, client *gohttp.Client, version string) (buildID string, err error) { + latestSnapshotURI := fmt.Sprintf("https://snapshots.elastic.co/latest/%s-SNAPSHOT.json", version) + request, err := gohttp.NewRequestWithContext(ctx, gohttp.MethodGet, latestSnapshotURI, nil) if err != nil { - return "", err + return "", fmt.Errorf("failed to create request to the snapshot API: %w", err) } - defer resp.Body.Close() - - body := struct { - Packages map[string]interface{} `json:"packages"` - }{} - dec := json.NewDecoder(resp.Body) - if err := dec.Decode(&body); err != nil { + resp, err := client.Do(request) + if err != nil { return "", err } + defer resp.Body.Close() - if len(body.Packages) == 0 { - return "", fmt.Errorf("no packages found in snapshot repo") - } + switch resp.StatusCode { + case gohttp.StatusNotFound: + return "", fmt.Errorf("snapshot for version %q not found", version) - for k, pkg := range body.Packages { - pkgMap, ok := pkg.(map[string]interface{}) - if !ok { - return "", fmt.Errorf("content of '%s' is not a map", k) + case gohttp.StatusOK: + var info struct { + BuildID string `json:"build_id"` } - uriVal, found := pkgMap["url"] - if !found { - return "", fmt.Errorf("item '%s' does not contain url", k) + dec := json.NewDecoder(resp.Body) + if err := dec.Decode(&info); err != nil { + return "", err } - uri, ok := uriVal.(string) - if !ok { - return "", fmt.Errorf("uri is not a string") + parts := strings.Split(info.BuildID, "-") + if len(parts) != 2 { + return "", fmt.Errorf("wrong format for a build ID: %s", info.BuildID) } - // Because we're iterating over a map from the API response, - // the order is random and some elements there do not contain the - // `/beats/elastic-agent/` substring, so we need to go through the - // whole map before returning an error. - // - // One of the elements that might be there and do not contain this - // substring is the `elastic-agent-shipper`, whose URL is something like: - // https://snapshots.elastic.co/8.7.0-d050210c/downloads/elastic-agent-shipper/elastic-agent-shipper-8.7.0-SNAPSHOT-linux-x86_64.tar.gz - index := strings.Index(uri, "/beats/elastic-agent/") - if index != -1 { - return uri[:index], nil - } - } + return parts[1], nil - return "", fmt.Errorf("uri not detected") + default: + return "", fmt.Errorf("unexpected status code %d from %s", resp.StatusCode, latestSnapshotURI) + } } diff --git a/internal/pkg/agent/application/upgrade/artifact/download/snapshot/downloader_test.go b/internal/pkg/agent/application/upgrade/artifact/download/snapshot/downloader_test.go index d7dc8d433e1..7260b11f351 100644 --- a/internal/pkg/agent/application/upgrade/artifact/download/snapshot/downloader_test.go +++ b/internal/pkg/agent/application/upgrade/artifact/download/snapshot/downloader_test.go @@ -12,6 +12,7 @@ import ( "net" "net/http" "net/http/httptest" + "os" "path/filepath" "testing" @@ -37,91 +38,30 @@ func TestNonDefaultSourceURI(t *testing.T) { } -const artifactAPIElasticAgentSearchResponse = ` -{ - "packages": { - "elastic-agent-1.2.3-SNAPSHOT-darwin-aarch64.tar.gz": { - "url": "https://snapshots.elastic.co/1.2.3-33e8d7e1/downloads/beats/elastic-agent/elastic-agent-1.2.3-SNAPSHOT-darwin-aarch64.tar.gz", - "sha_url": "https://snapshots.elastic.co/1.2.3-33e8d7e1/downloads/beats/elastic-agent/elastic-agent-1.2.3-SNAPSHOT-darwin-aarch64.tar.gz.sha512", - "asc_url": "https://snapshots.elastic.co/1.2.3-33e8d7e1/downloads/beats/elastic-agent/elastic-agent-1.2.3-SNAPSHOT-darwin-aarch64.tar.gz.asc", - "type": "tar", - "architecture": "aarch64", - "os": [ - "darwin" - ] - }, - "elastic-agent-1.2.3-SNAPSHOT-windows-x86_64.zip": { - "url": "https://snapshots.elastic.co/1.2.3-33e8d7e1/downloads/beats/elastic-agent/elastic-agent-1.2.3-SNAPSHOT-windows-x86_64.zip", - "sha_url": "https://snapshots.elastic.co/1.2.3-33e8d7e1/downloads/beats/elastic-agent/elastic-agent-1.2.3-SNAPSHOT-windows-x86_64.zip.sha512", - "asc_url": "https://snapshots.elastic.co/1.2.3-33e8d7e1/downloads/beats/elastic-agent/elastic-agent-1.2.3-SNAPSHOT-windows-x86_64.zip.asc", - "type": "zip", - "architecture": "x86_64", - "os": [ - "windows" - ] - }, - "elastic-agent-core-1.2.3-SNAPSHOT-linux-arm64.tar.gz": { - "url": "https://snapshots.elastic.co/1.2.3-33e8d7e1/downloads/elastic-agent-core/elastic-agent-core-1.2.3-SNAPSHOT-linux-arm64.tar.gz", - "sha_url": "https://snapshots.elastic.co/1.2.3-33e8d7e1/downloads/elastic-agent-core/elastic-agent-core-1.2.3-SNAPSHOT-linux-arm64.tar.gz.sha512", - "asc_url": "https://snapshots.elastic.co/1.2.3-33e8d7e1/downloads/elastic-agent-core/elastic-agent-core-1.2.3-SNAPSHOT-linux-arm64.tar.gz.asc", - "type": "tar", - "architecture": "arm64", - "os": [ - "linux" - ] - }, - "elastic-agent-1.2.3-SNAPSHOT-linux-x86_64.tar.gz": { - "url": "https://snapshots.elastic.co/1.2.3-33e8d7e1/downloads/beats/elastic-agent/elastic-agent-1.2.3-SNAPSHOT-linux-x86_64.tar.gz", - "sha_url": "https://snapshots.elastic.co/1.2.3-33e8d7e1/downloads/beats/elastic-agent/elastic-agent-1.2.3-SNAPSHOT-linux-x86_64.tar.gz.sha512", - "asc_url": "https://snapshots.elastic.co/1.2.3-33e8d7e1/downloads/beats/elastic-agent/elastic-agent-1.2.3-SNAPSHOT-linux-x86_64.tar.gz.asc", - "type": "tar", - "architecture": "x86_64", - "os": [ - "linux" - ] - }, - "elastic-agent-1.2.3-SNAPSHOT-linux-arm64.tar.gz": { - "url": "https://snapshots.elastic.co/1.2.3-33e8d7e1/downloads/beats/elastic-agent/elastic-agent-1.2.3-SNAPSHOT-linux-arm64.tar.gz", - "sha_url": "https://snapshots.elastic.co/1.2.3-33e8d7e1/downloads/beats/elastic-agent/elastic-agent-1.2.3-SNAPSHOT-linux-arm64.tar.gz.sha512", - "asc_url": "https://snapshots.elastic.co/1.2.3-33e8d7e1/downloads/beats/elastic-agent/elastic-agent-1.2.3-SNAPSHOT-linux-arm64.tar.gz.asc", - "type": "tar", - "architecture": "arm64", - "os": [ - "linux" - ] - }, - "elastic-agent-1.2.3-SNAPSHOT-darwin-x86_64.tar.gz": { - "url": "https://snapshots.elastic.co/1.2.3-33e8d7e1/downloads/beats/elastic-agent/elastic-agent-1.2.3-SNAPSHOT-darwin-x86_64.tar.gz", - "sha_url": "https://snapshots.elastic.co/1.2.3-33e8d7e1/downloads/beats/elastic-agent/elastic-agent-1.2.3-SNAPSHOT-darwin-x86_64.tar.gz.sha512", - "asc_url": "https://snapshots.elastic.co/1.2.3-33e8d7e1/downloads/beats/elastic-agent/elastic-agent-1.2.3-SNAPSHOT-darwin-x86_64.tar.gz.asc", - "type": "tar", - "architecture": "x86_64", - "os": [ - "darwin" - ] - } - }, - "manifests": { - "last-update-time": "Tue, 05 Dec 2023 15:47:06 UTC", - "seconds-since-last-update": 201 - } -} -` - var agentSpec = artifact.Artifact{ Name: "Elastic Agent", Cmd: "elastic-agent", Artifact: "beat/elastic-agent", } -type downloadHttpResponse struct { - statusCode int - headers http.Header - Body []byte +func readFile(t *testing.T, name string) []byte { + bytes, err := os.ReadFile(name) + require.NoError(t, err) + + return bytes } func TestDownloadVersion(t *testing.T) { - + files := map[string][]byte{ + // links for the latest snapshot + "/latest/8.14.0-SNAPSHOT.json": readFile(t, "./testdata/latest-snapshot.json"), + "/8.14.0-6d69ee76/downloads/beat/elastic-agent/elastic-agent-8.14.0-SNAPSHOT-linux-x86_64.tar.gz": {}, + "/8.14.0-6d69ee76/downloads/beat/elastic-agent/elastic-agent-8.14.0-SNAPSHOT-linux-x86_64.tar.gz.sha512": {}, + + // links for a specific build + "/8.13.3-76ce1a63/downloads/beat/elastic-agent/elastic-agent-8.13.3-SNAPSHOT-linux-x86_64.tar.gz": {}, + "/8.13.3-76ce1a63/downloads/beat/elastic-agent/elastic-agent-8.13.3-SNAPSHOT-linux-x86_64.tar.gz.sha512": {}, + } type fields struct { config *artifact.Config } @@ -131,7 +71,6 @@ func TestDownloadVersion(t *testing.T) { } tests := []struct { name string - files map[string]downloadHttpResponse fields fields args args want string @@ -139,51 +78,26 @@ func TestDownloadVersion(t *testing.T) { }{ { name: "happy path snapshot version", - files: map[string]downloadHttpResponse{ - "/1.2.3-33e8d7e1/downloads/beat/elastic-agent/elastic-agent-1.2.3-SNAPSHOT-linux-x86_64.tar.gz": { - statusCode: http.StatusOK, - Body: []byte("This is a fake linux elastic agent archive"), - }, - "/1.2.3-33e8d7e1/downloads/beat/elastic-agent/elastic-agent-1.2.3-SNAPSHOT-linux-x86_64.tar.gz.sha512": { - statusCode: http.StatusOK, - Body: []byte("somesha512 elastic-agent-1.2.3-SNAPSHOT-linux-x86_64.tar.gz"), - }, - "/v1/search/1.2.3-SNAPSHOT/elastic-agent": { - statusCode: http.StatusOK, - headers: map[string][]string{"Content-Type": {"application/json"}}, - Body: []byte(artifactAPIElasticAgentSearchResponse), - }, - }, fields: fields{ config: &artifact.Config{ OperatingSystem: "linux", Architecture: "64", }, }, - args: args{a: agentSpec, version: agtversion.NewParsedSemVer(1, 2, 3, "SNAPSHOT", "")}, - want: "elastic-agent-1.2.3-SNAPSHOT-linux-x86_64.tar.gz", + args: args{a: agentSpec, version: agtversion.NewParsedSemVer(8, 14, 0, "SNAPSHOT", "")}, + want: "elastic-agent-8.14.0-SNAPSHOT-linux-x86_64.tar.gz", wantErr: assert.NoError, }, { name: "happy path snapshot version with build metadata", - files: map[string]downloadHttpResponse{ - "/1.2.3-buildid/downloads/beat/elastic-agent/elastic-agent-1.2.3-SNAPSHOT-linux-x86_64.tar.gz": { - statusCode: http.StatusOK, - Body: []byte("This is a fake linux elastic agent archive"), - }, - "/1.2.3-buildid/downloads/beat/elastic-agent/elastic-agent-1.2.3-SNAPSHOT-linux-x86_64.tar.gz.sha512": { - statusCode: http.StatusOK, - Body: []byte("somesha512 elastic-agent-1.2.3-SNAPSHOT-linux-x86_64.tar.gz"), - }, - }, fields: fields{ config: &artifact.Config{ OperatingSystem: "linux", Architecture: "64", }, }, - args: args{a: agentSpec, version: agtversion.NewParsedSemVer(1, 2, 3, "SNAPSHOT", "buildid")}, - want: "elastic-agent-1.2.3-SNAPSHOT-linux-x86_64.tar.gz", + args: args{a: agentSpec, version: agtversion.NewParsedSemVer(8, 13, 3, "SNAPSHOT", "76ce1a63")}, + want: "elastic-agent-8.13.3-SNAPSHOT-linux-x86_64.tar.gz", wantErr: assert.NoError, }, } @@ -195,21 +109,15 @@ func TestDownloadVersion(t *testing.T) { handleDownload := func(rw http.ResponseWriter, req *http.Request) { path := req.URL.Path + t.Logf("incoming request for %s", path) - resp, ok := tt.files[path] + file, ok := files[path] if !ok { rw.WriteHeader(http.StatusNotFound) return } - for k, values := range resp.headers { - for _, v := range values { - rw.Header().Set(k, v) - } - } - - rw.WriteHeader(resp.statusCode) - _, err := io.Copy(rw, bytes.NewReader(resp.Body)) + _, err := io.Copy(rw, bytes.NewReader(file)) assert.NoError(t, err, "error writing out response body") } server := httptest.NewTLSServer(http.HandlerFunc(handleDownload)) diff --git a/internal/pkg/agent/application/upgrade/artifact/download/snapshot/testdata/latest-snapshot.json b/internal/pkg/agent/application/upgrade/artifact/download/snapshot/testdata/latest-snapshot.json new file mode 100644 index 00000000000..24ca34758e8 --- /dev/null +++ b/internal/pkg/agent/application/upgrade/artifact/download/snapshot/testdata/latest-snapshot.json @@ -0,0 +1,6 @@ +{ + "version": "8.14.0-SNAPSHOT", + "build_id": "8.14.0-6d69ee76", + "manifest_url": "https://snapshots.elastic.co/8.14.0-6d69ee76/manifest-8.14.0-SNAPSHOT.json", + "summary_url": "https://snapshots.elastic.co/8.14.0-6d69ee76/summary-8.14.0-SNAPSHOT.html" +} diff --git a/pkg/testing/fetcher_artifact.go b/pkg/testing/fetcher_artifact.go index 3f3c1ec8ce8..cbf0e7c7835 100644 --- a/pkg/testing/fetcher_artifact.go +++ b/pkg/testing/fetcher_artifact.go @@ -80,7 +80,7 @@ func (f *artifactFetcher) Fetch(ctx context.Context, operatingSystem string, arc uri, err := findURI(ctx, f.doer, ver) if err != nil { - return nil, fmt.Errorf("failed to find snapshot URI for version %s: %w", ver, err) + return nil, fmt.Errorf("failed to find artifact URI for version %s: %w", ver, err) } // this remote path cannot have the build metadata in it @@ -136,129 +136,62 @@ func (r *artifactResult) Fetch(ctx context.Context, l Logger, dir string) error return nil } -type projectResponse struct { - Packages map[string]interface{} `json:"packages"` -} - -type projectsResponse struct { - ElasticPackage projectResponse `json:"elastic-agent-package"` -} - -type manifestResponse struct { - Projects projectsResponse `json:"projects"` -} - -func findBuild(ctx context.Context, doer httpDoer, version *semver.ParsedSemVer) (*projectResponse, error) { - // e.g. https://snapshots.elastic.co/8.13.0-l5snflwr/manifest-8.13.0-SNAPSHOT.json - manifestURI := fmt.Sprintf("https://snapshots.elastic.co/%s-%s/manifest-%s-SNAPSHOT.json", version.CoreVersion(), version.BuildMetadata(), version.CoreVersion()) - req, err := http.NewRequestWithContext(ctx, http.MethodGet, manifestURI, nil) - if err != nil { - return nil, err - } - resp, err := doer.Do(req) - if err != nil { - return nil, err - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("%s; bad status: %s", manifestURI, resp.Status) +func findURI(ctx context.Context, doer httpDoer, version *semver.ParsedSemVer) (string, error) { + // if we know the exact build ID, we can build the URI right away + if version.BuildMetadata() != "" { + return fmt.Sprintf("https://snapshots.elastic.co/%s-%s/downloads/beats/elastic-agent/", version.CoreVersion(), version.BuildMetadata()), nil } - var body manifestResponse - - dec := json.NewDecoder(resp.Body) - if err := dec.Decode(&body); err != nil { - return nil, err + // if it's the latest snapshot of a version, we can find a build ID and build the URI in the same manner + if version.IsSnapshot() { + buildID, err := findLatestSnapshot(ctx, doer, version.CoreVersion()) + if err != nil { + return "", fmt.Errorf("failed to find snapshot information for version %q: %w", version, err) + } + return fmt.Sprintf("https://snapshots.elastic.co/%s-%s/downloads/beats/elastic-agent/", version.CoreVersion(), buildID), nil } - return &body.Projects.ElasticPackage, nil + // otherwise, we're looking for a publicly released version + return "https://artifacts.elastic.co/downloads/beats/elastic-agent/", nil } -func findVersion(ctx context.Context, doer httpDoer, version *semver.ParsedSemVer) (*projectResponse, error) { - artifactsURI := fmt.Sprintf("https://artifacts-api.elastic.co/v1/search/%s/elastic-agent", version.VersionWithPrerelease()) - req, err := http.NewRequestWithContext(ctx, http.MethodGet, artifactsURI, nil) - if err != nil { - return nil, err - } - resp, err := doer.Do(req) +func findLatestSnapshot(ctx context.Context, doer httpDoer, version string) (buildID string, err error) { + latestSnapshotURI := fmt.Sprintf("https://snapshots.elastic.co/latest/%s-SNAPSHOT.json", version) + request, err := http.NewRequestWithContext(ctx, http.MethodGet, latestSnapshotURI, nil) if err != nil { - return nil, err - } - defer resp.Body.Close() - if resp.StatusCode != http.StatusOK { - return nil, fmt.Errorf("%s; bad status: %s", artifactsURI, resp.Status) - } - - var body projectResponse - - dec := json.NewDecoder(resp.Body) - if err := dec.Decode(&body); err != nil { - return nil, err - } - - return &body, nil -} - -func findURI(ctx context.Context, doer httpDoer, version *semver.ParsedSemVer) (string, error) { - var ( - project *projectResponse - err error - ) - - if version.BuildMetadata() != "" { - project, err = findBuild(ctx, doer, version) - } else { - project, err = findVersion(ctx, doer, version) + return "", fmt.Errorf("failed to create request to the snapshot API: %w", err) } + resp, err := doer.Do(request) if err != nil { - return "", fmt.Errorf("failed to find package URL: %w", err) + return "", err } + defer resp.Body.Close() - if len(project.Packages) == 0 { - return "", fmt.Errorf("no packages found in repo") - } + switch resp.StatusCode { + case http.StatusNotFound: + return "", fmt.Errorf("snapshot for version %q not found", version) - for k, pkg := range project.Packages { - pkgMap, ok := pkg.(map[string]interface{}) - if !ok { - return "", fmt.Errorf("content of '%s' is not a map", k) + case http.StatusOK: + var info struct { + BuildID string `json:"build_id"` } - uriVal, found := pkgMap["url"] - if !found { - return "", fmt.Errorf("item '%s' does not contain url", k) + dec := json.NewDecoder(resp.Body) + if err := dec.Decode(&info); err != nil { + return "", err } - uri, ok := uriVal.(string) - if !ok { - return "", fmt.Errorf("uri is not a string") + parts := strings.Split(info.BuildID, "-") + if len(parts) != 2 { + return "", fmt.Errorf("wrong format for a build ID: %s", info.BuildID) } - // Because we're iterating over a map from the API response, - // the order is random and some elements there do not contain the - // `/beats/elastic-agent/` substring, so we need to go through the - // whole map before returning an error. - // - // One of the elements that might be there and do not contain this - // substring is the `elastic-agent-shipper`, whose URL is something like: - // https://snapshots.elastic.co/8.7.0-d050210c/downloads/elastic-agent-shipper/elastic-agent-shipper-8.7.0-SNAPSHOT-linux-x86_64.tar.gz - index := strings.Index(uri, "/beats/elastic-agent/") - if index != -1 { - if version.BuildMetadata() == "" { - // no build id, first is selected - return fmt.Sprintf("%s/beats/elastic-agent/", uri[:index]), nil - } - if strings.Contains(uri, fmt.Sprintf("%s-%s", version.CoreVersion(), version.BuildMetadata())) { - return fmt.Sprintf("%s/beats/elastic-agent/", uri[:index]), nil - } - } - } + return parts[1], nil - if version.BuildMetadata() == "" { - return "", fmt.Errorf("uri for version %q not detected", version) + default: + return "", fmt.Errorf("unexpected status code %d from %s", resp.StatusCode, latestSnapshotURI) } - return "", fmt.Errorf("uri not detected with specific build ID %q", version.BuildMetadata()) } func DownloadPackage(ctx context.Context, l Logger, doer httpDoer, downloadPath string, packageFile string) error { diff --git a/pkg/testing/fetcher_artifact_test.go b/pkg/testing/fetcher_artifact_test.go index f2f0aae51e2..db314bb739b 100644 --- a/pkg/testing/fetcher_artifact_test.go +++ b/pkg/testing/fetcher_artifact_test.go @@ -151,15 +151,15 @@ func newFakeHttpClient(t *testing.T) *fakeHttpClient { // actual artifacts // 8.12 release - "https://staging.elastic.co/8.12.0-xx1lc7my/downloads/beats/elastic-agent/elastic-agent-8.12.0-linux-x86_64.tar.gz": { + "https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-8.12.0-linux-x86_64.tar.gz": { StatusCode: 200, Body: io.NopCloser(bytes.NewReader([]byte(binaryResponse))), }, - "https://staging.elastic.co/8.12.0-xx1lc7my/downloads/beats/elastic-agent/elastic-agent-8.12.0-linux-x86_64.tar.gz.sha512": { + "https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-8.12.0-linux-x86_64.tar.gz.sha512": { StatusCode: 200, Body: io.NopCloser(bytes.NewReader([]byte(hashResponse))), }, - "https://staging.elastic.co/8.12.0-xx1lc7my/downloads/beats/elastic-agent/elastic-agent-8.12.0-linux-x86_64.tar.gz.asc": { + "https://artifacts.elastic.co/downloads/beats/elastic-agent/elastic-agent-8.12.0-linux-x86_64.tar.gz.asc": { StatusCode: 200, Body: io.NopCloser(bytes.NewReader([]byte(ascResponse))), }, @@ -176,6 +176,10 @@ func newFakeHttpClient(t *testing.T) *fakeHttpClient { StatusCode: 200, Body: io.NopCloser(bytes.NewReader([]byte(ascResponse))), }, + "https://snapshots.elastic.co/latest/8.13.0-SNAPSHOT.json": { + StatusCode: 200, + Body: io.NopCloser(bytes.NewReader([]byte(`{"build_id":"8.13.0-yil7wib0"}`))), + }, // 8.13 build l5snflwr "https://snapshots.elastic.co/8.13.0-l5snflwr/downloads/beats/elastic-agent/elastic-agent-8.13.0-SNAPSHOT-linux-x86_64.tar.gz": {