sigs.k8s.io/prow@v0.0.0-20240503223140-c5e374dc7eb1/cmd/pipeline/main.go (about)

     1  /*
     2  Copyright 2019 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  	"context"
    21  	"flag"
    22  	"fmt"
    23  	"os"
    24  	"time"
    25  
    26  	"github.com/sirupsen/logrus"
    27  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	"k8s.io/client-go/kubernetes"
    30  	"k8s.io/client-go/rest"
    31  	"sigs.k8s.io/prow/pkg/pjutil/pprof"
    32  
    33  	_ "k8s.io/client-go/plugin/pkg/client/auth/gcp" // support gcp users in .kube/config
    34  	prowjobset "sigs.k8s.io/prow/pkg/client/clientset/versioned"
    35  	prowjobinfo "sigs.k8s.io/prow/pkg/client/informers/externalversions"
    36  	prowflagutil "sigs.k8s.io/prow/pkg/flagutil"
    37  	configflagutil "sigs.k8s.io/prow/pkg/flagutil/config"
    38  	"sigs.k8s.io/prow/pkg/interrupts"
    39  	"sigs.k8s.io/prow/pkg/kube"
    40  	"sigs.k8s.io/prow/pkg/logrusutil"
    41  	pipelineset "sigs.k8s.io/prow/pkg/pipeline/clientset/versioned"
    42  	pipelineinfo "sigs.k8s.io/prow/pkg/pipeline/informers/externalversions"
    43  	pipelineinfov1beta1 "sigs.k8s.io/prow/pkg/pipeline/informers/externalversions/pipeline/v1beta1"
    44  )
    45  
    46  type options struct {
    47  	allContexts            bool
    48  	config                 configflagutil.ConfigOptions
    49  	kubernetes             prowflagutil.KubernetesOptions
    50  	totURL                 string
    51  	instrumentationOptions prowflagutil.InstrumentationOptions
    52  }
    53  
    54  func parseOptions() options {
    55  	var o options
    56  	if err := o.parse(flag.CommandLine, os.Args[1:]); err != nil {
    57  		logrus.Fatalf("Invalid flags: %v", err)
    58  	}
    59  	return o
    60  }
    61  
    62  func (o *options) parse(flags *flag.FlagSet, args []string) error {
    63  	o.config.ConfigPathFlagName = "config"
    64  	flags.BoolVar(&o.allContexts, "all-contexts", false, "Monitor all cluster contexts, not just default")
    65  	flags.StringVar(&o.totURL, "tot-url", "", "Tot URL")
    66  	o.kubernetes.AddFlags(flags)
    67  	o.instrumentationOptions.AddFlags(flags)
    68  	o.config.AddFlags(flags)
    69  	if err := flags.Parse(args); err != nil {
    70  		return fmt.Errorf("Parse flags: %w", err)
    71  	}
    72  	if err := o.config.Validate(false); err != nil {
    73  		return err
    74  	}
    75  	return nil
    76  }
    77  
    78  type pipelineConfig struct {
    79  	client   pipelineset.Interface
    80  	informer pipelineinfov1beta1.PipelineRunInformer
    81  }
    82  
    83  // newPipelineConfig returns a client and informer capable of mutating and monitoring the specified config.
    84  func newPipelineConfig(cfg rest.Config, stop <-chan struct{}) (*pipelineConfig, error) {
    85  	bc, err := pipelineset.NewForConfig(&cfg)
    86  	if err != nil {
    87  		return nil, err
    88  	}
    89  
    90  	// Ensure the pipeline CRD is deployed
    91  	// TODO(fejta): probably a better way to do this
    92  	if _, err := bc.TektonV1beta1().PipelineRuns("").List(context.TODO(), metav1.ListOptions{Limit: 1}); err != nil {
    93  		return nil, err
    94  	}
    95  
    96  	// Assume watches receive updates, but resync every 30m in case something wonky happens
    97  	bif := pipelineinfo.NewSharedInformerFactory(bc, 30*time.Minute)
    98  	bif.Tekton().V1beta1().PipelineRuns().Lister()
    99  	go bif.Start(stop)
   100  	return &pipelineConfig{
   101  		client:   bc,
   102  		informer: bif.Tekton().V1beta1().PipelineRuns(),
   103  	}, nil
   104  }
   105  
   106  func main() {
   107  	logrusutil.ComponentInit()
   108  
   109  	o := parseOptions()
   110  
   111  	defer interrupts.WaitForGracefulShutdown()
   112  
   113  	pprof.Instrument(o.instrumentationOptions)
   114  
   115  	configAgent, err := o.config.ConfigAgent()
   116  	if err != nil {
   117  		logrus.WithError(err).Fatal("failed to load prow config")
   118  	}
   119  
   120  	configs, err := o.kubernetes.LoadClusterConfigs(func() {
   121  		logrus.Fatal("Kubeconfig changed, exiting to trigger a restart")
   122  	})
   123  	if err != nil {
   124  		logrus.WithError(err).Fatal("Error building client configs")
   125  	}
   126  
   127  	local := configs[kube.InClusterContext]
   128  	if !o.allContexts {
   129  		logrus.Warn("Truncating to default context")
   130  		configs = map[string]rest.Config{
   131  			kube.DefaultClusterAlias: configs[kube.DefaultClusterAlias],
   132  		}
   133  	} else {
   134  		// the InClusterContext is always mapped to DefaultClusterAlias in the controller, so there is no need to watch for this config.
   135  		delete(configs, kube.InClusterContext)
   136  	}
   137  
   138  	kc, err := kubernetes.NewForConfig(&local)
   139  	if err != nil {
   140  		logrus.WithError(err).Fatalf("Failed to create local kubernetes client")
   141  	}
   142  	pjc, err := prowjobset.NewForConfig(&local)
   143  	if err != nil {
   144  		logrus.WithError(err).Fatal("Failed to create prowjob client")
   145  	}
   146  	pjif := prowjobinfo.NewSharedInformerFactory(pjc, 30*time.Minute)
   147  	pjif.Prow().V1().ProwJobs().Lister()
   148  	go pjif.Start(interrupts.Context().Done())
   149  
   150  	pipelineConfigs := map[string]pipelineConfig{}
   151  	for context, cfg := range configs {
   152  		var bc *pipelineConfig
   153  		bc, err = newPipelineConfig(cfg, interrupts.Context().Done())
   154  		if apierrors.IsNotFound(err) {
   155  			logrus.WithError(err).Infof("Ignoring cluster context %s: tekton pipeline CRD not deployed", context)
   156  			continue
   157  		}
   158  		// Don't panic when a build cluster cannot be reached
   159  		if err != nil {
   160  			logrus.WithError(err).Warningf("Failed to create %s pipeline client", context)
   161  			continue
   162  		}
   163  		pipelineConfigs[context] = *bc
   164  	}
   165  
   166  	opts := controllerOptions{
   167  		kc:              kc,
   168  		pjc:             pjc,
   169  		pji:             pjif.Prow().V1().ProwJobs(),
   170  		pipelineConfigs: pipelineConfigs,
   171  		totURL:          o.totURL,
   172  		prowConfig:      configAgent.Config,
   173  		rl:              kube.RateLimiter(controllerName),
   174  	}
   175  	controller, err := newController(opts)
   176  	if err != nil {
   177  		logrus.WithError(err).Fatal("Error creating controller")
   178  	}
   179  
   180  	if err := controller.Run(2, interrupts.Context().Done()); err != nil {
   181  		logrus.WithError(err).Fatal("Error running controller")
   182  	}
   183  	logrus.Info("Finished")
   184  }