cdk8s-team / cdk8s-core

Define Kubernetes native apps and abstractions using object-oriented programming
Apache License 2.0
67 stars 26 forks source link

Problem using dictionary in lieu of `k8s.WeightedPodAffinityTerm` #874

Open LS80 opened 1 year ago

LS80 commented 1 year ago

The following example produces a valid pod manifest

from constructs import Construct
from cdk8s import App, Chart

from imports import k8s

class MyChart(Chart):
    def __init__(self, scope: Construct, id: str):
        super().__init__(scope, id)

        k8s.KubePod(self, "pod",
            metadata=k8s.ObjectMeta(name="pod-affinity"),
            spec=k8s.PodSpec(
                affinity=k8s.Affinity(
                    pod_anti_affinity=dict(
                        preferred_during_scheduling_ignored_during_execution=[
                            k8s.WeightedPodAffinityTerm(
                                weight=100,
                                pod_affinity_term=dict(
                                    label_selector=dict(match_labels=dict(foo="bar")),
                                    topology_key="topology.kubernetes.io/zone"
                                )
                            )
                        ]
                    )
                ),
                containers=[
                    k8s.Container(
                        name="pod-affinity",
                        image="registry.k8s.io/pause:2.0"
                    )
                ],
            )
        )

app = App()
MyChart(app, "pod")

But using a dictionary in place of k8s.WeightedPodAffinityTerm produces an error Missing required properties for k8s.WeightedPodAffinityTerm: 'podAffinityTerm'.

from constructs import Construct
from cdk8s import App, Chart

from imports import k8s

class MyChart(Chart):
    def __init__(self, scope: Construct, id: str):
        super().__init__(scope, id)

        k8s.KubePod(self, "pod",
            metadata=k8s.ObjectMeta(name="pod-affinity"),
            spec=k8s.PodSpec(
                affinity=k8s.Affinity(
                    pod_anti_affinity=dict(
                        preferred_during_scheduling_ignored_during_execution=[
                            dict(
                                weight=100,
                                pod_affinity_term=dict(
                                    label_selector=dict(match_labels=dict(foo="bar")),
                                    topology_key="topology.kubernetes.io/zone"
                                )
                            )
                        ]
                    )
                ),
                containers=[
                    k8s.Container(
                        name="pod-affinity",
                        image="registry.k8s.io/pause:2.0"
                    )
                ],
            )
        )

app = App()
MyChart(app, "pod")

app.synth()

I assume it's an example of the issue discussed in https://github.com/aws/jsii/issues/1919 (Passing dicts in lieu of jsii structs does not consistently work).

github-actions[bot] commented 7 months ago

This issue has not received any attention in 1 year and will be closed soon. If you want to keep it open, please leave a comment below @mentioning a maintainer.

LS80 commented 7 months ago

@iliapolo is this something for the upstream https://github.com/aws/jsii to fix or can it be fixed here?

iliapolo commented 3 months ago

Interesting. This however does work:

from constructs import Construct
from cdk8s import App, Chart

from imports import k8s

class MyChart(Chart):
    def __init__(self, scope: Construct, id: str):
        super().__init__(scope, id)

        k8s.KubePod(self, "pod",
            metadata=k8s.ObjectMeta(name="pod-affinity"),
            spec=k8s.PodSpec(
                affinity=k8s.Affinity(
                    pod_anti_affinity=dict(
                        preferred_during_scheduling_ignored_during_execution=[
                            dict(
                                weight=100,
                                podAffinityTerm=dict(
                                    labelSelector=dict(match_labels=dict(foo="bar")),
                                    topologyKey="topology.kubernetes.io/zone"
                                )
                            )
                        ]
                    )
                ),
                containers=[
                    k8s.Container(
                        name="pod-affinity",
                        image="registry.k8s.io/pause:2.0"
                    )
                ],
            )
        )

app = App()
MyChart(app, "pod")

app.synth()

(Note the casing of properties inside the dict)

It looks like case conversion doesn't happen for literal dictionaries. It kind of makes sense because there's no apparent reason a literal dictionary should go through case conversion. jsii would have to detect that dictionary is being in lieu of a struct, right now it just passes it through. Definitely a jsii issue, i'll check with the team and update here.

Thanks for reporting!

iliapolo commented 3 months ago

So not much we can do here, the behavior here is explained in this issue (see Passing dict in lieu of jsii structs does not consistently work). Keeping this open for discoverability, but this will have to wait for a jsii fix.