new command kustomize edit add buildmetadata (#4413)

* new command kustomize edit add buildmetadata

* new commands kustomize edit set buildmetadata and kustomize edit remove buildmetadata
This commit is contained in:
Natasha Sarkar
2022-01-26 15:34:50 -08:00
committed by GitHub
parent c65ef489ca
commit bcebad1664
16 changed files with 450 additions and 6 deletions

View File

@@ -325,11 +325,7 @@ func (kt *KustTarget) runTransformers(ra *accumulator.ResAccumulator) error {
return err
}
r = append(r, lts...)
err = ra.Transform(newMultiTransformer(r))
if err != nil {
return err
}
return nil
return ra.Transform(newMultiTransformer(r))
}
func (kt *KustTarget) configureExternalTransformers(transformers []string) ([]*resmap.TransformerWithProperties, error) {

View File

@@ -23,6 +23,8 @@ const (
ManagedByLabelOption = "managedByLabel"
)
var BuildMetadataOptions = []string{OriginAnnotations, TransformerAnnotations, ManagedByLabelOption}
// Kustomization holds the information needed to generate customized k8s api resources.
type Kustomization struct {
TypeMeta `json:",inline" yaml:",inline"`

View File

@@ -0,0 +1,69 @@
// Copyright 2022 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package add
import (
"fmt"
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/kustomize/v4/commands/internal/kustfile"
"sigs.k8s.io/kustomize/kustomize/v4/commands/internal/util"
"sigs.k8s.io/kustomize/kyaml/filesys"
)
type addBuildMetadataOptions struct {
*util.BuildMetadataValidator
buildMetadataOptions []string
}
// newCmdAddBuildMetadata adds options to the kustomization's buildMetada field.
func newCmdAddBuildMetadata(fSys filesys.FileSystem) *cobra.Command {
var o addBuildMetadataOptions
cmd := &cobra.Command{
Use: "buildmetadata",
Short: "Adds one or more buildMetadata options to the kustomization.yaml in the current directory",
Long: `Adds one or more buildMetadata options to the kustomization.yaml in the current directory.
The following options are valid:
- originAnnotations
- transformerAnnotations
- managedByLabel
originAnnotations will add the annotation config.kubernetes.io/origin to each resource, describing where
each resource originated from.
transformerAnnotations will add the annotation alpha.config.kubernetes.io/transformations to each resource,
describing the transformers that have acted upon the resource.
managedByLabel will add the label app.kubernetes.io/managed-by to each resource, describing which version
of kustomize managed the resource.`,
Example: `
add buildmetadata {option1},{option2}`,
RunE: func(cmd *cobra.Command, args []string) error {
var err error
o.buildMetadataOptions, err = o.BuildMetadataValidator.Validate(args)
if err != nil {
return err
}
return o.RunAddBuildMetadata(fSys)
},
}
return cmd
}
// RunAddBuildMetadata runs addBuildMetadata command (do real work).
func (o *addBuildMetadataOptions) RunAddBuildMetadata(fSys filesys.FileSystem) error {
mf, err := kustfile.NewKustomizationFile(fSys)
if err != nil {
return err
}
m, err := mf.Read()
if err != nil {
return err
}
for _, opt := range o.buildMetadataOptions {
if kustfile.StringInSlice(opt, m.BuildMetadata) {
return fmt.Errorf("buildMetadata option %s already in kustomization file", opt)
}
m.BuildMetadata = append(m.BuildMetadata, opt)
}
return mf.Write(m)
}

View File

@@ -0,0 +1,63 @@
// Copyright 2022 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package add
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/api/types"
testutils_test "sigs.k8s.io/kustomize/kustomize/v4/commands/internal/testutils"
"sigs.k8s.io/kustomize/kyaml/filesys"
)
func TestAddBuildMetadata(t *testing.T) {
tests := map[string]struct {
input string
args []string
expectedErr string
}{
"happy path": {
input: ``,
args: []string{strings.Join(types.BuildMetadataOptions, ",")},
},
"option already there": {
input: `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
buildMetadata: [originAnnotations]`,
args: []string{types.OriginAnnotations},
expectedErr: "buildMetadata option originAnnotations already in kustomization file",
},
"invalid option": {
input: ``,
args: []string{"invalid_option"},
expectedErr: "invalid buildMetadata option: invalid_option",
},
"too many args": {
input: ``,
args: []string{"option1", "option2"},
expectedErr: "too many arguments: [option1 option2]; to provide multiple buildMetadata options, please separate options by comma",
},
}
for _, tc := range tests {
fSys := filesys.MakeFsInMemory()
testutils_test.WriteTestKustomizationWith(fSys, []byte(tc.input))
cmd := newCmdAddBuildMetadata(fSys)
err := cmd.RunE(cmd, tc.args)
if tc.expectedErr != "" {
assert.Error(t, err)
assert.Contains(t, err.Error(), tc.expectedErr)
} else {
assert.NoError(t, err)
content, err := testutils_test.ReadTestKustomization(fSys)
assert.NoError(t, err)
for _, opt := range strings.Split(tc.args[0], ",") {
assert.Contains(t, string(content), opt)
}
}
}
}

View File

@@ -57,6 +57,7 @@ func NewCmdAdd(
newCmdAddSecret(fSys, ldr, rf),
newCmdAddConfigMap(fSys, ldr, rf),
newCmdAddBase(fSys),
newCmdAddBuildMetadata(fSys),
newCmdAddLabel(fSys, ldr.Validator().MakeLabelValidator()),
newCmdAddAnnotation(fSys, ldr.Validator().MakeAnnotationValidator()),
newCmdAddTransformer(fSys),

View File

@@ -42,6 +42,7 @@ func NewCmdRemove(
newCmdRemoveAnnotation(fSys, v.MakeAnnotationNameValidator()),
newCmdRemovePatch(fSys),
newCmdRemoveTransformer(fSys),
newCmdRemoveBuildMetadata(fSys),
)
return c
}

View File

@@ -0,0 +1,71 @@
// Copyright 2022 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package remove
import (
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/kustomize/v4/commands/internal/kustfile"
"sigs.k8s.io/kustomize/kustomize/v4/commands/internal/util"
"sigs.k8s.io/kustomize/kyaml/filesys"
)
type removeBuildMetadataOptions struct {
*util.BuildMetadataValidator
buildMetadataOptions []string
}
// newCmdRemoveBuildMetadata removes options to the kustomization's buildMetada field.
func newCmdRemoveBuildMetadata(fSys filesys.FileSystem) *cobra.Command {
var o removeBuildMetadataOptions
cmd := &cobra.Command{
Use: "buildmetadata",
Short: "Removes one or more buildMetadata options to the kustomization.yaml in the current directory",
Long: `Removes one or more buildMetadata options to the kustomization.yaml in the current directory.
The following options are valid:
- originAnnotations
- transformerAnnotations
- managedByLabel
originAnnotations will remove the annotation config.kubernetes.io/origin to each resource, describing where
each resource originated from.
transformerAnnotations will remove the annotation alpha.config.kubernetes.io/transformations to each resource,
describing the transformers that have acted upon the resource.
managedByLabel will remove the label app.kubernetes.io/managed-by to each resource, describing which version
of kustomize managed the resource.`,
Example: `
remove buildmetadata {option1},{option2}`,
RunE: func(cmd *cobra.Command, args []string) error {
var err error
o.buildMetadataOptions, err = o.BuildMetadataValidator.Validate(args)
if err != nil {
return err
}
return o.RunRemoveBuildMetadata(fSys)
},
}
return cmd
}
// RunRemoveBuildMetadata runs removeBuildMetadata command (do real work).
func (o *removeBuildMetadataOptions) RunRemoveBuildMetadata(fSys filesys.FileSystem) error {
mf, err := kustfile.NewKustomizationFile(fSys)
if err != nil {
return err
}
m, err := mf.Read()
if err != nil {
return err
}
var newOptions []string
for _, opt := range m.BuildMetadata {
if !kustfile.StringInSlice(opt, o.buildMetadataOptions) {
newOptions = append(newOptions, opt)
}
}
m.BuildMetadata = newOptions
if len(m.BuildMetadata) == 0 {
m.BuildMetadata = nil
}
return mf.Write(m)
}

View File

@@ -0,0 +1,65 @@
// Copyright 2022 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package remove
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/api/types"
testutils_test "sigs.k8s.io/kustomize/kustomize/v4/commands/internal/testutils"
"sigs.k8s.io/kustomize/kyaml/filesys"
)
func TestRemoveBuildMetadata(t *testing.T) {
tests := map[string]struct {
input string
args []string
expectedErr string
}{
"happy path": {
input: `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
buildMetadata: [originAnnotations, transformerAnnotations, managedByLabel]`,
args: []string{types.OriginAnnotations},
},
"option already there": {
input: `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
buildMetadata: [originAnnotations]`,
args: []string{types.OriginAnnotations},
},
"invalid option": {
input: ``,
args: []string{"invalid_option"},
expectedErr: "invalid buildMetadata option: invalid_option",
},
"too many args": {
input: ``,
args: []string{"option1", "option2"},
expectedErr: "too many arguments: [option1 option2]; to provide multiple buildMetadata options, please separate options by comma",
},
}
for _, tc := range tests {
fSys := filesys.MakeFsInMemory()
testutils_test.WriteTestKustomizationWith(fSys, []byte(tc.input))
cmd := newCmdRemoveBuildMetadata(fSys)
err := cmd.RunE(cmd, tc.args)
if tc.expectedErr != "" {
assert.Error(t, err)
assert.Contains(t, err.Error(), tc.expectedErr)
} else {
assert.NoError(t, err)
content, err := testutils_test.ReadTestKustomization(fSys)
assert.NoError(t, err)
for _, opt := range strings.Split(tc.args[0], ",") {
assert.NotContains(t, string(content), opt)
}
}
}
}

