Merge pull request #6517 from ruiwen-zhao/return-resource

This commit is contained in:
Samuel Karp 2022-08-24 14:01:30 -07:00 committed by GitHub
commit 36d0cfd0fd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 3044 additions and 935 deletions

4
go.mod
View File

@ -63,7 +63,7 @@ require (
go.opentelemetry.io/otel/trace v1.7.0
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a
google.golang.org/genproto v0.0.0-20220426171045-31bebdecfb46
google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21
google.golang.org/grpc v1.47.0
google.golang.org/protobuf v1.28.0
k8s.io/api v0.24.1
@ -71,7 +71,7 @@ require (
k8s.io/apiserver v0.24.1
k8s.io/client-go v0.24.1
k8s.io/component-base v0.24.1
k8s.io/cri-api v0.25.0-alpha.2
k8s.io/cri-api v0.25.0-alpha.3.0.20220803042751-33bb7ae0d927
k8s.io/klog/v2 v2.60.1
k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9
)

9
go.sum
View File

@ -1455,8 +1455,8 @@ google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEc
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220426171045-31bebdecfb46 h1:G1IeWbjrqEq9ChWxEuRPJu6laA67+XgTFHVSAvepr38=
google.golang.org/genproto v0.0.0-20220426171045-31bebdecfb46/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 h1:hrbNEivu7Zn1pxvHk6MBrq9iE22woVILTHqexqBxe6I=
google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
@ -1485,7 +1485,6 @@ google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQ
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8=
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
@ -1590,8 +1589,8 @@ k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI=
k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI=
k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc=
k8s.io/cri-api v0.23.1/go.mod h1:REJE3PSU0h/LOV1APBrupxrEJqnoxZC8KWzkBUHwrK4=
k8s.io/cri-api v0.25.0-alpha.2 h1:KSB1Untl+/iXXPuoqWtiW0YZbjqnnYGzhz4BbaLd3pg=
k8s.io/cri-api v0.25.0-alpha.2/go.mod h1:bKbUiy31Ex/ogNMxLEikgk+5kPv1vevtbiLN+xWEXr8=
k8s.io/cri-api v0.25.0-alpha.3.0.20220803042751-33bb7ae0d927 h1:b5WiYCb7EDC4U4NQMlgzfkYhL8184YtfIRcYch2mc0g=
k8s.io/cri-api v0.25.0-alpha.3.0.20220803042751-33bb7ae0d927/go.mod h1:pVVfduJnUZIMACxas6tKC229MZ+Yl4lOZ2Ij/9sqIIA=
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=

View File

@ -60,7 +60,7 @@ require (
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 // indirect
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/text v0.3.7 // indirect
google.golang.org/genproto v0.0.0-20220426171045-31bebdecfb46 // indirect
google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 // indirect
google.golang.org/grpc v1.47.0 // indirect
google.golang.org/protobuf v1.28.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect

View File

@ -831,6 +831,7 @@ golang.org/x/net v0.0.0-20210825183410-e898025ed96a/go.mod h1:9nx3DQGgdP8bBQD5qx
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA=
golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@ -945,6 +946,7 @@ golang.org/x/sys v0.0.0-20211116061358-0a5406a5449c/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220319134239-a9b59b0215f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220405210540-1e041c57c461/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a h1:dGzPydgVsqGcTRVwiLJ1jVbufYwmzD3LfVPLKsKg+0k=
@ -1114,9 +1116,8 @@ google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaE
google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0=
google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220107163113-42d7afdf6368/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc=
google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI=
google.golang.org/genproto v0.0.0-20220426171045-31bebdecfb46 h1:G1IeWbjrqEq9ChWxEuRPJu6laA67+XgTFHVSAvepr38=
google.golang.org/genproto v0.0.0-20220426171045-31bebdecfb46/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo=
google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21 h1:hrbNEivu7Zn1pxvHk6MBrq9iE22woVILTHqexqBxe6I=
google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@ -1142,8 +1143,6 @@ google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQ
google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34=
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.43.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
google.golang.org/grpc v1.47.0 h1:9n77onPX5F3qfFCqjy9dhn8PbNQsIKeVU04J9G7umt8=
google.golang.org/grpc v1.47.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
@ -1212,7 +1211,7 @@ k8s.io/client-go v0.24.1/go.mod h1:f1kIDqcEYmwXS/vTbbhopMUbhKp2JhOeVTfxgaCIlF8=
k8s.io/code-generator v0.19.7/go.mod h1:lwEq3YnLYb/7uVXLorOJfxg+cUu2oihFhHZ0n9NIla0=
k8s.io/component-base v0.24.1/go.mod h1:DW5vQGYVCog8WYpNob3PMmmsY8A3L9QZNg4j/dV3s38=
k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI=
k8s.io/cri-api v0.25.0-alpha.2/go.mod h1:bKbUiy31Ex/ogNMxLEikgk+5kPv1vevtbiLN+xWEXr8=
k8s.io/cri-api v0.25.0-alpha.3.0.20220803042751-33bb7ae0d927/go.mod h1:pVVfduJnUZIMACxas6tKC229MZ+Yl4lOZ2Ij/9sqIIA=
k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20200428234225-8167cfdcfc14/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
k8s.io/gengo v0.0.0-20201113003025-83324d819ded/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=

View File

@ -58,6 +58,15 @@ func checkMemorySwapLimit(t *testing.T, spec *runtimespec.Spec, memLimit *int64)
}
}
func checkMemoryLimitInContainerStatus(t *testing.T, status *runtime.ContainerStatus, memLimit int64) {
t.Helper()
require.NotNil(t, status)
require.NotNil(t, status.Resources)
require.NotNil(t, status.Resources.Linux)
require.NotNil(t, status.Resources.Linux.MemoryLimitInBytes)
assert.Equal(t, memLimit, status.Resources.Linux.MemoryLimitInBytes)
}
func getCgroupSwapLimitForTask(t *testing.T, task containerd.Task) uint64 {
if cgroups.Mode() == cgroups.Unified {
groupPath, err := cgroupsv2.PidGroupPath(int(task.Pid()))
@ -281,3 +290,52 @@ func TestUpdateContainerResources_MemoryLimit(t *testing.T) {
memLimit = getCgroupMemoryLimitForTask(t, task)
assert.Equal(t, uint64(800*1024*1024), memLimit)
}
func TestUpdateContainerResources_StatusUpdated(t *testing.T) {
t.Log("Create a sandbox")
sb, sbConfig := PodSandboxConfigWithCleanup(t, "sandbox", "update-container-resources")
pauseImage := images.Get(images.Pause)
EnsureImageExists(t, pauseImage)
t.Log("Create a container with memory limit")
cnConfig := ContainerConfig(
"container",
pauseImage,
WithResources(&runtime.LinuxContainerResources{
MemoryLimitInBytes: 200 * 1024 * 1024,
}),
)
cn, err := runtimeService.CreateContainer(sb, cnConfig, sbConfig)
require.NoError(t, err)
t.Log("Check memory limit in container status")
status, err := runtimeService.ContainerStatus(cn)
checkMemoryLimitInContainerStatus(t, status, 200*1024*1024)
require.NoError(t, err)
t.Log("Update container memory limit after created")
err = runtimeService.UpdateContainerResources(cn, &runtime.LinuxContainerResources{
MemoryLimitInBytes: 400 * 1024 * 1024,
}, nil)
require.NoError(t, err)
t.Log("Check memory limit in container status")
status, err = runtimeService.ContainerStatus(cn)
checkMemoryLimitInContainerStatus(t, status, 400*1024*1024)
require.NoError(t, err)
t.Log("Start the container")
require.NoError(t, runtimeService.StartContainer(cn))
t.Log("Update container memory limit after started")
err = runtimeService.UpdateContainerResources(cn, &runtime.LinuxContainerResources{
MemoryLimitInBytes: 800 * 1024 * 1024,
}, nil)
require.NoError(t, err)
t.Log("Check memory limit in container status")
status, err = runtimeService.ContainerStatus(cn)
checkMemoryLimitInContainerStatus(t, status, 800*1024*1024)
require.NoError(t, err)
}

View File

@ -143,6 +143,9 @@ func TestContainerdImage(t *testing.T) {
if err != nil {
return false, err
}
if s.Resources == nil || (s.Resources.Linux == nil && s.Resources.Windows == nil) {
return false, fmt.Errorf("No Resource field in container status: %+v", s)
}
return s.GetState() == runtime.ContainerState_CONTAINER_RUNNING, nil
}
require.NoError(t, Eventually(checkContainer, 100*time.Millisecond, 10*time.Second))

View File

@ -0,0 +1,28 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package sbserver
import (
"context"
"errors"
runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
)
func (c *criService) CheckpointContainer(ctx context.Context, r *runtime.CheckpointContainerRequest) (res *runtime.CheckpointContainerResponse, err error) {
return nil, errors.New("not implemented")
}

View File

@ -0,0 +1,27 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package sbserver
import (
"errors"
runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
)
func (c *criService) GetContainerEvents(r *runtime.GetEventsRequest, s runtime.RuntimeService_GetContainerEventsServer) error {
return errors.New("not implemented")
}

View File

@ -1601,6 +1601,41 @@ func (in *instrumentedAlphaService) ReopenContainerLog(ctx context.Context, r *r
return res, errdefs.ToGRPC(err)
}
func (in *instrumentedService) CheckpointContainer(ctx context.Context, r *runtime.CheckpointContainerRequest) (res *runtime.CheckpointContainerResponse, err error) {
if err := in.checkInitialized(); err != nil {
return nil, err
}
defer func() {
if err != nil {
log.G(ctx).WithError(err).Errorf("CheckpointContainer failed, error")
} else {
log.G(ctx).Debug("CheckpointContainer returns successfully")
}
}()
res, err = in.c.CheckpointContainer(ctx, r)
return res, errdefs.ToGRPC(err)
}
func (in *instrumentedService) GetContainerEvents(r *runtime.GetEventsRequest, s runtime.RuntimeService_GetContainerEventsServer) (err error) {
if err := in.checkInitialized(); err != nil {
return err
}
ctx := s.Context()
defer func() {
if err != nil {
log.G(ctx).WithError(err).Errorf("GetContainerEvents failed, error")
} else {
log.G(ctx).Debug("GetContainerEvents returns successfully")
}
}()
err = in.c.GetContainerEvents(r, s)
return errdefs.ToGRPC(err)
}
func alphaReqToV1Req(
alphar interface{ Marshal() ([]byte, error) },
v1r interface{ Unmarshal(_ []byte) error },

View File

@ -0,0 +1,28 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package server
import (
"context"
"errors"
runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
)
func (c *criService) CheckpointContainer(ctx context.Context, r *runtime.CheckpointContainerRequest) (res *runtime.CheckpointContainerResponse, err error) {
return nil, errors.New("not implemented")
}

View File

@ -261,6 +261,7 @@ func (c *criService) CreateContainer(ctx context.Context, r *runtime.CreateConta
}()
status := containerstore.Status{CreatedAt: time.Now().UnixNano()}
status = copyResourcesToStatus(spec, status)
container, err := containerstore.NewContainer(meta,
containerstore.WithStatus(status, containerRootDir),
containerstore.WithContainer(cntr),

View File

@ -0,0 +1,27 @@
/*
Copyright The containerd Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package server
import (
"errors"
runtime "k8s.io/cri-api/pkg/apis/runtime/v1"
)
func (c *criService) GetContainerEvents(r *runtime.GetEventsRequest, s runtime.RuntimeService_GetContainerEventsServer) error {
return errors.New("not implemented")
}

View File

@ -60,6 +60,7 @@ func (c *criService) ContainerStatus(ctx context.Context, r *runtime.ContainerSt
}
}
status := toCRIContainerStatus(container, spec, imageRef)
if status.GetCreatedAt() == 0 {
// CRI doesn't allow CreatedAt == 0.
info, err := container.Container.Info(ctx)
@ -119,6 +120,7 @@ func toCRIContainerStatus(container containerstore.Container, spec *runtime.Imag
Annotations: meta.Config.GetAnnotations(),
Mounts: meta.Config.GetMounts(),
LogPath: meta.LogPath,
Resources: status.Resources,
}
}

View File

@ -45,8 +45,8 @@ func (c *criService) UpdateContainerResources(ctx context.Context, r *runtime.Up
// Update resources in status update transaction, so that:
// 1) There won't be race condition with container start.
// 2) There won't be concurrent resource update to the same container.
if err := container.Status.Update(func(status containerstore.Status) (containerstore.Status, error) {
return status, c.updateContainerResources(ctx, container, r, status)
if err := container.Status.UpdateSync(func(status containerstore.Status) (containerstore.Status, error) {
return c.updateContainerResources(ctx, container, r, status)
}); err != nil {
return nil, fmt.Errorf("failed to update resources: %w", err)
}
@ -56,11 +56,13 @@ func (c *criService) UpdateContainerResources(ctx context.Context, r *runtime.Up
func (c *criService) updateContainerResources(ctx context.Context,
cntr containerstore.Container,
r *runtime.UpdateContainerResourcesRequest,
status containerstore.Status) (retErr error) {
status containerstore.Status) (newStatus containerstore.Status, retErr error) {
newStatus = status
id := cntr.ID
// Do not update the container when there is a removal in progress.
if status.Removing {
return fmt.Errorf("container %q is in removing state", id)
return newStatus, fmt.Errorf("container %q is in removing state", id)
}
// Update container spec. If the container is not started yet, updating
@ -69,15 +71,15 @@ func (c *criService) updateContainerResources(ctx context.Context,
// the spec will become our source of truth for resource limits.
oldSpec, err := cntr.Container.Spec(ctx)
if err != nil {
return fmt.Errorf("failed to get container spec: %w", err)
return newStatus, fmt.Errorf("failed to get container spec: %w", err)
}
newSpec, err := updateOCIResource(ctx, oldSpec, r, c.config)
if err != nil {
return fmt.Errorf("failed to update resource in spec: %w", err)
return newStatus, fmt.Errorf("failed to update resource in spec: %w", err)
}
if err := updateContainerSpec(ctx, cntr.Container, newSpec); err != nil {
return err
return newStatus, err
}
defer func() {
if retErr != nil {
@ -87,32 +89,35 @@ func (c *criService) updateContainerResources(ctx context.Context,
if err := updateContainerSpec(deferCtx, cntr.Container, oldSpec); err != nil {
log.G(ctx).WithError(err).Errorf("Failed to update spec %+v for container %q", oldSpec, id)
}
} else {
// Update container status only when the spec is updated
newStatus = copyResourcesToStatus(newSpec, status)
}
}()
// If container is not running, only update spec is enough, new resource
// limit will be applied when container start.
if status.State() != runtime.ContainerState_CONTAINER_RUNNING {
return nil
return newStatus, nil
}
task, err := cntr.Container.Task(ctx, nil)
if err != nil {
if errdefs.IsNotFound(err) {
// Task exited already.
return nil
return newStatus, nil
}
return fmt.Errorf("failed to get task: %w", err)
return newStatus, fmt.Errorf("failed to get task: %w", err)
}
// newSpec.Linux / newSpec.Windows won't be nil
if err := task.Update(ctx, containerd.WithResources(getResources(newSpec))); err != nil {
if errdefs.IsNotFound(err) {
// Task exited already.
return nil
return newStatus, nil
}
return fmt.Errorf("failed to update resources: %w", err)
return newStatus, fmt.Errorf("failed to update resources: %w", err)
}
return nil
return newStatus, nil
}
// updateContainerSpec updates container spec.

View File

@ -38,6 +38,7 @@ import (
"github.com/containerd/containerd/runtime/linux/runctypes"
runcoptions "github.com/containerd/containerd/runtime/v2/runc/options"
"github.com/containerd/typeurl"
runtimespec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/sirupsen/logrus"
runhcsoptions "github.com/Microsoft/hcsshim/cmd/containerd-shim-runhcs-v1/options"
@ -429,3 +430,83 @@ func getPassthroughAnnotations(podAnnotations map[string]string,
}
return passthroughAnnotations
}
// copyResourcesToStatus copys container resource contraints from spec to
// container status.
// This will need updates when new fields are added to ContainerResources.
func copyResourcesToStatus(spec *runtimespec.Spec, status containerstore.Status) containerstore.Status {
status.Resources = &runtime.ContainerResources{}
if spec.Linux != nil {
status.Resources.Linux = &runtime.LinuxContainerResources{}
if spec.Process != nil && spec.Process.OOMScoreAdj != nil {
status.Resources.Linux.OomScoreAdj = int64(*spec.Process.OOMScoreAdj)
}
if spec.Linux.Resources == nil {
return status
}
if spec.Linux.Resources.CPU != nil {
if spec.Linux.Resources.CPU.Period != nil {
status.Resources.Linux.CpuPeriod = int64(*spec.Linux.Resources.CPU.Period)
}
if spec.Linux.Resources.CPU.Quota != nil {
status.Resources.Linux.CpuQuota = *spec.Linux.Resources.CPU.Quota
}
if spec.Linux.Resources.CPU.Shares != nil {
status.Resources.Linux.CpuShares = int64(*spec.Linux.Resources.CPU.Shares)
}
status.Resources.Linux.CpusetCpus = spec.Linux.Resources.CPU.Cpus
status.Resources.Linux.CpusetMems = spec.Linux.Resources.CPU.Mems
}
if spec.Linux.Resources.Memory != nil {
if spec.Linux.Resources.Memory.Limit != nil {
status.Resources.Linux.MemoryLimitInBytes = *spec.Linux.Resources.Memory.Limit
}
if spec.Linux.Resources.Memory.Swap != nil {
status.Resources.Linux.MemorySwapLimitInBytes = *spec.Linux.Resources.Memory.Swap
}
}
if spec.Linux.Resources.HugepageLimits != nil {
hugepageLimits := make([]*runtime.HugepageLimit, len(spec.Linux.Resources.HugepageLimits))
for _, l := range spec.Linux.Resources.HugepageLimits {
hugepageLimits = append(hugepageLimits, &runtime.HugepageLimit{
PageSize: l.Pagesize,
Limit: l.Limit,
})
}
status.Resources.Linux.HugepageLimits = hugepageLimits
}
if spec.Linux.Resources.Unified != nil {
status.Resources.Linux.Unified = spec.Linux.Resources.Unified
}
}
if spec.Windows != nil {
status.Resources.Windows = &runtime.WindowsContainerResources{}
if spec.Windows.Resources == nil {
return status
}
if spec.Windows.Resources.CPU != nil {
if spec.Windows.Resources.CPU.Shares != nil {
status.Resources.Windows.CpuShares = int64(*spec.Windows.Resources.CPU.Shares)
status.Resources.Windows.CpuCount = int64(*spec.Windows.Resources.CPU.Count)
status.Resources.Windows.CpuMaximum = int64(*spec.Windows.Resources.CPU.Maximum)
}
}
if spec.Windows.Resources.Memory != nil {
if spec.Windows.Resources.Memory.Limit != nil {
status.Resources.Windows.MemoryLimitInBytes = int64(*spec.Windows.Resources.Memory.Limit)
}
}
// TODO: Figure out how to get RootfsSizeInBytes
}
return status
}

View File

@ -1601,6 +1601,41 @@ func (in *instrumentedAlphaService) ReopenContainerLog(ctx context.Context, r *r
return res, errdefs.ToGRPC(err)
}
func (in *instrumentedService) CheckpointContainer(ctx context.Context, r *runtime.CheckpointContainerRequest) (res *runtime.CheckpointContainerResponse, err error) {
if err := in.checkInitialized(); err != nil {
return nil, err
}
defer func() {
if err != nil {
log.G(ctx).WithError(err).Errorf("CheckpointContainer failed, error")
} else {
log.G(ctx).Debug("CheckpointContainer returns successfully")
}
}()
res, err = in.c.CheckpointContainer(ctx, r)
return res, errdefs.ToGRPC(err)
}
func (in *instrumentedService) GetContainerEvents(r *runtime.GetEventsRequest, s runtime.RuntimeService_GetContainerEventsServer) (err error) {
if err := in.checkInitialized(); err != nil {
return err
}
ctx := s.Context()
defer func() {
if err != nil {
log.G(ctx).WithError(err).Errorf("GetContainerEvents failed, error")
} else {
log.G(ctx).Debug("GetContainerEvents returns successfully")
}
}()
err = in.c.GetContainerEvents(r, s)
return errdefs.ToGRPC(err)
}
func alphaReqToV1Req(
alphar interface{ Marshal() ([]byte, error) },
v1r interface{ Unmarshal(_ []byte) error },

View File

@ -97,6 +97,8 @@ type Status struct {
// Unknown indicates that the container status is not fully loaded.
// This field doesn't need to be checkpointed.
Unknown bool `json:"-"`
// Resources has container runtime resource constraints
Resources *runtime.ContainerResources
}
// State returns current state of the container based on the container status.
@ -203,7 +205,56 @@ type statusStorage struct {
func (s *statusStorage) Get() Status {
s.RLock()
defer s.RUnlock()
return s.status
// Deep copy is needed in case some fields in Status are updated after Get()
// is called.
return deepCopyOf(s.status)
}
func deepCopyOf(s Status) Status {
copy := s
// Resources is the only field that is a pointer, and therefore needs
// a manual deep copy.
// This will need updates when new fields are added to ContainerResources.
if s.Resources == nil {
return copy
}
copy.Resources = &runtime.ContainerResources{}
if s.Resources != nil && s.Resources.Linux != nil {
hugepageLimits := make([]*runtime.HugepageLimit, len(s.Resources.Linux.HugepageLimits))
for _, l := range s.Resources.Linux.HugepageLimits {
hugepageLimits = append(hugepageLimits, &runtime.HugepageLimit{
PageSize: l.PageSize,
Limit: l.Limit,
})
}
copy.Resources = &runtime.ContainerResources{
Linux: &runtime.LinuxContainerResources{
CpuPeriod: s.Resources.Linux.CpuPeriod,
CpuQuota: s.Resources.Linux.CpuQuota,
CpuShares: s.Resources.Linux.CpuShares,
CpusetCpus: s.Resources.Linux.CpusetCpus,
CpusetMems: s.Resources.Linux.CpusetMems,
MemoryLimitInBytes: s.Resources.Linux.MemoryLimitInBytes,
MemorySwapLimitInBytes: s.Resources.Linux.MemorySwapLimitInBytes,
OomScoreAdj: s.Resources.Linux.OomScoreAdj,
Unified: s.Resources.Linux.Unified,
HugepageLimits: hugepageLimits,
},
}
}
if s.Resources != nil && s.Resources.Windows != nil {
copy.Resources = &runtime.ContainerResources{
Windows: &runtime.WindowsContainerResources{
CpuShares: s.Resources.Windows.CpuShares,
CpuCount: s.Resources.Windows.CpuCount,
CpuMaximum: s.Resources.Windows.CpuMaximum,
MemoryLimitInBytes: s.Resources.Windows.MemoryLimitInBytes,
RootfsSizeInBytes: s.Resources.Windows.RootfsSizeInBytes,
},
}
}
return copy
}
// UpdateSync updates the container status and the on disk checkpoint.

File diff suppressed because it is too large Load Diff

View File

@ -79,7 +79,8 @@ service RuntimeService {
// ContainerStatus returns status of the container. If the container is not
// present, returns an error.
rpc ContainerStatus(ContainerStatusRequest) returns (ContainerStatusResponse) {}
// UpdateContainerResources updates ContainerConfig of the container.
// UpdateContainerResources updates ContainerConfig of the container synchronously.
// If runtime fails to transactionally update the requested resources, an error is returned.
rpc UpdateContainerResources(UpdateContainerResourcesRequest) returns (UpdateContainerResourcesResponse) {}
// ReopenContainerLog asks runtime to reopen the stdout/stderr log file
// for the container. This is often called after the log file has been
@ -114,6 +115,13 @@ service RuntimeService {
// Status returns the status of the runtime.
rpc Status(StatusRequest) returns (StatusResponse) {}
// CheckpointContainer checkpoints a container
rpc CheckpointContainer(CheckpointContainerRequest) returns (CheckpointContainerResponse) {}
// GetContainerEvents gets container events from the CRI runtime
rpc GetContainerEvents(GetEventsRequest) returns (stream ContainerEventResponse) {}
}
// ImageService defines the public APIs for managing images.
@ -755,7 +763,7 @@ message SELinuxOption {
// Capability contains the container capabilities to add or drop
// Dropping a capability will drop it from all sets.
// If a capability is added to only the add_capabilities list then it gets added to permitted,
// If a capability is added to only the add_capabilities list then it gets added to permitted,
// inheritable, effective and bounding sets, i.e. all sets except the ambient set.
// If a capability is added to only the add_ambient_capabilities list then it gets added to all sets, i.e permitted
// inheritable, effective, bounding and ambient sets.
@ -1151,6 +1159,8 @@ message ContainerStatus {
repeated Mount mounts = 14;
// Log path of container.
string log_path = 15;
// Resource limits configuration of the container.
ContainerResources resources = 16;
}
message ContainerStatusResponse {
@ -1163,6 +1173,14 @@ message ContainerStatusResponse {
map<string, string> info = 2;
}
// ContainerResources holds resource limits configuration for a container.
message ContainerResources {
// Resource limits configuration specific to Linux container.
LinuxContainerResources linux = 1;
// Resource limits configuration specific to Windows container.
WindowsContainerResources windows = 2;
}
message UpdateContainerResourcesRequest {
// ID of the container to update.
string container_id = 1;
@ -1543,3 +1561,46 @@ message ReopenContainerLogRequest {
message ReopenContainerLogResponse{
}
message CheckpointContainerRequest {
// ID of the container to be checkpointed.
string container_id = 1;
// Location of the checkpoint archive used for export
string location = 2;
// Timeout in seconds for the checkpoint to complete.
// Timeout of zero means to use the CRI default.
// Timeout > 0 means to use the user specified timeout.
int64 timeout = 3;
}
message CheckpointContainerResponse {}
message GetEventsRequest {}
message ContainerEventResponse {
// ID of the container
string container_id = 1;
// Type of the container event
ContainerEventType container_event_type = 2;
// Creation timestamp of this event
int64 created_at = 3;
// ID of the sandbox container
PodSandboxMetadata pod_sandbox_metadata = 4;
}
enum ContainerEventType {
// Container created
CONTAINER_CREATED_EVENT = 0;
// Container started
CONTAINER_STARTED_EVENT = 1;
// Container stopped
CONTAINER_STOPPED_EVENT = 2;
// Container deleted
CONTAINER_DELETED_EVENT = 3;
}

File diff suppressed because it is too large Load Diff

View File

@ -79,7 +79,8 @@ service RuntimeService {
// ContainerStatus returns status of the container. If the container is not
// present, returns an error.
rpc ContainerStatus(ContainerStatusRequest) returns (ContainerStatusResponse) {}
// UpdateContainerResources updates ContainerConfig of the container.
// UpdateContainerResources updates ContainerConfig of the container synchronously.
// If runtime fails to transactionally update the requested resources, an error is returned.
rpc UpdateContainerResources(UpdateContainerResourcesRequest) returns (UpdateContainerResourcesResponse) {}
// ReopenContainerLog asks runtime to reopen the stdout/stderr log file
// for the container. This is often called after the log file has been
@ -1151,6 +1152,8 @@ message ContainerStatus {
repeated Mount mounts = 14;
// Log path of container.
string log_path = 15;
// Resource limits configuration of the container.
ContainerResources resources = 16;
}
message ContainerStatusResponse {
@ -1163,6 +1166,14 @@ message ContainerStatusResponse {
map<string, string> info = 2;
}
// ContainerResources holds resource limits configuration for a container.
message ContainerResources {
// Resource limits configuration specific to Linux container.
LinuxContainerResources linux = 1;
// Resource limits configuration specific to Windows container.
WindowsContainerResources windows = 2;
}
message UpdateContainerResourcesRequest {
// ID of the container to update.
string container_id = 1;

4
vendor/modules.txt vendored
View File

@ -534,7 +534,7 @@ google.golang.org/appengine/internal/log
google.golang.org/appengine/internal/remote_api
google.golang.org/appengine/internal/urlfetch
google.golang.org/appengine/urlfetch
# google.golang.org/genproto v0.0.0-20220426171045-31bebdecfb46
# google.golang.org/genproto v0.0.0-20220502173005-c8bf987b8c21
## explicit; go 1.15
google.golang.org/genproto/googleapis/api/httpbody
google.golang.org/genproto/googleapis/rpc/code
@ -738,7 +738,7 @@ k8s.io/component-base/logs/logreduction
k8s.io/component-base/metrics
k8s.io/component-base/metrics/legacyregistry
k8s.io/component-base/version
# k8s.io/cri-api v0.25.0-alpha.2
# k8s.io/cri-api v0.25.0-alpha.3.0.20220803042751-33bb7ae0d927
## explicit; go 1.18
k8s.io/cri-api/pkg/apis/runtime/v1
k8s.io/cri-api/pkg/apis/runtime/v1alpha2