Skip to content

Commit dadf441

Browse files
committed
Retry when assets are not found
1 parent 8dc311d commit dadf441

File tree

2 files changed

+86
-59
lines changed

2 files changed

+86
-59
lines changed

internal/packages/assets.go

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ import (
1010
"fmt"
1111
"os"
1212
"path/filepath"
13-
"strings"
1413

1514
"gopkg.in/yaml.v3"
1615

@@ -55,12 +54,24 @@ var (
5554
type Asset struct {
5655
ID string `json:"id"`
5756
Type AssetType `json:"type"`
57+
Name string
5858
DataStream string
5959
SourcePath string
6060
}
6161

62+
// IDOrName returns the ID if set, or the Name if not.
63+
func (asset Asset) IDOrName() string {
64+
if asset.ID != "" {
65+
return asset.ID
66+
}
67+
return asset.Name
68+
}
69+
6270
// String method returns a string representation of the asset
6371
func (asset Asset) String() string {
72+
if asset.ID == "" && asset.Name != "" {
73+
return fmt.Sprintf("%q (type: %s)", asset.Name, asset.Type)
74+
}
6475
return fmt.Sprintf("%s (type: %s)", asset.ID, asset.Type)
6576
}
6677

@@ -143,18 +154,13 @@ func loadKibanaTags(pkgRootPath string) ([]Asset, error) {
143154

144155
assets := make([]Asset, len(tags))
145156
for i, tag := range tags {
146-
assets[i].ID = sharedTagID(tag.Text)
157+
assets[i].Name = tag.Text
147158
assets[i].Type = AssetTypeKibanaTag.typeName
148159
assets[i].SourcePath = tagsFilePath
149160
}
150161
return assets, nil
151162
}
152163

153-
// sharedTagID tries to mimick tags created by fleet for tags defined in tags.yml.
154-
func sharedTagID(text string) string {
155-
return strings.Join(append(strings.Split(strings.ToLower(text), " "), "default"), "-")
156-
}
157-
158164
func loadElasticsearchAssets(pkgRootPath string) ([]Asset, error) {
159165
packageManifestPath := filepath.Join(pkgRootPath, PackageManifestFile)
160166
pkgManifest, err := ReadPackageManifest(packageManifestPath)

internal/testrunner/runners/asset/tester.go

Lines changed: 73 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,19 @@ import (
1010
"fmt"
1111
"slices"
1212
"strings"
13+
"time"
1314

1415
"github.com/elastic/elastic-package/internal/common"
1516
"github.com/elastic/elastic-package/internal/kibana"
1617
"github.com/elastic/elastic-package/internal/logger"
1718
"github.com/elastic/elastic-package/internal/packages"
1819
"github.com/elastic/elastic-package/internal/resources"
1920
"github.com/elastic/elastic-package/internal/testrunner"
21+
"github.com/elastic/elastic-package/internal/wait"
2022
)
2123

24+
const assetsPresentTimeout = time.Minute
25+
2226
type tester struct {
2327
testFolder testrunner.TestFolder
2428
packageRootPath string
@@ -126,61 +130,72 @@ func (r *tester) run(ctx context.Context) ([]testrunner.TestResult, error) {
126130
if err != nil {
127131
return result.WithError(fmt.Errorf("cannot read the package manifest from %s: %w", r.packageRootPath, err))
128132
}
129-
installedPackage, err := r.kibanaClient.GetPackage(ctx, manifest.Name)
130-
if err != nil {
131-
return result.WithError(fmt.Errorf("cannot get installed package %q: %w", manifest.Name, err))
132-
}
133-
installedAssets := installedPackage.Assets()
134133

135-
installedTags, err := r.kibanaClient.ExportSavedObjects(ctx, kibana.ExportSavedObjectsRequest{Type: "tag"})
136-
if err != nil {
137-
return result.WithError(fmt.Errorf("cannot get installed tags: %w", err))
138-
}
134+
var results []testrunner.TestResult
135+
_, err = wait.UntilTrue(ctx, func(ctx context.Context) (bool, error) {
136+
installedPackage, err := r.kibanaClient.GetPackage(ctx, manifest.Name)
137+
if err != nil {
138+
results, err = result.WithError(fmt.Errorf("cannot get installed package %q: %w", manifest.Name, err))
139+
return false, err
140+
}
141+
installedAssets := installedPackage.Assets()
139142

140-
// No Elasticsearch asset is created when an Input package is installed through the API.
141-
// This would require to create a Agent policy and add that input package to the Agent policy.
142-
// As those input packages could have some required fields, it would also require to add
143-
// configuration files as in system tests to fill those fields.
144-
// In these tests, mainly it is required to test Kibana assets, therefore it is not added
145-
// support for Elasticsearch assets in input packages.
146-
// Related issue: https://github.com/elastic/elastic-package/issues/1623
147-
expectedAssets, err := packages.LoadPackageAssets(r.packageRootPath)
148-
if err != nil {
149-
return result.WithError(fmt.Errorf("could not load expected package assets: %w", err))
150-
}
143+
installedTags, err := r.kibanaClient.ExportSavedObjects(ctx, kibana.ExportSavedObjectsRequest{Type: "tag"})
144+
if err != nil {
145+
results, err = result.WithError(fmt.Errorf("cannot get installed tags: %w", err))
146+
return false, err
147+
}
151148

152-
results := make([]testrunner.TestResult, 0, len(expectedAssets))
153-
for _, e := range expectedAssets {
154-
rc := testrunner.NewResultComposer(testrunner.TestResult{
155-
Name: fmt.Sprintf("%s %s is loaded", e.Type, e.ID),
156-
Package: r.testFolder.Package,
157-
DataStream: e.DataStream,
158-
TestType: TestType,
159-
})
160-
161-
tr, _ := rc.WithSuccess()
162-
if !findActualAsset(installedAssets, installedTags, e) {
163-
tr, _ = rc.WithError(testrunner.ErrTestCaseFailed{
164-
Reason: "could not find expected asset",
165-
Details: fmt.Sprintf("could not find %s asset \"%s\". Assets loaded:\n%s", e.Type, e.ID, formatAssetsAsString(installedAssets, installedTags)),
166-
})
149+
// No Elasticsearch asset is created when an Input package is installed through the API.
150+
// This would require to create a Agent policy and add that input package to the Agent policy.
151+
// As those input packages could have some required fields, it would also require to add
152+
// configuration files as in system tests to fill those fields.
153+
// In these tests, mainly it is required to test Kibana assets, therefore it is not added
154+
// support for Elasticsearch assets in input packages.
155+
// Related issue: https://github.com/elastic/elastic-package/issues/1623
156+
expectedAssets, err := packages.LoadPackageAssets(r.packageRootPath)
157+
if err != nil {
158+
results, err = result.WithError(fmt.Errorf("could not load expected package assets: %w", err))
159+
return false, err
167160
}
168-
result := tr[0]
169-
if r.withCoverage && e.SourcePath != "" {
170-
result.Coverage, err = testrunner.GenerateBaseFileCoverageReport(rc.CoveragePackageName(), e.SourcePath, r.coverageType, true)
171-
if err != nil {
161+
162+
results = make([]testrunner.TestResult, 0, len(expectedAssets))
163+
success := true
164+
for _, e := range expectedAssets {
165+
rc := testrunner.NewResultComposer(testrunner.TestResult{
166+
Name: fmt.Sprintf("%s %s is loaded", e.Type, e.IDOrName()),
167+
Package: r.testFolder.Package,
168+
DataStream: e.DataStream,
169+
TestType: TestType,
170+
})
171+
172+
tr, _ := rc.WithSuccess()
173+
if !findActualAsset(installedAssets, installedTags, e) {
172174
tr, _ = rc.WithError(testrunner.ErrTestCaseFailed{
173-
Reason: "could not generate test coverage",
174-
Details: fmt.Sprintf("could not generate test coverage for asset in %s: %v", e.SourcePath, err),
175+
Reason: "could not find expected asset",
176+
Details: fmt.Sprintf("could not find %s asset \"%s\". Assets loaded:\n%s", e.Type, e.IDOrName(), formatAssetsAsString(installedAssets, installedTags)),
175177
})
176-
result = tr[0]
178+
success = false
179+
}
180+
result := tr[0]
181+
if r.withCoverage && e.SourcePath != "" {
182+
result.Coverage, err = testrunner.GenerateBaseFileCoverageReport(rc.CoveragePackageName(), e.SourcePath, r.coverageType, true)
183+
if err != nil {
184+
tr, _ = rc.WithError(testrunner.ErrTestCaseFailed{
185+
Reason: "could not generate test coverage",
186+
Details: fmt.Sprintf("could not generate test coverage for asset in %s: %v", e.SourcePath, err),
187+
})
188+
result = tr[0]
189+
}
190+
success = false
177191
}
178-
}
179192

180-
results = append(results, result)
181-
}
193+
results = append(results, result)
194+
}
195+
return success, nil
196+
}, time.Second, assetsPresentTimeout)
182197

183-
return results, nil
198+
return results, err
184199
}
185200

186201
func (r *tester) TearDown(ctx context.Context) error {
@@ -196,23 +211,29 @@ func (r *tester) TearDown(ctx context.Context) error {
196211
return nil
197212
}
198213

199-
func findActualAsset(actualAssets []packages.Asset, installedTags []common.MapStr, expectedAsset packages.Asset) bool {
214+
func findActualAsset(actualAssets []packages.Asset, savedObjects []common.MapStr, expectedAsset packages.Asset) bool {
200215
for _, a := range actualAssets {
201216
if a.Type == expectedAsset.Type && a.ID == expectedAsset.ID {
202217
return true
203218
}
204219
}
205220

206-
if expectedAsset.Type == "tag" {
207-
// If we haven't found the asset, and it is a tag, it could be some of the shared tags defined in tags.yml.
208-
for _, tag := range installedTags {
221+
if expectedAsset.Type == "tag" && expectedAsset.ID == "" {
222+
// If we haven't found the asset, and it is a tag, it could be some of the shared
223+
// tags defined in tags.yml, whose id can be unpredictable, so check by name.
224+
for _, tag := range savedObjects {
209225
managed, _ := tag.GetValue("managed")
210226
if managed, ok := managed.(bool); !ok || !managed {
211227
continue
212228
}
213229

214-
id, _ := tag.GetValue("id")
215-
if id, ok := id.(string); ok && id == expectedAsset.ID {
230+
soType, _ := tag.GetValue("type")
231+
if soType, ok := soType.(string); !ok || soType != "tag" {
232+
continue
233+
}
234+
235+
name, _ := tag.GetValue("attributes.name")
236+
if name, ok := name.(string); ok && name == expectedAsset.Name {
216237
return true
217238
}
218239
}

0 commit comments

Comments
 (0)