From 414f50cc856eff621fee02c270a642632ad0886d Mon Sep 17 00:00:00 2001 From: Kir Kolyshkin Date: Sun, 11 Oct 2026 16:20:29 -0700 Subject: [PATCH] validation/delete: allow delete of a created container Since runtime-spec PR #1324, attempting to delete a container in the created state MAY either have no effect and generate an error (as before), or terminate the container process and delete the container (as runc and crun do). Add a new DeleteCreatedNoEffectOrDelete spec error code, and modify the delete test so that the created case passes in either of the two cases. Update DeleteNonStop* comments and messages accordingly. See https://github.com/opencontainers/runtime-spec/pull/1324 Signed-off-by: Kir Kolyshkin --- specerror/runtime.go | 7 +++++-- validation/delete/delete.go | 26 ++++++++++++++++++++------ 2 files changed, 25 insertions(+), 8 deletions(-) diff --git a/specerror/runtime.go b/specerror/runtime.go index 383aea63..5b4abbe1 100644 --- a/specerror/runtime.go +++ b/specerror/runtime.go @@ -86,14 +86,16 @@ const ( KillSignalImplement // DeleteWithoutIDGenError represents "`delete` operation MUST generate an error if it is not provided the container ID." DeleteWithoutIDGenError - // DeleteNonStopHaveNoEffect represents "Attempting to `delete` a container that is not `stopped` MUST have no effect on the container." + // DeleteNonStopHaveNoEffect represents "Attempting to `delete` a container that is neither `created` nor `stopped` MUST have no effect on the container." DeleteNonStopHaveNoEffect - // DeleteNonStopGenError represents "Attempting to `delete` a container that is not `stopped` MUST generate an error." + // DeleteNonStopGenError represents "Attempting to `delete` a container that is neither `created` nor `stopped` MUST generate an error." DeleteNonStopGenError // DeleteResImplement represents "Deleting a container MUST delete the resources that were created during the `create` step." DeleteResImplement // DeleteOnlyCreatedRes represents "Note that resources associated with the container, but not created by this container, MUST NOT be deleted." DeleteOnlyCreatedRes + // DeleteCreatedNoEffectOrDelete represents "Attempting to `delete` a container that is `created` MAY either have no effect on the container and generate an error, or terminate the container process and then delete the container." + DeleteCreatedNoEffectOrDelete ) var ( @@ -176,4 +178,5 @@ func init() { register(DeleteNonStopGenError, rfc2119.Must, deleteRef) register(DeleteResImplement, rfc2119.Must, deleteRef) register(DeleteOnlyCreatedRes, rfc2119.Must, deleteRef) + register(DeleteCreatedNoEffectOrDelete, rfc2119.May, deleteRef) } diff --git a/validation/delete/delete.go b/validation/delete/delete.go index 76d59c98..23cbd818 100644 --- a/validation/delete/delete.go +++ b/validation/delete/delete.go @@ -43,15 +43,18 @@ func main() { errExpected bool // set true to check whether 'delete' takes effect and just check if 'delete' DOES NOT take effect effectCheck bool - err error + // set true if 'delete' may either fail and have no effect, or succeed and delete the container + mayDelete bool + err error }{ // Note: the nil config test case should run first since we are re-using the bundle // delete without id - {nil, "", util.LifecycleActionNone, false, false, specerror.NewError(specerror.DeleteWithoutIDGenError, fmt.Errorf("`delete` operation MUST generate an error if it is not provided the container ID"), rspecs.Version)}, + {nil, "", util.LifecycleActionNone, false, false, false, specerror.NewError(specerror.DeleteWithoutIDGenError, fmt.Errorf("`delete` operation MUST generate an error if it is not provided the container ID"), rspecs.Version)}, // delete a created container - {stoppedConfig, containerID, util.LifecycleActionCreate, false, true, specerror.NewError(specerror.DeleteNonStopGenError, fmt.Errorf("attempting to `delete` a container that is not `stopped` MUST generate an error"), rspecs.Version)}, + // TODO: bump runtime-spec to v1.3.1 once it's out (for the reference). + {stoppedConfig, containerID, util.LifecycleActionCreate, false, false, true, specerror.NewError(specerror.DeleteCreatedNoEffectOrDelete, fmt.Errorf("attempting to `delete` a container that is `created` MAY either have no effect on the container and generate an error, or terminate the container process and then delete the container"), rspecs.Version)}, // delete a running container - {runningConfig, containerID, util.LifecycleActionCreate | util.LifecycleActionStart, false, true, specerror.NewError(specerror.DeleteNonStopGenError, fmt.Errorf("attempting to `delete` a container that is not `stopped` MUST generate an error"), rspecs.Version)}, + {runningConfig, containerID, util.LifecycleActionCreate | util.LifecycleActionStart, false, true, false, specerror.NewError(specerror.DeleteNonStopGenError, fmt.Errorf("attempting to `delete` a container that is neither `created` nor `stopped` MUST generate an error"), rspecs.Version)}, } for _, c := range cases { @@ -70,14 +73,25 @@ func main() { testRuntime.SetID(c.id) util.WaitingForStatus(testRuntime, util.LifecycleStatusCreated|util.LifecycleStatusStopped, time.Second*10, time.Second*1) deletedErr := testRuntime.Delete() - util.SpecErrorOK(t, (deletedErr == nil) == c.errExpected, c.err, deletedErr) + if c.mayDelete { + if deletedErr == nil { + // waiting for the error of State, just in case the delete operation takes time + util.WaitingForStatus(testRuntime, util.LifecycleActionNone, time.Second*3, time.Second/2) + } + _, err = testRuntime.State() + // either 'delete' failed and the container is still there, + // or 'delete' succeeded and the container is gone + util.SpecErrorOK(t, (deletedErr == nil) == (err != nil), c.err, deletedErr) + } else { + util.SpecErrorOK(t, (deletedErr == nil) == c.errExpected, c.err, deletedErr) + } if c.effectCheck { // waiting for the error of State, just in case the delete operation takes time util.WaitingForStatus(testRuntime, util.LifecycleActionNone, time.Second*3, time.Second/2) _, err = testRuntime.State() // err == nil means the 'delete' operation does NOT take effect - util.SpecErrorOK(t, err == nil, specerror.NewError(specerror.DeleteNonStopHaveNoEffect, fmt.Errorf("attempting to `delete` a container that is not `stopped` MUST have no effect on the container"), rspecs.Version), err) + util.SpecErrorOK(t, err == nil, specerror.NewError(specerror.DeleteNonStopHaveNoEffect, fmt.Errorf("attempting to `delete` a container that is neither `created` nor `stopped` MUST have no effect on the container"), rspecs.Version), err) } // created and but deleted