@@ -42,6 +42,8 @@ import (
42
42
"k8s.io/minikube/pkg/minikube/out"
43
43
)
44
44
45
+ var deleteAll bool
46
+
45
47
// deleteCmd represents the delete command
46
48
var deleteCmd = & cobra.Command {
47
49
Use : "delete" ,
@@ -51,26 +53,121 @@ associated files.`,
51
53
Run : runDelete ,
52
54
}
53
55
56
+ type typeOfError int
57
+
58
+ const (
59
+ Fatal typeOfError = 0
60
+ MissingProfile typeOfError = 1
61
+ MissingCluster typeOfError = 2
62
+ )
63
+
64
+ type DeletionError struct {
65
+ Err error
66
+ Errtype typeOfError
67
+ }
68
+
69
+ func (error DeletionError ) Error () string {
70
+ return error .Err .Error ()
71
+ }
72
+
54
73
// runDelete handles the executes the flow of "minikube delete"
55
74
func runDelete (cmd * cobra.Command , args []string ) {
56
75
if len (args ) > 0 {
57
76
exit .UsageT ("Usage: minikube delete" )
58
77
}
59
- profile := viper .GetString (pkg_config .MachineProfile )
78
+ profileFlag , err := cmd .Flags ().GetString ("profile" )
79
+ if err != nil {
80
+ exit .WithError ("Could not get profile flag" , err )
81
+ }
82
+
83
+ if deleteAll {
84
+ if profileFlag != constants .DefaultMachineName {
85
+ exit .UsageT ("usage: minikube delete --all" )
86
+ }
87
+
88
+ validProfiles , invalidProfiles , err := pkg_config .ListProfiles ()
89
+ profilesToDelete := append (validProfiles , invalidProfiles ... )
90
+
91
+ if err != nil {
92
+ exit .WithError ("Error getting profiles to delete" , err )
93
+ }
94
+
95
+ errs := DeleteProfiles (profilesToDelete )
96
+ if len (errs ) > 0 {
97
+ HandleDeletionErrors (errs )
98
+ } else {
99
+ out .T (out .DeletingHost , "Successfully deleted all profiles" )
100
+ }
101
+ } else {
102
+ if len (args ) > 0 {
103
+ exit .UsageT ("usage: minikube delete" )
104
+ }
105
+
106
+ profileName := viper .GetString (pkg_config .MachineProfile )
107
+ profile , err := pkg_config .LoadProfile (profileName )
108
+ if err != nil {
109
+ out .ErrT (out .Meh , `"{{.name}}" profile does not exist` , out.V {"name" : profileName })
110
+ }
111
+
112
+ errs := DeleteProfiles ([]* pkg_config.Profile {profile })
113
+ if len (errs ) > 0 {
114
+ HandleDeletionErrors (errs )
115
+ } else {
116
+ out .T (out .DeletingHost , "Successfully deleted profile \" {{.name}}\" " , out.V {"name" : profileName })
117
+ }
118
+ }
119
+ }
120
+
121
+ // Deletes one or more profiles
122
+ func DeleteProfiles (profiles []* pkg_config.Profile ) []error {
123
+ var errs []error
124
+ for _ , profile := range profiles {
125
+ err := deleteProfile (profile )
126
+
127
+ if err != nil {
128
+ mm , loadErr := cluster .LoadMachine (profile .Name )
129
+
130
+ if ! profile .IsValid () || (loadErr != nil || ! mm .IsValid ()) {
131
+ invalidProfileDeletionErrs := deleteInvalidProfile (profile )
132
+ if len (invalidProfileDeletionErrs ) > 0 {
133
+ errs = append (errs , invalidProfileDeletionErrs ... )
134
+ }
135
+ } else {
136
+ errs = append (errs , err )
137
+ }
138
+ }
139
+ }
140
+ return errs
141
+ }
142
+
143
+ func deleteProfile (profile * pkg_config.Profile ) error {
144
+ viper .Set (pkg_config .MachineProfile , profile .Name )
145
+
60
146
api , err := machine .NewAPIClient ()
61
147
if err != nil {
62
- exit .WithError ("Error getting client" , err )
148
+ delErr := profileDeletionErr (profile .Name , fmt .Sprintf ("error getting client %v" , err ))
149
+ return DeletionError {Err : delErr , Errtype : Fatal }
63
150
}
64
151
defer api .Close ()
65
152
66
153
cc , err := pkg_config .Load ()
67
154
if err != nil && ! os .IsNotExist (err ) {
68
155
out .ErrT (out .Sad , "Error loading profile {{.name}}: {{.error}}" , out.V {"name" : profile , "error" : err })
156
+ delErr := profileDeletionErr (profile .Name , fmt .Sprintf ("error loading profile config: %v" , err ))
157
+ return DeletionError {Err : delErr , Errtype : MissingProfile }
69
158
}
70
159
71
160
// In the case of "none", we want to uninstall Kubernetes as there is no VM to delete
72
161
if err == nil && cc .MachineConfig .VMDriver == constants .DriverNone {
73
- uninstallKubernetes (api , cc .KubernetesConfig , viper .GetString (cmdcfg .Bootstrapper ))
162
+ if err := uninstallKubernetes (api , cc .KubernetesConfig , viper .GetString (cmdcfg .Bootstrapper )); err != nil {
163
+ deletionError , ok := err .(DeletionError )
164
+ if ok {
165
+ delErr := profileDeletionErr (profile .Name , fmt .Sprintf ("%v" , err ))
166
+ deletionError .Err = delErr
167
+ return deletionError
168
+ }
169
+ return err
170
+ }
74
171
}
75
172
76
173
if err := killMountProcess (); err != nil {
@@ -83,39 +180,111 @@ func runDelete(cmd *cobra.Command, args []string) {
83
180
out .T (out .Meh , `"{{.name}}" cluster does not exist. Proceeding ahead with cleanup.` , out.V {"name" : profile })
84
181
default :
85
182
out .T (out .FailureType , "Failed to delete cluster: {{.error}}" , out.V {"error" : err })
86
- out .T (out .Notice , `You may need to manually remove the "{{.name}}" VM from your hypervisor` , out.V {"name" : profile })
183
+ out .T (out .Notice , `You may need to manually remove the "{{.name}}" VM from your hypervisor` , out.V {"name" : profile . Name })
87
184
}
88
185
}
89
186
90
187
// In case DeleteHost didn't complete the job.
91
- deleteProfileDirectory (profile )
188
+ deleteProfileDirectory (profile . Name )
92
189
93
- if err := pkg_config .DeleteProfile (profile ); err != nil {
190
+ if err := pkg_config .DeleteProfile (profile . Name ); err != nil {
94
191
if os .IsNotExist (err ) {
95
- out . T ( out . Meh , `"{{.name}}" profile does not exist` , out. V { "name" : profile } )
96
- os . Exit ( 0 )
192
+ delErr := profileDeletionErr ( profile . Name , fmt . Sprintf ( " \" %s \" profile does not exist" , profile . Name ) )
193
+ return DeletionError { Err : delErr , Errtype : MissingProfile }
97
194
}
98
- exit .WithError ("Failed to remove profile" , err )
195
+ delErr := profileDeletionErr (profile .Name , fmt .Sprintf ("failed to remove profile %v" , err ))
196
+ return DeletionError {Err : delErr , Errtype : Fatal }
99
197
}
100
- out .T (out .Crushed , `The "{{.name}}" cluster has been deleted.` , out.V {"name" : profile })
198
+
199
+ out .T (out .Crushed , `The "{{.name}}" cluster has been deleted.` , out.V {"name" : profile .Name })
101
200
102
201
machineName := pkg_config .GetMachineName ()
103
202
if err := kubeconfig .DeleteContext (constants .KubeconfigPath , machineName ); err != nil {
104
- exit . WithError ("update config" , err )
203
+ return DeletionError { Err : fmt . Errorf ("update config: %v " , err ), Errtype : Fatal }
105
204
}
106
205
107
206
if err := cmdcfg .Unset (pkg_config .MachineProfile ); err != nil {
108
- exit . WithError ("unset minikube profile" , err )
207
+ return DeletionError { Err : fmt . Errorf ("unset minikube profile: %v " , err ), Errtype : Fatal }
109
208
}
209
+ return nil
110
210
}
111
211
112
- func uninstallKubernetes (api libmachine.API , kc pkg_config.KubernetesConfig , bsName string ) {
212
+ func deleteInvalidProfile (profile * pkg_config.Profile ) []error {
213
+ out .T (out .DeletingHost , "Trying to delete invalid profile {{.profile}}" , out.V {"profile" : profile .Name })
214
+
215
+ var errs []error
216
+ pathToProfile := pkg_config .ProfileFolderPath (profile .Name , localpath .MiniPath ())
217
+ if _ , err := os .Stat (pathToProfile ); ! os .IsNotExist (err ) {
218
+ err := os .RemoveAll (pathToProfile )
219
+ if err != nil {
220
+ errs = append (errs , DeletionError {err , Fatal })
221
+ }
222
+ }
223
+
224
+ pathToMachine := cluster .MachinePath (profile .Name , localpath .MiniPath ())
225
+ if _ , err := os .Stat (pathToMachine ); ! os .IsNotExist (err ) {
226
+ err := os .RemoveAll (pathToMachine )
227
+ if err != nil {
228
+ errs = append (errs , DeletionError {err , Fatal })
229
+ }
230
+ }
231
+ return errs
232
+ }
233
+
234
+ func profileDeletionErr (profileName string , additionalInfo string ) error {
235
+ return fmt .Errorf ("error deleting profile \" %s\" : %s" , profileName , additionalInfo )
236
+ }
237
+
238
+ func uninstallKubernetes (api libmachine.API , kc pkg_config.KubernetesConfig , bsName string ) error {
113
239
out .T (out .Resetting , "Uninstalling Kubernetes {{.kubernetes_version}} using {{.bootstrapper_name}} ..." , out.V {"kubernetes_version" : kc .KubernetesVersion , "bootstrapper_name" : bsName })
114
240
clusterBootstrapper , err := getClusterBootstrapper (api , bsName )
115
241
if err != nil {
116
- out . ErrT ( out . Empty , "Unable to get bootstrapper: {{.error}} " , out. V { "error" : err })
242
+ return DeletionError { Err : fmt . Errorf ( "unable to get bootstrapper: %v " , err ), Errtype : Fatal }
117
243
} else if err = clusterBootstrapper .DeleteCluster (kc ); err != nil {
118
- out .ErrT (out .Empty , "Failed to delete cluster: {{.error}}" , out.V {"error" : err })
244
+ return DeletionError {Err : fmt .Errorf ("failed to delete cluster: %v" , err ), Errtype : Fatal }
245
+ }
246
+ return nil
247
+ }
248
+
249
+ // Handles deletion error from DeleteProfiles
250
+ func HandleDeletionErrors (errors []error ) {
251
+ if len (errors ) == 1 {
252
+ handleSingleDeletionError (errors [0 ])
253
+ } else {
254
+ handleMultipleDeletionErrors (errors )
255
+ }
256
+ }
257
+
258
+ func handleSingleDeletionError (err error ) {
259
+ deletionError , ok := err .(DeletionError )
260
+
261
+ if ok {
262
+ switch deletionError .Errtype {
263
+ case Fatal :
264
+ out .FatalT (deletionError .Error ())
265
+ case MissingProfile :
266
+ out .ErrT (out .Sad , deletionError .Error ())
267
+ case MissingCluster :
268
+ out .ErrT (out .Meh , deletionError .Error ())
269
+ default :
270
+ out .FatalT (deletionError .Error ())
271
+ }
272
+ } else {
273
+ exit .WithError ("Could not process error from failed deletion" , err )
274
+ }
275
+ }
276
+
277
+ func handleMultipleDeletionErrors (errors []error ) {
278
+ out .ErrT (out .Sad , "Multiple errors deleting profiles" )
279
+
280
+ for _ , err := range errors {
281
+ deletionError , ok := err .(DeletionError )
282
+
283
+ if ok {
284
+ glog .Errorln (deletionError .Error ())
285
+ } else {
286
+ exit .WithError ("Could not process errors from failed deletion" , err )
287
+ }
119
288
}
120
289
}
121
290
@@ -177,3 +346,8 @@ func killMountProcess() error {
177
346
}
178
347
return nil
179
348
}
349
+
350
+ func init () {
351
+ deleteCmd .Flags ().BoolVar (& deleteAll , "all" , false , "Set flag to delete all profiles" )
352
+ RootCmd .AddCommand (deleteCmd )
353
+ }
0 commit comments