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

Add new docker-enterprise artifact #365

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
32 changes: 27 additions & 5 deletions arguments/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,27 @@ var (
Usage: "Overrides the docker repository of the built images",
Value: "grafana-pro",
}
ProTagFormatFlag = &cli.StringFlag{
Name: "pro-tag-format",
Usage: "Provide a go template for formatting the docker tag(s) for Grafana Pro images",
Value: docker.DefaultProTagFormat,

EntDockerRegistryFlag = &cli.StringFlag{
Name: "enterprise-registry",
Usage: "Prefix the image name with the registry provided",
Value: "docker.io",
}
EntDockerOrgFlag = &cli.StringFlag{
Name: "enterprise-org",
Usage: "Overrides the organization of the images",
Value: "grafana",
}
EntDockerRepoFlag = &cli.StringFlag{
Name: "enterprise-repo",
Usage: "Overrides the docker repository of the built images",
Value: "grafana-enterprise",
}

HGTagFormatFlag = &cli.StringFlag{
Name: "hg-tag-format",
Usage: "Provide a go template for formatting the docker tag(s) for Hosted Grafana images",
Value: docker.DefaultHGTagFormat,
}

DockerRegistry = pipeline.NewStringFlagArgument(DockerRegistryFlag)
Expand All @@ -76,5 +93,10 @@ var (
ProDockerRegistry = pipeline.NewStringFlagArgument(ProDockerRegistryFlag)
ProDockerOrg = pipeline.NewStringFlagArgument(ProDockerOrgFlag)
ProDockerRepo = pipeline.NewStringFlagArgument(ProDockerRepoFlag)
ProTagFormat = pipeline.NewStringFlagArgument(ProTagFormatFlag)

EntDockerRegistry = pipeline.NewStringFlagArgument(EntDockerRegistryFlag)
EntDockerOrg = pipeline.NewStringFlagArgument(EntDockerOrgFlag)
EntDockerRepo = pipeline.NewStringFlagArgument(EntDockerRepoFlag)

HGTagFormat = pipeline.NewStringFlagArgument(HGTagFormatFlag)
)
24 changes: 12 additions & 12 deletions arguments/pro.go → arguments/hg_docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
"github.com/urfave/cli/v2"
)

var ProDirectoryFlags = []cli.Flag{
var HGDirectoryFlags = []cli.Flag{
&cli.StringFlag{
Name: "hosted-grafana-dir",
Usage: "Local clone of HG to use, instead of git cloning",
Expand All @@ -29,33 +29,33 @@ var ProDirectoryFlags = []cli.Flag{
},
}

// ProDirectory will provide the valueFunc that initializes and returns a *dagger.Directory that has a repository that has the Grafana Pro docker image.
// HGDirectory will provide the valueFunc that initializes and returns a *dagger.Directory that has a repository that has the Grafana Pro/Enterprise docker image.
// Where possible, when cloning and no authentication options are provided, the valuefunc will try to use the configured github CLI for cloning.
var ProDirectory = pipeline.Argument{
Name: "pro-dir",
Description: "The source tree of that has the Dockerfile for Grafana Pro",
Flags: ProDirectoryFlags,
ValueFunc: proDirectory,
var HGDirectory = pipeline.Argument{
Name: "hg-dir",
Description: "The source tree of that has the Dockerfile for Grafana Pro/Enterprise",
Flags: HGDirectoryFlags,
ValueFunc: hgDirectory,
}

type ProDirectoryOpts struct {
type HGDirectoryOpts struct {
GitHubToken string
HGDir string
HGRepo string
HGRef string
}

func ProDirectoryOptsFromFlags(c cliutil.CLIContext) *ProDirectoryOpts {
return &ProDirectoryOpts{
func HGDirectoryOptsFromFlags(c cliutil.CLIContext) *HGDirectoryOpts {
return &HGDirectoryOpts{
GitHubToken: c.String("github-token"),
HGDir: c.String("hosted-grafana-dir"),
HGRepo: c.String("hosted-grafana-repo"),
HGRef: c.String("hosted-grafana-ref"),
}
}

func proDirectory(ctx context.Context, opts *pipeline.ArgumentOpts) (any, error) {
o := ProDirectoryOptsFromFlags(opts.CLIContext)
func hgDirectory(ctx context.Context, opts *pipeline.ArgumentOpts) (any, error) {
o := HGDirectoryOptsFromFlags(opts.CLIContext)
ght, err := githubToken(ctx, o.GitHubToken)
if err != nil {
return nil, fmt.Errorf("could not get GitHub token: %w", err)
Expand Down
202 changes: 202 additions & 0 deletions artifacts/package_docker_enterprise.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
package artifacts

import (
"context"
"fmt"
"log/slog"
"strings"

"dagger.io/dagger"
"github.com/grafana/grafana-build/arguments"
"github.com/grafana/grafana-build/backend"
"github.com/grafana/grafana-build/docker"
"github.com/grafana/grafana-build/flags"
"github.com/grafana/grafana-build/packages"
"github.com/grafana/grafana-build/pipeline"
)

var (
EntDockerArguments = arguments.Join(
DebArguments,
[]pipeline.Argument{
arguments.HGDirectory,
arguments.EntDockerRegistry,
arguments.EntDockerOrg,
arguments.EntDockerRepo,
arguments.HGTagFormat,
},
)
EntDockerFlags = flags.JoinFlags(
DebFlags,
flags.DockerFlags,
)
)

var EntDockerInitializer = Initializer{
InitializerFunc: NewEntDockerFromString,
Arguments: EntDockerArguments,
}

// EntDocker uses a built deb installer to create a docker image
type EntDocker struct {
Name packages.Name
Version string
BuildID string
Distro backend.Distribution
EntDir *dagger.Directory

// EntRegistry is the docker registry when using the `enterprise` name. (e.g. hub.docker.io)
EntRegistry string
// EntOrg is the docker org when using the `enterprise` name. (e.g. grafana)
EntOrg string
// EntOrg is the docker repo when using the `enterprise` name. (e.g. grafana-enterprise)
EntRepo string
// TagFormat is the docker tag format when using the `enterprise` name. (e.g. {{ .version }}-{{ .os }}-{{ .arch }})
TagFormat string

Deb *pipeline.Artifact
}

func (d *EntDocker) Dependencies(ctx context.Context) ([]*pipeline.Artifact, error) {
return []*pipeline.Artifact{
d.Deb,
}, nil
}

func (d *EntDocker) Builder(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
deb, err := opts.Store.File(ctx, d.Deb)
if err != nil {
return nil, fmt.Errorf("error getting deb from state: %w", err)
}

socket := opts.Client.Host().UnixSocket("/var/run/docker.sock")

return opts.Client.Container().From("docker").
WithUnixSocket("/var/run/docker.sock", socket).
WithMountedDirectory("/src", d.EntDir).
WithMountedFile("/src/grafana.deb", deb).
WithWorkdir("/src"), nil
}

func (d *EntDocker) BuildFile(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) {
tags, err := docker.Tags(d.EntOrg, d.EntRegistry, []string{d.EntRepo}, d.TagFormat, packages.NameOpts{
Name: d.Name,
Version: d.Version,
BuildID: d.BuildID,
Distro: d.Distro,
})

if err != nil {
return nil, err
}

builder = docker.Build(opts.Client, builder, &docker.BuildOpts{
Dockerfile: "./docker/hosted-grafana-all/Dockerfile",
Tags: tags,
Target: "hosted-grafana-localenterprise",
Platform: dagger.Platform("linux/amd64"),
BuildArgs: []string{
"RELEASE_TYPE=main",
// I think because deb files use a ~ as a version delimiter of some kind, so the hg docker image uses that instead of a -
fmt.Sprintf("GRAFANA_VERSION=%s", strings.Replace(d.Version, "-", "~", 1)),
},
})

// Save the resulting docker image to the local filesystem
return builder.WithExec([]string{"docker", "save", tags[0], "-o", "enterprise.tar"}).File("enterprise.tar"), nil
}

func (d *EntDocker) BuildDir(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.Directory, error) {
panic("This artifact does not produce directories")
}

func (d *EntDocker) Publisher(ctx context.Context, opts *pipeline.ArtifactContainerOpts) (*dagger.Container, error) {
panic("not implemented")
}

func (d *EntDocker) PublishFile(ctx context.Context, opts *pipeline.ArtifactPublishFileOpts) error {
panic("not implemented")
}

func (d *EntDocker) 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 *EntDocker) Filename(ctx context.Context) (string, error) {
ext := "docker-enterprise.tar.gz"

return packages.FileName(d.Name, d.Version, d.BuildID, d.Distro, ext)
}

func (d *EntDocker) VerifyFile(ctx context.Context, client *dagger.Client, file *dagger.File) error {
return nil
}

func (d *EntDocker) VerifyDirectory(ctx context.Context, client *dagger.Client, dir *dagger.Directory) error {
panic("not implemented") // TODO: Implement
}

func NewEntDockerFromString(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
}

deb, err := NewDebFromString(ctx, log, artifact, state)
if err != nil {
return nil, err
}

entRegistry, err := state.String(ctx, arguments.EntDockerRegistry)
if err != nil {
return nil, err
}
entOrg, err := state.String(ctx, arguments.EntDockerOrg)
if err != nil {
return nil, err
}
entRepo, err := state.String(ctx, arguments.EntDockerRepo)
if err != nil {
return nil, err
}
tagFormat, err := state.String(ctx, arguments.HGTagFormat)
if err != nil {
return nil, err
}

dir, err := state.Directory(ctx, arguments.HGDirectory)
if err != nil {
return nil, err
}

log.Info("initializing Enterprise Docker artifact", "Org", entOrg, "registry", entRegistry, "repo", entRepo, "tag", tagFormat)

return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{
ArtifactString: artifact,
Handler: &EntDocker{
Name: p.Name,
Version: p.Version,
BuildID: p.BuildID,
Distro: p.Distribution,
EntDir: dir,
Deb: deb,

EntRegistry: entRegistry,
EntOrg: entOrg,
EntRepo: entRepo,
TagFormat: tagFormat,
},
Type: pipeline.ArtifactTypeFile,
Flags: DockerFlags,
})
}
25 changes: 13 additions & 12 deletions artifacts/package_docker_pro.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,11 @@ var (
ProDockerArguments = arguments.Join(
DebArguments,
[]pipeline.Argument{
arguments.ProDirectory,
arguments.HGDirectory,
arguments.ProDockerRegistry,
arguments.ProDockerOrg,
arguments.ProDockerRepo,
arguments.ProTagFormat,
arguments.HGTagFormat,
},
)
ProDockerFlags = flags.JoinFlags(
Expand Down Expand Up @@ -51,8 +51,8 @@ type ProDocker struct {
ProOrg string
// ProOrg is the docker repo when using the `pro` name. (e.g. grafana-pro)
ProRepo string
// ProTagFormat is the docker tag format when using the `pro` name. (e.g. {{ .version }}-{{ .os }}-{{ .arch }})
ProTagFormat string
// TagFormat is the docker tag format when using the `pro` name. (e.g. {{ .version }}-{{ .os }}-{{ .arch }})
TagFormat string

// Building the Pro image requires a Debian package instead of a tar.gz
Deb *pipeline.Artifact
Expand Down Expand Up @@ -80,7 +80,7 @@ func (d *ProDocker) Builder(ctx context.Context, opts *pipeline.ArtifactContaine
}

func (d *ProDocker) BuildFile(ctx context.Context, builder *dagger.Container, opts *pipeline.ArtifactContainerOpts) (*dagger.File, error) {
tags, err := docker.Tags(d.ProOrg, d.ProRegistry, []string{d.ProRepo}, d.ProTagFormat, packages.NameOpts{
tags, err := docker.Tags(d.ProOrg, d.ProRegistry, []string{d.ProRepo}, d.TagFormat, packages.NameOpts{
Name: d.Name,
Version: d.Version,
BuildID: d.BuildID,
Expand All @@ -94,6 +94,7 @@ func (d *ProDocker) BuildFile(ctx context.Context, builder *dagger.Container, op
builder = docker.Build(opts.Client, builder, &docker.BuildOpts{
Dockerfile: "./docker/hosted-grafana-all/Dockerfile",
Tags: tags,
Target: "hosted-grafana-localpro",
Platform: dagger.Platform("linux/amd64"),
BuildArgs: []string{
"RELEASE_TYPE=main",
Expand Down Expand Up @@ -169,17 +170,17 @@ func NewProDockerFromString(ctx context.Context, log *slog.Logger, artifact stri
if err != nil {
return nil, err
}
proTagFormat, err := state.String(ctx, arguments.ProTagFormat)
tagFormat, err := state.String(ctx, arguments.HGTagFormat)
if err != nil {
return nil, err
}

dir, err := state.Directory(ctx, arguments.ProDirectory)
dir, err := state.Directory(ctx, arguments.HGDirectory)
if err != nil {
return nil, err
}

log.Info("initializing Pro Docker artifact", "Org", proOrg, "registry", proRegistry, "repo", proRepo, "tag", proTagFormat)
log.Info("initializing Pro Docker artifact", "Org", proOrg, "registry", proRegistry, "repo", proRepo, "tag", tagFormat)

return pipeline.ArtifactWithLogging(ctx, log, &pipeline.Artifact{
ArtifactString: artifact,
Expand All @@ -191,10 +192,10 @@ func NewProDockerFromString(ctx context.Context, log *slog.Logger, artifact stri
ProDir: dir,
Deb: deb,

ProRegistry: proRegistry,
ProOrg: proOrg,
ProRepo: proRepo,
ProTagFormat: proTagFormat,
ProRegistry: proRegistry,
ProOrg: proOrg,
ProRepo: proRepo,
TagFormat: tagFormat,
},
Type: pipeline.ArtifactTypeFile,
Flags: DockerFlags,
Expand Down
Loading