Operator-sdk: label.Selector based only on the key

Created on 14 Oct 2019  路  7Comments  路  Source: operator-framework/operator-sdk

I'm upgrading from the operator SDK 0.10.0 to 0.11.0 and I'm having some trouble in migrating a specific code.

According to the changelog, I have to move from &client.ListOptions{} to []client.ListOption{}. We have a code that was using SetLabelSelector to set an option to retrieve all objects of a certain type containing a label key, independent of the value. What's the current way to accomplish that, given that SetLabelSelector doesn't seem to exist anymore?

Our code up to v0.10.0:

func (b *Background) cleanDeployments() {
    ...
    deployOpts := &client.ListOptions{}

    // Only select fields that have the label 'sidecar.jaegertracing.io/injected'
    if err := deployOpts.SetLabelSelector(inject.Label); err != nil {
        log.WithError(err).Error("error cleaning orphaned deployment")
    }

    ...

    if err := b.cl.List(context.Background(), deployOpts, deployments); err != nil {
        log.WithError(err).Error("error cleaning orphaned deployment")
    }
}

Workaround:

func (b *Background) cleanDeployments() {
    ...
    deployOpts := []client.ListOption{
        matchingLabelKeys(map[string]string{inject.Label: ""}),
    }

    ...

    if err := b.cl.List(context.Background(), deployments, deployOpts...); err != nil {
        log.WithError(err).Error("error cleaning orphaned deployment")
    }
}
type matchingLabelKeys map[string]string

func (m matchingLabelKeys) ApplyToList(opts *client.ListOptions) {
    sel := labels.NewSelector()
    for k := range map[string]string(m) {
        req, err := labels.NewRequirement(k, selection.Exists, []string{})
        if err != nil {
            log.Warnf("failed to build label selector: %v", err)
            return
        }
        sel.Add(*req)
    }
    opts.LabelSelector = sel
}
triagsupport

Most helpful comment

@jpkrohling Ah, you're right I misread what you were trying to do initially.
I need to test this out but can you try setting the MatchingLabelsSelector option in that case?
Still a bit wonky setting up the selector but that's something that we might need to make easier upstream.

sel := labels.NewSelector()
req, err := labels.NewRequirement("mykey", selection.Exists, []string{})
if err != nil {
    ....
}
sel.Add(*req)

deployOpts := []client.ListOption{
  client.MatchingLabelsSelector{Selector: sel},
  ...
}

b.cl.List(context.Background(), deployments, deployOpts...)

All 7 comments

@jpkrohling You should be able to use the client.MatchingLabels() option to set the LabelSelector for your ListOption.

https://github.com/kubernetes-sigs/controller-runtime/blob/16c93b06b0a8cb99e002529cb1b43dce5e489baf/pkg/client/options.go#L378-L385

deployOpts := []client.ListOption{
  client.MatchingLabels{inject.Label: ""},
  client.InNamespace("foo"),
  ...
}

b.cl.List(context.Background(), deployments, deployOpts...)

It's pretty much the same thing as your workaround.

That's my source of inspiration. The problem with that is that it calls labels.SelectorFromSet(), which later calls labels.NewRequirement(k, selection.Equal, []string{}), so that both the key and the value has to match. Note how I use selection.Exists in my snippet, so that I only check for the key's existence.

Hi @jpkrohling,

Following a suggestion. The following code will List all Deployments in the namespace = foo and wth the label app=example-label.

    labelSelector := labels.SelectorFromSet(map[string]string{"app": "example-label"})
    listOps := &client.ListOptions{Namespace: "foo", LabelSelector: labelSelector}

        deploymentList := &v1.DeploymentList{}
    err := client.List(context.TODO(), deploymentList, listOps)
    if err != nil {
        return nil, err
    }

Also, you can check a similar code impl working well here

Please, let us know if it is what you are looking for and if we can close this one.

@camilamacedo86, my use case is somewhat different: I want all deployments with a label key app, not caring about the label's value.

When using SelectorFromSet, it uses the operator selection.Equals, which will use the value for comparison as well. I need a SelectorFromSet that uses selection.Exists instead.

@hasbro17, I just tried your example, and it attempts to also match a value (empty). In case you want to try it out on a concrete case, this is the relevant code and this here is the test that asserts the right behavior.

@jpkrohling Ah, you're right I misread what you were trying to do initially.
I need to test this out but can you try setting the MatchingLabelsSelector option in that case?
Still a bit wonky setting up the selector but that's something that we might need to make easier upstream.

sel := labels.NewSelector()
req, err := labels.NewRequirement("mykey", selection.Exists, []string{})
if err != nil {
    ....
}
sel.Add(*req)

deployOpts := []client.ListOption{
  client.MatchingLabelsSelector{Selector: sel},
  ...
}

b.cl.List(context.Background(), deployments, deployOpts...)

HI @jpkrohling,

I am closing this one since shows that the above comment answered your question. Also, note that it is more related to k8s projects instead of SDK since it came from https://github.com/kubernetes-sigs/controller-runtime.

However, please feel free to re-open it if you see that is required.

+1, the code from @hasbro17 is similar to what I have in the first comment and what I ended up using. I still think there could be a shortcut, but I can live without it.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

lsalazar1 picture lsalazar1  路  4Comments

chowmean picture chowmean  路  5Comments

joelanford picture joelanford  路  3Comments

smiklosovic picture smiklosovic  路  4Comments

nrvnrvn picture nrvnrvn  路  5Comments