mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-24 20:24:09 +00:00
Merge pull request #29580 from yujuhong/dshim2
Automatic merge from submit-queue dockershim: Implement more functions. Based on #29553. Only the last two commits are new.
This commit is contained in:
commit
94b5d52b90
@ -19,12 +19,15 @@ package dockershim
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"time"
|
||||||
|
|
||||||
dockertypes "github.com/docker/engine-api/types"
|
dockertypes "github.com/docker/engine-api/types"
|
||||||
dockercontainer "github.com/docker/engine-api/types/container"
|
dockercontainer "github.com/docker/engine-api/types/container"
|
||||||
dockerfilters "github.com/docker/engine-api/types/filters"
|
dockerfilters "github.com/docker/engine-api/types/filters"
|
||||||
dockerstrslice "github.com/docker/engine-api/types/strslice"
|
dockerstrslice "github.com/docker/engine-api/types/strslice"
|
||||||
|
|
||||||
runtimeApi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
|
runtimeApi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
|
||||||
|
"k8s.io/kubernetes/pkg/kubelet/dockertools"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ListContainers lists all containers matching the filter.
|
// ListContainers lists all containers matching the filter.
|
||||||
@ -190,14 +193,112 @@ func (ds *dockerService) RemoveContainer(rawContainerID string) error {
|
|||||||
return ds.client.RemoveContainer(rawContainerID, dockertypes.ContainerRemoveOptions{RemoveVolumes: true})
|
return ds.client.RemoveContainer(rawContainerID, dockertypes.ContainerRemoveOptions{RemoveVolumes: true})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getContainerTimestamps(r *dockertypes.ContainerJSON) (time.Time, time.Time, time.Time, error) {
|
||||||
|
var createdAt, startedAt, finishedAt time.Time
|
||||||
|
var err error
|
||||||
|
|
||||||
|
createdAt, err = dockertools.ParseDockerTimestamp(r.Created)
|
||||||
|
if err != nil {
|
||||||
|
return createdAt, startedAt, finishedAt, err
|
||||||
|
}
|
||||||
|
startedAt, err = dockertools.ParseDockerTimestamp(r.State.StartedAt)
|
||||||
|
if err != nil {
|
||||||
|
return createdAt, startedAt, finishedAt, err
|
||||||
|
}
|
||||||
|
finishedAt, err = dockertools.ParseDockerTimestamp(r.State.FinishedAt)
|
||||||
|
if err != nil {
|
||||||
|
return createdAt, startedAt, finishedAt, err
|
||||||
|
}
|
||||||
|
return createdAt, startedAt, finishedAt, nil
|
||||||
|
}
|
||||||
|
|
||||||
// ContainerStatus returns the container status.
|
// ContainerStatus returns the container status.
|
||||||
// TODO: Implement the function.
|
|
||||||
func (ds *dockerService) ContainerStatus(rawContainerID string) (*runtimeApi.ContainerStatus, error) {
|
func (ds *dockerService) ContainerStatus(rawContainerID string) (*runtimeApi.ContainerStatus, error) {
|
||||||
return nil, fmt.Errorf("not implemented")
|
r, err := ds.client.InspectContainer(rawContainerID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parse the timstamps.
|
||||||
|
createdAt, startedAt, finishedAt, err := getContainerTimestamps(r)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to parse timestamp for container %q: %v", rawContainerID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert the mounts.
|
||||||
|
mounts := []*runtimeApi.Mount{}
|
||||||
|
for _, m := range r.Mounts {
|
||||||
|
readonly := !m.RW
|
||||||
|
mounts = append(mounts, &runtimeApi.Mount{
|
||||||
|
Name: &m.Name,
|
||||||
|
HostPath: &m.Source,
|
||||||
|
ContainerPath: &m.Destination,
|
||||||
|
Readonly: &readonly,
|
||||||
|
// Note: Can't set SeLinuxRelabel
|
||||||
|
})
|
||||||
|
}
|
||||||
|
// Interpret container states.
|
||||||
|
var state runtimeApi.ContainerState
|
||||||
|
var reason string
|
||||||
|
if r.State.Running {
|
||||||
|
// Container is running.
|
||||||
|
state = runtimeApi.ContainerState_RUNNING
|
||||||
|
} else {
|
||||||
|
// Container is *not* running. We need to get more details.
|
||||||
|
// * Case 1: container has run and exited with non-zero finishedAt
|
||||||
|
// time.
|
||||||
|
// * Case 2: container has failed to start; it has a zero finishedAt
|
||||||
|
// time, but a non-zero exit code.
|
||||||
|
// * Case 3: container has been created, but not started (yet).
|
||||||
|
if !finishedAt.IsZero() { // Case 1
|
||||||
|
state = runtimeApi.ContainerState_EXITED
|
||||||
|
switch {
|
||||||
|
case r.State.OOMKilled:
|
||||||
|
// TODO: consider exposing OOMKilled via the runtimeAPI.
|
||||||
|
// Note: if an application handles OOMKilled gracefully, the
|
||||||
|
// exit code could be zero.
|
||||||
|
reason = "OOMKilled"
|
||||||
|
case r.State.ExitCode == 0:
|
||||||
|
reason = "Completed"
|
||||||
|
default:
|
||||||
|
reason = fmt.Sprintf("Error: %s", r.State.Error)
|
||||||
|
}
|
||||||
|
} else if !finishedAt.IsZero() && r.State.ExitCode != 0 { // Case 2
|
||||||
|
state = runtimeApi.ContainerState_EXITED
|
||||||
|
// Adjust finshedAt and startedAt time to createdAt time to avoid
|
||||||
|
// the confusion.
|
||||||
|
finishedAt, startedAt = createdAt, createdAt
|
||||||
|
reason = "ContainerCannotRun"
|
||||||
|
} else { // Case 3
|
||||||
|
state = runtimeApi.ContainerState_CREATED
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert to unix timestamps.
|
||||||
|
ct, st, ft := createdAt.Unix(), startedAt.Unix(), finishedAt.Unix()
|
||||||
|
exitCode := int32(r.State.ExitCode)
|
||||||
|
|
||||||
|
return &runtimeApi.ContainerStatus{
|
||||||
|
Id: &r.ID,
|
||||||
|
Name: &r.Name,
|
||||||
|
Image: &runtimeApi.ImageSpec{Image: &r.Config.Image},
|
||||||
|
ImageRef: &r.Image,
|
||||||
|
Mounts: mounts,
|
||||||
|
ExitCode: &exitCode,
|
||||||
|
State: &state,
|
||||||
|
CreatedAt: &ct,
|
||||||
|
StartedAt: &st,
|
||||||
|
FinishedAt: &ft,
|
||||||
|
Reason: &reason,
|
||||||
|
// TODO: We write annotations as labels on the docker containers. All
|
||||||
|
// these annotations will be read back as labels. Need to fix this.
|
||||||
|
Labels: r.Config.Labels,
|
||||||
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Exec execute a command in the container.
|
// Exec execute a command in the container.
|
||||||
// TODO: Implement the function.
|
// TODO: Need to handle terminal resizing before implementing this function.
|
||||||
|
// https://github.com/kubernetes/kubernetes/issues/29579.
|
||||||
func (ds *dockerService) Exec(rawContainerID string, cmd []string, tty bool, stdin io.Reader, stdout, stderr io.WriteCloser) error {
|
func (ds *dockerService) Exec(rawContainerID string, cmd []string, tty bool, stdin io.Reader, stdout, stderr io.WriteCloser) error {
|
||||||
return fmt.Errorf("not implemented")
|
return fmt.Errorf("not implemented")
|
||||||
}
|
}
|
||||||
|
27
pkg/kubelet/dockershim/docker_service_test.go
Normal file
27
pkg/kubelet/dockershim/docker_service_test.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
Copyright 2016 The Kubernetes 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 dockershim
|
||||||
|
|
||||||
|
import (
|
||||||
|
"k8s.io/kubernetes/pkg/kubelet/dockertools"
|
||||||
|
)
|
||||||
|
|
||||||
|
func newFakeDockerSevice() *dockerService {
|
||||||
|
return &dockerService{
|
||||||
|
client: dockertools.NewFakeDockerClient(),
|
||||||
|
}
|
||||||
|
}
|
@ -22,6 +22,7 @@ import (
|
|||||||
|
|
||||||
"k8s.io/kubernetes/pkg/api"
|
"k8s.io/kubernetes/pkg/api"
|
||||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
||||||
|
"k8s.io/kubernetes/pkg/kubelet/dockertools"
|
||||||
"k8s.io/kubernetes/pkg/util/term"
|
"k8s.io/kubernetes/pkg/util/term"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -31,11 +32,11 @@ import (
|
|||||||
|
|
||||||
// TODO: implement the methods in this file.
|
// TODO: implement the methods in this file.
|
||||||
func (ds *dockerService) AttachContainer(id kubecontainer.ContainerID, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan term.Size) (err error) {
|
func (ds *dockerService) AttachContainer(id kubecontainer.ContainerID, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan term.Size) (err error) {
|
||||||
return fmt.Errorf("not implemented")
|
return dockertools.AttachContainer(ds.client, id, stdin, stdout, stderr, tty, resize)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ds *dockerService) GetContainerLogs(pod *api.Pod, containerID kubecontainer.ContainerID, logOptions *api.PodLogOptions, stdout, stderr io.Writer) (err error) {
|
func (ds *dockerService) GetContainerLogs(pod *api.Pod, containerID kubecontainer.ContainerID, logOptions *api.PodLogOptions, stdout, stderr io.Writer) (err error) {
|
||||||
return fmt.Errorf("not implemented")
|
return dockertools.GetContainerLogs(ds.client, pod, containerID, logOptions, stdout, stderr)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ds *dockerService) PortForward(pod *kubecontainer.Pod, port uint16, stream io.ReadWriteCloser) error {
|
func (ds *dockerService) PortForward(pod *kubecontainer.Pod, port uint16, stream io.ReadWriteCloser) error {
|
||||||
|
@ -139,7 +139,7 @@ func (cgc *containerGC) evictableContainers(minAge time.Duration) (containersByE
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
created, err := parseDockerTimestamp(data.Created)
|
created, err := ParseDockerTimestamp(data.Created)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
glog.Errorf("Failed to parse Created timestamp %q for container %q", data.Created, container.ID)
|
glog.Errorf("Failed to parse Created timestamp %q for container %q", data.Created, container.ID)
|
||||||
}
|
}
|
||||||
|
@ -285,7 +285,13 @@ func NewDockerManager(
|
|||||||
// stream the log. Set 'follow' to false and specify the number of lines (e.g.
|
// stream the log. Set 'follow' to false and specify the number of lines (e.g.
|
||||||
// "100" or "all") to tail the log.
|
// "100" or "all") to tail the log.
|
||||||
// TODO: Make 'RawTerminal' option flagable.
|
// TODO: Make 'RawTerminal' option flagable.
|
||||||
func (dm *DockerManager) GetContainerLogs(pod *api.Pod, containerID kubecontainer.ContainerID, logOptions *api.PodLogOptions, stdout, stderr io.Writer) (err error) {
|
func (dm *DockerManager) GetContainerLogs(pod *api.Pod, containerID kubecontainer.ContainerID, logOptions *api.PodLogOptions, stdout, stderr io.Writer) error {
|
||||||
|
return GetContainerLogs(dm.client, pod, containerID, logOptions, stdout, stderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temporarily export this function to share with dockershim.
|
||||||
|
// TODO: clean this up.
|
||||||
|
func GetContainerLogs(client DockerInterface, pod *api.Pod, containerID kubecontainer.ContainerID, logOptions *api.PodLogOptions, stdout, stderr io.Writer) error {
|
||||||
var since int64
|
var since int64
|
||||||
if logOptions.SinceSeconds != nil {
|
if logOptions.SinceSeconds != nil {
|
||||||
t := unversioned.Now().Add(-time.Duration(*logOptions.SinceSeconds) * time.Second)
|
t := unversioned.Now().Add(-time.Duration(*logOptions.SinceSeconds) * time.Second)
|
||||||
@ -309,8 +315,7 @@ func (dm *DockerManager) GetContainerLogs(pod *api.Pod, containerID kubecontaine
|
|||||||
ErrorStream: stderr,
|
ErrorStream: stderr,
|
||||||
RawTerminal: false,
|
RawTerminal: false,
|
||||||
}
|
}
|
||||||
err = dm.client.Logs(containerID.ID, opts, sopts)
|
return client.Logs(containerID.ID, opts, sopts)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -374,13 +379,13 @@ func (dm *DockerManager) inspectContainer(id string, podName, podNamespace strin
|
|||||||
glog.Errorf("Failed to parse %q timestamp %q for container %q of pod %q", label, s, id, kubecontainer.BuildPodFullName(podName, podNamespace))
|
glog.Errorf("Failed to parse %q timestamp %q for container %q of pod %q", label, s, id, kubecontainer.BuildPodFullName(podName, podNamespace))
|
||||||
}
|
}
|
||||||
var createdAt, startedAt, finishedAt time.Time
|
var createdAt, startedAt, finishedAt time.Time
|
||||||
if createdAt, err = parseDockerTimestamp(iResult.Created); err != nil {
|
if createdAt, err = ParseDockerTimestamp(iResult.Created); err != nil {
|
||||||
parseTimestampError("Created", iResult.Created)
|
parseTimestampError("Created", iResult.Created)
|
||||||
}
|
}
|
||||||
if startedAt, err = parseDockerTimestamp(iResult.State.StartedAt); err != nil {
|
if startedAt, err = ParseDockerTimestamp(iResult.State.StartedAt); err != nil {
|
||||||
parseTimestampError("StartedAt", iResult.State.StartedAt)
|
parseTimestampError("StartedAt", iResult.State.StartedAt)
|
||||||
}
|
}
|
||||||
if finishedAt, err = parseDockerTimestamp(iResult.State.FinishedAt); err != nil {
|
if finishedAt, err = ParseDockerTimestamp(iResult.State.FinishedAt); err != nil {
|
||||||
parseTimestampError("FinishedAt", iResult.State.FinishedAt)
|
parseTimestampError("FinishedAt", iResult.State.FinishedAt)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1123,10 +1128,16 @@ func (dm *DockerManager) ExecInContainer(containerID kubecontainer.ContainerID,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dm *DockerManager) AttachContainer(containerID kubecontainer.ContainerID, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan term.Size) error {
|
func (dm *DockerManager) AttachContainer(containerID kubecontainer.ContainerID, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan term.Size) error {
|
||||||
|
return AttachContainer(dm.client, containerID, stdin, stdout, stderr, tty, resize)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temporarily export this function to share with dockershim.
|
||||||
|
// TODO: clean this up.
|
||||||
|
func AttachContainer(client DockerInterface, containerID kubecontainer.ContainerID, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan term.Size) error {
|
||||||
// Have to start this before the call to client.AttachToContainer because client.AttachToContainer is a blocking
|
// Have to start this before the call to client.AttachToContainer because client.AttachToContainer is a blocking
|
||||||
// call :-( Otherwise, resize events don't get processed and the terminal never resizes.
|
// call :-( Otherwise, resize events don't get processed and the terminal never resizes.
|
||||||
kubecontainer.HandleResizing(resize, func(size term.Size) {
|
kubecontainer.HandleResizing(resize, func(size term.Size) {
|
||||||
dm.client.ResizeContainerTTY(containerID.ID, int(size.Height), int(size.Width))
|
client.ResizeContainerTTY(containerID.ID, int(size.Height), int(size.Width))
|
||||||
})
|
})
|
||||||
|
|
||||||
// TODO(random-liu): Do we really use the *Logs* field here?
|
// TODO(random-liu): Do we really use the *Logs* field here?
|
||||||
@ -1142,7 +1153,7 @@ func (dm *DockerManager) AttachContainer(containerID kubecontainer.ContainerID,
|
|||||||
ErrorStream: stderr,
|
ErrorStream: stderr,
|
||||||
RawTerminal: tty,
|
RawTerminal: tty,
|
||||||
}
|
}
|
||||||
return dm.client.AttachToContainer(containerID.ID, opts, sopts)
|
return client.AttachToContainer(containerID.ID, opts, sopts)
|
||||||
}
|
}
|
||||||
|
|
||||||
func noPodInfraContainerError(podName, podNamespace string) error {
|
func noPodInfraContainerError(podName, podNamespace string) error {
|
||||||
|
@ -531,8 +531,8 @@ func (d *kubeDockerClient) getTimeoutContext() (context.Context, context.CancelF
|
|||||||
return context.WithTimeout(context.Background(), d.timeout)
|
return context.WithTimeout(context.Background(), d.timeout)
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseDockerTimestamp parses the timestamp returned by DockerInterface from string to time.Time
|
// ParseDockerTimestamp parses the timestamp returned by DockerInterface from string to time.Time
|
||||||
func parseDockerTimestamp(s string) (time.Time, error) {
|
func ParseDockerTimestamp(s string) (time.Time, error) {
|
||||||
// Timestamp returned by Docker is in time.RFC3339Nano format.
|
// Timestamp returned by Docker is in time.RFC3339Nano format.
|
||||||
return time.Parse(time.RFC3339Nano, s)
|
return time.Parse(time.RFC3339Nano, s)
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user