Merged master, fixed merge conflict

This commit is contained in:
Priya Wadhwa 2018-05-08 09:48:36 -07:00
commit 347ce66a9b
No known key found for this signature in database
GPG Key ID: 0D0DAFD8F7AA73AE
22 changed files with 616 additions and 50 deletions

3
Gopkg.lock generated
View File

@ -45,6 +45,7 @@
"pkg/longpath", "pkg/longpath",
"pkg/mount", "pkg/mount",
"pkg/pools", "pkg/pools",
"pkg/signal",
"pkg/system" "pkg/system"
] ]
revision = "b1a1234c60cf87048814aa37da523b03a7b0d344" revision = "b1a1234c60cf87048814aa37da523b03a7b0d344"
@ -332,6 +333,6 @@
[solve-meta] [solve-meta]
analyzer-name = "dep" analyzer-name = "dep"
analyzer-version = 1 analyzer-version = 1
inputs-digest = "9ed51a63daab2be9e4864e4d97d90ccc0f540d076f891b80f01bd3f161c9ef7a" inputs-digest = "18d970ab8a3487013ef13e0aed686929d30bcebc1b9adaa329e9db414189fcf6"
solver-name = "gps-cdcl" solver-name = "gps-cdcl"
solver-version = 1 solver-version = 1

View File

@ -12,6 +12,7 @@ Please let us know if you have any feature requests or find any bugs!
- [Kaniko](#kaniko) - [Kaniko](#kaniko)
- [How does kaniko work?](#how-does-kaniko-work) - [How does kaniko work?](#how-does-kaniko-work)
- [Known Issues](#known-issues) - [Known Issues](#known-issues)
- [Demo](#demo)
- [Development](#development) - [Development](#development)
- [kaniko Build Contexts](#kaniko-build-contexts) - [kaniko Build Contexts](#kaniko-build-contexts)
- [Running kaniko in a Kubernetes cluster](#running-kaniko-in-a-kubernetes-cluster) - [Running kaniko in a Kubernetes cluster](#running-kaniko-in-a-kubernetes-cluster)
@ -35,13 +36,16 @@ After each command, we append a layer of changed files to the base image (if the
The majority of Dockerfile commands can be executed with kaniko, but we're still working on supporting the following commands: The majority of Dockerfile commands can be executed with kaniko, but we're still working on supporting the following commands:
* HEALTHCHECK * HEALTHCHECK
* STOPSIGNAL
* ARG * ARG
Multi-Stage Dockerfiles are also unsupported currently, but will be ready soon. Multi-Stage Dockerfiles are also unsupported currently, but will be ready soon.
kaniko also does not support building Windows containers. kaniko also does not support building Windows containers.
## Demo
![Demo](/docs/demo.gif)
## Development ## Development
### kaniko Build Contexts ### kaniko Build Contexts
kaniko supports local directories and GCS buckets as build contexts. To specify a local directory, pass in the `--context` flag as an argument to the executor image. kaniko supports local directories and GCS buckets as build contexts. To specify a local directory, pass in the `--context` flag as an argument to the executor image.

View File

@ -18,11 +18,14 @@ FROM golang:1.10
WORKDIR /go/src/github.com/GoogleContainerTools/kaniko WORKDIR /go/src/github.com/GoogleContainerTools/kaniko
COPY . . COPY . .
RUN make RUN make
WORKDIR /usr/local/bin
ADD https://github.com/GoogleCloudPlatform/docker-credential-gcr/releases/download/v1.4.3-static/docker-credential-gcr_linux_amd64-1.4.3.tar.gz .
RUN tar -xvzf /usr/local/bin/docker-credential-gcr_linux_amd64-1.4.3.tar.gz
FROM scratch FROM scratch
COPY --from=0 /go/src/github.com/GoogleContainerTools/kaniko/out/executor /kaniko/executor COPY --from=0 /go/src/github.com/GoogleContainerTools/kaniko/out/executor /kaniko/executor
COPY --from=0 /usr/local/bin/docker-credential-gcr /usr/local/bin/docker-credential-gcr
COPY files/ca-certificates.crt /kaniko/ssl/certs/ COPY files/ca-certificates.crt /kaniko/ssl/certs/
COPY files/docker-credential-gcr /usr/local/bin/
COPY files/config.json /root/.docker/ COPY files/config.json /root/.docker/
RUN ["docker-credential-gcr", "config", "--token-source=env"] RUN ["docker-credential-gcr", "config", "--token-source=env"]
ENV HOME /root ENV HOME /root

BIN
docs/demo.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 MiB

Binary file not shown.

View File

@ -61,6 +61,8 @@ func GetCommand(cmd instructions.Command, buildcontext string) (DockerCommand, e
return &OnBuildCommand{cmd: c}, nil return &OnBuildCommand{cmd: c}, nil
case *instructions.VolumeCommand: case *instructions.VolumeCommand:
return &VolumeCommand{cmd: c}, nil return &VolumeCommand{cmd: c}, nil
case *instructions.StopSignalCommand:
return &StopSignalCommand{cmd: c}, nil
case *instructions.MaintainerCommand: case *instructions.MaintainerCommand:
logrus.Warnf("%s is deprecated, skipping", cmd.Name()) logrus.Warnf("%s is deprecated, skipping", cmd.Name())
return nil, nil return nil, nil

View File

@ -18,14 +18,13 @@ package commands
import ( import (
"github.com/GoogleContainerTools/kaniko/pkg/constants" "github.com/GoogleContainerTools/kaniko/pkg/constants"
"os"
"path/filepath"
"strings"
"github.com/GoogleContainerTools/kaniko/pkg/util" "github.com/GoogleContainerTools/kaniko/pkg/util"
"github.com/docker/docker/builder/dockerfile/instructions" "github.com/docker/docker/builder/dockerfile/instructions"
"github.com/google/go-containerregistry/v1" "github.com/google/go-containerregistry/v1"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"os"
"path/filepath"
"strings"
) )
type CopyCommand struct { type CopyCommand struct {
@ -43,7 +42,7 @@ func (c *CopyCommand) ExecuteCommand(config *v1.Config) error {
// Resolve from // Resolve from
if c.cmd.From != "" { if c.cmd.From != "" {
c.buildcontext = filepath.Join(constants.BuildContextDir, c.cmd.From) c.buildcontext = filepath.Join(constants.KanikoDir, c.cmd.From)
} }
// First, resolve any environment replacement // First, resolve any environment replacement
resolvedEnvs, err := util.ResolveEnvironmentReplacementList(c.cmd.SourcesAndDest, config.Env, true) resolvedEnvs, err := util.ResolveEnvironmentReplacementList(c.cmd.SourcesAndDest, config.Env, true)
@ -67,7 +66,7 @@ func (c *CopyCommand) ExecuteCommand(config *v1.Config) error {
if cwd == "" { if cwd == "" {
cwd = constants.RootDir cwd = constants.RootDir
} }
destPath, err := util.DestinationFilepath(src, dest, config.WorkingDir) destPath, err := util.DestinationFilepath(src, dest, cwd)
if err != nil { if err != nil {
return err return err
} }

View File

@ -0,0 +1,65 @@
/*
Copyright 2018 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package commands
import (
"strings"
"github.com/GoogleContainerTools/kaniko/pkg/util"
"github.com/docker/docker/builder/dockerfile/instructions"
"github.com/docker/docker/pkg/signal"
"github.com/google/go-containerregistry/v1"
"github.com/sirupsen/logrus"
)
type StopSignalCommand struct {
cmd *instructions.StopSignalCommand
}
// ExecuteCommand handles command processing similar to CMD and RUN,
func (s *StopSignalCommand) ExecuteCommand(config *v1.Config) error {
logrus.Info("cmd: STOPSIGNAL")
// resolve possible environment variables
resolvedEnvs, err := util.ResolveEnvironmentReplacementList([]string{s.cmd.Signal}, config.Env, false)
if err != nil {
return err
}
stopsignal := resolvedEnvs[0]
// validate stopsignal
_, err = signal.ParseSignal(stopsignal)
if err != nil {
return err
}
logrus.Infof("Replacing StopSignal in config with %v", stopsignal)
config.StopSignal = stopsignal
return nil
}
// FilesToSnapshot returns an empty array since this is a metadata command
func (s *StopSignalCommand) FilesToSnapshot() []string {
return []string{}
}
// CreatedBy returns some information about the command for the image config history
func (s *StopSignalCommand) CreatedBy() string {
entrypoint := []string{"STOPSIGNAL"}
return strings.Join(append(entrypoint, s.cmd.Signal), " ")
}

View File

@ -0,0 +1,60 @@
/*
Copyright 2018 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package commands
import (
"testing"
"github.com/GoogleContainerTools/kaniko/testutil"
"github.com/docker/docker/builder/dockerfile/instructions"
"github.com/google/go-containerregistry/v1"
)
var stopsignalTests = []struct {
signal string
expectedSignal string
}{
{
signal: "SIGKILL",
expectedSignal: "SIGKILL",
},
{
signal: "${STOPSIG}",
expectedSignal: "SIGKILL",
},
{
signal: "1",
expectedSignal: "1",
},
}
func TestStopsignalExecuteCmd(t *testing.T) {
cfg := &v1.Config{
StopSignal: "",
Env: []string{"STOPSIG=SIGKILL"},
}
for _, test := range stopsignalTests {
cmd := StopSignalCommand{
&instructions.StopSignalCommand{
Signal: test.signal,
},
}
err := cmd.ExecuteCommand(cfg)
testutil.CheckErrorAndDeepEqual(t, false, err, test.expectedSignal, cfg.StopSignal)
}
}

View File

@ -141,7 +141,8 @@ func DoBuild(dockerfilePath, srcContext, snapshotMode string) (name.Reference, v
mutate.Addendum{ mutate.Addendum{
Layer: layer, Layer: layer,
History: v1.History{ History: v1.History{
Author: constants.Author, Author: constants.Author,
CreatedBy: dockerCommand.CreatedBy(),
}, },
}, },
) )
@ -192,7 +193,7 @@ func saveStageDependencies(index int, stages []instructions.Stage) error {
} }
// Then, create the directory they will exist in // Then, create the directory they will exist in
i := strconv.Itoa(index) i := strconv.Itoa(index)
dependencyDir := filepath.Join(constants.BuildContextDir, i) dependencyDir := filepath.Join(constants.KanikoDir, i)
if err := os.MkdirAll(dependencyDir, 0755); err != nil { if err := os.MkdirAll(dependencyDir, 0755); err != nil {
return err return err
} }

View File

@ -19,10 +19,8 @@ package snapshot
import ( import (
"archive/tar" "archive/tar"
"bytes" "bytes"
"github.com/GoogleContainerTools/kaniko/pkg/util" "github.com/GoogleContainerTools/kaniko/pkg/util"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"io" "io"
"io/ioutil" "io/ioutil"
"os" "os"
@ -52,12 +50,14 @@ func (s *Snapshotter) Init() error {
// TakeSnapshot takes a snapshot of the filesystem, avoiding directories in the whitelist, and creates // TakeSnapshot takes a snapshot of the filesystem, avoiding directories in the whitelist, and creates
// a tarball of the changed files. Return contents of the tarball, and whether or not any files were changed // a tarball of the changed files. Return contents of the tarball, and whether or not any files were changed
func (s *Snapshotter) TakeSnapshot(files []string) ([]byte, error) { func (s *Snapshotter) TakeSnapshot(files []string) ([]byte, error) {
if files != nil {
return s.TakeSnapshotOfFiles(files)
}
logrus.Info("Taking snapshot of full filesystem...")
buf := bytes.NewBuffer([]byte{}) buf := bytes.NewBuffer([]byte{})
filesAdded, err := s.snapShotFS(buf) var filesAdded bool
var err error
if files == nil {
filesAdded, err = s.snapShotFS(buf)
} else {
filesAdded, err = s.snapshotFiles(buf, files)
}
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -68,45 +68,57 @@ func (s *Snapshotter) TakeSnapshot(files []string) ([]byte, error) {
return contents, err return contents, err
} }
// TakeSnapshotOfFiles takes a snapshot of specific files // snapshotFiles takes a snapshot of specific files
// Used for ADD/COPY commands, when we know which files have changed // Used for ADD/COPY commands, when we know which files have changed
func (s *Snapshotter) TakeSnapshotOfFiles(files []string) ([]byte, error) { func (s *Snapshotter) snapshotFiles(f io.Writer, files []string) (bool, error) {
logrus.Infof("Taking snapshot of files %v...", files) s.hardlinks = map[uint64]string{}
s.l.Snapshot() s.l.Snapshot()
if len(files) == 0 { if len(files) == 0 {
logrus.Info("No files changed in this command, skipping snapshotting.") logrus.Info("No files changed in this command, skipping snapshotting.")
return nil, nil return false, nil
} }
buf := bytes.NewBuffer([]byte{}) logrus.Infof("Taking snapshot of files %v...", files)
w := tar.NewWriter(buf) snapshottedFiles := make(map[string]bool)
defer w.Close()
filesAdded := false
for _, file := range files { for _, file := range files {
info, err := os.Lstat(file) parentDirs := util.ParentDirectories(file)
if err != nil { files = append(parentDirs, files...)
return nil, err }
filesAdded := false
w := tar.NewWriter(f)
defer w.Close()
// Now create the tar.
for _, file := range files {
file = filepath.Clean(file)
if val, ok := snapshottedFiles[file]; ok && val {
continue
} }
if util.PathInWhitelist(file, s.directory) { if util.PathInWhitelist(file, s.directory) {
logrus.Debugf("Not adding %s to layer, as it is whitelisted", file) logrus.Debugf("Not adding %s to layer, as it's whitelisted", file)
continue continue
} }
snapshottedFiles[file] = true
info, err := os.Lstat(file)
if err != nil {
return false, err
}
// Only add to the tar if we add it to the layeredmap. // Only add to the tar if we add it to the layeredmap.
maybeAdd, err := s.l.MaybeAdd(file) maybeAdd, err := s.l.MaybeAdd(file)
if err != nil { if err != nil {
return nil, err return false, err
} }
if maybeAdd { if maybeAdd {
filesAdded = true filesAdded = true
util.AddToTar(file, info, s.hardlinks, w) if err := util.AddToTar(file, info, s.hardlinks, w); err != nil {
return false, err
}
} }
} }
if !filesAdded { return filesAdded, nil
return nil, nil
}
return ioutil.ReadAll(buf)
} }
func (s *Snapshotter) snapShotFS(f io.Writer) (bool, error) { func (s *Snapshotter) snapShotFS(f io.Writer) (bool, error) {
logrus.Info("Taking snapshot of full filesystem...")
s.hardlinks = map[uint64]string{} s.hardlinks = map[uint64]string{}
s.l.Snapshot() s.l.Snapshot()
existingPaths := s.l.GetFlattenedPathsForWhiteOut() existingPaths := s.l.GetFlattenedPathsForWhiteOut()

View File

@ -149,30 +149,23 @@ func TestSnapshotFiles(t *testing.T) {
if err != nil { if err != nil {
t.Fatal(err) t.Fatal(err)
} }
expectedContents := map[string]string{ expectedFiles := []string{"/tmp", filepath.Join(testDir, "foo")}
filepath.Join(testDir, "foo"): "newbaz1",
}
// Check contents of the snapshot, make sure contents is equivalent to snapshotFiles // Check contents of the snapshot, make sure contents is equivalent to snapshotFiles
reader := bytes.NewReader(contents) reader := bytes.NewReader(contents)
tr := tar.NewReader(reader) tr := tar.NewReader(reader)
numFiles := 0 var actualFiles []string
for { for {
hdr, err := tr.Next() hdr, err := tr.Next()
if err == io.EOF { if err == io.EOF {
break break
} }
numFiles = numFiles + 1 if err != nil {
if _, isFile := expectedContents[hdr.Name]; !isFile { t.Fatal(err)
t.Fatalf("File %s unexpectedly in tar", hdr.Name)
}
contents, _ := ioutil.ReadAll(tr)
if string(contents) != expectedContents[hdr.Name] {
t.Fatalf("Contents of %s incorrect, expected: %s, actual: %s", hdr.Name, expectedContents[hdr.Name], string(contents))
} }
actualFiles = append(actualFiles, hdr.Name)
} }
if numFiles != 1 { testutil.CheckErrorAndDeepEqual(t, false, nil, expectedFiles, actualFiles)
t.Fatalf("%s was not added.", filepath.Join(testDir, "foo"))
}
} }
func TestEmptySnapshot(t *testing.T) { func TestEmptySnapshot(t *testing.T) {

View File

@ -326,6 +326,23 @@ func Files(root string) ([]string, error) {
return files, err return files, err
} }
// ParentDirectories returns a list of paths to all parent directories
// Ex. /some/temp/dir -> [/some, /some/temp, /some/temp/dir]
func ParentDirectories(path string) []string {
path = filepath.Clean(path)
dirs := strings.Split(path, "/")
dirPath := constants.RootDir
var paths []string
for index, dir := range dirs {
if dir == "" || index == (len(dirs)-1) {
continue
}
dirPath = filepath.Join(dirPath, dir)
paths = append(paths, dirPath)
}
return paths
}
// FilepathExists returns true if the path exists // FilepathExists returns true if the path exists
func FilepathExists(path string) bool { func FilepathExists(path string) bool {
_, err := os.Lstat(path) _, err := os.Lstat(path)

View File

@ -132,6 +132,35 @@ func Test_RelativeFiles(t *testing.T) {
} }
} }
func Test_ParentDirectories(t *testing.T) {
tests := []struct {
name string
path string
expected []string
}{
{
name: "regular path",
path: "/path/to/dir",
expected: []string{
"/path",
"/path/to",
},
},
{
name: "current directory",
path: ".",
expected: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
actual := ParentDirectories(tt.path)
testutil.CheckErrorAndDeepEqual(t, false, nil, tt.expected, actual)
})
}
}
func Test_checkWhiteouts(t *testing.T) { func Test_checkWhiteouts(t *testing.T) {
type args struct { type args struct {
path string path string

54
vendor/github.com/docker/docker/pkg/signal/signal.go generated vendored Normal file
View File

@ -0,0 +1,54 @@
// Package signal provides helper functions for dealing with signals across
// various operating systems.
package signal // import "github.com/docker/docker/pkg/signal"
import (
"fmt"
"os"
"os/signal"
"strconv"
"strings"
"syscall"
)
// CatchAll catches all signals and relays them to the specified channel.
func CatchAll(sigc chan os.Signal) {
handledSigs := []os.Signal{}
for _, s := range SignalMap {
handledSigs = append(handledSigs, s)
}
signal.Notify(sigc, handledSigs...)
}
// StopCatch stops catching the signals and closes the specified channel.
func StopCatch(sigc chan os.Signal) {
signal.Stop(sigc)
close(sigc)
}
// ParseSignal translates a string to a valid syscall signal.
// It returns an error if the signal map doesn't include the given signal.
func ParseSignal(rawSignal string) (syscall.Signal, error) {
s, err := strconv.Atoi(rawSignal)
if err == nil {
if s == 0 {
return -1, fmt.Errorf("Invalid signal: %s", rawSignal)
}
return syscall.Signal(s), nil
}
signal, ok := SignalMap[strings.TrimPrefix(strings.ToUpper(rawSignal), "SIG")]
if !ok {
return -1, fmt.Errorf("Invalid signal: %s", rawSignal)
}
return signal, nil
}
// ValidSignalForPlatform returns true if a signal is valid on the platform
func ValidSignalForPlatform(sig syscall.Signal) bool {
for _, v := range SignalMap {
if v == sig {
return true
}
}
return false
}

View File

@ -0,0 +1,41 @@
package signal // import "github.com/docker/docker/pkg/signal"
import (
"syscall"
)
// SignalMap is a map of Darwin signals.
var SignalMap = map[string]syscall.Signal{
"ABRT": syscall.SIGABRT,
"ALRM": syscall.SIGALRM,
"BUG": syscall.SIGBUS,
"CHLD": syscall.SIGCHLD,
"CONT": syscall.SIGCONT,
"EMT": syscall.SIGEMT,
"FPE": syscall.SIGFPE,
"HUP": syscall.SIGHUP,
"ILL": syscall.SIGILL,
"INFO": syscall.SIGINFO,
"INT": syscall.SIGINT,
"IO": syscall.SIGIO,
"IOT": syscall.SIGIOT,
"KILL": syscall.SIGKILL,
"PIPE": syscall.SIGPIPE,
"PROF": syscall.SIGPROF,
"QUIT": syscall.SIGQUIT,
"SEGV": syscall.SIGSEGV,
"STOP": syscall.SIGSTOP,
"SYS": syscall.SIGSYS,
"TERM": syscall.SIGTERM,
"TRAP": syscall.SIGTRAP,
"TSTP": syscall.SIGTSTP,
"TTIN": syscall.SIGTTIN,
"TTOU": syscall.SIGTTOU,
"URG": syscall.SIGURG,
"USR1": syscall.SIGUSR1,
"USR2": syscall.SIGUSR2,
"VTALRM": syscall.SIGVTALRM,
"WINCH": syscall.SIGWINCH,
"XCPU": syscall.SIGXCPU,
"XFSZ": syscall.SIGXFSZ,
}

View File

@ -0,0 +1,43 @@
package signal // import "github.com/docker/docker/pkg/signal"
import (
"syscall"
)
// SignalMap is a map of FreeBSD signals.
var SignalMap = map[string]syscall.Signal{
"ABRT": syscall.SIGABRT,
"ALRM": syscall.SIGALRM,
"BUF": syscall.SIGBUS,
"CHLD": syscall.SIGCHLD,
"CONT": syscall.SIGCONT,
"EMT": syscall.SIGEMT,
"FPE": syscall.SIGFPE,
"HUP": syscall.SIGHUP,
"ILL": syscall.SIGILL,
"INFO": syscall.SIGINFO,
"INT": syscall.SIGINT,
"IO": syscall.SIGIO,
"IOT": syscall.SIGIOT,
"KILL": syscall.SIGKILL,
"LWP": syscall.SIGLWP,
"PIPE": syscall.SIGPIPE,
"PROF": syscall.SIGPROF,
"QUIT": syscall.SIGQUIT,
"SEGV": syscall.SIGSEGV,
"STOP": syscall.SIGSTOP,
"SYS": syscall.SIGSYS,
"TERM": syscall.SIGTERM,
"THR": syscall.SIGTHR,
"TRAP": syscall.SIGTRAP,
"TSTP": syscall.SIGTSTP,
"TTIN": syscall.SIGTTIN,
"TTOU": syscall.SIGTTOU,
"URG": syscall.SIGURG,
"USR1": syscall.SIGUSR1,
"USR2": syscall.SIGUSR2,
"VTALRM": syscall.SIGVTALRM,
"WINCH": syscall.SIGWINCH,
"XCPU": syscall.SIGXCPU,
"XFSZ": syscall.SIGXFSZ,
}

View File

@ -0,0 +1,81 @@
package signal // import "github.com/docker/docker/pkg/signal"
import (
"syscall"
"golang.org/x/sys/unix"
)
const (
sigrtmin = 34
sigrtmax = 64
)
// SignalMap is a map of Linux signals.
var SignalMap = map[string]syscall.Signal{
"ABRT": unix.SIGABRT,
"ALRM": unix.SIGALRM,
"BUS": unix.SIGBUS,
"CHLD": unix.SIGCHLD,
"CLD": unix.SIGCLD,
"CONT": unix.SIGCONT,
"FPE": unix.SIGFPE,
"HUP": unix.SIGHUP,
"ILL": unix.SIGILL,
"INT": unix.SIGINT,
"IO": unix.SIGIO,
"IOT": unix.SIGIOT,
"KILL": unix.SIGKILL,
"PIPE": unix.SIGPIPE,
"POLL": unix.SIGPOLL,
"PROF": unix.SIGPROF,
"PWR": unix.SIGPWR,
"QUIT": unix.SIGQUIT,
"SEGV": unix.SIGSEGV,
"STKFLT": unix.SIGSTKFLT,
"STOP": unix.SIGSTOP,
"SYS": unix.SIGSYS,
"TERM": unix.SIGTERM,
"TRAP": unix.SIGTRAP,
"TSTP": unix.SIGTSTP,
"TTIN": unix.SIGTTIN,
"TTOU": unix.SIGTTOU,
"URG": unix.SIGURG,
"USR1": unix.SIGUSR1,
"USR2": unix.SIGUSR2,
"VTALRM": unix.SIGVTALRM,
"WINCH": unix.SIGWINCH,
"XCPU": unix.SIGXCPU,
"XFSZ": unix.SIGXFSZ,
"RTMIN": sigrtmin,
"RTMIN+1": sigrtmin + 1,
"RTMIN+2": sigrtmin + 2,
"RTMIN+3": sigrtmin + 3,
"RTMIN+4": sigrtmin + 4,
"RTMIN+5": sigrtmin + 5,
"RTMIN+6": sigrtmin + 6,
"RTMIN+7": sigrtmin + 7,
"RTMIN+8": sigrtmin + 8,
"RTMIN+9": sigrtmin + 9,
"RTMIN+10": sigrtmin + 10,
"RTMIN+11": sigrtmin + 11,
"RTMIN+12": sigrtmin + 12,
"RTMIN+13": sigrtmin + 13,
"RTMIN+14": sigrtmin + 14,
"RTMIN+15": sigrtmin + 15,
"RTMAX-14": sigrtmax - 14,
"RTMAX-13": sigrtmax - 13,
"RTMAX-12": sigrtmax - 12,
"RTMAX-11": sigrtmax - 11,
"RTMAX-10": sigrtmax - 10,
"RTMAX-9": sigrtmax - 9,
"RTMAX-8": sigrtmax - 8,
"RTMAX-7": sigrtmax - 7,
"RTMAX-6": sigrtmax - 6,
"RTMAX-5": sigrtmax - 5,
"RTMAX-4": sigrtmax - 4,
"RTMAX-3": sigrtmax - 3,
"RTMAX-2": sigrtmax - 2,
"RTMAX-1": sigrtmax - 1,
"RTMAX": sigrtmax,
}

View File

@ -0,0 +1,21 @@
// +build !windows
package signal // import "github.com/docker/docker/pkg/signal"
import (
"syscall"
)
// Signals used in cli/command (no windows equivalent, use
// invalid signals so they don't get handled)
const (
// SIGCHLD is a signal sent to a process when a child process terminates, is interrupted, or resumes after being interrupted.
SIGCHLD = syscall.SIGCHLD
// SIGWINCH is a signal sent to a process when its controlling terminal changes its size
SIGWINCH = syscall.SIGWINCH
// SIGPIPE is a signal sent to a process when a pipe is written to before the other end is open for reading
SIGPIPE = syscall.SIGPIPE
// DefaultStopSignal is the syscall signal used to stop a container in unix systems.
DefaultStopSignal = "SIGTERM"
)

View File

@ -0,0 +1,10 @@
// +build !linux,!darwin,!freebsd,!windows
package signal // import "github.com/docker/docker/pkg/signal"
import (
"syscall"
)
// SignalMap is an empty map of signals for unsupported platform.
var SignalMap = map[string]syscall.Signal{}

View File

@ -0,0 +1,26 @@
package signal // import "github.com/docker/docker/pkg/signal"
import (
"syscall"
)
// Signals used in cli/command (no windows equivalent, use
// invalid signals so they don't get handled)
const (
SIGCHLD = syscall.Signal(0xff)
SIGWINCH = syscall.Signal(0xff)
SIGPIPE = syscall.Signal(0xff)
// DefaultStopSignal is the syscall signal used to stop a container in windows systems.
DefaultStopSignal = "15"
)
// SignalMap is a map of "supported" signals. As per the comment in GOLang's
// ztypes_windows.go: "More invented values for signals". Windows doesn't
// really support signals in any way, shape or form that Unix does.
//
// We have these so that docker kill can be used to gracefully (TERM) and
// forcibly (KILL) terminate a container on Windows.
var SignalMap = map[string]syscall.Signal{
"KILL": syscall.SIGKILL,
"TERM": syscall.SIGTERM,
}

104
vendor/github.com/docker/docker/pkg/signal/trap.go generated vendored Normal file
View File

@ -0,0 +1,104 @@
package signal // import "github.com/docker/docker/pkg/signal"
import (
"fmt"
"os"
gosignal "os/signal"
"path/filepath"
"runtime"
"strings"
"sync/atomic"
"syscall"
"time"
"github.com/pkg/errors"
)
// Trap sets up a simplified signal "trap", appropriate for common
// behavior expected from a vanilla unix command-line tool in general
// (and the Docker engine in particular).
//
// * If SIGINT or SIGTERM are received, `cleanup` is called, then the process is terminated.
// * If SIGINT or SIGTERM are received 3 times before cleanup is complete, then cleanup is
// skipped and the process is terminated immediately (allows force quit of stuck daemon)
// * A SIGQUIT always causes an exit without cleanup, with a goroutine dump preceding exit.
// * Ignore SIGPIPE events. These are generated by systemd when journald is restarted while
// the docker daemon is not restarted and also running under systemd.
// Fixes https://github.com/docker/docker/issues/19728
//
func Trap(cleanup func(), logger interface {
Info(args ...interface{})
}) {
c := make(chan os.Signal, 1)
// we will handle INT, TERM, QUIT, SIGPIPE here
signals := []os.Signal{os.Interrupt, syscall.SIGTERM, syscall.SIGQUIT, syscall.SIGPIPE}
gosignal.Notify(c, signals...)
go func() {
interruptCount := uint32(0)
for sig := range c {
if sig == syscall.SIGPIPE {
continue
}
go func(sig os.Signal) {
logger.Info(fmt.Sprintf("Processing signal '%v'", sig))
switch sig {
case os.Interrupt, syscall.SIGTERM:
if atomic.LoadUint32(&interruptCount) < 3 {
// Initiate the cleanup only once
if atomic.AddUint32(&interruptCount, 1) == 1 {
// Call the provided cleanup handler
cleanup()
os.Exit(0)
} else {
return
}
} else {
// 3 SIGTERM/INT signals received; force exit without cleanup
logger.Info("Forcing docker daemon shutdown without cleanup; 3 interrupts received")
}
case syscall.SIGQUIT:
DumpStacks("")
logger.Info("Forcing docker daemon shutdown without cleanup on SIGQUIT")
}
//for the SIGINT/TERM, and SIGQUIT non-clean shutdown case, exit with 128 + signal #
os.Exit(128 + int(sig.(syscall.Signal)))
}(sig)
}
}()
}
const stacksLogNameTemplate = "goroutine-stacks-%s.log"
// DumpStacks appends the runtime stack into file in dir and returns full path
// to that file.
func DumpStacks(dir string) (string, error) {
var (
buf []byte
stackSize int
)
bufferLen := 16384
for stackSize == len(buf) {
buf = make([]byte, bufferLen)
stackSize = runtime.Stack(buf, true)
bufferLen *= 2
}
buf = buf[:stackSize]
var f *os.File
if dir != "" {
path := filepath.Join(dir, fmt.Sprintf(stacksLogNameTemplate, strings.Replace(time.Now().Format(time.RFC3339), ":", "", -1)))
var err error
f, err = os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0666)
if err != nil {
return "", errors.Wrap(err, "failed to open file to write the goroutine stacks")
}
defer f.Close()
defer f.Sync()
} else {
f = os.Stderr
}
if _, err := f.Write(buf); err != nil {
return "", errors.Wrap(err, "failed to write goroutine stacks")
}
return f.Name(), nil
}