mirror of
https://github.com/thegeeklab/wp-docker-buildx.git
synced 2024-11-24 23:00:39 +00:00
refactor: switch to plugin Cmd and add tests (#175)
This commit is contained in:
parent
3e74ff0c22
commit
757d2804ea
2
Makefile
2
Makefile
@ -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
112
docker/daemon.go
Normal 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
229
docker/docker.go
Normal 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
2
go.mod
@ -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.0
|
||||
github.com/urfave/cli/v2 v2.27.2
|
||||
golang.org/x/sys v0.20.0
|
||||
)
|
||||
|
4
go.sum
4
go.sum
@ -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.0 h1:9LOdITzjxEEbgcH9yU0tDZL0dcDZzNYzbk2+hZ5QsBA=
|
||||
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/go.mod h1:g0+79LmHHATl7DAcHO99smiR/T7uGLw84w8Y42x+4eM=
|
||||
github.com/xrash/smetrics v0.0.0-20240312152122-5f08fbb34913 h1:+qGGcbkzsfDQNPPe9UDgpxAWQrhbbBXOYJFQDq/dtJw=
|
||||
|
@ -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
|
||||
}
|
@ -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()
|
||||
}()
|
||||
}
|
273
plugin/docker.go
273
plugin/docker.go
@ -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...),
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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
38
plugin/util.go
Normal 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
|
||||
}
|
Loading…
Reference in New Issue
Block a user