Skip to content

Commit

Permalink
Use deterministic-suffix instead of random-suffix in backend name
Browse files Browse the repository at this point in the history
  • Loading branch information
diptadas authored and tamalsaha committed Jan 28, 2018
1 parent 1fff127 commit 10e06f9
Show file tree
Hide file tree
Showing 10 changed files with 226 additions and 17 deletions.
4 changes: 4 additions & 0 deletions apis/voyager/ingress.go
Expand Up @@ -243,6 +243,10 @@ type HTTPIngressPath struct {

// IngressBackend describes all endpoints for a given service and port.
type IngressBackend struct {
// User can specify backend name for using it with custom acl
// Otherwise it will be generated
Name string `json:"name,omitempty"`

// Host names to forward traffic to. If empty traffic will be
// forwarded to all subsets instance.
// If set only matched hosts will get the traffic.
Expand Down
4 changes: 4 additions & 0 deletions apis/voyager/v1beta1/ingress.go
Expand Up @@ -244,6 +244,10 @@ type HTTPIngressPath struct {

// IngressBackend describes all endpoints for a given service and port.
type IngressBackend struct {
// User can specify backend name for using it with custom acl
// Otherwise it will be generated
Name string `json:"name,omitempty"`

// Host names to forward traffic to. If empty traffic will be
// forwarded to all subsets instance.
// If set only matched hosts will get the traffic.
Expand Down
14 changes: 14 additions & 0 deletions apis/voyager/v1beta1/openapi_generated.go
Expand Up @@ -585,6 +585,13 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Properties: map[string]spec.Schema{
"name": {
SchemaProps: spec.SchemaProps{
Description: "User can specify backend name for using it with custom acl Otherwise it will be generated",
Type: []string{"string"},
Format: "",
},
},
"hostNames": {
SchemaProps: spec.SchemaProps{
Description: "Host names to forward traffic to. If empty traffic will be forwarded to all subsets instance. If set only matched hosts will get the traffic. This is an handy way to send traffic to Specific StatefulSet pod. IE. Setting [web-0] will send traffic to only web-0 host for this StatefulSet, https://kubernetes.io/docs/tasks/stateful-application/basic-stateful-set/#creating-a-statefulset",
Expand Down Expand Up @@ -783,6 +790,13 @@ func GetOpenAPIDefinitions(ref common.ReferenceCallback) map[string]common.OpenA
SchemaProps: spec.SchemaProps{
Description: "IngressBackend describes all endpoints for a given service and port.",
Properties: map[string]spec.Schema{
"name": {
SchemaProps: spec.SchemaProps{
Description: "User can specify backend name for using it with custom acl Otherwise it will be generated",
Type: []string{"string"},
Format: "",
},
},
"hostNames": {
SchemaProps: spec.SchemaProps{
Description: "Host names to forward traffic to. If empty traffic will be forwarded to all subsets instance. If set only matched hosts will get the traffic. This is an handy way to send traffic to Specific StatefulSet pod. IE. Setting [web-0] will send traffic to only web-0 host for this StatefulSet, https://kubernetes.io/docs/tasks/stateful-application/basic-stateful-set/#creating-a-statefulset",
Expand Down
2 changes: 2 additions & 0 deletions apis/voyager/v1beta1/zz_generated.conversion.go
Expand Up @@ -590,6 +590,7 @@ func Convert_voyager_Ingress_To_v1beta1_Ingress(in *voyager.Ingress, out *Ingres
}

func autoConvert_v1beta1_IngressBackend_To_voyager_IngressBackend(in *IngressBackend, out *voyager.IngressBackend, s conversion.Scope) error {
out.Name = in.Name
out.HostNames = *(*[]string)(unsafe.Pointer(&in.HostNames))
out.ServiceName = in.ServiceName
out.ServicePort = in.ServicePort
Expand All @@ -603,6 +604,7 @@ func Convert_v1beta1_IngressBackend_To_voyager_IngressBackend(in *IngressBackend
}

func autoConvert_voyager_IngressBackend_To_v1beta1_IngressBackend(in *voyager.IngressBackend, out *IngressBackend, s conversion.Scope) error {
out.Name = in.Name
out.HostNames = *(*[]string)(unsafe.Pointer(&in.HostNames))
out.ServiceName = in.ServiceName
out.ServicePort = in.ServicePort
Expand Down
38 changes: 36 additions & 2 deletions pkg/haproxy/api/helper.go
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"sort"
"strconv"
"strings"

"github.com/appscode/voyager/pkg/certificate/providers"
Expand Down Expand Up @@ -34,8 +35,9 @@ func (td *TemplateData) convertWildcardHostToEmpty() {
}

func (td *TemplateData) sort() {
backends := td.countBackendNames()
if td.DefaultBackend != nil {
td.DefaultBackend.canonicalize()
td.DefaultBackend.canonicalize(backends[td.DefaultBackend.Name] > 1, "", "", "")
}
for x := range td.HTTPService {
svc := td.HTTPService[x]
Expand All @@ -60,7 +62,12 @@ func (td *TemplateData) sort() {
host := svc.Hosts[y]
for z := range host.Paths {
if host.Paths[z].Backend != nil {
host.Paths[z].Backend.canonicalize()
host.Paths[z].Backend.canonicalize(
backends[host.Paths[z].Backend.Name] > 1,
host.Host,
strconv.Itoa(svc.Port),
host.Paths[z].Path,
)
}
}

Expand All @@ -82,6 +89,11 @@ func (td *TemplateData) sort() {

td.HTTPService[x] = svc
}

for _, svc := range td.TCPService {
svc.Backend.canonicalize(backends[svc.Backend.Name] > 1, svc.Host, svc.Port, "")
}

sort.Slice(td.HTTPService, func(i, j int) bool { return td.HTTPService[i].sortKey() < td.HTTPService[j].sortKey() })
sort.Slice(td.TCPService, func(i, j int) bool { return td.TCPService[i].sortKey() < td.TCPService[j].sortKey() })
sort.Slice(td.DNSResolvers, func(i, j int) bool { return td.DNSResolvers[i].Name < td.DNSResolvers[j].Name })
Expand All @@ -92,6 +104,28 @@ func (td *TemplateData) sort() {
sort.Slice(td.UserLists, func(i, j int) bool { return td.UserLists[i].Name < td.UserLists[j].Name })
}

func (td *TemplateData) countBackendNames() map[string]int {
backends := make(map[string]int)
if td.DefaultBackend != nil {
backends[td.DefaultBackend.Name]++
}
for _, svc := range td.HTTPService {
for _, host := range svc.Hosts {
for _, path := range host.Paths {
if path.Backend != nil {
backends[path.Backend.Name]++
}
}
}
}
for _, svc := range td.TCPService {
if svc.Backend != nil {
backends[svc.Backend.Name]++
}
}
return backends
}

func (td *TemplateData) moveAcmePathToTop() {
for i, svc := range td.HTTPService {
if svc.Port != 80 {
Expand Down
2 changes: 1 addition & 1 deletion pkg/haproxy/api/helper_test.go
Expand Up @@ -59,7 +59,7 @@ func TestPathOrdering(t *testing.T) {
host := hosts[y]
for z := range host.Paths {
if host.Paths[z].Backend != nil {
host.Paths[z].Backend.canonicalize()
host.Paths[z].Backend.canonicalize(false, host.Host, "80", host.Paths[z].Path)
}
}

Expand Down
13 changes: 10 additions & 3 deletions pkg/haproxy/api/types.go
@@ -1,6 +1,8 @@
package api

import (
"crypto/md5"
"encoding/hex"
"fmt"
"sort"

Expand Down Expand Up @@ -117,8 +119,9 @@ func (svc TCPService) sortKey() string {
}

type Backend struct {
Name string
BasicAuth *BasicAuth
Name string
NameGenerated bool
BasicAuth *BasicAuth

BackendRules []string
// Deprecated
Expand All @@ -132,7 +135,11 @@ type Backend struct {
StickyCookieHash string
}

func (be *Backend) canonicalize() {
func (be *Backend) canonicalize(hasDuplicate bool, host, port, path string) {
if be.NameGenerated && hasDuplicate { // assign unique backend name
hashed := md5.Sum([]byte(host + "-" + port + "-" + path))
be.Name = be.Name + "-" + hex.EncodeToString(hashed[:])
}
sort.Slice(be.Endpoints, func(i, j int) bool { return be.Endpoints[i].IP < be.Endpoints[j].IP })
if be.BasicAuth != nil {
be.BasicAuth.canonicalize()
Expand Down
2 changes: 1 addition & 1 deletion pkg/haproxy/template/renderer.go
Expand Up @@ -9,10 +9,10 @@ import (
)

func RenderConfig(data hpi.TemplateData) (string, error) {
data.Canonicalize()
if err := data.IsValid(); err != nil {
return "", err
}
data.Canonicalize()

var buf bytes.Buffer
err := haproxyTemplate.ExecuteTemplate(&buf, "haproxy.cfg", data)
Expand Down
34 changes: 24 additions & 10 deletions pkg/ingress/parser.go
Expand Up @@ -8,7 +8,6 @@ import (
"strconv"
"strings"

"github.com/appscode/go/crypto/rand"
"github.com/appscode/go/errors"
api "github.com/appscode/voyager/apis/voyager/v1beta1"
hpi "github.com/appscode/voyager/pkg/haproxy/api"
Expand Down Expand Up @@ -208,14 +207,12 @@ func getFrontendName(proto, addr string, port int) string {
}

func getBackendName(r *api.Ingress, be api.IngressBackend) string {
var seed string
parts := strings.Split(be.ServiceName, ".")
if len(parts) == 1 {
seed = fmt.Sprintf("%s.%s:%d", parts[0], r.Namespace, be.ServicePort.IntValue())
return fmt.Sprintf("%s.%s:%d", parts[0], r.Namespace, be.ServicePort.IntValue())
} else {
seed = fmt.Sprintf("%s.%s:%d", parts[0], parts[1], be.ServicePort.IntValue()) // drop DNS labels following svcName, i.e., parts[2:]
return fmt.Sprintf("%s.%s:%d", parts[0], parts[1], be.ServicePort.IntValue()) // drop DNS labels following svcName, i.e., parts[2:]
}
return rand.WithUniqSuffix(seed)
}

// ref: https://github.com/jcmoraisjr/haproxy-ingress/pull/57
Expand Down Expand Up @@ -337,7 +334,6 @@ func (c *controller) generateConfig() error {
return err
}
si.DefaultBackend = &hpi.Backend{
Name: "default-backend", // TODO: Use constant
BasicAuth: bk.BasicAuth,
Endpoints: bk.Endpoints,
BackendRules: c.Ingress.Spec.Backend.BackendRule,
Expand All @@ -347,6 +343,12 @@ func (c *controller) generateConfig() error {
StickyCookieName: bk.StickyCookieName,
StickyCookieHash: bk.StickyCookieHash,
}
if c.Ingress.Spec.Backend.Name != "" {
si.DefaultBackend.Name = c.Ingress.Spec.Backend.Name
} else {
si.DefaultBackend.Name = "default-backend" // TODO: Use constant
si.DefaultBackend.NameGenerated = true
}
if globalBasic != nil {
si.DefaultBackend.BasicAuth = globalBasic
}
Expand Down Expand Up @@ -436,10 +438,9 @@ func (c *controller) generateConfig() error {
return err
}
if len(bk.Endpoints) > 0 {
httpPaths = append(httpPaths, &hpi.HTTPPath{
httpPath := &hpi.HTTPPath{
Path: path.Path,
Backend: &hpi.Backend{
Name: getBackendName(c.Ingress, path.Backend.IngressBackend),
BasicAuth: bk.BasicAuth,
Endpoints: bk.Endpoints,
BackendRules: path.Backend.BackendRule,
Expand All @@ -449,7 +450,14 @@ func (c *controller) generateConfig() error {
StickyCookieName: bk.StickyCookieName,
StickyCookieHash: bk.StickyCookieHash,
},
})
}
if path.Backend.IngressBackend.Name != "" {
httpPath.Backend.Name = path.Backend.IngressBackend.Name
} else {
httpPath.Backend.Name = getBackendName(c.Ingress, path.Backend.IngressBackend)
httpPath.Backend.NameGenerated = true
}
httpPaths = append(httpPaths, httpPath)
}
}
info.Hosts[rule.Host] = httpPaths
Expand All @@ -469,14 +477,20 @@ func (c *controller) generateConfig() error {
ALPNOptions: parseALPNOptions(rule.TCP.ALPN),
FrontendRules: fr.Rules,
Backend: &hpi.Backend{
Name: getBackendName(c.Ingress, rule.TCP.Backend),
BackendRules: rule.TCP.Backend.BackendRule,
Endpoints: bk.Endpoints,
Sticky: bk.Sticky,
StickyCookieName: bk.StickyCookieName,
StickyCookieHash: bk.StickyCookieHash,
},
}
if rule.TCP.Backend.Name != "" {
srv.Backend.Name = rule.TCP.Backend.Name
} else {
srv.Backend.Name = getBackendName(c.Ingress, rule.TCP.Backend)
srv.Backend.NameGenerated = true
}

if globalTLS != nil {
srv.TLSAuth = globalTLS
} else if fr.Auth != nil && fr.Auth.TLS != nil {
Expand Down

0 comments on commit 10e06f9

Please sign in to comment.