mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-08-19 00:31:00 +00:00
populate object name for admission attributes when CREATE
This commit is contained in:
parent
ff1e1127e2
commit
34cab8f80a
@ -361,11 +361,6 @@ func (p *Plugin) admitNode(nodeName string, a admission.Attributes) error {
|
|||||||
if forbiddenUpdateLabels := p.getForbiddenUpdateLabels(modifiedLabels); len(forbiddenUpdateLabels) > 0 {
|
if forbiddenUpdateLabels := p.getForbiddenUpdateLabels(modifiedLabels); len(forbiddenUpdateLabels) > 0 {
|
||||||
klog.Warningf("node %q added disallowed labels on node creation: %s", nodeName, strings.Join(forbiddenUpdateLabels.List(), ", "))
|
klog.Warningf("node %q added disallowed labels on node creation: %s", nodeName, strings.Join(forbiddenUpdateLabels.List(), ", "))
|
||||||
}
|
}
|
||||||
|
|
||||||
// On create, get name from new object if unset in admission
|
|
||||||
if len(requestedName) == 0 {
|
|
||||||
requestedName = node.Name
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if requestedName != nodeName {
|
if requestedName != nodeName {
|
||||||
return admission.NewForbidden(a, fmt.Errorf("node %q is not allowed to modify node %q", nodeName, requestedName))
|
return admission.NewForbidden(a, fmt.Errorf("node %q is not allowed to modify node %q", nodeName, requestedName))
|
||||||
|
@ -826,19 +826,19 @@ func Test_nodePlugin_Admit(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "allow create of my node pulling name from object",
|
name: "allow create of my node pulling name from object",
|
||||||
podsGetter: noExistingPods,
|
podsGetter: noExistingPods,
|
||||||
attributes: admission.NewAttributesRecord(mynodeObj, nil, nodeKind, mynodeObj.Namespace, "", nodeResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode),
|
attributes: admission.NewAttributesRecord(mynodeObj, nil, nodeKind, mynodeObj.Namespace, "mynode", nodeResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode),
|
||||||
err: "",
|
err: "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "allow create of my node with taints",
|
name: "allow create of my node with taints",
|
||||||
podsGetter: noExistingPods,
|
podsGetter: noExistingPods,
|
||||||
attributes: admission.NewAttributesRecord(mynodeObjTaintA, nil, nodeKind, mynodeObj.Namespace, "", nodeResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode),
|
attributes: admission.NewAttributesRecord(mynodeObjTaintA, nil, nodeKind, mynodeObj.Namespace, "mynode", nodeResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode),
|
||||||
err: "",
|
err: "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "allow create of my node with labels",
|
name: "allow create of my node with labels",
|
||||||
podsGetter: noExistingPods,
|
podsGetter: noExistingPods,
|
||||||
attributes: admission.NewAttributesRecord(setAllowedCreateLabels(mynodeObj, ""), nil, nodeKind, mynodeObj.Namespace, "", nodeResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode),
|
attributes: admission.NewAttributesRecord(setAllowedCreateLabels(mynodeObj, ""), nil, nodeKind, mynodeObj.Namespace, "mynode", nodeResource, "", admission.Create, &metav1.CreateOptions{}, false, mynode),
|
||||||
err: "",
|
err: "",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -3665,7 +3665,7 @@ func TestParentResourceIsRequired(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateWithName(t *testing.T) {
|
func TestNamedCreaterWithName(t *testing.T) {
|
||||||
pathName := "helloworld"
|
pathName := "helloworld"
|
||||||
storage := &NamedCreaterRESTStorage{SimpleRESTStorage: &SimpleRESTStorage{}}
|
storage := &NamedCreaterRESTStorage{SimpleRESTStorage: &SimpleRESTStorage{}}
|
||||||
handler := handle(map[string]rest.Storage{
|
handler := handle(map[string]rest.Storage{
|
||||||
@ -3697,6 +3697,145 @@ func TestCreateWithName(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestNamedCreaterWithoutName(t *testing.T) {
|
||||||
|
storage := &NamedCreaterRESTStorage{
|
||||||
|
SimpleRESTStorage: &SimpleRESTStorage{
|
||||||
|
injectedFunction: func(obj runtime.Object) (runtime.Object, error) {
|
||||||
|
time.Sleep(5 * time.Millisecond)
|
||||||
|
return obj, nil
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
selfLinker := &setTestSelfLinker{
|
||||||
|
t: t,
|
||||||
|
name: "bar",
|
||||||
|
namespace: "default",
|
||||||
|
expectedSet: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/foo",
|
||||||
|
}
|
||||||
|
handler := handleLinker(map[string]rest.Storage{"foo": storage}, selfLinker)
|
||||||
|
server := httptest.NewServer(handler)
|
||||||
|
defer server.Close()
|
||||||
|
client := http.Client{}
|
||||||
|
|
||||||
|
simple := &genericapitesting.Simple{
|
||||||
|
Other: "bar",
|
||||||
|
}
|
||||||
|
data, err := runtime.Encode(testCodec, simple)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
request, err := http.NewRequest("POST", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/foo", bytes.NewBuffer(data))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(1)
|
||||||
|
var response *http.Response
|
||||||
|
go func() {
|
||||||
|
response, err = client.Do(request)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
wg.Wait()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
// empty name is not allowed for NamedCreater
|
||||||
|
if response.StatusCode != http.StatusBadRequest {
|
||||||
|
t.Errorf("Unexpected response %#v", response)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type namePopulatorAdmissionControl struct {
|
||||||
|
t *testing.T
|
||||||
|
populateName string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (npac *namePopulatorAdmissionControl) Validate(a admission.Attributes, o admission.ObjectInterfaces) (err error) {
|
||||||
|
if a.GetName() != npac.populateName {
|
||||||
|
npac.t.Errorf("Unexpected name: got %q, expected %q", a.GetName(), npac.populateName)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (npac *namePopulatorAdmissionControl) Handles(operation admission.Operation) bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestNamedCreaterWithGenerateName(t *testing.T) {
|
||||||
|
populateName := "bar"
|
||||||
|
storage := &SimpleRESTStorage{
|
||||||
|
injectedFunction: func(obj runtime.Object) (runtime.Object, error) {
|
||||||
|
time.Sleep(5 * time.Millisecond)
|
||||||
|
if metadata, err := meta.Accessor(obj); err == nil {
|
||||||
|
if len(metadata.GetName()) != 0 {
|
||||||
|
t.Errorf("Unexpected name %q", metadata.GetName())
|
||||||
|
}
|
||||||
|
metadata.SetName(populateName)
|
||||||
|
} else {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return obj, nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
selfLinker := &setTestSelfLinker{
|
||||||
|
t: t,
|
||||||
|
namespace: "default",
|
||||||
|
expectedSet: "/" + prefix + "/" + testGroupVersion.Group + "/" + testGroupVersion.Version + "/namespaces/default/foo",
|
||||||
|
}
|
||||||
|
|
||||||
|
ac := &namePopulatorAdmissionControl{
|
||||||
|
t: t,
|
||||||
|
populateName: populateName,
|
||||||
|
}
|
||||||
|
|
||||||
|
handler := handleInternal(map[string]rest.Storage{"foo": storage}, ac, selfLinker, nil)
|
||||||
|
server := httptest.NewServer(handler)
|
||||||
|
defer server.Close()
|
||||||
|
client := http.Client{}
|
||||||
|
|
||||||
|
simple := &genericapitesting.Simple{
|
||||||
|
Other: "bar",
|
||||||
|
}
|
||||||
|
data, err := runtime.Encode(testCodec, simple)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
request, err := http.NewRequest("POST", server.URL+"/"+prefix+"/"+testGroupVersion.Group+"/"+testGroupVersion.Version+"/namespaces/default/foo", bytes.NewBuffer(data))
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
wg := sync.WaitGroup{}
|
||||||
|
wg.Add(1)
|
||||||
|
var response *http.Response
|
||||||
|
go func() {
|
||||||
|
response, err = client.Do(request)
|
||||||
|
wg.Done()
|
||||||
|
}()
|
||||||
|
wg.Wait()
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v", err)
|
||||||
|
}
|
||||||
|
if response.StatusCode != http.StatusCreated {
|
||||||
|
t.Errorf("Unexpected status: %d, Expected: %d, %#v", response.StatusCode, http.StatusOK, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
var itemOut genericapitesting.Simple
|
||||||
|
body, err := extractBody(response, &itemOut)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("unexpected error: %v %#v", err, response)
|
||||||
|
}
|
||||||
|
|
||||||
|
itemOut.GetObjectKind().SetGroupVersionKind(schema.GroupVersionKind{})
|
||||||
|
simple.Name = populateName
|
||||||
|
if !reflect.DeepEqual(&itemOut, simple) {
|
||||||
|
t.Errorf("Unexpected data: %#v, expected %#v (%s)", itemOut, simple, string(body))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestUpdateChecksDecode(t *testing.T) {
|
func TestUpdateChecksDecode(t *testing.T) {
|
||||||
handler := handle(map[string]rest.Storage{"simple": &SimpleRESTStorage{}})
|
handler := handle(map[string]rest.Storage{"simple": &SimpleRESTStorage{}})
|
||||||
server := httptest.NewServer(handler)
|
server := httptest.NewServer(handler)
|
||||||
@ -3879,6 +4018,7 @@ func TestCreateYAML(t *testing.T) {
|
|||||||
t.Errorf("Never set self link")
|
t.Errorf("Never set self link")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestCreateInNamespace(t *testing.T) {
|
func TestCreateInNamespace(t *testing.T) {
|
||||||
storage := SimpleRESTStorage{
|
storage := SimpleRESTStorage{
|
||||||
injectedFunction: func(obj runtime.Object) (runtime.Object, error) {
|
injectedFunction: func(obj runtime.Object) (runtime.Object, error) {
|
||||||
|
@ -57,18 +57,20 @@ func createHandler(r rest.NamedCreater, scope *RequestScope, admit admission.Int
|
|||||||
// TODO: we either want to remove timeout or document it (if we document, move timeout out of this function and declare it in api_installer)
|
// TODO: we either want to remove timeout or document it (if we document, move timeout out of this function and declare it in api_installer)
|
||||||
timeout := parseTimeout(req.URL.Query().Get("timeout"))
|
timeout := parseTimeout(req.URL.Query().Get("timeout"))
|
||||||
|
|
||||||
var (
|
namespace, name, err := scope.Namer.Name(req)
|
||||||
namespace, name string
|
|
||||||
err error
|
|
||||||
)
|
|
||||||
if includeName {
|
|
||||||
namespace, name, err = scope.Namer.Name(req)
|
|
||||||
} else {
|
|
||||||
namespace, err = scope.Namer.Namespace(req)
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
scope.err(err, w, req)
|
if includeName {
|
||||||
return
|
// name was required, return
|
||||||
|
scope.err(err, w, req)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// otherwise attempt to look up the namespace
|
||||||
|
namespace, err = scope.Namer.Namespace(req)
|
||||||
|
if err != nil {
|
||||||
|
scope.err(err, w, req)
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ctx, cancel := context.WithTimeout(req.Context(), timeout)
|
ctx, cancel := context.WithTimeout(req.Context(), timeout)
|
||||||
@ -130,6 +132,11 @@ func createHandler(r rest.NamedCreater, scope *RequestScope, admit admission.Int
|
|||||||
audit.LogRequestObject(ae, obj, scope.Resource, scope.Subresource, scope.Serializer)
|
audit.LogRequestObject(ae, obj, scope.Resource, scope.Subresource, scope.Serializer)
|
||||||
|
|
||||||
userInfo, _ := request.UserFrom(ctx)
|
userInfo, _ := request.UserFrom(ctx)
|
||||||
|
|
||||||
|
// On create, get name from new object if unset
|
||||||
|
if len(name) == 0 {
|
||||||
|
_, name, _ = scope.Namer.ObjectName(obj)
|
||||||
|
}
|
||||||
admissionAttributes := admission.NewAttributesRecord(obj, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, options, dryrun.IsDryRun(options.DryRun), userInfo)
|
admissionAttributes := admission.NewAttributesRecord(obj, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, options, dryrun.IsDryRun(options.DryRun), userInfo)
|
||||||
if mutatingAdmission, ok := admit.(admission.MutationInterface); ok && mutatingAdmission.Handles(admission.Create) {
|
if mutatingAdmission, ok := admit.(admission.MutationInterface); ok && mutatingAdmission.Handles(admission.Create) {
|
||||||
err = mutatingAdmission.Admit(ctx, admissionAttributes, scope)
|
err = mutatingAdmission.Admit(ctx, admissionAttributes, scope)
|
||||||
|
@ -163,12 +163,20 @@ func AdmissionToValidateObjectFunc(admit admission.Interface, staticAttributes a
|
|||||||
return func(ctx context.Context, obj runtime.Object) error { return nil }
|
return func(ctx context.Context, obj runtime.Object) error { return nil }
|
||||||
}
|
}
|
||||||
return func(ctx context.Context, obj runtime.Object) error {
|
return func(ctx context.Context, obj runtime.Object) error {
|
||||||
|
name := staticAttributes.GetName()
|
||||||
|
// in case the generated name is populated
|
||||||
|
if len(name) == 0 {
|
||||||
|
if metadata, err := meta.Accessor(obj); err == nil {
|
||||||
|
name = metadata.GetName()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
finalAttributes := admission.NewAttributesRecord(
|
finalAttributes := admission.NewAttributesRecord(
|
||||||
obj,
|
obj,
|
||||||
staticAttributes.GetOldObject(),
|
staticAttributes.GetOldObject(),
|
||||||
staticAttributes.GetKind(),
|
staticAttributes.GetKind(),
|
||||||
staticAttributes.GetNamespace(),
|
staticAttributes.GetNamespace(),
|
||||||
staticAttributes.GetName(),
|
name,
|
||||||
staticAttributes.GetResource(),
|
staticAttributes.GetResource(),
|
||||||
staticAttributes.GetSubresource(),
|
staticAttributes.GetSubresource(),
|
||||||
staticAttributes.GetOperation(),
|
staticAttributes.GetOperation(),
|
||||||
|
Loading…
Reference in New Issue
Block a user