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

     1  /*
     2  Copyright 2017 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  	"flag"
    21  	"fmt"
    22  	"net/http"
    23  	"os"
    24  	"os/signal"
    25  	"syscall"
    26  	"time"
    27  
    28  	"github.com/prometheus/client_golang/prometheus/promhttp"
    29  	"github.com/sirupsen/logrus"
    30  	"k8s.io/apimachinery/pkg/labels"
    31  
    32  	"k8s.io/test-infra/pkg/flagutil"
    33  	"k8s.io/test-infra/prow/config"
    34  	"k8s.io/test-infra/prow/config/secret"
    35  	prowflagutil "k8s.io/test-infra/prow/flagutil"
    36  	"k8s.io/test-infra/prow/kube"
    37  	"k8s.io/test-infra/prow/logrusutil"
    38  	"k8s.io/test-infra/prow/metrics"
    39  	"k8s.io/test-infra/prow/plank"
    40  )
    41  
    42  type options struct {
    43  	totURL string
    44  
    45  	configPath    string
    46  	jobConfigPath string
    47  	buildCluster  string
    48  	selector      string
    49  	skipReport    bool
    50  
    51  	dryRun     bool
    52  	kubernetes prowflagutil.KubernetesOptions
    53  	github     prowflagutil.GitHubOptions
    54  }
    55  
    56  func gatherOptions() options {
    57  	o := options{}
    58  	fs := flag.NewFlagSet(os.Args[0], flag.ExitOnError)
    59  
    60  	fs.StringVar(&o.totURL, "tot-url", "", "Tot URL")
    61  
    62  	fs.StringVar(&o.configPath, "config-path", "/etc/config/config.yaml", "Path to config.yaml.")
    63  	fs.StringVar(&o.jobConfigPath, "job-config-path", "", "Path to prow job configs.")
    64  	fs.StringVar(&o.buildCluster, "build-cluster", "", "Path to file containing a YAML-marshalled kube.Cluster object. If empty, uses the local cluster.")
    65  	fs.StringVar(&o.selector, "label-selector", kube.EmptySelector, "Label selector to be applied in prowjobs. See https://kubernetes.io/docs/concepts/overview/working-with-objects/labels/#label-selectors for constructing a label selector.")
    66  	fs.BoolVar(&o.skipReport, "skip-report", false, "Whether or not to ignore report with githubClient")
    67  
    68  	fs.BoolVar(&o.dryRun, "dry-run", true, "Whether or not to make mutating API calls to GitHub.")
    69  	for _, group := range []flagutil.OptionGroup{&o.kubernetes, &o.github} {
    70  		group.AddFlags(fs)
    71  	}
    72  
    73  	fs.Parse(os.Args[1:])
    74  	return o
    75  }
    76  
    77  func (o *options) Validate() error {
    78  	for _, group := range []flagutil.OptionGroup{&o.kubernetes, &o.github} {
    79  		if err := group.Validate(o.dryRun); err != nil {
    80  			return err
    81  		}
    82  	}
    83  
    84  	if _, err := labels.Parse(o.selector); err != nil {
    85  		return fmt.Errorf("parse label selector: %v", err)
    86  	}
    87  
    88  	return nil
    89  }
    90  
    91  func main() {
    92  	o := gatherOptions()
    93  	if err := o.Validate(); err != nil {
    94  		logrus.WithError(err).Fatal("Invalid options")
    95  	}
    96  
    97  	logrus.SetFormatter(
    98  		logrusutil.NewDefaultFieldsFormatter(nil, logrus.Fields{"component": "plank"}),
    99  	)
   100  
   101  	configAgent := &config.Agent{}
   102  	if err := configAgent.Start(o.configPath, o.jobConfigPath); err != nil {
   103  		logrus.WithError(err).Fatal("Error starting config agent.")
   104  	}
   105  
   106  	secretAgent := &secret.Agent{}
   107  	if o.github.TokenPath != "" {
   108  		if err := secretAgent.Start([]string{o.github.TokenPath}); err != nil {
   109  			logrus.WithError(err).Fatal("Error starting secrets agent.")
   110  		}
   111  	}
   112  
   113  	githubClient, err := o.github.GitHubClient(secretAgent, o.dryRun)
   114  	if err != nil {
   115  		logrus.WithError(err).Fatal("Error getting GitHub client.")
   116  	}
   117  
   118  	kubeClient, err := o.kubernetes.Client(configAgent.Config().ProwJobNamespace, o.dryRun)
   119  	if err != nil {
   120  		logrus.WithError(err).Fatal("Error getting kube client.")
   121  	}
   122  
   123  	var pkcs map[string]*kube.Client
   124  	if o.dryRun {
   125  		pkcs = map[string]*kube.Client{kube.DefaultClusterAlias: kubeClient}
   126  	} else {
   127  		if o.buildCluster == "" {
   128  			pkc, err := kube.NewClientInCluster(configAgent.Config().PodNamespace)
   129  			if err != nil {
   130  				logrus.WithError(err).Fatal("Error getting kube client.")
   131  			}
   132  			pkcs = map[string]*kube.Client{kube.DefaultClusterAlias: pkc}
   133  		} else {
   134  			pkcs, err = kube.ClientMapFromFile(o.buildCluster, configAgent.Config().PodNamespace)
   135  			if err != nil {
   136  				logrus.WithError(err).Fatal("Error getting kube client to build cluster.")
   137  			}
   138  		}
   139  	}
   140  
   141  	c, err := plank.NewController(kubeClient, pkcs, githubClient, nil, configAgent, o.totURL, o.selector, o.skipReport)
   142  	if err != nil {
   143  		logrus.WithError(err).Fatal("Error creating plank controller.")
   144  	}
   145  
   146  	// Push metrics to the configured prometheus pushgateway endpoint.
   147  	pushGateway := configAgent.Config().PushGateway
   148  	if pushGateway.Endpoint != "" {
   149  		go metrics.PushMetrics("plank", pushGateway.Endpoint, pushGateway.Interval)
   150  	}
   151  	// serve prometheus metrics.
   152  	go serve()
   153  	// gather metrics for the jobs handled by plank.
   154  	go gather(c)
   155  
   156  	tick := time.Tick(30 * time.Second)
   157  	sig := make(chan os.Signal, 1)
   158  	signal.Notify(sig, os.Interrupt, syscall.SIGTERM)
   159  
   160  	for {
   161  		select {
   162  		case <-tick:
   163  			start := time.Now()
   164  			if err := c.Sync(); err != nil {
   165  				logrus.WithError(err).Error("Error syncing.")
   166  			}
   167  			logrus.WithField("duration", fmt.Sprintf("%v", time.Since(start))).Info("Synced")
   168  		case <-sig:
   169  			logrus.Info("Plank is shutting down...")
   170  			return
   171  		}
   172  	}
   173  }
   174  
   175  // serve starts a http server and serves prometheus metrics.
   176  // Meant to be called inside a goroutine.
   177  func serve() {
   178  	http.Handle("/metrics", promhttp.Handler())
   179  	logrus.WithError(http.ListenAndServe(":8080", nil)).Fatal("ListenAndServe returned.")
   180  }
   181  
   182  // gather metrics from plank.
   183  // Meant to be called inside a goroutine.
   184  func gather(c *plank.Controller) {
   185  	tick := time.Tick(30 * time.Second)
   186  	sig := make(chan os.Signal, 1)
   187  	signal.Notify(sig, os.Interrupt, syscall.SIGTERM)
   188  
   189  	for {
   190  		select {
   191  		case <-tick:
   192  			start := time.Now()
   193  			c.SyncMetrics()
   194  			logrus.WithField("metrics-duration", fmt.Sprintf("%v", time.Since(start))).Debug("Metrics synced")
   195  		case <-sig:
   196  			logrus.Debug("Plank gatherer is shutting down...")
   197  			return
   198  		}
   199  	}
   200  }