istio.io/istio@v0.0.0-20240520182934-d79c90f27776/pilot/pkg/config/kube/gateway/gatewayclass.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 gateway 16 17 import ( 18 "github.com/hashicorp/go-multierror" 19 kerrors "k8s.io/apimachinery/pkg/api/errors" 20 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 21 "k8s.io/apimachinery/pkg/types" 22 k8sv1 "sigs.k8s.io/gateway-api/apis/v1" 23 gateway "sigs.k8s.io/gateway-api/apis/v1beta1" 24 25 "istio.io/istio/pilot/pkg/model/kstatus" 26 "istio.io/istio/pkg/kube" 27 "istio.io/istio/pkg/kube/controllers" 28 "istio.io/istio/pkg/kube/kclient" 29 "istio.io/istio/pkg/util/istiomultierror" 30 ) 31 32 // ClassController is a controller that creates the default Istio GatewayClass(s). This will not 33 // continually reconcile the full state of the GatewayClass object, and instead only create the class 34 // if it doesn't exist. This allows users to manage it through other means or modify it as they wish. 35 // If it is deleted, however, it will be added back. 36 // This controller intentionally does not do leader election for simplicity. Because we only create 37 // and not update there is no need; the first controller to create the GatewayClass wins. 38 type ClassController struct { 39 queue controllers.Queue 40 classes kclient.Client[*gateway.GatewayClass] 41 } 42 43 func NewClassController(kc kube.Client) *ClassController { 44 gc := &ClassController{} 45 gc.queue = controllers.NewQueue("gateway class", 46 controllers.WithReconciler(gc.Reconcile), 47 controllers.WithMaxAttempts(25)) 48 49 gc.classes = kclient.New[*gateway.GatewayClass](kc) 50 gc.classes.AddEventHandler(controllers.FilteredObjectHandler(gc.queue.AddObject, func(o controllers.Object) bool { 51 _, f := builtinClasses[gateway.ObjectName(o.GetName())] 52 return f 53 })) 54 return gc 55 } 56 57 func (c *ClassController) Run(stop <-chan struct{}) { 58 // Ensure we initially reconcile the current state 59 c.queue.Add(types.NamespacedName{}) 60 c.queue.Run(stop) 61 } 62 63 func (c *ClassController) Reconcile(types.NamespacedName) error { 64 err := istiomultierror.New() 65 for class := range builtinClasses { 66 err = multierror.Append(err, c.reconcileClass(class)) 67 } 68 return err.ErrorOrNil() 69 } 70 71 func (c *ClassController) reconcileClass(class gateway.ObjectName) error { 72 if c.classes.Get(string(class), "") != nil { 73 log.Debugf("GatewayClass/%v already exists, no action", class) 74 return nil 75 } 76 controller := builtinClasses[class] 77 classInfo, f := classInfos[controller] 78 if !f { 79 // Should only happen when ambient is disabled; otherwise builtinClasses and classInfos should be consistent 80 return nil 81 } 82 gc := &gateway.GatewayClass{ 83 ObjectMeta: metav1.ObjectMeta{ 84 Name: string(class), 85 }, 86 Spec: gateway.GatewayClassSpec{ 87 ControllerName: gateway.GatewayController(classInfo.controller), 88 Description: &classInfo.description, 89 }, 90 } 91 _, err := c.classes.Create(gc) 92 if err != nil && !kerrors.IsConflict(err) { 93 return err 94 } else if err != nil && kerrors.IsConflict(err) { 95 // This is not really an error, just a race condition 96 log.Infof("Attempted to create GatewayClass/%v, but it was already created", class) 97 } 98 if err != nil { 99 return err 100 } 101 102 return nil 103 } 104 105 func GetClassStatus(existing *k8sv1.GatewayClassStatus, gen int64) k8sv1.GatewayClassStatus { 106 if existing == nil { 107 existing = &k8sv1.GatewayClassStatus{} 108 } 109 existing.Conditions = kstatus.UpdateConditionIfChanged(existing.Conditions, metav1.Condition{ 110 Type: string(k8sv1.GatewayClassConditionStatusAccepted), 111 Status: kstatus.StatusTrue, 112 ObservedGeneration: gen, 113 LastTransitionTime: metav1.Now(), 114 Reason: string(k8sv1.GatewayClassConditionStatusAccepted), 115 Message: "Handled by Istio controller", 116 }) 117 return *existing 118 }