Skip to content

Commit a6e4c38

Browse files
Project life-cycle updates
1 parent 15d4f21 commit a6e4c38

File tree

12 files changed

+267
-84
lines changed

12 files changed

+267
-84
lines changed

pkg/cmd/server/kubernetes/master.go

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,7 @@ import (
2424
latestschedulerapi "github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler/api/latest"
2525
"github.com/GoogleCloudPlatform/kubernetes/plugin/pkg/scheduler/factory"
2626

27-
// Namespace controller will be added
28-
_ "github.com/GoogleCloudPlatform/kubernetes/pkg/namespace"
27+
"github.com/GoogleCloudPlatform/kubernetes/pkg/namespace"
2928
)
3029

3130
const (
@@ -83,6 +82,12 @@ func (c *MasterConfig) InstallAPI(container *restful.Container) []string {
8382
}
8483
}
8584

85+
func (c *MasterConfig) RunNamespaceController() {
86+
namespaceController := namespace.NewNamespaceManager(c.KubeClient)
87+
namespaceController.Run(1 * time.Minute)
88+
glog.Infof("Started Kubernetes Namespace Manager")
89+
}
90+
8691
// RunReplicationController starts the Kubernetes replication controller sync loop
8792
func (c *MasterConfig) RunReplicationController() {
8893
controllerManager := controller.NewReplicationManager(c.KubeClient)

pkg/cmd/server/origin/master.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ import (
7575
clientauthorizationregistry "github.com/openshift/origin/pkg/oauth/registry/clientauthorization"
7676
oauthetcd "github.com/openshift/origin/pkg/oauth/registry/etcd"
7777
projectcontroller "github.com/openshift/origin/pkg/project/controller"
78-
projectregistry "github.com/openshift/origin/pkg/project/registry/project"
78+
projectproxy "github.com/openshift/origin/pkg/project/registry/proxy"
7979
routeallocationcontroller "github.com/openshift/origin/pkg/route/controller/allocation"
8080
routeetcd "github.com/openshift/origin/pkg/route/registry/etcd"
8181
routeregistry "github.com/openshift/origin/pkg/route/registry/route"
@@ -224,7 +224,7 @@ func (c *MasterConfig) InstallProtectedAPI(container *restful.Container) []strin
224224

225225
"routes": routeregistry.NewREST(routeEtcd, routeAllocator),
226226

227-
"projects": projectregistry.NewREST(kclient.Namespaces(), c.ProjectAuthorizationCache),
227+
"projects": projectproxy.NewREST(kclient.Namespaces(), c.ProjectAuthorizationCache),
228228

229229
"users": userStorage,
230230
"identities": identityStorage,

pkg/cmd/server/start/start_master.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,7 @@ func StartMaster(openshiftMasterConfig *configapi.MasterConfig) error {
339339
kubeConfig.RunEndpointController()
340340
kubeConfig.RunMinionController()
341341
kubeConfig.RunResourceQuotaManager()
342+
kubeConfig.RunNamespaceController()
342343

343344
} else {
344345
_, kubeConfig, err := configapi.GetKubeClient(openshiftMasterConfig.MasterClients.KubernetesKubeConfig)

pkg/project/api/types.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,15 @@ type ProjectList struct {
1111
Items []Project
1212
}
1313

14+
// These are internal finalizer values to Origin
15+
const (
16+
FinalizerProject kapi.FinalizerName = "openshift.com/project"
17+
)
18+
1419
// ProjectSpec describes the attributes on a Project
1520
type ProjectSpec struct {
21+
// Finalizers is an opaque list of values that must be empty to permanently remove object from storage
22+
Finalizers []kapi.FinalizerName
1623
}
1724

1825
// ProjectStatus is information about the current status of a Project

pkg/project/api/v1beta1/types.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,15 @@ type ProjectList struct {
1111
Items []Project `json:"items"`
1212
}
1313

14+
// These are internal finalizer values to Origin
15+
const (
16+
FinalizerProject kapi.FinalizerName = "openshift.com/project"
17+
)
18+
1419
// ProjectSpec describes the attributes on a Project
1520
type ProjectSpec struct {
21+
// Finalizers is an opaque list of values that must be empty to permanently remove object from storage
22+
Finalizers []kapi.FinalizerName `json:"finalizers,omitempty" description:"an opaque list of values that must be empty to permanently remove object from storage"`
1623
}
1724

1825
// ProjectStatus is information about the current status of a Project

pkg/project/api/validation/validation.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package validation
33
import (
44
"strings"
55

6+
kvalidation "github.com/GoogleCloudPlatform/kubernetes/pkg/api/validation"
67
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
78
"github.com/GoogleCloudPlatform/kubernetes/pkg/util/fielderrors"
89
"github.com/openshift/origin/pkg/project/api"
@@ -29,3 +30,12 @@ func ValidateProject(project *api.Project) fielderrors.ValidationErrorList {
2930
func validateNoNewLineOrTab(s string) bool {
3031
return !(strings.Contains(s, "\n") || strings.Contains(s, "\t"))
3132
}
33+
34+
// ValidateProjectUpdate tests to make sure a project update can be applied. Modifies newProject with immutable fields.
35+
func ValidateProjectUpdate(newProject *api.Project, oldProject *api.Project) fielderrors.ValidationErrorList {
36+
allErrs := fielderrors.ValidationErrorList{}
37+
allErrs = append(allErrs, kvalidation.ValidateObjectMetaUpdate(&oldProject.ObjectMeta, &newProject.ObjectMeta).Prefix("metadata")...)
38+
newProject.Spec.Finalizers = oldProject.Spec.Finalizers
39+
newProject.Status = oldProject.Status
40+
return allErrs
41+
}

pkg/project/controller/controller.go

Lines changed: 50 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import (
55
kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
66
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
77
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
8+
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
89
osclient "github.com/openshift/origin/pkg/client"
10+
"github.com/openshift/origin/pkg/project/api"
911
)
1012

1113
// NamespaceController is responsible for participating in Kubernetes Namespace termination
@@ -23,17 +25,61 @@ type fatalError string
2325
func (e fatalError) Error() string { return "fatal error handling namespace: " + string(e) }
2426

2527
// Handle processes a namespace and deletes content in origin if its terminating
26-
func (c *NamespaceController) Handle(namespace *kapi.Namespace) error {
27-
// ignore namespaces that are not terminating
28+
func (c *NamespaceController) Handle(namespace *kapi.Namespace) (err error) {
29+
// if namespace is not terminating, ignore it
2830
if namespace.Status.Phase != kapi.NamespaceTerminating {
2931
return nil
3032
}
3133

32-
deleteAllContent(c.Client, namespace.Name)
33-
// TODO: finalize namespace (remove openshift.com/origin)
34+
// if we already processed this namespace, ignore it
35+
if finalized(namespace) {
36+
return nil
37+
}
38+
39+
// there may still be content for us to remove
40+
err = deleteAllContent(c.Client, namespace.Name)
41+
if err != nil {
42+
return err
43+
}
44+
45+
// we have removed content, so mark it finalized by us
46+
err = finalize(c.KubeClient, namespace)
47+
if err != nil {
48+
return err
49+
}
50+
3451
return nil
3552
}
3653

54+
// finalized returns true if the spec.finalizers does not contain the project finalizer
55+
func finalized(namespace *kapi.Namespace) bool {
56+
for i := range namespace.Spec.Finalizers {
57+
if api.FinalizerProject == namespace.Spec.Finalizers[i] {
58+
return false
59+
}
60+
}
61+
return true
62+
}
63+
64+
// finalize will finalize the namespace for kubernetes
65+
func finalize(kubeClient kclient.Interface, namespace *kapi.Namespace) error {
66+
namespaceFinalize := kapi.Namespace{}
67+
namespaceFinalize.ObjectMeta = namespace.ObjectMeta
68+
namespaceFinalize.Spec = namespace.Spec
69+
finalizerSet := util.NewStringSet()
70+
for i := range namespace.Spec.Finalizers {
71+
if namespace.Spec.Finalizers[i] != api.FinalizerProject {
72+
finalizerSet.Insert(string(namespace.Spec.Finalizers[i]))
73+
}
74+
}
75+
namespaceFinalize.Spec.Finalizers = make([]kapi.FinalizerName, 0, len(finalizerSet))
76+
for _, value := range finalizerSet.List() {
77+
namespaceFinalize.Spec.Finalizers = append(namespaceFinalize.Spec.Finalizers, kapi.FinalizerName(value))
78+
}
79+
_, err := kubeClient.Namespaces().Finalize(&namespaceFinalize)
80+
return err
81+
}
82+
3783
// deleteAllContent will purge all content in openshift in the specified namespace
3884
func deleteAllContent(client osclient.Interface, namespace string) (err error) {
3985
err = deleteBuildConfigs(client, namespace)

pkg/project/controller/controller_test.go

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ package controller
33
import (
44
"testing"
55

6-
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
6+
kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
77
kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
88
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
9-
109
osclient "github.com/openshift/origin/pkg/client"
10+
"github.com/openshift/origin/pkg/project/api"
1111
)
1212

1313
func TestSyncNamespaceThatIsTerminating(t *testing.T) {
@@ -17,18 +17,18 @@ func TestSyncNamespaceThatIsTerminating(t *testing.T) {
1717
KubeClient: mockKubeClient,
1818
Client: mockOriginClient,
1919
}
20-
//now := util.Now()
21-
testNamespace := &api.Namespace{
22-
ObjectMeta: api.ObjectMeta{
23-
Name: "test",
24-
ResourceVersion: "1",
25-
// DeletionTimestamp: &now,
20+
now := util.Now()
21+
testNamespace := &kapi.Namespace{
22+
ObjectMeta: kapi.ObjectMeta{
23+
Name: "test",
24+
ResourceVersion: "1",
25+
DeletionTimestamp: &now,
2626
},
27-
// Spec: api.NamespaceSpec{
28-
// Finalizers: []api.FinalizerName{"kubernetes"},
29-
// },
30-
Status: api.NamespaceStatus{
31-
Phase: api.NamespaceTerminating,
27+
Spec: kapi.NamespaceSpec{
28+
Finalizers: []kapi.FinalizerName{kapi.FinalizerKubernetes, api.FinalizerProject},
29+
},
30+
Status: kapi.NamespaceStatus{
31+
Phase: kapi.NamespaceTerminating,
3232
},
3333
}
3434
err := nm.Handle(testNamespace)
@@ -67,18 +67,16 @@ func TestSyncNamespaceThatIsActive(t *testing.T) {
6767
KubeClient: mockKubeClient,
6868
Client: mockOriginClient,
6969
}
70-
//now := util.Now()
71-
testNamespace := &api.Namespace{
72-
ObjectMeta: api.ObjectMeta{
70+
testNamespace := &kapi.Namespace{
71+
ObjectMeta: kapi.ObjectMeta{
7372
Name: "test",
7473
ResourceVersion: "1",
75-
// DeletionTimestamp: &now,
7674
},
77-
// Spec: api.NamespaceSpec{
78-
// Finalizers: []api.FinalizerName{"kubernetes"},
79-
// },
80-
Status: api.NamespaceStatus{
81-
Phase: api.NamespaceActive,
75+
Spec: kapi.NamespaceSpec{
76+
Finalizers: []kapi.FinalizerName{kapi.FinalizerKubernetes, api.FinalizerProject},
77+
},
78+
Status: kapi.NamespaceStatus{
79+
Phase: kapi.NamespaceActive,
8280
},
8381
}
8482
err := nm.Handle(testNamespace)

pkg/project/registry/project/rest.go renamed to pkg/project/registry/proxy/proxy.go

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,40 @@
1-
package project
1+
package proxy
22

33
import (
44
"fmt"
55

66
kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
77
kerrors "github.com/GoogleCloudPlatform/kubernetes/pkg/api/errors"
8+
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest"
89
kclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
910
"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
1011
"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
1112
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
1213

1314
"github.com/openshift/origin/pkg/project/api"
14-
"github.com/openshift/origin/pkg/project/api/validation"
1515
projectauth "github.com/openshift/origin/pkg/project/auth"
16+
"github.com/openshift/origin/pkg/project/registry"
1617
)
1718

1819
type REST struct {
20+
// client can modify Kubernetes namespaces
1921
client kclient.NamespaceInterface
22+
// lister can enumerate project lists that enforce policy
2023
lister projectauth.Lister
24+
// Allows extended behavior during creation, required
25+
createStrategy rest.RESTCreateStrategy
26+
// Allows extended behavior during updates, required
27+
updateStrategy rest.RESTUpdateStrategy
2128
}
2229

2330
// NewREST returns a RESTStorage object that will work against Project resources
2431
func NewREST(client kclient.NamespaceInterface, lister projectauth.Lister) *REST {
25-
return &REST{client: client, lister: lister}
32+
return &REST{
33+
client: client,
34+
lister: lister,
35+
createStrategy: registry.Strategy,
36+
updateStrategy: registry.Strategy,
37+
}
2638
}
2739

2840
// New returns a new Project
@@ -41,7 +53,9 @@ func convertNamespace(namespace *kapi.Namespace) *api.Project {
4153
return &api.Project{
4254
ObjectMeta: namespace.ObjectMeta,
4355
DisplayName: displayName,
44-
Spec: api.ProjectSpec{},
56+
Spec: api.ProjectSpec{
57+
Finalizers: namespace.Spec.Finalizers,
58+
},
4559
Status: api.ProjectStatus{
4660
Phase: namespace.Status.Phase,
4761
},
@@ -52,6 +66,12 @@ func convertNamespace(namespace *kapi.Namespace) *api.Project {
5266
func convertProject(project *api.Project) *kapi.Namespace {
5367
namespace := &kapi.Namespace{
5468
ObjectMeta: project.ObjectMeta,
69+
Spec: kapi.NamespaceSpec{
70+
Finalizers: project.Spec.Finalizers,
71+
},
72+
Status: kapi.NamespaceStatus{
73+
Phase: project.Status.Phase,
74+
},
5575
}
5676
if namespace.Annotations == nil {
5777
namespace.Annotations = map[string]string{}
@@ -98,7 +118,8 @@ func (s *REST) Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, err
98118
return nil, fmt.Errorf("not a project: %#v", obj)
99119
}
100120
kapi.FillObjectMetaSystemFields(ctx, &project.ObjectMeta)
101-
if errs := validation.ValidateProject(project); len(errs) > 0 {
121+
s.createStrategy.PrepareForCreate(obj)
122+
if errs := s.createStrategy.Validate(ctx, obj); len(errs) > 0 {
102123
return nil, kerrors.NewInvalid("project", project.Name, errs)
103124
}
104125
namespace, err := s.client.Create(convertProject(project))

0 commit comments

Comments
 (0)