Skip to content
This repository has been archived by the owner on Sep 21, 2023. It is now read-only.

Commit

Permalink
Packaging for elastic agent (#126)
Browse files Browse the repository at this point in the history
Packaging for elastic agent (#126)
  • Loading branch information
michalpristas authored Sep 29, 2022
1 parent e8c18e1 commit 5319426
Show file tree
Hide file tree
Showing 3 changed files with 252 additions and 8 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ by running `mage -l` from the root of the repository. The most commonly used com
* `mage build` to build the data shipper binary.
* `mage check` to check license files and dependencies.
* `mage lint` to lint the source code using [golangci-lint](https://golangci-lint.run/).
* `mage package` to produce release artifacts.
* `go test ./...` to run all tests.

Run mage commands with the `-v` flag for more detailed output, for example `mage -v check`.
14 changes: 14 additions & 0 deletions elastic-agent-shipper.spec.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
version: 2
outputs:
- name: elasticsearch
description: Elasticsearch
platforms: &platforms
- linux/amd64
- linux/arm64
- darwin/amd64
- darwin/arm64
- windows/amd64
- container/amd64
- container/arm64
command: {}

245 changes: 237 additions & 8 deletions magefile.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,15 @@
package main

import (
"archive/tar"
"archive/zip"
"compress/gzip"
"context"
"crypto/sha512"
"errors"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"runtime"
Expand All @@ -29,7 +35,10 @@ import (
)

const (
GoreleaserRepo = "github.com/goreleaser/[email protected]"
GoreleaserRepo = "github.com/goreleaser/[email protected]"
platformsEnvName = "PLATFORMS"
specFileName = devtools.ProjectName + ".spec.yml"
configFileName = devtools.ProjectName + ".yml"
)

// Aliases are shortcuts to long target names.
Expand All @@ -52,7 +61,7 @@ func (Build) All() {
}

// Clean removes the build directory.
func (Build) Clean(test string) {
func (Build) Clean() {
os.RemoveAll("build") // nolint:errcheck //not required
}

Expand Down Expand Up @@ -95,7 +104,7 @@ func (Build) Binary() error {
}
}

platform := os.Getenv("PLATFORMS")
platform := os.Getenv(platformsEnvName)
if platform != "" && devtools.PlatformFiles[platform] == nil {
return fmt.Errorf("Platform %s not recognized, only supported options: all, darwin, linux, windows, darwin/amd64, darwin/arm64, linux/386, linux/amd64, linux/arm64, windows/386, windows/amd64", platform)
}
Expand Down Expand Up @@ -177,11 +186,7 @@ func CheckBinaries(platform string, version string) error {
return errors.New("No selected platform files found")
}
for _, platform := range selectedPlatformFiles {
var execName = devtools.ProjectName
if strings.Contains(platform, "windows") {
execName += ".exe"
}
binary := filepath.Join(path, fmt.Sprintf("%s-%s-%s", devtools.ProjectName, version, platform), execName)
binary := binaryName(path, version, platform)
if _, err := os.Stat(binary); err != nil {
return fmt.Errorf("Build: binary check failed: %w", err)
}
Expand Down Expand Up @@ -215,3 +220,227 @@ func Notice() error {
// See https://github.com/golang/go/issues/43994#issuecomment-770053099
return gotool.Mod.Tidy()
}

// PACKAGE

// Package runs all the checks including licence, notice, gomod, git changes, followed by binary build.
func Package() {
// these are not allowed in parallel
mg.SerialDeps(
Build.Binary,
)

version, err := fullVersion()
if err != nil {
panic(err)
}

platforms := devtools.PlatformFiles[os.Getenv(platformsEnvName)]
// current by default
if platforms == nil {
platforms = devtools.PlatformFiles[runtime.GOOS+"/"+runtime.GOARCH]
}

archivePath := filepath.Join("build", "distributions")
if err := os.MkdirAll(archivePath, 0755); err != nil {
panic(err)
}

for _, platform := range platforms {
isWindows := strings.Contains(platform, "windows")

// grab binary
binaryPath := binaryFilePath(version, platform)

// prepare package
var err error
var archiveFileName string
targetDir := fmt.Sprintf("%s-%s", devtools.ProjectName, platform)
if isWindows {
archiveFileName = fmt.Sprintf("%s-%s.zip", devtools.ProjectName, platform)
err = prepareZipArchive(
archivePath,
targetDir,
archiveFileName,
map[string]string{
specFileName: specFileName,
configFileName: configFileName,
execName(platform): binaryPath,
},
)
} else {
archiveFileName = fmt.Sprintf("%s-%s.tar.gz", devtools.ProjectName, platform)
err = prepareTarArchive(
archivePath,
targetDir,
archiveFileName,
map[string]string{
specFileName: specFileName,
configFileName: configFileName,
execName(platform): binaryPath,
},
)
}

if err != nil {
panic(err)
}

// generate sha sum
archiveFullPath := filepath.Join(archivePath, archiveFileName)
archive, err := os.Open(archiveFullPath)
if err != nil {
panic(fmt.Errorf("failed opening archive %q: %w", archiveFileName, err))
}

h := sha512.New()
if _, err := io.Copy(h, archive); err != nil {
panic(fmt.Errorf("failed computing hash for archive %q: %w", archiveFileName, err))
}

shaFile := archiveFullPath + ".sha512"
content := fmt.Sprintf("%x %s\n", h.Sum(nil), archiveFileName)
if err := ioutil.WriteFile(shaFile, []byte(content), 0644); err != nil {
panic(fmt.Errorf("failed writing hash for archive %q: %w", archiveFileName, err))
}

archive.Close()
}
}

func prepareZipArchive(path, targetDir, archiveFileName string, files map[string]string) error {
archiveFullName := filepath.Join(path, archiveFileName)
archiveFile, err := os.Create(archiveFullName)
if err != nil {
return fmt.Errorf("failed to create %q: %w", archiveFullName, err)
}
defer archiveFile.Close()

zipWriter := zip.NewWriter(archiveFile)
defer zipWriter.Close()

addFile := func(fileName, filePath string, zipWriter *zip.Writer) error {
file, err := os.Open(filePath)
if err != nil {
return fmt.Errorf("failed opening a file: %w", err)
}
defer file.Close()

stat, err := file.Stat()
if err != nil {
return fmt.Errorf("failed retrieving stat info for a file: %w", err)
}

header, err := zip.FileInfoHeader(stat)
header.Method = zip.Deflate
header.Name = filepath.Join(targetDir, fileName)

hWriter, err := zipWriter.CreateHeader(header)
if err != nil {
return fmt.Errorf("failed writing zip header: %w", err)
}

if _, err := io.Copy(hWriter, file); err != nil {
return fmt.Errorf("failed adding a file into an archive: %w", err)
}
return nil
}

for fileName, filePath := range files {
if err := addFile(fileName, filePath, zipWriter); err != nil {
return fmt.Errorf("failed adding file %q into zip archive: %w", filePath, err)
}
}

return nil
}

func prepareTarArchive(path, targetDir, archiveFileName string, files map[string]string) error {
archiveFullName := filepath.Join(path, archiveFileName)
archiveFile, err := os.Create(archiveFullName)
if err != nil {
return fmt.Errorf("failed to create %q: %w", archiveFullName, err)
}
defer archiveFile.Close()

gzipWriter := gzip.NewWriter(archiveFile)
defer gzipWriter.Close()

tarWriter := tar.NewWriter(gzipWriter)
defer tarWriter.Close()

addFile := func(fileName, filePath string, tarWriter *tar.Writer) error {
file, err := os.Open(filePath)
if err != nil {
return fmt.Errorf("failed opening a file: %w", err)
}
defer file.Close()

stat, err := file.Stat()
if err != nil {
return fmt.Errorf("failed retrieving stat info for a file: %w", err)
}

header := &tar.Header{
Name: filepath.Join(targetDir, fileName),
Size: stat.Size(),
Mode: int64(stat.Mode()),
ModTime: stat.ModTime(),
}

if err := tarWriter.WriteHeader(header); err != nil {
return fmt.Errorf("failed writing tar header: %w", err)
}

if _, err := io.Copy(tarWriter, file); err != nil {
return fmt.Errorf("failed adding a file into an archive: %w", err)
}

return nil
}

for fileName, filePath := range files {
if err := addFile(fileName, filePath, tarWriter); err != nil {
return fmt.Errorf("failed adding file %q into tar archive: %w", filePath, err)
}
}

return nil
}

func fullVersion() (string, error) {
version := tools.DefaultBeatVersion
if versionQualifier, versionQualified := os.LookupEnv("VERSION_QUALIFIER"); versionQualified {
version += fmt.Sprintf("-%s", versionQualifier)
}

if snapshotEnv := os.Getenv("SNAPSHOT"); snapshotEnv != "" {
isSnapshot, err := strconv.ParseBool(snapshotEnv)
if err != nil {
return "", err
}
if isSnapshot {
version += fmt.Sprintf("-%s", "SNAPSHOT")
}
}

return version, nil
}

func binaryFilePath(version, platform string) string {
path := filepath.Join("build", "binaries")
return binaryName(path, version, platform)
}

func binaryName(path, version, platform string) string {
return filepath.Join(path, fmt.Sprintf("%s-%s-%s", devtools.ProjectName, version, platform), execName(platform))
}

func execName(platform string) string {
var execName = devtools.ProjectName
if strings.Contains(platform, "windows") {
execName += ".exe"
}

return execName
}

0 comments on commit 5319426

Please sign in to comment.