github.com/kiali/kiali@v1.84.0/business/checkers/virtualservices/single_host_checker.go (about)

     1  package virtualservices
     2  
     3  import (
     4  	networking_v1beta1 "istio.io/client-go/pkg/apis/networking/v1beta1"
     5  
     6  	"github.com/kiali/kiali/kubernetes"
     7  	"github.com/kiali/kiali/models"
     8  )
     9  
    10  type SingleHostChecker struct {
    11  	Cluster         string
    12  	Namespaces      models.Namespaces
    13  	VirtualServices []*networking_v1beta1.VirtualService
    14  }
    15  
    16  func (s SingleHostChecker) Check() models.IstioValidations {
    17  	hostCounter := make(map[string]map[string]map[string]map[string][]*networking_v1beta1.VirtualService)
    18  	validations := models.IstioValidations{}
    19  
    20  	for _, vs := range s.VirtualServices {
    21  		for _, host := range s.getHosts(vs) {
    22  			storeHost(hostCounter, vs, host)
    23  		}
    24  	}
    25  
    26  	for _, gateways := range hostCounter {
    27  		for _, clusterCounter := range gateways {
    28  			for _, namespaceCounter := range clusterCounter {
    29  				for _, serviceCounter := range namespaceCounter {
    30  					isNamespaceWildcard := len(namespaceCounter["*"]) > 0
    31  					targetSameHost := len(serviceCounter) > 1
    32  					otherServiceHosts := len(namespaceCounter) > 1
    33  					for _, virtualService := range serviceCounter {
    34  						// Marking virtualService as invalid if:
    35  						// - there is more than one virtual service per a host
    36  						// - there is one virtual service with wildcard and there are other virtual services pointing
    37  						//   a host for that namespace
    38  						if targetSameHost {
    39  							// Reference everything within serviceCounter
    40  							multipleVirtualServiceCheck(virtualService, validations, serviceCounter, s.Cluster)
    41  						}
    42  
    43  						if isNamespaceWildcard && otherServiceHosts {
    44  							// Reference the * or in case of * the other hosts inside namespace
    45  							// or other stars
    46  							refs := make([]*networking_v1beta1.VirtualService, 0, len(namespaceCounter))
    47  							// here in case of a, b and *, references should be a -> *, b -> *, * -> q,b
    48  							// * should be referenced to a,b
    49  							if containsVirtualService(virtualService, namespaceCounter["*"]) {
    50  								for _, _serviceCounter := range namespaceCounter {
    51  									refs = append(refs, _serviceCounter...)
    52  								}
    53  							} else {
    54  								// a or b referencing to *
    55  								refs = append(refs, namespaceCounter["*"]...)
    56  							}
    57  							multipleVirtualServiceCheck(virtualService, validations, refs, s.Cluster)
    58  						}
    59  					}
    60  				}
    61  			}
    62  		}
    63  	}
    64  
    65  	return validations
    66  }
    67  
    68  func containsVirtualService(vs *networking_v1beta1.VirtualService, vss []*networking_v1beta1.VirtualService) bool {
    69  	for _, item := range vss {
    70  		if vs.Name == item.Name && vs.Namespace == item.Namespace {
    71  			return true
    72  		}
    73  	}
    74  	return false
    75  }
    76  
    77  func multipleVirtualServiceCheck(virtualService *networking_v1beta1.VirtualService, validations models.IstioValidations, references []*networking_v1beta1.VirtualService, cluster string) {
    78  	virtualServiceName := virtualService.Name
    79  	key := models.IstioValidationKey{Name: virtualServiceName, Namespace: virtualService.Namespace, ObjectType: "virtualservice", Cluster: cluster}
    80  	checks := models.Build("virtualservices.singlehost", "spec/hosts")
    81  	rrValidation := &models.IstioValidation{
    82  		Name:       virtualServiceName,
    83  		ObjectType: "virtualservice",
    84  		Valid:      true,
    85  		Checks: []*models.IstioCheck{
    86  			&checks,
    87  		},
    88  		References: make([]models.IstioValidationKey, 0, len(references)),
    89  	}
    90  
    91  	for _, ref := range references {
    92  		refKey := models.IstioValidationKey{Name: ref.Name, Namespace: ref.Namespace, ObjectType: "virtualservice", Cluster: cluster}
    93  		if refKey != key {
    94  			rrValidation.References = append(rrValidation.References, refKey)
    95  		}
    96  	}
    97  
    98  	validations.MergeValidations(models.IstioValidations{key: rrValidation})
    99  }
   100  
   101  func storeHost(hostCounter map[string]map[string]map[string]map[string][]*networking_v1beta1.VirtualService, vs *networking_v1beta1.VirtualService, host kubernetes.Host) {
   102  	vsList := []*networking_v1beta1.VirtualService{vs}
   103  
   104  	gwList := vs.Spec.Gateways
   105  	if len(gwList) == 0 {
   106  		gwList = []string{"no-gateway"}
   107  	}
   108  
   109  	cluster := host.Cluster
   110  	namespace := host.Namespace
   111  	service := host.Service
   112  
   113  	for _, gw := range gwList {
   114  		if hostCounter[gw] == nil {
   115  			hostCounter[gw] = map[string]map[string]map[string][]*networking_v1beta1.VirtualService{
   116  				cluster: {
   117  					namespace: {
   118  						service: vsList,
   119  					},
   120  				},
   121  			}
   122  		} else if hostCounter[gw][cluster] == nil {
   123  			hostCounter[gw][cluster] = map[string]map[string][]*networking_v1beta1.VirtualService{
   124  				namespace: {
   125  					service: vsList,
   126  				},
   127  			}
   128  		} else if hostCounter[gw][cluster][namespace] == nil {
   129  			hostCounter[gw][cluster][namespace] = map[string][]*networking_v1beta1.VirtualService{
   130  				service: vsList,
   131  			}
   132  		} else if _, ok := hostCounter[gw][cluster][namespace][service]; !ok {
   133  			hostCounter[gw][cluster][namespace][service] = vsList
   134  		} else {
   135  			hostCounter[gw][cluster][namespace][service] = append(hostCounter[gw][cluster][namespace][service], vs)
   136  		}
   137  	}
   138  }
   139  
   140  func (s SingleHostChecker) getHosts(virtualService *networking_v1beta1.VirtualService) []kubernetes.Host {
   141  	namespace := virtualService.Namespace
   142  
   143  	if len(virtualService.Spec.Hosts) == 0 {
   144  		return []kubernetes.Host{}
   145  	}
   146  
   147  	targetHosts := make([]kubernetes.Host, 0, len(virtualService.Spec.Hosts))
   148  
   149  	for _, hostName := range virtualService.Spec.Hosts {
   150  		targetHosts = append(targetHosts, kubernetes.GetHost(hostName, namespace, s.Namespaces.GetNames()))
   151  	}
   152  	return targetHosts
   153  }