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

More precisely extract strings for Replacements #4555

Closed
tedtramonte opened this issue Mar 29, 2022 · 6 comments
Closed

More precisely extract strings for Replacements #4555

tedtramonte opened this issue Mar 29, 2022 · 6 comments
Labels
kind/feature Categorizes issue or PR as related to a new feature. triage/unresolved Indicates an issue that can not or will not be resolved.

Comments

@tedtramonte
Copy link

Is your feature request related to a problem? Please describe.

Following https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/ I began using the Replacements Transformer to ensure my app.kubernetes.io/version label matched what was in the image its container. For example, a simple Mongo setup might look like:

# kustomization.yaml
images:
  - name: mongo
    newName: mongo
    newTag: "5.0.5"

replacements:
  - source:
      kind: Deployment
      name: mongodb
      fieldPath: spec.template.spec.containers.[name=mongodb].image
      options:
        delimiter: ":"
        index: 1
    targets:
      - select:
          kind: Deployment
          name: mongodb
        fieldPaths:
          - metadata.labels.[app.kubernetes.io/version]
          - spec.template.metadata.labels.[app.kubernetes.io/version]

---
# deployment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: mongodb
  labels:
    app.kubernetes.io/name: mongodb
    app.kubernetes.io/version: "mongo:latest"
    app.kubernetes.io/component: db
    app.kubernetes.io/part-of: logging-stack
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: mongodb
      app.kubernetes.io/part-of: logging-stack
  template:
    metadata:
      labels:
        app.kubernetes.io/name: mongodb
        app.kubernetes.io/version: "mongo:latest"
        app.kubernetes.io/component: db
        app.kubernetes.io/part-of: logging-stack
    spec:
      containers:
        - name: mongodb
          image: mongo:latest

And this works great, albeit not intuitively.

However, when my images are tagged with an sha256, things break down. Here's one non-functioning configuration:

# kustomization.yaml
images:
  - name: mongo
    newName: mongo
    newTag: "5.0.5@sha256:4897d6662abfbefef888ff25d1bea5e6443501bdc299af6aa1c6944a65d1794a"

replacements:
  - source:
      kind: Deployment
      name: mongodb
      fieldPath: spec.template.spec.containers.[name=mongodb].image
      options:
        delimiter: ":"
        index: 1
    targets:
      - select:
          kind: Deployment
          name: mongodb
        fieldPaths:
          - metadata.labels.[app.kubernetes.io/version]
          - spec.template.metadata.labels.[app.kubernetes.io/version]
# Error from server ... [metadata.labels: Invalid value: "5.0.5@sha256" ...
# That's fair, @ is an unusual symbol, but it also has less useful data (@sha256)

What about using JUST the hash as the version?

# kustomization.yaml
images:
  - name: mongo
    newName: mongo
    newTag: "5.0.5@sha256:4897d6662abfbefef888ff25d1bea5e6443501bdc299af6aa1c6944a65d1794a"

replacements:
  - source:
      kind: Deployment
      name: mongodb
      fieldPath: spec.template.spec.containers.[name=mongodb].image
      options:
        delimiter: ":"
        index: 2
    targets:
      - select:
          kind: Deployment
          name: mongodb
        fieldPaths:
          - metadata.labels.[app.kubernetes.io/version]
          - spec.template.metadata.labels.[app.kubernetes.io/version]
# ... [metadata.labels: Invalid value: "4897d6662abfbefef888ff25d1bea5e6443501bdc299af6aa1c6944a65d1794a": must be no more than 63 characters ...
# The hash is 64 characters, one more than the limit on labels

Well, how about just getting the image version using @ as the delimiter?

# kustomization.yaml
images:
  - name: mongo
    newName: mongo
    newTag: "5.0.5@sha256:4897d6662abfbefef888ff25d1bea5e6443501bdc299af6aa1c6944a65d1794a"

replacements:
  - source:
      kind: Deployment
      name: mongodb
      fieldPath: spec.template.spec.containers.[name=mongodb].image
      options:
        delimiter: "@"
        index: 0
    targets:
      - select:
          kind: Deployment
          name: mongodb
        fieldPaths:
          - metadata.labels.[app.kubernetes.io/version]
          - spec.template.metadata.labels.[app.kubernetes.io/version]
