github.com/jlmeeker/kismatic@v1.10.1-0.20180612190640-57f9005a1f1a/pkg/cli/validate.go (about) 1 package cli 2 3 import ( 4 "fmt" 5 "io" 6 "path/filepath" 7 8 "os" 9 10 "github.com/apprenda/kismatic/pkg/install" 11 "github.com/apprenda/kismatic/pkg/util" 12 "github.com/spf13/cobra" 13 ) 14 15 type validateOpts struct { 16 generatedAssetsDir string 17 planFile string 18 verbose bool 19 outputFormat string 20 skipPreFlight bool 21 limit []string 22 } 23 24 // NewCmdValidate creates a new install validate command 25 func NewCmdValidate(out io.Writer, installOpts *installOpts) *cobra.Command { 26 opts := &validateOpts{} 27 cmd := &cobra.Command{ 28 Use: "validate", 29 Short: "validate your plan file", 30 RunE: func(cmd *cobra.Command, args []string) error { 31 if len(args) != 0 { 32 return fmt.Errorf("Unexpected args: %v", args) 33 } 34 planner := &install.FilePlanner{File: installOpts.planFilename} 35 opts.planFile = installOpts.planFilename 36 return doValidate(out, planner, opts) 37 }, 38 } 39 cmd.Flags().StringSliceVar(&opts.limit, "limit", []string{}, "comma-separated list of hostnames to limit the execution to a subset of nodes") 40 cmd.Flags().StringVar(&opts.generatedAssetsDir, "generated-assets-dir", "generated", "path to the directory where assets generated during the installation process will be stored") 41 cmd.Flags().BoolVar(&opts.verbose, "verbose", false, "enable verbose logging from the installation") 42 cmd.Flags().StringVarP(&opts.outputFormat, "output", "o", "simple", "installation output format (options simple|raw)") 43 cmd.Flags().BoolVar(&opts.skipPreFlight, "skip-preflight", false, "skip pre-flight checks") 44 return cmd 45 } 46 47 func doValidate(out io.Writer, planner install.Planner, opts *validateOpts) error { 48 util.PrintHeader(out, "Validating", '=') 49 // Check if plan file exists 50 if !planner.PlanExists() { 51 util.PrettyPrintErr(out, "Reading installation plan file [ERROR]") 52 fmt.Fprintln(out, "Run \"kismatic install plan\" to generate it") 53 return fmt.Errorf("plan does not exist") 54 } 55 plan, err := planner.Read() 56 if err != nil { 57 util.PrettyPrintErr(out, "Reading installation plan file %q", opts.planFile) 58 return fmt.Errorf("error reading plan file: %v", err) 59 } 60 for _, host := range opts.limit { 61 if !plan.HostExists(host) { 62 return fmt.Errorf("host %q in '--limit' option does not match any hosts in the plan file", host) 63 } 64 } 65 util.PrettyPrintOk(out, "Reading installation plan file %q", opts.planFile) 66 67 // Validate plan file 68 if err := validatePlan(out, plan); err != nil { 69 return err 70 } 71 72 // Validate SSH connections 73 if err := validateSSHConnectivity(out, plan); err != nil { 74 return err 75 } 76 77 // get a new pki 78 pki, err := newPKI(out, opts) 79 if err != nil { 80 return err 81 } 82 // Validate Certificates 83 ok, errs := install.ValidateCertificates(plan, pki) 84 if !ok { 85 util.PrettyPrintErr(out, "Validating cluster certificates") 86 util.PrintValidationErrors(out, errs) 87 return fmt.Errorf("Cluster certificates validation error prevents installation from proceeding") 88 } 89 90 if opts.skipPreFlight { 91 return nil 92 } 93 // Run pre-flight 94 options := install.ExecutorOptions{ 95 OutputFormat: opts.outputFormat, 96 Verbose: opts.verbose, 97 } 98 e, err := install.NewPreFlightExecutor(out, os.Stderr, options) 99 if err != nil { 100 return err 101 } 102 return e.RunPreFlightCheck(plan, opts.limit...) 103 } 104 105 // TODO this should really not be here 106 func newPKI(stdout io.Writer, options *validateOpts) (*install.LocalPKI, error) { 107 ansibleDir := "ansible" 108 if options.generatedAssetsDir == "" { 109 return nil, fmt.Errorf("GeneratedAssetsDirectory option cannot be empty") 110 } 111 certsDir := filepath.Join(options.generatedAssetsDir, "keys") 112 pki := &install.LocalPKI{ 113 CACsr: filepath.Join(ansibleDir, "playbooks", "tls", "ca-csr.json"), 114 GeneratedCertsDirectory: certsDir, 115 Log: stdout, 116 } 117 return pki, nil 118 } 119 120 func validatePlan(out io.Writer, plan *install.Plan) error { 121 ok, errs := install.ValidatePlan(plan) 122 if !ok { 123 util.PrettyPrintErr(out, "Validating installation plan file") 124 util.PrintValidationErrors(out, errs) 125 return fmt.Errorf("Plan file validation error prevents installation from proceeding") 126 } 127 util.PrettyPrintOk(out, "Validating installation plan file") 128 return nil 129 } 130 131 func validateSSHConnectivity(out io.Writer, plan *install.Plan) error { 132 ok, errs := install.ValidatePlanSSHConnections(plan) 133 if !ok { 134 util.PrettyPrintErr(out, "Validating SSH connectivity to nodes") 135 util.PrintValidationErrors(out, errs) 136 return fmt.Errorf("SSH connectivity validation error prevents installation from proceeding") 137 } 138 util.PrettyPrintOk(out, "Validating SSH connectivity to nodes") 139 return nil 140 }