package validate
import (
"errors"
"fmt"
"io"
"os"
"text/tabwriter"
"github.com/spf13/cobra"
cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
"k8s.io/kubernetes/pkg/util/validation/field"
configapilatest "github.com/openshift/origin/pkg/cmd/server/api/latest"
"github.com/openshift/origin/pkg/cmd/templates"
"github.com/openshift/origin/pkg/cmd/server/api/validation"
)
const (
ValidateMasterConfigRecommendedName = "master-config"
validateMasterConfigDeprecationMessage = `This command is deprecated and will be removed. Use 'oadm diagnostics MasterConfigCheck --master-config=path/to/config.yaml' instead.`
)
var (
validateMasterConfigLong = templates.LongDesc(`
Validate the configuration file for a master server.
This command validates that a configuration file intended to be used for a master server is valid.`)
validateMasterConfigExample = templates.Examples(`
# Validate master server configuration file
%s openshift.local.config/master/master-config.yaml`)
)
type ValidateMasterConfigOptions struct {
// MasterConfigFile is the location of the config file to be validated
MasterConfigFile string
// Out is the writer to write output to
Out io.Writer
}
// NewCommandValidateMasterConfig provides a CLI handler for the `validate all-in-one` command
func NewCommandValidateMasterConfig(name, fullName string, out io.Writer) *cobra.Command {
options := &ValidateMasterConfigOptions{
Out: out,
}
cmd := &cobra.Command{
Use: fmt.Sprintf("%s SOURCE", name),
Short: "Validate the configuration file for a master server",
Long: validateMasterConfigLong,
Example: fmt.Sprintf(validateMasterConfigExample, fullName),
Deprecated: validateMasterConfigDeprecationMessage,
Run: func(c *cobra.Command, args []string) {
if err := options.Complete(args); err != nil {
cmdutil.CheckErr(cmdutil.UsageError(c, err.Error()))
}
ok, err := options.Run()
cmdutil.CheckErr(err)
if !ok {
fmt.Fprintf(options.Out, "FAILURE: Validation failed for file: %s\n", options.MasterConfigFile)
os.Exit(1)
}
fmt.Fprintf(options.Out, "SUCCESS: Validation succeeded for file: %s\n", options.MasterConfigFile)
},
}
return cmd
}
func (o *ValidateMasterConfigOptions) Complete(args []string) error {
if len(args) != 1 {
return errors.New("exactly one source file is required")
}
o.MasterConfigFile = args[0]
return nil
}
// Run runs the master config validation and returns the result of the validation as a boolean as well as any errors
// that occurred trying to validate the file
func (o *ValidateMasterConfigOptions) Run() (bool, error) {
masterConfig, err := configapilatest.ReadAndResolveMasterConfig(o.MasterConfigFile)
if err != nil {
return true, err
}
results := validation.ValidateMasterConfig(masterConfig, nil)
writer := tabwriter.NewWriter(o.Out, minColumnWidth, tabWidth, padding, padchar, flags)
err = prettyPrintValidationResults(results, writer)
if err != nil {
return len(results.Errors) == 0, fmt.Errorf("could not print results: %v", err)
}
writer.Flush()
return len(results.Errors) == 0, nil
}
const (
minColumnWidth = 4
tabWidth = 4
padding = 2
padchar = byte(' ')
flags = 0
validationErrorHeadings = "ERROR\tFIELD\tVALUE\tDETAILS\n"
validationWarningHeadings = "WARNING\tFIELD\tVALUE\tDETAILS\n"
)
// prettyPrintValidationResults prints the contents of the ValidationResults into the buffer of a tabwriter.Writer.
// The writer must be Flush()ed after calling this to write the buffered data.
func prettyPrintValidationResults(results validation.ValidationResults, writer *tabwriter.Writer) error {
if len(results.Errors) > 0 {
fmt.Fprintf(writer, "VALIDATION ERRORS:\t\t\t\n")
err := prettyPrintValidationErrorList(validationErrorHeadings, results.Errors, writer)
if err != nil {
return err
}
}
if len(results.Warnings) > 0 {
fmt.Fprintf(writer, "VALIDATION WARNINGS:\t\t\t\n")
err := prettyPrintValidationErrorList(validationWarningHeadings, results.Warnings, writer)
if err != nil {
return err
}
}
return nil
}
// prettyPrintValidationErrorList prints the contents of the ValidationErrorList into the buffer of a tabwriter.Writer.
// The writer must be Flush()ed after calling this to write the buffered data.
func prettyPrintValidationErrorList(headings string, validationErrors field.ErrorList, writer *tabwriter.Writer) error {
if len(validationErrors) > 0 {
fmt.Fprintf(writer, headings)
for _, err := range validationErrors {
err := prettyPrintValidationError(err, writer)
if err != nil {
return err
}
}
}
return nil
}
// prettyPrintValidationError prints the contents of the ValidationError into the buffer of a tabwriter.Writer.
// The writer must be Flush()ed after calling this to write the buffered data.
func prettyPrintValidationError(validationError *field.Error, writer *tabwriter.Writer) error {
_, printError := fmt.Fprintf(writer, "%s\t%s\t%s\t%s\n",
toString(validationError.Type),
validationError.Field,
toString(validationError.BadValue),
validationError.Detail)
return printError
}
const missingValue = "<none>"
func toString(v interface{}) string {
value := fmt.Sprintf("%v", v)
if len(value) == 0 {
value = missingValue
}
return value
}
// prettyPrintGenericError prints the contents of the generic error into the buffer of a tabwriter.Writer.
// The writer must be Flush()ed after calling this to write the buffered data.
func prettyPrintGenericError(err error, writer *tabwriter.Writer) error {
_, printError := fmt.Fprintf(writer, "\t\t\t%s\n", err.Error())
return printError
}