# ... [metadata.labels: Invalid value: "mongo:5.0.5" ...
# : is also invalid

Describe the solution you'd like
I'd like to be able to more precisely extract substrings.

Ideally I'd like to have the entire SHA256 hash as the version label, but that isn't going to happen as long as labels are limited to 63 characters. My initial reaction was to truncate the replacement to 63 characters, but Replacements is a general tool not limited to labels. Further, a 63 character hash isn't very useful as a label, anyway.

One idea is to have a starting and ending delimiter option. Example:

# kustomization.yaml
images:
  - name: mongo
    newName: mongo
    newTag: "5.0.5@sha256:4897d6662abfbefef888ff25d1bea5e6443501bdc299af6aa1c6944a65d1794a"

replacements:
  - source:
      kind: Deployment
      name: mongodb
      fieldPath: spec.template.spec.containers.[name=mongodb].image
      options:
        delimiter:
          start:
            character: ":"
            index: 1
          end:
            character: "@"
            index: 0
    targets:
      - select:
          kind: Deployment
          name: mongodb
        fieldPaths:
          - metadata.labels.[app.kubernetes.io/version]
          - spec.template.metadata.labels.[app.kubernetes.io/version]
# 5.0.5

Of course, having just written that out, it looks awful to use.

A better option might be to allow using regex for extracting the replacement:

# kustomization.yaml
images:
  - name: mongo
    newName: mongo
    newTag: "5.0.5@sha256:4897d6662abfbefef888ff25d1bea5e6443501bdc299af6aa1c6944a65d1794a"

replacements:
  - source:
      kind: Deployment
      name: mongodb
      fieldPath: spec.template.spec.containers.[name=mongodb].image
      options:
        regex: "/(?<=:)(.*?)(?=@)/"
    targets:
      - select:
          kind: Deployment
          name: mongodb
        fieldPaths:
          - metadata.labels.[app.kubernetes.io/version]
          - spec.template.metadata.labels.[app.kubernetes.io/version]
# 5.0.5

I'm willing to take a shot at implementing this if it's not an unreasonable addition.

@tedtramonte tedtramonte added the kind/feature Categorizes issue or PR as related to a new feature. label Mar 29, 2022
@k8s-ci-robot
Copy link
Contributor

@tedtramonte: This issue is currently awaiting triage.

SIG CLI takes a lead on issue triage for this repo, but any Kubernetes member can accept issues by applying the triage/accepted label.

The triage/accepted label can be added by org members by writing /triage accepted in a comment.

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.

@k8s-ci-robot k8s-ci-robot added the needs-triage Indicates an issue or PR lacks a `triage/foo` label and requires one. label Mar 29, 2022
@natasha41575
Copy link
Contributor

natasha41575 commented Apr 13, 2022

Please read the kustomize eschewed features. We are intentionally only permitting structured edits in replacements as per the reasoning described under "unstructured edits" in our eschewed features page. I understand that it makes kustomize and replacements much less powerful in the kinds of transformations that you can do, but it is a drawback we accept to avoid unstructured edits.

Your particular case is an interesting one, and I empathize with it, but your issue seems to more directly be caused by the label length limit rather than replacements. I don't think it makes sense for kustomize to add such a broad, powerful, and explicitly eschewed feature based on one use case for which there might be other options.

My advice would be to truncate the SHA to 63 characters if that is sufficient for your use case, or use versioned images without a SHA. Another option you have is to write a third party extension to kustomize to do what you want, which I'd be happy to provide some more guidance on if you are interested in taking that route.

@natasha41575 natasha41575 added triage/unresolved Indicates an issue that can not or will not be resolved. and removed needs-triage Indicates an issue or PR lacks a `triage/foo` label and requires one. labels Apr 13, 2022
@tedtramonte
Copy link
Author

While you're not wrong about length being the core issue, how would you propose truncating that? Replacements is hands off and automatic as part of the overlay and truncating isn't something that Kustomize or Kubernetes does.

While in my case I am technically abusing the "YAML in" part of the "YAML in / YAML out" philosophy by setting the invalid label as the replacement target, there are certainly other use cases where it would be beneficial to have more freedom in extracting a string for Replacement (starting and ending characters and indexes, regex, even a string index range). To me, what WOULD be keeping with the structured edits philosophy would be empowering the Replacements feature so that valid "YAML in" doesn't accidentally become invalid "YAML out".

@natasha41575
Copy link
Contributor

@tedtramonte All are good points. You're right that truncating isn't something that we automatically do, and I agree that in the ideal case, replacements would do some validation to make sure it's not generating invalid yaml. One idea that comes to mind is, what should replacements do when it tries to copy a string longer than 63 characters to the labels field? It could either truncate it or throw an error. The latter seems more intuitively correct to me, but if we do the former, does that resolve your issue?

@tedtramonte
Copy link
Author

It would resolve my issue certainly, but like you mentioned, it IS an error and probably should remain that way. But not every field is limited to 63 characters, so generically truncating to 63 isn't correct.
The simplest solution might be an option to truncate to whatever the limit of the field is. But Kustomize doesn't really have that information, so the user should probably supply that.

# kustomization.yaml
images:
  - name: mongo
    newName: mongo
    newTag: "5.0.5@sha256:4897d6662abfbefef888ff25d1bea5e6443501bdc299af6aa1c6944a65d1794a"

replacements:
  - truncate: 63 
    source:
      kind: Deployment
      name: mongodb
      fieldPath: spec.template.spec.containers.[name=mongodb].image
      options:
        delimiter: ":"
        index: 2
    targets:
      - select:
          kind: Deployment
          name: mongodb
        fieldPaths:
          - metadata.labels.[app.kubernetes.io/version]
          - spec.template.metadata.labels.[app.kubernetes.io/version]

That would give me a label of 4897d6662abfbefef888ff25d1bea5e6443501bdc299af6aa1c6944a65d1794 which, while not TECHNICALLY correct, does get the point across and allows me to continue using this neat trick for both versioned and SHA'd deployments.


RE: plugins, mostly just leaving my thoughts here in case anyone else stumbles on this

After reading up on Kustomize plugins, that would probably be a more flexible solution, but as it stands, configuring transformer plugins (in my case specifying which manifest to get the label value from, what format is the data in, etc.) would place a large burden on the user of this hypothetical auto-version-labeler plugin. A user would have to look up and and then maintain a manifest just for adding a "standard label" which seems a bit much. Plus, the area is in active development, with a new plugin API in the works.

# user has to create and maintain this in each Kustomize application
apiVersion: autoversionlabeler/v1
kind: AutoVersionLabeler
metadata:
  name: notImportantHere
  annotations:
    config.kubernetes.io/function: |
      container:
        image: example.docker.com/auto-version-labeler:1.0.0
spec:
  kind: Deployment
  name: mongodb
  # a user would probably want to specify a container within the deployment, too

My issues with the above are that anyone not versed in custom Kustomize plugins isn't going to know that apiVersion kind and metadata.name are all merely formalities. But, it would work. I haven't ruled it out.

@tedtramonte
Copy link
Author

Just following up, I did end up writing a Kustomize plugin to do this. The source and docs are available at https://gitlab.com/tedtramonte/auto-version-labeler.

At this time, there's some undesired behavior if the plugin is used in a Component. For example, if a Kustomize overlay configuration uses a base (an application deployment) and a Component (redis for storage), and both make use of auto-version-labeler, the Component's auto-version-labeler configuration is applied to any resources that belong to the overlay directly (like Secrets and ConfigMaps), leading to strange or misleading labels.

In testing, listing the Component as a base prevents the undesired labeling, but I need to read up on the differences between a base and a Component before settling on that. Another option I'm considering is checking for an annotation(s) on each resource for configuration rather than specifying the auto-version-labeler configuration in its plugin spec.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind/feature Categorizes issue or PR as related to a new feature. triage/unresolved Indicates an issue that can not or will not be resolved.
Projects
None yet
Development

No branches or pull requests

3 participants