diff --git a/pkg/cluster/router.go b/pkg/cluster/router.go index 4fe2694a72..39dc5e2164 100644 --- a/pkg/cluster/router.go +++ b/pkg/cluster/router.go @@ -1,7 +1,10 @@ package cluster import ( + "fmt" "net/http" + + "github.com/k3s-io/k3s/pkg/util" ) // getHandler returns a basic request handler that processes requests through @@ -19,11 +22,10 @@ func (c *Cluster) getHandler(handler http.Handler) (http.Handler, error) { // if no additional handlers are available. func (c *Cluster) router() http.Handler { return http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) { - if c.config.Runtime.Handler == nil { - http.Error(rw, "starting", http.StatusServiceUnavailable) - return + if c.config.Runtime.Handler != nil { + c.config.Runtime.Handler.ServeHTTP(rw, req) + } else { + util.SendError(fmt.Errorf("starting"), rw, req, http.StatusServiceUnavailable) } - - c.config.Runtime.Handler.ServeHTTP(rw, req) }) } diff --git a/pkg/daemons/control/tunnel.go b/pkg/daemons/control/tunnel.go index 3c4b2d54ce..86c685318b 100644 --- a/pkg/daemons/control/tunnel.go +++ b/pkg/daemons/control/tunnel.go @@ -29,8 +29,7 @@ var defaultDialer = net.Dialer{} func loggingErrorWriter(rw http.ResponseWriter, req *http.Request, code int, err error) { logrus.Debugf("Tunnel server error: %d %v", code, err) - rw.WriteHeader(code) - rw.Write([]byte(err.Error())) + util.SendError(err, rw, req, code) } func setupTunnel(ctx context.Context, cfg *config.Control) (http.Handler, error) { diff --git a/pkg/etcd/etcd.go b/pkg/etcd/etcd.go index c34f1396ff..d92ae900d8 100644 --- a/pkg/etcd/etcd.go +++ b/pkg/etcd/etcd.go @@ -761,7 +761,7 @@ func getEndpoints(control *config.Control) []string { // for use by etcd. func toTLSConfig(runtime *config.ControlRuntime) (*tls.Config, error) { if runtime.ClientETCDCert == "" || runtime.ClientETCDKey == "" || runtime.ETCDServerCA == "" { - return nil, errors.New("runtime is not ready yet") + return nil, util.ErrCoreNotReady } clientCert, err := tls.LoadX509KeyPair(runtime.ClientETCDCert, runtime.ClientETCDKey) @@ -1177,7 +1177,7 @@ func (e *ETCD) manageLearners(ctx context.Context) { func (e *ETCD) getETCDNodes() ([]*v1.Node, error) { if e.config.Runtime.Core == nil { - return nil, errors.New("runtime core not ready") + return nil, util.ErrCoreNotReady } nodes := e.config.Runtime.Core.Core().V1().Node() diff --git a/pkg/server/cert.go b/pkg/server/cert.go index b7c4d2f9ae..0fcdf0efe3 100644 --- a/pkg/server/cert.go +++ b/pkg/server/cert.go @@ -30,8 +30,8 @@ import ( func caCertReplaceHandler(server *config.Control) http.HandlerFunc { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { - if req.TLS == nil || req.Method != http.MethodPut { - resp.WriteHeader(http.StatusNotFound) + if req.Method != http.MethodPut { + util.SendError(fmt.Errorf("method not allowed"), resp, req, http.StatusMethodNotAllowed) return } force, _ := strconv.ParseBool(req.FormValue("force")) diff --git a/pkg/server/router.go b/pkg/server/router.go index d2dba1f878..98ed472963 100644 --- a/pkg/server/router.go +++ b/pkg/server/router.go @@ -200,11 +200,6 @@ func getCACertAndKeys(caCertFile, caKeyFile, signingKeyFile string) ([]*x509.Cer func servingKubeletCert(server *config.Control, keyFile string, auth nodePassBootstrapper) http.Handler { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { - if req.TLS == nil { - resp.WriteHeader(http.StatusNotFound) - return - } - nodeName, errCode, err := auth(req) if err != nil { util.SendError(err, resp, req, errCode) @@ -256,11 +251,6 @@ func servingKubeletCert(server *config.Control, keyFile string, auth nodePassBoo func clientKubeletCert(server *config.Control, keyFile string, auth nodePassBootstrapper) http.Handler { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { - if req.TLS == nil { - resp.WriteHeader(http.StatusNotFound) - return - } - nodeName, errCode, err := auth(req) if err != nil { util.SendError(err, resp, req, errCode) @@ -296,10 +286,6 @@ func clientKubeletCert(server *config.Control, keyFile string, auth nodePassBoot func fileHandler(fileName ...string) http.Handler { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { - if req.TLS == nil { - resp.WriteHeader(http.StatusNotFound) - return - } resp.Header().Set("Content-Type", "text/plain") if len(fileName) == 1 { @@ -310,8 +296,7 @@ func fileHandler(fileName ...string) http.Handler { for _, f := range fileName { bytes, err := os.ReadFile(f) if err != nil { - logrus.Errorf("Failed to read %s: %v", f, err) - resp.WriteHeader(http.StatusInternalServerError) + util.SendError(errors.Wrapf(err, "failed to read %s", f), resp, req, http.StatusInternalServerError) return } resp.Write(bytes) @@ -336,18 +321,13 @@ func apiserversHandler(server *config.Control) http.Handler { resp.Header().Set("content-type", "application/json") if err := json.NewEncoder(resp).Encode(endpoints); err != nil { - logrus.Errorf("Failed to encode apiserver endpoints: %v", err) - resp.WriteHeader(http.StatusInternalServerError) + util.SendError(errors.Wrap(err, "failed to encode apiserver endpoints"), resp, req, http.StatusInternalServerError) } }) } func configHandler(server *config.Control, cfg *cmds.Server) http.Handler { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { - if req.TLS == nil { - resp.WriteHeader(http.StatusNotFound) - return - } // Startup hooks may read and modify cmds.Server in a goroutine, but as these are copied into // config.Control before the startup hooks are called, any modifications need to be sync'd back // into the struct before it is sent to agents. @@ -355,23 +335,21 @@ func configHandler(server *config.Control, cfg *cmds.Server) http.Handler { server.DisableKubeProxy = cfg.DisableKubeProxy resp.Header().Set("content-type", "application/json") if err := json.NewEncoder(resp).Encode(server); err != nil { - logrus.Errorf("Failed to encode agent config: %v", err) - resp.WriteHeader(http.StatusInternalServerError) + util.SendError(errors.Wrap(err, "failed to encode agent config"), resp, req, http.StatusInternalServerError) } }) } func readyzHandler(server *config.Control) http.Handler { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { - code := http.StatusOK - data := []byte("ok") if server.Runtime.Core == nil { - code = http.StatusInternalServerError - data = []byte("runtime core not ready") + util.SendError(util.ErrCoreNotReady, resp, req, http.StatusServiceUnavailable) + return } - resp.WriteHeader(code) + data := []byte("ok") + resp.WriteHeader(http.StatusOK) resp.Header().Set("Content-Type", "text/plain") - resp.Header().Set("Content-length", strconv.Itoa(len(data))) + resp.Header().Set("Content-Length", strconv.Itoa(len(data))) resp.Write(data) }) } @@ -379,6 +357,7 @@ func readyzHandler(server *config.Control) http.Handler { func ping() http.Handler { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { data := []byte("pong") + resp.WriteHeader(http.StatusOK) resp.Header().Set("Content-Type", "text/plain") resp.Header().Set("Content-Length", strconv.Itoa(len(data))) resp.Write(data) @@ -432,7 +411,7 @@ func passwordBootstrap(ctx context.Context, config *Config) nodePassBootstrapper return verifyRemotePassword(ctx, config, &mu, deferredNodes, node) } else { // Otherwise, reject the request until the core is ready. - return "", http.StatusServiceUnavailable, errors.New("runtime core not ready") + return "", http.StatusServiceUnavailable, util.ErrCoreNotReady } } diff --git a/pkg/server/secrets-encrypt.go b/pkg/server/secrets-encrypt.go index ebf1a4c1d0..3172ae8970 100644 --- a/pkg/server/secrets-encrypt.go +++ b/pkg/server/secrets-encrypt.go @@ -56,10 +56,6 @@ func getEncryptionRequest(req *http.Request) (*EncryptionRequest, error) { func encryptionStatusHandler(server *config.Control) http.Handler { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { - if req.TLS == nil { - resp.WriteHeader(http.StatusNotFound) - return - } status, err := encryptionStatus(server) if err != nil { util.SendErrorWithID(err, "secret-encrypt", resp, req, http.StatusInternalServerError) @@ -160,18 +156,13 @@ func encryptionEnable(ctx context.Context, server *config.Control, enable bool) func encryptionConfigHandler(ctx context.Context, server *config.Control) http.Handler { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { - if req.TLS == nil { - resp.WriteHeader(http.StatusNotFound) - return - } if req.Method != http.MethodPut { - resp.WriteHeader(http.StatusBadRequest) + util.SendError(fmt.Errorf("method not allowed"), resp, req, http.StatusMethodNotAllowed) return } encryptReq, err := getEncryptionRequest(req) if err != nil { - resp.WriteHeader(http.StatusBadRequest) - resp.Write([]byte(err.Error())) + util.SendError(err, resp, req, http.StatusBadRequest) return } if encryptReq.Stage != nil { diff --git a/pkg/server/token.go b/pkg/server/token.go index c5da332fa6..efd095013f 100644 --- a/pkg/server/token.go +++ b/pkg/server/token.go @@ -32,16 +32,15 @@ func getServerTokenRequest(req *http.Request) (TokenRotateRequest, error) { func tokenRequestHandler(ctx context.Context, server *config.Control) http.Handler { return http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) { - if req.TLS == nil || req.Method != http.MethodPut { - resp.WriteHeader(http.StatusBadRequest) + if req.Method != http.MethodPut { + util.SendError(fmt.Errorf("method not allowed"), resp, req, http.StatusMethodNotAllowed) return } var err error sTokenReq, err := getServerTokenRequest(req) logrus.Debug("Received token request") if err != nil { - resp.WriteHeader(http.StatusBadRequest) - resp.Write([]byte(err.Error())) + util.SendError(err, resp, req, http.StatusBadRequest) return } if err = tokenRotate(ctx, server, *sTokenReq.NewToken); err != nil { diff --git a/pkg/spegel/bootstrap.go b/pkg/spegel/bootstrap.go index e50dd25551..3263bc65c6 100644 --- a/pkg/spegel/bootstrap.go +++ b/pkg/spegel/bootstrap.go @@ -10,6 +10,7 @@ import ( "github.com/k3s-io/k3s/pkg/clientaccess" "github.com/k3s-io/k3s/pkg/daemons/config" + "github.com/k3s-io/k3s/pkg/util" "github.com/k3s-io/k3s/pkg/version" "github.com/libp2p/go-libp2p/core/peer" "github.com/pkg/errors" @@ -133,7 +134,7 @@ func (s *serverBootstrapper) Run(_ context.Context, id string) error { func (s *serverBootstrapper) Get() (addrInfo *peer.AddrInfo, err error) { if s.controlConfig.Runtime.Core == nil { - return nil, errors.New("runtime core not ready") + return nil, util.ErrCoreNotReady } nodeName := os.Getenv("NODE_NAME") if nodeName == "" { diff --git a/pkg/util/apierrors.go b/pkg/util/apierrors.go index 2edca11139..ec61ecea54 100644 --- a/pkg/util/apierrors.go +++ b/pkg/util/apierrors.go @@ -17,6 +17,7 @@ import ( var ErrAPINotReady = errors.New("apiserver not ready") var ErrAPIDisabled = errors.New("apiserver disabled") +var ErrCoreNotReady = errors.New("runtime core not ready") // SendErrorWithID sends and logs a random error ID so that logs can be correlated // between the REST API (which does not provide any detailed error output, to avoid