forked from kubernetes/kubernetes
-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
client_builder.go
207 lines (180 loc) · 7.15 KB
/
client_builder.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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
/*
Copyright 2014 Google Inc. All rights reserved.
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 clientcmd
import (
"fmt"
"os"
"reflect"
"github.com/spf13/pflag"
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/latest"
"github.com/GoogleCloudPlatform/kubernetes/pkg/client"
"github.com/GoogleCloudPlatform/kubernetes/pkg/clientauth"
"github.com/GoogleCloudPlatform/kubernetes/pkg/version"
)
// Builder are used to bind and interpret command line flags to make it easy to get an api server client
type Builder interface {
// BindFlags must bind and keep track of all the flags required to build a client config object
BindFlags(flags *pflag.FlagSet)
// Client calls BuildConfig under the covers and uses that config to return a client
Client() (*client.Client, error)
// Config uses the values of the bound flags and builds a complete client config
Config() (*client.Config, error)
// Override invokes Config(), then passes that to the provided function, and returns a new
// builder that will use that config as its default. If Config() returns an error for the default
// values the function will not be invoked, and the error will be available when Client() is called.
Override(func(*client.Config)) Builder
}
// cmdAuthInfo is used to track whether flags have been set
type cmdAuthInfo struct {
User StringFlag
Password StringFlag
CAFile StringFlag
CertFile StringFlag
KeyFile StringFlag
BearerToken StringFlag
Insecure BoolFlag
}
// builder is a default implementation of a Builder
type builder struct {
authLoader AuthLoader
cmdAuthInfo cmdAuthInfo
authPath string
apiserver string
apiVersion string
matchApiVersion bool
config *client.Config
}
// NewBuilder returns a valid Builder that uses the passed authLoader. If authLoader is nil, the NewDefaultAuthLoader is used.
func NewBuilder(authLoader AuthLoader) Builder {
if authLoader == nil {
authLoader = NewDefaultAuthLoader()
}
return &builder{
authLoader: authLoader,
}
}
const (
FlagApiServer = "server"
FlagMatchApiVersion = "match-server-version"
FlagApiVersion = "api-version"
FlagAuthPath = "auth-path"
FlagInsecure = "insecure-skip-tls-verify"
FlagCertFile = "client-certificate"
FlagKeyFile = "client-key"
FlagCAFile = "certificate-authority"
FlagBearerToken = "token"
)
// BindFlags implements Builder
func (builder *builder) BindFlags(flags *pflag.FlagSet) {
flags.StringVarP(&builder.apiserver, FlagApiServer, "s", builder.apiserver, "The address of the Kubernetes API server")
flags.BoolVar(&builder.matchApiVersion, FlagMatchApiVersion, false, "Require server version to match client version")
flags.StringVar(&builder.apiVersion, FlagApiVersion, latest.Version, "The API version to use when talking to the server")
flags.StringVarP(&builder.authPath, FlagAuthPath, "a", os.Getenv("HOME")+"/.kubernetes_auth", "Path to the auth info file. If missing, prompt the user. Only used if using https.")
flags.Var(&builder.cmdAuthInfo.Insecure, FlagInsecure, "If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure.")
flags.Var(&builder.cmdAuthInfo.CertFile, FlagCertFile, "Path to a client key file for TLS.")
flags.Var(&builder.cmdAuthInfo.KeyFile, FlagKeyFile, "Path to a client key file for TLS.")
flags.Var(&builder.cmdAuthInfo.CAFile, FlagCAFile, "Path to a cert. file for the certificate authority.")
flags.Var(&builder.cmdAuthInfo.BearerToken, FlagBearerToken, "Bearer token for authentication to the API server.")
}
// Client implements Builder
func (builder *builder) Client() (*client.Client, error) {
clientConfig, err := builder.Config()
if err != nil {
return nil, err
}
c, err := client.New(clientConfig)
if err != nil {
return nil, err
}
if builder.matchApiVersion {
clientVersion := version.Get()
serverVersion, err := c.ServerVersion()
if err != nil {
return nil, fmt.Errorf("couldn't read version from server: %v\n", err)
}
if s := *serverVersion; !reflect.DeepEqual(clientVersion, s) {
return nil, fmt.Errorf("server version (%#v) differs from client version (%#v)!\n", s, clientVersion)
}
}
return c, nil
}
// Config implements Builder
func (builder *builder) Config() (*client.Config, error) {
if builder.config != nil {
return builder.config, nil
}
return builder.newConfig()
}
// Override implements Builder
func (builder *builder) Override(fn func(*client.Config)) Builder {
config, err := builder.newConfig()
if err != nil {
return builder
}
fn(config)
b := *builder
b.config = config
return &b
}
// newConfig creates a new config object for this builder
func (builder *builder) newConfig() (*client.Config, error) {
clientConfig := client.Config{}
if len(builder.apiserver) > 0 {
clientConfig.Host = builder.apiserver
} else if len(os.Getenv("KUBERNETES_MASTER")) > 0 {
clientConfig.Host = os.Getenv("KUBERNETES_MASTER")
} else {
// TODO: eventually apiserver should start on 443 and be secure by default
clientConfig.Host = "http://localhost:8080"
}
clientConfig.Version = builder.apiVersion
// only try to read the auth information if we are secure
if client.IsConfigTransportTLS(&clientConfig) {
authInfoFileFound := true
authInfo, err := builder.authLoader.LoadAuth(builder.authPath)
if authInfo == nil && err != nil { // only consider failing if we don't have any auth info
if !os.IsNotExist(err) { // if it's just a case of a missing file, simply flag the auth as not found and use the command line arguments
return nil, err
}
authInfoFileFound = false
authInfo = &clientauth.Info{}
}
// If provided, the command line options override options from the auth file
if !authInfoFileFound || builder.cmdAuthInfo.User.Provided() {
authInfo.User = builder.cmdAuthInfo.User.Value
}
if !authInfoFileFound || builder.cmdAuthInfo.Password.Provided() {
authInfo.Password = builder.cmdAuthInfo.Password.Value
}
if !authInfoFileFound || builder.cmdAuthInfo.CAFile.Provided() {
authInfo.CAFile = builder.cmdAuthInfo.CAFile.Value
}
if !authInfoFileFound || builder.cmdAuthInfo.CertFile.Provided() {
authInfo.CertFile = builder.cmdAuthInfo.CertFile.Value
}
if !authInfoFileFound || builder.cmdAuthInfo.KeyFile.Provided() {
authInfo.KeyFile = builder.cmdAuthInfo.KeyFile.Value
}
if !authInfoFileFound || builder.cmdAuthInfo.BearerToken.Provided() {
authInfo.BearerToken = builder.cmdAuthInfo.BearerToken.Value
}
if !authInfoFileFound || builder.cmdAuthInfo.Insecure.Provided() {
authInfo.Insecure = &builder.cmdAuthInfo.Insecure.Value
}
clientConfig, err = authInfo.MergeWithConfig(clientConfig)
if err != nil {
return nil, err
}
}
return &clientConfig, nil
}