in pilot/pkg/config/kube/gateway/conversion.go [1142:1306]
func convertGateways(r *KubernetesResources) ([]config.Config, map[parentKey]map[k8s.SectionName]*parentInfo, sets.Set) {
// result stores our generated Istio Gateways
result := []config.Config{}
// gwMap stores an index to access parentInfo (which corresponds to a Kubernetes Gateway)
gwMap := map[parentKey]map[k8s.SectionName]*parentInfo{}
// namespaceLabelReferences keeps track of all namespace label keys referenced by Gateways. This is
// used to ensure we handle namespace updates for those keys.
namespaceLabelReferences := sets.New()
classes := getGatewayClasses(r)
for _, obj := range r.Gateway {
obj := obj
kgw := obj.Spec.(*k8s.GatewaySpec)
if _, f := classes[string(kgw.GatewayClassName)]; !f {
// No gateway class found, this may be meant for another controller; should be skipped.
continue
}
// Setup initial conditions to the success state. If we encounter errors, we will update this.
gatewayConditions := map[string]*condition{
string(k8s.GatewayConditionReady): {
reason: "ListenersValid",
message: "Listeners valid",
},
}
if isManaged(kgw) {
gatewayConditions[string(k8s.GatewayConditionScheduled)] = &condition{
error: &ConfigError{
Reason: "ResourcesPending",
Message: "Resources not yet deployed to the cluster",
},
setOnce: string(k8s.GatewayReasonNotReconciled), // Default reason
}
} else {
gatewayConditions[string(k8s.GatewayConditionScheduled)] = &condition{
reason: "ResourcesAvailable",
message: "Resources available",
}
}
servers := []*istio.Server{}
// Extract the addresses. A gateway will bind to a specific Service
gatewayServices, skippedAddresses := extractGatewayServices(r, kgw, obj)
invalidListeners := []k8s.SectionName{}
for i, l := range kgw.Listeners {
i := i
namespaceLabelReferences.InsertAll(getNamespaceLabelReferences(l.AllowedRoutes)...)
server, ok := buildListener(r, obj, l, i)
if !ok {
invalidListeners = append(invalidListeners, l.Name)
continue
}
meta := parentMeta(obj, &l.Name)
meta[model.InternalGatewayServiceAnnotation] = strings.Join(gatewayServices, ",")
// Each listener generates an Istio Gateway with a single Server. This allows binding to a specific listener.
gatewayConfig := config.Config{
Meta: config.Meta{
CreationTimestamp: obj.CreationTimestamp,
GroupVersionKind: gvk.Gateway,
Name: fmt.Sprintf("%s-%s-%s", obj.Name, constants.KubernetesGatewayName, l.Name),
Annotations: meta,
Namespace: obj.Namespace,
Domain: r.Domain,
},
Spec: &istio.Gateway{
Servers: []*istio.Server{server},
},
}
ref := parentKey{
Kind: gvk.KubernetesGateway,
Name: obj.Name,
Namespace: obj.Namespace,
}
if _, f := gwMap[ref]; !f {
gwMap[ref] = map[k8s.SectionName]*parentInfo{}
}
pri := &parentInfo{
InternalName: obj.Namespace + "/" + gatewayConfig.Name,
AllowedKinds: generateSupportedKinds(l),
Hostnames: server.Hosts,
OriginalHostname: emptyIfNil((*string)(l.Hostname)),
}
pri.ReportAttachedRoutes = func() {
reportListenerAttachedRoutes(i, obj, pri.AttachedRoutes)
}
gwMap[ref][l.Name] = pri
result = append(result, gatewayConfig)
servers = append(servers, server)
}
// If "gateway.istio.io/alias-for" annotation is present, any Route
// that binds to the gateway will bind to its alias instead.
// The typical usage is when the original gateway is not managed by the gateway controller
// but the ( generated ) alias is. This allows people to build their own
// gateway controllers on top of Istio Gateway Controller.
if obj.Annotations != nil && obj.Annotations[gatewayAliasForAnnotationKey] != "" {
ref := parentKey{
Kind: gvk.KubernetesGateway,
Name: obj.Annotations[gatewayAliasForAnnotationKey],
Namespace: obj.Namespace,
}
alias := parentKey{
Kind: gvk.KubernetesGateway,
Name: obj.Name,
Namespace: obj.Namespace,
}
gwMap[ref] = gwMap[alias]
}
internal, external, warnings := r.Context.ResolveGatewayInstances(obj.Namespace, gatewayServices, servers)
if len(skippedAddresses) > 0 {
warnings = append(warnings, fmt.Sprintf("Only Hostname is supported, ignoring %v", skippedAddresses))
}
if len(warnings) > 0 {
var msg string
if len(internal) > 0 {
msg = fmt.Sprintf("Assigned to service(s) %s, but failed to assign to all requested addresses: %s",
humanReadableJoin(internal), strings.Join(warnings, "; "))
} else {
msg = fmt.Sprintf("failed to assign to any requested addresses: %s", strings.Join(warnings, "; "))
}
gatewayConditions[string(k8s.GatewayConditionReady)].error = &ConfigError{
Reason: string(k8s.GatewayReasonAddressNotAssigned),
Message: msg,
}
} else if len(invalidListeners) > 0 {
gatewayConditions[string(k8s.GatewayConditionReady)].error = &ConfigError{
Reason: string(k8s.GatewayReasonListenersNotValid),
Message: fmt.Sprintf("Invalid listeners: %v", invalidListeners),
}
} else {
gatewayConditions[string(k8s.GatewayConditionReady)].message = fmt.Sprintf("Gateway valid, assigned to service(s) %s", humanReadableJoin(internal))
}
obj.Status.(*kstatus.WrappedStatus).Mutate(func(s config.Status) config.Status {
gs := s.(*k8s.GatewayStatus)
addressesToReport := external
addrType := k8s.IPAddressType
if len(addressesToReport) == 0 {
// There are no external addresses, so report the internal ones
// TODO: should we always report both?
addressesToReport = internal
addrType = k8s.HostnameAddressType
}
gs.Addresses = make([]k8s.GatewayAddress, 0, len(addressesToReport))
for _, addr := range addressesToReport {
gs.Addresses = append(gs.Addresses, k8s.GatewayAddress{
Type: &addrType,
Value: addr,
})
}
return gs
})
reportGatewayCondition(obj, gatewayConditions)
}
// Insert a parent for Mesh references.
gwMap[parentKey{
Kind: meshGVK,
Name: "istio",
}] = map[k8s.SectionName]*parentInfo{
"": {
InternalName: "mesh",
},
}
return result, gwMap, namespaceLabelReferences
}