2015-01-07 20:59:22 +00:00
/ *
Copyright 2014 Google Inc . All rights reserved .
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 clientcmd
import (
"errors"
"fmt"
"os"
"strings"
2014-12-17 13:03:03 +00:00
clientcmdapi "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd/api"
2015-01-07 20:59:22 +00:00
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
2015-01-09 06:10:03 +00:00
utilerrors "github.com/GoogleCloudPlatform/kubernetes/pkg/util/errors"
2015-01-07 20:59:22 +00:00
)
var ErrNoContext = errors . New ( "no context chosen" )
type errContextNotFound struct {
ContextName string
}
func ( e * errContextNotFound ) Error ( ) string {
return fmt . Sprintf ( "context was not found for specified context: %v" , e . ContextName )
}
// IsContextNotFound returns a boolean indicating whether the error is known to
// report that a context was not found
func IsContextNotFound ( err error ) bool {
if err == nil {
return false
}
return strings . Contains ( err . Error ( ) , "context was not found for specified context" )
}
// Validate checks for errors in the Config. It does not return early so that it can find as many errors as possible.
2014-12-17 13:03:03 +00:00
func Validate ( config clientcmdapi . Config ) error {
2015-01-07 20:59:22 +00:00
validationErrors := make ( [ ] error , 0 )
if len ( config . CurrentContext ) != 0 {
if _ , exists := config . Contexts [ config . CurrentContext ] ; ! exists {
validationErrors = append ( validationErrors , & errContextNotFound { config . CurrentContext } )
}
}
for contextName , context := range config . Contexts {
validationErrors = append ( validationErrors , validateContext ( contextName , context , config ) ... )
}
for authInfoName , authInfo := range config . AuthInfos {
validationErrors = append ( validationErrors , validateAuthInfo ( authInfoName , authInfo ) ... )
}
for clusterName , clusterInfo := range config . Clusters {
validationErrors = append ( validationErrors , validateClusterInfo ( clusterName , clusterInfo ) ... )
}
2015-01-09 06:10:03 +00:00
return utilerrors . NewAggregate ( validationErrors )
2015-01-07 20:59:22 +00:00
}
// ConfirmUsable looks a particular context and determines if that particular part of the config is useable. There might still be errors in the config,
// but no errors in the sections requested or referenced. It does not return early so that it can find as many errors as possible.
2014-12-17 13:03:03 +00:00
func ConfirmUsable ( config clientcmdapi . Config , passedContextName string ) error {
2015-01-07 20:59:22 +00:00
validationErrors := make ( [ ] error , 0 )
var contextName string
if len ( passedContextName ) != 0 {
contextName = passedContextName
} else {
contextName = config . CurrentContext
}
if len ( contextName ) == 0 {
return ErrNoContext
}
context , exists := config . Contexts [ contextName ]
if ! exists {
validationErrors = append ( validationErrors , & errContextNotFound { contextName } )
}
if exists {
validationErrors = append ( validationErrors , validateContext ( contextName , context , config ) ... )
validationErrors = append ( validationErrors , validateAuthInfo ( context . AuthInfo , config . AuthInfos [ context . AuthInfo ] ) ... )
validationErrors = append ( validationErrors , validateClusterInfo ( context . Cluster , config . Clusters [ context . Cluster ] ) ... )
}
2015-01-09 06:10:03 +00:00
return utilerrors . NewAggregate ( validationErrors )
2015-01-07 20:59:22 +00:00
}
// validateClusterInfo looks for conflicts and errors in the cluster info
2014-12-17 13:03:03 +00:00
func validateClusterInfo ( clusterName string , clusterInfo clientcmdapi . Cluster ) [ ] error {
2015-01-07 20:59:22 +00:00
validationErrors := make ( [ ] error , 0 )
if len ( clusterInfo . Server ) == 0 {
validationErrors = append ( validationErrors , fmt . Errorf ( "no server found for %v" , clusterName ) )
}
2015-02-18 02:37:43 +00:00
// Make sure CA data and CA file aren't both specified
if len ( clusterInfo . CertificateAuthority ) != 0 && len ( clusterInfo . CertificateAuthorityData ) != 0 {
validationErrors = append ( validationErrors , fmt . Errorf ( "certificate-authority-data and certificate-authority are both specified for %v. certificate-authority-data will override." , clusterName ) )
}
2015-01-07 20:59:22 +00:00
if len ( clusterInfo . CertificateAuthority ) != 0 {
clientCertCA , err := os . Open ( clusterInfo . CertificateAuthority )
defer clientCertCA . Close ( )
if err != nil {
validationErrors = append ( validationErrors , fmt . Errorf ( "unable to read certificate-authority %v for %v due to %v" , clusterInfo . CertificateAuthority , clusterName , err ) )
}
}
return validationErrors
}
// validateAuthInfo looks for conflicts and errors in the auth info
2014-12-17 13:03:03 +00:00
func validateAuthInfo ( authInfoName string , authInfo clientcmdapi . AuthInfo ) [ ] error {
2015-01-07 20:59:22 +00:00
validationErrors := make ( [ ] error , 0 )
2015-01-08 13:40:02 +00:00
usingAuthPath := false
2015-01-07 20:59:22 +00:00
methods := make ( [ ] string , 0 , 3 )
if len ( authInfo . Token ) != 0 {
methods = append ( methods , "token" )
}
2015-02-18 02:37:43 +00:00
if len ( authInfo . Username ) != 0 || len ( authInfo . Password ) != 0 {
methods = append ( methods , "basicAuth" )
}
2015-01-07 20:59:22 +00:00
if len ( authInfo . AuthPath ) != 0 {
2015-01-08 13:40:02 +00:00
usingAuthPath = true
2015-01-07 20:59:22 +00:00
methods = append ( methods , "authFile" )
file , err := os . Open ( authInfo . AuthPath )
os . IsNotExist ( err )
defer file . Close ( )
if err != nil {
validationErrors = append ( validationErrors , fmt . Errorf ( "unable to read auth-path %v for %v due to %v" , authInfo . AuthPath , authInfoName , err ) )
}
}
2015-02-18 02:37:43 +00:00
if len ( authInfo . ClientCertificate ) != 0 || len ( authInfo . ClientCertificateData ) != 0 {
// Make sure cert data and file aren't both specified
if len ( authInfo . ClientCertificate ) != 0 && len ( authInfo . ClientCertificateData ) != 0 {
validationErrors = append ( validationErrors , fmt . Errorf ( "client-cert-data and client-cert are both specified for %v. client-cert-data will override." , authInfoName ) )
2015-01-07 20:59:22 +00:00
}
2015-02-18 02:37:43 +00:00
// Make sure key data and file aren't both specified
if len ( authInfo . ClientKey ) != 0 && len ( authInfo . ClientKeyData ) != 0 {
validationErrors = append ( validationErrors , fmt . Errorf ( "client-key-data and client-key are both specified for %v. client-key-data will override." , authInfoName ) )
}
// Make sure a key is specified
if len ( authInfo . ClientKey ) == 0 && len ( authInfo . ClientKeyData ) == 0 {
validationErrors = append ( validationErrors , fmt . Errorf ( "client-key-data or client-key must be specified for %v to use the clientCert authentication method." , authInfoName ) )
}
if len ( authInfo . ClientCertificate ) != 0 {
clientCertFile , err := os . Open ( authInfo . ClientCertificate )
defer clientCertFile . Close ( )
if err != nil {
validationErrors = append ( validationErrors , fmt . Errorf ( "unable to read client-cert %v for %v due to %v" , authInfo . ClientCertificate , authInfoName , err ) )
}
}
if len ( authInfo . ClientKey ) != 0 {
clientKeyFile , err := os . Open ( authInfo . ClientKey )
defer clientKeyFile . Close ( )
if err != nil {
validationErrors = append ( validationErrors , fmt . Errorf ( "unable to read client-key %v for %v due to %v" , authInfo . ClientKey , authInfoName , err ) )
}
2015-01-07 20:59:22 +00:00
}
}
2015-01-08 13:40:02 +00:00
// authPath also provides information for the client to identify the server, so allow multiple auth methods in that case
if ( len ( methods ) > 1 ) && ( ! usingAuthPath ) {
2015-01-07 20:59:22 +00:00
validationErrors = append ( validationErrors , fmt . Errorf ( "more than one authentication method found for %v. Found %v, only one is allowed" , authInfoName , methods ) )
}
return validationErrors
}
// validateContext looks for errors in the context. It is not transitive, so errors in the reference authInfo or cluster configs are not included in this return
2014-12-17 13:03:03 +00:00
func validateContext ( contextName string , context clientcmdapi . Context , config clientcmdapi . Config ) [ ] error {
2015-01-07 20:59:22 +00:00
validationErrors := make ( [ ] error , 0 )
if len ( context . AuthInfo ) == 0 {
validationErrors = append ( validationErrors , fmt . Errorf ( "user was not specified for Context %v" , contextName ) )
} else if _ , exists := config . AuthInfos [ context . AuthInfo ] ; ! exists {
validationErrors = append ( validationErrors , fmt . Errorf ( "user, %v, was not found for Context %v" , context . AuthInfo , contextName ) )
}
if len ( context . Cluster ) == 0 {
validationErrors = append ( validationErrors , fmt . Errorf ( "cluster was not specified for Context %v" , contextName ) )
} else if _ , exists := config . Clusters [ context . Cluster ] ; ! exists {
validationErrors = append ( validationErrors , fmt . Errorf ( "cluster, %v, was not found for Context %v" , context . Cluster , contextName ) )
}
if ( len ( context . Namespace ) != 0 ) && ! util . IsDNS952Label ( context . Namespace ) {
2015-01-18 07:32:34 +00:00
validationErrors = append ( validationErrors , fmt . Errorf ( "namespace, %v, for context %v, does not conform to the kubernetes DNS952 rules" , context . Namespace , contextName ) )
2015-01-07 20:59:22 +00:00
}
return validationErrors
}