github.com/docker/compose-on-kubernetes@v0.5.0/install/uninstall.go (about) 1 package install 2 3 import ( 4 "context" 5 "time" 6 7 stacksclient "github.com/docker/compose-on-kubernetes/api/client/clientset/typed/compose/v1beta1" 8 log "github.com/sirupsen/logrus" 9 apiextensionsclient "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset" 10 apierrors "k8s.io/apimachinery/pkg/api/errors" 11 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 12 appsv1 "k8s.io/client-go/kubernetes/typed/apps/v1" 13 corev1 "k8s.io/client-go/kubernetes/typed/core/v1" 14 rbacv1 "k8s.io/client-go/kubernetes/typed/rbac/v1" 15 "k8s.io/client-go/rest" 16 typedkubeaggreagatorv1beta1 "k8s.io/kube-aggregator/pkg/client/clientset_generated/clientset/typed/apiregistration/v1beta1" 17 ) 18 19 var ( 20 deletePolicy = metav1.DeletePropagationForeground 21 deleteBackgroundPolicy = metav1.DeletePropagationBackground 22 deleteOrphanPolicy = metav1.DeletePropagationOrphan 23 deleteOptions = metav1.DeleteOptions{ 24 PropagationPolicy: &deletePolicy, 25 } 26 deleteBackgroundOptions = metav1.DeleteOptions{ 27 PropagationPolicy: &deleteBackgroundPolicy, 28 } 29 listOptions = metav1.ListOptions{ 30 LabelSelector: everythingSelector, 31 } 32 ) 33 34 func uninstallErrorFilter(err error) error { 35 if apierrors.IsNotFound(err) { 36 return nil 37 } 38 return err 39 } 40 41 func uninstallCore(config *rest.Config, namespace string) error { 42 client, err := corev1.NewForConfig(config) 43 if err != nil { 44 return err 45 } 46 if err = uninstallErrorFilter(client.Secrets(namespace).DeleteCollection(&deleteOptions, listOptions)); err != nil { 47 return err 48 } 49 svcs, err := client.Services(namespace).List(listOptions) 50 if uninstallErrorFilter(err) != nil { 51 return err 52 } 53 for _, svc := range svcs.Items { 54 if err = uninstallErrorFilter(client.Services(namespace).Delete(svc.Name, &deleteBackgroundOptions)); err != nil { 55 return err 56 } 57 } 58 return uninstallErrorFilter(client.ServiceAccounts(namespace).DeleteCollection(&deleteOptions, listOptions)) 59 } 60 61 func uninstallApps(config *rest.Config, namespace string) error { 62 apps, err := appsv1.NewForConfig(config) 63 if uninstallErrorFilter(err) != nil { 64 return err 65 } 66 return uninstallErrorFilter(apps.Deployments(namespace).DeleteCollection(&deleteOptions, listOptions)) 67 } 68 69 func uninstallRbac(config *rest.Config) error { 70 rbac, err := rbacv1.NewForConfig(config) 71 if err != nil { 72 return err 73 } 74 if err = uninstallErrorFilter(rbac.ClusterRoleBindings().DeleteCollection(&deleteOptions, listOptions)); err != nil { 75 return err 76 } 77 if err = uninstallErrorFilter(rbac.ClusterRoles().DeleteCollection(&deleteOptions, listOptions)); err != nil { 78 return err 79 } 80 return uninstallErrorFilter(rbac.RoleBindings("kube-system").DeleteCollection(&deleteOptions, listOptions)) 81 } 82 83 func orphanAllStacks(stacks stacksclient.ComposeV1beta1Interface) error { 84 85 type namespaceAndName struct { 86 namespace string 87 name string 88 } 89 toDelete := []namespaceAndName{} 90 listOpts := metav1.ListOptions{} 91 for { 92 stackList, err := stacks.Stacks(metav1.NamespaceAll).List(listOpts) 93 if err != nil { 94 return err 95 } 96 for _, stack := range stackList.Items { 97 toDelete = append(toDelete, namespaceAndName{namespace: stack.Namespace, name: stack.Name}) 98 } 99 if stackList.Continue == "" { 100 break 101 } 102 listOpts.Continue = stackList.Continue 103 } 104 105 log.Infof("Orphaning %d stack(s)", len(toDelete)) 106 for _, stack := range toDelete { 107 if err := stacks.Stacks(stack.namespace).Delete(stack.name, &metav1.DeleteOptions{ 108 PropagationPolicy: &deleteOrphanPolicy, 109 }); err != nil { 110 return err 111 } 112 } 113 // wait for all stacks to be effectively deleted 114 log.Info("Waiting for stack to be removed") 115 for { 116 list, err := stacks.Stacks(metav1.NamespaceAll).List(metav1.ListOptions{}) 117 if err != nil { 118 return err 119 } 120 if len(list.Items) == 0 { 121 break 122 } 123 log.Infof("%d stack(s) waiting to be removed", len(list.Items)) 124 time.Sleep(time.Second) 125 } 126 return nil 127 } 128 129 // UninstallCRD uninstalls the CustomResourceDefinition and preserves running stacks 130 func UninstallCRD(config *rest.Config) error { 131 crds, err := apiextensionsclient.NewForConfig(config) 132 if err != nil { 133 return err 134 } 135 _, err = crds.ApiextensionsV1beta1().CustomResourceDefinitions().Get("stacks.compose.docker.com", metav1.GetOptions{}) 136 if err != nil { 137 if apierrors.IsNotFound(err) { 138 return nil 139 } 140 return err 141 } 142 143 // deleteOrphanPolicy is not propagated if we straight out delete the CRD, 144 // so let's go and delete the stacks manually before 145 stacks, err := stacksclient.NewForConfig(config) 146 if err != nil { 147 return err 148 } 149 if err = orphanAllStacks(stacks); err != nil { 150 return err 151 } 152 153 log.Info("Removing CRD") 154 if err = uninstallErrorFilter(crds.ApiextensionsV1beta1().CustomResourceDefinitions().Delete("stacks.compose.docker.com", 155 &metav1.DeleteOptions{})); err != nil { 156 return err 157 } 158 // wait for crd removal 159 for { 160 _, err = crds.ApiextensionsV1beta1().CustomResourceDefinitions().Get("stacks.compose.docker.com", metav1.GetOptions{}) 161 if err != nil { 162 if apierrors.IsNotFound(err) { 163 return nil 164 } 165 return err 166 } 167 time.Sleep(time.Second) 168 } 169 } 170 171 // Uninstall uninstalls the Compose feature. 172 func Uninstall(config *rest.Config, namespace string, keepCRD bool) error { 173 log.Debugf("Uninstall from namespace %q", namespace) 174 175 crds, err := apiextensionsclient.NewForConfig(config) 176 if err != nil { 177 return err 178 } 179 180 err = uninstallApps(config, namespace) 181 if err != nil { 182 return err 183 } 184 185 if !keepCRD { 186 err = crds.ApiextensionsV1beta1().CustomResourceDefinitions().Delete("stacks.compose.docker.com", &deleteOptions) 187 if err != nil && !apierrors.IsNotFound(err) { 188 return err 189 } 190 } 191 err = uninstallRbac(config) 192 if err != nil { 193 return err 194 } 195 196 aggregator, err := typedkubeaggreagatorv1beta1.NewForConfig(config) 197 if err != nil { 198 return err 199 } 200 201 apisvcs, err := aggregator.APIServices().List(metav1.ListOptions{}) 202 if err != nil { 203 return err 204 } 205 for _, apisvc := range apisvcs.Items { 206 if apisvc.Labels[fryKey] == composeAPIServerFry { 207 err = aggregator.APIServices().Delete(apisvc.Name, &deleteBackgroundOptions) 208 if err != nil { 209 return err 210 } 211 } 212 } 213 return uninstallCore(config, namespace) 214 } 215 func isDone(ctx context.Context) bool { 216 select { 217 case <-ctx.Done(): 218 return true 219 default: 220 return false 221 } 222 } 223 224 func waitForNoCrd(ctx context.Context, config *rest.Config, namespace string) error { 225 crds, err := apiextensionsclient.NewForConfig(config) 226 if err != nil { 227 return err 228 } 229 for { 230 if isDone(ctx) { 231 return context.DeadlineExceeded 232 } 233 lst, err := crds.ApiextensionsV1beta1().CustomResourceDefinitions().List(listOptions) 234 if err != nil { 235 return err 236 } 237 if len(lst.Items) == 0 { 238 return nil 239 } 240 time.Sleep(time.Second) 241 } 242 } 243 244 func waitForNoApps(ctx context.Context, config *rest.Config, namespace string) error { 245 apps, err := appsv1.NewForConfig(config) 246 if err != nil { 247 return err 248 } 249 for { 250 if isDone(ctx) { 251 return context.DeadlineExceeded 252 } 253 lst, err := apps.Deployments(namespace).List(listOptions) 254 if err != nil { 255 return err 256 } 257 if len(lst.Items) == 0 { 258 return nil 259 } 260 time.Sleep(time.Second) 261 } 262 } 263 264 func waitForNoRbac(ctx context.Context, config *rest.Config, namespace string) error { 265 rbac, err := rbacv1.NewForConfig(config) 266 if err != nil { 267 return err 268 } 269 270 for { 271 if isDone(ctx) { 272 return context.DeadlineExceeded 273 } 274 lst1, err := rbac.ClusterRoleBindings().List(listOptions) 275 // if rbac is not enabled, List will fail with status 404 instead of returning an empty list 276 // in that case we can just leave early 277 if apierrors.IsNotFound(err) { 278 return nil 279 } 280 if err != nil { 281 return err 282 } 283 if len(lst1.Items) > 0 { 284 time.Sleep(time.Second) 285 continue 286 } 287 lst2, err := rbac.ClusterRoles().List(listOptions) 288 if err != nil { 289 return err 290 } 291 if len(lst2.Items) > 0 { 292 time.Sleep(time.Second) 293 continue 294 } 295 lst3, err := rbac.RoleBindings("kube-system").List(listOptions) 296 if err != nil { 297 return err 298 } 299 if len(lst3.Items) > 0 { 300 time.Sleep(time.Second) 301 continue 302 } 303 return nil 304 } 305 } 306 307 func waitForNoAPIAggregation(ctx context.Context, config *rest.Config, namespace string) error { 308 aggregator, err := typedkubeaggreagatorv1beta1.NewForConfig(config) 309 if err != nil { 310 return err 311 } 312 313 for { 314 if isDone(ctx) { 315 return context.DeadlineExceeded 316 } 317 apisvcs, err := aggregator.APIServices().List(metav1.ListOptions{}) 318 if err != nil { 319 return err 320 } 321 for _, apisvc := range apisvcs.Items { 322 if apisvc.Labels[fryKey] == composeAPIServerFry { 323 time.Sleep(time.Second) 324 continue 325 } 326 } 327 return nil 328 } 329 } 330 func waitForNoCoreComponents(ctx context.Context, config *rest.Config, namespace string) error { 331 client, err := corev1.NewForConfig(config) 332 if err != nil { 333 return err 334 } 335 for { 336 if isDone(ctx) { 337 return context.DeadlineExceeded 338 } 339 secrets, err := client.Secrets(namespace).List(listOptions) 340 if err != nil { 341 return err 342 } 343 if len(secrets.Items) > 0 { 344 time.Sleep(time.Second) 345 continue 346 } 347 348 svcs, err := client.Services(namespace).List(listOptions) 349 if err != nil { 350 return err 351 } 352 if len(svcs.Items) > 0 { 353 time.Sleep(time.Second) 354 continue 355 } 356 357 sas, err := client.ServiceAccounts(namespace).List(listOptions) 358 if err != nil { 359 return err 360 } 361 if len(sas.Items) > 0 { 362 time.Sleep(time.Second) 363 continue 364 } 365 return nil 366 } 367 } 368 369 type uninstallWaiter func(context.Context, *rest.Config, string) error 370 371 // WaitForUninstallCompletion waits for an unistall operation to complete 372 func WaitForUninstallCompletion(ctx context.Context, config *rest.Config, namespace string, skipCRD bool) error { 373 waiters := []uninstallWaiter{ 374 waitForNoApps, 375 waitForNoRbac, 376 waitForNoAPIAggregation, 377 waitForNoCoreComponents, 378 } 379 if !skipCRD { 380 waiters = append(waiters, waitForNoCrd) 381 } 382 for _, w := range waiters { 383 if err := w(ctx, config, namespace); err != nil { 384 return err 385 } 386 } 387 return nil 388 }