Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Patch literal JSON in ConfigMap item #680

Closed
narg95 opened this issue Jan 9, 2019 · 31 comments
Closed

Patch literal JSON in ConfigMap item #680

narg95 opened this issue Jan 9, 2019 · 31 comments

Comments

@narg95
Copy link
Contributor

narg95 commented Jan 9, 2019

Hi all,

I want to patch a literal json from a configMap item, but I have not found an example in the docu, do you know if that is possible? For example, assuming the following configmap:

Base configMap

apiVersion: v1
kind: ConfigMap
metadata:
  name: demo-configmap
data:   
   settings:  |-
      {
         foo: "foo_1",
         bar: "bar_1"
         ...  // assume more attributes
      }
   other-settings: |-
      { 
          ...
      }

I want to apply this patch:

apiVersion: v1
kind: ConfigMap
metadata:
  name: demo-configmap
data:   
settings:  |-
   {
         bar: "bar_2"
   }

So the resulting configMap looks like:

apiVersion: v1
kind: ConfigMap
metadata:
  name: demo-configmap
data:   
   settings:  |-
      {
         foo: "foo_1",
         bar: "bar_2" // Here got patched !
         ...  // all the other attributes remain
      }
   other-settings: |-
      { 
          ... // same here
      }

Thanks !

P.S. Great job with kustomize!

@Liujingfang1
Copy link
Contributor

@narg95 Patching doesn't support that. It will replace the value of settings rather than merging the values.
I'd suggest use ConfigMapGenerator from files. For different base or overlays, you can adjust different the file content.

@dperetti
Copy link

It seems to be a common practice to store config files in ConfigMap properties.
It would be great to be able to use a pattern / regex to replace for instance a port that is set in a nginx.conf.

@ajbouh
Copy link

ajbouh commented Jan 27, 2019

I need this as well.

Another option would be to pass along var references to the external program as environment variables and use the output of that program as the configmap. @Liujingfang1, would that be better aligned with the philosophy of kustomize?

@narg95
Copy link
Contributor Author

narg95 commented Jan 28, 2019

Hi @Liujingfang1,

I'd suggest use ConfigMapGenerator from files. For different base or overlays, you can adjust different the file content.

In a greenfield project I will do as suggested, but I am porting an application, that uses a rather complex json config file, to kubernetes, so using a ConfigMapGenerator will not be ideal because I would have to repeate the whole JSON file for a trivial json property change. I currently solve it post-processing kustomize's output with envsubst but this is something I want to avoid therefore it would be great that kustomize would help me to do so.

If this is a wanted feature I am willing to invest some time in a PR, but if this is something very specific to my use-case I rather solve it by using configMap literals, which can be re-used and overrided, and add a sidecar that converts it to the expected application config json format.

@Liujingfang1 let me know your thoughts!

@Liujingfang1
Copy link
Contributor

@narg95 According to the comment history here, it could be a common use case. If would be nice if Kustomize can provide some convenience here.

The example listed is a JSON file as the value. There are other cases as well. For example, I may have the configmap generated from following files

key1=value1
key2=value2

There is no limitation on what type of files can be used to create a ConfigMap. How to detect the format and apply proper merging is tricky.

@narg95
Copy link
Contributor Author

narg95 commented Jan 30, 2019

@Liujingfang1 thanks for your feedback!

I may have the configmap generated from following files

did you mean: "generated from a file with the following values" right?

There is no limitation on what type of files can be used to create a ConfigMap.

right, it should be extensible to incrementally support more patching strategies, we can start with strategy-merge for json and yaml, then props (key=value), later json-6902.

The following example shows my two-cents proposal :) , note the new patchContent attribute, it contains patch strategy to apply:

Example

Overlay

kustomization with patch

-base: ../base/my-app/ 

configMapGenerator:
- name: demo-settings
   behavior: merge
   patchContent: json-strategic-merge
- file: my-app-settings=patch.json
- literals:
   - foo= -|
              {
                      "logLevel": "info"
              }

patch.json

{
    "version": "2.0-rc1",
    "args": ["--debug-mode", "off"]
}

base

base/my-app/kustomization.yaml

configMapGenerator:
name: demo-settings
- file: my-app-settings.json
- literals: 
   - logging-config: -| 
                 {
                         "log-level": "debug",
                          "output": "stdout" 
                 }

my-app-settings.json

{
    "name": "my app",
    "environment": "demo",
    "version": "1.0",
    "args": ["--debug-mode"]
}

output

kind: ConfigMap
apiVersion: v1
metadata:
   name: demo-settings
data: 
   my-app-settings.json: -|
           {
                "name": "my app",
                "environment": "demo",
                "version": "2.0-rc1",        // PATCHED
               "args": ["--debug-mode=off"]       // PATCHED
           }
   logging-config: -| 
                 {
                         "log-level": "info",        // PATCHED
                          "output": "stdout" 
                 }

I'd love to hear your opinions @Liujingfang1 @dperetti @ajbouh !

@ikatson
Copy link

ikatson commented Feb 4, 2019

One more option to discuss here: make it possible to render configmap/secret content as templates, e.g. golang templates.

I understand that kustomize's awesomeness stems from the fact that it does not use templating to build kubernetes objects which are structured data by nature. But configmap/secret content is opaque data, so making it possible to render with templates sounds cool.

For example make it work like helm template processing, but only for configmap/secret content. Maybe other literal field content, e.g. JSON stored in annotations.

Here's a very rough sketch of how this might look like:

#### base/app.properties
foo={{ .Values.foo }}
bar={{ .Values.bar }}

#### base/values-defaults.yaml
foo: 'foo-value'
bar: 'bar-value'

#### base/kustomization.yaml
valueSources:
- values-defaults.yaml
  
configMapGenerator
- name: myJavaServerProps
  render:
  # the values for rendering app.properties will come from "valueSources", which might get enriched in overlays
  - app.properties

#### overlays/production/production-values.yaml
bar: 'bar-value-production'

#### overlays/production/kustomization.yaml
valueSources: 
- production-values.yaml

bases:
# the base gets rendered using combined value sources: "production-values.yaml" overlaid on top of "base/values-defaults.yaml"
- ../base/

I'm not presenting an API for review here as it definitely needs more thought, but more an idea of what you could accomplish with it.

@monopole
Copy link
Contributor

Attemping to summarize:

Say you want to use kustomize to make the configmaps used in production differ in some way from that used in development and staging.

Mixins might work. Suppose the configmap held a java properties file (k=v pairs, one per line), and one wanted to use kustomize to “edit” the value for one particular key. The kustomize way to do this would be to split the file - put the bulk of the k=v’s into a configmap in the base, then put the particular k=v in an overlay and get the final configmap as a merger (example) which then goes to production or development, etc. This works because kustomize understands that particular file format. It knows the values are whatever comes between the = and EOL characters.

More generally, configmap values are opaque to k8s - they could be a simple port number, or some snippet of json/javascript/lisp/whatever.

A scheme to allow kustomize to edit any configmap value would have to be in the form of an unstructured edit, e.g.

  • for configmap foo, look up the value for key bar, and run this regexp replacement pattern over it.
  • Or - as @ikatson suggests - the final value for bar is the expansion of some Go template, and we get different variants by specifying different template mappings in different overlays. This is the helm approach, but just for map values instead of everything in sight.

@narg95 focusses on a structured JSON edit instead of an unstructure edit, to address the particular case of an app being ported to k8s that consumes some complex JSON config file, and @narg95 wants to apply structured edits (e.g. to get loglevel:info in production instead of loglevel:debug).

@narg95 SGTM - can you write a short KEP (e.g 892, 886) - you pretty much have the meat of it above - and then provide the impl? Do you have any notion of how common this might be? It’s a structured edit, so it’s consistent with kustomize in that sense.

As an aside, Secrets are getting a big upgrade, where we allow a plugin to generate KV pairs (since the V’s are not just opaque - they are secret). This isn’t an patch mechanism (create KV’s only), and it’s not going to be active in configmaps - but folks on this thread might be interested.

@yellowmegaman
Copy link

@monopole Maybe I'm missing something, but adding another file to configMap is not actually a merge.
What we do want (speaking for my company) is to have some basic common application.conf, and some custom additions to it, like dev.conf/stage.conf which are ok to just to append to common config.
With your example https://github.com/kubernetes-sigs/kustomize/blob/master/examples/combineConfigs.md we'll achieve two separate config files, which are not supported at the moment.

@matti
Copy link
Contributor

matti commented Apr 3, 2019

this is another case where variables would help like https://github.com/kontena/mortar has

@kkasravi
Copy link

kkasravi commented Apr 7, 2019

@narg95 this can be done in the following way:

Below is in a file resources.yaml

apiVersion: v1
kind: ConfigMap
metadata:
  name: demo-configmap
data:   
   settings:  |-
      {
         foo: $(foo),
         bar: $(bar)
      }

The kustomization file looks like

apiVersion: kustomize.config.k8s.io/v1beta1
kind: Kustomization
resources:
- resources.yaml
configMapGenerator:
- name: demo-configmap-parameters
  env: params.env
generatorOptions:
  disableNameSuffixHash: true
vars:
- name: foo
  objref:
    kind: ConfigMap
    name: demo-configmap-parameters
    apiVersion: v1
  fieldref:
    fieldpath: data.foo
- name: bar
  objref:
    kind: ConfigMap
    name: demo-configmap-parameters
    apiVersion: v1
  fieldref:
    fieldpath: data.bar
configurations:
- params.yaml

params.env contains

foo=foo_1
bar=bar_2

params.yaml contains

varReference:
- path: data/settings
  kind: ConfigMap

Note that this inserts an extra ConfigMap which you would probably delete afterwards.
eg

$ ☞  kubectl create ns demo 1>/dev/null && kustomize build | kubectl apply -f - -n demo 1>/dev/null && kubectl delete cm demo-configmap-parameters -n demo 1>/dev/null && kubectl get cm demo-configmap -oyaml -n demo && kubectl delete ns demo 1>/dev/null

outputs

apiVersion: v1
data:
  settings: |-
    {
       foo: "foo_1",
       bar: "bar_2"
    }
kind: ConfigMap
metadata:
  annotations:
    kubectl.kubernetes.io/last-applied-configuration: |
      {"apiVersion":"v1","data":{"settings":"{\n   foo: \"foo_1\",\n   bar: \"bar_2\"\n}"},"kind":"ConfigMap","metadata":{"annotations":{},"name":"demo-configmap","namespace":"demo"}}
  creationTimestamp: "2019-04-07T14:47:54Z"
  name: demo-configmap
  namespace: demo
  resourceVersion: "40657790"
  selfLink: /api/v1/namespaces/demo/configmaps/demo-configmap
  uid: 203db6aa-5944-11e9-9a72-42010a8a016c

@killwing
Copy link

killwing commented May 8, 2019

@kkasravi works fine except the config hash (if enabled) won't be updated when vars change, which result in the deployment can not rolling update.
cc @Liujingfang1

@narg95
Copy link
Contributor Author

narg95 commented May 8, 2019

Hi @kkasravi, thanks for the explanation and the code!

I ended up with a similar solution but in my case at that time I generated all the configs with kustomize config save -d ./configs and I appended my custom configuration including the path data/setings from configmaps. I did it so because from my understanding the custom configurations did not get merged with defaults so I did not wanted to lose kustomize's default config. But in your example you just added a new configuration so I am wondering if you get the defaults plus the ones defined in your params.yml file.

@benjamin-bergia
Copy link

Hi, just met the same issue here.
Without going into templating or "smart" merging, being able to concatenate multiple file under the same key in the config map would make a huge difference. I think the current syntax is neat and doesn't have to change but having (as in the example):
base/kustomization.yml

configMapGenerator:
- name: my-configmap
  files:
  - settings.cfg

overlays/prod/kustomization.yml

configMapGenerator:
- name: my-configmap
  behavior: merge
  files:
  - settings.cfg

Should result in a configmap with a single key settings.cfg and the content of both files concatenated.
It is not optimal since it requires using the same file names but would still be a big improvement in maintainability.

@SilverXXX
Copy link

Hi, same problem here.
@benjamin-bergia solution would be enough even for us, solving problems when multiple files cannot be loaded.

@jbrette
Copy link
Contributor

jbrette commented Jul 19, 2019

@SilverXXX @benjamin-bergia The configMapGenerator overlay does not seems to work write. There is bug in the Resource.replace/merge implementation and we end up with invalid labels:

apiVersion: v1
data:
  settings.cfg: |
    Content of the overlay settings.cfg
kind: ConfigMap
metadata:
  annotations: {}
  labels: {}
  name: my-configmap-8gkkcddg96

@jbrette
Copy link
Contributor

jbrette commented Jul 19, 2019

@ikatson @monopole @Liujingfang1 I think this another use case where the autovar PR

We are basically able to achieve exactly what ikatson was asking without templating and by levering multiple key features of kustomize. Have a look at the third solution

The output is basically the following:

apiVersion: v1
data:
  app.properties: |
    foo=production-foo-value
    bar=production-foo-value
