github.com/zppinho/prow@v0.0.0-20240510014325-1738badeb017/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" 26 "net/http" 27 "os" 28 "strings" 29 30 "github.com/sirupsen/logrus" 31 "sigs.k8s.io/yaml" 32 33 "sigs.k8s.io/prow/pkg/config" 34 "sigs.k8s.io/prow/pkg/pjutil" 35 "sigs.k8s.io/prow/pkg/pod-utils/downwardapi" 36 "sigs.k8s.io/prow/pkg/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 := io.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.AllStaticPresubmits(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.AllStaticPostsubmits(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 }