sigs.k8s.io/prow@v0.0.0-20240503223140-c5e374dc7eb1/pkg/moonraker/moonraker.go (about)

     1  /*
     2  Copyright 2023 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 moonraker
    18  
    19  import (
    20  	"context"
    21  	"encoding/json"
    22  	"fmt"
    23  	"io"
    24  	"net/http"
    25  	"reflect"
    26  
    27  	"github.com/sirupsen/logrus"
    28  
    29  	prowapi "sigs.k8s.io/prow/pkg/apis/prowjobs/v1"
    30  	"sigs.k8s.io/prow/pkg/config"
    31  )
    32  
    33  const (
    34  	PathGetInrepoconfig = "inrepoconfig"
    35  	PathPing            = "ping"
    36  )
    37  
    38  type Moonraker struct {
    39  	ConfigAgent       *config.Agent
    40  	InRepoConfigCache *config.InRepoConfigCache
    41  }
    42  
    43  type configSectionsToWatch struct {
    44  	config.InRepoConfig // Map of allowlisted inrepoconfig URLs to clone from
    45  }
    46  
    47  // payload is the message payload we use for Moonraker. For
    48  // forward-compatibility, we don't use a prowapi.Refs directly.
    49  type payload struct {
    50  	Refs prowapi.Refs `json:"refs"`
    51  }
    52  
    53  type ProwYAMLGetter interface {
    54  	GetProwYAML(payload *payload) (*config.ProwYAML, error)
    55  }
    56  
    57  // ServePing responds with "pong". It's meant to be used by clients to check if
    58  // the service is up.
    59  func (mr *Moonraker) ServePing(w http.ResponseWriter, r *http.Request) {
    60  	fmt.Fprintf(w, "pong")
    61  }
    62  
    63  // serveGetInrepoconfig returns a ProwYAML object marshaled into JSON.
    64  func (mr *Moonraker) ServeGetInrepoconfig(w http.ResponseWriter, r *http.Request) {
    65  	body, err := io.ReadAll(r.Body)
    66  	if err != nil {
    67  		logrus.WithError(err).Info("unable to read request")
    68  		http.Error(w, fmt.Sprintf("bad request %v", err), http.StatusBadRequest)
    69  		return
    70  	}
    71  
    72  	payload := &payload{}
    73  	err = json.Unmarshal(body, payload)
    74  	if err != nil {
    75  		logrus.WithError(err).Info("unable to unmarshal getInrepoconfig request")
    76  		http.Error(w, fmt.Sprintf("unable to unmarshal getInrepoconfig request: %v", err), http.StatusBadRequest)
    77  		return
    78  	}
    79  
    80  	baseSHAGetter := func() (string, error) {
    81  		return payload.Refs.BaseSHA, nil
    82  	}
    83  	var headSHAGetters []func() (string, error)
    84  	for _, pull := range payload.Refs.Pulls {
    85  		pull := pull
    86  		headSHAGetters = append(headSHAGetters, func() (string, error) {
    87  			return pull.SHA, nil
    88  		})
    89  	}
    90  	identifier := payload.Refs.Org + "/" + payload.Refs.Repo
    91  
    92  	prowYAML, err := mr.InRepoConfigCache.GetProwYAMLWithoutDefaults(identifier, payload.Refs.BaseRef, baseSHAGetter, headSHAGetters...)
    93  	if err != nil {
    94  		logrus.WithError(err).Error("unable to retrieve inrepoconfig ProwYAML")
    95  		http.Error(w, fmt.Sprintf("unable to retrieve inrepoconfig ProwYAML: %v", err), http.StatusBadRequest)
    96  		return
    97  	}
    98  
    99  	w.Header().Set("Content-Type", "application/json")
   100  	if err := json.NewEncoder(w).Encode(prowYAML); err != nil {
   101  		logrus.WithError(err).Error("unable to encode inrepoconfig ProwYAML into JSON")
   102  		http.Error(w, fmt.Sprintf("unable to encode inrepoconfig ProwYAML into JSON: %v", err), http.StatusBadRequest)
   103  		return
   104  	}
   105  }
   106  
   107  func (mr *Moonraker) RunConfigWatcher(ctx context.Context) error {
   108  	configEvent := make(chan config.Delta, 2)
   109  	mr.ConfigAgent.Subscribe(configEvent)
   110  
   111  	var err error
   112  	defer func() {
   113  		if err != nil {
   114  			logrus.WithError(ctx.Err()).Error("ConfigWatcher shutting down.")
   115  		}
   116  		logrus.Debug("Pull server shutting down.")
   117  	}()
   118  	currentConfig := configSectionsToWatch{
   119  		mr.ConfigAgent.Config().InRepoConfig,
   120  	}
   121  
   122  	for {
   123  		select {
   124  		// Parent context. Shutdown
   125  		case <-ctx.Done():
   126  			return nil
   127  		// Checking for update config
   128  		case event := <-configEvent:
   129  			newConfig := configSectionsToWatch{
   130  				event.After.InRepoConfig,
   131  			}
   132  			logrus.Info("Received new config")
   133  			if !reflect.DeepEqual(currentConfig, newConfig) {
   134  				logrus.Info("New config found, resetting Config in ConfigAgent")
   135  				mr.ConfigAgent.SetWithoutBroadcast(&event.After)
   136  				currentConfig = newConfig
   137  			}
   138  		}
   139  	}
   140  }