Skip to content

Commit

Permalink
Merge pull request #526 from DataDog/affinity-support
Browse files Browse the repository at this point in the history
BackendConfig support for session affinity
  • Loading branch information
k8s-ci-robot authored Oct 29, 2018
2 parents 9404a9a + 8bcada0 commit d379563
Show file tree
Hide file tree
Showing 10 changed files with 374 additions and 20 deletions.
10 changes: 9 additions & 1 deletion pkg/apis/backendconfig/v1beta1/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ type BackendConfigSpec struct {
SecurityPolicy *SecurityPolicyConfig `json:"securityPolicy,omitempty"`
TimeoutSec *int64 `json:"timeoutSec,omitempty"`
ConnectionDraining *ConnectionDrainingConfig `json:"connectionDraining,omitempty"`
SessionAffinity *SessionAffinityConfig `json:"sessionAffinity,omitempty"`
}

// BackendConfigStatus is the status for a BackendConfig resource
Expand Down Expand Up @@ -115,5 +116,12 @@ type SecurityPolicyConfig struct {
// +k8s:openapi-gen=true
type ConnectionDrainingConfig struct {
// Draining timeout in seconds.
DrainingTimeoutSec *int64 `json:"drainingTimeoutSec,omitempty"`
DrainingTimeoutSec int64 `json:"drainingTimeoutSec,omitempty"`
}

// SessionAffinityConfig contains configuration for stickyness parameters.
// +k8s:openapi-gen=true
type SessionAffinityConfig struct {
AffinityType string `json:"affinityType,omitempty"`
AffinityCookieTtlSec *int64 `json:"affinityCookieTtlSec,omitempty"`
}
43 changes: 34 additions & 9 deletions pkg/apis/backendconfig/v1beta1/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

29 changes: 28 additions & 1 deletion pkg/apis/backendconfig/v1beta1/zz_generated.openapi.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 38 additions & 1 deletion pkg/backendconfig/validation.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,11 +29,26 @@ const (
OAuthClientSecretKey = "client_secret"
)

var supportedAffinities = map[string]bool{
"NONE": true,
"CLIENT_IP": true,
"GENERATED_COOKIE": true,
}

func Validate(kubeClient kubernetes.Interface, beConfig *backendconfigv1beta1.BackendConfig) error {
if beConfig == nil {
return nil
}
return validateIAP(kubeClient, beConfig)

if err := validateIAP(kubeClient, beConfig); err != nil {
return err
}

if err := validateSessionAffinity(kubeClient, beConfig); err != nil {
return err
}

return nil
}

// TODO(rramkumar): Return errors as constants so that the unit tests can distinguish
Expand Down Expand Up @@ -67,3 +82,25 @@ func validateIAP(kubeClient kubernetes.Interface, beConfig *backendconfigv1beta1
}
return nil
}

func validateSessionAffinity(kubeClient kubernetes.Interface, beConfig *backendconfigv1beta1.BackendConfig) error {
if beConfig.Spec.SessionAffinity == nil {
return nil
}

if beConfig.Spec.SessionAffinity.AffinityType != "" {
if _, ok := supportedAffinities[beConfig.Spec.SessionAffinity.AffinityType]; !ok {
return fmt.Errorf("unsupported AffinityType: %s, should be one of NONE, CLIENT_IP, or GENERATED_COOKIE",
beConfig.Spec.SessionAffinity.AffinityType)
}
}

if beConfig.Spec.SessionAffinity.AffinityCookieTtlSec != nil {
if *beConfig.Spec.SessionAffinity.AffinityCookieTtlSec < 0 || *beConfig.Spec.SessionAffinity.AffinityCookieTtlSec > 86400 {
return fmt.Errorf("unsupported AffinityCookieTtlSec: %d, should be between 0 and 86400",
*beConfig.Spec.SessionAffinity.AffinityCookieTtlSec)
}
}

return nil
}
80 changes: 80 additions & 0 deletions pkg/backendconfig/validation_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ import (
)

var (
goodTTL int64 = 86400
badTTL int64 = 86400 + 1

defaultBeConfig = &backendconfigv1beta1.BackendConfig{
ObjectMeta: meta_v1.ObjectMeta{
Namespace: "default",
Expand Down Expand Up @@ -159,3 +162,80 @@ func TestValidateIAP(t *testing.T) {
}
}
}

func TestValidateSessionAffinity(t *testing.T) {
testCases := []struct {
desc string
beConfig *backendconfigv1beta1.BackendConfig
expectError bool
}{

{
desc: "unsupported affinity type",
beConfig: &backendconfigv1beta1.BackendConfig{
ObjectMeta: meta_v1.ObjectMeta{
Namespace: "default",
},
Spec: backendconfigv1beta1.BackendConfigSpec{
SessionAffinity: &backendconfigv1beta1.SessionAffinityConfig{
AffinityType: "WRONG_TYPE",
},
},
},
expectError: true,
},
{
desc: "supported affinity type",
beConfig: &backendconfigv1beta1.BackendConfig{
ObjectMeta: meta_v1.ObjectMeta{
Namespace: "default",
},
Spec: backendconfigv1beta1.BackendConfigSpec{
SessionAffinity: &backendconfigv1beta1.SessionAffinityConfig{
AffinityType: "CLIENT_IP",
},
},
},
expectError: false,
},
{
desc: "unsupported ttl value",
beConfig: &backendconfigv1beta1.BackendConfig{
ObjectMeta: meta_v1.ObjectMeta{
Namespace: "default",
},
Spec: backendconfigv1beta1.BackendConfigSpec{
SessionAffinity: &backendconfigv1beta1.SessionAffinityConfig{
AffinityCookieTtlSec: &badTTL,
},
},
},
expectError: true,
},
{
desc: "supported ttl value",
beConfig: &backendconfigv1beta1.BackendConfig{
ObjectMeta: meta_v1.ObjectMeta{
Namespace: "default",
},
Spec: backendconfigv1beta1.BackendConfigSpec{
SessionAffinity: &backendconfigv1beta1.SessionAffinityConfig{
AffinityCookieTtlSec: &goodTTL,
},
},
},
expectError: false,
},
}

for _, testCase := range testCases {
kubeClient := fake.NewSimpleClientset()
err := Validate(kubeClient, testCase.beConfig)
if testCase.expectError && err == nil {
t.Errorf("%v: Expected error but got nil", testCase.desc)
}
if !testCase.expectError && err != nil {
t.Errorf("%v: Did not expect error but got: %v", testCase.desc, err)
}
}
}
53 changes: 53 additions & 0 deletions pkg/backends/features/affinity.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package features

import (
"reflect"

"github.com/golang/glog"
"k8s.io/ingress-gce/pkg/composite"
"k8s.io/ingress-gce/pkg/utils"
)

// EnsureAffinity reads the sessionAffinity and AffinityCookieTtlSec configuration
// specified in the ServicePort.BackendConfig and applies it to the BackendService.
// It returns true if there were existing settings on the BackendService
// that were overwritten.
func EnsureAffinity(sp utils.ServicePort, be *composite.BackendService) bool {
if sp.BackendConfig.Spec.SessionAffinity == nil {
return false
}
beTemp := &composite.BackendService{}
applyAffinitySettings(sp, beTemp)
if !reflect.DeepEqual(beTemp.AffinityCookieTtlSec, be.AffinityCookieTtlSec) || beTemp.SessionAffinity != be.SessionAffinity {
applyAffinitySettings(sp, be)
glog.V(2).Infof("Updated SessionAffinity settings for service %v/%v.", sp.ID.Service.Namespace, sp.ID.Service.Name)
return true
}
return false
}

// applyAffinitySettings applies the session affinity settings specified in the
// BackendConfig to the passed in composite.BackendService. A GCE API call still
// needs to be made to actually persist the changes.
func applyAffinitySettings(sp utils.ServicePort, be *composite.BackendService) {
be.SessionAffinity = sp.BackendConfig.Spec.SessionAffinity.AffinityType
if sp.BackendConfig.Spec.SessionAffinity.AffinityCookieTtlSec != nil {
be.AffinityCookieTtlSec = *sp.BackendConfig.Spec.SessionAffinity.AffinityCookieTtlSec
}
}
Loading

0 comments on commit d379563

Please sign in to comment.