Skip to content

Commit

Permalink
Merge pull request #4780 from marekschwarz/DELETE_ALL_PROFILES
Browse files Browse the repository at this point in the history
Added option to delete all profiles
  • Loading branch information
tstromberg committed Oct 16, 2019
2 parents 37a69ee + d13f58f commit 7a7689b
Show file tree
Hide file tree
Showing 50 changed files with 2,517 additions and 26 deletions.
204 changes: 189 additions & 15 deletions cmd/minikube/cmd/delete.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@ import (
"k8s.io/minikube/pkg/minikube/out"
)

var deleteAll bool

// deleteCmd represents the delete command
var deleteCmd = &cobra.Command{
Use: "delete",
Expand All @@ -51,26 +53,121 @@ associated files.`,
Run: runDelete,
}

type typeOfError int

const (
Fatal typeOfError = 0
MissingProfile typeOfError = 1
MissingCluster typeOfError = 2
)

type DeletionError struct {
Err error
Errtype typeOfError
}

func (error DeletionError) Error() string {
return error.Err.Error()
}

// runDelete handles the executes the flow of "minikube delete"
func runDelete(cmd *cobra.Command, args []string) {
if len(args) > 0 {
exit.UsageT("Usage: minikube delete")
}
profile := viper.GetString(pkg_config.MachineProfile)
profileFlag, err := cmd.Flags().GetString("profile")
if err != nil {
exit.WithError("Could not get profile flag", err)
}

if deleteAll {
if profileFlag != constants.DefaultMachineName {
exit.UsageT("usage: minikube delete --all")
}

validProfiles, invalidProfiles, err := pkg_config.ListProfiles()
profilesToDelete := append(validProfiles, invalidProfiles...)

if err != nil {
exit.WithError("Error getting profiles to delete", err)
}

errs := DeleteProfiles(profilesToDelete)
if len(errs) > 0 {
HandleDeletionErrors(errs)
} else {
out.T(out.DeletingHost, "Successfully deleted all profiles")
}
} else {
if len(args) > 0 {
exit.UsageT("usage: minikube delete")
}

profileName := viper.GetString(pkg_config.MachineProfile)
profile, err := pkg_config.LoadProfile(profileName)
if err != nil {
out.ErrT(out.Meh, `"{{.name}}" profile does not exist`, out.V{"name": profileName})
}

errs := DeleteProfiles([]*pkg_config.Profile{profile})
if len(errs) > 0 {
HandleDeletionErrors(errs)
} else {
out.T(out.DeletingHost, "Successfully deleted profile \"{{.name}}\"", out.V{"name": profileName})
}
}
}

// Deletes one or more profiles
func DeleteProfiles(profiles []*pkg_config.Profile) []error {
var errs []error
for _, profile := range profiles {
err := deleteProfile(profile)

if err != nil {
mm, loadErr := cluster.LoadMachine(profile.Name)

if !profile.IsValid() || (loadErr != nil || !mm.IsValid()) {
invalidProfileDeletionErrs := deleteInvalidProfile(profile)
if len(invalidProfileDeletionErrs) > 0 {
errs = append(errs, invalidProfileDeletionErrs...)
}
} else {
errs = append(errs, err)
}
}
}
return errs
}

func deleteProfile(profile *pkg_config.Profile) error {
viper.Set(pkg_config.MachineProfile, profile.Name)

api, err := machine.NewAPIClient()
if err != nil {
exit.WithError("Error getting client", err)
delErr := profileDeletionErr(profile.Name, fmt.Sprintf("error getting client %v", err))
return DeletionError{Err: delErr, Errtype: Fatal}
}
defer api.Close()

cc, err := pkg_config.Load()
if err != nil && !os.IsNotExist(err) {
out.ErrT(out.Sad, "Error loading profile {{.name}}: {{.error}}", out.V{"name": profile, "error": err})
delErr := profileDeletionErr(profile.Name, fmt.Sprintf("error loading profile config: %v", err))
return DeletionError{Err: delErr, Errtype: MissingProfile}
}

// In the case of "none", we want to uninstall Kubernetes as there is no VM to delete
if err == nil && cc.MachineConfig.VMDriver == constants.DriverNone {
uninstallKubernetes(api, cc.KubernetesConfig, viper.GetString(cmdcfg.Bootstrapper))
if err := uninstallKubernetes(api, cc.KubernetesConfig, viper.GetString(cmdcfg.Bootstrapper)); err != nil {
deletionError, ok := err.(DeletionError)
if ok {
delErr := profileDeletionErr(profile.Name, fmt.Sprintf("%v", err))
deletionError.Err = delErr
return deletionError
}
return err
}
}

if err := killMountProcess(); err != nil {
Expand All @@ -83,39 +180,111 @@ func runDelete(cmd *cobra.Command, args []string) {
out.T(out.Meh, `"{{.name}}" cluster does not exist. Proceeding ahead with cleanup.`, out.V{"name": profile})
default:
out.T(out.FailureType, "Failed to delete cluster: {{.error}}", out.V{"error": err})
out.T(out.Notice, `You may need to manually remove the "{{.name}}" VM from your hypervisor`, out.V{"name": profile})
out.T(out.Notice, `You may need to manually remove the "{{.name}}" VM from your hypervisor`, out.V{"name": profile.Name})
}
}

// In case DeleteHost didn't complete the job.
deleteProfileDirectory(profile)
deleteProfileDirectory(profile.Name)

if err := pkg_config.DeleteProfile(profile); err != nil {
if err := pkg_config.DeleteProfile(profile.Name); err != nil {
if os.IsNotExist(err) {
out.T(out.Meh, `"{{.name}}" profile does not exist`, out.V{"name": profile})
os.Exit(0)
delErr := profileDeletionErr(profile.Name, fmt.Sprintf("\"%s\" profile does not exist", profile.Name))
return DeletionError{Err: delErr, Errtype: MissingProfile}
}
exit.WithError("Failed to remove profile", err)
delErr := profileDeletionErr(profile.Name, fmt.Sprintf("failed to remove profile %v", err))
return DeletionError{Err: delErr, Errtype: Fatal}
}
out.T(out.Crushed, `The "{{.name}}" cluster has been deleted.`, out.V{"name": profile})

out.T(out.Crushed, `The "{{.name}}" cluster has been deleted.`, out.V{"name": profile.Name})

machineName := pkg_config.GetMachineName()
if err := kubeconfig.DeleteContext(constants.KubeconfigPath, machineName); err != nil {
exit.WithError("update config", err)
return DeletionError{Err: fmt.Errorf("update config: %v", err), Errtype: Fatal}
}

if err := cmdcfg.Unset(pkg_config.MachineProfile); err != nil {
exit.WithError("unset minikube profile", err)
return DeletionError{Err: fmt.Errorf("unset minikube profile: %v", err), Errtype: Fatal}
}
return nil
}

func uninstallKubernetes(api libmachine.API, kc pkg_config.KubernetesConfig, bsName string) {
func deleteInvalidProfile(profile *pkg_config.Profile) []error {
out.T(out.DeletingHost, "Trying to delete invalid profile {{.profile}}", out.V{"profile": profile.Name})

var errs []error
pathToProfile := pkg_config.ProfileFolderPath(profile.Name, localpath.MiniPath())
if _, err := os.Stat(pathToProfile); !os.IsNotExist(err) {
err := os.RemoveAll(pathToProfile)
if err != nil {
errs = append(errs, DeletionError{err, Fatal})
}
}

pathToMachine := cluster.MachinePath(profile.Name, localpath.MiniPath())
if _, err := os.Stat(pathToMachine); !os.IsNotExist(err) {
err := os.RemoveAll(pathToMachine)
if err != nil {
errs = append(errs, DeletionError{err, Fatal})
}
}
return errs
}

func profileDeletionErr(profileName string, additionalInfo string) error {
return fmt.Errorf("error deleting profile \"%s\": %s", profileName, additionalInfo)
}

func uninstallKubernetes(api libmachine.API, kc pkg_config.KubernetesConfig, bsName string) error {
out.T(out.Resetting, "Uninstalling Kubernetes {{.kubernetes_version}} using {{.bootstrapper_name}} ...", out.V{"kubernetes_version": kc.KubernetesVersion, "bootstrapper_name": bsName})
clusterBootstrapper, err := getClusterBootstrapper(api, bsName)
if err != nil {
out.ErrT(out.Empty, "Unable to get bootstrapper: {{.error}}", out.V{"error": err})
return DeletionError{Err: fmt.Errorf("unable to get bootstrapper: %v", err), Errtype: Fatal}
} else if err = clusterBootstrapper.DeleteCluster(kc); err != nil {
out.ErrT(out.Empty, "Failed to delete cluster: {{.error}}", out.V{"error": err})
return DeletionError{Err: fmt.Errorf("failed to delete cluster: %v", err), Errtype: Fatal}
}
return nil
}

// Handles deletion error from DeleteProfiles
func HandleDeletionErrors(errors []error) {
if len(errors) == 1 {
handleSingleDeletionError(errors[0])
} else {
handleMultipleDeletionErrors(errors)
}
}

func handleSingleDeletionError(err error) {
deletionError, ok := err.(DeletionError)

if ok {
switch deletionError.Errtype {
case Fatal:
out.FatalT(deletionError.Error())
case MissingProfile:
out.ErrT(out.Sad, deletionError.Error())
case MissingCluster:
out.ErrT(out.Meh, deletionError.Error())
default:
out.FatalT(deletionError.Error())
}
} else {
exit.WithError("Could not process error from failed deletion", err)
}
}

func handleMultipleDeletionErrors(errors []error) {
out.ErrT(out.Sad, "Multiple errors deleting profiles")

for _, err := range errors {
deletionError, ok := err.(DeletionError)

if ok {
glog.Errorln(deletionError.Error())
} else {
exit.WithError("Could not process errors from failed deletion", err)
}
}
}

Expand Down Expand Up @@ -177,3 +346,8 @@ func killMountProcess() error {
}
return nil
}

func init() {
deleteCmd.Flags().BoolVar(&deleteAll, "all", false, "Set flag to delete all profiles")
RootCmd.AddCommand(deleteCmd)
}
Loading

0 comments on commit 7a7689b

Please sign in to comment.