// updateNamespaceStatusFunc will verify that the status of the namespace is correct func updateNamespaceStatusFunc(kubeClient clientset.Interface, namespace *api.Namespace) (*api.Namespace, error) { if namespace.DeletionTimestamp.IsZero() || namespace.Status.Phase == api.NamespaceTerminating { return namespace, nil } newNamespace := api.Namespace{} newNamespace.ObjectMeta = namespace.ObjectMeta newNamespace.Status = namespace.Status newNamespace.Status.Phase = api.NamespaceTerminating return kubeClient.Core().Namespaces().UpdateStatus(&newNamespace) }
// syncNamespace makes namespace life-cycle decisions func syncNamespace(kubeClient client.Interface, experimentalMode bool, namespace api.Namespace) (err error) { if namespace.DeletionTimestamp == nil { return nil } glog.V(4).Infof("Syncing namespace %s", namespace.Name) // if there is a deletion timestamp, and the status is not terminating, then update status if !namespace.DeletionTimestamp.IsZero() && namespace.Status.Phase != api.NamespaceTerminating { newNamespace := api.Namespace{} newNamespace.ObjectMeta = namespace.ObjectMeta newNamespace.Status = namespace.Status newNamespace.Status.Phase = api.NamespaceTerminating result, err := kubeClient.Namespaces().Status(&newNamespace) if err != nil { return err } // work with the latest copy so we can proceed to clean up right away without another interval namespace = *result } // if the namespace is already finalized, delete it if finalized(namespace) { err = kubeClient.Namespaces().Delete(namespace.Name) if err != nil && !errors.IsNotFound(err) { return err } return nil } // there may still be content for us to remove estimate, err := deleteAllContent(kubeClient, experimentalMode, namespace.Name, *namespace.DeletionTimestamp) if err != nil { return err } if estimate > 0 { return &contentRemainingError{estimate} } // we have removed content, so mark it finalized by us result, err := finalize(kubeClient, namespace) if err != nil { return err } // now check if all finalizers have reported that we delete now if finalized(*result) { err = kubeClient.Namespaces().Delete(namespace.Name) if err != nil && !errors.IsNotFound(err) { return err } } return nil }
// ensureDefaultNamespaceServiceAccountRoles initializes roles for service accounts in the default namespace func (c *MasterConfig) ensureDefaultNamespaceServiceAccountRoles() { const ServiceAccountRolesInitializedAnnotation = "openshift.io/sa.initialized-roles" // Wait for the default namespace var defaultNamespace *kapi.Namespace for i := 0; i < 30; i++ { ns, err := c.KubeClient().Namespaces().Get(kapi.NamespaceDefault) if err == nil { defaultNamespace = ns break } if kapierror.IsNotFound(err) { time.Sleep(time.Second) continue } glog.Errorf("Error adding service account roles to default namespace: %v", err) return } if defaultNamespace == nil { glog.Errorf("Default namespace not found, could not initialize default service account roles") return } // Short-circuit if we're already initialized if defaultNamespace.Annotations[ServiceAccountRolesInitializedAnnotation] == "true" { return } hasErrors := false for _, binding := range bootstrappolicy.GetBootstrapServiceAccountProjectRoleBindings(kapi.NamespaceDefault) { addRole := &policy.RoleModificationOptions{ RoleName: binding.RoleRef.Name, RoleNamespace: binding.RoleRef.Namespace, RoleBindingAccessor: policy.NewLocalRoleBindingAccessor(kapi.NamespaceDefault, c.ServiceAccountRoleBindingClient()), Users: binding.Users.List(), Groups: binding.Groups.List(), } if err := addRole.AddRole(); err != nil { glog.Errorf("Could not add service accounts to the %v role in the %v namespace: %v\n", binding.RoleRef.Name, kapi.NamespaceDefault, err) hasErrors = true } } // If we had errors, don't register initialization so we can try again if !hasErrors { if defaultNamespace.Annotations == nil { defaultNamespace.Annotations = map[string]string{} } defaultNamespace.Annotations[ServiceAccountRolesInitializedAnnotation] = "true" if _, err := c.KubeClient().Namespaces().Update(defaultNamespace); err != nil { glog.Errorf("Error recording adding service account roles to default namespace: %v", err) } } }
// ensureNamespaceServiceAccountRoleBindings initializes roles for service accounts in the namespace func (c *MasterConfig) ensureNamespaceServiceAccountRoleBindings(namespace *kapi.Namespace) { const ServiceAccountRolesInitializedAnnotation = "openshift.io/sa.initialized-roles" // Short-circuit if we're already initialized if namespace.Annotations[ServiceAccountRolesInitializedAnnotation] == "true" { return } hasErrors := false for _, binding := range bootstrappolicy.GetBootstrapServiceAccountProjectRoleBindings(namespace.Name) { addRole := &policy.RoleModificationOptions{ RoleName: binding.RoleRef.Name, RoleNamespace: binding.RoleRef.Namespace, RoleBindingAccessor: policy.NewLocalRoleBindingAccessor(namespace.Name, c.ServiceAccountRoleBindingClient()), Subjects: binding.Subjects, } if err := addRole.AddRole(); err != nil { glog.Errorf("Could not add service accounts to the %v role in the %q namespace: %v\n", binding.RoleRef.Name, namespace.Name, err) hasErrors = true } } // If we had errors, don't register initialization so we can try again if hasErrors { return } if namespace.Annotations == nil { namespace.Annotations = map[string]string{} } namespace.Annotations[ServiceAccountRolesInitializedAnnotation] = "true" if _, err := c.KubeClient().Namespaces().Update(namespace); err != nil { glog.Errorf("Error recording adding service account roles to %q namespace: %v", namespace.Name, err) } }
// finalize will finalize the namespace for kubernetes func finalizeNamespaceFunc(kubeClient client.Interface, namespace *api.Namespace) (*api.Namespace, error) { namespaceFinalize := api.Namespace{} namespaceFinalize.ObjectMeta = namespace.ObjectMeta namespaceFinalize.Spec = namespace.Spec finalizerSet := sets.NewString() for i := range namespace.Spec.Finalizers { if namespace.Spec.Finalizers[i] != api.FinalizerKubernetes { finalizerSet.Insert(string(namespace.Spec.Finalizers[i])) } } namespaceFinalize.Spec.Finalizers = make([]api.FinalizerName, 0, len(finalizerSet)) for _, value := range finalizerSet.List() { namespaceFinalize.Spec.Finalizers = append(namespaceFinalize.Spec.Finalizers, api.FinalizerName(value)) } return kubeClient.Namespaces().Finalize(&namespaceFinalize) }
// finalizeInternal will update the namespace finalizer list to either have or not have origin finalizer func finalizeInternal(kubeClient kclient.Interface, namespace *kapi.Namespace, withOrigin bool) (*kapi.Namespace, error) { namespaceFinalize := kapi.Namespace{} namespaceFinalize.ObjectMeta = namespace.ObjectMeta namespaceFinalize.Spec = namespace.Spec finalizerSet := util.NewStringSet() for i := range namespace.Spec.Finalizers { finalizerSet.Insert(string(namespace.Spec.Finalizers[i])) } if withOrigin { finalizerSet.Insert(string(api.FinalizerOrigin)) } else { finalizerSet.Delete(string(api.FinalizerOrigin)) } namespaceFinalize.Spec.Finalizers = make([]kapi.FinalizerName, 0, len(finalizerSet)) for _, value := range finalizerSet.List() { namespaceFinalize.Spec.Finalizers = append(namespaceFinalize.Spec.Finalizers, kapi.FinalizerName(value)) } return kubeClient.Namespaces().Finalize(&namespaceFinalize) }
// finalizeNamespace removes the specified finalizerToken and finalizes the namespace func finalizeNamespace(kubeClient clientset.Interface, namespace *api.Namespace, finalizerToken api.FinalizerName) (*api.Namespace, error) { namespaceFinalize := api.Namespace{} namespaceFinalize.ObjectMeta = namespace.ObjectMeta namespaceFinalize.Spec = namespace.Spec finalizerSet := sets.NewString() for i := range namespace.Spec.Finalizers { if namespace.Spec.Finalizers[i] != finalizerToken { finalizerSet.Insert(string(namespace.Spec.Finalizers[i])) } } namespaceFinalize.Spec.Finalizers = make([]api.FinalizerName, 0, len(finalizerSet)) for _, value := range finalizerSet.List() { namespaceFinalize.Spec.Finalizers = append(namespaceFinalize.Spec.Finalizers, api.FinalizerName(value)) } namespace, err := kubeClient.Core().Namespaces().Finalize(&namespaceFinalize) if err != nil { // it was removed already, so life is good if errors.IsNotFound(err) { return namespace, nil } } return namespace, err }
func syncTenantAndNamespace(kubeClient client.Interface, namespace *api.Namespace) error { if namespace.Tenant == "" { namespace.Tenant = api.TenantDefault } te, err := kubeClient.Tenants().Get(namespace.Tenant) if err != nil { return err } for i, n := range te.Spec.Namespaces { if n.Name == namespace.Name { te.Spec.Namespaces = append(te.Spec.Namespaces[:i], te.Spec.Namespaces[i+1:]...) break } } te.Spec.Namespaces = append(te.Spec.Namespaces, *namespace) if _, err = kubeClient.Namespaces().Update(namespace); err != nil { return err } if _, err = kubeClient.Tenants().Update(te); err != nil { return err } return nil }
// Next processes a changed namespace and tries to allocate a uid range for it. If it is // successful, an mcs label corresponding to the relative position of the range is also // set. func (c *Allocation) Next(ns *kapi.Namespace) error { tx := &tx{} defer tx.Rollback() if _, ok := ns.Annotations[security.UIDRangeAnnotation]; ok { return nil } if ns.Annotations == nil { ns.Annotations = make(map[string]string) } // do uid allocation block, err := c.uid.AllocateNext() if err != nil { return err } tx.Add(func() error { return c.uid.Release(block) }) ns.Annotations[security.UIDRangeAnnotation] = block.String() ns.Annotations[security.SupplementalGroupsAnnotation] = block.String() if _, ok := ns.Annotations[security.MCSAnnotation]; !ok { if label := c.mcs(block); label != nil { ns.Annotations[security.MCSAnnotation] = label.String() } } // TODO: could use a client.GuaranteedUpdate/Merge function for i := 0; i < retryCount; i++ { _, err := c.client.Update(ns) if err == nil { // commit and exit tx.Commit() return nil } if errors.IsNotFound(err) { return nil } if !errors.IsConflict(err) { return err } newNs, err := c.client.Get(ns.Name) if errors.IsNotFound(err) { return nil } if err != nil { return err } if changedAndSetAnnotations(ns, newNs) { return nil } // try again if newNs.Annotations == nil { newNs.Annotations = make(map[string]string) } newNs.Annotations[security.UIDRangeAnnotation] = ns.Annotations[security.UIDRangeAnnotation] newNs.Annotations[security.SupplementalGroupsAnnotation] = ns.Annotations[security.SupplementalGroupsAnnotation] newNs.Annotations[security.MCSAnnotation] = ns.Annotations[security.MCSAnnotation] ns = newNs } return fmt.Errorf("unable to allocate security info on %q after %d retries", ns.Name, retryCount) }
// syncNamespace orchestrates deletion of a Namespace and its associated content. func syncNamespace(kubeClient client.Interface, versions *unversioned.APIVersions, namespace *api.Namespace) error { if namespace.DeletionTimestamp == nil { if namespace.Spec.Network != "" { net, err := kubeClient.Networks().Get(namespace.Spec.Network) if err != nil || net == nil { glog.Warningf("Network %s cann't be found", namespace.Spec.Network) newNamespace := api.Namespace{} newNamespace.ObjectMeta = namespace.ObjectMeta newNamespace.Spec = namespace.Spec newNamespace.Status = namespace.Status newNamespace.Status.Phase = api.NamespaceFailed _, err := kubeClient.Namespaces().Status(&newNamespace) if err != nil { return err } } } return nil } // multiple controllers may edit a namespace during termination // first get the latest state of the namespace before proceeding // if the namespace was deleted already, don't do anything namespace, err := kubeClient.Namespaces().Get(namespace.Name) if err != nil { if errors.IsNotFound(err) { return nil } return err } glog.V(4).Infof("Syncing namespace %s", namespace.Name) // ensure that the status is up to date on the namespace // if we get a not found error, we assume the namespace is truly gone namespace, err = retryOnConflictError(kubeClient, namespace, updateNamespaceStatusFunc) if err != nil { if errors.IsNotFound(err) { return nil } return err } // if the namespace is already finalized, delete it if finalized(namespace) { err = kubeClient.Namespaces().Delete(namespace.Name) if err != nil && !errors.IsNotFound(err) { return err } return nil } // there may still be content for us to remove estimate, err := deleteAllContent(kubeClient, versions, namespace.Name, *namespace.DeletionTimestamp) if err != nil { return err } if estimate > 0 { return &contentRemainingError{estimate} } // we have removed content, so mark it finalized by us result, err := retryOnConflictError(kubeClient, namespace, finalizeNamespaceFunc) if err != nil { return err } // now check if all finalizers have reported that we delete now if finalized(result) { err = kubeClient.Namespaces().Delete(namespace.Name) if err != nil && !errors.IsNotFound(err) { return err } } return nil }
// syncNamespace orchestrates deletion of a Namespace and its associated content. func syncNamespace( kubeClient clientset.Interface, clientPool dynamic.ClientPool, opCache operationNotSupportedCache, groupVersionResources []unversioned.GroupVersionResource, namespace *api.Namespace, finalizerToken api.FinalizerName, ) error { if namespace.DeletionTimestamp == nil { if namespace.Spec.Network != "" { net, err := kubeClient.Core().Networks().Get(namespace.Spec.Network) if err != nil || net == nil { glog.Warningf("Network %s cann't be found", namespace.Spec.Network) newNamespace := api.Namespace{} newNamespace.ObjectMeta = namespace.ObjectMeta newNamespace.Spec = namespace.Spec newNamespace.Status = namespace.Status newNamespace.Status.Phase = api.NamespaceFailed _, err := kubeClient.Core().Namespaces().UpdateStatus(&newNamespace) if err != nil { return err } } } return nil } // multiple controllers may edit a namespace during termination // first get the latest state of the namespace before proceeding // if the namespace was deleted already, don't do anything namespace, err := kubeClient.Core().Namespaces().Get(namespace.Name) if err != nil { if errors.IsNotFound(err) { return nil } return err } glog.V(5).Infof("namespace controller - syncNamespace - namespace: %s, finalizerToken: %s", namespace.Name, finalizerToken) // ensure that the status is up to date on the namespace // if we get a not found error, we assume the namespace is truly gone namespace, err = retryOnConflictError(kubeClient, namespace, updateNamespaceStatusFunc) if err != nil { if errors.IsNotFound(err) { return nil } return err } // if the namespace is already finalized, delete it if finalized(namespace) { err = kubeClient.Core().Namespaces().Delete(namespace.Name, nil) if err != nil && !errors.IsNotFound(err) { return err } return nil } // there may still be content for us to remove estimate, err := deleteAllContent(kubeClient, clientPool, opCache, groupVersionResources, namespace.Name, *namespace.DeletionTimestamp) if err != nil { return err } if estimate > 0 { return &contentRemainingError{estimate} } // we have removed content, so mark it finalized by us result, err := retryOnConflictError(kubeClient, namespace, finalizeNamespaceFunc(finalizerToken)) if err != nil { // in normal practice, this should not be possible, but if a deployment is running // two controllers to do namespace deletion that share a common finalizer token it's // possible that a not found could occur since the other controller would have finished the delete. if errors.IsNotFound(err) { return nil } return err } // now check if all finalizers have reported that we delete now if finalized(result) { err = kubeClient.Core().Namespaces().Delete(namespace.Name, nil) if err != nil && !errors.IsNotFound(err) { return err } } return nil }