Browse code

add self-provisioned newproject

deads2k authored on 2015/04/20 23:25:41
Showing 23 changed files
... ...
@@ -12,9 +12,9 @@
12 12
   </div>
13 13
   <div ng-if="emptyMessage && (projects | hashSize) == 0">{{emptyMessage}}</div>
14 14
   <div style="margin-top: 10px;">
15
-    To create a new project, run <code>openshift ex new-project &lt;projectname&gt; --admin={{user.metadata.name || '&lt;YourUsername&gt;'}}</code>
15
+    To create a new project, run <code>osc new-project &lt;projectname&gt;</code>
16 16
   </div>
17 17
   <div style="margin-top: 10px;">
18
-    To be added as an admin to an existing project, run <code>openshift ex policy add-role-to-user admin {{user.metadata.name || '&lt;YourUsername&gt;'}} -n &lt;projectname&gt;</code>
18
+    To be added as an admin to an existing project, run <code>osadm policy add-role-to-user admin {{user.metadata.name || '&lt;YourUsername&gt;'}} -n &lt;projectname&gt;</code>
19 19
   </div>
20 20
 </div>
... ...
@@ -80,7 +80,7 @@ var originTypes = []string{
80 80
 	"Image", "ImageRepository", "ImageStream", "ImageRepositoryMapping", "ImageStreamMapping", "ImageRepositoryTag", "ImageStreamTag", "ImageStreamImage",
81 81
 	"Template", "TemplateConfig", "ProcessedTemplate",
82 82
 	"Route",
83
-	"Project",
83
+	"Project", "ProjectRequest",
84 84
 	"User", "Identity", "UserIdentityMapping",
85 85
 	"OAuthClient", "OAuthClientAuthorization", "OAuthAccessToken", "OAuthAuthorizeToken",
86 86
 	"Role", "RoleBinding", "Policy", "PolicyBinding", "ResourceAccessReview", "SubjectAccessReview",
... ...
@@ -130,7 +130,8 @@ func init() {
130 130
 	// the list of kinds that are scoped at the root of the api hierarchy
131 131
 	// if a kind is not enumerated here, it is assumed to have a namespace scope
132 132
 	kindToRootScope := map[string]bool{
133
-		"Project": true,
133
+		"Project":        true,
134
+		"ProjectRequest": true,
134 135
 
135 136
 		"User":                true,
136 137
 		"Identity":            true,
... ...
@@ -62238,10 +62238,10 @@ var _views_projects_html = []byte(`<div class="container">
62238 62238
 </div>
62239 62239
 <div ng-if="emptyMessage && (projects | hashSize) == 0">{{emptyMessage}}</div>
62240 62240
 <div style="margin-top: 10px">
62241
-To create a new project, run <code>openshift ex new-project &lt;projectname&gt; --admin={{user.metadata.name || '&lt;YourUsername&gt;'}}</code>
62241
+To create a new project, run <code>osc new-project &lt;projectname&gt;</code>
62242 62242
 </div>
62243 62243
 <div style="margin-top: 10px">
62244
-To be added as an admin to an existing project, run <code>openshift ex policy add-role-to-user admin {{user.metadata.name || '&lt;YourUsername&gt;'}} -n &lt;projectname&gt;</code>
62244
+To be added as an admin to an existing project, run <code>osadm policy add-role-to-user admin {{user.metadata.name || '&lt;YourUsername&gt;'}} -n &lt;projectname&gt;</code>
62245 62245
 </div>
62246 62246
 </div>`)
62247 62247
 
... ...
@@ -440,7 +440,7 @@ func newDefaultGlobalBinding() []authorizationapi.PolicyBinding {
440 440
 			Namespace: bootstrappolicy.DefaultMasterAuthorizationNamespace,
441 441
 		},
442 442
 		RoleBindings: map[string]authorizationapi.RoleBinding{
443
-			"cluster-admins": {
443
+			"extra-cluster-admins": {
444 444
 				ObjectMeta: kapi.ObjectMeta{
445 445
 					Name:      "cluster-admins",
446 446
 					Namespace: bootstrappolicy.DefaultMasterAuthorizationNamespace,
... ...
@@ -33,6 +33,7 @@ type Interface interface {
33 33
 	UsersInterface
34 34
 	UserIdentityMappingsInterface
35 35
 	ProjectsInterface
36
+	ProjectRequestsInterface
36 37
 	PoliciesNamespacer
37 38
 	RolesNamespacer
38 39
 	RoleBindingsNamespacer
... ...
@@ -134,6 +135,11 @@ func (c *Client) Projects() ProjectInterface {
134 134
 	return newProjects(c)
135 135
 }
136 136
 
137
+// ProjectRequests provides a REST client for Projects
138
+func (c *Client) ProjectRequests() ProjectRequestInterface {
139
+	return newProjectRequests(c)
140
+}
141
+
137 142
 // TemplateConfigs provides a REST client for TemplateConfig
138 143
 func (c *Client) TemplateConfigs(namespace string) TemplateConfigInterface {
139 144
 	return newTemplateConfigs(c, namespace)
... ...
@@ -108,6 +108,10 @@ func (c *Fake) Projects() ProjectInterface {
108 108
 	return &FakeProjects{Fake: c}
109 109
 }
110 110
 
111
+func (c *Fake) ProjectRequests() ProjectRequestInterface {
112
+	return &FakeProjectRequests{Fake: c}
113
+}
114
+
111 115
 func (c *Fake) Policies(namespace string) PolicyInterface {
112 116
 	return &FakePolicies{Fake: c}
113 117
 }
114 118
new file mode 100644
... ...
@@ -0,0 +1,14 @@
0
+package client
1
+
2
+import (
3
+	projectapi "github.com/openshift/origin/pkg/project/api"
4
+)
5
+
6
+type FakeProjectRequests struct {
7
+	Fake *Fake
8
+}
9
+
10
+func (c *FakeProjectRequests) Create(project *projectapi.ProjectRequest) (*projectapi.Project, error) {
11
+	obj, err := c.Fake.Invokes(FakeAction{Action: "create-newProject", Value: project}, &projectapi.ProjectRequest{})
12
+	return obj.(*projectapi.Project), err
13
+}
0 14
new file mode 100644
... ...
@@ -0,0 +1,34 @@
0
+package client
1
+
2
+import (
3
+	projectapi "github.com/openshift/origin/pkg/project/api"
4
+	_ "github.com/openshift/origin/pkg/user/api/v1beta1"
5
+)
6
+
7
+// UsersInterface has methods to work with User resources in a namespace
8
+type ProjectRequestsInterface interface {
9
+	ProjectRequests() ProjectRequestInterface
10
+}
11
+
12
+// UserInterface exposes methods on user resources.
13
+type ProjectRequestInterface interface {
14
+	Create(p *projectapi.ProjectRequest) (*projectapi.Project, error)
15
+}
16
+
17
+type newProjectRequestsStruct struct {
18
+	r *Client
19
+}
20
+
21
+// newUsers returns a users
22
+func newProjectRequests(c *Client) *newProjectRequestsStruct {
23
+	return &newProjectRequestsStruct{
24
+		r: c,
25
+	}
26
+}
27
+
28
+// Create creates a new ProjectRequest
29
+func (c *newProjectRequestsStruct) Create(p *projectapi.ProjectRequest) (result *projectapi.Project, err error) {
30
+	result = &projectapi.Project{}
31
+	err = c.r.Post().Resource("projectrequests").Body(p).Do().Into(result)
32
+	return
33
+}
... ...
@@ -64,6 +64,7 @@ func NewCommandCLI(name, fullName string) *cobra.Command {
64 64
 
65 65
 	cmds.AddCommand(cmd.NewCmdLogin(f, in, out))
66 66
 	cmds.AddCommand(cmd.NewCmdProject(f, out))
67
+	cmds.AddCommand(cmd.NewCmdRequestProject("new-project", fullName+" new-project", fullName+" login", fullName+" project", f, out))
67 68
 	cmds.AddCommand(cmd.NewCmdNewApplication(fullName, f, out))
68 69
 	cmds.AddCommand(cmd.NewCmdStatus(fullName, f, out))
69 70
 	cmds.AddCommand(cmd.NewCmdStartBuild(fullName, f, out))
70 71
new file mode 100644
... ...
@@ -0,0 +1,93 @@
0
+package cmd
1
+
2
+import (
3
+	"fmt"
4
+	"io"
5
+
6
+	"github.com/golang/glog"
7
+	"github.com/spf13/cobra"
8
+
9
+	"github.com/openshift/origin/pkg/client"
10
+	"github.com/openshift/origin/pkg/cmd/util/clientcmd"
11
+	projectapi "github.com/openshift/origin/pkg/project/api"
12
+)
13
+
14
+type NewProjectOptions struct {
15
+	ProjectName string
16
+	DisplayName string
17
+	Description string
18
+
19
+	Client client.Interface
20
+}
21
+
22
+const requestProjectLongDesc = `
23
+Create a new project for yourself in OpenShift with you as the project admin.
24
+
25
+Assuming your cluster admin has granted you permission, this command will 
26
+create a new project for you and assign you as the project admin.  You must 
27
+be logged in, so you might have to run %[2]s first.
28
+
29
+Examples:
30
+
31
+	$ Create a new project with minimal information
32
+	$ %[1]s web-team-dev
33
+
34
+	# Create a new project with a description
35
+	$ %[1]s web-team-dev --display-name="Web Team Development" --description="Development project for the web team."
36
+
37
+After your project is created you can switch to it using %[3]s <project name>.
38
+`
39
+
40
+func NewCmdRequestProject(name, fullName, oscLoginName, oscProjectName string, f *clientcmd.Factory, out io.Writer) *cobra.Command {
41
+	options := &NewProjectOptions{}
42
+
43
+	cmd := &cobra.Command{
44
+		Use:   fmt.Sprintf("%s <project-name> [--display-name=<your display name> --description=<your description]", name),
45
+		Short: "request a new project",
46
+		Long:  fmt.Sprintf(requestProjectLongDesc, fullName, oscLoginName, oscProjectName),
47
+		Run: func(cmd *cobra.Command, args []string) {
48
+			if !options.complete(cmd) {
49
+				return
50
+			}
51
+
52
+			var err error
53
+			if options.Client, _, err = f.Clients(); err != nil {
54
+				glog.Fatalf("Error getting client: %v", err)
55
+			}
56
+			if err := options.Run(); err != nil {
57
+				glog.Fatal(err)
58
+			}
59
+		},
60
+	}
61
+	cmd.SetOutput(out)
62
+
63
+	cmd.Flags().StringVar(&options.DisplayName, "display-name", "", "project display name")
64
+	cmd.Flags().StringVar(&options.Description, "description", "", "project description")
65
+
66
+	return cmd
67
+}
68
+
69
+func (o *NewProjectOptions) complete(cmd *cobra.Command) bool {
70
+	args := cmd.Flags().Args()
71
+	if len(args) != 1 {
72
+		cmd.Help()
73
+		return false
74
+	}
75
+
76
+	o.ProjectName = args[0]
77
+
78
+	return true
79
+}
80
+
81
+func (o *NewProjectOptions) Run() error {
82
+	projectRequest := &projectapi.ProjectRequest{}
83
+	projectRequest.Name = o.ProjectName
84
+	projectRequest.DisplayName = o.DisplayName
85
+	projectRequest.Annotations = make(map[string]string)
86
+	projectRequest.Annotations["description"] = o.Description
87
+	if _, err := o.Client.ProjectRequests().Create(projectRequest); err != nil {
88
+		return err
89
+	}
90
+
91
+	return nil
92
+}
... ...
@@ -36,6 +36,7 @@ const (
36 36
 	AdminRoleName             = "admin"
37 37
 	EditRoleName              = "edit"
38 38
 	ViewRoleName              = "view"
39
+	SelfProvisionerRoleName   = "self-provisioner"
39 40
 	BasicUserRoleName         = "basic-user"
40 41
 	StatusCheckerRoleName     = "cluster-status"
41 42
 	DeployerRoleName          = "system:deployer"
... ...
@@ -49,14 +50,15 @@ const (
49 49
 
50 50
 // RoleBindings
51 51
 const (
52
-	InternalComponentRoleBindingName = InternalComponentRoleName + "-binding"
53
-	DeployerRoleBindingName          = DeployerRoleName + "-binding"
54
-	ClusterAdminRoleBindingName      = ClusterAdminRoleName + "-binding"
55
-	BasicUserRoleBindingName         = BasicUserRoleName + "-binding"
52
+	SelfProvisionerRoleBindingName   = SelfProvisionerRoleName + "s"
53
+	InternalComponentRoleBindingName = InternalComponentRoleName + "s"
54
+	DeployerRoleBindingName          = DeployerRoleName + "s"
55
+	ClusterAdminRoleBindingName      = ClusterAdminRoleName + "s"
56
+	BasicUserRoleBindingName         = BasicUserRoleName + "s"
56 57
 	DeleteTokensRoleBindingName      = DeleteTokensRoleName + "-binding"
57 58
 	StatusCheckerRoleBindingName     = StatusCheckerRoleName + "-binding"
58
-	RouterRoleBindingName            = RouterRoleName + "-binding"
59
-	RegistryRoleBindingName          = RegistryRoleName + "-binding"
59
+	RouterRoleBindingName            = RouterRoleName + "s"
60
+	RegistryRoleBindingName          = RegistryRoleName + "s"
60 61
 
61
-	OpenshiftSharedResourceViewRoleBindingName = OpenshiftSharedResourceViewRoleName + "-binding"
62
+	OpenshiftSharedResourceViewRoleBindingName = OpenshiftSharedResourceViewRoleName + "s"
62 63
 )
... ...
@@ -102,12 +102,22 @@ func GetBootstrapMasterRoles(masterNamespace string) []authorizationapi.Role {
102 102
 			},
103 103
 			Rules: []authorizationapi.PolicyRule{
104 104
 				{Verbs: util.NewStringSet("get"), Resources: util.NewStringSet("users"), ResourceNames: util.NewStringSet("~")},
105
+				{Verbs: util.NewStringSet("get"), Resources: util.NewStringSet("projectrequests")},
105 106
 				{Verbs: util.NewStringSet("list"), Resources: util.NewStringSet("projects")},
106 107
 				{Verbs: util.NewStringSet("create"), Resources: util.NewStringSet("subjectaccessreviews"), AttributeRestrictions: runtime.EmbeddedObject{&authorizationapi.IsPersonalSubjectAccessReview{}}},
107 108
 			},
108 109
 		},
109 110
 		{
110 111
 			ObjectMeta: kapi.ObjectMeta{
112
+				Name:      SelfProvisionerRoleName,
113
+				Namespace: masterNamespace,
114
+			},
115
+			Rules: []authorizationapi.PolicyRule{
116
+				{Verbs: util.NewStringSet("create"), Resources: util.NewStringSet("projectrequests")},
117
+			},
118
+		},
119
+		{
120
+			ObjectMeta: kapi.ObjectMeta{
111 121
 				Name:      StatusCheckerRoleName,
112 122
 				Namespace: masterNamespace,
113 123
 			},
... ...
@@ -258,6 +268,17 @@ func GetBootstrapMasterRoleBindings(masterNamespace string) []authorizationapi.R
258 258
 		},
259 259
 		{
260 260
 			ObjectMeta: kapi.ObjectMeta{
261
+				Name:      SelfProvisionerRoleBindingName,
262
+				Namespace: masterNamespace,
263
+			},
264
+			RoleRef: kapi.ObjectReference{
265
+				Name:      SelfProvisionerRoleName,
266
+				Namespace: masterNamespace,
267
+			},
268
+			Groups: util.NewStringSet(AuthenticatedGroup),
269
+		},
270
+		{
271
+			ObjectMeta: kapi.ObjectMeta{
261 272
 				Name:      DeleteTokensRoleBindingName,
262 273
 				Namespace: masterNamespace,
263 274
 			},
... ...
@@ -76,6 +76,7 @@ import (
76 76
 	projectapi "github.com/openshift/origin/pkg/project/api"
77 77
 	projectcontroller "github.com/openshift/origin/pkg/project/controller"
78 78
 	projectproxy "github.com/openshift/origin/pkg/project/registry/project/proxy"
79
+	projectrequeststorage "github.com/openshift/origin/pkg/project/registry/projectrequest/delegated"
79 80
 	routeallocationcontroller "github.com/openshift/origin/pkg/route/controller/allocation"
80 81
 	routeetcd "github.com/openshift/origin/pkg/route/registry/etcd"
81 82
 	routeregistry "github.com/openshift/origin/pkg/route/registry/route"
... ...
@@ -154,7 +155,7 @@ func (c *MasterConfig) InstallProtectedAPI(container *restful.Container) []strin
154 154
 	policyRegistry := policyregistry.NewRegistry(policyStorage)
155 155
 	policyBindingStorage := policybindingetcd.NewStorage(c.EtcdHelper)
156 156
 	policyBindingRegistry := policybindingregistry.NewRegistry(policyBindingStorage)
157
-
157
+	roleBindingRegistry := rolebindingregistry.NewVirtualRegistry(policyBindingRegistry, policyRegistry, c.Options.PolicyConfig.MasterAuthorizationNamespace)
158 158
 	subjectAccessReviewStorage := subjectaccessreview.NewREST(c.Authorizer)
159 159
 	subjectAccessReviewRegistry := subjectaccessreview.NewRegistry(subjectAccessReviewStorage)
160 160
 
... ...
@@ -201,6 +202,8 @@ func (c *MasterConfig) InstallProtectedAPI(container *restful.Container) []strin
201 201
 		GRFn: deployRollback.GenerateRollback,
202 202
 	}
203 203
 
204
+	projectStorage := projectproxy.NewREST(kclient.Namespaces(), c.ProjectAuthorizationCache)
205
+
204 206
 	// initialize OpenShift API
205 207
 	storage := map[string]rest.Storage{
206 208
 		"builds":                   buildregistry.NewREST(buildEtcd),
... ...
@@ -232,7 +235,8 @@ func (c *MasterConfig) InstallProtectedAPI(container *restful.Container) []strin
232 232
 
233 233
 		"routes": routeregistry.NewREST(routeEtcd, routeAllocator),
234 234
 
235
-		"projects": projectproxy.NewREST(kclient.Namespaces(), c.ProjectAuthorizationCache),
235
+		"projects":        projectStorage,
236
+		"projectRequests": projectrequeststorage.NewREST(c.Options.PolicyConfig.MasterAuthorizationNamespace, roleBindingRegistry, *projectStorage),
236 237
 
237 238
 		"users":                userStorage,
238 239
 		"identities":           identityStorage,
... ...
@@ -246,7 +250,7 @@ func (c *MasterConfig) InstallProtectedAPI(container *restful.Container) []strin
246 246
 		"policies":              policyStorage,
247 247
 		"policyBindings":        policyBindingStorage,
248 248
 		"roles":                 roleregistry.NewREST(roleregistry.NewVirtualRegistry(policyRegistry)),
249
-		"roleBindings":          rolebindingregistry.NewREST(rolebindingregistry.NewVirtualRegistry(policyBindingRegistry, policyRegistry, c.Options.PolicyConfig.MasterAuthorizationNamespace)),
249
+		"roleBindings":          rolebindingregistry.NewREST(roleBindingRegistry),
250 250
 		"resourceAccessReviews": resourceaccessreviewregistry.NewREST(c.Authorizer),
251 251
 		"subjectAccessReviews":  subjectAccessReviewStorage,
252 252
 	}
... ...
@@ -8,8 +8,10 @@ func init() {
8 8
 	api.Scheme.AddKnownTypes("",
9 9
 		&Project{},
10 10
 		&ProjectList{},
11
+		&ProjectRequest{},
11 12
 	)
12 13
 }
13 14
 
14
-func (*Project) IsAnAPIObject()     {}
15
-func (*ProjectList) IsAnAPIObject() {}
15
+func (*ProjectRequest) IsAnAPIObject() {}
16
+func (*Project) IsAnAPIObject()        {}
17
+func (*ProjectList) IsAnAPIObject()    {}
... ...
@@ -37,3 +37,10 @@ type Project struct {
37 37
 	Spec        ProjectSpec
38 38
 	Status      ProjectStatus
39 39
 }
40
+
41
+type ProjectRequest struct {
42
+	kapi.TypeMeta
43
+	kapi.ObjectMeta
44
+
45
+	DisplayName string
46
+}
... ...
@@ -8,8 +8,10 @@ func init() {
8 8
 	api.Scheme.AddKnownTypes("v1beta1",
9 9
 		&Project{},
10 10
 		&ProjectList{},
11
+		&ProjectRequest{},
11 12
 	)
12 13
 }
13 14
 
14
-func (*Project) IsAnAPIObject()     {}
15
-func (*ProjectList) IsAnAPIObject() {}
15
+func (*ProjectRequest) IsAnAPIObject() {}
16
+func (*Project) IsAnAPIObject()        {}
17
+func (*ProjectList) IsAnAPIObject()    {}
... ...
@@ -39,3 +39,9 @@ type Project struct {
39 39
 	// Status describes the current status of a Namespace
40 40
 	Status ProjectStatus `json:"status,omitempty" description:"status describes the current status of a Project; read-only"`
41 41
 }
42
+
43
+type ProjectRequest struct {
44
+	kapi.TypeMeta   `json:",inline"`
45
+	kapi.ObjectMeta `json:"metadata,omitempty"`
46
+	DisplayName     string `json:"displayName,omitempty"`
47
+}
... ...
@@ -8,8 +8,10 @@ func init() {
8 8
 	api.Scheme.AddKnownTypes("v1beta3",
9 9
 		&Project{},
10 10
 		&ProjectList{},
11
+		&ProjectRequest{},
11 12
 	)
12 13
 }
13 14
 
14
-func (*Project) IsAnAPIObject()     {}
15
-func (*ProjectList) IsAnAPIObject() {}
15
+func (*ProjectRequest) IsAnAPIObject() {}
16
+func (*Project) IsAnAPIObject()        {}
17
+func (*ProjectList) IsAnAPIObject()    {}
... ...
@@ -39,3 +39,9 @@ type Project struct {
39 39
 	// Status describes the current status of a Namespace
40 40
 	Status ProjectStatus `json:"status,omitempty" description:"status describes the current status of a Project; read-only"`
41 41
 }
42
+
43
+type ProjectRequest struct {
44
+	kapi.TypeMeta   `json:",inline"`
45
+	kapi.ObjectMeta `json:"metadata,omitempty"`
46
+	DisplayName     string `json:"displayName,omitempty"`
47
+}
... ...
@@ -39,3 +39,11 @@ func ValidateProjectUpdate(newProject *api.Project, oldProject *api.Project) fie
39 39
 	newProject.Status = oldProject.Status
40 40
 	return allErrs
41 41
 }
42
+
43
+func ValidateProjectRequest(request *api.ProjectRequest) fielderrors.ValidationErrorList {
44
+	project := &api.Project{}
45
+	project.ObjectMeta = request.ObjectMeta
46
+	project.DisplayName = request.DisplayName
47
+
48
+	return ValidateProject(project)
49
+}
42 50
new file mode 100644
... ...
@@ -0,0 +1,67 @@
0
+package delegated
1
+
2
+import (
3
+	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
4
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/api/rest"
5
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
6
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
7
+
8
+	authorizationapi "github.com/openshift/origin/pkg/authorization/api"
9
+	"github.com/openshift/origin/pkg/authorization/registry/rolebinding"
10
+
11
+	projectapi "github.com/openshift/origin/pkg/project/api"
12
+	projectstorage "github.com/openshift/origin/pkg/project/registry/project/proxy"
13
+	projectrequestregistry "github.com/openshift/origin/pkg/project/registry/projectrequest"
14
+)
15
+
16
+type REST struct {
17
+	masterNamespace     string
18
+	roleBindingRegistry rolebinding.Registry
19
+
20
+	projectStorage projectstorage.REST
21
+}
22
+
23
+func NewREST(masterNamespace string, roleBindingRegistry rolebinding.Registry, projectStorage projectstorage.REST) *REST {
24
+	return &REST{
25
+		masterNamespace:     masterNamespace,
26
+		roleBindingRegistry: roleBindingRegistry,
27
+		projectStorage:      projectStorage,
28
+	}
29
+}
30
+
31
+func (r *REST) New() runtime.Object {
32
+	return &projectapi.ProjectRequest{}
33
+}
34
+
35
+func (r *REST) Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, error) {
36
+	if err := rest.BeforeCreate(projectrequestregistry.Strategy, ctx, obj); err != nil {
37
+		return nil, err
38
+	}
39
+
40
+	projectRequest := obj.(*projectapi.ProjectRequest)
41
+
42
+	project := &projectapi.Project{}
43
+	project.ObjectMeta = projectRequest.ObjectMeta
44
+	project.DisplayName = projectRequest.DisplayName
45
+
46
+	projectObj, err := r.projectStorage.Create(ctx, project)
47
+	if err != nil {
48
+		return nil, err
49
+	}
50
+	realizedProject := projectObj.(*projectapi.Project)
51
+
52
+	adminBinding := &authorizationapi.RoleBinding{}
53
+	adminBinding.Namespace = realizedProject.Name
54
+	adminBinding.Name = "admins"
55
+	adminBinding.RoleRef = kapi.ObjectReference{Namespace: r.masterNamespace, Name: "admin"}
56
+	if userInfo, exists := kapi.UserFrom(ctx); exists {
57
+		adminBinding.Users = util.NewStringSet(userInfo.GetName())
58
+	}
59
+
60
+	projectContext := kapi.WithNamespace(ctx, realizedProject.Name)
61
+	if err := r.roleBindingRegistry.CreateRoleBinding(projectContext, adminBinding, true); err != nil {
62
+		return nil, err
63
+	}
64
+
65
+	return realizedProject, nil
66
+}
0 67
new file mode 100644
... ...
@@ -0,0 +1,47 @@
0
+package projectrequest
1
+
2
+import (
3
+	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
4
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
5
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/util/fielderrors"
6
+
7
+	projectapi "github.com/openshift/origin/pkg/project/api"
8
+	projectvalidation "github.com/openshift/origin/pkg/project/api/validation"
9
+)
10
+
11
+// strategy implements behavior for OAuthClient objects
12
+type strategy struct {
13
+	runtime.ObjectTyper
14
+}
15
+
16
+var Strategy = strategy{kapi.Scheme}
17
+
18
+func (strategy) PrepareForUpdate(obj, old runtime.Object) {}
19
+
20
+// NamespaceScoped is false for projectrequest objects
21
+func (strategy) NamespaceScoped() bool {
22
+	return false
23
+}
24
+
25
+func (strategy) GenerateName(base string) string {
26
+	return base
27
+}
28
+
29
+func (strategy) PrepareForCreate(obj runtime.Object) {
30
+}
31
+
32
+// Validate validates a new client
33
+func (strategy) Validate(ctx kapi.Context, obj runtime.Object) fielderrors.ValidationErrorList {
34
+	projectrequest := obj.(*projectapi.ProjectRequest)
35
+	return projectvalidation.ValidateProjectRequest(projectrequest)
36
+}
37
+
38
+// ValidateUpdate validates a client update
39
+func (strategy) ValidateUpdate(ctx kapi.Context, obj runtime.Object, old runtime.Object) fielderrors.ValidationErrorList {
40
+	return nil
41
+}
42
+
43
+// AllowCreateOnUpdate is false for OAuth objects
44
+func (strategy) AllowCreateOnUpdate() bool {
45
+	return false
46
+}
0 47
new file mode 100644
... ...
@@ -0,0 +1,60 @@
0
+// +build integration,!no-etcd
1
+
2
+package integration
3
+
4
+import (
5
+	"testing"
6
+	"time"
7
+
8
+	"github.com/openshift/origin/pkg/client"
9
+
10
+	osc "github.com/openshift/origin/pkg/cmd/cli/cmd"
11
+	"github.com/openshift/origin/pkg/cmd/util/tokencmd"
12
+	testutil "github.com/openshift/origin/test/util"
13
+)
14
+
15
+func TestUnprivilegedNewProject(t *testing.T) {
16
+	_, clusterAdminKubeConfig, err := testutil.StartTestMaster()
17
+	if err != nil {
18
+		t.Fatalf("unexpected error: %v", err)
19
+	}
20
+
21
+	clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig)
22
+	if err != nil {
23
+		t.Errorf("unexpected error: %v", err)
24
+	}
25
+
26
+	valerieClientConfig := *clusterAdminClientConfig
27
+	valerieClientConfig.Username = ""
28
+	valerieClientConfig.Password = ""
29
+	valerieClientConfig.BearerToken = ""
30
+	valerieClientConfig.CertFile = ""
31
+	valerieClientConfig.KeyFile = ""
32
+	valerieClientConfig.CertData = nil
33
+	valerieClientConfig.KeyData = nil
34
+
35
+	accessToken, err := tokencmd.RequestToken(&valerieClientConfig, nil, "valerie", "security!")
36
+	if err != nil {
37
+		t.Fatalf("unexpected error: %v", err)
38
+	}
39
+
40
+	valerieClientConfig.BearerToken = accessToken
41
+	valerieOpenshiftClient, err := client.New(&valerieClientConfig)
42
+	if err != nil {
43
+		t.Fatalf("unexpected error: %v", err)
44
+	}
45
+
46
+	requestProject := osc.NewProjectOptions{
47
+		ProjectName: "new-project",
48
+		DisplayName: "display name here",
49
+		Description: "the special description",
50
+
51
+		Client: valerieOpenshiftClient,
52
+	}
53
+
54
+	if err := requestProject.Run(); err != nil {
55
+		t.Errorf("unexpected error: %v", err)
56
+	}
57
+
58
+	waitForProject(t, valerieOpenshiftClient, "new-project", 5*time.Second, 10)
59
+}