update vendor

Signed-off-by: Jess Frazelle <acidburn@microsoft.com>
This commit is contained in:
Jess Frazelle 2018-09-25 12:27:46 -04:00
parent 19a32db84d
commit 94d1cfbfbf
No known key found for this signature in database
GPG key ID: 18F3685C0022BFF3
10501 changed files with 2307943 additions and 29279 deletions

View file

@ -0,0 +1,61 @@
package containerized
import (
"context"
"github.com/containerd/containerd/containers"
"github.com/containerd/containerd/oci"
specs "github.com/opencontainers/runtime-spec/specs-go"
)
// WithAllCapabilities enables all capabilities required to run privileged containers
func WithAllCapabilities(_ context.Context, _ oci.Client, c *containers.Container, s *specs.Spec) error {
caps := []string{
"CAP_CHOWN",
"CAP_DAC_OVERRIDE",
"CAP_DAC_READ_SEARCH",
"CAP_FOWNER",
"CAP_FSETID",
"CAP_KILL",
"CAP_SETGID",
"CAP_SETUID",
"CAP_SETPCAP",
"CAP_LINUX_IMMUTABLE",
"CAP_NET_BIND_SERVICE",
"CAP_NET_BROADCAST",
"CAP_NET_ADMIN",
"CAP_NET_RAW",
"CAP_IPC_LOCK",
"CAP_IPC_OWNER",
"CAP_SYS_MODULE",
"CAP_SYS_RAWIO",
"CAP_SYS_CHROOT",
"CAP_SYS_PTRACE",
"CAP_SYS_PACCT",
"CAP_SYS_ADMIN",
"CAP_SYS_BOOT",
"CAP_SYS_NICE",
"CAP_SYS_RESOURCE",
"CAP_SYS_TIME",
"CAP_SYS_TTY_CONFIG",
"CAP_MKNOD",
"CAP_LEASE",
"CAP_AUDIT_WRITE",
"CAP_AUDIT_CONTROL",
"CAP_SETFCAP",
"CAP_MAC_OVERRIDE",
"CAP_MAC_ADMIN",
"CAP_SYSLOG",
"CAP_WAKE_ALARM",
"CAP_BLOCK_SUSPEND",
"CAP_AUDIT_READ",
}
if s.Process.Capabilities == nil {
s.Process.Capabilities = &specs.LinuxCapabilities{}
}
s.Process.Capabilities.Bounding = caps
s.Process.Capabilities.Effective = caps
s.Process.Capabilities.Inheritable = caps
s.Process.Capabilities.Permitted = caps
return nil
}

View file

@ -0,0 +1,21 @@
package containerized
import (
"context"
"testing"
"github.com/containerd/containerd/containers"
specs "github.com/opencontainers/runtime-spec/specs-go"
"gotest.tools/assert"
)
func TestWithAllCapabilities(t *testing.T) {
c := &containers.Container{}
s := &specs.Spec{
Process: &specs.Process{},
}
ctx := context.Background()
err := WithAllCapabilities(ctx, nil, c, s)
assert.NilError(t, err)
assert.Assert(t, len(s.Process.Capabilities.Bounding) > 0)
}

View file

@ -0,0 +1,74 @@
package containerized
import (
"context"
"github.com/containerd/containerd"
"github.com/containerd/containerd/errdefs"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
)
// AtomicImageUpdate will perform an update of the given container with the new image
// and verify success via the provided healthcheckFn. If the healthcheck fails, the
// container will be reverted to the prior image
func AtomicImageUpdate(ctx context.Context, container containerd.Container, image containerd.Image, healthcheckFn func() error) error {
updateCompleted := false
err := pauseAndRun(ctx, container, func() error {
if err := container.Update(ctx, WithUpgrade(image)); err != nil {
return errors.Wrap(err, "failed to update to new image")
}
updateCompleted = true
task, err := container.Task(ctx, nil)
if err != nil {
if errdefs.IsNotFound(err) {
return nil
}
return errors.Wrap(err, "failed to lookup task")
}
return task.Kill(ctx, sigTERM)
})
if err != nil {
if updateCompleted {
logrus.WithError(err).Error("failed to update, rolling back")
return rollBack(ctx, container)
}
return err
}
if err := healthcheckFn(); err != nil {
logrus.WithError(err).Error("failed health check, rolling back")
return rollBack(ctx, container)
}
return nil
}
func rollBack(ctx context.Context, container containerd.Container) error {
return pauseAndRun(ctx, container, func() error {
if err := container.Update(ctx, WithRollback); err != nil {
return err
}
task, err := container.Task(ctx, nil)
if err != nil {
if errdefs.IsNotFound(err) {
return nil
}
return errors.Wrap(err, "failed to lookup task")
}
return task.Kill(ctx, sigTERM)
})
}
func pauseAndRun(ctx context.Context, container containerd.Container, fn func() error) error {
task, err := container.Task(ctx, nil)
if err != nil {
if errdefs.IsNotFound(err) {
return fn()
}
return errors.Wrap(err, "failed to lookup task")
}
if err := task.Pause(ctx); err != nil {
return errors.Wrap(err, "failed to pause task")
}
defer task.Resume(ctx)
return fn()
}

