package clientcmd
import (
"crypto/x509"
"errors"
"fmt"
"strings"
kerrors "k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
)
const (
unknownReason = iota
noServerFoundReason
certificateAuthorityUnknownReason
certificateHostnameErrorReason
certificateInvalidReason
configurationInvalidReason
tlsOversizedRecordReason
certificateAuthorityUnknownMsg = "The server uses a certificate signed by unknown authority. You may need to use the --certificate-authority flag to provide the path to a certificate file for the certificate authority, or --insecure-skip-tls-verify to bypass the certificate check and use insecure connections."
notConfiguredMsg = `The client is not configured. You need to run the login command in order to create a default config for your server and credentials:
oc login
You can also run this command again providing the path to a config file directly, either through the --config flag of the KUBECONFIG environment variable.
`
tlsOversizedRecordMsg = `Unable to connect to %[2]s using TLS: %[1]s.
Ensure the specified server supports HTTPS.`
)
// GetPrettyMessageFor prettifys the message of the provided error
func GetPrettyMessageFor(err error) string {
return GetPrettyMessageForServer(err, "")
}
// GetPrettyMessageForServer prettifys the message of the provided error
func GetPrettyMessageForServer(err error, serverName string) string {
if err == nil {
return ""
}
reason := detectReason(err)
switch reason {
case noServerFoundReason:
return notConfiguredMsg
case certificateAuthorityUnknownReason:
return certificateAuthorityUnknownMsg
case tlsOversizedRecordReason:
if len(serverName) == 0 {
serverName = "server"
}
return fmt.Sprintf(tlsOversizedRecordMsg, err, serverName)
case certificateHostnameErrorReason:
return fmt.Sprintf("The server is using a certificate that does not match its hostname: %s", err)
case certificateInvalidReason:
return fmt.Sprintf("The server is using an invalid certificate: %s", err)
}
return err.Error()
}
// GetPrettyErrorFor prettifys the message of the provided error
func GetPrettyErrorFor(err error) error {
return GetPrettyErrorForServer(err, "")
}
// GetPrettyErrorForServer prettifys the message of the provided error
func GetPrettyErrorForServer(err error, serverName string) error {
return errors.New(GetPrettyMessageForServer(err, serverName))
}
// IsNoServerFound checks whether the provided error is a 'no server found' error or not
func IsNoServerFound(err error) bool {
return detectReason(err) == noServerFoundReason
}
// IsConfigurationInvalid checks whether the provided error is a 'invalid configuration' error or not
func IsConfigurationInvalid(err error) bool {
return detectReason(err) == configurationInvalidReason
}
// IsCertificateAuthorityUnknown checks whether the provided error is a 'certificate authority unknown' error or not
func IsCertificateAuthorityUnknown(err error) bool {
return detectReason(err) == certificateAuthorityUnknownReason
}
// IsForbidden checks whether the provided error is a 'forbidden' error or not
func IsForbidden(err error) bool {
return kerrors.IsForbidden(err)
}
// IsTLSOversizedRecord checks whether the provided error is a url.Error
// with "tls: oversized record received", which usually means TLS not supported.
func IsTLSOversizedRecord(err error) bool {
return detectReason(err) == tlsOversizedRecordReason
}
// IsCertificateHostnameError checks whether the set of authorized names doesn't match the requested name
func IsCertificateHostnameError(err error) bool {
return detectReason(err) == certificateHostnameErrorReason
}
// IsCertificateInvalid checks whether the certificate is invalid for reasons like expired, CA not authorized
// to sign, there are too many cert intermediates, or the cert usage is not valid for the wanted purpose.
func IsCertificateInvalid(err error) bool {
return detectReason(err) == certificateInvalidReason
}
func detectReason(err error) int {
if err != nil {
switch {
case strings.Contains(err.Error(), "certificate signed by unknown authority"):
return certificateAuthorityUnknownReason
case strings.Contains(err.Error(), "no server defined"):
return noServerFoundReason
case clientcmd.IsConfigurationInvalid(err):
return configurationInvalidReason
case strings.Contains(err.Error(), "tls: oversized record received"):
return tlsOversizedRecordReason
}
switch err.(type) {
case x509.UnknownAuthorityError:
return certificateAuthorityUnknownReason
case x509.HostnameError:
return certificateHostnameErrorReason
case x509.CertificateInvalidError:
return certificateInvalidReason
}
}
return unknownReason
}