Commit 2a87baba authored by Kubernetes Submit Queue's avatar Kubernetes Submit Queue Committed by GitHub

Merge pull request #45199 from yujuhong/clean-up-dockertools

Automatic merge from submit-queue Clean up code in dockertools Move functions and sub packages to dockershim. Part of #43234.
parents fc51574b c35c00f3
...@@ -939,18 +939,6 @@ func RunDockershim(c *componentconfig.KubeletConfiguration, dockershimRootDir st ...@@ -939,18 +939,6 @@ func RunDockershim(c *componentconfig.KubeletConfiguration, dockershimRootDir st
dockerClient := dockertools.ConnectToDockerOrDie(c.DockerEndpoint, c.RuntimeRequestTimeout.Duration, dockerClient := dockertools.ConnectToDockerOrDie(c.DockerEndpoint, c.RuntimeRequestTimeout.Duration,
c.ImagePullProgressDeadline.Duration) c.ImagePullProgressDeadline.Duration)
// Initialize docker exec handler.
var dockerExecHandler dockertools.ExecHandler
switch c.DockerExecHandlerName {
case "native":
dockerExecHandler = &dockertools.NativeExecHandler{}
case "nsenter":
dockerExecHandler = &dockertools.NsenterExecHandler{}
default:
glog.Warningf("Unknown Docker exec handler %q; defaulting to native", c.DockerExecHandlerName)
dockerExecHandler = &dockertools.NativeExecHandler{}
}
// Initialize network plugin settings. // Initialize network plugin settings.
binDir := c.CNIBinDir binDir := c.CNIBinDir
if binDir == "" { if binDir == "" {
...@@ -976,7 +964,7 @@ func RunDockershim(c *componentconfig.KubeletConfiguration, dockershimRootDir st ...@@ -976,7 +964,7 @@ func RunDockershim(c *componentconfig.KubeletConfiguration, dockershimRootDir st
} }
ds, err := dockershim.NewDockerService(dockerClient, c.SeccompProfileRoot, c.PodInfraContainerImage, ds, err := dockershim.NewDockerService(dockerClient, c.SeccompProfileRoot, c.PodInfraContainerImage,
streamingConfig, &pluginSettings, c.RuntimeCgroups, c.CgroupDriver, dockerExecHandler, dockershimRootDir, streamingConfig, &pluginSettings, c.RuntimeCgroups, c.CgroupDriver, c.DockerExecHandlerName, dockershimRootDir,
!c.DockerEnableSharedPID) !c.DockerEnableSharedPID)
if err != nil { if err != nil {
return err return err
......
...@@ -21,6 +21,7 @@ go_library( ...@@ -21,6 +21,7 @@ go_library(
"docker_sandbox.go", "docker_sandbox.go",
"docker_service.go", "docker_service.go",
"docker_streaming.go", "docker_streaming.go",
"exec.go",
"helpers.go", "helpers.go",
"naming.go", "naming.go",
"security_context.go", "security_context.go",
...@@ -37,8 +38,8 @@ go_library( ...@@ -37,8 +38,8 @@ go_library(
"//pkg/kubelet/container:go_default_library", "//pkg/kubelet/container:go_default_library",
"//pkg/kubelet/dockershim/cm:go_default_library", "//pkg/kubelet/dockershim/cm:go_default_library",
"//pkg/kubelet/dockershim/errors:go_default_library", "//pkg/kubelet/dockershim/errors:go_default_library",
"//pkg/kubelet/dockershim/securitycontext:go_default_library",
"//pkg/kubelet/dockertools:go_default_library", "//pkg/kubelet/dockertools:go_default_library",
"//pkg/kubelet/dockertools/securitycontext:go_default_library",
"//pkg/kubelet/leaky:go_default_library", "//pkg/kubelet/leaky:go_default_library",
"//pkg/kubelet/network:go_default_library", "//pkg/kubelet/network:go_default_library",
"//pkg/kubelet/network/cni:go_default_library", "//pkg/kubelet/network/cni:go_default_library",
...@@ -49,7 +50,10 @@ go_library( ...@@ -49,7 +50,10 @@ go_library(
"//pkg/kubelet/types:go_default_library", "//pkg/kubelet/types:go_default_library",
"//pkg/kubelet/util/cache:go_default_library", "//pkg/kubelet/util/cache:go_default_library",
"//pkg/kubelet/util/ioutils:go_default_library", "//pkg/kubelet/util/ioutils:go_default_library",
"//pkg/security/apparmor:go_default_library",
"//pkg/util/exec:go_default_library",
"//pkg/util/hash:go_default_library", "//pkg/util/hash:go_default_library",
"//pkg/util/term:go_default_library",
"//vendor/github.com/blang/semver:go_default_library", "//vendor/github.com/blang/semver:go_default_library",
"//vendor/github.com/docker/engine-api/types:go_default_library", "//vendor/github.com/docker/engine-api/types:go_default_library",
"//vendor/github.com/docker/engine-api/types/container:go_default_library", "//vendor/github.com/docker/engine-api/types/container:go_default_library",
...@@ -57,6 +61,7 @@ go_library( ...@@ -57,6 +61,7 @@ go_library(
"//vendor/github.com/docker/engine-api/types/strslice:go_default_library", "//vendor/github.com/docker/engine-api/types/strslice:go_default_library",
"//vendor/github.com/docker/go-connections/nat:go_default_library", "//vendor/github.com/docker/go-connections/nat:go_default_library",
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/sets:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/wait:go_default_library",
...@@ -86,9 +91,9 @@ go_test( ...@@ -86,9 +91,9 @@ go_test(
"//pkg/kubelet/container:go_default_library", "//pkg/kubelet/container:go_default_library",
"//pkg/kubelet/container/testing:go_default_library", "//pkg/kubelet/container/testing:go_default_library",
"//pkg/kubelet/dockershim/errors:go_default_library", "//pkg/kubelet/dockershim/errors:go_default_library",
"//pkg/kubelet/dockershim/securitycontext:go_default_library",
"//pkg/kubelet/dockershim/testing:go_default_library", "//pkg/kubelet/dockershim/testing:go_default_library",
"//pkg/kubelet/dockertools:go_default_library", "//pkg/kubelet/dockertools:go_default_library",
"//pkg/kubelet/dockertools/securitycontext:go_default_library",
"//pkg/kubelet/network:go_default_library", "//pkg/kubelet/network:go_default_library",
"//pkg/kubelet/network/testing:go_default_library", "//pkg/kubelet/network/testing:go_default_library",
"//pkg/kubelet/types:go_default_library", "//pkg/kubelet/types:go_default_library",
...@@ -119,6 +124,7 @@ filegroup( ...@@ -119,6 +124,7 @@ filegroup(
"//pkg/kubelet/dockershim/cm:all-srcs", "//pkg/kubelet/dockershim/cm:all-srcs",
"//pkg/kubelet/dockershim/errors:all-srcs", "//pkg/kubelet/dockershim/errors:all-srcs",
"//pkg/kubelet/dockershim/remote:all-srcs", "//pkg/kubelet/dockershim/remote:all-srcs",
"//pkg/kubelet/dockershim/securitycontext:all-srcs",
"//pkg/kubelet/dockershim/testing:all-srcs", "//pkg/kubelet/dockershim/testing:all-srcs",
], ],
tags = ["automanaged"], tags = ["automanaged"],
......
...@@ -17,6 +17,8 @@ limitations under the License. ...@@ -17,6 +17,8 @@ limitations under the License.
package dockershim package dockershim
import ( import (
"fmt"
dockertypes "github.com/docker/engine-api/types" dockertypes "github.com/docker/engine-api/types"
runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
"k8s.io/kubernetes/pkg/kubelet/dockertools" "k8s.io/kubernetes/pkg/kubelet/dockertools"
...@@ -80,7 +82,7 @@ func (ds *dockerService) PullImage(image *runtimeapi.ImageSpec, auth *runtimeapi ...@@ -80,7 +82,7 @@ func (ds *dockerService) PullImage(image *runtimeapi.ImageSpec, auth *runtimeapi
return "", err return "", err
} }
return dockertools.GetImageRef(ds.client, image.Image) return getImageRef(ds.client, image.Image)
} }
// RemoveImage removes the image. // RemoveImage removes the image.
...@@ -101,3 +103,21 @@ func (ds *dockerService) RemoveImage(image *runtimeapi.ImageSpec) error { ...@@ -101,3 +103,21 @@ func (ds *dockerService) RemoveImage(image *runtimeapi.ImageSpec) error {
_, err = ds.client.RemoveImage(image.Image, dockertypes.ImageRemoveOptions{PruneChildren: true}) _, err = ds.client.RemoveImage(image.Image, dockertypes.ImageRemoveOptions{PruneChildren: true})
return err return err
} }
// getImageRef returns the image digest if exists, or else returns the image ID.
func getImageRef(client dockertools.DockerInterface, image string) (string, error) {
img, err := client.InspectImageByRef(image)
if err != nil {
return "", err
}
if img == nil {
return "", fmt.Errorf("unable to inspect image %s", image)
}
// Returns the digest if it exist.
if len(img.RepoDigests) > 0 {
return img.RepoDigests[0], nil
}
return img.ID, nil
}
...@@ -20,12 +20,14 @@ import ( ...@@ -20,12 +20,14 @@ import (
"fmt" "fmt"
"io" "io"
"net/http" "net/http"
"strconv"
"time" "time"
"github.com/blang/semver" "github.com/blang/semver"
dockertypes "github.com/docker/engine-api/types" dockertypes "github.com/docker/engine-api/types"
"github.com/golang/glog" "github.com/golang/glog"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/apis/componentconfig" "k8s.io/kubernetes/pkg/apis/componentconfig"
internalapi "k8s.io/kubernetes/pkg/kubelet/api" internalapi "k8s.io/kubernetes/pkg/kubelet/api"
...@@ -147,12 +149,23 @@ var internalLabelKeys []string = []string{containerTypeLabelKey, containerLogPat ...@@ -147,12 +149,23 @@ var internalLabelKeys []string = []string{containerTypeLabelKey, containerLogPat
// NOTE: Anything passed to DockerService should be eventually handled in another way when we switch to running the shim as a different process. // NOTE: Anything passed to DockerService should be eventually handled in another way when we switch to running the shim as a different process.
func NewDockerService(client dockertools.DockerInterface, seccompProfileRoot string, podSandboxImage string, streamingConfig *streaming.Config, func NewDockerService(client dockertools.DockerInterface, seccompProfileRoot string, podSandboxImage string, streamingConfig *streaming.Config,
pluginSettings *NetworkPluginSettings, cgroupsName string, kubeCgroupDriver string, execHandler dockertools.ExecHandler, dockershimRootDir string, disableSharedPID bool) (DockerService, error) { pluginSettings *NetworkPluginSettings, cgroupsName string, kubeCgroupDriver string, execHandlerName, dockershimRootDir string, disableSharedPID bool) (DockerService, error) {
c := dockertools.NewInstrumentedDockerInterface(client) c := dockertools.NewInstrumentedDockerInterface(client)
checkpointHandler, err := NewPersistentCheckpointHandler(dockershimRootDir) checkpointHandler, err := NewPersistentCheckpointHandler(dockershimRootDir)
if err != nil { if err != nil {
return nil, err return nil, err
} }
var execHandler ExecHandler
switch execHandlerName {
case "native":
execHandler = &NativeExecHandler{}
case "nsenter":
execHandler = &NsenterExecHandler{}
default:
glog.Warningf("Unknown Docker exec handler %q; defaulting to native", execHandlerName)
execHandler = &NativeExecHandler{}
}
ds := &dockerService{ ds := &dockerService{
seccompProfileRoot: seccompProfileRoot, seccompProfileRoot: seccompProfileRoot,
client: c, client: c,
...@@ -478,7 +491,32 @@ func (d *dockerLegacyService) GetContainerLogs(pod *v1.Pod, containerID kubecont ...@@ -478,7 +491,32 @@ func (d *dockerLegacyService) GetContainerLogs(pod *v1.Pod, containerID kubecont
if err != nil { if err != nil {
return err return err
} }
return dockertools.GetContainerLogs(d.client, pod, containerID, logOptions, stdout, stderr, container.Config.Tty)
var since int64
if logOptions.SinceSeconds != nil {
t := metav1.Now().Add(-time.Duration(*logOptions.SinceSeconds) * time.Second)
since = t.Unix()
}
if logOptions.SinceTime != nil {
since = logOptions.SinceTime.Unix()
}
opts := dockertypes.ContainerLogsOptions{
ShowStdout: true,
ShowStderr: true,
Since: strconv.FormatInt(since, 10),
Timestamps: logOptions.Timestamps,
Follow: logOptions.Follow,
}
if logOptions.TailLines != nil {
opts.Tail = strconv.FormatInt(*logOptions.TailLines, 10)
}
sopts := dockertools.StreamOptions{
OutputStream: stdout,
ErrorStream: stderr,
RawTerminal: container.Config.Tty,
}
return d.client.Logs(containerID.ID, opts, sopts)
} }
// criSupportedLogDrivers are log drivers supported by native CRI integration. // criSupportedLogDrivers are log drivers supported by native CRI integration.
......
...@@ -21,11 +21,15 @@ import ( ...@@ -21,11 +21,15 @@ import (
"fmt" "fmt"
"io" "io"
"math" "math"
"os/exec"
"strings"
"time" "time"
dockertypes "github.com/docker/engine-api/types" dockertypes "github.com/docker/engine-api/types"
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/client/unversioned/remotecommand" "k8s.io/kubernetes/pkg/client/unversioned/remotecommand"
runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
"k8s.io/kubernetes/pkg/kubelet/dockertools" "k8s.io/kubernetes/pkg/kubelet/dockertools"
"k8s.io/kubernetes/pkg/kubelet/server/streaming" "k8s.io/kubernetes/pkg/kubelet/server/streaming"
"k8s.io/kubernetes/pkg/kubelet/util/ioutils" "k8s.io/kubernetes/pkg/kubelet/util/ioutils"
...@@ -33,7 +37,7 @@ import ( ...@@ -33,7 +37,7 @@ import (
type streamingRuntime struct { type streamingRuntime struct {
client dockertools.DockerInterface client dockertools.DockerInterface
execHandler dockertools.ExecHandler execHandler ExecHandler
} }
var _ streaming.Runtime = &streamingRuntime{} var _ streaming.Runtime = &streamingRuntime{}
...@@ -57,14 +61,14 @@ func (r *streamingRuntime) Attach(containerID string, in io.Reader, out, errw io ...@@ -57,14 +61,14 @@ func (r *streamingRuntime) Attach(containerID string, in io.Reader, out, errw io
return err return err
} }
return dockertools.AttachContainer(r.client, containerID, in, out, errw, tty, resize) return attachContainer(r.client, containerID, in, out, errw, tty, resize)
} }
func (r *streamingRuntime) PortForward(podSandboxID string, port int32, stream io.ReadWriteCloser) error { func (r *streamingRuntime) PortForward(podSandboxID string, port int32, stream io.ReadWriteCloser) error {
if port < 0 || port > math.MaxUint16 { if port < 0 || port > math.MaxUint16 {
return fmt.Errorf("invalid port %d", port) return fmt.Errorf("invalid port %d", port)
} }
return dockertools.PortForward(r.client, podSandboxID, port, stream) return portForward(r.client, podSandboxID, port, stream)
} }
// ExecSync executes a command in the container, and returns the stdout output. // ExecSync executes a command in the container, and returns the stdout output.
...@@ -128,3 +132,83 @@ func checkContainerStatus(client dockertools.DockerInterface, containerID string ...@@ -128,3 +132,83 @@ func checkContainerStatus(client dockertools.DockerInterface, containerID string
} }
return container, nil return container, nil
} }
func attachContainer(client dockertools.DockerInterface, containerID string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize) error {
// 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.
kubecontainer.HandleResizing(resize, func(size remotecommand.TerminalSize) {
client.ResizeContainerTTY(containerID, int(size.Height), int(size.Width))
})
// TODO(random-liu): Do we really use the *Logs* field here?
opts := dockertypes.ContainerAttachOptions{
Stream: true,
Stdin: stdin != nil,
Stdout: stdout != nil,
Stderr: stderr != nil,
}
sopts := dockertools.StreamOptions{
InputStream: stdin,
OutputStream: stdout,
ErrorStream: stderr,
RawTerminal: tty,
}
return client.AttachToContainer(containerID, opts, sopts)
}
func portForward(client dockertools.DockerInterface, podInfraContainerID string, port int32, stream io.ReadWriteCloser) error {
container, err := client.InspectContainer(podInfraContainerID)
if err != nil {
return err
}
if !container.State.Running {
return fmt.Errorf("container not running (%s)", container.ID)
}
containerPid := container.State.Pid
socatPath, lookupErr := exec.LookPath("socat")
if lookupErr != nil {
return fmt.Errorf("unable to do port forwarding: socat not found.")
}
args := []string{"-t", fmt.Sprintf("%d", containerPid), "-n", socatPath, "-", fmt.Sprintf("TCP4:localhost:%d", port)}
nsenterPath, lookupErr := exec.LookPath("nsenter")
if lookupErr != nil {
return fmt.Errorf("unable to do port forwarding: nsenter not found.")
}
commandString := fmt.Sprintf("%s %s", nsenterPath, strings.Join(args, " "))
glog.V(4).Infof("executing port forwarding command: %s", commandString)
command := exec.Command(nsenterPath, args...)
command.Stdout = stream
stderr := new(bytes.Buffer)
command.Stderr = stderr
// If we use Stdin, command.Run() won't return until the goroutine that's copying
// from stream finishes. Unfortunately, if you have a client like telnet connected
// via port forwarding, as long as the user's telnet client is connected to the user's
// local listener that port forwarding sets up, the telnet session never exits. This
// means that even if socat has finished running, command.Run() won't ever return
// (because the client still has the connection and stream open).
//
// The work around is to use StdinPipe(), as Wait() (called by Run()) closes the pipe
// when the command (socat) exits.
inPipe, err := command.StdinPipe()
if err != nil {
return fmt.Errorf("unable to do port forwarding: error creating stdin pipe: %v", err)
}
go func() {
io.Copy(inPipe, stream)
inPipe.Close()
}()
if err := command.Run(); err != nil {
return fmt.Errorf("%v: %s", err, stderr.String())
}
return nil
}
...@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and ...@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package dockertools package dockershim
import ( import (
"fmt" "fmt"
...@@ -25,22 +25,44 @@ import ( ...@@ -25,22 +25,44 @@ import (
dockertypes "github.com/docker/engine-api/types" dockertypes "github.com/docker/engine-api/types"
"github.com/golang/glog" "github.com/golang/glog"
"k8s.io/kubernetes/pkg/client/unversioned/remotecommand" "k8s.io/kubernetes/pkg/client/unversioned/remotecommand"
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container" kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
"k8s.io/kubernetes/pkg/kubelet/dockertools"
utilexec "k8s.io/kubernetes/pkg/util/exec" utilexec "k8s.io/kubernetes/pkg/util/exec"
"k8s.io/kubernetes/pkg/util/term" "k8s.io/kubernetes/pkg/util/term"
) )
// ExecHandler knows how to execute a command in a running Docker container. // ExecHandler knows how to execute a command in a running Docker container.
type ExecHandler interface { type ExecHandler interface {
ExecInContainer(client DockerInterface, container *dockertypes.ContainerJSON, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize, timeout time.Duration) error ExecInContainer(client dockertools.DockerInterface, container *dockertypes.ContainerJSON, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize, timeout time.Duration) error
} }
// NsenterExecHandler executes commands in Docker containers using nsenter. // NsenterExecHandler executes commands in Docker containers using nsenter.
type NsenterExecHandler struct{} type NsenterExecHandler struct{}
type dockerExitError struct {
Inspect *dockertypes.ContainerExecInspect
}
func (d *dockerExitError) String() string {
return d.Error()
}
func (d *dockerExitError) Error() string {
return fmt.Sprintf("Error executing in Docker Container: %d", d.Inspect.ExitCode)
}
func (d *dockerExitError) Exited() bool {
return !d.Inspect.Running
}
func (d *dockerExitError) ExitStatus() int {
return d.Inspect.ExitCode
}
// TODO should we support nsenter in a container, running with elevated privs and --pid=host? // TODO should we support nsenter in a container, running with elevated privs and --pid=host?
func (*NsenterExecHandler) ExecInContainer(client DockerInterface, container *dockertypes.ContainerJSON, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize, timeout time.Duration) error { func (*NsenterExecHandler) ExecInContainer(client dockertools.DockerInterface, container *dockertypes.ContainerJSON, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize, timeout time.Duration) error {
nsenter, err := exec.LookPath("nsenter") nsenter, err := exec.LookPath("nsenter")
if err != nil { if err != nil {
return fmt.Errorf("exec unavailable - unable to locate nsenter") return fmt.Errorf("exec unavailable - unable to locate nsenter")
...@@ -111,7 +133,7 @@ func (*NsenterExecHandler) ExecInContainer(client DockerInterface, container *do ...@@ -111,7 +133,7 @@ func (*NsenterExecHandler) ExecInContainer(client DockerInterface, container *do
// NativeExecHandler executes commands in Docker containers using Docker's exec API. // NativeExecHandler executes commands in Docker containers using Docker's exec API.
type NativeExecHandler struct{} type NativeExecHandler struct{}
func (*NativeExecHandler) ExecInContainer(client DockerInterface, container *dockertypes.ContainerJSON, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize, timeout time.Duration) error { func (*NativeExecHandler) ExecInContainer(client dockertools.DockerInterface, container *dockertypes.ContainerJSON, cmd []string, stdin io.Reader, stdout, stderr io.WriteCloser, tty bool, resize <-chan remotecommand.TerminalSize, timeout time.Duration) error {
createOpts := dockertypes.ExecConfig{ createOpts := dockertypes.ExecConfig{
Cmd: cmd, Cmd: cmd,
AttachStdin: stdin != nil, AttachStdin: stdin != nil,
...@@ -131,7 +153,7 @@ func (*NativeExecHandler) ExecInContainer(client DockerInterface, container *doc ...@@ -131,7 +153,7 @@ func (*NativeExecHandler) ExecInContainer(client DockerInterface, container *doc
}) })
startOpts := dockertypes.ExecStartCheck{Detach: false, Tty: tty} startOpts := dockertypes.ExecStartCheck{Detach: false, Tty: tty}
streamOpts := StreamOptions{ streamOpts := dockertools.StreamOptions{
InputStream: stdin, InputStream: stdin,
OutputStream: stdout, OutputStream: stdout,
ErrorStream: stderr, ErrorStream: stderr,
......
...@@ -17,7 +17,12 @@ limitations under the License. ...@@ -17,7 +17,12 @@ limitations under the License.
package dockershim package dockershim
import ( import (
"bytes"
"crypto/md5"
"encoding/json"
"fmt" "fmt"
"io/ioutil"
"path/filepath"
"regexp" "regexp"
"strconv" "strconv"
"strings" "strings"
...@@ -28,14 +33,21 @@ import ( ...@@ -28,14 +33,21 @@ import (
dockernat "github.com/docker/go-connections/nat" dockernat "github.com/docker/go-connections/nat"
"github.com/golang/glog" "github.com/golang/glog"
"k8s.io/kubernetes/pkg/api/v1"
v1helper "k8s.io/kubernetes/pkg/api/v1/helper" v1helper "k8s.io/kubernetes/pkg/api/v1/helper"
runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
"k8s.io/kubernetes/pkg/kubelet/dockertools" "k8s.io/kubernetes/pkg/kubelet/dockertools"
"k8s.io/kubernetes/pkg/kubelet/types" "k8s.io/kubernetes/pkg/kubelet/types"
"k8s.io/kubernetes/pkg/security/apparmor"
) )
const ( const (
annotationPrefix = "annotation." annotationPrefix = "annotation."
// Docker changed the API for specifying options in v1.11
securityOptSeparatorChangeVersion = "1.23.0" // Corresponds to docker 1.11.x
securityOptSeparatorOld = ':'
securityOptSeparatorNew = '='
) )
var ( var (
...@@ -43,7 +55,9 @@ var ( ...@@ -43,7 +55,9 @@ var (
// Docker changes the security option separator from ':' to '=' in the 1.23 // Docker changes the security option separator from ':' to '=' in the 1.23
// API version. // API version.
optsSeparatorChangeVersion = semver.MustParse(dockertools.SecurityOptSeparatorChangeVersion) optsSeparatorChangeVersion = semver.MustParse(securityOptSeparatorChangeVersion)
defaultSeccompOpt = []dockerOpt{{"seccomp", "unconfined", ""}}
) )
// generateEnvList converts KeyValue list to a list of strings, in the form of // generateEnvList converts KeyValue list to a list of strings, in the form of
...@@ -181,17 +195,57 @@ func makePortsAndBindings(pm []*runtimeapi.PortMapping) (map[dockernat.Port]stru ...@@ -181,17 +195,57 @@ func makePortsAndBindings(pm []*runtimeapi.PortMapping) (map[dockernat.Port]stru
return exposedPorts, portBindings return exposedPorts, portBindings
} }
func getSeccompDockerOpts(annotations map[string]string, ctrName, profileRoot string) ([]dockerOpt, error) {
profile, profileOK := annotations[v1.SeccompContainerAnnotationKeyPrefix+ctrName]
if !profileOK {
// try the pod profile
profile, profileOK = annotations[v1.SeccompPodAnnotationKey]
if !profileOK {
// return early the default
return defaultSeccompOpt, nil
}
}
if profile == "unconfined" {
// return early the default
return defaultSeccompOpt, nil
}
if profile == "docker/default" {
// return nil so docker will load the default seccomp profile
return nil, nil
}
if !strings.HasPrefix(profile, "localhost/") {
return nil, fmt.Errorf("unknown seccomp profile option: %s", profile)
}
name := strings.TrimPrefix(profile, "localhost/") // by pod annotation validation, name is a valid subpath
fname := filepath.Join(profileRoot, filepath.FromSlash(name))
file, err := ioutil.ReadFile(fname)
if err != nil {
return nil, fmt.Errorf("cannot load seccomp profile %q: %v", name, err)
}
b := bytes.NewBuffer(nil)
if err := json.Compact(b, file); err != nil {
return nil, err
}
// Rather than the full profile, just put the filename & md5sum in the event log.
msg := fmt.Sprintf("%s(md5:%x)", name, md5.Sum(file))
return []dockerOpt{{"seccomp", b.String(), msg}}, nil
}
// getSeccompSecurityOpts gets container seccomp options from container and sandbox // getSeccompSecurityOpts gets container seccomp options from container and sandbox
// config, currently from sandbox annotations. // config, currently from sandbox annotations.
// It is an experimental feature and may be promoted to official runtime api in the future. // It is an experimental feature and may be promoted to official runtime api in the future.
func getSeccompSecurityOpts(containerName string, sandboxConfig *runtimeapi.PodSandboxConfig, seccompProfileRoot string, separator rune) ([]string, error) { func getSeccompSecurityOpts(containerName string, sandboxConfig *runtimeapi.PodSandboxConfig, seccompProfileRoot string, separator rune) ([]string, error) {
seccompOpts, err := dockertools.GetSeccompOpts(sandboxConfig.GetAnnotations(), containerName, seccompProfileRoot) seccompOpts, err := getSeccompDockerOpts(sandboxConfig.GetAnnotations(), containerName, seccompProfileRoot)
if err != nil { if err != nil {
return nil, err return nil, err
} }
return fmtDockerOpts(seccompOpts, separator), nil
fmtOpts := dockertools.FmtDockerOpts(seccompOpts, separator)
return fmtOpts, nil
} }
// getApparmorSecurityOpts gets apparmor options from container config. // getApparmorSecurityOpts gets apparmor options from container config.
...@@ -200,12 +254,12 @@ func getApparmorSecurityOpts(sc *runtimeapi.LinuxContainerSecurityContext, separ ...@@ -200,12 +254,12 @@ func getApparmorSecurityOpts(sc *runtimeapi.LinuxContainerSecurityContext, separ
return nil, nil return nil, nil
} }
appArmorOpts, err := dockertools.GetAppArmorOpts(sc.ApparmorProfile) appArmorOpts, err := getAppArmorOpts(sc.ApparmorProfile)
if err != nil { if err != nil {
return nil, err return nil, err
} }
fmtOpts := dockertools.FmtDockerOpts(appArmorOpts, separator) fmtOpts := fmtDockerOpts(appArmorOpts, separator)
return fmtOpts, nil return fmtOpts, nil
} }
...@@ -258,10 +312,23 @@ func (f *dockerFilter) AddLabel(key, value string) { ...@@ -258,10 +312,23 @@ func (f *dockerFilter) AddLabel(key, value string) {
f.Add("label", fmt.Sprintf("%s=%s", key, value)) f.Add("label", fmt.Sprintf("%s=%s", key, value))
} }
// parseUserFromImageUser splits the user out of an user:group string.
func parseUserFromImageUser(id string) string {
if id == "" {
return id
}
// split instances where the id may contain user:group
if strings.Contains(id, ":") {
return strings.Split(id, ":")[0]
}
// no group, just return the id
return id
}
// getUserFromImageUser gets uid or user name of the image user. // getUserFromImageUser gets uid or user name of the image user.
// If user is numeric, it will be treated as uid; or else, it is treated as user name. // If user is numeric, it will be treated as uid; or else, it is treated as user name.
func getUserFromImageUser(imageUser string) (*int64, string) { func getUserFromImageUser(imageUser string) (*int64, string) {
user := dockertools.GetUserFromImageUser(imageUser) user := parseUserFromImageUser(imageUser)
// return both nil if user is not specified in the image. // return both nil if user is not specified in the image.
if user == "" { if user == "" {
return nil, "" return nil, ""
...@@ -321,9 +388,9 @@ func getSecurityOptSeparator(v *semver.Version) rune { ...@@ -321,9 +388,9 @@ func getSecurityOptSeparator(v *semver.Version) rune {
case -1: case -1:
// Current version is less than the API change version; use the old // Current version is less than the API change version; use the old
// separator. // separator.
return dockertools.SecurityOptSeparatorOld return securityOptSeparatorOld
default: default:
return dockertools.SecurityOptSeparatorNew return securityOptSeparatorNew
} }
} }
...@@ -342,3 +409,35 @@ func ensureSandboxImageExists(client dockertools.DockerInterface, image string) ...@@ -342,3 +409,35 @@ func ensureSandboxImageExists(client dockertools.DockerInterface, image string)
} }
return nil return nil
} }
func getAppArmorOpts(profile string) ([]dockerOpt, error) {
if profile == "" || profile == apparmor.ProfileRuntimeDefault {
// The docker applies the default profile by default.
return nil, nil
}
// Assume validation has already happened.
profileName := strings.TrimPrefix(profile, apparmor.ProfileNamePrefix)
return []dockerOpt{{"apparmor", profileName, ""}}, nil
}
// fmtDockerOpts formats the docker security options using the given separator.
func fmtDockerOpts(opts []dockerOpt, sep rune) []string {
fmtOpts := make([]string, len(opts))
for i, opt := range opts {
fmtOpts[i] = fmt.Sprintf("%s%c%s", opt.key, sep, opt.value)
}
return fmtOpts
}
type dockerOpt struct {
// The key-value pair passed to docker.
key, value string
// The alternative value to use in log/event messages.
msg string
}
// Expose key/value from dockertools
func (d dockerOpt) GetKV() (string, string) {
return d.key, d.value
}
...@@ -26,7 +26,7 @@ import ( ...@@ -26,7 +26,7 @@ import (
"k8s.io/kubernetes/pkg/api/v1" "k8s.io/kubernetes/pkg/api/v1"
runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
"k8s.io/kubernetes/pkg/kubelet/dockertools/securitycontext" "k8s.io/kubernetes/pkg/kubelet/dockershim/securitycontext"
knetwork "k8s.io/kubernetes/pkg/kubelet/network" knetwork "k8s.io/kubernetes/pkg/kubelet/network"
) )
......
...@@ -26,7 +26,7 @@ import ( ...@@ -26,7 +26,7 @@ import (
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime" runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
"k8s.io/kubernetes/pkg/kubelet/dockertools/securitycontext" "k8s.io/kubernetes/pkg/kubelet/dockershim/securitycontext"
) )
func TestModifyContainerConfig(t *testing.T) { func TestModifyContainerConfig(t *testing.T) {
......
...@@ -15,4 +15,4 @@ limitations under the License. ...@@ -15,4 +15,4 @@ limitations under the License.
*/ */
// Package securitycontext contains security context api implementations // Package securitycontext contains security context api implementations
package securitycontext // import "k8s.io/kubernetes/pkg/kubelet/dockertools/securitycontext" package securitycontext // import "k8s.io/kubernetes/pkg/kubelet/dockershim/securitycontext"
...@@ -14,7 +14,6 @@ go_library( ...@@ -14,7 +14,6 @@ go_library(
"docker.go", "docker.go",
"docker_manager.go", "docker_manager.go",
"docker_manager_linux.go", "docker_manager_linux.go",
"exec.go",
"fake_docker_client.go", "fake_docker_client.go",
"instrumented_docker.go", "instrumented_docker.go",
"kube_docker_client.go", "kube_docker_client.go",
...@@ -22,15 +21,11 @@ go_library( ...@@ -22,15 +21,11 @@ go_library(
tags = ["automanaged"], tags = ["automanaged"],
deps = [ deps = [
"//pkg/api/v1:go_default_library", "//pkg/api/v1:go_default_library",
"//pkg/client/unversioned/remotecommand:go_default_library",
"//pkg/credentialprovider:go_default_library", "//pkg/credentialprovider:go_default_library",
"//pkg/kubelet/container:go_default_library", "//pkg/kubelet/container:go_default_library",
"//pkg/kubelet/images:go_default_library", "//pkg/kubelet/images:go_default_library",
"//pkg/kubelet/leaky:go_default_library", "//pkg/kubelet/leaky:go_default_library",
"//pkg/kubelet/metrics:go_default_library", "//pkg/kubelet/metrics:go_default_library",
"//pkg/security/apparmor:go_default_library",
"//pkg/util/exec:go_default_library",
"//pkg/util/term:go_default_library",
"//vendor/github.com/docker/distribution/digest:go_default_library", "//vendor/github.com/docker/distribution/digest:go_default_library",
"//vendor/github.com/docker/distribution/reference:go_default_library", "//vendor/github.com/docker/distribution/reference:go_default_library",
"//vendor/github.com/docker/docker/pkg/jsonmessage:go_default_library", "//vendor/github.com/docker/docker/pkg/jsonmessage:go_default_library",
...@@ -40,7 +35,6 @@ go_library( ...@@ -40,7 +35,6 @@ go_library(
"//vendor/github.com/docker/engine-api/types/container:go_default_library", "//vendor/github.com/docker/engine-api/types/container:go_default_library",
"//vendor/github.com/golang/glog:go_default_library", "//vendor/github.com/golang/glog:go_default_library",
"//vendor/golang.org/x/net/context:go_default_library", "//vendor/golang.org/x/net/context:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
"//vendor/k8s.io/client-go/util/clock:go_default_library", "//vendor/k8s.io/client-go/util/clock:go_default_library",
...@@ -82,9 +76,6 @@ filegroup( ...@@ -82,9 +76,6 @@ filegroup(
filegroup( filegroup(
name = "all-srcs", name = "all-srcs",
srcs = [ srcs = [":package-srcs"],
":package-srcs",
"//pkg/kubelet/dockertools/securitycontext:all-srcs",
],
tags = ["automanaged"], tags = ["automanaged"],
) )
...@@ -364,17 +364,6 @@ func NewMainKubelet(kubeCfg *componentconfig.KubeletConfiguration, kubeDeps *Kub ...@@ -364,17 +364,6 @@ func NewMainKubelet(kubeCfg *componentconfig.KubeletConfiguration, kubeDeps *Kub
KernelMemcgNotification: kubeCfg.ExperimentalKernelMemcgNotification, KernelMemcgNotification: kubeCfg.ExperimentalKernelMemcgNotification,
} }
var dockerExecHandler dockertools.ExecHandler
switch kubeCfg.DockerExecHandlerName {
case "native":
dockerExecHandler = &dockertools.NativeExecHandler{}
case "nsenter":
dockerExecHandler = &dockertools.NsenterExecHandler{}
default:
glog.Warningf("Unknown Docker exec handler %q; defaulting to native", kubeCfg.DockerExecHandlerName)
dockerExecHandler = &dockertools.NativeExecHandler{}
}
serviceIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc}) serviceIndexer := cache.NewIndexer(cache.MetaNamespaceKeyFunc, cache.Indexers{cache.NamespaceIndex: cache.MetaNamespaceIndexFunc})
if kubeDeps.KubeClient != nil { if kubeDeps.KubeClient != nil {
serviceLW := cache.NewListWatchFromClient(kubeDeps.KubeClient.Core().RESTClient(), "services", metav1.NamespaceAll, fields.Everything()) serviceLW := cache.NewListWatchFromClient(kubeDeps.KubeClient.Core().RESTClient(), "services", metav1.NamespaceAll, fields.Everything())
...@@ -556,7 +545,7 @@ func NewMainKubelet(kubeCfg *componentconfig.KubeletConfiguration, kubeDeps *Kub ...@@ -556,7 +545,7 @@ func NewMainKubelet(kubeCfg *componentconfig.KubeletConfiguration, kubeDeps *Kub
// Create and start the CRI shim running as a grpc server. // Create and start the CRI shim running as a grpc server.
streamingConfig := getStreamingConfig(kubeCfg, kubeDeps) streamingConfig := getStreamingConfig(kubeCfg, kubeDeps)
ds, err := dockershim.NewDockerService(klet.dockerClient, kubeCfg.SeccompProfileRoot, kubeCfg.PodInfraContainerImage, ds, err := dockershim.NewDockerService(klet.dockerClient, kubeCfg.SeccompProfileRoot, kubeCfg.PodInfraContainerImage,
streamingConfig, &pluginSettings, kubeCfg.RuntimeCgroups, kubeCfg.CgroupDriver, dockerExecHandler, dockershimRootDir, streamingConfig, &pluginSettings, kubeCfg.RuntimeCgroups, kubeCfg.CgroupDriver, kubeCfg.DockerExecHandlerName, dockershimRootDir,
!kubeCfg.DockerEnableSharedPID) !kubeCfg.DockerEnableSharedPID)
if err != nil { if err != nil {
return nil, err return nil, err
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment