automatically start/upgrade buildkitd

- Automatically start a buildkit daemon if no BUILDKIT_HOST is
  provided (and if not already started)
- Customization of BUILDKIT_HOST is still possible, just like before
- Automatically upgrade the managed daemon to the version used by dagger
  if necessary
- Add CI test to make sure the managed buildkit and the vendored
  buildkit versions match

Signed-off-by: Andrea Luzzardi <aluzzardi@gmail.com>
This commit is contained in:
Andrea Luzzardi 2021-03-02 16:14:53 -08:00
parent 50e8b8c07d
commit 14ff14ea4f
5 changed files with 184 additions and 19 deletions

View File

@ -35,10 +35,6 @@ jobs:
run: | run: |
make lint make lint
- name: Start buildkit
run: |
docker run -d --name buildkitd --privileged moby/buildkit:v0.8.2
- name: Integration test - name: Integration test
run: | run: |
make integration make integration

View File

@ -19,10 +19,17 @@ cuefmt:
@(cue fmt -s ./examples/*) @(cue fmt -s ./examples/*)
.PHONY: lint .PHONY: lint
lint: cuefmt lint: cuefmt check-buildkit-version
golangci-lint run golangci-lint run
@test -z "$$(git status -s . | grep -e "^ M" | grep .cue | cut -d ' ' -f3 | tee /dev/stderr)" @test -z "$$(git status -s . | grep -e "^ M" | grep .cue | cut -d ' ' -f3 | tee /dev/stderr)"
.PHONY: check-buildkit-version
check-buildkit-version:
@test \
"$(shell grep buildkit ./go.mod | cut -d' ' -f2)" = \
"$(shell grep ' = "v' ./pkg/buildkitd/buildkitd.go | sed -E 's/^.*version.*=.*\"(v.*)\"/\1/' )" \
|| { echo buildkit version mismatch go.mod != pkg/buildkitd/buildkitd.go ; exit 1; }
.PHONY: integration .PHONY: integration
integration: dagger-debug integration: dagger-debug
# Self-diagnostics # Self-diagnostics

View File

@ -74,15 +74,7 @@ $ make
$ cp ./cmd/dagger/dagger /usr/local/bin $ cp ./cmd/dagger/dagger /usr/local/bin
``` ```
3. Run [buildkitd](https://github.com/moby/buildkit) on your local machine. The simplest way to do this is using [Docker](https://docker.com): `docker run -d --name buildkitd --privileged moby/buildkit:latest` 3. Compute a test configuration
On a machine with Docker installed, run:
```
$ docker run -d --name buildkitd --privileged moby/buildkit:latest
```
4. Compute a test configuration
Currently `dagger` can only do one thing: compute a configuration with optional inputs, and print the result. Currently `dagger` can only do one thing: compute a configuration with optional inputs, and print the result.

View File

@ -22,15 +22,12 @@ import (
bkgw "github.com/moby/buildkit/frontend/gateway/client" bkgw "github.com/moby/buildkit/frontend/gateway/client"
// docker output // docker output
"dagger.io/go/pkg/buildkitd"
"dagger.io/go/pkg/progressui" "dagger.io/go/pkg/progressui"
"dagger.io/go/dagger/compiler" "dagger.io/go/dagger/compiler"
) )
const (
defaultBuildkitHost = "docker-container://buildkitd"
)
// A dagger client // A dagger client
type Client struct { type Client struct {
c *bk.Client c *bk.Client
@ -41,7 +38,12 @@ func NewClient(ctx context.Context, host string) (*Client, error) {
host = os.Getenv("BUILDKIT_HOST") host = os.Getenv("BUILDKIT_HOST")
} }
if host == "" { if host == "" {
host = defaultBuildkitHost h, err := buildkitd.Start(ctx)
if err != nil {
return nil, err
}
host = h
} }
c, err := bk.New(ctx, host) c, err := bk.New(ctx, host)
if err != nil { if err != nil {

168
pkg/buildkitd/buildkitd.go Normal file
View File

@ -0,0 +1,168 @@
package buildkitd
import (
"context"
"fmt"
"os/exec"
"strings"
"github.com/docker/distribution/reference"
"github.com/rs/zerolog/log"
)
const (
image = "moby/buildkit"
version = "v0.8.2"
imageVersion = image + ":" + version
containerName = "dagger-buildkitd"
)
func Start(ctx context.Context) (string, error) {
lg := log.Ctx(ctx)
// Attempt to detect the current buildkit version
currentVersion, err := getBuildkitVersion(ctx)
if err != nil {
// If that failed, it might either be because buildkitd is not running
// or because the docker CLI is out of service.
if err := checkDocker(ctx); err != nil {
return "", err
}
currentVersion = ""
lg.Debug().Msg("no buildkit daemon detected")
} else {
lg.Debug().Str("version", currentVersion).Msg("detected buildkit version")
}
if currentVersion != version {
if currentVersion != "" {
lg.
Info().
Str("version", version).
Msg("upgrading buildkit")
if err := remvoveBuildkit(ctx); err != nil {
return "", err
}
} else {
lg.
Info().
Str("version", version).
Msg("starting buildkit")
}
if err := startBuildkit(ctx); err != nil {
return "", err
}
}
return fmt.Sprintf("docker-container://%s", containerName), nil
}
// ensure the docker CLI is available and properly set up (e.g. permissions to
// communicate with the daemon, etc)
func checkDocker(ctx context.Context) error {
cmd := exec.CommandContext(ctx, "docker", "info")
output, err := cmd.CombinedOutput()
if err != nil {
log.
Ctx(ctx).
Error().
Err(err).
Bytes("output", output).
Msg("failed to run docker")
return err
}
return nil
}
func startBuildkit(ctx context.Context) error {
lg := log.
Ctx(ctx).
With().
Str("version", version).
Logger()
lg.Debug().Msg("pulling buildkit image")
cmd := exec.CommandContext(ctx,
"docker",
"pull",
imageVersion,
)
output, err := cmd.CombinedOutput()
if err != nil {
lg.
Error().
Err(err).
Bytes("output", output).
Msg("failed to pull buildkit image")
return err
}
cmd = exec.CommandContext(ctx,
"docker",
"run",
"-d",
"--restart", "always",
"--name", containerName,
"--privileged",
imageVersion,
)
output, err = cmd.CombinedOutput()
if err != nil {
log.
Ctx(ctx).
Error().
Err(err).
Bytes("output", output).
Msg("unable to start buildkitd")
return err
}
return nil
}
func remvoveBuildkit(ctx context.Context) error {
lg := log.
Ctx(ctx)
cmd := exec.CommandContext(ctx,
"docker",
"rm",
"-fv",
containerName,
)
output, err := cmd.CombinedOutput()
if err != nil {
lg.
Error().
Err(err).
Bytes("output", output).
Msg("failed to stop buildkit")
return err
}
return nil
}
func getBuildkitVersion(ctx context.Context) (string, error) {
cmd := exec.CommandContext(ctx,
"docker",
"inspect",
"--format",
"{{.Config.Image}}",
containerName,
)
output, err := cmd.CombinedOutput()
if err != nil {
return "", err
}
ref, err := reference.ParseNormalizedNamed(strings.TrimSpace(string(output)))
if err != nil {
return "", err
}
tag, ok := ref.(reference.Tagged)
if !ok {
return "", fmt.Errorf("failed to parse image: %s", output)
}
return tag.Tag(), nil
}