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

[Ind Agent] Update packaging to properly package from manifest if given #4885

Merged
merged 37 commits into from
Jul 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
7a0913e
Download from manifest if version has +1 patch
dwhyrock Jun 6, 2024
e7d6a3e
Try modified globExpr
dwhyrock Jun 7, 2024
41fe9fb
use version package
dwhyrock Jun 7, 2024
9f1af73
catch err
dwhyrock Jun 7, 2024
1fad3b2
use panic
dwhyrock Jun 7, 2024
18456a9
Add the filepath
dwhyrock Jun 7, 2024
ac0f535
better package finding
dwhyrock Jun 12, 2024
5c13998
more intermediate work
dwhyrock Jun 13, 2024
9b0a181
more progress
dwhyrock Jun 17, 2024
bacda1f
it seems to maybe work
dwhyrock Jun 18, 2024
49a9dfc
fixed bug
dwhyrock Jun 18, 2024
8c9e21a
Copying spec files as well
dwhyrock Jun 18, 2024
2f05b52
temp test value
dwhyrock Jun 18, 2024
4f827be
fixing linting errors
dwhyrock Jun 18, 2024
01ead7e
Merge branch 'main' into ind-agent-fix-package-endpoint-next-version
dwhyrock Jun 18, 2024
3d9f4ca
Clean up manifest code
dwhyrock Jun 18, 2024
48c65c3
Cleaning up
dwhyrock Jun 18, 2024
5f1ab42
Cleanup 2
dwhyrock Jun 19, 2024
619512d
removing test variable
dwhyrock Jun 19, 2024
9afa054
addressing PR comments and cleanup
dwhyrock Jun 20, 2024
36c2fac
Adding manifest tests
dwhyrock Jun 20, 2024
5f11a2b
Update magefile.go
dwhyrock Jun 20, 2024
8d9bd37
Merge branch 'main' into ind-agent-fix-package-endpoint-next-version
pierrehilbert Jun 21, 2024
799ed15
Switch to go:embed for tests.
cmacknz Jun 24, 2024
4fd3c50
Build component specs from external binaries.
cmacknz Jun 26, 2024
ff3d84a
Convert component to project in var names
cmacknz Jun 26, 2024
6a8c4ae
Return error when package not found.
cmacknz Jun 26, 2024
9fd0ffa
Filter unsupported platforms.
cmacknz Jun 26, 2024
9eda0ac
Fix darwin/arm64 build.
cmacknz Jun 26, 2024
026ece7
Several renames for consistency.
cmacknz Jun 26, 2024
ab3ecae
A few more renames.
cmacknz Jun 26, 2024
b4ce8c7
Move code out of magefile
cmacknz Jun 26, 2024
11404b1
mage fmt
cmacknz Jun 26, 2024
539d648
Fix log message.
cmacknz Jun 26, 2024
c495c94
Fix lint warnings.
cmacknz Jun 26, 2024
03e64b1
Rename test.
cmacknz Jun 26, 2024
7754ffd
Refactor to share download from manifest logic.
cmacknz Jun 28, 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
281 changes: 281 additions & 0 deletions dev-tools/mage/checksums.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,281 @@
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License;
// you may not use this file except in compliance with the Elastic License.

package mage

import (
"fmt"
"log"
"os"
"path/filepath"
"strings"

"github.com/magefile/mage/mg"
"github.com/otiai10/copy"

"github.com/elastic/elastic-agent/dev-tools/mage/manifest"
"github.com/elastic/elastic-agent/pkg/testing/tools"
)

const ComponentSpecFileSuffix = ".spec.yml"

func CopyComponentSpecs(componentName, versionedDropPath string) (string, error) {
specFileName := componentName + ComponentSpecFileSuffix
targetPath := filepath.Join(versionedDropPath, specFileName)

if _, err := os.Stat(targetPath); err != nil {
fmt.Printf(">> File %s does not exist, reverting to local specfile\n", targetPath)
// spec not present copy from local
sourceSpecFile := filepath.Join("specs", specFileName)
if mg.Verbose() {
log.Printf("Copy spec from %s to %s", sourceSpecFile, targetPath)
}
err := Copy(sourceSpecFile, targetPath)
if err != nil {
return "", fmt.Errorf("failed copying spec file %q to %q: %w", sourceSpecFile, targetPath, err)
}
}

// compute checksum
return GetSHA512Hash(targetPath)
}

