forked from kubernetes/test-infra
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgithub.go
187 lines (161 loc) · 6.6 KB
/
github.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
/*
Copyright 2018 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package flagutil
import (
"bytes"
"flag"
"fmt"
"net/url"
"strings"
"github.com/sirupsen/logrus"
"golang.org/x/oauth2"
"k8s.io/test-infra/pkg/ghclient"
"k8s.io/test-infra/prow/config/secret"
"k8s.io/test-infra/prow/git"
"k8s.io/test-infra/prow/github"
"k8s.io/test-infra/prow/githuboauth"
)
// GitHubOptions holds options for interacting with GitHub.
type GitHubOptions struct {
host string
endpoint Strings
graphqlEndpoint string
TokenPath string
deprecatedTokenFile string
}
// NewGitHubOptions creates a GitHubOptions with default values.
func NewGitHubOptions() *GitHubOptions {
return &GitHubOptions{
host: github.DefaultHost,
endpoint: NewStrings(github.DefaultAPIEndpoint),
graphqlEndpoint: github.DefaultAPIEndpoint,
}
}
// AddFlags injects GitHub options into the given FlagSet.
func (o *GitHubOptions) AddFlags(fs *flag.FlagSet) {
o.addFlags(true, fs)
}
// AddFlagsWithoutDefaultGitHubTokenPath injects GitHub options into the given
// Flagset without setting a default for for the githubTokenPath, allowing to
// use an anonymous GitHub client
func (o *GitHubOptions) AddFlagsWithoutDefaultGitHubTokenPath(fs *flag.FlagSet) {
o.addFlags(false, fs)
}
func (o *GitHubOptions) addFlags(wantDefaultGitHubTokenPath bool, fs *flag.FlagSet) {
fs.StringVar(&o.host, "github-host", github.DefaultHost, "GitHub's default host (may differ for enterprise)")
o.endpoint = NewStrings(github.DefaultAPIEndpoint)
fs.Var(&o.endpoint, "github-endpoint", "GitHub's API endpoint (may differ for enterprise).")
fs.StringVar(&o.graphqlEndpoint, "github-graphql-endpoint", github.DefaultGraphQLEndpoint, "GitHub GraphQL API endpoint (may differ for enterprise).")
defaultGitHubTokenPath := ""
if wantDefaultGitHubTokenPath {
defaultGitHubTokenPath = "/etc/github/oauth"
}
fs.StringVar(&o.TokenPath, "github-token-path", defaultGitHubTokenPath, "Path to the file containing the GitHub OAuth secret.")
fs.StringVar(&o.deprecatedTokenFile, "github-token-file", "", "DEPRECATED: use -github-token-path instead. -github-token-file may be removed anytime after 2019-01-01.")
}
// Validate validates GitHub options.
func (o *GitHubOptions) Validate(dryRun bool) error {
for _, uri := range o.endpoint.Strings() {
if uri == "" {
uri = github.DefaultAPIEndpoint
} else if _, err := url.ParseRequestURI(uri); err != nil {
return fmt.Errorf("invalid -github-endpoint URI: %q", uri)
}
}
if o.graphqlEndpoint == "" {
o.graphqlEndpoint = github.DefaultGraphQLEndpoint
} else if _, err := url.Parse(o.graphqlEndpoint); err != nil {
return fmt.Errorf("invalid -github-graphql-endpoint URI: %q", o.graphqlEndpoint)
}
if o.deprecatedTokenFile != "" {
o.TokenPath = o.deprecatedTokenFile
logrus.Error("-github-token-file is deprecated and may be removed anytime after 2019-01-01. Use -github-token-path instead.")
}
return nil
}
// GitHubClientWithLogFields returns a GitHub client with extra logging fields
func (o *GitHubOptions) GitHubClientWithLogFields(secretAgent *secret.Agent, dryRun bool, fields logrus.Fields) (client github.Client, err error) {
var generator *func() []byte
if o.TokenPath == "" {
logrus.Warn("empty -github-token-path, will use anonymous github client")
generatorFunc := func() []byte {
return []byte{}
}
generator = &generatorFunc
} else {
if secretAgent == nil {
return nil, fmt.Errorf("cannot store token from %q without a secret agent", o.TokenPath)
}
generatorFunc := secretAgent.GetTokenGenerator(o.TokenPath)
generator = &generatorFunc
}
if dryRun {
return github.NewDryRunClientWithFields(fields, *generator, secretAgent.Censor, o.graphqlEndpoint, o.endpoint.Strings()...), nil
}
return github.NewClientWithFields(fields, *generator, secretAgent.Censor, o.graphqlEndpoint, o.endpoint.Strings()...), nil
}
// GitHubClient returns a GitHub client.
func (o *GitHubOptions) GitHubClient(secretAgent *secret.Agent, dryRun bool) (client github.Client, err error) {
return o.GitHubClientWithLogFields(secretAgent, dryRun, logrus.Fields{})
}
// GitHubClientWithAccessToken creates a GitHub client from an access token.
func (o *GitHubOptions) GitHubClientWithAccessToken(token string) github.Client {
return github.NewClient(func() []byte { return []byte(token) }, func(content []byte) []byte {
trimmedToken := strings.TrimSpace(token)
if trimmedToken != token {
token = trimmedToken
}
if token == "" {
return content
}
return bytes.ReplaceAll(content, []byte(token), []byte("CENSORED"))
}, o.graphqlEndpoint, o.endpoint.Strings()...)
}
// GitClient returns a Git client.
func (o *GitHubOptions) GitClient(secretAgent *secret.Agent, dryRun bool) (client *git.Client, err error) {
client, err = git.NewClientWithHost(o.host)
if err != nil {
return nil, err
}
// We must capture the value of client here to prevent issues related
// to the use of named return values when an error is encountered.
// Without this, we risk a nil pointer dereference.
defer func(client *git.Client) {
if err != nil {
client.Clean()
}
}(client)
// Get the bot's name in order to set credentials for the Git client.
githubClient, err := o.GitHubClient(secretAgent, dryRun)
if err != nil {
return nil, fmt.Errorf("error getting GitHub client: %v", err)
}
botName, err := githubClient.BotName()
if err != nil {
return nil, fmt.Errorf("error getting bot name: %v", err)
}
client.SetCredentials(botName, secretAgent.GetTokenGenerator(o.TokenPath))
return client, nil
}
// GitHubOAuthClient returns an oauth client.
func (o *GitHubOptions) GitHubOAuthClient(oauthConfig *oauth2.Config) githuboauth.OAuthClient {
oauthConfig.Endpoint = oauth2.Endpoint{
AuthURL: fmt.Sprintf("https://%s/login/oauth/authorize", o.host),
TokenURL: fmt.Sprintf("https://%s/login/oauth/access_token", o.host),
}
return githuboauth.NewClient(oauthConfig)
}
// GetGitHubClient returns a github client wrapper.
func (o *GitHubOptions) GetGitHubClient(accessToken string, dryRun bool) githuboauth.GitHubClientWrapper {
return ghclient.NewClientWithEndpoint(o.endpoint.Strings()[0], accessToken, dryRun)
}