diff --git a/Gopkg.lock b/Gopkg.lock index 7a899bbe1..6c182e1d4 100644 --- a/Gopkg.lock +++ b/Gopkg.lock @@ -45,6 +45,7 @@ "pkg/longpath", "pkg/mount", "pkg/pools", + "pkg/signal", "pkg/system" ] revision = "b1a1234c60cf87048814aa37da523b03a7b0d344" @@ -332,6 +333,6 @@ [solve-meta] analyzer-name = "dep" analyzer-version = 1 - inputs-digest = "9ed51a63daab2be9e4864e4d97d90ccc0f540d076f891b80f01bd3f161c9ef7a" + inputs-digest = "18d970ab8a3487013ef13e0aed686929d30bcebc1b9adaa329e9db414189fcf6" solver-name = "gps-cdcl" solver-version = 1 diff --git a/README.md b/README.md index 54a086d40..dfbd11a5b 100644 --- a/README.md +++ b/README.md @@ -12,6 +12,7 @@ Please let us know if you have any feature requests or find any bugs! - [Kaniko](#kaniko) - [How does kaniko work?](#how-does-kaniko-work) - [Known Issues](#known-issues) +- [Demo](#demo) - [Development](#development) - [kaniko Build Contexts](#kaniko-build-contexts) - [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: * HEALTHCHECK -* STOPSIGNAL * ARG Multi-Stage Dockerfiles are also unsupported currently, but will be ready soon. kaniko also does not support building Windows containers. +## Demo + +![Demo](/docs/demo.gif) + ## Development ### 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. diff --git a/deploy/Dockerfile b/deploy/Dockerfile index 227a184c7..1b93031bd 100644 --- a/deploy/Dockerfile +++ b/deploy/Dockerfile @@ -18,11 +18,14 @@ FROM golang:1.10 WORKDIR /go/src/github.com/GoogleContainerTools/kaniko COPY . . 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 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/docker-credential-gcr /usr/local/bin/ COPY files/config.json /root/.docker/ RUN ["docker-credential-gcr", "config", "--token-source=env"] ENV HOME /root diff --git a/docs/demo.gif b/docs/demo.gif new file mode 100644 index 000000000..3adbd1d34 Binary files /dev/null and b/docs/demo.gif differ diff --git a/files/docker-credential-gcr b/files/docker-credential-gcr deleted file mode 100755 index cff5a9be1..000000000 Binary files a/files/docker-credential-gcr and /dev/null differ diff --git a/pkg/commands/commands.go b/pkg/commands/commands.go index 96a5bed02..0d8e6cafa 100644 --- a/pkg/commands/commands.go +++ b/pkg/commands/commands.go @@ -61,6 +61,8 @@ func GetCommand(cmd instructions.Command, buildcontext string) (DockerCommand, e return &OnBuildCommand{cmd: c}, nil case *instructions.VolumeCommand: return &VolumeCommand{cmd: c}, nil + case *instructions.StopSignalCommand: + return &StopSignalCommand{cmd: c}, nil case *instructions.MaintainerCommand: logrus.Warnf("%s is deprecated, skipping", cmd.Name()) return nil, nil diff --git a/pkg/commands/copy.go b/pkg/commands/copy.go index 2610799b9..83e1bbcca 100644 --- a/pkg/commands/copy.go +++ b/pkg/commands/copy.go @@ -18,14 +18,13 @@ package commands import ( "github.com/GoogleContainerTools/kaniko/pkg/constants" - "os" - "path/filepath" - "strings" - "github.com/GoogleContainerTools/kaniko/pkg/util" "github.com/docker/docker/builder/dockerfile/instructions" "github.com/google/go-containerregistry/v1" "github.com/sirupsen/logrus" + "os" + "path/filepath" + "strings" ) type CopyCommand struct { @@ -43,7 +42,7 @@ func (c *CopyCommand) ExecuteCommand(config *v1.Config) error { // Resolve 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 resolvedEnvs, err := util.ResolveEnvironmentReplacementList(c.cmd.SourcesAndDest, config.Env, true) @@ -67,7 +66,7 @@ func (c *CopyCommand) ExecuteCommand(config *v1.Config) error { if cwd == "" { cwd = constants.RootDir } - destPath, err := util.DestinationFilepath(src, dest, config.WorkingDir) + destPath, err := util.DestinationFilepath(src, dest, cwd) if err != nil { return err } diff --git a/pkg/commands/stopsignal.go b/pkg/commands/stopsignal.go new file mode 100644 index 000000000..65fb7d42f --- /dev/null +++ b/pkg/commands/stopsignal.go @@ -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), " ") +} diff --git a/pkg/commands/stopsignal_test.go b/pkg/commands/stopsignal_test.go new file mode 100644 index 000000000..c6e717f76 --- /dev/null +++ b/pkg/commands/stopsignal_test.go @@ -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) + } +} diff --git a/pkg/executor/executor.go b/pkg/executor/executor.go index 90dde38c4..9cc94ce19 100644 --- a/pkg/executor/executor.go +++ b/pkg/executor/executor.go @@ -141,7 +141,8 @@ func DoBuild(dockerfilePath, srcContext, snapshotMode string) (name.Reference, v mutate.Addendum{ Layer: layer, 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 i := strconv.Itoa(index) - dependencyDir := filepath.Join(constants.BuildContextDir, i) + dependencyDir := filepath.Join(constants.KanikoDir, i) if err := os.MkdirAll(dependencyDir, 0755); err != nil { return err } diff --git a/pkg/snapshot/snapshot.go b/pkg/snapshot/snapshot.go index 9da540f23..478095afc 100644 --- a/pkg/snapshot/snapshot.go +++ b/pkg/snapshot/snapshot.go @@ -19,10 +19,8 @@ package snapshot import ( "archive/tar" "bytes" - "github.com/GoogleContainerTools/kaniko/pkg/util" "github.com/sirupsen/logrus" - "io" "io/ioutil" "os" @@ -52,12 +50,14 @@ func (s *Snapshotter) Init() error { // 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 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{}) - 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 { return nil, err } @@ -68,45 +68,57 @@ func (s *Snapshotter) TakeSnapshot(files []string) ([]byte, error) { 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 -func (s *Snapshotter) TakeSnapshotOfFiles(files []string) ([]byte, error) { - logrus.Infof("Taking snapshot of files %v...", files) +func (s *Snapshotter) snapshotFiles(f io.Writer, files []string) (bool, error) { + s.hardlinks = map[uint64]string{} s.l.Snapshot() if len(files) == 0 { logrus.Info("No files changed in this command, skipping snapshotting.") - return nil, nil + return false, nil } - buf := bytes.NewBuffer([]byte{}) - w := tar.NewWriter(buf) - defer w.Close() - filesAdded := false + logrus.Infof("Taking snapshot of files %v...", files) + snapshottedFiles := make(map[string]bool) for _, file := range files { - info, err := os.Lstat(file) - if err != nil { - return nil, err + parentDirs := util.ParentDirectories(file) + files = append(parentDirs, files...) + } + 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) { - 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 } + 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. maybeAdd, err := s.l.MaybeAdd(file) if err != nil { - return nil, err + return false, err } if maybeAdd { 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 nil, nil - } - return ioutil.ReadAll(buf) + return filesAdded, nil } func (s *Snapshotter) snapShotFS(f io.Writer) (bool, error) { + logrus.Info("Taking snapshot of full filesystem...") s.hardlinks = map[uint64]string{} s.l.Snapshot() existingPaths := s.l.GetFlattenedPathsForWhiteOut() diff --git a/pkg/snapshot/snapshot_test.go b/pkg/snapshot/snapshot_test.go index 2def85db0..7cee2ad11 100644 --- a/pkg/snapshot/snapshot_test.go +++ b/pkg/snapshot/snapshot_test.go @@ -149,30 +149,23 @@ func TestSnapshotFiles(t *testing.T) { if err != nil { t.Fatal(err) } - expectedContents := map[string]string{ - filepath.Join(testDir, "foo"): "newbaz1", - } + expectedFiles := []string{"/tmp", filepath.Join(testDir, "foo")} + // Check contents of the snapshot, make sure contents is equivalent to snapshotFiles reader := bytes.NewReader(contents) tr := tar.NewReader(reader) - numFiles := 0 + var actualFiles []string for { hdr, err := tr.Next() if err == io.EOF { break } - numFiles = numFiles + 1 - if _, isFile := expectedContents[hdr.Name]; !isFile { - 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)) + if err != nil { + t.Fatal(err) } + actualFiles = append(actualFiles, hdr.Name) } - if numFiles != 1 { - t.Fatalf("%s was not added.", filepath.Join(testDir, "foo")) - } + testutil.CheckErrorAndDeepEqual(t, false, nil, expectedFiles, actualFiles) } func TestEmptySnapshot(t *testing.T) { diff --git a/pkg/util/fs_util.go b/pkg/util/fs_util.go index 9722dbc57..ce8225fbf 100644 --- a/pkg/util/fs_util.go +++ b/pkg/util/fs_util.go @@ -326,6 +326,23 @@ func Files(root string) ([]string, error) { 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 func FilepathExists(path string) bool { _, err := os.Lstat(path) diff --git a/pkg/util/fs_util_test.go b/pkg/util/fs_util_test.go index 62e9b6091..7b6ef7082 100644 --- a/pkg/util/fs_util_test.go +++ b/pkg/util/fs_util_test.go @@ -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) { type args struct { path string diff --git a/vendor/github.com/docker/docker/pkg/signal/signal.go b/vendor/github.com/docker/docker/pkg/signal/signal.go new file mode 100644 index 000000000..6a663091a --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/signal/signal.go @@ -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 +} diff --git a/vendor/github.com/docker/docker/pkg/signal/signal_darwin.go b/vendor/github.com/docker/docker/pkg/signal/signal_darwin.go new file mode 100644 index 000000000..ee5501e3d --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/signal/signal_darwin.go @@ -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, +} diff --git a/vendor/github.com/docker/docker/pkg/signal/signal_freebsd.go b/vendor/github.com/docker/docker/pkg/signal/signal_freebsd.go new file mode 100644 index 000000000..764f90e26 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/signal/signal_freebsd.go @@ -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, +} diff --git a/vendor/github.com/docker/docker/pkg/signal/signal_linux.go b/vendor/github.com/docker/docker/pkg/signal/signal_linux.go new file mode 100644 index 000000000..caed97c96 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/signal/signal_linux.go @@ -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, +} diff --git a/vendor/github.com/docker/docker/pkg/signal/signal_unix.go b/vendor/github.com/docker/docker/pkg/signal/signal_unix.go new file mode 100644 index 000000000..a2aa4248f --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/signal/signal_unix.go @@ -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" +) diff --git a/vendor/github.com/docker/docker/pkg/signal/signal_unsupported.go b/vendor/github.com/docker/docker/pkg/signal/signal_unsupported.go new file mode 100644 index 000000000..1fd25a83c --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/signal/signal_unsupported.go @@ -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{} diff --git a/vendor/github.com/docker/docker/pkg/signal/signal_windows.go b/vendor/github.com/docker/docker/pkg/signal/signal_windows.go new file mode 100644 index 000000000..65752f24a --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/signal/signal_windows.go @@ -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, +} diff --git a/vendor/github.com/docker/docker/pkg/signal/trap.go b/vendor/github.com/docker/docker/pkg/signal/trap.go new file mode 100644 index 000000000..2a6e69fb5 --- /dev/null +++ b/vendor/github.com/docker/docker/pkg/signal/trap.go @@ -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 +}