Files
kustomize/kyaml/order/syncorder_test.go
2022-03-31 11:37:26 -04:00

429 lines
11 KiB
Go

// Copyright 2021 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0
package order
import (
"bytes"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"sigs.k8s.io/kustomize/kyaml/kio"
"sigs.k8s.io/kustomize/kyaml/yaml"
)
func TestSyncOrder(t *testing.T) {
testCases := []struct {
name string
from string
to string
expected string
}{
{
name: "sort data fields configmap with comments",
from: `apiVersion: v1
kind: ConfigMap
metadata:
name: setters-config
data:
# This should be the name of your Config Controller instance
cluster-name: cluster-name
# This should be the project where you deployed Config Controller
project-id: project-id # pro
project-number: "1234567890123"
# You can leave these defaults
namespace: config-control
deployment-repo: deployment-repo
source-repo: source-repo
`,
to: `apiVersion: v1
kind: ConfigMap
metadata: # kpt-merge: /setters-config
name: setters-config
data:
# You can leave these defaults
namespace: config-control
# This should be the name of your Config Controller instance
cluster-name: cluster-name
deployment-repo: deployment-repo
# This should be the project where you deployed Config Controller
project-id: project-id # project
project-number: "1234567890123"
source-repo: source-repo
`,
expected: `apiVersion: v1
kind: ConfigMap
metadata: # kpt-merge: /setters-config
name: setters-config
data:
# This should be the name of your Config Controller instance
cluster-name: cluster-name
# This should be the project where you deployed Config Controller
project-id: project-id # project
project-number: "1234567890123"
# You can leave these defaults
namespace: config-control
deployment-repo: deployment-repo
source-repo: source-repo
`,
},
{
name: "sort data fields configmap but retain order of extra fields",
from: `apiVersion: v1
kind: ConfigMap
data:
baz: bar
cluster-name: cluster-name
foo: config-control
`,
to: `kind: ConfigMap
apiVersion: v1
metadata:
name: foo
data:
color: orange
foo: config-control
abc: def
cluster-name: cluster-name
`,
expected: `apiVersion: v1
kind: ConfigMap
data:
cluster-name: cluster-name
foo: config-control
color: orange
abc: def
metadata:
name: foo
`,
},
{
name: "sort containers list node with sequence head comments preservation",
from: `apiVersion: apps/v1
kind: Deployment
metadata:
name: before
spec:
containers:
- name: nginx
# nginx image
image: "nginx:1.16.1"
ports:
- protocol: TCP # tcp protocol
containerPort: 80
`,
to: `apiVersion: apps/v1
kind: Deployment
metadata:
name: after
spec:
containers:
# ports comment
- ports:
- containerPort: 80
protocol: TCP # tcp protocol
# nginx image
image: "nginx:1.16.2"
# nginx container
name: nginx
# end of resource
`,
expected: `apiVersion: apps/v1
kind: Deployment
metadata:
name: after
spec:
containers:
# ports comment
# nginx container
- name: nginx
# nginx image
image: "nginx:1.16.2"
ports:
- protocol: TCP # tcp protocol
containerPort: 80
# end of resource
`,
},
{
name: "Do not alter sequence order",
from: `apiVersion: v1
kind: KRMFile
metadata:
name: before
pipeline:
mutators:
- image: apply-setters:v0.1
configPath: setters.yaml
- image: set-namespace:v0.1
configPath: ns.yaml
`,
to: `apiVersion: v1
kind: KRMFile
metadata:
name: after
pipeline:
mutators:
- configPath: sr.yaml
image: search-replace:v0.1
- image: apply-setters:v0.1
configPath: setters.yaml
- image: set-namespace:v0.1
configPath: ns.yaml
`,
expected: `apiVersion: v1
kind: KRMFile
metadata:
name: after
pipeline:
mutators:
- image: search-replace:v0.1
configPath: sr.yaml
- image: apply-setters:v0.1
configPath: setters.yaml
- image: set-namespace:v0.1
configPath: ns.yaml
`,
},
{
name: "sort fields with null value",
from: `apiVersion: v1
kind: ConfigMap
metadata:
creationTimestamp: null
name: workspaces.app.terraform.io
`,
to: `apiVersion: v1
kind: ConfigMap
metadata:
name: workspaces.app.terraform.io
creationTimestamp: null
`,
expected: `apiVersion: v1
kind: ConfigMap
metadata:
creationTimestamp: null
name: workspaces.app.terraform.io
`,
},
{
name: "Complex ASM reorder example",
from: `apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
annotations:
controller-gen.kubebuilder.io/version: (unknown)
creationTimestamp: null
name: controlplanerevisions.mesh.cloud.google.com
spec:
group: mesh.cloud.google.com
names:
kind: ControlPlaneRevision
listKind: ControlPlaneRevisionList
plural: controlplanerevisions
singular: controlplanerevision
scope: Namespaced
subresources:
status: {}
validation:
openAPIV3Schema:
description: ControlPlaneRevision is the Schema for the ControlPlaneRevision API
properties:
apiVersion:
description: 'APIVersion'
type: string
kind:
description: 'Kind'
type: string
metadata:
type: object
spec:
description: ControlPlaneRevisionSpec defines the desired state of ControlPlaneRevision
properties:
channel:
description: ReleaseChannel determines the aggressiveness of upgrades.
enum:
- regular
- rapid
- stable
type: string
type:
description: ControlPlaneRevisionType determines how the revision should be managed.
enum:
- managed_service
type: string
type: object
status:
description: ControlPlaneRevisionStatus defines the observed state of ControlPlaneRevision.
properties:
conditions:
items:
description: ControlPlaneRevisionCondition is a repeated struct defining the current conditions of a ControlPlaneRevision.
properties:
lastTransitionTime:
description: Last time the condition transitioned from one status to another
format: date-time
type: string
message:
description: Human-readable message indicating details about last transition
type: string
reason:
description: Unique, one-word, CamelCase reason for the condition's last transition
type: string
status:
description: Status is the status of the condition. Can be True, False, or Unknown.
type: string
type:
description: Type is the type of the condition.
type: string
type: object
type: array
type: object
type: object
version: v1alpha1
versions:
- name: v1alpha1
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []
`,
to: `apiVersion: apiextensions.k8s.io/v1beta1
kind: CustomResourceDefinition
metadata:
name: controlplanerevisions.mesh.cloud.google.com
annotations:
controller-gen.kubebuilder.io/version: (unknown)
creationTimestamp: null
spec:
group: mesh.cloud.google.com
names:
kind: ControlPlaneRevision
listKind: ControlPlaneRevisionList
plural: controlplanerevisions
singular: controlplanerevision
scope: Namespaced
subresources:
status: {}
validation:
openAPIV3Schema:
type: object
description: ControlPlaneRevision is the Schema for the ControlPlaneRevision API
properties:
apiVersion:
type: string
description: 'APIVersion'
kind:
type: string
description: 'Kind'
metadata:
type: object
spec:
type: object
description: ControlPlaneRevisionSpec defines the desired state of ControlPlaneRevision
properties:
type:
type: string
description: ControlPlaneRevisionType determines how the revision should be managed.
enum:
- managed_service
channel:
type: string
description: ReleaseChannel determines the aggressiveness of upgrades.
enum:
- regular
- rapid
- stable
status:
type: object
description: ControlPlaneRevisionStatus defines the observed state of ControlPlaneRevision.
properties:
conditions:
type: array
items:
type: object
description: ControlPlaneRevisionCondition is a repeated struct defining the current conditions of a ControlPlaneRevision.
properties:
type:
type: string
description: Type is the type of the condition.
status:
type: string
description: Status is the status of the condition. Can be True, False, or Unknown.
lastTransitionTime:
type: string
description: Last time the condition transitioned from one status to another
format: date-time
message:
type: string
description: Human-readable message indicating details about last transition
reason:
type: string
description: Unique, one-word, CamelCase reason for the condition's last transition
version: v1alpha1
versions:
- name: v1alpha1
served: true
storage: true
status:
acceptedNames:
kind: ""
plural: ""
conditions: []
storedVersions: []
`,
expected: `test.from`,
},
}
for i := range testCases {
tc := testCases[i]
t.Run(tc.name, func(t *testing.T) {
from, err := yaml.Parse(tc.from)
if !assert.NoError(t, err) {
t.FailNow()
}
to, err := yaml.Parse(tc.to)
if !assert.NoError(t, err) {
t.FailNow()
}
err = SyncOrder(from, to)
if !assert.NoError(t, err) {
t.FailNow()
}
out := &bytes.Buffer{}
kio.ByteWriter{
Writer: out,
KeepReaderAnnotations: false,
}.Write([]*yaml.RNode{to})
// this means "to" is just a reordered version of "from" and after syncing order,
// resultant "to" must be equal to "from"
if tc.expected == "test.from" {
tc.expected = tc.from
}
if !assert.Equal(t, tc.expected, out.String()) {
t.FailNow()
}
actualFrom, err := from.String()
if !assert.NoError(t, err) {
t.FailNow()
}
if !assert.Equal(t, strings.TrimSpace(tc.from), strings.TrimSpace(actualFrom)) {
t.FailNow()
}
})
}
}