Skip to content
Merged
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
1 change: 1 addition & 0 deletions go.work.sum
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
github.com/klauspost/cpuid v0.0.0-20170728055534-ae7887de9fa5 h1:2U0HzY8BJ8hVwDKIzp7y4voR9CX/nvcfymLmg2UiOio=
github.com/urfave/cli v1.22.2 h1:gsqYFH8bb9ekPA12kRo0hfjngWQjkJPlN9R0N78BoUo=
6 changes: 4 additions & 2 deletions hive.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ func main() {
simLogLevel = flag.Int("sim.loglevel", 3, "Selects log `level` of client instances. Supports values 0-5.")
simDevMode = flag.Bool("dev", false, "Only starts the simulator API endpoint (listening at 127.0.0.1:3000 by default) without starting any simulators.")
simDevModeAPIEndpoint = flag.String("dev.addr", "127.0.0.1:3000", "Endpoint that the simulator API listens on")
useCredHelper = flag.Bool("docker.cred-helper", false, "configure docker authentication using locally-configured credential helper")

clients = flag.String("client", "go-ethereum", "Comma separated `list` of clients to use. Client names in the list may be given as\n"+
"just the client name, or a client_branch specifier. If a branch name is supplied,\n"+
Expand Down Expand Up @@ -71,8 +72,9 @@ func main() {

// Create the docker backends.
dockerConfig := &libdocker.Config{
Inventory: inv,
PullEnabled: *dockerPull,
Inventory: inv,
PullEnabled: *dockerPull,
UseCredentialHelper: *useCredHelper,
}
if *dockerNoCache != "" {
re, err := regexp.Compile(*dockerNoCache)
Expand Down
76 changes: 76 additions & 0 deletions internal/libdocker/authenticator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package libdocker

import (
"encoding/json"
"os"
"path"

docker "github.com/fsouza/go-dockerclient"
)

// CredHelpers represents the data stored by default in $HOME/.docker/config.json
type CredHelpers struct {
CredHelpers map[string]string `json:"credHelpers"`
CredsStore string `json:"credsStore"`
}

// Authenticator holds the configuration for docker registry authentication.
type Authenticator interface {
// AuthConfigs returns the auth configurations for all configured registries.
AuthConfigs() docker.AuthConfigurations
}

func NewCredHelperAuthenticator() (CredHelperAuthenticator, error) {
authConfigs, err := configureCredHelperAuth()
return CredHelperAuthenticator{authConfigs}, err
}

type CredHelperAuthenticator struct {
configs docker.AuthConfigurations
}

// AuthConfigs returns the auth configurations for all configured registries
func (c CredHelperAuthenticator) AuthConfigs() (a docker.AuthConfigurations) {
return c.configs
}

// configureCredHelperAuth - configures authentication for the specified registry based on $HOME/.docker/config.json
func configureCredHelperAuth() (docker.AuthConfigurations, error) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here we just load all of the cred helpers configured in $HOME/.docker/config.json so that a user doesn't need to specify them via command args

authConfigurations := make(map[string]docker.AuthConfiguration)
credsHelpers, err := loadCredsHelpers()
if err != nil {
return docker.AuthConfigurations{}, err
}

for registry := range credsHelpers.CredHelpers {
authConfig, err := docker.NewAuthConfigurationsFromCredsHelpers(registry)
if err != nil {
return docker.AuthConfigurations{
Configs: authConfigurations,
}, err
}
authConfigurations[registry] = *authConfig
}
return docker.AuthConfigurations{Configs: authConfigurations}, nil
}

func loadCredsHelpers() (c CredHelpers, err error) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Based on the go-dockerclient code, it appears this is the only way to load cred helper configs without having a command-line option for registry.

cfgPath, err := dockerConfigPath()
if err != nil {
return
}
b, err := os.ReadFile(cfgPath)
if err != nil {
return
}
err = json.Unmarshal(b, &c)
return
}

func dockerConfigPath() (string, error) {
homeDir, err := os.UserHomeDir()
if err != nil {
return homeDir, err
}
return path.Join(homeDir, ".docker", "config.json"), nil
}
69 changes: 34 additions & 35 deletions internal/libdocker/builder.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"fmt"
"io"
"io/fs"
"io/ioutil"
"os"
"path/filepath"
"strings"
Expand All @@ -21,13 +20,19 @@ import (

// Builder takes care of building docker images.
type Builder struct {
client *docker.Client
config *Config
logger log15.Logger
client *docker.Client
config *Config
logger log15.Logger
authenticator Authenticator
}

func NewBuilder(client *docker.Client, cfg *Config) *Builder {
b := &Builder{client: client, config: cfg, logger: cfg.Logger}
func NewBuilder(client *docker.Client, cfg *Config, auth Authenticator) *Builder {
b := &Builder{
client: client,
config: cfg,
logger: cfg.Logger,
authenticator: auth,
}
if b.logger == nil {
b.logger = log15.Root()
}
Expand Down Expand Up @@ -88,31 +93,38 @@ func (b *Builder) BuildSimulatorImage(ctx context.Context, name string) (string,
// BuildImage creates a container by archiving the given file system,
// which must contain a file called "Dockerfile".
func (b *Builder) BuildImage(ctx context.Context, name string, fsys fs.FS) error {
opts := b.buildConfig(ctx, name)
pipeR, pipeW := io.Pipe()
opts.InputStream = pipeR
go b.archiveFS(ctx, pipeW, fsys)

b.logger.Info("building image", "image", name, "nocache", opts.NoCache, "pull", b.config.PullEnabled)
if err := b.client.BuildImage(opts); err != nil {
b.logger.Error("image build failed", "image", name, "err", err)
return err
}
return nil
}

func (b *Builder) buildConfig(ctx context.Context, name string) docker.BuildImageOptions {
nocache := false
if b.config.NoCachePattern != nil {
nocache = b.config.NoCachePattern.MatchString(name)
}

pipeR, pipeW := io.Pipe()
go b.archiveFS(ctx, pipeW, fsys)

opts := docker.BuildImageOptions{
Context: ctx,
Name: name,
InputStream: pipeR,
OutputStream: ioutil.Discard,
OutputStream: io.Discard,
NoCache: nocache,
Pull: b.config.PullEnabled,
}
if b.authenticator != nil {
opts.AuthConfigs = b.authenticator.AuthConfigs()
}
if b.config.BuildOutput != nil {
opts.OutputStream = b.config.BuildOutput
}
b.logger.Info("building image", "image", name, "nocache", nocache, "pull", b.config.PullEnabled)
if err := b.client.BuildImage(opts); err != nil {
b.logger.Error("image build failed", "image", name, "err", err)
return err
}
return nil
return opts
}

func (b *Builder) archiveFS(ctx context.Context, out io.WriteCloser, fsys fs.FS) error {
Expand Down Expand Up @@ -219,29 +231,16 @@ func (b *Builder) ReadFile(ctx context.Context, image, path string) ([]byte, err
// buildImage builds a single docker image from the specified context.
// branch specifes a build argument to use a specific base image branch or github source branch.
func (b *Builder) buildImage(ctx context.Context, contextDir, dockerFile, branch, imageTag string) error {
nocache := false
if b.config.NoCachePattern != nil {
nocache = b.config.NoCachePattern.MatchString(imageTag)
}

logger := b.logger.New("image", imageTag)
context, err := filepath.Abs(contextDir)
if err != nil {
logger.Error("can't find path to context directory", "err", err)
return err
}
opts := docker.BuildImageOptions{
Context: ctx,
Name: imageTag,
ContextDir: context,
OutputStream: ioutil.Discard,
Dockerfile: dockerFile,
NoCache: nocache,
Pull: b.config.PullEnabled,
}
if b.config.BuildOutput != nil {
opts.OutputStream = b.config.BuildOutput
}

opts := b.buildConfig(ctx, imageTag)
opts.ContextDir = context
opts.Dockerfile = dockerFile
logctx := []interface{}{"dir", contextDir, "nocache", opts.NoCache, "pull", opts.Pull}
if branch != "" {
logctx = append(logctx, "branch", branch)
Expand Down
21 changes: 20 additions & 1 deletion internal/libdocker/docker.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ type Config struct {
// These two are log destinations for output from docker.
ContainerOutput io.Writer
BuildOutput io.Writer

// This tells the docker client whether to authenticate requests with credential helper
UseCredentialHelper bool
}

func Connect(dockerEndpoint string, cfg *Config) (*Builder, *ContainerBackend, error) {
Expand All @@ -48,7 +51,23 @@ func Connect(dockerEndpoint string, cfg *Config) (*Builder, *ContainerBackend, e
return nil, nil, fmt.Errorf("can't get docker version: %v", err)
}
logger.Debug("docker daemon online", "version", env.Get("Version"))
builder := NewBuilder(client, cfg)

builder, err := createBuilder(client, cfg)
if err != nil {
return nil, nil, err
}
backend := NewContainerBackend(client, cfg)
return builder, backend, nil
}

func createBuilder(client *docker.Client, cfg *Config) (*Builder, error) {
var auth Authenticator
var err error
if cfg.UseCredentialHelper {
auth, err = NewCredHelperAuthenticator()
if err != nil {
return nil, err
}
}
return NewBuilder(client, cfg, auth), nil
}