2016-02-22 00:13:08 +00:00
/ *
2016-09-08 11:02:39 +00:00
Copyright 2015 The Kubernetes Authors .
2016-02-22 00:13:08 +00:00
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 (
2017-04-18 19:44:17 +00:00
"encoding/json"
2016-02-22 00:13:08 +00:00
"fmt"
2016-05-29 23:05:38 +00:00
"sort"
2016-02-22 00:13:08 +00:00
"strconv"
"time"
2017-04-01 14:38:58 +00:00
"github.com/golang/glog"
2016-02-22 00:13:08 +00:00
compute "google.golang.org/api/compute/v1"
2017-04-01 14:38:58 +00:00
"k8s.io/apimachinery/pkg/api/meta"
meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/labels"
2017-05-05 18:34:40 +00:00
"k8s.io/apimachinery/pkg/types"
2017-04-01 14:38:58 +00:00
"k8s.io/apimachinery/pkg/util/intstr"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apimachinery/pkg/util/wait"
listers "k8s.io/client-go/listers/core/v1"
2017-04-07 05:27:50 +00:00
api_v1 "k8s.io/client-go/pkg/api/v1"
2017-04-01 14:38:58 +00:00
extensions "k8s.io/client-go/pkg/apis/extensions/v1beta1"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/util/workqueue"
2017-04-18 19:44:17 +00:00
"k8s.io/ingress/controllers/gce/backends"
2016-11-10 23:31:49 +00:00
"k8s.io/ingress/controllers/gce/loadbalancers"
"k8s.io/ingress/controllers/gce/utils"
2016-02-22 00:13:08 +00:00
)
2016-03-10 03:29:23 +00:00
const (
2016-06-29 01:03:12 +00:00
// 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.
2016-03-10 03:29:23 +00:00
staticIPNameKey = "kubernetes.io/ingress.global-static-ip-name"
2016-05-22 00:46:09 +00:00
2017-03-07 21:42:41 +00:00
// 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"
2017-04-18 19:44:17 +00:00
// 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"
2016-06-29 01:03:12 +00:00
// 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"
2016-05-22 00:46:09 +00:00
// Label key to denote which GCE zone a Kubernetes node is in.
zoneKey = "failure-domain.beta.kubernetes.io/zone"
defaultZone = ""
2016-03-10 03:29:23 +00:00
)
2016-02-22 00:13:08 +00:00
// 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
}
2017-03-07 21:42:41 +00:00
// 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
}
2016-03-10 03:29:23 +00:00
func ( ing ingAnnotations ) staticIPName ( ) string {
val , ok := ing [ staticIPNameKey ]
if ! ok {
return ""
}
return val
}
2016-06-29 01:03:12 +00:00
func ( ing ingAnnotations ) ingressClass ( ) string {
val , ok := ing [ ingressClassKey ]
if ! ok {
return ""
}
return val
}
2017-04-18 19:44:17 +00:00
// 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
}
2016-06-29 01:03:12 +00:00
// 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
}
2016-02-22 00:13:08 +00:00
// errorNodePortNotFound is an implementation of error.
type errorNodePortNotFound struct {
backend extensions . IngressBackend
origErr error
}
func ( e errorNodePortNotFound ) Error ( ) string {
return fmt . Sprintf ( "Could not find nodeport for backend %+v: %v" ,
e . backend , e . origErr )
}
2017-04-18 19:44:17 +00:00
type errorSvcAppProtosParsing struct {
svc * api_v1 . Service
origErr error
}
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 )
}
2016-02-22 00:13:08 +00:00
// taskQueue manages a work queue through an independent worker that
// invokes the given sync function for every work item inserted.
type taskQueue struct {
// queue is the work queue the worker polls
2016-06-28 03:06:35 +00:00
queue workqueue . RateLimitingInterface
2016-02-22 00:13:08 +00:00
// sync is called for each item in the queue
2016-06-28 03:06:35 +00:00
sync func ( string ) error
2016-02-22 00:13:08 +00:00
// workerDone is closed when the worker exits
workerDone chan struct { }
}
func ( t * taskQueue ) run ( period time . Duration , stopCh <- chan struct { } ) {
wait . Until ( t . worker , period , stopCh )
}
// enqueue enqueues ns/name of the given api object in the task queue.
func ( t * taskQueue ) enqueue ( obj interface { } ) {
key , err := keyFunc ( obj )
if err != nil {
glog . Infof ( "Couldn't get key for object %+v: %v" , obj , err )
return
}
t . queue . Add ( key )
}
// worker processes work in the queue through sync.
func ( t * taskQueue ) worker ( ) {
for {
key , quit := t . queue . Get ( )
if quit {
close ( t . workerDone )
return
}
glog . V ( 3 ) . Infof ( "Syncing %v" , key )
2016-06-28 03:06:35 +00:00
if err := t . sync ( key . ( string ) ) ; err != nil {
glog . Errorf ( "Requeuing %v, err %v" , key , err )
t . queue . AddRateLimited ( key )
} else {
t . queue . Forget ( key )
}
2016-02-22 00:13:08 +00:00
t . queue . Done ( key )
}
}
// shutdown shuts down the work queue and waits for the worker to ACK
func ( t * taskQueue ) shutdown ( ) {
t . queue . ShutDown ( )
<- t . workerDone
}
// NewTaskQueue creates a new task queue with the given sync function.
// The sync function is called for every element inserted into the queue.
2016-06-28 03:06:35 +00:00
func NewTaskQueue ( syncFn func ( string ) error ) * taskQueue {
2016-02-22 00:13:08 +00:00
return & taskQueue {
2016-06-28 03:06:35 +00:00
queue : workqueue . NewRateLimitingQueue ( workqueue . DefaultControllerRateLimiter ( ) ) ,
2016-02-22 00:13:08 +00:00
sync : syncFn ,
workerDone : make ( chan struct { } ) ,
}
}
// compareLinks returns true if the 2 self links are equal.
func compareLinks ( l1 , l2 string ) bool {
// TODO: These can be partial links
return l1 == l2 && l1 != ""
}
// StoreToIngressLister makes a Store that lists Ingress.
// TODO: Move this to cache/listers post 1.1.
type StoreToIngressLister struct {
cache . Store
}
2017-04-01 14:38:58 +00:00
// StoreToNodeLister makes a Store that lists Node.
type StoreToNodeLister struct {
cache . Indexer
}
// StoreToServiceLister makes a Store that lists Service.
type StoreToServiceLister struct {
cache . Indexer
}
// StoreToPodLister makes a Store that lists Pods.
type StoreToPodLister struct {
cache . Indexer
}
2017-04-18 19:44:17 +00:00
// List returns a list of all pods based on selector
2017-04-07 05:27:50 +00:00
func ( s * StoreToPodLister ) List ( selector labels . Selector ) ( ret [ ] * api_v1 . Pod , err error ) {
2017-04-01 14:38:58 +00:00
err = ListAll ( s . Indexer , selector , func ( m interface { } ) {
2017-04-07 05:27:50 +00:00
ret = append ( ret , m . ( * api_v1 . Pod ) )
2017-04-01 14:38:58 +00:00
} )
return ret , err
}
2017-04-18 19:44:17 +00:00
// ListAll iterates a store and passes selected item to a func
2017-04-01 14:38:58 +00:00
func ListAll ( store cache . Store , selector labels . Selector , appendFn cache . AppendFunc ) error {
for _ , m := range store . List ( ) {
metadata , err := meta . Accessor ( m )
if err != nil {
return err
}
if selector . Matches ( labels . Set ( metadata . GetLabels ( ) ) ) {
appendFn ( m )
}
}
return nil
}
2016-02-22 00:13:08 +00:00
// List lists all Ingress' in the store.
func ( s * StoreToIngressLister ) List ( ) ( ing extensions . IngressList , err error ) {
for _ , m := range s . Store . List ( ) {
2016-08-08 17:27:36 +00:00
newIng := m . ( * extensions . Ingress )
if isGCEIngress ( newIng ) {
ing . Items = append ( ing . Items , * newIng )
}
2016-02-22 00:13:08 +00:00
}
return ing , nil
}
// GetServiceIngress gets all the Ingress' that have rules pointing to a service.
// Note that this ignores services without the right nodePorts.
2017-04-07 05:27:50 +00:00
func ( s * StoreToIngressLister ) GetServiceIngress ( svc * api_v1 . Service ) ( ings [ ] extensions . Ingress , err error ) {
2017-03-22 00:05:17 +00:00
IngressLoop :
2016-02-22 00:13:08 +00:00
for _ , m := range s . Store . List ( ) {
ing := * m . ( * extensions . Ingress )
if ing . Namespace != svc . Namespace {
continue
}
2017-03-22 00:05:17 +00:00
// Check service of default backend
if ing . Spec . Backend != nil && ing . Spec . Backend . ServiceName == svc . Name {
ings = append ( ings , ing )
continue
}
// Check the target service for each path rule
for _ , rule := range ing . Spec . Rules {
if rule . IngressRuleValue . HTTP == nil {
2016-02-22 00:13:08 +00:00
continue
}
2017-03-22 00:05:17 +00:00
for _ , p := range rule . IngressRuleValue . HTTP . Paths {
2016-02-22 00:13:08 +00:00
if p . Backend . ServiceName == svc . Name {
ings = append ( ings , ing )
2017-03-22 00:05:17 +00:00
// Skip the rest of the rules to avoid duplicate ingresses in list
continue IngressLoop
2016-02-22 00:13:08 +00:00
}
}
}
}
if len ( ings ) == 0 {
2017-01-06 08:12:25 +00:00
err = fmt . Errorf ( "no ingress for service %v" , svc . Name )
2016-02-22 00:13:08 +00:00
}
return
}
// GCETranslator helps with kubernetes -> gce api conversion.
type GCETranslator struct {
* LoadBalancerController
}
2016-11-11 01:45:20 +00:00
// toURLMap converts an ingress to a map of subdomain: url-regex: gce backend.
func ( t * GCETranslator ) toURLMap ( ing * extensions . Ingress ) ( utils . GCEURLMap , error ) {
2016-02-22 00:13:08 +00:00
hostPathBackend := utils . GCEURLMap { }
for _ , rule := range ing . Spec . Rules {
if rule . HTTP == nil {
glog . Errorf ( "Ignoring non http Ingress rule" )
continue
}
pathToBackend := map [ string ] * compute . BackendService { }
for _ , p := range rule . HTTP . Paths {
backend , err := t . toGCEBackend ( & p . Backend , ing . Namespace )
if err != nil {
// If a service doesn't have a nodeport we can still forward traffic
// to all other services under the assumption that the user will
// modify nodeport.
if _ , ok := err . ( errorNodePortNotFound ) ; ok {
2017-04-07 05:27:50 +00:00
t . recorder . Eventf ( ing , api_v1 . EventTypeWarning , "Service" , err . ( errorNodePortNotFound ) . Error ( ) )
2016-02-22 00:13:08 +00:00
continue
}
// If a service doesn't have a backend, there's nothing the user
// can do to correct this (the admin might've limited quota).
// So keep requeuing the l7 till all backends exist.
return utils . GCEURLMap { } , err
}
// The Ingress spec defines empty path as catch-all, so if a user
// asks for a single host and multiple empty paths, all traffic is
// sent to one of the last backend in the rules list.
path := p . Path
if path == "" {
path = loadbalancers . DefaultPath
}
pathToBackend [ path ] = backend
}
// If multiple hostless rule sets are specified, last one wins
host := rule . Host
if host == "" {
host = loadbalancers . DefaultHost
}
hostPathBackend [ host ] = pathToBackend
}
2017-01-09 15:52:48 +00:00
var defaultBackend * compute . BackendService
if ing . Spec . Backend != nil {
var err error
defaultBackend , err = t . toGCEBackend ( ing . Spec . Backend , ing . Namespace )
if err != nil {
msg := fmt . Sprintf ( "%v" , err )
if _ , ok := err . ( errorNodePortNotFound ) ; ok {
msg = fmt . Sprintf ( "couldn't find nodeport for %v/%v" , ing . Namespace , ing . Spec . Backend . ServiceName )
}
2017-04-07 05:27:50 +00:00
t . recorder . Eventf ( ing , api_v1 . EventTypeWarning , "Service" , fmt . Sprintf ( "failed to identify user specified default backend, %v, using system default" , msg ) )
2017-01-09 15:52:48 +00:00
} else if defaultBackend != nil {
2017-04-07 05:27:50 +00:00
t . recorder . Eventf ( ing , api_v1 . EventTypeNormal , "Service" , fmt . Sprintf ( "default backend set to %v:%v" , ing . Spec . Backend . ServiceName , defaultBackend . Port ) )
2017-01-09 15:52:48 +00:00
}
} else {
2017-04-07 05:27:50 +00:00
t . recorder . Eventf ( ing , api_v1 . EventTypeNormal , "Service" , "no user specified default backend, using system default" )
2016-11-23 02:30:31 +00:00
}
2017-01-09 15:52:48 +00:00
hostPathBackend . PutDefaultBackend ( defaultBackend )
2016-02-22 00:13:08 +00:00
return hostPathBackend , nil
}
func ( t * GCETranslator ) toGCEBackend ( be * extensions . IngressBackend , ns string ) ( * compute . BackendService , error ) {
if be == nil {
return nil , nil
}
port , err := t . getServiceNodePort ( * be , ns )
if err != nil {
return nil , err
}
2017-04-18 19:44:17 +00:00
backend , err := t . CloudClusterManager . backendPool . Get ( port . Port )
2016-02-22 00:13:08 +00:00
if err != nil {
2017-04-18 19:44:17 +00:00
return nil , fmt . Errorf ( "no GCE backend exists for port %v, kube backend %+v" , port , be )
2016-02-22 00:13:08 +00:00
}
return backend , nil
}
// getServiceNodePort looks in the svc store for a matching service:port,
// and returns the nodeport.
2017-04-18 19:44:17 +00:00
func ( t * GCETranslator ) getServiceNodePort ( be extensions . IngressBackend , namespace string ) ( backends . ServicePort , error ) {
2016-11-10 23:31:49 +00:00
obj , exists , err := t . svcLister . Indexer . Get (
2017-04-07 05:27:50 +00:00
& api_v1 . Service {
2017-04-01 14:38:58 +00:00
ObjectMeta : meta_v1 . ObjectMeta {
2016-02-22 00:13:08 +00:00
Name : be . ServiceName ,
Namespace : namespace ,
} ,
} )
if ! exists {
2017-04-18 19:44:17 +00:00
return backends . ServicePort { } , errorNodePortNotFound { be , fmt . Errorf ( "service %v/%v not found in store" , namespace , be . ServiceName ) }
}
if err != nil {
return backends . ServicePort { } , errorNodePortNotFound { be , err }
2016-02-22 00:13:08 +00:00
}
2017-04-18 19:44:17 +00:00
svc := obj . ( * api_v1 . Service )
appProtocols , err := svcAnnotations ( svc . GetAnnotations ( ) ) . ApplicationProtocols ( )
2016-02-22 00:13:08 +00:00
if err != nil {
2017-04-18 19:44:17 +00:00
return backends . ServicePort { } , errorSvcAppProtosParsing { svc , err }
2016-02-22 00:13:08 +00:00
}
2017-04-18 19:44:17 +00:00
var port * api_v1 . ServicePort
PortLoop :
for _ , p := range svc . Spec . Ports {
np := p
2016-02-22 00:13:08 +00:00
switch be . ServicePort . Type {
case intstr . Int :
2016-05-09 19:06:09 +00:00
if p . Port == be . ServicePort . IntVal {
2017-04-18 19:44:17 +00:00
port = & np
break PortLoop
2016-02-22 00:13:08 +00:00
}
default :
if p . Name == be . ServicePort . StrVal {
2017-04-18 19:44:17 +00:00
port = & np
break PortLoop
2016-02-22 00:13:08 +00:00
}
}
}
2017-04-18 19:44:17 +00:00
if port == nil {
return backends . ServicePort { } , errorNodePortNotFound { be , fmt . Errorf ( "could not find matching nodeport from service" ) }
2016-02-22 00:13:08 +00:00
}
2017-04-18 19:44:17 +00:00
proto := utils . ProtocolHTTP
if protoStr , exists := appProtocols [ port . Name ] ; exists {
proto = utils . AppProtocol ( protoStr )
}
2017-05-05 18:34:40 +00:00
p := backends . ServicePort {
Port : int64 ( port . NodePort ) ,
Protocol : proto ,
SvcName : types . NamespacedName { Namespace : namespace , Name : be . ServiceName } ,
SvcPort : be . ServicePort ,
}
2017-04-18 19:44:17 +00:00
return p , nil
2016-02-22 00:13:08 +00:00
}
// toNodePorts converts a pathlist to a flat list of nodeports.
2017-04-18 19:44:17 +00:00
func ( t * GCETranslator ) toNodePorts ( ings * extensions . IngressList ) [ ] backends . ServicePort {
var knownPorts [ ] backends . ServicePort
2016-02-22 00:13:08 +00:00
for _ , ing := range ings . Items {
defaultBackend := ing . Spec . Backend
if defaultBackend != nil {
port , err := t . getServiceNodePort ( * defaultBackend , ing . Namespace )
if err != nil {
glog . Infof ( "%v" , err )
} else {
2017-04-18 19:44:17 +00:00
knownPorts = append ( knownPorts , port )
2016-02-22 00:13:08 +00:00
}
}
for _ , rule := range ing . Spec . Rules {
if rule . HTTP == nil {
2017-01-06 08:12:25 +00:00
glog . Errorf ( "ignoring non http Ingress rule" )
2016-02-22 00:13:08 +00:00
continue
}
for _ , path := range rule . HTTP . Paths {
port , err := t . getServiceNodePort ( path . Backend , ing . Namespace )
if err != nil {
glog . Infof ( "%v" , err )
continue
}
2017-04-18 19:44:17 +00:00
knownPorts = append ( knownPorts , port )
2016-02-22 00:13:08 +00:00
}
}
}
return knownPorts
}
2016-05-22 00:46:09 +00:00
2017-04-07 05:27:50 +00:00
func getZone ( n * api_v1 . Node ) string {
2016-05-22 00:46:09 +00:00
zone , ok := n . Labels [ zoneKey ]
if ! ok {
return defaultZone
}
return zone
}
// GetZoneForNode returns the zone for a given node by looking up its zone label.
func ( t * GCETranslator ) GetZoneForNode ( name string ) ( string , error ) {
2017-04-01 14:38:58 +00:00
nodes , err := listers . NewNodeLister ( t . nodeLister . Indexer ) . ListWithPredicate ( getNodeReadyPredicate ( ) )
2016-05-22 00:46:09 +00:00
if err != nil {
return "" , err
}
2016-08-10 19:00:16 +00:00
for _ , n := range nodes {
2016-05-22 00:46:09 +00:00
if n . Name == name {
// TODO: Make this more resilient to label changes by listing
// cloud nodes and figuring out zone.
return getZone ( n ) , nil
}
}
2017-01-06 08:12:25 +00:00
return "" , fmt . Errorf ( "node not found %v" , name )
2016-05-22 00:46:09 +00:00
}
// ListZones returns a list of zones this Kubernetes cluster spans.
func ( t * GCETranslator ) ListZones ( ) ( [ ] string , error ) {
zones := sets . String { }
2017-04-01 14:38:58 +00:00
readyNodes , err := listers . NewNodeLister ( t . nodeLister . Indexer ) . ListWithPredicate ( getNodeReadyPredicate ( ) )
2016-05-22 00:46:09 +00:00
if err != nil {
return zones . List ( ) , err
}
2016-08-10 19:00:16 +00:00
for _ , n := range readyNodes {
2016-05-22 00:46:09 +00:00
zones . Insert ( getZone ( n ) )
}
return zones . List ( ) , nil
}
2016-05-29 05:02:39 +00:00
// geHTTPProbe returns the http readiness probe from the first container
// that matches targetPort, from the set of pods matching the given labels.
2017-04-18 19:44:17 +00:00
func ( t * GCETranslator ) getHTTPProbe ( svc api_v1 . Service , targetPort intstr . IntOrString , protocol utils . AppProtocol ) ( * api_v1 . Probe , error ) {
2016-09-22 21:56:50 +00:00
l := svc . Spec . Selector
2016-05-29 05:02:39 +00:00
// Lookup any container with a matching targetPort from the set of pods
// with a matching label selector.
pl , err := t . podLister . List ( labels . SelectorFromSet ( labels . Set ( l ) ) )
if err != nil {
return nil , err
}
2016-05-29 23:05:38 +00:00
// If multiple endpoints have different health checks, take the first
sort . Sort ( PodsByCreationTimestamp ( pl ) )
2016-05-29 05:02:39 +00:00
for _ , pod := range pl {
2016-09-22 21:56:50 +00:00
if pod . Namespace != svc . Namespace {
continue
}
2016-05-29 05:02:39 +00:00
logStr := fmt . Sprintf ( "Pod %v matching service selectors %v (targetport %+v)" , pod . Name , l , targetPort )
for _ , c := range pod . Spec . Containers {
2017-04-18 19:44:17 +00:00
if ! isSimpleHTTPProbe ( c . ReadinessProbe ) || string ( protocol ) != string ( c . ReadinessProbe . HTTPGet . Scheme ) {
2016-05-29 05:02:39 +00:00
continue
}
2017-04-18 19:44:17 +00:00
2016-05-29 05:02:39 +00:00
for _ , p := range c . Ports {
2017-04-18 19:44:17 +00:00
if ( targetPort . Type == intstr . Int && targetPort . IntVal == p . ContainerPort ) ||
( targetPort . Type == intstr . String && targetPort . StrVal == p . Name ) {
2017-01-27 22:22:44 +00:00
readinessProbePort := c . ReadinessProbe . Handler . HTTPGet . Port
switch readinessProbePort . Type {
case intstr . Int :
if readinessProbePort . IntVal == p . ContainerPort {
return c . ReadinessProbe , nil
}
case intstr . String :
if readinessProbePort . StrVal == p . Name {
return c . ReadinessProbe , nil
}
2016-05-29 05:02:39 +00:00
}
2017-01-27 22:22:44 +00:00
2016-05-29 23:05:38 +00:00
glog . Infof ( "%v: found matching targetPort on container %v, but not on readinessProbe (%+v)" ,
logStr , c . Name , c . ReadinessProbe . Handler . HTTPGet . Port )
2016-05-29 05:02:39 +00:00
}
}
}
glog . V ( 4 ) . Infof ( "%v: lacks a matching HTTP probe for use in health checks." , logStr )
}
return nil , nil
}
2016-06-28 04:04:17 +00:00
// isSimpleHTTPProbe returns true if the given Probe is:
// - an HTTPGet probe, as opposed to a tcp or exec probe
// - has no special host or headers fields
2017-04-07 05:27:50 +00:00
func isSimpleHTTPProbe ( probe * api_v1 . Probe ) bool {
2016-06-28 04:04:17 +00:00
return ( probe != nil && probe . Handler . HTTPGet != nil && probe . Handler . HTTPGet . Host == "" &&
2017-04-18 19:44:17 +00:00
len ( probe . Handler . HTTPGet . HTTPHeaders ) == 0 )
2016-06-28 04:04:17 +00:00
}
2017-04-18 19:44:17 +00:00
// GetProbe returns a probe that's used for the given nodeport
func ( t * GCETranslator ) GetProbe ( port backends . ServicePort ) ( * api_v1 . Probe , error ) {
2017-04-01 14:38:58 +00:00
sl := t . svcLister . List ( )
2017-04-18 19:44:17 +00:00
2016-05-29 05:02:39 +00:00
// Find the label and target port of the one service with the given nodePort
2017-04-18 19:44:17 +00:00
var service api_v1 . Service
var svcPort api_v1 . ServicePort
var found bool
OuterLoop :
2017-04-01 14:38:58 +00:00
for _ , as := range sl {
2017-04-18 19:44:17 +00:00
service = * as . ( * api_v1 . Service )
for _ , sp := range service . Spec . Ports {
svcPort = sp
2016-11-23 02:30:31 +00:00
// only one Service can match this nodePort, try and look up
// the readiness probe of the pods behind it
2017-04-18 19:44:17 +00:00
if int32 ( port . Port ) == sp . NodePort {
found = true
break OuterLoop
2016-11-23 02:30:31 +00:00
}
2016-05-29 05:02:39 +00:00
}
}
2017-04-18 19:44:17 +00:00
if ! found {
return nil , fmt . Errorf ( "unable to find nodeport %v in any service" , port )
2016-11-23 02:30:31 +00:00
}
2017-04-18 19:44:17 +00:00
return t . getHTTPProbe ( service , svcPort . TargetPort , port . Protocol )
2016-05-29 23:05:38 +00:00
}
// PodsByCreationTimestamp sorts a list of Pods by creation timestamp, using their names as a tie breaker.
2017-04-07 05:27:50 +00:00
type PodsByCreationTimestamp [ ] * api_v1 . Pod
2016-05-29 23:05:38 +00:00
func ( o PodsByCreationTimestamp ) Len ( ) int { return len ( o ) }
func ( o PodsByCreationTimestamp ) Swap ( i , j int ) { o [ i ] , o [ j ] = o [ j ] , o [ i ] }
func ( o PodsByCreationTimestamp ) Less ( i , j int ) bool {
if o [ i ] . CreationTimestamp . Equal ( o [ j ] . CreationTimestamp ) {
return o [ i ] . Name < o [ j ] . Name
}
return o [ i ] . CreationTimestamp . Before ( o [ j ] . CreationTimestamp )
2016-05-29 05:02:39 +00:00
}