mirror of
https://github.com/kubernetes-sigs/kustomize.git
synced 2026-06-10 08:20:59 +00:00
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:
@@ -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) {
|
||||
|
||||
@@ -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"`
|
||||
|
||||
69
kustomize/commands/edit/add/addbuildmetadata.go
Normal file
69
kustomize/commands/edit/add/addbuildmetadata.go
Normal 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)
|
||||
}
|
||||
63
kustomize/commands/edit/add/addbuildmetadata_test.go
Normal file
63
kustomize/commands/edit/add/addbuildmetadata_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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),
|
||||
|
||||
@@ -42,6 +42,7 @@ func NewCmdRemove(
|
||||
newCmdRemoveAnnotation(fSys, v.MakeAnnotationNameValidator()),
|
||||
newCmdRemovePatch(fSys),
|
||||
newCmdRemoveTransformer(fSys),
|
||||
newCmdRemoveBuildMetadata(fSys),
|
||||
)
|
||||
return c
|
||||
}
|
||||
|
||||
71
kustomize/commands/edit/remove/removebuildmetadata.go
Normal file
71
kustomize/commands/edit/remove/removebuildmetadata.go
Normal 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)
|
||||
}
|
||||
65
kustomize/commands/edit/remove/removebuildmetadata_test.go
Normal file
65
kustomize/commands/edit/remove/removebuildmetadata_test.go
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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()),
|
||||
|
||||
63
kustomize/commands/edit/set/setbuildmetadata.go
Normal file
63
kustomize/commands/edit/set/setbuildmetadata.go
Normal 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)
|
||||
}
|
||||
76
kustomize/commands/edit/set/setbuildmetadata_test.go
Normal file
76
kustomize/commands/edit/set/setbuildmetadata_test.go
Normal 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))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -67,6 +67,7 @@ func determineFieldOrder() []string {
|
||||
"Inventory",
|
||||
"Components",
|
||||
"OpenAPI",
|
||||
"BuildMetadata",
|
||||
}
|
||||
|
||||
// Add deprecated fields here.
|
||||
|
||||
@@ -49,6 +49,7 @@ func TestFieldOrder(t *testing.T) {
|
||||
"Inventory",
|
||||
"Components",
|
||||
"OpenAPI",
|
||||
"BuildMetadata",
|
||||
}
|
||||
actual := determineFieldOrder()
|
||||
if len(expected) != len(actual) {
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
// Copyright 2019 The Kubernetes Authors.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package util
|
||||
|
||||
import (
|
||||
|
||||
31
kustomize/commands/internal/util/validate.go
Normal file
31
kustomize/commands/internal/util/validate.go
Normal 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
|
||||
}
|
||||
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user