Compare commits

...

3 Commits

12 changed files with 443 additions and 486 deletions

View File

@ -27,7 +27,7 @@ TARGETARCH ?= amd64
ifneq ("$(TARGETVARIANT)","")
GOARM ?= $(subst v,,$(TARGETVARIANT))
endif
TAGS ?= netgo
TAGS ?= netgo,osusergo
ifndef VERSION
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
}

6
go.mod
View File

@ -5,7 +5,7 @@ go 1.22
require (
github.com/cenkalti/backoff/v4 v4.3.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.1
github.com/urfave/cli/v2 v2.27.2
golang.org/x/sys v0.20.0
)
@ -27,6 +27,6 @@ require (
github.com/shopspring/decimal v1.2.0 // indirect
github.com/spf13/cast v1.3.1 // indirect
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 // indirect
golang.org/x/crypto v0.22.0 // indirect
golang.org/x/net v0.24.0 // indirect
golang.org/x/crypto v0.23.0 // indirect
golang.org/x/net v0.25.0 // indirect
)

12
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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
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.0.1/go.mod h1:KRfDolkPSpO7Zx54Y0ofTFA7Cvd+7bHTHzYnYAo9WYg=
github.com/thegeeklab/wp-plugin-go/v2 v2.3.1 h1:ARwYgTPZ5iPsmOenmqcCf8TjiEe8wBOHKO7H/Xshe48=
github.com/thegeeklab/wp-plugin-go/v2 v2.3.1/go.mod h1:0t8M8txtEFiaB6RqLX8vLrxkqAo5FT5Hx7dztN592D4=
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/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 h1:+qGGcbkzsfDQNPPe9UDgpxAWQrhbbBXOYJFQDq/dtJw=
@ -58,15 +58,15 @@ github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5t
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=

View File

