CI: move grafana-build into pkg/build (#105640)
* move grafana-build into pkg/build
This commit is contained in:
@@ -0,0 +1,265 @@
|
||||
package artifacts
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"strings"
|
||||
|
||||
"dagger.io/dagger"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/arguments"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/backend"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/docker"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/flags"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/packages"
|
||||
"github.com/grafana/grafana/pkg/build/daggerbuild/pipeline"
|
||||
)
|
||||
|
||||
var (
|
||||
DockerArguments = arguments.Join(
|
||||
TargzArguments,
|
||||
[]pipeline.Argument{
|
||||
arguments.DockerRegistry,
|
||||
arguments.DockerOrg,
|
||||
arguments.AlpineImage,
|
||||
arguments.UbuntuImage,
|
||||
arguments.TagFormat,
|
||||
arguments.UbuntuTagFormat,
|
||||
arguments.BoringTagFormat,
|
||||
},
|
||||
)
|
||||
DockerFlags = flags.JoinFlags(
|
||||
TargzFlags,
|
||||
flags.DockerFlags,
|
||||
)
|
||||
)
|
||||
|
||||
var DockerInitializer = Initializer{
|
||||
InitializerFunc: NewDockerFromString,
|
||||
Arguments: DockerArguments,
|
||||
}
|
||||
|
||||
// PacakgeDocker uses a built tar.gz package to create a docker image from the Dockerfile in the tar.gz
|
||||
type Docker struct {
|
||||
Name packages.Name
|
||||
Version string
|
||||
BuildID string
|
||||
Distro backend.Distribution
|
||||
Enterprise bool
|
||||
|
||||
Ubuntu bool
|
||||
Registry string
|
||||
Repositories []string
|
||||
Org string
|
||||
BaseImage string
|
||||
TagFormat string
|
||||
|
||||
Tarball *pipeline.Artifact
|
||||
|
||||
// Src is the Grafana source code for running e2e tests when validating.
|
||||
// The grafana source should not be used for anything else when building a docker image. All files in the Docker image, including the Dockerfile, should be
|
||||
// from the tar.gz file.
|
||||
Src *dagger.Directory
|
||||
YarnCache *dagger.CacheVolume
|
||||
}
|
||||
|
||||
func (d *Docker) Dependencies(ctx context.Context) ([]*pipeline.Artifact, error) {
|
||||
return []*pipeline.Artifact{
|
||||
d.Tarball,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (d *Docker) Builder(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
|
||||
targz, err := opts.Store.File(ctx, d.Tarball)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return docker.Builder(opts.Client, opts.Client.Host().UnixSocket("/var/run/docker.sock"), targz), nil
|
||||
}
|
||||
|
||||
func (d *Docker) BuildFile(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) {
|
||||
// Unlike most other things we push to, docker image tags do not support all characters.
|
||||
// Specifically, the `+` character used in the `buildmetadata` section of semver.
|
||||
version := strings.ReplaceAll(d.Version, "+", "-")
|
||||
|
||||
tags, err := docker.Tags(d.Org, d.Registry, d.Repositories, d.TagFormat, packages.NameOpts{
|
||||
Name: d.Name,
|
||||
Version: version,
|
||||
BuildID: d.BuildID,
|
||||
Distro: d.Distro,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
buildOpts := &docker.BuildOpts{
|
||||
// Tags are provided as the '-t' argument, and can include the registry domain as well as the repository.
|
||||
// Docker build supports building the same image with multiple tags.
|
||||
// You might want to also include a 'latest' version of the tag.
|
||||
Tags: tags,
|
||||
Platform: backend.Platform(d.Distro),
|
||||
BuildArgs: []string{
|
||||
"GRAFANA_TGZ=grafana.tar.gz",
|
||||
"GO_SRC=tgz-builder",
|
||||
"JS_SRC=tgz-builder",
|
||||
fmt.Sprintf("BASE_IMAGE=%s", d.BaseImage),
|
||||
},
|
||||
}
|
||||
|
||||
b := docker.Build(opts.Client, builder, buildOpts)
|
||||
|
||||
return docker.Save(b, buildOpts), nil
|
||||
}
|
||||
|
||||
func (d *Docker) BuildDir(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.Directory, error) {
|
||||
panic("This artifact does not produce directories")
|
||||
}
|
||||
|
||||
func (d *Docker) Publisher(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
|
||||
socket := opts.Client.Host().UnixSocket("/var/run/docker.sock")
|
||||
return opts.Client.Container().From("docker").WithUnixSocket("/var/run/docker.sock", socket), nil
|
||||
}
|
||||
|
||||
func (d *Docker) PublishFile(ctx context.Context, opts *pipeline.ArtifactPublishFileOpts) error {
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
func (d *Docker) PublishDir(ctx context.Context, opts *pipeline.ArtifactPublishDirOpts) error {
|
||||
panic("This artifact does not produce directories")
|
||||
}
|
||||
|
||||
// Filename should return a deterministic file or folder name that this build will produce.
|
||||
// This filename is used as a map key for caching, so implementers need to ensure that arguments or flags that affect the output
|
||||
// also affect the filename to ensure that there are no collisions.
|
||||
// For example, the backend for `linux/amd64` and `linux/arm64` should not both produce a `bin` folder, they should produce a
|
||||
// `bin/linux-amd64` folder and a `bin/linux-arm64` folder. Callers can mount this as `bin` or whatever if they want.
|
||||
func (d *Docker) Filename(ctx context.Context) (string, error) {
|
||||
ext := "docker.tar.gz"
|
||||
if d.Ubuntu {
|
||||
ext = "ubuntu.docker.tar.gz"
|
||||
}
|
||||
|
||||
return packages.FileName(d.Name, d.Version, d.BuildID, d.Distro, ext)
|
||||
}
|
||||
|
||||
func (d *Docker) VerifyFile(ctx context.Context, client *dagger.Client, file *dagger.File) error {
|
||||
// Currently verifying riscv64 is unsupported (because alpine and ubuntu don't have riscv64 images yet)
|
||||
if _, arch := backend.OSAndArch(d.Distro); arch == "riscv64" {
|
||||
return nil
|
||||
}
|
||||
|
||||
return docker.Verify(ctx, client, file, d.Src, d.YarnCache, d.Distro)
|
||||
}
|
||||
|
||||
func (d *Docker) VerifyDirectory(ctx context.Context, client *dagger.Client, dir *dagger.Directory) error {
|
||||
panic("not implemented") // TODO: Implement
|
||||
}
|
||||
|
||||
func NewDockerFromString(ctx context.Context, log *slog.Logger, artifact string, state pipeline.StateHandler) (*pipeline.Artifact, error) {
|
||||
options, err := pipeline.ParseFlags(artifact, DockerFlags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
p, err := GetPackageDetails(ctx, options, state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tarball, err := NewTarballFromString(ctx, log, artifact, state)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ubuntu, err := options.Bool(flags.Ubuntu)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Ubuntu Version to use as the base for the Grafana docker image (if this is a ubuntu artifact)
|
||||
// This shouldn't fail if it's not set by the user, instead it'll default to 22.04 or something.
|
||||
ubuntuImage, err := state.String(ctx, arguments.UbuntuImage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Same for Alpine
|
||||
alpineImage, err := state.String(ctx, arguments.AlpineImage)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
registry, err := state.String(ctx, arguments.DockerRegistry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
org, err := state.String(ctx, arguments.DockerOrg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
repos, err := options.StringSlice(flags.DockerRepositories)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
format, err := state.String(ctx, arguments.TagFormat)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ubuntuFormat, err := state.String(ctx, arguments.UbuntuTagFormat)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
boringFormat, err := state.String(ctx, arguments.BoringTagFormat)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
base := alpineImage
|
||||
if ubuntu {
|
||||
format = ubuntuFormat
|
||||
base = ubuntuImage
|
||||
}
|
||||
|
||||
if p.Name == packages.PackageEnterpriseBoring {
|
||||
format = boringFormat
|
||||
}
|
||||
|
||||
src, err := state.Directory(ctx, arguments.GrafanaDirectory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
yarnCache, err := state.CacheVolume(ctx, arguments.YarnCacheDirectory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Info("initializing Docker artifact", "Org", org, "registry", registry, "repos", repos, "tag", format)
|
||||
|
||||
return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{
|
||||
ArtifactString: artifact,
|
||||
Handler: &Docker{
|
||||
Name: p.Name,
|
||||
Version: p.Version,
|
||||
BuildID: p.BuildID,
|
||||
Distro: p.Distribution,
|
||||
Enterprise: p.Enterprise,
|
||||
Tarball: tarball,
|
||||
|
||||
Ubuntu: ubuntu,
|
||||
BaseImage: base,
|
||||
Registry: registry,
|
||||
Org: org,
|
||||
Repositories: repos,
|
||||
TagFormat: format,
|
||||
|
||||
Src: src,
|
||||
YarnCache: yarnCache,
|
||||
},
|
||||
Type: pipeline.ArtifactTypeFile,
|
||||
Flags: DockerFlags,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user