-
Notifications
You must be signed in to change notification settings - Fork 4.8k
Delete project should delete all project related resources #1601
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,7 +5,9 @@ import ( | |
| kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client" | ||
| "github.com/GoogleCloudPlatform/kubernetes/pkg/fields" | ||
| "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" | ||
| "github.com/GoogleCloudPlatform/kubernetes/pkg/util" | ||
| osclient "github.com/openshift/origin/pkg/client" | ||
| "github.com/openshift/origin/pkg/project/api" | ||
| ) | ||
|
|
||
| // NamespaceController is responsible for participating in Kubernetes Namespace termination | ||
|
|
@@ -23,17 +25,61 @@ type fatalError string | |
| func (e fatalError) Error() string { return "fatal error handling namespace: " + string(e) } | ||
|
|
||
| // Handle processes a namespace and deletes content in origin if its terminating | ||
| func (c *NamespaceController) Handle(namespace *kapi.Namespace) error { | ||
| // ignore namespaces that are not terminating | ||
| func (c *NamespaceController) Handle(namespace *kapi.Namespace) (err error) { | ||
| // if namespace is not terminating, ignore it | ||
| if namespace.Status.Phase != kapi.NamespaceTerminating { | ||
| return nil | ||
| } | ||
|
|
||
| deleteAllContent(c.Client, namespace.Name) | ||
| // TODO: finalize namespace (remove openshift.com/origin) | ||
| // if we already processed this namespace, ignore it | ||
| if finalized(namespace) { | ||
| return nil | ||
| } | ||
|
|
||
| // there may still be content for us to remove | ||
| err = deleteAllContent(c.Client, namespace.Name) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| // we have removed content, so mark it finalized by us | ||
| err = finalize(c.KubeClient, namespace) | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| // finalized returns true if the spec.finalizers does not contain the project finalizer | ||
| func finalized(namespace *kapi.Namespace) bool { | ||
| for i := range namespace.Spec.Finalizers { | ||
| if api.FinalizerProject == namespace.Spec.Finalizers[i] { | ||
| return false | ||
| } | ||
| } | ||
| return true | ||
| } | ||
|
|
||
| // finalize will finalize the namespace for kubernetes | ||
| func finalize(kubeClient kclient.Interface, namespace *kapi.Namespace) error { | ||
| namespaceFinalize := kapi.Namespace{} | ||
| namespaceFinalize.ObjectMeta = namespace.ObjectMeta | ||
| namespaceFinalize.Spec = namespace.Spec | ||
| finalizerSet := util.NewStringSet() | ||
| for i := range namespace.Spec.Finalizers { | ||
| if namespace.Spec.Finalizers[i] != api.FinalizerProject { | ||
| finalizerSet.Insert(string(namespace.Spec.Finalizers[i])) | ||
| } | ||
| } | ||
| namespaceFinalize.Spec.Finalizers = make([]kapi.FinalizerName, 0, len(finalizerSet)) | ||
| for _, value := range finalizerSet.List() { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does the order of finalizers matter? This effectively reorders the list.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Order does not matter. I should change upstream to make sure the field cannot have duplicate values though. Will make that change in Kube repo Sent from my iPhone
|
||
| namespaceFinalize.Spec.Finalizers = append(namespaceFinalize.Spec.Finalizers, kapi.FinalizerName(value)) | ||
| } | ||
| _, err := kubeClient.Namespaces().Finalize(&namespaceFinalize) | ||
| return err | ||
| } | ||
|
|
||
| // deleteAllContent will purge all content in openshift in the specified namespace | ||
| func deleteAllContent(client osclient.Interface, namespace string) (err error) { | ||
| err = deleteBuildConfigs(client, namespace) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,135 @@ | ||
| package proxy | ||
|
|
||
| import ( | ||
| "fmt" | ||
|
|
||
| kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api" | ||
| kerrors "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors" | ||
| "github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest" | ||
| kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client" | ||
| "github.com/GoogleCloudPlatform/kubernetes/pkg/fields" | ||
| "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" | ||
| "github.com/GoogleCloudPlatform/kubernetes/pkg/runtime" | ||
|
|
||
| "github.com/openshift/origin/pkg/project/api" | ||
| projectauth "github.com/openshift/origin/pkg/project/auth" | ||
| projectregistry "github.com/openshift/origin/pkg/project/registry/project" | ||
| ) | ||
|
|
||
| type REST struct { | ||
| // client can modify Kubernetes namespaces | ||
| client kclient.NamespaceInterface | ||
| // lister can enumerate project lists that enforce policy | ||
| lister projectauth.Lister | ||
| // Allows extended behavior during creation, required | ||
| createStrategy rest.RESTCreateStrategy | ||
| // Allows extended behavior during updates, required | ||
| updateStrategy rest.RESTUpdateStrategy | ||
| } | ||
|
|
||
| // NewREST returns a RESTStorage object that will work against Project resources | ||
| func NewREST(client kclient.NamespaceInterface, lister projectauth.Lister) *REST { | ||
| return &REST{ | ||
| client: client, | ||
| lister: lister, | ||
| createStrategy: projectregistry.Strategy, | ||
| updateStrategy: projectregistry.Strategy, | ||
| } | ||
| } | ||
|
|
||
| // New returns a new Project | ||
| func (s *REST) New() runtime.Object { | ||
| return &api.Project{} | ||
| } | ||
|
|
||
| // NewList returns a new ProjectList | ||
| func (*REST) NewList() runtime.Object { | ||
| return &api.ProjectList{} | ||
| } | ||
|
|
||
| // convertNamespace transforms a Namespace into a Project | ||
| func convertNamespace(namespace *kapi.Namespace) *api.Project { | ||
| displayName := namespace.Annotations["displayname"] | ||
| return &api.Project{ | ||
| ObjectMeta: namespace.ObjectMeta, | ||
| DisplayName: displayName, | ||
| Spec: api.ProjectSpec{ | ||
| Finalizers: namespace.Spec.Finalizers, | ||
| }, | ||
| Status: api.ProjectStatus{ | ||
| Phase: namespace.Status.Phase, | ||
| }, | ||
| } | ||
| } | ||
|
|
||
| // convertProject transforms a Project into a Namespace | ||
| func convertProject(project *api.Project) *kapi.Namespace { | ||
| namespace := &kapi.Namespace{ | ||
| ObjectMeta: project.ObjectMeta, | ||
| Spec: kapi.NamespaceSpec{ | ||
| Finalizers: project.Spec.Finalizers, | ||
| }, | ||
| Status: kapi.NamespaceStatus{ | ||
| Phase: project.Status.Phase, | ||
| }, | ||
| } | ||
| if namespace.Annotations == nil { | ||
| namespace.Annotations = map[string]string{} | ||
| } | ||
| namespace.Annotations["displayname"] = project.DisplayName | ||
| return namespace | ||
| } | ||
|
|
||
| // convertNamespaceList transforms a NamespaceList into a ProjectList | ||
| func convertNamespaceList(namespaceList *kapi.NamespaceList) *api.ProjectList { | ||
| projects := &api.ProjectList{} | ||
| for _, n := range namespaceList.Items { | ||
| projects.Items = append(projects.Items, *convertNamespace(&n)) | ||
| } | ||
| return projects | ||
| } | ||
|
|
||
| // List retrieves a list of Projects that match label. | ||
| func (s *REST) List(ctx kapi.Context, label labels.Selector, field fields.Selector) (runtime.Object, error) { | ||
| user, ok := kapi.UserFrom(ctx) | ||
| if !ok { | ||
| return nil, kerrors.NewForbidden("Project", "", fmt.Errorf("Unable to list projects without a user on the context")) | ||
| } | ||
| namespaceList, err := s.lister.List(user) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| return convertNamespaceList(namespaceList), nil | ||
| } | ||
|
|
||
| // Get retrieves a Project by name | ||
| func (s *REST) Get(ctx kapi.Context, name string) (runtime.Object, error) { | ||
| namespace, err := s.client.Get(name) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| return convertNamespace(namespace), nil | ||
| } | ||
|
|
||
| // Create registers the given Project. | ||
| func (s *REST) Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, error) { | ||
| project, ok := obj.(*api.Project) | ||
| if !ok { | ||
| return nil, fmt.Errorf("not a project: %#v", obj) | ||
| } | ||
| kapi.FillObjectMetaSystemFields(ctx, &project.ObjectMeta) | ||
| s.createStrategy.PrepareForCreate(obj) | ||
| if errs := s.createStrategy.Validate(ctx, obj); len(errs) > 0 { | ||
| return nil, kerrors.NewInvalid("project", project.Name, errs) | ||
| } | ||
| namespace, err := s.client.Create(convertProject(project)) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| return convertNamespace(namespace), nil | ||
| } | ||
|
|
||
| // Delete deletes a Project specified by its name | ||
| func (s *REST) Delete(ctx kapi.Context, name string) (runtime.Object, error) { | ||
| return &kapi.Status{Status: kapi.StatusSuccess}, s.client.Delete(name) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems slow. Does this mean that my namespace may not be cleaned up for a minute? During that minute, can I keep creating things?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This means you will see a namespace appear in "Terminating" status for ~1 min. The value is the same default as upstream. You are unable to create content in a terminating namespace. There are two controllers that need to run in order to delete content (the k8s one, and the openshift one) so it will take ~1-2 minutes for something to truly be gone.