// This is a helper function for flattenDependencies that's used when not packaging from a manifest
func ChecksumsWithoutManifest(versionedFlatPath string, versionedDropPath string, packageVersion string) map[string]string {
globExpr := filepath.Join(versionedFlatPath, fmt.Sprintf("*%s*", packageVersion))
if mg.Verbose() {
log.Printf("Finding files to copy with %s", globExpr)
}
files, err := filepath.Glob(globExpr)
if err != nil {
panic(err)
}
if mg.Verbose() {
log.Printf("Validating checksums for %+v", files)
log.Printf("--- Copying into %s: %v", versionedDropPath, files)
}

checksums := make(map[string]string)
for _, f := range files {
options := copy.Options{
OnSymlink: func(_ string) copy.SymlinkAction {
return copy.Shallow
},
Sync: true,
}
if mg.Verbose() {
log.Printf("> prepare to copy %s into %s ", f, versionedDropPath)
}

err = copy.Copy(f, versionedDropPath, options)
if err != nil {
panic(err)
}

// copy spec file for match
specName := filepath.Base(f)
idx := strings.Index(specName, "-"+packageVersion)
if idx != -1 {
specName = specName[:idx]
}
if mg.Verbose() {
log.Printf(">>>> Looking to copy spec file: [%s]", specName)
}

checksum, err := CopyComponentSpecs(specName, versionedDropPath)
if err != nil {
panic(err)
}

checksums[specName+ComponentSpecFileSuffix] = checksum
}

return checksums
}

// This is a helper function for flattenDependencies that's used when building from a manifest
func ChecksumsWithManifest(requiredPackage string, versionedFlatPath string, versionedDropPath string, manifestResponse *tools.Build) map[string]string {
checksums := make(map[string]string)
if manifestResponse == nil {
return checksums
}

// Iterate over the component projects in the manifest
projects := manifestResponse.Projects
for componentName := range projects {
// Iterate over the individual package files within each component project
for pkgName := range projects[componentName].Packages {
// Only care about packages that match the required package constraint (os/arch)
if strings.Contains(pkgName, requiredPackage) {
// Iterate over the external binaries that we care about for packaging agent
for binary := range manifest.ExpectedBinaries {
// If the individual package doesn't match the expected prefix, then continue
if !strings.HasPrefix(pkgName, binary) {
continue
}

if mg.Verbose() {
log.Printf(">>>>>>> Package [%s] matches requiredPackage [%s]", pkgName, requiredPackage)
}

// Get the version from the component based on the version in the package name
// This is useful in the case where it's an Independent Agent Release, where
// the opted-in projects will be one patch version ahead of the rest of the
// opted-out/previously-released projects
componentVersion := getComponentVersion(componentName, requiredPackage, projects[componentName])
if mg.Verbose() {
log.Printf(">>>>>>> Component [%s]/[%s] version is [%s]", componentName, requiredPackage, componentVersion)
}

// Combine the package name w/ the versioned flat path
fullPath := filepath.Join(versionedFlatPath, pkgName)

// Eliminate the file extensions to get the proper directory
// name that we need to copy
var dirToCopy string
if strings.HasSuffix(fullPath, ".tar.gz") {
dirToCopy = fullPath[:strings.LastIndex(fullPath, ".tar.gz")]
} else if strings.HasSuffix(fullPath, ".zip") {
dirToCopy = fullPath[:strings.LastIndex(fullPath, ".zip")]
} else {
dirToCopy = fullPath
}
if mg.Verbose() {
log.Printf(">>>>>>> Calculated directory to copy: [%s]", dirToCopy)
}

// cloud-defend path exception
// When untarred, cloud defend untars to:
// cloud-defend-8.14.0-arm64
// but the manifest (and most of this code) expects to be the same as
// the name in the manifest, which is:
// cloud-defend-8.14.0-linux-x86_64
// So we have to do a bit of a transformation here
if strings.Contains(dirToCopy, "cloud-defend") {
if strings.Contains(dirToCopy, "x86_64") {
dirToCopy = fixCloudDefendDirPath(dirToCopy, componentVersion, "x86_64", "amd64")
}
if strings.Contains(dirToCopy, "arm64") {
// Not actually replacing the arch, but removing the "linux"
dirToCopy = fixCloudDefendDirPath(dirToCopy, componentVersion, "arm64", "arm64")
}
if mg.Verbose() {
log.Printf(">>>>>>> Adjusted cloud-defend directory to copy: [%s]", dirToCopy)
}
}

// Set copy options
options := copy.Options{
OnSymlink: func(_ string) copy.SymlinkAction {
return copy.Shallow
},
Sync: true,
}
if mg.Verbose() {
log.Printf("> prepare to copy %s into %s ", dirToCopy, versionedDropPath)
}

// Do the copy
err := copy.Copy(dirToCopy, versionedDropPath, options)
if err != nil {
panic(err)
}

// copy spec file for match
specName := filepath.Base(dirToCopy)
idx := strings.Index(specName, "-"+componentVersion)
if idx != -1 {
specName = specName[:idx]
}
if mg.Verbose() {
log.Printf(">>>> Looking to copy spec file: [%s]", specName)
}

checksum, err := CopyComponentSpecs(specName, versionedDropPath)
if err != nil {
panic(err)
}

checksums[specName+ComponentSpecFileSuffix] = checksum
}
}
}
}

return checksums
}

