Skip to content
This repository has been archived by the owner on Feb 27, 2023. It is now read-only.

Optional loadBalancerIP input in Contour #336

Merged
merged 2 commits into from
Jun 9, 2021
Merged

Optional loadBalancerIP input in Contour #336

merged 2 commits into from
Jun 9, 2021

Conversation

BostjanBozic
Copy link
Contributor

@BostjanBozic BostjanBozic commented Apr 30, 2021

Adds support for configuring Load Balancer IP in Envoy service. Previously, loadBalancerIP parameter of Envoy service was dynamically configured (from provider's side). Now user can preconfigure desired IP. This PR adds the following:

  • option to provide cloud provider specific parameters under Spec.NetworkPublishing.Envoy.LoadBalancer.ProviderParameters.<cloudProvider>
  • Those parameters include LoadBalancer IP configuration as well as cloud provider specific annotations
  • Unit tests for new parameters
  • Validation of LoadBalancer IP parameter

Updates #326
Updates #327

Signed-off-by: Bostjan Bozic [email protected]

@BostjanBozic BostjanBozic requested a review from a team as a code owner April 30, 2021 18:06
@BostjanBozic BostjanBozic requested review from stevesloka and youngnick and removed request for a team April 30, 2021 18:06
@@ -260,6 +260,9 @@ func DesiredEnvoyService(contour *operatorv1alpha1.Contour) *corev1.Service {
switch epType {
case operatorv1alpha1.LoadBalancerServicePublishingType:
svc.Spec.Type = corev1.ServiceTypeLoadBalancer
if len(contour.Spec.NetworkPublishing.Envoy.LoadBalancer.ProviderParameters.LoadBalancerIP) > 0 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think a test in service_test.go would be sufficient to ensure we have coverage over this change

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unit test added with hardcoded IP address.

@@ -266,6 +266,11 @@ type ProviderLoadBalancerParameters struct {
// +unionDiscriminator
// +kubebuilder:default=AWS
Type LoadBalancerProviderType `json:"type,omitempty"`

// loadBalancerIP contains IP for LoadBalancer service type, optional
// +kubebuilder:validation:Format=ipv4
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we allow ipv6 ips as well?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking about it, but I am not sure how to handle this. Problem is that of course format can only be specified one, so e.g. I can not do Format=ipv4;ipv6. So I was thinking, either we move everything to validation.go, we have separate field (e.g. LoadBalancerIPv6), or maybe simplest way would be to just use same spec as it is implemented into * corev1.ServiceSpec (json:"loadBalancerIP,omitempty" protobuf:"bytes,8,opt,name=loadBalancerIP").

What do you think, maybe last option would make the most sense as it follows same structure as corev1?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we can leave it as IPv4 for now and if there is some requirement for IPv6, we can add support later?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i would be on the side of letting ipv6 addresses be specified to start (probably via adding to the validation code we have today, probably simple enough to use net.ParseIP), though leaving things restricted and opening it up when needed make sense

I would like to get other @projectcontour/maintainers thoughts on this as well

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would expect LoadBalancerIP to be a field of LoadBalancerStrategy or ProviderLoadBalancerParameters. @BostjanBozic a few questions:

  • Currently LoadBalancerStrategy supports LB scope. Must a loadbalancer have an external scope to specify an IP?
  • Do the major cloud providers support specifying a LB IP? From the Service spec: "This feature depends on whether the underlying cloud-provider supports specifying the loadBalancerIP when a load balancer is created. This field will be ignored if the cloud-provider does not support the feature." If this feature is not widely supported, it should be part of ProviderLoadBalancerParameters.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@danehans Cheers for feedback. I put that under ProviderLoadBalancerParameters, as I was thinking this is parameter of LB itself, just for naming semantics. If preferred I can move it to LoadBalancerStrategy as well.
Regarding questions:

  • As far as I am aware, you can also use this when externalTrafficPolicy: Local, but you need to provide IP in correct subnet
  • I did check AWS, Azure and GCP. For Azure and GCP they support explicitly configuring loadBalancerIP (with Azure you might have to configure annotation in case public IP resource is configured in another resource group - Pass annotations to created resource #327). For AWS, you have to provide annotation service.beta.kubernetes.io/aws-load-balancer-eip-allocations. Good thing is if cloud provider does not support setting loadBalancerIP, this field will be ignored.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ProviderLoadBalancerParameters is meant for passing provider-specific LB config and why I'm trying to understand what cloud providers support specifying a LB IP. If the major cloud providers support the ability to specify an IP, then the field should be in LoadBalancerStrategy.

As far as I am aware, you can also use this when externalTrafficPolicy: Local, but you need to provide IP in correct subnet

I'm referring to this scope, which is used to specify an internal or external LB. All major cloud providers support specifying an internal load-balancer, hence why scope is part of LoadBalancerStrategy and not ProviderLoadBalancerParameters. I'm trying to understand how specifying loadBalancerIP will work with scope: internal. Do cloud providers allow an internal LB scope when loadBalancerIP is set? If so, so any limitations exist? For example, cloud provider foo allows loadBalancerIP with internally-scoped LB but the address must be from "10.x.x.x/8".

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is only Azure and GCP that are currently supporting providing exact IP. In AWS's case, binding existing LB to a service requires annotating the service. So basically following is the statue:

  • Azure: you can specify loadBalancerIP parameter, in case of Public IP resource being in another resource group, you have to annotate service with service.beta.kubernetes.io/azure-load-balancer-resource-group: myResourceGroup
  • GCP: you can specify loadBalancerIP parameter, I think nothing else is required
  • AWS: specifying loadBalancerIP parameter has no effect, service needs annotation (service.beta.kubernetes.io/aws-load-balancer-eip-allocations: eipalloc-<xxxxxxxxxxxxxxxxx>)

Regarding the scope, I was only able to test Azure. Using internal scope following results occur:

  • loadBalancerIP parameter can be configured
  • loadBalancerIP needs to be in specific range
  • when creating Public IP on Azure, this will never be in range required above, hence preconfiguring loadBalancerIP parameter for Internal scope makes no sense, at least for Azure

Copy link

@jroper jroper May 13, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have used GCP in this way - allocate a regional static IP address, and that set that IP address to the loadBalancerIP in the service, and it works. We do it this way because we have all our networking created in terraform - so we let terraform create the IP addresses, and then also let terraform create the DNS records that depend on those IP addresses. Then when we run our k8s deployment, we get the IP from terraform and plug it into our k8s deploy scripts.

Copy link
Member

@sunjayBhatia sunjayBhatia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this might need a main merge/rebase to get rid of some of the extraneous changes that crept in

@@ -112,6 +112,14 @@ func checkServiceHasExternalTrafficPolicy(t *testing.T, svc *corev1.Service, pol
}
}

func checkServiceHasLoadBalancerIP(t *testing.T, svc *corev1.Service, loadBalancerIp string) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added test seems good to me, thanks

@BostjanBozic
Copy link
Contributor Author

I think this might need a main merge/rebase to get rid of some of the extraneous changes that crept in

@sunjayBhatia That is the confusing part, I actually did a rebase on my branch (as it is stated in dev guide that rebase is required before merge) and those changes crawled in.
What I did was:

git remote add upstream https://github.com/projectcontour/contour-operator.git
git fetch upstream
git rebase upstream/main
git push

Am I missing something here/doing it wrong?

@danehans
Copy link
Contributor

danehans commented May 4, 2021

@BostjanBozic it looks like you have #317 are part of this PR. Can you please clean this up so the PR is easier to review?

@BostjanBozic
Copy link
Contributor Author

@BostjanBozic it looks like you have #317 are part of this PR. Can you please clean this up so the PR is easier to review?

@danehans Yes, it seems that after rebasing my fork, this mess occurred. Should I just checkout commit that I forked from and provide changes or actually just remove changes in my latest commit (I am worried that this PR would then overwrite work that was already done)?
Sorry for those basic questions, but I never pushed from fork into repo that was actively developed :)

@danehans
Copy link
Contributor

danehans commented May 4, 2021

@BostjanBozic give these steps a try:

  1. Reset to the head of upstream main (note: commit 790b3f5 is the most recent commit to the main branch at the time of this writing):
git reset 790b3f5
  1. Remove your changes:
git checkout .
  1. Make sure your branch is up-to-date with upstream (note "origin" is the name of my upstream remote):
git pull origin main
  1. Git status should now show PR Updates Kind Node Image #342 as the latest commit for your branch:
$ git log --oneline
790b3f5 (HEAD -> BostjanBozic-main) Adds Envoy NodePort Service Configuration (#317)
b5e3b45 Bump Gateway API dependency (#324)
<SNIP>
  1. Apply your commits:
git cherry-pick 00fc58aade01ed77dd2621d43717533ef6dbedf9
git cherry-pick 50f9f9938b649cff5fd02319f06bc859480dbfb2
git cherry-pick d6310de0db05c60c74368264dbb65243159b64dc
git cherry-pick c037a9647931e6828e395b9c8393f47a9729abd9
  1. Git status should now show commit 512aa2c as the latest commit for your branch:
$ git log --oneline
512aa2c (HEAD -> BostjanBozic-main) fix checkServiceHasLoadBalancerIP param for lint
ec75f44 renamed loadbalancer func and var for lint
<SNIP>
  1. Push your local branch to your remote:
git push BostjanBozic main --force

In the future, it's a good idea to create a topic branch when developing, instead of using "main/master". Check out the Kubernetes GitHub workflow guide for additional details.

@danehans
Copy link
Contributor

danehans commented May 4, 2021

@BostjanBozic xref #327 (comment) for my thoughts on next steps to add this feature. I'm happy to help you through the process.

@BostjanBozic
Copy link
Contributor Author

BostjanBozic commented May 5, 2021

@danehans Sounds good, I guess I will wait until we decide on the structure and I can review the implementation. As mentioned in #327 it would make sense to provide cloudProvider type under providerParameters.

Discussion docs available here.

Also thanks again for helping me reset changes on my PR, it did the trick :)

@BostjanBozic
Copy link
Contributor Author

BostjanBozic commented May 6, 2021

I have set things up based on doc, just so we can take a look how this would work out.
I have added new structure, updated service.go and added validation test in service_test.go. Also load balancer IP filed validation has been added (accepts IPv4 and IPv6 format).

@BostjanBozic
Copy link
Contributor Author

BostjanBozic commented May 10, 2021

@projectcontour/maintainers @sunjayBhatia @danehans Once you have time, can you check the changes and let me know if that sort of implementation would be suitable or would we change the structure?
Description of implementation and reasoning can be found in docs.

Copy link
Contributor

@danehans danehans left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This review focused on the API changes. I would like to get the API changes solidified before reviewing the implementation.

api/v1alpha1/contour_types.go Outdated Show resolved Hide resolved
api/v1alpha1/contour_types.go Outdated Show resolved Hide resolved
api/v1alpha1/contour_types.go Outdated Show resolved Hide resolved
api/v1alpha1/contour_types.go Outdated Show resolved Hide resolved
internal/objects/service/service.go Outdated Show resolved Hide resolved
internal/objects/service/service.go Outdated Show resolved Hide resolved
internal/objects/service/service.go Outdated Show resolved Hide resolved
@codecov
Copy link

codecov bot commented May 12, 2021

Codecov Report

Merging #336 (f8d4d33) into main (96a17cb) will increase coverage by 0.56%.
The diff coverage is 72.72%.

❗ Current head f8d4d33 differs from pull request most recent head 5d30ec7. Consider uploading reports for the commit 5d30ec7 to get more accurate results
Impacted file tree graph

@@            Coverage Diff             @@
##             main     #336      +/-   ##
==========================================
+ Coverage   58.18%   58.75%   +0.56%     
==========================================
  Files          19       19              
  Lines        1801     1862      +61     
==========================================
+ Hits         1048     1094      +46     
- Misses        727      741      +14     
- Partials       26       27       +1     
Impacted Files Coverage Δ
internal/equality/equality.go 54.80% <0.00%> (-0.81%) ⬇️
pkg/validation/validation.go 77.60% <0.00%> (-8.25%) ⬇️
internal/objects/service/service.go 61.03% <100.00%> (+10.14%) ⬆️
internal/objects/daemonset/daemonset.go 84.31% <0.00%> (+0.06%) ⬆️
internal/objects/deployment/deployment.go 82.06% <0.00%> (+0.08%) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 96a17cb...5d30ec7. Read the comment docs.

@jroper
Copy link

jroper commented May 13, 2021

@BostjanBozic Thanks for this! It's exactly what I need!

@danehans
Copy link
Contributor

@BostjanBozic thanks again for working on this feature and for your patience to get #366 merged. I think this PR is very close. I provided a few comments that need to get resolved before I can /approve. We're cutting v1.16.0 tomorrow. I'll check this PR before cutting the release to see if it's ready to be included in the release.

@youngnick
Copy link
Member

youngnick commented May 27, 2021

I don't think this one will make 1.16, pushing to 1.17. @danehans, please undo if you think otherwise.

@youngnick youngnick modified the milestones: 1.16.0, 1.17.0 May 27, 2021
@BostjanBozic
Copy link
Contributor Author

@danehans Thank you for feedback, I pushed latest changes, PTAL. Only thing that I am not sure about is equality for LoadBalancerIP - as mentioned in review I am not running into any errors there and at least from usability this seems to work as desired.

@danehans
Copy link
Contributor

danehans commented May 27, 2021

as mentioned in review I am not running into any errors there and at least from usability this seems to work as desired.

@BostjanBozic you will not see any errors because it's not an error condition. Every time the operator reconciles the Contour, the equality (current vs desired) comparison will fail, causing the operator to update the Envoy service. This is because we do not set the this field in desired. You can simulate by running the operator locally, creating a Contour, ensure it becomes "Available" and check the resourceVersion. Every time the operator reconciles the COntour, it will update the Envoy service resourceVersion. Simulate a periodic reconcile by restarting the operator, check the Envoy service resource version and you should a new version.

@BostjanBozic
Copy link
Contributor Author

@danehans thank you for explanation. I am a bit lost though how can I fix this, can you maybe help me with this?
I added this part since this was the only way I was able to update loadBalancerIP once I change this in Contour.

@danehans
Copy link
Contributor

@danehans
Copy link
Contributor

danehans commented Jun 2, 2021

@BostjanBozic please ignore #336 (comment). This PR looks good and should be ready to merge after fixing the merge conflicts and resolving #336 (comment).

@danehans
Copy link
Contributor

danehans commented Jun 2, 2021

@BostjanBozic can you also add unit tests to cover the changes you made to the validation pkg?

@danehans
Copy link
Contributor

danehans commented Jun 2, 2021

@BostjanBozic a few more comments above that need to get resolved... this PR is very close. Thanks again for your efforts and I look forward to getting this merged.

@BostjanBozic
Copy link
Contributor Author

@danehans Thanks for check again, I will get this fixed on Friday and rebase everything. For unit test they are configured in test/e2e/operator_test.go right?

@danehans
Copy link
Contributor

danehans commented Jun 2, 2021

For unit test they are configured in test/e2e/operator_test.go right?

test/e2e/operator_test.go are the e2e tests. Unit tests are in the accompanying go file, i.e. validation_test.go for validation.go.

@danehans
Copy link
Contributor

danehans commented Jun 2, 2021

xref #379 for adding e2e tests (requires testing against cloud-specific clusters).

Copy link
Contributor

@danehans danehans left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Most of the feedback is small nits, but there are a few areas related to testing and validation that need to get resolved. PTAL and let me know if you have any questions and thank you for your efforts in supporting the project.

api/v1alpha1/contour_types.go Outdated Show resolved Hide resolved
api/v1alpha1/contour_types.go Outdated Show resolved Hide resolved
api/v1alpha1/contour_types.go Outdated Show resolved Hide resolved
api/v1alpha1/contour_types.go Outdated Show resolved Hide resolved
api/v1alpha1/contour_types.go Outdated Show resolved Hide resolved
internal/objects/service/service_test.go Show resolved Hide resolved
internal/objects/service/service_test.go Outdated Show resolved Hide resolved
internal/objects/service/service_test.go Outdated Show resolved Hide resolved
pkg/validation/validation.go Outdated Show resolved Hide resolved
api/v1alpha1/contour_types.go Show resolved Hide resolved
@BostjanBozic
Copy link
Contributor Author

@danehans Thanks for looking into it. I went through your review and updated the code, PTAL. Maybe just a comment on changes within equality_test.go - I had to use different provider for my test case, as AWS does not support address parameter, though AWS is required for annotation test to pass. Due to that I changed to GCP there and had to init the parameter block.

Copy link
Contributor

@danehans danehans left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this PR is ready after these comments are resolved.

api/v1alpha1/contour_types.go Outdated Show resolved Hide resolved
pkg/validation/validation.go Outdated Show resolved Hide resolved
pkg/validation/validation.go Show resolved Hide resolved
pkg/validation/validation.go Outdated Show resolved Hide resolved
pkg/validation/validation_test.go Outdated Show resolved Hide resolved
pkg/validation/validation_test.go Outdated Show resolved Hide resolved
Copy link
Member

@sunjayBhatia sunjayBhatia left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

looks good, config piped through to the service

just one comment about structure of the equality test suite that can be taken into another issue

cntr.Spec.NetworkPublishing.Envoy.Type = operatorv1alpha1.LoadBalancerServicePublishingType
cntr.Spec.NetworkPublishing.Envoy.LoadBalancer.Scope = operatorv1alpha1.ExternalLoadBalancer
cntr.Spec.NetworkPublishing.Envoy.LoadBalancer.ProviderParameters.Type = operatorv1alpha1.AWSLoadBalancerProvider
if tc.description == "if load balancer IP changed" {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

might be ok to leave now, but would be good to refactor this test suite a bit

we're currently taking a global contour variable and modifying it in each test case, then generating a service from that contour, would be good to not use that global variable if we are going to modify it and for this test just create a service object to check the equality against with the copy/mutate pattern which is there and nice to avoid duplication

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking the same. It would be quite a change, as it would affect whole equality_testo.go, so I thought we can set this up separately.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
release-note Denotes a PR that will be considered when it comes time to generate release notes.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants