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

Promotes Source IP preservation for Virtual IPs from Beta to GA #41162

Merged
merged 5 commits into from
May 12, 2017

Conversation

MrHohn
Copy link
Member

@MrHohn MrHohn commented Feb 8, 2017

Fixes #33625. Feature issue: kubernetes/enhancements#27.

Bullet points:

  • Declare 2 fields (ExternalTraffic and HealthCheckNodePort) that mirror the ESIPP annotations.
  • ESIPP alpha annotations will be ignored.
  • Existing ESIPP beta annotations will still be fully supported.
  • Allow promoting beta annotations to first class fields or reversely.
  • Disallow setting invalid ExternalTraffic and HealthCheckNodePort on services. Default ExternalTraffic field for nodePort or loadBalancer type service to "Global" if not set.

Release note:

Promotes Source IP preservation for Virtual IPs to GA.

Two api fields are defined correspondingly:
- Service.Spec.ExternalTrafficPolicy <- 'service.beta.kubernetes.io/external-traffic' annotation.
- Service.Spec.HealthCheckNodePort <- 'service.beta.kubernetes.io/healthcheck-nodeport' annotation.

@k8s-ci-robot k8s-ci-robot added the cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. label Feb 8, 2017
@k8s-reviewable
Copy link

This change is Reviewable

@k8s-github-robot k8s-github-robot added kind/api-change Categorizes issue or PR as related to adding, removing, or otherwise changing an API size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files. release-note-label-needed labels Feb 8, 2017
@MrHohn MrHohn force-pushed the esipp-ga branch 2 times, most recently from da4410b to bf27c47 Compare February 9, 2017 02:09
@MrHohn
Copy link
Member Author

MrHohn commented Feb 9, 2017

@bprashanth @thockin

@bprashanth bprashanth assigned thockin and bprashanth and unassigned rootfs Feb 9, 2017
@bprashanth bprashanth added release-note Denotes a PR that will be considered when it comes time to generate release notes. and removed release-note-label-needed labels Feb 9, 2017
@bprashanth bprashanth added this to the 1.6 milestone Feb 9, 2017
@calebamiles calebamiles modified the milestones: v1.6, 1.6 Feb 13, 2017
@k8s-github-robot k8s-github-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Feb 13, 2017
AnnotationValueExternalTrafficGlobal = "Global"

// TODO: The alpha annotations have been deprecated, remove them when we move this feature to GA.
// TODO: The beta annotations have been deprecated, remove them when we release k8s 1.7.
Copy link
Member

Choose a reason for hiding this comment

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

You'll have to send a notice to kubernetes-announce, too

Copy link
Member Author

Choose a reason for hiding this comment

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

Sure, made a note for this.

// If they transition to the first class field, there's no way to go back to beta without
// rolling back the cluster.
if l, ok := service.Annotations[BetaAnnotationExternalTraffic]; ok {
if l == string(api.ServiceExternalTrafficTypeOnlyLocal) {
Copy link
Member

Choose a reason for hiding this comment

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

This would read better as a switch

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

pkg/api/types.go Outdated
// local endpoints only. This preserves Source IP and avoids a second hop for
// LoadBalancer and Nodeport type services.
// +optional
ExternalTraffic ServiceExternalTrafficType
Copy link
Member

Choose a reason for hiding this comment

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

I'm not in love with the naming around this feature (never was). Can we think of anything more expressive?

Maybe externalTrafficPolicy: Local or something a little less awkward?

Copy link
Member Author

Choose a reason for hiding this comment

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

Sounds good, so the candidates would be:

externalTrafficPolicy: Local
externalTrafficPolicy: Global

And do we want to use externalTrafficPolicy: None for the other service types (not NodePort or LoadBalancer)? This field is now being defaulted to empty string ("").

Copy link
Member

Choose a reason for hiding this comment

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

I guess "" is appropriate for those cases. Defaulting (and update) should detect LoadBalancer and NodePort and choose "Global", I think. Thoughts? If we do that then we really should change it back to "" if the type is updated to anything else.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah I have the same thought.
function defaultExternalTrafficIfNeeded() exists for defaulting this field to Global for LoadBalancer and NodePort service if this field is not set.
And function externalTrafficUpdate() exists for changing this field back to "" when the type is updated to others (not LoadBalancer nor NodePort).

Copy link
Member Author

Choose a reason for hiding this comment

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

Changed to use externalTrafficPolicy: Local or Global.

// Validate ExternalTraffic and HealthCheckNodePort have legal value.
if service.Spec.HealthCheckNodePort < 0 {
allErrs = append(allErrs, field.Invalid(specPath.Child("healthCheckNodePort"), service.Spec.HealthCheckNodePort,
"can not set HealthCheckNodePort to negative value"))
Copy link
Member

Choose a reason for hiding this comment

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

error messages should be positive: "must be greater than 0"

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

allErrs = append(allErrs, field.Invalid(specPath.Child("healthCheckNodePort"), service.Spec.HealthCheckNodePort,
"can not set HealthCheckNodePort to negative value"))
}
if service.Spec.ExternalTraffic != "" &&
Copy link
Member

Choose a reason for hiding this comment

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

why is "" allowed? won't defaulting set it to a sane value?

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah I also think this is inappropriate. I'm changing the default value to None.

Copy link
Member Author

Choose a reason for hiding this comment

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

Updates...Per discussion above, "" might be allowed for service that does not have LoadBalancer or NodePort type.

service.Spec.Type != api.ServiceTypeNodePort &&
service.Spec.ExternalTraffic != "" {
allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "externalTraffic"), service.Spec.ExternalTraffic,
"can only be set on nodePort / loadBalancer service"))
Copy link
Member

Choose a reason for hiding this comment

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

NodePort and LoadBalancer

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

(service.Spec.Type != api.ServiceTypeLoadBalancer ||
service.Spec.ExternalTraffic != api.ServiceExternalTrafficTypeOnlyLocal) {
allErrs = append(allErrs, field.Invalid(field.NewPath("spec", "healthCheckNodePort"), service.Spec.ExternalTraffic,
"can only be set on loadBalancer service with ExternalTraffic=OnlyLocal"))
Copy link
Member

Choose a reason for hiding this comment

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

LoadBalancer

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.


_, healthCheckBetaOk := service.Annotations[apiservice.BetaAnnotationHealthCheckNodePort]
_, onlyLocalBetaOk := service.Annotations[apiservice.BetaAnnotationExternalTraffic]
type serviceExternalTrafficAnnotationsUpdateStatus struct {
Copy link
Member

Choose a reason for hiding this comment

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

comments?

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

// to first class fields before making any modifications, even though the
// system continues to respect the beta version.

// On upgrading to a 1.6 cluster, the user is locked in at the current
Copy link
Member

Choose a reason for hiding this comment

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

I'm not sure I get this. Why do we need to stop users from creating new instances with the beta annotation - this is a breaking change, and I don't think we can allow it. Beta has to keep working for at least 1 release, and that means fully working.

Copy link
Member Author

Choose a reason for hiding this comment

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

I wanted to say up-conversion, but saw your old comment about it. Supporting beta annotations for one more release seems to be the right thing to do. I will re-think this part.

Copy link
Member

Choose a reason for hiding this comment

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

Yeah, we should not up-convert

@thockin
Copy link
Member

thockin commented Feb 22, 2017

@bprashanth - when this went Beta I raised an issue wrt healthchecks and NodePort. I can't find the comment - do you recall what the issue I raised was?

@MrHohn MrHohn force-pushed the esipp-ga branch 2 times, most recently from 5d037ff to 7825dcf Compare February 24, 2017 18:02
@k8s-github-robot k8s-github-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Feb 24, 2017
@MrHohn
Copy link
Member Author

MrHohn commented Feb 24, 2017

Per discussion above, updated to fully support both ESIPP Beta annotations and first class fields:

  • Allow legally create Beta annotations or first class fields.
  • Allow legally modify Beta annotations or first class fields.
  • Allow legally delete Beta annotations or reset first class fields.
  • Allow transition from Beta annotations to first class fields or reverse.
  • Disallow mixing Beta annotations with first class fields.

@MrHohn
Copy link
Member Author

MrHohn commented Feb 24, 2017

cc @freehan who might also be familiar with ESIPP validation :)

@k8s-github-robot k8s-github-robot added the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Feb 27, 2017
@k8s-github-robot k8s-github-robot removed the needs-rebase Indicates a PR cannot be merged because it has merge conflicts with HEAD. label Feb 27, 2017
@MrHohn
Copy link
Member Author

MrHohn commented May 7, 2017

Rebased.

Note that most of the worth-looking-at changes are in the first two commits :)

@MrHohn
Copy link
Member Author

MrHohn commented May 9, 2017

/assign @freehan

Many thanks for helping out!

@MrHohn
Copy link
Member Author

MrHohn commented May 11, 2017

Rebased :)

@freehan Any changes to take a look?

@MrHohn
Copy link
Member Author

MrHohn commented May 11, 2017

@k8s-bot kops aws e2e test this, issue: #43236

Copy link
Contributor

@freehan freehan 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 overall. Some nits.

continue
}
return int32(p)
// First check the beta annotation and then the first class field. This is so
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: s/This is so/so that

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.


// ClearExternalTrafficPolicy resets the ExternalTrafficPolicy field.
func ClearExternalTrafficPolicy(service *api.Service) {
// First check the beta annotation and then the first class field. This is so existing
Copy link
Contributor

Choose a reason for hiding this comment

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

same here

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

// SetServiceHealthCheckNodePort sets the given health check node port on service.
// It does not check whether this service needs healthCheckNodePort.
func SetServiceHealthCheckNodePort(service *api.Service, hcNodePort int32) {
// First check the beta annotation and then the first class field. This is so existing
Copy link
Contributor

Choose a reason for hiding this comment

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

same here

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

// DefaultExternalTrafficPolicyIfNeeded defaults the ExternalTrafficPolicy field
// for NodePort / LoadBalancer service to Global for consistency.
// TODO: Move this default logic to default.go once beta annotation is deprecated.
func DefaultExternalTrafficPolicyIfNeeded(service *api.Service) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: s/DefaultExternalTrafficPolicyIfNeeded/SetDefaultExternalTrafficPolicyIfNeeded

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

// DefaultExternalTrafficPolicyIfNeeded defaults the ExternalTrafficPolicy field
// for NodePort / LoadBalancer service to Global for consistency.
// TODO: Move this default logic to default.go once beta annotation is deprecated.
func DefaultExternalTrafficPolicyIfNeeded(service *v1.Service) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: s/DefaultExternalTrafficPolicyIfNeeded/SetDefaultExternalTrafficPolicyIfNeeded

Copy link
Member Author

Choose a reason for hiding this comment

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

Done.

// have legal value.
func validateServiceExternalTrafficFields(service *api.Service) field.ErrorList {
func validateServiceExternalTrafficFieldsValue(service *api.Service) field.ErrorList {
Copy link
Contributor

Choose a reason for hiding this comment

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

Not sure if it is relevant.
Is it allowed to use healthcheck node port with Global?
Is it allowed to mixa usage of beta annotation and fields? (I think annotaion is preempting, so we do not care? )

Copy link
Member Author

Choose a reason for hiding this comment

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

Is it allowed to use healthcheck node port with Global?

Nope, we disallow this case in ValidateServiceExternalTrafficFieldsCombination().

Is it allowed to mixa usage of beta annotation and fields? (I think annotaion is preempting, so we do not care? )

Nope, we disallow this case in validateServiceExternalTrafficAPIVersion().

Sorry for the mess, but I have to separate the validation into multiple pieces because they are called in different phases --- especially ValidateServiceExternalTrafficFieldsCombination() needs to be called after defaulting and resetting ESIPP fields :(

Copy link
Member Author

@MrHohn MrHohn left a comment

Choose a reason for hiding this comment

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

Thanks! WIll address the comments soon.

// have legal value.
func validateServiceExternalTrafficFields(service *api.Service) field.ErrorList {
func validateServiceExternalTrafficFieldsValue(service *api.Service) field.ErrorList {
Copy link
Member Author

Choose a reason for hiding this comment

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

Is it allowed to use healthcheck node port with Global?

Nope, we disallow this case in ValidateServiceExternalTrafficFieldsCombination().

Is it allowed to mixa usage of beta annotation and fields? (I think annotaion is preempting, so we do not care? )

Nope, we disallow this case in validateServiceExternalTrafficAPIVersion().

Sorry for the mess, but I have to separate the validation into multiple pieces because they are called in different phases --- especially ValidateServiceExternalTrafficFieldsCombination() needs to be called after defaulting and resetting ESIPP fields :(

@freehan
Copy link
Contributor

freehan commented May 12, 2017

/lgtm

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label May 12, 2017
@k8s-github-robot
Copy link

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: MrHohn, freehan
We suggest the following additional approver: thockin

Assign the PR to them by writing /assign @thockin in a comment when ready.

Needs approval from an approver in each of these OWNERS Files:

You can indicate your approval by writing /approve in a comment
You can cancel your approval by writing /approve cancel in a comment

@MrHohn
Copy link
Member Author

MrHohn commented May 12, 2017

Thanks! Squashing commits.

@k8s-github-robot k8s-github-robot removed the lgtm "Looks good to me", indicates that a PR is ready to be merged. label May 12, 2017
@freehan freehan added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label May 12, 2017
@k8s-github-robot
Copy link

Automatic merge from submit-queue (batch tested with PRs 45623, 45241, 45460, 41162)

@k8s-github-robot k8s-github-robot merged commit 35eba22 into kubernetes:master May 12, 2017
k8s-github-robot pushed a commit that referenced this pull request May 15, 2017
Automatic merge from submit-queue

[Federation] Fix federated service reconcilation issue due to addition of External…

…TrafficPolicy field to v1.Service

**What this PR does / why we need it**:
New fields (ExternalTrafficPolicy) are introduced to v1.Service by this PR #41162. If this field is not specified in service spec, the service controller will assign default and updates the service spec.
In federation, the service spec is not updated and we continuously try to reconcile as the federated service and the service in federated cluster do not match.

**Which issue this PR fixes** *(optional, in `fixes #<issue number>(, fixes #<issue_number>, ...)` format, will close that issue when PR gets merged)*: fixes #45795


**Special notes for your reviewer**:

**Release note**:
```
NONE
```

cc @kubernetes/sig-federation-bugs @madhusudancs
@MrHohn MrHohn deleted the esipp-ga branch May 16, 2017 23:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. kind/api-change Categorizes issue or PR as related to adding, removing, or otherwise changing an API lgtm "Looks good to me", indicates that a PR is ready to be merged. release-note Denotes a PR that will be considered when it comes time to generate release notes. size/XXL Denotes a PR that changes 1000+ lines, ignoring generated files.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

10 participants