kind: ConfigMap
metadata:
  name: myJavaServerProps-7m6dhd66bg
---
apiVersion: v1
kind: Values
metadata:
  name: file1
spec:
  bar: production-foo-value
  foo: production-foo-value

@antl3x
Copy link

antl3x commented Oct 12, 2019

Nothing?

Sometimes the unresponsive attitude of kustomize team about open issues/prs are really sad.

@fejta-bot
Copy link

Issues go stale after 90d of inactivity.
Mark the issue as fresh with /remove-lifecycle stale.
Stale issues rot after an additional 30d of inactivity and eventually close.

If this issue is safe to close now please do so with /close.

Send feedback to sig-testing, kubernetes/test-infra and/or fejta.
/lifecycle stale

@k8s-ci-robot k8s-ci-robot added the lifecycle/stale Denotes an issue or PR has remained open with no activity and has become stale. label Jan 10, 2020
@matti
Copy link
Contributor

matti commented Jan 10, 2020

/remove-lifecycle stale

@k8s-ci-robot k8s-ci-robot removed the lifecycle/stale Denotes an issue or PR has remained open with no activity and has become stale. label Jan 10, 2020
@davisford
Copy link

Will there ever be a valid solution for patching a JSON ConfigMap? The lame workaround right now is to copy the full JSON file into every overlay and manually override it like:

base/thing/kustomization.yaml

configMapGenerator:
 - name: thing-config
    files: 
        - config/thing-config.json

overlay/foo/kustomization.yaml

configMapGenerator:
  - name: thing-config
    behavior: merge
    files:
      - thing-config.json

This is really error prone and not ideal. Is there not some way we can use patchesJson6902 here to patch a JSON file? That's what it was intended for, but so far I've yet to work out a config that works with this kind of setup.

@fejta-bot
Copy link

Issues go stale after 90d of inactivity.
Mark the issue as fresh with /remove-lifecycle stale.
Stale issues rot after an additional 30d of inactivity and eventually close.

If this issue is safe to close now please do so with /close.

Send feedback to sig-testing, kubernetes/test-infra and/or fejta.
/lifecycle stale

@k8s-ci-robot k8s-ci-robot added the lifecycle/stale Denotes an issue or PR has remained open with no activity and has become stale. label Apr 22, 2020
@fejta-bot
Copy link

Stale issues rot after 30d of inactivity.
Mark the issue as fresh with /remove-lifecycle rotten.
Rotten issues close after an additional 30d of inactivity.

If this issue is safe to close now please do so with /close.

Send feedback to sig-testing, kubernetes/test-infra and/or fejta.
/lifecycle rotten

@k8s-ci-robot k8s-ci-robot added lifecycle/rotten Denotes an issue or PR that has aged beyond stale and will be auto-closed. and removed lifecycle/stale Denotes an issue or PR has remained open with no activity and has become stale. labels May 22, 2020
@fejta-bot
Copy link

Rotten issues close after 30d of inactivity.
Reopen the issue with /reopen.
Mark the issue as fresh with /remove-lifecycle rotten.

Send feedback to sig-testing, kubernetes/test-infra and/or fejta.
/close

@k8s-ci-robot
Copy link
Contributor

@fejta-bot: Closing this issue.

In response to this:

Rotten issues close after 30d of inactivity.
Reopen the issue with /reopen.
Mark the issue as fresh with /remove-lifecycle rotten.

Send feedback to sig-testing, kubernetes/test-infra and/or fejta.
/close

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

@liger1978
Copy link

Just a note to say this is still needed...

@afirth
Copy link
Contributor

afirth commented Apr 6, 2021

not sure if i can/should but
/reopen
the ability to patch structured configmaps is such a pain point in older apps, particularly in javaland.

@k8s-ci-robot
Copy link
Contributor

@afirth: You can't reopen an issue/PR unless you authored it or you are a collaborator.

In response to this:

not sure if i can/should but
/reopen
the ability to patch structured configmaps is such a pain point in older apps, particularly in javaland.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository.

@matti
Copy link
Contributor

matti commented Apr 7, 2021 via email

@m62534
Copy link

m62534 commented Jul 27, 2022

How do we do this now that var is deprecated?
I don't think varReference works anymore either when we replace with replacements

@gushob21
Copy link

/remove-lifecycle rotten

@k8s-ci-robot k8s-ci-robot removed the lifecycle/rotten Denotes an issue or PR that has aged beyond stale and will be auto-closed. label Feb 14, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests