package client
import (
"fmt"
"io/ioutil"
"os"
flag "github.com/spf13/pflag"
"k8s.io/kubernetes/pkg/client/unversioned/clientcmd"
"github.com/openshift/origin/pkg/cmd/cli/config"
"github.com/openshift/origin/pkg/diagnostics/types"
"github.com/openshift/origin/pkg/diagnostics/util"
)
// ConfigLoading is a little special in that it is run separately as a precondition
// in order to determine whether we can run other dependent diagnostics.
type ConfigLoading struct {
ConfFlagName string
ClientFlags *flag.FlagSet
successfulLoad bool // set if at least one file loaded
}
// Name is part of the Diagnostic interface and just returns name.
func (d *ConfigLoading) Name() string {
return "ConfigLoading"
}
// Description is part of the Diagnostic interface and provides a user-focused description of what the diagnostic does.
func (d *ConfigLoading) Description() string {
return "Try to load client config file(s) and report what happens"
}
// CanRun is part of the Diagnostic interface; it determines if the conditions are right to run this diagnostic.
func (d *ConfigLoading) CanRun() (bool, error) {
return true, nil
}
// SuccessfulLoad returns whether the client config was found
func (d *ConfigLoading) SuccessfulLoad() bool {
return d.successfulLoad
}
// Check is part of the Diagnostic interface; it runs the actual diagnostic logic
func (d *ConfigLoading) Check() types.DiagnosticResult {
r := types.NewDiagnosticResult("ConfigLoading")
confFlagValue := d.ClientFlags.Lookup(d.ConfFlagName).Value.String()
var foundPath string
rules := config.NewOpenShiftClientConfigLoadingRules()
paths := append([]string{confFlagValue}, rules.Precedence...)
for index, path := range paths {
errmsg := ""
switch index {
case 0:
errmsg = fmt.Sprintf("--%s specified that client config should be at %s\n", d.ConfFlagName, path)
case len(paths) - 1: // config in ~/.kube
// no error message indicated if it is not there... user didn't say it would be
default: // can be multiple paths from the env var in theory; all cases should go here
if len(os.Getenv(config.OpenShiftConfigPathEnvVar)) != 0 {
errmsg = fmt.Sprintf("Env var %s specified that client config could be at %s\n", config.OpenShiftConfigPathEnvVar, path)
}
}
if d.canOpenConfigFile(path, errmsg, r) && foundPath == "" {
d.successfulLoad = true
foundPath = path
}
}
if foundPath != "" {
if confFlagValue != "" && confFlagValue != foundPath {
// found config but not where --config said
r.Error("DCli1001", nil, fmt.Sprintf(`
The client configuration file was not found where the --%s flag indicated:
%s
A config file was found at the following location:
%s
If you wish to use this file for client configuration, you can specify it
with the --%[1]s flag, or just not specify the flag.
`, d.ConfFlagName, confFlagValue, foundPath))
}
} else { // not found, check for master-generated ones to recommend
if confFlagValue != "" {
r.Error("DCli1002", nil, fmt.Sprintf("Did not find config file where --%s=%s indicated", d.ConfFlagName, confFlagValue))
}
adminWarningF := `
No client config file was available; however, one exists at
%[2]s
which may have been generated automatically by the master.
If you want to use this config, you should copy it to the
standard location (%[3]s),
or you can set the environment variable %[1]s:
export %[1]s=%[2]s
If not, obtain a config file and place it in the standard
location for use by the client and diagnostics.
`
// look for it in auto-generated locations when not found properly
for _, path := range util.AdminKubeConfigPaths {
msg := fmt.Sprintf("Looking for a possible client config at %s\n", path)
if d.canOpenConfigFile(path, msg, r) {
r.Warn("DCli1003", nil, fmt.Sprintf(adminWarningF, config.OpenShiftConfigPathEnvVar, path, config.RecommendedHomeFile))
break
}
}
}
return r
}
// ----------------------------------------------------------
// Attempt to open file at path as client config
// If there is a problem and errmsg is set, log an error
func (d ConfigLoading) canOpenConfigFile(path string, errmsg string, r types.DiagnosticResult) bool {
var file *os.File
var err error
if path == "" { // empty param/envvar
return false
} else if file, err = os.Open(path); err == nil {
r.Debug("DCli1004", fmt.Sprintf("Reading client config at %s", path))
} else if errmsg == "" {
r.Debug("DCli1005", fmt.Sprintf("Could not read client config at %s:\n%#v", path, err))
} else if os.IsNotExist(err) {
r.Debug("DCli1006", errmsg+"but that file does not exist.")
} else if os.IsPermission(err) {
r.Error("DCli1007", err, errmsg+"but lack permission to read that file.")
} else {
r.Error("DCli1008", err, fmt.Sprintf("%sbut there was an error opening it:\n%#v", errmsg, err))
}
if file != nil { // it is open for reading
defer file.Close()
if buffer, err := ioutil.ReadAll(file); err != nil {
r.Error("DCli1009", err, fmt.Sprintf("Unexpected error while reading client config file (%s): %v", path, err))
} else if _, err := clientcmd.Load(buffer); err != nil {
r.Error("DCli1010", err, fmt.Sprintf(`
Error reading YAML from client config file (%s):
%v
This file may have been truncated or mis-edited.
Please fix, remove, or obtain a new client config`, file.Name(), err))
} else {
r.Info("DCli1011", fmt.Sprintf("Successfully read a client config file at '%s'", path))
/* Note, we're not going to use this config file directly.
* Instead, we'll defer to the openshift client code to assimilate
* flags, env vars, and the potential hierarchy of config files
* into an actual configuration that the client uses.
* However, for diagnostic purposes, record the files we find.
*/
return true
}
}
return false
}