mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-30 23:15:14 +00:00
componentstatus: support client cert health check
etcd has support for client-cert-auth, which can be configured via the flag `--ca-file`, when that is enabled, all the client requests must present with a client certificate, however, the current component status check uses a single transport for all of the checks, this is wrong, the checks should be different for each of different component, and make each of them use different transport(tls configurations).
This commit is contained in:
parent
dee81ed56a
commit
b1040171b6
@ -32,7 +32,12 @@ import (
|
||||
|
||||
func New() HTTPProber {
|
||||
tlsConfig := &tls.Config{InsecureSkipVerify: true}
|
||||
transport := utilnet.SetTransportDefaults(&http.Transport{TLSClientConfig: tlsConfig, DisableKeepAlives: true})
|
||||
return NewWithTLSConfig(tlsConfig)
|
||||
}
|
||||
|
||||
// NewWithTLSConfig takes tls config as parameter.
|
||||
func NewWithTLSConfig(config *tls.Config) HTTPProber {
|
||||
transport := utilnet.SetTransportDefaults(&http.Transport{TLSClientConfig: config, DisableKeepAlives: true})
|
||||
return httpProber{transport}
|
||||
}
|
||||
|
||||
|
@ -27,19 +27,16 @@ import (
|
||||
"k8s.io/apiserver/pkg/registry/rest"
|
||||
"k8s.io/kubernetes/pkg/api"
|
||||
"k8s.io/kubernetes/pkg/probe"
|
||||
httpprober "k8s.io/kubernetes/pkg/probe/http"
|
||||
)
|
||||
|
||||
type REST struct {
|
||||
GetServersToValidate func() map[string]Server
|
||||
prober httpprober.HTTPProber
|
||||
GetServersToValidate func() map[string]*Server
|
||||
}
|
||||
|
||||
// NewStorage returns a new REST.
|
||||
func NewStorage(serverRetriever func() map[string]Server) *REST {
|
||||
func NewStorage(serverRetriever func() map[string]*Server) *REST {
|
||||
return &REST{
|
||||
GetServersToValidate: serverRetriever,
|
||||
prober: httpprober.New(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,7 +57,7 @@ func (rs *REST) List(ctx genericapirequest.Context, options *metainternalversion
|
||||
wait.Add(len(servers))
|
||||
statuses := make(chan api.ComponentStatus, len(servers))
|
||||
for k, v := range servers {
|
||||
go func(name string, server Server) {
|
||||
go func(name string, server *Server) {
|
||||
defer wait.Done()
|
||||
status := rs.getComponentStatus(name, server)
|
||||
statuses <- *status
|
||||
@ -97,8 +94,8 @@ func ToConditionStatus(s probe.Result) api.ConditionStatus {
|
||||
}
|
||||
}
|
||||
|
||||
func (rs *REST) getComponentStatus(name string, server Server) *api.ComponentStatus {
|
||||
status, msg, err := server.DoServerCheck(rs.prober)
|
||||
func (rs *REST) getComponentStatus(name string, server *Server) *api.ComponentStatus {
|
||||
status, msg, err := server.DoServerCheck()
|
||||
errorMsg := ""
|
||||
if err != nil {
|
||||
errorMsg = err.Error()
|
||||
|
@ -50,17 +50,17 @@ type testResponse struct {
|
||||
}
|
||||
|
||||
func NewTestREST(resp testResponse) *REST {
|
||||
prober := &fakeHttpProber{
|
||||
result: resp.result,
|
||||
body: resp.data,
|
||||
err: resp.err,
|
||||
}
|
||||
return &REST{
|
||||
GetServersToValidate: func() map[string]Server {
|
||||
return map[string]Server{
|
||||
"test1": {Addr: "testserver1", Port: 8000, Path: "/healthz"},
|
||||
GetServersToValidate: func() map[string]*Server {
|
||||
return map[string]*Server{
|
||||
"test1": {Addr: "testserver1", Port: 8000, Path: "/healthz", Prober: prober},
|
||||
}
|
||||
},
|
||||
prober: &fakeHttpProber{
|
||||
result: resp.result,
|
||||
body: resp.data,
|
||||
err: resp.err,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -17,8 +17,9 @@ limitations under the License.
|
||||
package componentstatus
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net/http"
|
||||
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
utilnet "k8s.io/apimachinery/pkg/util/net"
|
||||
@ -42,7 +43,10 @@ type Server struct {
|
||||
Port int
|
||||
Path string
|
||||
EnableHTTPS bool
|
||||
TLSConfig *tls.Config
|
||||
Validate ValidatorFn
|
||||
Prober httpprober.HTTPProber
|
||||
Once sync.Once
|
||||
}
|
||||
|
||||
type ServerStatus struct {
|
||||
@ -58,14 +62,22 @@ type ServerStatus struct {
|
||||
Err string `json:"err,omitempty"`
|
||||
}
|
||||
|
||||
func (server *Server) DoServerCheck(prober httpprober.HTTPProber) (probe.Result, string, error) {
|
||||
func (server *Server) DoServerCheck() (probe.Result, string, error) {
|
||||
// setup the prober
|
||||
server.Once.Do(func() {
|
||||
if server.Prober != nil {
|
||||
return
|
||||
}
|
||||
server.Prober = httpprober.NewWithTLSConfig(server.TLSConfig)
|
||||
})
|
||||
|
||||
scheme := "http"
|
||||
if server.EnableHTTPS {
|
||||
scheme = "https"
|
||||
}
|
||||
url := utilnet.FormatURL(scheme, server.Addr, server.Port, server.Path)
|
||||
|
||||
result, data, err := prober.Probe(url, nil, probeTimeOut)
|
||||
result, data, err := server.Prober.Probe(url, nil, probeTimeOut)
|
||||
|
||||
if err != nil {
|
||||
return probe.Unknown, "", err
|
||||
|
@ -59,7 +59,8 @@ func TestValidate(t *testing.T) {
|
||||
}
|
||||
|
||||
s.Validate = test.validator
|
||||
result, data, err := s.DoServerCheck(fakeProber)
|
||||
s.Prober = fakeProber
|
||||
result, data, err := s.DoServerCheck()
|
||||
if test.expectErr && err == nil {
|
||||
t.Error("unexpected non-error")
|
||||
}
|
||||
|
@ -15,6 +15,7 @@ go_test(
|
||||
tags = ["automanaged"],
|
||||
deps = [
|
||||
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/server/storage:go_default_library",
|
||||
"//vendor/k8s.io/apiserver/pkg/storage/storagebackend:go_default_library",
|
||||
],
|
||||
)
|
||||
|
@ -242,14 +242,14 @@ type componentStatusStorage struct {
|
||||
storageFactory serverstorage.StorageFactory
|
||||
}
|
||||
|
||||
func (s componentStatusStorage) serversToValidate() map[string]componentstatus.Server {
|
||||
serversToValidate := map[string]componentstatus.Server{
|
||||
func (s componentStatusStorage) serversToValidate() map[string]*componentstatus.Server {
|
||||
serversToValidate := map[string]*componentstatus.Server{
|
||||
"controller-manager": {Addr: "127.0.0.1", Port: ports.ControllerManagerPort, Path: "/healthz"},
|
||||
"scheduler": {Addr: "127.0.0.1", Port: ports.SchedulerPort, Path: "/healthz"},
|
||||
}
|
||||
|
||||
for ix, machine := range s.storageFactory.Backends() {
|
||||
etcdUrl, err := url.Parse(machine)
|
||||
etcdUrl, err := url.Parse(machine.Server)
|
||||
if err != nil {
|
||||
glog.Errorf("Failed to parse etcd url for validation: %v", err)
|
||||
continue
|
||||
@ -269,9 +269,10 @@ func (s componentStatusStorage) serversToValidate() map[string]componentstatus.S
|
||||
port = 2379
|
||||
}
|
||||
// TODO: etcd health checking should be abstracted in the storage tier
|
||||
serversToValidate[fmt.Sprintf("etcd-%d", ix)] = componentstatus.Server{
|
||||
serversToValidate[fmt.Sprintf("etcd-%d", ix)] = &componentstatus.Server{
|
||||
Addr: addr,
|
||||
EnableHTTPS: etcdUrl.Scheme == "https",
|
||||
TLSConfig: machine.TLSConfig,
|
||||
Port: port,
|
||||
Path: "/health",
|
||||
Validate: etcdutil.EtcdHealthCheck,
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"testing"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime/schema"
|
||||
"k8s.io/apiserver/pkg/server/storage"
|
||||
"k8s.io/apiserver/pkg/storage/storagebackend"
|
||||
)
|
||||
|
||||
@ -47,6 +48,6 @@ func (f fakeStorageFactory) ResourcePrefix(groupResource schema.GroupResource) s
|
||||
return ""
|
||||
}
|
||||
|
||||
func (f fakeStorageFactory) Backends() []string {
|
||||
return []string{"etcd-0"}
|
||||
func (f fakeStorageFactory) Backends() []storage.Backend {
|
||||
return []storage.Backend{{Server: "etcd-0"}}
|
||||
}
|
||||
|
@ -17,6 +17,9 @@ limitations under the License.
|
||||
package storage
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"github.com/golang/glog"
|
||||
@ -27,6 +30,15 @@ import (
|
||||
"k8s.io/apiserver/pkg/storage/storagebackend"
|
||||
)
|
||||
|
||||
// Backend describes the storage servers, the information here should be enough
|
||||
// for health validations.
|
||||
type Backend struct {
|
||||
// the url of storage backend like: https://etcd.domain:2379
|
||||
Server string
|
||||
// the required tls config
|
||||
TLSConfig *tls.Config
|
||||
}
|
||||
|
||||
// StorageFactory is the interface to locate the storage for a given GroupResource
|
||||
type StorageFactory interface {
|
||||
// New finds the storage destination for the given group and resource. It will
|
||||
@ -40,7 +52,7 @@ type StorageFactory interface {
|
||||
|
||||
// Backends gets all backends for all registered storage destinations.
|
||||
// Used for getting all instances for health validations.
|
||||
Backends() []string
|
||||
Backends() []Backend
|
||||
}
|
||||
|
||||
// DefaultStorageFactory takes a GroupResource and returns back its storage interface. This result includes:
|
||||
@ -252,15 +264,45 @@ func (s *DefaultStorageFactory) NewConfig(groupResource schema.GroupResource) (*
|
||||
return &storageConfig, nil
|
||||
}
|
||||
|
||||
// Get all backends for all registered storage destinations.
|
||||
// Backends returns all backends for all registered storage destinations.
|
||||
// Used for getting all instances for health validations.
|
||||
func (s *DefaultStorageFactory) Backends() []string {
|
||||
backends := sets.NewString(s.StorageConfig.ServerList...)
|
||||
func (s *DefaultStorageFactory) Backends() []Backend {
|
||||
servers := sets.NewString(s.StorageConfig.ServerList...)
|
||||
|
||||
for _, overrides := range s.Overrides {
|
||||
backends.Insert(overrides.etcdLocation...)
|
||||
servers.Insert(overrides.etcdLocation...)
|
||||
}
|
||||
return backends.List()
|
||||
|
||||
tlsConfig := &tls.Config{
|
||||
InsecureSkipVerify: true,
|
||||
}
|
||||
if len(s.StorageConfig.CertFile) > 0 && len(s.StorageConfig.KeyFile) > 0 {
|
||||
cert, err := tls.LoadX509KeyPair(s.StorageConfig.CertFile, s.StorageConfig.KeyFile)
|
||||
if err != nil {
|
||||
glog.Errorf("failed to load key pair while getting backends: %s", err)
|
||||
} else {
|
||||
tlsConfig.Certificates = []tls.Certificate{cert}
|
||||
}
|
||||
}
|
||||
if len(s.StorageConfig.CAFile) > 0 {
|
||||
if caCert, err := ioutil.ReadFile(s.StorageConfig.CAFile); err != nil {
|
||||
glog.Errorf("failed to read ca file while getting backends: %s", err)
|
||||
} else {
|
||||
caPool := x509.NewCertPool()
|
||||
caPool.AppendCertsFromPEM(caCert)
|
||||
tlsConfig.RootCAs = caPool
|
||||
tlsConfig.InsecureSkipVerify = false
|
||||
}
|
||||
}
|
||||
|
||||
backends := []Backend{}
|
||||
for server := range servers {
|
||||
backends = append(backends, Backend{
|
||||
Server: server,
|
||||
TLSConfig: tlsConfig,
|
||||
})
|
||||
}
|
||||
return backends
|
||||
}
|
||||
|
||||
func (s *DefaultStorageFactory) ResourcePrefix(groupResource schema.GroupResource) string {
|
||||
|
Loading…
Reference in New Issue
Block a user