Vendor integration dependencies.
This commit is contained in:
parent
dd5e3fba01
commit
55b57c736b
2451 changed files with 731611 additions and 0 deletions
191
integration/vendor/github.com/opencontainers/runc/LICENSE
generated
vendored
Normal file
191
integration/vendor/github.com/opencontainers/runc/LICENSE
generated
vendored
Normal file
|
@ -0,0 +1,191 @@
|
|||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright 2014 Docker, Inc.
|
||||
|
||||
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.
|
17
integration/vendor/github.com/opencontainers/runc/NOTICE
generated
vendored
Normal file
17
integration/vendor/github.com/opencontainers/runc/NOTICE
generated
vendored
Normal file
|
@ -0,0 +1,17 @@
|
|||
runc
|
||||
|
||||
Copyright 2012-2015 Docker, Inc.
|
||||
|
||||
This product includes software developed at Docker, Inc. (http://www.docker.com).
|
||||
|
||||
The following is courtesy of our legal counsel:
|
||||
|
||||
|
||||
Use and transfer of Docker may be subject to certain restrictions by the
|
||||
United States and other governments.
|
||||
It is your responsibility to ensure that your use and/or transfer does not
|
||||
violate applicable laws.
|
||||
|
||||
For more information, please see http://www.bis.doc.gov
|
||||
|
||||
See also http://www.apache.org/dev/crypto.html and/or seek legal counsel.
|
90
integration/vendor/github.com/opencontainers/runc/checkpoint.go
generated
vendored
Normal file
90
integration/vendor/github.com/opencontainers/runc/checkpoint.go
generated
vendored
Normal file
|
@ -0,0 +1,90 @@
|
|||
// +build linux
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/opencontainers/runc/libcontainer"
|
||||
)
|
||||
|
||||
var checkpointCommand = cli.Command{
|
||||
Name: "checkpoint",
|
||||
Usage: "checkpoint a running container",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{Name: "image-path", Value: "", Usage: "path for saving criu image files"},
|
||||
cli.StringFlag{Name: "work-path", Value: "", Usage: "path for saving work files and logs"},
|
||||
cli.BoolFlag{Name: "leave-running", Usage: "leave the process running after checkpointing"},
|
||||
cli.BoolFlag{Name: "tcp-established", Usage: "allow open tcp connections"},
|
||||
cli.BoolFlag{Name: "ext-unix-sk", Usage: "allow external unix sockets"},
|
||||
cli.BoolFlag{Name: "shell-job", Usage: "allow shell jobs"},
|
||||
cli.StringFlag{Name: "page-server", Value: "", Usage: "ADDRESS:PORT of the page server"},
|
||||
cli.BoolFlag{Name: "file-locks", Usage: "handle file locks, for safety"},
|
||||
cli.StringFlag{Name: "manage-cgroups-mode", Value: "", Usage: "cgroups mode: 'soft' (default), 'full' and 'strict'."},
|
||||
},
|
||||
Action: func(context *cli.Context) {
|
||||
container, err := getContainer(context)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
options := criuOptions(context)
|
||||
status, err := container.Status()
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
if status == libcontainer.Checkpointed {
|
||||
fatal(fmt.Errorf("Container with id %s already checkpointed", context.GlobalString("id")))
|
||||
}
|
||||
// these are the mandatory criu options for a container
|
||||
setPageServer(context, options)
|
||||
setManageCgroupsMode(context, options)
|
||||
if err := container.Checkpoint(options); err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func getCheckpointImagePath(context *cli.Context) string {
|
||||
imagePath := context.String("image-path")
|
||||
if imagePath == "" {
|
||||
imagePath = getDefaultImagePath(context)
|
||||
}
|
||||
return imagePath
|
||||
}
|
||||
|
||||
func setPageServer(context *cli.Context, options *libcontainer.CriuOpts) {
|
||||
// xxx following criu opts are optional
|
||||
// The dump image can be sent to a criu page server
|
||||
if psOpt := context.String("page-server"); psOpt != "" {
|
||||
addressPort := strings.Split(psOpt, ":")
|
||||
if len(addressPort) != 2 {
|
||||
fatal(fmt.Errorf("Use --page-server ADDRESS:PORT to specify page server"))
|
||||
}
|
||||
portInt, err := strconv.Atoi(addressPort[1])
|
||||
if err != nil {
|
||||
fatal(fmt.Errorf("Invalid port number"))
|
||||
}
|
||||
options.PageServer = libcontainer.CriuPageServerInfo{
|
||||
Address: addressPort[0],
|
||||
Port: int32(portInt),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setManageCgroupsMode(context *cli.Context, options *libcontainer.CriuOpts) {
|
||||
if cgOpt := context.String("manage-cgroups-mode"); cgOpt != "" {
|
||||
switch cgOpt {
|
||||
case "soft":
|
||||
options.ManageCgroupsMode = libcontainer.CRIU_CG_MODE_SOFT
|
||||
case "full":
|
||||
options.ManageCgroupsMode = libcontainer.CRIU_CG_MODE_FULL
|
||||
case "strict":
|
||||
options.ManageCgroupsMode = libcontainer.CRIU_CG_MODE_STRICT
|
||||
default:
|
||||
fatal(fmt.Errorf("Invalid manage cgroups mode"))
|
||||
}
|
||||
}
|
||||
}
|
95
integration/vendor/github.com/opencontainers/runc/events.go
generated
vendored
Normal file
95
integration/vendor/github.com/opencontainers/runc/events.go
generated
vendored
Normal file
|
@ -0,0 +1,95 @@
|
|||
// +build linux
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/opencontainers/runc/libcontainer"
|
||||
)
|
||||
|
||||
// event struct for encoding the event data to json.
|
||||
type event struct {
|
||||
Type string `json:"type"`
|
||||
ID string `json:"id"`
|
||||
Data interface{} `json:"data,omitempty"`
|
||||
}
|
||||
|
||||
var eventsCommand = cli.Command{
|
||||
Name: "events",
|
||||
Usage: "display container events such as OOM notifications, cpu, memory, IO and network stats",
|
||||
Flags: []cli.Flag{
|
||||
cli.DurationFlag{Name: "interval", Value: 5 * time.Second, Usage: "set the stats collection interval"},
|
||||
cli.BoolFlag{Name: "stats", Usage: "display the container's stats then exit"},
|
||||
},
|
||||
Action: func(context *cli.Context) {
|
||||
container, err := getContainer(context)
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
var (
|
||||
stats = make(chan *libcontainer.Stats, 1)
|
||||
events = make(chan *event, 1024)
|
||||
group = &sync.WaitGroup{}
|
||||
)
|
||||
group.Add(1)
|
||||
go func() {
|
||||
defer group.Done()
|
||||
enc := json.NewEncoder(os.Stdout)
|
||||
for e := range events {
|
||||
if err := enc.Encode(e); err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
if context.Bool("stats") {
|
||||
s, err := container.Stats()
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
events <- &event{Type: "stats", ID: container.ID(), Data: s}
|
||||
close(events)
|
||||
group.Wait()
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
for range time.Tick(context.Duration("interval")) {
|
||||
s, err := container.Stats()
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
continue
|
||||
}
|
||||
stats <- s
|
||||
}
|
||||
}()
|
||||
n, err := container.NotifyOOM()
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case _, ok := <-n:
|
||||
if ok {
|
||||
// this means an oom event was received, if it is !ok then
|
||||
// the channel was closed because the container stopped and
|
||||
// the cgroups no longer exist.
|
||||
events <- &event{Type: "oom", ID: container.ID()}
|
||||
} else {
|
||||
n = nil
|
||||
}
|
||||
case s := <-stats:
|
||||
events <- &event{Type: "stats", ID: container.ID(), Data: s}
|
||||
}
|
||||
if n == nil {
|
||||
close(events)
|
||||
break
|
||||
}
|
||||
}
|
||||
group.Wait()
|
||||
},
|
||||
}
|
75
integration/vendor/github.com/opencontainers/runc/exec.go
generated
vendored
Normal file
75
integration/vendor/github.com/opencontainers/runc/exec.go
generated
vendored
Normal file
|
@ -0,0 +1,75 @@
|
|||
// +build linux
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/opencontainers/specs"
|
||||
)
|
||||
|
||||
var execCommand = cli.Command{
|
||||
Name: "exec",
|
||||
Usage: "execute new process inside the container",
|
||||
Action: func(context *cli.Context) {
|
||||
config, err := loadProcessConfig(context.Args().First())
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
if os.Geteuid() != 0 {
|
||||
logrus.Fatal("runc should be run as root")
|
||||
}
|
||||
if config.Args == nil {
|
||||
logrus.Fatal("args missing in process configuration")
|
||||
}
|
||||
status, err := execProcess(context, config)
|
||||
if err != nil {
|
||||
logrus.Fatalf("exec failed: %v", err)
|
||||
}
|
||||
os.Exit(status)
|
||||
},
|
||||
}
|
||||
|
||||
func execProcess(context *cli.Context, config *specs.Process) (int, error) {
|
||||
container, err := getContainer(context)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
process := newProcess(*config)
|
||||
|
||||
rootuid, err := container.Config().HostUID()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
tty, err := newTty(config.Terminal, process, rootuid)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
handler := newSignalHandler(tty)
|
||||
defer handler.Close()
|
||||
if err := container.Start(process); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return handler.forward(process)
|
||||
}
|
||||
|
||||
// loadProcessConfig loads the process configuration from the provided path.
|
||||
func loadProcessConfig(path string) (*specs.Process, error) {
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil, fmt.Errorf("JSON configuration file for %s not found", path)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
var s *specs.Process
|
||||
if err := json.NewDecoder(f).Decode(&s); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s, nil
|
||||
}
|
87
integration/vendor/github.com/opencontainers/runc/kill.go
generated
vendored
Normal file
87
integration/vendor/github.com/opencontainers/runc/kill.go
generated
vendored
Normal file
|
@ -0,0 +1,87 @@
|
|||
// +build linux
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
var signalMap = map[string]syscall.Signal{
|
||||
"ABRT": syscall.SIGABRT,
|
||||
"ALRM": syscall.SIGALRM,
|
||||
"BUS": syscall.SIGBUS,
|
||||
"CHLD": syscall.SIGCHLD,
|
||||
"CLD": syscall.SIGCLD,
|
||||
"CONT": syscall.SIGCONT,
|
||||
"FPE": syscall.SIGFPE,
|
||||
"HUP": syscall.SIGHUP,
|
||||
"ILL": syscall.SIGILL,
|
||||
"INT": syscall.SIGINT,
|
||||
"IO": syscall.SIGIO,
|
||||
"IOT": syscall.SIGIOT,
|
||||
"KILL": syscall.SIGKILL,
|
||||
"PIPE": syscall.SIGPIPE,
|
||||
"POLL": syscall.SIGPOLL,
|
||||
"PROF": syscall.SIGPROF,
|
||||
"PWR": syscall.SIGPWR,
|
||||
"QUIT": syscall.SIGQUIT,
|
||||
"SEGV": syscall.SIGSEGV,
|
||||
"STKFLT": syscall.SIGSTKFLT,
|
||||
"STOP": syscall.SIGSTOP,
|
||||
"SYS": syscall.SIGSYS,
|
||||
"TERM": syscall.SIGTERM,
|
||||
"TRAP": syscall.SIGTRAP,
|
||||
"TSTP": syscall.SIGTSTP,
|
||||
"TTIN": syscall.SIGTTIN,
|
||||
"TTOU": syscall.SIGTTOU,
|
||||
"UNUSED": syscall.SIGUNUSED,
|
||||
"URG": syscall.SIGURG,
|
||||
"USR1": syscall.SIGUSR1,
|
||||
"USR2": syscall.SIGUSR2,
|
||||
"VTALRM": syscall.SIGVTALRM,
|
||||
"WINCH": syscall.SIGWINCH,
|
||||
"XCPU": syscall.SIGXCPU,
|
||||
"XFSZ": syscall.SIGXFSZ,
|
||||
}
|
||||
|
||||
var killCommand = cli.Command{
|
||||
Name: "kill",
|
||||
Usage: "kill sends the specified signal (default: SIGTERM) to the container's init process",
|
||||
Action: func(context *cli.Context) {
|
||||
container, err := getContainer(context)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
|
||||
sigstr := context.Args().First()
|
||||
if sigstr == "" {
|
||||
sigstr = "SIGTERM"
|
||||
}
|
||||
|
||||
signal, err := parseSignal(sigstr)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
|
||||
if err := container.Signal(signal); err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func parseSignal(rawSignal string) (syscall.Signal, error) {
|
||||
s, err := strconv.Atoi(rawSignal)
|
||||
if err == nil {
|
||||
return syscall.Signal(s), nil
|
||||
}
|
||||
signal, ok := signalMap[strings.TrimPrefix(strings.ToUpper(rawSignal), "SIG")]
|
||||
if !ok {
|
||||
return -1, fmt.Errorf("unknown signal %q", rawSignal)
|
||||
}
|
||||
return signal, nil
|
||||
}
|
108
integration/vendor/github.com/opencontainers/runc/libcontainer/user/lookup.go
generated
vendored
Normal file
108
integration/vendor/github.com/opencontainers/runc/libcontainer/user/lookup.go
generated
vendored
Normal file
|
@ -0,0 +1,108 @@
|
|||
package user
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
var (
|
||||
// The current operating system does not provide the required data for user lookups.
|
||||
ErrUnsupported = errors.New("user lookup: operating system does not provide passwd-formatted data")
|
||||
)
|
||||
|
||||
func lookupUser(filter func(u User) bool) (User, error) {
|
||||
// Get operating system-specific passwd reader-closer.
|
||||
passwd, err := GetPasswd()
|
||||
if err != nil {
|
||||
return User{}, err
|
||||
}
|
||||
defer passwd.Close()
|
||||
|
||||
// Get the users.
|
||||
users, err := ParsePasswdFilter(passwd, filter)
|
||||
if err != nil {
|
||||
return User{}, err
|
||||
}
|
||||
|
||||
// No user entries found.
|
||||
if len(users) == 0 {
|
||||
return User{}, fmt.Errorf("no matching entries in passwd file")
|
||||
}
|
||||
|
||||
// Assume the first entry is the "correct" one.
|
||||
return users[0], nil
|
||||
}
|
||||
|
||||
// CurrentUser looks up the current user by their user id in /etc/passwd. If the
|
||||
// user cannot be found (or there is no /etc/passwd file on the filesystem),
|
||||
// then CurrentUser returns an error.
|
||||
func CurrentUser() (User, error) {
|
||||
return LookupUid(syscall.Getuid())
|
||||
}
|
||||
|
||||
// LookupUser looks up a user by their username in /etc/passwd. If the user
|
||||
// cannot be found (or there is no /etc/passwd file on the filesystem), then
|
||||
// LookupUser returns an error.
|
||||
func LookupUser(username string) (User, error) {
|
||||
return lookupUser(func(u User) bool {
|
||||
return u.Name == username
|
||||
})
|
||||
}
|
||||
|
||||
// LookupUid looks up a user by their user id in /etc/passwd. If the user cannot
|
||||
// be found (or there is no /etc/passwd file on the filesystem), then LookupId
|
||||
// returns an error.
|
||||
func LookupUid(uid int) (User, error) {
|
||||
return lookupUser(func(u User) bool {
|
||||
return u.Uid == uid
|
||||
})
|
||||
}
|
||||
|
||||
func lookupGroup(filter func(g Group) bool) (Group, error) {
|
||||
// Get operating system-specific group reader-closer.
|
||||
group, err := GetGroup()
|
||||
if err != nil {
|
||||
return Group{}, err
|
||||
}
|
||||
defer group.Close()
|
||||
|
||||
// Get the users.
|
||||
groups, err := ParseGroupFilter(group, filter)
|
||||
if err != nil {
|
||||
return Group{}, err
|
||||
}
|
||||
|
||||
// No user entries found.
|
||||
if len(groups) == 0 {
|
||||
return Group{}, fmt.Errorf("no matching entries in group file")
|
||||
}
|
||||
|
||||
// Assume the first entry is the "correct" one.
|
||||
return groups[0], nil
|
||||
}
|
||||
|
||||
// CurrentGroup looks up the current user's group by their primary group id's
|
||||
// entry in /etc/passwd. If the group cannot be found (or there is no
|
||||
// /etc/group file on the filesystem), then CurrentGroup returns an error.
|
||||
func CurrentGroup() (Group, error) {
|
||||
return LookupGid(syscall.Getgid())
|
||||
}
|
||||
|
||||
// LookupGroup looks up a group by its name in /etc/group. If the group cannot
|
||||
// be found (or there is no /etc/group file on the filesystem), then LookupGroup
|
||||
// returns an error.
|
||||
func LookupGroup(groupname string) (Group, error) {
|
||||
return lookupGroup(func(g Group) bool {
|
||||
return g.Name == groupname
|
||||
})
|
||||
}
|
||||
|
||||
// LookupGid looks up a group by its group id in /etc/group. If the group cannot
|
||||
// be found (or there is no /etc/group file on the filesystem), then LookupGid
|
||||
// returns an error.
|
||||
func LookupGid(gid int) (Group, error) {
|
||||
return lookupGroup(func(g Group) bool {
|
||||
return g.Gid == gid
|
||||
})
|
||||
}
|
30
integration/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go
generated
vendored
Normal file
30
integration/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unix.go
generated
vendored
Normal file
|
@ -0,0 +1,30 @@
|
|||
// +build darwin dragonfly freebsd linux netbsd openbsd solaris
|
||||
|
||||
package user
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// Unix-specific path to the passwd and group formatted files.
|
||||
const (
|
||||
unixPasswdPath = "/etc/passwd"
|
||||
unixGroupPath = "/etc/group"
|
||||
)
|
||||
|
||||
func GetPasswdPath() (string, error) {
|
||||
return unixPasswdPath, nil
|
||||
}
|
||||
|
||||
func GetPasswd() (io.ReadCloser, error) {
|
||||
return os.Open(unixPasswdPath)
|
||||
}
|
||||
|
||||
func GetGroupPath() (string, error) {
|
||||
return unixGroupPath, nil
|
||||
}
|
||||
|
||||
func GetGroup() (io.ReadCloser, error) {
|
||||
return os.Open(unixGroupPath)
|
||||
}
|
21
integration/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unsupported.go
generated
vendored
Normal file
21
integration/vendor/github.com/opencontainers/runc/libcontainer/user/lookup_unsupported.go
generated
vendored
Normal file
|
@ -0,0 +1,21 @@
|
|||
// +build !darwin,!dragonfly,!freebsd,!linux,!netbsd,!openbsd,!solaris
|
||||
|
||||
package user
|
||||
|
||||
import "io"
|
||||
|
||||
func GetPasswdPath() (string, error) {
|
||||
return "", ErrUnsupported
|
||||
}
|
||||
|
||||
func GetPasswd() (io.ReadCloser, error) {
|
||||
return nil, ErrUnsupported
|
||||
}
|
||||
|
||||
func GetGroupPath() (string, error) {
|
||||
return "", ErrUnsupported
|
||||
}
|
||||
|
||||
func GetGroup() (io.ReadCloser, error) {
|
||||
return nil, ErrUnsupported
|
||||
}
|
418
integration/vendor/github.com/opencontainers/runc/libcontainer/user/user.go
generated
vendored
Normal file
418
integration/vendor/github.com/opencontainers/runc/libcontainer/user/user.go
generated
vendored
Normal file
|
@ -0,0 +1,418 @@
|
|||
package user
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
minId = 0
|
||||
maxId = 1<<31 - 1 //for 32-bit systems compatibility
|
||||
)
|
||||
|
||||
var (
|
||||
ErrRange = fmt.Errorf("Uids and gids must be in range %d-%d", minId, maxId)
|
||||
)
|
||||
|
||||
type User struct {
|
||||
Name string
|
||||
Pass string
|
||||
Uid int
|
||||
Gid int
|
||||
Gecos string
|
||||
Home string
|
||||
Shell string
|
||||
}
|
||||
|
||||
type Group struct {
|
||||
Name string
|
||||
Pass string
|
||||
Gid int
|
||||
List []string
|
||||
}
|
||||
|
||||
func parseLine(line string, v ...interface{}) {
|
||||
if line == "" {
|
||||
return
|
||||
}
|
||||
|
||||
parts := strings.Split(line, ":")
|
||||
for i, p := range parts {
|
||||
if len(v) <= i {
|
||||
// if we have more "parts" than we have places to put them, bail for great "tolerance" of naughty configuration files
|
||||
break
|
||||
}
|
||||
|
||||
switch e := v[i].(type) {
|
||||
case *string:
|
||||
// "root", "adm", "/bin/bash"
|
||||
*e = p
|
||||
case *int:
|
||||
// "0", "4", "1000"
|
||||
// ignore string to int conversion errors, for great "tolerance" of naughty configuration files
|
||||
*e, _ = strconv.Atoi(p)
|
||||
case *[]string:
|
||||
// "", "root", "root,adm,daemon"
|
||||
if p != "" {
|
||||
*e = strings.Split(p, ",")
|
||||
} else {
|
||||
*e = []string{}
|
||||
}
|
||||
default:
|
||||
// panic, because this is a programming/logic error, not a runtime one
|
||||
panic("parseLine expects only pointers! argument " + strconv.Itoa(i) + " is not a pointer!")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func ParsePasswdFile(path string) ([]User, error) {
|
||||
passwd, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer passwd.Close()
|
||||
return ParsePasswd(passwd)
|
||||
}
|
||||
|
||||
func ParsePasswd(passwd io.Reader) ([]User, error) {
|
||||
return ParsePasswdFilter(passwd, nil)
|
||||
}
|
||||
|
||||
func ParsePasswdFileFilter(path string, filter func(User) bool) ([]User, error) {
|
||||
passwd, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer passwd.Close()
|
||||
return ParsePasswdFilter(passwd, filter)
|
||||
}
|
||||
|
||||
func ParsePasswdFilter(r io.Reader, filter func(User) bool) ([]User, error) {
|
||||
if r == nil {
|
||||
return nil, fmt.Errorf("nil source for passwd-formatted data")
|
||||
}
|
||||
|
||||
var (
|
||||
s = bufio.NewScanner(r)
|
||||
out = []User{}
|
||||
)
|
||||
|
||||
for s.Scan() {
|
||||
if err := s.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
text := strings.TrimSpace(s.Text())
|
||||
if text == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// see: man 5 passwd
|
||||
// name:password:UID:GID:GECOS:directory:shell
|
||||
// Name:Pass:Uid:Gid:Gecos:Home:Shell
|
||||
// root:x:0:0:root:/root:/bin/bash
|
||||
// adm:x:3:4:adm:/var/adm:/bin/false
|
||||
p := User{}
|
||||
parseLine(
|
||||
text,
|
||||
&p.Name, &p.Pass, &p.Uid, &p.Gid, &p.Gecos, &p.Home, &p.Shell,
|
||||
)
|
||||
|
||||
if filter == nil || filter(p) {
|
||||
out = append(out, p)
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func ParseGroupFile(path string) ([]Group, error) {
|
||||
group, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer group.Close()
|
||||
return ParseGroup(group)
|
||||
}
|
||||
|
||||
func ParseGroup(group io.Reader) ([]Group, error) {
|
||||
return ParseGroupFilter(group, nil)
|
||||
}
|
||||
|
||||
func ParseGroupFileFilter(path string, filter func(Group) bool) ([]Group, error) {
|
||||
group, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer group.Close()
|
||||
return ParseGroupFilter(group, filter)
|
||||
}
|
||||
|
||||
func ParseGroupFilter(r io.Reader, filter func(Group) bool) ([]Group, error) {
|
||||
if r == nil {
|
||||
return nil, fmt.Errorf("nil source for group-formatted data")
|
||||
}
|
||||
|
||||
var (
|
||||
s = bufio.NewScanner(r)
|
||||
out = []Group{}
|
||||
)
|
||||
|
||||
for s.Scan() {
|
||||
if err := s.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
text := s.Text()
|
||||
if text == "" {
|
||||
continue
|
||||
}
|
||||
|
||||
// see: man 5 group
|
||||
// group_name:password:GID:user_list
|
||||
// Name:Pass:Gid:List
|
||||
// root:x:0:root
|
||||
// adm:x:4:root,adm,daemon
|
||||
p := Group{}
|
||||
parseLine(
|
||||
text,
|
||||
&p.Name, &p.Pass, &p.Gid, &p.List,
|
||||
)
|
||||
|
||||
if filter == nil || filter(p) {
|
||||
out = append(out, p)
|
||||
}
|
||||
}
|
||||
|
||||
return out, nil
|
||||
}
|
||||
|
||||
type ExecUser struct {
|
||||
Uid, Gid int
|
||||
Sgids []int
|
||||
Home string
|
||||
}
|
||||
|
||||
// GetExecUserPath is a wrapper for GetExecUser. It reads data from each of the
|
||||
// given file paths and uses that data as the arguments to GetExecUser. If the
|
||||
// files cannot be opened for any reason, the error is ignored and a nil
|
||||
// io.Reader is passed instead.
|
||||
func GetExecUserPath(userSpec string, defaults *ExecUser, passwdPath, groupPath string) (*ExecUser, error) {
|
||||
passwd, err := os.Open(passwdPath)
|
||||
if err != nil {
|
||||
passwd = nil
|
||||
} else {
|
||||
defer passwd.Close()
|
||||
}
|
||||
|
||||
group, err := os.Open(groupPath)
|
||||
if err != nil {
|
||||
group = nil
|
||||
} else {
|
||||
defer group.Close()
|
||||
}
|
||||
|
||||
return GetExecUser(userSpec, defaults, passwd, group)
|
||||
}
|
||||
|
||||
// GetExecUser parses a user specification string (using the passwd and group
|
||||
// readers as sources for /etc/passwd and /etc/group data, respectively). In
|
||||
// the case of blank fields or missing data from the sources, the values in
|
||||
// defaults is used.
|
||||
//
|
||||
// GetExecUser will return an error if a user or group literal could not be
|
||||
// found in any entry in passwd and group respectively.
|
||||
//
|
||||
// Examples of valid user specifications are:
|
||||
// * ""
|
||||
// * "user"
|
||||
// * "uid"
|
||||
// * "user:group"
|
||||
// * "uid:gid
|
||||
// * "user:gid"
|
||||
// * "uid:group"
|
||||
func GetExecUser(userSpec string, defaults *ExecUser, passwd, group io.Reader) (*ExecUser, error) {
|
||||
var (
|
||||
userArg, groupArg string
|
||||
name string
|
||||
)
|
||||
|
||||
if defaults == nil {
|
||||
defaults = new(ExecUser)
|
||||
}
|
||||
|
||||
// Copy over defaults.
|
||||
user := &ExecUser{
|
||||
Uid: defaults.Uid,
|
||||
Gid: defaults.Gid,
|
||||
Sgids: defaults.Sgids,
|
||||
Home: defaults.Home,
|
||||
}
|
||||
|
||||
// Sgids slice *cannot* be nil.
|
||||
if user.Sgids == nil {
|
||||
user.Sgids = []int{}
|
||||
}
|
||||
|
||||
// allow for userArg to have either "user" syntax, or optionally "user:group" syntax
|
||||
parseLine(userSpec, &userArg, &groupArg)
|
||||
|
||||
users, err := ParsePasswdFilter(passwd, func(u User) bool {
|
||||
if userArg == "" {
|
||||
return u.Uid == user.Uid
|
||||
}
|
||||
return u.Name == userArg || strconv.Itoa(u.Uid) == userArg
|
||||
})
|
||||
if err != nil && passwd != nil {
|
||||
if userArg == "" {
|
||||
userArg = strconv.Itoa(user.Uid)
|
||||
}
|
||||
return nil, fmt.Errorf("Unable to find user %v: %v", userArg, err)
|
||||
}
|
||||
|
||||
haveUser := users != nil && len(users) > 0
|
||||
if haveUser {
|
||||
// if we found any user entries that matched our filter, let's take the first one as "correct"
|
||||
name = users[0].Name
|
||||
user.Uid = users[0].Uid
|
||||
user.Gid = users[0].Gid
|
||||
user.Home = users[0].Home
|
||||
} else if userArg != "" {
|
||||
// we asked for a user but didn't find them... let's check to see if we wanted a numeric user
|
||||
user.Uid, err = strconv.Atoi(userArg)
|
||||
if err != nil {
|
||||
// not numeric - we have to bail
|
||||
return nil, fmt.Errorf("Unable to find user %v", userArg)
|
||||
}
|
||||
|
||||
// Must be inside valid uid range.
|
||||
if user.Uid < minId || user.Uid > maxId {
|
||||
return nil, ErrRange
|
||||
}
|
||||
|
||||
// if userArg couldn't be found in /etc/passwd but is numeric, just roll with it - this is legit
|
||||
}
|
||||
|
||||
if groupArg != "" || name != "" {
|
||||
groups, err := ParseGroupFilter(group, func(g Group) bool {
|
||||
// Explicit group format takes precedence.
|
||||
if groupArg != "" {
|
||||
return g.Name == groupArg || strconv.Itoa(g.Gid) == groupArg
|
||||
}
|
||||
|
||||
// Check if user is a member.
|
||||
for _, u := range g.List {
|
||||
if u == name {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
})
|
||||
if err != nil && group != nil {
|
||||
return nil, fmt.Errorf("Unable to find groups for user %v: %v", users[0].Name, err)
|
||||
}
|
||||
|
||||
haveGroup := groups != nil && len(groups) > 0
|
||||
if groupArg != "" {
|
||||
if haveGroup {
|
||||
// if we found any group entries that matched our filter, let's take the first one as "correct"
|
||||
user.Gid = groups[0].Gid
|
||||
} else {
|
||||
// we asked for a group but didn't find id... let's check to see if we wanted a numeric group
|
||||
user.Gid, err = strconv.Atoi(groupArg)
|
||||
if err != nil {
|
||||
// not numeric - we have to bail
|
||||
return nil, fmt.Errorf("Unable to find group %v", groupArg)
|
||||
}
|
||||
|
||||
// Ensure gid is inside gid range.
|
||||
if user.Gid < minId || user.Gid > maxId {
|
||||
return nil, ErrRange
|
||||
}
|
||||
|
||||
// if groupArg couldn't be found in /etc/group but is numeric, just roll with it - this is legit
|
||||
}
|
||||
} else if haveGroup {
|
||||
// If implicit group format, fill supplementary gids.
|
||||
user.Sgids = make([]int, len(groups))
|
||||
for i, group := range groups {
|
||||
user.Sgids[i] = group.Gid
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
// GetAdditionalGroups looks up a list of groups by name or group id
|
||||
// against the given /etc/group formatted data. If a group name cannot
|
||||
// be found, an error will be returned. If a group id cannot be found,
|
||||
// or the given group data is nil, the id will be returned as-is
|
||||
// provided it is in the legal range.
|
||||
func GetAdditionalGroups(additionalGroups []string, group io.Reader) ([]int, error) {
|
||||
var groups = []Group{}
|
||||
if group != nil {
|
||||
var err error
|
||||
groups, err = ParseGroupFilter(group, func(g Group) bool {
|
||||
for _, ag := range additionalGroups {
|
||||
if g.Name == ag || strconv.Itoa(g.Gid) == ag {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to find additional groups %v: %v", additionalGroups, err)
|
||||
}
|
||||
}
|
||||
|
||||
gidMap := make(map[int]struct{})
|
||||
for _, ag := range additionalGroups {
|
||||
var found bool
|
||||
for _, g := range groups {
|
||||
// if we found a matched group either by name or gid, take the
|
||||
// first matched as correct
|
||||
if g.Name == ag || strconv.Itoa(g.Gid) == ag {
|
||||
if _, ok := gidMap[g.Gid]; !ok {
|
||||
gidMap[g.Gid] = struct{}{}
|
||||
found = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
// we asked for a group but didn't find it. let's check to see
|
||||
// if we wanted a numeric group
|
||||
if !found {
|
||||
gid, err := strconv.Atoi(ag)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Unable to find group %s", ag)
|
||||
}
|
||||
// Ensure gid is inside gid range.
|
||||
if gid < minId || gid > maxId {
|
||||
return nil, ErrRange
|
||||
}
|
||||
gidMap[gid] = struct{}{}
|
||||
}
|
||||
}
|
||||
gids := []int{}
|
||||
for gid := range gidMap {
|
||||
gids = append(gids, gid)
|
||||
}
|
||||
return gids, nil
|
||||
}
|
||||
|
||||
// GetAdditionalGroupsPath is a wrapper around GetAdditionalGroups
|
||||
// that opens the groupPath given and gives it as an argument to
|
||||
// GetAdditionalGroups.
|
||||
func GetAdditionalGroupsPath(additionalGroups []string, groupPath string) ([]int, error) {
|
||||
group, err := os.Open(groupPath)
|
||||
if err == nil {
|
||||
defer group.Close()
|
||||
}
|
||||
return GetAdditionalGroups(additionalGroups, group)
|
||||
}
|
106
integration/vendor/github.com/opencontainers/runc/main.go
generated
vendored
Normal file
106
integration/vendor/github.com/opencontainers/runc/main.go
generated
vendored
Normal file
|
@ -0,0 +1,106 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/opencontainers/specs"
|
||||
)
|
||||
|
||||
const (
|
||||
version = "0.0.5"
|
||||
specConfig = "config.json"
|
||||
runtimeConfig = "runtime.json"
|
||||
usage = `Open Container Initiative runtime
|
||||
|
||||
runc is a command line client for running applications packaged according to
|
||||
the Open Container Format (OCF) and is a compliant implementation of the
|
||||
Open Container Initiative specification.
|
||||
|
||||
runc integrates well with existing process supervisors to provide a production
|
||||
container runtime environment for applications. It can be used with your
|
||||
existing process monitoring tools and the container will be spawned as a
|
||||
direct child of the process supervisor.
|
||||
|
||||
After creating config files for your root filesystem with runc, you can execute
|
||||
a container in your shell by running:
|
||||
|
||||
# cd /mycontainer
|
||||
# runc start [ -b bundle ]
|
||||
|
||||
If not specified, the default value for the 'bundle' is the current directory.
|
||||
'Bundle' is the directory where '` + specConfig + `' and '` + runtimeConfig + `' must be located.`
|
||||
)
|
||||
|
||||
func main() {
|
||||
app := cli.NewApp()
|
||||
app.Name = "runc"
|
||||
app.Usage = usage
|
||||
app.Version = version
|
||||
app.Flags = []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "id",
|
||||
Value: getDefaultID(),
|
||||
Usage: "specify the ID to be used for the container",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "debug",
|
||||
Usage: "enable debug output for logging",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "log",
|
||||
Usage: "set the log file path where internal debug information is written",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "log-format",
|
||||
Value: "text",
|
||||
Usage: "set the format used by logs ('text' (default), or 'json')",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "root",
|
||||
Value: specs.LinuxStateDirectory,
|
||||
Usage: "root directory for storage of container state (this should be located in tmpfs)",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "criu",
|
||||
Value: "criu",
|
||||
Usage: "path to the criu binary used for checkpoint and restore",
|
||||
},
|
||||
}
|
||||
app.Commands = []cli.Command{
|
||||
startCommand,
|
||||
checkpointCommand,
|
||||
eventsCommand,
|
||||
restoreCommand,
|
||||
killCommand,
|
||||
specCommand,
|
||||
pauseCommand,
|
||||
resumeCommand,
|
||||
execCommand,
|
||||
}
|
||||
app.Before = func(context *cli.Context) error {
|
||||
if context.GlobalBool("debug") {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
}
|
||||
if path := context.GlobalString("log"); path != "" {
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
logrus.SetOutput(f)
|
||||
}
|
||||
switch context.GlobalString("log-format") {
|
||||
case "text":
|
||||
// retain logrus's default.
|
||||
case "json":
|
||||
logrus.SetFormatter(new(logrus.JSONFormatter))
|
||||
default:
|
||||
logrus.Fatalf("unknown log-format %q", context.GlobalString("log-format"))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
}
|
5
integration/vendor/github.com/opencontainers/runc/main_unix.go
generated
vendored
Normal file
5
integration/vendor/github.com/opencontainers/runc/main_unix.go
generated
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
// +build linux
|
||||
|
||||
package main
|
||||
|
||||
import _ "github.com/opencontainers/runc/libcontainer/nsenter"
|
24
integration/vendor/github.com/opencontainers/runc/main_unsupported.go
generated
vendored
Normal file
24
integration/vendor/github.com/opencontainers/runc/main_unsupported.go
generated
vendored
Normal file
|
@ -0,0 +1,24 @@
|
|||
// +build !linux
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/codegangsta/cli"
|
||||
)
|
||||
|
||||
func getDefaultID() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
var (
|
||||
checkpointCommand cli.Command
|
||||
eventsCommand cli.Command
|
||||
restoreCommand cli.Command
|
||||
specCommand cli.Command
|
||||
killCommand cli.Command
|
||||
)
|
||||
|
||||
func runAction(*cli.Context) {
|
||||
logrus.Fatal("Current OS is not supported yet")
|
||||
}
|
33
integration/vendor/github.com/opencontainers/runc/pause.go
generated
vendored
Normal file
33
integration/vendor/github.com/opencontainers/runc/pause.go
generated
vendored
Normal file
|
@ -0,0 +1,33 @@
|
|||
// +build linux
|
||||
|
||||
package main
|
||||
|
||||
import "github.com/codegangsta/cli"
|
||||
|
||||
var pauseCommand = cli.Command{
|
||||
Name: "pause",
|
||||
Usage: "pause suspends all processes inside the container",
|
||||
Action: func(context *cli.Context) {
|
||||
container, err := getContainer(context)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
if err := container.Pause(); err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
var resumeCommand = cli.Command{
|
||||
Name: "resume",
|
||||
Usage: "resume resumes all processes that have been previously paused",
|
||||
Action: func(context *cli.Context) {
|
||||
container, err := getContainer(context)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
if err := container.Resume(); err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
},
|
||||
}
|
166
integration/vendor/github.com/opencontainers/runc/restore.go
generated
vendored
Normal file
166
integration/vendor/github.com/opencontainers/runc/restore.go
generated
vendored
Normal file
|
@ -0,0 +1,166 @@
|
|||
// +build linux
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/opencontainers/runc/libcontainer"
|
||||
"github.com/opencontainers/runc/libcontainer/configs"
|
||||
"github.com/opencontainers/specs"
|
||||
)
|
||||
|
||||
var restoreCommand = cli.Command{
|
||||
Name: "restore",
|
||||
Usage: "restore a container from a previous checkpoint",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "image-path",
|
||||
Value: "",
|
||||
Usage: "path to criu image files for restoring",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "work-path",
|
||||
Value: "",
|
||||
Usage: "path for saving work files and logs",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "tcp-established",
|
||||
Usage: "allow open tcp connections",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "ext-unix-sk",
|
||||
Usage: "allow external unix sockets",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "shell-job",
|
||||
Usage: "allow shell jobs",
|
||||
},
|
||||
cli.BoolFlag{
|
||||
Name: "file-locks",
|
||||
Usage: "handle file locks, for safety",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "manage-cgroups-mode",
|
||||
Value: "",
|
||||
Usage: "cgroups mode: 'soft' (default), 'full' and 'strict'.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
Name: "bundle, b",
|
||||
Value: "",
|
||||
Usage: "path to the root of the bundle directory",
|
||||
},
|
||||
},
|
||||
Action: func(context *cli.Context) {
|
||||
imagePath := context.String("image-path")
|
||||
if imagePath == "" {
|
||||
imagePath = getDefaultImagePath(context)
|
||||
}
|
||||
bundle := context.String("bundle")
|
||||
if bundle != "" {
|
||||
if err := os.Chdir(bundle); err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
}
|
||||
spec, rspec, err := loadSpec(specConfig, runtimeConfig)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
config, err := createLibcontainerConfig(context.GlobalString("id"), spec, rspec)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
status, err := restoreContainer(context, spec, config, imagePath)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
os.Exit(status)
|
||||
},
|
||||
}
|
||||
|
||||
func restoreContainer(context *cli.Context, spec *specs.LinuxSpec, config *configs.Config, imagePath string) (code int, err error) {
|
||||
rootuid := 0
|
||||
factory, err := loadFactory(context)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
container, err := factory.Load(context.GlobalString("id"))
|
||||
if err != nil {
|
||||
container, err = factory.Create(context.GlobalString("id"), config)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
}
|
||||
options := criuOptions(context)
|
||||
|
||||
status, err := container.Status()
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
if status == libcontainer.Running {
|
||||
fatal(fmt.Errorf("Container with id %s already running", context.GlobalString("id")))
|
||||
}
|
||||
|
||||
setManageCgroupsMode(context, options)
|
||||
|
||||
// ensure that the container is always removed if we were the process
|
||||
// that created it.
|
||||
defer func() {
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
status, err := container.Status()
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
if status != libcontainer.Checkpointed {
|
||||
if err := container.Destroy(); err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
if err := os.RemoveAll(options.ImagesDirectory); err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
process := &libcontainer.Process{}
|
||||
tty, err := newTty(spec.Process.Terminal, process, rootuid)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
handler := newSignalHandler(tty)
|
||||
defer handler.Close()
|
||||
if err := container.Restore(process, options); err != nil {
|
||||
cstatus, cerr := container.Status()
|
||||
if cerr != nil {
|
||||
logrus.Error(cerr)
|
||||
}
|
||||
if cstatus == libcontainer.Destroyed {
|
||||
dest := filepath.Join(context.GlobalString("root"), context.GlobalString("id"))
|
||||
if errVal := os.RemoveAll(dest); errVal != nil {
|
||||
logrus.Error(errVal)
|
||||
}
|
||||
}
|
||||
return -1, err
|
||||
}
|
||||
return handler.forward(process)
|
||||
}
|
||||
|
||||
func criuOptions(context *cli.Context) *libcontainer.CriuOpts {
|
||||
imagePath := getCheckpointImagePath(context)
|
||||
if err := os.MkdirAll(imagePath, 0655); err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
return &libcontainer.CriuOpts{
|
||||
ImagesDirectory: imagePath,
|
||||
WorkDirectory: context.String("work-path"),
|
||||
LeaveRunning: context.Bool("leave-running"),
|
||||
TcpEstablished: context.Bool("tcp-established"),
|
||||
ExternalUnixConnections: context.Bool("ext-unix-sk"),
|
||||
ShellJob: context.Bool("shell-job"),
|
||||
FileLocks: context.Bool("file-locks"),
|
||||
}
|
||||
}
|
49
integration/vendor/github.com/opencontainers/runc/rlimit_linux.go
generated
vendored
Normal file
49
integration/vendor/github.com/opencontainers/runc/rlimit_linux.go
generated
vendored
Normal file
|
@ -0,0 +1,49 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
const (
|
||||
RLIMIT_CPU = iota // CPU time in sec
|
||||
RLIMIT_FSIZE // Maximum filesize
|
||||
RLIMIT_DATA // max data size
|
||||
RLIMIT_STACK // max stack size
|
||||
RLIMIT_CORE // max core file size
|
||||
RLIMIT_RSS // max resident set size
|
||||
RLIMIT_NPROC // max number of processes
|
||||
RLIMIT_NOFILE // max number of open files
|
||||
RLIMIT_MEMLOCK // max locked-in-memory address space
|
||||
RLIMIT_AS // address space limit
|
||||
RLIMIT_LOCKS // maximum file locks held
|
||||
RLIMIT_SIGPENDING // max number of pending signals
|
||||
RLIMIT_MSGQUEUE // maximum bytes in POSIX mqueues
|
||||
RLIMIT_NICE // max nice prio allowed to raise to
|
||||
RLIMIT_RTPRIO // maximum realtime priority
|
||||
RLIMIT_RTTIME // timeout for RT tasks in us
|
||||
)
|
||||
|
||||
var rlimitMap = map[string]int{
|
||||
"RLIMIT_CPU": RLIMIT_CPU,
|
||||
"RLIMIT_FSIZE": RLIMIT_FSIZE,
|
||||
"RLIMIT_DATA": RLIMIT_DATA,
|
||||
"RLIMIT_STACK": RLIMIT_STACK,
|
||||
"RLIMIT_CORE": RLIMIT_CORE,
|
||||
"RLIMIT_RSS": RLIMIT_RSS,
|
||||
"RLIMIT_NPROC": RLIMIT_NPROC,
|
||||
"RLIMIT_NOFILE": RLIMIT_NOFILE,
|
||||
"RLIMIT_MEMLOCK": RLIMIT_MEMLOCK,
|
||||
"RLIMIT_AS": RLIMIT_AS,
|
||||
"RLIMIT_LOCKS": RLIMIT_LOCKS,
|
||||
"RLIMIT_SGPENDING": RLIMIT_SIGPENDING,
|
||||
"RLIMIT_MSGQUEUE": RLIMIT_MSGQUEUE,
|
||||
"RLIMIT_NICE": RLIMIT_NICE,
|
||||
"RLIMIT_RTPRIO": RLIMIT_RTPRIO,
|
||||
"RLIMIT_RTTIME": RLIMIT_RTTIME,
|
||||
}
|
||||
|
||||
func strToRlimit(key string) (int, error) {
|
||||
rl, ok := rlimitMap[key]
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("Wrong rlimit value: %s", key)
|
||||
}
|
||||
return rl, nil
|
||||
}
|
113
integration/vendor/github.com/opencontainers/runc/signals.go
generated
vendored
Normal file
113
integration/vendor/github.com/opencontainers/runc/signals.go
generated
vendored
Normal file
|
@ -0,0 +1,113 @@
|
|||
// +build linux
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/signal"
|
||||
"syscall"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/opencontainers/runc/libcontainer"
|
||||
"github.com/opencontainers/runc/libcontainer/utils"
|
||||
)
|
||||
|
||||
const signalBufferSize = 2048
|
||||
|
||||
// newSignalHandler returns a signal handler for processing SIGCHLD and SIGWINCH signals
|
||||
// while still forwarding all other signals to the process.
|
||||
func newSignalHandler(tty *tty) *signalHandler {
|
||||
// ensure that we have a large buffer size so that we do not miss any signals
|
||||
// incase we are not processing them fast enough.
|
||||
s := make(chan os.Signal, signalBufferSize)
|
||||
// handle all signals for the process.
|
||||
signal.Notify(s)
|
||||
return &signalHandler{
|
||||
tty: tty,
|
||||
signals: s,
|
||||
}
|
||||
}
|
||||
|
||||
// exit models a process exit status with the pid and
|
||||
// exit status.
|
||||
type exit struct {
|
||||
pid int
|
||||
status int
|
||||
}
|
||||
|
||||
type signalHandler struct {
|
||||
signals chan os.Signal
|
||||
tty *tty
|
||||
}
|
||||
|
||||
// forward handles the main signal event loop forwarding, resizing, or reaping depending
|
||||
// on the signal received.
|
||||
func (h *signalHandler) forward(process *libcontainer.Process) (int, error) {
|
||||
// make sure we know the pid of our main process so that we can return
|
||||
// after it dies.
|
||||
pid1, err := process.Pid()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
// perform the initial tty resize.
|
||||
h.tty.resize()
|
||||
for s := range h.signals {
|
||||
switch s {
|
||||
case syscall.SIGWINCH:
|
||||
h.tty.resize()
|
||||
case syscall.SIGCHLD:
|
||||
exits, err := h.reap()
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
for _, e := range exits {
|
||||
logrus.WithFields(logrus.Fields{
|
||||
"pid": e.pid,
|
||||
"status": e.status,
|
||||
}).Debug("process exited")
|
||||
if e.pid == pid1 {
|
||||
// call Wait() on the process even though we already have the exit
|
||||
// status because we must ensure that any of the go specific process
|
||||
// fun such as flushing pipes are complete before we return.
|
||||
process.Wait()
|
||||
return e.status, nil
|
||||
}
|
||||
}
|
||||
default:
|
||||
logrus.Debugf("sending signal to process %s", s)
|
||||
if err := syscall.Kill(pid1, s.(syscall.Signal)); err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
// reap runs wait4 in a loop until we have finished processing any existing exits
|
||||
// then returns all exits to the main event loop for further processing.
|
||||
func (h *signalHandler) reap() (exits []exit, err error) {
|
||||
var (
|
||||
ws syscall.WaitStatus
|
||||
rus syscall.Rusage
|
||||
)
|
||||
for {
|
||||
pid, err := syscall.Wait4(-1, &ws, syscall.WNOHANG, &rus)
|
||||
if err != nil {
|
||||
if err == syscall.ECHILD {
|
||||
return exits, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if pid <= 0 {
|
||||
return exits, nil
|
||||
}
|
||||
exits = append(exits, exit{
|
||||
pid: pid,
|
||||
status: utils.ExitStatus(ws),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (h *signalHandler) Close() error {
|
||||
return h.tty.Close()
|
||||
}
|
730
integration/vendor/github.com/opencontainers/runc/spec.go
generated
vendored
Normal file
730
integration/vendor/github.com/opencontainers/runc/spec.go
generated
vendored
Normal file
|
@ -0,0 +1,730 @@
|
|||
// +build linux
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/opencontainers/runc/libcontainer/cgroups"
|
||||
"github.com/opencontainers/runc/libcontainer/configs"
|
||||
"github.com/opencontainers/runc/libcontainer/seccomp"
|
||||
"github.com/opencontainers/specs"
|
||||
)
|
||||
|
||||
var specCommand = cli.Command{
|
||||
Name: "spec",
|
||||
Usage: "create a new specification file",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "bundle, b",
|
||||
Value: "",
|
||||
Usage: "path to the root of the bundle directory",
|
||||
},
|
||||
},
|
||||
Action: func(context *cli.Context) {
|
||||
spec := specs.LinuxSpec{
|
||||
Spec: specs.Spec{
|
||||
Version: specs.Version,
|
||||
Platform: specs.Platform{
|
||||
OS: runtime.GOOS,
|
||||
Arch: runtime.GOARCH,
|
||||
},
|
||||
Root: specs.Root{
|
||||
Path: "rootfs",
|
||||
Readonly: true,
|
||||
},
|
||||
Process: specs.Process{
|
||||
Terminal: true,
|
||||
User: specs.User{},
|
||||
Args: []string{
|
||||
"sh",
|
||||
},
|
||||
Env: []string{
|
||||
"PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin",
|
||||
"TERM=xterm",
|
||||
},
|
||||
},
|
||||
Hostname: "shell",
|
||||
Mounts: []specs.MountPoint{
|
||||
{
|
||||
Name: "proc",
|
||||
Path: "/proc",
|
||||
},
|
||||
{
|
||||
Name: "dev",
|
||||
Path: "/dev",
|
||||
},
|
||||
{
|
||||
Name: "devpts",
|
||||
Path: "/dev/pts",
|
||||
},
|
||||
{
|
||||
Name: "shm",
|
||||
Path: "/dev/shm",
|
||||
},
|
||||
{
|
||||
Name: "mqueue",
|
||||
Path: "/dev/mqueue",
|
||||
},
|
||||
{
|
||||
Name: "sysfs",
|
||||
Path: "/sys",
|
||||
},
|
||||
{
|
||||
Name: "cgroup",
|
||||
Path: "/sys/fs/cgroup",
|
||||
},
|
||||
},
|
||||
},
|
||||
Linux: specs.Linux{
|
||||
Capabilities: []string{
|
||||
"CAP_AUDIT_WRITE",
|
||||
"CAP_KILL",
|
||||
"CAP_NET_BIND_SERVICE",
|
||||
},
|
||||
},
|
||||
}
|
||||
rspec := specs.LinuxRuntimeSpec{
|
||||
RuntimeSpec: specs.RuntimeSpec{
|
||||
Mounts: map[string]specs.Mount{
|
||||
"proc": {
|
||||
Type: "proc",
|
||||
Source: "proc",
|
||||
Options: nil,
|
||||
},
|
||||
"dev": {
|
||||
Type: "tmpfs",
|
||||
Source: "tmpfs",
|
||||
Options: []string{"nosuid", "strictatime", "mode=755", "size=65536k"},
|
||||
},
|
||||
"devpts": {
|
||||
Type: "devpts",
|
||||
Source: "devpts",
|
||||
Options: []string{"nosuid", "noexec", "newinstance", "ptmxmode=0666", "mode=0620", "gid=5"},
|
||||
},
|
||||
"shm": {
|
||||
Type: "tmpfs",
|
||||
Source: "shm",
|
||||
Options: []string{"nosuid", "noexec", "nodev", "mode=1777", "size=65536k"},
|
||||
},
|
||||
"mqueue": {
|
||||
Type: "mqueue",
|
||||
Source: "mqueue",
|
||||
Options: []string{"nosuid", "noexec", "nodev"},
|
||||
},
|
||||
"sysfs": {
|
||||
Type: "sysfs",
|
||||
Source: "sysfs",
|
||||
Options: []string{"nosuid", "noexec", "nodev"},
|
||||
},
|
||||
"cgroup": {
|
||||
Type: "cgroup",
|
||||
Source: "cgroup",
|
||||
Options: []string{"nosuid", "noexec", "nodev", "relatime", "ro"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Linux: specs.LinuxRuntime{
|
||||
Namespaces: []specs.Namespace{
|
||||
{
|
||||
Type: "pid",
|
||||
},
|
||||
{
|
||||
Type: "network",
|
||||
},
|
||||
{
|
||||
Type: "ipc",
|
||||
},
|
||||
{
|
||||
Type: "uts",
|
||||
},
|
||||
{
|
||||
Type: "mount",
|
||||
},
|
||||
},
|
||||
Rlimits: []specs.Rlimit{
|
||||
{
|
||||
Type: "RLIMIT_NOFILE",
|
||||
Hard: uint64(1024),
|
||||
Soft: uint64(1024),
|
||||
},
|
||||
},
|
||||
Devices: []specs.Device{
|
||||
{
|
||||
Type: 'c',
|
||||
Path: "/dev/null",
|
||||
Major: 1,
|
||||
Minor: 3,
|
||||
Permissions: "rwm",
|
||||
FileMode: 0666,
|
||||
UID: 0,
|
||||
GID: 0,
|
||||
},
|
||||
{
|
||||
Type: 'c',
|
||||
Path: "/dev/random",
|
||||
Major: 1,
|
||||
Minor: 8,
|
||||
Permissions: "rwm",
|
||||
FileMode: 0666,
|
||||
UID: 0,
|
||||
GID: 0,
|
||||
},
|
||||
{
|
||||
Type: 'c',
|
||||
Path: "/dev/full",
|
||||
Major: 1,
|
||||
Minor: 7,
|
||||
Permissions: "rwm",
|
||||
FileMode: 0666,
|
||||
UID: 0,
|
||||
GID: 0,
|
||||
},
|
||||
{
|
||||
Type: 'c',
|
||||
Path: "/dev/tty",
|
||||
Major: 5,
|
||||
Minor: 0,
|
||||
Permissions: "rwm",
|
||||
FileMode: 0666,
|
||||
UID: 0,
|
||||
GID: 0,
|
||||
},
|
||||
{
|
||||
Type: 'c',
|
||||
Path: "/dev/zero",
|
||||
Major: 1,
|
||||
Minor: 5,
|
||||
Permissions: "rwm",
|
||||
FileMode: 0666,
|
||||
UID: 0,
|
||||
GID: 0,
|
||||
},
|
||||
{
|
||||
Type: 'c',
|
||||
Path: "/dev/urandom",
|
||||
Major: 1,
|
||||
Minor: 9,
|
||||
Permissions: "rwm",
|
||||
FileMode: 0666,
|
||||
UID: 0,
|
||||
GID: 0,
|
||||
},
|
||||
},
|
||||
Resources: &specs.Resources{
|
||||
Memory: specs.Memory{
|
||||
Swappiness: -1,
|
||||
},
|
||||
},
|
||||
Seccomp: specs.Seccomp{
|
||||
DefaultAction: "SCMP_ACT_ALLOW",
|
||||
Syscalls: []*specs.Syscall{},
|
||||
},
|
||||
},
|
||||
}
|
||||
checkNoFile := func(name string) error {
|
||||
_, err := os.Stat(name)
|
||||
if err == nil {
|
||||
return fmt.Errorf("File %s exists. Remove it first", name)
|
||||
}
|
||||
if !os.IsNotExist(err) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
bundle := context.String("bundle")
|
||||
if bundle != "" {
|
||||
if err := os.Chdir(bundle); err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
}
|
||||
if err := checkNoFile(specConfig); err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
if err := checkNoFile(runtimeConfig); err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
data, err := json.MarshalIndent(&spec, "", "\t")
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
if err := ioutil.WriteFile(specConfig, data, 0666); err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
rdata, err := json.MarshalIndent(&rspec, "", "\t")
|
||||
if err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
if err := ioutil.WriteFile(runtimeConfig, rdata, 0666); err != nil {
|
||||
logrus.Fatal(err)
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
var namespaceMapping = map[specs.NamespaceType]configs.NamespaceType{
|
||||
specs.PIDNamespace: configs.NEWPID,
|
||||
specs.NetworkNamespace: configs.NEWNET,
|
||||
specs.MountNamespace: configs.NEWNS,
|
||||
specs.UserNamespace: configs.NEWUSER,
|
||||
specs.IPCNamespace: configs.NEWIPC,
|
||||
specs.UTSNamespace: configs.NEWUTS,
|
||||
}
|
||||
|
||||
var mountPropagationMapping = map[string]int{
|
||||
"rprivate": syscall.MS_PRIVATE | syscall.MS_REC,
|
||||
"private": syscall.MS_PRIVATE,
|
||||
"rslave": syscall.MS_SLAVE | syscall.MS_REC,
|
||||
"slave": syscall.MS_SLAVE,
|
||||
"rshared": syscall.MS_SHARED | syscall.MS_REC,
|
||||
"shared": syscall.MS_SHARED,
|
||||
"": syscall.MS_PRIVATE | syscall.MS_REC,
|
||||
}
|
||||
|
||||
// loadSpec loads the specification from the provided path.
|
||||
// If the path is empty then the default path will be "config.json"
|
||||
func loadSpec(cPath, rPath string) (spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec, err error) {
|
||||
cf, err := os.Open(cPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil, nil, fmt.Errorf("JSON specification file %s not found", cPath)
|
||||
}
|
||||
return spec, rspec, err
|
||||
}
|
||||
defer cf.Close()
|
||||
|
||||
rf, err := os.Open(rPath)
|
||||
if err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return nil, nil, fmt.Errorf("JSON runtime config file %s not found", rPath)
|
||||
}
|
||||
return spec, rspec, err
|
||||
}
|
||||
defer rf.Close()
|
||||
|
||||
if err = json.NewDecoder(cf).Decode(&spec); err != nil {
|
||||
return spec, rspec, err
|
||||
}
|
||||
if err = json.NewDecoder(rf).Decode(&rspec); err != nil {
|
||||
return spec, rspec, err
|
||||
}
|
||||
return spec, rspec, checkSpecVersion(spec)
|
||||
}
|
||||
|
||||
// checkSpecVersion makes sure that the spec version matches runc's while we are in the initial
|
||||
// development period. It is better to hard fail than have missing fields or options in the spec.
|
||||
func checkSpecVersion(s *specs.LinuxSpec) error {
|
||||
if s.Version != specs.Version {
|
||||
return fmt.Errorf("spec version is not compatible with implemented version %q: spec %q", specs.Version, s.Version)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func createLibcontainerConfig(cgroupName string, spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec) (*configs.Config, error) {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rootfsPath := spec.Root.Path
|
||||
if !filepath.IsAbs(rootfsPath) {
|
||||
rootfsPath = filepath.Join(cwd, rootfsPath)
|
||||
}
|
||||
config := &configs.Config{
|
||||
Rootfs: rootfsPath,
|
||||
Capabilities: spec.Linux.Capabilities,
|
||||
Readonlyfs: spec.Root.Readonly,
|
||||
Hostname: spec.Hostname,
|
||||
}
|
||||
|
||||
exists := false
|
||||
if config.RootPropagation, exists = mountPropagationMapping[rspec.Linux.RootfsPropagation]; !exists {
|
||||
return nil, fmt.Errorf("rootfsPropagation=%v is not supported", rspec.Linux.RootfsPropagation)
|
||||
}
|
||||
|
||||
for _, ns := range rspec.Linux.Namespaces {
|
||||
t, exists := namespaceMapping[ns.Type]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("namespace %q does not exist", ns)
|
||||
}
|
||||
config.Namespaces.Add(t, ns.Path)
|
||||
}
|
||||
if config.Namespaces.Contains(configs.NEWNET) {
|
||||
config.Networks = []*configs.Network{
|
||||
{
|
||||
Type: "loopback",
|
||||
},
|
||||
}
|
||||
}
|
||||
for _, mp := range spec.Mounts {
|
||||
m, ok := rspec.Mounts[mp.Name]
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("Mount with Name %q not found in runtime config", mp.Name)
|
||||
}
|
||||
config.Mounts = append(config.Mounts, createLibcontainerMount(cwd, mp.Path, m))
|
||||
}
|
||||
if err := createDevices(rspec, config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := setupUserNamespace(rspec, config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, rlimit := range rspec.Linux.Rlimits {
|
||||
rl, err := createLibContainerRlimit(rlimit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.Rlimits = append(config.Rlimits, rl)
|
||||
}
|
||||
c, err := createCgroupConfig(cgroupName, rspec, config.Devices)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.Cgroups = c
|
||||
if config.Readonlyfs {
|
||||
setReadonly(config)
|
||||
config.MaskPaths = []string{
|
||||
"/proc/kcore",
|
||||
}
|
||||
config.ReadonlyPaths = []string{
|
||||
"/proc/sys", "/proc/sysrq-trigger", "/proc/irq", "/proc/bus",
|
||||
}
|
||||
}
|
||||
seccomp, err := setupSeccomp(&rspec.Linux.Seccomp)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
config.Seccomp = seccomp
|
||||
config.Sysctl = rspec.Linux.Sysctl
|
||||
config.ProcessLabel = rspec.Linux.SelinuxProcessLabel
|
||||
config.AppArmorProfile = rspec.Linux.ApparmorProfile
|
||||
for _, g := range spec.Process.User.AdditionalGids {
|
||||
config.AdditionalGroups = append(config.AdditionalGroups, strconv.FormatUint(uint64(g), 10))
|
||||
}
|
||||
createHooks(rspec, config)
|
||||
config.Version = specs.Version
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func createLibcontainerMount(cwd, dest string, m specs.Mount) *configs.Mount {
|
||||
flags, pgflags, data := parseMountOptions(m.Options)
|
||||
source := m.Source
|
||||
if m.Type == "bind" {
|
||||
if !filepath.IsAbs(source) {
|
||||
source = filepath.Join(cwd, m.Source)
|
||||
}
|
||||
}
|
||||
return &configs.Mount{
|
||||
Device: m.Type,
|
||||
Source: source,
|
||||
Destination: dest,
|
||||
Data: data,
|
||||
Flags: flags,
|
||||
PropagationFlags: pgflags,
|
||||
}
|
||||
}
|
||||
|
||||
func createCgroupConfig(name string, spec *specs.LinuxRuntimeSpec, devices []*configs.Device) (*configs.Cgroup, error) {
|
||||
myCgroupPath, err := cgroups.GetThisCgroupDir("devices")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
c := &configs.Cgroup{
|
||||
Name: name,
|
||||
Parent: myCgroupPath,
|
||||
AllowedDevices: append(devices, allowedDevices...),
|
||||
}
|
||||
r := spec.Linux.Resources
|
||||
c.Memory = r.Memory.Limit
|
||||
c.MemoryReservation = r.Memory.Reservation
|
||||
c.MemorySwap = r.Memory.Swap
|
||||
c.KernelMemory = r.Memory.Kernel
|
||||
c.MemorySwappiness = r.Memory.Swappiness
|
||||
c.CpuShares = r.CPU.Shares
|
||||
c.CpuQuota = r.CPU.Quota
|
||||
c.CpuPeriod = r.CPU.Period
|
||||
c.CpuRtRuntime = r.CPU.RealtimeRuntime
|
||||
c.CpuRtPeriod = r.CPU.RealtimePeriod
|
||||
c.CpusetCpus = r.CPU.Cpus
|
||||
c.CpusetMems = r.CPU.Mems
|
||||
c.BlkioWeight = r.BlockIO.Weight
|
||||
c.BlkioLeafWeight = r.BlockIO.LeafWeight
|
||||
for _, wd := range r.BlockIO.WeightDevice {
|
||||
weightDevice := configs.NewWeightDevice(wd.Major, wd.Minor, wd.Weight, wd.LeafWeight)
|
||||
c.BlkioWeightDevice = append(c.BlkioWeightDevice, weightDevice)
|
||||
}
|
||||
for _, td := range r.BlockIO.ThrottleReadBpsDevice {
|
||||
throttleDevice := configs.NewThrottleDevice(td.Major, td.Minor, td.Rate)
|
||||
c.BlkioThrottleReadBpsDevice = append(c.BlkioThrottleReadBpsDevice, throttleDevice)
|
||||
}
|
||||
for _, td := range r.BlockIO.ThrottleWriteBpsDevice {
|
||||
throttleDevice := configs.NewThrottleDevice(td.Major, td.Minor, td.Rate)
|
||||
c.BlkioThrottleWriteBpsDevice = append(c.BlkioThrottleWriteBpsDevice, throttleDevice)
|
||||
}
|
||||
for _, td := range r.BlockIO.ThrottleReadIOPSDevice {
|
||||
throttleDevice := configs.NewThrottleDevice(td.Major, td.Minor, td.Rate)
|
||||
c.BlkioThrottleReadIOPSDevice = append(c.BlkioThrottleReadIOPSDevice, throttleDevice)
|
||||
}
|
||||
for _, td := range r.BlockIO.ThrottleWriteIOPSDevice {
|
||||
throttleDevice := configs.NewThrottleDevice(td.Major, td.Minor, td.Rate)
|
||||
c.BlkioThrottleWriteIOPSDevice = append(c.BlkioThrottleWriteIOPSDevice, throttleDevice)
|
||||
}
|
||||
for _, l := range r.HugepageLimits {
|
||||
c.HugetlbLimit = append(c.HugetlbLimit, &configs.HugepageLimit{
|
||||
Pagesize: l.Pagesize,
|
||||
Limit: l.Limit,
|
||||
})
|
||||
}
|
||||
c.OomKillDisable = r.DisableOOMKiller
|
||||
c.NetClsClassid = r.Network.ClassID
|
||||
for _, m := range r.Network.Priorities {
|
||||
c.NetPrioIfpriomap = append(c.NetPrioIfpriomap, &configs.IfPrioMap{
|
||||
Interface: m.Name,
|
||||
Priority: m.Priority,
|
||||
})
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
||||
func createDevices(spec *specs.LinuxRuntimeSpec, config *configs.Config) error {
|
||||
for _, d := range spec.Linux.Devices {
|
||||
device := &configs.Device{
|
||||
Type: d.Type,
|
||||
Path: d.Path,
|
||||
Major: d.Major,
|
||||
Minor: d.Minor,
|
||||
Permissions: d.Permissions,
|
||||
FileMode: d.FileMode,
|
||||
Uid: d.UID,
|
||||
Gid: d.GID,
|
||||
}
|
||||
config.Devices = append(config.Devices, device)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func setReadonly(config *configs.Config) {
|
||||
for _, m := range config.Mounts {
|
||||
if m.Device == "sysfs" {
|
||||
m.Flags |= syscall.MS_RDONLY
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setupUserNamespace(spec *specs.LinuxRuntimeSpec, config *configs.Config) error {
|
||||
if len(spec.Linux.UIDMappings) == 0 {
|
||||
return nil
|
||||
}
|
||||
config.Namespaces.Add(configs.NEWUSER, "")
|
||||
create := func(m specs.IDMapping) configs.IDMap {
|
||||
return configs.IDMap{
|
||||
HostID: int(m.HostID),
|
||||
ContainerID: int(m.ContainerID),
|
||||
Size: int(m.Size),
|
||||
}
|
||||
}
|
||||
for _, m := range spec.Linux.UIDMappings {
|
||||
config.UidMappings = append(config.UidMappings, create(m))
|
||||
}
|
||||
for _, m := range spec.Linux.GIDMappings {
|
||||
config.GidMappings = append(config.GidMappings, create(m))
|
||||
}
|
||||
rootUID, err := config.HostUID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rootGID, err := config.HostGID()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, node := range config.Devices {
|
||||
node.Uid = uint32(rootUID)
|
||||
node.Gid = uint32(rootGID)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func createLibContainerRlimit(rlimit specs.Rlimit) (configs.Rlimit, error) {
|
||||
rl, err := strToRlimit(rlimit.Type)
|
||||
if err != nil {
|
||||
return configs.Rlimit{}, err
|
||||
}
|
||||
return configs.Rlimit{
|
||||
Type: rl,
|
||||
Hard: uint64(rlimit.Hard),
|
||||
Soft: uint64(rlimit.Soft),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// parseMountOptions parses the string and returns the flags, propagation
|
||||
// flags and any mount data that it contains.
|
||||
func parseMountOptions(options []string) (int, []int, string) {
|
||||
var (
|
||||
flag int
|
||||
pgflag []int
|
||||
data []string
|
||||
)
|
||||
flags := map[string]struct {
|
||||
clear bool
|
||||
flag int
|
||||
}{
|
||||
"async": {true, syscall.MS_SYNCHRONOUS},
|
||||
"atime": {true, syscall.MS_NOATIME},
|
||||
"bind": {false, syscall.MS_BIND},
|
||||
"defaults": {false, 0},
|
||||
"dev": {true, syscall.MS_NODEV},
|
||||
"diratime": {true, syscall.MS_NODIRATIME},
|
||||
"dirsync": {false, syscall.MS_DIRSYNC},
|
||||
"exec": {true, syscall.MS_NOEXEC},
|
||||
"mand": {false, syscall.MS_MANDLOCK},
|
||||
"noatime": {false, syscall.MS_NOATIME},
|
||||
"nodev": {false, syscall.MS_NODEV},
|
||||
"nodiratime": {false, syscall.MS_NODIRATIME},
|
||||
"noexec": {false, syscall.MS_NOEXEC},
|
||||
"nomand": {true, syscall.MS_MANDLOCK},
|
||||
"norelatime": {true, syscall.MS_RELATIME},
|
||||
"nostrictatime": {true, syscall.MS_STRICTATIME},
|
||||
"nosuid": {false, syscall.MS_NOSUID},
|
||||
"rbind": {false, syscall.MS_BIND | syscall.MS_REC},
|
||||
"relatime": {false, syscall.MS_RELATIME},
|
||||
"remount": {false, syscall.MS_REMOUNT},
|
||||
"ro": {false, syscall.MS_RDONLY},
|
||||
"rw": {true, syscall.MS_RDONLY},
|
||||
"strictatime": {false, syscall.MS_STRICTATIME},
|
||||
"suid": {true, syscall.MS_NOSUID},
|
||||
"sync": {false, syscall.MS_SYNCHRONOUS},
|
||||
}
|
||||
propagationFlags := map[string]struct {
|
||||
clear bool
|
||||
flag int
|
||||
}{
|
||||
"private": {false, syscall.MS_PRIVATE},
|
||||
"shared": {false, syscall.MS_SHARED},
|
||||
"slave": {false, syscall.MS_SLAVE},
|
||||
"unbindable": {false, syscall.MS_UNBINDABLE},
|
||||
"rprivate": {false, syscall.MS_PRIVATE | syscall.MS_REC},
|
||||
"rshared": {false, syscall.MS_SHARED | syscall.MS_REC},
|
||||
"rslave": {false, syscall.MS_SLAVE | syscall.MS_REC},
|
||||
"runbindable": {false, syscall.MS_UNBINDABLE | syscall.MS_REC},
|
||||
}
|
||||
for _, o := range options {
|
||||
// If the option does not exist in the flags table or the flag
|
||||
// is not supported on the platform,
|
||||
// then it is a data value for a specific fs type
|
||||
if f, exists := flags[o]; exists && f.flag != 0 {
|
||||
if f.clear {
|
||||
flag &= ^f.flag
|
||||
} else {
|
||||
flag |= f.flag
|
||||
}
|
||||
} else if f, exists := propagationFlags[o]; exists && f.flag != 0 {
|
||||
pgflag = append(pgflag, f.flag)
|
||||
} else {
|
||||
data = append(data, o)
|
||||
}
|
||||
}
|
||||
return flag, pgflag, strings.Join(data, ",")
|
||||
}
|
||||
|
||||
func setupSeccomp(config *specs.Seccomp) (*configs.Seccomp, error) {
|
||||
if config == nil {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// No default action specified, no syscalls listed, assume seccomp disabled
|
||||
if config.DefaultAction == "" && len(config.Syscalls) == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
newConfig := new(configs.Seccomp)
|
||||
newConfig.Syscalls = []*configs.Syscall{}
|
||||
|
||||
if len(config.Architectures) > 0 {
|
||||
newConfig.Architectures = []string{}
|
||||
for _, arch := range config.Architectures {
|
||||
newArch, err := seccomp.ConvertStringToArch(string(arch))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newConfig.Architectures = append(newConfig.Architectures, newArch)
|
||||
}
|
||||
}
|
||||
|
||||
// Convert default action from string representation
|
||||
newDefaultAction, err := seccomp.ConvertStringToAction(string(config.DefaultAction))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newConfig.DefaultAction = newDefaultAction
|
||||
|
||||
// Loop through all syscall blocks and convert them to libcontainer format
|
||||
for _, call := range config.Syscalls {
|
||||
newAction, err := seccomp.ConvertStringToAction(string(call.Action))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newCall := configs.Syscall{
|
||||
Name: call.Name,
|
||||
Action: newAction,
|
||||
Args: []*configs.Arg{},
|
||||
}
|
||||
|
||||
// Loop through all the arguments of the syscall and convert them
|
||||
for _, arg := range call.Args {
|
||||
newOp, err := seccomp.ConvertStringToOperator(string(arg.Op))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newArg := configs.Arg{
|
||||
Index: arg.Index,
|
||||
Value: arg.Value,
|
||||
ValueTwo: arg.ValueTwo,
|
||||
Op: newOp,
|
||||
}
|
||||
|
||||
newCall.Args = append(newCall.Args, &newArg)
|
||||
}
|
||||
|
||||
newConfig.Syscalls = append(newConfig.Syscalls, &newCall)
|
||||
}
|
||||
|
||||
return newConfig, nil
|
||||
}
|
||||
|
||||
func createHooks(rspec *specs.LinuxRuntimeSpec, config *configs.Config) {
|
||||
config.Hooks = &configs.Hooks{}
|
||||
for _, h := range rspec.Hooks.Prestart {
|
||||
cmd := configs.Command{
|
||||
Path: h.Path,
|
||||
Args: h.Args,
|
||||
Env: h.Env,
|
||||
}
|
||||
config.Hooks.Prestart = append(config.Hooks.Prestart, configs.NewCommandHook(cmd))
|
||||
}
|
||||
for _, h := range rspec.Hooks.Poststart {
|
||||
cmd := configs.Command{
|
||||
Path: h.Path,
|
||||
Args: h.Args,
|
||||
Env: h.Env,
|
||||
}
|
||||
config.Hooks.Poststart = append(config.Hooks.Poststart, configs.NewCommandHook(cmd))
|
||||
}
|
||||
for _, h := range rspec.Hooks.Poststop {
|
||||
cmd := configs.Command{
|
||||
Path: h.Path,
|
||||
Args: h.Args,
|
||||
Env: h.Env,
|
||||
}
|
||||
config.Hooks.Poststop = append(config.Hooks.Poststop, configs.NewCommandHook(cmd))
|
||||
}
|
||||
}
|
154
integration/vendor/github.com/opencontainers/runc/start.go
generated
vendored
Normal file
154
integration/vendor/github.com/opencontainers/runc/start.go
generated
vendored
Normal file
|
@ -0,0 +1,154 @@
|
|||
// +build linux
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strconv"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/opencontainers/runc/libcontainer"
|
||||
"github.com/opencontainers/specs"
|
||||
)
|
||||
|
||||
const SD_LISTEN_FDS_START = 3
|
||||
|
||||
// default action is to start a container
|
||||
var startCommand = cli.Command{
|
||||
Name: "start",
|
||||
Usage: "create and run a container",
|
||||
Flags: []cli.Flag{
|
||||
cli.StringFlag{
|
||||
Name: "bundle, b",
|
||||
Value: "",
|
||||
Usage: "path to the root of the bundle directory",
|
||||
},
|
||||
},
|
||||
Action: func(context *cli.Context) {
|
||||
bundle := context.String("bundle")
|
||||
if bundle != "" {
|
||||
if err := os.Chdir(bundle); err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
}
|
||||
spec, rspec, err := loadSpec(specConfig, runtimeConfig)
|
||||
if err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
|
||||
notifySocket := os.Getenv("NOTIFY_SOCKET")
|
||||
if notifySocket != "" {
|
||||
setupSdNotify(spec, rspec, notifySocket)
|
||||
}
|
||||
|
||||
listenFds := os.Getenv("LISTEN_FDS")
|
||||
listenPid := os.Getenv("LISTEN_PID")
|
||||
|
||||
if listenFds != "" && listenPid == strconv.Itoa(os.Getpid()) {
|
||||
setupSocketActivation(spec, listenFds)
|
||||
}
|
||||
|
||||
if os.Geteuid() != 0 {
|
||||
logrus.Fatal("runc should be run as root")
|
||||
}
|
||||
status, err := startContainer(context, spec, rspec)
|
||||
if err != nil {
|
||||
logrus.Fatalf("Container start failed: %v", err)
|
||||
}
|
||||
// exit with the container's exit status so any external supervisor is
|
||||
// notified of the exit with the correct exit status.
|
||||
os.Exit(status)
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
if len(os.Args) > 1 && os.Args[1] == "init" {
|
||||
runtime.GOMAXPROCS(1)
|
||||
runtime.LockOSThread()
|
||||
factory, _ := libcontainer.New("")
|
||||
if err := factory.StartInitialization(); err != nil {
|
||||
fatal(err)
|
||||
}
|
||||
panic("--this line should have never been executed, congratulations--")
|
||||
}
|
||||
}
|
||||
|
||||
func startContainer(context *cli.Context, spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec) (int, error) {
|
||||
config, err := createLibcontainerConfig(context.GlobalString("id"), spec, rspec)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
if _, err := os.Stat(config.Rootfs); err != nil {
|
||||
if os.IsNotExist(err) {
|
||||
return -1, fmt.Errorf("Rootfs (%q) does not exist", config.Rootfs)
|
||||
}
|
||||
return -1, err
|
||||
}
|
||||
rootuid, err := config.HostUID()
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
factory, err := loadFactory(context)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
container, err := factory.Create(context.GlobalString("id"), config)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
// ensure that the container is always removed if we were the process
|
||||
// that created it.
|
||||
defer destroy(container)
|
||||
process := newProcess(spec.Process)
|
||||
|
||||
// Support on-demand socket activation by passing file descriptors into the container init process.
|
||||
if os.Getenv("LISTEN_FDS") != "" {
|
||||
listenFdsInt, err := strconv.Atoi(os.Getenv("LISTEN_FDS"))
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
for i := SD_LISTEN_FDS_START; i < (listenFdsInt + SD_LISTEN_FDS_START); i++ {
|
||||
process.ExtraFiles = append(process.ExtraFiles, os.NewFile(uintptr(i), ""))
|
||||
}
|
||||
}
|
||||
tty, err := newTty(spec.Process.Terminal, process, rootuid)
|
||||
if err != nil {
|
||||
return -1, err
|
||||
}
|
||||
handler := newSignalHandler(tty)
|
||||
defer handler.Close()
|
||||
if err := container.Start(process); err != nil {
|
||||
return -1, err
|
||||
}
|
||||
return handler.forward(process)
|
||||
}
|
||||
|
||||
// If systemd is supporting sd_notify protocol, this function will add support
|
||||
// for sd_notify protocol from within the container.
|
||||
func setupSdNotify(spec *specs.LinuxSpec, rspec *specs.LinuxRuntimeSpec, notifySocket string) {
|
||||
mountName := "sdNotify"
|
||||
spec.Mounts = append(spec.Mounts, specs.MountPoint{Name: mountName, Path: notifySocket})
|
||||
spec.Process.Env = append(spec.Process.Env, fmt.Sprintf("NOTIFY_SOCKET=%s", notifySocket))
|
||||
rspec.Mounts[mountName] = specs.Mount{Type: "bind", Source: notifySocket, Options: []string{"bind"}}
|
||||
}
|
||||
|
||||
// If systemd is supporting on-demand socket activation, this function will add support
|
||||
// for on-demand socket activation for the containerized service.
|
||||
func setupSocketActivation(spec *specs.LinuxSpec, listenFds string) {
|
||||
spec.Process.Env = append(spec.Process.Env, fmt.Sprintf("LISTEN_FDS=%s", listenFds), "LISTEN_PID=1")
|
||||
}
|
||||
|
||||
func destroy(container libcontainer.Container) {
|
||||
status, err := container.Status()
|
||||
if err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
if status != libcontainer.Checkpointed {
|
||||
if err := container.Destroy(); err != nil {
|
||||
logrus.Error(err)
|
||||
}
|
||||
}
|
||||
}
|
90
integration/vendor/github.com/opencontainers/runc/tty.go
generated
vendored
Normal file
90
integration/vendor/github.com/opencontainers/runc/tty.go
generated
vendored
Normal file
|
@ -0,0 +1,90 @@
|
|||
// +build linux
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/docker/docker/pkg/term"
|
||||
"github.com/opencontainers/runc/libcontainer"
|
||||
)
|
||||
|
||||
// newTty creates a new tty for use with the container. If a tty is not to be
|
||||
// created for the process, pipes are created so that the TTY of the parent
|
||||
// process are not inherited by the container.
|
||||
func newTty(create bool, p *libcontainer.Process, rootuid int) (*tty, error) {
|
||||
if create {
|
||||
return createTty(p, rootuid)
|
||||
}
|
||||
return createStdioPipes(p, rootuid)
|
||||
}
|
||||
|
||||
// setup standard pipes so that the TTY of the calling runc process
|
||||
// is not inherited by the container.
|
||||
func createStdioPipes(p *libcontainer.Process, rootuid int) (*tty, error) {
|
||||
i, err := p.InitializeIO(rootuid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
t := &tty{
|
||||
closers: []io.Closer{
|
||||
i.Stdin,
|
||||
i.Stdout,
|
||||
i.Stderr,
|
||||
},
|
||||
}
|
||||
go io.Copy(i.Stdin, os.Stdin)
|
||||
go io.Copy(os.Stdout, i.Stdout)
|
||||
go io.Copy(os.Stderr, i.Stderr)
|
||||
return t, nil
|
||||
}
|
||||
|
||||
func createTty(p *libcontainer.Process, rootuid int) (*tty, error) {
|
||||
console, err := p.NewConsole(rootuid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
go io.Copy(console, os.Stdin)
|
||||
go io.Copy(os.Stdout, console)
|
||||
state, err := term.SetRawTerminal(os.Stdin.Fd())
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to set the terminal from the stdin: %v", err)
|
||||
}
|
||||
t := &tty{
|
||||
console: console,
|
||||
state: state,
|
||||
closers: []io.Closer{
|
||||
console,
|
||||
},
|
||||
}
|
||||
return t, nil
|
||||
}
|
||||
|
||||
type tty struct {
|
||||
console libcontainer.Console
|
||||
state *term.State
|
||||
closers []io.Closer
|
||||
}
|
||||
|
||||
func (t *tty) Close() error {
|
||||
for _, c := range t.closers {
|
||||
c.Close()
|
||||
}
|
||||
if t.state != nil {
|
||||
term.RestoreTerminal(os.Stdin.Fd(), t.state)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *tty) resize() error {
|
||||
if t.console == nil {
|
||||
return nil
|
||||
}
|
||||
ws, err := term.GetWinsize(os.Stdin.Fd())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return term.SetWinsize(t.console.Fd(), ws)
|
||||
}
|
167
integration/vendor/github.com/opencontainers/runc/utils.go
generated
vendored
Normal file
167
integration/vendor/github.com/opencontainers/runc/utils.go
generated
vendored
Normal file
|
@ -0,0 +1,167 @@
|
|||
// +build linux
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/codegangsta/cli"
|
||||
"github.com/opencontainers/runc/libcontainer"
|
||||
"github.com/opencontainers/runc/libcontainer/configs"
|
||||
"github.com/opencontainers/specs"
|
||||
)
|
||||
|
||||
const wildcard = -1
|
||||
|
||||
var allowedDevices = []*configs.Device{
|
||||
// allow mknod for any device
|
||||
{
|
||||
Type: 'c',
|
||||
Major: wildcard,
|
||||
Minor: wildcard,
|
||||
Permissions: "m",
|
||||
},
|
||||
{
|
||||
Type: 'b',
|
||||
Major: wildcard,
|
||||
Minor: wildcard,
|
||||
Permissions: "m",
|
||||
},
|
||||
{
|
||||
Path: "/dev/console",
|
||||
Type: 'c',
|
||||
Major: 5,
|
||||
Minor: 1,
|
||||
Permissions: "rwm",
|
||||
},
|
||||
{
|
||||
Path: "/dev/tty0",
|
||||
Type: 'c',
|
||||
Major: 4,
|
||||
Minor: 0,
|
||||
Permissions: "rwm",
|
||||
},
|
||||
{
|
||||
Path: "/dev/tty1",
|
||||
Type: 'c',
|
||||
Major: 4,
|
||||
Minor: 1,
|
||||
Permissions: "rwm",
|
||||
},
|
||||
// /dev/pts/ - pts namespaces are "coming soon"
|
||||
{
|
||||
Path: "",
|
||||
Type: 'c',
|
||||
Major: 136,
|
||||
Minor: wildcard,
|
||||
Permissions: "rwm",
|
||||
},
|
||||
{
|
||||
Path: "",
|
||||
Type: 'c',
|
||||
Major: 5,
|
||||
Minor: 2,
|
||||
Permissions: "rwm",
|
||||
},
|
||||
// tuntap
|
||||
{
|
||||
Path: "",
|
||||
Type: 'c',
|
||||
Major: 10,
|
||||
Minor: 200,
|
||||
Permissions: "rwm",
|
||||
},
|
||||
}
|
||||
|
||||
var container libcontainer.Container
|
||||
|
||||
func containerPreload(context *cli.Context) error {
|
||||
c, err := getContainer(context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
container = c
|
||||
return nil
|
||||
}
|
||||
|
||||
var factory libcontainer.Factory
|
||||
|
||||
func factoryPreload(context *cli.Context) error {
|
||||
f, err := loadFactory(context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
factory = f
|
||||
return nil
|
||||
}
|
||||
|
||||
// loadFactory returns the configured factory instance for execing containers.
|
||||
func loadFactory(context *cli.Context) (libcontainer.Factory, error) {
|
||||
root := context.GlobalString("root")
|
||||
abs, err := filepath.Abs(root)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return libcontainer.New(abs, libcontainer.Cgroupfs, func(l *libcontainer.LinuxFactory) error {
|
||||
l.CriuPath = context.GlobalString("criu")
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// getContainer returns the specified container instance by loading it from state
|
||||
// with the default factory.
|
||||
func getContainer(context *cli.Context) (libcontainer.Container, error) {
|
||||
factory, err := loadFactory(context)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
container, err := factory.Load(context.GlobalString("id"))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return container, nil
|
||||
}
|
||||
|
||||
// fatal prints the error's details if it is a libcontainer specific error type
|
||||
// then exits the program with an exit status of 1.
|
||||
func fatal(err error) {
|
||||
if lerr, ok := err.(libcontainer.Error); ok {
|
||||
lerr.Detail(os.Stderr)
|
||||
os.Exit(1)
|
||||
}
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// getDefaultID returns a string to be used as the container id based on the
|
||||
// current working directory of the runc process. This function panics
|
||||
// if the cwd is unable to be found based on a system error.
|
||||
func getDefaultID() string {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return filepath.Base(cwd)
|
||||
}
|
||||
|
||||
func getDefaultImagePath(context *cli.Context) string {
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return filepath.Join(cwd, "checkpoint")
|
||||
}
|
||||
|
||||
// newProcess returns a new libcontainer Process with the arguments from the
|
||||
// spec and stdio from the current process.
|
||||
func newProcess(p specs.Process) *libcontainer.Process {
|
||||
return &libcontainer.Process{
|
||||
Args: p.Args,
|
||||
Env: p.Env,
|
||||
// TODO: fix libcontainer's API to better support uid/gid in a typesafe way.
|
||||
User: fmt.Sprintf("%d:%d", p.User.UID, p.User.GID),
|
||||
Cwd: p.Cwd,
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue