...

Source file src/sigs.k8s.io/kustomize/kyaml/order/syncorder_test.go

Documentation: sigs.k8s.io/kustomize/kyaml/order

     1  // Copyright 2021 The Kubernetes Authors.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package order
     5  
     6  import (
     7  	"bytes"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/stretchr/testify/assert"
    12  	"sigs.k8s.io/kustomize/kyaml/kio"
    13  	"sigs.k8s.io/kustomize/kyaml/yaml"
    14  )
    15  
    16  func TestSyncOrder(t *testing.T) {
    17  	testCases := []struct {
    18  		name     string
    19  		from     string
    20  		to       string
    21  		expected string
    22  	}{
    23  		{
    24  			name: "sort data fields configmap with comments",
    25  			from: `apiVersion: v1
    26  kind: ConfigMap
    27  metadata:
    28    name: setters-config
    29  data:
    30    # This should be the name of your Config Controller instance
    31    cluster-name: cluster-name
    32    # This should be the project where you deployed Config Controller
    33    project-id: project-id # pro
    34    project-number: "1234567890123"
    35    # You can leave these defaults
    36    namespace: config-control
    37    deployment-repo: deployment-repo
    38    source-repo: source-repo
    39  `,
    40  			to: `apiVersion: v1
    41  kind: ConfigMap
    42  metadata: # kpt-merge: /setters-config
    43    name: setters-config
    44  data:
    45    # You can leave these defaults
    46    namespace: config-control
    47    # This should be the name of your Config Controller instance
    48    cluster-name: cluster-name
    49    deployment-repo: deployment-repo
    50    # This should be the project where you deployed Config Controller
    51    project-id: project-id # project
    52    project-number: "1234567890123"
    53    source-repo: source-repo
    54  `,
    55  			expected: `apiVersion: v1
    56  kind: ConfigMap
    57  metadata: # kpt-merge: /setters-config
    58    name: setters-config
    59  data:
    60    # This should be the name of your Config Controller instance
    61    cluster-name: cluster-name
    62    # This should be the project where you deployed Config Controller
    63    project-id: project-id # project
    64    project-number: "1234567890123"
    65    # You can leave these defaults
    66    namespace: config-control
    67    deployment-repo: deployment-repo
    68    source-repo: source-repo
    69  `,
    70  		},
    71  		{
    72  			name: "sort data fields configmap but retain order of extra fields",
    73  			from: `apiVersion: v1
    74  kind: ConfigMap
    75  data:
    76    baz: bar
    77    cluster-name: cluster-name
    78    foo: config-control
    79  `,
    80  			to: `kind: ConfigMap
    81  apiVersion: v1
    82  metadata:
    83    name: foo
    84  data:
    85    color: orange
    86    foo: config-control
    87    abc: def
    88    cluster-name: cluster-name
    89  `,
    90  			expected: `apiVersion: v1
    91  kind: ConfigMap
    92  data:
    93    cluster-name: cluster-name
    94    foo: config-control
    95    color: orange
    96    abc: def
    97  metadata:
    98    name: foo
    99  `,
   100  		},
   101  		{
   102  			name: "sort containers list node with sequence head comments preservation",
   103  			from: `apiVersion: apps/v1
   104  kind: Deployment
   105  metadata:
   106    name: before
   107  spec:
   108    containers:
   109    - name: nginx
   110      # nginx image
   111      image: "nginx:1.16.1"
   112      ports:
   113      - protocol: TCP # tcp protocol
   114        containerPort: 80
   115  `,
   116  			to: `apiVersion: apps/v1
   117  kind: Deployment
   118  metadata:
   119    name: after
   120  spec:
   121    containers:
   122    # ports comment
   123    - ports:
   124      - containerPort: 80
   125        protocol: TCP # tcp protocol
   126      # nginx image
   127      image: "nginx:1.16.2"
   128      # nginx container
   129      name: nginx
   130  # end of resource
   131  `,
   132  			expected: `apiVersion: apps/v1
   133  kind: Deployment
   134  metadata:
   135    name: after
   136  spec:
   137    containers:
   138    # ports comment
   139    # nginx container
   140    - name: nginx
   141      # nginx image
   142      image: "nginx:1.16.2"
   143      ports:
   144      - protocol: TCP # tcp protocol
   145        containerPort: 80
   146  # end of resource
   147  `,
   148  		},
   149  		{
   150  			name: "Do not alter sequence order",
   151  			from: `apiVersion: v1
   152  kind: KRMFile
   153  metadata:
   154    name: before
   155  pipeline:
   156    mutators:
   157    - image: apply-setters:v0.1
   158      configPath: setters.yaml
   159    - image: set-namespace:v0.1
   160      configPath: ns.yaml
   161  `,
   162  			to: `apiVersion: v1
   163  kind: KRMFile
   164  metadata:
   165    name: after
   166  pipeline:
   167    mutators:
   168    - configPath: sr.yaml
   169      image: search-replace:v0.1
   170    - image: apply-setters:v0.1
   171      configPath: setters.yaml
   172    - image: set-namespace:v0.1
   173      configPath: ns.yaml
   174  `,
   175  			expected: `apiVersion: v1
   176  kind: KRMFile
   177  metadata:
   178    name: after
   179  pipeline:
   180    mutators:
   181    - image: search-replace:v0.1
   182      configPath: sr.yaml
   183    - image: apply-setters:v0.1
   184      configPath: setters.yaml
   185    - image: set-namespace:v0.1
   186      configPath: ns.yaml
   187  `,
   188  		},
   189  		{
   190  			name: "sort fields with null value",
   191  			from: `apiVersion: v1
   192  kind: ConfigMap
   193  metadata:
   194    creationTimestamp: null
   195    name: workspaces.app.terraform.io
   196  `,
   197  			to: `apiVersion: v1
   198  kind: ConfigMap
   199  metadata:
   200    name: workspaces.app.terraform.io
   201    creationTimestamp: null
   202  `,
   203  			expected: `apiVersion: v1
   204  kind: ConfigMap
   205  metadata:
   206    creationTimestamp: null
   207    name: workspaces.app.terraform.io
   208  `,
   209  		},
   210  		{
   211  			name: "Complex ASM reorder example",
   212  			from: `apiVersion: apiextensions.k8s.io/v1beta1
   213  kind: CustomResourceDefinition
   214  metadata:
   215    annotations:
   216      controller-gen.kubebuilder.io/version: (unknown)
   217    creationTimestamp: null
   218    name: controlplanerevisions.mesh.cloud.google.com
   219  spec:
   220    group: mesh.cloud.google.com
   221    names:
   222      kind: ControlPlaneRevision
   223      listKind: ControlPlaneRevisionList
   224      plural: controlplanerevisions
   225      singular: controlplanerevision
   226    scope: Namespaced
   227    subresources:
   228      status: {}
   229    validation:
   230      openAPIV3Schema:
   231        description: ControlPlaneRevision is the Schema for the ControlPlaneRevision API
   232        properties:
   233          apiVersion:
   234            description: 'APIVersion'
   235            type: string
   236          kind:
   237            description: 'Kind'
   238            type: string
   239          metadata:
   240            type: object
   241          spec:
   242            description: ControlPlaneRevisionSpec defines the desired state of ControlPlaneRevision
   243            properties:
   244              channel:
   245                description: ReleaseChannel determines the aggressiveness of upgrades.
   246                enum:
   247                - regular
   248                - rapid
   249                - stable
   250                type: string
   251              type:
   252                description: ControlPlaneRevisionType determines how the revision should be managed.
   253                enum:
   254                - managed_service
   255                type: string
   256            type: object
   257          status:
   258            description: ControlPlaneRevisionStatus defines the observed state of ControlPlaneRevision.
   259            properties:
   260              conditions:
   261                items:
   262                  description: ControlPlaneRevisionCondition is a repeated struct defining the current conditions of a ControlPlaneRevision.
   263                  properties:
   264                    lastTransitionTime:
   265                      description: Last time the condition transitioned from one status to another
   266                      format: date-time
   267                      type: string
   268                    message:
   269                      description: Human-readable message indicating details about last transition
   270                      type: string
   271                    reason:
   272                      description: Unique, one-word, CamelCase reason for the condition's last transition
   273                      type: string
   274                    status:
   275                      description: Status is the status of the condition. Can be True, False, or Unknown.
   276                      type: string
   277                    type:
   278                      description: Type is the type of the condition.
   279                      type: string
   280                  type: object
   281                type: array
   282            type: object
   283        type: object
   284    version: v1alpha1
   285    versions:
   286    - name: v1alpha1
   287      served: true
   288      storage: true
   289  status:
   290    acceptedNames:
   291      kind: ""
   292      plural: ""
   293    conditions: []
   294    storedVersions: []
   295  `,
   296  			to: `apiVersion: apiextensions.k8s.io/v1beta1
   297  kind: CustomResourceDefinition
   298  metadata:
   299    name: controlplanerevisions.mesh.cloud.google.com
   300    annotations:
   301      controller-gen.kubebuilder.io/version: (unknown)
   302    creationTimestamp: null
   303  spec:
   304    group: mesh.cloud.google.com
   305    names:
   306      kind: ControlPlaneRevision
   307      listKind: ControlPlaneRevisionList
   308      plural: controlplanerevisions
   309      singular: controlplanerevision
   310    scope: Namespaced
   311    subresources:
   312      status: {}
   313    validation:
   314      openAPIV3Schema:
   315        type: object
   316        description: ControlPlaneRevision is the Schema for the ControlPlaneRevision API
   317        properties:
   318          apiVersion:
   319            type: string
   320            description: 'APIVersion'
   321          kind:
   322            type: string
   323            description: 'Kind'
   324          metadata:
   325            type: object
   326          spec:
   327            type: object
   328            description: ControlPlaneRevisionSpec defines the desired state of ControlPlaneRevision
   329            properties:
   330              type:
   331                type: string
   332                description: ControlPlaneRevisionType determines how the revision should be managed.
   333                enum:
   334                  - managed_service
   335              channel:
   336                type: string
   337                description: ReleaseChannel determines the aggressiveness of upgrades.
   338                enum:
   339                  - regular
   340                  - rapid
   341                  - stable
   342          status:
   343            type: object
   344            description: ControlPlaneRevisionStatus defines the observed state of ControlPlaneRevision.
   345            properties:
   346              conditions:
   347                type: array
   348                items:
   349                  type: object
   350                  description: ControlPlaneRevisionCondition is a repeated struct defining the current conditions of a ControlPlaneRevision.
   351                  properties:
   352                    type:
   353                      type: string
   354                      description: Type is the type of the condition.
   355                    status:
   356                      type: string
   357                      description: Status is the status of the condition. Can be True, False, or Unknown.
   358                    lastTransitionTime:
   359                      type: string
   360                      description: Last time the condition transitioned from one status to another
   361                      format: date-time
   362                    message:
   363                      type: string
   364                      description: Human-readable message indicating details about last transition
   365                    reason:
   366                      type: string
   367                      description: Unique, one-word, CamelCase reason for the condition's last transition
   368    version: v1alpha1
   369    versions:
   370      - name: v1alpha1
   371        served: true
   372        storage: true
   373  status:
   374    acceptedNames:
   375      kind: ""
   376      plural: ""
   377    conditions: []
   378    storedVersions: []
   379  `,
   380  			expected: `test.from`,
   381  		},
   382  	}
   383  
   384  	for i := range testCases {
   385  		tc := testCases[i]
   386  		t.Run(tc.name, func(t *testing.T) {
   387  			from, err := yaml.Parse(tc.from)
   388  			if !assert.NoError(t, err) {
   389  				t.FailNow()
   390  			}
   391  
   392  			to, err := yaml.Parse(tc.to)
   393  			if !assert.NoError(t, err) {
   394  				t.FailNow()
   395  			}
   396  
   397  			err = SyncOrder(from, to)
   398  			if !assert.NoError(t, err) {
   399  				t.FailNow()
   400  			}
   401  
   402  			out := &bytes.Buffer{}
   403  			kio.ByteWriter{
   404  				Writer:                out,
   405  				KeepReaderAnnotations: false,
   406  			}.Write([]*yaml.RNode{to})
   407  
   408  			// this means "to" is just a reordered version of "from" and after syncing order,
   409  			// resultant "to" must be equal to "from"
   410  			if tc.expected == "test.from" {
   411  				tc.expected = tc.from
   412  			}
   413  
   414  			if !assert.Equal(t, tc.expected, out.String()) {
   415  				t.FailNow()
   416  			}
   417  
   418  			actualFrom, err := from.String()
   419  			if !assert.NoError(t, err) {
   420  				t.FailNow()
   421  			}
   422  
   423  			if !assert.Equal(t, strings.TrimSpace(tc.from), strings.TrimSpace(actualFrom)) {
   424  				t.FailNow()
   425  			}
   426  		})
   427  	}
   428  }
   429  

View as plain text