Browse code

Implement scaffolding to purge project content on deletion

derekwaynecarr authored on 2015/03/26 03:16:58
Showing 6 changed files
... ...
@@ -71,6 +71,7 @@ import (
71 71
 	clientregistry "github.com/openshift/origin/pkg/oauth/registry/client"
72 72
 	clientauthorizationregistry "github.com/openshift/origin/pkg/oauth/registry/clientauthorization"
73 73
 	oauthetcd "github.com/openshift/origin/pkg/oauth/registry/etcd"
74
+	projectcontroller "github.com/openshift/origin/pkg/project/controller"
74 75
 	projectregistry "github.com/openshift/origin/pkg/project/registry/project"
75 76
 	routeallocationcontroller "github.com/openshift/origin/pkg/route/controller/allocation"
76 77
 	routeetcd "github.com/openshift/origin/pkg/route/registry/etcd"
... ...
@@ -465,6 +466,17 @@ func (c *MasterConfig) RunProjectAuthorizationCache() {
465 465
 	c.ProjectAuthorizationCache.Run(period)
466 466
 }
467 467
 
468
+// RunOriginNamespaceController starts the controller that takes part in namespace termination of openshift content
469
+func (c *MasterConfig) RunOriginNamespaceController() {
470
+	osclient, kclient := c.OriginNamespaceControllerClients()
471
+	factory := projectcontroller.NamespaceControllerFactory{
472
+		Client:     osclient,
473
+		KubeClient: kclient,
474
+	}
475
+	controller := factory.Create()
476
+	controller.Run()
477
+}
478
+
468 479
 // RunPolicyCache starts the policy cache
469 480
 func (c *MasterConfig) RunPolicyCache() {
470 481
 	c.PolicyCache.Run()
... ...
@@ -295,3 +295,10 @@ func (c *MasterConfig) DeploymentConfigChangeControllerClients() (*osclient.Clie
295 295
 func (c *MasterConfig) DeploymentImageChangeControllerClient() *osclient.Client {
296 296
 	return c.OSClient
297 297
 }
298
+
299
+// OriginNamespaceControllerClients returns a client for openshift and kubernetes.
300
+// The openshift client object must have authority to delete openshift content in any namespace
301
+// The kubernetes client object must have authority to execute a finalize request on a namespace
302
+func (c *MasterConfig) OriginNamespaceControllerClients() (*osclient.Client, *kclient.Client) {
303
+	return c.OSClient, c.KubernetesClient
304
+}
... ...
@@ -404,6 +404,7 @@ func StartMaster(openshiftMasterConfig *configapi.MasterConfig) error {
404 404
 	openshiftConfig.RunDeploymentConfigChangeController()
405 405
 	openshiftConfig.RunDeploymentImageChangeTriggerController()
406 406
 	openshiftConfig.RunImageImportController()
407
+	openshiftConfig.RunOriginNamespaceController()
407 408
 	openshiftConfig.RunProjectAuthorizationCache()
408 409
 
409 410
 	return nil
410 411
new file mode 100644
... ...
@@ -0,0 +1,220 @@
0
+package controller
1
+
2
+import (
3
+	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
4
+	kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
5
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
6
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
7
+	osclient "github.com/openshift/origin/pkg/client"
8
+)
9
+
10
+// NamespaceController is responsible for participating in Kubernetes Namespace termination
11
+// Use the NamespaceControllerFactory to create this controller.
12
+type NamespaceController struct {
13
+	// Client is an OpenShift client.
14
+	Client osclient.Interface
15
+	// KubeClient is a Kubernetes client.
16
+	KubeClient kclient.Interface
17
+}
18
+
19
+// fatalError is an error which can't be retried.
20
+type fatalError string
21
+
22
+func (e fatalError) Error() string { return "fatal error handling namespace: " + string(e) }
23
+
24
+// Handle processes a namespace and deletes content in origin if its terminating
25
+func (c *NamespaceController) Handle(namespace *kapi.Namespace) error {
26
+	// ignore namespaces that are not terminating
27
+	if namespace.Status.Phase != kapi.NamespaceTerminating {
28
+		return nil
29
+	}
30
+
31
+	deleteAllContent(c.Client, namespace.Name)
32
+	// TODO: finalize namespace (remove openshift.com/origin)
33
+	return nil
34
+}
35
+
36
+// deleteAllContent will purge all content in openshift in the specified namespace
37
+func deleteAllContent(client osclient.Interface, namespace string) (err error) {
38
+	err = deleteBuildConfigs(client, namespace)
39
+	if err != nil {
40
+		return err
41
+	}
42
+	err = deleteBuilds(client, namespace)
43
+	if err != nil {
44
+		return err
45
+	}
46
+	err = deleteDeploymentConfigs(client, namespace)
47
+	if err != nil {
48
+		return err
49
+	}
50
+	err = deleteDeployments(client, namespace)
51
+	if err != nil {
52
+		return err
53
+	}
54
+	err = deleteImageRepositories(client, namespace)
55
+	if err != nil {
56
+		return err
57
+	}
58
+	err = deletePolicies(client, namespace)
59
+	if err != nil {
60
+		return err
61
+	}
62
+	err = deletePolicyBindings(client, namespace)
63
+	if err != nil {
64
+		return err
65
+	}
66
+	err = deleteRoleBindings(client, namespace)
67
+	if err != nil {
68
+		return err
69
+	}
70
+	err = deleteRoles(client, namespace)
71
+	if err != nil {
72
+		return err
73
+	}
74
+	err = deleteRoutes(client, namespace)
75
+	if err != nil {
76
+		return err
77
+	}
78
+	return nil
79
+}
80
+
81
+func deleteRoutes(client osclient.Interface, ns string) error {
82
+	items, err := client.Routes(ns).List(labels.Everything(), fields.Everything())
83
+	if err != nil {
84
+		return err
85
+	}
86
+	for i := range items.Items {
87
+		err := client.Routes(ns).Delete(items.Items[i].Name)
88
+		if err != nil {
89
+			return err
90
+		}
91
+	}
92
+	return nil
93
+}
94
+
95
+func deleteRoles(client osclient.Interface, ns string) error {
96
+	items, err := client.Roles(ns).List(labels.Everything(), fields.Everything())
97
+	if err != nil {
98
+		return err
99
+	}
100
+	for i := range items.Items {
101
+		err := client.Roles(ns).Delete(items.Items[i].Name)
102
+		if err != nil {
103
+			return err
104
+		}
105
+	}
106
+	return nil
107
+}
108
+
109
+func deleteRoleBindings(client osclient.Interface, ns string) error {
110
+	items, err := client.RoleBindings(ns).List(labels.Everything(), fields.Everything())
111
+	if err != nil {
112
+		return err
113
+	}
114
+	for i := range items.Items {
115
+		err := client.RoleBindings(ns).Delete(items.Items[i].Name)
116
+		if err != nil {
117
+			return err
118
+		}
119
+	}
120
+	return nil
121
+}
122
+
123
+func deletePolicyBindings(client osclient.Interface, ns string) error {
124
+	items, err := client.PolicyBindings(ns).List(labels.Everything(), fields.Everything())
125
+	if err != nil {
126
+		return err
127
+	}
128
+	for i := range items.Items {
129
+		err := client.PolicyBindings(ns).Delete(items.Items[i].Name)
130
+		if err != nil {
131
+			return err
132
+		}
133
+	}
134
+	return nil
135
+}
136
+
137
+func deletePolicies(client osclient.Interface, ns string) error {
138
+	items, err := client.Policies(ns).List(labels.Everything(), fields.Everything())
139
+	if err != nil {
140
+		return err
141
+	}
142
+	for i := range items.Items {
143
+		err := client.Policies(ns).Delete(items.Items[i].Name)
144
+		if err != nil {
145
+			return err
146
+		}
147
+	}
148
+	return nil
149
+}
150
+
151
+func deleteImageRepositories(client osclient.Interface, ns string) error {
152
+	items, err := client.ImageRepositories(ns).List(labels.Everything(), fields.Everything())
153
+	if err != nil {
154
+		return err
155
+	}
156
+	for i := range items.Items {
157
+		err := client.ImageRepositories(ns).Delete(items.Items[i].Name)
158
+		if err != nil {
159
+			return err
160
+		}
161
+	}
162
+	return nil
163
+}
164
+
165
+func deleteDeployments(client osclient.Interface, ns string) error {
166
+	items, err := client.Deployments(ns).List(labels.Everything(), fields.Everything())
167
+	if err != nil {
168
+		return err
169
+	}
170
+	for i := range items.Items {
171
+		err := client.Deployments(ns).Delete(items.Items[i].Name)
172
+		if err != nil {
173
+			return err
174
+		}
175
+	}
176
+	return nil
177
+}
178
+
179
+func deleteDeploymentConfigs(client osclient.Interface, ns string) error {
180
+	items, err := client.DeploymentConfigs(ns).List(labels.Everything(), fields.Everything())
181
+	if err != nil {
182
+		return err
183
+	}
184
+	for i := range items.Items {
185
+		err := client.DeploymentConfigs(ns).Delete(items.Items[i].Name)
186
+		if err != nil {
187
+			return err
188
+		}
189
+	}
190
+	return nil
191
+}
192
+
193
+func deleteBuilds(client osclient.Interface, ns string) error {
194
+	items, err := client.Builds(ns).List(labels.Everything(), fields.Everything())
195
+	if err != nil {
196
+		return err
197
+	}
198
+	for i := range items.Items {
199
+		err := client.Builds(ns).Delete(items.Items[i].Name)
200
+		if err != nil {
201
+			return err
202
+		}
203
+	}
204
+	return nil
205
+}
206
+
207
+func deleteBuildConfigs(client osclient.Interface, ns string) error {
208
+	items, err := client.BuildConfigs(ns).List(labels.Everything(), fields.Everything())
209
+	if err != nil {
210
+		return err
211
+	}
212
+	for i := range items.Items {
213
+		err := client.BuildConfigs(ns).Delete(items.Items[i].Name)
214
+		if err != nil {
215
+			return err
216
+		}
217
+	}
218
+	return nil
219
+}
0 220
new file mode 100644
... ...
@@ -0,0 +1,98 @@
0
+package controller
1
+
2
+import (
3
+	"testing"
4
+
5
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
6
+	kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
7
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
8
+
9
+	osclient "github.com/openshift/origin/pkg/client"
10
+)
11
+
12
+func TestSyncNamespaceThatIsTerminating(t *testing.T) {
13
+	mockKubeClient := &kclient.Fake{}
14
+	mockOriginClient := &osclient.Fake{}
15
+	nm := NamespaceController{
16
+		KubeClient: mockKubeClient,
17
+		Client:     mockOriginClient,
18
+	}
19
+	//now := util.Now()
20
+	testNamespace := &api.Namespace{
21
+		ObjectMeta: api.ObjectMeta{
22
+			Name:            "test",
23
+			ResourceVersion: "1",
24
+			//			DeletionTimestamp: &now,
25
+		},
26
+		//		Spec: api.NamespaceSpec{
27
+		//			Finalizers: []api.FinalizerName{"kubernetes"},
28
+		//		},
29
+		Status: api.NamespaceStatus{
30
+			Phase: api.NamespaceTerminating,
31
+		},
32
+	}
33
+	err := nm.Handle(testNamespace)
34
+	if err != nil {
35
+		t.Errorf("Unexpected error when handling namespace %v", err)
36
+	}
37
+
38
+	// TODO: we will expect a finalize namespace call after rebase
39
+	expectedActionSet := util.NewStringSet(
40
+		"list-buildconfig",
41
+		"list-policies",
42
+		"list-imagerepositries",
43
+		"list-policyBindings",
44
+		"list-roleBinding",
45
+		"list-role",
46
+		"list-routes",
47
+		"list-builds",
48
+		"list-deploymentconfig",
49
+		"list-deployment")
50
+	actionSet := util.NewStringSet()
51
+	for i := range mockKubeClient.Actions {
52
+		actionSet.Insert(mockKubeClient.Actions[i].Action)
53
+	}
54
+	for i := range mockOriginClient.Actions {
55
+		actionSet.Insert(mockOriginClient.Actions[i].Action)
56
+	}
57
+	if !actionSet.HasAll(expectedActionSet.List()...) {
58
+		t.Errorf("Expected actions: %v, but got: %v", expectedActionSet, actionSet)
59
+	}
60
+}
61
+
62
+func TestSyncNamespaceThatIsActive(t *testing.T) {
63
+	mockKubeClient := &kclient.Fake{}
64
+	mockOriginClient := &osclient.Fake{}
65
+	nm := NamespaceController{
66
+		KubeClient: mockKubeClient,
67
+		Client:     mockOriginClient,
68
+	}
69
+	//now := util.Now()
70
+	testNamespace := &api.Namespace{
71
+		ObjectMeta: api.ObjectMeta{
72
+			Name:            "test",
73
+			ResourceVersion: "1",
74
+			//      DeletionTimestamp: &now,
75
+		},
76
+		//    Spec: api.NamespaceSpec{
77
+		//      Finalizers: []api.FinalizerName{"kubernetes"},
78
+		//    },
79
+		Status: api.NamespaceStatus{
80
+			Phase: api.NamespaceActive,
81
+		},
82
+	}
83
+	err := nm.Handle(testNamespace)
84
+	if err != nil {
85
+		t.Errorf("Unexpected error when handling namespace %v", err)
86
+	}
87
+	actionSet := util.NewStringSet()
88
+	for i := range mockKubeClient.Actions {
89
+		actionSet.Insert(mockKubeClient.Actions[i].Action)
90
+	}
91
+	for i := range mockOriginClient.Actions {
92
+		actionSet.Insert(mockOriginClient.Actions[i].Action)
93
+	}
94
+	if len(actionSet) != 0 {
95
+		t.Errorf("Expected no action from controller, but got: %v", actionSet)
96
+	}
97
+}
0 98
new file mode 100644
... ...
@@ -0,0 +1,65 @@
0
+package controller
1
+
2
+import (
3
+	"time"
4
+
5
+	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
6
+	kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
7
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/client/cache"
8
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
9
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
10
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
11
+	kutil "github.com/GoogleCloudPlatform/kubernetes/pkg/util"
12
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/watch"
13
+
14
+	osclient "github.com/openshift/origin/pkg/client"
15
+	controller "github.com/openshift/origin/pkg/controller"
16
+)
17
+
18
+type NamespaceControllerFactory struct {
19
+	// Client is an OpenShift client.
20
+	Client osclient.Interface
21
+	// KubeClient is a Kubernetes client.
22
+	KubeClient kclient.Interface
23
+}
24
+
25
+// Create creates a NamespaceController.
26
+func (factory *NamespaceControllerFactory) Create() controller.RunnableController {
27
+	namespaceLW := &cache.ListWatch{
28
+		ListFunc: func() (runtime.Object, error) {
29
+			return factory.KubeClient.Namespaces().List(labels.Everything())
30
+		},
31
+		WatchFunc: func(resourceVersion string) (watch.Interface, error) {
32
+			return factory.KubeClient.Namespaces().Watch(labels.Everything(), fields.Everything(), resourceVersion)
33
+		},
34
+	}
35
+	queue := cache.NewFIFO(cache.MetaNamespaceKeyFunc)
36
+	cache.NewReflector(namespaceLW, &kapi.Namespace{}, queue, 1*time.Minute).Run()
37
+
38
+	namespaceController := &NamespaceController{
39
+		Client:     factory.Client,
40
+		KubeClient: factory.KubeClient,
41
+	}
42
+
43
+	return &controller.RetryController{
44
+		Queue: queue,
45
+		RetryManager: controller.NewQueueRetryManager(
46
+			queue,
47
+			cache.MetaNamespaceKeyFunc,
48
+			func(obj interface{}, err error, count int) bool {
49
+				kutil.HandleError(err)
50
+				if _, isFatal := err.(fatalError); isFatal {
51
+					return false
52
+				}
53
+				if count > 0 {
54
+					return false
55
+				}
56
+				return true
57
+			},
58
+		),
59
+		Handle: func(obj interface{}) error {
60
+			namespace := obj.(*kapi.Namespace)
61
+			return namespaceController.Handle(namespace)
62
+		},
63
+	}
64
+}