k8s.io/test-infra@v0.0.0-20240520184403-27c6b4c223d8/experiment/manual-trigger/manual-trigger.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  // manual-trigger triggers jenkins jobs based a specified github pull request
    18  package main
    19  
    20  import (
    21  	"flag"
    22  	"fmt"
    23  	"log"
    24  	"net/url"
    25  	"strings"
    26  
    27  	"github.com/sirupsen/logrus"
    28  
    29  	prowapi "sigs.k8s.io/prow/pkg/apis/prowjobs/v1"
    30  	"sigs.k8s.io/prow/pkg/config/secret"
    31  	"sigs.k8s.io/prow/pkg/github"
    32  	"sigs.k8s.io/prow/pkg/jenkins"
    33  	"sigs.k8s.io/prow/pkg/pod-utils/downwardapi"
    34  )
    35  
    36  type options struct {
    37  	githubEndpoint         string
    38  	graphqlEndpoint        string
    39  	githubTokenFile        string
    40  	jenkinsBearerTokenFile string
    41  	jenkinsURL             string
    42  	jenkinsTokenFile       string
    43  	jenkinsUserName        string
    44  	jobName                string
    45  	num                    int
    46  	org                    string
    47  	repo                   string
    48  }
    49  
    50  func flagOptions() options {
    51  	o := options{}
    52  
    53  	flag.StringVar(&o.jenkinsBearerTokenFile, "jenkins-bearer-token-file", "", "Path to the file containing the Jenkins API bearer token.")
    54  	flag.StringVar(&o.jenkinsURL, "jenkins-url", "", "Jenkins URL.")
    55  	flag.StringVar(&o.jenkinsTokenFile, "jenkins-token-file", "", "Path to the file containing the Jenkins API token.")
    56  	flag.StringVar(&o.jenkinsUserName, "jenkins-user-name", "", "Jenkins username.")
    57  
    58  	flag.StringVar(&o.githubEndpoint, "github-endpoint", github.DefaultAPIEndpoint, "GitHub's API endpoint.")
    59  	flag.StringVar(&o.graphqlEndpoint, "graphql-endpoint", github.DefaultGraphQLEndpoint, "GitHub's GraphQL API endpoint.")
    60  	flag.StringVar(&o.githubTokenFile, "github-token-file", "", "Path to file containing GitHub OAuth token.")
    61  
    62  	flag.StringVar(&o.jobName, "job-name", "", "Name of Jenkins job")
    63  
    64  	flag.IntVar(&o.num, "num", 0, "GitHub issue number")
    65  	flag.StringVar(&o.org, "org", "", "GitHub organization")
    66  	flag.StringVar(&o.repo, "repo", "", "GitHub repository")
    67  	flag.Parse()
    68  	return o
    69  }
    70  
    71  func sanityCheckFlags(o options) error {
    72  	if o.num <= 0 {
    73  		return fmt.Errorf("empty or invalid --num")
    74  	}
    75  	if o.org == "" {
    76  		return fmt.Errorf("empty --org")
    77  	}
    78  	if o.repo == "" {
    79  		return fmt.Errorf("empty --repo")
    80  	}
    81  	if o.githubTokenFile == "" {
    82  		return fmt.Errorf("empty --github-token-file")
    83  	}
    84  	if o.jobName == "" {
    85  		return fmt.Errorf("empty --job-name")
    86  	}
    87  
    88  	if o.jenkinsBearerTokenFile == "" && (o.jenkinsUserName == "" || o.jenkinsTokenFile == "") {
    89  		return fmt.Errorf("neither --jenkins-bearer-token-file nor the combination of --jenkins-user-name and --jenkins-token-file were provided")
    90  	}
    91  
    92  	if o.githubEndpoint == "" {
    93  		return fmt.Errorf("empty --github-endpoint")
    94  	} else if _, err := url.Parse(o.githubEndpoint); err != nil {
    95  		return fmt.Errorf("bad --github-endpoint provided: %w", err)
    96  	}
    97  
    98  	if o.graphqlEndpoint == "" {
    99  		return fmt.Errorf("empty --graphql-endpoint")
   100  	} else if _, err := url.Parse(o.graphqlEndpoint); err != nil {
   101  		return fmt.Errorf("bad --graphql-endpoint provided: %w", err)
   102  	}
   103  
   104  	if o.jenkinsURL == "" {
   105  		return fmt.Errorf("empty --jenkins-url")
   106  	} else if _, err := url.Parse(o.jenkinsURL); err != nil {
   107  		return fmt.Errorf("bad --jenkins-url provided: %w", err)
   108  	}
   109  
   110  	return nil
   111  }
   112  
   113  func main() {
   114  	o := flagOptions()
   115  	err := sanityCheckFlags(o)
   116  	if err != nil {
   117  		log.Fatal(err)
   118  	}
   119  
   120  	var tokens []string
   121  	tokens = append(tokens, o.githubTokenFile)
   122  
   123  	if o.jenkinsTokenFile != "" {
   124  		tokens = append(tokens, o.jenkinsTokenFile)
   125  	}
   126  
   127  	if o.jenkinsBearerTokenFile != "" {
   128  		tokens = append(tokens, o.jenkinsBearerTokenFile)
   129  	}
   130  
   131  	if err := secret.Add(tokens...); err != nil {
   132  		logrus.WithError(err).Fatal("Error starting secrets agent.")
   133  	}
   134  
   135  	// TODO: dry this out
   136  	ac := jenkins.AuthConfig{}
   137  	if o.jenkinsTokenFile != "" {
   138  		ac.Basic = &jenkins.BasicAuthConfig{
   139  			User:     o.jenkinsUserName,
   140  			GetToken: secret.GetTokenGenerator(o.jenkinsTokenFile),
   141  		}
   142  	} else if o.jenkinsBearerTokenFile != "" {
   143  		ac.BearerToken = &jenkins.BearerTokenAuthConfig{
   144  			GetToken: secret.GetTokenGenerator(o.jenkinsBearerTokenFile),
   145  		}
   146  	} else {
   147  		log.Fatalf("no jenkins auth token provided")
   148  	}
   149  
   150  	jc, err := jenkins.NewClient(o.jenkinsURL, false, nil, &ac, nil, nil)
   151  	if err != nil {
   152  		log.Fatalf("cannot setup Jenkins client: %v", err)
   153  	}
   154  
   155  	gc, err := github.NewClient(secret.GetTokenGenerator(o.githubTokenFile), secret.Censor, o.graphqlEndpoint, o.githubEndpoint)
   156  	if err != nil {
   157  		log.Fatalf("failed to construct GitHub client: %v", err)
   158  	}
   159  
   160  	pr, err := gc.GetPullRequest(o.org, o.repo, o.num)
   161  	if err != nil {
   162  		log.Fatalf("Unable to get information on pull request %s/%s#%d: %v", o.org, o.repo, o.num, err)
   163  	}
   164  
   165  	spec := prowapi.ProwJobSpec{
   166  		Type: prowapi.PresubmitJob,
   167  		Job:  o.jobName,
   168  		Refs: &prowapi.Refs{
   169  			Org:     o.org,
   170  			Repo:    o.repo,
   171  			BaseRef: pr.Base.Ref,
   172  			BaseSHA: pr.Base.SHA,
   173  			Pulls: []prowapi.Pull{
   174  				{
   175  					Number:  pr.Number,
   176  					Author:  pr.User.Login,
   177  					SHA:     pr.Head.SHA,
   178  					HeadRef: pr.Head.Ref,
   179  				},
   180  			},
   181  		},
   182  
   183  		Report:         false,
   184  		Context:        "",
   185  		RerunCommand:   "",
   186  		MaxConcurrency: 1,
   187  	}
   188  
   189  	if err = jc.BuildFromSpec(&spec, "0", o.jobName); err != nil {
   190  		log.Println("Submitting the following to Jenkins:")
   191  		env, _ := downwardapi.EnvForSpec(downwardapi.NewJobSpec(spec, "0", o.jobName))
   192  		for k, v := range env {
   193  			log.Printf("  %s=%s\n", k, v)
   194  		}
   195  		log.Fatalf("for %s/%s#%d resulted in an error: %v", o.org, o.repo, o.num, err)
   196  	} else {
   197  		slash := "/"
   198  		if strings.HasSuffix(o.jenkinsURL, "/") {
   199  			slash = ""
   200  		}
   201  		log.Printf("Successfully submitted job to %s%sjob/%s", o.jenkinsURL, slash, o.jobName)
   202  	}
   203  }