...

Source file src/sigs.k8s.io/kustomize/api/krusty/multiplepatch_test.go

Documentation: sigs.k8s.io/kustomize/api/krusty

     1  // Copyright 2019 The Kubernetes Authors.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package krusty_test
     5  
     6  import (
     7  	"testing"
     8  
     9  	kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
    10  )
    11  
    12  func TestPatchesInOneFile(t *testing.T) {
    13  	th := kusttest_test.MakeHarness(t)
    14  	th.WriteK("base", `
    15  resources:
    16  - namespace.yaml
    17  - deployment-controller-manager.yaml
    18  - deployment-audit-manager.yaml
    19  `)
    20  	th.WriteF("base/namespace.yaml", `
    21  apiVersion: v1
    22  kind: Namespace
    23  metadata:
    24    labels:
    25      control-plane: controller-manager
    26      admission.gatekeeper.sh/ignore: no-self-managing
    27    name: system
    28  `)
    29  	th.WriteF("base/deployment-controller-manager.yaml", `
    30  apiVersion: apps/v1
    31  kind: Deployment
    32  metadata:
    33    name: controller-manager
    34    namespace: system
    35    labels:
    36      control-plane: controller-manager
    37      gatekeeper.sh/operation: webhook
    38  spec:
    39    selector:
    40      matchLabels:
    41        control-plane: controller-manager
    42        gatekeeper.sh/operation: webhook
    43    replicas: 3
    44    template:
    45      metadata:
    46        annotations:
    47          container.seccomp.security.alpha.kubernetes.io/manager: runtime/default
    48        labels:
    49          control-plane: controller-manager
    50          gatekeeper.sh/operation: webhook
    51      spec:
    52        containers:
    53          - command:
    54              - /manager
    55            args:
    56              - "--port=8443"
    57              - "--logtostderr"
    58              - "--exempt-namespace=gatekeeper-system"
    59              - "--operation=webhook"
    60            image: openpolicyagent/gatekeeper:v3.4.0
    61            imagePullPolicy: Always
    62            name: manager
    63        terminationGracePeriodSeconds: 60
    64        nodeSelector:
    65          kubernetes.io/os: linux
    66        priorityClassName: system-cluster-critical
    67  `)
    68  	th.WriteF("base/deployment-audit-manager.yaml", `
    69  apiVersion: apps/v1
    70  kind: Deployment
    71  metadata:
    72    name: audit
    73    namespace: system
    74    labels:
    75      control-plane: audit-controller
    76      gatekeeper.sh/operation: audit
    77  spec:
    78    selector:
    79      matchLabels:
    80        control-plane: audit-controller
    81        gatekeeper.sh/operation: audit
    82    replicas: 1
    83    template:
    84      metadata:
    85        labels:
    86          control-plane: audit-controller
    87          gatekeeper.sh/operation: audit
    88        annotations:
    89          container.seccomp.security.alpha.kubernetes.io/manager: runtime/default
    90      spec:
    91        automountServiceAccountToken: true
    92        containers:
    93          - args:
    94              - --operation=audit
    95              - --operation=status
    96              - --logtostderr
    97            command:
    98              - /manager
    99            env:
   100              - name: POD_NAMESPACE
   101                valueFrom:
   102                  fieldRef:
   103                    apiVersion: v1
   104                    fieldPath: metadata.namespace
   105              - name: POD_NAME
   106                valueFrom:
   107                  fieldRef:
   108                    fieldPath: metadata.name
   109            image: openpolicyagent/gatekeeper:v3.4.0
   110            imagePullPolicy: Always
   111            livenessProbe:
   112              httpGet:
   113                path: /healthz
   114                port: 9090
   115            name: manager
   116        serviceAccountName: gatekeeper-admin
   117        terminationGracePeriodSeconds: 60
   118        nodeSelector:
   119          kubernetes.io/os: linux
   120        priorityClassName: system-cluster-critical
   121  `)
   122  	const imagePatchAuditManager = `
   123  apiVersion: apps/v1
   124  kind: Deployment
   125  metadata:
   126    name: audit
   127    namespace: system
   128  spec:
   129    template:
   130      spec:
   131        containers:
   132        - image: AUDIT_IMAGE
   133          name: manager
   134          args:
   135          - --port=8443
   136          - --logtostderr
   137          - --emit-admission-events
   138          - --exempt-namespace=gatekeeper-system
   139          - --operation=webhook
   140          - --disable-opa-builtin=http.send
   141  `
   142  	const imagePatchControllerManager = `
   143  apiVersion: apps/v1
   144  kind: Deployment
   145  metadata:
   146    name: controller-manager
   147    namespace: system
   148  spec:
   149    template:
   150      spec:
   151        containers:
   152        - image: CONTROLLER_IMAGE
   153          name: manager
   154          args:
   155          - --emit-audit-events
   156          - --operation=audit
   157          - --operation=status
   158          - --logtostderr
   159  `
   160  	th.WriteF(
   161  		"overlay/image_patch_audit_manager.yaml",
   162  		imagePatchAuditManager)
   163  	th.WriteF(
   164  		"overlay/image_patch_controller_manager.yaml",
   165  		imagePatchControllerManager)
   166  	const expected = `
   167  apiVersion: v1
   168  kind: Namespace
   169  metadata:
   170    labels:
   171      admission.gatekeeper.sh/ignore: no-self-managing
   172      control-plane: controller-manager
   173    name: system
   174  ---
   175  apiVersion: apps/v1
   176  kind: Deployment
   177  metadata:
   178    labels:
   179      control-plane: controller-manager
   180      gatekeeper.sh/operation: webhook
   181    name: controller-manager
   182    namespace: system
   183  spec:
   184    replicas: 3
   185    selector:
   186      matchLabels:
   187        control-plane: controller-manager
   188        gatekeeper.sh/operation: webhook
   189    template:
   190      metadata:
   191        annotations:
   192          container.seccomp.security.alpha.kubernetes.io/manager: runtime/default
   193        labels:
   194          control-plane: controller-manager
   195          gatekeeper.sh/operation: webhook
   196      spec:
   197        containers:
   198        - args:
   199          - --emit-audit-events
   200          - --operation=audit
   201          - --operation=status
   202          - --logtostderr
   203          command:
   204          - /manager
   205          image: CONTROLLER_IMAGE
   206          imagePullPolicy: Always
   207          name: manager
   208        nodeSelector:
   209          kubernetes.io/os: linux
   210        priorityClassName: system-cluster-critical
   211        terminationGracePeriodSeconds: 60
   212  ---
   213  apiVersion: apps/v1
   214  kind: Deployment
   215  metadata:
   216    labels:
   217      control-plane: audit-controller
   218      gatekeeper.sh/operation: audit
   219    name: audit
   220    namespace: system
   221  spec:
   222    replicas: 1
   223    selector:
   224      matchLabels:
   225        control-plane: audit-controller
   226        gatekeeper.sh/operation: audit
   227    template:
   228      metadata:
   229        annotations:
   230          container.seccomp.security.alpha.kubernetes.io/manager: runtime/default
   231        labels:
   232          control-plane: audit-controller
   233          gatekeeper.sh/operation: audit
   234      spec:
   235        automountServiceAccountToken: true
   236        containers:
   237        - args:
   238          - --port=8443
   239          - --logtostderr
   240          - --emit-admission-events
   241          - --exempt-namespace=gatekeeper-system
   242          - --operation=webhook
   243          - --disable-opa-builtin=http.send
   244          command:
   245          - /manager
   246          env:
   247          - name: POD_NAMESPACE
   248            valueFrom:
   249              fieldRef:
   250                apiVersion: v1
   251                fieldPath: metadata.namespace
   252          - name: POD_NAME
   253            valueFrom:
   254              fieldRef:
   255                fieldPath: metadata.name
   256          image: AUDIT_IMAGE
   257          imagePullPolicy: Always
   258          livenessProbe:
   259            httpGet:
   260              path: /healthz
   261              port: 9090
   262          name: manager
   263        nodeSelector:
   264          kubernetes.io/os: linux
   265        priorityClassName: system-cluster-critical
   266        serviceAccountName: gatekeeper-admin
   267        terminationGracePeriodSeconds: 60
   268  `
   269  	// Technique 1: "patchesStrategicMerge:" field, two patch files.
   270  	th.WriteK("overlay", `
   271  resources:
   272  - ../base
   273  patchesStrategicMerge:
   274  - image_patch_controller_manager.yaml
   275  - image_patch_audit_manager.yaml
   276  `)
   277  	m := th.Run("overlay", th.MakeDefaultOptions())
   278  	th.AssertActualEqualsExpected(m, expected)
   279  
   280  	// Technique 2: "patches:" field, two patch files.
   281  	th.WriteK("overlay", `
   282  resources:
   283  - ../base
   284  patches:
   285  - path: image_patch_controller_manager.yaml
   286  - path: image_patch_audit_manager.yaml
   287  `)
   288  	m = th.Run("overlay", th.MakeDefaultOptions())
   289  	th.AssertActualEqualsExpected(m, expected)
   290  
   291  	// Technique 3: "patchesStrategicMerge:" field, one patch file.
   292  	th.WriteK("overlay", `
   293  resources:
   294  - ../base
   295  patchesStrategicMerge:
   296  - twoPatchesInOneFile.yaml
   297  `)
   298  	th.WriteF(
   299  		"overlay/twoPatchesInOneFile.yaml",
   300  		imagePatchAuditManager+"\n---\n"+imagePatchControllerManager)
   301  	m = th.Run("overlay", th.MakeDefaultOptions())
   302  	th.AssertActualEqualsExpected(m, expected)
   303  
   304  	// Technique 4: "patches:" field, one patch file.
   305  	th.WriteK("overlay", `
   306  resources:
   307  - ../base
   308  patches:
   309  - path: twoPatchesInOneFile.yaml
   310  `)
   311  	m = th.Run("overlay", th.MakeDefaultOptions())
   312  	th.AssertActualEqualsExpected(m, expected)
   313  }
   314  
   315  func TestRemoveEmptyDirWithNullFieldInSmp(t *testing.T) {
   316  	th := kusttest_test.MakeHarness(t)
   317  	th.WriteK(".", `
   318  resources:
   319  - deployment.yaml
   320  patchesStrategicMerge:
   321  - patch.yaml
   322  `)
   323  	th.WriteF("deployment.yaml", `
   324  apiVersion: apps/v1
   325  kind: Deployment
   326  metadata:
   327    name: nginx
   328  spec:
   329    template:
   330      spec:
   331        volumes:
   332        - name: fancyDisk
   333          emptyDir: {}
   334  `)
   335  	th.WriteF("patch.yaml", `
   336  apiVersion: apps/v1
   337  kind: Deployment
   338  metadata:
   339    name: nginx
   340  spec:
   341    template:
   342      spec:
   343        volumes:
   344        - name: fancyDisk
   345          emptyDir: null
   346  `)
   347  	m := th.Run(".", th.MakeDefaultOptions())
   348  	th.AssertActualEqualsExpected(m, `
   349  apiVersion: apps/v1
   350  kind: Deployment
   351  metadata:
   352    name: nginx
   353  spec:
   354    template:
   355      spec:
   356        volumes:
   357        - name: fancyDisk
   358  `)
   359  }
   360  
   361  func TestRemoveEmptyDirAddPersistentDisk(t *testing.T) {
   362  	th := kusttest_test.MakeHarness(t)
   363  	th.WriteK(".", `
   364  resources:
   365  - deployment.yaml
   366  patchesStrategicMerge:
   367  - patch.yaml
   368  `)
   369  	th.WriteF("deployment.yaml", `
   370  apiVersion: apps/v1
   371  kind: Deployment
   372  metadata:
   373    name: nginx
   374  spec:
   375    template:
   376      spec:
   377        volumes:
   378        - name: fancyDisk
   379          emptyDir: {}
   380  `)
   381  	th.WriteF("patch.yaml", `
   382  apiVersion: apps/v1
   383  kind: Deployment
   384  metadata:
   385    name: nginx
   386  spec:
   387    template:
   388      spec:
   389        volumes:
   390        - name: fancyDisk
   391          emptyDir: null
   392          gcePersistentDisk:
   393            pdName: fancyDisk
   394  `)
   395  	m := th.Run(".", th.MakeDefaultOptions())
   396  	th.AssertActualEqualsExpected(m, `
   397  apiVersion: apps/v1
   398  kind: Deployment
   399  metadata:
   400    name: nginx
   401  spec:
   402    template:
   403      spec:
   404        volumes:
   405        - gcePersistentDisk:
   406            pdName: fancyDisk
   407          name: fancyDisk
   408  `)
   409  }
   410  
   411  func TestVolumeRemoveEmptyDirInOverlay(t *testing.T) {
   412  	th := kusttest_test.MakeHarness(t)
   413  	th.WriteK("base", `
   414  resources:
   415  - deployment.yaml
   416  configMapGenerator:
   417  - name: baseCm
   418    literals:
   419    - foo=bar
   420  `)
   421  	th.WriteF("base/deployment.yaml", `
   422  apiVersion: apps/v1
   423  kind: Deployment
   424  metadata:
   425    name: nginx
   426  spec:
   427    template:
   428      spec:
   429        containers:
   430        - name: nginx
   431          image: nginx
   432          volumeMounts:
   433          - name: fancyDisk
   434            mountPath: /tmp/ps
   435        volumes:
   436        - name: fancyDisk
   437          emptyDir: {}
   438        - configMap:
   439            name: baseCm
   440          name: baseCm
   441  `)
   442  	m := th.Run("base", th.MakeDefaultOptions())
   443  	th.AssertActualEqualsExpected(m, `
   444  apiVersion: apps/v1
   445  kind: Deployment
   446  metadata:
   447    name: nginx
   448  spec:
   449    template:
   450      spec:
   451        containers:
   452        - image: nginx
   453          name: nginx
   454          volumeMounts:
   455          - mountPath: /tmp/ps
   456            name: fancyDisk
   457        volumes:
   458        - emptyDir: {}
   459          name: fancyDisk
   460        - configMap:
   461            name: baseCm-798k5k7g9f
   462          name: baseCm
   463  ---
   464  apiVersion: v1
   465  data:
   466    foo: bar
   467  kind: ConfigMap
   468  metadata:
   469    name: baseCm-798k5k7g9f
   470  `)
   471  
   472  	th.WriteK("overlay", `
   473  patchesStrategicMerge:
   474  - patch.yaml
   475  resources:
   476  - ../base
   477  configMapGenerator:
   478  - name: overlayCm
   479    literals:
   480    - hello=world
   481  `)
   482  	th.WriteF("overlay/patch.yaml", `
   483  apiVersion: apps/v1
   484  kind: Deployment
   485  metadata:
   486    name: nginx
   487  spec:
   488    template:
   489      spec:
   490        volumes:
   491        - name: fancyDisk
   492          emptyDir: null
   493          gcePersistentDisk:
   494            pdName: fancyDisk
   495        - configMap:
   496            name: overlayCm
   497          name: overlayCm
   498  `)
   499  	m = th.Run("overlay", th.MakeDefaultOptions())
   500  	th.AssertActualEqualsExpected(m, `
   501  apiVersion: apps/v1
   502  kind: Deployment
   503  metadata:
   504    name: nginx
   505  spec:
   506    template:
   507      spec:
   508        containers:
   509        - image: nginx
   510          name: nginx
   511          volumeMounts:
   512          - mountPath: /tmp/ps
   513            name: fancyDisk
   514        volumes:
   515        - gcePersistentDisk:
   516            pdName: fancyDisk
   517          name: fancyDisk
   518        - configMap:
   519            name: overlayCm-dc6fm46dhm
   520          name: overlayCm
   521        - configMap:
   522            name: baseCm-798k5k7g9f
   523          name: baseCm
   524  ---
   525  apiVersion: v1
   526  data:
   527    foo: bar
   528  kind: ConfigMap
   529  metadata:
   530    name: baseCm-798k5k7g9f
   531  ---
   532  apiVersion: v1
   533  data:
   534    hello: world
   535  kind: ConfigMap
   536  metadata:
   537    name: overlayCm-dc6fm46dhm
   538  `)
   539  }
   540  
   541  // Goal is to remove "  emptyDir: {}" with a patch.
   542  func TestRemoveEmptyDirWithPatchesAtSameLevel(t *testing.T) {
   543  	th := kusttest_test.MakeHarness(t)
   544  	th.WriteK("base", `
   545  resources:
   546  - deployment.yaml
   547  `)
   548  	th.WriteF("base/deployment.yaml", `
   549  apiVersion: apps/v1
   550  kind: Deployment
   551  metadata:
   552    name: nginx
   553  spec:
   554    template:
   555      spec:
   556        containers:
   557        - name: nginx
   558          image: nginx
   559        - name: sidecar
   560          image: sidecar:latest
   561        volumes:
   562        - name: nginx-persistent-storage
   563          emptyDir: {}
   564  `)
   565  	th.WriteK("overlay", `
   566  patchesStrategicMerge:
   567  - deployment-patch1.yaml
   568  - deployment-patch2.yaml
   569  resources:
   570  - ../base
   571  `)
   572  	th.WriteF("overlay/deployment-patch1.yaml", `
   573  apiVersion: apps/v1
   574  kind: Deployment
   575  metadata:
   576    name: nginx
   577  spec:
   578    template:
   579      spec:
   580        volumes:
   581        - name: nginx-persistent-storage
   582          emptyDir: null
   583          gcePersistentDisk:
   584            pdName: nginx-persistent-storage
   585  `)
   586  	th.WriteF("overlay/deployment-patch2.yaml", `
   587  apiVersion: apps/v1
   588  kind: Deployment
   589  metadata:
   590    name: nginx
   591  spec:
   592    template:
   593      spec:
   594        containers:
   595        - image: nginx
   596          name: nginx
   597          env:
   598          - name: ANOTHERENV
   599            value: FOO
   600        volumes:
   601        - name: nginx-persistent-storage
   602  `)
   603  	opts := th.MakeDefaultOptions()
   604  	m := th.Run("overlay", opts)
   605  	th.AssertActualEqualsExpected(
   606  		m, `
   607  apiVersion: apps/v1
   608  kind: Deployment
   609  metadata:
   610    name: nginx
   611  spec:
   612    template:
   613      spec:
   614        containers:
   615        - env:
   616          - name: ANOTHERENV
   617            value: FOO
   618          image: nginx
   619          name: nginx
   620        - image: sidecar:latest
   621          name: sidecar
   622        volumes:
   623        - gcePersistentDisk:
   624            pdName: nginx-persistent-storage
   625          name: nginx-persistent-storage
   626  `)
   627  }
   628  
   629  func TestSimpleMultiplePatches(t *testing.T) {
   630  	th := kusttest_test.MakeHarness(t)
   631  	th.WriteK("base", `
   632  namePrefix: b-
   633  commonLabels:
   634    team: foo
   635  resources:
   636  - deployment.yaml
   637  - service.yaml
   638  configMapGenerator:
   639  - name: configmap-in-base
   640    literals:
   641    - foo=bar
   642  `)
   643  	th.WriteF("base/deployment.yaml", `
   644  apiVersion: apps/v1
   645  kind: Deployment
   646  metadata:
   647    name: nginx
   648  spec:
   649    template:
   650      spec:
   651        containers:
   652        - name: nginx
   653          image: nginx
   654          volumeMounts:
   655          - name: nginx-persistent-storage
   656            mountPath: /tmp/ps
   657        - name: sidecar
   658          image: sidecar:latest
   659        volumes:
   660        - configMap:
   661            name: configmap-in-base
   662          name: configmap-in-base
   663  `)
   664  	th.WriteF("base/service.yaml", `
   665  apiVersion: v1
   666  kind: Service
   667  metadata:
   668    name: nginx
   669  spec:
   670    ports:
   671    - port: 80
   672  `)
   673  	th.WriteK("overlay", `
   674  namePrefix: a-
   675  commonLabels:
   676    env: staging
   677  patchesStrategicMerge:
   678  - deployment-patch1.yaml
   679  - deployment-patch2.yaml
   680  resources:
   681  - ../base
   682  configMapGenerator:
   683  - name: configmap-in-overlay
   684    literals:
   685    - hello=world
   686  `)
   687  	th.WriteF("overlay/deployment-patch1.yaml", `
   688  apiVersion: apps/v1
   689  kind: Deployment
   690  metadata:
   691    name: nginx
   692  spec:
   693    template:
   694      spec:
   695        containers:
   696        - name: nginx
   697          image: nginx:latest
   698          env:
   699          - name: ENVKEY
   700            value: ENVVALUE
   701        volumes:
   702        - name: nginx-persistent-storage
   703          gcePersistentDisk:
   704            pdName: nginx-persistent-storage
   705        - configMap:
   706            name: configmap-in-overlay
   707          name: configmap-in-overlay
   708  `)
   709  	th.WriteF("overlay/deployment-patch2.yaml", `
   710  apiVersion: apps/v1
   711  kind: Deployment
   712  metadata:
   713    name: nginx
   714  spec:
   715    template:
   716      spec:
   717        containers:
   718        - name: nginx
   719          env:
   720          - name: ANOTHERENV
   721            value: FOO
   722  `)
   723  	m := th.Run("overlay", th.MakeDefaultOptions())
   724  	th.AssertActualEqualsExpected(m, `
   725  apiVersion: apps/v1
   726  kind: Deployment
   727  metadata:
   728    labels:
   729      env: staging
   730      team: foo
   731    name: a-b-nginx
   732  spec:
   733    selector:
   734      matchLabels:
   735        env: staging
   736        team: foo
   737    template:
   738      metadata:
   739        labels:
   740          env: staging
   741          team: foo
   742      spec:
   743        containers:
   744        - env:
   745          - name: ANOTHERENV
   746            value: FOO
   747          - name: ENVKEY
   748            value: ENVVALUE
   749          image: nginx:latest
   750          name: nginx
   751          volumeMounts:
   752          - mountPath: /tmp/ps
   753            name: nginx-persistent-storage
   754        - image: sidecar:latest
   755          name: sidecar
   756        volumes:
   757        - gcePersistentDisk:
   758            pdName: nginx-persistent-storage
   759          name: nginx-persistent-storage
   760        - configMap:
   761            name: a-configmap-in-overlay-dc6fm46dhm
   762          name: configmap-in-overlay
   763        - configMap:
   764            name: a-b-configmap-in-base-798k5k7g9f
   765          name: configmap-in-base
   766  ---
   767  apiVersion: v1
   768  kind: Service
   769  metadata:
   770    labels:
   771      env: staging
   772      team: foo
   773    name: a-b-nginx
   774  spec:
   775    ports:
   776    - port: 80
   777    selector:
   778      env: staging
   779      team: foo
   780  ---
   781  apiVersion: v1
   782  data:
   783    foo: bar
   784  kind: ConfigMap
   785  metadata:
   786    labels:
   787      env: staging
   788      team: foo
   789    name: a-b-configmap-in-base-798k5k7g9f
   790  ---
   791  apiVersion: v1
   792  data:
   793    hello: world
   794  kind: ConfigMap
   795  metadata:
   796    labels:
   797      env: staging
   798    name: a-configmap-in-overlay-dc6fm46dhm
   799  `)
   800  }
   801  
   802  func makeCommonFilesForMultiplePatchTests(th kusttest_test.Harness) {
   803  	th.WriteK("base", `
   804  namePrefix: team-foo-
   805  commonLabels:
   806    app: mynginx
   807    org: example.com
   808    team: foo
   809  commonAnnotations:
   810    note: This is a test annotation
   811  resources:
   812    - deployment.yaml
   813    - service.yaml
   814  configMapGenerator:
   815  - name: configmap-in-base
   816    literals:
   817    - foo=bar
   818  `)
   819  	th.WriteF("base/deployment.yaml", `
   820  apiVersion: apps/v1
   821  kind: Deployment
   822  metadata:
   823    name: nginx
   824    labels:
   825      app: nginx
   826  spec:
   827    template:
   828      metadata:
   829        labels:
   830          app: nginx
   831      spec:
   832        containers:
   833        - name: nginx
   834          image: nginx
   835          volumeMounts:
   836          - name: nginx-persistent-storage
   837            mountPath: /tmp/ps
   838        - name: sidecar
   839          image: sidecar:latest
   840        volumes:
   841        - configMap:
   842            name: configmap-in-base
   843          name: configmap-in-base
   844  `)
   845  	th.WriteF("base/service.yaml", `
   846  apiVersion: v1
   847  kind: Service
   848  metadata:
   849    name: nginx
   850    labels:
   851      app: nginx
   852  spec:
   853    ports:
   854      - port: 80
   855    selector:
   856      app: nginx
   857  `)
   858  	th.WriteK("overlay/staging", `
   859  namePrefix: staging-
   860  commonLabels:
   861    env: staging
   862  patchesStrategicMerge:
   863    - deployment-patch1.yaml
   864    - deployment-patch2.yaml
   865  resources:
   866    - ../../base
   867  configMapGenerator:
   868  - name: configmap-in-overlay
   869    literals:
   870    - hello=world
   871  `)
   872  }
   873  
   874  func TestMultiplePatchesNoConflict(t *testing.T) {
   875  	th := kusttest_test.MakeHarness(t)
   876  	makeCommonFilesForMultiplePatchTests(th)
   877  	th.WriteF("overlay/staging/deployment-patch1.yaml", `
   878  apiVersion: apps/v1
   879  kind: Deployment
   880  metadata:
   881    name: nginx
   882  spec:
   883    template:
   884      spec:
   885        containers:
   886        - name: nginx
   887          image: nginx:latest
   888          env:
   889          - name: ENVKEY
   890            value: ENVVALUE
   891        volumes:
   892        - name: nginx-persistent-storage
   893          gcePersistentDisk:
   894            pdName: nginx-persistent-storage
   895        - configMap:
   896            name: configmap-in-overlay
   897          name: configmap-in-overlay
   898  `)
   899  	th.WriteF("overlay/staging/deployment-patch2.yaml", `
   900  apiVersion: apps/v1
   901  kind: Deployment
   902  metadata:
   903    name: nginx
   904  spec:
   905    template:
   906      spec:
   907        containers:
   908        - name: nginx
   909          env:
   910          - name: ANOTHERENV
   911            value: FOO
   912  `)
   913  	m := th.Run("overlay/staging", th.MakeDefaultOptions())
   914  	th.AssertActualEqualsExpected(m, `
   915  apiVersion: apps/v1
   916  kind: Deployment
   917  metadata:
   918    annotations:
   919      note: This is a test annotation
   920    labels:
   921      app: mynginx
   922      env: staging
   923      org: example.com
   924      team: foo
   925    name: staging-team-foo-nginx
   926  spec:
   927    selector:
   928      matchLabels:
   929        app: mynginx
   930        env: staging
   931        org: example.com
   932        team: foo
   933    template:
   934      metadata:
   935        annotations:
   936          note: This is a test annotation
   937        labels:
   938          app: mynginx
   939          env: staging
   940          org: example.com
   941          team: foo
   942      spec:
   943        containers:
   944        - env:
   945          - name: ANOTHERENV
   946            value: FOO
   947          - name: ENVKEY
   948            value: ENVVALUE
   949          image: nginx:latest
   950          name: nginx
   951          volumeMounts:
   952          - mountPath: /tmp/ps
   953            name: nginx-persistent-storage
   954        - image: sidecar:latest
   955          name: sidecar
   956        volumes:
   957        - gcePersistentDisk:
   958            pdName: nginx-persistent-storage
   959          name: nginx-persistent-storage
   960        - configMap:
   961            name: staging-configmap-in-overlay-dc6fm46dhm
   962          name: configmap-in-overlay
   963        - configMap:
   964            name: staging-team-foo-configmap-in-base-798k5k7g9f
   965          name: configmap-in-base
   966  ---
   967  apiVersion: v1
   968  kind: Service
   969  metadata:
   970    annotations:
   971      note: This is a test annotation
   972    labels:
   973      app: mynginx
   974      env: staging
   975      org: example.com
   976      team: foo
   977    name: staging-team-foo-nginx
   978  spec:
   979    ports:
   980    - port: 80
   981    selector:
   982      app: mynginx
   983      env: staging
   984      org: example.com
   985      team: foo
   986  ---
   987  apiVersion: v1
   988  data:
   989    foo: bar
   990  kind: ConfigMap
   991  metadata:
   992    annotations:
   993      note: This is a test annotation
   994    labels:
   995      app: mynginx
   996      env: staging
   997      org: example.com
   998      team: foo
   999    name: staging-team-foo-configmap-in-base-798k5k7g9f
  1000  ---
  1001  apiVersion: v1
  1002  data:
  1003    hello: world
  1004  kind: ConfigMap
  1005  metadata:
  1006    labels:
  1007      env: staging
  1008    name: staging-configmap-in-overlay-dc6fm46dhm
  1009  `)
  1010  }
  1011  
  1012  func TestNonCommutablePatches(t *testing.T) {
  1013  	th := kusttest_test.MakeHarness(t)
  1014  	makeCommonFilesForMultiplePatchTests(th)
  1015  	th.WriteF("overlay/staging/deployment-patch1.yaml", `
  1016  apiVersion: apps/v1
  1017  kind: Deployment
  1018  metadata:
  1019    name: nginx
  1020  spec:
  1021    template:
  1022      spec:
  1023        containers:
  1024        - name: nginx
  1025          env:
  1026          - name: ENABLE_FEATURE_FOO
  1027            value: TRUE
  1028        volumes:
  1029        - name: nginx-persistent-storage
  1030          gcePersistentDisk:
  1031            pdName: nginx-persistent-storage
  1032        - configMap:
  1033            name: configmap-in-overlay
  1034          name: configmap-in-overlay
  1035  `)
  1036  	th.WriteF("overlay/staging/deployment-patch2.yaml", `
  1037  apiVersion: apps/v1
  1038  kind: Deployment
  1039  metadata:
  1040    name: nginx
  1041  spec:
  1042    template:
  1043      spec:
  1044        containers:
  1045        - name: nginx
  1046          env:
  1047          - name: ENABLE_FEATURE_FOO
  1048            value: FALSE
  1049  `)
  1050  	// kyaml doesn't try to detect conflicts in patches
  1051  	// (so ENABLE_FEATURE_FOO FALSE wins).
  1052  	m := th.Run("overlay/staging", th.MakeDefaultOptions())
  1053  	th.AssertActualEqualsExpected(m, `
  1054  apiVersion: apps/v1
  1055  kind: Deployment
  1056  metadata:
  1057    annotations:
  1058      note: This is a test annotation
  1059    labels:
  1060      app: mynginx
  1061      env: staging
  1062      org: example.com
  1063      team: foo
  1064    name: staging-team-foo-nginx
  1065  spec:
  1066    selector:
  1067      matchLabels:
  1068        app: mynginx
  1069        env: staging
  1070        org: example.com
  1071        team: foo
  1072    template:
  1073      metadata:
  1074        annotations:
  1075          note: This is a test annotation
  1076        labels:
  1077          app: mynginx
  1078          env: staging
  1079          org: example.com
  1080          team: foo
  1081      spec:
  1082        containers:
  1083        - env:
  1084          - name: ENABLE_FEATURE_FOO
  1085            value: false
  1086          image: nginx
  1087          name: nginx
  1088          volumeMounts:
  1089          - mountPath: /tmp/ps
  1090            name: nginx-persistent-storage
  1091        - image: sidecar:latest
  1092          name: sidecar
  1093        volumes:
  1094        - gcePersistentDisk:
  1095            pdName: nginx-persistent-storage
  1096          name: nginx-persistent-storage
  1097        - configMap:
  1098            name: staging-configmap-in-overlay-dc6fm46dhm
  1099          name: configmap-in-overlay
  1100        - configMap:
  1101            name: staging-team-foo-configmap-in-base-798k5k7g9f
  1102          name: configmap-in-base
  1103  ---
  1104  apiVersion: v1
  1105  kind: Service
  1106  metadata:
  1107    annotations:
  1108      note: This is a test annotation
  1109    labels:
  1110      app: mynginx
  1111      env: staging
  1112      org: example.com
  1113      team: foo
  1114    name: staging-team-foo-nginx
  1115  spec:
  1116    ports:
  1117    - port: 80
  1118    selector:
  1119      app: mynginx
  1120      env: staging
  1121      org: example.com
  1122      team: foo
  1123  ---
  1124  apiVersion: v1
  1125  data:
  1126    foo: bar
  1127  kind: ConfigMap
  1128  metadata:
  1129    annotations:
  1130      note: This is a test annotation
  1131    labels:
  1132      app: mynginx
  1133      env: staging
  1134      org: example.com
  1135      team: foo
  1136    name: staging-team-foo-configmap-in-base-798k5k7g9f
  1137  ---
  1138  apiVersion: v1
  1139  data:
  1140    hello: world
  1141  kind: ConfigMap
  1142  metadata:
  1143    labels:
  1144      env: staging
  1145    name: staging-configmap-in-overlay-dc6fm46dhm
  1146  `)
  1147  }
  1148  
  1149  func TestMultiplePatchesWithOnePatchDeleteDirective(t *testing.T) {
  1150  	additivePatch := `apiVersion: apps/v1
  1151  kind: Deployment
  1152  metadata:
  1153    name: nginx
  1154  spec:
  1155    template:
  1156      spec:
  1157        containers:
  1158        - name: nginx
  1159          env:
  1160          - name: SOME_NAME
  1161            value: somevalue
  1162  `
  1163  	deletePatch := `apiVersion: apps/v1
  1164  kind: Deployment
  1165  metadata:
  1166    name: nginx
  1167  spec:
  1168    template:
  1169      spec:
  1170        containers:
  1171        - $patch: delete
  1172          name: sidecar
  1173  `
  1174  	cases := map[string]struct {
  1175  		patch1      string
  1176  		patch2      string
  1177  		expectError bool
  1178  	}{
  1179  		"Patch with delete directive first": {
  1180  			patch1: deletePatch,
  1181  			patch2: additivePatch,
  1182  		},
  1183  		"Patch with delete directive second": {
  1184  			patch1: additivePatch,
  1185  			patch2: deletePatch,
  1186  		},
  1187  	}
  1188  	for name := range cases {
  1189  		c := cases[name]
  1190  		t.Run(name, func(t *testing.T) {
  1191  			th := kusttest_test.MakeHarness(t)
  1192  			makeCommonFilesForMultiplePatchTests(th)
  1193  			th.WriteF("overlay/staging/deployment-patch1.yaml", c.patch1)
  1194  			th.WriteF("overlay/staging/deployment-patch2.yaml", c.patch2)
  1195  			m := th.Run("overlay/staging", th.MakeDefaultOptions())
  1196  			th.AssertActualEqualsExpected(m, `apiVersion: apps/v1
  1197  kind: Deployment
  1198  metadata:
  1199    annotations:
  1200      note: This is a test annotation
  1201    labels:
  1202      app: mynginx
  1203      env: staging
  1204      org: example.com
  1205      team: foo
  1206    name: staging-team-foo-nginx
  1207  spec:
  1208    selector:
  1209      matchLabels:
  1210        app: mynginx
  1211        env: staging
  1212        org: example.com
  1213        team: foo
  1214    template:
  1215      metadata:
  1216        annotations:
  1217          note: This is a test annotation
  1218        labels:
  1219          app: mynginx
  1220          env: staging
  1221          org: example.com
  1222          team: foo
  1223      spec:
  1224        containers:
  1225        - env:
  1226          - name: SOME_NAME
  1227            value: somevalue
  1228          image: nginx
  1229          name: nginx
  1230          volumeMounts:
  1231          - mountPath: /tmp/ps
  1232            name: nginx-persistent-storage
  1233        volumes:
  1234        - configMap:
  1235            name: staging-team-foo-configmap-in-base-798k5k7g9f
  1236          name: configmap-in-base
  1237  ---
  1238  apiVersion: v1
  1239  kind: Service
  1240  metadata:
  1241    annotations:
  1242      note: This is a test annotation
  1243    labels:
  1244      app: mynginx
  1245      env: staging
  1246      org: example.com
  1247      team: foo
  1248    name: staging-team-foo-nginx
  1249  spec:
  1250    ports:
  1251    - port: 80
  1252    selector:
  1253      app: mynginx
  1254      env: staging
  1255      org: example.com
  1256      team: foo
  1257  ---
  1258  apiVersion: v1
  1259  data:
  1260    foo: bar
  1261  kind: ConfigMap
  1262  metadata:
  1263    annotations:
  1264      note: This is a test annotation
  1265    labels:
  1266      app: mynginx
  1267      env: staging
  1268      org: example.com
  1269      team: foo
  1270    name: staging-team-foo-configmap-in-base-798k5k7g9f
  1271  ---
  1272  apiVersion: v1
  1273  data:
  1274    hello: world
  1275  kind: ConfigMap
  1276  metadata:
  1277    labels:
  1278      env: staging
  1279    name: staging-configmap-in-overlay-dc6fm46dhm
  1280  `)
  1281  		})
  1282  	}
  1283  }
  1284  
  1285  func TestMultiplePatchesBothWithPatchDeleteDirective(t *testing.T) {
  1286  	th := kusttest_test.MakeHarness(t)
  1287  	makeCommonFilesForMultiplePatchTests(th)
  1288  	th.WriteF("overlay/staging/deployment-patch1.yaml", `
  1289  apiVersion: apps/v1
  1290  kind: Deployment
  1291  metadata:
  1292    name: nginx
  1293  spec:
  1294    template:
  1295      spec:
  1296        containers:
  1297        - $patch: delete
  1298          name: sidecar
  1299  `)
  1300  	th.WriteF("overlay/staging/deployment-patch2.yaml", `
  1301  apiVersion: apps/v1
  1302  kind: Deployment
  1303  metadata:
  1304    name: nginx
  1305  spec:
  1306    template:
  1307      spec:
  1308        containers:
  1309        - $patch: delete
  1310          name: nginx
  1311  `)
  1312  	// kyaml doesn't fail on conflicts in patches; both containers
  1313  	// (nginx and sidecar) are deleted per this patching instruction.
  1314  	m := th.Run("overlay/staging", th.MakeDefaultOptions())
  1315  	th.AssertActualEqualsExpected(m, `
  1316  apiVersion: apps/v1
  1317  kind: Deployment
  1318  metadata:
  1319    annotations:
  1320      note: This is a test annotation
  1321    labels:
  1322      app: mynginx
  1323      env: staging
  1324      org: example.com
  1325      team: foo
  1326    name: staging-team-foo-nginx
  1327  spec:
  1328    selector:
  1329      matchLabels:
  1330        app: mynginx
  1331        env: staging
  1332        org: example.com
  1333        team: foo
  1334    template:
  1335      metadata:
  1336        annotations:
  1337          note: This is a test annotation
  1338        labels:
  1339          app: mynginx
  1340          env: staging
  1341          org: example.com
  1342          team: foo
  1343      spec:
  1344        containers: []
  1345        volumes:
  1346        - configMap:
  1347            name: staging-team-foo-configmap-in-base-798k5k7g9f
  1348          name: configmap-in-base
  1349  ---
  1350  apiVersion: v1
  1351  kind: Service
  1352  metadata:
  1353    annotations:
  1354      note: This is a test annotation
  1355    labels:
  1356      app: mynginx
  1357      env: staging
  1358      org: example.com
  1359      team: foo
  1360    name: staging-team-foo-nginx
  1361  spec:
  1362    ports:
  1363    - port: 80
  1364    selector:
  1365      app: mynginx
  1366      env: staging
  1367      org: example.com
  1368      team: foo
  1369  ---
  1370  apiVersion: v1
  1371  data:
  1372    foo: bar
  1373  kind: ConfigMap
  1374  metadata:
  1375    annotations:
  1376      note: This is a test annotation
  1377    labels:
  1378      app: mynginx
  1379      env: staging
  1380      org: example.com
  1381      team: foo
  1382    name: staging-team-foo-configmap-in-base-798k5k7g9f
  1383  ---
  1384  apiVersion: v1
  1385  data:
  1386    hello: world
  1387  kind: ConfigMap
  1388  metadata:
  1389    labels:
  1390      env: staging
  1391    name: staging-configmap-in-overlay-dc6fm46dhm
  1392  `)
  1393  }
  1394  
  1395  // test for #3513
  1396  func TestSmpWithDifferentKeysOnDifferentPorts(t *testing.T) {
  1397  	th := kusttest_test.MakeHarness(t)
  1398  	th.WriteK(".", `
  1399  resources:
  1400    - resource.yaml
  1401  patches:
  1402    - path: patch.yaml
  1403      target:
  1404        kind: StatefulSet
  1405        name: myapp
  1406  `)
  1407  	th.WriteF("resource.yaml", `
  1408  apiVersion: apps/v1
  1409  kind: StatefulSet
  1410  metadata:
  1411    name: myapp
  1412  spec:
  1413    template:
  1414      spec:
  1415        containers:
  1416          - name: consul
  1417            image: "dashicorp/consul:1.9.1"
  1418            ports:
  1419              - containerPort: 8500
  1420                name: http
  1421              - containerPort: 8501
  1422                name: https
  1423              - containerPort: 8301
  1424                protocol: "TCP"
  1425                name: serflan-tcp
  1426              - containerPort: 8301
  1427                protocol: "UDP"
  1428                name: serflan-udp
  1429              - containerPort: 8302
  1430                name: serfwan
  1431              - containerPort: 8300
  1432                name: server
  1433              - containerPort: 8600
  1434                name: dns-tcp
  1435                protocol: "TCP"
  1436              - containerPort: 8600
  1437                name: dns-udp
  1438                protocol: "UDP"`)
  1439  	th.WriteF("patch.yaml", `
  1440  kind: StatefulSet
  1441  metadata:
  1442    name: myapp
  1443    labels:
  1444      test: label
  1445  `)
  1446  	m := th.Run(".", th.MakeDefaultOptions())
  1447  	th.AssertActualEqualsExpected(m, `
  1448  apiVersion: apps/v1
  1449  kind: StatefulSet
  1450  metadata:
  1451    labels:
  1452      test: label
  1453    name: myapp
  1454  spec:
  1455    template:
  1456      spec:
  1457        containers:
  1458        - image: dashicorp/consul:1.9.1
  1459          name: consul
  1460          ports:
  1461          - containerPort: 8500
  1462            name: http
  1463          - containerPort: 8501
  1464            name: https
  1465          - containerPort: 8301
  1466            name: serflan-tcp
  1467            protocol: TCP
  1468          - containerPort: 8301
  1469            name: serflan-udp
  1470            protocol: UDP
  1471          - containerPort: 8302
  1472            name: serfwan
  1473          - containerPort: 8300
  1474            name: server
  1475          - containerPort: 8600
  1476            name: dns-tcp
  1477            protocol: TCP
  1478          - containerPort: 8600
  1479            name: dns-udp
  1480            protocol: UDP
  1481  `)
  1482  }
  1483  
  1484  // test for #3616
  1485  func TestSmpDeleteOnResource(t *testing.T) {
  1486  	th := kusttest_test.MakeHarness(t)
  1487  	th.WriteK(".", `
  1488  resources:
  1489  - workloads.yaml
  1490  patches:
  1491  - patch: |
  1492      apiVersion: monitoring.coreos.com/v1
  1493      kind: PrometheusRule
  1494      metadata:
  1495        name: rule1
  1496      $patch: delete
  1497  `)
  1498  	th.WriteF("workloads.yaml", `
  1499  apiVersion: monitoring.coreos.com/v1
  1500  kind: PrometheusRule
  1501  metadata:
  1502    labels:
  1503      role: alert-rules
  1504    name: rule1
  1505  spec:
  1506    groups:
  1507    - name: rabbitmq.rules
  1508  ---
  1509  apiVersion: monitoring.coreos.com/v1
  1510  kind: PrometheusRule
  1511  metadata:
  1512    labels:
  1513      role: alert-rules
  1514    name: rule2
  1515  spec:
  1516    groups:
  1517    - name: rabbitmq.rules
  1518  `)
  1519  	m := th.Run(".", th.MakeDefaultOptions())
  1520  	th.AssertActualEqualsExpected(m, `
  1521  apiVersion: monitoring.coreos.com/v1
  1522  kind: PrometheusRule
  1523  metadata:
  1524    labels:
  1525      role: alert-rules
  1526    name: rule2
  1527  spec:
  1528    groups:
  1529    - name: rabbitmq.rules
  1530  `)
  1531  }
  1532  
  1533  // test for #3620
  1534  func TestPatchPortHasNoProtocol(t *testing.T) {
  1535  	th := kusttest_test.MakeHarness(t)
  1536  	th.WriteK(".", `
  1537  resources:
  1538    - service.yaml
  1539  patchesStrategicMerge:
  1540    - patch.yaml
  1541  `)
  1542  	th.WriteF("service.yaml", `
  1543  apiVersion: v1
  1544  kind: Service
  1545  metadata:
  1546    name: web
  1547  spec:
  1548    ports:
  1549      - port: 30900
  1550        targetPort: 30900
  1551        protocol: TCP
  1552    type: NodePort
  1553  `)
  1554  	th.WriteF("patch.yaml", `
  1555  apiVersion: v1
  1556  kind: Service
  1557  metadata:
  1558    name: web
  1559    labels:
  1560      service: web
  1561  spec:
  1562    ports:
  1563      - port: 30900
  1564        targetPort: 30900
  1565    selector:
  1566      service: web
  1567  `)
  1568  	m := th.Run(".", th.MakeDefaultOptions())
  1569  	th.AssertActualEqualsExpected(m, `
  1570  apiVersion: v1
  1571  kind: Service
  1572  metadata:
  1573    labels:
  1574      service: web
  1575    name: web
  1576  spec:
  1577    ports:
  1578    - port: 30900
  1579      protocol: TCP
  1580      targetPort: 30900
  1581    selector:
  1582      service: web
  1583    type: NodePort
  1584  `)
  1585  }
  1586  
  1587  // test for #3620
  1588  func TestPatchAddNewServicePort(t *testing.T) {
  1589  	th := kusttest_test.MakeHarness(t)
  1590  	th.WriteK(".", `
  1591  resources:
  1592    - service.yaml
  1593  patchesStrategicMerge:
  1594    - patch.yaml
  1595  `)
  1596  	th.WriteF("service.yaml", `
  1597  apiVersion: v1
  1598  kind: Service
  1599  metadata:
  1600    name: web
  1601  spec:
  1602    ports:
  1603      - port: 30900
  1604        targetPort: 30900
  1605        protocol: TCP
  1606    type: NodePort
  1607  `)
  1608  	th.WriteF("patch.yaml", `
  1609  apiVersion: v1
  1610  kind: Service
  1611  metadata:
  1612    name: web
  1613    labels:
  1614      service: web
  1615  spec:
  1616    ports:
  1617      - port: 30901
  1618        targetPort: 30901
  1619    selector:
  1620      service: web
  1621  `)
  1622  	m := th.Run(".", th.MakeDefaultOptions())
  1623  	th.AssertActualEqualsExpected(m, `
  1624  apiVersion: v1
  1625  kind: Service
  1626  metadata:
  1627    labels:
  1628      service: web
  1629    name: web
  1630  spec:
  1631    ports:
  1632    - port: 30901
  1633      targetPort: 30901
  1634    - port: 30900
  1635      protocol: TCP
  1636      targetPort: 30900
  1637    selector:
  1638      service: web
  1639    type: NodePort
  1640  `)
  1641  }
  1642  
  1643  // test for #4111
  1644  func TestPatchPreservesInternalAnnotations(t *testing.T) {
  1645  	th := kusttest_test.MakeHarness(t)
  1646  	th.WriteK(".", `
  1647  nameSuffix: -abc
  1648  resources:
  1649    - fluentd.yaml
  1650  patchesJson6902:
  1651    - path: patch.yaml
  1652      target:
  1653        name: fluentd-sa
  1654        kind: ServiceAccount
  1655        version: v1
  1656  `)
  1657  	th.WriteF("fluentd.yaml", `
  1658  apiVersion: v1
  1659  kind: DaemonSet
  1660  metadata:
  1661    name: fluentd
  1662  spec:
  1663    template:
  1664      spec:
  1665        containers:
  1666          - image: fluentd:latest
  1667            name: fluentd
  1668        serviceAccountName: fluentd-sa
  1669  ---
  1670  apiVersion: v1
  1671  kind: ServiceAccount
  1672  metadata:
  1673    name: fluentd-sa
  1674  `)
  1675  	th.WriteF("patch.yaml", `
  1676  - op: add
  1677    path: /metadata/annotations
  1678    value:
  1679      note: this is a test annotation
  1680  `)
  1681  	m := th.Run(".", th.MakeDefaultOptions())
  1682  	th.AssertActualEqualsExpected(m, `
  1683  apiVersion: v1
  1684  kind: DaemonSet
  1685  metadata:
  1686    name: fluentd-abc
  1687  spec:
  1688    template:
  1689      spec:
  1690        containers:
  1691        - image: fluentd:latest
  1692          name: fluentd
  1693        serviceAccountName: fluentd-sa-abc
  1694  ---
  1695  apiVersion: v1
  1696  kind: ServiceAccount
  1697  metadata:
  1698    annotations:
  1699      note: this is a test annotation
  1700    name: fluentd-sa-abc
  1701  `)
  1702  }
  1703  

View as plain text