View File

@@ -30,6 +30,7 @@ func NewCmdSet(fSys filesys.FileSystem, ldr ifc.KvLoader, v ifc.Validator) *cobr
newCmdSetNameSuffix(fSys),
newCmdSetNamespace(fSys, v),
newCmdSetImage(fSys),
newCmdSetBuildMetadata(fSys),
newCmdSetReplicas(fSys),
newCmdSetLabel(fSys, ldr.Validator().MakeLabelValidator()),
newCmdSetAnnotation(fSys, ldr.Validator().MakeAnnotationValidator()),

View File

@@ -0,0 +1,63 @@
// Copyright 2022 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package set
import (
"github.com/spf13/cobra"
"sigs.k8s.io/kustomize/kustomize/v4/commands/internal/kustfile"
"sigs.k8s.io/kustomize/kustomize/v4/commands/internal/util"
"sigs.k8s.io/kustomize/kyaml/filesys"
)
type setBuildMetadataOptions struct {
*util.BuildMetadataValidator
buildMetadataOptions []string
}
// newCmdSetBuildMetadata sets options in the kustomization's buildMetada field.
func newCmdSetBuildMetadata(fSys filesys.FileSystem) *cobra.Command {
var o setBuildMetadataOptions
cmd := &cobra.Command{
Use: "buildmetadata",
Short: "Sets one or more buildMetadata options to the kustomization.yaml in the current directory",
Long: `Sets one or more buildMetadata options to the kustomization.yaml in the current directory.
Existing options in the buildMetadata field will be replaced entirely by the new options set by this command.
The following options are valid:
- originAnnotations
- transformerAnnotations
- managedByLabel
originAnnotations will add the annotation config.kubernetes.io/origin to each resource, describing where
each resource originated from.
transformerAnnotations will add the annotation alpha.config.kubernetes.io/transformations to each resource,
describing the transformers that have acted upon the resource.
managedByLabel will add the label app.kubernetes.io/managed-by to each resource, describing which version
of kustomize managed the resource.`,
Example: `
set buildmetadata {option1},{option2}`,
RunE: func(cmd *cobra.Command, args []string) error {
var err error
o.buildMetadataOptions, err = o.BuildMetadataValidator.Validate(args)
if err != nil {
return err
}
return o.RunSetBuildMetadata(fSys)
},
}
return cmd
}
// RunSetBuildMetadata runs setBuildMetadata command (do real work).
func (o *setBuildMetadataOptions) RunSetBuildMetadata(fSys filesys.FileSystem) error {
mf, err := kustfile.NewKustomizationFile(fSys)
if err != nil {
return err
}
m, err := mf.Read()
if err != nil {
return err
}
m.BuildMetadata = o.buildMetadataOptions
return mf.Write(m)
}