// This function is used when building with a Manifest. In that manifest, it's possible
// for projects in an Independent Agent Release to have different versions since the opted-in
// ones will be one patch version higher than the opted-out/previously released projects.
// This function tries to find the versions from the package name
func getComponentVersion(componentName string, requiredPackage string, componentProject tools.Project) string {
var componentVersion string
var foundIt bool
// Iterate over all the packages in the component project
for pkgName := range componentProject.Packages {
// Only care about the external binaries that we want to package
for binary, project := range manifest.ExpectedBinaries {
// If the given component name doesn't match the external binary component, skip
if componentName != project.Name {
continue
}

// Split the package name on the binary name prefix plus a dash
firstSplit := strings.Split(pkgName, binary+"-")
if len(firstSplit) < 2 {
continue
}

// Get the second part of the first split
secondHalf := firstSplit[1]
if len(secondHalf) < 2 {
continue
}

// Make sure the second half matches the required package
if strings.Contains(secondHalf, requiredPackage) {
// ignore packages with names where this splitting doesn't results in proper version
if strings.Contains(secondHalf, "docker-image") {
continue
}
if strings.Contains(secondHalf, "oss-") {
continue
}

// The component version should be the first entry after splitting w/ the requiredPackage
componentVersion = strings.Split(secondHalf, "-"+requiredPackage)[0]
foundIt = true
// break out of inner loop
break
}
}
if foundIt {
// break out of outer loop
break
}
}

if componentVersion == "" {
errMsg := fmt.Sprintf("Unable to determine component version for [%s]", componentName)
panic(errMsg)
}

return componentVersion
}

// This is a helper function for the cloud-defend package.
// When it is untarred, it does not have the same dirname as the package name.
// This adjusts for that and returns the actual path on disk for cloud-defend
func fixCloudDefendDirPath(dirPath string, componentVersion string, expectedArch string, actualArch string) string {
fixedDirPath := dirPath

cloudDefendExpectedDirName := fmt.Sprintf("cloud-defend-%s-linux-%s", componentVersion, expectedArch)
cloudDefendActualDirName := fmt.Sprintf("cloud-defend-%s-%s", componentVersion, actualArch)
if strings.Contains(fixedDirPath, cloudDefendExpectedDirName) {
fixedDirPath = strings.ReplaceAll(fixedDirPath, cloudDefendExpectedDirName, cloudDefendActualDirName)
}

return fixedDirPath
}
Loading