From e4691cd704986c0cbe7a542de34d4f968ef74d58 Mon Sep 17 00:00:00 2001 From: Benoit Sigoure Date: Tue, 15 Jan 2019 17:23:52 -0800 Subject: [PATCH] nodeup: Add support for Docker 18.09.3. Starting from Docker 18.09.0, the Docker distribution has been split in 3 packages: the Docker daemon, the Docker CLI, and for containerd. This adds a twist to how to upgrade Docker from the base image as the daemon and CLI packages must be installed at the same time, otherwise dpkg/rpm will refuse to upgrade (the new CLI is incompatible with the old package and the daemon can't be installed without first installing the CLI and the new containerd, so the upgrade MUST happen in a single transaction). This code change thus adds the possibility to specify additional packages to install in the same dpkg/yum transaction, such as the Docker CLI and containerd in nodeup, and the ability to apply the multi-package upgrade atomically with dpkg/rpm. We also use this new mechanism for the SELinux policy on RHEL/CentOS. --- nodeup/pkg/model/docker.go | 225 +++++++++++++----------- upup/pkg/fi/nodeup/nodetasks/package.go | 47 +++-- 2 files changed, 156 insertions(+), 116 deletions(-) diff --git a/nodeup/pkg/model/docker.go b/nodeup/pkg/model/docker.go index d5357e519c..f7992ebeef 100644 --- a/nodeup/pkg/model/docker.go +++ b/nodeup/pkg/model/docker.go @@ -24,6 +24,7 @@ import ( "strconv" "strings" + "github.com/golang/glog" "k8s.io/kops/nodeup/pkg/distros" "k8s.io/kops/nodeup/pkg/model/resources" "k8s.io/kops/pkg/apis/kops" @@ -31,8 +32,6 @@ import ( "k8s.io/kops/pkg/systemd" "k8s.io/kops/upup/pkg/fi" "k8s.io/kops/upup/pkg/fi/nodeup/nodetasks" - - "github.com/golang/glog" ) // DockerBuilder install docker (just the packages at the moment) @@ -42,6 +41,12 @@ type DockerBuilder struct { var _ fi.ModelBuilder = &DockerBuilder{} +type packageInfo struct { + Version string // Package version + Source string // URL to download the package from + Hash string // sha1sum of the package file +} + type dockerVersion struct { Name string @@ -54,8 +59,19 @@ type dockerVersion struct { // Hash is the sha1 hash of the file Hash string + // Extra packages to install during the same dpkg/yum transaction. + // This is used for: + // - On RHEL/CentOS, the SELinux policy needs to be installed. + // - Starting from Docker 18.09, the Docker package has been split in 3 + // separate packages: one for the daemon, one for the CLI, one for + // containerd. All 3 must be installed at the same time when + // upgrading from an older version of Docker. + ExtraPackages map[string]packageInfo + DockerVersion string Distros []distros.Distribution + // List of dependencies that can be installed using the system's package + // manager (e.g. apt-get install or yum install). Dependencies []string Architectures []Architecture @@ -104,16 +120,14 @@ var dockerVersions = []dockerVersion{ Version: "1.11.2", Source: "https://yum.dockerproject.org/repo/main/centos/7/Packages/docker-engine-1.11.2-1.el7.centos.x86_64.rpm", Hash: "432e6d7948df9e05f4190fce2f423eedbfd673d5", - Dependencies: []string{"libtool-ltdl"}, - }, - { - DockerVersion: "1.11.2", - Name: "docker-engine-selinux", - Distros: []distros.Distribution{distros.DistributionRhel7, distros.DistributionCentos7}, - Architectures: []Architecture{ArchitectureAmd64}, - Version: "1.11.2", - Source: "https://yum.dockerproject.org/repo/main/centos/7/Packages/docker-engine-selinux-1.11.2-1.el7.centos.noarch.rpm", - Hash: "f6da608fa8eeb2be8071489086ed9ff035f6daba", + ExtraPackages: map[string]packageInfo{ + "selinux": { + Version: "1.11.2", + Source: "https://yum.dockerproject.org/repo/main/centos/7/Packages/docker-engine-selinux-1.11.2-1.el7.centos.noarch.rpm", + Hash: "f6da608fa8eeb2be8071489086ed9ff035f6daba", + }, + }, + Dependencies: []string{"libtool-ltdl"}, }, // 1.12.1 - Jessie @@ -149,16 +163,14 @@ var dockerVersions = []dockerVersion{ Version: "1.12.1", Source: "https://yum.dockerproject.org/repo/main/centos/7/Packages/docker-engine-1.12.1-1.el7.centos.x86_64.rpm", Hash: "636471665665546224444052c3b48001397036be", - Dependencies: []string{"libtool-ltdl"}, - }, - { - DockerVersion: "1.12.1", - Name: "docker-engine-selinux", - Distros: []distros.Distribution{distros.DistributionRhel7, distros.DistributionCentos7}, - Architectures: []Architecture{ArchitectureAmd64}, - Version: "1.12.1", - Source: "https://yum.dockerproject.org/repo/main/centos/7/Packages/docker-engine-selinux-1.12.1-1.el7.centos.noarch.rpm", - Hash: "52ec22128e70acc2f76b3a8e87ff96785995116a", + ExtraPackages: map[string]packageInfo{ + "selinux": { + Version: "1.12.1", + Source: "https://yum.dockerproject.org/repo/main/centos/7/Packages/docker-engine-selinux-1.12.1-1.el7.centos.noarch.rpm", + Hash: "52ec22128e70acc2f76b3a8e87ff96785995116a", + }, + }, + Dependencies: []string{"libtool-ltdl"}, }, // 1.12.3 - k8s 1.5 @@ -210,16 +222,14 @@ var dockerVersions = []dockerVersion{ Version: "1.12.3", Source: "https://yum.dockerproject.org/repo/main/centos/7/Packages/docker-engine-1.12.3-1.el7.centos.x86_64.rpm", Hash: "67fbb78cfb9526aaf8142c067c10384df199d8f9", - Dependencies: []string{"libtool-ltdl", "libseccomp"}, - }, - { - DockerVersion: "1.12.3", - Name: "docker-engine-selinux", - Distros: []distros.Distribution{distros.DistributionRhel7, distros.DistributionCentos7}, - Architectures: []Architecture{ArchitectureAmd64}, - Version: "1.12.3", - Source: "https://yum.dockerproject.org/repo/main/centos/7/Packages/docker-engine-selinux-1.12.3-1.el7.centos.noarch.rpm", - Hash: "a6b0243af348140236ed96f2e902b259c590eefa", + ExtraPackages: map[string]packageInfo{ + "selinux": { + Version: "1.12.3", + Source: "https://yum.dockerproject.org/repo/main/centos/7/Packages/docker-engine-selinux-1.12.3-1.el7.centos.noarch.rpm", + Hash: "a6b0243af348140236ed96f2e902b259c590eefa", + }, + }, + Dependencies: []string{"libtool-ltdl", "libseccomp"}, }, // 1.12.6 - k8s 1.6 @@ -286,17 +296,14 @@ var dockerVersions = []dockerVersion{ Version: "1.12.6", Source: "https://yum.dockerproject.org/repo/main/centos/7/Packages/docker-engine-1.12.6-1.el7.centos.x86_64.rpm", Hash: "776dbefa9dc7733000e46049293555a9a422c50e", - Dependencies: []string{"libtool-ltdl", "libseccomp", "libcgroup"}, - }, - { - DockerVersion: "1.12.6", - Name: "docker-engine-selinux", - Distros: []distros.Distribution{distros.DistributionRhel7, distros.DistributionCentos7}, - Architectures: []Architecture{ArchitectureAmd64}, - Version: "1.12.6", - Source: "https://yum.dockerproject.org/repo/main/centos/7/Packages/docker-engine-selinux-1.12.6-1.el7.centos.noarch.rpm", - Hash: "9a6ee0d631ca911b6927450a3c396e9a5be75047", - Dependencies: []string{"policycoreutils-python"}, + ExtraPackages: map[string]packageInfo{ + "selinux": { + Version: "1.12.6", + Source: "https://yum.dockerproject.org/repo/main/centos/7/Packages/docker-engine-selinux-1.12.6-1.el7.centos.noarch.rpm", + Hash: "9a6ee0d631ca911b6927450a3c396e9a5be75047", + }, + }, + Dependencies: []string{"libtool-ltdl", "libseccomp", "libcgroup", "policycoreutils-python"}, }, // 1.13.1 - k8s 1.8 @@ -363,17 +370,14 @@ var dockerVersions = []dockerVersion{ Version: "1.13.1", Source: "https://yum.dockerproject.org/repo/main/centos/7/Packages/docker-engine-1.13.1-1.el7.centos.x86_64.rpm", Hash: "b18f7fd8057665e7d2871d29640e214173f70fe1", - Dependencies: []string{"libtool-ltdl", "libseccomp", "libcgroup"}, - }, - { - DockerVersion: "1.13.1", - Name: "docker-engine-selinux", - Distros: []distros.Distribution{distros.DistributionRhel7, distros.DistributionCentos7}, - Architectures: []Architecture{ArchitectureAmd64}, - Version: "1.13.1", - Source: "https://yum.dockerproject.org/repo/main/centos/7/Packages/docker-engine-selinux-1.13.1-1.el7.centos.noarch.rpm", - Hash: "948c518a610af631fa98aa32d9bcd43e9ddd5ebc", - Dependencies: []string{"policycoreutils-python", "selinux-policy-base", "selinux-policy-targeted"}, + ExtraPackages: map[string]packageInfo{ + "selinux": { + Version: "1.13.1", + Source: "https://yum.dockerproject.org/repo/main/centos/7/Packages/docker-engine-selinux-1.13.1-1.el7.centos.noarch.rpm", + Hash: "948c518a610af631fa98aa32d9bcd43e9ddd5ebc", + }, + }, + Dependencies: []string{"libtool-ltdl", "libseccomp", "libcgroup", "policycoreutils-python", "selinux-policy-base", "selinux-policy-targeted"}, }, // 17.03.2 - k8s 1.8 @@ -451,19 +455,16 @@ var dockerVersions = []dockerVersion{ Version: "17.03.2.ce", Source: "https://download.docker.com/linux/centos/7/x86_64/stable/Packages/docker-ce-17.03.2.ce-1.el7.centos.x86_64.rpm", Hash: "494ca888f5b1553f93b9d9a5dad4a67f76cf9eb5", - Dependencies: []string{"libtool-ltdl", "libseccomp", "libcgroup"}, + ExtraPackages: map[string]packageInfo{ + "selinux": { + Version: "17.03.2.ce", + Source: "https://download.docker.com/linux/centos/7/x86_64/stable/Packages/docker-ce-selinux-17.03.2.ce-1.el7.centos.noarch.rpm", + Hash: "4659c937b66519c88ef2a82a906bb156db29d191", + }, + }, + Dependencies: []string{"libtool-ltdl", "libseccomp", "libcgroup", "policycoreutils-python"}, MarkImmutable: []string{"/usr/bin/docker-runc"}, }, - { - DockerVersion: "17.03.2", - Name: "docker-ce-selinux", - Distros: []distros.Distribution{distros.DistributionRhel7, distros.DistributionCentos7}, - Architectures: []Architecture{ArchitectureAmd64}, - Version: "17.03.2.ce", - Source: "https://download.docker.com/linux/centos/7/x86_64/stable/Packages/docker-ce-selinux-17.03.2.ce-1.el7.centos.noarch.rpm", - Hash: "4659c937b66519c88ef2a82a906bb156db29d191", - Dependencies: []string{"policycoreutils-python"}, - }, // 17.09.0 - k8s 1.8 // 17.09.0 - Jessie @@ -531,16 +532,6 @@ var dockerVersions = []dockerVersion{ }, // 17.09.0 - Centos / Rhel7 (two packages) - { - DockerVersion: "17.09.0", - Name: "container-selinux", - Distros: []distros.Distribution{distros.DistributionRhel7, distros.DistributionCentos7}, - Architectures: []Architecture{ArchitectureAmd64}, - Version: "2.68", - Source: "http://mirror.centos.org/centos/7/extras/x86_64/Packages/container-selinux-2.68-1.el7.noarch.rpm", - Hash: "d9f87f7f4f2e8e611f556d873a17b8c0c580fec0", - Dependencies: []string{"policycoreutils-python"}, - }, { DockerVersion: "17.09.0", Name: "docker-ce", @@ -549,7 +540,14 @@ var dockerVersions = []dockerVersion{ Version: "17.09.0.ce", Source: "https://download.docker.com/linux/centos/7/x86_64/stable/Packages/docker-ce-17.09.0.ce-1.el7.centos.x86_64.rpm", Hash: "b4ce72e80ff02926de943082821bbbe73958f87a", - Dependencies: []string{"libtool-ltdl", "libseccomp", "libcgroup"}, + ExtraPackages: map[string]packageInfo{ + "selinux": { + Version: "17.09.0.ce", + Source: "http://mirror.centos.org/centos/7/extras/x86_64/Packages/container-selinux-2.68-1.el7.noarch.rpm", + Hash: "d9f87f7f4f2e8e611f556d873a17b8c0c580fec0", + }, + }, + Dependencies: []string{"libtool-ltdl", "libseccomp", "libcgroup", "policycoreutils-python"}, }, // 18.03.1 - Bionic @@ -582,7 +580,6 @@ var dockerVersions = []dockerVersion{ // 18.06.1 - Debian Stretch { - DockerVersion: "18.06.1", Name: "docker-ce", Distros: []distros.Distribution{distros.DistributionDebian9}, @@ -619,16 +616,6 @@ var dockerVersions = []dockerVersion{ }, // 18.06.1 - CentOS / Rhel7 (two packages) - { - DockerVersion: "18.06.1", - Name: "container-selinux", - Distros: []distros.Distribution{distros.DistributionRhel7, distros.DistributionCentos7}, - Architectures: []Architecture{ArchitectureAmd64}, - Version: "2.68", - Source: "http://mirror.centos.org/centos/7/extras/x86_64/Packages/container-selinux-2.68-1.el7.noarch.rpm", - Hash: "d9f87f7f4f2e8e611f556d873a17b8c0c580fec0", - Dependencies: []string{"policycoreutils-python"}, - }, { DockerVersion: "18.06.1", Name: "docker-ce", @@ -637,7 +624,38 @@ var dockerVersions = []dockerVersion{ Version: "18.06.1.ce", Source: "https://download.docker.com/linux/centos/7/x86_64/stable/Packages/docker-ce-18.06.1.ce-3.el7.x86_64.rpm", Hash: "0a1325e570c5e54111a79623c9fd0c0c714d3a11", - Dependencies: []string{"libtool-ltdl", "libseccomp", "libcgroup"}, + ExtraPackages: map[string]packageInfo{ + "selinux": { + Version: "18.06.1.ce", + Source: "http://mirror.centos.org/centos/7/extras/x86_64/Packages/container-selinux-2.68-1.el7.noarch.rpm", + Hash: "d9f87f7f4f2e8e611f556d873a17b8c0c580fec0", + }, + }, + Dependencies: []string{"libtool-ltdl", "libseccomp", "libcgroup", "policycoreutils-python"}, + }, + + // 18.09.3 - Debian Stretch + { + DockerVersion: "18.09.3", + Name: "docker-ce", + Distros: []distros.Distribution{distros.DistributionDebian9}, + Architectures: []Architecture{ArchitectureAmd64}, + Version: "18.09.3-0~debian", + Source: "https://download.docker.com/linux/debian/dists/stretch/pool/stable/amd64/docker-ce_18.09.3~3-0~debian-stretch_amd64.deb", + Hash: "009b9a2d8bfaa97c74773fe4ec25b6bb396b10d0", + ExtraPackages: map[string]packageInfo{ + "cli": { + Version: "18.09.3-0~debian", + Source: "https://download.docker.com/linux/debian/dists/stretch/pool/stable/amd64/docker-ce-cli_18.09.3~3-0~debian-stretch_amd64.deb", + Hash: "557f868ec63e5251639ebd1d8669eb0c61dd555c", + }, + "containerd": { + Version: "1.2.4-1", + Source: "https://download.docker.com/linux/debian/dists/stretch/pool/stable/amd64/containerd.io_1.2.4-1_amd64.deb", + Hash: "48c6ab0c908316af9a183de5aad64703bc516bdf", + }, + }, + Dependencies: []string{"bridge-utils", "libapparmor1", "libltdl7"}, }, // 18.06.2 - CentOS / Rhel7 (two packages) @@ -712,17 +730,14 @@ var dockerVersions = []dockerVersion{ Version: "2.68", Source: "http://mirror.centos.org/centos/7/extras/x86_64/Packages/container-selinux-2.68-1.el7.noarch.rpm", Hash: "d9f87f7f4f2e8e611f556d873a17b8c0c580fec0", - Dependencies: []string{"policycoreutils-python"}, - }, - { - DockerVersion: "18.06.3", - Name: "docker-ce", - Distros: []distros.Distribution{distros.DistributionRhel7, distros.DistributionCentos7}, - Architectures: []Architecture{ArchitectureAmd64}, - Version: "18.06.3.ce", - Source: "https://download.docker.com/linux/centos/7/x86_64/stable/Packages/docker-ce-18.06.3.ce-3.el7.x86_64.rpm", - Hash: "5369602f88406d4fb9159dc1d3fd44e76fb4cab8", - Dependencies: []string{"libtool-ltdl", "libseccomp", "libcgroup"}, + ExtraPackages: map[string]packageInfo{ + "selinux": { + Version: "18.06.3.ce", + Source: "https://download.docker.com/linux/centos/7/x86_64/stable/Packages/docker-ce-18.06.3.ce-3.el7.x86_64.rpm", + Hash: "5369602f88406d4fb9159dc1d3fd44e76fb4cab8", + }, + }, + Dependencies: []string{"libtool-ltdl", "libseccomp", "libcgroup", "policycoreutils-python"}, }, // TIP: When adding the next version, copy the previous @@ -828,11 +843,23 @@ func (b *DockerBuilder) Build(c *fi.ModelBuilderContext) error { c.AddTask(b.buildDockerGroup()) c.AddTask(b.buildSystemdSocket()) } else { + var extraPkgs []*nodetasks.Package + for name, pkg := range dv.ExtraPackages { + dep := &nodetasks.Package{ + Name: dv.Name + "-" + name, + Version: s(pkg.Version), + Source: s(pkg.Source), + Hash: s(pkg.Hash), + PreventStart: fi.Bool(true), + } + extraPkgs = append(extraPkgs, dep) + } packageTask = &nodetasks.Package{ Name: dv.Name, Version: s(dv.Version), Source: s(dv.Source), Hash: s(dv.Hash), + Deps: extraPkgs, // TODO: PreventStart is now unused? PreventStart: fi.Bool(true), diff --git a/upup/pkg/fi/nodeup/nodetasks/package.go b/upup/pkg/fi/nodeup/nodetasks/package.go index f6674411ac..dcc20382ec 100644 --- a/upup/pkg/fi/nodeup/nodetasks/package.go +++ b/upup/pkg/fi/nodeup/nodetasks/package.go @@ -44,6 +44,13 @@ type Package struct { // Healthy is true if the package installation did not fail Healthy *bool `json:"healthy,omitempty"` + + // Additional dependencies that must be installed before this package. + // These will actually be passed together with this package to rpm/dpkg, + // which will then figure out the correct order in which to install them. + // This means that Deps don't get installed unless this package needs to + // get installed. + Deps []*Package `json:"deps,omitempty"` } const ( @@ -162,7 +169,7 @@ func (e *Package) findDpkg(c *fi.Context) (*Package, error) { installed = true installedVersion = version healthy = fi.Bool(true) - case "iF": + case "iF", "iU": installed = true installedVersion = version healthy = fi.Bool(false) @@ -257,37 +264,43 @@ func (_ *Package) RenderLocal(t *local.LocalTarget, a, e, changes *Package) erro defer packageManagerLock.Unlock() if a == nil || changes.Version != nil { - glog.Infof("Installing package %q", e.Name) + glog.Infof("Installing package %q (dependencies: %v)", e.Name, e.Deps) if e.Source != nil { - // Install a deb - local := path.Join(localPackageDir, e.Name) + // Install a deb or rpm. err := os.MkdirAll(localPackageDir, 0755) if err != nil { - return fmt.Errorf("error creating directories %q: %v", path.Dir(local), err) + return fmt.Errorf("error creating directories %q: %v", localPackageDir, err) } - var hash *hashing.Hash - if fi.StringValue(e.Hash) != "" { - parsed, err := hashing.FromString(fi.StringValue(e.Hash)) - if err != nil { - return fmt.Errorf("error paring hash: %v", err) + // Download all the debs/rpms. + localPkgs := make([]string, 1+len(e.Deps)) + for i, pkg := range append([]*Package{e}, e.Deps...) { + local := path.Join(localPackageDir, pkg.Name) + localPkgs[i] = local + var hash *hashing.Hash + if fi.StringValue(pkg.Hash) != "" { + parsed, err := hashing.FromString(fi.StringValue(pkg.Hash)) + if err != nil { + return fmt.Errorf("error paring hash: %v", err) + } + hash = parsed + } + _, err = fi.DownloadURL(fi.StringValue(pkg.Source), local, hash) + if err != nil { + return err } - hash = parsed - } - _, err = fi.DownloadURL(fi.StringValue(e.Source), local, hash) - if err != nil { - return err } var args []string if t.HasTag(tags.TagOSFamilyDebian) { - args = []string{"dpkg", "-i", local} + args = []string{"dpkg", "-i"} } else if t.HasTag(tags.TagOSFamilyRHEL) { - args = []string{"/usr/bin/rpm", "-i", local} + args = []string{"/usr/bin/rpm", "-i"} } else { return fmt.Errorf("unsupported package system") } + args = append(args, localPkgs...) glog.Infof("running command %s", args) cmd := exec.Command(args[0], args[1:]...) output, err := cmd.CombinedOutput()