@@ -27,7 +27,6 @@ import (
2727 "fmt"
2828 "io"
2929 "math/big"
30- "net"
3130 "net/http"
3231 "os"
3332 "path/filepath"
@@ -38,7 +37,6 @@ import (
3837 "github.com/fsnotify/fsnotify"
3938 "go.opentelemetry.io/otel"
4039
41- porchapi "github.com/nephio-project/porch/api/porch/v1alpha1"
4240 configapi "github.com/nephio-project/porch/api/porchconfig/v1alpha1"
4341 "github.com/nephio-project/porch/pkg/util"
4442 admissionv1 "k8s.io/api/admission/v1"
@@ -57,7 +55,6 @@ type WebhookType string
5755const (
5856 WebhookTypeService WebhookType = "service"
5957 WebhookTypeUrl WebhookType = "url"
60- serverEndpoint = "/validate-deletion"
6158 repositoryValidationEndpoint = "/validate-repository"
6259)
6360
@@ -66,15 +63,14 @@ var (
6663 certModTime time.Time
6764)
6865
69- var tracer = otel .Tracer ("deletion -webhook" )
66+ var tracer = otel .Tracer ("repository -webhook" )
7067
71- // WebhookConfig defines the configuration for the PackageRevision deletion webhook
68+ // WebhookConfig defines the configuration for the Repository validation webhook
7269type WebhookConfig struct {
7370 Type WebhookType
7471 ServiceName string // only used if Type == WebhookTypeService
7572 ServiceNamespace string // only used if Type == WebhookTypeService
7673 Host string // only used if Type == WebhookTypeUrl
77- Path string
7874 Port int32
7975 RepositoryPath string
8076 RepoServiceName string
@@ -102,7 +98,6 @@ func newWebhookConfig(ctx context.Context) *WebhookConfig {
10298 cfg .Type = WebhookTypeUrl
10399 cfg .Host = getEnv ("WEBHOOK_HOST" , "localhost" )
104100 }
105- cfg .Path = serverEndpoint
106101 // Always use the WebhookTypeService for repository webhook validation
107102 cfg .RepositoryPath = repositoryValidationEndpoint
108103 cfg .RepoServiceName , cfg .RepoServiceNamespace = webhookServiceName (ctx )
@@ -168,7 +163,7 @@ func webhookServiceName(ctx context.Context) (serviceName, serviceNamespace stri
168163func setupWebhooks (ctx context.Context , clientReader client.Reader ) error {
169164 cfg := newWebhookConfig (ctx )
170165 // TODO: Refactor webhook setup to support optional webhooks and better separation of concerns.
171- // Currently webhooks are always enabled and required for Repository/PackageRevision validation.
166+ // Currently webhooks are always enabled and required for Repository validation.
172167 // Consider: 1) Making webhooks optional via explicit flag, 2) Separating cert management from webhook lifecycle,
173168 // 3) Supporting webhook-less mode for development/testing.
174169 if ! cfg .CertManWebhook {
@@ -310,39 +305,11 @@ func createValidatingWebhook(ctx context.Context, cfg *WebhookConfig, caCert []b
310305 // Set max timeout value for ValidatingWebhooks
311306 cfg .timeout = 30
312307 var (
313- validationCfgName = "packagerev-deletion-validating-webhook"
314308 repositoryCfgName = "repository-validating-webhook"
315309 fail = admissionregistrationv1 .Fail
316310 none = admissionregistrationv1 .SideEffectClassNone
317311 )
318312
319- // Webhook for PackageRevision deletion
320- validateConfig := & admissionregistrationv1.ValidatingWebhookConfiguration {
321- ObjectMeta : metav1.ObjectMeta {
322- Name : validationCfgName ,
323- },
324- Webhooks : []admissionregistrationv1.ValidatingWebhook {{
325- Name : "packagerevdeletion.google.com" ,
326- ClientConfig : admissionregistrationv1.WebhookClientConfig {
327- CABundle : caCert ,
328- },
329- Rules : []admissionregistrationv1.RuleWithOperations {{
330- Operations : []admissionregistrationv1.OperationType {
331- admissionregistrationv1 .Delete ,
332- },
333- Rule : admissionregistrationv1.Rule {
334- APIGroups : []string {porchapi .SchemeGroupVersion .Group },
335- APIVersions : []string {porchapi .SchemeGroupVersion .Version },
336- Resources : []string {porchapi .PackageRevisionGVR .Resource },
337- },
338- }},
339- AdmissionReviewVersions : []string {"v1" },
340- SideEffects : & none ,
341- FailurePolicy : & fail ,
342- TimeoutSeconds : & cfg .timeout ,
343- }},
344- }
345-
346313 // Webhook for Repository validation
347314 repositoryWebhook := admissionregistrationv1.ValidatingWebhookConfiguration {
348315 ObjectMeta : metav1.ObjectMeta {
@@ -371,37 +338,17 @@ func createValidatingWebhook(ctx context.Context, cfg *WebhookConfig, caCert []b
371338 }},
372339 }
373340
374- // Set service or URL for both webhooks
375- switch cfg .Type {
376- case WebhookTypeService :
377- validateConfig .Webhooks [0 ].ClientConfig .Service = & admissionregistrationv1.ServiceReference {
378- Name : cfg .ServiceName ,
379- Namespace : cfg .ServiceNamespace ,
380- Path : & cfg .Path ,
381- Port : & cfg .Port ,
382- }
383- case WebhookTypeUrl :
384- url := fmt .Sprintf ("https://%s%s" , net .JoinHostPort (cfg .Host , fmt .Sprintf ("%d" , cfg .Port )), cfg .Path )
385- validateConfig .Webhooks [0 ].ClientConfig .URL = & url
386- default :
387- return fmt .Errorf ("invalid webhook type: %s" , cfg .Type )
388- }
389-
341+ // Set service for repository webhook
390342 repositoryWebhook .Webhooks [0 ].ClientConfig .Service = & admissionregistrationv1.ServiceReference {
391343 Name : cfg .RepoServiceName ,
392344 Namespace : cfg .RepoServiceNamespace ,
393345 Path : & cfg .RepositoryPath ,
394346 Port : & cfg .Port ,
395347 }
396348
397- // Delete and recreate both webhook to allow updates in webhook configurations
398- _ = kubeClient .AdmissionregistrationV1 ().ValidatingWebhookConfigurations ().Delete (ctx , validationCfgName , metav1.DeleteOptions {})
349+ // Delete and recreate repository webhook to allow updates in webhook configurations
399350 _ = kubeClient .AdmissionregistrationV1 ().ValidatingWebhookConfigurations ().Delete (ctx , repositoryCfgName , metav1.DeleteOptions {})
400351
401- if _ , err := kubeClient .AdmissionregistrationV1 ().ValidatingWebhookConfigurations ().Create (ctx , validateConfig , metav1.CreateOptions {}); err != nil {
402- return fmt .Errorf ("failed to create package revision webhook: %w" , err )
403- }
404-
405352 if _ , err := kubeClient .AdmissionregistrationV1 ().ValidatingWebhookConfigurations ().Create (ctx , & repositoryWebhook , metav1.CreateOptions {}); err != nil {
406353 return fmt .Errorf ("failed to create repository validation webhook: %w" , err )
407354 }
@@ -489,9 +436,6 @@ func runWebhookServer(ctx context.Context, cfg *WebhookConfig, clientReader clie
489436 go watchCertificates (ctx , cfg .CertStorageDir , certFile , keyFile )
490437 }
491438 klog .Infoln ("Starting webhook server" )
492- http .HandleFunc (cfg .Path , func (w http.ResponseWriter , r * http.Request ) {
493- validateDeletion (w , r , clientReader )
494- })
495439 http .HandleFunc (cfg .RepositoryPath , func (w http.ResponseWriter , r * http.Request ) {
496440 validateRepository (w , r , clientReader )
497441 })
@@ -513,66 +457,6 @@ func runWebhookServer(ctx context.Context, cfg *WebhookConfig, clientReader clie
513457
514458}
515459
516- func validateDeletion (w http.ResponseWriter , r * http.Request , clientReader client.Reader ) {
517- ctx , span := tracer .Start (r .Context (), "validateDeletion" )
518- defer span .End ()
519- klog .Infoln ("received request to validate deletion" )
520-
521- admissionReviewRequest , err := decodeAdmissionReview (r )
522- if err != nil {
523- errMsg := fmt .Sprintf ("error getting admission review from request: %v" , err )
524- writeErr (errMsg , & w )
525- return
526- }
527-
528- // Verify that we have a PackageRevision resource
529- if admissionReviewRequest .Request .Resource != util .SchemaToMetaGVR (porchapi .PackageRevisionGVR ) {
530- errMsg := fmt .Sprintf ("did not receive PackageRevision, got %s" , admissionReviewRequest .Request .Resource .Resource )
531- writeErr (errMsg , & w )
532- return
533- }
534-
535- // Get the package revision using the name and namespace from the request.
536- pr := porchapi.PackageRevision {}
537- if err := clientReader .Get (ctx , client.ObjectKey {
538- Namespace : admissionReviewRequest .Request .Namespace ,
539- Name : admissionReviewRequest .Request .Name ,
540- }, & pr ); err != nil {
541- klog .Errorf ("could not get package revision: %s" , err .Error ())
542- }
543-
544- admissionResponse := & admissionv1.AdmissionResponse {}
545- if pr .Spec .Lifecycle == porchapi .PackageRevisionLifecyclePublished {
546- admissionResponse .Allowed = false
547- admissionResponse .Result = & metav1.Status {
548- Status : "Failure" ,
549- Message : fmt .Sprintf ("failed to delete package revision %q: published PackageRevisions must be proposed for deletion by setting spec.lifecycle to 'DeletionProposed' prior to deletion" , pr .Name ),
550- Reason : "Published PackageRevisions must be proposed for deletion by setting spec.lifecycle to 'DeletionProposed' prior to deletion." ,
551- }
552- } else {
553- admissionResponse .Allowed = true
554- admissionResponse .Result = & metav1.Status {
555- Status : "Success" ,
556- Message : fmt .Sprintf ("Successfully deleted package revision %q" , pr .Name ),
557- }
558- }
559-
560- resp , err := constructResponse (admissionResponse , admissionReviewRequest )
561- if err != nil {
562- errMsg := fmt .Sprintf ("error constructing response: %v" , err )
563- writeErr (errMsg , & w )
564- return
565- }
566-
567- w .Header ().Set ("Content-Type" , "application/json" )
568- _ , err = w .Write (resp ) // #nosec G705
569- if err != nil {
570- errMsg := fmt .Sprintf ("error writing response: %v" , err )
571- writeErr (errMsg , & w )
572- return
573- }
574- }
575-
576460func decodeAdmissionReview (r * http.Request ) (* admissionv1.AdmissionReview , error ) {
577461 if r .Header .Get ("Content-Type" ) != "application/json" {
578462 return nil , fmt .Errorf ("expected Content-Type 'application/json'" )
@@ -656,6 +540,9 @@ func getEnvInt32(key string, defaultValue int32) int32 {
656540}
657541
658542func validateRepository (w http.ResponseWriter , r * http.Request , clientReader client.Reader ) {
543+ ctx , span := tracer .Start (r .Context (), "validateRepository" )
544+ defer span .End ()
545+
659546 admissionReviewRequest , err := decodeAdmissionReview (r )
660547 if err != nil {
661548 writeErr (fmt .Sprintf ("error decoding admission review: %v" , err ), & w )
@@ -683,7 +570,7 @@ func validateRepository(w http.ResponseWriter, r *http.Request, clientReader cli
683570
684571 // Check for conflicts with existing repositories
685572 var repoList configapi.RepositoryList
686- if err := clientReader .List (context . Background () , & repoList ); err != nil {
573+ if err := clientReader .List (ctx , & repoList ); err != nil {
687574 klog .Errorf ("failed to list repositories: %v" , err )
688575 writeErr (fmt .Sprintf ("could not list repositories: %v" , err ), & w )
689576 return
0 commit comments