Skip to content

Commit dfedb66

Browse files
pchilamergify[bot]
authored andcommitted
Make components in packages configurable (#7602)
* Redefine ExpectedBinaries as YAML config * Move ExpectedBinaries closer to package spec file * Fix error formatting in downloadDRAArtifacts * add packageName template to ExpectedPackages * use a relaxed dependencies version for IAR releases * Remove FIPS hack introduced in PR #7486 * Allow for a looser match on relaxing dependencies versions * Add debug logging when packaging with EXTERNAL=true * move package tests to dedicated package (cherry picked from commit 894ef93) # Conflicts: # dev-tools/mage/checksums.go # dev-tools/mage/manifest/manifest.go
1 parent a14e553 commit dfedb66

File tree

9 files changed

+540
-115
lines changed

9 files changed

+540
-115
lines changed

dev-tools/mage/checksums.go

Lines changed: 68 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"github.com/otiai10/copy"
1616

1717
"github.com/elastic/elastic-agent/dev-tools/mage/manifest"
18+
"github.com/elastic/elastic-agent/dev-tools/packaging"
1819
)
1920

2021
const ComponentSpecFileSuffix = ".spec.yml"
@@ -94,30 +95,16 @@ func ChecksumsWithoutManifest(versionedFlatPath string, versionedDropPath string
9495
}
9596

9697
// This is a helper function for flattenDependencies that's used when building from a manifest
97-
func ChecksumsWithManifest(requiredPackage string, versionedFlatPath string, versionedDropPath string, manifestResponse *manifest.Build) map[string]string {
98+
func ChecksumsWithManifest(platform, dependenciesVersion string, versionedFlatPath string, versionedDropPath string, manifestResponse *manifest.Build) map[string]string {
9899
checksums := make(map[string]string)
99100
if manifestResponse == nil {
100101
return checksums
101102
}
102103

103-
// Iterate over the component projects in the manifest
104-
projects := manifestResponse.Projects
105-
for componentName := range projects {
106-
// Iterate over the individual package files within each component project
107-
for pkgName := range projects[componentName].Packages {
108-
// Only care about packages that match the required package constraint (os/arch)
109-
if strings.Contains(pkgName, requiredPackage) {
110-
// Iterate over the external binaries that we care about for packaging agent
111-
for _, spec := range manifest.ExpectedBinaries {
112-
// If the individual package doesn't match the expected prefix, then continue
113-
// FIXME temporarily skip fips packages until elastic-agent FIPS is in place
114-
if !strings.HasPrefix(pkgName, spec.BinaryName) || strings.Contains(pkgName, "-fips-") {
115-
if mg.Verbose() {
116-
log.Printf(">>>>>>> Package [%s] skipped", pkgName)
117-
}
118-
continue
119-
}
104+
// Iterate over the external binaries that we care about for packaging agent
105+
for _, spec := range packaging.ExpectedBinaries {
120106

107+
<<<<<<< HEAD
121108
if mg.Verbose() {
122109
log.Printf(">>>>>>> Package [%s] matches requiredPackage [%s]", pkgName, requiredPackage)
123110
}
@@ -202,12 +189,73 @@ func ChecksumsWithManifest(requiredPackage string, versionedFlatPath string, ver
202189

203190
checksums[specName+ComponentSpecFileSuffix] = checksum
204191
}
192+
=======
193+
if spec.PythonWheel {
194+
if mg.Verbose() {
195+
log.Printf(">>>>>>> Component %s/%s is a Python wheel, skipping", spec.ProjectName, spec.BinaryName)
196+
>>>>>>> 894ef9385 (Make components in packages configurable (#7602))
197+
}
198+
continue
199+
}
200+
201+
if !spec.SupportsPlatform(platform) {
202+
log.Printf(">>>>>>> Component %s/%s does not support platform %s, skipping", spec.ProjectName, spec.BinaryName, platform)
203+
continue
204+
}
205+
206+
manifestPackage, err := manifest.ResolveManifestPackage(manifestResponse.Projects[spec.ProjectName], spec, dependenciesVersion, platform)
207+
if err != nil {
208+
if mg.Verbose() {
209+
log.Printf(">>>>>>> Error resolving package for [%s/%s]", spec.BinaryName, platform)
205210
}
211+
continue
212+
}
213+
214+
// Combine the package name w/ the versioned flat path
215+
fullPath := filepath.Join(versionedFlatPath, manifestPackage.Name)
216+
217+
// Eliminate the file extensions to get the proper directory
218+
// name that we need to copy
219+
var dirToCopy string
220+
if strings.HasSuffix(fullPath, ".tar.gz") {
221+
dirToCopy = fullPath[:strings.LastIndex(fullPath, ".tar.gz")]
222+
} else if strings.HasSuffix(fullPath, ".zip") {
223+
dirToCopy = fullPath[:strings.LastIndex(fullPath, ".zip")]
224+
} else {
225+
dirToCopy = fullPath
226+
}
227+
if mg.Verbose() {
228+
log.Printf(">>>>>>> Calculated directory to copy: [%s]", dirToCopy)
206229
}
230+
231+
// Set copy options
232+
options := copy.Options{
233+
OnSymlink: func(_ string) copy.SymlinkAction {
234+
return copy.Shallow
235+
},
236+
Sync: true,
237+
}
238+
if mg.Verbose() {
239+
log.Printf("> prepare to copy %s into %s ", dirToCopy, versionedDropPath)
240+
}
241+
242+
// Do the copy
243+
err = copy.Copy(dirToCopy, versionedDropPath, options)
244+
if err != nil {
245+
panic(err)
246+
}
247+
248+
checksum, err := CopyComponentSpecs(spec.BinaryName, versionedDropPath)
249+
if err != nil {
250+
panic(err)
251+
}
252+
253+
checksums[spec.BinaryName+ComponentSpecFileSuffix] = checksum
207254
}
208255

209256
return checksums
210257
}
258+
<<<<<<< HEAD
211259

212260
// This function is used when building with a Manifest. In that manifest, it's possible
213261
// for projects in an Independent Agent Release to have different versions since the opted-in
@@ -283,3 +331,5 @@ func fixCloudDefendDirPath(dirPath string, componentVersion string, expectedArch
283331

284332
return fixedDirPath
285333
}
334+
=======
335+
>>>>>>> 894ef9385 (Make components in packages configurable (#7602))

dev-tools/mage/manifest/manifest.go

Lines changed: 104 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,14 @@ import (
1313
"os"
1414
"path"
1515
"path/filepath"
16+
"regexp"
1617
"strings"
1718
"time"
1819

1920
"github.com/magefile/mage/mg"
2021
"golang.org/x/sync/errgroup"
2122

22-
"github.com/elastic/elastic-agent/dev-tools/mage/pkgcommon"
23+
"github.com/elastic/elastic-agent/dev-tools/packaging"
2324
"github.com/elastic/elastic-agent/pkg/version"
2425
)
2526

@@ -94,6 +95,7 @@ var PlatformPackages = map[string]string{
9495
"windows/amd64": "windows-x86_64.zip",
9596
}
9697

98+
<<<<<<< HEAD
9799
// ExpectedBinaries is a map of binaries agent needs to their project in the unified-release manager.
98100
// The project names are those used in the "projects" list in the unified release manifest.
99101
// See the sample manifests in the testdata directory.
@@ -161,6 +163,8 @@ func (p Platform) Platform() string {
161163

162164
var AllPlatforms = []Platform{{"linux", "x86_64"}, {"linux", "arm64"}, {"windows", "x86_64"}, {"darwin", "x86_64"}, {"darwin", "aarch64"}}
163165

166+
=======
167+
>>>>>>> 894ef9385 (Make components in packages configurable (#7602))
164168
// DownloadManifest is going to download the given manifest file and return the ManifestResponse
165169
func DownloadManifest(ctx context.Context, manifest string) (Build, error) {
166170
manifestUrl, urlError := url.Parse(manifest)
@@ -211,7 +215,7 @@ func DownloadComponents(ctx context.Context, manifest string, platforms []string
211215

212216
errGrp, downloadsCtx := errgroup.WithContext(ctx)
213217
// for project, pkgs := range expectedProjectPkgs() {
214-
for _, spec := range ExpectedBinaries {
218+
for _, spec := range packaging.ExpectedBinaries {
215219
for _, platform := range platforms {
216220
targetPath := filepath.Join(dropPath)
217221
err := os.MkdirAll(targetPath, 0755)
@@ -225,12 +229,12 @@ func DownloadComponents(ctx context.Context, manifest string, platforms []string
225229
continue
226230
}
227231

228-
pkgURL, err := resolveManifestPackage(projects[spec.ProjectName], spec, majorMinorPatchVersion, platform)
232+
resolvedPackage, err := ResolveManifestPackage(projects[spec.ProjectName], spec, majorMinorPatchVersion, platform)
229233
if err != nil {
230234
return err
231235
}
232236

233-
for _, p := range pkgURL {
237+
for _, p := range resolvedPackage.URLs {
234238
log.Printf(">>>>>>>>> Downloading [%s] [%s] ", spec.BinaryName, p)
235239
pkgFilename := path.Base(p)
236240
downloadTarget := filepath.Join(targetPath, pkgFilename)
@@ -252,76 +256,114 @@ func DownloadComponents(ctx context.Context, manifest string, platforms []string
252256
return nil
253257
}
254258

255-
func resolveManifestPackage(project Project, spec BinarySpec, version string, platform string) ([]string, error) {
256-
var val Package
257-
var ok bool
259+
type ResolvedPackage struct {
260+
Name string
261+
URLs []string
262+
}
263+
264+
func ResolveManifestPackage(project Project, spec packaging.BinarySpec, dependencyVersion string, platform string) (*ResolvedPackage, error) {
258265

259266
// Try the normal/easy case first
260-
packageName := spec.GetPackageName(version, platform)
261-
val, ok = project.Packages[packageName]
262-
if !ok {
263-
// If we didn't find it, it may be an Independent Agent Release, where
264-
// the opted-in projects will have a patch version one higher than
265-
// the rest of the projects, so we need to seek that out
267+
packageName := spec.GetPackageName(dependencyVersion, platform)
268+
if mg.Verbose() {
269+
log.Printf(">>>>>>>>>>> Got packagename [%s], looking for exact match", packageName)
270+
}
271+
272+
if exactMatch, ok := project.Packages[packageName]; ok {
273+
// We found the exact filename we are looking for
266274
if mg.Verbose() {
267-
log.Printf(">>>>>>>>>>> Looking for package [%s] of type [%s]", spec.BinaryName, PlatformPackages[platform])
275+
log.Printf(">>>>>>>>>>> Found exact match packageName for [%s, %s]: %s", project.Branch, project.CommitHash, exactMatch)
268276
}
269277

270-
var foundIt bool
271-
for pkgName := range project.Packages {
272-
if strings.HasPrefix(pkgName, spec.BinaryName) {
273-
firstSplit := strings.Split(pkgName, spec.BinaryName+"-")
274-
if len(firstSplit) < 2 {
275-
continue
276-
}
278+
return &ResolvedPackage{
279+
Name: packageName,
280+
URLs: []string{exactMatch.URL, exactMatch.ShaURL, exactMatch.AscURL},
281+
}, nil
282+
}
277283

278-
secondHalf := firstSplit[1]
279-
// Make sure we're finding one w/ the same required package type
280-
if strings.Contains(secondHalf, PlatformPackages[platform]) {
281-
282-
// Split again after the version with the required package string
283-
secondSplit := strings.Split(secondHalf, "-"+PlatformPackages[platform])
284-
if len(secondSplit) < 2 {
285-
continue
286-
}
287-
288-
// The first element after the split should normally be the version
289-
pkgVersion := secondSplit[0]
290-
if mg.Verbose() {
291-
log.Printf(">>>>>>>>>>> Using derived version for package [%s]: %s ", pkgName, pkgVersion)
292-
}
293-
294-
// Create a project/package key with the package, derived version, and required package
295-
foundPkgKey := fmt.Sprintf("%s-%s-%s", spec.BinaryName, pkgVersion, PlatformPackages[platform])
296-
if mg.Verbose() {
297-
log.Printf(">>>>>>>>>>> Looking for project package key: [%s]", foundPkgKey)
298-
}
299-
300-
// Get the package value, if it exists
301-
val, ok = project.Packages[foundPkgKey]
302-
if !ok {
303-
continue
304-
}
305-
306-
if mg.Verbose() {
307-
log.Printf(">>>>>>>>>>> Found package key [%s]", foundPkgKey)
308-
}
309-
310-
foundIt = true
311-
}
312-
}
313-
}
284+
// If we didn't find it, it may be an Independent Agent Release, where
285+
// the opted-in projects will have a patch version one higher than
286+
// the rest of the projects, so we "relax" the version constraint
314287

315-
if !foundIt {
316-
return nil, fmt.Errorf("package [%s] not found in project manifest at %s", packageName, project.ExternalArtifactsManifestURL)
317-
}
288+
// Find the original version in the filename
289+
versionIndex := strings.Index(packageName, dependencyVersion)
290+
if versionIndex == -1 {
291+
return nil, fmt.Errorf("no exact match and filename %q does not seem to contain dependencyVersion %q to try a fallback", packageName, dependencyVersion)
292+
}
293+
294+
// TODO move relaxVersion to the version package so we can rewrite the version like so
295+
//parseVersion, _ := version.ParseVersion(dependencyVersion)
296+
//parseVersion.GetRelaxedPatchRegexp()
297+
relaxedVersion, err := relaxVersion(dependencyVersion)
298+
if err != nil {
299+
return nil, fmt.Errorf("relaxing dependencyVersion %q: %w", dependencyVersion, err)
300+
}
301+
302+
if mg.Verbose() {
303+
log.Printf(">>>>>>>>>>> Couldn't find exact match, relaxing agent dependencyVersion to %s", relaxedVersion)
318304
}
319305

306+
// locate the original version in the filename and substitute the relaxed version regexp, quoting everything around that
307+
relaxedPackageName := regexp.QuoteMeta(packageName[:versionIndex])
308+
relaxedPackageName += relaxedVersion
309+
relaxedPackageName += regexp.QuoteMeta(packageName[versionIndex+len(dependencyVersion):])
310+
320311
if mg.Verbose() {
321-
log.Printf(">>>>>>>>>>> Project branch/commit [%s, %s]", project.Branch, project.CommitHash)
312+
log.Printf(">>>>>>>>>>> Attempting to match a filename with %s", relaxedPackageName)
322313
}
323314

324-
return []string{val.URL, val.ShaURL, val.AscURL}, nil
315+
relaxedPackageNameRegexp, err := regexp.Compile(relaxedPackageName)
316+
if err != nil {
317+
return nil, fmt.Errorf("compiling relaxed package name regex %q: %w", relaxedPackageName, err)
318+
}
319+
320+
for pkgName, pkg := range project.Packages {
321+
if mg.Verbose() {
322+
log.Printf(">>>>>>>>>>> Evaluating filename %s", pkgName)
323+
}
324+
if relaxedPackageNameRegexp.MatchString(pkgName) {
325+
if mg.Verbose() {
326+
log.Printf(">>>>>>>>>>> Found matching packageName for [%s, %s]: %s", project.Branch, project.CommitHash, pkgName)
327+
}
328+
return &ResolvedPackage{
329+
Name: pkgName,
330+
URLs: []string{pkg.URL, pkg.ShaURL, pkg.AscURL},
331+
}, nil
332+
}
333+
}
334+
335+
return nil, fmt.Errorf("package [%s] not found in project manifest at %s", packageName, project.ExternalArtifactsManifestURL)
336+
}
337+
338+
// versionRegexp is taken from https://semver.org/ (see the FAQ section/Is there a suggested regular expression (RegEx) to check a SemVer string?)
339+
const versionRegexp = `^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(0|[1-9]\d*)(?:-(?:(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?:[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$`
340+
const anyPatchVersionRegexp = `(?:0|[1-9]\d*)`
341+
342+
var versionRegExp = regexp.MustCompile(versionRegexp)
343+
344+
func relaxVersion(version string) (string, error) {
345+
matchIndices := versionRegExp.FindSubmatchIndex([]byte(version))
346+
// Matches index pairs are (0,1) for the whole regexp and (2,3) for the patch group
347+
// check that we have matched correctly
348+
if len(matchIndices) < 4 {
349+
return "", fmt.Errorf("failed to match regexp for version [%s]", version)
350+
}
351+
352+
// take the starting index of the patch version
353+
patchStartIndex := matchIndices[2]
354+
// copy everything before the patch version escaping the regexp
355+
relaxedVersion := regexp.QuoteMeta(version[:patchStartIndex])
356+
// add the patch regexp
357+
relaxedVersion += anyPatchVersionRegexp
358+
// check if there's more characters after the patch version
359+
remainderIndex := matchIndices[3]
360+
if remainderIndex < len(version) {
361+
// This is a looser regexp that allows anything beyond the major version to change (while still enforcing a valid patch version though)
362+
// see TestResolveManifestPackage/Independent_Agent_Staging_8.14_apm-server and TestResolveManifestPackage/Independent_Agent_Staging_8.14_endpoint-dev
363+
// Be more relaxed and allow for any character sequence after this
364+
relaxedVersion += `.*`
365+
}
366+
return relaxedVersion, nil
325367
}
326368

327369
func DownloadPackage(ctx context.Context, downloadUrl string, target string) error {

0 commit comments

Comments
 (0)