View File

@@ -0,0 +1,76 @@
// Copyright 2022 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package set
import (
"strings"
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kustomize/v4/commands/internal/kustfile"
testutils_test "sigs.k8s.io/kustomize/kustomize/v4/commands/internal/testutils"
"sigs.k8s.io/kustomize/kyaml/filesys"
)
func TestSetBuildMetadata(t *testing.T) {
tests := map[string]struct {
input string
args []string
expectedErr string
}{
"happy path": {
input: ``,
args: []string{strings.Join(types.BuildMetadataOptions, ",")},
},
"option already there": {
input: `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
buildMetadata: [originAnnotations]`,
args: []string{types.OriginAnnotations},
},
"invalid option": {
input: ``,
args: []string{"invalid_option"},
expectedErr: "invalid buildMetadata option: invalid_option",
},
"too many args": {
input: ``,
args: []string{"option1", "option2"},
expectedErr: "too many arguments: [option1 option2]; to provide multiple buildMetadata options, please separate options by comma",
},
"remove old options": {
input: `
apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
buildMetadata: [originAnnotations, transformerAnnotations, managedByLabel]`,
args: []string{types.OriginAnnotations},
},
}
for _, tc := range tests {
fSys := filesys.MakeFsInMemory()
testutils_test.WriteTestKustomizationWith(fSys, []byte(tc.input))
cmd := newCmdSetBuildMetadata(fSys)
err := cmd.RunE(cmd, tc.args)
if tc.expectedErr != "" {
assert.Error(t, err)
assert.Contains(t, err.Error(), tc.expectedErr)
} else {
assert.NoError(t, err)
content, err := testutils_test.ReadTestKustomization(fSys)
assert.NoError(t, err)
args := strings.Split(tc.args[0], ",")
for _, opt := range args {
assert.Contains(t, string(content), opt)
}
mf, err := kustfile.NewKustomizationFile(fSys)
assert.NoError(t, err)
m, err := mf.Read()
assert.NoError(t, err)
assert.Equal(t, len(m.BuildMetadata), len(args))
}
}
}