@ -12,7 +12,7 @@ import (
"github.com/thegeeklab/wp-docker-buildx/plugin"
"github.com/thegeeklab/wp-plugin-go/v2/docs"
wp_template "github.com/thegeeklab/wp-plugin-go/v2/template"
"github.com/thegeeklab/wp-plugin-go/v2/template"
)
func main() {
@ -23,7 +23,7 @@ func main() {
p := plugin.New(nil)
out, err := wp_template.Render(context.Background(), client, tmpl, docs.GetTemplateData(p.App))
out, err := template.Render(context.Background(), client, tmpl, docs.GetTemplateData(p.App))
if err != nil {
panic(err)
}

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"
"fmt"
"os"
"path/filepath"
"time"
"github.com/cenkalti/backoff/v4"
"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/trace"
"github.com/thegeeklab/wp-plugin-go/v2/types"
"github.com/thegeeklab/wp-plugin-go/v2/util"
"github.com/urfave/cli/v2"
)
@ -46,7 +47,7 @@ func (p *Plugin) run(ctx context.Context) error {
func (p *Plugin) Validate() error {
p.Settings.Build.Branch = p.Metadata.Repository.Branch
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 {
// return true if tag event or default branch
@ -75,24 +76,29 @@ func (p *Plugin) Validate() error {
}
// Execute provides the implementation of the plugin.
//
//nolint:gocognit
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
//nolint: nestif
if !p.Settings.Daemon.Disabled {
// If no custom DNS value set start internal DNS server
if len(p.Settings.Daemon.DNS.Value()) == 0 {
ip, err := getContainerIP()
ip, err := GetContainerIP()
if err != nil {
log.Warn().Msgf("error detecting IP address: %v", err)
}
if 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 {
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
// ready to accept connections before we proceed.
for i := 0; i < 15; i++ {
cmd := commandInfo()
cmd := docker.Info()
err := cmd.Run()
if err == nil {
@ -116,57 +125,40 @@ func (p *Plugin) Execute() error {
time.Sleep(time.Second * 1)
}
// Create Auth Config File
if p.Settings.Login.Config != "" {
if err := os.MkdirAll(dockerHome, strictFilePerm); err != nil {
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)
if p.Settings.Registry.Config != "" {
if err := WriteDockerConf(homeDir, p.Settings.Registry.Config); err != nil {
return fmt.Errorf("error writing docker config: %w", err)
}
}
// login to the Docker registry
if p.Settings.Login.Password != "" {
cmd := commandLogin(p.Settings.Login)
err := cmd.Run()
if err != nil {
if p.Settings.Registry.Password != "" {
if err := p.Settings.Registry.Login().Run(); err != nil {
return fmt.Errorf("error authenticating: %w", err)
}
}
if p.Settings.Daemon.BuildkitConfig != "" {
err := os.WriteFile(buildkitConfig, []byte(p.Settings.Daemon.BuildkitConfig), strictFilePerm)
if err != nil {
return fmt.Errorf("error writing buildkit.toml: %w", err)
buildkitConf := p.Settings.BuildkitConfig
if buildkitConf != "" {
if p.Settings.Daemon.BuildkitConfigFile, err = file.WriteTmpFile("buildkit.toml", buildkitConf); err != nil {
return fmt.Errorf("error writing buildkit config: %w", err)
}
defer os.Remove(p.Settings.Daemon.BuildkitConfigFile)
}
switch {
case p.Settings.Login.Password != "":
case p.Settings.Registry.Password != "":
log.Info().Msgf("Detected registry credentials")
case p.Settings.Login.Config != "":
case p.Settings.Registry.Config != "":
log.Info().Msgf("Detected registry credentials file")
default:
log.Info().Msgf("Registry credentials or Docker config not provided. Guest mode enabled.")
}
// add proxy build args
addProxyBuildArgs(&p.Settings.Build)
p.Settings.Build.AddProxyBuildArgs()
backoffOps := func() error {
versionCmd := commandVersion() // docker version
versionCmd.Stdout = os.Stdout
versionCmd.Stderr = os.Stderr
trace.Cmd(versionCmd.Cmd)
return versionCmd.Run()
return docker.Version().Run()
}
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))
@ -176,19 +168,13 @@ func (p *Plugin) Execute() error {
return err
}
batchCmd = append(batchCmd, commandInfo()) // docker info
batchCmd = append(batchCmd, commandBuilder(p.Settings.Daemon))
batchCmd = append(batchCmd, commandBuildx())
batchCmd = append(batchCmd, commandBuild(p.Settings.Build, p.Settings.Dryrun)) // docker build
batchCmd = append(batchCmd, docker.Info())
batchCmd = append(batchCmd, p.Settings.Daemon.CreateBuilder())
batchCmd = append(batchCmd, p.Settings.Daemon.ListBuilder())
batchCmd = append(batchCmd, p.Settings.Build.Run())
// execute all commands in batch mode.
for _, bc := range batchCmd {
bc.Stdout = os.Stdout
bc.Stderr = os.Stderr
trace.Cmd(bc.Cmd)
err := bc.Run()
if err != nil {
for _, cmd := range batchCmd {
if err := cmd.Run(); err != nil {
return err
}
}

View File

@ -3,10 +3,10 @@ package plugin
import (
"fmt"
"github.com/thegeeklab/wp-docker-buildx/docker"
wp "github.com/thegeeklab/wp-plugin-go/v2/plugin"
"github.com/thegeeklab/wp-plugin-go/v2/types"
"github.com/urfave/cli/v2"
"golang.org/x/sys/execabs"
)
//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.
type Settings struct {
Daemon Daemon
Login Login
Build Build
Dryrun bool
}
BuildkitConfig string
// 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
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
Daemon docker.Daemon
Registry docker.Registry
Build docker.Build
}
func New(e wp.ExecuteFunc, build ...string) *Plugin {
@ -127,7 +65,7 @@ func Flags(settings *Settings, category string) []cli.Flag {
Name: "dry-run",
EnvVars: []string{"PLUGIN_DRY_RUN"},
Usage: "disable docker push",
Destination: &settings.Dryrun,
Destination: &settings.Build.Dryrun,
Category: category,
},
&cli.StringFlag{
@ -225,7 +163,7 @@ func Flags(settings *Settings, category string) []cli.Flag {
Name: "daemon.buildkit-config",
EnvVars: []string{"PLUGIN_BUILDKIT_CONFIG"},
Usage: "content of the docker buildkit toml config",
Destination: &settings.Daemon.BuildkitConfig,
Destination: &settings.BuildkitConfig,
Category: category,
},
&cli.StringFlag{
@ -367,14 +305,14 @@ func Flags(settings *Settings, category string) []cli.Flag {
EnvVars: []string{"PLUGIN_REGISTRY", "DOCKER_REGISTRY"},
Usage: "docker registry to authenticate with",
Value: "https://index.docker.io/v1/",
Destination: &settings.Login.Registry,
Destination: &settings.Registry.Address,
Category: category,
},
&cli.StringFlag{
Name: "docker.username",
EnvVars: []string{"PLUGIN_USERNAME", "DOCKER_USERNAME"},
Usage: "username for registry authentication",
Destination: &settings.Login.Username,
Destination: &settings.Registry.Username,
DefaultText: "$DOCKER_USERNAME",
Category: category,
},
@ -382,7 +320,7 @@ func Flags(settings *Settings, category string) []cli.Flag {
Name: "docker.password",
EnvVars: []string{"PLUGIN_PASSWORD", "DOCKER_PASSWORD"},
Usage: "password for registry authentication",
Destination: &settings.Login.Password,
Destination: &settings.Registry.Password,
DefaultText: "$DOCKER_PASSWORD",
Category: category,
},
@ -390,7 +328,7 @@ func Flags(settings *Settings, category string) []cli.Flag {
Name: "docker.email",
EnvVars: []string{"PLUGIN_EMAIL", "DOCKER_EMAIL"},
Usage: "email address for registry authentication",
Destination: &settings.Login.Email,
Destination: &settings.Registry.Email,
DefaultText: "$DOCKER_EMAIL",
Category: category,
},
@ -398,7 +336,7 @@ func Flags(settings *Settings, category string) []cli.Flag {
Name: "docker.config",
EnvVars: []string{"PLUGIN_CONFIG", "DOCKER_PLUGIN_CONFIG"},
Usage: "content of the docker daemon json config",
Destination: &settings.Login.Config,
Destination: &settings.Registry.Config,
DefaultText: "$DOCKER_PLUGIN_CONFIG",
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
}