apply annotation tags to NLB & listeners on update#1447
Conversation
|
This issue is currently awaiting triage. If cloud-provider-aws contributors determine this is a relevant issue, they will accept it by applying the The DetailsInstructions 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-sigs/prow repository. |
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
|
Hi @alimx07. Thanks for your PR. I'm waiting for a kubernetes member to verify that this patch is reasonable to test. If it is, they should reply with Regular contributors should join the org to skip this step. Once the patch is verified, the new status will be reflected by the I understand the commands that are listed here. DetailsInstructions 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-sigs/prow repository. |
| err := c.addLoadBalancerTagsv2(ctx, aws.ToString(listener.ListenerArn), tags) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| } | ||
| } | ||
| } | ||
|
|
||
| if err := c.addLoadBalancerTagsv2(ctx, aws.ToString(loadBalancer.LoadBalancerArn), tags); err != nil { |
There was a problem hiding this comment.
I think we'll need to implement some guardrails to safeguard user-provided tags through ServiceAnnotationLoadBalancerAdditionalTags replace controller-managed/private tags, such as Name, kubernetes.io/cluster/<cluster-id>, etc (which else?) in aws_validations?
There was a problem hiding this comment.
Good point, addressed. I think controller-managed tags are kubernetes.io/cluster/<cluster-id> , KubernetesCluster and kubernetes.io/service-name. All three are guarded in the validation now, but I couldn't find a Name tag being set anywhere in the NLB path, could you point me to where you're seeing it or other worth guarded tags so I can take a look?
There was a problem hiding this comment.
thanks for restricting those.
you are correct, Name is not added, only LB name is set, which is different. I don't have a strong opinion about restricting Name since lookup may also use LB names. cc @kmala
e4e212b to
a10c415
Compare
|
preface: trying to understand more about cloud providers, a question just for my own understanding is the case for tag updates that we only care to propagate new tags on reconcile but not remove removed tags on reconcile? |
Hi @joshuakguo, Actually we should, this is missing right. I also noticed this was not found in the previous version on EnsureLoadBalancer. May need clarification from a maintainer, but what comes to my mind now as the simplest way is doing describeTags then diff and call remove on the extra ones. Thanks for mentioning this. |
I think the reason is because we don't have a way to know who owns the tags on the LB, since these are values of annotations. |
|
@kmala , Could we borrow from 3 way merge and introduce new annotation (e.g. something/last-applied-tags) as some sort of ownership we can diff on it? |
| return &elbv2.DeleteListenerOutput{}, nil | ||
| } | ||
|
|
||
| func TestEnsureLoadBalancerv2_TaggingLifecycle(t *testing.T) { |
There was a problem hiding this comment.
Thanks for setting up those test. Since you are defining a function to test EnsureLoadBalancerv2, I'd suggest:
- use Table-Driven test pattern like other units defined in this file for further maintenance :)
- making this more generically to allow other scenarios that may want to validate the annotations more generically through EnsureLoadBalancerv2 it can be expanded by adding cases, reusing all that pre-setup (mocks ec2, etc)
- Use
t.Context()instead ofcontext.TODO()
There was a problem hiding this comment.
Okay @mtulio , Makes sense on the table-driven pattern and t.Context(), will update both.
Just for clarification on the generic part, the goal is a table-driven TestEnsureLoadBalancerv2_annotations* where each case exercises a different annotation and asserts what it produces, So some tests like this, could be just cases included in our new generic one ?
There was a problem hiding this comment.
on the generic part, the goal is a table-driven TestEnsureLoadBalancerv2_annotations* where each case exercises a different annotation and asserts what it produces,
yeah, I think that way will pave a good path for annotations' tests maitainance and increase coverage. thanks
There was a problem hiding this comment.
@mtulio, I have done it. Would love if you any considerations about the test now?
|
|
||
| // addLoadBalancerTagsv2 tags a single ELBv2 resource (LB, listener, target group, etc.). | ||
| // https://github.com/aws/aws-sdk-go-v2/blob/dc2d13fa6f1db25f1c6d804567e1ecfcdff4f040/service/elasticloadbalancingv2/api_op_AddTags.go#L14 | ||
| func (c *Cloud) addLoadBalancerTagsv2(ctx context.Context, resourceARN string, requested map[string]string) error { |
There was a problem hiding this comment.
we need to skip api calls when no tags requested to added
| for port, protocols := range actual { | ||
| for protocol, listener := range protocols { | ||
| if _, ok := frontEndPorts[port][protocol]; ok { | ||
| err := c.addLoadBalancerTagsv2(ctx, aws.ToString(listener.ListenerArn), tags) |
There was a problem hiding this comment.
is it possible to save API calls here from N to 1 AddTag, or less? by:
- check if needs update tags and
- send ARN list (LB + listeners), instead each ARN?
There was a problem hiding this comment.
it seems that even AddTags take a slice , this error comes from the SDK when trying to update more than one.
An error occurred (ValidationError) when calling the AddTags operation: Only one resource can be tagged at a timeSo this is limitation from AWS API for now.
But you are right , we could save API calls by first call DescribeTags , then diff with tags and call AddTags per listener if needed. It will be either 1 or N+1 calls.
| return &elbv2.DeleteListenerOutput{}, nil | ||
| } | ||
|
|
||
| func TestEnsureLoadBalancerv2_TaggingLifecycle(t *testing.T) { |
There was a problem hiding this comment.
on the generic part, the goal is a table-driven TestEnsureLoadBalancerv2_annotations* where each case exercises a different annotation and asserts what it produces,
yeah, I think that way will pave a good path for annotations' tests maitainance and increase coverage. thanks
a10c415 to
d4f63bb
Compare
| } | ||
| } | ||
|
|
||
| // Reconcile tags on the LB and all surviving listeners. Newly created |
There was a problem hiding this comment.
I feel this block is not added in the better place it should be, or at least ensureLoadBalancerResourceTagsv2 is not.
Here is the end of the big block to "sync mappings" inside the existing LB synchronization.
Considering the problem below, I think this could be added the end of the ensureLoadBalancerV2 function using the "reconciliation pattern".
Question 1) tag removal support
Question/issue I am seeing here: how that annotation controls removed tags? given the following scenrio:
t1) user update a svc with tags: env=prod,team=eng
t2) user remove a tag team=eng: will team tag leaked ?
t3) user remove the tag annotation: will env tag leaked?
How we could prevent that "leak"?
Question 2) Tagging TGs
do we need tag the target groups as may be part of the "load balancer resources"?
There was a problem hiding this comment.
For Tag removal support. the current approach will not handle that and we could say tags will leak , which is the same approach for the old ensureLoadBalancer. The reason is we do not have a way to know who owns tags on LB (e.g. user , controller, terraform) , so naive describe + remove will not work.
we could borrow from 3 way merge and introduce new controller managed annotation (e.g. something/last-applied-tags) as some sort of ownership we can diff on it, if we are okay with that, I could go on with it.
For TGs, tags are obtained through the reconcile loop, so you're right. I missed that and was focused only on Listeners. With the current approach, this should be a minimal amount of work. Nice pushback :)
There was a problem hiding this comment.
we could borrow from 3 way merge and introduce new controller managed annotation (e.g. something/last-applied-tags) as some sort of ownership we can diff on it, if we are okay with that, I could go on with it.
yep, that's a good idea. Beforehand I would check what is the AWS LBC behavior of existing BYO tags, so we can prevent diverging from there and here in the UX. (I quickly read the documentation and did't get much insights)
Having said that, exploring that idea of managing the BYO tags, it indeed be interesting to have controller managed tag managing which tag is added (through values containing tagKeys?)
For example:
when annotation is added: service.beta.kubernetes.io/aws-load-balancer-additional-resource-tags: team=squadA,env=stage,group=tmp
The resource will have tags - managed by controller:
- kubernetes.io/cloud-provider/managed-tags: team,env,group
- team: squadA
- env: stage
- group: tmp
when annotation is updated to service.beta.kubernetes.io/aws-load-balancer-additional-resource-tags: team=squadA,env=prod (removing group, new value for env)
the tag reconciliation will detect drift from annotation vs managed tag, and remove stale tag group, as well update value of env, resulting in resource tags:
- kubernetes.io/cloud-provider/managed-tags: team,env
- team: squadA
- env: prod
when annotation is removed from service, the tag reconciliation must detect managed tag exists in the resource && annotation not present, resulting in removing any tag added by controller, as well the managed tag
There was a problem hiding this comment.
Hi @mtulio, sorry for the late reply.
For AWS LBC, it assumes itself as the source of truth for tags. On each reconcile it removes any tag not in its desired set (annotation tags + --default-tags + its own tracking tags), except AWS system tags (aws:*) and anything listed in --external-managed-tags (code, docs).
Maybe I am wrong but I went with managed annotation for now to a tag propagation feature, it could be a weird UX, but matching LBC here making CCM authoritative seems like a breaking change I think.
| cfg.svc = updated | ||
|
|
||
| framework.Logf("verifying updated tag e2e-tag=two on LB and listeners") | ||
| framework.ExpectNoError(verifyResourceTags(cfg.ctx, elbClient, arns, map[string]string{"e2e-tag": "two"})) |
There was a problem hiding this comment.
I believe you need to refresh listenerARNs as you are updating the svc, if the controller eventually recreate the listener you'll be checking old listener (if not delted) which will have old tag values
There was a problem hiding this comment.
okay I will do it.
Just a question for curiosity, in the reconcile loop, listeners only deleted/created as ports mapping change. while this test just update some tags, so why recreation could happen here?
d4f63bb to
4055028
Compare
4055028 to
0e01ab6
Compare
|
[APPROVALNOTIFIER] This PR is NOT APPROVED This pull-request has been approved by: The full list of commands accepted by this bot can be found here. DetailsNeeds approval from an approver in each of these files:Approvers can indicate their approval by writing |
Previously, ServiceAnnotationLoadBalancerAdditionalTags was only applied to the LB resource at creation time via CreateLoadBalancer input tags. Listeners received no tags on subsequent reconcile calls. Introduce addLoadBalancerTagsv2 which calls AddTags per-ARN. On every ensureLoadBalancerv2 update pass, tag all surviving listeners individually, then tag the LB itself, so annotation tag changes propagate to both resources. Add validateServiceAnnotationAdditionalTags func which validates that additional tags do not override controller-managed tags Add unit tests covering create→tag and multi-listener modify+delete lifecycle Add e2e-test covering propagation of additional tags on creation and update
0e01ab6 to
32f882c
Compare
|
/ok-to-test |
|
@alimx07: The following test failed, say
Full PR test history. Your PR dashboard. Please help us cut down on flakes by linking to an open issue when you hit one in your PR. DetailsInstructions 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-sigs/prow repository. I understand the commands that are listed here. |
What type of PR is this?
/kind bug
What this PR does / why we need it:
service.beta.kubernetes.io/aws-load-balancer-additional-resource-tags was only applied to the NLB at creation time (via
CreateLoadBalancerinput tags). Annotation changes never propagated on later reconciles, and removed tags were never cleaned up.This reconciles additional tags on every
ensureLoadBalancerv2update across the load balancer, all surviving listeners, and target groups ,adding/updating to match the annotation and removing keys the controller previously applied but no longer wants.Two tag keys are involved:
service.beta.kubernetes.io/aws-load-balancer-additional-resource-tags: existing user annotation, now propagated on update.kubernetes.io/cloud-controller/managed-tags: new marker tag recording which keys the controller owns, so removed keys are reconciled away while out-of-band and system tags are preserved.Which issue(s) this PR fixes:
Fixes #1334
Special notes for your reviewer:
Does this PR introduce a user-facing change?: