2019-01-12 04:58:27 +00:00
|
|
|
/*
|
|
|
|
Copyright 2014 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 client
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
2019-09-27 21:51:53 +00:00
|
|
|
"fmt"
|
2019-01-12 04:58:27 +00:00
|
|
|
"net/http"
|
2019-04-14 14:55:34 +00:00
|
|
|
"net/url"
|
2019-01-12 04:58:27 +00:00
|
|
|
"strconv"
|
|
|
|
"time"
|
|
|
|
|
2019-09-27 21:51:53 +00:00
|
|
|
v1 "k8s.io/api/core/v1"
|
2019-01-12 04:58:27 +00:00
|
|
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
|
|
"k8s.io/apimachinery/pkg/types"
|
|
|
|
utilnet "k8s.io/apimachinery/pkg/util/net"
|
2019-09-27 21:51:53 +00:00
|
|
|
"k8s.io/apiserver/pkg/server/egressselector"
|
2019-01-12 04:58:27 +00:00
|
|
|
restclient "k8s.io/client-go/rest"
|
|
|
|
"k8s.io/client-go/transport"
|
|
|
|
nodeutil "k8s.io/kubernetes/pkg/util/node"
|
|
|
|
)
|
|
|
|
|
2019-09-27 21:51:53 +00:00
|
|
|
// KubeletClientConfig defines config parameters for the kubelet client
|
2019-01-12 04:58:27 +00:00
|
|
|
type KubeletClientConfig struct {
|
2019-09-27 21:51:53 +00:00
|
|
|
// Port specifies the default port - used if no information about Kubelet port can be found in Node.NodeStatus.DaemonEndpoints.
|
|
|
|
Port uint
|
|
|
|
|
|
|
|
// ReadOnlyPort specifies the Port for ReadOnly communications.
|
2019-01-12 04:58:27 +00:00
|
|
|
ReadOnlyPort uint
|
2019-09-27 21:51:53 +00:00
|
|
|
|
|
|
|
// EnableHTTPs specifies if traffic should be encrypted.
|
|
|
|
EnableHTTPS bool
|
2019-01-12 04:58:27 +00:00
|
|
|
|
|
|
|
// PreferredAddressTypes - used to select an address from Node.NodeStatus.Addresses
|
|
|
|
PreferredAddressTypes []string
|
|
|
|
|
|
|
|
// TLSClientConfig contains settings to enable transport layer security
|
|
|
|
restclient.TLSClientConfig
|
|
|
|
|
|
|
|
// Server requires Bearer authentication
|
|
|
|
BearerToken string
|
|
|
|
|
|
|
|
// HTTPTimeout is used by the client to timeout http requests to Kubelet.
|
|
|
|
HTTPTimeout time.Duration
|
|
|
|
|
|
|
|
// Dial is a custom dialer used for the client
|
|
|
|
Dial utilnet.DialFunc
|
2019-04-14 14:55:34 +00:00
|
|
|
|
2019-09-27 21:51:53 +00:00
|
|
|
// Lookup will give us a dialer if the egress selector is configured for it
|
|
|
|
Lookup egressselector.Lookup
|
|
|
|
|
2019-04-14 14:55:34 +00:00
|
|
|
// Proxy is a custom proxy function for the client
|
|
|
|
Proxy func(*http.Request) (*url.URL, error)
|
2019-01-12 04:58:27 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ConnectionInfo provides the information needed to connect to a kubelet
|
|
|
|
type ConnectionInfo struct {
|
|
|
|
Scheme string
|
|
|
|
Hostname string
|
|
|
|
Port string
|
|
|
|
Transport http.RoundTripper
|
|
|
|
}
|
|
|
|
|
|
|
|
// ConnectionInfoGetter provides ConnectionInfo for the kubelet running on a named node
|
|
|
|
type ConnectionInfoGetter interface {
|
|
|
|
GetConnectionInfo(ctx context.Context, nodeName types.NodeName) (*ConnectionInfo, error)
|
|
|
|
}
|
|
|
|
|
2019-09-27 21:51:53 +00:00
|
|
|
// MakeTransport creates a RoundTripper for HTTP Transport.
|
2019-01-12 04:58:27 +00:00
|
|
|
func MakeTransport(config *KubeletClientConfig) (http.RoundTripper, error) {
|
|
|
|
tlsConfig, err := transport.TLSConfigFor(config.transportConfig())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
rt := http.DefaultTransport
|
2019-09-27 21:51:53 +00:00
|
|
|
dialer := config.Dial
|
|
|
|
if dialer == nil && config.Lookup != nil {
|
|
|
|
// Assuming EgressSelector if SSHTunnel is not turned on.
|
|
|
|
// We will not get a dialer if egress selector is disabled.
|
|
|
|
networkContext := egressselector.Cluster.AsNetworkContext()
|
|
|
|
dialer, err = config.Lookup(networkContext)
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("failed to get context dialer for 'cluster': got %v", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if dialer != nil || tlsConfig != nil {
|
|
|
|
// If SSH Tunnel is turned on
|
2019-01-12 04:58:27 +00:00
|
|
|
rt = utilnet.SetOldTransportDefaults(&http.Transport{
|
2019-09-27 21:51:53 +00:00
|
|
|
DialContext: dialer,
|
2019-01-12 04:58:27 +00:00
|
|
|
TLSClientConfig: tlsConfig,
|
2019-04-14 14:55:34 +00:00
|
|
|
Proxy: config.Proxy,
|
2019-01-12 04:58:27 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
return transport.HTTPWrappersForConfig(config.transportConfig(), rt)
|
|
|
|
}
|
|
|
|
|
|
|
|
// transportConfig converts a client config to an appropriate transport config.
|
|
|
|
func (c *KubeletClientConfig) transportConfig() *transport.Config {
|
|
|
|
cfg := &transport.Config{
|
|
|
|
TLS: transport.TLSConfig{
|
2019-09-27 21:51:53 +00:00
|
|
|
CAFile: c.CAFile,
|
|
|
|
CAData: c.CAData,
|
|
|
|
CertFile: c.CertFile,
|
|
|
|
CertData: c.CertData,
|
|
|
|
KeyFile: c.KeyFile,
|
|
|
|
KeyData: c.KeyData,
|
|
|
|
NextProtos: c.NextProtos,
|
2019-01-12 04:58:27 +00:00
|
|
|
},
|
|
|
|
BearerToken: c.BearerToken,
|
|
|
|
}
|
2019-09-27 21:51:53 +00:00
|
|
|
if c.EnableHTTPS && !cfg.HasCA() {
|
2019-01-12 04:58:27 +00:00
|
|
|
cfg.TLS.Insecure = true
|
|
|
|
}
|
|
|
|
return cfg
|
|
|
|
}
|
|
|
|
|
|
|
|
// NodeGetter defines an interface for looking up a node by name
|
|
|
|
type NodeGetter interface {
|
|
|
|
Get(ctx context.Context, name string, options metav1.GetOptions) (*v1.Node, error)
|
|
|
|
}
|
|
|
|
|
|
|
|
// NodeGetterFunc allows implementing NodeGetter with a function
|
|
|
|
type NodeGetterFunc func(ctx context.Context, name string, options metav1.GetOptions) (*v1.Node, error)
|
|
|
|
|
2019-09-27 21:51:53 +00:00
|
|
|
// Get fetches information via NodeGetterFunc.
|
2019-01-12 04:58:27 +00:00
|
|
|
func (f NodeGetterFunc) Get(ctx context.Context, name string, options metav1.GetOptions) (*v1.Node, error) {
|
|
|
|
return f(ctx, name, options)
|
|
|
|
}
|
|
|
|
|
|
|
|
// NodeConnectionInfoGetter obtains connection info from the status of a Node API object
|
|
|
|
type NodeConnectionInfoGetter struct {
|
|
|
|
// nodes is used to look up Node objects
|
|
|
|
nodes NodeGetter
|
|
|
|
// scheme is the scheme to use to connect to all kubelets
|
|
|
|
scheme string
|
|
|
|
// defaultPort is the port to use if no Kubelet endpoint port is recorded in the node status
|
|
|
|
defaultPort int
|
|
|
|
// transport is the transport to use to send a request to all kubelets
|
|
|
|
transport http.RoundTripper
|
|
|
|
// preferredAddressTypes specifies the preferred order to use to find a node address
|
|
|
|
preferredAddressTypes []v1.NodeAddressType
|
|
|
|
}
|
|
|
|
|
2019-09-27 21:51:53 +00:00
|
|
|
// NewNodeConnectionInfoGetter creates a new NodeConnectionInfoGetter.
|
2019-01-12 04:58:27 +00:00
|
|
|
func NewNodeConnectionInfoGetter(nodes NodeGetter, config KubeletClientConfig) (ConnectionInfoGetter, error) {
|
|
|
|
scheme := "http"
|
2019-09-27 21:51:53 +00:00
|
|
|
if config.EnableHTTPS {
|
2019-01-12 04:58:27 +00:00
|
|
|
scheme = "https"
|
|
|
|
}
|
|
|
|
|
|
|
|
transport, err := MakeTransport(&config)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
types := []v1.NodeAddressType{}
|
|
|
|
for _, t := range config.PreferredAddressTypes {
|
|
|
|
types = append(types, v1.NodeAddressType(t))
|
|
|
|
}
|
|
|
|
|
|
|
|
return &NodeConnectionInfoGetter{
|
|
|
|
nodes: nodes,
|
|
|
|
scheme: scheme,
|
|
|
|
defaultPort: int(config.Port),
|
|
|
|
transport: transport,
|
|
|
|
|
|
|
|
preferredAddressTypes: types,
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
2019-09-27 21:51:53 +00:00
|
|
|
// GetConnectionInfo retrieves connection info from the status of a Node API object.
|
2019-01-12 04:58:27 +00:00
|
|
|
func (k *NodeConnectionInfoGetter) GetConnectionInfo(ctx context.Context, nodeName types.NodeName) (*ConnectionInfo, error) {
|
|
|
|
node, err := k.nodes.Get(ctx, string(nodeName), metav1.GetOptions{})
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find a kubelet-reported address, using preferred address type
|
|
|
|
host, err := nodeutil.GetPreferredNodeAddress(node, k.preferredAddressTypes)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Use the kubelet-reported port, if present
|
|
|
|
port := int(node.Status.DaemonEndpoints.KubeletEndpoint.Port)
|
|
|
|
if port <= 0 {
|
|
|
|
port = k.defaultPort
|
|
|
|
}
|
|
|
|
|
|
|
|
return &ConnectionInfo{
|
|
|
|
Scheme: k.scheme,
|
|
|
|
Hostname: host,
|
|
|
|
Port: strconv.Itoa(port),
|
|
|
|
Transport: k.transport,
|
|
|
|
}, nil
|
|
|
|
}
|