From 5000a2e5035ab39eece7d74001fbce68a3839087 Mon Sep 17 00:00:00 2001 From: Damien Robichaud Date: Wed, 29 May 2019 18:27:39 -0700 Subject: [PATCH 1/3] Implement replica transformer as patch alternative --- pkg/replica/replica.go | 13 +++++ pkg/target/kusttarget_configplugin.go | 22 +++++++ pkg/transformers/config/transformerconfig.go | 2 + pkg/types/kustomization.go | 5 ++ plugin/builtin/ReplicaCountTransformer.go | 57 ++++++++++++++++++ .../ReplicaCountTransformer.go | 58 +++++++++++++++++++ .../ReplicaCountTransformer_test.go | 49 ++++++++++++++++ 7 files changed, 206 insertions(+) create mode 100644 pkg/replica/replica.go create mode 100644 plugin/builtin/ReplicaCountTransformer.go create mode 100644 plugin/builtin/replicacounttransformer/ReplicaCountTransformer.go create mode 100644 plugin/builtin/replicacounttransformer/ReplicaCountTransformer_test.go diff --git a/pkg/replica/replica.go b/pkg/replica/replica.go new file mode 100644 index 000000000..90ce319e6 --- /dev/null +++ b/pkg/replica/replica.go @@ -0,0 +1,13 @@ +package replica + +// Replica specifies a modification to a replica config. +// The number of replicas of a resource whose name matches will be set to count. +// This struct is used by the ReplicaCountTransform, and is meant to supplement +// the existing patch functionality with a simpler syntax for replica configuration. +type Replica struct { + // The name of the resource to change the replica count + Name string `json:"name,omitempty" yaml:"name,omitempty"` + + // The number of replicas required. + Count uint `json:"count,omitempty" yaml:"count,omitempty"` +} diff --git a/pkg/target/kusttarget_configplugin.go b/pkg/target/kusttarget_configplugin.go index 71dcd0454..5afe53541 100644 --- a/pkg/target/kusttarget_configplugin.go +++ b/pkg/target/kusttarget_configplugin.go @@ -7,6 +7,7 @@ import ( "github.com/pkg/errors" "sigs.k8s.io/kustomize/pkg/image" "sigs.k8s.io/kustomize/pkg/plugins" + "sigs.k8s.io/kustomize/pkg/replica" "sigs.k8s.io/kustomize/pkg/transformers" "sigs.k8s.io/kustomize/pkg/transformers/config" "sigs.k8s.io/kustomize/pkg/types" @@ -69,6 +70,7 @@ func (kt *KustTarget) configureBuiltinTransformers( kt.configureBuiltinLabelTransformer, kt.configureBuiltinAnnotationsTransformer, kt.configureBuiltinPatchJson6902Transformer, + kt.configureBuiltinReplicaCountTransformer, } var result []transformers.Transformer for _, f := range configurators { @@ -233,6 +235,26 @@ func (kt *KustTarget) configureBuiltinImageTagTransformer( return } +func (kt *KustTarget) configureBuiltinReplicaCountTransformer( + tConfig *config.TransformerConfig) ( + result []transformers.Transformer, err error) { + var c struct { + Replica replica.Replica + FieldSpecs []config.FieldSpec + } + for _, args := range kt.kustomization.Replicas { + c.Replica = args + c.FieldSpecs = tConfig.Replicas + p := builtin.NewReplicaCountTransformerPlugin() + err = kt.configureBuiltinPlugin(p, c, "replica") + if err != nil { + return nil, err + } + result = append(result, p) + } + return +} + func (kt *KustTarget) configureBuiltinPlugin( p plugins.Configurable, c interface{}, id string) (err error) { var y []byte diff --git a/pkg/transformers/config/transformerconfig.go b/pkg/transformers/config/transformerconfig.go index a00c6da66..3e47d1401 100644 --- a/pkg/transformers/config/transformerconfig.go +++ b/pkg/transformers/config/transformerconfig.go @@ -35,6 +35,7 @@ type TransformerConfig struct { NameReference nbrSlice `json:"nameReference,omitempty" yaml:"nameReference,omitempty"` VarReference fsSlice `json:"varReference,omitempty" yaml:"varReference,omitempty"` Images fsSlice `json:"images,omitempty" yaml:"images,omitempty"` + Replicas fsSlice `json:"replicas,omitempty" yaml:"replicas,omitempty"` } // MakeEmptyConfig returns an empty TransformerConfig object @@ -61,6 +62,7 @@ func (t *TransformerConfig) sortFields() { sort.Sort(t.NameReference) sort.Sort(t.VarReference) sort.Sort(t.Images) + sort.Sort(t.Replicas) } // AddPrefixFieldSpec adds a FieldSpec to NamePrefix diff --git a/pkg/types/kustomization.go b/pkg/types/kustomization.go index 86fd1f985..acaa47c5f 100644 --- a/pkg/types/kustomization.go +++ b/pkg/types/kustomization.go @@ -22,6 +22,7 @@ import ( "sigs.k8s.io/kustomize/pkg/gvk" "sigs.k8s.io/kustomize/pkg/image" + "sigs.k8s.io/kustomize/pkg/replica" ) const ( @@ -79,6 +80,10 @@ type Kustomization struct { // patch, but this operator is simpler to specify. Images []image.Image `json:"images,omitempty" yaml:"images,omitempty"` + // Replicas is a list of {resourcename, count} that allows for simpler replica + // specification. This can also be done with a patch. + Replicas []replica.Replica `json:"replicas,omitempty" yaml:"replicas,omitempty"` + // Vars allow things modified by kustomize to be injected into a // container specification. A var is a name (e.g. FOO) associated // with a field in a specific resource instance. The field must diff --git a/plugin/builtin/ReplicaCountTransformer.go b/plugin/builtin/ReplicaCountTransformer.go new file mode 100644 index 000000000..41412deb0 --- /dev/null +++ b/plugin/builtin/ReplicaCountTransformer.go @@ -0,0 +1,57 @@ +// Code generated by pluginator on ReplicaCountTransformer; DO NOT EDIT. +package builtin + +import ( + "github.com/pkg/errors" + "sigs.k8s.io/kustomize/pkg/ifc" + "sigs.k8s.io/kustomize/pkg/replica" + "sigs.k8s.io/kustomize/pkg/resid" + "sigs.k8s.io/kustomize/pkg/resmap" + "sigs.k8s.io/kustomize/pkg/transformers/config" + "sigs.k8s.io/yaml" +) + +// Find matching replicas declarations and replace +// the count. +type ReplicaCountTransformerPlugin struct { + Replica replica.Replica `json:"replica,omitempty" yaml:"replica,omitempty"` + FieldSpecs []config.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"` +} + +func NewReplicaCountTransformerPlugin() *ReplicaCountTransformerPlugin { + return &ReplicaCountTransformerPlugin{} +} + +func (p *ReplicaCountTransformerPlugin) Config( + ldr ifc.Loader, rf *resmap.Factory, c []byte) (err error) { + + p.Replica = replica.Replica{} + p.FieldSpecs = nil + return yaml.Unmarshal(c, p) +} + +func (p *ReplicaCountTransformerPlugin) Transform(m resmap.ResMap) error { + matcher := func(r resid.ResId) bool { + return r.ItemId.Name == p.Replica.Name + } + + for _, r := range m.GetMatchingIds(matcher) { + kMap := m[r].Kunstructured.Map() + + specInterface, ok := kMap["spec"] + if !ok { + return errors.New("'spec' not specified, replicas cannot be modified") + } + + if spec, ok := specInterface.(map[string]interface{}); ok { + spec["replicas"] = p.Replica.Count + kMap["spec"] = spec + } else { + return errors.New("'spec' not structured as expected") + } + + m[r].Kunstructured.SetMap(kMap) + } + + return nil +} diff --git a/plugin/builtin/replicacounttransformer/ReplicaCountTransformer.go b/plugin/builtin/replicacounttransformer/ReplicaCountTransformer.go new file mode 100644 index 000000000..d78e0237b --- /dev/null +++ b/plugin/builtin/replicacounttransformer/ReplicaCountTransformer.go @@ -0,0 +1,58 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +//go:generate go run sigs.k8s.io/kustomize/plugin/pluginator +package main + +import ( + "github.com/pkg/errors" + "sigs.k8s.io/kustomize/pkg/ifc" + "sigs.k8s.io/kustomize/pkg/replica" + "sigs.k8s.io/kustomize/pkg/resid" + "sigs.k8s.io/kustomize/pkg/resmap" + "sigs.k8s.io/kustomize/pkg/transformers/config" + "sigs.k8s.io/yaml" +) + +// Find matching replicas declarations and replace the count. +// Eases the kustomization configuration of replica changes. +type plugin struct { + Replica replica.Replica `json:"replica,omitempty" yaml:"replica,omitempty"` + FieldSpecs []config.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"` +} + +var KustomizePlugin plugin + +func (p *plugin) Config( + ldr ifc.Loader, rf *resmap.Factory, c []byte) (err error) { + + p.Replica = replica.Replica{} + p.FieldSpecs = nil + return yaml.Unmarshal(c, p) +} + +func (p *plugin) Transform(m resmap.ResMap) error { + matcher := func(r resid.ResId) bool { + return r.ItemId.Name == p.Replica.Name + } + + for _, r := range m.GetMatchingIds(matcher) { + kMap := m[r].Kunstructured.Map() + + specInterface, ok := kMap["spec"] + if !ok { + return errors.New("'spec' not specified, replicas cannot be modified") + } + + if spec, ok := specInterface.(map[string]interface{}); ok { + spec["replicas"] = p.Replica.Count + kMap["spec"] = spec + } else { + return errors.New("'spec' not structured as expected") + } + + m[r].Kunstructured.SetMap(kMap) + } + + return nil +} diff --git a/plugin/builtin/replicacounttransformer/ReplicaCountTransformer_test.go b/plugin/builtin/replicacounttransformer/ReplicaCountTransformer_test.go new file mode 100644 index 000000000..3d22c30f5 --- /dev/null +++ b/plugin/builtin/replicacounttransformer/ReplicaCountTransformer_test.go @@ -0,0 +1,49 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +package main_test + +import ( + "testing" + + "sigs.k8s.io/kustomize/pkg/kusttest" + "sigs.k8s.io/kustomize/plugin" +) + +func TestReplicaCountTransformer(t *testing.T) { + tc := plugin.NewEnvForTest(t).Set() + defer tc.Reset() + + tc.BuildGoPlugin( + "builtin", "", "ReplicaCountTransformer") + + th := kusttest_test.NewKustTestPluginHarness(t, "/app") + + rm := th.LoadAndRunTransformer(` +apiVersion: builtin +kind: ReplicaCountTransformer +metadata: + name: notImportantHere +replica: + name: deploy1 + count: 23 +`, ` +group: apps +apiVersion: v1 +kind: Deployment +metadata: + name: deploy1 +spec: + replicas: 1 +`) + + th.AssertActualEqualsExpected(rm, ` +apiVersion: v1 +group: apps +kind: Deployment +metadata: + name: deploy1 +spec: + replicas: 23 +`) +} From d4842ebd90ec43e69a8be1ddee3aab51bbd6e871 Mon Sep 17 00:00:00 2001 From: Damien Robichaud Date: Fri, 31 May 2019 11:20:36 -0700 Subject: [PATCH 2/3] Cleanup the replica plugin implementation. --- pkg/replica/replica.go | 13 ------- pkg/target/kusttarget_configplugin.go | 6 ++-- pkg/transformers/config/transformerconfig.go | 2 -- pkg/types/kustomization.go | 15 ++++++-- plugin/builtin/ReplicaCountTransformer.go | 36 ++++++++++--------- .../ReplicaCountTransformer.go | 32 +++++++++-------- 6 files changed, 51 insertions(+), 53 deletions(-) delete mode 100644 pkg/replica/replica.go diff --git a/pkg/replica/replica.go b/pkg/replica/replica.go deleted file mode 100644 index 90ce319e6..000000000 --- a/pkg/replica/replica.go +++ /dev/null @@ -1,13 +0,0 @@ -package replica - -// Replica specifies a modification to a replica config. -// The number of replicas of a resource whose name matches will be set to count. -// This struct is used by the ReplicaCountTransform, and is meant to supplement -// the existing patch functionality with a simpler syntax for replica configuration. -type Replica struct { - // The name of the resource to change the replica count - Name string `json:"name,omitempty" yaml:"name,omitempty"` - - // The number of replicas required. - Count uint `json:"count,omitempty" yaml:"count,omitempty"` -} diff --git a/pkg/target/kusttarget_configplugin.go b/pkg/target/kusttarget_configplugin.go index 5afe53541..a6371089a 100644 --- a/pkg/target/kusttarget_configplugin.go +++ b/pkg/target/kusttarget_configplugin.go @@ -7,7 +7,6 @@ import ( "github.com/pkg/errors" "sigs.k8s.io/kustomize/pkg/image" "sigs.k8s.io/kustomize/pkg/plugins" - "sigs.k8s.io/kustomize/pkg/replica" "sigs.k8s.io/kustomize/pkg/transformers" "sigs.k8s.io/kustomize/pkg/transformers/config" "sigs.k8s.io/kustomize/pkg/types" @@ -80,6 +79,7 @@ func (kt *KustTarget) configureBuiltinTransformers( } result = append(result, r...) } + return result, nil } @@ -239,12 +239,10 @@ func (kt *KustTarget) configureBuiltinReplicaCountTransformer( tConfig *config.TransformerConfig) ( result []transformers.Transformer, err error) { var c struct { - Replica replica.Replica - FieldSpecs []config.FieldSpec + Replica types.Replica } for _, args := range kt.kustomization.Replicas { c.Replica = args - c.FieldSpecs = tConfig.Replicas p := builtin.NewReplicaCountTransformerPlugin() err = kt.configureBuiltinPlugin(p, c, "replica") if err != nil { diff --git a/pkg/transformers/config/transformerconfig.go b/pkg/transformers/config/transformerconfig.go index 3e47d1401..a00c6da66 100644 --- a/pkg/transformers/config/transformerconfig.go +++ b/pkg/transformers/config/transformerconfig.go @@ -35,7 +35,6 @@ type TransformerConfig struct { NameReference nbrSlice `json:"nameReference,omitempty" yaml:"nameReference,omitempty"` VarReference fsSlice `json:"varReference,omitempty" yaml:"varReference,omitempty"` Images fsSlice `json:"images,omitempty" yaml:"images,omitempty"` - Replicas fsSlice `json:"replicas,omitempty" yaml:"replicas,omitempty"` } // MakeEmptyConfig returns an empty TransformerConfig object @@ -62,7 +61,6 @@ func (t *TransformerConfig) sortFields() { sort.Sort(t.NameReference) sort.Sort(t.VarReference) sort.Sort(t.Images) - sort.Sort(t.Replicas) } // AddPrefixFieldSpec adds a FieldSpec to NamePrefix diff --git a/pkg/types/kustomization.go b/pkg/types/kustomization.go index acaa47c5f..eb6fc2af2 100644 --- a/pkg/types/kustomization.go +++ b/pkg/types/kustomization.go @@ -22,7 +22,6 @@ import ( "sigs.k8s.io/kustomize/pkg/gvk" "sigs.k8s.io/kustomize/pkg/image" - "sigs.k8s.io/kustomize/pkg/replica" ) const ( @@ -82,7 +81,7 @@ type Kustomization struct { // Replicas is a list of {resourcename, count} that allows for simpler replica // specification. This can also be done with a patch. - Replicas []replica.Replica `json:"replicas,omitempty" yaml:"replicas,omitempty"` + Replicas []Replica `json:"replicas,omitempty" yaml:"replicas,omitempty"` // Vars allow things modified by kustomize to be injected into a // container specification. A var is a name (e.g. FOO) associated @@ -360,3 +359,15 @@ type PatchTarget struct { // stategic merge patch with the format // https://github.com/kubernetes/community/blob/master/contributors/devel/strategic-merge-patch.md type PatchStrategicMerge string + +// Replica specifies a modification to a replica config. +// The number of replicas of a resource whose name matches will be set to count. +// This struct is used by the ReplicaCountTransform, and is meant to supplement +// the existing patch functionality with a simpler syntax for replica configuration. +type Replica struct { + // The name of the resource to change the replica count + Name string `json:"name,omitempty" yaml:"name,omitempty"` + + // The number of replicas required. + Count uint `json:"count,omitempty" yaml:"count,omitempty"` +} diff --git a/plugin/builtin/ReplicaCountTransformer.go b/plugin/builtin/ReplicaCountTransformer.go index 41412deb0..018079189 100644 --- a/plugin/builtin/ReplicaCountTransformer.go +++ b/plugin/builtin/ReplicaCountTransformer.go @@ -2,20 +2,24 @@ package builtin import ( - "github.com/pkg/errors" + "fmt" + "sigs.k8s.io/kustomize/pkg/ifc" - "sigs.k8s.io/kustomize/pkg/replica" "sigs.k8s.io/kustomize/pkg/resid" "sigs.k8s.io/kustomize/pkg/resmap" - "sigs.k8s.io/kustomize/pkg/transformers/config" + "sigs.k8s.io/kustomize/pkg/types" "sigs.k8s.io/yaml" ) -// Find matching replicas declarations and replace -// the count. +const ( + fldReplica = "replicas" + fldSpec = "spec" +) + +// Find matching replicas declarations and replace the count. +// Eases the kustomization configuration of replica changes. type ReplicaCountTransformerPlugin struct { - Replica replica.Replica `json:"replica,omitempty" yaml:"replica,omitempty"` - FieldSpecs []config.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"` + Replica types.Replica `json:"replica,omitempty" yaml:"replica,omitempty"` } func NewReplicaCountTransformerPlugin() *ReplicaCountTransformerPlugin { @@ -25,8 +29,7 @@ func NewReplicaCountTransformerPlugin() *ReplicaCountTransformerPlugin { func (p *ReplicaCountTransformerPlugin) Config( ldr ifc.Loader, rf *resmap.Factory, c []byte) (err error) { - p.Replica = replica.Replica{} - p.FieldSpecs = nil + p.Replica = types.Replica{} return yaml.Unmarshal(c, p) } @@ -36,21 +39,20 @@ func (p *ReplicaCountTransformerPlugin) Transform(m resmap.ResMap) error { } for _, r := range m.GetMatchingIds(matcher) { - kMap := m[r].Kunstructured.Map() + kMap := m[r].Map() - specInterface, ok := kMap["spec"] + specInterface, ok := kMap[fldSpec] if !ok { - return errors.New("'spec' not specified, replicas cannot be modified") + return fmt.Errorf("object %s missing field %s, cannot update %s", + p.Replica.Name, fldSpec, fldReplica) } if spec, ok := specInterface.(map[string]interface{}); ok { - spec["replicas"] = p.Replica.Count - kMap["spec"] = spec + spec[fldReplica] = p.Replica.Count + kMap[fldSpec] = spec } else { - return errors.New("'spec' not structured as expected") + return fmt.Errorf("object %s has a malformed %s", p.Replica.Name, fldSpec) } - - m[r].Kunstructured.SetMap(kMap) } return nil diff --git a/plugin/builtin/replicacounttransformer/ReplicaCountTransformer.go b/plugin/builtin/replicacounttransformer/ReplicaCountTransformer.go index d78e0237b..d50fc2af6 100644 --- a/plugin/builtin/replicacounttransformer/ReplicaCountTransformer.go +++ b/plugin/builtin/replicacounttransformer/ReplicaCountTransformer.go @@ -5,20 +5,24 @@ package main import ( - "github.com/pkg/errors" + "fmt" + "sigs.k8s.io/kustomize/pkg/ifc" - "sigs.k8s.io/kustomize/pkg/replica" "sigs.k8s.io/kustomize/pkg/resid" "sigs.k8s.io/kustomize/pkg/resmap" - "sigs.k8s.io/kustomize/pkg/transformers/config" + "sigs.k8s.io/kustomize/pkg/types" "sigs.k8s.io/yaml" ) +const ( + fldReplica = "replicas" + fldSpec = "spec" +) + // Find matching replicas declarations and replace the count. // Eases the kustomization configuration of replica changes. type plugin struct { - Replica replica.Replica `json:"replica,omitempty" yaml:"replica,omitempty"` - FieldSpecs []config.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"` + Replica types.Replica `json:"replica,omitempty" yaml:"replica,omitempty"` } var KustomizePlugin plugin @@ -26,8 +30,7 @@ var KustomizePlugin plugin func (p *plugin) Config( ldr ifc.Loader, rf *resmap.Factory, c []byte) (err error) { - p.Replica = replica.Replica{} - p.FieldSpecs = nil + p.Replica = types.Replica{} return yaml.Unmarshal(c, p) } @@ -37,21 +40,20 @@ func (p *plugin) Transform(m resmap.ResMap) error { } for _, r := range m.GetMatchingIds(matcher) { - kMap := m[r].Kunstructured.Map() + kMap := m[r].Map() - specInterface, ok := kMap["spec"] + specInterface, ok := kMap[fldSpec] if !ok { - return errors.New("'spec' not specified, replicas cannot be modified") + return fmt.Errorf("object %s missing field %s, cannot update %s", + p.Replica.Name, fldSpec, fldReplica) } if spec, ok := specInterface.(map[string]interface{}); ok { - spec["replicas"] = p.Replica.Count - kMap["spec"] = spec + spec[fldReplica] = p.Replica.Count + kMap[fldSpec] = spec } else { - return errors.New("'spec' not structured as expected") + return fmt.Errorf("object %s has a malformed %s", p.Replica.Name, fldSpec) } - - m[r].Kunstructured.SetMap(kMap) } return nil From d925939795401015b3bfa733d734ce1880b29f6a Mon Sep 17 00:00:00 2001 From: Damien Robichaud Date: Fri, 31 May 2019 13:29:04 -0700 Subject: [PATCH 3/3] Add documentation for the replicas transform --- docs/fields.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/fields.md b/docs/fields.md index 39d4e5dd3..48f9832a1 100644 --- a/docs/fields.md +++ b/docs/fields.md @@ -36,6 +36,7 @@ What transformations (customizations) should be applied? | [nameSuffix](#namesuffix) | string | The value is appended to the names of all resources. | | [commonLabels](#commonlabels) | string | Adds annotions (non-identifying metadata) to add all resources. Like labels, these are key value pairs. | | [images](#images) | list | Images modify the name, tags and/or digest for images without creating patches. | +| [replicas](#replicas) | list | Replicas modify the number of replicas for a resource without creating patches. | |[patchesStrategicMerge](#patchesstrategicmerge)| list |Each entry in this list should resolve to a partial or complete resource definition file.| |[patchesJson6902](#patchesjson6902)| list |Each entry in this list should resolve to a kubernetes object and a JSON patch that will be applied to the object.| |[transformers](#transformers)|list|[plugin](plugins.md) configuration files| @@ -236,6 +237,24 @@ images: digest: sha256:24a0c4b4a4c0eb97a1aabb8e29f18e917d05abfe1b7a7c07857230879ce7d3d3 ``` +### replicas + +Replicas modify the number of replicas for a resource without creating patches. +E.g. Given this kubernetes Deployment fragment: +``` +metadata: + name: deployment-name +spec: + replicas: 3 +``` + +one can change the number of replicas to 5 with the following *kustomization*: +``` +replicas: +- name: deployment-name + count: 5 +``` +note that replicas is a list, so many replicas can be modified at the same time. ### kind