View file

@ -0,0 +1,66 @@
package containerized
import (
"encoding/json"
"io/ioutil"
"os"
"path/filepath"
)
var (
proxydir = "/etc/containerd-proxy"
)
type proxyConfig struct {
ID string `json:"-"`
Namespace string `json:"namespace"`
Image string `json:"image"`
ImagePath string `json:"imagePath"`
Args []string `json:"args"`
Scope string `json:"scope"`
}
func updateConfig(name, newImage string) error {
cfg, err := loadConfig(name)
if err != nil && os.IsNotExist(err) {
return nil
}
if err != nil {
return err
}
cfg.Image = newImage
cfg.ImagePath = ""
return storeConfig(name, cfg)
}
func loadConfig(name string) (*proxyConfig, error) {
configFile := filepath.Join(proxydir, name+".json")
data, err := ioutil.ReadFile(configFile)
if err != nil {
return nil, err
}
var cfg proxyConfig
err = json.Unmarshal(data, &cfg)
if err != nil {
return nil, err
}
return &cfg, nil
}
// storeConfig will write out the config only if it already exists
func storeConfig(name string, cfg *proxyConfig) error {
configFile := filepath.Join(proxydir, name+".json")
fd, err := os.OpenFile(configFile, os.O_RDWR, 0644)
if err != nil && os.IsNotExist(err) {
return nil
}
if err != nil {
return err
}
err = fd.Truncate(0)
if err != nil {
return err
}
enc := json.NewEncoder(fd)
return enc.Encode(cfg)
}

View file

@ -0,0 +1,68 @@
package containerized
import (
"encoding/json"
"io/ioutil"
"os"
"path/filepath"
"testing"
"gotest.tools/assert"
)
func TestUpdateConfigNotExist(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "cfg-update")
assert.NilError(t, err)
defer os.RemoveAll(tmpdir)
origProxyDir := proxydir
defer func() {
proxydir = origProxyDir
}()
proxydir = tmpdir
name := "myname"
newImage := "newimage:foo"
err = updateConfig(name, newImage)
assert.NilError(t, err)
}
func TestUpdateConfigBadJson(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "cfg-update")
assert.NilError(t, err)
defer os.RemoveAll(tmpdir)
origProxyDir := proxydir
defer func() {
proxydir = origProxyDir
}()
proxydir = tmpdir
filename := filepath.Join(tmpdir, "dockerd.json")
err = ioutil.WriteFile(filename, []byte("not json"), 0644)
assert.NilError(t, err)
name := "dockerd"
newImage := "newimage:foo"
err = updateConfig(name, newImage)
assert.ErrorContains(t, err, "invalid character")
}
func TestUpdateConfigHappyPath(t *testing.T) {
tmpdir, err := ioutil.TempDir("", "cfg-update")
assert.NilError(t, err)
defer os.RemoveAll(tmpdir)
origProxyDir := proxydir
defer func() {
proxydir = origProxyDir
}()
proxydir = tmpdir
filename := filepath.Join(tmpdir, "dockerd.json")
err = ioutil.WriteFile(filename, []byte("{}"), 0644)
assert.NilError(t, err)
name := "dockerd"
newImage := "newimage:foo"
err = updateConfig(name, newImage)
assert.NilError(t, err)
data, err := ioutil.ReadFile(filename)
assert.NilError(t, err)
var cfg map[string]string
err = json.Unmarshal(data, &cfg)
assert.NilError(t, err)
assert.Assert(t, cfg["image"] == newImage)
}

View file

@ -0,0 +1,12 @@
// +build !windows
package containerized
import (
"golang.org/x/sys/unix"
)
var (
// sigTERM maps to unix.SIGTERM
sigTERM = unix.SIGTERM
)

View file

@ -0,0 +1,12 @@
// +build windows
package containerized
import (
"syscall"
)
var (
// sigTERM all signals are ignored by containerd kill windows
sigTERM = syscall.Signal(0)
)

View file

