Split implementations from generic code
This commit is contained in:
parent
d1e8a629ca
commit
ed9a416b01
107 changed files with 5777 additions and 3546 deletions
161
core/pkg/ingress/controller/backend_ssl.go
Normal file
161
core/pkg/ingress/controller/backend_ssl.go
Normal file
|
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
Copyright 2015 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 controller
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
"k8s.io/kubernetes/pkg/client/cache"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/ingress/core/pkg/ingress"
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/parser"
|
||||
ssl "k8s.io/ingress/core/pkg/net/ssl"
|
||||
)
|
||||
|
||||
// syncSecret keeps in sync Secrets used by Ingress rules with files to allow
|
||||
// being used in controllers.
|
||||
func (ic *GenericController) syncSecret(k interface{}) error {
|
||||
if ic.secretQueue.IsShuttingDown() {
|
||||
return nil
|
||||
}
|
||||
if !ic.controllersInSync() {
|
||||
time.Sleep(podStoreSyncedPollPeriod)
|
||||
return fmt.Errorf("deferring sync till endpoints controller has synced")
|
||||
}
|
||||
|
||||
// check if the default certificate is configured
|
||||
key := fmt.Sprintf("default/%v", defServerName)
|
||||
_, exists := ic.sslCertTracker.Get(key)
|
||||
var cert *ingress.SSLCert
|
||||
var err error
|
||||
if !exists {
|
||||
if ic.cfg.DefaultSSLCertificate != "" {
|
||||
cert, err = ic.getPemCertificate(ic.cfg.DefaultSSLCertificate)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
defCert, defKey := ssl.GetFakeSSLCert()
|
||||
cert, err = ssl.AddOrUpdateCertAndKey("system-snake-oil-certificate", defCert, defKey, []byte{})
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
cert.Name = defServerName
|
||||
cert.Namespace = api.NamespaceDefault
|
||||
ic.sslCertTracker.Add(key, cert)
|
||||
}
|
||||
|
||||
key = k.(string)
|
||||
|
||||
// get secret
|
||||
secObj, exists, err := ic.secrLister.Store.GetByKey(key)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting secret %v: %v", key, err)
|
||||
}
|
||||
if !exists {
|
||||
return fmt.Errorf("secret %v was not found", key)
|
||||
}
|
||||
sec := secObj.(*api.Secret)
|
||||
if !ic.secrReferenced(sec.Name, sec.Namespace) {
|
||||
glog.V(2).Infof("secret %v/%v is not used in Ingress rules. skipping ", sec.Namespace, sec.Name)
|
||||
return nil
|
||||
}
|
||||
|
||||
cert, err = ic.getPemCertificate(key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// create certificates and add or update the item in the store
|
||||
_, exists = ic.sslCertTracker.Get(key)
|
||||
if exists {
|
||||
ic.sslCertTracker.Update(key, cert)
|
||||
return nil
|
||||
}
|
||||
ic.sslCertTracker.Add(key, cert)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (ic *GenericController) getPemCertificate(secretName string) (*ingress.SSLCert, error) {
|
||||
secretInterface, exists, err := ic.secrLister.Store.GetByKey(secretName)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error retriveing secret %v: %v", secretName, err)
|
||||
}
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("secret named %v does not exists", secretName)
|
||||
}
|
||||
|
||||
secret := secretInterface.(*api.Secret)
|
||||
cert, ok := secret.Data[api.TLSCertKey]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("secret named %v has no private key", secretName)
|
||||
}
|
||||
key, ok := secret.Data[api.TLSPrivateKeyKey]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("secret named %v has no cert", secretName)
|
||||
}
|
||||
|
||||
ca := secret.Data["ca.crt"]
|
||||
|
||||
nsSecName := strings.Replace(secretName, "/", "-", -1)
|
||||
s, err := ssl.AddOrUpdateCertAndKey(nsSecName, cert, key, ca)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.Name = secret.Name
|
||||
s.Namespace = secret.Namespace
|
||||
return s, nil
|
||||
}
|
||||
|
||||
// check if secret is referenced in this controller's config
|
||||
func (ic *GenericController) secrReferenced(name, namespace string) bool {
|
||||
for _, ingIf := range ic.ingLister.Store.List() {
|
||||
ing := ingIf.(*extensions.Ingress)
|
||||
str, err := parser.GetStringAnnotation("ingress.kubernetes.io/auth-tls-secret", ing)
|
||||
if err == nil && str == fmt.Sprintf("%v/%v", namespace, name) {
|
||||
return true
|
||||
}
|
||||
|
||||
if ing.Namespace != namespace {
|
||||
continue
|
||||
}
|
||||
for _, tls := range ing.Spec.TLS {
|
||||
if tls.SecretName == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// sslCertTracker ...
|
||||
type sslCertTracker struct {
|
||||
cache.ThreadSafeStore
|
||||
}
|
||||
|
||||
func newSSLCertTracker() *sslCertTracker {
|
||||
return &sslCertTracker{
|
||||
cache.NewThreadSafeStore(cache.Indexers{}, cache.Indices{}),
|
||||
}
|
||||
}
|
||||
1076
core/pkg/ingress/controller/controller.go
Normal file
1076
core/pkg/ingress/controller/controller.go
Normal file
File diff suppressed because it is too large
Load diff
182
core/pkg/ingress/controller/launch.go
Normal file
182
core/pkg/ingress/controller/launch.go
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
package controller
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/pprof"
|
||||
"os"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/golang/glog"
|
||||
"github.com/spf13/pflag"
|
||||
|
||||
"k8s.io/ingress/core/pkg/ingress"
|
||||
"k8s.io/ingress/core/pkg/k8s"
|
||||
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
clientset "k8s.io/kubernetes/pkg/client/clientset_generated/internalclientset"
|
||||
"k8s.io/kubernetes/pkg/client/restclient"
|
||||
"k8s.io/kubernetes/pkg/healthz"
|
||||
kubectl_util "k8s.io/kubernetes/pkg/kubectl/cmd/util"
|
||||
)
|
||||
|
||||
// NewIngressController returns a configured Ingress controller ready to start
|
||||
func NewIngressController(backend ingress.Controller) Interface {
|
||||
var (
|
||||
flags = pflag.NewFlagSet("", pflag.ExitOnError)
|
||||
|
||||
defaultSvc = flags.String("default-backend-service", "",
|
||||
`Service used to serve a 404 page for the default backend. Takes the form
|
||||
namespace/name. The controller uses the first node port of this Service for
|
||||
the default backend.`)
|
||||
|
||||
ingressClass = flags.String("ingress-class", "nginx",
|
||||
`Name of the ingress class to route through this controller.`)
|
||||
|
||||
configMap = flags.String("configmap", "",
|
||||
`Name of the ConfigMap that contains the custom configuration to use`)
|
||||
|
||||
publishSvc = flags.String("publish-service", "",
|
||||
`Service fronting the ingress controllers. Takes the form
|
||||
namespace/name. The controller will set the endpoint records on the
|
||||
ingress objects to reflect those on the service.`)
|
||||
|
||||
tcpConfigMapName = flags.String("tcp-services-configmap", "",
|
||||
`Name of the ConfigMap that contains the definition of the TCP services to expose.
|
||||
The key in the map indicates the external port to be used. The value is the name of the
|
||||
service with the format namespace/serviceName and the port of the service could be a
|
||||
number of the name of the port.
|
||||
The ports 80 and 443 are not allowed as external ports. This ports are reserved for nginx`)
|
||||
|
||||
udpConfigMapName = flags.String("udp-services-configmap", "",
|
||||
`Name of the ConfigMap that contains the definition of the UDP services to expose.
|
||||
The key in the map indicates the external port to be used. The value is the name of the
|
||||
service with the format namespace/serviceName and the port of the service could be a
|
||||
number of the name of the port.`)
|
||||
|
||||
resyncPeriod = flags.Duration("sync-period", 60*time.Second,
|
||||
`Relist and confirm cloud resources this often.`)
|
||||
|
||||
watchNamespace = flags.String("watch-namespace", api.NamespaceAll,
|
||||
`Namespace to watch for Ingress. Default is to watch all namespaces`)
|
||||
|
||||
healthzPort = flags.Int("healthz-port", 10254, "port for healthz endpoint.")
|
||||
|
||||
profiling = flags.Bool("profiling", true, `Enable profiling via web interface host:port/debug/pprof/`)
|
||||
|
||||
defSSLCertificate = flags.String("default-ssl-certificate", "", `Name of the secret
|
||||
that contains a SSL certificate to be used as default for a HTTPS catch-all server`)
|
||||
|
||||
defHealthzURL = flags.String("health-check-path", "/healthz", `Defines
|
||||
the URL to be used as health check inside in the default server in NGINX.`)
|
||||
)
|
||||
|
||||
flags.AddGoFlagSet(flag.CommandLine)
|
||||
flags.Parse(os.Args)
|
||||
clientConfig := kubectl_util.DefaultClientConfig(flags)
|
||||
|
||||
flag.Set("logtostderr", "true")
|
||||
|
||||
glog.Info(backend.Info())
|
||||
|
||||
if *ingressClass != "" {
|
||||
glog.Infof("Watching for ingress class: %s", *ingressClass)
|
||||
}
|
||||
|
||||
if *defaultSvc == "" {
|
||||
glog.Fatalf("Please specify --default-backend-service")
|
||||
}
|
||||
|
||||
kubeconfig, err := restclient.InClusterConfig()
|
||||
if err != nil {
|
||||
kubeconfig, err = clientConfig.ClientConfig()
|
||||
if err != nil {
|
||||
glog.Fatalf("error configuring the client: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
kubeClient, err := clientset.NewForConfig(kubeconfig)
|
||||
if err != nil {
|
||||
glog.Fatalf("failed to create client: %v", err)
|
||||
}
|
||||
|
||||
_, err = k8s.IsValidService(kubeClient, *defaultSvc)
|
||||
if err != nil {
|
||||
glog.Fatalf("no service with name %v found: %v", *defaultSvc, err)
|
||||
}
|
||||
glog.Infof("validated %v as the default backend", *defaultSvc)
|
||||
|
||||
if *publishSvc != "" {
|
||||
svc, err := k8s.IsValidService(kubeClient, *publishSvc)
|
||||
if err != nil {
|
||||
glog.Fatalf("no service with name %v found: %v", *publishSvc, err)
|
||||
}
|
||||
|
||||
if len(svc.Status.LoadBalancer.Ingress) == 0 {
|
||||
// We could poll here, but we instead just exit and rely on k8s to restart us
|
||||
glog.Fatalf("service %s does not (yet) have ingress points", *publishSvc)
|
||||
}
|
||||
|
||||
glog.Infof("service %v validated as source of Ingress status", *publishSvc)
|
||||
}
|
||||
|
||||
if *configMap != "" {
|
||||
_, _, err = k8s.ParseNameNS(*configMap)
|
||||
if err != nil {
|
||||
glog.Fatalf("configmap error: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
os.MkdirAll(ingress.DefaultSSLDirectory, 0655)
|
||||
|
||||
config := &Configuration{
|
||||
Client: kubeClient,
|
||||
ResyncPeriod: *resyncPeriod,
|
||||
DefaultService: *defaultSvc,
|
||||
IngressClass: *ingressClass,
|
||||
Namespace: *watchNamespace,
|
||||
ConfigMapName: *configMap,
|
||||
TCPConfigMapName: *tcpConfigMapName,
|
||||
UDPConfigMapName: *udpConfigMapName,
|
||||
DefaultSSLCertificate: *defSSLCertificate,
|
||||
DefaultHealthzURL: *defHealthzURL,
|
||||
PublishService: *publishSvc,
|
||||
Backend: backend,
|
||||
}
|
||||
|
||||
ic := newIngressController(config)
|
||||
go registerHandlers(*profiling, *healthzPort, ic)
|
||||
return ic
|
||||
}
|
||||
|
||||
func registerHandlers(enableProfiling bool, port int, ic Interface) {
|
||||
mux := http.NewServeMux()
|
||||
healthz.InstallHandler(mux, ic)
|
||||
|
||||
mux.Handle("/metrics", prometheus.Handler())
|
||||
|
||||
mux.HandleFunc("/build", func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
fmt.Fprintf(w, ic.Info())
|
||||
})
|
||||
|
||||
mux.HandleFunc("/stop", func(w http.ResponseWriter, r *http.Request) {
|
||||
syscall.Kill(syscall.Getpid(), syscall.SIGTERM)
|
||||
})
|
||||
|
||||
if enableProfiling {
|
||||
mux.HandleFunc("/debug/pprof/", pprof.Index)
|
||||
mux.HandleFunc("/debug/pprof/profile", pprof.Profile)
|
||||
mux.HandleFunc("/debug/pprof/symbol", pprof.Symbol)
|
||||
mux.HandleFunc("/debug/pprof/trace", pprof.Trace)
|
||||
}
|
||||
|
||||
server := &http.Server{
|
||||
Addr: fmt.Sprintf(":%v", port),
|
||||
Handler: mux,
|
||||
}
|
||||
glog.Fatal(server.ListenAndServe())
|
||||
}
|
||||
60
core/pkg/ingress/controller/metrics.go
Normal file
60
core/pkg/ingress/controller/metrics.go
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
Copyright 2015 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 controller
|
||||
|
||||
import "github.com/prometheus/client_golang/prometheus"
|
||||
|
||||
const (
|
||||
ns = "ingress_controller"
|
||||
operation = "count"
|
||||
reloadLabel = "reloads"
|
||||
)
|
||||
|
||||
func init() {
|
||||
prometheus.MustRegister(reloadOperation)
|
||||
prometheus.MustRegister(reloadOperationErrors)
|
||||
|
||||
reloadOperationErrors.WithLabelValues(reloadLabel).Set(0)
|
||||
reloadOperation.WithLabelValues(reloadLabel).Set(0)
|
||||
}
|
||||
|
||||
var (
|
||||
reloadOperation = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Namespace: ns,
|
||||
Name: "success",
|
||||
Help: "Cumulative number of Ingress controller reload operations",
|
||||
},
|
||||
[]string{operation},
|
||||
)
|
||||
reloadOperationErrors = prometheus.NewCounterVec(
|
||||
prometheus.CounterOpts{
|
||||
Namespace: ns,
|
||||
Name: "errors",
|
||||
Help: "Cumulative number of Ingress controller errors during reload operations",
|
||||
},
|
||||
[]string{operation},
|
||||
)
|
||||
)
|
||||
|
||||
func incReloadCount() {
|
||||
reloadOperation.WithLabelValues(reloadLabel).Inc()
|
||||
}
|
||||
|
||||
func incReloadErrorCount() {
|
||||
reloadOperationErrors.WithLabelValues(reloadLabel).Inc()
|
||||
}
|
||||
105
core/pkg/ingress/controller/named_port.go
Normal file
105
core/pkg/ingress/controller/named_port.go
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
Copyright 2015 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 controller
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/service"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
podutil "k8s.io/kubernetes/pkg/api/pod"
|
||||
"k8s.io/kubernetes/pkg/labels"
|
||||
)
|
||||
|
||||
// checkSvcForUpdate verifies if one of the running pods for a service contains
|
||||
// named port. If the annotation in the service does not exists or is not equals
|
||||
// to the port mapping obtained from the pod the service must be updated to reflect
|
||||
// the current state
|
||||
func (ic *GenericController) checkSvcForUpdate(svc *api.Service) error {
|
||||
// get the pods associated with the service
|
||||
// TODO: switch this to a watch
|
||||
pods, err := ic.cfg.Client.Pods(svc.Namespace).List(api.ListOptions{
|
||||
LabelSelector: labels.Set(svc.Spec.Selector).AsSelector(),
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("error searching service pods %v/%v: %v", svc.Namespace, svc.Name, err)
|
||||
}
|
||||
|
||||
if len(pods.Items) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
namedPorts := map[string]string{}
|
||||
|
||||
// we need to check only one pod searching for named ports
|
||||
pod := &pods.Items[0]
|
||||
glog.V(4).Infof("checking pod %v/%v for named port information", pod.Namespace, pod.Name)
|
||||
for i := range svc.Spec.Ports {
|
||||
servicePort := &svc.Spec.Ports[i]
|
||||
|
||||
_, err := strconv.Atoi(servicePort.TargetPort.StrVal)
|
||||
if err != nil {
|
||||
portNum, err := podutil.FindPort(pod, servicePort)
|
||||
if err != nil {
|
||||
glog.V(4).Infof("failed to find port for service %s/%s: %v", portNum, svc.Namespace, svc.Name, err)
|
||||
continue
|
||||
}
|
||||
|
||||
if servicePort.TargetPort.StrVal == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
namedPorts[servicePort.TargetPort.StrVal] = fmt.Sprintf("%v", portNum)
|
||||
}
|
||||
}
|
||||
|
||||
if svc.ObjectMeta.Annotations == nil {
|
||||
svc.ObjectMeta.Annotations = map[string]string{}
|
||||
}
|
||||
|
||||
curNamedPort := svc.ObjectMeta.Annotations[service.NamedPortAnnotation]
|
||||
if len(namedPorts) > 0 && !reflect.DeepEqual(curNamedPort, namedPorts) {
|
||||
data, _ := json.Marshal(namedPorts)
|
||||
|
||||
newSvc, err := ic.cfg.Client.Services(svc.Namespace).Get(svc.Name)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error getting service %v/%v: %v", svc.Namespace, svc.Name, err)
|
||||
}
|
||||
|
||||
if newSvc.ObjectMeta.Annotations == nil {
|
||||
newSvc.ObjectMeta.Annotations = map[string]string{}
|
||||
}
|
||||
|
||||
newSvc.ObjectMeta.Annotations[service.NamedPortAnnotation] = string(data)
|
||||
glog.Infof("updating service %v with new named port mappings", svc.Name)
|
||||
_, err = ic.cfg.Client.Services(svc.Namespace).Update(newSvc)
|
||||
if err != nil {
|
||||
return fmt.Errorf("error syncing service %v/%v: %v", svc.Namespace, svc.Name, err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
95
core/pkg/ingress/controller/util.go
Normal file
95
core/pkg/ingress/controller/util.go
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
/*
|
||||
Copyright 2015 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 controller
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"k8s.io/ingress/core/pkg/ingress"
|
||||
"k8s.io/ingress/core/pkg/ingress/annotations/parser"
|
||||
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
)
|
||||
|
||||
// newDefaultServer return an UpstreamServer to be use as default server that returns 503.
|
||||
func newDefaultServer() ingress.UpstreamServer {
|
||||
return ingress.UpstreamServer{Address: "127.0.0.1", Port: "8181"}
|
||||
}
|
||||
|
||||
// newUpstream creates an upstream without servers.
|
||||
func newUpstream(name string) *ingress.Upstream {
|
||||
return &ingress.Upstream{
|
||||
Name: name,
|
||||
Backends: []ingress.UpstreamServer{},
|
||||
}
|
||||
}
|
||||
|
||||
func isHostValid(host string, cert *ingress.SSLCert) bool {
|
||||
if cert == nil {
|
||||
return false
|
||||
}
|
||||
for _, cn := range cert.CN {
|
||||
if matchHostnames(cn, host) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func matchHostnames(pattern, host string) bool {
|
||||
host = strings.TrimSuffix(host, ".")
|
||||
pattern = strings.TrimSuffix(pattern, ".")
|
||||
|
||||
if len(pattern) == 0 || len(host) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
patternParts := strings.Split(pattern, ".")
|
||||
hostParts := strings.Split(host, ".")
|
||||
|
||||
if len(patternParts) != len(hostParts) {
|
||||
return false
|
||||
}
|
||||
|
||||
for i, patternPart := range patternParts {
|
||||
if i == 0 && patternPart == "*" {
|
||||
continue
|
||||
}
|
||||
if patternPart != hostParts[i] {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// IsValidClass returns true if the given Ingress either doesn't specify
|
||||
// the ingress.class annotation, or it's set to the configured in the
|
||||
// ingress controller.
|
||||
func IsValidClass(ing *extensions.Ingress, class string) bool {
|
||||
if class == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
cc, _ := parser.GetStringAnnotation(ingressClassKey, ing)
|
||||
if cc == "" {
|
||||
return true
|
||||
}
|
||||
|
||||
return cc == class
|
||||
}
|
||||
50
core/pkg/ingress/controller/util_test.go
Normal file
50
core/pkg/ingress/controller/util_test.go
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
Copyright 2015 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 controller
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/apis/extensions"
|
||||
)
|
||||
|
||||
func TestIsValidClass(t *testing.T) {
|
||||
ing := &extensions.Ingress{
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
Namespace: api.NamespaceDefault,
|
||||
},
|
||||
}
|
||||
|
||||
b := IsValidClass(ing, "")
|
||||
if !b {
|
||||
t.Error("Expected a valid class (missing annotation)")
|
||||
}
|
||||
|
||||
data := map[string]string{}
|
||||
data[ingressClassKey] = "custom"
|
||||
ing.SetAnnotations(data)
|
||||
b = IsValidClass(ing, "custom")
|
||||
if !b {
|
||||
t.Errorf("Expected valid class but %v returned", b)
|
||||
}
|
||||
b = IsValidClass(ing, "nginx")
|
||||
if b {
|
||||
t.Errorf("Expected invalid class but %v returned", b)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue