mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-30 23:15:14 +00:00
Move exported constants/functions from dockertools to dockershim
Previously we exported many constants and functions in dockertools to share with the dockershim package. This change moves such constants/functions to dockershim and unexport them. This change involves only mechnical changes and should not have any functional impact.
This commit is contained in:
parent
93ecaf6812
commit
b209f47562
@ -17,6 +17,8 @@ limitations under the License.
|
||||
package dockershim
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
dockertypes "github.com/docker/engine-api/types"
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockertools"
|
||||
@ -80,7 +82,7 @@ func (ds *dockerService) PullImage(image *runtimeapi.ImageSpec, auth *runtimeapi
|
||||
return "", err
|
||||
}
|
||||
|
||||
return dockertools.GetImageRef(ds.client, image.Image)
|
||||
return getImageRef(ds.client, image.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})
|
||||
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"
|
||||
"io"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"github.com/blang/semver"
|
||||
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/apis/componentconfig"
|
||||
internalapi "k8s.io/kubernetes/pkg/kubelet/api"
|
||||
@ -489,7 +491,32 @@ func (d *dockerLegacyService) GetContainerLogs(pod *v1.Pod, containerID kubecont
|
||||
if err != nil {
|
||||
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.
|
||||
|
@ -21,11 +21,15 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"os/exec"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
dockertypes "github.com/docker/engine-api/types"
|
||||
"github.com/golang/glog"
|
||||
"k8s.io/kubernetes/pkg/client/unversioned/remotecommand"
|
||||
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/server/streaming"
|
||||
"k8s.io/kubernetes/pkg/kubelet/util/ioutils"
|
||||
@ -57,14 +61,14 @@ func (r *streamingRuntime) Attach(containerID string, in io.Reader, out, errw io
|
||||
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 {
|
||||
if port < 0 || port > math.MaxUint16 {
|
||||
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.
|
||||
@ -128,3 +132,83 @@ func checkContainerStatus(client dockertools.DockerInterface, containerID string
|
||||
}
|
||||
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
|
||||
}
|
||||
|
@ -17,7 +17,12 @@ limitations under the License.
|
||||
package dockershim
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
@ -28,14 +33,21 @@ import (
|
||||
dockernat "github.com/docker/go-connections/nat"
|
||||
"github.com/golang/glog"
|
||||
|
||||
"k8s.io/kubernetes/pkg/api/v1"
|
||||
v1helper "k8s.io/kubernetes/pkg/api/v1/helper"
|
||||
runtimeapi "k8s.io/kubernetes/pkg/kubelet/api/v1alpha1/runtime"
|
||||
"k8s.io/kubernetes/pkg/kubelet/dockertools"
|
||||
"k8s.io/kubernetes/pkg/kubelet/types"
|
||||
"k8s.io/kubernetes/pkg/security/apparmor"
|
||||
)
|
||||
|
||||
const (
|
||||
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 (
|
||||
@ -43,7 +55,9 @@ var (
|
||||
|
||||
// Docker changes the security option separator from ':' to '=' in the 1.23
|
||||
// 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
|
||||
@ -181,17 +195,57 @@ func makePortsAndBindings(pm []*runtimeapi.PortMapping) (map[dockernat.Port]stru
|
||||
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
|
||||
// config, currently from sandbox annotations.
|
||||
// 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) {
|
||||
seccompOpts, err := dockertools.GetSeccompOpts(sandboxConfig.GetAnnotations(), containerName, seccompProfileRoot)
|
||||
seccompOpts, err := getSeccompDockerOpts(sandboxConfig.GetAnnotations(), containerName, seccompProfileRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmtOpts := dockertools.FmtDockerOpts(seccompOpts, separator)
|
||||
return fmtOpts, nil
|
||||
return fmtDockerOpts(seccompOpts, separator), nil
|
||||
}
|
||||
|
||||
// getApparmorSecurityOpts gets apparmor options from container config.
|
||||
@ -200,12 +254,12 @@ func getApparmorSecurityOpts(sc *runtimeapi.LinuxContainerSecurityContext, separ
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
appArmorOpts, err := dockertools.GetAppArmorOpts(sc.ApparmorProfile)
|
||||
appArmorOpts, err := getAppArmorOpts(sc.ApparmorProfile)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fmtOpts := dockertools.FmtDockerOpts(appArmorOpts, separator)
|
||||
fmtOpts := fmtDockerOpts(appArmorOpts, separator)
|
||||
return fmtOpts, nil
|
||||
}
|
||||
|
||||
@ -258,10 +312,23 @@ func (f *dockerFilter) AddLabel(key, value string) {
|
||||
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.
|
||||
// If user is numeric, it will be treated as uid; or else, it is treated as user name.
|
||||
func getUserFromImageUser(imageUser string) (*int64, string) {
|
||||
user := dockertools.GetUserFromImageUser(imageUser)
|
||||
user := parseUserFromImageUser(imageUser)
|
||||
// return both nil if user is not specified in the image.
|
||||
if user == "" {
|
||||
return nil, ""
|
||||
@ -321,9 +388,9 @@ func getSecurityOptSeparator(v *semver.Version) rune {
|
||||
case -1:
|
||||
// Current version is less than the API change version; use the old
|
||||
// separator.
|
||||
return dockertools.SecurityOptSeparatorOld
|
||||
return securityOptSeparatorOld
|
||||
default:
|
||||
return dockertools.SecurityOptSeparatorNew
|
||||
return securityOptSeparatorNew
|
||||
}
|
||||
}
|
||||
|
||||
@ -342,3 +409,35 @@ func ensureSandboxImageExists(client dockertools.DockerInterface, image string)
|
||||
}
|
||||
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
|
||||
}
|
||||
|
@ -16,38 +16,8 @@ limitations under the License.
|
||||
|
||||
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 (
|
||||
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 = '='
|
||||
DockerType = "docker"
|
||||
|
||||
// https://docs.docker.com/engine/reference/api/docker_remote_api/
|
||||
// docker version should be at least 1.10.x
|
||||
@ -56,284 +26,4 @@ const (
|
||||
statusRunningPrefix = "Up"
|
||||
statusExitedPrefix = "Exited"
|
||||
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
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user