diff --git a/README.md b/README.md index 91d8692..7499b15 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Configure your squadron ```yaml # https://raw.githubusercontent.com/foomo/squadron/refs/heads/main/squadron.schema.json -version: '2.1' +version: '2.2' # squadron template vars vars: {} diff --git a/_examples/helloworld/squadron.yaml b/_examples/helloworld/squadron.yaml index dc03646..5bd56fc 100644 --- a/_examples/helloworld/squadron.yaml +++ b/_examples/helloworld/squadron.yaml @@ -1,4 +1,4 @@ -version: '2.1' +version: '2.2' squadron: storefinder: diff --git a/_examples/kustomize/squadron.yaml b/_examples/kustomize/squadron.yaml index 9be0f81..68526c3 100644 --- a/_examples/kustomize/squadron.yaml +++ b/_examples/kustomize/squadron.yaml @@ -1,4 +1,4 @@ -version: '2.1' +version: '2.2' squadron: storefinder: diff --git a/_examples/monorepo/squadrons/checkout/backend/squadron.yaml b/_examples/monorepo/squadrons/checkout/backend/squadron.yaml index edda783..e65a905 100644 --- a/_examples/monorepo/squadrons/checkout/backend/squadron.yaml +++ b/_examples/monorepo/squadrons/checkout/backend/squadron.yaml @@ -1,5 +1,5 @@ # Schema version -version: '2.1' +version: '2.2' squadron: checkout: diff --git a/_examples/monorepo/squadrons/checkout/frontend/squadron.yaml b/_examples/monorepo/squadrons/checkout/frontend/squadron.yaml index aa3260b..9f3aeca 100644 --- a/_examples/monorepo/squadrons/checkout/frontend/squadron.yaml +++ b/_examples/monorepo/squadrons/checkout/frontend/squadron.yaml @@ -1,5 +1,5 @@ # Schema version -version: '2.1' +version: '2.2' squadron: checkout: diff --git a/_examples/monorepo/squadrons/squadron.yaml b/_examples/monorepo/squadrons/squadron.yaml index b8bf709..8853b58 100644 --- a/_examples/monorepo/squadrons/squadron.yaml +++ b/_examples/monorepo/squadrons/squadron.yaml @@ -1,4 +1,4 @@ -version: '2.1' +version: '2.2' global: docker: diff --git a/_examples/monorepo/squadrons/storefinder/backend/squadron.yaml b/_examples/monorepo/squadrons/storefinder/backend/squadron.yaml index 5ad9aee..0a07d54 100644 --- a/_examples/monorepo/squadrons/storefinder/backend/squadron.yaml +++ b/_examples/monorepo/squadrons/storefinder/backend/squadron.yaml @@ -1,5 +1,5 @@ # Schema version -version: '2.1' +version: '2.2' squadron: storefinder: diff --git a/_examples/monorepo/squadrons/storefinder/frontend/squadron.yaml b/_examples/monorepo/squadrons/storefinder/frontend/squadron.yaml index 0c652b9..13a439b 100644 --- a/_examples/monorepo/squadrons/storefinder/frontend/squadron.yaml +++ b/_examples/monorepo/squadrons/storefinder/frontend/squadron.yaml @@ -1,5 +1,5 @@ # Schema version -version: '2.1' +version: '2.2' squadron: storefinder: diff --git a/go.mod b/go.mod index 54d179f..a3a0e91 100644 --- a/go.mod +++ b/go.mod @@ -5,6 +5,7 @@ go 1.23.2 replace github.com/miracl/conflate v1.2.1 => github.com/runz0rd/conflate v1.2.2-0.20210920145208-fa48576ef06d require ( + dario.cat/mergo v1.0.1 github.com/1Password/connect-sdk-go v1.5.3 github.com/BurntSushi/toml v1.4.0 github.com/Masterminds/sprig/v3 v3.3.0 @@ -29,7 +30,6 @@ require ( atomicgo.dev/cursor v0.2.0 // indirect atomicgo.dev/keyboard v0.2.9 // indirect atomicgo.dev/schedule v0.1.0 // indirect - dario.cat/mergo v1.0.1 // indirect github.com/Masterminds/goutils v1.1.1 // indirect github.com/Masterminds/semver/v3 v3.3.0 // indirect github.com/bahlo/generic-list-go v0.2.0 // indirect diff --git a/internal/config/chart.go b/internal/config/chart.go index 185852d..c83186b 100644 --- a/internal/config/chart.go +++ b/internal/config/chart.go @@ -35,21 +35,26 @@ func (d *Chart) UnmarshalYAML(value *yaml.Node) error { 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), "Chart.yaml")) if err != nil { return errors.New("failed to load local chart: " + vString) } + d.Name = localChart.Name d.Repository = fmt.Sprintf("file://%v", vString) d.Version = localChart.Version + wd, err := os.Getwd() if err != nil { return errors.Wrap(err, "failed to get working directory") } + schemaPath := string(vBytes) if value, err := filepath.Rel(wd, string(vBytes)); err == nil { schemaPath = value diff --git a/internal/config/unit.go b/internal/config/unit.go index 3e68c54..d702f60 100644 --- a/internal/config/unit.go +++ b/internal/config/unit.go @@ -4,13 +4,17 @@ import ( "bytes" "context" "fmt" + "os" "path" "sort" "strings" + "dario.cat/mergo" + "github.com/foomo/squadron/internal/template" "github.com/foomo/squadron/internal/util" "github.com/pkg/errors" yamlv2 "gopkg.in/yaml.v2" + "gopkg.in/yaml.v3" ) type Unit struct { @@ -24,12 +28,45 @@ type Unit struct { Builds map[string]Build `json:"builds,omitempty" yaml:"builds,omitempty"` // Chart values Values map[string]any `json:"values,omitempty" yaml:"values,omitempty"` + // Extend chart values + Extends string `json:"extends,omitempty" yaml:"extends,omitempty"` } // ------------------------------------------------------------------------------------------------ // ~ Public methods // ------------------------------------------------------------------------------------------------ +func (u *Unit) UnmarshalYAML(value *yaml.Node) error { + type wrapper Unit + if err := value.Decode((*wrapper)(u)); err != nil { + return err + } + if u.Extends != "" { + // render filename + filename, err := template.ExecuteFileTemplate(context.Background(), u.Extends, nil, true) + if err != nil { + return errors.Wrap(err, "failed to render defaults filename") + } + + // read defaults + defaults, err := os.ReadFile(string(filename)) + if err != nil { + return errors.Wrap(err, "failed to read defaults") + } + + var m map[string]any + if err := yaml.Unmarshal(defaults, &m); err != nil { + return errors.Wrap(err, "failed to unmarshal defaults") + } + if err := mergo.Merge(&u.Values, m); err != nil { + return err + } + + u.Extends = "" + } + return nil +} + // JSONSchemaProperty type workaround func (Unit) JSONSchemaProperty(prop string) any { var x any @@ -39,7 +76,7 @@ func (Unit) JSONSchemaProperty(prop string) any { return nil } -func (u *Unit) ValuesYAML(global, vars map[string]any) ([]byte, error) { +func (u *Unit) ValuesYAML(global map[string]any) ([]byte, error) { values := u.Values if values == nil { values = map[string]any{} @@ -49,11 +86,6 @@ func (u *Unit) ValuesYAML(global, vars map[string]any) ([]byte, error) { values["global"] = global } } - if vars != nil { - if _, ok := values["vars"]; !ok { - values["vars"] = vars - } - } return yamlv2.Marshal(values) } @@ -66,9 +98,9 @@ func (u *Unit) BuildNames() []string { return ret } -func (u *Unit) Template(ctx context.Context, name, squadron, unit, namespace string, global, vars map[string]any, helmArgs []string) ([]byte, error) { +func (u *Unit) Template(ctx context.Context, name, squadron, unit, namespace string, global map[string]any, helmArgs []string) ([]byte, error) { var ret bytes.Buffer - valueBytes, err := u.ValuesYAML(global, vars) + valueBytes, err := u.ValuesYAML(global) if err != nil { return nil, err } diff --git a/internal/config/version.go b/internal/config/version.go index 477d3ee..5c024c8 100644 --- a/internal/config/version.go +++ b/internal/config/version.go @@ -1,3 +1,3 @@ package config -const Version = "2.1" +const Version = "2.2" diff --git a/squadron.go b/squadron.go index 0456a67..154bcfb 100644 --- a/squadron.go +++ b/squadron.go @@ -398,7 +398,7 @@ func (sq *Squadron) Diff(ctx context.Context, helmArgs []string, parallel int) e if err != nil { return err } - valueBytes, err := v.ValuesYAML(sq.c.Global, sq.c.Vars) + valueBytes, err := v.ValuesYAML(sq.c.Global) if err != nil { return err } @@ -676,7 +676,7 @@ func (sq *Squadron) Up(ctx context.Context, helmArgs []string, username, version if err != nil { return err } - valueBytes, err := v.ValuesYAML(sq.c.Global, sq.c.Vars) + valueBytes, err := v.ValuesYAML(sq.c.Global) if err != nil { return err } @@ -687,7 +687,8 @@ func (sq *Squadron) Up(ctx context.Context, helmArgs []string, username, version Stdin(bytes.NewReader(valueBytes)). Stdout(os.Stdout). Args("upgrade", name, "--install"). - Args("--set", fmt.Sprintf("squadron=%s,unit=%s", key, k)). + Args("--set", fmt.Sprintf("squadron=%s", key)). + Args("--set", fmt.Sprintf("unit=%s", k)). Args("--description", string(description)). Args("--namespace", namespace). Args("--dependency-update"). @@ -748,7 +749,7 @@ func (sq *Squadron) Template(ctx context.Context, helmArgs []string, parallel in } pterm.Debug.Printfln("running helm template for chart: %s", name) - out, err := v.Template(ctx, name, key, k, namespace, sq.c.Global, sq.c.Vars, helmArgs) + out, err := v.Template(ctx, name, key, k, namespace, sq.c.Global, helmArgs) if err != nil { return err } diff --git a/squadron.schema.json b/squadron.schema.json index 8cfd547..b74da69 100644 --- a/squadron.schema.json +++ b/squadron.schema.json @@ -250,6 +250,10 @@ "values": { "type": "object", "description": "Chart values" + }, + "extends": { + "type": "string", + "description": "Extend chart values" } }, "additionalProperties": false, diff --git a/squadron_test.go b/squadron_test.go index 403ce63..5fb03a3 100644 --- a/squadron_test.go +++ b/squadron_test.go @@ -65,6 +65,10 @@ func TestConfigSimpleSnapshot(t *testing.T) { name: "simple", files: []string{"squadron.yaml"}, }, + { + name: "extends", + files: []string{"squadron.yaml"}, + }, { name: "override", files: []string{"squadron.yaml", "squadron.override.yaml"}, diff --git a/testdata/blank/snapshop-config-norender.yaml b/testdata/blank/snapshop-config-norender.yaml index 193899e..da36b5e 100644 --- a/testdata/blank/snapshop-config-norender.yaml +++ b/testdata/blank/snapshop-config-norender.yaml @@ -1 +1 @@ -version: "2.1" +version: "2.2" diff --git a/testdata/blank/snapshop-config.yaml b/testdata/blank/snapshop-config.yaml index 193899e..da36b5e 100644 --- a/testdata/blank/snapshop-config.yaml +++ b/testdata/blank/snapshop-config.yaml @@ -1 +1 @@ -version: "2.1" +version: "2.2" diff --git a/testdata/blank/squadron.yaml b/testdata/blank/squadron.yaml index 193899e..da36b5e 100644 --- a/testdata/blank/squadron.yaml +++ b/testdata/blank/squadron.yaml @@ -1 +1 @@ -version: "2.1" +version: "2.2" diff --git a/testdata/extends/snapshop-config-norender.yaml b/testdata/extends/snapshop-config-norender.yaml new file mode 100644 index 0000000..2028f43 --- /dev/null +++ b/testdata/extends/snapshop-config-norender.yaml @@ -0,0 +1,19 @@ +version: "2.2" +squadron: + storefinder: + frontend: + chart: + name: frontend + repository: file://<% env "PROJECT_ROOT" %>/_examples/common/charts/frontend + version: 0.0.1 + values: + env: + ONE: foo + THREE: baz + TWO: bar + image: + repository: nginx + tag: latest + tags: + - ONE + - TWO diff --git a/testdata/extends/snapshop-config.yaml b/testdata/extends/snapshop-config.yaml new file mode 100644 index 0000000..da64fe9 --- /dev/null +++ b/testdata/extends/snapshop-config.yaml @@ -0,0 +1,19 @@ +version: "2.2" +squadron: + storefinder: + frontend: + chart: + name: frontend + repository: file://./_examples/common/charts/frontend + version: 0.0.1 + values: + env: + ONE: foo + THREE: baz + TWO: bar + image: + repository: nginx + tag: latest + tags: + - ONE + - TWO diff --git a/testdata/extends/snapshop-template.yaml b/testdata/extends/snapshop-template.yaml new file mode 100644 index 0000000..1766fa0 --- /dev/null +++ b/testdata/extends/snapshop-template.yaml @@ -0,0 +1,78 @@ +--- +# Source: frontend/templates/service.yaml +apiVersion: v1 +kind: Service +metadata: + name: storefinder-frontend + labels: + app.kubernetes.io/name: storefinder-frontend + app.kubernetes.io/component: frontend + app.kubernetes.io/managed-by: Helm + helm.sh/chart: 'frontend-0.0.1' + namespace: default +spec: + type: ClusterIP + selector: + app.kubernetes.io/name: storefinder-frontend + app.kubernetes.io/component: frontend + ports: + - name: http + port: 80 +--- +# Source: frontend/templates/deployment.yaml +apiVersion: apps/v1 +kind: Deployment +metadata: + name: storefinder-frontend + labels: + app.kubernetes.io/name: storefinder-frontend + app.kubernetes.io/component: frontend + app.kubernetes.io/managed-by: Helm + helm.sh/chart: 'frontend-0.0.1' + namespace: default +spec: + selector: + matchLabels: + app.kubernetes.io/name: storefinder-frontend + app.kubernetes.io/component: frontend + template: + metadata: + labels: + app.kubernetes.io/name: storefinder-frontend + app.kubernetes.io/component: frontend + spec: + containers: + - name: storefinder-frontend + image: 'nginx:latest' + ports: + - name: http + protocol: TCP + containerPort: 80 +--- +# Source: frontend/templates/ingress.yaml +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: storefinder-frontend + labels: + app.kubernetes.io/name: storefinder-frontend + app.kubernetes.io/component: frontend + app.kubernetes.io/managed-by: Helm + helm.sh/chart: 'frontend-0.0.1' + namespace: default +spec: + tls: + - hosts: ['foo.com'] + secretName: foo-com-cert + rules: + - host: foo.com + http: + paths: + - pathType: Prefix + path: / + backend: + service: + name: storefinder-frontend + port: + name: http + number: 80 diff --git a/testdata/extends/squadron.base.yaml b/testdata/extends/squadron.base.yaml new file mode 100644 index 0000000..506492c --- /dev/null +++ b/testdata/extends/squadron.base.yaml @@ -0,0 +1,8 @@ +image: + tag: unknown + repository: nginx +env: + ONE: unknown + THREE: baz +tags: + - unknown diff --git a/testdata/extends/squadron.yaml b/testdata/extends/squadron.yaml new file mode 100644 index 0000000..aee7f8a --- /dev/null +++ b/testdata/extends/squadron.yaml @@ -0,0 +1,16 @@ +version: "2.2" + +squadron: + storefinder: + frontend: + chart: <% env "PROJECT_ROOT" %>/_examples/common/charts/frontend + extends: <% env "PROJECT_ROOT" %>/testdata/extends/squadron.base.yaml + values: + image: + tag: latest + env: + ONE: foo + TWO: bar + tags: + - ONE + - TWO diff --git a/testdata/global/snapshop-config-norender.yaml b/testdata/global/snapshop-config-norender.yaml index 961c0f7..6d18917 100644 --- a/testdata/global/snapshop-config-norender.yaml +++ b/testdata/global/snapshop-config-norender.yaml @@ -1,4 +1,4 @@ -version: "2.1" +version: "2.2" global: bar: - one diff --git a/testdata/global/snapshop-config.yaml b/testdata/global/snapshop-config.yaml index 961c0f7..6d18917 100644 --- a/testdata/global/snapshop-config.yaml +++ b/testdata/global/snapshop-config.yaml @@ -1,4 +1,4 @@ -version: "2.1" +version: "2.2" global: bar: - one diff --git a/testdata/global/squadron.override.yaml b/testdata/global/squadron.override.yaml index 1d53ceb..da8bcaf 100644 --- a/testdata/global/squadron.override.yaml +++ b/testdata/global/squadron.override.yaml @@ -1,4 +1,4 @@ -version: "2.1" +version: "2.2" global: foo: "two" diff --git a/testdata/global/squadron.yaml b/testdata/global/squadron.yaml index 73cf4c2..324e51e 100644 --- a/testdata/global/squadron.yaml +++ b/testdata/global/squadron.yaml @@ -1,4 +1,4 @@ -version: "2.1" +version: "2.2" global: foo: "one" diff --git a/testdata/override/snapshop-config-norender.yaml b/testdata/override/snapshop-config-norender.yaml index d92c639..27a415d 100644 --- a/testdata/override/snapshop-config-norender.yaml +++ b/testdata/override/snapshop-config-norender.yaml @@ -1,4 +1,4 @@ -version: "2.1" +version: "2.2" squadron: storefinder: frontend: diff --git a/testdata/override/snapshop-config.yaml b/testdata/override/snapshop-config.yaml index c20d1ae..bcd2e64 100644 --- a/testdata/override/snapshop-config.yaml +++ b/testdata/override/snapshop-config.yaml @@ -1,4 +1,4 @@ -version: "2.1" +version: "2.2" squadron: storefinder: frontend: diff --git a/testdata/override/squadron.override.yaml b/testdata/override/squadron.override.yaml index 1679d75..de87d60 100644 --- a/testdata/override/squadron.override.yaml +++ b/testdata/override/squadron.override.yaml @@ -1,4 +1,4 @@ -version: "2.1" +version: "2.2" squadron: storefinder: diff --git a/testdata/override/squadron.yaml b/testdata/override/squadron.yaml index e824294..0510955 100644 --- a/testdata/override/squadron.yaml +++ b/testdata/override/squadron.yaml @@ -1,4 +1,4 @@ -version: "2.1" +version: "2.2" squadron: storefinder: diff --git a/testdata/simple/snapshop-config-norender.yaml b/testdata/simple/snapshop-config-norender.yaml index 12ac8a1..75c587e 100644 --- a/testdata/simple/snapshop-config-norender.yaml +++ b/testdata/simple/snapshop-config-norender.yaml @@ -1,4 +1,4 @@ -version: "2.1" +version: "2.2" squadron: storefinder: frontend: diff --git a/testdata/simple/snapshop-config.yaml b/testdata/simple/snapshop-config.yaml index 4839ef5..baf5fde 100644 --- a/testdata/simple/snapshop-config.yaml +++ b/testdata/simple/snapshop-config.yaml @@ -1,4 +1,4 @@ -version: "2.1" +version: "2.2" squadron: storefinder: frontend: diff --git a/testdata/simple/squadron.yaml b/testdata/simple/squadron.yaml index be330d1..61f5bbf 100644 --- a/testdata/simple/squadron.yaml +++ b/testdata/simple/squadron.yaml @@ -1,4 +1,4 @@ -version: "2.1" +version: "2.2" squadron: storefinder: diff --git a/testdata/tags/snapshop-config-norender.yaml b/testdata/tags/snapshop-config-norender.yaml index f8c5a4f..79b6167 100644 --- a/testdata/tags/snapshop-config-norender.yaml +++ b/testdata/tags/snapshop-config-norender.yaml @@ -1,4 +1,4 @@ -version: "2.1" +version: "2.2" squadron: checkout: backend: diff --git a/testdata/tags/snapshop-config.yaml b/testdata/tags/snapshop-config.yaml index deb7cae..019bdc0 100644 --- a/testdata/tags/snapshop-config.yaml +++ b/testdata/tags/snapshop-config.yaml @@ -1,4 +1,4 @@ -version: "2.1" +version: "2.2" squadron: checkout: backend: diff --git a/testdata/tags/squadron.yaml b/testdata/tags/squadron.yaml index d69312d..c0a42e4 100644 --- a/testdata/tags/squadron.yaml +++ b/testdata/tags/squadron.yaml @@ -1,4 +1,4 @@ -version: "2.1" +version: "2.2" squadron: storefinder: diff --git a/testdata/template/snapshop-config-norender.yaml b/testdata/template/snapshop-config-norender.yaml index 9cf8e39..c5bfc07 100644 --- a/testdata/template/snapshop-config-norender.yaml +++ b/testdata/template/snapshop-config-norender.yaml @@ -1,4 +1,4 @@ -version: "2.1" +version: "2.2" global: enabled: true host: mycompany.com diff --git a/testdata/template/snapshop-config.yaml b/testdata/template/snapshop-config.yaml index 1636c64..a3fc64a 100644 --- a/testdata/template/snapshop-config.yaml +++ b/testdata/template/snapshop-config.yaml @@ -1,4 +1,4 @@ -version: "2.1" +version: "2.2" global: enabled: true host: mycompany.com diff --git a/testdata/template/squadron.yaml b/testdata/template/squadron.yaml index 9856f28..d13dfd1 100644 --- a/testdata/template/squadron.yaml +++ b/testdata/template/squadron.yaml @@ -1,4 +1,4 @@ -version: "2.1" +version: "2.2" global: host: mycompany.com diff --git a/testdata/vars/snapshop-config-norender.yaml b/testdata/vars/snapshop-config-norender.yaml index 20e53d6..44ae926 100644 --- a/testdata/vars/snapshop-config-norender.yaml +++ b/testdata/vars/snapshop-config-norender.yaml @@ -1,4 +1,4 @@ -version: "2.1" +version: "2.2" vars: bar: - one diff --git a/testdata/vars/snapshop-config.yaml b/testdata/vars/snapshop-config.yaml index 33d125c..f1690f4 100644 --- a/testdata/vars/snapshop-config.yaml +++ b/testdata/vars/snapshop-config.yaml @@ -1,4 +1,4 @@ -version: "2.1" +version: "2.2" vars: bar: - one diff --git a/testdata/vars/squadron.override.yaml b/testdata/vars/squadron.override.yaml index a20d515..793376d 100644 --- a/testdata/vars/squadron.override.yaml +++ b/testdata/vars/squadron.override.yaml @@ -1,4 +1,4 @@ -version: "2.1" +version: "2.2" vars: foo: "two" diff --git a/testdata/vars/squadron.yaml b/testdata/vars/squadron.yaml index 7abf073..bc1d50d 100644 --- a/testdata/vars/squadron.yaml +++ b/testdata/vars/squadron.yaml @@ -1,4 +1,4 @@ -version: "2.1" +version: "2.2" vars: foo: "one"