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

Extracting out annotations to a separate package to allow reuse #58

Merged
merged 1 commit into from
Oct 25, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 137 additions & 0 deletions pkg/annotations/annotations.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
/*
Copyright 2017 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 annotations

import (
"encoding/json"
"fmt"
"strconv"

"k8s.io/ingress-gce/pkg/utils"
)

const (
// AllowHTTPKey tells the Ingress controller to allow/block HTTP access.
// If either unset or set to true, the controller will create a
// forwarding-rule for port 80, and any additional rules based on the TLS
// section of the Ingress. If set to false, the controller will only create
// rules for port 443 based on the TLS section.
AllowHTTPKey = "kubernetes.io/ingress.allow-http"

// StaticIPNameKey tells the Ingress controller to use a specific GCE
// static ip for its forwarding rules. If specified, the Ingress controller
// assigns the static ip by this name to the forwarding rules of the given
// Ingress. The controller *does not* manage this ip, it is the users
// responsibility to create/delete it.
StaticIPNameKey = "kubernetes.io/ingress.global-static-ip-name"

// PreSharedCertKey represents the specific pre-shared SSL
// certicate for the Ingress controller to use. The controller *does not*
// manage this certificate, it is the users responsibility to create/delete it.
// In GCP, the Ingress controller assigns the SSL certificate with this name
// to the target proxies of the Ingress.
PreSharedCertKey = "ingress.gcp.kubernetes.io/pre-shared-cert"

// ServiceApplicationProtocolKey is a stringified JSON map of port names to
// protocol strings. Possible values are HTTP, HTTPS
// Example:
// '{"my-https-port":"HTTPS","my-http-port":"HTTP"}'
ServiceApplicationProtocolKey = "service.alpha.kubernetes.io/app-protocols"

// IngressClassKey picks a specific "class" for the Ingress. The controller
// only processes Ingresses with this annotation either unset, or set
// to either gceIngessClass or the empty string.
IngressClassKey = "kubernetes.io/ingress.class"
GceIngressClass = "gce"
GceMultiIngressClass = "gce-multi-cluster"

// Label key to denote which GCE zone a Kubernetes node is in.
ZoneKey = "failure-domain.beta.kubernetes.io/zone"
DefaultZone = ""

// InstanceGroupsAnnotationKey is the annotation key used by controller to
// specify the name and zone of instance groups created for the ingress.
// This is read only for users. Controller will overrite any user updates.
// This is only set for ingresses with ingressClass = "gce-multi-cluster"
InstanceGroupsAnnotationKey = "ingress.gcp.kubernetes.io/instance-groups"
)

// IngAnnotations represents ingress annotations.
type IngAnnotations map[string]string

// AllowHTTP returns the allowHTTP flag. True by default.
func (ing IngAnnotations) AllowHTTP() bool {
val, ok := ing[AllowHTTPKey]
if !ok {
return true
}
v, err := strconv.ParseBool(val)
if err != nil {
return true
}
return v
}

// UseNamedTLS returns the name of the GCE SSL certificate. Empty by default.
func (ing IngAnnotations) UseNamedTLS() string {
val, ok := ing[PreSharedCertKey]
if !ok {
return ""
}

return val
}

func (ing IngAnnotations) StaticIPName() string {
val, ok := ing[StaticIPNameKey]
if !ok {
return ""
}
return val
}

func (ing IngAnnotations) IngressClass() string {
val, ok := ing[IngressClassKey]
if !ok {
return ""
}
return val
}

// SvcAnnotations represents Service annotations.
type SvcAnnotations map[string]string

func (svc SvcAnnotations) ApplicationProtocols() (map[string]utils.AppProtocol, error) {
val, ok := svc[ServiceApplicationProtocolKey]
if !ok {
return map[string]utils.AppProtocol{}, nil
}

var portToProtos map[string]utils.AppProtocol
err := json.Unmarshal([]byte(val), &portToProtos)

// Verify protocol is an accepted value
for _, proto := range portToProtos {
switch proto {
case utils.ProtocolHTTP, utils.ProtocolHTTPS:
default:
return nil, fmt.Errorf("invalid port application protocol: %v", proto)
}
}

return portToProtos, err
}
15 changes: 8 additions & 7 deletions pkg/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
listers "k8s.io/client-go/listers/core/v1"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/record"
"k8s.io/ingress-gce/pkg/annotations"
"k8s.io/ingress-gce/pkg/context"
"k8s.io/ingress-gce/pkg/firewalls"
"k8s.io/ingress-gce/pkg/loadbalancers"
Expand Down Expand Up @@ -122,7 +123,7 @@ func NewLoadBalancerController(kubeClient kubernetes.Interface, ctx *context.Con
AddFunc: func(obj interface{}) {
addIng := obj.(*extensions.Ingress)
if !isGCEIngress(addIng) && !isGCEMultiClusterIngress(addIng) {
glog.Infof("Ignoring add for ingress %v based on annotation %v", addIng.Name, ingressClassKey)
glog.Infof("Ignoring add for ingress %v based on annotation %v", addIng.Name, annotations.IngressClassKey)
return
}
lbc.recorder.Eventf(addIng, apiv1.EventTypeNormal, "ADD", fmt.Sprintf("%s/%s", addIng.Namespace, addIng.Name))
Expand All @@ -131,7 +132,7 @@ func NewLoadBalancerController(kubeClient kubernetes.Interface, ctx *context.Con
DeleteFunc: func(obj interface{}) {
delIng := obj.(*extensions.Ingress)
if !isGCEIngress(delIng) && !isGCEMultiClusterIngress(delIng) {
glog.Infof("Ignoring delete for ingress %v based on annotation %v", delIng.Name, ingressClassKey)
glog.Infof("Ignoring delete for ingress %v based on annotation %v", delIng.Name, annotations.IngressClassKey)
return
}
glog.Infof("Delete notification received for Ingress %v/%v", delIng.Namespace, delIng.Name)
Expand Down Expand Up @@ -416,10 +417,10 @@ func (lbc *LoadBalancerController) toRuntimeInfo(ingList extensions.IngressList)

var tls *loadbalancers.TLSCerts

annotations := ingAnnotations(ing.ObjectMeta.Annotations)
annotations := annotations.IngAnnotations(ing.ObjectMeta.Annotations)
// Load the TLS cert from the API Spec if it is not specified in the annotation.
// TODO: enforce this with validation.
if annotations.useNamedTLS() == "" {
if annotations.UseNamedTLS() == "" {
tls, err = lbc.tlsLoader.load(&ing)
if err != nil {
glog.Warningf("Cannot get certs for Ingress %v/%v: %v", ing.Namespace, ing.Name, err)
Expand All @@ -429,9 +430,9 @@ func (lbc *LoadBalancerController) toRuntimeInfo(ingList extensions.IngressList)
lbs = append(lbs, &loadbalancers.L7RuntimeInfo{
Name: k,
TLS: tls,
TLSName: annotations.useNamedTLS(),
AllowHTTP: annotations.allowHTTP(),
StaticIPName: annotations.staticIPName(),
TLSName: annotations.UseNamedTLS(),
AllowHTTP: annotations.AllowHTTP(),
StaticIPName: annotations.StaticIPName(),
})
}
return lbs, nil
Expand Down
3 changes: 2 additions & 1 deletion pkg/controller/controller_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
"k8s.io/client-go/kubernetes/fake"
"k8s.io/kubernetes/pkg/api"

"k8s.io/ingress-gce/pkg/annotations"
"k8s.io/ingress-gce/pkg/context"
"k8s.io/ingress-gce/pkg/firewalls"
"k8s.io/ingress-gce/pkg/loadbalancers"
Expand Down Expand Up @@ -429,7 +430,7 @@ func TestLbChangeStaticIP(t *testing.T) {
t.Fatalf("Expected 2 forwarding rules with the same IP.")
}

ing.Annotations = map[string]string{staticIPNameKey: "testip"}
ing.Annotations = map[string]string{annotations.StaticIPNameKey: "testip"}
cm.fakeLbs.ReserveGlobalAddress(&compute.Address{Name: "testip", Address: "1.2.3.4"})

// Second sync reassigns 1.2.3.4 to existing forwarding rule (by recreating it)
Expand Down
132 changes: 10 additions & 122 deletions pkg/controller/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ import (
"encoding/json"
"fmt"
"sort"
"strconv"
"time"

"github.com/golang/glog"
Expand All @@ -40,135 +39,24 @@ import (
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue"

"k8s.io/ingress-gce/pkg/annotations"
"k8s.io/ingress-gce/pkg/backends"
"k8s.io/ingress-gce/pkg/loadbalancers"
"k8s.io/ingress-gce/pkg/utils"
)

const (
// allowHTTPKey tells the Ingress controller to allow/block HTTP access.
// If either unset or set to true, the controller will create a
// forwarding-rule for port 80, and any additional rules based on the TLS
// section of the Ingress. If set to false, the controller will only create
// rules for port 443 based on the TLS section.
allowHTTPKey = "kubernetes.io/ingress.allow-http"

// staticIPNameKey tells the Ingress controller to use a specific GCE
// static ip for its forwarding rules. If specified, the Ingress controller
// assigns the static ip by this name to the forwarding rules of the given
// Ingress. The controller *does not* manage this ip, it is the users
// responsibility to create/delete it.
staticIPNameKey = "kubernetes.io/ingress.global-static-ip-name"

// preSharedCertKey represents the specific pre-shared SSL
// certicate for the Ingress controller to use. The controller *does not*
// manage this certificate, it is the users responsibility to create/delete it.
// In GCP, the Ingress controller assigns the SSL certificate with this name
// to the target proxies of the Ingress.
preSharedCertKey = "ingress.gcp.kubernetes.io/pre-shared-cert"

// serviceApplicationProtocolKey is a stringified JSON map of port names to
// protocol strings. Possible values are HTTP, HTTPS
// Example:
// '{"my-https-port":"HTTPS","my-http-port":"HTTP"}'
serviceApplicationProtocolKey = "service.alpha.kubernetes.io/app-protocols"

// ingressClassKey picks a specific "class" for the Ingress. The controller
// only processes Ingresses with this annotation either unset, or set
// to either gceIngessClass or the empty string.
ingressClassKey = "kubernetes.io/ingress.class"
gceIngressClass = "gce"
gceMultiIngressClass = "gce-multi-cluster"

// Label key to denote which GCE zone a Kubernetes node is in.
zoneKey = "failure-domain.beta.kubernetes.io/zone"
defaultZone = ""

// instanceGroupsAnnotationKey is the annotation key used by controller to
// specify the name and zone of instance groups created for the ingress.
// This is read only for users. Controller will overrite any user updates.
// This is only set for ingresses with ingressClass = "gce-multi-cluster"
instanceGroupsAnnotationKey = "ingress.gcp.kubernetes.io/instance-groups"
)

// ingAnnotations represents Ingress annotations.
type ingAnnotations map[string]string

// allowHTTP returns the allowHTTP flag. True by default.
func (ing ingAnnotations) allowHTTP() bool {
val, ok := ing[allowHTTPKey]
if !ok {
return true
}
v, err := strconv.ParseBool(val)
if err != nil {
return true
}
return v
}

// useNamedTLS returns the name of the GCE SSL certificate. Empty by default.
func (ing ingAnnotations) useNamedTLS() string {
val, ok := ing[preSharedCertKey]
if !ok {
return ""
}

return val
}

func (ing ingAnnotations) staticIPName() string {
val, ok := ing[staticIPNameKey]
if !ok {
return ""
}
return val
}

func (ing ingAnnotations) ingressClass() string {
val, ok := ing[ingressClassKey]
if !ok {
return ""
}
return val
}

// svcAnnotations represents Service annotations.
type svcAnnotations map[string]string

func (svc svcAnnotations) ApplicationProtocols() (map[string]utils.AppProtocol, error) {
val, ok := svc[serviceApplicationProtocolKey]
if !ok {
return map[string]utils.AppProtocol{}, nil
}

var portToProtos map[string]utils.AppProtocol
err := json.Unmarshal([]byte(val), &portToProtos)

// Verify protocol is an accepted value
for _, proto := range portToProtos {
switch proto {
case utils.ProtocolHTTP, utils.ProtocolHTTPS:
default:
return nil, fmt.Errorf("invalid port application protocol: %v", proto)
}
}

return portToProtos, err
}

// isGCEIngress returns true if the given Ingress either doesn't specify the
// ingress.class annotation, or it's set to "gce".
func isGCEIngress(ing *extensions.Ingress) bool {
class := ingAnnotations(ing.ObjectMeta.Annotations).ingressClass()
return class == "" || class == gceIngressClass
class := annotations.IngAnnotations(ing.ObjectMeta.Annotations).IngressClass()
return class == "" || class == annotations.GceIngressClass
}

// isGCEMultiClusterIngress returns true if the given Ingress has
// ingress.class annotation set to "gce-multi-cluster".
func isGCEMultiClusterIngress(ing *extensions.Ingress) bool {
class := ingAnnotations(ing.ObjectMeta.Annotations).ingressClass()
return class == gceMultiIngressClass
class := annotations.IngAnnotations(ing.ObjectMeta.Annotations).IngressClass()
return class == annotations.GceMultiIngressClass
}

// errorNodePortNotFound is an implementation of error.
Expand All @@ -188,7 +76,7 @@ type errorSvcAppProtosParsing struct {
}

func (e errorSvcAppProtosParsing) Error() string {
return fmt.Sprintf("could not parse %v annotation on Service %v/%v, err: %v", serviceApplicationProtocolKey, e.svc.Namespace, e.svc.Name, e.origErr)
return fmt.Sprintf("could not parse %v annotation on Service %v/%v, err: %v", annotations.ServiceApplicationProtocolKey, e.svc.Namespace, e.svc.Name, e.origErr)
}

// taskQueue manages a work queue through an independent worker that
Expand Down Expand Up @@ -456,7 +344,7 @@ func (t *GCETranslator) getServiceNodePort(be extensions.IngressBackend, namespa
return backends.ServicePort{}, errorNodePortNotFound{be, err}
}
svc := obj.(*api_v1.Service)
appProtocols, err := svcAnnotations(svc.GetAnnotations()).ApplicationProtocols()
appProtocols, err := annotations.SvcAnnotations(svc.GetAnnotations()).ApplicationProtocols()
if err != nil {
return backends.ServicePort{}, errorSvcAppProtosParsing{svc, err}
}
Expand Down Expand Up @@ -536,9 +424,9 @@ func (t *GCETranslator) ingressToNodePorts(ing *extensions.Ingress) []backends.S
}

func getZone(n *api_v1.Node) string {
zone, ok := n.Labels[zoneKey]
zone, ok := n.Labels[annotations.ZoneKey]
if !ok {
return defaultZone
return annotations.DefaultZone
}
return zone
}
Expand Down Expand Up @@ -688,7 +576,7 @@ func setInstanceGroupsAnnotation(existing map[string]string, igs []*compute.Inst
if err != nil {
return err
}
existing[instanceGroupsAnnotationKey] = string(jsonValue)
existing[annotations.InstanceGroupsAnnotationKey] = string(jsonValue)
return nil
}

Expand Down
Loading