mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-27 13:37:30 +00:00
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.
This commit is contained in:
commit
2a87baba6c
@ -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
|
|||||||
}
|
}
|
||||||
|
|
||||||
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(
|
|||||||
"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(
|
|||||||
"//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(
|
|||||||
"//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(
|
|||||||
"//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(
|
|||||||
"//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(
|
|||||||
"//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.
|
|||||||
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
|
|||||||
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 {
|
|||||||
_, 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 (
|
|||||||
"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
|
|||||||
|
|
||||||
// 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
|
|||||||
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 (
|
|||||||
"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 (
|
|||||||
|
|
||||||
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
|
|||||||
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
|
|||||||
}
|
}
|
||||||
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
|
|||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package dockertools
|
package dockershim
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -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
|
|||||||
// 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
|
|||||||
})
|
})
|
||||||
|
|
||||||
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.
|
|||||||
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 (
|
|||||||
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 (
|
|||||||
|
|
||||||
// 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
|
|||||||
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
|
|||||||
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) {
|
|||||||
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 {
|
|||||||
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)
|
|||||||
}
|
}
|
||||||
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 (
|
|||||||
|
|
||||||
"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 (
|
|||||||
"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.
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
// 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(
|
|||||||
"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(
|
|||||||
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(
|
|||||||
"//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(
|
|||||||
|
|
||||||
filegroup(
|
filegroup(
|
||||||
name = "all-srcs",
|
name = "all-srcs",
|
||||||
srcs = [
|
srcs = [":package-srcs"],
|
||||||
":package-srcs",
|
|
||||||
"//pkg/kubelet/dockertools/securitycontext:all-srcs",
|
|
||||||
],
|
|
||||||
tags = ["automanaged"],
|
tags = ["automanaged"],
|
||||||
)
|
)
|
||||||
|
@ -16,38 +16,8 @@ limitations under the License.
|
|||||||
|
|
||||||
package dockertools
|
package dockertools
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/md5"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"path/filepath"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
dockertypes "github.com/docker/engine-api/types"
|
|
||||||
"github.com/golang/glog"
|
|
||||||
|
|
||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
|
||||||
"k8s.io/kubernetes/pkg/api/v1"
|
|
||||||
"k8s.io/kubernetes/pkg/client/unversioned/remotecommand"
|
|
||||||
kubecontainer "k8s.io/kubernetes/pkg/kubelet/container"
|
|
||||||
"k8s.io/kubernetes/pkg/security/apparmor"
|
|
||||||
)
|
|
||||||
|
|
||||||
const (
|
const (
|
||||||
DockerType = "docker"
|
DockerType = "docker"
|
||||||
dockerDefaultLoggingDriver = "json-file"
|
|
||||||
|
|
||||||
// Docker changed the API for specifying options in v1.11
|
|
||||||
SecurityOptSeparatorChangeVersion = "1.23.0" // Corresponds to docker 1.11.x
|
|
||||||
SecurityOptSeparatorOld = ':'
|
|
||||||
SecurityOptSeparatorNew = '='
|
|
||||||
|
|
||||||
// https://docs.docker.com/engine/reference/api/docker_remote_api/
|
// https://docs.docker.com/engine/reference/api/docker_remote_api/
|
||||||
// docker version should be at least 1.10.x
|
// docker version should be at least 1.10.x
|
||||||
@ -56,304 +26,4 @@ const (
|
|||||||
statusRunningPrefix = "Up"
|
statusRunningPrefix = "Up"
|
||||||
statusExitedPrefix = "Exited"
|
statusExitedPrefix = "Exited"
|
||||||
statusCreatedPrefix = "Created"
|
statusCreatedPrefix = "Created"
|
||||||
|
|
||||||
ndotsDNSOption = "options ndots:5\n"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
|
||||||
defaultSeccompOpt = []dockerOpt{{"seccomp", "unconfined", ""}}
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetImageRef returns the image digest if exists, or else returns the image ID.
|
|
||||||
// It is exported for reusing in dockershim.
|
|
||||||
func GetImageRef(client 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// Temporarily export this function to share with dockershim.
|
|
||||||
// TODO: clean this up.
|
|
||||||
func GetContainerLogs(client DockerInterface, pod *v1.Pod, containerID kubecontainer.ContainerID, logOptions *v1.PodLogOptions, stdout, stderr io.Writer, rawTerm bool) error {
|
|
||||||
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 := StreamOptions{
|
|
||||||
OutputStream: stdout,
|
|
||||||
ErrorStream: stderr,
|
|
||||||
RawTerminal: rawTerm,
|
|
||||||
}
|
|
||||||
return client.Logs(containerID.ID, opts, sopts)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Temporarily export this function to share with dockershim.
|
|
||||||
// TODO: clean this up.
|
|
||||||
func AttachContainer(client 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 := StreamOptions{
|
|
||||||
InputStream: stdin,
|
|
||||||
OutputStream: stdout,
|
|
||||||
ErrorStream: stderr,
|
|
||||||
RawTerminal: tty,
|
|
||||||
}
|
|
||||||
return client.AttachToContainer(containerID, opts, sopts)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Temporarily export this function to share with dockershim.
|
|
||||||
func PortForward(client 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// Temporarily export this function to share with dockershim.
|
|
||||||
// TODO: clean this up.
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// Temporarily export this function to share with dockershim.
|
|
||||||
// TODO: clean this up.
|
|
||||||
func GetSeccompOpts(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
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetUserFromImageUser splits the user out of an user:group string.
|
|
||||||
func GetUserFromImageUser(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
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
// RewriteResolvFile rewrites resolv.conf file generated by docker.
|
|
||||||
// Exported for reusing in dockershim.
|
|
||||||
func RewriteResolvFile(resolvFilePath string, dns []string, dnsSearch []string, useClusterFirstPolicy bool) error {
|
|
||||||
if len(resolvFilePath) == 0 {
|
|
||||||
glog.Errorf("ResolvConfPath is empty.")
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if _, err := os.Stat(resolvFilePath); os.IsNotExist(err) {
|
|
||||||
return fmt.Errorf("ResolvConfPath %q does not exist", resolvFilePath)
|
|
||||||
}
|
|
||||||
|
|
||||||
var resolvFileContent []string
|
|
||||||
|
|
||||||
for _, srv := range dns {
|
|
||||||
resolvFileContent = append(resolvFileContent, "nameserver "+srv)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(dnsSearch) > 0 {
|
|
||||||
resolvFileContent = append(resolvFileContent, "search "+strings.Join(dnsSearch, " "))
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(resolvFileContent) > 0 {
|
|
||||||
if useClusterFirstPolicy {
|
|
||||||
resolvFileContent = append(resolvFileContent, ndotsDNSOption)
|
|
||||||
}
|
|
||||||
|
|
||||||
resolvFileContentStr := strings.Join(resolvFileContent, "\n")
|
|
||||||
resolvFileContentStr += "\n"
|
|
||||||
|
|
||||||
glog.V(4).Infof("Will attempt to re-write config file %s with: \n%s", resolvFilePath, resolvFileContent)
|
|
||||||
if err := rewriteFile(resolvFilePath, resolvFileContentStr); err != nil {
|
|
||||||
glog.Errorf("resolv.conf could not be updated: %v", err)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func rewriteFile(filePath, stringToWrite string) error {
|
|
||||||
f, err := os.OpenFile(filePath, os.O_TRUNC|os.O_WRONLY, 0644)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
_, err = f.WriteString(stringToWrite)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
@ -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
|
|||||||
// 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
|
||||||
|
Loading…
Reference in New Issue
Block a user