mirror of
https://github.com/foomo/squadron.git
synced 2025-10-16 12:35:42 +00:00
feat: add squadron name to schema
This commit is contained in:
parent
2cc181ef3a
commit
c0c52b85d9
92
chart.go
92
chart.go
@ -1,92 +0,0 @@
|
||||
package squadron
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"path"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/foomo/squadron/util"
|
||||
)
|
||||
|
||||
type ChartDependency struct {
|
||||
Name string `yaml:"name,omitempty"`
|
||||
Repository string `yaml:"repository,omitempty"`
|
||||
Version string `yaml:"version,omitempty"`
|
||||
Alias string `yaml:"alias,omitempty"`
|
||||
}
|
||||
|
||||
func (cd *ChartDependency) UnmarshalYAML(value *yaml.Node) error {
|
||||
switch value.Tag {
|
||||
case TagMap:
|
||||
type wrapper ChartDependency
|
||||
return value.Decode((*wrapper)(cd))
|
||||
case TagString:
|
||||
var vString string
|
||||
if err := value.Decode(&vString); err != nil {
|
||||
return err
|
||||
}
|
||||
vBytes, err := executeFileTemplate(context.Background(), vString, nil, true)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to render chart string")
|
||||
}
|
||||
localChart, err := loadChart(path.Join(string(vBytes), chartFile))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load local chart: " + vString)
|
||||
}
|
||||
cd.Name = localChart.Name
|
||||
cd.Repository = fmt.Sprintf("file://%v", vString)
|
||||
cd.Version = localChart.Version
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("unsupported node tag type for %T: %q", cd, value.Tag)
|
||||
}
|
||||
}
|
||||
|
||||
type Chart struct {
|
||||
APIVersion string `yaml:"apiVersion"`
|
||||
Name string `yaml:"name,omitempty"`
|
||||
Description string `yaml:"description,omitempty"`
|
||||
Type string `yaml:"type,omitempty"`
|
||||
Version string `yaml:"version,omitempty"`
|
||||
Dependencies []ChartDependency `yaml:"dependencies,omitempty"`
|
||||
}
|
||||
|
||||
func newChart(name, version string) *Chart {
|
||||
return &Chart{
|
||||
APIVersion: chartAPIVersionV2,
|
||||
Name: name,
|
||||
Description: fmt.Sprintf("A helm parent chart for squadron %v", name),
|
||||
Type: defaultChartType,
|
||||
Version: version,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Chart) addDependency(alias string, cd ChartDependency) {
|
||||
cd.Alias = alias
|
||||
c.Dependencies = append(c.Dependencies, cd)
|
||||
}
|
||||
|
||||
func loadChart(path string) (*Chart, error) {
|
||||
c := Chart{}
|
||||
file, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error while opening file: %v", err)
|
||||
}
|
||||
if err := yaml.Unmarshal(file, &c); err != nil {
|
||||
return nil, fmt.Errorf("error while unmarshalling template file: %s", err)
|
||||
}
|
||||
return &c, nil
|
||||
}
|
||||
|
||||
func (c Chart) generate(chartPath string, overrides interface{}) error {
|
||||
// generate Chart.yaml
|
||||
if err := util.GenerateYaml(path.Join(chartPath, chartFile), c); err != nil {
|
||||
return err
|
||||
}
|
||||
// generate values.yaml
|
||||
return util.GenerateYaml(path.Join(chartPath, valuesFile), overrides)
|
||||
}
|
||||
@ -1,101 +1,48 @@
|
||||
package actions
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/sync/errgroup"
|
||||
|
||||
"github.com/foomo/squadron"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
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")
|
||||
buildCmd.Flags().StringSliceVar(&flagBuildArgs, "build-args", nil, "additional docker buildx build args")
|
||||
buildCmd.Flags().StringSliceVar(&flagPushArgs, "push-args", nil, "additional docker push args")
|
||||
}
|
||||
|
||||
var buildCmd = &cobra.Command{
|
||||
Use: "build [UNIT...]",
|
||||
Use: "build [SQUADRON.UNIT...]",
|
||||
Short: "build or rebuild squadron units",
|
||||
Example: " squadron build frontend backend",
|
||||
Example: " squadron build storefinder frontend backend",
|
||||
Args: cobra.MinimumNArgs(0),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return build(cmd.Context(), args, cwd, flagFiles, flagPush, flagParallel)
|
||||
sq := squadron.New(cwd, "", flagFiles)
|
||||
|
||||
if err := sq.MergeConfigFiles(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
squadronName, unitNames := parseSquadronAndUnitNames(args)
|
||||
if err := sq.FilterConfig(squadronName, unitNames); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := sq.RenderConfig(cmd.Context()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := sq.Build(cmd.Context(), flagBuildArgs, flagParallel); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if flagPush {
|
||||
if err := sq.Push(cmd.Context(), flagPushArgs, flagParallel); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func build(ctx context.Context, args []string, cwd string, files []string, push bool, parallel int) error {
|
||||
sq := squadron.New(cwd, "", files)
|
||||
|
||||
if err := sq.MergeConfigFiles(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
unitsNames, err := parseUnitNames(args, sq.GetConfig().Units)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if unitsNames != nil {
|
||||
if err := sq.FilterConfig(unitsNames); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := sq.RenderConfig(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
units, err := parseUnitArgs(args, sq.GetConfig().Units)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
{
|
||||
g, gctx := errgroup.WithContext(ctx)
|
||||
g.SetLimit(parallel)
|
||||
|
||||
_ = squadron.Units(units).Iterate(func(n string, u *squadron.Unit) error {
|
||||
name := n
|
||||
unit := u
|
||||
g.Go(func() error {
|
||||
if out, err := unit.Build(gctx, sq.Name(), name, strings.Split(flagBuildArgs, " ")); err != nil {
|
||||
return errors.Wrap(err, out)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
})
|
||||
err := g.Wait()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if push {
|
||||
g, gctx := errgroup.WithContext(ctx)
|
||||
g.SetLimit(parallel)
|
||||
|
||||
_ = squadron.Units(units).Iterate(func(n string, u *squadron.Unit) error {
|
||||
name := n
|
||||
unit := u
|
||||
g.Go(func() error {
|
||||
if out, err := unit.Push(gctx, sq.Name(), name, strings.Split(flagPushArgs, " ")); err != nil {
|
||||
return errors.Wrap(err, out)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
})
|
||||
|
||||
if err := g.Wait(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
package actions
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/foomo/squadron/internal/util"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/foomo/squadron"
|
||||
@ -14,39 +14,30 @@ func init() {
|
||||
}
|
||||
|
||||
var configCmd = &cobra.Command{
|
||||
Use: "config [UNIT...]",
|
||||
Use: "config [SQUADRON] [UNIT...]",
|
||||
Short: "generate and view the squadron config",
|
||||
Example: " squadron config --file squadron.yaml --file squadron.override.yaml",
|
||||
Example: " squadron config storefinder frontend backend",
|
||||
Args: cobra.MinimumNArgs(0),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return config(cmd.Context(), args, cwd, flagFiles, flagNoRender)
|
||||
sq := squadron.New(cwd, "", flagFiles)
|
||||
|
||||
if err := sq.MergeConfigFiles(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
squadronName, unitNames := parseSquadronAndUnitNames(args)
|
||||
if err := sq.FilterConfig(squadronName, unitNames); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !flagNoRender {
|
||||
if err := sq.RenderConfig(cmd.Context()); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Print(util.Highlight(sq.ConfigYAML()))
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
func config(ctx context.Context, args []string, cwd string, files []string, noRender bool) error {
|
||||
sq := squadron.New(cwd, "", files)
|
||||
|
||||
if err := sq.MergeConfigFiles(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
unitsNames, err := parseUnitNames(args, sq.GetConfig().Units)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if unitsNames != nil {
|
||||
if err := sq.FilterConfig(unitsNames); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !noRender {
|
||||
if err := sq.RenderConfig(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Println(sq.GetConfigYAML())
|
||||
return nil
|
||||
}
|
||||
|
||||
45
cmd/actions/diff.go
Normal file
45
cmd/actions/diff.go
Normal file
@ -0,0 +1,45 @@
|
||||
package actions
|
||||
|
||||
import (
|
||||
"github.com/foomo/squadron"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
diffCmd.Flags().StringVarP(&flagNamespace, "namespace", "n", "default", "specifies the namespace")
|
||||
diffCmd.Flags().IntVar(&flagParallel, "parallel", 1, "run command in parallel")
|
||||
}
|
||||
|
||||
var diffCmd = &cobra.Command{
|
||||
Use: "diff [SQUADRON] [UNIT...]",
|
||||
Short: "shows the diff between the installed and local chart",
|
||||
Example: " squadron diff storefinder frontend backend --namespace demo",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
sq := squadron.New(cwd, flagNamespace, flagFiles)
|
||||
|
||||
if err := sq.MergeConfigFiles(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args, helmArgs := parseExtraArgs(args)
|
||||
|
||||
if len(args) > 0 {
|
||||
if err := sq.Config().Squadrons.Filter(args[0]); err != nil {
|
||||
return errors.Wrap(err, "invalid SQUADRON argument")
|
||||
}
|
||||
}
|
||||
|
||||
if len(args) > 1 {
|
||||
if err := sq.Config().Squadrons[args[0]].Filter(args[1:]...); err != nil {
|
||||
return errors.Wrap(err, "invalid UNIT argument")
|
||||
}
|
||||
}
|
||||
|
||||
if err := sq.RenderConfig(cmd.Context()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sq.Diff(cmd.Context(), helmArgs, flagParallel)
|
||||
},
|
||||
}
|
||||
@ -1,39 +1,35 @@
|
||||
package actions
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/foomo/squadron"
|
||||
)
|
||||
|
||||
func init() {
|
||||
downCmd.Flags().IntVar(&flagParallel, "parallel", 1, "run command in parallel")
|
||||
downCmd.Flags().StringVarP(&flagNamespace, "namespace", "n", "default", "Specifies the namespace")
|
||||
}
|
||||
|
||||
var downCmd = &cobra.Command{
|
||||
Use: "down [UNIT...]",
|
||||
Use: "down [SQUADRON] [UNIT...]",
|
||||
Short: "uninstalls the squadron or given units",
|
||||
Example: " squadron down frontend backend --namespace demo",
|
||||
Example: " squadron down storefinder frontend backend --namespace demo",
|
||||
Args: cobra.MinimumNArgs(0),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return down(cmd.Context(), args, cwd, flagNamespace, flagFiles)
|
||||
sq := squadron.New(cwd, flagNamespace, flagFiles)
|
||||
|
||||
if err := sq.MergeConfigFiles(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args, helmArgs := parseExtraArgs(args)
|
||||
|
||||
squadronName, unitNames := parseSquadronAndUnitNames(args)
|
||||
if err := sq.FilterConfig(squadronName, unitNames); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sq.Down(cmd.Context(), helmArgs, flagParallel)
|
||||
},
|
||||
}
|
||||
|
||||
func down(ctx context.Context, args []string, cwd, namespace string, files []string) error {
|
||||
sq := squadron.New(cwd, namespace, files)
|
||||
|
||||
if err := sq.MergeConfigFiles(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args, helmArgs := parseExtraArgs(args)
|
||||
units, err := parseUnitArgs(args, sq.GetConfig().Units)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sq.Down(ctx, units, helmArgs)
|
||||
}
|
||||
|
||||
@ -1,44 +0,0 @@
|
||||
package actions
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/foomo/squadron"
|
||||
)
|
||||
|
||||
var generateCmd = &cobra.Command{
|
||||
Use: "generate [UNIT...]",
|
||||
Short: "generate and view the squadron or given units charts",
|
||||
Example: " squadron generate fronted backend",
|
||||
Args: cobra.MinimumNArgs(0),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return generate(cmd.Context(), args, cwd, flagFiles)
|
||||
},
|
||||
}
|
||||
|
||||
func generate(ctx context.Context, args []string, cwd string, files []string) error {
|
||||
sq := squadron.New(cwd, "", files)
|
||||
|
||||
if err := sq.MergeConfigFiles(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
unitsNames, err := parseUnitNames(args, sq.GetConfig().Units)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if unitsNames != nil {
|
||||
if err := sq.FilterConfig(unitsNames); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := sq.RenderConfig(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sq.Generate(ctx, sq.GetConfig().Units)
|
||||
}
|
||||
@ -3,42 +3,36 @@ package actions
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/foomo/squadron"
|
||||
"github.com/foomo/squadron/internal/config"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var flagPrefixSquadron bool
|
||||
|
||||
func init() {
|
||||
listCmd.Flags().BoolVar(&flagPrefixSquadron, "prefix-squadron", false, "add squadron prefix")
|
||||
}
|
||||
|
||||
var listCmd = &cobra.Command{
|
||||
Use: "list",
|
||||
Use: "list [SQUADRON]",
|
||||
Short: "list squadron units",
|
||||
Example: " squadron list",
|
||||
Example: " squadron list storefinder",
|
||||
Args: cobra.MinimumNArgs(0),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return list(cwd, flagFiles)
|
||||
sq := squadron.New(cwd, "", flagFiles)
|
||||
|
||||
if err := sq.MergeConfigFiles(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
squadronName, unitNames := parseSquadronAndUnitNames(args)
|
||||
if err := sq.FilterConfig(squadronName, unitNames); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sq.Config().Squadrons.Iterate(func(key string, value config.Map[*config.Unit]) error {
|
||||
fmt.Println("Squadron:", key)
|
||||
return value.Iterate(func(k string, v *config.Unit) error {
|
||||
fmt.Println(" ", k)
|
||||
return nil
|
||||
})
|
||||
})
|
||||
},
|
||||
}
|
||||
|
||||
func list(cwd string, files []string) error {
|
||||
sq := squadron.New(cwd, "", files)
|
||||
|
||||
if err := sq.MergeConfigFiles(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_ = sq.GetConfig().Units.Iterate(func(name string, unit *squadron.Unit) error {
|
||||
if flagPrefixSquadron {
|
||||
fmt.Printf("%s/%s\n", sq.Name(), name)
|
||||
} else {
|
||||
fmt.Println(name)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -1,110 +1,44 @@
|
||||
package actions
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"golang.org/x/sync/semaphore"
|
||||
|
||||
"github.com/foomo/squadron"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
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")
|
||||
pushCmd.Flags().StringSliceVar(&flagBuildArgs, "build-args", nil, "additional docker buildx build args")
|
||||
pushCmd.Flags().StringSliceVar(&flagPushArgs, "push-args", nil, "additional docker push args")
|
||||
}
|
||||
|
||||
var pushCmd = &cobra.Command{
|
||||
Use: "push [UNIT...]",
|
||||
Use: "push [SQUADRON] [UNIT...]",
|
||||
Short: "pushes the squadron or given units",
|
||||
Example: " squadron push frontend backend --namespace demo --build",
|
||||
Example: " squadron push storefinder frontend backend --namespace demo --build",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return push(cmd.Context(), args, cwd, flagNamespace, flagBuild, flagParallel, flagFiles)
|
||||
sq := squadron.New(cwd, flagNamespace, flagFiles)
|
||||
|
||||
if err := sq.MergeConfigFiles(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
squadronName, unitNames := parseSquadronAndUnitNames(args)
|
||||
if err := sq.FilterConfig(squadronName, unitNames); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := sq.RenderConfig(cmd.Context()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if flagBuild {
|
||||
if err := sq.Build(cmd.Context(), flagBuildArgs, flagParallel); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return sq.Push(cmd.Context(), flagPushArgs, flagParallel)
|
||||
},
|
||||
}
|
||||
|
||||
func push(ctx context.Context, args []string, cwd, namespace string, build bool, parallel int, files []string) error {
|
||||
sq := squadron.New(cwd, namespace, files)
|
||||
|
||||
if err := sq.MergeConfigFiles(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
unitsNames, err := parseUnitNames(args, sq.GetConfig().Units)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if unitsNames != nil {
|
||||
if err := sq.FilterConfig(unitsNames); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := sq.RenderConfig(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
units, err := parseUnitArgs(args, sq.GetConfig().Units)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if build {
|
||||
sem := semaphore.NewWeighted(int64(parallel))
|
||||
wg, wgCtx := errgroup.WithContext(ctx)
|
||||
|
||||
_ = squadron.Units(units).Iterate(func(n string, u *squadron.Unit) error {
|
||||
name := n
|
||||
unit := u
|
||||
wg.Go(func() error {
|
||||
if err := sem.Acquire(wgCtx, 1); err != nil {
|
||||
return err
|
||||
}
|
||||
defer sem.Release(1)
|
||||
if out, err := unit.Build(wgCtx, sq.Name(), name, strings.Split(flagBuildArgs, " ")); err != nil {
|
||||
return errors.Wrap(err, out)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
})
|
||||
|
||||
if err := wg.Wait(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
sem := semaphore.NewWeighted(int64(parallel))
|
||||
wg, wgCtx := errgroup.WithContext(ctx)
|
||||
|
||||
_ = squadron.Units(units).Iterate(func(n string, u *squadron.Unit) error {
|
||||
name := n
|
||||
unit := u
|
||||
wg.Go(func() error {
|
||||
if err := sem.Acquire(wgCtx, 1); err != nil {
|
||||
return err
|
||||
}
|
||||
defer sem.Release(1)
|
||||
if out, err := unit.Push(wgCtx, sq.Name(), name, strings.Split(flagPushArgs, " ")); err != nil {
|
||||
return errors.Wrap(err, out)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
})
|
||||
|
||||
if err := wg.Wait(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -1,55 +1,39 @@
|
||||
package actions
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/foomo/squadron"
|
||||
)
|
||||
|
||||
func init() {
|
||||
rollbackCmd.Flags().IntVar(&flagParallel, "parallel", 1, "run command in parallel")
|
||||
rollbackCmd.Flags().StringVarP(&flagNamespace, "namespace", "n", "default", "specifies the namespace")
|
||||
rollbackCmd.Flags().StringVarP(&flagRevision, "revision", "r", "", "specifies the revision to roll back to")
|
||||
}
|
||||
|
||||
var rollbackCmd = &cobra.Command{
|
||||
Use: "rollback [UNIT...]",
|
||||
Use: "rollback [SQUADRON] [UNIT...]",
|
||||
Short: "rolls back the squadron or given units",
|
||||
Example: " squadron rollback frontend backend --namespace demo",
|
||||
Example: " squadron rollback storefinder frontend backend --namespace demo",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return rollback(cmd.Context(), args, cwd, flagNamespace, flagRevision, flagFiles)
|
||||
},
|
||||
}
|
||||
sq := squadron.New(cwd, flagNamespace, flagFiles)
|
||||
|
||||
func rollback(ctx context.Context, args []string, cwd, namespace string, revision string, files []string) error {
|
||||
sq := squadron.New(cwd, namespace, files)
|
||||
|
||||
if err := sq.MergeConfigFiles(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args, helmArgs := parseExtraArgs(args)
|
||||
|
||||
unitsNames, err := parseUnitNames(args, sq.GetConfig().Units)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if unitsNames != nil {
|
||||
if err := sq.FilterConfig(unitsNames); err != nil {
|
||||
if err := sq.MergeConfigFiles(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := sq.RenderConfig(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
args, helmArgs := parseExtraArgs(args)
|
||||
|
||||
units, err := parseUnitArgs(args, sq.GetConfig().Units)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
squadronName, unitNames := parseSquadronAndUnitNames(args)
|
||||
if err := sq.FilterConfig(squadronName, unitNames); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sq.Rollback(ctx, units, revision, helmArgs)
|
||||
if err := sq.RenderConfig(cmd.Context()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sq.Rollback(cmd.Context(), flagRevision, helmArgs, flagParallel)
|
||||
},
|
||||
}
|
||||
|
||||
@ -4,13 +4,10 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/foomo/squadron/internal/util"
|
||||
"github.com/pterm/pterm"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/foomo/squadron"
|
||||
"github.com/foomo/squadron/util"
|
||||
)
|
||||
|
||||
var (
|
||||
@ -47,8 +44,8 @@ var (
|
||||
flagBuild bool
|
||||
flagPush bool
|
||||
flagParallel int
|
||||
flagBuildArgs string
|
||||
flagPushArgs string
|
||||
flagBuildArgs []string
|
||||
flagPushArgs []string
|
||||
flagDiff bool
|
||||
flagFiles []string
|
||||
)
|
||||
@ -59,7 +56,7 @@ func init() {
|
||||
rootCmd.PersistentFlags().BoolVarP(&flagVerbose, "verbose", "v", false, "show more output")
|
||||
rootCmd.PersistentFlags().StringSliceVarP(&flagFiles, "file", "f", []string{"squadron.yaml"}, "specify alternative squadron files")
|
||||
|
||||
rootCmd.AddCommand(upCmd, downCmd, buildCmd, pushCmd, listCmd, generateCmd, rollbackCmd, statusCmd, configCmd, versionCmd, completionCmd, templateCmd)
|
||||
rootCmd.AddCommand(upCmd, diffCmd, downCmd, buildCmd, pushCmd, listCmd, rollbackCmd, statusCmd, configCmd, versionCmd, completionCmd, templateCmd)
|
||||
|
||||
pterm.Info = *pterm.Info.WithPrefix(pterm.Prefix{Text: "INFO", Style: pterm.Info.Prefix.Style})
|
||||
pterm.Error = *pterm.Info.WithPrefix(pterm.Prefix{Text: "ERROR", Style: pterm.Error.Prefix.Style})
|
||||
@ -85,33 +82,15 @@ func parseExtraArgs(args []string) (out []string, extraArgs []string) {
|
||||
return args, nil
|
||||
}
|
||||
|
||||
// parseUnitArgs helper
|
||||
func parseUnitArgs(args []string, units map[string]*squadron.Unit) (map[string]*squadron.Unit, error) {
|
||||
func parseSquadronAndUnitNames(args []string) (squadron string, units []string) {
|
||||
if len(args) == 0 {
|
||||
return units, nil
|
||||
return "", nil
|
||||
}
|
||||
ret := map[string]*squadron.Unit{}
|
||||
for _, arg := range args {
|
||||
if unit, ok := units[arg]; ok {
|
||||
ret[arg] = unit
|
||||
} else {
|
||||
return nil, errors.Errorf("unknown unit name %s", arg)
|
||||
}
|
||||
if len(args) > 0 {
|
||||
squadron = args[0]
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func parseUnitNames(args []string, units map[string]*squadron.Unit) ([]string, error) {
|
||||
if len(args) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
ret := make([]string, 0, len(args))
|
||||
for _, arg := range args {
|
||||
if _, ok := units[arg]; ok {
|
||||
ret = append(ret, arg)
|
||||
} else {
|
||||
return nil, errors.Errorf("unknown unit name %s", arg)
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
if len(args) > 1 {
|
||||
units = args[1:]
|
||||
}
|
||||
return squadron, units
|
||||
}
|
||||
|
||||
@ -1,54 +1,38 @@
|
||||
package actions
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
|
||||
"github.com/foomo/squadron"
|
||||
)
|
||||
|
||||
func init() {
|
||||
statusCmd.Flags().IntVar(&flagParallel, "parallel", 1, "run command in parallel")
|
||||
statusCmd.Flags().StringVarP(&flagNamespace, "namespace", "n", "default", "specifies the namespace")
|
||||
}
|
||||
|
||||
var statusCmd = &cobra.Command{
|
||||
Use: "status [UNIT...]",
|
||||
Use: "status [SQUADRON] [UNIT...]",
|
||||
Short: "installs the squadron or given units",
|
||||
Example: " squadron status frontend backend --namespace demo",
|
||||
Example: " squadron status storefinder frontend backend --namespace demo",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return status(cmd.Context(), args, cwd, flagNamespace, flagFiles)
|
||||
},
|
||||
}
|
||||
sq := squadron.New(cwd, flagNamespace, flagFiles)
|
||||
|
||||
func status(ctx context.Context, args []string, cwd, namespace string, files []string) error {
|
||||
sq := squadron.New(cwd, namespace, files)
|
||||
|
||||
if err := sq.MergeConfigFiles(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args, helmArgs := parseExtraArgs(args)
|
||||
|
||||
unitsNames, err := parseUnitNames(args, sq.GetConfig().Units)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if unitsNames != nil {
|
||||
if err := sq.FilterConfig(unitsNames); err != nil {
|
||||
if err := sq.MergeConfigFiles(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := sq.RenderConfig(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
args, helmArgs := parseExtraArgs(args)
|
||||
|
||||
units, err := parseUnitArgs(args, sq.GetConfig().Units)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
squadronName, unitNames := parseSquadronAndUnitNames(args)
|
||||
if err := sq.FilterConfig(squadronName, unitNames); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sq.Status(ctx, units, helmArgs)
|
||||
if err := sq.RenderConfig(cmd.Context()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return sq.Status(cmd.Context(), helmArgs, flagParallel)
|
||||
},
|
||||
}
|
||||
|
||||
@ -1,11 +1,11 @@
|
||||
package actions
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/spf13/cobra"
|
||||
"fmt"
|
||||
|
||||
"github.com/foomo/squadron"
|
||||
"github.com/foomo/squadron/internal/util"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -13,49 +13,35 @@ func init() {
|
||||
}
|
||||
|
||||
var templateCmd = &cobra.Command{
|
||||
Use: "template [UNIT...]",
|
||||
Use: "template [SQUADRON] [UNIT...]",
|
||||
Short: "render chart templates locally and display the output",
|
||||
Example: " squadron template frontend backend --namespace demo",
|
||||
Example: " squadron template storefinder frontend backend --namespace demo",
|
||||
Args: cobra.MinimumNArgs(0),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return template(cmd.Context(), args, cwd, flagNamespace, flagFiles)
|
||||
},
|
||||
}
|
||||
sq := squadron.New(cwd, flagNamespace, flagFiles)
|
||||
|
||||
func template(ctx context.Context, args []string, cwd, namespace string, files []string) error {
|
||||
sq := squadron.New(cwd, namespace, files)
|
||||
|
||||
if err := sq.MergeConfigFiles(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args, helmArgs := parseExtraArgs(args)
|
||||
|
||||
unitsNames, err := parseUnitNames(args, sq.GetConfig().Units)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if unitsNames != nil {
|
||||
if err := sq.FilterConfig(unitsNames); err != nil {
|
||||
if err := sq.MergeConfigFiles(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := sq.RenderConfig(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
args, helmArgs := parseExtraArgs(args)
|
||||
|
||||
units, err := parseUnitArgs(args, sq.GetConfig().Units)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
squadronName, unitNames := parseSquadronAndUnitNames(args)
|
||||
if err := sq.FilterConfig(squadronName, unitNames); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := sq.Generate(ctx, sq.GetConfig().Units); err != nil {
|
||||
return err
|
||||
} else if err := sq.Template(ctx, units, helmArgs); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := sq.RenderConfig(cmd.Context()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
out, err := sq.Template(cmd.Context(), helmArgs)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
fmt.Print(util.Highlight(out))
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
@ -1,144 +1,73 @@
|
||||
package actions
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os/user"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
"golang.org/x/sync/errgroup"
|
||||
"golang.org/x/sync/semaphore"
|
||||
|
||||
"github.com/foomo/squadron"
|
||||
"github.com/foomo/squadron/util"
|
||||
"github.com/foomo/squadron/internal/util"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func init() {
|
||||
upCmd.Flags().StringVarP(&flagNamespace, "namespace", "n", "default", "specifies the namespace")
|
||||
upCmd.Flags().BoolVarP(&flagBuild, "build", "b", false, "builds or rebuilds units")
|
||||
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")
|
||||
upCmd.Flags().StringSliceVar(&flagBuildArgs, "build-args", nil, "additional docker buildx build args")
|
||||
upCmd.Flags().StringSliceVar(&flagPushArgs, "push-args", nil, "additional docker push args")
|
||||
}
|
||||
|
||||
var upCmd = &cobra.Command{
|
||||
Use: "up [UNIT...]",
|
||||
Use: "up [SQUADRON] [UNIT...]",
|
||||
Short: "installs the squadron or given units",
|
||||
Example: " squadron up frontend backend --namespace demo --build --push -- --dry-run",
|
||||
Example: " squadron up storefinder frontend backend --namespace demo --build --push -- --dry-run",
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
return up(cmd.Context(), args, cwd, flagNamespace, flagBuild, flagPush, flagDiff, flagParallel, flagFiles)
|
||||
sq := squadron.New(cwd, flagNamespace, flagFiles)
|
||||
|
||||
if err := sq.MergeConfigFiles(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args, helmArgs := parseExtraArgs(args)
|
||||
|
||||
squadronName, unitNames := parseSquadronAndUnitNames(args)
|
||||
if err := sq.FilterConfig(squadronName, unitNames); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := sq.RenderConfig(cmd.Context()); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if flagBuild {
|
||||
if err := sq.Build(cmd.Context(), flagBuildArgs, flagParallel); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if flagPush {
|
||||
if err := sq.Push(cmd.Context(), flagPushArgs, flagParallel); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
username := "unknown"
|
||||
if value, err := util.NewCommand("git").Args("config", "user.name").Run(cmd.Context()); err == nil {
|
||||
username = strings.TrimSpace(value)
|
||||
} else if value, err := user.Current(); err == nil {
|
||||
username = strings.TrimSpace(value.Name)
|
||||
}
|
||||
|
||||
branch := ""
|
||||
if value, err := util.NewCommand("sh").Args("-c", "git describe --tags --exact-match 2> /dev/null || git symbolic-ref -q --short HEAD || git rev-parse --short HEAD").Run(cmd.Context()); err == nil {
|
||||
branch = strings.TrimSpace(value)
|
||||
}
|
||||
commit := ""
|
||||
if value, err := util.NewCommand("sh").Args("-c", "git rev-parse --short HEAD").Run(cmd.Context()); err == nil {
|
||||
commit = strings.TrimSpace(value)
|
||||
}
|
||||
|
||||
return sq.Up(cmd.Context(), helmArgs, username, version, commit, branch, flagParallel)
|
||||
},
|
||||
}
|
||||
|
||||
func up(ctx context.Context, args []string, cwd, namespace string, build, push, diff bool, parallel int, files []string) error {
|
||||
sq := squadron.New(cwd, namespace, files)
|
||||
|
||||
if err := sq.MergeConfigFiles(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
args, helmArgs := parseExtraArgs(args)
|
||||
|
||||
unitsNames, err := parseUnitNames(args, sq.GetConfig().Units)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if unitsNames != nil {
|
||||
if err := sq.FilterConfig(unitsNames); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := sq.RenderConfig(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
units, err := parseUnitArgs(args, sq.GetConfig().Units)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if build {
|
||||
sem := semaphore.NewWeighted(int64(parallel))
|
||||
wg, wgCtx := errgroup.WithContext(ctx)
|
||||
|
||||
_ = sq.GetConfig().Units.Iterate(func(n string, u *squadron.Unit) error {
|
||||
name := n
|
||||
unit := u
|
||||
wg.Go(func() error {
|
||||
if err := sem.Acquire(wgCtx, 1); err != nil {
|
||||
return err
|
||||
}
|
||||
defer sem.Release(1)
|
||||
if out, err := unit.Build(wgCtx, sq.Name(), name, strings.Split(flagBuildArgs, " ")); err != nil {
|
||||
return errors.Wrap(err, out)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
})
|
||||
if err := wg.Wait(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if push {
|
||||
sem := semaphore.NewWeighted(int64(parallel))
|
||||
wg, wgCtx := errgroup.WithContext(ctx)
|
||||
|
||||
_ = sq.GetConfig().Units.Iterate(func(n string, u *squadron.Unit) error {
|
||||
name := n
|
||||
unit := u
|
||||
wg.Go(func() error {
|
||||
if err := sem.Acquire(wgCtx, 1); err != nil {
|
||||
return err
|
||||
}
|
||||
defer sem.Release(1)
|
||||
if out, err := unit.Push(wgCtx, sq.Name(), name, strings.Split(flagPushArgs, " ")); err != nil {
|
||||
return errors.Wrap(err, out)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
})
|
||||
|
||||
if err := wg.Wait(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if err := sq.Generate(ctx, units); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
username := "unknown"
|
||||
if value, err := util.NewCommand("git").Args("config", "user.name").Run(ctx); err == nil {
|
||||
username = strings.TrimSpace(value)
|
||||
} else if value, err := user.Current(); err == nil {
|
||||
username = strings.TrimSpace(value.Name)
|
||||
}
|
||||
|
||||
branch := ""
|
||||
if value, err := util.NewCommand("sh").Args("-c", "git describe --tags --exact-match 2> /dev/null || git symbolic-ref -q --short HEAD || git rev-parse --short HEAD").Run(ctx); err == nil {
|
||||
branch = strings.TrimSpace(value)
|
||||
}
|
||||
commit := ""
|
||||
if value, err := util.NewCommand("sh").Args("-c", "git rev-parse --short HEAD").Run(ctx); err == nil {
|
||||
commit = strings.TrimSpace(value)
|
||||
}
|
||||
|
||||
if !diff {
|
||||
return sq.Up(ctx, units, helmArgs, username, version, commit, branch, parallel)
|
||||
} else if out, err := sq.Diff(ctx, units, helmArgs); err != nil {
|
||||
return err
|
||||
} else {
|
||||
fmt.Println(out)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -1,38 +0,0 @@
|
||||
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 Units `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)
|
||||
}
|
||||
}
|
||||
}
|
||||
29
go.mod
29
go.mod
@ -1,12 +1,13 @@
|
||||
module github.com/foomo/squadron
|
||||
|
||||
go 1.20
|
||||
go 1.21
|
||||
|
||||
require (
|
||||
github.com/1Password/connect-sdk-go v1.5.3
|
||||
github.com/alecthomas/chroma v0.10.0
|
||||
github.com/miracl/conflate v1.2.1
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/pterm/pterm v0.12.65
|
||||
github.com/pterm/pterm v0.12.67
|
||||
github.com/sergi/go-diff v1.3.1
|
||||
github.com/sirupsen/logrus v1.9.3
|
||||
github.com/spf13/cobra v1.7.0
|
||||
@ -14,21 +15,22 @@ require (
|
||||
golang.org/x/sync v0.3.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/api v0.22.2
|
||||
k8s.io/api v0.28.2
|
||||
)
|
||||
|
||||
require (
|
||||
atomicgo.dev/cursor v0.2.0 // indirect
|
||||
atomicgo.dev/keyboard v0.2.9 // indirect
|
||||
atomicgo.dev/schedule v0.0.2 // indirect
|
||||
atomicgo.dev/schedule v0.1.0 // indirect
|
||||
github.com/BurntSushi/toml v1.2.0 // indirect
|
||||
github.com/containerd/console v1.0.3 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dlclark/regexp2 v1.4.0 // indirect
|
||||
github.com/ghodss/yaml v1.0.0 // indirect
|
||||
github.com/go-logr/logr v1.2.3 // indirect
|
||||
github.com/go-logr/logr v1.2.4 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/gookit/color v1.5.3 // indirect
|
||||
github.com/gookit/color v1.5.4 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/lithammer/fuzzysearch v1.1.8 // indirect
|
||||
@ -47,15 +49,14 @@ require (
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
go.uber.org/atomic v1.10.0 // indirect
|
||||
golang.org/x/net v0.8.0 // indirect
|
||||
golang.org/x/sys v0.10.0 // indirect
|
||||
golang.org/x/term v0.10.0 // indirect
|
||||
golang.org/x/text v0.11.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
golang.org/x/net v0.13.0 // indirect
|
||||
golang.org/x/sys v0.11.0 // indirect
|
||||
golang.org/x/term v0.11.0 // indirect
|
||||
golang.org/x/text v0.12.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
k8s.io/apimachinery v0.27.4 // indirect
|
||||
k8s.io/klog/v2 v2.90.1 // indirect
|
||||
k8s.io/utils v0.0.0-20230209194617-a36077c30491 // indirect
|
||||
k8s.io/apimachinery v0.28.2 // indirect
|
||||
k8s.io/klog/v2 v2.100.1 // indirect
|
||||
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
|
||||
)
|
||||
|
||||
203
go.sum
203
go.sum
@ -1,17 +1,18 @@
|
||||
atomicgo.dev/assert v0.0.2 h1:FiKeMiZSgRrZsPo9qn/7vmr7mCsh5SZyXY4YGYiYwrg=
|
||||
atomicgo.dev/assert v0.0.2/go.mod h1:ut4NcI3QDdJtlmAxQULOmA13Gz6e2DWbSAS8RUOmNYQ=
|
||||
atomicgo.dev/cursor v0.2.0 h1:H6XN5alUJ52FZZUkI7AlJbUc1aW38GWZalpYRPpoPOw=
|
||||
atomicgo.dev/cursor v0.2.0/go.mod h1:Lr4ZJB3U7DfPPOkbH7/6TOtJ4vFGHlgj1nc+n900IpU=
|
||||
atomicgo.dev/keyboard v0.2.9 h1:tOsIid3nlPLZ3lwgG8KZMp/SFmr7P0ssEN5JUsm78K8=
|
||||
atomicgo.dev/keyboard v0.2.9/go.mod h1:BC4w9g00XkxH/f1HXhW2sXmJFOCWbKn9xrOunSFtExQ=
|
||||
atomicgo.dev/schedule v0.0.2 h1:2e/4KY6t3wokja01Cyty6qgkQM8MotJzjtqCH70oX2Q=
|
||||
atomicgo.dev/schedule v0.0.2/go.mod h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
atomicgo.dev/schedule v0.1.0 h1:nTthAbhZS5YZmgYbb2+DH8uQIZcTlIrd4eYr3UQxEjs=
|
||||
atomicgo.dev/schedule v0.1.0/go.mod h1:xeUa3oAkiuHYh8bKiQBRojqAMq3PXXbJujjb0hw8pEU=
|
||||
github.com/1Password/connect-sdk-go v1.5.3 h1:KyjJ+kCKj6BwB2Y8tPM1Ixg5uIS6HsB0uWA8U38p/Uk=
|
||||
github.com/1Password/connect-sdk-go v1.5.3/go.mod h1:5rSymY4oIYtS4G3t0oMkGAXBeoYiukV3vkqlnEjIDJs=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.2.0 h1:Rt8g24XnyGTyglgET/PRUNlrUeu9F5L+7FilkXfZgs0=
|
||||
github.com/BurntSushi/toml v1.2.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
|
||||
github.com/HdrHistogram/hdrhistogram-go v1.1.2 h1:5IcZpTvzydCQeHzK4Ef/D5rrSqwxob0t8PQPMybUNFM=
|
||||
github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
|
||||
github.com/MarvinJWendt/testza v0.1.0/go.mod h1:7AxNvlfeHP7Z/hDQ5JtE3OKYT3XFUeLCDE2DQninSqs=
|
||||
github.com/MarvinJWendt/testza v0.2.1/go.mod h1:God7bhG8n6uQxwdScay+gjm9/LnO4D3kkcZX4hv9Rp8=
|
||||
github.com/MarvinJWendt/testza v0.2.8/go.mod h1:nwIcjmr0Zz+Rcwfh3/4UhBp7ePKVhuBExvZqnKYWlII=
|
||||
@ -20,78 +21,38 @@ github.com/MarvinJWendt/testza v0.2.12/go.mod h1:JOIegYyV7rX+7VZ9r77L/eH6CfJHHzX
|
||||
github.com/MarvinJWendt/testza v0.3.0/go.mod h1:eFcL4I0idjtIx8P9C6KkAuLgATNKpX4/2oUqKc6bF2c=
|
||||
github.com/MarvinJWendt/testza v0.4.2/go.mod h1:mSdhXiKH8sg/gQehJ63bINcCKp7RtYewEjXsvsVUPbE=
|
||||
github.com/MarvinJWendt/testza v0.5.2 h1:53KDo64C1z/h/d/stCYCPY69bt/OSwjq5KpFNwi+zB4=
|
||||
github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ=
|
||||
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||
github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY=
|
||||
github.com/MarvinJWendt/testza v0.5.2/go.mod h1:xu53QFE5sCdjtMCKk8YMQ2MnymimEctc4n3EjyIYvEY=
|
||||
github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
|
||||
github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
|
||||
github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw=
|
||||
github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE=
|
||||
github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc=
|
||||
github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/evanphx/json-patch v4.11.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/dlclark/regexp2 v1.4.0 h1:F1rxgk7p4uKjwIQxBs9oAXe5CqrXlCduYEJvrF4u93E=
|
||||
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
|
||||
github.com/go-logr/logr v0.4.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
|
||||
github.com/go-logr/logr v1.2.0/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.2.3 h1:2DntVwHkVopvECVRSlL5PSo9eG+cAkDCuckLubN+rq0=
|
||||
github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
||||
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/googleapis/gnostic v0.5.1/go.mod h1:6U4PtQXGIEt/Z3h5MAT7FNofLnw9vXk2cUuW7uA/OeU=
|
||||
github.com/googleapis/gnostic v0.5.5/go.mod h1:7+EbHbldMins07ALC74bsA81Ovc97DwqyJO1AENw9kA=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gookit/color v1.4.2/go.mod h1:fqRyamkC1W8uxl+lxCQxOT09l/vYfZ+QeiX3rKQHCoQ=
|
||||
github.com/gookit/color v1.5.0/go.mod h1:43aQb+Zerm/BWh2GnrgOQm7ffz7tvQXEKV6BFMl7wAo=
|
||||
github.com/gookit/color v1.5.3 h1:twfIhZs4QLCtimkP7MOxlF3A0U/5cDPseRT9M/+2SCE=
|
||||
github.com/gookit/color v1.5.3/go.mod h1:NUzwzeehUfl7GIb36pqId+UGmRfQcU/WiiyTTeNjHtE=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
|
||||
github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
@ -100,48 +61,30 @@ github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02
|
||||
github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||
github.com/klauspost/cpuid/v2 v2.2.3 h1:sxCkb+qR91z4vsqw4vGGZlDgPz3G7gjaLyK3V8y70BU=
|
||||
github.com/klauspost/cpuid/v2 v2.2.3/go.mod h1:RVVoqg1df56z8g3pUjL/3lE5UfnlrJX8tyFgg4nqhuY=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/lithammer/fuzzysearch v1.1.8 h1:/HIuJnjHuXS8bKaiTMeeDlW2/AyIWk2brx1V8LFgLN4=
|
||||
github.com/lithammer/fuzzysearch v1.1.8/go.mod h1:IdqeyBClc3FFqSzYq/MXESsS4S0FsZ5ajtkr5xPLts4=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
|
||||
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
|
||||
github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
|
||||
github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY=
|
||||
github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
|
||||
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
|
||||
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/pterm/pterm v0.12.27/go.mod h1:PhQ89w4i95rhgE+xedAoqous6K9X+r6aSOI2eFF7DZI=
|
||||
github.com/pterm/pterm v0.12.29/go.mod h1:WI3qxgvoQFFGKGjGnJR849gU0TsEOvKn5Q8LlY1U7lg=
|
||||
github.com/pterm/pterm v0.12.30/go.mod h1:MOqLIyMOgmTDz9yorcYbcw+HsgoZo3BQfg2wtl3HEFE=
|
||||
@ -149,8 +92,8 @@ github.com/pterm/pterm v0.12.31/go.mod h1:32ZAWZVXD7ZfG0s8qqHXePte42kdz8ECtRyEej
|
||||
github.com/pterm/pterm v0.12.33/go.mod h1:x+h2uL+n7CP/rel9+bImHD5lF3nM9vJj80k9ybiiTTE=
|
||||
github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5bUw8T8=
|
||||
github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s=
|
||||
github.com/pterm/pterm v0.12.65 h1:HNMNCh2Zi6Lk+g5b8pORrFM9Ygz10GZUUcCFUkGpK2Q=
|
||||
github.com/pterm/pterm v0.12.65/go.mod h1:CpJq+fr0+xKGlPFDhKTkepte2fY3Ydr5bzSJ9di67uI=
|
||||
github.com/pterm/pterm v0.12.67 h1:5iB7ajIQROYfxYD7+sFJ4+KJhFJ+xn7QOVBm4s6RUF0=
|
||||
github.com/pterm/pterm v0.12.67/go.mod h1:nFuT9ZVkkCi8o4L1dtWuYPwDQxggLh4C263qG5nTLpQ=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.4 h1:8TfxU8dW6PdqD27gjM8MVNuicgxIjxpm4K7x4jp8sis=
|
||||
github.com/rivo/uniseg v0.4.4/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
@ -166,15 +109,13 @@ github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/spf13/cobra v1.7.0 h1:hyqWnYt1ZQShIddO5kBpj3vu05/++x6tJ6dg8EC572I=
|
||||
github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0=
|
||||
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||
@ -202,37 +143,22 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191125084936-ffdde1057850/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210520170846-37e1c6afe023/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.8.0 h1:Zrh2ngAOFYneWTAIAPethzeaQLuHwhuBkuV6ZiRnUaQ=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/net v0.13.0 h1:Nvo8UFsZ8X3BhAC9699Z1j7XQ3rsZnUUm7jfBEk1ueY=
|
||||
golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@ -240,51 +166,36 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.3.0 h1:ftCYgMx6zT/asHUrPw8BLLscYtGznsLAnjq5RH9P66E=
|
||||
golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0 h1:SqMFp9UcQJZa+pmYuAKjd9xq1f0j5rLcDIk0mj4qAsA=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0 h1:eG7RXZHdqOJ1i+0lgLgCpSXAp6M3LYlAo6osgSi0xOM=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.10.0 h1:3R7pNqamzBraeqj/Tj8qt1aQ2HpmlC+Cx/qL/7hn4/c=
|
||||
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
|
||||
golang.org/x/term v0.11.0 h1:F9tnn/DA/Im8nCwm+fX+1/eBwi4qFjRT++MhtVC4ZX0=
|
||||
golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.11.0 h1:LAntKIrcmeSKERyiOh0XMV39LXS8IE9UL2yP7+f5ij4=
|
||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.12.0 h1:k+n5B8goJNdU7hSvEtMUz3d1Q6D/XW4COJSJR6fN0mc=
|
||||
golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
@ -294,69 +205,33 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20201019141844-1ed22bb0c154/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
||||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.7/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
k8s.io/api v0.22.2 h1:M8ZzAD0V6725Fjg53fKeTJxGsJvRbk4TEm/fexHMtfw=
|
||||
k8s.io/api v0.22.2/go.mod h1:y3ydYpLJAaDI+BbSe2xmGcqxiWHmWjkEeIbiwHvnPR8=
|
||||
k8s.io/apimachinery v0.22.2/go.mod h1:O3oNtNadZdeOMxHFVxOreoznohCpy0z6mocxbZr7oJ0=
|
||||
k8s.io/apimachinery v0.27.4 h1:CdxflD4AF61yewuid0fLl6bM4a3q04jWel0IlP+aYjs=
|
||||
k8s.io/apimachinery v0.27.4/go.mod h1:XNfZ6xklnMCOGGFNqXG7bUrQCoR04dh/E7FprV6pb+E=
|
||||
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE=
|
||||
k8s.io/klog/v2 v2.9.0/go.mod h1:hy9LJ/NvuK+iVyP4Ehqva4HxZG/oXyIS3n3Jmire4Ec=
|
||||
k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw=
|
||||
k8s.io/klog/v2 v2.90.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
|
||||
k8s.io/kube-openapi v0.0.0-20210421082810-95288971da7e/go.mod h1:vHXdDvt9+2spS2Rx9ql3I8tycm3H9FDfdUoIuKCefvw=
|
||||
k8s.io/utils v0.0.0-20230209194617-a36077c30491 h1:r0BAOLElQnnFhE/ApUsg3iHdVYYPBjNSSOMowRZxxsY=
|
||||
k8s.io/utils v0.0.0-20230209194617-a36077c30491/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
k8s.io/api v0.28.2 h1:9mpl5mOb6vXZvqbQmankOfPIGiudghwCoLl1EYfUZbw=
|
||||
k8s.io/api v0.28.2/go.mod h1:RVnJBsjU8tcMq7C3iaRSGMeaKt2TWEUXcpIt/90fjEg=
|
||||
k8s.io/apimachinery v0.28.2 h1:KCOJLrc6gu+wV1BYgwik4AF4vXOlVJPdiqn0yAWWwXQ=
|
||||
k8s.io/apimachinery v0.28.2/go.mod h1:RdzF87y/ngqk9H4z3EL2Rppv5jj95vGS/HaFXrLDApU=
|
||||
k8s.io/klog/v2 v2.100.1 h1:7WCHKK6K8fNhTqfBhISHQ97KrnJNFZMcQvKp7gP/tmg=
|
||||
k8s.io/klog/v2 v2.100.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0=
|
||||
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 h1:qY1Ad8PODbnymg2pRbkyMT/ylpTrCM8P2RJ0yroCyIk=
|
||||
k8s.io/utils v0.0.0-20230406110748-d93618cff8a2/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
|
||||
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.1.2/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E=
|
||||
sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
|
||||
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
|
||||
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
|
||||
|
||||
15
init.go
Normal file
15
init.go
Normal file
@ -0,0 +1,15 @@
|
||||
package squadron
|
||||
|
||||
import (
|
||||
"github.com/miracl/conflate"
|
||||
yamlv2 "gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
func init() {
|
||||
yamlv2.FutureLineWrap()
|
||||
// define the unmarshallers for the given file extensions, blank extension is the global unmarshaller
|
||||
conflate.Unmarshallers = conflate.UnmarshallerMap{
|
||||
".yaml": conflate.UnmarshallerFuncs{conflate.YAMLUnmarshal},
|
||||
".yml": conflate.UnmarshallerFuncs{conflate.YAMLUnmarshal},
|
||||
}
|
||||
}
|
||||
@ -1,4 +1,4 @@
|
||||
package squadron
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
@ -7,12 +7,7 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/foomo/squadron/util"
|
||||
)
|
||||
|
||||
const (
|
||||
TagMap = "!!map"
|
||||
TagString = "!!str"
|
||||
"github.com/foomo/squadron/internal/util"
|
||||
)
|
||||
|
||||
type Build struct {
|
||||
@ -45,7 +40,6 @@ type Build struct {
|
||||
// ~ Public methods
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
// Build ...
|
||||
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).
|
||||
@ -78,24 +72,24 @@ func (b *Build) Build(ctx context.Context, args []string) (string, error) {
|
||||
Run(ctx)
|
||||
}
|
||||
|
||||
// Push ...
|
||||
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(b.Image, b.Tag).Args(args...).Run(ctx)
|
||||
}
|
||||
|
||||
// UnmarshalYAML ...
|
||||
func (b *Build) UnmarshalYAML(value *yaml.Node) error {
|
||||
if value.Tag == TagMap {
|
||||
switch value.Tag {
|
||||
case "!!map":
|
||||
type wrapper Build
|
||||
return value.Decode((*wrapper)(b))
|
||||
}
|
||||
if value.Tag == TagString {
|
||||
case "!!str":
|
||||
var vString string
|
||||
if err := value.Decode(&vString); err != nil {
|
||||
return err
|
||||
}
|
||||
b.Context = vString
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("unsupported node tag type for %T: %q", b, value.Tag)
|
||||
}
|
||||
return fmt.Errorf("unsupported node tag type for %T: %q", b, value.Tag)
|
||||
}
|
||||
31
internal/config/config.go
Normal file
31
internal/config/config.go
Normal file
@ -0,0 +1,31 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Version string `yaml:"version,omitempty"`
|
||||
Global map[string]interface{} `yaml:"global,omitempty"`
|
||||
Squadrons Map[Map[*Unit]] `yaml:"squadron,omitempty"`
|
||||
}
|
||||
|
||||
func (c *Config) Trim() {
|
||||
_ = c.Squadrons.Iterate(func(key string, value Map[*Unit]) error {
|
||||
value.Trim()
|
||||
return nil
|
||||
})
|
||||
c.Squadrons.Trim()
|
||||
}
|
||||
|
||||
func (c *Config) UnmarshalYAML(value *yaml.Node) error {
|
||||
switch value.Tag {
|
||||
case "!!map":
|
||||
type wrapper Config
|
||||
return value.Decode((*wrapper)(c))
|
||||
default:
|
||||
return fmt.Errorf("unsupported node tag type for %T: %q", c, value.Tag)
|
||||
}
|
||||
}
|
||||
1
internal/config/doc.go
Normal file
1
internal/config/doc.go
Normal file
@ -0,0 +1 @@
|
||||
package config
|
||||
93
internal/config/map.go
Normal file
93
internal/config/map.go
Normal file
@ -0,0 +1,93 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"slices"
|
||||
"sort"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type Map[T any] map[string]T
|
||||
|
||||
// Trim remove empty entries
|
||||
func (m Map[T]) Trim() {
|
||||
for key, value := range m {
|
||||
val := reflect.ValueOf(value)
|
||||
if val.Kind() == reflect.Ptr {
|
||||
val = val.Elem()
|
||||
}
|
||||
if !val.IsValid() {
|
||||
delete(m, key)
|
||||
continue
|
||||
}
|
||||
if val.IsZero() {
|
||||
delete(m, key)
|
||||
continue
|
||||
}
|
||||
|
||||
switch val.Kind() {
|
||||
case reflect.Map, reflect.Slice:
|
||||
if val.Len() == 0 {
|
||||
delete(m, key)
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Keys returns the keys as a sorted list
|
||||
func (m Map[T]) Keys() []string {
|
||||
if reflect.ValueOf(m).IsZero() {
|
||||
return nil
|
||||
}
|
||||
ret := make([]string, 0, len(m))
|
||||
for key := range m {
|
||||
ret = append(ret, key)
|
||||
}
|
||||
sort.Strings(ret)
|
||||
return ret
|
||||
}
|
||||
|
||||
// Values returns all values sorted by keys
|
||||
func (m Map[T]) Values() []T {
|
||||
if len(m) == 0 {
|
||||
return nil
|
||||
}
|
||||
keys := m.Keys()
|
||||
ret := make([]T, 0, len(keys))
|
||||
for i, key := range keys {
|
||||
ret[i] = m[key]
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (m Map[T]) Filter(keys ...string) error {
|
||||
if len(keys) == 0 {
|
||||
return nil
|
||||
}
|
||||
validKeys := m.Keys()
|
||||
for _, key := range keys {
|
||||
if !slices.Contains(validKeys, key) {
|
||||
return errors.Errorf("key not found: `%s`", key)
|
||||
}
|
||||
}
|
||||
for key := range m {
|
||||
if !slices.Contains(keys, key) {
|
||||
delete(m, key)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m Map[T]) Iterate(handler func(key string, value T) error) error {
|
||||
if len(m) == 0 {
|
||||
return nil
|
||||
}
|
||||
for _, key := range m.Keys() {
|
||||
if err := handler(key, m[key]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
50
internal/config/map_test.go
Normal file
50
internal/config/map_test.go
Normal file
@ -0,0 +1,50 @@
|
||||
package config_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/foomo/squadron/internal/config"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestMap_Trim(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
value config.Map[any]
|
||||
want int
|
||||
}{
|
||||
{
|
||||
name: "string",
|
||||
value: config.Map[any]{
|
||||
"foo": "bar",
|
||||
"baz": "",
|
||||
},
|
||||
want: 1,
|
||||
},
|
||||
{
|
||||
name: "slice",
|
||||
value: config.Map[any]{
|
||||
"foo": []string{"foo"},
|
||||
"baz": []string{},
|
||||
"bar": nil,
|
||||
},
|
||||
want: 1,
|
||||
},
|
||||
{
|
||||
name: "map",
|
||||
value: config.Map[any]{
|
||||
"foo": map[string]string{"foo": "foo"},
|
||||
"baz": map[string]string{},
|
||||
"bar": nil,
|
||||
},
|
||||
want: 1,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(tt *testing.T) {
|
||||
test.value.Trim()
|
||||
assert.Len(tt, test.value, test.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1,13 +1,19 @@
|
||||
package squadron
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/foomo/squadron/internal/helm"
|
||||
"github.com/foomo/squadron/internal/util"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/pterm/pterm"
|
||||
yamlv2 "gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
type Unit struct {
|
||||
Chart ChartDependency `yaml:"chart,omitempty"`
|
||||
Chart helm.Dependency `yaml:"chart,omitempty"`
|
||||
Builds map[string]Build `yaml:"builds,omitempty"`
|
||||
Values map[string]interface{} `yaml:"values,omitempty"`
|
||||
}
|
||||
@ -16,7 +22,17 @@ type Unit struct {
|
||||
// ~ Public methods
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
// Build ...
|
||||
func (u *Unit) ValuesYAML(global map[string]interface{}) ([]byte, error) {
|
||||
values := u.Values
|
||||
if values == nil {
|
||||
values = map[string]interface{}{}
|
||||
}
|
||||
if global != nil {
|
||||
values["global"] = global
|
||||
}
|
||||
return yamlv2.Marshal(values)
|
||||
}
|
||||
|
||||
func (u *Unit) Build(ctx context.Context, squadron, unit string, args []string) (string, error) {
|
||||
var i int
|
||||
for _, build := range u.Builds {
|
||||
@ -32,7 +48,6 @@ func (u *Unit) Build(ctx context.Context, squadron, unit string, args []string)
|
||||
return "", nil
|
||||
}
|
||||
|
||||
// Push ...
|
||||
func (u *Unit) Push(ctx context.Context, squadron, unit string, args []string) (string, error) {
|
||||
var i int
|
||||
for _, build := range u.Builds {
|
||||
@ -47,3 +62,19 @@ func (u *Unit) Push(ctx context.Context, squadron, unit string, args []string) (
|
||||
}
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (u *Unit) DependencyUpdate(ctx context.Context) error {
|
||||
// update local chart dependencies
|
||||
// https://stackoverflow.com/questions/59210148/error-found-in-chart-yaml-but-missing-in-charts-directory-mysql
|
||||
if strings.HasPrefix(u.Chart.Repository, "file:///") {
|
||||
pterm.Debug.Printfln("running helm dependency update for %s", u.Chart.Repository)
|
||||
if out, err := util.NewHelmCommand().
|
||||
Stdout(os.Stdout).
|
||||
Args("dependency", "update").
|
||||
Cwd(strings.TrimPrefix(u.Chart.Repository, "file://")).
|
||||
Run(ctx); err != nil {
|
||||
return errors.Wrap(err, out)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
3
internal/config/units.go
Normal file
3
internal/config/units.go
Normal file
@ -0,0 +1,3 @@
|
||||
package config
|
||||
|
||||
type Units[T any] Map[T]
|
||||
41
internal/helm/chart.go
Normal file
41
internal/helm/chart.go
Normal file
@ -0,0 +1,41 @@
|
||||
package helm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
|
||||
"github.com/foomo/squadron/internal/util"
|
||||
)
|
||||
|
||||
type Chart struct {
|
||||
APIVersion string `yaml:"apiVersion"`
|
||||
Name string `yaml:"name,omitempty"`
|
||||
Description string `yaml:"description,omitempty"`
|
||||
Type string `yaml:"type,omitempty"`
|
||||
Version string `yaml:"version,omitempty"`
|
||||
Dependencies []Dependency `yaml:"dependencies,omitempty"`
|
||||
}
|
||||
|
||||
func NewChart(name, version string) *Chart {
|
||||
return &Chart{
|
||||
APIVersion: chartAPIVersionV2,
|
||||
Name: name,
|
||||
Description: fmt.Sprintf("A helm parent chart for squadron %v", name),
|
||||
Type: defaultChartType,
|
||||
Version: version,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Chart) AddDependency(alias string, cd Dependency) {
|
||||
cd.Alias = alias
|
||||
c.Dependencies = append(c.Dependencies, cd)
|
||||
}
|
||||
|
||||
func (c *Chart) Generate(chartPath string, overrides interface{}) error {
|
||||
// generate Chart.yaml
|
||||
if err := util.GenerateYaml(path.Join(chartPath, chartFile), c); err != nil {
|
||||
return err
|
||||
}
|
||||
// generate values.yaml
|
||||
return util.GenerateYaml(path.Join(chartPath, valuesFile), overrides)
|
||||
}
|
||||
45
internal/helm/depency.go
Normal file
45
internal/helm/depency.go
Normal file
@ -0,0 +1,45 @@
|
||||
package helm
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"path"
|
||||
|
||||
"github.com/foomo/squadron/internal/template"
|
||||
"github.com/pkg/errors"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
type Dependency struct {
|
||||
Name string `yaml:"name,omitempty"`
|
||||
Repository string `yaml:"repository,omitempty"`
|
||||
Version string `yaml:"version,omitempty"`
|
||||
Alias string `yaml:"alias,omitempty"`
|
||||
}
|
||||
|
||||
func (cd *Dependency) UnmarshalYAML(value *yaml.Node) error {
|
||||
switch value.Tag {
|
||||
case "!!map":
|
||||
type wrapper Dependency
|
||||
return value.Decode((*wrapper)(cd))
|
||||
case "!!str":
|
||||
var vString string
|
||||
if err := value.Decode(&vString); err != nil {
|
||||
return err
|
||||
}
|
||||
vBytes, err := template.ExecuteFileTemplate(context.Background(), vString, nil, true)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to render chart string")
|
||||
}
|
||||
localChart, err := loadChart(path.Join(string(vBytes), chartFile))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load local chart: " + vString)
|
||||
}
|
||||
cd.Name = localChart.Name
|
||||
cd.Repository = fmt.Sprintf("file://%v", vString)
|
||||
cd.Version = localChart.Version
|
||||
return nil
|
||||
default:
|
||||
return fmt.Errorf("unsupported node tag type for %T: %q", cd, value.Tag)
|
||||
}
|
||||
}
|
||||
27
internal/helm/doc.go
Normal file
27
internal/helm/doc.go
Normal file
@ -0,0 +1,27 @@
|
||||
package helm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
const (
|
||||
chartAPIVersionV2 = "v2"
|
||||
defaultChartType = "application" // application or library
|
||||
chartFile = "Chart.yaml"
|
||||
valuesFile = "values.yaml"
|
||||
)
|
||||
|
||||
func loadChart(path string) (*Chart, error) {
|
||||
c := Chart{}
|
||||
file, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error while opening file: %v", err)
|
||||
}
|
||||
if err := yaml.Unmarshal(file, &c); err != nil {
|
||||
return nil, fmt.Errorf("error while unmarshalling template file: %s", err)
|
||||
}
|
||||
return &c, nil
|
||||
}
|
||||
16
internal/template/default.go
Normal file
16
internal/template/default.go
Normal file
@ -0,0 +1,16 @@
|
||||
package template
|
||||
|
||||
func defaultValue(value string, def interface{}) interface{} {
|
||||
if value == "" {
|
||||
return def
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func defaultIndexValue(v map[string]interface{}, index string, def interface{}) interface{} {
|
||||
var ok bool
|
||||
if _, ok = v[index]; ok {
|
||||
return v[index]
|
||||
}
|
||||
return def
|
||||
}
|
||||
9
internal/template/encode.go
Normal file
9
internal/template/encode.go
Normal file
@ -0,0 +1,9 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
b64 "encoding/base64"
|
||||
)
|
||||
|
||||
func base64(v string) string {
|
||||
return b64.StdEncoding.EncodeToString([]byte(v))
|
||||
}
|
||||
22
internal/template/env.go
Normal file
22
internal/template/env.go
Normal file
@ -0,0 +1,22 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
func env(name string) (string, error) {
|
||||
if value := os.Getenv(name); value == "" {
|
||||
return "", fmt.Errorf("env variable %q was empty", name)
|
||||
} else {
|
||||
return value, nil
|
||||
}
|
||||
}
|
||||
|
||||
func envDefault(name, fallback string) (string, error) {
|
||||
if value := os.Getenv(name); value == "" {
|
||||
return fallback, nil
|
||||
} else {
|
||||
return value, nil
|
||||
}
|
||||
}
|
||||
23
internal/template/file.go
Normal file
23
internal/template/file.go
Normal file
@ -0,0 +1,23 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"os"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func file(ctx context.Context, templateVars interface{}, errorOnMissing bool) func(v string) (string, error) {
|
||||
return func(v string) (string, error) {
|
||||
if v == "" {
|
||||
return "", nil
|
||||
} else if fileBytes, err := os.ReadFile(v); err != nil {
|
||||
return "", errors.Wrap(err, "failed to read file")
|
||||
} else if renderedBytes, err := ExecuteFileTemplate(ctx, string(fileBytes), templateVars, errorOnMissing); err != nil {
|
||||
return "", errors.Wrap(err, "failed to render file")
|
||||
} else {
|
||||
return string(bytes.TrimSpace(renderedBytes)), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
18
internal/template/format.go
Normal file
18
internal/template/format.go
Normal file
@ -0,0 +1,18 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
func indent(spaces int, v string) string {
|
||||
pad := strings.Repeat(" ", spaces)
|
||||
return strings.ReplaceAll(v, "\n", "\n"+pad)
|
||||
}
|
||||
|
||||
func quote(v string) string {
|
||||
return "'" + v + "'"
|
||||
}
|
||||
|
||||
func replace(old, new, v string) string {
|
||||
return strings.ReplaceAll(v, old, new)
|
||||
}
|
||||
28
internal/template/git.go
Normal file
28
internal/template/git.go
Normal file
@ -0,0 +1,28 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"os/exec"
|
||||
)
|
||||
|
||||
func git(ctx context.Context) func(action string) (string, error) {
|
||||
return func(action string) (string, error) {
|
||||
cmd := exec.CommandContext(ctx, "git")
|
||||
|
||||
switch action {
|
||||
case "commitsha":
|
||||
cmd.Args = append(cmd.Args, "rev-list", "-1", "HEAD")
|
||||
case "abbrevcommitsha":
|
||||
cmd.Args = append(cmd.Args, "rev-list", "-1", "HEAD", "--abbrev-commit")
|
||||
default:
|
||||
cmd.Args = append(cmd.Args, "describe", "--tags", "--always")
|
||||
}
|
||||
res, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(bytes.TrimSpace(res)), nil
|
||||
}
|
||||
}
|
||||
@ -1,9 +1,8 @@
|
||||
package squadron
|
||||
package template
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
b64 "encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
@ -15,235 +14,14 @@ import (
|
||||
|
||||
"github.com/1Password/connect-sdk-go/connect"
|
||||
"github.com/1Password/connect-sdk-go/onepassword"
|
||||
"github.com/miracl/conflate"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func init() {
|
||||
// define the unmarshallers for the given file extensions, blank extension is the global unmarshaller
|
||||
conflate.Unmarshallers = conflate.UnmarshallerMap{
|
||||
".yaml": conflate.UnmarshallerFuncs{conflate.YAMLUnmarshal},
|
||||
".yml": conflate.UnmarshallerFuncs{conflate.YAMLUnmarshal},
|
||||
}
|
||||
}
|
||||
|
||||
type TemplateVars map[string]interface{}
|
||||
|
||||
func (tv *TemplateVars) add(name string, value interface{}) {
|
||||
(*tv)[name] = value
|
||||
}
|
||||
|
||||
func executeFileTemplate(ctx context.Context, text string, templateVars interface{}, errorOnMissing bool) ([]byte, error) {
|
||||
templateFunctions := template.FuncMap{}
|
||||
templateFunctions["env"] = env
|
||||
templateFunctions["envDefault"] = envDefault
|
||||
templateFunctions["op"] = onePassword(ctx, templateVars, errorOnMissing)
|
||||
templateFunctions["base64"] = base64
|
||||
templateFunctions["default"] = defaultValue
|
||||
templateFunctions["defaultIndex"] = defaultIndexValue
|
||||
templateFunctions["indent"] = indent
|
||||
templateFunctions["replace"] = replace
|
||||
templateFunctions["file"] = file(ctx, templateVars, errorOnMissing)
|
||||
templateFunctions["git"] = git(ctx)
|
||||
templateFunctions["quote"] = quote
|
||||
|
||||
tpl, err := template.New("squadron").Delims("<% ", " %>").Funcs(templateFunctions).Parse(text)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := bytes.NewBuffer([]byte{})
|
||||
if errorOnMissing {
|
||||
tpl = tpl.Option("missingkey=error")
|
||||
}
|
||||
if err := tpl.Funcs(templateFunctions).Execute(out, templateVars); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out.Bytes(), nil
|
||||
}
|
||||
|
||||
func env(name string) (string, error) {
|
||||
if value := os.Getenv(name); value == "" {
|
||||
return "", fmt.Errorf("env variable %q was empty", name)
|
||||
} else {
|
||||
return value, nil
|
||||
}
|
||||
}
|
||||
|
||||
func envDefault(name, fallback string) (string, error) {
|
||||
if value := os.Getenv(name); value == "" {
|
||||
return fallback, nil
|
||||
} else {
|
||||
return value, nil
|
||||
}
|
||||
}
|
||||
|
||||
func file(ctx context.Context, templateVars interface{}, errorOnMissing bool) func(v string) (string, error) {
|
||||
return func(v string) (string, error) {
|
||||
if v == "" {
|
||||
return "", nil
|
||||
} else if fileBytes, err := os.ReadFile(v); err != nil {
|
||||
return "", errors.Wrap(err, "failed to read file")
|
||||
} else if renderedBytes, err := executeFileTemplate(ctx, string(fileBytes), templateVars, errorOnMissing); err != nil {
|
||||
return "", errors.Wrap(err, "failed to render file")
|
||||
} else {
|
||||
return string(bytes.TrimSpace(renderedBytes)), nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func defaultValue(value string, def interface{}) interface{} {
|
||||
if value == "" {
|
||||
return def
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func defaultIndexValue(v map[string]interface{}, index string, def interface{}) interface{} {
|
||||
var ok bool
|
||||
if _, ok = v[index]; ok {
|
||||
return v[index]
|
||||
}
|
||||
return def
|
||||
}
|
||||
|
||||
func base64(v string) string {
|
||||
return b64.StdEncoding.EncodeToString([]byte(v))
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
toSnakeCaseKeys(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func git(ctx context.Context) func(action string) (string, error) {
|
||||
return func(action string) (string, error) {
|
||||
cmd := exec.CommandContext(ctx, "git")
|
||||
|
||||
switch action {
|
||||
case "commitsha":
|
||||
cmd.Args = append(cmd.Args, "rev-list", "-1", "HEAD")
|
||||
case "abbrevcommitsha":
|
||||
cmd.Args = append(cmd.Args, "rev-list", "-1", "HEAD", "--abbrev-commit")
|
||||
default:
|
||||
cmd.Args = append(cmd.Args, "describe", "--tags", "--always")
|
||||
}
|
||||
res, err := cmd.Output()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(bytes.TrimSpace(res)), nil
|
||||
}
|
||||
}
|
||||
|
||||
func indent(spaces int, v string) string {
|
||||
pad := strings.Repeat(" ", spaces)
|
||||
return strings.ReplaceAll(v, "\n", "\n"+pad)
|
||||
}
|
||||
|
||||
func quote(v string) string {
|
||||
return "'" + v + "'"
|
||||
}
|
||||
|
||||
func replace(old, new, v string) string {
|
||||
return strings.ReplaceAll(v, old, new)
|
||||
}
|
||||
|
||||
func render(name, text string, data interface{}, errorOnMissing bool) (string, error) {
|
||||
var opts []string
|
||||
if !errorOnMissing {
|
||||
opts = append(opts, "missingkey=error")
|
||||
}
|
||||
out := bytes.NewBuffer([]byte{})
|
||||
if uuidTpl, err := template.New(name).Option(opts...).Parse(text); err != nil {
|
||||
return "", err
|
||||
} else if err := uuidTpl.Execute(out, data); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return out.String(), nil
|
||||
}
|
||||
|
||||
var (
|
||||
onePasswordCache map[string]map[string]string
|
||||
onePasswordUUID = regexp.MustCompile(`^[a-z0-9]{26}$`)
|
||||
)
|
||||
|
||||
func onePassword(ctx context.Context, templateVars interface{}, errorOnMissing bool) func(account, vaultUUID, itemUUID, field string) (string, error) {
|
||||
if onePasswordCache == nil {
|
||||
onePasswordCache = map[string]map[string]string{}
|
||||
}
|
||||
return func(account, vaultUUID, itemUUID, field string) (string, error) {
|
||||
// validate command
|
||||
if mode := os.Getenv("OP_MODE"); mode == "ci" {
|
||||
// do nothing
|
||||
} else if _, err := exec.LookPath("op"); err != nil {
|
||||
fmt.Println("Your templates includes a call to 1Password, please install it:")
|
||||
fmt.Println("https://support.1password.com/command-line-getting-started/#set-up-the-command-line-tool")
|
||||
return "", err
|
||||
} else if os.Getenv(fmt.Sprintf("OP_SESSION_%s", account)) == "" {
|
||||
if err := onePasswordSignIn(ctx, account); err != nil {
|
||||
return "", errors.Wrap(err, "failed to sign in")
|
||||
}
|
||||
}
|
||||
|
||||
// render uuid & field params
|
||||
if value, err := render("op", itemUUID, templateVars, errorOnMissing); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
itemUUID = value
|
||||
}
|
||||
if value, err := render("op", field, templateVars, errorOnMissing); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
field = value
|
||||
}
|
||||
|
||||
// create cache key
|
||||
cacheKey := strings.Join([]string{account, vaultUUID, itemUUID}, "#")
|
||||
|
||||
if mode := os.Getenv("OP_MODE"); mode == "ci" {
|
||||
if _, ok := onePasswordCache[cacheKey]; !ok {
|
||||
if client, err := connect.NewClientFromEnvironment(); err != nil {
|
||||
return "", err
|
||||
} else if res, err := onePasswordCIGet(client, vaultUUID, itemUUID); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
onePasswordCache[cacheKey] = res
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if _, ok := onePasswordCache[cacheKey]; !ok {
|
||||
if res, err := onePasswordGet(ctx, account, vaultUUID, itemUUID); !errors.Is(err, ErrOnePasswordNotSignedIn) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
onePasswordCache[cacheKey] = res
|
||||
}
|
||||
} else if err := onePasswordSignIn(ctx, account); err != nil {
|
||||
return "", errors.Wrap(err, "failed to sign in second time")
|
||||
} else if res, err = onePasswordGet(ctx, account, vaultUUID, itemUUID); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
onePasswordCache[cacheKey] = res
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if value, ok := onePasswordCache[cacheKey][field]; !ok {
|
||||
return "", nil
|
||||
} else {
|
||||
return value, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var ErrOnePasswordNotSignedIn = errors.New("not signed in")
|
||||
|
||||
func onePasswordCIGet(client connect.Client, vaultUUID, itemUUID string) (map[string]string, error) {
|
||||
@ -336,3 +114,86 @@ func onePasswordSignIn(ctx context.Context, account string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func onePassword(ctx context.Context, templateVars interface{}, errorOnMissing bool) func(account, vaultUUID, itemUUID, field string) (string, error) {
|
||||
if onePasswordCache == nil {
|
||||
onePasswordCache = map[string]map[string]string{}
|
||||
}
|
||||
return func(account, vaultUUID, itemUUID, field string) (string, error) {
|
||||
// validate command
|
||||
if mode := os.Getenv("OP_MODE"); mode == "ci" {
|
||||
// do nothing
|
||||
} else if _, err := exec.LookPath("op"); err != nil {
|
||||
fmt.Println("Your templates includes a call to 1Password, please install it:")
|
||||
fmt.Println("https://support.1password.com/command-line-getting-started/#set-up-the-command-line-tool")
|
||||
return "", err
|
||||
} else if os.Getenv(fmt.Sprintf("OP_SESSION_%s", account)) == "" {
|
||||
if err := onePasswordSignIn(ctx, account); err != nil {
|
||||
return "", errors.Wrap(err, "failed to sign in")
|
||||
}
|
||||
}
|
||||
|
||||
// render uuid & field params
|
||||
if value, err := onePasswordRender("op", itemUUID, templateVars, errorOnMissing); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
itemUUID = value
|
||||
}
|
||||
if value, err := onePasswordRender("op", field, templateVars, errorOnMissing); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
field = value
|
||||
}
|
||||
|
||||
// create cache key
|
||||
cacheKey := strings.Join([]string{account, vaultUUID, itemUUID}, "#")
|
||||
|
||||
if mode := os.Getenv("OP_MODE"); mode == "ci" {
|
||||
if _, ok := onePasswordCache[cacheKey]; !ok {
|
||||
if client, err := connect.NewClientFromEnvironment(); err != nil {
|
||||
return "", err
|
||||
} else if res, err := onePasswordCIGet(client, vaultUUID, itemUUID); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
onePasswordCache[cacheKey] = res
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if _, ok := onePasswordCache[cacheKey]; !ok {
|
||||
if res, err := onePasswordGet(ctx, account, vaultUUID, itemUUID); !errors.Is(err, ErrOnePasswordNotSignedIn) {
|
||||
if err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
onePasswordCache[cacheKey] = res
|
||||
}
|
||||
} else if err := onePasswordSignIn(ctx, account); err != nil {
|
||||
return "", errors.Wrap(err, "failed to sign in second time")
|
||||
} else if res, err = onePasswordGet(ctx, account, vaultUUID, itemUUID); err != nil {
|
||||
return "", err
|
||||
} else {
|
||||
onePasswordCache[cacheKey] = res
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if value, ok := onePasswordCache[cacheKey][field]; !ok {
|
||||
return "", nil
|
||||
} else {
|
||||
return value, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func onePasswordRender(name, text string, data interface{}, errorOnMissing bool) (string, error) {
|
||||
var opts []string
|
||||
if !errorOnMissing {
|
||||
opts = append(opts, "missingkey=error")
|
||||
}
|
||||
out := bytes.NewBuffer([]byte{})
|
||||
if uuidTpl, err := template.New(name).Option(opts...).Parse(text); err != nil {
|
||||
return "", err
|
||||
} else if err := uuidTpl.Execute(out, data); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return out.String(), nil
|
||||
}
|
||||
35
internal/template/template.go
Normal file
35
internal/template/template.go
Normal file
@ -0,0 +1,35 @@
|
||||
package template
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"text/template"
|
||||
)
|
||||
|
||||
func ExecuteFileTemplate(ctx context.Context, text string, templateVars interface{}, errorOnMissing bool) ([]byte, error) {
|
||||
funcMap := template.FuncMap{
|
||||
"env": env,
|
||||
"envDefault": envDefault,
|
||||
"op": onePassword(ctx, templateVars, errorOnMissing),
|
||||
"base64": base64,
|
||||
"default": defaultValue,
|
||||
"defaultIndex": defaultIndexValue,
|
||||
"indent": indent,
|
||||
"replace": replace,
|
||||
"file": file(ctx, templateVars, errorOnMissing),
|
||||
"git": git(ctx),
|
||||
"quote": quote,
|
||||
}
|
||||
tpl, err := template.New("squadron").Delims("<% ", " %>").Funcs(funcMap).Parse(text)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
out := bytes.NewBuffer([]byte{})
|
||||
if errorOnMissing {
|
||||
tpl = tpl.Option("missingkey=error")
|
||||
}
|
||||
if err := tpl.Funcs(funcMap).Execute(out, templateVars); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out.Bytes(), nil
|
||||
}
|
||||
7
internal/template/vars.go
Normal file
7
internal/template/vars.go
Normal file
@ -0,0 +1,7 @@
|
||||
package template
|
||||
|
||||
type Vars map[string]interface{}
|
||||
|
||||
func (tv *Vars) Add(name string, value interface{}) {
|
||||
(*tv)[name] = value
|
||||
}
|
||||
36
internal/testutils/snapshot.go
Normal file
36
internal/testutils/snapshot.go
Normal file
@ -0,0 +1,36 @@
|
||||
package testutils
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
// Snapshot compares v with its snapshot file
|
||||
func Snapshot(t *testing.T, name, yaml string) {
|
||||
t.Helper()
|
||||
snapshot := readSnapshot(t, name)
|
||||
if *UpdateFlag || snapshot == "" {
|
||||
writeSnapshot(t, name, yaml)
|
||||
}
|
||||
assert.Equal(t, snapshot, yaml)
|
||||
}
|
||||
|
||||
// writeSnapshot updates the snapshot file for a given test t.
|
||||
func writeSnapshot(t *testing.T, name string, content string) {
|
||||
t.Helper()
|
||||
require.NoError(t, os.WriteFile(name, []byte(content), 0600), "failed to update snapshot", name)
|
||||
}
|
||||
|
||||
// readSnapshot reads the snapshot file for a given test t.
|
||||
func readSnapshot(t *testing.T, name string) string {
|
||||
t.Helper()
|
||||
g, err := os.ReadFile(name)
|
||||
if !errors.Is(err, os.ErrNotExist) {
|
||||
require.NoError(t, err, "failed reading file", name)
|
||||
}
|
||||
return string(g)
|
||||
}
|
||||
@ -8,6 +8,7 @@ import (
|
||||
"os/exec"
|
||||
"time"
|
||||
|
||||
"github.com/pterm/pterm"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
@ -144,7 +145,7 @@ func (c *Cmd) Run(ctx context.Context) (string, error) {
|
||||
if c.cwd != "" {
|
||||
cmd.Dir = c.cwd
|
||||
}
|
||||
logrus.Debugf("executing %q", cmd.String())
|
||||
pterm.Debug.Printfln("executing %s", cmd.String())
|
||||
|
||||
combinedBuf := new(bytes.Buffer)
|
||||
traceWriter := logrus.StandardLogger().WriterLevel(logrus.TraceLevel)
|
||||
@ -153,7 +154,7 @@ func (c *Cmd) Run(ctx context.Context) (string, error) {
|
||||
cmd.Stderr = io.MultiWriter(append(c.stderrWriters, combinedBuf, traceWriter)...)
|
||||
|
||||
if c.preStartFunc != nil {
|
||||
logrus.Debug("executing pre start func")
|
||||
pterm.Debug.Println("executing pre start func")
|
||||
if err := c.preStartFunc(); err != nil {
|
||||
return combinedBuf.String(), err
|
||||
}
|
||||
@ -164,7 +165,7 @@ func (c *Cmd) Run(ctx context.Context) (string, error) {
|
||||
}
|
||||
|
||||
if c.postStartFunc != nil {
|
||||
logrus.Debug("executing post start func")
|
||||
pterm.Debug.Println("executing post start func")
|
||||
if err := c.postStartFunc(); err != nil {
|
||||
return combinedBuf.String(), err
|
||||
}
|
||||
9
internal/util/helm.go
Normal file
9
internal/util/helm.go
Normal file
@ -0,0 +1,9 @@
|
||||
package util
|
||||
|
||||
type HelmCmd struct {
|
||||
Cmd
|
||||
}
|
||||
|
||||
func NewHelmCommand() *HelmCmd {
|
||||
return &HelmCmd{*NewCommand("helm")}
|
||||
}
|
||||
47
internal/util/highlight.go
Normal file
47
internal/util/highlight.go
Normal file
@ -0,0 +1,47 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/alecthomas/chroma"
|
||||
"github.com/alecthomas/chroma/formatters"
|
||||
"github.com/alecthomas/chroma/lexers"
|
||||
"github.com/alecthomas/chroma/styles"
|
||||
"github.com/pterm/pterm"
|
||||
)
|
||||
|
||||
func Highlight(source string) string {
|
||||
var out bytes.Buffer
|
||||
// Determine lexer.
|
||||
l := lexers.Get("yaml")
|
||||
if l == nil {
|
||||
l = lexers.Analyse(source)
|
||||
}
|
||||
if l == nil {
|
||||
l = lexers.Fallback
|
||||
}
|
||||
l = chroma.Coalesce(l)
|
||||
|
||||
// Determine formatter.
|
||||
f := formatters.Get("terminal256")
|
||||
if f == nil {
|
||||
f = formatters.Fallback
|
||||
}
|
||||
|
||||
// Determine style.
|
||||
s := styles.Get("monokai")
|
||||
if s == nil {
|
||||
s = styles.Fallback
|
||||
}
|
||||
|
||||
it, err := l.Tokenise(nil, source)
|
||||
if err != nil {
|
||||
pterm.Error.Println(err.Error())
|
||||
}
|
||||
err = f.Format(&out, s, it)
|
||||
if err != nil {
|
||||
pterm.Error.Println(err.Error())
|
||||
}
|
||||
|
||||
return out.String()
|
||||
}
|
||||
23
internal/util/path.go
Normal file
23
internal/util/path.go
Normal file
@ -0,0 +1,23 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func ValidatePath(wd string, p *string) error {
|
||||
if !filepath.IsAbs(*p) {
|
||||
*p = path.Join(wd, *p)
|
||||
}
|
||||
absPath, err := filepath.Abs(*p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = os.Stat(absPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*p = absPath
|
||||
return nil
|
||||
}
|
||||
17
internal/util/strings.go
Normal file
17
internal/util/strings.go
Normal file
@ -0,0 +1,17 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"strings"
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
ToSnakeCaseKeys(v)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,14 +2,12 @@ package util
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
yamlv2 "gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
func GenerateYaml(path string, data interface{}) (err error) {
|
||||
out, marshalErr := yaml.Marshal(data)
|
||||
out, marshalErr := yamlv2.Marshal(data)
|
||||
if marshalErr != nil {
|
||||
return marshalErr
|
||||
}
|
||||
@ -25,19 +23,3 @@ func GenerateYaml(path string, data interface{}) (err error) {
|
||||
_, err = file.Write(out)
|
||||
return
|
||||
}
|
||||
|
||||
func ValidatePath(wd string, p *string) error {
|
||||
if !filepath.IsAbs(*p) {
|
||||
*p = path.Join(wd, *p)
|
||||
}
|
||||
absPath, err := filepath.Abs(*p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_, err = os.Stat(absPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*p = absPath
|
||||
return nil
|
||||
}
|
||||
809
squadron.go
809
squadron.go
@ -7,68 +7,72 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"github.com/foomo/squadron/internal/config"
|
||||
templatex "github.com/foomo/squadron/internal/template"
|
||||
"github.com/foomo/squadron/internal/util"
|
||||
"github.com/miracl/conflate"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/pterm/pterm"
|
||||
"github.com/sergi/go-diff/diffmatchpatch"
|
||||
"github.com/sirupsen/logrus"
|
||||
"golang.org/x/sync/errgroup"
|
||||
yamlv2 "gopkg.in/yaml.v2"
|
||||
"gopkg.in/yaml.v3"
|
||||
|
||||
"github.com/foomo/squadron/util"
|
||||
)
|
||||
|
||||
func init() {
|
||||
yamlv2.FutureLineWrap()
|
||||
}
|
||||
|
||||
const (
|
||||
defaultOutputDir = ".squadron"
|
||||
chartAPIVersionV2 = "v2"
|
||||
defaultChartType = "application" // application or library
|
||||
chartFile = "Chart.yaml"
|
||||
valuesFile = "values.yaml"
|
||||
errHelmReleaseNotFound = "Error: release: not found"
|
||||
)
|
||||
|
||||
type Squadron struct {
|
||||
name string
|
||||
basePath string
|
||||
namespace string
|
||||
files []string
|
||||
config string
|
||||
c Configuration
|
||||
c config.Config
|
||||
}
|
||||
|
||||
func New(basePath, namespace string, files []string) *Squadron {
|
||||
return &Squadron{
|
||||
name: filepath.Base(basePath),
|
||||
basePath: basePath,
|
||||
namespace: namespace,
|
||||
files: files,
|
||||
c: Configuration{},
|
||||
c: config.Config{},
|
||||
}
|
||||
}
|
||||
|
||||
func (sq *Squadron) Name() string {
|
||||
return sq.name
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ~ Getter
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
func (sq *Squadron) Namespace(ctx context.Context, squadron, unit string) (string, error) {
|
||||
var out bytes.Buffer
|
||||
t, err := template.New("namespace").Parse(sq.namespace)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
if err := t.Execute(&out, map[string]string{"Squadron": squadron, "Unit": unit}); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return out.String(), nil
|
||||
}
|
||||
|
||||
func (sq *Squadron) GetConfig() Configuration {
|
||||
func (sq *Squadron) Config() config.Config {
|
||||
return sq.c
|
||||
}
|
||||
|
||||
func (sq *Squadron) GetConfigYAML() string {
|
||||
func (sq *Squadron) ConfigYAML() string {
|
||||
return sq.config
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
// ~ Public methods
|
||||
// ------------------------------------------------------------------------------------------------
|
||||
|
||||
func (sq *Squadron) MergeConfigFiles() error {
|
||||
logrus.Info("merging config files")
|
||||
pterm.Debug.Println("merging config files")
|
||||
pterm.Debug.Println(strings.Join(append([]string{"using files"}, sq.files...), "\n└ "))
|
||||
|
||||
mergedFiles, err := conflate.FromFiles(sq.files...)
|
||||
@ -82,59 +86,78 @@ func (sq *Squadron) MergeConfigFiles() error {
|
||||
if err := yaml.Unmarshal(fileBytes, &sq.c); err != nil {
|
||||
return err
|
||||
}
|
||||
sq.config = string(fileBytes)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sq *Squadron) FilterConfig(units []string) error {
|
||||
unitsMap := make(map[string]bool, len(units))
|
||||
for _, unit := range units {
|
||||
unitsMap[unit] = true
|
||||
if sq.c.Version != "2.0" {
|
||||
return errors.New("Please upgrade your YAML definition to 2.0")
|
||||
}
|
||||
|
||||
for name := range sq.c.Units {
|
||||
if _, ok := unitsMap[name]; !ok {
|
||||
delete(sq.c.Units, name)
|
||||
}
|
||||
}
|
||||
value, err := yaml.Marshal(sq.c)
|
||||
sq.c.Trim()
|
||||
|
||||
value, err := yamlv2.Marshal(sq.c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sq.config = string(value)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sq *Squadron) FilterConfig(squadron string, units []string) error {
|
||||
if len(squadron) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := sq.Config().Squadrons.Filter(squadron); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(squadron) > 0 && len(units) > 0 {
|
||||
if err := sq.Config().Squadrons[squadron].Filter(units...); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
value, err := yamlv2.Marshal(sq.c)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sq.config = string(value)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sq *Squadron) RenderConfig(ctx context.Context) error {
|
||||
logrus.Info("rendering config")
|
||||
var tv TemplateVars
|
||||
pterm.Debug.Println("rendering config")
|
||||
|
||||
var tv templatex.Vars
|
||||
var vars map[string]interface{}
|
||||
if err := yaml.Unmarshal([]byte(sq.config), &vars); err != nil {
|
||||
return err
|
||||
}
|
||||
// execute again with loaded template vars
|
||||
tv = TemplateVars{}
|
||||
tv = templatex.Vars{}
|
||||
if value, ok := vars["global"]; ok {
|
||||
toSnakeCaseKeys(value)
|
||||
tv.add("Global", value)
|
||||
util.ToSnakeCaseKeys(value)
|
||||
tv.Add("Global", value)
|
||||
}
|
||||
if value, ok := vars["squadron"]; ok {
|
||||
toSnakeCaseKeys(value)
|
||||
tv.add("Squadron", value)
|
||||
util.ToSnakeCaseKeys(value)
|
||||
tv.Add("Squadron", value)
|
||||
}
|
||||
|
||||
// execute without errors to get existing values
|
||||
pterm.Debug.Println("executing file template")
|
||||
// pterm.Debug.Println(sq.config)
|
||||
out, err := executeFileTemplate(ctx, sq.config, tv, false)
|
||||
out, err := templatex.ExecuteFileTemplate(ctx, sq.config, tv, false)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to execute initial file template")
|
||||
return errors.Wrapf(err, "failed to execute initial file template\n%s", util.Highlight(sq.config))
|
||||
}
|
||||
|
||||
// re-execute for rendering copied values
|
||||
pterm.Debug.Println("re-executing file template")
|
||||
// pterm.Debug.Println(string(out))
|
||||
out, err = executeFileTemplate(ctx, string(out), tv, false)
|
||||
out, err = templatex.ExecuteFileTemplate(ctx, string(out), tv, false)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to re-execute initial file template")
|
||||
}
|
||||
@ -146,169 +169,165 @@ func (sq *Squadron) RenderConfig(ctx context.Context) error {
|
||||
}
|
||||
|
||||
// execute again with loaded template vars
|
||||
tv = TemplateVars{}
|
||||
tv = templatex.Vars{}
|
||||
if value, ok := vars["global"]; ok {
|
||||
toSnakeCaseKeys(value)
|
||||
tv.add("Global", value)
|
||||
util.ToSnakeCaseKeys(value)
|
||||
tv.Add("Global", value)
|
||||
}
|
||||
if value, ok := vars["squadron"]; ok {
|
||||
toSnakeCaseKeys(value)
|
||||
tv.add("Squadron", value)
|
||||
util.ToSnakeCaseKeys(value)
|
||||
tv.Add("Squadron", value)
|
||||
}
|
||||
|
||||
pterm.Debug.Println("executing file template")
|
||||
out, err = executeFileTemplate(ctx, sq.config, tv, true)
|
||||
out, err = templatex.ExecuteFileTemplate(ctx, sq.config, tv, true)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to execute second file template")
|
||||
}
|
||||
|
||||
pterm.Debug.Println("unmarshalling vars")
|
||||
if err := yaml.Unmarshal(out, &sq.c); err != nil {
|
||||
pterm.Error.Println(string(out))
|
||||
return errors.Wrap(err, "failed to unmarshal vars")
|
||||
}
|
||||
|
||||
sq.config = string(out)
|
||||
|
||||
if sq.c.Name != "" {
|
||||
sq.name = sq.c.Name
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sq *Squadron) Generate(ctx context.Context, units Units) error {
|
||||
logrus.WithField("path", sq.chartPath()).Infof("generating charts")
|
||||
if err := sq.cleanupOutput(sq.chartPath()); err != nil {
|
||||
func (sq *Squadron) Push(ctx context.Context, pushArgs []string, parallel int) error {
|
||||
wg, gctx := errgroup.WithContext(ctx)
|
||||
wg.SetLimit(parallel)
|
||||
|
||||
_ = sq.Config().Squadrons.Iterate(func(key string, value config.Map[*config.Unit]) error {
|
||||
return value.Iterate(func(k string, v *config.Unit) error {
|
||||
wg.Go(func() error {
|
||||
if out, err := v.Push(gctx, key, k, pushArgs); err != nil {
|
||||
return errors.Wrap(err, out)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
})
|
||||
})
|
||||
|
||||
return wg.Wait()
|
||||
}
|
||||
|
||||
func (sq *Squadron) Build(ctx context.Context, buildArgs []string, parallel int) error {
|
||||
wg, gctx := errgroup.WithContext(ctx)
|
||||
wg.SetLimit(parallel)
|
||||
|
||||
_ = sq.Config().Squadrons.Iterate(func(key string, value config.Map[*config.Unit]) error {
|
||||
return value.Iterate(func(k string, v *config.Unit) error {
|
||||
wg.Go(func() error {
|
||||
if out, err := v.Build(gctx, key, k, buildArgs); err != nil {
|
||||
return errors.Wrap(err, out)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
})
|
||||
})
|
||||
|
||||
return wg.Wait()
|
||||
}
|
||||
|
||||
func (sq *Squadron) Down(ctx context.Context, helmArgs []string, parallel int) error {
|
||||
wg, ctx := errgroup.WithContext(ctx)
|
||||
wg.SetLimit(parallel)
|
||||
|
||||
_ = sq.Config().Squadrons.Iterate(func(key string, value config.Map[*config.Unit]) error {
|
||||
return value.Iterate(func(k string, v *config.Unit) error {
|
||||
wg.Go(func() error {
|
||||
name := fmt.Sprintf("%s-%s", key, k)
|
||||
namespace, err := sq.Namespace(ctx, key, k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
stdErr := bytes.NewBuffer([]byte{})
|
||||
pterm.Debug.Printfln("running helm uninstall for: %s", name)
|
||||
if out, err := util.NewHelmCommand().Args("uninstall", name).
|
||||
Stderr(stdErr).
|
||||
Stdout(os.Stdout).
|
||||
Args("--namespace", namespace).
|
||||
Args(helmArgs...).
|
||||
Run(ctx); err != nil &&
|
||||
string(bytes.TrimSpace(stdErr.Bytes())) != fmt.Sprintf("Error: uninstall: Release not loaded: %s: release: not found", name) {
|
||||
return errors.Wrap(err, out)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
})
|
||||
})
|
||||
|
||||
return wg.Wait()
|
||||
}
|
||||
|
||||
func (sq *Squadron) Diff(ctx context.Context, helmArgs []string, parallel int) error {
|
||||
var diff string
|
||||
wg, ctx := errgroup.WithContext(ctx)
|
||||
wg.SetLimit(parallel)
|
||||
|
||||
_ = sq.Config().Squadrons.Iterate(func(key string, value config.Map[*config.Unit]) error {
|
||||
return value.Iterate(func(k string, v *config.Unit) error {
|
||||
wg.Go(func() error {
|
||||
name := fmt.Sprintf("%s-%s", key, k)
|
||||
namespace, err := sq.Namespace(ctx, key, k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
valueBytes, err := v.ValuesYAML(sq.c.Global)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pterm.Debug.Printfln("running helm diff for: %s", k)
|
||||
manifest, err := exec.CommandContext(ctx, "helm", "get", "manifest", name, "--namespace", sq.namespace).CombinedOutput()
|
||||
if err != nil && string(bytes.TrimSpace(manifest)) != errHelmReleaseNotFound {
|
||||
return errors.Wrap(err, string(manifest))
|
||||
}
|
||||
cmd := exec.CommandContext(ctx, "helm", "upgrade", name,
|
||||
"--install",
|
||||
"--namespace", namespace,
|
||||
"--set", fmt.Sprintf("squadron=%s", key),
|
||||
"--set", fmt.Sprintf("unit=%s", k),
|
||||
"--values", "-",
|
||||
"--dry-run",
|
||||
)
|
||||
cmd.Stdin = bytes.NewReader(valueBytes)
|
||||
if strings.Contains(v.Chart.Repository, "file://") {
|
||||
cmd.Args = append(cmd.Args, "/"+strings.TrimPrefix(v.Chart.Repository, "file://"))
|
||||
} else {
|
||||
cmd.Args = append(cmd.Args, v.Chart.Name, "--repo", v.Chart.Repository, "--version", v.Chart.Version)
|
||||
}
|
||||
cmd.Args = append(cmd.Args, helmArgs...)
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, string(out))
|
||||
}
|
||||
|
||||
dmp := diffmatchpatch.New()
|
||||
diff += dmp.DiffPrettyText(dmp.DiffMain(string(manifest), string(out), false))
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
})
|
||||
})
|
||||
|
||||
if err := wg.Wait(); err != nil {
|
||||
return err
|
||||
}
|
||||
if sq.c.Unite {
|
||||
return sq.generateUmbrellaChart(ctx, units)
|
||||
}
|
||||
for _, uName := range units.Keys() {
|
||||
u := units[uName]
|
||||
// update local chart dependencies
|
||||
// https://stackoverflow.com/questions/59210148/error-found-in-chart-yaml-but-missing-in-charts-directory-mysql
|
||||
if strings.HasPrefix(u.Chart.Repository, "file:///") {
|
||||
pterm.Debug.Printfln("running helm dependency update for %s", u.Chart.Repository)
|
||||
if out, err := util.NewHelmCommand().
|
||||
Stdout(os.Stdout).
|
||||
Args("dependency", "update").
|
||||
Cwd(strings.TrimPrefix(u.Chart.Repository, "file://")).
|
||||
Run(ctx); err != nil {
|
||||
return errors.Wrap(err, out)
|
||||
}
|
||||
}
|
||||
|
||||
pterm.Debug.Printfln("generating %q value overrides file in %q", uName, sq.chartPath())
|
||||
if err := sq.generateValues(u.Values, sq.chartPath(), uName); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sq *Squadron) generateUmbrellaChart(ctx context.Context, units Units) error {
|
||||
pterm.Debug.Printfln("generating chart %q files in %q", sq.name, sq.chartPath())
|
||||
if err := sq.generateChart(units, sq.chartPath(), sq.name, sq.c.Version); err != nil {
|
||||
return err
|
||||
}
|
||||
pterm.Debug.Printfln("running helm dependency update for chart: %v", sq.chartPath())
|
||||
if out, err := util.NewHelmCommand().UpdateDependency(ctx, sq.chartPath()); err != nil {
|
||||
return errors.Wrap(err, out)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sq *Squadron) Package(ctx context.Context) error {
|
||||
pterm.Debug.Printfln("running helm package for chart: %v", sq.chartPath())
|
||||
if out, err := util.NewHelmCommand().Package(ctx, sq.chartPath(), sq.basePath); err != nil {
|
||||
return errors.Wrap(err, out)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sq *Squadron) Down(ctx context.Context, units Units, helmArgs []string) error {
|
||||
if sq.c.Unite {
|
||||
pterm.Debug.Printfln("running helm uninstall for: %s", sq.chartPath())
|
||||
stdErr := bytes.NewBuffer([]byte{})
|
||||
if out, err := util.NewHelmCommand().Args("uninstall", sq.name).
|
||||
Stderr(stdErr).
|
||||
Stdout(os.Stdout).
|
||||
Args("--namespace", sq.namespace).
|
||||
Args(helmArgs...).
|
||||
Run(ctx); err != nil &&
|
||||
string(bytes.TrimSpace(stdErr.Bytes())) != fmt.Sprintf("Error: uninstall: Release not loaded: %s: release: not found", sq.name) {
|
||||
return errors.Wrap(err, out)
|
||||
}
|
||||
}
|
||||
for _, uName := range units.Keys() {
|
||||
// todo use release prefix on install: squadron name or --name
|
||||
rName := fmt.Sprintf("%s-%s", sq.name, uName)
|
||||
pterm.Debug.Printfln("running helm uninstall for: %s", uName)
|
||||
stdErr := bytes.NewBuffer([]byte{})
|
||||
if out, err := util.NewHelmCommand().Args("uninstall", rName).
|
||||
Stderr(stdErr).
|
||||
Stdout(os.Stdout).
|
||||
Args("--namespace", sq.namespace).
|
||||
Args(helmArgs...).
|
||||
Run(ctx); err != nil &&
|
||||
string(bytes.TrimSpace(stdErr.Bytes())) != fmt.Sprintf("Error: uninstall: Release not loaded: %s: release: not found", rName) {
|
||||
return errors.Wrap(err, out)
|
||||
}
|
||||
}
|
||||
fmt.Println(diff)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sq *Squadron) Diff(ctx context.Context, units Units, helmArgs []string) (string, error) {
|
||||
if sq.c.Unite {
|
||||
pterm.Debug.Printfln("running helm diff for: %s", sq.chartPath())
|
||||
manifest, err := exec.CommandContext(ctx, "helm", "get", "manifest", sq.name, "--namespace", sq.namespace).CombinedOutput() //nolint:gosec
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, string(manifest))
|
||||
}
|
||||
cmd := exec.CommandContext(ctx, "helm", "upgrade", sq.name, sq.chartPath(), "--namespace", sq.namespace, "--dry-run") //nolint:gosec
|
||||
cmd.Args = append(cmd.Args, helmArgs...)
|
||||
template, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, string(template))
|
||||
}
|
||||
dmp := diffmatchpatch.New()
|
||||
return dmp.DiffPrettyText(dmp.DiffMain(string(manifest), string(template), false)), nil
|
||||
}
|
||||
for _, uName := range units.Keys() {
|
||||
u := units[uName]
|
||||
// todo use release prefix on install: squadron name or --name
|
||||
rName := fmt.Sprintf("%s-%s", sq.name, uName)
|
||||
pterm.Debug.Printfln("running helm diff for: %s", uName)
|
||||
manifest, err := exec.CommandContext(ctx, "helm", "get", "manifest", rName, "--namespace", sq.namespace).CombinedOutput()
|
||||
if err != nil && string(bytes.TrimSpace(manifest)) != errHelmReleaseNotFound {
|
||||
return "", errors.Wrap(err, string(manifest))
|
||||
}
|
||||
cmd := exec.CommandContext(ctx, "helm", "upgrade", rName,
|
||||
"--install",
|
||||
"--namespace", sq.namespace,
|
||||
"-f", path.Join(sq.chartPath(), uName+".yaml"),
|
||||
"--set", fmt.Sprintf("squadron=%s,unit=%s", sq.name, uName),
|
||||
"--dry-run",
|
||||
)
|
||||
if strings.Contains(u.Chart.Repository, "file://") {
|
||||
cmd.Args = append(cmd.Args, "/"+strings.TrimPrefix(u.Chart.Repository, "file://"))
|
||||
} else {
|
||||
cmd.Args = append(cmd.Args, u.Chart.Name, "--repo", u.Chart.Repository, "--version", u.Chart.Version)
|
||||
}
|
||||
cmd.Args = append(cmd.Args, helmArgs...)
|
||||
template, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return "", errors.Wrap(err, string(template))
|
||||
}
|
||||
dmp := diffmatchpatch.New()
|
||||
_, _ = fmt.Println(dmp.DiffPrettyText(dmp.DiffMain(string(manifest), string(template), false)))
|
||||
}
|
||||
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func (sq *Squadron) Status(ctx context.Context, units Units, helmArgs []string) error {
|
||||
func (sq *Squadron) Status(ctx context.Context, helmArgs []string, parallel int) error {
|
||||
tbd := pterm.TableData{
|
||||
{"Name", "Revision", "Status", "Deployed by", "Commit", "Branch", "Last deployed", "Notes"},
|
||||
}
|
||||
@ -329,269 +348,209 @@ func (sq *Squadron) Status(ctx context.Context, units Units, helmArgs []string)
|
||||
gitBranch string `json:"-"`
|
||||
}
|
||||
|
||||
var status statusType
|
||||
wg, ctx := errgroup.WithContext(ctx)
|
||||
wg.SetLimit(parallel)
|
||||
|
||||
if sq.c.Unite {
|
||||
stdErr := bytes.NewBuffer([]byte{})
|
||||
pterm.Debug.Printfln("running helm status for chart: %s", sq.chartPath())
|
||||
if out, err := util.NewHelmCommand().Args("status", sq.name).
|
||||
Stderr(stdErr).
|
||||
Args("--namespace", sq.namespace, "--output", "json", "--show-desc").
|
||||
Args(helmArgs...).
|
||||
Run(ctx); err != nil && string(bytes.TrimSpace(stdErr.Bytes())) == errHelmReleaseNotFound {
|
||||
tbd = append(tbd, []string{sq.name, "0", "not installed", "", ""})
|
||||
} else if err != nil {
|
||||
return errors.Wrap(err, out)
|
||||
} else if err := json.Unmarshal([]byte(out), &status); err != nil {
|
||||
return errors.Wrap(err, out)
|
||||
} else {
|
||||
var notes []string
|
||||
for _, line := range strings.Split(status.Info.Description, "\n") {
|
||||
if strings.HasPrefix(line, "Managed-By: ") {
|
||||
// do nothing
|
||||
} else if strings.HasPrefix(line, "Deployed-By: ") {
|
||||
status.deployedBy = strings.TrimPrefix(line, "Deployed-By: ")
|
||||
} else if strings.HasPrefix(line, "Git-Commit: ") {
|
||||
status.gitCommit = strings.TrimPrefix(line, "Git-Commit: ")
|
||||
} else if strings.HasPrefix(line, "Git-Branch: ") {
|
||||
status.gitBranch = strings.TrimPrefix(line, "Git-Branch: ")
|
||||
} else {
|
||||
notes = append(notes, line)
|
||||
}
|
||||
_ = sq.Config().Squadrons.Iterate(func(key string, value config.Map[*config.Unit]) error {
|
||||
return value.Iterate(func(k string, v *config.Unit) error {
|
||||
var status statusType
|
||||
name := fmt.Sprintf("%s-%s", key, k)
|
||||
namespace, err := sq.Namespace(ctx, key, k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tbd = append(tbd, []string{
|
||||
status.Name,
|
||||
fmt.Sprintf("%d", status.Version),
|
||||
status.Info.Status,
|
||||
status.deployedBy,
|
||||
status.gitCommit,
|
||||
status.gitBranch,
|
||||
status.Info.LastDeployed, strings.Join(notes, " | "),
|
||||
})
|
||||
}
|
||||
}
|
||||
for _, uName := range units.Keys() {
|
||||
stdErr := bytes.NewBuffer([]byte{})
|
||||
// todo use release prefix on install: squadron name or --name
|
||||
rName := fmt.Sprintf("%s-%s", sq.name, uName)
|
||||
pterm.Debug.Printfln("running helm status for %s", uName)
|
||||
if out, err := util.NewHelmCommand().Args("status", rName).
|
||||
Stderr(stdErr).
|
||||
Args("--namespace", sq.namespace, "--output", "json", "--show-desc").
|
||||
Args(helmArgs...).Run(ctx); err != nil && string(bytes.TrimSpace(stdErr.Bytes())) == errHelmReleaseNotFound {
|
||||
tbd = append(tbd, []string{rName, "0", "not installed", "", ""})
|
||||
} else if err != nil {
|
||||
return errors.Wrap(err, out)
|
||||
} else if err := json.Unmarshal([]byte(out), &status); err != nil {
|
||||
return errors.Wrap(err, out)
|
||||
} else {
|
||||
var notes []string
|
||||
for _, line := range strings.Split(status.Info.Description, "\n") {
|
||||
if strings.HasPrefix(line, "Managed-By: ") {
|
||||
// do nothing
|
||||
} else if strings.HasPrefix(line, "Deployed-By: ") {
|
||||
status.deployedBy = strings.TrimPrefix(line, "Deployed-By: ")
|
||||
} else if strings.HasPrefix(line, "Git-Commit: ") {
|
||||
status.gitCommit = strings.TrimPrefix(line, "Git-Commit: ")
|
||||
} else if strings.HasPrefix(line, "Git-Branch: ") {
|
||||
status.gitBranch = strings.TrimPrefix(line, "Git-Branch: ")
|
||||
} else {
|
||||
notes = append(notes, line)
|
||||
|
||||
stdErr := bytes.NewBuffer([]byte{})
|
||||
pterm.Debug.Printfln("running helm status for %s", name)
|
||||
if out, err := util.NewHelmCommand().Args("status", name).
|
||||
Stderr(stdErr).
|
||||
Args("--namespace", namespace, "--output", "json", "--show-desc").
|
||||
Args(helmArgs...).Run(ctx); err != nil && string(bytes.TrimSpace(stdErr.Bytes())) == errHelmReleaseNotFound {
|
||||
tbd = append(tbd, []string{name, "0", "not installed", "", ""})
|
||||
} else if err != nil {
|
||||
return errors.Wrap(err, out)
|
||||
} else if err := json.Unmarshal([]byte(out), &status); err != nil {
|
||||
return errors.Wrap(err, out)
|
||||
} else {
|
||||
var notes []string
|
||||
for _, line := range strings.Split(status.Info.Description, "\n") {
|
||||
if strings.HasPrefix(line, "Managed-By: ") {
|
||||
// do nothing
|
||||
} else if strings.HasPrefix(line, "Deployed-By: ") {
|
||||
status.deployedBy = strings.TrimPrefix(line, "Deployed-By: ")
|
||||
} else if strings.HasPrefix(line, "Git-Commit: ") {
|
||||
status.gitCommit = strings.TrimPrefix(line, "Git-Commit: ")
|
||||
} else if strings.HasPrefix(line, "Git-Branch: ") {
|
||||
status.gitBranch = strings.TrimPrefix(line, "Git-Branch: ")
|
||||
} else {
|
||||
notes = append(notes, line)
|
||||
}
|
||||
}
|
||||
tbd = append(tbd, []string{
|
||||
status.Name,
|
||||
fmt.Sprintf("%d", status.Version),
|
||||
status.Info.Status,
|
||||
status.deployedBy,
|
||||
status.gitCommit,
|
||||
status.gitBranch,
|
||||
status.Info.LastDeployed, strings.Join(notes, " | "),
|
||||
})
|
||||
}
|
||||
tbd = append(tbd, []string{
|
||||
status.Name,
|
||||
fmt.Sprintf("%d", status.Version),
|
||||
status.Info.Status,
|
||||
status.deployedBy,
|
||||
status.gitCommit,
|
||||
status.gitBranch,
|
||||
status.Info.LastDeployed, strings.Join(notes, " | "),
|
||||
})
|
||||
}
|
||||
return nil
|
||||
})
|
||||
})
|
||||
|
||||
if err := wg.Wait(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return pterm.DefaultTable.WithHasHeader().WithData(tbd).Render()
|
||||
}
|
||||
|
||||
func (sq *Squadron) Rollback(ctx context.Context, units Units, revision string, helmArgs []string) error {
|
||||
func (sq *Squadron) Rollback(ctx context.Context, revision string, helmArgs []string, parallel int) error {
|
||||
if revision != "" {
|
||||
helmArgs = append([]string{revision}, helmArgs...)
|
||||
}
|
||||
if sq.c.Unite {
|
||||
pterm.Debug.Printfln("running helm rollback for: %s", sq.chartPath())
|
||||
stdErr := bytes.NewBuffer([]byte{})
|
||||
if out, err := util.NewHelmCommand().Args("rollback", sq.name).
|
||||
Stderr(stdErr).
|
||||
Stdout(os.Stdout).
|
||||
Args(helmArgs...).
|
||||
Args("--namespace", sq.namespace).
|
||||
Run(ctx); err != nil &&
|
||||
string(bytes.TrimSpace(stdErr.Bytes())) != fmt.Sprintf("Error: uninstall: Release not loaded: %s: release: not found", sq.name) {
|
||||
return errors.Wrap(err, out)
|
||||
}
|
||||
}
|
||||
for _, uName := range units.Keys() {
|
||||
// todo use release prefix on install: squadron name or --name
|
||||
rName := fmt.Sprintf("%s-%s", sq.name, uName)
|
||||
pterm.Debug.Printfln("running helm uninstall for: %s", uName)
|
||||
stdErr := bytes.NewBuffer([]byte{})
|
||||
if out, err := util.NewHelmCommand().Args("rollback", rName).
|
||||
Stderr(stdErr).
|
||||
Stdout(os.Stdout).
|
||||
Args(helmArgs...).
|
||||
Args("--namespace", sq.namespace).
|
||||
Run(ctx); err != nil &&
|
||||
string(bytes.TrimSpace(stdErr.Bytes())) != fmt.Sprintf("Error: uninstall: Release not loaded: %s: release: not found", rName) {
|
||||
return errors.Wrap(err, out)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
wg, ctx := errgroup.WithContext(ctx)
|
||||
wg.SetLimit(parallel)
|
||||
|
||||
func (sq *Squadron) Up(ctx context.Context, units Units, helmArgs []string, username, version, commit, branch string, parallel int) error {
|
||||
description := fmt.Sprintf("\nDeployed-By: %s\nManaged-By: Squadron %s\nGit-Commit: %s\nGit-Branch: %s", username, version, commit, branch)
|
||||
_ = sq.Config().Squadrons.Iterate(func(key string, value config.Map[*config.Unit]) error {
|
||||
return value.Iterate(func(k string, v *config.Unit) error {
|
||||
name := fmt.Sprintf("%s-%s", key, k)
|
||||
namespace, err := sq.Namespace(ctx, key, k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if sq.c.Unite {
|
||||
pterm.Debug.Printfln("running helm upgrade for chart: %s", sq.chartPath())
|
||||
if out, err := util.NewHelmCommand().
|
||||
Stdout(os.Stdout).
|
||||
Args("upgrade", sq.name, sq.chartPath()).
|
||||
Args("--namespace", sq.namespace).
|
||||
Args("--dependency-update").
|
||||
Args("--description", description).
|
||||
Args("--install").
|
||||
Args(helmArgs...).
|
||||
Run(ctx); err != nil {
|
||||
return errors.Wrap(err, out)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
g, gctx := errgroup.WithContext(ctx)
|
||||
g.SetLimit(parallel)
|
||||
|
||||
for _, uName := range units.Keys() {
|
||||
u := units[uName]
|
||||
// todo use release prefix on install: squadron name or --name
|
||||
rName := fmt.Sprintf("%s-%s", sq.name, uName)
|
||||
|
||||
// update local chart dependencies
|
||||
// https://stackoverflow.com/questions/59210148/error-found-in-chart-yaml-but-missing-in-charts-directory-mysql
|
||||
if strings.HasPrefix(u.Chart.Repository, "file:///") {
|
||||
pterm.Debug.Printfln("running helm dependency update for %s", u.Chart.Repository)
|
||||
if out, err := util.NewHelmCommand().
|
||||
stdErr := bytes.NewBuffer([]byte{})
|
||||
pterm.Debug.Printfln("running helm uninstall for: `%s`", name)
|
||||
if out, err := util.NewHelmCommand().Args("rollback", name).
|
||||
Stderr(stdErr).
|
||||
Stdout(os.Stdout).
|
||||
Args("dependency", "update").
|
||||
Cwd(strings.TrimPrefix(u.Chart.Repository, "file://")).
|
||||
Run(ctx); err != nil {
|
||||
Args(helmArgs...).
|
||||
Args("--namespace", namespace).
|
||||
Run(ctx); err != nil &&
|
||||
string(bytes.TrimSpace(stdErr.Bytes())) != fmt.Sprintf("Error: uninstall: Release not loaded: %s: release: not found", name) {
|
||||
return errors.Wrap(err, out)
|
||||
}
|
||||
}
|
||||
|
||||
// install chart
|
||||
pterm.Debug.Printfln("running helm upgrade for %s", uName)
|
||||
cmd := util.NewHelmCommand().
|
||||
Stdout(os.Stdout).
|
||||
Args("upgrade", rName, "--install").
|
||||
Args("--set", fmt.Sprintf("squadron=%s,unit=%s", sq.name, uName)).
|
||||
Args("--description", description).
|
||||
Args("--namespace", sq.namespace).
|
||||
Args("--dependency-update").
|
||||
Args("--install").
|
||||
Args("-f", path.Join(sq.chartPath(), uName+".yaml")).
|
||||
Args(helmArgs...)
|
||||
if strings.Contains(u.Chart.Repository, "file://") {
|
||||
cmd.Args(strings.TrimPrefix(u.Chart.Repository, "file://"))
|
||||
} else {
|
||||
cmd.Args(u.Chart.Name, "--repo", u.Chart.Repository, "--version", u.Chart.Version)
|
||||
}
|
||||
|
||||
g.Go(func() error {
|
||||
if out, err := cmd.Run(gctx); err != nil {
|
||||
return errors.Wrap(err, out)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
return g.Wait()
|
||||
}
|
||||
|
||||
func (sq *Squadron) Template(ctx context.Context, units Units, helmArgs []string) error {
|
||||
if sq.c.Unite {
|
||||
pterm.Debug.Printfln("running helm template for chart: %s", sq.chartPath())
|
||||
if out, err := util.NewHelmCommand().Args("template", sq.name, sq.chartPath()).
|
||||
Stdout(os.Stdout).
|
||||
Args("--dependency-update").
|
||||
Args("--namespace", sq.namespace).
|
||||
Args(helmArgs...).
|
||||
Run(ctx); err != nil {
|
||||
return errors.Wrap(err, out)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
for _, uName := range units.Keys() {
|
||||
u := units[uName]
|
||||
// todo use release prefix on install: squadron name or --name
|
||||
rName := fmt.Sprintf("%s-%s", sq.name, uName)
|
||||
pterm.Debug.Printfln("running helm template for chart: %s", uName)
|
||||
cmd := util.NewHelmCommand().Args("template", rName).
|
||||
Stdout(os.Stdout).
|
||||
Args("--dependency-update").
|
||||
Args("--namespace", sq.namespace).
|
||||
Args("--set", fmt.Sprintf("squadron=%s,unit=%s", sq.name, uName)).
|
||||
Args("-f", path.Join(sq.chartPath(), uName+".yaml")).
|
||||
Args(helmArgs...)
|
||||
if strings.Contains(u.Chart.Repository, "file://") {
|
||||
cmd.Args("/" + strings.TrimPrefix(u.Chart.Repository, "file://"))
|
||||
} else {
|
||||
cmd.Args(u.Chart.Name, "--repo", u.Chart.Repository, "--version", u.Chart.Version)
|
||||
}
|
||||
if out, err := cmd.Run(ctx); err != nil {
|
||||
return errors.Wrap(err, out)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sq *Squadron) chartPath() string {
|
||||
return path.Join(sq.basePath, defaultOutputDir, sq.name)
|
||||
}
|
||||
|
||||
func (sq *Squadron) cleanupOutput(chartPath string) error {
|
||||
if _, err := os.Stat(chartPath); err == nil {
|
||||
if err := os.RemoveAll(chartPath); err != nil {
|
||||
logrus.Warnf("could not delete chart output directory: %q", err)
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(chartPath); os.IsNotExist(err) {
|
||||
if err := os.MkdirAll(chartPath, 0o744); err != nil {
|
||||
return fmt.Errorf("could not create chart output directory: %w", err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (sq *Squadron) generateChart(units Units, chartPath, chartName, version string) error {
|
||||
chart := newChart(chartName, version)
|
||||
values := map[string]interface{}{}
|
||||
if sq.c.Global != nil {
|
||||
values["global"] = sq.c.Global
|
||||
}
|
||||
_ = units.Iterate(func(name string, unit *Unit) error {
|
||||
chart.addDependency(name, unit.Chart)
|
||||
values[name] = unit.Values
|
||||
return nil
|
||||
})
|
||||
return chart.generate(chartPath, values)
|
||||
|
||||
return wg.Wait()
|
||||
}
|
||||
|
||||
func (sq *Squadron) generateValues(values map[string]interface{}, vPath, vName string) error {
|
||||
if values == nil {
|
||||
values = map[string]interface{}{}
|
||||
}
|
||||
if sq.c.Global != nil {
|
||||
values["global"] = sq.c.Global
|
||||
}
|
||||
return util.GenerateYaml(path.Join(vPath, vName+".yaml"), values)
|
||||
func (sq *Squadron) Up(ctx context.Context, helmArgs []string, username, version, commit, branch string, parallel int) error {
|
||||
description := fmt.Sprintf("\nDeployed-By: %s\nManaged-By: Squadron %s\nGit-Commit: %s\nGit-Branch: %s", username, version, commit, branch)
|
||||
|
||||
wg, gctx := errgroup.WithContext(ctx)
|
||||
wg.SetLimit(parallel)
|
||||
|
||||
_ = sq.Config().Squadrons.Iterate(func(key string, value config.Map[*config.Unit]) error {
|
||||
return value.Iterate(func(k string, v *config.Unit) error {
|
||||
wg.Go(func() error {
|
||||
name := fmt.Sprintf("%s-%s", key, k)
|
||||
namespace, err := sq.Namespace(ctx, key, k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
valueBytes, err := v.ValuesYAML(sq.c.Global)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// update local chart dependencies
|
||||
// https://stackoverflow.com/questions/59210148/error-found-in-chart-yaml-but-missing-in-charts-directory-mysql
|
||||
if strings.HasPrefix(v.Chart.Repository, "file:///") {
|
||||
pterm.Debug.Printfln("running helm dependency update for %s", v.Chart.Repository)
|
||||
if out, err := util.NewHelmCommand().
|
||||
Stdout(os.Stdout).
|
||||
Args("dependency", "update").
|
||||
Cwd(strings.TrimPrefix(v.Chart.Repository, "file://")).
|
||||
Run(ctx); err != nil {
|
||||
return errors.Wrap(err, out)
|
||||
}
|
||||
}
|
||||
|
||||
// install chart
|
||||
pterm.Debug.Printfln("running helm upgrade for %s", name)
|
||||
cmd := util.NewHelmCommand().
|
||||
Stdin(bytes.NewReader(valueBytes)).
|
||||
Stdout(os.Stdout).
|
||||
Args("upgrade", name, "--install").
|
||||
Args("--set", fmt.Sprintf("squadron=%s,unit=%s", key, k)).
|
||||
Args("--description", description).
|
||||
Args("--namespace", namespace).
|
||||
Args("--dependency-update").
|
||||
Args("--install").
|
||||
Args("-values", "-").
|
||||
Args(helmArgs...)
|
||||
|
||||
if strings.Contains(v.Chart.Repository, "file://") {
|
||||
cmd.Args(strings.TrimPrefix(v.Chart.Repository, "file://"))
|
||||
} else {
|
||||
cmd.Args(v.Chart.Name, "--repo", v.Chart.Repository, "--version", v.Chart.Version)
|
||||
}
|
||||
|
||||
if out, err := cmd.Run(gctx); err != nil {
|
||||
return errors.Wrap(err, out)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
return nil
|
||||
})
|
||||
})
|
||||
|
||||
return wg.Wait()
|
||||
}
|
||||
|
||||
func (sq *Squadron) Template(ctx context.Context, helmArgs []string) (string, error) {
|
||||
var ret bytes.Buffer
|
||||
|
||||
err := sq.Config().Squadrons.Iterate(func(key string, value config.Map[*config.Unit]) error {
|
||||
return value.Iterate(func(k string, v *config.Unit) error {
|
||||
name := fmt.Sprintf("%s-%s", key, k)
|
||||
namespace, err := sq.Namespace(ctx, key, k)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
valueBytes, err := v.ValuesYAML(sq.c.Global)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pterm.Debug.Printfln("running helm template for chart: %s", name)
|
||||
cmd := util.NewHelmCommand().Args("template", k).
|
||||
Stdin(bytes.NewReader(valueBytes)).
|
||||
Stdout(&ret).
|
||||
Args("--dependency-update").
|
||||
Args("--namespace", namespace).
|
||||
Args("--set", fmt.Sprintf("squadron=%s", key)).
|
||||
Args("--set", fmt.Sprintf("unit=%s", k)).
|
||||
Args("--values", "-").
|
||||
Args(helmArgs...)
|
||||
if strings.Contains(v.Chart.Repository, "file://") {
|
||||
file := strings.TrimPrefix(v.Chart.Repository, "file://")
|
||||
if !strings.HasPrefix(file, ".") {
|
||||
file = "/" + file
|
||||
}
|
||||
cmd.Args(file)
|
||||
} else {
|
||||
cmd.Args(v.Chart.Name, "--repo", v.Chart.Repository, "--version", v.Chart.Version)
|
||||
}
|
||||
if out, err := cmd.Run(ctx); err != nil {
|
||||
return errors.Wrap(err, out)
|
||||
}
|
||||
|
||||
return nil
|
||||
})
|
||||
})
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return ret.String(), nil
|
||||
}
|
||||
|
||||
163
squadron_test.go
163
squadron_test.go
@ -2,121 +2,86 @@ package squadron_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"path"
|
||||
"testing"
|
||||
|
||||
"github.com/foomo/squadron"
|
||||
testutils "github.com/foomo/squadron/tests/utils"
|
||||
"github.com/foomo/squadron/util"
|
||||
"github.com/foomo/squadron/internal/testutils"
|
||||
"github.com/foomo/squadron/internal/util"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestConfigSimpleSnapshot(t *testing.T) {
|
||||
testConfigSnapshot(t,
|
||||
[]string{
|
||||
path.Join("testdata", "config-simple", "squadron.yaml"),
|
||||
tests := []struct {
|
||||
name string
|
||||
files []string
|
||||
squadron string
|
||||
units []string
|
||||
}{
|
||||
{
|
||||
name: "blank",
|
||||
files: []string{"squadron.yaml"},
|
||||
},
|
||||
path.Join("testdata", "config-simple", "squadron.yaml.snapshot"),
|
||||
nil,
|
||||
true,
|
||||
)
|
||||
{
|
||||
name: "simple",
|
||||
files: []string{"squadron.yaml"},
|
||||
},
|
||||
{
|
||||
name: "override",
|
||||
files: []string{"squadron.yaml", "squadron.override.yaml"},
|
||||
},
|
||||
{
|
||||
name: "global",
|
||||
files: []string{"squadron.yaml", "squadron.override.yaml"},
|
||||
},
|
||||
{
|
||||
name: "template",
|
||||
files: []string{"squadron.yaml"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(tt *testing.T) {
|
||||
config(tt, test.name, test.files, test.squadron, test.units)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestConfigNoValuesSnapshot(t *testing.T) {
|
||||
testConfigSnapshot(t,
|
||||
[]string{
|
||||
path.Join("testdata", "config-no-values", "squadron.yaml"),
|
||||
},
|
||||
path.Join("testdata", "config-no-values", "squadron.yaml.snapshot"),
|
||||
nil,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
func TestConfigOverrideSnapshot(t *testing.T) {
|
||||
testConfigSnapshot(t,
|
||||
[]string{
|
||||
path.Join("testdata", "config-override", "squadron.yaml"),
|
||||
path.Join("testdata", "config-override", "squadron.override.yaml"),
|
||||
},
|
||||
path.Join("testdata", "config-override", "squadron.yaml.snapshot"),
|
||||
nil,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
func TestConfigGlobalSnapshot(t *testing.T) {
|
||||
testConfigSnapshot(t,
|
||||
[]string{
|
||||
path.Join("testdata", "config-global", "squadron.yaml"),
|
||||
path.Join("testdata", "config-global", "squadron.override.yaml"),
|
||||
},
|
||||
path.Join("testdata", "config-global", "squadron.yaml.snapshot"),
|
||||
nil,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
func TestConfigTemplateSnapshot(t *testing.T) {
|
||||
testConfigSnapshot(t,
|
||||
[]string{
|
||||
path.Join("testdata", "config-template", "squadron.yaml"),
|
||||
},
|
||||
path.Join("testdata", "config-template", "squadron.yaml.snapshot"),
|
||||
nil,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
func TestConfigTemplateFrontendSnapshot(t *testing.T) {
|
||||
testConfigSnapshot(t,
|
||||
[]string{
|
||||
path.Join("testdata", "config-template-frontend", "squadron.yaml"),
|
||||
},
|
||||
path.Join("testdata", "config-template-frontend", "squadron.yaml.snapshot"),
|
||||
[]string{"frontend"},
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
func TestConfigNoRenderSnapshot(t *testing.T) {
|
||||
testConfigSnapshot(t,
|
||||
[]string{
|
||||
path.Join("testdata", "config-no-render", "squadron.yaml"),
|
||||
},
|
||||
path.Join("testdata", "config-no-render", "squadron.yaml.snapshot"),
|
||||
nil,
|
||||
false,
|
||||
)
|
||||
}
|
||||
|
||||
func TestConfigOverrideSnapshotNulled(t *testing.T) {
|
||||
testConfigSnapshot(t,
|
||||
[]string{
|
||||
path.Join("testdata", "config-override-null", "squadron.yaml"),
|
||||
path.Join("testdata", "config-override-null", "squadron.override.yaml"),
|
||||
},
|
||||
path.Join("testdata", "config-override-null", "squadron.yaml.snapshot"),
|
||||
nil,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
func testConfigSnapshot(t *testing.T, configs []string, snapshot string, units []string, render bool) {
|
||||
func config(t *testing.T, name string, files []string, squadronName string, unitNames []string) {
|
||||
t.Helper()
|
||||
var cwd string
|
||||
testutils.Must(t, util.ValidatePath(".", &cwd))
|
||||
ctx := context.TODO()
|
||||
require.NoError(t, util.ValidatePath(".", &cwd))
|
||||
require.NoError(t, os.Setenv("PROJECT_ROOT", "."))
|
||||
|
||||
sq := squadron.New(cwd, "", configs)
|
||||
for i, file := range files {
|
||||
files[i] = path.Join("testdata", name, file)
|
||||
}
|
||||
sq := squadron.New(cwd, "default", files)
|
||||
|
||||
testutils.Must(t, sq.MergeConfigFiles(), "failed to merge files")
|
||||
|
||||
if units != nil {
|
||||
testutils.Must(t, sq.FilterConfig(units), "failed to filter units")
|
||||
{
|
||||
require.NoError(t, sq.MergeConfigFiles(), "failed to merge files")
|
||||
}
|
||||
|
||||
if render {
|
||||
testutils.Must(t, sq.RenderConfig(context.Background()), "failed to render config")
|
||||
{
|
||||
require.NoError(t, sq.FilterConfig(squadronName, unitNames), "failed to filter config")
|
||||
testutils.Snapshot(t, path.Join("testdata", name, "snapshop-config-norender.yaml"), sq.ConfigYAML())
|
||||
}
|
||||
|
||||
testutils.MustCheckSnapshot(t, snapshot, sq.GetConfigYAML())
|
||||
{
|
||||
require.NoError(t, sq.RenderConfig(ctx), "failed to render config")
|
||||
testutils.Snapshot(t, path.Join("testdata", name, "snapshop-config.yaml"), sq.ConfigYAML())
|
||||
}
|
||||
|
||||
{
|
||||
require.NoError(t, sq.RenderConfig(ctx), "failed to render config")
|
||||
testutils.Snapshot(t, path.Join("testdata", name, "snapshop-config.yaml"), sq.ConfigYAML())
|
||||
}
|
||||
|
||||
{
|
||||
out, err := sq.Template(ctx, nil)
|
||||
require.NoError(t, err, "failed to render template")
|
||||
testutils.Snapshot(t, path.Join("testdata", name, "snapshop-template.yaml"), out)
|
||||
}
|
||||
}
|
||||
|
||||
1
testdata/blank/snapshop-config-norender.yaml
vendored
Normal file
1
testdata/blank/snapshop-config-norender.yaml
vendored
Normal file
@ -0,0 +1 @@
|
||||
version: "2.0"
|
||||
1
testdata/blank/snapshop-config.yaml
vendored
Normal file
1
testdata/blank/snapshop-config.yaml
vendored
Normal file
@ -0,0 +1 @@
|
||||
version: "2.0"
|
||||
0
testdata/blank/snapshop-template.yaml
vendored
Normal file
0
testdata/blank/snapshop-template.yaml
vendored
Normal file
1
testdata/blank/squadron.yaml
vendored
Normal file
1
testdata/blank/squadron.yaml
vendored
Normal file
@ -0,0 +1 @@
|
||||
version: "2.0"
|
||||
7
testdata/global/snapshop-config-norender.yaml
vendored
Normal file
7
testdata/global/snapshop-config-norender.yaml
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
version: "2.0"
|
||||
global:
|
||||
bar:
|
||||
- one
|
||||
- two
|
||||
baz: null
|
||||
foo: two
|
||||
7
testdata/global/snapshop-config.yaml
vendored
Normal file
7
testdata/global/snapshop-config.yaml
vendored
Normal file
@ -0,0 +1,7 @@
|
||||
version: "2.0"
|
||||
global:
|
||||
bar:
|
||||
- one
|
||||
- two
|
||||
baz: null
|
||||
foo: two
|
||||
0
testdata/global/snapshop-template.yaml
vendored
Normal file
0
testdata/global/snapshop-template.yaml
vendored
Normal file
22
testdata/override/snapshop-config-norender.yaml
vendored
Normal file
22
testdata/override/snapshop-config-norender.yaml
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
version: "2.0"
|
||||
squadron:
|
||||
storefinder:
|
||||
frontend:
|
||||
chart:
|
||||
name: frontend
|
||||
repository: file://<% env "PROJECT_ROOT" %>/_examples/common/charts/frontend
|
||||
version: 0.0.1
|
||||
builds:
|
||||
default:
|
||||
args:
|
||||
- foo=foo
|
||||
- bar=bar
|
||||
- bar=baz
|
||||
- baz=baz
|
||||
dockerfile: Dockerfile
|
||||
image: docker.mycompany.com/mycomapny/frontend
|
||||
tag: nightly
|
||||
values:
|
||||
image:
|
||||
repository: <% .Squadron.storefinder.frontend.builds.default.image %>
|
||||
tag: <% .Squadron.storefinder.frontend.builds.default.tag %>
|
||||
22
testdata/override/snapshop-config.yaml
vendored
Normal file
22
testdata/override/snapshop-config.yaml
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
version: "2.0"
|
||||
squadron:
|
||||
storefinder:
|
||||
frontend:
|
||||
chart:
|
||||
name: frontend
|
||||
repository: file://./_examples/common/charts/frontend
|
||||
version: 0.0.1
|
||||
builds:
|
||||
default:
|
||||
args:
|
||||
- foo=foo
|
||||
- bar=bar
|
||||
- bar=baz
|
||||
- baz=baz
|
||||
dockerfile: Dockerfile
|
||||
image: docker.mycompany.com/mycomapny/frontend
|
||||
tag: nightly
|
||||
values:
|
||||
image:
|
||||
repository: docker.mycompany.com/mycomapny/frontend
|
||||
tag: nightly
|
||||
49
testdata/override/snapshop-template.yaml
vendored
Normal file
49
testdata/override/snapshop-template.yaml
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
---
|
||||
# Source: frontend/templates/service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: frontend
|
||||
labels:
|
||||
app.kubernetes.io/name: frontend
|
||||
app.kubernetes.io/component: frontend
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
helm.sh/chart: frontend-0.0.1
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app.kubernetes.io/name: frontend
|
||||
app.kubernetes.io/component: frontend
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
---
|
||||
# Source: frontend/templates/deployment.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: frontend
|
||||
labels:
|
||||
app.kubernetes.io/name: frontend
|
||||
app.kubernetes.io/component: frontend
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
helm.sh/chart: frontend-0.0.1
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: frontend
|
||||
app.kubernetes.io/component: frontend
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: frontend
|
||||
app.kubernetes.io/component: frontend
|
||||
spec:
|
||||
containers:
|
||||
- name: frontend
|
||||
image: nginx:latest
|
||||
ports:
|
||||
- name: http
|
||||
protocol: TCP
|
||||
containerPort: 80
|
||||
12
testdata/simple/snapshop-config-norender.yaml
vendored
Normal file
12
testdata/simple/snapshop-config-norender.yaml
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
version: "2.0"
|
||||
squadron:
|
||||
storefinder:
|
||||
frontend:
|
||||
chart:
|
||||
name: frontend
|
||||
repository: file://<% env "PROJECT_ROOT" %>/_examples/common/charts/frontend
|
||||
version: 0.0.1
|
||||
values:
|
||||
image:
|
||||
repository: nginx
|
||||
tag: latest
|
||||
12
testdata/simple/snapshop-config.yaml
vendored
Normal file
12
testdata/simple/snapshop-config.yaml
vendored
Normal file
@ -0,0 +1,12 @@
|
||||
version: "2.0"
|
||||
squadron:
|
||||
storefinder:
|
||||
frontend:
|
||||
chart:
|
||||
name: frontend
|
||||
repository: file://./_examples/common/charts/frontend
|
||||
version: 0.0.1
|
||||
values:
|
||||
image:
|
||||
repository: nginx
|
||||
tag: latest
|
||||
49
testdata/simple/snapshop-template.yaml
vendored
Normal file
49
testdata/simple/snapshop-template.yaml
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
---
|
||||
# Source: frontend/templates/service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: frontend
|
||||
labels:
|
||||
app.kubernetes.io/name: frontend
|
||||
app.kubernetes.io/component: frontend
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
helm.sh/chart: frontend-0.0.1
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app.kubernetes.io/name: frontend
|
||||
app.kubernetes.io/component: frontend
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
---
|
||||
# Source: frontend/templates/deployment.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: frontend
|
||||
labels:
|
||||
app.kubernetes.io/name: frontend
|
||||
app.kubernetes.io/component: frontend
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
helm.sh/chart: frontend-0.0.1
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: frontend
|
||||
app.kubernetes.io/component: frontend
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: frontend
|
||||
app.kubernetes.io/component: frontend
|
||||
spec:
|
||||
containers:
|
||||
- name: frontend
|
||||
image: nginx:latest
|
||||
ports:
|
||||
- name: http
|
||||
protocol: TCP
|
||||
containerPort: 80
|
||||
10
testdata/simple/squadron.yaml
vendored
Normal file
10
testdata/simple/squadron.yaml
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
version: "2.0"
|
||||
|
||||
squadron:
|
||||
storefinder:
|
||||
frontend:
|
||||
chart: <% env "PROJECT_ROOT" %>/_examples/common/charts/frontend
|
||||
values:
|
||||
image:
|
||||
tag: latest
|
||||
repository: nginx
|
||||
39
testdata/template/snapshop-config-norender.yaml
vendored
Normal file
39
testdata/template/snapshop-config-norender.yaml
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
version: "2.0"
|
||||
global:
|
||||
host: mycompany.com
|
||||
squadron:
|
||||
storefinder:
|
||||
backend:
|
||||
chart:
|
||||
name: backend
|
||||
repository: file://<% env "PROJECT_ROOT" %>/_examples/common/charts/backend
|
||||
version: 0.0.1
|
||||
builds:
|
||||
default:
|
||||
image: docker.mycompany.com/mycomapny/frontend-admin
|
||||
tag: latest
|
||||
values:
|
||||
image:
|
||||
repository: <% .Squadron.storefinder.backend.builds.default.image %>
|
||||
tag: <% .Squadron.storefinder.backend.builds.default.tag %>
|
||||
frontend:
|
||||
chart:
|
||||
name: frontend
|
||||
repository: file://<% env "PROJECT_ROOT" %>/_examples/common/charts/frontend
|
||||
version: 0.0.1
|
||||
builds:
|
||||
default:
|
||||
image: docker.mycompany.com/mycomapny/frontend
|
||||
tag: latest
|
||||
values:
|
||||
env:
|
||||
BASE64: <% base64 "1234567890" %>
|
||||
DEFAULT_INDEX_VALUE: <% defaultIndex .Global "notexists" "fallback" %>
|
||||
DEFAULT_VALUE: <% "" | default "fallback" %>
|
||||
ENV: <% env "SHELL" %>
|
||||
GLOBAL: <% .Global.host %>
|
||||
image:
|
||||
repository: <% .Squadron.storefinder.frontend.builds.default.image %>
|
||||
tag: <% .Squadron.storefinder.frontend.builds.default.tag %>
|
||||
values: |
|
||||
<% file "testdata/template/squadron.values.yaml" | indent 5 %>
|
||||
45
testdata/template/snapshop-config.yaml
vendored
Normal file
45
testdata/template/snapshop-config.yaml
vendored
Normal file
@ -0,0 +1,45 @@
|
||||
version: "2.0"
|
||||
global:
|
||||
host: mycompany.com
|
||||
squadron:
|
||||
storefinder:
|
||||
backend:
|
||||
chart:
|
||||
name: backend
|
||||
repository: file://./_examples/common/charts/backend
|
||||
version: 0.0.1
|
||||
builds:
|
||||
default:
|
||||
image: docker.mycompany.com/mycomapny/frontend-admin
|
||||
tag: latest
|
||||
values:
|
||||
image:
|
||||
repository: docker.mycompany.com/mycomapny/frontend-admin
|
||||
tag: latest
|
||||
frontend:
|
||||
chart:
|
||||
name: frontend
|
||||
repository: file://./_examples/common/charts/frontend
|
||||
version: 0.0.1
|
||||
builds:
|
||||
default:
|
||||
image: docker.mycompany.com/mycomapny/frontend
|
||||
tag: latest
|
||||
values:
|
||||
env:
|
||||
BASE64: MTIzNDU2Nzg5MA==
|
||||
DEFAULT_INDEX_VALUE: fallback
|
||||
DEFAULT_VALUE: fallback
|
||||
ENV: /bin/zsh
|
||||
GLOBAL: mycompany.com
|
||||
image:
|
||||
repository: docker.mycompany.com/mycomapny/frontend
|
||||
tag: latest
|
||||
values: |
|
||||
foo: bar
|
||||
bar:
|
||||
- foo
|
||||
- bar
|
||||
env: /bin/zsh
|
||||
global: mycompany.com
|
||||
base64: MTIzNDU2Nzg5MA==
|
||||
98
testdata/template/snapshop-template.yaml
vendored
Normal file
98
testdata/template/snapshop-template.yaml
vendored
Normal file
@ -0,0 +1,98 @@
|
||||
---
|
||||
# Source: backend/templates/service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: backend
|
||||
labels:
|
||||
app.kubernetes.io/name: backend
|
||||
app.kubernetes.io/component: backend
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
helm.sh/chart: backend-0.0.1
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app.kubernetes.io/name: backend
|
||||
app.kubernetes.io/component: backend
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
---
|
||||
# Source: backend/templates/deployment.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: backend
|
||||
labels:
|
||||
app.kubernetes.io/name: backend
|
||||
app.kubernetes.io/component: backend
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
helm.sh/chart: backend-0.0.1
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: backend
|
||||
app.kubernetes.io/component: backend
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: backend
|
||||
app.kubernetes.io/component: backend
|
||||
spec:
|
||||
containers:
|
||||
- name: backend
|
||||
image: nginx:latest
|
||||
ports:
|
||||
- name: http
|
||||
protocol: TCP
|
||||
containerPort: 80
|
||||
---
|
||||
# Source: frontend/templates/service.yaml
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: frontend
|
||||
labels:
|
||||
app.kubernetes.io/name: frontend
|
||||
app.kubernetes.io/component: frontend
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
helm.sh/chart: frontend-0.0.1
|
||||
spec:
|
||||
type: ClusterIP
|
||||
selector:
|
||||
app.kubernetes.io/name: frontend
|
||||
app.kubernetes.io/component: frontend
|
||||
ports:
|
||||
- name: http
|
||||
port: 80
|
||||
---
|
||||
# Source: frontend/templates/deployment.yaml
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
metadata:
|
||||
name: frontend
|
||||
labels:
|
||||
app.kubernetes.io/name: frontend
|
||||
app.kubernetes.io/component: frontend
|
||||
app.kubernetes.io/managed-by: Helm
|
||||
helm.sh/chart: frontend-0.0.1
|
||||
spec:
|
||||
replicas: 1
|
||||
selector:
|
||||
matchLabels:
|
||||
app.kubernetes.io/name: frontend
|
||||
app.kubernetes.io/component: frontend
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
app.kubernetes.io/name: frontend
|
||||
app.kubernetes.io/component: frontend
|
||||
spec:
|
||||
containers:
|
||||
- name: frontend
|
||||
image: nginx:latest
|
||||
ports:
|
||||
- name: http
|
||||
protocol: TCP
|
||||
containerPort: 80
|
||||
@ -1,14 +0,0 @@
|
||||
package testutils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Must(t *testing.T, err error, args ...interface{}) {
|
||||
t.Helper()
|
||||
if err != nil {
|
||||
args = append([]interface{}{fmt.Sprintf("err: %s", err)}, args...)
|
||||
t.Fatal(args...)
|
||||
}
|
||||
}
|
||||
@ -1,34 +0,0 @@
|
||||
package testutils
|
||||
|
||||
import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
// MustWriteSnapshot updates the snapshot file for a given test t.
|
||||
func MustWriteSnapshot(t *testing.T, name string, content string) {
|
||||
t.Helper()
|
||||
Must(t, os.WriteFile(name, []byte(content), 0o600), "failed to update snapshot", name)
|
||||
}
|
||||
|
||||
// MustReadSnapshot reads the snapshot file for a given test t.
|
||||
func MustReadSnapshot(t *testing.T, name string) string {
|
||||
t.Helper()
|
||||
g, err := os.ReadFile(name)
|
||||
Must(t, err, "failed reading file", name)
|
||||
return string(g)
|
||||
}
|
||||
|
||||
// MustCheckSnapshot compares v with its snapshot file
|
||||
func MustCheckSnapshot(t *testing.T, name, yaml string) {
|
||||
t.Helper()
|
||||
if *UpdateFlag {
|
||||
MustWriteSnapshot(t, name, yaml)
|
||||
}
|
||||
snapshot := MustReadSnapshot(t, name)
|
||||
if !assert.YAMLEq(t, snapshot, yaml) {
|
||||
t.Fatalf("err: %s not equal to %s", yaml, snapshot)
|
||||
}
|
||||
}
|
||||
62
units.go
62
units.go
@ -1,62 +0,0 @@
|
||||
package squadron
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
type Units map[string]*Unit
|
||||
|
||||
func (u Units) Keys() []string {
|
||||
if len(u) == 0 {
|
||||
return nil
|
||||
}
|
||||
ret := make([]string, 0, len(u))
|
||||
for name := range u {
|
||||
ret = append(ret, name)
|
||||
}
|
||||
sort.Strings(ret)
|
||||
return ret
|
||||
}
|
||||
|
||||
func (u Units) Values() []*Unit {
|
||||
if len(u) == 0 {
|
||||
return nil
|
||||
}
|
||||
ret := make([]*Unit, len(u))
|
||||
for _, name := range u.Keys() {
|
||||
ret = append(ret, u[name])
|
||||
}
|
||||
return ret
|
||||
}
|
||||
|
||||
func (u Units) Filter(names []string) (Units, error) {
|
||||
if len(u) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
if len(names) == 0 {
|
||||
return u, nil
|
||||
}
|
||||
ret := make(Units, len(u))
|
||||
for _, name := range names {
|
||||
if unit, ok := u[name]; !ok {
|
||||
return nil, errors.Errorf("unknown unit: %s", name)
|
||||
} else {
|
||||
ret[name] = unit
|
||||
}
|
||||
}
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (u Units) Iterate(i func(name string, unit *Unit) error) error {
|
||||
if len(u) == 0 {
|
||||
return nil
|
||||
}
|
||||
for _, name := range u.Keys() {
|
||||
if err := i(name, u[name]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
21
util/helm.go
21
util/helm.go
@ -1,21 +0,0 @@
|
||||
package util
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
type HelmCmd struct {
|
||||
Cmd
|
||||
}
|
||||
|
||||
func NewHelmCommand() *HelmCmd {
|
||||
return &HelmCmd{*NewCommand("helm")}
|
||||
}
|
||||
|
||||
func (c HelmCmd) UpdateDependency(ctx context.Context, chartPath string) (string, error) {
|
||||
return c.Base().Args("dependency", "update", chartPath).Run(ctx)
|
||||
}
|
||||
|
||||
func (c HelmCmd) Package(ctx context.Context, chartPath, destPath string) (string, error) {
|
||||
return c.Base().Args("package", chartPath, "--destination", destPath).Run(ctx)
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user