Browse code

add Group kind

deads2k authored on 2015/07/22 06:42:39
Showing 29 changed files
... ...
@@ -4232,6 +4232,432 @@
4232 4232
     ]
4233 4233
    },
4234 4234
    {
4235
+    "path": "/oapi/v1/groups",
4236
+    "description": "OpenShift REST API, version v1",
4237
+    "operations": [
4238
+     {
4239
+      "type": "v1.GroupList",
4240
+      "method": "GET",
4241
+      "summary": "list or watch objects of kind Group",
4242
+      "nickname": "listGroup",
4243
+      "parameters": [
4244
+       {
4245
+        "type": "string",
4246
+        "paramType": "query",
4247
+        "name": "pretty",
4248
+        "description": "If 'true', then the output is pretty printed.",
4249
+        "required": false,
4250
+        "allowMultiple": false
4251
+       },
4252
+       {
4253
+        "type": "string",
4254
+        "paramType": "query",
4255
+        "name": "labelSelector",
4256
+        "description": "a selector to restrict the list of returned objects by their labels; defaults to everything",
4257
+        "required": false,
4258
+        "allowMultiple": false
4259
+       },
4260
+       {
4261
+        "type": "string",
4262
+        "paramType": "query",
4263
+        "name": "fieldSelector",
4264
+        "description": "a selector to restrict the list of returned objects by their fields; defaults to everything",
4265
+        "required": false,
4266
+        "allowMultiple": false
4267
+       },
4268
+       {
4269
+        "type": "boolean",
4270
+        "paramType": "query",
4271
+        "name": "watch",
4272
+        "description": "watch for changes to the described resources and return them as a stream of add, update, and remove notifications; specify resourceVersion",
4273
+        "required": false,
4274
+        "allowMultiple": false
4275
+       },
4276
+       {
4277
+        "type": "string",
4278
+        "paramType": "query",
4279
+        "name": "resourceVersion",
4280
+        "description": "when specified with a watch call, shows changes that occur after that particular version of a resource; defaults to changes from the beginning of history",
4281
+        "required": false,
4282
+        "allowMultiple": false
4283
+       }
4284
+      ],
4285
+      "responseMessages": [
4286
+       {
4287
+        "code": 200,
4288
+        "message": "OK",
4289
+        "responseModel": "v1.GroupList"
4290
+       }
4291
+      ],
4292
+      "produces": [
4293
+       "application/json"
4294
+      ],
4295
+      "consumes": [
4296
+       "*/*"
4297
+      ]
4298
+     },
4299
+     {
4300
+      "type": "v1.Group",
4301
+      "method": "POST",
4302
+      "summary": "create a Group",
4303
+      "nickname": "createGroup",
4304
+      "parameters": [
4305
+       {
4306
+        "type": "string",
4307
+        "paramType": "query",
4308
+        "name": "pretty",
4309
+        "description": "If 'true', then the output is pretty printed.",
4310
+        "required": false,
4311
+        "allowMultiple": false
4312
+       },
4313
+       {
4314
+        "type": "v1.Group",
4315
+        "paramType": "body",
4316
+        "name": "body",
4317
+        "description": "",
4318
+        "required": true,
4319
+        "allowMultiple": false
4320
+       }
4321
+      ],
4322
+      "responseMessages": [
4323
+       {
4324
+        "code": 200,
4325
+        "message": "OK",
4326
+        "responseModel": "v1.Group"
4327
+       }
4328
+      ],
4329
+      "produces": [
4330
+       "application/json"
4331
+      ],
4332
+      "consumes": [
4333
+       "*/*"
4334
+      ]
4335
+     }
4336
+    ]
4337
+   },
4338
+   {
4339
+    "path": "/oapi/v1/watch/groups",
4340
+    "description": "OpenShift REST API, version v1",
4341
+    "operations": [
4342
+     {
4343
+      "type": "json.WatchEvent",
4344
+      "method": "GET",
4345
+      "summary": "watch individual changes to a list of Group",
4346
+      "nickname": "watchGroupList",
4347
+      "parameters": [
4348
+       {
4349
+        "type": "string",
4350
+        "paramType": "query",
4351
+        "name": "pretty",
4352
+        "description": "If 'true', then the output is pretty printed.",
4353
+        "required": false,
4354
+        "allowMultiple": false
4355
+       },
4356
+       {
4357
+        "type": "string",
4358
+        "paramType": "query",
4359
+        "name": "labelSelector",
4360
+        "description": "a selector to restrict the list of returned objects by their labels; defaults to everything",
4361
+        "required": false,
4362
+        "allowMultiple": false
4363
+       },
4364
+       {
4365
+        "type": "string",
4366
+        "paramType": "query",
4367
+        "name": "fieldSelector",
4368
+        "description": "a selector to restrict the list of returned objects by their fields; defaults to everything",
4369
+        "required": false,
4370
+        "allowMultiple": false
4371
+       },
4372
+       {
4373
+        "type": "boolean",
4374
+        "paramType": "query",
4375
+        "name": "watch",
4376
+        "description": "watch for changes to the described resources and return them as a stream of add, update, and remove notifications; specify resourceVersion",
4377
+        "required": false,
4378
+        "allowMultiple": false
4379
+       },
4380
+       {
4381
+        "type": "string",
4382
+        "paramType": "query",
4383
+        "name": "resourceVersion",
4384
+        "description": "when specified with a watch call, shows changes that occur after that particular version of a resource; defaults to changes from the beginning of history",
4385
+        "required": false,
4386
+        "allowMultiple": false
4387
+       }
4388
+      ],
4389
+      "responseMessages": [
4390
+       {
4391
+        "code": 200,
4392
+        "message": "OK",
4393
+        "responseModel": "json.WatchEvent"
4394
+       }
4395
+      ],
4396
+      "produces": [
4397
+       "application/json"
4398
+      ],
4399
+      "consumes": [
4400
+       "*/*"
4401
+      ]
4402
+     }
4403
+    ]
4404
+   },
4405
+   {
4406
+    "path": "/oapi/v1/groups/{name}",
4407
+    "description": "OpenShift REST API, version v1",
4408
+    "operations": [
4409
+     {
4410
+      "type": "v1.Group",
4411
+      "method": "GET",
4412
+      "summary": "read the specified Group",
4413
+      "nickname": "readGroup",
4414
+      "parameters": [
4415
+       {
4416
+        "type": "string",
4417
+        "paramType": "query",
4418
+        "name": "pretty",
4419
+        "description": "If 'true', then the output is pretty printed.",
4420
+        "required": false,
4421
+        "allowMultiple": false
4422
+       },
4423
+       {
4424
+        "type": "string",
4425
+        "paramType": "path",
4426
+        "name": "name",
4427
+        "description": "name of the Group",
4428
+        "required": true,
4429
+        "allowMultiple": false
4430
+       }
4431
+      ],
4432
+      "responseMessages": [
4433
+       {
4434
+        "code": 200,
4435
+        "message": "OK",
4436
+        "responseModel": "v1.Group"
4437
+       }
4438
+      ],
4439
+      "produces": [
4440
+       "application/json"
4441
+      ],
4442
+      "consumes": [
4443
+       "*/*"
4444
+      ]
4445
+     },
4446
+     {
4447
+      "type": "v1.Group",
4448
+      "method": "PUT",
4449
+      "summary": "replace the specified Group",
4450
+      "nickname": "replaceGroup",
4451
+      "parameters": [
4452
+       {
4453
+        "type": "string",
4454
+        "paramType": "query",
4455
+        "name": "pretty",
4456
+        "description": "If 'true', then the output is pretty printed.",
4457
+        "required": false,
4458
+        "allowMultiple": false
4459
+       },
4460
+       {
4461
+        "type": "v1.Group",
4462
+        "paramType": "body",
4463
+        "name": "body",
4464
+        "description": "",
4465
+        "required": true,
4466
+        "allowMultiple": false
4467
+       },
4468
+       {
4469
+        "type": "string",
4470
+        "paramType": "path",
4471
+        "name": "name",
4472
+        "description": "name of the Group",
4473
+        "required": true,
4474
+        "allowMultiple": false
4475
+       }
4476
+      ],
4477
+      "responseMessages": [
4478
+       {
4479
+        "code": 200,
4480
+        "message": "OK",
4481
+        "responseModel": "v1.Group"
4482
+       }
4483
+      ],
4484
+      "produces": [
4485
+       "application/json"
4486
+      ],
4487
+      "consumes": [
4488
+       "*/*"
4489
+      ]
4490
+     },
4491
+     {
4492
+      "type": "v1.Group",
4493
+      "method": "PATCH",
4494
+      "summary": "partially update the specified Group",
4495
+      "nickname": "patchGroup",
4496
+      "parameters": [
4497
+       {
4498
+        "type": "string",
4499
+        "paramType": "query",
4500
+        "name": "pretty",
4501
+        "description": "If 'true', then the output is pretty printed.",
4502
+        "required": false,
4503
+        "allowMultiple": false
4504
+       },
4505
+       {
4506
+        "type": "api.Patch",
4507
+        "paramType": "body",
4508
+        "name": "body",
4509
+        "description": "",
4510
+        "required": true,
4511
+        "allowMultiple": false
4512
+       },
4513
+       {
4514
+        "type": "string",
4515
+        "paramType": "path",
4516
+        "name": "name",
4517
+        "description": "name of the Group",
4518
+        "required": true,
4519
+        "allowMultiple": false
4520
+       }
4521
+      ],
4522
+      "responseMessages": [
4523
+       {
4524
+        "code": 200,
4525
+        "message": "OK",
4526
+        "responseModel": "v1.Group"
4527
+       }
4528
+      ],
4529
+      "produces": [
4530
+       "application/json"
4531
+      ],
4532
+      "consumes": [
4533
+       "application/json-patch+json",
4534
+       "application/merge-patch+json",
4535
+       "application/strategic-merge-patch+json"
4536
+      ]
4537
+     },
4538
+     {
4539
+      "type": "v1.Status",
4540
+      "method": "DELETE",
4541
+      "summary": "delete a Group",
4542
+      "nickname": "deleteGroup",
4543
+      "parameters": [
4544
+       {
4545
+        "type": "string",
4546
+        "paramType": "query",
4547
+        "name": "pretty",
4548
+        "description": "If 'true', then the output is pretty printed.",
4549
+        "required": false,
4550
+        "allowMultiple": false
4551
+       },
4552
+       {
4553
+        "type": "v1.DeleteOptions",
4554
+        "paramType": "body",
4555
+        "name": "body",
4556
+        "description": "",
4557
+        "required": true,
4558
+        "allowMultiple": false
4559
+       },
4560
+       {
4561
+        "type": "string",
4562
+        "paramType": "path",
4563
+        "name": "name",
4564
+        "description": "name of the Group",
4565
+        "required": true,
4566
+        "allowMultiple": false
4567
+       }
4568
+      ],
4569
+      "responseMessages": [
4570
+       {
4571
+        "code": 200,
4572
+        "message": "OK",
4573
+        "responseModel": "v1.Status"
4574
+       }
4575
+      ],
4576
+      "produces": [
4577
+       "application/json"
4578
+      ],
4579
+      "consumes": [
4580
+       "*/*"
4581
+      ]
4582
+     }
4583
+    ]
4584
+   },
4585
+   {
4586
+    "path": "/oapi/v1/watch/groups/{name}",
4587
+    "description": "OpenShift REST API, version v1",
4588
+    "operations": [
4589
+     {
4590
+      "type": "json.WatchEvent",
4591
+      "method": "GET",
4592
+      "summary": "watch changes to an object of kind Group",
4593
+      "nickname": "watchGroup",
4594
+      "parameters": [
4595
+       {
4596
+        "type": "string",
4597
+        "paramType": "query",
4598
+        "name": "pretty",
4599
+        "description": "If 'true', then the output is pretty printed.",
4600
+        "required": false,
4601
+        "allowMultiple": false
4602
+       },
4603
+       {
4604
+        "type": "string",
4605
+        "paramType": "query",
4606
+        "name": "labelSelector",
4607
+        "description": "a selector to restrict the list of returned objects by their labels; defaults to everything",
4608
+        "required": false,
4609
+        "allowMultiple": false
4610
+       },
4611
+       {
4612
+        "type": "string",
4613
+        "paramType": "query",
4614
+        "name": "fieldSelector",
4615
+        "description": "a selector to restrict the list of returned objects by their fields; defaults to everything",
4616
+        "required": false,
4617
+        "allowMultiple": false
4618
+       },
4619
+       {
4620
+        "type": "boolean",
4621
+        "paramType": "query",
4622
+        "name": "watch",
4623
+        "description": "watch for changes to the described resources and return them as a stream of add, update, and remove notifications; specify resourceVersion",
4624
+        "required": false,
4625
+        "allowMultiple": false
4626
+       },
4627
+       {
4628
+        "type": "string",
4629
+        "paramType": "query",
4630
+        "name": "resourceVersion",
4631
+        "description": "when specified with a watch call, shows changes that occur after that particular version of a resource; defaults to changes from the beginning of history",
4632
+        "required": false,
4633
+        "allowMultiple": false
4634
+       },
4635
+       {
4636
+        "type": "string",
4637
+        "paramType": "path",
4638
+        "name": "name",
4639
+        "description": "name of the Group",
4640
+        "required": true,
4641
+        "allowMultiple": false
4642
+       }
4643
+      ],
4644
+      "responseMessages": [
4645
+       {
4646
+        "code": 200,
4647
+        "message": "OK",
4648
+        "responseModel": "json.WatchEvent"
4649
+       }
4650
+      ],
4651
+      "produces": [
4652
+       "application/json"
4653
+      ],
4654
+      "consumes": [
4655
+       "*/*"
4656
+      ]
4657
+     }
4658
+    ]
4659
+   },
4660
+   {
4235 4661
     "path": "/oapi/v1/hostsubnets",
4236 4662
     "description": "OpenShift REST API, version v1",
4237 4663
     "operations": [
... ...
@@ -14652,6 +15078,58 @@
14652 14652
      }
14653 14653
     }
