forked from openshift/origin
-
Notifications
You must be signed in to change notification settings - Fork 0
/
node_args.go
321 lines (259 loc) · 10.8 KB
/
node_args.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
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
package start
import (
"errors"
"fmt"
"net"
"net/url"
"os/exec"
"regexp"
"strconv"
"strings"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/apiserver/pkg/util/flag"
"k8s.io/kubernetes/pkg/master/ports"
"github.com/openshift/origin/pkg/cmd/server/admin"
configapi "github.com/openshift/origin/pkg/cmd/server/api"
configapiv1 "github.com/openshift/origin/pkg/cmd/server/api/v1"
cmdutil "github.com/openshift/origin/pkg/cmd/util"
utilflags "github.com/openshift/origin/pkg/cmd/util/flags"
)
const (
ComponentGroupNetwork = "network"
ComponentProxy = "proxy"
ComponentDNS = "dns"
ComponentPlugins = "plugins"
ComponentKubelet = "kubelet"
)
// NewNodeComponentFlag returns a flag capable of handling enabled components for the node
func NewNodeComponentFlag() *utilflags.ComponentFlag {
return utilflags.NewComponentFlag(
map[string][]string{ComponentGroupNetwork: {ComponentProxy, ComponentPlugins}},
ComponentKubelet, ComponentProxy, ComponentPlugins, ComponentDNS,
)
}
// NewNodeComponentFlag returns a flag capable of handling enabled components for the network
func NewNetworkComponentFlag() *utilflags.ComponentFlag {
return utilflags.NewComponentFlag(nil, ComponentProxy, ComponentPlugins, ComponentDNS)
}
// NodeArgs is a struct that the command stores flag values into. It holds a partially complete set of parameters for starting a node.
// This object should hold the common set values, but not attempt to handle all cases. The expected path is to use this object to create
// a fully specified config later on. If you need something not set here, then create a fully specified config file and pass that as argument
// to starting the master.
type NodeArgs struct {
// Components is the set of enabled components.
Components *utilflags.ComponentFlag
// NodeName is the hostname to identify this node with the master.
NodeName string
// Bootstrap is true if the node should rely on the server to set initial configuration.
Bootstrap bool
MasterCertDir string
ConfigDir flag.StringFlag
AllowDisabledDocker bool
// VolumeDir is the volume storage directory.
VolumeDir string
DefaultKubernetesURL *url.URL
ClusterDomain string
ClusterDNS net.IP
// DNSBindAddr is provided for the all-in-one start only and is not exposed via a flag
DNSBindAddr string
// RecursiveResolvConf
RecursiveResolvConf string
// NetworkPluginName is the network plugin to be called for configuring networking for pods.
NetworkPluginName string
ListenArg *ListenArg
ImageFormatArgs *ImageFormatArgs
KubeConnectionArgs *KubeConnectionArgs
}
// BindNodeArgs binds the options to the flags with prefix + default flag names
func BindNodeArgs(args *NodeArgs, flags *pflag.FlagSet, prefix string, components bool) {
if components {
args.Components.Bind(flags, prefix+"%s", "The set of node components to")
}
flags.StringVar(&args.RecursiveResolvConf, prefix+"recursive-resolv-conf", args.RecursiveResolvConf, "An optional upstream resolv.conf that will override the DNS config.")
flags.StringVar(&args.NetworkPluginName, prefix+"network-plugin", args.NetworkPluginName, "The network plugin to be called for configuring networking for pods.")
flags.StringVar(&args.VolumeDir, prefix+"volume-dir", "openshift.local.volumes", "The volume storage directory.")
// TODO rename this node-name and recommend uname -n
flags.StringVar(&args.NodeName, prefix+"hostname", args.NodeName, "The hostname to identify this node with the master.")
// set dynamic value annotation - allows man pages to be generated and verified
flags.SetAnnotation(prefix+"hostname", "manpage-def-value", []string{"<hostname>"})
// autocompletion hints
cobra.MarkFlagFilename(flags, prefix+"volume-dir")
}
// BindNodeNetworkArgs binds the options to the flags with prefix + default flag names
func BindNodeNetworkArgs(args *NodeArgs, flags *pflag.FlagSet, prefix string) {
args.Components.Bind(flags, "%s", "The set of network components to")
flags.StringVar(&args.RecursiveResolvConf, prefix+"recursive-resolv-conf", args.RecursiveResolvConf, "An optional upstream resolv.conf that will override the DNS config.")
flags.StringVar(&args.NetworkPluginName, prefix+"network-plugin", args.NetworkPluginName, "The network plugin to be called for configuring networking for pods.")
}
// NewDefaultNodeArgs creates NodeArgs with sub-objects created and default values set.
func NewDefaultNodeArgs() *NodeArgs {
hostname, err := defaultHostname()
if err != nil {
hostname = "localhost"
}
var dnsIP net.IP
if clusterDNS := cmdutil.Env("OPENSHIFT_DNS_ADDR", ""); len(clusterDNS) > 0 {
dnsIP = net.ParseIP(clusterDNS)
}
config := &NodeArgs{
Components: NewNodeComponentFlag(),
NodeName: hostname,
MasterCertDir: "openshift.local.config/master",
ClusterDomain: cmdutil.Env("OPENSHIFT_DNS_DOMAIN", "cluster.local"),
ClusterDNS: dnsIP,
NetworkPluginName: "",
ListenArg: NewDefaultListenArg(),
ImageFormatArgs: NewDefaultImageFormatArgs(),
KubeConnectionArgs: NewDefaultKubeConnectionArgs(),
}
config.ConfigDir.Default("openshift.local.config/node")
return config
}
func (args NodeArgs) Validate() error {
if err := args.KubeConnectionArgs.Validate(); err != nil {
return err
}
if addr, _ := args.KubeConnectionArgs.GetKubernetesAddress(args.DefaultKubernetesURL); addr == nil {
return errors.New("--kubeconfig must be set to provide API server connection information")
}
return nil
}
func ValidateRuntime(config *configapi.NodeConfig, components *utilflags.ComponentFlag) error {
actual, err := components.Validate()
if err != nil {
return err
}
if actual.Len() == 0 {
return fmt.Errorf("at least one node component must be enabled (%s)", strings.Join(components.Allowed().List(), ", "))
}
return nil
}
// BuildSerializeableNodeConfig takes the NodeArgs (partially complete config) and uses them along with defaulting behavior to create the fully specified
// config object for starting the node
// TODO: reconcile that this is not used by CreateNodeConfig in all-in-one start.
func (args NodeArgs) BuildSerializeableNodeConfig() (*configapi.NodeConfig, error) {
var dnsIP string
if len(args.ClusterDNS) > 0 {
dnsIP = args.ClusterDNS.String()
}
config := &configapi.NodeConfig{
NodeName: args.NodeName,
ServingInfo: configapi.ServingInfo{
BindAddress: net.JoinHostPort(args.ListenArg.ListenAddr.Host, strconv.Itoa(ports.KubeletPort)),
},
ImageConfig: configapi.ImageConfig{
Format: args.ImageFormatArgs.ImageTemplate.Format,
Latest: args.ImageFormatArgs.ImageTemplate.Latest,
},
NetworkConfig: configapi.NodeNetworkConfig{
NetworkPluginName: args.NetworkPluginName,
},
VolumeDirectory: args.VolumeDir,
AllowDisabledDocker: args.AllowDisabledDocker,
DNSBindAddress: args.DNSBindAddr,
DNSDomain: args.ClusterDomain,
DNSIP: dnsIP,
DNSRecursiveResolvConf: args.RecursiveResolvConf,
MasterKubeConfig: admin.DefaultNodeKubeConfigFile(args.ConfigDir.Value()),
PodManifestConfig: nil,
EnableUnidling: true,
}
if args.ListenArg.UseTLS() {
config.ServingInfo.ServerCert = admin.DefaultNodeServingCertInfo(args.ConfigDir.Value())
config.ServingInfo.ClientCA = admin.DefaultKubeletClientCAFile(args.MasterCertDir)
}
internal, err := applyDefaults(config, configapiv1.SchemeGroupVersion)
if err != nil {
return nil, err
}
config = internal.(*configapi.NodeConfig)
// When creating a new config, use Protobuf
configapi.SetProtobufClientDefaults(config.MasterClientConnectionOverrides)
return config, nil
}
// MergeSerializeableNodeConfig takes the NodeArgs (partially complete config) and overlays them onto an existing
// config. Only a subset of node args are allowed to override this config - those that may reasonably be specified
// as local overrides.
func (args NodeArgs) MergeSerializeableNodeConfig(config *configapi.NodeConfig) error {
if len(args.ClusterDNS) > 0 {
config.DNSIP = args.ClusterDNS.String()
}
if len(args.NodeName) > 0 {
config.NodeName = args.NodeName
}
config.ServingInfo.BindAddress = net.JoinHostPort(args.ListenArg.ListenAddr.Host, strconv.Itoa(ports.KubeletPort))
config.VolumeDirectory = args.VolumeDir
config.AllowDisabledDocker = args.AllowDisabledDocker
return nil
}
// GetServerCertHostnames returns the set of hostnames and IP addresses a serving certificate for node on this host might need to be valid for.
func (args NodeArgs) GetServerCertHostnames() (sets.String, error) {
allHostnames := sets.NewString(args.NodeName)
listenIP := net.ParseIP(args.ListenArg.ListenAddr.Host)
// add the IPs that might be used based on the ListenAddr.
if listenIP != nil && listenIP.IsUnspecified() {
allAddresses, _ := cmdutil.AllLocalIP4()
for _, ip := range allAddresses {
allHostnames.Insert(ip.String())
}
} else {
allHostnames.Insert(args.ListenArg.ListenAddr.Host)
}
certHostnames := sets.String{}
for hostname := range allHostnames {
if host, _, err := net.SplitHostPort(hostname); err == nil {
// add the hostname without the port
certHostnames.Insert(host)
} else {
// add the originally specified hostname
certHostnames.Insert(hostname)
}
}
return certHostnames, nil
}
// FindLocalIPForDNS attempts to find an IP that will be reachable from
// inside containers as an IP address. It will try to use the Host values of
// the DNSBindAddr, the MasterAddr, and the MasterPublicAddr, before falling
// back to the local IP. This method will fail if the Master*Addrs point to
// an IP loadbalancer, so this method is at best a heuristic.
func findLocalIPForDNS(m *MasterArgs) (net.IP, error) {
if ip := specifiedIP(m.DNSBindAddr.Host); ip != nil {
return ip, nil
}
if ip := specifiedIP(m.MasterAddr.Host); ip != nil {
return ip, nil
}
if ip := specifiedIP(m.MasterPublicAddr.Host); ip != nil {
return ip, nil
}
return cmdutil.DefaultLocalIP4()
}
// specifiedIP parses the provided string as an IP, returning nil if the IP
// is considered unspecified (0.0.0.0)
func specifiedIP(s string) net.IP {
ip := net.ParseIP(s)
if ip.IsUnspecified() {
return nil
}
return ip
}
// defaultHostname returns the default hostname for this system.
func defaultHostname() (string, error) {
// Note: We use exec here instead of os.Hostname() because we
// want the FQDN, and this is the easiest way to get it.
fqdn, err := exec.Command("uname", "-n").Output()
if err != nil {
return "", fmt.Errorf("Couldn't determine hostname: %v", err)
}
return strings.ToLower(strings.TrimSpace(string(fqdn))), nil
}
var invalidNameCharactersRegexp = regexp.MustCompile("[^-a-z0-9]")
func safeSecretName(s string) string {
// Remove everything except [-0-9a-z]
s = invalidNameCharactersRegexp.ReplaceAllString(strings.ToLower(s), "-")
// Remove leading and trailing hyphen(s) that may be introduced by the previous step
s = strings.Trim(s, "-")
return s
}