github.com/munnerz/test-infra@v0.0.0-20190108210205-ce3d181dc989/experiment/add-hook/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 "bytes" 21 "errors" 22 "flag" 23 "fmt" 24 "io/ioutil" 25 "os" 26 "strings" 27 28 "github.com/sirupsen/logrus" 29 30 "k8s.io/test-infra/prow/config/secret" 31 "k8s.io/test-infra/prow/flagutil" 32 "k8s.io/test-infra/prow/github" 33 ) 34 35 type options struct { 36 flagutil.GitHubOptions 37 repo flagutil.Strings 38 hookURL string 39 hmacPath string 40 confirm bool 41 events flagutil.Strings 42 } 43 44 func (o options) githubClient() (*github.Client, error) { 45 agent := &secret.Agent{} 46 if err := agent.Start([]string{o.TokenPath}); err != nil { 47 return nil, fmt.Errorf("start %s: %v", o.TokenPath, err) 48 } 49 return o.GitHubClient(agent, !o.confirm) 50 } 51 52 func getOptions(fs *flag.FlagSet, args []string) (*options, error) { 53 o := options{} 54 o.AddFlags(fs) 55 o.events = flagutil.NewStrings(github.AllHookEvents...) 56 fs.Var(&o.events, "event", "Receive hooks for the following events, defaults to [\"*\"] (all events)") 57 fs.Var(&o.repo, "repo", "Add hooks for this org or org/repo") 58 fs.StringVar(&o.hookURL, "hook-url", "", "URL to send hooks") 59 fs.StringVar(&o.hmacPath, "hmac-path", "", "Path to hmac secret") 60 fs.BoolVar(&o.confirm, "confirm", false, "Apply changes to github") 61 fs.Parse(args) 62 if o.hmacPath == "" { 63 return nil, errors.New("--hmac-path must be set") 64 } 65 if o.hookURL == "" { 66 return nil, errors.New("--hook-url must be set") 67 } 68 if len(o.repo.Strings()) == 0 { 69 return nil, errors.New("no --repos set") 70 } 71 if err := o.Validate(!o.confirm); err != nil { 72 return nil, err 73 } 74 return &o, nil 75 } 76 77 func (o options) hmac() (string, error) { 78 b, err := ioutil.ReadFile(o.hmacPath) 79 if err != nil { 80 return "", fmt.Errorf("read %s: %v", o.hmacPath, err) 81 } 82 return string(bytes.TrimSpace(b)), nil 83 } 84 85 func findHook(hooks []github.Hook, url string) *int { 86 for _, h := range hooks { 87 if h.Config.URL == url { 88 return &h.ID 89 } 90 } 91 return nil 92 } 93 94 type changer struct { 95 lister func(org string) ([]github.Hook, error) 96 editor func(org string, id int, req github.HookRequest) error 97 creator func(org string, req github.HookRequest) (int, error) 98 } 99 100 func orgChanger(client *github.Client) changer { 101 return changer{ 102 lister: client.ListOrgHooks, 103 editor: client.EditOrgHook, 104 creator: client.CreateOrgHook, 105 } 106 } 107 108 func repoChanger(client *github.Client, repo string) changer { 109 return changer{ 110 lister: func(org string) ([]github.Hook, error) { 111 return client.ListRepoHooks(org, repo) 112 }, 113 editor: func(org string, id int, req github.HookRequest) error { 114 return client.EditRepoHook(org, repo, id, req) 115 }, 116 creator: func(org string, req github.HookRequest) (int, error) { 117 return client.CreateRepoHook(org, repo, req) 118 }, 119 } 120 } 121 122 func main() { 123 o, err := getOptions(flag.NewFlagSet(os.Args[0], flag.ExitOnError), os.Args[1:]) 124 if err != nil { 125 logrus.Fatalf("Bad flags: %v", err) 126 } 127 128 client, err := o.githubClient() 129 if err != nil { 130 logrus.Fatalf("Could not create github client: %v", err) 131 } 132 133 hmac, err := o.hmac() 134 if err != nil { 135 logrus.Fatalf("Could not load hmac secret: %v", err) 136 } 137 138 yes := true 139 j := "json" 140 req := github.HookRequest{ 141 Name: "web", 142 Active: &yes, 143 Config: &github.HookConfig{ 144 URL: o.hookURL, 145 ContentType: &j, 146 Secret: &hmac, 147 }, 148 Events: o.events.Strings(), 149 } 150 for _, orgRepo := range o.repo.Strings() { 151 parts := strings.SplitN(orgRepo, "/", 2) 152 var ch changer 153 if len(parts) == 1 { 154 ch = orgChanger(client) 155 } else { 156 ch = repoChanger(client, parts[1]) 157 } 158 159 org := parts[0] 160 if err := reconcileHook(ch, org, req); err != nil { 161 logrus.Fatalf("Could not apply hook to %s: %v", orgRepo, err) 162 } 163 } 164 } 165 166 func reconcileHook(ch changer, org string, req github.HookRequest) error { 167 hooks, err := ch.lister(org) 168 if err != nil { 169 return fmt.Errorf("list: %v", err) 170 } 171 id := findHook(hooks, req.Config.URL) 172 if id == nil { 173 _, err := ch.creator(org, req) 174 return err 175 } 176 return ch.editor(org, *id, req) 177 }