github.com/zppinho/prow@v0.0.0-20240510014325-1738badeb017/cmd/webhook-server/validation.go (about)

     1  /*
     2  Copyright 2022 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package main
    18  
    19  import (
    20  	"encoding/json"
    21  	"fmt"
    22  	"io"
    23  	"net/http"
    24  
    25  	"github.com/sirupsen/logrus"
    26  
    27  	"k8s.io/api/admission/v1beta1"
    28  	apiv1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	"k8s.io/apimachinery/pkg/types"
    30  	"k8s.io/apimachinery/pkg/util/sets"
    31  	"sigs.k8s.io/prow/pkg/apis/prowjobs/v1"
    32  	"sigs.k8s.io/prow/pkg/kube"
    33  	"sigs.k8s.io/prow/pkg/plank"
    34  )
    35  
    36  var agentsNotSupportingCluster = sets.New[string]("jenkins")
    37  
    38  const (
    39  	denied   = "DENIED"
    40  	accepted = "ACCEPTED"
    41  )
    42  
    43  func (wa *webhookAgent) serveValidate(w http.ResponseWriter, r *http.Request) {
    44  	body, err := io.ReadAll(r.Body)
    45  	if err != nil {
    46  		logrus.WithError(err).Info("unable to read request")
    47  		http.Error(w, fmt.Sprintf("bad request %v", err), http.StatusBadRequest)
    48  		return
    49  	}
    50  	admissionReview := &v1beta1.AdmissionReview{}
    51  	err = json.Unmarshal(body, admissionReview)
    52  	if err != nil {
    53  		logrus.WithError(err).Info("unable to unmarshal admission review request")
    54  		http.Error(w, fmt.Sprintf("unable to unmarshal admission review request %v", err), http.StatusBadRequest)
    55  		return
    56  	}
    57  	admissionRequest := admissionReview.Request
    58  	var prowJob v1.ProwJob
    59  	err = json.Unmarshal(admissionRequest.Object.Raw, &prowJob)
    60  	if err != nil {
    61  		logrus.WithError(err).Info("unable to prowjob from request")
    62  		http.Error(w, fmt.Sprintf("unable to unmarshal prowjob %v", err), http.StatusBadRequest)
    63  		return
    64  	}
    65  	var admissionResponse *v1beta1.AdmissionResponse
    66  	if admissionRequest.Operation == "CREATE" {
    67  		if err := validateProwJobClusterOnCreate(prowJob, wa.statuses); err != nil {
    68  			admissionResponse = createValidatingAdmissionResponse(admissionRequest.UID, err)
    69  		} else {
    70  			admissionResponse = createValidatingAdmissionResponse(admissionRequest.UID, nil)
    71  		}
    72  	}
    73  	admissionReview.Response = admissionResponse
    74  	resp, err := json.Marshal(admissionReview)
    75  	if err != nil {
    76  		logrus.WithError(err).Info("unable to marshal response")
    77  		http.Error(w, fmt.Sprintf("unable to unmarshal prowjob %v", err), http.StatusInternalServerError)
    78  		return
    79  	}
    80  	if _, err := w.Write(resp); err != nil {
    81  		logrus.WithError(err).Info("unable to write response")
    82  		http.Error(w, fmt.Sprintf("unable to write response: %v", err), http.StatusInternalServerError)
    83  		return
    84  	}
    85  }
    86  
    87  func validateProwJobClusterOnCreate(prowJob v1.ProwJob, statuses map[string]plank.ClusterStatus) error {
    88  	if prowJob.Spec.Cluster != "" && prowJob.Spec.Cluster != kube.DefaultClusterAlias && agentsNotSupportingCluster.Has(string(prowJob.Spec.Agent)) {
    89  		return fmt.Errorf("%s: cannot set cluster field if agent is %s", prowJob.Name, prowJob.Spec.Agent)
    90  	}
    91  	if prowJob.Spec.Agent == v1.KubernetesAgent {
    92  		_, ok := statuses[prowJob.ClusterAlias()]
    93  		if !ok {
    94  			return fmt.Errorf("job configuration for %q specifies unknown 'cluster' value %q", prowJob.Name, prowJob.ClusterAlias())
    95  		}
    96  	}
    97  	return nil
    98  }
    99  
   100  func createValidatingAdmissionResponse(uid types.UID, err error) *v1beta1.AdmissionResponse {
   101  	var ar *v1beta1.AdmissionResponse
   102  	var result *apiv1.Status
   103  	if err != nil {
   104  		result = &apiv1.Status{
   105  			Message: denied,
   106  			Reason:  apiv1.StatusReason(err.Error()),
   107  		}
   108  	} else {
   109  		result = &apiv1.Status{
   110  			Message: accepted,
   111  		}
   112  	}
   113  	ar = &v1beta1.AdmissionResponse{
   114  		UID:     uid,
   115  		Allowed: err == nil,
   116  		Result:  result,
   117  	}
   118  	return ar
   119  }