kubernetes-sigs / kustomize

Customization of kubernetes YAML configurations
Apache License 2.0
11.02k stars 2.25k forks source link

Patch literal JSON in ConfigMap item #680

Closed narg95 closed 4 years ago

narg95 commented 5 years ago

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 commented 5 years ago

@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 commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

@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 commented 5 years ago

@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 commented 5 years ago

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 commented 5 years ago

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.

@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 commented 5 years ago

@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 commented 5 years ago

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

kkasravi commented 5 years ago

@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 commented 5 years ago

@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 commented 5 years ago

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 commented 5 years ago

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 commented 5 years ago

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

jbrette commented 5 years ago

@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 commented 5 years ago

@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 commented 5 years ago

Nothing?

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

fejta-bot commented 4 years ago

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

matti commented 4 years ago

/remove-lifecycle stale

davisford commented 4 years ago

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 commented 4 years ago

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

fejta-bot commented 4 years ago

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

fejta-bot commented 4 years ago

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 commented 4 years ago

@fejta-bot: Closing this issue.

In response to [this](https://github.com/kubernetes-sigs/kustomize/issues/680#issuecomment-647191309): >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](https://github.com/fejta). >/close Instructions for interacting with me using PR comments are available [here](https://git.k8s.io/community/contributors/guide/pull-requests.md). If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
liger1978 commented 3 years ago

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

afirth commented 3 years ago

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 commented 3 years ago

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

In response to [this](https://github.com/kubernetes-sigs/kustomize/issues/680#issuecomment-814329518): >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](https://git.k8s.io/community/contributors/guide/pull-requests.md). If you have questions or suggestions related to my behavior, please file an issue against the [kubernetes/test-infra](https://github.com/kubernetes/test-infra/issues/new?title=Prow%20issue:) repository.
matti commented 3 years ago

Open a new issue

On 6. Apr 2021, at 21.08, Kubernetes Prow Robot @.***> wrote:

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

— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or unsubscribe.

m62534 commented 2 years ago

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

gushob21 commented 1 year ago

/remove-lifecycle rotten