github.com/stakater/IngressMonitorController@v1.0.103/pkg/kube/wrappers/ingress-wrapper.go (about)

     1  package wrappers
     2  
     3  import (
     4  	"context"
     5  	"log"
     6  	"net/url"
     7  	"path"
     8  	"strings"
     9  
    10  	"github.com/stakater/IngressMonitorController/pkg/constants"
    11  	"k8s.io/api/extensions/v1beta1"
    12  	meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    13  	"k8s.io/apimachinery/pkg/labels"
    14  	"k8s.io/client-go/kubernetes"
    15  )
    16  
    17  type IngressWrapper struct {
    18  	Ingress    *v1beta1.Ingress
    19  	Namespace  string
    20  	KubeClient kubernetes.Interface
    21  }
    22  
    23  func (iw *IngressWrapper) supportsTLS() bool {
    24  	if iw.Ingress.Spec.TLS != nil && len(iw.Ingress.Spec.TLS) > 0 && iw.Ingress.Spec.TLS[0].Hosts != nil && len(iw.Ingress.Spec.TLS[0].Hosts) > 0 && len(iw.Ingress.Spec.TLS[0].Hosts[0]) > 0 {
    25  		return true
    26  	}
    27  	return false
    28  }
    29  
    30  func (iw *IngressWrapper) tryGetTLSHost() (string, bool) {
    31  	if iw.supportsTLS() {
    32  		return "https://" + iw.Ingress.Spec.TLS[0].Hosts[0], true
    33  	}
    34  
    35  	annotations := iw.Ingress.GetAnnotations()
    36  	if value, ok := annotations[constants.ForceHTTPSAnnotation]; ok {
    37  		if value == "true" {
    38  			// Annotation exists and is enabled
    39  			return "https://" + iw.Ingress.Spec.Rules[0].Host, true
    40  		}
    41  	}
    42  
    43  	return "", false
    44  }
    45  
    46  func (iw *IngressWrapper) getHost() string {
    47  	return "http://" + iw.Ingress.Spec.Rules[0].Host
    48  }
    49  
    50  func (iw *IngressWrapper) rulesExist() bool {
    51  	if iw.Ingress.Spec.Rules != nil && len(iw.Ingress.Spec.Rules) > 0 {
    52  		return true
    53  	}
    54  	return false
    55  }
    56  
    57  func (iw *IngressWrapper) getIngressSubPath() string {
    58  	rule := iw.Ingress.Spec.Rules[0]
    59  	if rule.HTTP != nil {
    60  		if rule.HTTP.Paths != nil && len(rule.HTTP.Paths) > 0 {
    61  			path := rule.HTTP.Paths[0].Path
    62  
    63  			// Remove * from path if exists
    64  			path = strings.TrimRight(path, "*")
    65  			// Remove regex caputure group from path if exists
    66  			parsed := strings.Split(path, "(")
    67  			path = parsed[0]
    68  
    69  			return path
    70  		}
    71  	}
    72  	return ""
    73  }
    74  
    75  func (iw *IngressWrapper) GetURL() string {
    76  	if !iw.rulesExist() {
    77  		log.Println("No rules exist in ingress: " + iw.Ingress.GetName())
    78  		return ""
    79  	}
    80  
    81  	var URL string
    82  
    83  	if host, exists := iw.tryGetTLSHost(); exists { // Get TLS Host if it exists
    84  		URL = host
    85  	} else {
    86  		URL = iw.getHost() // Fallback for normal Host
    87  	}
    88  
    89  	// Convert url to url object
    90  	u, err := url.Parse(URL)
    91  
    92  	if err != nil {
    93  		log.Printf("URL parsing error in getURL() :%v", err)
    94  		return ""
    95  	}
    96  
    97  	annotations := iw.Ingress.GetAnnotations()
    98  
    99  	if value, ok := annotations[constants.OverridePathAnnotation]; ok {
   100  		u.Path = value
   101  	} else {
   102  		// ingressSubPath
   103  		ingressSubPath := iw.getIngressSubPath()
   104  		u.Path = path.Join(u.Path, ingressSubPath)
   105  
   106  		// Find pod by backtracking ingress -> service -> pod
   107  		healthEndpoint, exists := iw.tryGetHealthEndpointFromIngress()
   108  
   109  		// Health endpoint from pod successful
   110  		if exists {
   111  			u.Path = path.Join(u.Path, healthEndpoint)
   112  		} else { // Try to get annotation and set
   113  
   114  			// Annotation for health Endpoint exists
   115  			if value, ok := annotations[constants.MonitorHealthAnnotation]; ok {
   116  				u.Path = path.Join(u.Path, value)
   117  			}
   118  		}
   119  	}
   120  
   121  	return u.String()
   122  }
   123  
   124  func (iw *IngressWrapper) hasService() (string, bool) {
   125  	ingress := iw.Ingress
   126  	if ingress.Spec.Rules[0].HTTP != nil &&
   127  		ingress.Spec.Rules[0].HTTP.Paths != nil &&
   128  		len(ingress.Spec.Rules[0].HTTP.Paths) > 0 &&
   129  		ingress.Spec.Rules[0].HTTP.Paths[0].Backend.ServiceName != "" {
   130  		return ingress.Spec.Rules[0].HTTP.Paths[0].Backend.ServiceName, true
   131  	}
   132  	return "", false
   133  }
   134  
   135  func (iw *IngressWrapper) tryGetHealthEndpointFromIngress() (string, bool) {
   136  
   137  	serviceName, exists := iw.hasService()
   138  
   139  	if !exists {
   140  		return "", false
   141  	}
   142  
   143  	service, err := iw.KubeClient.CoreV1().Services(iw.Ingress.Namespace).Get(context.TODO(), serviceName, meta_v1.GetOptions{})
   144  	if err != nil {
   145  		log.Printf("Get service from kubernetes cluster error:%v", err)
   146  		return "", false
   147  	}
   148  
   149  	set := labels.Set(service.Spec.Selector)
   150  
   151  	if pods, err := iw.KubeClient.CoreV1().Pods(iw.Ingress.Namespace).List(context.TODO(), meta_v1.ListOptions{LabelSelector: set.AsSelector().String()}); err != nil {
   152  		log.Printf("List Pods of service[%s] error:%v", service.GetName(), err)
   153  	} else if len(pods.Items) > 0 {
   154  		pod := pods.Items[0]
   155  
   156  		podContainers := pod.Spec.Containers
   157  
   158  		if len(podContainers) == 1 {
   159  			if podContainers[0].ReadinessProbe != nil && podContainers[0].ReadinessProbe.HTTPGet != nil {
   160  				return podContainers[0].ReadinessProbe.HTTPGet.Path, true
   161  			}
   162  		} else {
   163  			log.Printf("Pod has %d containers so skipping health endpoint", len(podContainers))
   164  		}
   165  	}
   166  
   167  	return "", false
   168  }