pkg/testing/buildkite/buildkite.go (298 lines of code) (raw):
// Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
// or more contributor license agreements. Licensed under the Elastic License 2.0;
// you may not use this file except in compliance with the Elastic License 2.0.
package buildkite
import (
"errors"
"fmt"
"strings"
"gopkg.in/yaml.v3"
"github.com/elastic/elastic-agent/pkg/testing/common"
"github.com/elastic/elastic-agent/pkg/testing/define"
"github.com/elastic/elastic-agent/pkg/testing/supported"
)
const (
defaultProvider = "gcp"
defaultImageProject = "elastic-images-qa"
defaultAMD64MachineType = "n1-standard-8"
defaultARM64MachineType = "t2a-standard-8"
)
var (
bkStackAgent = StepAgent{
Provider: "gcp",
ImageProject: "elastic-images-qa",
MachineType: "n1-standard-8", // does it need to be this large?
Image: "family/platform-ingest-elastic-agent-ubuntu-2204", // is this the correct image for creating a stack?
}
bkUbuntuAMD64_2004 = StepAgent{
Provider: defaultProvider,
ImageProject: defaultImageProject,
MachineType: defaultAMD64MachineType,
Image: "family/platform-ingest-elastic-agent-ubuntu-2004",
}
bkUbuntuAMD64_2204 = StepAgent{
Provider: defaultProvider,
ImageProject: defaultImageProject,
MachineType: defaultAMD64MachineType,
Image: "family/platform-ingest-elastic-agent-ubuntu-2204",
}
bkUbuntuAMD64_2404 = StepAgent{
Provider: defaultProvider,
ImageProject: defaultImageProject,
MachineType: defaultAMD64MachineType,
Image: "family/platform-ingest-elastic-agent-ubuntu-2404",
}
bkUbuntuARM64_2004 = StepAgent{
Provider: defaultProvider,
ImageProject: defaultImageProject,
MachineType: defaultARM64MachineType,
Image: "family/platform-ingest-elastic-agent-ubuntu-2004-arm",
}
bkUbuntuARM64_2204 = StepAgent{
Provider: defaultProvider,
ImageProject: defaultImageProject,
MachineType: defaultARM64MachineType,
Image: "family/platform-ingest-elastic-agent-ubuntu-2204-arm",
}
bkUbuntuARM64_2404 = StepAgent{
Provider: defaultProvider,
ImageProject: defaultImageProject,
MachineType: defaultARM64MachineType,
Image: "family/platform-ingest-elastic-agent-ubuntu-2404-arm",
}
bkRHELAMD64_8 = StepAgent{
Provider: defaultProvider,
ImageProject: defaultImageProject,
MachineType: defaultAMD64MachineType,
Image: "family/platform-ingest-elastic-agent-rhel-8",
}
bkRHELARM64_8 = StepAgent{
Provider: defaultProvider,
ImageProject: defaultImageProject,
MachineType: defaultARM64MachineType,
Image: "family/platform-ingest-elastic-agent-rhel-8-arm",
}
bkWindowsAMD64_2019 = StepAgent{
Provider: defaultProvider,
ImageProject: defaultImageProject,
MachineType: defaultAMD64MachineType,
Image: "family/platform-ingest-elastic-agent-windows-2019",
}
bkWindowsAMD64_2019_Core = StepAgent{
Provider: defaultProvider,
ImageProject: defaultImageProject,
MachineType: defaultAMD64MachineType,
Image: "family/platform-ingest-elastic-agent-windows-2019-core",
}
bkWindowsAMD64_2022 = StepAgent{
Provider: defaultProvider,
ImageProject: defaultImageProject,
MachineType: defaultAMD64MachineType,
Image: "family/platform-ingest-elastic-agent-windows-2022",
}
bkWindowsAMD64_2022_Core = StepAgent{
Provider: defaultProvider,
ImageProject: defaultImageProject,
MachineType: defaultAMD64MachineType,
Image: "family/platform-ingest-elastic-agent-windows-2022-core",
}
)
// getAgent returns the agent to use for the provided batch.
func getAgent(os common.SupportedOS) (StepAgent, error) {
switch os.Arch {
case define.AMD64:
switch os.Type {
case define.Linux:
switch os.Distro {
case "", "ubuntu": // default is Ubuntu
switch os.Version {
case "20.04":
return bkUbuntuAMD64_2004, nil
case "22.04":
return bkUbuntuAMD64_2204, nil
case "", "24.04": // default is 24.04
return bkUbuntuAMD64_2404, nil
default:
return StepAgent{}, fmt.Errorf("unknown ubuntu version: %s", os.Version)
}
case "rhel":
switch os.Version {
case "", "8": // default is 8
return bkRHELAMD64_8, nil
default:
return StepAgent{}, fmt.Errorf("unknown rhel version: %s", os.Version)
}
}
case define.Kubernetes:
return bkUbuntuAMD64_2404, nil
case define.Windows:
switch os.Version {
case "2019":
return bkWindowsAMD64_2019, nil
case "2019-core":
return bkWindowsAMD64_2019_Core, nil
case "", "2022": // default is 2022
return bkWindowsAMD64_2022, nil
case "2022-core":
return bkWindowsAMD64_2022_Core, nil
default:
return StepAgent{}, fmt.Errorf("unknown windows version: %s", os.Version)
}
}
case define.ARM64:
switch os.Type {
case define.Linux:
switch os.Distro {
case "", "ubuntu": // default is Ubuntu
switch os.Version {
case "20.04":
return bkUbuntuARM64_2004, nil
case "22.04":
return bkUbuntuARM64_2204, nil
case "", "24.04": // default is 24.04
return bkUbuntuARM64_2404, nil
default:
return StepAgent{}, fmt.Errorf("unknown ubuntu version: %s", os.Version)
}
case "rhel":
switch os.Version {
case "", "8": // default is 8
return bkRHELARM64_8, nil
default:
return StepAgent{}, fmt.Errorf("unknown rhel version: %s", os.Version)
}
}
case define.Kubernetes:
return bkUbuntuARM64_2404, nil
case define.Windows:
return StepAgent{}, errors.New("windows ARM support not enabled")
case define.Darwin:
return StepAgent{}, errors.New("darwin ARM support not enabled")
default:
return StepAgent{}, fmt.Errorf("unknown OS type: %s", os.Type)
}
default:
return StepAgent{}, fmt.Errorf("unknown architecture: %s", os.Arch)
}
return StepAgent{}, fmt.Errorf("case missing for %+v", os)
}
func getCommand(b common.OSBatch) string {
if b.OS.Type == define.Linux {
return "mage integration:testOnRemote"
}
return "TODO"
}
func shouldSkip(os common.SupportedOS) bool {
if os.Arch == define.AMD64 && os.Type == define.Linux {
// currently only linux/amd64 is being supported
// (but all steps are generated)
return false
}
return true
}
// GenerateSteps returns a computed set of steps to run the integration tests on buildkite.
func GenerateSteps(cfg common.Config, batches ...define.Batch) (string, error) {
stackSteps := map[string]Step{}
stackTeardown := map[string][]string{}
var steps []Step
// create the supported batches first
platforms, err := cfg.GetPlatforms()
if err != nil {
return "", err
}
osBatches, err := supported.CreateBatches(batches, platforms, cfg.Groups, cfg.Matrix, cfg.SingleTest)
if err != nil {
return "", err
}
// create the stack steps first
for _, lb := range osBatches {
if !lb.Skip && lb.Batch.Stack != nil {
if lb.Batch.Stack.Version == "" {
// no version defined on the stack; set it to the defined stack version
lb.Batch.Stack.Version = cfg.StackVersion
}
_, ok := stackSteps[lb.Batch.Stack.Version]
if !ok {
// add a step for creating the stack
stackKey := getStackKey(lb.Batch.Stack)
stackStep := Step{
Label: fmt.Sprintf("Integration Stack: %s", lb.Batch.Stack.Version),
Key: stackKey,
Command: "false",
Agents: []StepAgent{bkStackAgent},
}
steps = append(steps, stackStep)
stackSteps[lb.Batch.Stack.Version] = stackStep
stackTeardown[stackKey] = append(stackTeardown[stackKey], stackKey)
}
}
}
// generate the steps for the tests
for _, lb := range osBatches {
if lb.Skip {
continue
}
agentStep, err := getAgent(lb.OS)
if err != nil {
return "", fmt.Errorf("unable to get machine and image: %w", err)
}
if len(lb.Batch.Tests) > 0 {
var step Step
step.Label = fmt.Sprintf("Integration Test (non-sudo): %s", lb.ID)
step.Key = fmt.Sprintf("integration-non-sudo-%s", lb.ID)
if lb.Batch.Stack != nil {
stackKey := getStackKey(lb.Batch.Stack)
step.DependsOn = append(step.DependsOn, stackKey)
stackTeardown[stackKey] = append(stackTeardown[stackKey], step.Key)
}
step.ArtifactPaths = []string{"build/**"}
step.Agents = []StepAgent{agentStep}
step.Env = map[string]string{
"AGENT_VERSION": cfg.AgentVersion,
"TEST_DEFINE_PREFIX": step.Key,
"TEST_DEFINE_TESTS": strings.Join(getTestNames(lb.Batch.Tests), ","),
}
step.Command = getCommand(lb)
step.Skip = shouldSkip(lb.OS)
steps = append(steps, step)
}
if len(lb.Batch.SudoTests) > 0 {
var step Step
step.Label = fmt.Sprintf("Integration Test (sudo): %s", lb.ID)
step.Key = fmt.Sprintf("integration-sudo-%s", lb.ID)
if lb.Batch.Stack != nil {
stackKey := getStackKey(lb.Batch.Stack)
step.DependsOn = append(step.DependsOn, stackKey)
stackTeardown[stackKey] = append(stackTeardown[stackKey], step.Key)
}
step.ArtifactPaths = []string{"build/**"}
step.Agents = []StepAgent{agentStep}
step.Env = map[string]string{
"AGENT_VERSION": cfg.AgentVersion,
"TEST_DEFINE_PREFIX": step.Key,
"TEST_DEFINE_TESTS": strings.Join(getTestNames(lb.Batch.SudoTests), ","),
}
step.Command = getCommand(lb)
step.Skip = shouldSkip(lb.OS)
steps = append(steps, step)
}
}
// add the teardown steps for the stacks
for _, step := range stackSteps {
steps = append(steps, Step{
Label: fmt.Sprintf("Teardown: %s", step.Label),
Key: fmt.Sprintf("teardown-%s", step.Key),
DependsOn: stackTeardown[step.Key],
AllowDependencyFailure: true,
Command: "false",
Agents: []StepAgent{bkStackAgent},
})
}
yamlOutput, err := yaml.Marshal(Step{
Steps: steps,
})
if err != nil {
return "", fmt.Errorf("unable to marshal yaml: %w", err)
}
return string(yamlOutput), nil
}
func getTestNames(pt []define.BatchPackageTests) []string {
var tests []string
for _, pkg := range pt {
for _, test := range pkg.Tests {
tests = append(tests, fmt.Sprintf("%s:%s", pkg.Name, test.Name))
}
}
return tests
}
func getStackKey(s *define.Stack) string {
version := strings.Replace(s.Version, ".", "-", -1)
return fmt.Sprintf("integration-stack-%s", version)
}