feat: add tags

This commit is contained in:
Kevin Franklin Kim 2023-09-21 13:25:18 +02:00
parent c0c52b85d9
commit bede368045
No known key found for this signature in database
25 changed files with 198 additions and 31 deletions

View File

@ -5,6 +5,7 @@ squadron:
checkout:
backend:
chart: <% env "PROJECT_ROOT" %>/../common/charts/backend
tags: [ "backend" ]
builds:
default:
tag: <% .Global.docker.tag | quote %>

View File

@ -5,6 +5,7 @@ squadron:
checkout:
frontend:
chart: <% env "PROJECT_ROOT" %>/../common/charts/frontend
tags: [ "frontend" ]
builds:
default:
tag: "<% .Global.docker.tag %>"

View File

@ -5,6 +5,7 @@ squadron:
storefinder:
backend:
chart: <% env "PROJECT_ROOT" %>/../common/charts/backend
tags: [ "backend" ]
builds:
default:
tag: <% .Global.docker.tag | quote %>

View File

@ -5,6 +5,7 @@ squadron:
storefinder:
frontend:
chart: <% env "PROJECT_ROOT" %>/../common/charts/frontend
tags: [ "frontend" ]
builds:
default:
tag: "<% .Global.docker.tag %>"

View File

@ -8,6 +8,7 @@ import (
func init() {
buildCmd.Flags().BoolVarP(&flagPush, "push", "p", false, "pushes built squadron units to the registry")
buildCmd.Flags().IntVar(&flagParallel, "parallel", 1, "run command in parallel")
buildCmd.Flags().StringSliceVar(&flagTags, "tags", nil, "list of tags to include or exclude (can specify multiple or separate values with commas: tag1,tag2,-tag3)")
buildCmd.Flags().StringSliceVar(&flagBuildArgs, "build-args", nil, "additional docker buildx build args")
buildCmd.Flags().StringSliceVar(&flagPushArgs, "push-args", nil, "additional docker push args")
}
@ -25,7 +26,7 @@ var buildCmd = &cobra.Command{
}
squadronName, unitNames := parseSquadronAndUnitNames(args)
if err := sq.FilterConfig(squadronName, unitNames); err != nil {
if err := sq.FilterConfig(squadronName, unitNames, flagTags); err != nil {
return err
}

View File

@ -3,14 +3,14 @@ package actions
import (
"fmt"
"github.com/foomo/squadron"
"github.com/foomo/squadron/internal/util"
"github.com/spf13/cobra"
"github.com/foomo/squadron"
)
func init() {
configCmd.Flags().BoolVar(&flagNoRender, "no-render", false, "don't render the config template")
configCmd.Flags().StringSliceVar(&flagTags, "tags", nil, "list of tags to include or exclude (can specify multiple or separate values with commas: tag1,tag2,-tag3)")
}
var configCmd = &cobra.Command{
@ -26,7 +26,7 @@ var configCmd = &cobra.Command{
}
squadronName, unitNames := parseSquadronAndUnitNames(args)
if err := sq.FilterConfig(squadronName, unitNames); err != nil {
if err := sq.FilterConfig(squadronName, unitNames, flagTags); err != nil {
return err
}

View File

@ -7,7 +7,7 @@ import (
)
func init() {
diffCmd.Flags().StringVarP(&flagNamespace, "namespace", "n", "default", "specifies the namespace")
diffCmd.Flags().StringVarP(&flagNamespace, "namespace", "n", "default", "set the namespace name or template (default, squadron-{{.Squadron}}-{{.Unit}})")
diffCmd.Flags().IntVar(&flagParallel, "parallel", 1, "run command in parallel")
}

View File

@ -8,7 +8,8 @@ import (
func init() {
downCmd.Flags().IntVar(&flagParallel, "parallel", 1, "run command in parallel")
downCmd.Flags().StringVarP(&flagNamespace, "namespace", "n", "default", "Specifies the namespace")
downCmd.Flags().StringVarP(&flagNamespace, "namespace", "n", "default", "set the namespace name or template (default, squadron-{{.Squadron}}-{{.Unit}})")
downCmd.Flags().StringSliceVar(&flagTags, "tags", nil, "list of tags to include or exclude (can specify multiple or separate values with commas: tag1,tag2,-tag3)")
}
var downCmd = &cobra.Command{
@ -26,7 +27,7 @@ var downCmd = &cobra.Command{
args, helmArgs := parseExtraArgs(args)
squadronName, unitNames := parseSquadronAndUnitNames(args)
if err := sq.FilterConfig(squadronName, unitNames); err != nil {
if err := sq.FilterConfig(squadronName, unitNames, flagTags); err != nil {
return err
}

View File

@ -10,6 +10,10 @@ import (
var flagPrefixSquadron bool
func init() {
listCmd.Flags().StringSliceVar(&flagTags, "tags", nil, "list of tags to include or exclude (can specify multiple or separate values with commas: tag1,tag2,-tag3)")
}
var listCmd = &cobra.Command{
Use: "list [SQUADRON]",
Short: "list squadron units",
@ -23,7 +27,7 @@ var listCmd = &cobra.Command{
}
squadronName, unitNames := parseSquadronAndUnitNames(args)
if err := sq.FilterConfig(squadronName, unitNames); err != nil {
if err := sq.FilterConfig(squadronName, unitNames, flagTags); err != nil {
return err
}

View File

@ -6,11 +6,12 @@ import (
)
func init() {
pushCmd.Flags().StringVarP(&flagNamespace, "namespace", "n", "default", "specifies the namespace")
pushCmd.Flags().StringVarP(&flagNamespace, "namespace", "n", "default", "set the namespace name or template (default, squadron-{{.Squadron}}-{{.Unit}})")
pushCmd.Flags().BoolVarP(&flagBuild, "build", "b", false, "builds or rebuilds units")
pushCmd.Flags().IntVar(&flagParallel, "parallel", 1, "run command in parallel")
pushCmd.Flags().StringSliceVar(&flagBuildArgs, "build-args", nil, "additional docker buildx build args")
pushCmd.Flags().StringSliceVar(&flagPushArgs, "push-args", nil, "additional docker push args")
pushCmd.Flags().StringSliceVar(&flagTags, "tags", nil, "list of tags to include or exclude (can specify multiple or separate values with commas: tag1,tag2,-tag3)")
}
var pushCmd = &cobra.Command{
@ -25,7 +26,7 @@ var pushCmd = &cobra.Command{
}
squadronName, unitNames := parseSquadronAndUnitNames(args)
if err := sq.FilterConfig(squadronName, unitNames); err != nil {
if err := sq.FilterConfig(squadronName, unitNames, flagTags); err != nil {
return err
}

View File

@ -8,8 +8,9 @@ import (
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(&flagNamespace, "namespace", "n", "default", "set the namespace name or template (default, squadron-{{.Squadron}}-{{.Unit}})")
rollbackCmd.Flags().StringVarP(&flagRevision, "revision", "r", "", "specifies the revision to roll back to")
rollbackCmd.Flags().StringSliceVar(&flagTags, "tags", nil, "list of tags to include or exclude (can specify multiple or separate values with commas: tag1,tag2,-tag3)")
}
var rollbackCmd = &cobra.Command{
@ -26,7 +27,7 @@ var rollbackCmd = &cobra.Command{
args, helmArgs := parseExtraArgs(args)
squadronName, unitNames := parseSquadronAndUnitNames(args)
if err := sq.FilterConfig(squadronName, unitNames); err != nil {
if err := sq.FilterConfig(squadronName, unitNames, flagTags); err != nil {
return err
}

View File

@ -46,6 +46,7 @@ var (
flagParallel int
flagBuildArgs []string
flagPushArgs []string
flagTags []string
flagDiff bool
flagFiles []string
)

View File

@ -8,7 +8,8 @@ import (
func init() {
statusCmd.Flags().IntVar(&flagParallel, "parallel", 1, "run command in parallel")
statusCmd.Flags().StringVarP(&flagNamespace, "namespace", "n", "default", "specifies the namespace")
statusCmd.Flags().StringVarP(&flagNamespace, "namespace", "n", "default", "set the namespace name or template (default, squadron-{{.Squadron}}-{{.Unit}})")
statusCmd.Flags().StringSliceVar(&flagTags, "tags", nil, "list of tags to include or exclude (can specify multiple or separate values with commas: tag1,tag2,-tag3)")
}
var statusCmd = &cobra.Command{
@ -25,7 +26,7 @@ var statusCmd = &cobra.Command{
args, helmArgs := parseExtraArgs(args)
squadronName, unitNames := parseSquadronAndUnitNames(args)
if err := sq.FilterConfig(squadronName, unitNames); err != nil {
if err := sq.FilterConfig(squadronName, unitNames, flagTags); err != nil {
return err
}

View File

@ -9,7 +9,8 @@ import (
)
func init() {
templateCmd.Flags().StringVarP(&flagNamespace, "namespace", "n", "default", "specifies the namespace")
templateCmd.Flags().StringVarP(&flagNamespace, "namespace", "n", "default", "set the namespace name or template (default, squadron-{{.Squadron}}-{{.Unit}})")
templateCmd.Flags().StringSliceVar(&flagTags, "tags", nil, "list of tags to include or exclude (can specify multiple or separate values with commas: tag1,tag2,-tag3)")
}
var templateCmd = &cobra.Command{
@ -27,7 +28,7 @@ var templateCmd = &cobra.Command{
args, helmArgs := parseExtraArgs(args)
squadronName, unitNames := parseSquadronAndUnitNames(args)
if err := sq.FilterConfig(squadronName, unitNames); err != nil {
if err := sq.FilterConfig(squadronName, unitNames, flagTags); err != nil {
return err
}

View File

@ -10,12 +10,13 @@ import (
)
func init() {
upCmd.Flags().StringVarP(&flagNamespace, "namespace", "n", "default", "specifies the namespace")
upCmd.Flags().StringVarP(&flagNamespace, "namespace", "n", "default", "set the namespace name or template (default, squadron-{{.Squadron}}-{{.Unit}})")
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().IntVar(&flagParallel, "parallel", 1, "run command in parallel")
upCmd.Flags().StringSliceVar(&flagBuildArgs, "build-args", nil, "additional docker buildx build args")
upCmd.Flags().StringSliceVar(&flagPushArgs, "push-args", nil, "additional docker push args")
upCmd.Flags().StringSliceVar(&flagTags, "tags", nil, "list of tags to include or exclude (can specify multiple or separate values with commas: tag1,tag2,-tag3)")
}
var upCmd = &cobra.Command{
@ -32,7 +33,7 @@ var upCmd = &cobra.Command{
args, helmArgs := parseExtraArgs(args)
squadronName, unitNames := parseSquadronAndUnitNames(args)
if err := sq.FilterConfig(squadronName, unitNames); err != nil {
if err := sq.FilterConfig(squadronName, unitNames, flagTags); err != nil {
return err
}

View File

@ -4,10 +4,9 @@ import (
"context"
"fmt"
"github.com/foomo/squadron/internal/util"
"github.com/sirupsen/logrus"
"gopkg.in/yaml.v3"
"github.com/foomo/squadron/internal/util"
)
type Build struct {

View File

@ -80,6 +80,15 @@ func (m Map[T]) Filter(keys ...string) error {
return nil
}
func (m Map[T]) FilterFn(handler func(key string, value T) bool) error {
for key, value := range m {
if !handler(key, value) {
delete(m, key)
}
}
return nil
}
func (m Map[T]) Iterate(handler func(key string, value T) error) error {
if len(m) == 0 {
return nil

3
internal/config/tag.go Normal file
View File

@ -0,0 +1,3 @@
package config
type Tag string

View File

@ -14,6 +14,7 @@ import (
type Unit struct {
Chart helm.Dependency `yaml:"chart,omitempty"`
Tags []Tag `yaml:"tags,omitempty"`
Builds map[string]Build `yaml:"builds,omitempty"`
Values map[string]interface{} `yaml:"values,omitempty"`
}

View File

@ -7,6 +7,7 @@ import (
"fmt"
"os"
"os/exec"
"slices"
"strings"
"text/template"
@ -102,13 +103,11 @@ func (sq *Squadron) MergeConfigFiles() error {
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
func (sq *Squadron) FilterConfig(squadron string, units, tags []string) error {
if len(squadron) > 0 {
if err := sq.Config().Squadrons.Filter(squadron); err != nil {
return err
}
}
if len(squadron) > 0 && len(units) > 0 {
@ -117,6 +116,27 @@ func (sq *Squadron) FilterConfig(squadron string, units []string) error {
}
}
if len(tags) > 0 {
if err := sq.Config().Squadrons.Iterate(func(key string, value config.Map[*config.Unit]) error {
return value.FilterFn(func(k string, v *config.Unit) bool {
for _, tag := range tags {
if strings.HasPrefix(tag, "-") {
if slices.Contains(v.Tags, config.Tag(strings.TrimPrefix(tag, "-"))) {
return false
}
} else if !slices.Contains(v.Tags, config.Tag(tag)) {
return false
}
}
return true
})
}); err != nil {
return err
}
}
sq.c.Trim()
value, err := yamlv2.Marshal(sq.c)
if err != nil {
return err

View File

@ -9,15 +9,20 @@ import (
"github.com/foomo/squadron"
"github.com/foomo/squadron/internal/testutils"
"github.com/foomo/squadron/internal/util"
"github.com/pterm/pterm"
"github.com/stretchr/testify/require"
)
func TestConfigSimpleSnapshot(t *testing.T) {
pterm.EnableDebugMessages()
require.NoError(t, os.Setenv("PROJECT_ROOT", "."))
tests := []struct {
name string
files []string
squadron string
units []string
tags []string
}{
{
name: "blank",
@ -39,21 +44,25 @@ func TestConfigSimpleSnapshot(t *testing.T) {
name: "template",
files: []string{"squadron.yaml"},
},
{
name: "tags",
tags: []string{"backend", "-skip"},
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)
config(tt, test.name, test.files, test.squadron, test.units, test.tags)
})
}
}
func config(t *testing.T, name string, files []string, squadronName string, unitNames []string) {
func config(t *testing.T, name string, files []string, squadronName string, unitNames, tags []string) {
t.Helper()
var cwd string
ctx := context.TODO()
require.NoError(t, util.ValidatePath(".", &cwd))
require.NoError(t, os.Setenv("PROJECT_ROOT", "."))
for i, file := range files {
files[i] = path.Join("testdata", name, file)
@ -65,7 +74,7 @@ func config(t *testing.T, name string, files []string, squadronName string, unit
}
{
require.NoError(t, sq.FilterConfig(squadronName, unitNames), "failed to filter config")
require.NoError(t, sq.FilterConfig(squadronName, unitNames, tags), "failed to filter config")
testutils.Snapshot(t, path.Join("testdata", name, "snapshop-config-norender.yaml"), sq.ConfigYAML())
}

View File

@ -0,0 +1,14 @@
version: "2.0"
squadron:
checkout:
backend:
chart:
name: backend
repository: file://<% env "PROJECT_ROOT" %>/_examples/common/charts/backend
version: 0.0.1
tags:
- backend
values:
image:
repository: nginx
tag: latest

14
testdata/tags/snapshop-config.yaml vendored Normal file
View File

@ -0,0 +1,14 @@
version: "2.0"
squadron:
checkout:
backend:
chart:
name: backend
repository: file://./_examples/common/charts/backend
version: 0.0.1
tags:
- backend
values:
image:
repository: nginx
tag: latest

49
testdata/tags/snapshop-template.yaml vendored Normal file
View File

@ -0,0 +1,49 @@
---
# 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

33
testdata/tags/squadron.yaml vendored Normal file
View File

@ -0,0 +1,33 @@
version: "2.0"
squadron:
storefinder:
frontend:
chart: <% env "PROJECT_ROOT" %>/_examples/common/charts/frontend
tags: ["frontend"]
values:
image:
tag: latest
repository: nginx
backend:
chart: <% env "PROJECT_ROOT" %>/_examples/common/charts/backend
tags: ["backend", "skip"]
values:
image:
tag: latest
repository: nginx
checkout:
frontend:
chart: <% env "PROJECT_ROOT" %>/_examples/common/charts/frontend
tags: ["frontend"]
values:
image:
tag: latest
repository: nginx
backend:
chart: <% env "PROJECT_ROOT" %>/_examples/common/charts/backend
tags: ["backend"]
values:
image:
tag: latest
repository: nginx