istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pkg/config/analysis/analyzers/virtualservice/gateways.go (about)

     1  // Copyright Istio Authors
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package virtualservice
    16  
    17  import (
    18  	"fmt"
    19  	"strings"
    20  
    21  	"istio.io/api/networking/v1alpha3"
    22  	"istio.io/istio/pkg/config"
    23  	"istio.io/istio/pkg/config/analysis"
    24  	"istio.io/istio/pkg/config/analysis/analyzers/util"
    25  	"istio.io/istio/pkg/config/analysis/msg"
    26  	kubeconfig "istio.io/istio/pkg/config/gateway/kube"
    27  	"istio.io/istio/pkg/config/host"
    28  	"istio.io/istio/pkg/config/resource"
    29  	"istio.io/istio/pkg/config/schema/gvk"
    30  )
    31  
    32  // GatewayAnalyzer checks the gateways associated with each virtual service
    33  type GatewayAnalyzer struct{}
    34  
    35  var _ analysis.Analyzer = &GatewayAnalyzer{}
    36  
    37  // Metadata implements Analyzer
    38  func (s *GatewayAnalyzer) Metadata() analysis.Metadata {
    39  	return analysis.Metadata{
    40  		Name:        "virtualservice.GatewayAnalyzer",
    41  		Description: "Checks the gateways associated with each virtual service",
    42  		Inputs: []config.GroupVersionKind{
    43  			gvk.Gateway,
    44  			gvk.VirtualService,
    45  		},
    46  	}
    47  }
    48  
    49  // Analyze implements Analyzer
    50  func (s *GatewayAnalyzer) Analyze(c analysis.Context) {
    51  	c.ForEach(gvk.VirtualService, func(r *resource.Instance) bool {
    52  		s.analyzeVirtualService(r, c)
    53  		return true
    54  	})
    55  }
    56  
    57  func (s *GatewayAnalyzer) analyzeVirtualService(r *resource.Instance, c analysis.Context) {
    58  	vs := r.Message.(*v1alpha3.VirtualService)
    59  	vsNs := r.Metadata.FullName.Namespace
    60  	vsName := r.Metadata.FullName
    61  
    62  	for i, gwName := range vs.Gateways {
    63  		// This is a special-case accepted value
    64  		if gwName == util.MeshGateway {
    65  			continue
    66  		}
    67  
    68  		if kubeconfig.IsInternalGatewayReference(gwName) {
    69  			m := msg.NewReferencedInternalGateway(r, vsName.String(), gwName)
    70  
    71  			if line, ok := util.ErrorLine(r, fmt.Sprintf(util.VSGateway, i)); ok {
    72  				m.Line = line
    73  			}
    74  
    75  			c.Report(gvk.VirtualService, m)
    76  			continue
    77  		}
    78  
    79  		gwFullName := resource.NewShortOrFullName(vsNs, gwName)
    80  
    81  		if !c.Exists(gvk.Gateway, gwFullName) {
    82  			m := msg.NewReferencedResourceNotFound(r, "gateway", gwName)
    83  
    84  			if line, ok := util.ErrorLine(r, fmt.Sprintf(util.VSGateway, i)); ok {
    85  				m.Line = line
    86  			}
    87  
    88  			c.Report(gvk.VirtualService, m)
    89  		}
    90  
    91  		if !vsHostInGateway(c, gwFullName, vs.Hosts, vsNs.String()) {
    92  			m := msg.NewVirtualServiceHostNotFoundInGateway(r, vs.Hosts, vsName.String(), gwFullName.String())
    93  
    94  			if line, ok := util.ErrorLine(r, fmt.Sprintf(util.VSGateway, i)); ok {
    95  				m.Line = line
    96  			}
    97  
    98  			c.Report(gvk.VirtualService, m)
    99  		}
   100  	}
   101  }
   102  
   103  func vsHostInGateway(c analysis.Context, gateway resource.FullName, vsHosts []string, vsNamespace string) bool {
   104  	var gatewayHosts []string
   105  	var gatewayNs string
   106  
   107  	c.ForEach(gvk.Gateway, func(r *resource.Instance) bool {
   108  		if r.Metadata.FullName == gateway {
   109  			s := r.Message.(*v1alpha3.Gateway)
   110  			gatewayNs = r.Metadata.FullName.Namespace.String()
   111  			for _, v := range s.Servers {
   112  				sanitizeServerHostNamespace(v, gatewayNs)
   113  				gatewayHosts = append(gatewayHosts, v.Hosts...)
   114  			}
   115  		}
   116  
   117  		return true
   118  	})
   119  
   120  	gatewayHostNames := host.NamesForNamespace(gatewayHosts, vsNamespace)
   121  	for _, gh := range gatewayHostNames {
   122  		for _, vsh := range vsHosts {
   123  			gatewayHost := gh
   124  			vsHost := host.Name(vsh)
   125  
   126  			if gatewayHost.Matches(vsHost) {
   127  				return true
   128  			}
   129  		}
   130  	}
   131  
   132  	return false
   133  }
   134  
   135  // convert ./host to currentNamespace/Host
   136  // */host to just host
   137  // */* to just *
   138  func sanitizeServerHostNamespace(server *v1alpha3.Server, namespace string) {
   139  	for i, h := range server.Hosts {
   140  		if strings.Contains(h, "/") {
   141  			parts := strings.Split(h, "/")
   142  			if parts[0] == "." {
   143  				server.Hosts[i] = fmt.Sprintf("%s/%s", namespace, parts[1])
   144  			} else if parts[0] == "*" {
   145  				if parts[1] == "*" {
   146  					server.Hosts = []string{"*"}
   147  					return
   148  				}
   149  				server.Hosts[i] = parts[1]
   150  			}
   151  		}
   152  	}
   153  }