github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/prow/cmd/sub/main.go (about)

     1  /*
     2  Copyright 2018 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  	"net/http"
    23  	"os"
    24  	"os/signal"
    25  	"strconv"
    26  	"sync"
    27  	"syscall"
    28  	"time"
    29  
    30  	"github.com/prometheus/client_golang/prometheus/promhttp"
    31  	"github.com/sirupsen/logrus"
    32  	"golang.org/x/sync/errgroup"
    33  
    34  	"k8s.io/test-infra/prow/client/clientset/versioned"
    35  	"k8s.io/test-infra/prow/config"
    36  	"k8s.io/test-infra/prow/config/secret"
    37  	"k8s.io/test-infra/prow/flagutil"
    38  	"k8s.io/test-infra/prow/kube"
    39  	"k8s.io/test-infra/prow/logrusutil"
    40  	"k8s.io/test-infra/prow/metrics"
    41  	"k8s.io/test-infra/prow/pubsub/subscriber"
    42  )
    43  
    44  var (
    45  	flagOptions *options
    46  )
    47  
    48  type options struct {
    49  	client         flagutil.KubernetesClientOptions
    50  	port           int
    51  	pushSecretFile string
    52  
    53  	configPath    string
    54  	jobConfigPath string
    55  	pluginConfig  string
    56  
    57  	dryRun      bool
    58  	gracePeriod time.Duration
    59  }
    60  
    61  type kubeClient struct {
    62  	client    versioned.Interface
    63  	namespace string
    64  	dryRun    bool
    65  }
    66  
    67  func (c *kubeClient) CreateProwJob(job *kube.ProwJob) (*kube.ProwJob, error) {
    68  	if c.dryRun {
    69  		return job, nil
    70  	}
    71  	return c.client.ProwV1().ProwJobs(c.namespace).Create(job)
    72  }
    73  
    74  func init() {
    75  	flagOptions = &options{}
    76  	fs := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
    77  
    78  	fs.IntVar(&flagOptions.port, "port", 80, "HTTP Port.")
    79  	fs.StringVar(&flagOptions.pushSecretFile, "push-secret-file", "", "Path to Pub/Sub Push secret file.")
    80  
    81  	fs.StringVar(&flagOptions.configPath, "config-path", "/etc/config/config.yaml", "Path to config.yaml.")
    82  	fs.StringVar(&flagOptions.jobConfigPath, "job-config-path", "", "Path to prow job configs.")
    83  
    84  	fs.BoolVar(&flagOptions.dryRun, "dry-run", true, "Dry run for testing. Uses API tokens but does not mutate.")
    85  	fs.DurationVar(&flagOptions.gracePeriod, "grace-period", 180*time.Second, "On shutdown, try to handle remaining events for the specified duration. ")
    86  
    87  	flagOptions.client.AddFlags(fs)
    88  
    89  	fs.Parse(os.Args[1:])
    90  }
    91  
    92  func main() {
    93  
    94  	logrus.SetFormatter(logrusutil.NewDefaultFieldsFormatter(nil, logrus.Fields{"component": "pubsub-subscriber"}))
    95  
    96  	configAgent := &config.Agent{}
    97  	if err := configAgent.Start(flagOptions.configPath, flagOptions.jobConfigPath); err != nil {
    98  		logrus.WithError(err).Fatal("Error starting config agent.")
    99  	}
   100  
   101  	var tokenGenerator func() []byte
   102  	if flagOptions.pushSecretFile != "" {
   103  		var tokens []string
   104  		tokens = append(tokens, flagOptions.pushSecretFile)
   105  
   106  		secretAgent := &secret.Agent{}
   107  		if err := secretAgent.Start(tokens); err != nil {
   108  			logrus.WithError(err).Fatal("Error starting secrets agent.")
   109  		}
   110  		tokenGenerator = secretAgent.GetTokenGenerator(flagOptions.pushSecretFile)
   111  	}
   112  
   113  	prowjobClient, err := flagOptions.client.ProwJobClient()
   114  	if err != nil {
   115  		logrus.WithError(err).Fatal("unable to create prow job client")
   116  	}
   117  	kubeClient := &kubeClient{
   118  		client:    prowjobClient,
   119  		namespace: configAgent.Config().ProwJobNamespace,
   120  		dryRun:    flagOptions.dryRun,
   121  	}
   122  
   123  	promMetrics := subscriber.NewMetrics()
   124  
   125  	// Push metrics to the configured prometheus pushgateway endpoint.
   126  	pushGateway := configAgent.Config().PushGateway
   127  	if pushGateway.Endpoint != "" {
   128  		go metrics.PushMetrics("sub", pushGateway.Endpoint, pushGateway.Interval)
   129  	}
   130  
   131  	s := &subscriber.Subscriber{
   132  		ConfigAgent: configAgent,
   133  		Metrics:     promMetrics,
   134  		KubeClient:  kubeClient,
   135  	}
   136  
   137  	// Return 200 on / for health checks.
   138  	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {})
   139  	http.Handle("/metrics", promhttp.Handler())
   140  
   141  	// Will call shutdown which will stop the errGroup
   142  	shutdownCtx, shutdown := context.WithCancel(context.Background())
   143  	errGroup, derivedCtx := errgroup.WithContext(shutdownCtx)
   144  	wg := sync.WaitGroup{}
   145  
   146  	// Setting up Push Server
   147  	logrus.Info("Setting up Push Server")
   148  	pushServer := &subscriber.PushServer{
   149  		Subscriber:     s,
   150  		TokenGenerator: tokenGenerator,
   151  	}
   152  	http.Handle("/push", pushServer)
   153  
   154  	// Setting up Pull Server
   155  	logrus.Info("Setting up Pull Server")
   156  	pullServer := subscriber.NewPullServer(s)
   157  	errGroup.Go(func() error {
   158  		wg.Add(1)
   159  		defer wg.Done()
   160  		logrus.Info("Starting Pull Server")
   161  		err := pullServer.Run(derivedCtx)
   162  		logrus.WithError(err).Warn("Pull Server exited.")
   163  		return err
   164  	})
   165  
   166  	httpServer := &http.Server{Addr: ":" + strconv.Itoa(flagOptions.port)}
   167  	errGroup.Go(func() error {
   168  		wg.Add(1)
   169  		defer wg.Done()
   170  		logrus.Info("Starting HTTP Server")
   171  		err := httpServer.ListenAndServe()
   172  		logrus.WithError(err).Warn("HTTP Server exited.")
   173  		return err
   174  	})
   175  
   176  	sig := make(chan os.Signal, 1)
   177  	signal.Notify(sig, os.Interrupt, syscall.SIGTERM, syscall.SIGABRT)
   178  
   179  	select {
   180  	case <-shutdownCtx.Done():
   181  		err = shutdownCtx.Err()
   182  		break
   183  	case <-derivedCtx.Done():
   184  		err = derivedCtx.Err()
   185  		break
   186  	case <-sig:
   187  		break
   188  	}
   189  
   190  	logrus.WithError(err).Warn("Starting Shutdown")
   191  	shutdown()
   192  	// Shutdown gracefully on SIGTERM or SIGINT
   193  	timeoutCtx, cancel := context.WithTimeout(context.Background(), flagOptions.gracePeriod)
   194  	defer cancel()
   195  	httpServer.Shutdown(timeoutCtx)
   196  	errGroup.Wait()
   197  	wg.Wait()
   198  }