@@ -27,18 +27,21 @@ import (
2727 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2828 "k8s.io/apimachinery/pkg/labels"
2929 "k8s.io/apimachinery/pkg/runtime/schema"
30+ "k8s.io/apimachinery/pkg/util/wait"
3031 "k8s.io/apiserver/pkg/admission"
3132 "k8s.io/apiserver/pkg/admission/configuration"
3233 "k8s.io/apiserver/pkg/admission/plugin/webhook/generic"
3334 "k8s.io/apiserver/pkg/admission/plugin/webhook/validating"
3435 genericapirequest "k8s.io/apiserver/pkg/endpoints/request"
36+ "k8s.io/client-go/tools/cache"
3537
3638 kcpkubernetesinformers "github.com/kcp-dev/client-go/informers"
3739 kcpkubernetesclientset "github.com/kcp-dev/client-go/kubernetes"
3840 "github.com/kcp-dev/logicalcluster/v3"
3941
4042 kcpinitializers "github.com/kcp-dev/kcp/pkg/admission/initializers"
4143 apisv1alpha2 "github.com/kcp-dev/kcp/sdk/apis/apis/v1alpha2"
44+ corev1alpha1 "github.com/kcp-dev/kcp/sdk/apis/core/v1alpha1"
4245 kcpinformers "github.com/kcp-dev/kcp/sdk/client/informers/externalversions"
4346)
4447
@@ -54,11 +57,13 @@ type Plugin struct {
5457 kubeClusterClient kcpkubernetesclientset.ClusterInterface
5558 localKubeSharedInformerFactory kcpkubernetesinformers.SharedInformerFactory
5659 globalKubeSharedInformerFactory kcpkubernetesinformers.SharedInformerFactory
60+ serverStopChannel <- chan struct {}
5761
5862 getAPIBindings func (clusterName logicalcluster.Name ) ([]* apisv1alpha2.APIBinding , error )
5963
60- managerLock sync.Mutex
61- managersCache map [logicalcluster.Name ]generic.Source
64+ managerLock sync.Mutex
65+ managersCache map [logicalcluster.Name ]generic.Source
66+ managersCancel map [logicalcluster.Name ]context.CancelFunc
6267}
6368
6469var (
7176
7277func NewValidatingAdmissionWebhook (configFile io.Reader ) (* Plugin , error ) {
7378 p := & Plugin {
74- managerLock : sync.Mutex {},
75- managersCache : make (map [logicalcluster.Name ]generic.Source ),
76- Handler : admission .NewHandler (admission .Connect , admission .Create , admission .Delete , admission .Update ),
79+ managerLock : sync.Mutex {},
80+ managersCache : make (map [logicalcluster.Name ]generic.Source ),
81+ managersCancel : make (map [logicalcluster.Name ]context.CancelFunc ),
82+ Handler : admission .NewHandler (admission .Connect , admission .Create , admission .Delete , admission .Update ),
7783 }
7884 if configFile != nil {
7985 config , err := io .ReadAll (configFile )
@@ -145,10 +151,13 @@ func (p *Plugin) getHookSource(clusterName logicalcluster.Name, groupResource sc
145151
146152 p .managerLock .Lock ()
147153 defer p .managerLock .Unlock ()
154+
148155 if _ , ok := p .managersCache [clusterNameForGroupResource ]; ! ok {
156+ ctx , cancel := context .WithCancel (wait .ContextForChannel (p .serverStopChannel ))
149157 p .managersCache [clusterNameForGroupResource ] = configuration .NewValidatingWebhookConfigurationManagerForInformer (
150- p .globalKubeSharedInformerFactory .Admissionregistration ().V1 ().ValidatingWebhookConfigurations ().Cluster ( clusterNameForGroupResource ),
158+ p .globalKubeSharedInformerFactory .Admissionregistration ().V1 ().ValidatingWebhookConfigurations ().ClusterWithContext ( ctx , clusterNameForGroupResource ),
151159 )
160+ p .managersCancel [clusterNameForGroupResource ] = cancel
152161 }
153162
154163 return p .managersCache [clusterNameForGroupResource ], nil
@@ -183,6 +192,9 @@ func (p *Plugin) ValidateInitialization() error {
183192 if p .globalKubeSharedInformerFactory == nil {
184193 return errors .New ("missing globalKubeSharedInformerFactory" )
185194 }
195+ if p .serverStopChannel == nil {
196+ return errors .New ("missing serverStopChannel" )
197+ }
186198 return nil
187199}
188200
@@ -199,6 +211,33 @@ func (p *Plugin) SetKcpInformers(local, global kcpinformers.SharedInformerFactor
199211 p .getAPIBindings = func (clusterName logicalcluster.Name ) ([]* apisv1alpha2.APIBinding , error ) {
200212 return local .Apis ().V1alpha2 ().APIBindings ().Lister ().Cluster (clusterName ).List (labels .Everything ())
201213 }
214+
215+ // handler doesn't need to be deregistered - the webhook is valid
216+ // for as long as kcp runs and when kcp stops the informer is
217+ // stopped and the handler gets cleaned up.
218+ _ , _ = local .Core ().V1alpha1 ().LogicalClusters ().Informer ().AddEventHandler (
219+ cache.ResourceEventHandlerFuncs {
220+ DeleteFunc : func (obj interface {}) {
221+ cl , ok := obj .(* corev1alpha1.LogicalCluster )
222+ if ! ok {
223+ return
224+ }
225+
226+ clName := logicalcluster .Name (cl .Annotations [logicalcluster .AnnotationKey ])
227+
228+ p .managerLock .Lock ()
229+ defer p .managerLock .Unlock ()
230+
231+ cancel , ok := p .managersCancel [clName ]
232+ if ! ok {
233+ return
234+ }
235+ delete (p .managersCancel , clName )
236+ delete (p .managersCache , clName )
237+ cancel ()
238+ },
239+ },
240+ )
202241}
203242
204243// SetClusterAnnotation sets the cluster annotation on the given object to the given clusterName,
@@ -225,3 +264,7 @@ func SetClusterAnnotation(obj metav1.Object, clusterName logicalcluster.Name) fu
225264 obj .SetAnnotations (anns )
226265 return undoFn
227266}
267+
268+ func (p * Plugin ) SetServerShutdownChannel (ch <- chan struct {}) {
269+ p .serverStopChannel = ch
270+ }
0 commit comments