code: func runPlan(flags *planFlags, userVersion string) error {
  	// Start with the basics, verify that the cluster is healthy, build a client and a versionGetter. Never dry-run when planning.
  	klog.V(1).Infoln("[upgrade/plan] verifying health of cluster")
  	klog.V(1).Infoln("[upgrade/plan] retrieving configuration from cluster")
  	client, versionGetter, cfg, err := enforceRequirements(flags.applyPlanFlags, false, userVersion)
  	if err != nil {
  		return err
  	}
  
  	var etcdClient etcdutil.ClusterInterrogator
  
  	// Currently this is the only method we have for distinguishing
  	// external etcd vs static pod etcd
  	isExternalEtcd := cfg.Etcd.External != nil
  	if isExternalEtcd {
  		etcdClient, err = etcdutil.New(
  			cfg.Etcd.External.Endpoints,
  			cfg.Etcd.External.CAFile,
  			cfg.Etcd.External.CertFile,
  			cfg.Etcd.External.KeyFile)
  	} else {
  		// Connects to local/stacked etcd existing in the cluster
  		etcdClient, err = etcdutil.NewFromCluster(client, cfg.CertificatesDir)
  	}
  	if err != nil {
  		return err
  	}
  
  	// Compute which upgrade possibilities there are
  	klog.V(1).Infoln("[upgrade/plan] computing upgrade possibilities")
  	availUpgrades, err := upgrade.GetAvailableUpgrades(versionGetter, flags.allowExperimentalUpgrades, flags.allowRCUpgrades, etcdClient, cfg.DNS.Type, client)
  	if err != nil {
  		return errors.Wrap(err, "[upgrade/versions] FATAL")
  	}
  
  	// Tell the user which upgrades are available
  	printAvailableUpgrades(availUpgrades, os.Stdout, isExternalEtcd)
  	return nil
  }
docs: // runPlan takes care of outputting available versions to upgrade to for the user