View File

@@ -67,6 +67,7 @@ func determineFieldOrder() []string {
"Inventory",
"Components",
"OpenAPI",
"BuildMetadata",
}
// Add deprecated fields here.

View File

@@ -49,6 +49,7 @@ func TestFieldOrder(t *testing.T) {
"Inventory",
"Components",
"OpenAPI",
"BuildMetadata",
}
actual := determineFieldOrder()
if len(expected) != len(actual) {

View File

@@ -1,3 +1,6 @@
// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package util
import (

View File

@@ -0,0 +1,31 @@
// Copyright 2022 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package util
import (
"errors"
"fmt"
"strings"
"sigs.k8s.io/kustomize/api/types"
"sigs.k8s.io/kustomize/kustomize/v4/commands/internal/kustfile"
)
type BuildMetadataValidator struct{}
func (b *BuildMetadataValidator) Validate(args []string) ([]string, error) {
if len(args) == 0 {
return nil, errors.New("must specify a buildMetadata option")
}
if len(args) > 1 {
return nil, fmt.Errorf("too many arguments: %s; to provide multiple buildMetadata options, please separate options by comma", args)
}
opts := strings.Split(args[0], ",")
for _, opt := range opts {
if !kustfile.StringInSlice(opt, types.BuildMetadataOptions) {
return nil, fmt.Errorf("invalid buildMetadata option: %s", opt)
}
}
return opts, nil
}

View File

@@ -71,7 +71,7 @@ func (p *plugin) Transform(m resmap.ResMap) error {
if p.Prefix != "" {
// TODO: There are multiple transformers that can change a resource's name, and each makes a call to
// StorePreviousID(). We should make it so that we only call StorePreviousID once per kustomization layer
// to avoid storing intermediate names between transformations, to prevent intermediate name conflicts.
// to avoid storing intermediate names between transformations, to prevent intermediate name conflicts.
r.StorePreviousId()
}
}