feat: use docker buildx by default and add args

This commit is contained in:
Kevin Franklin Kim 2021-12-10 11:47:01 +01:00
parent 87966bea58
commit 29d784eb19
11 changed files with 131 additions and 85 deletions

View File

@ -16,19 +16,28 @@ const (
)
type Build struct {
Image string `yaml:"image,omitempty"`
Tag string `yaml:"tag,omitempty"`
Context string `yaml:"context,omitempty"`
Dockerfile string `yaml:"dockerfile,omitempty"`
Args []string `yaml:"args,omitempty"`
Secrets []string `yaml:"secrets,omitempty"`
Labels []string `yaml:"labels,omitempty"`
CacheFrom []string `yaml:"cache_from,omitempty"`
Network string `yaml:"network,omitempty"`
Target string `yaml:"target,omitempty"`
ShmSize string `yaml:"shm_size,omitempty"`
ExtraHosts []string `yaml:"extra_hosts,omitempty"`
Isolation string `yaml:"isolation,omitempty"`
Args []string `yaml:"args,omitempty"`
Builder string `yaml:"builder,omitempty"`
CacheFrom []string `yaml:"cache_from,omitempty"`
Context string `yaml:"context,omitempty"`
Dockerfile string `yaml:"dockerfile,omitempty"`
ExtraHosts []string `yaml:"extra_hosts,omitempty"`
Image string `yaml:"image,omitempty"`
IIDFile string `yaml:"iidfile,omitempty"`
Labels []string `yaml:"labels,omitempty"`
Load bool `yaml:"load,omitempty"`
MetadataFile string `yaml:"metadata_file,omitempty"`
Network string `yaml:"network,omitempty"`
NoCache bool `yaml:"no_cache,omitempty"`
Output string `yaml:"output,omitempty"`
Platform string `yaml:"platform,omitempty"`
Platforms []string `yaml:"platforms,omitempty"`
Secrets []string `yaml:"secrets,omitempty"`
ShmSize string `yaml:"shm_size,omitempty"`
SSH string `yaml:"ssh,omitempty"`
Tag string `yaml:"tag,omitempty"`
Target string `yaml:"target,omitempty"`
ULimit string `yaml:"ulimit,omitempty"`
}
// ------------------------------------------------------------------------------------------------
@ -36,26 +45,41 @@ type Build struct {
// ------------------------------------------------------------------------------------------------
// Build ...
func (b *Build) Build(ctx context.Context) (string, error) {
func (b *Build) Build(ctx context.Context, args []string) (string, error) {
logrus.Debugf("running docker build for %q", b.Context)
return util.NewDockerCommand().Build(b.Context).
Arg("-t", fmt.Sprintf("%s:%s", b.Image, b.Tag)).
Arg("--file", b.Dockerfile).
ListArg("--build-arg", b.Args).
ListArg("--secrets", b.Secrets).
ListArg("--label", b.Labels).
ListArg("--cache-from", b.CacheFrom).
Arg("--network", b.Network).
Arg("--target", b.Target).
Arg("--shm-size", b.ShmSize).
ListArg("--add-host", b.ExtraHosts).
Arg("--isolation", b.Isolation).Run(ctx)
ListArg("--build-arg", b.Args).
Arg("--builder", b.Builder).
ListArg("--cache-from", b.CacheFrom).
Arg("--file", b.Dockerfile).
Arg("--iidfile", b.IIDFile).
ListArg("--label", b.Labels).
BoolArg("--load", b.Load).
Arg("--metadata-file", b.MetadataFile).
Arg("--network", b.Network).
BoolArg("--no-cache", b.NoCache).
Arg("--output", b.Output).
Arg("--platform", b.Platform).
ListArg("--platform", b.Platforms).
// Arg("--progress", xxx).
// Arg("--push", xxx).
// Arg("--pull", xxx).
// Arg("--quiet", xxx).
ListArg("--secret", b.Secrets).
Arg("--shm-size", b.ShmSize).
Arg("--ssh", b.SSH).
Arg("--tag", fmt.Sprintf("%s:%s", b.Image, b.Tag)).
Arg("--target", b.Target).
Arg("--ulimit", b.ULimit).
Args(args...).
Run(ctx)
}
// Push ...
func (b *Build) Push(ctx context.Context) (string, error) {
func (b *Build) Push(ctx context.Context, args []string) (string, error) {
logrus.Debugf("running docker push for %s:%s", b.Image, b.Tag)
return util.NewDockerCommand().Push(ctx, b.Image, b.Tag)
return util.NewDockerCommand().Push(b.Image, b.Tag).Args(args...).Run(ctx)
}
// UnmarshalYAML ...

View File

@ -2,6 +2,7 @@ package actions
import (
"context"
"strings"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@ -14,6 +15,8 @@ import (
func init() {
buildCmd.Flags().BoolVarP(&flagPush, "push", "p", false, "pushes built squadron units to the registry")
buildCmd.Flags().IntVar(&flagParallel, "parallel", 1, "run command in parallel")
buildCmd.Flags().StringVar(&flagBuildArgs, "build-args", "", "additional docker buildx build args")
buildCmd.Flags().StringVar(&flagPushArgs, "push-args", "", "additional docker push args")
}
var buildCmd = &cobra.Command{
@ -65,7 +68,7 @@ func build(ctx context.Context, args []string, cwd string, files []string, push
return err
}
defer sem.Release(1)
if out, err := unit.Build(wgCtx, sq.Name(), name); err != nil {
if out, err := unit.Build(wgCtx, sq.Name(), name, strings.Split(flagBuildArgs, " ")); err != nil {
return errors.Wrap(err, out)
}
return nil
@ -88,7 +91,7 @@ func build(ctx context.Context, args []string, cwd string, files []string, push
return err
}
defer sem.Release(1)
if out, err := unit.Push(wgCtx, sq.Name(), name); err != nil {
if out, err := unit.Push(wgCtx, sq.Name(), name, strings.Split(flagPushArgs, " ")); err != nil {
return errors.Wrap(err, out)
}
return nil

View File

@ -2,6 +2,7 @@ package actions
import (
"context"
"strings"
"github.com/pkg/errors"
"github.com/spf13/cobra"
@ -15,6 +16,8 @@ func init() {
pushCmd.Flags().StringVarP(&flagNamespace, "namespace", "n", "default", "specifies the namespace")
pushCmd.Flags().BoolVarP(&flagBuild, "build", "b", false, "builds or rebuilds units")
pushCmd.Flags().IntVar(&flagParallel, "parallel", 1, "run command in parallel")
pushCmd.Flags().StringVar(&flagBuildArgs, "build-args", "", "additional docker buildx build args")
pushCmd.Flags().StringVar(&flagPushArgs, "push-args", "", "additional docker push args")
}
var pushCmd = &cobra.Command{
@ -65,7 +68,7 @@ func push(ctx context.Context, args []string, cwd, namespace string, build bool,
return err
}
defer sem.Release(1)
if out, err := unit.Build(wgCtx, sq.Name(), name); err != nil {
if out, err := unit.Build(wgCtx, sq.Name(), name, strings.Split(flagBuildArgs, " ")); err != nil {
return errors.Wrap(err, out)
}
return nil
@ -89,7 +92,7 @@ func push(ctx context.Context, args []string, cwd, namespace string, build bool,
return err
}
defer sem.Release(1)
if out, err := unit.Push(wgCtx, sq.Name(), name); err != nil {
if out, err := unit.Push(wgCtx, sq.Name(), name, strings.Split(flagPushArgs, " ")); err != nil {
return errors.Wrap(err, out)
}
return nil

View File

@ -45,6 +45,8 @@ var (
flagBuild bool
flagPush bool
flagParallel int
flagBuildArgs string
flagPushArgs string
flagDiff bool
flagFiles []string
)

View File

@ -21,6 +21,8 @@ func init() {
upCmd.Flags().BoolVarP(&flagPush, "push", "p", false, "pushes units to the registry")
upCmd.Flags().BoolVar(&flagDiff, "diff", false, "preview upgrade as a coloured diff")
upCmd.Flags().IntVar(&flagParallel, "parallel", 1, "run command in parallel")
upCmd.Flags().StringVar(&flagBuildArgs, "build-args", "", "additional docker buildx build args")
upCmd.Flags().StringVar(&flagPushArgs, "push-args", "", "additional docker push args")
}
var upCmd = &cobra.Command{
@ -73,7 +75,7 @@ func up(ctx context.Context, args []string, cwd, namespace string, build, push,
return err
}
defer sem.Release(1)
if out, err := unit.Build(wgCtx, sq.Name(), name); err != nil {
if out, err := unit.Build(wgCtx, sq.Name(), name, strings.Split(flagBuildArgs, " ")); err != nil {
return errors.Wrap(err, out)
}
return nil
@ -96,7 +98,7 @@ func up(ctx context.Context, args []string, cwd, namespace string, build, push,
return err
}
defer sem.Release(1)
if out, err := unit.Push(wgCtx, sq.Name(), name); err != nil {
if out, err := unit.Push(wgCtx, sq.Name(), name, strings.Split(flagPushArgs, " ")); err != nil {
return errors.Wrap(err, out)
}
return nil

38
configuration.go Normal file
View File

@ -0,0 +1,38 @@
package squadron
import (
"fmt"
"gopkg.in/yaml.v3"
)
type Configuration struct {
Name string `yaml:"name,omitempty"`
Version string `yaml:"version,omitempty"`
Prefix string `yaml:"prefix,omitempty"`
Unite bool `yaml:"unite,omitempty"`
Global map[string]interface{} `yaml:"global,omitempty"`
Units map[string]*Unit `yaml:"squadron,omitempty"`
}
// UnmarshalYAML ...
func (c *Configuration) UnmarshalYAML(value *yaml.Node) error {
if value.Tag == TagMap {
type wrapper Configuration
err := value.Decode((*wrapper)(c))
if err == nil {
// if the decode is successful, remove units that are nil
c.removeNilUnits()
}
return err
}
return fmt.Errorf("unsupported node tag type for %T: %q", c, value.Tag)
}
func (c *Configuration) removeNilUnits() {
for uName, u := range c.Units {
if u == nil {
delete(c.Units, uName)
}
}
}

View File

@ -33,15 +33,6 @@ const (
errHelmReleaseNotFound = "Error: release: not found"
)
type Configuration struct {
Name string `yaml:"name,omitempty"`
Version string `yaml:"version,omitempty"`
Prefix string `yaml:"prefix,omitempty"`
Unite bool `yaml:"unite,omitempty"`
Global map[string]interface{} `yaml:"global,omitempty"`
Units map[string]*Unit `yaml:"squadron,omitempty"`
}
type Squadron struct {
name string
basePath string
@ -73,28 +64,6 @@ func (sq *Squadron) GetConfigYAML() string {
return sq.config
}
// UnmarshalYAML ...
func (c *Configuration) UnmarshalYAML(value *yaml.Node) error {
if value.Tag == TagMap {
type wrapper Configuration
err := value.Decode((*wrapper)(c))
if err == nil {
// if the decode is successful, remove units that are nil
c.removeNilUnits()
}
return err
}
return fmt.Errorf("unsupported node tag type for %T: %q", c, value.Tag)
}
func (c *Configuration) removeNilUnits() {
for uName, u := range c.Units {
if u == nil {
delete(c.Units, uName)
}
}
}
func (sq *Squadron) MergeConfigFiles() error {
logrus.Info("merging config files")
logrus.WithField("files", sq.files).Debug("using files")
@ -142,36 +111,40 @@ func (sq *Squadron) RenderConfig(ctx context.Context) error {
// execute again with loaded template vars
tv = TemplateVars{}
if value, ok := vars["global"]; ok {
replace(value)
toSnakeCaseKeys(value)
tv.add("Global", value)
}
if value, ok := vars["squadron"]; ok {
replace(value)
toSnakeCaseKeys(value)
tv.add("Squadron", value)
}
logrus.Debug("executing file template")
// execute without errors to get existing values
out, err := executeFileTemplate(ctx, sq.config, tv, false)
if err != nil {
return errors.Wrap(err, "failed to execute initial file template")
}
logrus.Debug("unmarshalling vars")
if err := yaml.Unmarshal(out, &vars); err != nil {
return err
}
// execute again with loaded template vars
tv = TemplateVars{}
if value, ok := vars["global"]; ok {
replace(value)
toSnakeCaseKeys(value)
tv.add("Global", value)
}
if value, ok := vars["squadron"]; ok {
replace(value)
toSnakeCaseKeys(value)
tv.add("Squadron", value)
}
logrus.Debug("executing file template")
out, err = executeFileTemplate(ctx, sq.config, tv, true)
if err != nil {
return errors.Wrap(err, "failed to execute second file template")
}
logrus.Debug("unmarshalling vars")
if err := yaml.Unmarshal(out, &sq.c); err != nil {
return err
}

View File

@ -101,14 +101,14 @@ func base64(v string) string {
return b64.StdEncoding.EncodeToString([]byte(v))
}
func replace(in interface{}) {
func toSnakeCaseKeys(in interface{}) {
if value, ok := in.(map[string]interface{}); ok {
for k, v := range value {
if strings.Contains(k, "-") {
value[strings.ReplaceAll(k, "-", "_")] = v
delete(value, k)
}
replace(v)
toSnakeCaseKeys(v)
}
}
}
@ -140,7 +140,7 @@ func indent(spaces int, v string) string {
}
func quote(v string) string {
return "\"" + v + "\""
return "'" + v + "'"
}
func render(name, text string, data interface{}, errorOnMissing bool) (string, error) {

View File

@ -17,13 +17,13 @@ type Unit struct {
// ------------------------------------------------------------------------------------------------
// Build ...
func (u *Unit) Build(ctx context.Context, squadron, unit string) (string, error) {
func (u *Unit) Build(ctx context.Context, squadron, unit string, args []string) (string, error) {
var i int
for _, build := range u.Builds {
i++
pterm.Info.Printfln("[%d/%d] Building %s/%s", i, len(u.Builds), squadron, unit)
pterm.FgGray.Printfln("└ %s:%s", build.Image, build.Tag)
if out, err := build.Build(ctx); err != nil {
if out, err := build.Build(ctx, args); err != nil {
pterm.Error.Printfln("[%d/%d] Failed to build squadron unit %s/%s", i, len(u.Builds), squadron, unit)
pterm.FgGray.Printfln("└ %s:%s", build.Image, build.Tag)
return out, err
@ -33,13 +33,13 @@ func (u *Unit) Build(ctx context.Context, squadron, unit string) (string, error)
}
// Push ...
func (u *Unit) Push(ctx context.Context, squadron, unit string) (string, error) {
func (u *Unit) Push(ctx context.Context, squadron, unit string, args []string) (string, error) {
var i int
for _, build := range u.Builds {
i++
pterm.Info.Printfln("[%d/%d] Pushing %s/%s", i, len(u.Builds), squadron, unit)
pterm.FgGray.Printfln("└ %s:%s", build.Image, build.Tag)
if out, err := build.Push(ctx); err != nil {
if out, err := build.Push(ctx, args); err != nil {
pterm.Error.Printfln("[%d/%d] Failed to push %s/%s", i, len(u.Builds), squadron, unit)
pterm.FgGray.Printfln("└ %s:%s", build.Image, build.Tag)
return out, err

View File

@ -61,6 +61,14 @@ func (c *Cmd) Arg(name, v string) *Cmd {
return c
}
func (c *Cmd) BoolArg(name string, v bool) *Cmd {
if name == "" || !v {
return c
}
c.command = append(c.command, name)
return c
}
func (c *Cmd) ListArg(name string, vs []string) *Cmd {
if name == "" {
return c

View File

@ -1,9 +1,7 @@
package util
import (
"context"
"fmt"
"os"
)
type DockerCmd struct {
@ -16,14 +14,9 @@ func NewDockerCommand() *DockerCmd {
}
func (c *DockerCmd) Build(workDir string) *Cmd {
args := []string{"build"}
if platform := os.Getenv("SQUADRON_DOCKER_BUILDX"); platform != "" {
args = []string{"buildx", "build", "--platform", platform}
}
args = append(args, ".")
return c.Cwd(workDir).Args(args...)
return c.Cwd(workDir).Args("buildx", "build", ".")
}
func (c *DockerCmd) Push(ctx context.Context, image, tag string) (string, error) {
return c.Args("push", fmt.Sprintf("%s:%s", image, tag)).Run(ctx)
func (c *DockerCmd) Push(image, tag string) *Cmd {
return c.Args("push", fmt.Sprintf("%s:%s", image, tag))
}