2021-01-11 20:54:49 +00:00
|
|
|
package plugin
|
|
|
|
|
|
|
|
import (
|
2023-08-13 20:08:53 +00:00
|
|
|
"context"
|
|
|
|
"errors"
|
2021-01-11 20:54:49 +00:00
|
|
|
"fmt"
|
|
|
|
"os"
|
|
|
|
"path/filepath"
|
|
|
|
"time"
|
|
|
|
|
2023-08-13 20:08:53 +00:00
|
|
|
"github.com/rs/zerolog/log"
|
2023-08-21 09:35:52 +00:00
|
|
|
"github.com/thegeeklab/wp-plugin-go/types"
|
2021-01-11 20:54:49 +00:00
|
|
|
"github.com/urfave/cli/v2"
|
2023-02-08 09:13:28 +00:00
|
|
|
"golang.org/x/sys/execabs"
|
2021-01-11 20:54:49 +00:00
|
|
|
)
|
|
|
|
|
2023-08-13 20:08:53 +00:00
|
|
|
var ErrTypeAssertionFailed = errors.New("type assertion failed")
|
2021-01-11 20:54:49 +00:00
|
|
|
|
2023-08-13 20:08:53 +00:00
|
|
|
const strictFilePerm = 0o600
|
2023-08-09 09:35:58 +00:00
|
|
|
|
2023-08-13 20:08:53 +00:00
|
|
|
//nolint:revive
|
2023-08-21 09:35:52 +00:00
|
|
|
func (p *Plugin) run(ctx context.Context) error {
|
|
|
|
if err := p.FlagsFromContext(); err != nil {
|
|
|
|
return fmt.Errorf("validation failed: %w", err)
|
2023-08-13 20:08:53 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if err := p.Validate(); err != nil {
|
|
|
|
return fmt.Errorf("validation failed: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := p.Execute(); err != nil {
|
|
|
|
return fmt.Errorf("execution failed: %w", err)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2023-02-08 09:13:28 +00:00
|
|
|
|
2021-01-11 20:54:49 +00:00
|
|
|
// Validate handles the settings validation of the plugin.
|
|
|
|
func (p *Plugin) Validate() error {
|
2023-08-13 20:08:53 +00:00
|
|
|
p.Settings.Build.Branch = p.Metadata.Repository.Branch
|
|
|
|
p.Settings.Build.Ref = p.Metadata.Curr.Ref
|
|
|
|
p.Settings.Daemon.Registry = p.Settings.Login.Registry
|
2021-01-11 20:54:49 +00:00
|
|
|
|
2023-08-13 20:08:53 +00:00
|
|
|
if p.Settings.Build.TagsAuto {
|
2021-01-11 20:54:49 +00:00
|
|
|
// return true if tag event or default branch
|
|
|
|
if UseDefaultTag(
|
2023-08-13 20:08:53 +00:00
|
|
|
p.Settings.Build.Ref,
|
|
|
|
p.Settings.Build.Branch,
|
2021-01-11 20:54:49 +00:00
|
|
|
) {
|
|
|
|
tag, err := DefaultTagSuffix(
|
2023-08-13 20:08:53 +00:00
|
|
|
p.Settings.Build.Ref,
|
|
|
|
p.Settings.Build.TagsSuffix,
|
2021-01-11 20:54:49 +00:00
|
|
|
)
|
|
|
|
if err != nil {
|
2023-08-13 20:08:53 +00:00
|
|
|
return fmt.Errorf("cannot generate tags from %s, invalid semantic version: %w", p.Settings.Build.Ref, err)
|
2021-01-11 20:54:49 +00:00
|
|
|
}
|
2023-02-08 09:13:28 +00:00
|
|
|
|
2023-08-13 20:08:53 +00:00
|
|
|
p.Settings.Build.Tags = *cli.NewStringSlice(tag...)
|
2021-01-11 20:54:49 +00:00
|
|
|
} else {
|
2023-08-13 20:08:53 +00:00
|
|
|
log.Info().Msgf("skip auto-tagging for %s, not on default branch or tag", p.Settings.Build.Ref)
|
2023-02-08 09:13:28 +00:00
|
|
|
|
2021-01-11 20:54:49 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Execute provides the implementation of the plugin.
|
2023-03-24 13:04:29 +00:00
|
|
|
//
|
|
|
|
//nolint:gocognit
|
2021-01-11 20:54:49 +00:00
|
|
|
func (p *Plugin) Execute() error {
|
|
|
|
// start the Docker daemon server
|
2023-03-24 13:04:29 +00:00
|
|
|
//nolint: nestif
|
2023-08-13 20:08:53 +00:00
|
|
|
if !p.Settings.Daemon.Disabled {
|
2023-03-24 13:04:29 +00:00
|
|
|
// If no custom DNS value set start internal DNS server
|
2023-08-13 20:08:53 +00:00
|
|
|
if len(p.Settings.Daemon.DNS.Value()) == 0 {
|
2023-03-24 13:04:29 +00:00
|
|
|
ip, err := getContainerIP()
|
|
|
|
if err != nil {
|
2023-08-13 20:08:53 +00:00
|
|
|
log.Warn().Msgf("error detecting IP address: %v", err)
|
2023-03-24 13:04:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if ip != "" {
|
2023-08-13 20:08:53 +00:00
|
|
|
log.Debug().Msgf("discovered IP address: %v", ip)
|
2023-03-24 13:04:29 +00:00
|
|
|
p.startCoredns()
|
|
|
|
|
2023-08-13 20:08:53 +00:00
|
|
|
if err := p.Settings.Daemon.DNS.Set(ip); err != nil {
|
2023-03-24 13:04:29 +00:00
|
|
|
return fmt.Errorf("error setting daemon dns: %w", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-11 20:54:49 +00:00
|
|
|
p.startDaemon()
|
|
|
|
}
|
|
|
|
|
|
|
|
// 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()
|
2023-02-08 09:13:28 +00:00
|
|
|
|
2021-01-11 20:54:49 +00:00
|
|
|
err := cmd.Run()
|
|
|
|
if err == nil {
|
|
|
|
break
|
|
|
|
}
|
2023-02-08 09:13:28 +00:00
|
|
|
|
2021-01-11 20:54:49 +00:00
|
|
|
time.Sleep(time.Second * 1)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create Auth Config File
|
2023-08-13 20:08:53 +00:00
|
|
|
if p.Settings.Login.Config != "" {
|
2023-02-08 09:13:28 +00:00
|
|
|
if err := os.MkdirAll(dockerHome, strictFilePerm); err != nil {
|
|
|
|
return fmt.Errorf("failed to create docker home: %w", err)
|
2022-04-25 10:51:42 +00:00
|
|
|
}
|
2021-01-11 20:54:49 +00:00
|
|
|
|
|
|
|
path := filepath.Join(dockerHome, "config.json")
|
2023-02-08 09:13:28 +00:00
|
|
|
|
2023-08-13 20:08:53 +00:00
|
|
|
err := os.WriteFile(path, []byte(p.Settings.Login.Config), strictFilePerm)
|
2021-01-11 20:54:49 +00:00
|
|
|
if err != nil {
|
2023-02-08 09:13:28 +00:00
|
|
|
return fmt.Errorf("error writing config.json: %w", err)
|
2021-01-11 20:54:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// login to the Docker registry
|
2023-08-13 20:08:53 +00:00
|
|
|
if p.Settings.Login.Password != "" {
|
|
|
|
cmd := commandLogin(p.Settings.Login)
|
2023-08-09 09:35:58 +00:00
|
|
|
|
2023-08-11 07:13:42 +00:00
|
|
|
err := cmd.Run()
|
2023-08-09 09:35:58 +00:00
|
|
|
if err != nil {
|
2023-08-11 07:13:42 +00:00
|
|
|
return fmt.Errorf("error authenticating: %w", err)
|
2023-08-09 09:35:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-08-13 20:08:53 +00:00
|
|
|
if p.Settings.Daemon.BuildkitConfig != "" {
|
|
|
|
err := os.WriteFile(buildkitConfig, []byte(p.Settings.Daemon.BuildkitConfig), strictFilePerm)
|
2021-07-25 12:28:33 +00:00
|
|
|
if err != nil {
|
2023-02-08 09:13:28 +00:00
|
|
|
return fmt.Errorf("error writing buildkit.toml: %w", err)
|
2021-07-25 12:28:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-01-11 20:54:49 +00:00
|
|
|
switch {
|
2023-08-13 20:08:53 +00:00
|
|
|
case p.Settings.Login.Password != "":
|
|
|
|
log.Info().Msgf("Detected registry credentials")
|
|
|
|
case p.Settings.Login.Config != "":
|
|
|
|
log.Info().Msgf("Detected registry credentials file")
|
2021-01-11 20:54:49 +00:00
|
|
|
default:
|
2023-08-13 20:08:53 +00:00
|
|
|
log.Info().Msgf("Registry credentials or Docker config not provided. Guest mode enabled.")
|
2021-01-11 20:54:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// add proxy build args
|
2023-08-13 20:08:53 +00:00
|
|
|
addProxyBuildArgs(&p.Settings.Build)
|
2021-01-11 20:54:49 +00:00
|
|
|
|
2023-02-08 09:13:28 +00:00
|
|
|
var cmds []*execabs.Cmd
|
2021-01-11 20:54:49 +00:00
|
|
|
cmds = append(cmds, commandVersion()) // docker version
|
|
|
|
cmds = append(cmds, commandInfo()) // docker info
|
2023-08-13 20:08:53 +00:00
|
|
|
cmds = append(cmds, commandBuilder(p.Settings.Daemon))
|
2021-01-11 20:54:49 +00:00
|
|
|
cmds = append(cmds, commandBuildx())
|
|
|
|
|
2023-08-13 20:08:53 +00:00
|
|
|
cmds = append(cmds, commandBuild(p.Settings.Build, p.Settings.Dryrun)) // docker build
|
2021-01-11 20:54:49 +00:00
|
|
|
|
|
|
|
// execute all commands in batch mode.
|
|
|
|
for _, cmd := range cmds {
|
|
|
|
cmd.Stdout = os.Stdout
|
|
|
|
cmd.Stderr = os.Stderr
|
|
|
|
trace(cmd)
|
|
|
|
|
|
|
|
err := cmd.Run()
|
2022-08-08 11:36:23 +00:00
|
|
|
if err != nil {
|
2021-01-11 20:54:49 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
2023-08-21 09:35:52 +00:00
|
|
|
|
|
|
|
func (p *Plugin) FlagsFromContext() error {
|
|
|
|
cacheFrom, ok := p.Context.Generic("cache-from").(*types.StringSliceFlag)
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("%w: failed to read cache-from input", ErrTypeAssertionFailed)
|
|
|
|
}
|
|
|
|
|
|
|
|
p.Settings.Build.CacheFrom = cacheFrom.Get()
|
|
|
|
|
|
|
|
secrets, ok := p.Context.Generic("secrets").(*types.StringSliceFlag)
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("%w: failed to read secrets input", ErrTypeAssertionFailed)
|
|
|
|
}
|
|
|
|
|
|
|
|
p.Settings.Build.Secrets = secrets.Get()
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|