@ -0,0 +1,166 @@
package containerized
import (
"context"
"errors"
"fmt"
"strings"
"time"
"github.com/containerd/containerd"
"github.com/containerd/containerd/containers"
"github.com/containerd/containerd/diff/apply"
"github.com/containerd/containerd/mount"
"github.com/containerd/containerd/rootfs"
"github.com/containerd/containerd/snapshots"
"github.com/opencontainers/image-spec/identity"
)
const (
gcRoot = "containerd.io/gc.root"
timestampFormat = "01-02-2006-15:04:05"
previousRevision = "docker.com/revision.previous"
imageLabel = "docker.com/revision.image"
)
// ErrNoPreviousRevision returned if the container has to previous revision
var ErrNoPreviousRevision = errors.New("no previous revision")
// WithNewSnapshot creates a new snapshot managed by containerized
func WithNewSnapshot(i containerd.Image) containerd.NewContainerOpts {
return func(ctx context.Context, client *containerd.Client, c *containers.Container) error {
if c.Snapshotter == "" {
c.Snapshotter = containerd.DefaultSnapshotter
}
r, err := create(ctx, client, i, c.ID, "")
if err != nil {
return err
}
c.SnapshotKey = r.Key
c.Image = i.Name()
return nil
}
}
// WithUpgrade upgrades an existing container's image to a new one
func WithUpgrade(i containerd.Image) containerd.UpdateContainerOpts {
return func(ctx context.Context, client *containerd.Client, c *containers.Container) error {
revision, err := save(ctx, client, i, c)
if err != nil {
return err
}
c.Image = i.Name()
err = updateConfig(c.ID, c.Image)
if err != nil {
return err
}
c.SnapshotKey = revision.Key
return nil
}
}
// WithRollback rolls back to the previous container's revision
func WithRollback(ctx context.Context, client *containerd.Client, c *containers.Container) error {
prev, err := previous(ctx, client, c)
if err != nil {
return err
}
ss := client.SnapshotService(c.Snapshotter)
sInfo, err := ss.Stat(ctx, prev.Key)
if err != nil {
return err
}
snapshotImage, ok := sInfo.Labels[imageLabel]
if !ok {
return fmt.Errorf("snapshot %s does not have a service image label", prev.Key)
}
if snapshotImage == "" {
return fmt.Errorf("snapshot %s has an empty service image label", prev.Key)
}
c.Image = snapshotImage
err = updateConfig(c.ID, c.Image)
if err != nil {
return err
}
c.SnapshotKey = prev.Key
return nil
}
func newRevision(id string) *revision {
now := time.Now()
return &revision{
Timestamp: now,
Key: fmt.Sprintf("boss.io.%s.%s", id, now.Format(timestampFormat)),
}
}
type revision struct {
Timestamp time.Time
Key string
mounts []mount.Mount
}
// nolint: interfacer
func create(ctx context.Context, client *containerd.Client, i containerd.Image, id string, previous string) (*revision, error) {
diffIDs, err := i.RootFS(ctx)
if err != nil {
return nil, err
}
var (
parent = identity.ChainID(diffIDs).String()
r = newRevision(id)
)
labels := map[string]string{
gcRoot: r.Timestamp.Format(time.RFC3339),
imageLabel: i.Name(),
}
if previous != "" {
labels[previousRevision] = previous
}
mounts, err := client.SnapshotService(containerd.DefaultSnapshotter).Prepare(ctx, r.Key, parent, snapshots.WithLabels(labels))
if err != nil {
return nil, err
}
r.mounts = mounts
return r, nil
}
func save(ctx context.Context, client *containerd.Client, updatedImage containerd.Image, c *containers.Container) (*revision, error) {
snapshot, err := create(ctx, client, updatedImage, c.ID, c.SnapshotKey)
if err != nil {
return nil, err
}
service := client.SnapshotService(c.Snapshotter)
// create a diff from the existing snapshot
diff, err := rootfs.CreateDiff(ctx, c.SnapshotKey, service, client.DiffService())
if err != nil {
return nil, err
}
applier := apply.NewFileSystemApplier(client.ContentStore())
if _, err := applier.Apply(ctx, diff, snapshot.mounts); err != nil {
return nil, err
}
return snapshot, nil
}
// nolint: interfacer
func previous(ctx context.Context, client *containerd.Client, c *containers.Container) (*revision, error) {
service := client.SnapshotService(c.Snapshotter)
sInfo, err := service.Stat(ctx, c.SnapshotKey)
if err != nil {
return nil, err
}
key := sInfo.Labels[previousRevision]
if key == "" {
return nil, ErrNoPreviousRevision
}
parts := strings.Split(key, ".")
timestamp, err := time.Parse(timestampFormat, parts[len(parts)-1])
if err != nil {
return nil, err
}
return &revision{
Timestamp: timestamp,
Key: key,
}, nil
}