diff --git a/pkg/commands/build/build.go b/pkg/commands/build/build.go index 2c0b3ad85..fc1cbd372 100644 --- a/pkg/commands/build/build.go +++ b/pkg/commands/build/build.go @@ -9,17 +9,17 @@ import ( "path/filepath" "strings" - "sigs.k8s.io/kustomize/pkg/ifc" - "github.com/pkg/errors" "github.com/spf13/cobra" "sigs.k8s.io/kustomize/pkg/fs" + "sigs.k8s.io/kustomize/pkg/ifc" "sigs.k8s.io/kustomize/pkg/ifc/transformer" "sigs.k8s.io/kustomize/pkg/loader" "sigs.k8s.io/kustomize/pkg/pgmconfig" "sigs.k8s.io/kustomize/pkg/plugins" "sigs.k8s.io/kustomize/pkg/resmap" "sigs.k8s.io/kustomize/pkg/target" + "sigs.k8s.io/kustomize/plugin/builtin" "sigs.k8s.io/yaml" ) @@ -153,7 +153,12 @@ func (o *Options) emitResources( if o.outputPath != "" && fSys.IsDir(o.outputPath) { return writeIndividualFiles(fSys, o.outputPath, m) } - res, err := m.AsYaml(resmap.LegacySort) + // Done this way just to prove that overall sorting + // can be performed by a plugin. This particular + // plugin doesn't require configuration; just make + // it and call transform. + builtin.NewPreferredOrderTransformerPlugin().Transform(m) + res, err := m.AsYaml() if err != nil { return err } diff --git a/pkg/kusttest/kusttestharness.go b/pkg/kusttest/kusttestharness.go index 0e10a031f..af7855caa 100644 --- a/pkg/kusttest/kusttestharness.go +++ b/pkg/kusttest/kusttestharness.go @@ -20,6 +20,7 @@ import ( "sigs.k8s.io/kustomize/pkg/target" "sigs.k8s.io/kustomize/pkg/transformers/config/defaultconfig" "sigs.k8s.io/kustomize/pkg/types" + "sigs.k8s.io/kustomize/plugin/builtin" ) // KustTestHarness helps test kustomization generation and transformation. @@ -193,7 +194,9 @@ func (th *KustTestHarness) AssertActualEqualsExpected( if len(expected) > 0 && expected[0] == 10 { expected = expected[1:] } - actual, err := m.AsYaml(resmap.LegacySort) + // The tests currently expect a particular ordering. + builtin.NewPreferredOrderTransformerPlugin().Transform(m) + actual, err := m.AsYaml() if err != nil { th.t.Fatalf("Unexpected err: %v", err) } diff --git a/pkg/plugins/execplugin.go b/pkg/plugins/execplugin.go index 42ceeb2be..f5c341be9 100644 --- a/pkg/plugins/execplugin.go +++ b/pkg/plugins/execplugin.go @@ -140,7 +140,7 @@ func (p *ExecPlugin) Transform(rm resmap.ResMap) error { } // encode the ResMap so it can be fed to the plugin - resources, err := inputRM.AsYaml(resmap.Identity) + resources, err := inputRM.AsYaml() if err != nil { return err } diff --git a/pkg/resmap/resmap.go b/pkg/resmap/resmap.go index 4616c952e..c173b5b36 100644 --- a/pkg/resmap/resmap.go +++ b/pkg/resmap/resmap.go @@ -8,10 +8,8 @@ package resmap import ( "bytes" "fmt" - "sort" "github.com/pkg/errors" - "sigs.k8s.io/kustomize/pkg/resid" "sigs.k8s.io/kustomize/pkg/resource" "sigs.k8s.io/kustomize/pkg/types" @@ -109,9 +107,8 @@ type ResMap interface { // so the resources themselves can be modified. AsMap() map[resid.ResId]*resource.Resource - // AsYaml returns the yaml form of resources, after twiddling. - // Certainly nobody will abuse the twiddler. - AsYaml(f ResTwiddler) ([]byte, error) + // AsYaml returns the yaml form of resources. + AsYaml() ([]byte, error) // Gets the resource with the given Id, else nil. GetById(resid.ResId) *resource.Resource @@ -134,6 +131,9 @@ type ResMap interface { // Remove removes the Id and the resource it points to. Remove(resid.ResId) error + // Clear removes all resources and Ids. + Clear() + // ResourcesThatCouldReference returns a new ResMap with // resources that _might_ reference the resource represented // by the argument Id, excluding resources that should @@ -188,14 +188,20 @@ type resWrangler struct { func newOne() *resWrangler { result := &resWrangler{} - result.rIndex = make(map[resid.ResId]int) + result.Clear() return result } +// Clear implements ResMap. +func (m *resWrangler) Clear() { + m.rList = nil + m.rIndex = make(map[resid.ResId]int) +} + // Size implements ResMap. func (m *resWrangler) Size() int { if len(m.rList) != len(m.rIndex) { - panic(errors.New("class invariant violation")) + panic("class size invariant violation") } return len(m.rList) } @@ -366,30 +372,12 @@ func (m *resWrangler) GetMatchingIds(matches IdMatcher) []resid.ResId { return result } -// Identity returns Resources as is. -func Identity(m ResMap) []*resource.Resource { - return m.Resources() -} - -// LegacySort return Resources in a particular order. -func LegacySort(m ResMap) []*resource.Resource { - resources := make([]*resource.Resource, m.Size()) - ids := m.AllIds() - sort.Sort(IdSlice(ids)) - for i, id := range ids { - resources[i] = m.GetById(id) - } - return resources -} - -type ResTwiddler func(ResMap) []*resource.Resource - // AsYaml implements ResMap. -func (m *resWrangler) AsYaml(twiddle ResTwiddler) ([]byte, error) { +func (m *resWrangler) AsYaml() ([]byte, error) { firstObj := true var b []byte buf := bytes.NewBuffer(b) - for _, res := range twiddle(m) { + for _, res := range m.Resources() { out, err := yaml.Marshal(res.Map()) if err != nil { return nil, err @@ -412,7 +400,7 @@ func (m *resWrangler) AsYaml(twiddle ResTwiddler) ([]byte, error) { func (m *resWrangler) ErrorIfNotEqualSets(other ResMap) error { m2, ok := other.(*resWrangler) if !ok { - panic(fmt.Errorf("bad cast")) + panic("bad cast") } if m.Size() != m2.Size() { return fmt.Errorf( @@ -461,7 +449,7 @@ func (m *resWrangler) makeCopy(copier resCopier) ResMap { result.rList[i] = copier(r) id, err := m.idMappingToIndex(i) if err != nil { - panic(fmt.Errorf("corrupt index map")) + panic("corrupt index map") } result.rIndex[id] = i } @@ -494,12 +482,12 @@ func (m *resWrangler) AppendAll(other ResMap) error { } w2, ok := other.(*resWrangler) if !ok { - panic(fmt.Errorf("bad cast")) + panic("bad cast") } for i, res := range w2.Resources() { id, err := w2.idMappingToIndex(i) if err != nil { - panic("map is unrecoverably corrupted; " + err.Error()) + panic("map is irrecoverably corrupted; " + err.Error()) } err = m.AppendWithId(id, res) if err != nil { @@ -516,12 +504,12 @@ func (m *resWrangler) AbsorbAll(other ResMap) error { } w2, ok := other.(*resWrangler) if !ok { - panic(fmt.Errorf("bad cast")) + panic("bad cast") } for i, r := range w2.Resources() { id, err := w2.idMappingToIndex(i) if err != nil { - panic("map is unrecoverably corrupted; " + err.Error()) + panic("map is irrecoverably corrupted; " + err.Error()) } err = m.appendReplaceOrMerge(id, r) if err != nil { diff --git a/pkg/resmap/resmap_test.go b/pkg/resmap/resmap_test.go index 2d6d37038..8e6097d6c 100644 --- a/pkg/resmap/resmap_test.go +++ b/pkg/resmap/resmap_test.go @@ -177,7 +177,7 @@ metadata: "name": "cm2", }, })) - out, err := input.AsYaml(Identity) + out, err := input.AsYaml() if err != nil { t.Fatalf("unexpected error: %v", err) } diff --git a/plugin/builtin/PreferredOrderTransformer.go b/plugin/builtin/PreferredOrderTransformer.go new file mode 100644 index 000000000..670959d93 --- /dev/null +++ b/plugin/builtin/PreferredOrderTransformer.go @@ -0,0 +1,44 @@ +// Code generated by pluginator on PreferredOrderTransformer; DO NOT EDIT. +package builtin + +import ( + "sigs.k8s.io/kustomize/pkg/resmap" + "sigs.k8s.io/kustomize/pkg/resource" + "sigs.k8s.io/kustomize/pkg/transformers/config" + "sort" +) + +// Sort the resources using an ordering defined in the Gvk class. +// This puts cluster-wide basic resources with no +// dependencies (like Namespace, StorageClass, etc.) +// first, and resources with a high number of dependencies +// (like ValidatingWebhookConfiguration) last. +type PreferredOrderTransformerPlugin struct { + Prefix string `json:"prefix,omitempty" yaml:"prefix,omitempty"` + Suffix string `json:"suffix,omitempty" yaml:"suffix,omitempty"` + FieldSpecs []config.FieldSpec `json:"fieldSpecs,omitempty" yaml:"fieldSpecs,omitempty"` +} + +func NewPreferredOrderTransformerPlugin() *PreferredOrderTransformerPlugin { + return &PreferredOrderTransformerPlugin{} +} + +/* +func (p *PreferredOrderTransformerPlugin) Config( + ldr ifc.Loader, rf *resmap.Factory, c []byte) (err error) { + return nil +} +*/ +func (p *PreferredOrderTransformerPlugin) Transform(m resmap.ResMap) error { + resources := make([]*resource.Resource, m.Size()) + ids := m.AllIds() + sort.Sort(resmap.IdSlice(ids)) + for i, id := range ids { + resources[i] = m.GetById(id) + } + m.Clear() + for i, id := range ids { + m.AppendWithId(id, resources[i]) + } + return nil +} diff --git a/plugin/builtin/preferredordertransformer/PreferredOrderTransformer.go b/plugin/builtin/preferredordertransformer/PreferredOrderTransformer.go new file mode 100644 index 000000000..72eea3da2 --- /dev/null +++ b/plugin/builtin/preferredordertransformer/PreferredOrderTransformer.go @@ -0,0 +1,41 @@ +// Copyright 2019 The Kubernetes Authors. +// SPDX-License-Identifier: Apache-2.0 + +//go:generate go run sigs.k8s.io/kustomize/plugin/pluginator +package main + +import ( + "sigs.k8s.io/kustomize/pkg/ifc" + "sigs.k8s.io/kustomize/pkg/resmap" + "sigs.k8s.io/kustomize/pkg/resource" + "sort" +) + +// Sort the resources using an ordering defined in the Gvk class. +// This puts cluster-wide basic resources with no +// dependencies (like Namespace, StorageClass, etc.) +// first, and resources with a high number of dependencies +// (like ValidatingWebhookConfiguration) last. +type plugin struct {} + +var KustomizePlugin plugin + +// Nothing needed for configuration. +func (p *plugin) Config( + ldr ifc.Loader, rf *resmap.Factory, c []byte) (err error) { + return nil +} + +func (p *plugin) Transform(m resmap.ResMap) error { + resources := make([]*resource.Resource, m.Size()) + ids := m.AllIds() + sort.Sort(resmap.IdSlice(ids)) + for i, id := range ids { + resources[i] = m.GetById(id) + } + m.Clear() + for i, id := range ids { + m.AppendWithId(id, resources[i]) + } + return nil +} diff --git a/plugin/builtin/preferredordertransformer/PreferredOrderTransformer_test.go b/plugin/builtin/preferredordertransformer/PreferredOrderTransformer_test.go new file mode 100644 index 000000000..96e313cd9 --- /dev/null +++ b/plugin/builtin/preferredordertransformer/PreferredOrderTransformer_test.go @@ -0,0 +1,119 @@ +// 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 TestPreferredOrderTransformer(t *testing.T) { + tc := plugin.NewEnvForTest(t).Set() + defer tc.Reset() + + tc.BuildGoPlugin( + "builtin", "", "PreferredOrderTransformer") + + th := kusttest_test.NewKustTestPluginHarness(t, "/app") + rm := th.LoadAndRunTransformer(` +apiVersion: builtin +kind: PreferredOrderTransformer +metadata: + name: notImportantHere +`, ` +apiVersion: v1 +kind: Service +metadata: + name: papaya +--- +apiVersion: v1 +kind: Role +metadata: + name: banana +--- +apiVersion: v1 +kind: ValidatingWebhookConfiguration +metadata: + name: pomegranate +--- +apiVersion: v1 +kind: LimitRange +metadata: + name: peach +--- +apiVersion: v1 +kind: Deployment +metadata: + name: pear +--- +apiVersion: v1 +kind: Namespace +metadata: + name: apple +--- +apiVersion: v1 +kind: Secret +metadata: + name: quince +--- +apiVersion: v1 +kind: Ingress +metadata: + name: durian +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: apricot +`) + + th.AssertActualEqualsExpected(rm, ` +apiVersion: v1 +kind: Namespace +metadata: + name: apple +--- +apiVersion: v1 +kind: Role +metadata: + name: banana +--- +apiVersion: v1 +kind: ConfigMap +metadata: + name: apricot +--- +apiVersion: v1 +kind: Secret +metadata: + name: quince +--- +apiVersion: v1 +kind: Service +metadata: + name: papaya +--- +apiVersion: v1 +kind: LimitRange +metadata: + name: peach +--- +apiVersion: v1 +kind: Deployment +metadata: + name: pear +--- +apiVersion: v1 +kind: Ingress +metadata: + name: durian +--- +apiVersion: v1 +kind: ValidatingWebhookConfiguration +metadata: + name: pomegranate +`) +} diff --git a/plugin/someteam.example.com/v1/printworkdir/PrintWorkDir_test.go b/plugin/someteam.example.com/v1/printworkdir/PrintWorkDir_test.go index d5f515cf1..64c2cfd5b 100644 --- a/plugin/someteam.example.com/v1/printworkdir/PrintWorkDir_test.go +++ b/plugin/someteam.example.com/v1/printworkdir/PrintWorkDir_test.go @@ -8,7 +8,6 @@ import ( "testing" "sigs.k8s.io/kustomize/pkg/kusttest" - "sigs.k8s.io/kustomize/pkg/resmap" "sigs.k8s.io/kustomize/plugin" ) @@ -33,7 +32,7 @@ kind: PrintWorkDir metadata: name: whatever `) - a, err := m.AsYaml(resmap.Identity) + a, err := m.AsYaml() if err != nil { t.Error(err) }