kubelet: wire checkpoint container support through

This adds the last pieces to wire through the container checkpoint
support in the kubelet.

Signed-off-by: Adrian Reber <areber@redhat.com>
This commit is contained in:
Adrian Reber
2021-09-10 12:38:08 +00:00
parent 8c24857ba3
commit fc37a7a990
9 changed files with 369 additions and 4 deletions

View File

@@ -62,6 +62,7 @@ import (
"k8s.io/component-base/logs"
compbasemetrics "k8s.io/component-base/metrics"
"k8s.io/component-base/metrics/legacyregistry"
runtimeapi "k8s.io/cri-api/pkg/apis/runtime/v1"
podresourcesapi "k8s.io/kubelet/pkg/apis/podresources/v1"
podresourcesapiv1alpha1 "k8s.io/kubelet/pkg/apis/podresources/v1alpha1"
"k8s.io/kubernetes/pkg/api/legacyscheme"
@@ -224,6 +225,7 @@ type HostInterface interface {
GetCachedMachineInfo() (*cadvisorapi.MachineInfo, error)
GetRunningPods() ([]*v1.Pod, error)
RunInContainer(name string, uid types.UID, container string, cmd []string) ([]byte, error)
CheckpointContainer(podUID types.UID, podFullName, containerName string, options *runtimeapi.CheckpointContainerRequest) error
GetKubeletContainerLogs(ctx context.Context, podFullName, containerName string, logOptions *v1.PodLogOptions, stdout, stderr io.Writer) error
ServeLogs(w http.ResponseWriter, req *http.Request)
ResyncInterval() time.Duration
@@ -403,6 +405,17 @@ func (s *Server) InstallDefaultHandlers() {
s.restfulCont.Handle(proberMetricsPath,
compbasemetrics.HandlerFor(p, compbasemetrics.HandlerOpts{ErrorHandling: compbasemetrics.ContinueOnError}),
)
// Only enable checkpoint API if the feature is enabled
if utilfeature.DefaultFeatureGate.Enabled(features.ContainerCheckpoint) {
s.addMetricsBucketMatcher("checkpoint")
ws = &restful.WebService{}
ws.Path("/checkpoint").Produces(restful.MIME_JSON)
ws.Route(ws.POST("/{podNamespace}/{podID}/{containerName}").
To(s.checkpoint).
Operation("checkpoint"))
s.restfulCont.Add(ws)
}
}
// InstallDebuggingHandlers registers the HTTP request patterns that serve logs or run commands/containers
@@ -878,6 +891,83 @@ func (s *Server) getPortForward(request *restful.Request, response *restful.Resp
proxyStream(response.ResponseWriter, request.Request, url)
}
// checkpoint handles the checkpoint API request. It checks if the requested
// podNamespace, pod and container actually exist and only then calls out
// to the runtime to actually checkpoint the container.
func (s *Server) checkpoint(request *restful.Request, response *restful.Response) {
pod, ok := s.host.GetPodByName(request.PathParameter("podNamespace"), request.PathParameter("podID"))
if !ok {
response.WriteError(http.StatusNotFound, fmt.Errorf("pod does not exist"))
return
}
containerName := request.PathParameter("containerName")
found := false
for _, container := range pod.Spec.Containers {
if container.Name == containerName {
found = true
}
}
if !found {
for _, container := range pod.Spec.InitContainers {
if container.Name == containerName {
found = true
}
}
}
if !found && utilfeature.DefaultFeatureGate.Enabled(features.EphemeralContainers) {
for _, container := range pod.Spec.EphemeralContainers {
if container.Name == containerName {
found = true
}
}
}
if !found {
response.WriteError(
http.StatusNotFound,
fmt.Errorf("container %v does not exist", containerName),
)
return
}
options := &runtimeapi.CheckpointContainerRequest{}
// Query parameter to select an optional timeout. Without the timeout parameter
// the checkpoint command will use the default CRI timeout.
timeouts := request.Request.URL.Query()["timeout"]
if len(timeouts) > 0 {
// If the user specified one or multiple values for timeouts we
// are using the last available value.
timeout, err := strconv.ParseInt(timeouts[len(timeouts)-1], 10, 64)
if err != nil {
response.WriteError(
http.StatusNotFound,
fmt.Errorf("cannot parse value of timeout parameter"),
)
return
}
options.Timeout = timeout
}
if err := s.host.CheckpointContainer(pod.UID, kubecontainer.GetPodFullName(pod), containerName, options); err != nil {
response.WriteError(
http.StatusInternalServerError,
fmt.Errorf(
"checkpointing of %v/%v/%v failed (%v)",
request.PathParameter("podNamespace"),
request.PathParameter("podID"),
containerName,
err,
),
)
return
}
writeJSONResponse(
response,
[]byte(fmt.Sprintf("{\"items\":[\"%s\"]}", options.Location)),
)
}
// getURLRootPath trims a URL path.
// For paths in the format of "/metrics/xxx", "metrics/xxx" is returned;
// For all other paths, the first part of the path is returned.