14654 14654
    },
14655
+   "v1.GroupList": {
14656
+    "id": "v1.GroupList",
14657
+    "required": [
14658
+     "items"
14659
+    ],
14660
+    "properties": {
14661
+     "kind": {
14662
+      "type": "string",
14663
+      "description": "kind of object, in CamelCase; cannot be updated; see http://releases.k8s.io/v1.0.0/docs/api-conventions.md#types-kinds"
14664
+     },
14665
+     "apiVersion": {
14666
+      "type": "string",
14667
+      "description": "version of the schema the object should have; see http://releases.k8s.io/v1.0.0/docs/api-conventions.md#resources"
14668
+     },
14669
+     "metadata": {
14670
+      "$ref": "v1.ListMeta"
14671
+     },
14672
+     "items": {
14673
+      "type": "array",
14674
+      "items": {
14675
+       "$ref": "v1.Group"
14676
+      },
14677
+      "description": "list of groups"
14678
+     }
14679
+    }
14680
+   },
14681
+   "v1.Group": {
14682
+    "id": "v1.Group",
14683
+    "required": [
14684
+     "users"
14685
+    ],
14686
+    "properties": {
14687
+     "kind": {
14688
+      "type": "string",
14689
+      "description": "kind of object, in CamelCase; cannot be updated; see http://releases.k8s.io/v1.0.0/docs/api-conventions.md#types-kinds"
14690
+     },
14691
+     "apiVersion": {
14692
+      "type": "string",
14693
+      "description": "version of the schema the object should have; see http://releases.k8s.io/v1.0.0/docs/api-conventions.md#resources"
14694
+     },
14695
+     "metadata": {
14696
+      "$ref": "v1.ObjectMeta"
14697
+     },
14698
+     "users": {
14699
+      "type": "array",
14700
+      "items": {
14701
+       "type": "string"
14702
+      },
14703
+      "description": "list of users in this group"
14704
+     }
14705
+    }
14706
+   },
14655 14707
    "v1.HostSubnetList": {
14656 14708
     "id": "v1.HostSubnetList",
14657 14709
     "required": [
... ...
@@ -2303,6 +2303,52 @@ func deepCopy_api_TemplateList(in templateapi.TemplateList, out *templateapi.Tem
2303 2303
 	return nil
2304 2304
 }
2305 2305
 
2306
+func deepCopy_api_Group(in userapi.Group, out *userapi.Group, c *conversion.Cloner) error {
2307
+	if newVal, err := c.DeepCopy(in.TypeMeta); err != nil {
2308
+		return err
2309
+	} else {
2310
+		out.TypeMeta = newVal.(api.TypeMeta)
2311
+	}
2312
+	if newVal, err := c.DeepCopy(in.ObjectMeta); err != nil {
2313
+		return err
2314
+	} else {
2315
+		out.ObjectMeta = newVal.(api.ObjectMeta)
2316
+	}
2317
+	if in.Users != nil {
2318
+		out.Users = make([]string, len(in.Users))
2319
+		for i := range in.Users {
2320
+			out.Users[i] = in.Users[i]
2321
+		}
2322
+	} else {
2323
+		out.Users = nil
2324
+	}
2325
+	return nil
2326
+}
2327
+
2328
+func deepCopy_api_GroupList(in userapi.GroupList, out *userapi.GroupList, c *conversion.Cloner) error {
2329
+	if newVal, err := c.DeepCopy(in.TypeMeta); err != nil {
2330
+		return err
2331
+	} else {
2332
+		out.TypeMeta = newVal.(api.TypeMeta)
2333
+	}
2334
+	if newVal, err := c.DeepCopy(in.ListMeta); err != nil {
2335
+		return err
2336
+	} else {
2337
+		out.ListMeta = newVal.(api.ListMeta)
2338
+	}
2339
+	if in.Items != nil {
2340
+		out.Items = make([]userapi.Group, len(in.Items))
2341
+		for i := range in.Items {
2342
+			if err := deepCopy_api_Group(in.Items[i], &out.Items[i], c); err != nil {
2343
+				return err
2344
+			}
2345
+		}
2346
+	} else {
2347
+		out.Items = nil
2348
+	}
2349
+	return nil
2350
+}
2351
+
2306 2352
 func deepCopy_api_Identity(in userapi.Identity, out *userapi.Identity, c *conversion.Cloner) error {
2307 2353
 	if newVal, err := c.DeepCopy(in.TypeMeta); err != nil {
2308 2354
 		return err
... ...
@@ -2536,6 +2582,8 @@ func init() {
2536 2536
 		deepCopy_api_Parameter,
2537 2537
 		deepCopy_api_Template,
2538 2538
 		deepCopy_api_TemplateList,
2539
+		deepCopy_api_Group,
2540
+		deepCopy_api_GroupList,
2539 2541
 		deepCopy_api_Identity,
2540 2542
 		deepCopy_api_IdentityList,
2541 2543
 		deepCopy_api_User,
... ...
@@ -124,6 +124,7 @@ func init() {
124 124
 		"User":                true,
125 125
 		"Identity":            true,
126 126
 		"UserIdentityMapping": true,
127
+		"Group":               true,
127 128
 
128 129
 		"OAuthAccessToken":         true,
129 130
 		"OAuthAuthorizeToken":      true,
... ...
@@ -2679,6 +2679,50 @@ func convert_v1_TemplateList_To_api_TemplateList(in *templateapiv1.TemplateList,
2679 2679
 	return nil
2680 2680
 }
2681 2681
 
2682
+func convert_api_Group_To_v1_Group(in *userapi.Group, out *userapiv1.Group, s conversion.Scope) error {
2683
+	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
2684
+		defaulting.(func(*userapi.Group))(in)
2685
+	}
2686
+	if err := convert_api_TypeMeta_To_v1_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
2687
+		return err
2688
+	}
2689
+	if err := convert_api_ObjectMeta_To_v1_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil {
2690
+		return err
2691
+	}
2692
+	if in.Users != nil {
2693
+		out.Users = make([]string, len(in.Users))
2694
+		for i := range in.Users {
2695
+			out.Users[i] = in.Users[i]
2696
+		}
2697
+	} else {
2698
+		out.Users = nil
2699
+	}
2700
+	return nil
2701
+}
2702
+
2703
+func convert_api_GroupList_To_v1_GroupList(in *userapi.GroupList, out *userapiv1.GroupList, s conversion.Scope) error {
2704
+	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
2705
+		defaulting.(func(*userapi.GroupList))(in)
2706
+	}
2707
+	if err := convert_api_TypeMeta_To_v1_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
2708
+		return err
2709
+	}
2710
+	if err := convert_api_ListMeta_To_v1_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil {
2711
+		return err
2712
+	}
2713
+	if in.Items != nil {
2714
+		out.Items = make([]userapiv1.Group, len(in.Items))
2715
+		for i := range in.Items {
2716
+			if err := convert_api_Group_To_v1_Group(&in.Items[i], &out.Items[i], s); err != nil {
2717
+				return err
2718
+			}
2719
+		}
2720
+	} else {
2721
+		out.Items = nil
2722
+	}
2723
+	return nil
2724
+}
2725
+
2682 2726
 func convert_api_Identity_To_v1_Identity(in *userapi.Identity, out *userapiv1.Identity, s conversion.Scope) error {
2683 2727
 	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
2684 2728
 		defaulting.(func(*userapi.Identity))(in)
... ...
@@ -2800,6 +2844,50 @@ func convert_api_UserList_To_v1_UserList(in *userapi.UserList, out *userapiv1.Us
2800 2800
 	return nil
2801 2801
 }
2802 2802
 
2803
+func convert_v1_Group_To_api_Group(in *userapiv1.Group, out *userapi.Group, s conversion.Scope) error {
2804
+	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
2805
+		defaulting.(func(*userapiv1.Group))(in)
2806
+	}
2807
+	if err := convert_v1_TypeMeta_To_api_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
2808
+		return err
2809
+	}
2810
+	if err := convert_v1_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil {
2811
+		return err
2812
+	}
2813
+	if in.Users != nil {
2814
+		out.Users = make([]string, len(in.Users))
2815
+		for i := range in.Users {
2816
+			out.Users[i] = in.Users[i]
2817
+		}
2818
+	} else {
2819
+		out.Users = nil
2820
+	}
2821
+	return nil
2822
+}
2823
+
2824
+func convert_v1_GroupList_To_api_GroupList(in *userapiv1.GroupList, out *userapi.GroupList, s conversion.Scope) error {
2825
+	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
2826
+		defaulting.(func(*userapiv1.GroupList))(in)
2827
+	}
2828
+	if err := convert_v1_TypeMeta_To_api_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
2829
+		return err
2830
+	}
2831
+	if err := convert_v1_ListMeta_To_api_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil {
2832
+		return err
2833
+	}
2834
+	if in.Items != nil {
2835
+		out.Items = make([]userapi.Group, len(in.Items))
2836
+		for i := range in.Items {
2837
+			if err := convert_v1_Group_To_api_Group(&in.Items[i], &out.Items[i], s); err != nil {
2838
+				return err
2839
+			}
2840
+		}
2841
+	} else {
2842
+		out.Items = nil
2843
+	}
2844
+	return nil
2845
+}
2846
+
2803 2847
 func convert_v1_Identity_To_api_Identity(in *userapiv1.Identity, out *userapi.Identity, s conversion.Scope) error {
2804 2848
 	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
2805 2849
 		defaulting.(func(*userapiv1.Identity))(in)
... ...
@@ -2950,6 +3038,8 @@ func init() {
2950 2950
 		convert_api_EnvVar_To_v1_EnvVar,
2951 2951
 		convert_api_GitBuildSource_To_v1_GitBuildSource,
2952 2952
 		convert_api_GitSourceRevision_To_v1_GitSourceRevision,
2953
+		convert_api_GroupList_To_v1_GroupList,
2954
+		convert_api_Group_To_v1_Group,
2953 2955
 		convert_api_HostSubnetList_To_v1_HostSubnetList,
2954 2956
 		convert_api_HostSubnet_To_v1_HostSubnet,
2955 2957
 		convert_api_IdentityList_To_v1_IdentityList,
... ...
@@ -3024,6 +3114,8 @@ func init() {
3024 3024
 		convert_v1_EnvVar_To_api_EnvVar,
3025 3025
 		convert_v1_GitBuildSource_To_api_GitBuildSource,
3026 3026
 		convert_v1_GitSourceRevision_To_api_GitSourceRevision,
3027
+		convert_v1_GroupList_To_api_GroupList,
3028
+		convert_v1_Group_To_api_Group,
3027 3029
 		convert_v1_HostSubnetList_To_api_HostSubnetList,
3028 3030
 		convert_v1_HostSubnet_To_api_HostSubnet,
3029 3031
 		convert_v1_IdentityList_To_api_IdentityList,
... ...
@@ -2180,6 +2180,52 @@ func deepCopy_v1_TemplateList(in templateapiv1.TemplateList, out *templateapiv1.
2180 2180
 	return nil
2181 2181
 }
2182 2182
 
2183
+func deepCopy_v1_Group(in userapiv1.Group, out *userapiv1.Group, c *conversion.Cloner) error {
2184
+	if newVal, err := c.DeepCopy(in.TypeMeta); err != nil {
2185
+		return err
2186
+	} else {
2187
+		out.TypeMeta = newVal.(v1.TypeMeta)
2188
+	}
2189
+	if newVal, err := c.DeepCopy(in.ObjectMeta); err != nil {
2190
+		return err
2191
+	} else {
2192
+		out.ObjectMeta = newVal.(v1.ObjectMeta)
2193
+	}
2194
+	if in.Users != nil {
2195
+		out.Users = make([]string, len(in.Users))
2196
+		for i := range in.Users {
2197
+			out.Users[i] = in.Users[i]
2198
+		}
2199
+	} else {
2200
+		out.Users = nil
2201
+	}
2202
+	return nil
2203
+}
2204
+
2205
+func deepCopy_v1_GroupList(in userapiv1.GroupList, out *userapiv1.GroupList, c *conversion.Cloner) error {
2206
+	if newVal, err := c.DeepCopy(in.TypeMeta); err != nil {
2207
+		return err
2208
+	} else {
2209
+		out.TypeMeta = newVal.(v1.TypeMeta)
2210
+	}
2211
+	if newVal, err := c.DeepCopy(in.ListMeta); err != nil {
2212
+		return err
2213
+	} else {
2214
+		out.ListMeta = newVal.(v1.ListMeta)
2215
+	}
2216
+	if in.Items != nil {
2217
+		out.Items = make([]userapiv1.Group, len(in.Items))
2218
+		for i := range in.Items {
2219
+			if err := deepCopy_v1_Group(in.Items[i], &out.Items[i], c); err != nil {
2220
+				return err
2221
+			}
2222
+		}
2223
+	} else {
2224
+		out.Items = nil
2225
+	}
2226
+	return nil
2227
+}
2228
+
2183 2229
 func deepCopy_v1_Identity(in userapiv1.Identity, out *userapiv1.Identity, c *conversion.Cloner) error {
2184 2230
 	if newVal, err := c.DeepCopy(in.TypeMeta); err != nil {
2185 2231
 		return err
... ...
@@ -2418,6 +2464,8 @@ func init() {
2418 2418
 		deepCopy_v1_Parameter,
2419 2419
 		deepCopy_v1_Template,
2420 2420
 		deepCopy_v1_TemplateList,
2421
+		deepCopy_v1_Group,
2422
+		deepCopy_v1_GroupList,
2421 2423
 		deepCopy_v1_Identity,
2422 2424
 		deepCopy_v1_IdentityList,
2423 2425
 		deepCopy_v1_User,
... ...
@@ -2615,6 +2615,50 @@ func convert_v1beta3_TemplateList_To_api_TemplateList(in *templateapiv1beta3.Tem
2615 2615
 	return nil
2616 2616
 }
2617 2617
 
2618
+func convert_api_Group_To_v1beta3_Group(in *userapi.Group, out *userapiv1beta3.Group, s conversion.Scope) error {
2619
+	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
2620
+		defaulting.(func(*userapi.Group))(in)
2621
+	}
2622
+	if err := convert_api_TypeMeta_To_v1beta3_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
2623
+		return err
2624
+	}
2625
+	if err := convert_api_ObjectMeta_To_v1beta3_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil {
2626
+		return err
2627
+	}
2628
+	if in.Users != nil {
2629
+		out.Users = make([]string, len(in.Users))
2630
+		for i := range in.Users {
2631
+			out.Users[i] = in.Users[i]
2632
+		}
2633
+	} else {
2634
+		out.Users = nil
2635
+	}
2636
+	return nil
2637
+}
2638
+
2639
+func convert_api_GroupList_To_v1beta3_GroupList(in *userapi.GroupList, out *userapiv1beta3.GroupList, s conversion.Scope) error {
2640
+	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
2641
+		defaulting.(func(*userapi.GroupList))(in)
2642
+	}
2643
+	if err := convert_api_TypeMeta_To_v1beta3_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
2644
+		return err
2645
+	}
2646
+	if err := convert_api_ListMeta_To_v1beta3_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil {
2647
+		return err
2648
+	}
2649
+	if in.Items != nil {
2650
+		out.Items = make([]userapiv1beta3.Group, len(in.Items))
2651
+		for i := range in.Items {
2652
+			if err := convert_api_Group_To_v1beta3_Group(&in.Items[i], &out.Items[i], s); err != nil {
2653
+				return err
2654
+			}
2655
+		}
2656
+	} else {
2657
+		out.Items = nil
2658
+	}
2659
+	return nil
2660
+}
2661
+
2618 2662
 func convert_api_Identity_To_v1beta3_Identity(in *userapi.Identity, out *userapiv1beta3.Identity, s conversion.Scope) error {
2619 2663
 	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
2620 2664
 		defaulting.(func(*userapi.Identity))(in)
... ...
@@ -2736,6 +2780,50 @@ func convert_api_UserList_To_v1beta3_UserList(in *userapi.UserList, out *userapi
2736 2736
 	return nil
2737 2737
 }
2738 2738
 
2739
+func convert_v1beta3_Group_To_api_Group(in *userapiv1beta3.Group, out *userapi.Group, s conversion.Scope) error {
2740
+	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
2741
+		defaulting.(func(*userapiv1beta3.Group))(in)
2742
+	}
2743
+	if err := convert_v1beta3_TypeMeta_To_api_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
2744
+		return err
2745
+	}
2746
+	if err := convert_v1beta3_ObjectMeta_To_api_ObjectMeta(&in.ObjectMeta, &out.ObjectMeta, s); err != nil {
2747
+		return err
2748
+	}
2749
+	if in.Users != nil {
2750
+		out.Users = make([]string, len(in.Users))
2751
+		for i := range in.Users {
2752
+			out.Users[i] = in.Users[i]
2753
+		}
2754
+	} else {
2755
+		out.Users = nil
2756
+	}
2757
+	return nil
2758
+}
2759
+
2760
+func convert_v1beta3_GroupList_To_api_GroupList(in *userapiv1beta3.GroupList, out *userapi.GroupList, s conversion.Scope) error {
2761
+	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
2762
+		defaulting.(func(*userapiv1beta3.GroupList))(in)
2763
+	}
2764
+	if err := convert_v1beta3_TypeMeta_To_api_TypeMeta(&in.TypeMeta, &out.TypeMeta, s); err != nil {
2765
+		return err
2766
+	}
2767
+	if err := convert_v1beta3_ListMeta_To_api_ListMeta(&in.ListMeta, &out.ListMeta, s); err != nil {
2768
+		return err
2769
+	}
2770
+	if in.Items != nil {
2771
+		out.Items = make([]userapi.Group, len(in.Items))
2772
+		for i := range in.Items {
2773
+			if err := convert_v1beta3_Group_To_api_Group(&in.Items[i], &out.Items[i], s); err != nil {
2774
+				return err
2775
+			}
2776
+		}
2777
+	} else {
2778
+		out.Items = nil
2779
+	}
2780
+	return nil
2781
+}
2782
+
2739 2783
 func convert_v1beta3_Identity_To_api_Identity(in *userapiv1beta3.Identity, out *userapi.Identity, s conversion.Scope) error {
2740 2784
 	if defaulting, found := s.DefaultingInterface(reflect.TypeOf(*in)); found {
2741 2785
 		defaulting.(func(*userapiv1beta3.Identity))(in)
... ...
@@ -2887,6 +2975,8 @@ func init() {
2887 2887
 		convert_api_EnvVar_To_v1beta3_EnvVar,
2888 2888
 		convert_api_GitBuildSource_To_v1beta3_GitBuildSource,
2889 2889
 		convert_api_GitSourceRevision_To_v1beta3_GitSourceRevision,
2890
+		convert_api_GroupList_To_v1beta3_GroupList,
2891
+		convert_api_Group_To_v1beta3_Group,
2890 2892
 		convert_api_HostSubnetList_To_v1beta3_HostSubnetList,
2891 2893
 		convert_api_HostSubnet_To_v1beta3_HostSubnet,
2892 2894
 		convert_api_IdentityList_To_v1beta3_IdentityList,
... ...
@@ -2959,6 +3049,8 @@ func init() {
2959 2959
 		convert_v1beta3_EnvVar_To_api_EnvVar,
2960 2960
 		convert_v1beta3_GitBuildSource_To_api_GitBuildSource,
2961 2961
 		convert_v1beta3_GitSourceRevision_To_api_GitSourceRevision,
2962
+		convert_v1beta3_GroupList_To_api_GroupList,
2963
+		convert_v1beta3_Group_To_api_Group,
2962 2964
 		convert_v1beta3_HostSubnetList_To_api_HostSubnetList,
2963 2965
 		convert_v1beta3_HostSubnet_To_api_HostSubnet,
2964 2966
 		convert_v1beta3_IdentityList_To_api_IdentityList,
... ...
@@ -2170,6 +2170,52 @@ func deepCopy_v1beta3_TemplateList(in templateapiv1beta3.TemplateList, out *temp
2170 2170
 	return nil
2171 2171
 }
2172 2172
 
2173
+func deepCopy_v1beta3_Group(in userapiv1beta3.Group, out *userapiv1beta3.Group, c *conversion.Cloner) error {
2174
+	if newVal, err := c.DeepCopy(in.TypeMeta); err != nil {
2175
+		return err
2176
+	} else {
2177
+		out.TypeMeta = newVal.(v1beta3.TypeMeta)
2178
+	}
2179
+	if newVal, err := c.DeepCopy(in.ObjectMeta); err != nil {
2180
+		return err
2181
+	} else {
2182
+		out.ObjectMeta = newVal.(v1beta3.ObjectMeta)
2183
+	}
2184
+	if in.Users != nil {
2185
+		out.Users = make([]string, len(in.Users))
2186
+		for i := range in.Users {
2187
+			out.Users[i] = in.Users[i]
2188
+		}
2189
+	} else {
2190
+		out.Users = nil
2191
+	}
2192
+	return nil
2193
+}
2194
+
2195
+func deepCopy_v1beta3_GroupList(in userapiv1beta3.GroupList, out *userapiv1beta3.GroupList, c *conversion.Cloner) error {
2196
+	if newVal, err := c.DeepCopy(in.TypeMeta); err != nil {
2197
+		return err
2198
+	} else {
2199
+		out.TypeMeta = newVal.(v1beta3.TypeMeta)
2200
+	}
2201
+	if newVal, err := c.DeepCopy(in.ListMeta); err != nil {
2202
+		return err
2203
+	} else {
2204
+		out.ListMeta = newVal.(v1beta3.ListMeta)
2205
+	}
2206
+	if in.Items != nil {
2207
+		out.Items = make([]userapiv1beta3.Group, len(in.Items))
2208
+		for i := range in.Items {
2209
+			if err := deepCopy_v1beta3_Group(in.Items[i], &out.Items[i], c); err != nil {
2210
+				return err
2211
+			}
2212
+		}
2213
+	} else {
2214
+		out.Items = nil
2215
+	}
2216
+	return nil
2217
+}
2218
+
2173 2219
 func deepCopy_v1beta3_Identity(in userapiv1beta3.Identity, out *userapiv1beta3.Identity, c *conversion.Cloner) error {
2174 2220
 	if newVal, err := c.DeepCopy(in.TypeMeta); err != nil {
2175 2221
 		return err
... ...
@@ -2408,6 +2454,8 @@ func init() {
2408 2408
 		deepCopy_v1beta3_Parameter,
2409 2409
 		deepCopy_v1beta3_Template,
2410 2410
 		deepCopy_v1beta3_TemplateList,
2411
+		deepCopy_v1beta3_Group,
2412
+		deepCopy_v1beta3_GroupList,
2411 2413
 		deepCopy_v1beta3_Identity,
2412 2414
 		deepCopy_v1beta3_IdentityList,
2413 2415
 		deepCopy_v1beta3_User,
... ...
@@ -67,4 +67,5 @@ func init() {
67 67
 	Validator.Register(&userapi.User{}, uservalidation.ValidateUser, uservalidation.ValidateUserUpdate)
68 68
 	Validator.Register(&userapi.Identity{}, uservalidation.ValidateIdentity, uservalidation.ValidateIdentityUpdate)
69 69
 	Validator.Register(&userapi.UserIdentityMapping{}, uservalidation.ValidateUserIdentityMapping, uservalidation.ValidateUserIdentityMappingUpdate)
70
+	Validator.Register(&userapi.Group{}, uservalidation.ValidateGroup, uservalidation.ValidateGroupUpdate)
70 71
 }
... ...
@@ -69,7 +69,7 @@ var (
69 69
 		DeploymentGroupName:         {"deployments", "deploymentconfigs", "generatedeploymentconfigs", "deploymentconfigrollbacks"},
70 70
 		SDNGroupName:                {"clusternetworks", "hostsubnets"},
71 71
 		TemplateGroupName:           {"templates", "templateconfigs", "processedtemplates"},
72
-		UserGroupName:               {"identities", "users", "useridentitymappings"},
72
+		UserGroupName:               {"identities", "users", "useridentitymappings", "groups"},
73 73
 		OAuthGroupName:              {"oauthauthorizetokens", "oauthaccesstokens", "oauthclients", "oauthclientauthorizations"},
74 74
 		PolicyOwnerGroupName:        {"policies", "policybindings"},
75 75
 		PermissionGrantingGroupName: {"roles", "rolebindings", "resourceaccessreviews", "subjectaccessreviews"},
... ...
@@ -29,6 +29,7 @@ type Interface interface {
29 29
 	ClusterNetworkingInterface
30 30
 	IdentitiesInterface
31 31
 	UsersInterface
32
+	GroupsInterface
32 33
 	UserIdentityMappingsInterface
33 34
 	ProjectsInterface
34 35
 	ProjectRequestsInterface
... ...
@@ -124,6 +125,11 @@ func (c *Client) UserIdentityMappings() UserIdentityMappingInterface {
124 124
 	return newUserIdentityMappings(c)
125 125
 }
126 126
 
127
+// Groups provides a REST client for Groups
128
+func (c *Client) Groups() GroupInterface {
129
+	return newGroups(c)
130
+}
131
+
127 132
 // Projects provides a REST client for Projects
128 133
 func (c *Client) Projects() ProjectInterface {
129 134
 	return newProjects(c)
130 135
new file mode 100644
... ...
@@ -0,0 +1,71 @@
0
+package client
1
+
2
+import (
3
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
4
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
5
+	userapi "github.com/openshift/origin/pkg/user/api"
6
+)
7
+
8
+// GroupsInterface has methods to work with Group resources
9
+type GroupsInterface interface {
10
+	Groups() GroupInterface
11
+}
12
+
13
+// GroupInterface exposes methods on group resources.
14
+type GroupInterface interface {
15
+	List(label labels.Selector, field fields.Selector) (*userapi.GroupList, error)
16
+	Get(name string) (*userapi.Group, error)
17
+	Create(group *userapi.Group) (*userapi.Group, error)
18
+	Update(group *userapi.Group) (*userapi.Group, error)
19
+	Delete(name string) error
20
+}
21
+
22
+// groups implements GroupInterface interface
23
+type groups struct {
24
+	r *Client
25
+}
26
+
27
+// newGroups returns a groups
28
+func newGroups(c *Client) *groups {
29
+	return &groups{
30
+		r: c,
31
+	}
32
+}
33
+
34
+// List returns a list of groups that match the label and field selectors.
35
+func (c *groups) List(label labels.Selector, field fields.Selector) (result *userapi.GroupList, err error) {
36
+	result = &userapi.GroupList{}
37
+	err = c.r.Get().
38
+		Resource("groups").
39
+		LabelsSelectorParam(label).
40
+		FieldsSelectorParam(field).
41
+		Do().
42
+		Into(result)
43
+	return
44
+}
45
+
46
+// Get returns information about a particular group or an error
47
+func (c *groups) Get(name string) (result *userapi.Group, err error) {
48
+	result = &userapi.Group{}
49
+	err = c.r.Get().Resource("groups").Name(name).Do().Into(result)
50
+	return
51
+}
52
+
53
+// Create creates a new group. Returns the server's representation of the group and error if one occurs.
54
+func (c *groups) Create(group *userapi.Group) (result *userapi.Group, err error) {
55
+	result = &userapi.Group{}
56
+	err = c.r.Post().Resource("groups").Body(group).Do().Into(result)
57
+	return
58
+}
59
+
60
+// Update updates the group on server. Returns the server's representation of the group and error if one occurs.
61
+func (c *groups) Update(group *userapi.Group) (result *userapi.Group, err error) {
62
+	result = &userapi.Group{}
63
+	err = c.r.Put().Resource("groups").Name(group.Name).Body(group).Do().Into(result)
64
+	return
65
+}
66
+
67
+// Delete takes the name of the groups, and returns an error if one occurs during deletion of the groups
68
+func (c *groups) Delete(name string) error {
69
+	return c.r.Delete().Resource("groups").Name(name).Do().Error()
70
+}
... ...
@@ -139,6 +139,11 @@ func (c *Fake) UserIdentityMappings() client.UserIdentityMappingInterface {
139 139
 	return &FakeUserIdentityMappings{Fake: c}
140 140
 }
141 141
 
142
+// Groups provides a fake REST client for Groups
143
+func (c *Fake) Groups() client.GroupInterface {
144
+	return &FakeGroups{Fake: c}
145
+}
146
+
142 147
 // Projects provides a fake REST client for Projects
143 148
 func (c *Fake) Projects() client.ProjectInterface {
144 149
 	return &FakeProjects{Fake: c}
145 150
new file mode 100644
... ...
@@ -0,0 +1,41 @@
0
+package testclient
1
+
2
+import (
3
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
4
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
5
+	userapi "github.com/openshift/origin/pkg/user/api"
6
+)
7
+
8
+// FakeGroups implements GroupsInterface. Meant to be embedded into a struct to get a default
9
+// implementation. This makes faking out just the methods you want to test easier.
10
+type FakeGroups struct {
11
+	Fake *Fake
12
+}
13
+
14
+func (c *FakeGroups) List(label labels.Selector, field fields.Selector) (*userapi.GroupList, error) {
15
+	obj, err := c.Fake.Invokes(FakeAction{Action: "list-groups"}, &userapi.GroupList{})
16
+	return obj.(*userapi.GroupList), err
17
+}
18
+
19
+func (c *FakeGroups) Get(name string) (*userapi.Group, error) {
20
+	obj, err := c.Fake.Invokes(FakeAction{Action: "get-group", Value: name}, &userapi.Group{})
21
+	return obj.(*userapi.Group), err
22
+}
23
+
24
+func (c *FakeGroups) Create(group *userapi.Group) (*userapi.Group, error) {
25
+	obj, err := c.Fake.Invokes(FakeAction{Action: "create-group", Value: group}, &userapi.Group{})
26
+	return obj.(*userapi.Group), err
27
+}
28
+
29
+func (c *FakeGroups) Update(group *userapi.Group) (*userapi.Group, error) {
30
+	obj, err := c.Fake.Invokes(FakeAction{Action: "update-group", Value: group}, &userapi.Group{})
31
+	return obj.(*userapi.Group), err
32
+}
33
+
34
+func (c *FakeGroups) Delete(name string) error {
35
+	c.Fake.Lock.Lock()
36
+	defer c.Fake.Lock.Unlock()
37
+
38
+	c.Fake.Actions = append(c.Fake.Actions, FakeAction{Action: "delete-group"})
39
+	return nil
40
+}
... ...
@@ -54,6 +54,7 @@ func describerMap(c *client.Client, kclient kclient.Interface, host string) map[
54 54
 		"ClusterRoleBinding":   &ClusterRoleBindingDescriber{c},
55 55
 		"ClusterRole":          &ClusterRoleDescriber{c},
56 56
 		"User":                 &UserDescriber{c},
57
+		"Group":                &GroupDescriber{c.Groups()},
57 58
 		"UserIdentityMapping":  &UserIdentityMappingDescriber{c},
58 59
 	}
59 60
 	return m
... ...
@@ -878,6 +879,36 @@ func (d *UserDescriber) Describe(namespace, name string) (string, error) {
878 878
 	})
879 879
 }
880 880
 
881
+// GroupDescriber generates information about a group
882
+type GroupDescriber struct {
883
+	c client.GroupInterface
884
+}
885
+
886
+// Describe returns the description of a group
887
+func (d *GroupDescriber) Describe(namespace, name string) (string, error) {
888
+	group, err := d.c.Get(name)
889
+	if err != nil {
890
+		return "", err
891
+	}
892
+
893
+	return tabbedString(func(out *tabwriter.Writer) error {
894
+		formatMeta(out, group.ObjectMeta)
895
+
896
+		if len(group.Users) == 0 {
897
+			formatString(out, "Users", "<none>")
898
+		} else {
899
+			for i, user := range group.Users {
900
+				if i == 0 {
901
+					formatString(out, "Users", user)
902
+				} else {
903
+					fmt.Fprintf(out, "           \t%s\n", user)
904
+				}
905
+			}
906
+		}
907
+		return nil
908
+	})
909
+}
910
+
881 911
 // policy describers
882 912
 
883 913
 // PolicyDescriber generates information about a Project
... ...
@@ -50,6 +50,7 @@ var (
50 50
 	userColumns                = []string{"NAME", "UID", "FULL NAME", "IDENTITIES"}
51 51
 	identityColumns            = []string{"NAME", "IDP NAME", "IDP USER NAME", "USER NAME", "USER UID"}
52 52
 	userIdentityMappingColumns = []string{"NAME", "IDENTITY", "USER NAME", "USER UID"}
53
+	groupColumns               = []string{"NAME", "USERS"}
53 54
 
54 55
 	// IsPersonalSubjectAccessReviewColumns contains known custom role extensions
55 56
 	IsPersonalSubjectAccessReviewColumns = []string{"NAME"}
... ...
@@ -113,6 +114,8 @@ func NewHumanReadablePrinter(noHeaders, withNamespace, wide bool, columnLabels [
113 113
 	p.Handler(identityColumns, printIdentity)
114 114
 	p.Handler(identityColumns, printIdentityList)
115 115
 	p.Handler(userIdentityMappingColumns, printUserIdentityMapping)
116
+	p.Handler(groupColumns, printGroup)
117
+	p.Handler(groupColumns, printGroupList)
116 118
 
117 119
 	p.Handler(IsPersonalSubjectAccessReviewColumns, printIsPersonalSubjectAccessReview)
118 120
 
... ...
@@ -549,6 +552,19 @@ func printUserIdentityMapping(mapping *userapi.UserIdentityMapping, w io.Writer,
549 549
 	return err
550 550
 }
551 551
 
552
+func printGroup(group *userapi.Group, w io.Writer, withNamespace, wide bool, columnLabels []string) error {
553
+	_, err := fmt.Fprintf(w, "%s\t%s\n", group.Name, strings.Join(group.Users, ", "))
554
+	return err
555
+}
556
+func printGroupList(list *userapi.GroupList, w io.Writer, withNamespace, wide bool, columnLabels []string) error {
557
+	for _, item := range list.Items {
558
+		if err := printGroup(&item, w, withNamespace, wide, columnLabels); err != nil {
559
+			return err
560
+		}
561
+	}
562
+	return nil
563
+}
564
+
552 565
 func printHostSubnet(h *sdnapi.HostSubnet, w io.Writer, withNamespace, wide bool, columnLabels []string) error {
553 566
 	_, err := fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", h.Name, h.Host, h.HostIP, h.Subnet)
554 567
 	return err
... ...
@@ -59,6 +59,7 @@ import (
59 59
 	"github.com/openshift/origin/pkg/service"
60 60
 	templateregistry "github.com/openshift/origin/pkg/template/registry"
61 61
 	templateetcd "github.com/openshift/origin/pkg/template/registry/etcd"
62
+	groupetcd "github.com/openshift/origin/pkg/user/registry/group/etcd"
62 63
 	identityregistry "github.com/openshift/origin/pkg/user/registry/identity"
63 64
 	identityetcd "github.com/openshift/origin/pkg/user/registry/identity/etcd"
64 65
 	userregistry "github.com/openshift/origin/pkg/user/registry/user"
... ...
@@ -427,6 +428,7 @@ func (c *MasterConfig) GetRestStorage() map[string]rest.Storage {
427 427
 		"clusterNetworks": clusterNetworkStorage,
428 428
 
429 429
 		"users":                userStorage,
430
+		"groups":               groupetcd.NewREST(c.EtcdHelper),
430 431
 		"identities":           identityStorage,
431 432
 		"userIdentityMappings": userIdentityMappingStorage,
432 433
 
... ...
@@ -11,5 +11,7 @@ func init() {
11 11
 		&Identity{},
12 12
 		&IdentityList{},
13 13
 		&UserIdentityMapping{},
14
+		&Group{},
15
+		&GroupList{},
14 16
 	)
15 17
 }
... ...
@@ -53,6 +53,22 @@ type UserIdentityMapping struct {
53 53
 	User     kapi.ObjectReference
54 54
 }
55 55
 
56
+// Group represents a referenceable set of Users
57
+type Group struct {
58
+	kapi.TypeMeta
59
+	kapi.ObjectMeta
60
+
61
+	Users []string
62
+}
63
+
64
+type GroupList struct {
65
+	kapi.TypeMeta
66
+	kapi.ListMeta
67
+	Items []Group
68
+}
69
+
70
+func (*GroupList) IsAnAPIObject()           {}
71
+func (*Group) IsAnAPIObject()               {}
56 72
 func (*User) IsAnAPIObject()                {}
57 73
 func (*UserList) IsAnAPIObject()            {}
58 74
 func (*Identity) IsAnAPIObject()            {}
... ...
@@ -11,5 +11,7 @@ func init() {
11 11
 		&Identity{},
12 12
 		&IdentityList{},
13 13
 		&UserIdentityMapping{},
14
+		&Group{},
15
+		&GroupList{},
14 16
 	)
15 17
 }
... ...
@@ -53,6 +53,23 @@ type UserIdentityMapping struct {
53 53
 	User     kapi.ObjectReference `json:"user,omitempty" description:"reference to a user"`
54 54
 }
55 55
 
56
+// Group represents a referenceable set of Users
57
+type Group struct {
58
+	kapi.TypeMeta   `json:",inline"`
59
+	kapi.ObjectMeta `json:"metadata,omitempty"`
60
+
61
+	// Users is the list of users in this group.
62
+	Users []string `json:"users" description:"list of users in this group"`
63
+}
64
+
65
+type GroupList struct {
66
+	kapi.TypeMeta `json:",inline"`
67
+	kapi.ListMeta `json:"metadata,omitempty"`
68
+	Items         []Group `json:"items" description:"list of groups"`
69
+}
70
+
71
+func (*GroupList) IsAnAPIObject()           {}
72
+func (*Group) IsAnAPIObject()               {}
56 73
 func (*User) IsAnAPIObject()                {}
57 74
 func (*UserList) IsAnAPIObject()            {}
58 75
 func (*Identity) IsAnAPIObject()            {}
... ...
@@ -11,5 +11,7 @@ func init() {
11 11
 		&Identity{},
12 12
 		&IdentityList{},
13 13
 		&UserIdentityMapping{},
14
+		&Group{},
15
+		&GroupList{},
14 16
 	)
15 17
 }
... ...
@@ -53,6 +53,23 @@ type UserIdentityMapping struct {
53 53
 	User     kapi.ObjectReference `json:"user,omitempty"`
54 54
 }
55 55
 
56
+// Group represents a referenceable set of Users
57
+type Group struct {
58
+	kapi.TypeMeta   `json:",inline"`
59
+	kapi.ObjectMeta `json:"metadata,omitempty"`
60
+
61
+	// Users is the list of users in this group.
62
+	Users []string `json:"users" description:"list of users in this group"`
63
+}
64
+
65
+type GroupList struct {
66
+	kapi.TypeMeta `json:",inline"`
67
+	kapi.ListMeta `json:"metadata,omitempty"`
68
+	Items         []Group `json:"items" description:"list of groups"`
69
+}
70
+
71
+func (*GroupList) IsAnAPIObject()           {}
72
+func (*Group) IsAnAPIObject()               {}
56 73
 func (*User) IsAnAPIObject()                {}
57 74
 func (*UserList) IsAnAPIObject()            {}
58 75
 func (*Identity) IsAnAPIObject()            {}
... ...
@@ -73,6 +73,30 @@ func ValidateIdentityProviderUserName(name string) (bool, string) {
73 73
 	return ValidateUserName(name, false)
74 74
 }
75 75
 
76
+func ValidateGroup(group *api.Group) fielderrors.ValidationErrorList {
77
+	allErrs := fielderrors.ValidationErrorList{}
78
+	allErrs = append(allErrs, kvalidation.ValidateObjectMeta(&group.ObjectMeta, false, ValidateGroupName).Prefix("metadata")...)
79
+
80
+	for index, user := range group.Users {
81
+		if len(user) == 0 {
82
+			allErrs = append(allErrs, fielderrors.NewFieldInvalid(fmt.Sprintf("users[%d]", index), user, "may not be empty"))
83
+			continue
84
+		}
85
+		if ok, msg := ValidateUserName(user, false); !ok {
86
+			allErrs = append(allErrs, fielderrors.NewFieldInvalid(fmt.Sprintf("users[%d]", index), user, msg))
87
+		}
88
+	}
89
+
90
+	return allErrs
91
+}
92
+
93
+func ValidateGroupUpdate(group *api.Group, old *api.Group) fielderrors.ValidationErrorList {
94
+	allErrs := fielderrors.ValidationErrorList{}
95
+	allErrs = append(allErrs, kvalidation.ValidateObjectMetaUpdate(&group.ObjectMeta, &old.ObjectMeta).Prefix("metadata")...)
96
+	allErrs = append(allErrs, ValidateGroup(group)...)
97
+	return allErrs
98
+}
99
+
76 100
 func ValidateUser(user *api.User) fielderrors.ValidationErrorList {
77 101
 	allErrs := fielderrors.ValidationErrorList{}
78 102
 	allErrs = append(allErrs, kvalidation.ValidateObjectMeta(&user.ObjectMeta, false, ValidateUserName).Prefix("metadata")...)
... ...
@@ -7,6 +7,75 @@ import (
7 7
 	"github.com/openshift/origin/pkg/user/api"
8 8
 )
9 9
 
10
+func TestValidateGroup(t *testing.T) {
11
+	validObj := func() *api.Group {
12
+		return &api.Group{
13
+			ObjectMeta: kapi.ObjectMeta{
14
+				Name: "myname",
15
+			},
16
+			Users: []string{"myuser"},
17
+		}
18
+	}
19
+
20
+	if errs := ValidateGroup(validObj()); len(errs) > 0 {
21
+		t.Errorf("Expected no errors, got %v", errs)
22
+	}
23
+
24
+	emptyUser := validObj()
25
+	emptyUser.Users = []string{""}
26
+	if errs := ValidateGroup(emptyUser); len(errs) == 0 {
27
+		t.Errorf("Expected error, got none")
28
+	}
29
+
30
+	invalidUser := validObj()
31
+	invalidUser.Users = []string{"bad:user:name"}
32
+	if errs := ValidateGroup(invalidUser); len(errs) == 0 {
33
+		t.Errorf("Expected error, got none")
34
+	}
35
+
36
+	invalidName := validObj()
37
+	invalidName.Name = "bad:group:name"
38
+	if errs := ValidateGroup(invalidName); len(errs) == 0 {
39
+		t.Errorf("Expected error, got none")
40
+	}
41
+}
42
+
43
+func TestValidateGroupUpdate(t *testing.T) {
44
+	validObj := func() *api.Group {
45
+		return &api.Group{
46
+			ObjectMeta: kapi.ObjectMeta{
47
+				Name:            "myname",
48
+				ResourceVersion: "1",
49
+			},
50
+			Users: []string{"myuser"},
51
+		}
52
+	}
53
+
54
+	oldObj := validObj()
55
+
56
+	if errs := ValidateGroupUpdate(validObj(), oldObj); len(errs) > 0 {
57
+		t.Errorf("Expected no errors, got %v", errs)
58
+	}
59
+
60
+	emptyUser := validObj()
61
+	emptyUser.Users = []string{""}
62
+	if errs := ValidateGroupUpdate(emptyUser, oldObj); len(errs) == 0 {
63
+		t.Errorf("Expected error, got none")
64
+	}
65
+
66
+	invalidUser := validObj()
67
+	invalidUser.Users = []string{"bad:user:name"}
68
+	if errs := ValidateGroupUpdate(invalidUser, oldObj); len(errs) == 0 {
69
+		t.Errorf("Expected error, got none")
70
+	}
71
+
72
+	invalidName := validObj()
73
+	invalidName.Name = "bad:group:name"
74
+	if errs := ValidateGroupUpdate(invalidName, oldObj); len(errs) == 0 {
75
+		t.Errorf("Expected error, got none")
76
+	}
77
+}
78
+
10 79
 func TestValidateUser(t *testing.T) {
11 80
 	validObj := func() *api.User {
12 81
 		return &api.User{
13 82
new file mode 100644
... ...
@@ -0,0 +1,50 @@
0
+package etcd
1
+
2
+import (
3
+	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
4
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
5
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
6
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/generic"
7
+	etcdgeneric "github.com/GoogleCloudPlatform/kubernetes/pkg/registry/generic/etcd"
8
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
9
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/tools"
10
+
11
+	"github.com/openshift/origin/pkg/user/api"
12
+	"github.com/openshift/origin/pkg/user/registry/group"
13
+	"github.com/openshift/origin/pkg/util"
14
+)
15
+
16
+const EtcdPrefix = "/groups"
17
+
18
+// REST implements a RESTStorage for groups against etcd
19
+type REST struct {
20
+	*etcdgeneric.Etcd
21
+}
22
+
23
+// NewREST returns a RESTStorage object that will work against groups
24
+func NewREST(h tools.EtcdHelper) *REST {
25
+	store := &etcdgeneric.Etcd{
26
+		NewFunc:     func() runtime.Object { return &api.Group{} },
27
+		NewListFunc: func() runtime.Object { return &api.GroupList{} },
28
+		KeyRootFunc: func(ctx kapi.Context) string {
29
+			return EtcdPrefix
30
+		},
31
+		KeyFunc: func(ctx kapi.Context, name string) (string, error) {
32
+			return util.NoNamespaceKeyFunc(ctx, EtcdPrefix, name)
33
+		},
34
+		ObjectNameFunc: func(obj runtime.Object) (string, error) {
35
+			return obj.(*api.Group).Name, nil
36
+		},
37
+		PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher {
38
+			return group.MatchGroup(label, field)
39
+		},
40
+		EndpointName: "groups",
41
+
42
+		CreateStrategy: group.Strategy,
43
+		UpdateStrategy: group.Strategy,
44
+
45
+		Helper: h,
46
+	}
47
+
48
+	return &REST{store}
49
+}
0 50
new file mode 100644
... ...
@@ -0,0 +1,77 @@
0
+package group
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/fields"
6
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
7
+
8
+	"github.com/openshift/origin/pkg/user/api"
9
+)
10
+
11
+// Registry is an interface implemented by things that know how to store Group objects.
12
+type Registry interface {
13
+	// ListGroups obtains a list of groups having labels which match selector.
14
+	ListGroups(ctx kapi.Context, selector labels.Selector) (*api.GroupList, error)
15
+	// GetGroup returns a specific group
16
+	GetGroup(ctx kapi.Context, name string) (*api.Group, error)
17
+	// CreateGroup creates a group
18
+	CreateGroup(ctx kapi.Context, group *api.Group) (*api.Group, error)
19
+	// UpdateGroup updates an existing group
20
+	UpdateGroup(ctx kapi.Context, group *api.Group) (*api.Group, error)
21
+	// DeleteGroup deletes a name.
22
+	DeleteGroup(ctx kapi.Context, name string) error
23
+}
24
+
25
+// Storage is an interface for a standard REST Storage backend
26
+type Storage interface {
27
+	rest.StandardStorage
28
+}
29
+
30
+// storage puts strong typing around storage calls
31
+type storage struct {
32
+	Storage
33
+}
34
+
35
+// NewRegistry returns a new Registry interface for the given Storage. Any mismatched
36
+// types will panic.
37
+func NewRegistry(s Storage) Registry {
38
+	return &storage{s}
39
+}
40
+
41
+func (s *storage) ListGroups(ctx kapi.Context, label labels.Selector) (*api.GroupList, error) {
42
+	obj, err := s.List(ctx, label, fields.Everything())
43
+	if err != nil {
44
+		return nil, err
45
+	}
46
+	return obj.(*api.GroupList), nil
47
+}
48
+
49
+func (s *storage) GetGroup(ctx kapi.Context, name string) (*api.Group, error) {
50
+	obj, err := s.Get(ctx, name)
51
+	if err != nil {
52
+		return nil, err
53
+	}
54
+	return obj.(*api.Group), nil
55
+}
56
+
57
+func (s *storage) CreateGroup(ctx kapi.Context, group *api.Group) (*api.Group, error) {
58
+	obj, err := s.Create(ctx, group)
59
+	if err != nil {
60
+		return nil, err
61
+	}
62
+	return obj.(*api.Group), nil
63
+}
64
+
65
+func (s *storage) UpdateGroup(ctx kapi.Context, group *api.Group) (*api.Group, error) {
66
+	obj, _, err := s.Update(ctx, group)
67
+	if err != nil {
68
+		return nil, err
69
+	}
70
+	return obj.(*api.Group), nil
71
+}
72
+
73
+func (s *storage) DeleteGroup(ctx kapi.Context, name string) error {
74
+	_, err := s.Delete(ctx, name, nil)
75
+	return err
76
+}
0 77
new file mode 100644
... ...
@@ -0,0 +1,76 @@
0
+package group
1
+
2
+import (
3
+	"fmt"
4
+
5
+	kapi "github.com/GoogleCloudPlatform/kubernetes/pkg/api"
6
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/fields"
7
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/labels"
8
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/registry/generic"
9
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
10
+	"github.com/GoogleCloudPlatform/kubernetes/pkg/util/fielderrors"
11
+
12
+	"github.com/openshift/origin/pkg/user/api"
13
+	"github.com/openshift/origin/pkg/user/api/validation"
14
+)
15
+
16
+// groupStrategy implements behavior for Groups
17
+type groupStrategy struct {
18
+	runtime.ObjectTyper
19
+}
20
+
21
+// Strategy is the default logic that applies when creating and updating Group
22
+// objects via the REST API.
23
+var Strategy = groupStrategy{kapi.Scheme}
24
+
25
+func (groupStrategy) PrepareForUpdate(obj, old runtime.Object) {}
26
+
27
+// NamespaceScoped is false for groups
28
+func (groupStrategy) NamespaceScoped() bool {
29
+	return false
30
+}
31
+
32
+func (groupStrategy) GenerateName(base string) string {
33
+	return base
34
+}
35
+
36
+func (groupStrategy) PrepareForCreate(obj runtime.Object) {
37
+}
38
+
39
+// Validate validates a new group
40
+func (groupStrategy) Validate(ctx kapi.Context, obj runtime.Object) fielderrors.ValidationErrorList {
41
+	return validation.ValidateGroup(obj.(*api.Group))
42
+}
43
+
44
+// AllowCreateOnUpdate is false for groups
45
+func (groupStrategy) AllowCreateOnUpdate() bool {
46
+	return false
47
+}
48
+
49
+func (groupStrategy) AllowUnconditionalUpdate() bool {
50
+	return false
51
+}
52
+
53
+// ValidateUpdate is the default update validation for an end group.
54
+func (groupStrategy) ValidateUpdate(ctx kapi.Context, obj, old runtime.Object) fielderrors.ValidationErrorList {
55
+	return validation.ValidateGroupUpdate(obj.(*api.Group), old.(*api.Group))
56
+}
57
+
58
+// MatchGroup returns a generic matcher for a given label and field selector.
59
+func MatchGroup(label labels.Selector, field fields.Selector) generic.Matcher {
60
+	return generic.MatcherFunc(func(obj runtime.Object) (bool, error) {
61
+		groupObj, ok := obj.(*api.Group)
62
+		if !ok {
63
+			return false, fmt.Errorf("not a group")
64
+		}
65
+		fields := GroupToSelectableFields(groupObj)
66
+		return label.Matches(labels.Set(groupObj.Labels)) && field.Matches(fields), nil
67
+	})
68
+}
69
+
70
+// GroupToSelectableFields returns a label set that represents the object
71
+func GroupToSelectableFields(group *api.Group) labels.Set {
72
+	return labels.Set{
73
+		"name": group.Name,
74
+	}
75
+}
... ...
@@ -602,6 +602,7 @@ _oc_get()
602 602
     must_have_one_noun+=("deploymentconfig")
603 603
     must_have_one_noun+=("endpoints")
604 604
     must_have_one_noun+=("event")
605
+    must_have_one_noun+=("group")
605 606
     must_have_one_noun+=("hostsubnet")
606 607
     must_have_one_noun+=("identity")
607 608
     must_have_one_noun+=("image")
... ...
@@ -662,6 +663,7 @@ _oc_describe()
662 662
     must_have_one_noun+=("clusterrole")
663 663
     must_have_one_noun+=("clusterrolebinding")
664 664
     must_have_one_noun+=("deploymentconfig")
665
+    must_have_one_noun+=("group")
665 666
     must_have_one_noun+=("identity")
666 667
     must_have_one_noun+=("image")
667 668
     must_have_one_noun+=("imagestream")
... ...
@@ -2020,6 +2020,7 @@ _openshift_cli_get()
2020 2020
     must_have_one_noun+=("deploymentconfig")
2021 2021
     must_have_one_noun+=("endpoints")
2022 2022
     must_have_one_noun+=("event")
2023
+    must_have_one_noun+=("group")
2023 2024
     must_have_one_noun+=("hostsubnet")
2024 2025
     must_have_one_noun+=("identity")
2025 2026
     must_have_one_noun+=("image")
... ...
@@ -2080,6 +2081,7 @@ _openshift_cli_describe()
2080 2080
     must_have_one_noun+=("clusterrole")
2081 2081
     must_have_one_noun+=("clusterrolebinding")
2082 2082
     must_have_one_noun+=("deploymentconfig")
2083
+    must_have_one_noun+=("group")
2083 2084
     must_have_one_noun+=("identity")
2084 2085
     must_have_one_noun+=("image")
2085 2086
     must_have_one_noun+=("imagestream")