Skip to content

Commit 5549a0d

Browse files
authored
Merge pull request kubernetes#95012 from nodo/add-namespace-to-post-based-namespace-creation
Make the creation of namespace using POST and PATCH consistent
2 parents 1ec3345 + 3cb510e commit 5549a0d

File tree

5 files changed

+64
-10
lines changed

5 files changed

+64
-10
lines changed

staging/src/k8s.io/apiserver/pkg/admission/plugin/resourcequota/BUILD

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,10 @@ go_test(
6060
"//staging/src/k8s.io/apimachinery/pkg/api/resource:go_default_library",
6161
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
6262
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
63+
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
64+
"//staging/src/k8s.io/apiserver/pkg/admission:go_default_library",
6365
"//staging/src/k8s.io/apiserver/pkg/admission/plugin/resourcequota/apis/resourcequota:go_default_library",
66+
"//staging/src/k8s.io/apiserver/pkg/admission/plugin/resourcequota/apis/resourcequota/v1:go_default_library",
6467
"//staging/src/k8s.io/client-go/informers:go_default_library",
6568
"//staging/src/k8s.io/client-go/kubernetes/fake:go_default_library",
6669
"//vendor/github.com/hashicorp/golang-lru:go_default_library",

staging/src/k8s.io/apiserver/pkg/admission/plugin/resourcequota/admission.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"k8s.io/apiserver/pkg/admission"
2727
genericadmissioninitializer "k8s.io/apiserver/pkg/admission/initializer"
2828
resourcequotaapi "k8s.io/apiserver/pkg/admission/plugin/resourcequota/apis/resourcequota"
29+
v1 "k8s.io/apiserver/pkg/admission/plugin/resourcequota/apis/resourcequota/v1"
2930
"k8s.io/apiserver/pkg/admission/plugin/resourcequota/apis/resourcequota/validation"
3031
quota "k8s.io/apiserver/pkg/quota/v1"
3132
"k8s.io/apiserver/pkg/quota/v1/generic"
@@ -36,6 +37,8 @@ import (
3637
// PluginName is a string with the name of the plugin
3738
const PluginName = "ResourceQuota"
3839

40+
var namespaceGVK = v1.SchemeGroupVersion.WithKind("Namespace").GroupKind()
41+
3942
// Register registers a plugin
4043
func Register(plugins *admission.Plugins) {
4144
plugins.Register(PluginName,
@@ -136,9 +139,13 @@ func (a *QuotaAdmission) Validate(ctx context.Context, attr admission.Attributes
136139
if attr.GetSubresource() != "" {
137140
return nil
138141
}
139-
// ignore all operations that are not namespaced
140-
if attr.GetNamespace() == "" {
142+
// ignore all operations that are not namespaced or creation of namespaces
143+
if attr.GetNamespace() == "" || isNamespaceCreation(attr) {
141144
return nil
142145
}
143146
return a.evaluator.Evaluate(attr)
144147
}
148+
149+
func isNamespaceCreation(attr admission.Attributes) bool {
150+
return attr.GetOperation() == admission.Create && attr.GetKind().GroupKind() == namespaceGVK
151+
}

staging/src/k8s.io/apiserver/pkg/admission/plugin/resourcequota/admission_test.go

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,15 @@ limitations under the License.
1717
package resourcequota
1818

1919
import (
20+
"context"
21+
"errors"
2022
"testing"
2123

2224
corev1 "k8s.io/api/core/v1"
2325
"k8s.io/apimachinery/pkg/api/resource"
26+
"k8s.io/apimachinery/pkg/runtime/schema"
27+
"k8s.io/apiserver/pkg/admission"
28+
v1 "k8s.io/apiserver/pkg/admission/plugin/resourcequota/apis/resourcequota/v1"
2429
)
2530

2631
func TestPrettyPrint(t *testing.T) {
@@ -122,3 +127,37 @@ func TestHasUsageStats(t *testing.T) {
122127
}
123128
}
124129
}
130+
131+
type fakeEvaluator struct{}
132+
133+
func (fakeEvaluator) Evaluate(a admission.Attributes) error {
134+
return errors.New("should not be called")
135+
}
136+
137+
func TestExcludedOperations(t *testing.T) {
138+
a := &QuotaAdmission{
139+
evaluator: fakeEvaluator{},
140+
}
141+
testCases := []struct {
142+
desc string
143+
attr admission.Attributes
144+
}{
145+
{
146+
"subresource",
147+
admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, "namespace", "name", schema.GroupVersionResource{}, "subresource", admission.Create, nil, false, nil),
148+
},
149+
{
150+
"non-namespaced resource",
151+
admission.NewAttributesRecord(nil, nil, schema.GroupVersionKind{}, "", "namespace", schema.GroupVersionResource{}, "", admission.Create, nil, false, nil),
152+
},
153+
{
154+
"namespace creation",
155+
admission.NewAttributesRecord(nil, nil, v1.SchemeGroupVersion.WithKind("Namespace"), "namespace", "namespace", schema.GroupVersionResource{}, "", admission.Create, nil, false, nil),
156+
},
157+
}
158+
for _, test := range testCases {
159+
if err := a.Validate(context.TODO(), test.attr, nil); err != nil {
160+
t.Errorf("Test case: %q. Expected no error but got: %v", test.desc, err)
161+
}
162+
}
163+
}

staging/src/k8s.io/apiserver/pkg/endpoints/handlers/create.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ import (
4444
utiltrace "k8s.io/utils/trace"
4545
)
4646

47+
var namespaceGVK = schema.GroupVersionKind{Group: "", Version: "v1", Kind: "Namespace"}
48+
4749
func createHandler(r rest.NamedCreater, scope *RequestScope, admit admission.Interface, includeName bool) http.HandlerFunc {
4850
return func(w http.ResponseWriter, req *http.Request) {
4951
// For performance tracking purposes.
@@ -76,7 +78,6 @@ func createHandler(r rest.NamedCreater, scope *RequestScope, admit admission.Int
7678

7779
ctx, cancel := context.WithTimeout(req.Context(), timeout)
7880
defer cancel()
79-
ctx = request.WithNamespace(ctx, namespace)
8081
outputMediaType, _, err := negotiation.NegotiateOutputMediaType(req, scope.Serializer, scope)
8182
if err != nil {
8283
scope.err(err, w, req)
@@ -128,17 +129,21 @@ func createHandler(r rest.NamedCreater, scope *RequestScope, admit admission.Int
128129
}
129130
trace.Step("Conversion done")
130131

132+
// On create, get name from new object if unset
133+
if len(name) == 0 {
134+
_, name, _ = scope.Namer.ObjectName(obj)
135+
}
136+
if len(namespace) == 0 && *gvk == namespaceGVK {
137+
namespace = name
138+
}
139+
ctx = request.WithNamespace(ctx, namespace)
140+
131141
ae := request.AuditEventFrom(ctx)
132142
admit = admission.WithAudit(admit, ae)
133143
audit.LogRequestObject(ae, obj, scope.Resource, scope.Subresource, scope.Serializer)
134144

135145
userInfo, _ := request.UserFrom(ctx)
136146

137-
// On create, get name from new object if unset
138-
if len(name) == 0 {
139-
_, name, _ = scope.Namer.ObjectName(obj)
140-
}
141-
142147
trace.Step("About to store object in database")
143148
admissionAttributes := admission.NewAttributesRecord(obj, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, options, dryrun.IsDryRun(options.DryRun), userInfo)
144149
requestFunc := func() (runtime.Object, error) {

test/integration/apiserver/admissionwebhook/admission_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,8 +218,8 @@ func (h *holder) reset(t *testing.T) {
218218
}
219219
}
220220
func (h *holder) expect(gvr schema.GroupVersionResource, gvk, optionsGVK schema.GroupVersionKind, operation v1beta1.Operation, name, namespace string, object, oldObject, options bool) {
221-
// Special-case namespaces, since the object name shows up in request attributes for update/delete requests
222-
if len(namespace) == 0 && gvk.Group == "" && gvk.Version == "v1" && gvk.Kind == "Namespace" && operation != v1beta1.Create {
221+
// Special-case namespaces, since the object name shows up in request attributes
222+
if len(namespace) == 0 && gvk.Group == "" && gvk.Version == "v1" && gvk.Kind == "Namespace" {
223223
namespace = name
224224
}
225225

0 commit comments

Comments
 (0)