github.com/abayer/test-infra@v0.0.5/prow/cmd/tot/fallbackcheck/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  // fallbackcheck reports whether jobs in the provided prow
    18  // deployment have fallback build numbers in GCS.
    19  package main
    20  
    21  import (
    22  	"errors"
    23  	"flag"
    24  	"fmt"
    25  	"io/ioutil"
    26  	"net/http"
    27  	"os"
    28  	"strings"
    29  
    30  	"github.com/ghodss/yaml"
    31  	"github.com/sirupsen/logrus"
    32  
    33  	"k8s.io/test-infra/prow/config"
    34  	"k8s.io/test-infra/prow/pjutil"
    35  	"k8s.io/test-infra/prow/pod-utils/downwardapi"
    36  	"k8s.io/test-infra/prow/pod-utils/gcs"
    37  )
    38  
    39  type options struct {
    40  	prowURL string
    41  	bucket  string
    42  }
    43  
    44  func gatherOptions() options {
    45  	o := options{}
    46  
    47  	// TODO: Support reading from a local file.
    48  	flag.StringVar(&o.prowURL, "prow-url", "", "Prow frontend URL.")
    49  	flag.StringVar(&o.bucket, "bucket", "", "Top-level GCS bucket where job artifacts are pushed.")
    50  
    51  	flag.Parse()
    52  	return o
    53  }
    54  
    55  func (o *options) Validate() error {
    56  	if o.prowURL == "" {
    57  		return errors.New("you need to provide a URL to a live prow deployment")
    58  	}
    59  	if o.bucket == "" {
    60  		return errors.New("you need to provide the GCS bucket where all job artifacts are pushed")
    61  	}
    62  	return nil
    63  }
    64  
    65  func main() {
    66  	o := gatherOptions()
    67  	if err := o.Validate(); err != nil {
    68  		logrus.Fatalf("Invalid options: %v", err)
    69  	}
    70  
    71  	// TODO: Retries
    72  	resp, err := http.Get(o.prowURL + "/config")
    73  	if err != nil {
    74  		logrus.Fatalf("cannot get prow config: %v", err)
    75  	}
    76  	defer resp.Body.Close()
    77  	if resp.StatusCode < 200 || resp.StatusCode > 299 {
    78  		logrus.Fatalf("status code not 2XX: %v", resp.Status)
    79  	}
    80  
    81  	data, err := ioutil.ReadAll(resp.Body)
    82  	if err != nil {
    83  		logrus.Fatalf("cannot read request body: %v", err)
    84  	}
    85  
    86  	cfg := &config.Config{}
    87  	if err := yaml.Unmarshal(data, &cfg); err != nil {
    88  		logrus.Fatalf("cannot unmarshal data from prow: %v", err)
    89  	}
    90  
    91  	var notFound bool
    92  	for _, pre := range cfg.AllPresubmits(nil) {
    93  		spec := pjutil.PresubmitToJobSpec(pre)
    94  		nf, err := getJobFallbackNumber(o.bucket, spec)
    95  		if err != nil {
    96  			logrus.Fatalf("cannot get fallback number: %v", err)
    97  		}
    98  		notFound = notFound || nf
    99  	}
   100  	for _, post := range cfg.AllPostsubmits(nil) {
   101  		spec := pjutil.PostsubmitToJobSpec(post)
   102  		nf, err := getJobFallbackNumber(o.bucket, spec)
   103  		if err != nil {
   104  			logrus.Fatalf("cannot get fallback number: %v", err)
   105  		}
   106  		notFound = notFound || nf
   107  	}
   108  
   109  	for _, per := range cfg.AllPeriodics() {
   110  		spec := pjutil.PeriodicToJobSpec(per)
   111  		nf, err := getJobFallbackNumber(o.bucket, spec)
   112  		if err != nil {
   113  			logrus.Fatalf("cannot get fallback number: %v", err)
   114  		}
   115  		notFound = notFound || nf
   116  	}
   117  
   118  	if notFound {
   119  		os.Exit(1)
   120  	}
   121  }
   122  
   123  func getJobFallbackNumber(bucket string, spec *downwardapi.JobSpec) (bool, error) {
   124  	url := fmt.Sprintf("%s/%s", strings.TrimSuffix(bucket, "/"), gcs.LatestBuildForSpec(spec, nil)[0])
   125  
   126  	resp, err := http.Get(url)
   127  	if err != nil {
   128  		return false, err
   129  	}
   130  	defer resp.Body.Close()
   131  	switch resp.StatusCode {
   132  	case http.StatusOK:
   133  		fmt.Printf("OK: %s\n", spec.Job)
   134  		return false, nil
   135  	case http.StatusNotFound:
   136  		rootURL := fmt.Sprintf("%s/%s", strings.TrimSuffix(bucket, "/"), gcs.RootForSpec(spec))
   137  		resp, err := http.Get(rootURL)
   138  		if err != nil {
   139  			return false, err
   140  		}
   141  		defer resp.Body.Close()
   142  		switch resp.StatusCode {
   143  		case http.StatusOK:
   144  			fmt.Printf("NOT FOUND: %s\n", spec.Job)
   145  			return true, nil
   146  		case http.StatusNotFound:
   147  			fmt.Printf("IGNORE: %s\n", spec.Job)
   148  			return false, nil
   149  		default:
   150  			return false, fmt.Errorf("unexpected status when checking the existence of %s: %v", spec.Job, resp.Status)
   151  		}
   152  
   153  	default:
   154  		return false, fmt.Errorf("unexpected status for %s: %v", spec.Job, resp.Status)
   155  	}
   156  }