0
0
mirror of https://github.com/thegeeklab/wp-docker-buildx.git synced 2024-11-22 00:00:40 +00:00

refactor: switch to plugin Cmd and add tests (#175)

This commit is contained in:
Robert Kaussow 2024-05-06 20:29:51 +02:00 committed by GitHub
parent 3e74ff0c22
commit 757d2804ea
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 435 additions and 478 deletions

View File

@ -27,7 +27,7 @@ TARGETARCH ?= amd64
ifneq ("$(TARGETVARIANT)","") ifneq ("$(TARGETVARIANT)","")
GOARM ?= $(subst v,,$(TARGETVARIANT)) GOARM ?= $(subst v,,$(TARGETVARIANT))
endif endif
TAGS ?= netgo TAGS ?= netgo,osusergo
ifndef VERSION ifndef VERSION
ifneq ($(CI_COMMIT_TAG),) ifneq ($(CI_COMMIT_TAG),)

112
docker/daemon.go Normal file
View File

@ -0,0 +1,112 @@
package docker
import (
"os/exec"
"github.com/thegeeklab/wp-plugin-go/v2/types"
"github.com/urfave/cli/v2"
"golang.org/x/sys/execabs"
)
const dockerdBin = "/usr/local/bin/dockerd"
// Daemon defines Docker daemon parameters.
type Daemon struct {
Registry string // Docker registry
Mirror string // Docker registry mirror
Insecure bool // Docker daemon enable insecure registries
StorageDriver string // Docker daemon storage driver
StoragePath string // Docker daemon storage path
Disabled bool // DOcker daemon is disabled (already running)
Debug bool // Docker daemon started in debug mode
Bip string // Docker daemon network bridge IP address
DNS cli.StringSlice // Docker daemon dns server
DNSSearch cli.StringSlice // Docker daemon dns search domain
MTU string // Docker daemon mtu setting
IPv6 bool // Docker daemon IPv6 networking
Experimental bool // Docker daemon enable experimental mode
BuildkitConfigFile string // Docker buildkit config file
MaxConcurrentUploads string // Docker daemon max concurrent uploads
}
// helper function to create the docker daemon command.
func (d *Daemon) Start() *types.Cmd {
args := []string{
"--data-root", d.StoragePath,
"--host=unix:///var/run/docker.sock",
}
if d.StorageDriver != "" {
args = append(args, "-s", d.StorageDriver)
}
if d.Insecure && d.Registry != "" {
args = append(args, "--insecure-registry", d.Registry)
}
if d.IPv6 {
args = append(args, "--ipv6")
}
if d.Mirror != "" {
args = append(args, "--registry-mirror", d.Mirror)
}
if d.Bip != "" {
args = append(args, "--bip", d.Bip)
}
for _, dns := range d.DNS.Value() {
args = append(args, "--dns", dns)
}
for _, dnsSearch := range d.DNSSearch.Value() {
args = append(args, "--dns-search", dnsSearch)
}
if d.MTU != "" {
args = append(args, "--mtu", d.MTU)
}
if d.Experimental {
args = append(args, "--experimental")
}
if d.MaxConcurrentUploads != "" {
args = append(args, "--max-concurrent-uploads", d.MaxConcurrentUploads)
}
return &types.Cmd{
Cmd: execabs.Command(dockerdBin, args...),
Private: !d.Debug,
}
}
func (d *Daemon) CreateBuilder() *types.Cmd {
args := []string{
"buildx",
"create",
"--use",
}
if d.BuildkitConfigFile != "" {
args = append(args, "--config", d.BuildkitConfigFile)
}
return &types.Cmd{
Cmd: execabs.Command(dockerBin, args...),
}
}
func (d *Daemon) ListBuilder() *types.Cmd {
return &types.Cmd{
Cmd: execabs.Command(dockerBin, "buildx", "ls"),
}
}
func (d *Daemon) StartCoreDNS() *types.Cmd {
return &types.Cmd{
Cmd: exec.Command("coredns", "-conf", "/etc/coredns/Corefile"),
Private: !d.Debug,
}
}

229
docker/docker.go Normal file
View File

@ -0,0 +1,229 @@
package docker
import (
"fmt"
"os"
"strings"
"time"
"github.com/thegeeklab/wp-plugin-go/v2/types"
"github.com/urfave/cli/v2"
"golang.org/x/sys/execabs"
)
const dockerBin = "/usr/local/bin/docker"
// Login defines Docker login parameters.
type Registry struct {
Address string // Docker registry address
Username string // Docker registry username
Password string // Docker registry password
Email string // Docker registry email
Config string // Docker Auth Config
}
// Build defines Docker build parameters.
type Build struct {
Ref string // Git commit ref
Branch string // Git repository branch
Containerfile string // Docker build Containerfile
Context string // Docker build context
TagsAuto bool // Docker build auto tag
TagsSuffix string // Docker build tags with suffix
Tags cli.StringSlice // Docker build tags
ExtraTags cli.StringSlice // Docker build tags including registry
Platforms cli.StringSlice // Docker build target platforms
Args cli.StringSlice // Docker build args
ArgsEnv cli.StringSlice // Docker build args from env
Target string // Docker build target
Pull bool // Docker build pull
CacheFrom []string // Docker build cache-from
CacheTo string // Docker build cache-to
Compress bool // Docker build compress
Repo string // Docker build repository
NoCache bool // Docker build no-cache
AddHost cli.StringSlice // Docker build add-host
Quiet bool // Docker build quiet
Output string // Docker build output folder
NamedContext cli.StringSlice // Docker build named context
Labels cli.StringSlice // Docker build labels
Provenance string // Docker build provenance attestation
SBOM string // Docker build sbom attestation
Secrets []string // Docker build secrets
Dryrun bool // Docker build dryrun
}
// helper function to create the docker login command.
func (r *Registry) Login() *types.Cmd {
args := []string{
"login",
"-u", r.Username,
"-p", r.Password,
}
if r.Email != "" {
args = append(args, "-e", r.Email)
}
args = append(args, r.Address)
return &types.Cmd{
Cmd: execabs.Command(dockerBin, args...),
}
}
// helper function to create the docker info command.
func Version() *types.Cmd {
return &types.Cmd{
Cmd: execabs.Command(dockerBin, "version"),
}
}
// helper function to create the docker info command.
func Info() *types.Cmd {
return &types.Cmd{
Cmd: execabs.Command(dockerBin, "info"),
}
}
// helper function to create the docker build command.
func (b *Build) Run() *types.Cmd {
args := []string{
"buildx",
"build",
"--rm=true",
"-f", b.Containerfile,
}
defaultBuildArgs := []string{
fmt.Sprintf("DOCKER_IMAGE_CREATED=%s", time.Now().Format(time.RFC3339)),
}
args = append(args, b.Context)
if !b.Dryrun && b.Output == "" && len(b.Tags.Value()) > 0 {
args = append(args, "--push")
}
if b.Compress {
args = append(args, "--compress")
}
if b.Pull {
args = append(args, "--pull=true")
}
if b.NoCache {
args = append(args, "--no-cache")
}
for _, arg := range b.CacheFrom {
args = append(args, "--cache-from", arg)
}
if b.CacheTo != "" {
args = append(args, "--cache-to", b.CacheTo)
}
for _, arg := range b.ArgsEnv.Value() {
b.addProxyValue(arg)
}
for _, arg := range append(defaultBuildArgs, b.Args.Value()...) {
args = append(args, "--build-arg", arg)
}
for _, host := range b.AddHost.Value() {
args = append(args, "--add-host", host)
}
if b.Target != "" {
args = append(args, "--target", b.Target)
}
if b.Quiet {
args = append(args, "--quiet")
}
if b.Output != "" {
args = append(args, "--output", b.Output)
}
for _, arg := range b.NamedContext.Value() {
args = append(args, "--build-context", arg)
}
if len(b.Platforms.Value()) > 0 {
args = append(args, "--platform", strings.Join(b.Platforms.Value(), ","))
}
for _, arg := range b.Tags.Value() {
args = append(args, "-t", fmt.Sprintf("%s:%s", b.Repo, arg))
}
for _, arg := range b.ExtraTags.Value() {
args = append(args, "-t", arg)
}
for _, arg := range b.Labels.Value() {
args = append(args, "--label", arg)
}
if b.Provenance != "" {
args = append(args, "--provenance", b.Provenance)
}
if b.SBOM != "" {
args = append(args, "--sbom", b.SBOM)
}
for _, secret := range b.Secrets {
args = append(args, "--secret", secret)
}
return &types.Cmd{
Cmd: execabs.Command(dockerBin, args...),
}
}
// helper function to add proxy values from the environment.
func (b *Build) AddProxyBuildArgs() {
b.addProxyValue("http_proxy")
b.addProxyValue("https_proxy")
b.addProxyValue("no_proxy")
}
// helper function to add the upper and lower case version of a proxy value.
func (b *Build) addProxyValue(key string) {
value := b.getProxyValue(key)
if len(value) > 0 && !b.hasProxyBuildArg(key) {
b.Args = *cli.NewStringSlice(append(b.Args.Value(), fmt.Sprintf("%s=%s", key, value))...)
b.Args = *cli.NewStringSlice(append(b.Args.Value(), fmt.Sprintf("%s=%s", strings.ToUpper(key), value))...)
}
}
// helper function to get a proxy value from the environment.
//
// assumes that the upper and lower case versions of are the same.
func (b *Build) getProxyValue(key string) string {
value := os.Getenv(key)
if len(value) > 0 {
return value
}
return os.Getenv(strings.ToUpper(key))
}
// helper function that looks to see if a proxy value was set in the build args.
func (b *Build) hasProxyBuildArg(key string) bool {
keyUpper := strings.ToUpper(key)
for _, s := range b.Args.Value() {
if strings.HasPrefix(s, key) || strings.HasPrefix(s, keyUpper) {
return true
}
}
return false
}

2
go.mod
View File

@ -5,7 +5,7 @@ go 1.22
require ( require (
github.com/cenkalti/backoff/v4 v4.3.0 github.com/cenkalti/backoff/v4 v4.3.0
github.com/rs/zerolog v1.32.0 github.com/rs/zerolog v1.32.0
github.com/thegeeklab/wp-plugin-go/v2 v2.0.1 github.com/thegeeklab/wp-plugin-go/v2 v2.3.0
github.com/urfave/cli/v2 v2.27.2 github.com/urfave/cli/v2 v2.27.2
golang.org/x/sys v0.20.0 golang.org/x/sys v0.20.0
) )

4
go.sum
View File

@ -48,8 +48,8 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/thegeeklab/wp-plugin-go/v2 v2.0.1 h1:42kqe5U1x5Ysa9I8tDEhh+tyvfFkfXKvlb3UsigBmN4= github.com/thegeeklab/wp-plugin-go/v2 v2.3.0 h1:9LOdITzjxEEbgcH9yU0tDZL0dcDZzNYzbk2+hZ5QsBA=
github.com/thegeeklab/wp-plugin-go/v2 v2.0.1/go.mod h1:KRfDolkPSpO7Zx54Y0ofTFA7Cvd+7bHTHzYnYAo9WYg= github.com/thegeeklab/wp-plugin-go/v2 v2.3.0/go.mod h1:I/3M/4OPvr4FFS+s0aaImpX1llA/lS2KC6Bnp+qzsCs=
github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI= github.com/urfave/cli/v2 v2.27.2 h1:6e0H+AkS+zDckwPCUrZkKX38mRaau4nL2uipkJpbkcI=
github.com/urfave/cli/v2 v2.27.2/go.mod h1:g0+79LmHHATl7DAcHO99smiR/T7uGLw84w8Y42x+4eM= github.com/urfave/cli/v2 v2.27.2/go.mod h1:g0+79LmHHATl7DAcHO99smiR/T7uGLw84w8Y42x+4eM=
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 h1:+qGGcbkzsfDQNPPe9UDgpxAWQrhbbBXOYJFQDq/dtJw= github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 h1:+qGGcbkzsfDQNPPe9UDgpxAWQrhbbBXOYJFQDq/dtJw=

View File

@ -1,42 +0,0 @@
package plugin
import (
"io"
"net"
"os"
"os/exec"
"github.com/thegeeklab/wp-plugin-go/v2/trace"
)
func (p Plugin) startCoredns() {
cmd := exec.Command("coredns", "-conf", "/etc/coredns/Corefile")
if p.Settings.Daemon.Debug {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
} else {
cmd.Stdout = io.Discard
cmd.Stderr = io.Discard
}
go func() {
trace.Cmd(cmd)
_ = cmd.Run()
}()
}
func getContainerIP() (string, error) {
netInterfaceAddrList, err := net.InterfaceAddrs()
if err != nil {
return "", err
}
for _, netInterfaceAddr := range netInterfaceAddrList {
netIP, ok := netInterfaceAddr.(*net.IPNet)
if ok && !netIP.IP.IsLoopback() && netIP.IP.To4() != nil {
return netIP.IP.String(), nil
}
}
return "", nil
}

View File

@ -1,31 +0,0 @@
package plugin
import (
"io"
"os"
"github.com/thegeeklab/wp-plugin-go/v2/trace"
)
const (
dockerBin = "/usr/local/bin/docker"
dockerdBin = "/usr/local/bin/dockerd"
dockerHome = "/root/.docker/"
buildkitConfig = "/tmp/buildkit.toml"
)
func (p Plugin) startDaemon() {
cmd := commandDaemon(p.Settings.Daemon)
if p.Settings.Daemon.Debug {
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
} else {
cmd.Stdout = io.Discard
cmd.Stderr = io.Discard
}
go func() {
trace.Cmd(cmd.Cmd)
_ = cmd.Run()
}()
}

View File

@ -1,273 +0,0 @@
package plugin
import (
"fmt"
"os"
"strings"
"time"
"github.com/urfave/cli/v2"
"golang.org/x/sys/execabs"
)
// helper function to create the docker login command.
func commandLogin(login Login) *Cmd {
if login.Email != "" {
return commandLoginEmail(login)
}
args := []string{
"login",
"-u", login.Username,
"-p", login.Password,
login.Registry,
}
return &Cmd{
Cmd: execabs.Command(dockerBin, args...),
}
}
func commandLoginEmail(login Login) *Cmd {
args := []string{
"login",
"-u", login.Username,
"-p", login.Password,
"-e", login.Email,
login.Registry,
}
return &Cmd{
Cmd: execabs.Command(dockerBin, args...),
}
}
// helper function to create the docker info command.
func commandVersion() *Cmd {
return &Cmd{
Cmd: execabs.Command(dockerBin, "version"),
}
}
// helper function to create the docker info command.
func commandInfo() *Cmd {
return &Cmd{
Cmd: execabs.Command(dockerBin, "info"),
}
}
func commandBuilder(daemon Daemon) *Cmd {
args := []string{
"buildx",
"create",
"--use",
}
if daemon.BuildkitConfig != "" {
args = append(args, "--config", buildkitConfig)
}
return &Cmd{
Cmd: execabs.Command(dockerBin, args...),
}
}
func commandBuildx() *Cmd {
return &Cmd{
Cmd: execabs.Command(dockerBin, "buildx", "ls"),
}
}
// helper function to create the docker build command.
func commandBuild(build Build, dryrun bool) *Cmd {
args := []string{
"buildx",
"build",
"--rm=true",
"-f", build.Containerfile,
}
defaultBuildArgs := []string{
fmt.Sprintf("DOCKER_IMAGE_CREATED=%s", time.Now().Format(time.RFC3339)),
}
args = append(args, build.Context)
if !dryrun && build.Output == "" && len(build.Tags.Value()) > 0 {
args = append(args, "--push")
}
if build.Compress {
args = append(args, "--compress")
}
if build.Pull {
args = append(args, "--pull=true")
}
if build.NoCache {
args = append(args, "--no-cache")
}
for _, arg := range build.CacheFrom {
args = append(args, "--cache-from", arg)
}
if build.CacheTo != "" {
args = append(args, "--cache-to", build.CacheTo)
}
for _, arg := range build.ArgsEnv.Value() {
addProxyValue(&build, arg)
}
for _, arg := range append(defaultBuildArgs, build.Args.Value()...) {
args = append(args, "--build-arg", arg)
}
for _, host := range build.AddHost.Value() {
args = append(args, "--add-host", host)
}
if build.Target != "" {
args = append(args, "--target", build.Target)
}
if build.Quiet {
args = append(args, "--quiet")
}
if build.Output != "" {
args = append(args, "--output", build.Output)
}
for _, arg := range build.NamedContext.Value() {
args = append(args, "--build-context", arg)
}
if len(build.Platforms.Value()) > 0 {
args = append(args, "--platform", strings.Join(build.Platforms.Value(), ","))
}
for _, arg := range build.Tags.Value() {
args = append(args, "-t", fmt.Sprintf("%s:%s", build.Repo, arg))
}
for _, arg := range build.ExtraTags.Value() {
args = append(args, "-t", arg)
}
for _, arg := range build.Labels.Value() {
args = append(args, "--label", arg)
}
if build.Provenance != "" {
args = append(args, "--provenance", build.Provenance)
}
if build.SBOM != "" {
args = append(args, "--sbom", build.SBOM)
}
for _, secret := range build.Secrets {
args = append(args, "--secret", secret)
}
return &Cmd{
Cmd: execabs.Command(dockerBin, args...),
}
}
// helper function to add proxy values from the environment.
func addProxyBuildArgs(build *Build) {
addProxyValue(build, "http_proxy")
addProxyValue(build, "https_proxy")
addProxyValue(build, "no_proxy")
}
// helper function to add the upper and lower case version of a proxy value.
func addProxyValue(build *Build, key string) {
value := getProxyValue(key)
if len(value) > 0 && !hasProxyBuildArg(build, key) {
build.Args = *cli.NewStringSlice(append(build.Args.Value(), fmt.Sprintf("%s=%s", key, value))...)
build.Args = *cli.NewStringSlice(append(build.Args.Value(), fmt.Sprintf("%s=%s", strings.ToUpper(key), value))...)
}
}
// helper function to get a proxy value from the environment.
//
// assumes that the upper and lower case versions of are the same.
func getProxyValue(key string) string {
value := os.Getenv(key)
if len(value) > 0 {
return value
}
return os.Getenv(strings.ToUpper(key))
}
// helper function that looks to see if a proxy value was set in the build args.
func hasProxyBuildArg(build *Build, key string) bool {
keyUpper := strings.ToUpper(key)
for _, s := range build.Args.Value() {
if strings.HasPrefix(s, key) || strings.HasPrefix(s, keyUpper) {
return true
}
}
return false
}
// helper function to create the docker daemon command.
func commandDaemon(daemon Daemon) *Cmd {
args := []string{
"--data-root", daemon.StoragePath,
"--host=unix:///var/run/docker.sock",
}
if daemon.StorageDriver != "" {
args = append(args, "-s", daemon.StorageDriver)
}
if daemon.Insecure && daemon.Registry != "" {
args = append(args, "--insecure-registry", daemon.Registry)
}
if daemon.IPv6 {
args = append(args, "--ipv6")
}
if daemon.Mirror != "" {
args = append(args, "--registry-mirror", daemon.Mirror)
}
if daemon.Bip != "" {
args = append(args, "--bip", daemon.Bip)
}
for _, dns := range daemon.DNS.Value() {
args = append(args, "--dns", dns)
}
for _, dnsSearch := range daemon.DNSSearch.Value() {
args = append(args, "--dns-search", dnsSearch)
}
if daemon.MTU != "" {
args = append(args, "--mtu", daemon.MTU)
}
if daemon.Experimental {
args = append(args, "--experimental")
}
if daemon.MaxConcurrentUploads != "" {
args = append(args, "--max-concurrent-uploads", daemon.MaxConcurrentUploads)
}
return &Cmd{
Cmd: execabs.Command(dockerdBin, args...),
}
}

View File

@ -5,14 +5,15 @@ import (
"errors" "errors"
"fmt" "fmt"
"os" "os"
"path/filepath"
"time" "time"
"github.com/cenkalti/backoff/v4" "github.com/cenkalti/backoff/v4"
"github.com/rs/zerolog/log" "github.com/rs/zerolog/log"
"github.com/thegeeklab/wp-docker-buildx/docker"
"github.com/thegeeklab/wp-plugin-go/v2/file"
"github.com/thegeeklab/wp-plugin-go/v2/tag" "github.com/thegeeklab/wp-plugin-go/v2/tag"
"github.com/thegeeklab/wp-plugin-go/v2/trace"
"github.com/thegeeklab/wp-plugin-go/v2/types" "github.com/thegeeklab/wp-plugin-go/v2/types"
"github.com/thegeeklab/wp-plugin-go/v2/util"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
) )
@ -46,7 +47,7 @@ func (p *Plugin) run(ctx context.Context) error {
func (p *Plugin) Validate() error { func (p *Plugin) Validate() error {
p.Settings.Build.Branch = p.Metadata.Repository.Branch p.Settings.Build.Branch = p.Metadata.Repository.Branch
p.Settings.Build.Ref = p.Metadata.Curr.Ref p.Settings.Build.Ref = p.Metadata.Curr.Ref
p.Settings.Daemon.Registry = p.Settings.Login.Registry p.Settings.Daemon.Registry = p.Settings.Registry.Address
if p.Settings.Build.TagsAuto { if p.Settings.Build.TagsAuto {
// return true if tag event or default branch // return true if tag event or default branch
@ -75,24 +76,29 @@ func (p *Plugin) Validate() error {
} }
// Execute provides the implementation of the plugin. // Execute provides the implementation of the plugin.
//
//nolint:gocognit
func (p *Plugin) Execute() error { func (p *Plugin) Execute() error {
batchCmd := make([]*Cmd, 0) var err error
homeDir := util.GetUserHomeDir()
batchCmd := make([]*types.Cmd, 0)
// start the Docker daemon server // start the Docker daemon server
//nolint: nestif //nolint: nestif
if !p.Settings.Daemon.Disabled { if !p.Settings.Daemon.Disabled {
// If no custom DNS value set start internal DNS server // If no custom DNS value set start internal DNS server
if len(p.Settings.Daemon.DNS.Value()) == 0 { if len(p.Settings.Daemon.DNS.Value()) == 0 {
ip, err := getContainerIP() ip, err := GetContainerIP()
if err != nil { if err != nil {
log.Warn().Msgf("error detecting IP address: %v", err) log.Warn().Msgf("error detecting IP address: %v", err)
} }
if ip != "" { if ip != "" {
log.Debug().Msgf("discovered IP address: %v", ip) log.Debug().Msgf("discovered IP address: %v", ip)
p.startCoredns()
cmd := p.Settings.Daemon.StartCoreDNS()
go func() {
_ = cmd.Run()
}()
if err := p.Settings.Daemon.DNS.Set(ip); err != nil { if err := p.Settings.Daemon.DNS.Set(ip); err != nil {
return fmt.Errorf("error setting daemon dns: %w", err) return fmt.Errorf("error setting daemon dns: %w", err)
@ -100,13 +106,16 @@ func (p *Plugin) Execute() error {
} }
} }
p.startDaemon() cmd := p.Settings.Daemon.Start()
go func() {
_ = cmd.Run()
}()
} }
// poll the docker daemon until it is started. This ensures the daemon is // poll the docker daemon until it is started. This ensures the daemon is
// ready to accept connections before we proceed. // ready to accept connections before we proceed.
for i := 0; i < 15; i++ { for i := 0; i < 15; i++ {
cmd := commandInfo() cmd := docker.Info()
err := cmd.Run() err := cmd.Run()
if err == nil { if err == nil {
@ -116,57 +125,40 @@ func (p *Plugin) Execute() error {
time.Sleep(time.Second * 1) time.Sleep(time.Second * 1)
} }
// Create Auth Config File if p.Settings.Registry.Config != "" {
if p.Settings.Login.Config != "" { if err := WriteDockerConf(homeDir, p.Settings.Registry.Config); err != nil {
if err := os.MkdirAll(dockerHome, strictFilePerm); err != nil { return fmt.Errorf("error writing docker config: %w", err)
return fmt.Errorf("failed to create docker home: %w", err)
}
path := filepath.Join(dockerHome, "config.json")
err := os.WriteFile(path, []byte(p.Settings.Login.Config), strictFilePerm)
if err != nil {
return fmt.Errorf("error writing config.json: %w", err)
} }
} }
// login to the Docker registry if p.Settings.Registry.Password != "" {
if p.Settings.Login.Password != "" { if err := p.Settings.Registry.Login().Run(); err != nil {
cmd := commandLogin(p.Settings.Login)
err := cmd.Run()
if err != nil {
return fmt.Errorf("error authenticating: %w", err) return fmt.Errorf("error authenticating: %w", err)
} }
} }
if p.Settings.Daemon.BuildkitConfig != "" { buildkitConf := p.Settings.BuildkitConfig
err := os.WriteFile(buildkitConfig, []byte(p.Settings.Daemon.BuildkitConfig), strictFilePerm) if buildkitConf != "" {
if err != nil { if p.Settings.Daemon.BuildkitConfigFile, err = file.WriteTmpFile("buildkit.toml", buildkitConf); err != nil {
return fmt.Errorf("error writing buildkit.toml: %w", err) return fmt.Errorf("error writing buildkit config: %w", err)
} }
defer os.Remove(p.Settings.Daemon.BuildkitConfigFile)
} }
switch { switch {
case p.Settings.Login.Password != "": case p.Settings.Registry.Password != "":
log.Info().Msgf("Detected registry credentials") log.Info().Msgf("Detected registry credentials")
case p.Settings.Login.Config != "": case p.Settings.Registry.Config != "":
log.Info().Msgf("Detected registry credentials file") log.Info().Msgf("Detected registry credentials file")
default: default:
log.Info().Msgf("Registry credentials or Docker config not provided. Guest mode enabled.") log.Info().Msgf("Registry credentials or Docker config not provided. Guest mode enabled.")
} }
// add proxy build args p.Settings.Build.AddProxyBuildArgs()
addProxyBuildArgs(&p.Settings.Build)
backoffOps := func() error { backoffOps := func() error {
versionCmd := commandVersion() // docker version return docker.Version().Run()
versionCmd.Stdout = os.Stdout
versionCmd.Stderr = os.Stderr
trace.Cmd(versionCmd.Cmd)
return versionCmd.Run()
} }
backoffLog := func(err error, delay time.Duration) { backoffLog := func(err error, delay time.Duration) {
log.Error().Msgf("failed to run docker version command: %v: retry in %s", err, delay.Truncate(time.Second)) log.Error().Msgf("failed to run docker version command: %v: retry in %s", err, delay.Truncate(time.Second))
@ -176,19 +168,13 @@ func (p *Plugin) Execute() error {
return err return err
} }
batchCmd = append(batchCmd, commandInfo()) // docker info batchCmd = append(batchCmd, docker.Info())
batchCmd = append(batchCmd, commandBuilder(p.Settings.Daemon)) batchCmd = append(batchCmd, p.Settings.Daemon.CreateBuilder())
batchCmd = append(batchCmd, commandBuildx()) batchCmd = append(batchCmd, p.Settings.Daemon.ListBuilder())
batchCmd = append(batchCmd, commandBuild(p.Settings.Build, p.Settings.Dryrun)) // docker build batchCmd = append(batchCmd, p.Settings.Build.Run())
// execute all commands in batch mode. for _, cmd := range batchCmd {
for _, bc := range batchCmd { if err := cmd.Run(); err != nil {
bc.Stdout = os.Stdout
bc.Stderr = os.Stderr
trace.Cmd(bc.Cmd)
err := bc.Run()
if err != nil {
return err return err
} }
} }

View File

@ -3,10 +3,10 @@ package plugin
import ( import (
"fmt" "fmt"
"github.com/thegeeklab/wp-docker-buildx/docker"
wp "github.com/thegeeklab/wp-plugin-go/v2/plugin" wp "github.com/thegeeklab/wp-plugin-go/v2/plugin"
"github.com/thegeeklab/wp-plugin-go/v2/types" "github.com/thegeeklab/wp-plugin-go/v2/types"
"github.com/urfave/cli/v2" "github.com/urfave/cli/v2"
"golang.org/x/sys/execabs"
) )
//go:generate go run ../internal/docs/main.go -output=../docs/data/data-raw.yaml //go:generate go run ../internal/docs/main.go -output=../docs/data/data-raw.yaml
@ -19,73 +19,11 @@ type Plugin struct {
// Settings for the Plugin. // Settings for the Plugin.
type Settings struct { type Settings struct {
Daemon Daemon BuildkitConfig string
Login Login
Build Build
Dryrun bool
}
// Daemon defines Docker daemon parameters. Daemon docker.Daemon
type Daemon struct { Registry docker.Registry
Registry string // Docker registry Build docker.Build
Mirror string // Docker registry mirror
Insecure bool // Docker daemon enable insecure registries
StorageDriver string // Docker daemon storage driver
StoragePath string // Docker daemon storage path
Disabled bool // DOcker daemon is disabled (already running)
Debug bool // Docker daemon started in debug mode
Bip string // Docker daemon network bridge IP address
DNS cli.StringSlice // Docker daemon dns server
DNSSearch cli.StringSlice // Docker daemon dns search domain
MTU string // Docker daemon mtu setting
IPv6 bool // Docker daemon IPv6 networking
Experimental bool // Docker daemon enable experimental mode
BuildkitConfig string // Docker buildkit config
MaxConcurrentUploads string
}
// Login defines Docker login parameters.
type Login struct {
Registry string // Docker registry address
Username string // Docker registry username
Password string // Docker registry password
Email string // Docker registry email
Config string // Docker Auth Config
}
// Build defines Docker build parameters.
type Build struct {
Ref string // Git commit ref
Branch string // Git repository branch
Containerfile string // Docker build Containerfile
Context string // Docker build context
TagsAuto bool // Docker build auto tag
TagsSuffix string // Docker build tags with suffix
Tags cli.StringSlice // Docker build tags
ExtraTags cli.StringSlice // Docker build tags including registry
Platforms cli.StringSlice // Docker build target platforms
Args cli.StringSlice // Docker build args
ArgsEnv cli.StringSlice // Docker build args from env
Target string // Docker build target
Pull bool // Docker build pull
CacheFrom []string // Docker build cache-from
CacheTo string // Docker build cache-to
Compress bool // Docker build compress
Repo string // Docker build repository
NoCache bool // Docker build no-cache
AddHost cli.StringSlice // Docker build add-host
Quiet bool // Docker build quiet
Output string // Docker build output folder
NamedContext cli.StringSlice // Docker build named context
Labels cli.StringSlice // Docker build labels
Provenance string // Docker build provenance attestation
SBOM string // Docker build sbom attestation
Secrets []string // Docker build secrets
}
type Cmd struct {
*execabs.Cmd
Private bool
} }
func New(e wp.ExecuteFunc, build ...string) *Plugin { func New(e wp.ExecuteFunc, build ...string) *Plugin {
@ -127,7 +65,7 @@ func Flags(settings *Settings, category string) []cli.Flag {
Name: "dry-run", Name: "dry-run",
EnvVars: []string{"PLUGIN_DRY_RUN"}, EnvVars: []string{"PLUGIN_DRY_RUN"},
Usage: "disable docker push", Usage: "disable docker push",
Destination: &settings.Dryrun, Destination: &settings.Build.Dryrun,
Category: category, Category: category,
}, },
&cli.StringFlag{ &cli.StringFlag{
@ -225,7 +163,7 @@ func Flags(settings *Settings, category string) []cli.Flag {
Name: "daemon.buildkit-config", Name: "daemon.buildkit-config",
EnvVars: []string{"PLUGIN_BUILDKIT_CONFIG"}, EnvVars: []string{"PLUGIN_BUILDKIT_CONFIG"},
Usage: "content of the docker buildkit toml config", Usage: "content of the docker buildkit toml config",
Destination: &settings.Daemon.BuildkitConfig, Destination: &settings.BuildkitConfig,
Category: category, Category: category,
}, },
&cli.StringFlag{ &cli.StringFlag{
@ -367,14 +305,14 @@ func Flags(settings *Settings, category string) []cli.Flag {
EnvVars: []string{"PLUGIN_REGISTRY", "DOCKER_REGISTRY"}, EnvVars: []string{"PLUGIN_REGISTRY", "DOCKER_REGISTRY"},
Usage: "docker registry to authenticate with", Usage: "docker registry to authenticate with",
Value: "https://index.docker.io/v1/", Value: "https://index.docker.io/v1/",
Destination: &settings.Login.Registry, Destination: &settings.Registry.Address,
Category: category, Category: category,
}, },
&cli.StringFlag{ &cli.StringFlag{
Name: "docker.username", Name: "docker.username",
EnvVars: []string{"PLUGIN_USERNAME", "DOCKER_USERNAME"}, EnvVars: []string{"PLUGIN_USERNAME", "DOCKER_USERNAME"},
Usage: "username for registry authentication", Usage: "username for registry authentication",
Destination: &settings.Login.Username, Destination: &settings.Registry.Username,
DefaultText: "$DOCKER_USERNAME", DefaultText: "$DOCKER_USERNAME",
Category: category, Category: category,
}, },
@ -382,7 +320,7 @@ func Flags(settings *Settings, category string) []cli.Flag {
Name: "docker.password", Name: "docker.password",
EnvVars: []string{"PLUGIN_PASSWORD", "DOCKER_PASSWORD"}, EnvVars: []string{"PLUGIN_PASSWORD", "DOCKER_PASSWORD"},
Usage: "password for registry authentication", Usage: "password for registry authentication",
Destination: &settings.Login.Password, Destination: &settings.Registry.Password,
DefaultText: "$DOCKER_PASSWORD", DefaultText: "$DOCKER_PASSWORD",
Category: category, Category: category,
}, },
@ -390,7 +328,7 @@ func Flags(settings *Settings, category string) []cli.Flag {
Name: "docker.email", Name: "docker.email",
EnvVars: []string{"PLUGIN_EMAIL", "DOCKER_EMAIL"}, EnvVars: []string{"PLUGIN_EMAIL", "DOCKER_EMAIL"},
Usage: "email address for registry authentication", Usage: "email address for registry authentication",
Destination: &settings.Login.Email, Destination: &settings.Registry.Email,
DefaultText: "$DOCKER_EMAIL", DefaultText: "$DOCKER_EMAIL",
Category: category, Category: category,
}, },
@ -398,7 +336,7 @@ func Flags(settings *Settings, category string) []cli.Flag {
Name: "docker.config", Name: "docker.config",
EnvVars: []string{"PLUGIN_CONFIG", "DOCKER_PLUGIN_CONFIG"}, EnvVars: []string{"PLUGIN_CONFIG", "DOCKER_PLUGIN_CONFIG"},
Usage: "content of the docker daemon json config", Usage: "content of the docker daemon json config",
Destination: &settings.Login.Config, Destination: &settings.Registry.Config,
DefaultText: "$DOCKER_PLUGIN_CONFIG", DefaultText: "$DOCKER_PLUGIN_CONFIG",
Category: category, Category: category,
}, },

38
plugin/util.go Normal file
View File

@ -0,0 +1,38 @@
package plugin
import (
"net"
"os"
"path/filepath"
)
func GetContainerIP() (string, error) {
netInterfaceAddrList, err := net.InterfaceAddrs()
if err != nil {
return "", err
}
for _, netInterfaceAddr := range netInterfaceAddrList {
netIP, ok := netInterfaceAddr.(*net.IPNet)
if ok && !netIP.IP.IsLoopback() && netIP.IP.To4() != nil {
return netIP.IP.String(), nil
}
}
return "", nil
}
func WriteDockerConf(path, conf string) error {
confPath := filepath.Join(path, ".docker", "config.json")
if err := os.MkdirAll(confPath, strictFilePerm); err != nil {
return err
}
err := os.WriteFile(path, []byte(conf), strictFilePerm)
if err != nil {
return err
}
return nil
}