...

Source file src/sigs.k8s.io/kustomize/api/krusty/complexcomposition_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  	"strings"
     8  	"testing"
     9  
    10  	. "sigs.k8s.io/kustomize/api/krusty"
    11  	kusttest_test "sigs.k8s.io/kustomize/api/testutils/kusttest"
    12  	"sigs.k8s.io/kustomize/api/types"
    13  )
    14  
    15  const httpsService = `
    16  apiVersion: v1
    17  kind: Service
    18  metadata:
    19    name: my-https-svc
    20  spec:
    21    ports:
    22    - port: 443
    23      protocol: TCP
    24      name: https
    25    selector:
    26      app: my-app
    27  `
    28  
    29  func writeStatefulSetBase(th kusttest_test.Harness) {
    30  	th.WriteK("base", `
    31  resources:
    32  - statefulset.yaml
    33  `)
    34  	th.WriteF("base/statefulset.yaml", `
    35  apiVersion: apps/v1
    36  kind: StatefulSet
    37  metadata:
    38    name: my-sts
    39  spec:
    40    serviceName: my-svc
    41    selector:
    42      matchLabels:
    43        app: my-app
    44    template:
    45      metadata:
    46        labels:
    47          app: my-app
    48      spec:
    49        containers:
    50        - name: app
    51          image: my-image
    52    volumeClaimTemplates:
    53    - spec:
    54        storageClassName: default
    55  `)
    56  }
    57  
    58  func writeHTTPSOverlay(th kusttest_test.Harness) {
    59  	th.WriteK("https", `
    60  resources:
    61  - ../base
    62  - https-svc.yaml
    63  patchesStrategicMerge:
    64  - sts-patch.yaml
    65  `)
    66  	th.WriteF("https/https-svc.yaml", httpsService)
    67  	th.WriteF("https/sts-patch.yaml", `
    68  apiVersion: apps/v1
    69  kind: StatefulSet
    70  metadata:
    71    name: my-sts
    72  spec:
    73    serviceName: my-https-svc
    74  `)
    75  }
    76  
    77  func writeHTTPSTransformerRaw(th kusttest_test.Harness) {
    78  	th.WriteF("https/service/https-svc.yaml", httpsService)
    79  	th.WriteF("https/transformer/transformer.yaml", `
    80  apiVersion: builtin
    81  kind: PatchTransformer
    82  metadata:
    83    name: svcNameTran
    84  target: 
    85    group: apps
    86    version: v1
    87    kind: StatefulSet
    88    name: my-sts
    89  patch: |-
    90    apiVersion: apps/v1
    91    kind: StatefulSet
    92    metadata:
    93      name: my-sts
    94    spec:
    95      serviceName: my-https-svc
    96  `)
    97  }
    98  
    99  func writeHTTPSTransformerBase(th kusttest_test.Harness) {
   100  	th.WriteK("https/service", `
   101  resources:
   102  - https-svc.yaml
   103  `)
   104  	th.WriteK("https/transformer", `
   105  resources:
   106  - transformer.yaml
   107  `)
   108  	writeHTTPSTransformerRaw(th)
   109  }
   110  
   111  func writeConfigFromEnvOverlay(th kusttest_test.Harness) {
   112  	th.WriteK("config", `
   113  resources:
   114  - ../base
   115  configMapGenerator:
   116  - name: my-config
   117    literals:
   118    - MY_ENV=foo
   119  generatorOptions:
   120    disableNameSuffixHash: true
   121  patchesStrategicMerge:
   122  - sts-patch.yaml
   123  `)
   124  	th.WriteF("config/sts-patch.yaml", `
   125  apiVersion: apps/v1
   126  kind: StatefulSet
   127  metadata:
   128    name: my-sts
   129  spec:
   130    template:
   131      spec:
   132        containers:
   133        - name: app
   134          envFrom:
   135          - configMapRef:
   136              name: my-config
   137  `)
   138  }
   139  
   140  func writeConfigFromEnvTransformerRaw(th kusttest_test.Harness) {
   141  	th.WriteF("config/map/generator.yaml", `
   142  apiVersion: builtin
   143  kind: ConfigMapGenerator
   144  metadata:
   145    name: my-config
   146  options:
   147    disableNameSuffixHash: true
   148  literals:
   149  - MY_ENV=foo
   150  `)
   151  	th.WriteF("config/transformer/transformer.yaml", `
   152  apiVersion: builtin
   153  kind: PatchTransformer
   154  metadata:
   155    name: envFromConfigTrans
   156  target: 
   157    group: apps
   158    version: v1
   159    kind: StatefulSet
   160    name: my-sts
   161  patch: |-
   162    apiVersion: apps/v1
   163    kind: StatefulSet
   164    metadata:
   165      name: my-sts
   166    spec:
   167      template:
   168        spec:
   169          containers:
   170          - name: app
   171            envFrom:
   172            - configMapRef:
   173                name: my-config
   174  `)
   175  }
   176  func writeConfigFromEnvTransformerBase(th kusttest_test.Harness) {
   177  	th.WriteK("config/map", `
   178  resources:
   179  - generator.yaml
   180  `)
   181  	th.WriteK("config/transformer", `
   182  resources:
   183  - transformer.yaml
   184  `)
   185  	writeConfigFromEnvTransformerRaw(th)
   186  }
   187  
   188  func writeTolerationsOverlay(th kusttest_test.Harness) {
   189  	th.WriteK("tolerations", `
   190  resources:
   191  - ../base
   192  patchesStrategicMerge:
   193  - sts-patch.yaml
   194  `)
   195  	th.WriteF("tolerations/sts-patch.yaml", `
   196  apiVersion: apps/v1
   197  kind: StatefulSet
   198  metadata:
   199    name: my-sts
   200  spec:
   201    template:
   202      spec:
   203        tolerations:
   204        - effect: NoExecute
   205          key: node.kubernetes.io/not-ready
   206          tolerationSeconds: 30
   207  `)
   208  }
   209  
   210  func writeTolerationsTransformerRaw(th kusttest_test.Harness) {
   211  	th.WriteF("tolerations/transformer.yaml", `
   212  apiVersion: builtin
   213  kind: PatchTransformer
   214  metadata:
   215    name: tolTrans
   216  target: 
   217    group: apps
   218    version: v1
   219    kind: StatefulSet
   220    name: my-sts
   221  patch: |-
   222    apiVersion: apps/v1
   223    kind: StatefulSet
   224    metadata:
   225      name: my-sts
   226    spec:
   227      template:
   228        spec:
   229          tolerations:
   230          - effect: NoExecute
   231            key: node.kubernetes.io/not-ready
   232            tolerationSeconds: 30
   233  `)
   234  }
   235  
   236  func writeTolerationsTransformerBase(th kusttest_test.Harness) {
   237  	th.WriteK("tolerations", `
   238  resources:
   239  - transformer.yaml
   240  `)
   241  	writeTolerationsTransformerRaw(th)
   242  }
   243  
   244  func writeStorageOverlay(th kusttest_test.Harness) {
   245  	th.WriteK("storage", `
   246  resources:
   247  - ../base
   248  patchesJson6902:
   249  - target:
   250      group: apps
   251      version: v1
   252      kind: StatefulSet
   253      name: my-sts
   254    path: sts-patch.json
   255  `)
   256  	th.WriteF("storage/sts-patch.json", `
   257  [{"op": "replace", "path": "/spec/volumeClaimTemplates/0/spec/storageClassName", "value": "my-sc"}]
   258  `)
   259  }
   260  
   261  func writeStorageTransformerRaw(th kusttest_test.Harness) {
   262  	th.WriteF("storage/transformer.yaml", `
   263  apiVersion: builtin
   264  kind: PatchTransformer
   265  metadata:
   266    name: storageTrans
   267  target: 
   268    group: apps
   269    version: v1
   270    kind: StatefulSet
   271    name: my-sts
   272  patch: |-
   273    [{"op": "replace", "path": "/spec/volumeClaimTemplates/0/spec/storageClassName", "value": "my-sc"}]
   274  `)
   275  }
   276  
   277  func writeStorageTransformerBase(th kusttest_test.Harness) {
   278  	th.WriteK("storage", `
   279  resources:
   280  - transformer.yaml
   281  `)
   282  	writeStorageTransformerRaw(th)
   283  }
   284  
   285  func writePatchingOverlays(th kusttest_test.Harness) {
   286  	writeStorageOverlay(th)
   287  	writeConfigFromEnvOverlay(th)
   288  	writeTolerationsOverlay(th)
   289  	writeHTTPSOverlay(th)
   290  }
   291  
   292  func writePatchingTransformersRaw(th kusttest_test.Harness) {
   293  	writeStorageTransformerRaw(th)
   294  	writeConfigFromEnvTransformerRaw(th)
   295  	writeTolerationsTransformerRaw(th)
   296  	writeHTTPSTransformerRaw(th)
   297  }
   298  
   299  // Similar to writePatchingTransformersRaw, except here the
   300  // transformers and related artifacts are addressable as _bases_.
   301  // They are listed in a kustomization file, and consumers of
   302  // the plugin refer to the kustomization instead of to the local
   303  // file in the "transformers:" field.
   304  //
   305  // Using bases makes the set of files relocatable with
   306  // respect to the overlays, and avoids the need to relax load
   307  // restrictions on file paths reaching outside the `dev` and
   308  // `prod` kustomization roots.  I.e. with bases tests can use
   309  // NewKustTestHarness instead of NewKustTestHarnessNoLoadRestrictor.
   310  //
   311  // Using transformer plugins from _bases_ means the plugin config
   312  // must be self-contained, i.e. the config may not have fields that
   313  // refer to local files, since those files won't be present when
   314  // the plugin is instantiated and used.
   315  func writePatchingTransformerBases(th kusttest_test.Harness) {
   316  	writeStorageTransformerBase(th)
   317  	writeConfigFromEnvTransformerBase(th)
   318  	writeTolerationsTransformerBase(th)
   319  	writeHTTPSTransformerBase(th)
   320  }
   321  
   322  // Here's a complex kustomization scenario that combines multiple overlays
   323  // on a common base:
   324  //
   325  //                 dev                       prod
   326  //                  |                         |
   327  //                  |                         |
   328  //        + ------- +          + ------------ + ------------- +
   329  //        |         |          |              |               |
   330  //        |         |          |              |               |
   331  //        v         |          v              v               v
   332  //     storage      + -----> config       tolerations       https
   333  //        |                    |              |               |
   334  //        |                    |              |               |
   335  //        |                    + --- +  + --- +               |
   336  //        |                          |  |                     |
   337  //        |                          v  v                     |
   338  //        + -----------------------> base <------------------ +
   339  //
   340  // The base resource is a statefulset. Each intermediate overlay manages or
   341  // generates new resources and patches different aspects of the same base
   342  // resource, without using any of the `namePrefix`, `nameSuffix` or `namespace`
   343  // kustomization keywords.
   344  //
   345  // Intermediate overlays:
   346  //   - storage: Changes the storage class of the stateful set with a JSON patch.
   347  //   - config: Generates a config map and adds a field as an environment
   348  //             variable.
   349  //   - tolerations: Adds a new tolerations field in the spec.
   350  //   - https: Adds a new service resource and changes the service name in the
   351  //            stateful set.
   352  //
   353  // Top overlays:
   354  //   - dev: Combines the storage and config intermediate overlays.
   355  //   - prod: Combines the config, tolerations and https intermediate overlays.
   356  
   357  func TestComplexComposition_Dev_Failure(t *testing.T) {
   358  	th := kusttest_test.MakeHarness(t)
   359  	writeStatefulSetBase(th)
   360  	writePatchingOverlays(th)
   361  	th.WriteK("dev", `
   362  resources:
   363  - ../storage
   364  - ../config
   365  `)
   366  	err := th.RunWithErr("dev", th.MakeDefaultOptions())
   367  	if err == nil {
   368  		t.Fatalf("Expected resource accumulation error")
   369  	}
   370  	if !strings.Contains(
   371  		err.Error(), "already registered id: StatefulSet.v1.apps/my-sts.[noNs]") {
   372  		t.Fatalf("Unexpected err: %v", err)
   373  	}
   374  }
   375  
   376  const devDesiredResult = `
   377  apiVersion: apps/v1
   378  kind: StatefulSet
   379  metadata:
   380    name: my-sts
   381  spec:
   382    selector:
   383      matchLabels:
   384        app: my-app
   385    serviceName: my-svc
   386    template:
   387      metadata:
   388        labels:
   389          app: my-app
   390      spec:
   391        containers:
   392        - envFrom:
   393          - configMapRef:
   394              name: my-config
   395          image: my-image
   396          name: app
   397    volumeClaimTemplates:
   398    - spec:
   399        storageClassName: my-sc
   400  ---
   401  apiVersion: v1
   402  data:
   403    MY_ENV: foo
   404  kind: ConfigMap
   405  metadata:
   406    name: my-config
   407  `
   408  
   409  func TestComplexComposition_Dev_SuccessWithRawTransformers(t *testing.T) {
   410  	th := kusttest_test.MakeHarness(t)
   411  	writeStatefulSetBase(th)
   412  	writePatchingTransformersRaw(th)
   413  	th.WriteK("dev", `
   414  resources:
   415  - ../base
   416  generators:
   417  - ../config/map/generator.yaml
   418  transformers:
   419  - ../config/transformer/transformer.yaml
   420  - ../storage/transformer.yaml
   421  `)
   422  	m := th.Run("dev", func() Options {
   423  		o := th.MakeDefaultOptions()
   424  		o.LoadRestrictions = types.LoadRestrictionsNone
   425  		return o
   426  	}())
   427  	th.AssertActualEqualsExpected(m, devDesiredResult)
   428  }
   429  
   430  func TestComplexComposition_Dev_SuccessWithBaseTransformers(t *testing.T) {
   431  	th := kusttest_test.MakeHarness(t)
   432  	writeStatefulSetBase(th)
   433  	writePatchingTransformerBases(th)
   434  	th.WriteK("dev", `
   435  resources:
   436  - ../base
   437  generators:
   438  - ../config/map
   439  transformers:
   440  - ../config/transformer
   441  - ../storage
   442  `)
   443  	m := th.Run("dev", th.MakeDefaultOptions())
   444  	th.AssertActualEqualsExpected(m, devDesiredResult)
   445  }
   446  
   447  func TestComplexComposition_Prod_Failure(t *testing.T) {
   448  	th := kusttest_test.MakeHarness(t)
   449  	writeStatefulSetBase(th)
   450  	writePatchingOverlays(th)
   451  	th.WriteK("prod", `
   452  resources:
   453  - ../config
   454  - ../tolerations
   455  - ../https
   456  `)
   457  	err := th.RunWithErr("prod", th.MakeDefaultOptions())
   458  	if err == nil {
   459  		t.Fatalf("Expected resource accumulation error")
   460  	}
   461  	if !strings.Contains(
   462  		err.Error(), "already registered id: StatefulSet.v1.apps/my-sts.[noNs]") {
   463  		t.Fatalf("Unexpected err: %v", err)
   464  	}
   465  }
   466  
   467  const prodDesiredResult = `
   468  apiVersion: apps/v1
   469  kind: StatefulSet
   470  metadata:
   471    name: my-sts
   472  spec:
   473    selector:
   474      matchLabels:
   475        app: my-app
   476    serviceName: my-https-svc
   477    template:
   478      metadata:
   479        labels:
   480          app: my-app
   481      spec:
   482        containers:
   483        - envFrom:
   484          - configMapRef:
   485              name: my-config
   486          image: my-image
   487          name: app
   488        tolerations:
   489        - effect: NoExecute
   490          key: node.kubernetes.io/not-ready
   491          tolerationSeconds: 30
   492    volumeClaimTemplates:
   493    - spec:
   494        storageClassName: default
   495  ---
   496  apiVersion: v1
   497  kind: Service
   498  metadata:
   499    name: my-https-svc
   500  spec:
   501    ports:
   502    - name: https
   503      port: 443
   504      protocol: TCP
   505    selector:
   506      app: my-app
   507  ---
   508  apiVersion: v1
   509  data:
   510    MY_ENV: foo
   511  kind: ConfigMap
   512  metadata:
   513    name: my-config
   514  `
   515  
   516  func TestComplexComposition_Prod_SuccessWithRawTransformers(t *testing.T) {
   517  	th := kusttest_test.MakeHarness(t)
   518  	writeStatefulSetBase(th)
   519  	writePatchingTransformersRaw(th)
   520  	th.WriteK("prod", `
   521  resources:
   522  - ../base
   523  - ../https/service/https-svc.yaml
   524  generators:
   525  - ../config/map/generator.yaml
   526  transformers:
   527  - ../config/transformer/transformer.yaml
   528  - ../https/transformer/transformer.yaml
   529  - ../tolerations/transformer.yaml
   530  `)
   531  	m := th.Run("prod", func() Options {
   532  		o := th.MakeDefaultOptions()
   533  		o.LoadRestrictions = types.LoadRestrictionsNone
   534  		return o
   535  	}())
   536  	th.AssertActualEqualsExpected(m, prodDesiredResult)
   537  }
   538  
   539  func TestComplexComposition_Prod_SuccessWithBaseTransformers(t *testing.T) {
   540  	th := kusttest_test.MakeHarness(t)
   541  	writeStatefulSetBase(th)
   542  	writePatchingTransformerBases(th)
   543  	th.WriteK("prod", `
   544  resources:
   545  - ../base
   546  - ../https/service
   547  generators:
   548  - ../config/map
   549  transformers:
   550  - ../config/transformer
   551  - ../https/transformer
   552  - ../tolerations
   553  `)
   554  	m := th.Run("prod", th.MakeDefaultOptions())
   555  	th.AssertActualEqualsExpected(m, prodDesiredResult)
   556  }
   557  

View as plain text