diff --git a/nodeup/pkg/model/context.go b/nodeup/pkg/model/context.go index 2edf112c5b..e3b5f4cf39 100644 --- a/nodeup/pkg/model/context.go +++ b/nodeup/pkg/model/context.go @@ -20,6 +20,7 @@ import ( "fmt" "os" "path/filepath" + "regexp" "strings" "k8s.io/klog/v2" @@ -598,29 +599,31 @@ func (c *NodeupModelContext) GetPrivateKey(name string) ([]byte, error) { func (b *NodeupModelContext) AddCNIBinAssets(c *fi.ModelBuilderContext, assetNames []string) error { for _, assetName := range assetNames { - if err := b.addCNIBinAsset(c, assetName); err != nil { + re, err := regexp.Compile(fmt.Sprintf("^%s$", assetName)) + if err != nil { + return err + } + if err := b.addCNIBinAsset(c, re); err != nil { return err } } return nil } -func (b *NodeupModelContext) addCNIBinAsset(c *fi.ModelBuilderContext, assetName string) error { - assetPath := "" - asset, err := b.Assets.Find(assetName, assetPath) - if err != nil { - return fmt.Errorf("error trying to locate asset %q: %v", assetName, err) - } - if asset == nil { - return fmt.Errorf("unable to locate asset %q", assetName) +func (b *NodeupModelContext) addCNIBinAsset(c *fi.ModelBuilderContext, assetPath *regexp.Regexp) error { + a := b.Assets.FindMatches(assetPath) + if len(a) != 1 { + return fmt.Errorf("unable to locate asset %q", assetPath.String()) } - c.AddTask(&nodetasks.File{ - Path: filepath.Join(b.CNIBinDir(), assetName), - Contents: asset, - Type: nodetasks.FileType_File, - Mode: fi.String("0755"), - }) + for k, v := range a { + c.AddTask(&nodetasks.File{ + Path: filepath.Join(b.CNIBinDir(), k), + Contents: v, + Type: nodetasks.FileType_File, + Mode: fi.String("0755"), + }) + } return nil } diff --git a/nodeup/pkg/model/convenience.go b/nodeup/pkg/model/convenience.go index e423176d22..1c526ef828 100644 --- a/nodeup/pkg/model/convenience.go +++ b/nodeup/pkg/model/convenience.go @@ -21,13 +21,10 @@ import ( "sort" "strconv" - "k8s.io/kops/pkg/apis/kops" - "k8s.io/kops/upup/pkg/fi" - "k8s.io/kops/util/pkg/architectures" - "k8s.io/kops/util/pkg/distributions" - v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/kops/pkg/apis/kops" + "k8s.io/kops/upup/pkg/fi" ) // s is a helper that builds a *string from a string value @@ -109,77 +106,3 @@ func addHostPathVolume(pod *v1.Pod, container *v1.Container, hostPath v1.HostPat func convEtcdSettingsToMs(dur *metav1.Duration) string { return strconv.FormatInt(dur.Nanoseconds()/1000000, 10) } - -// packageInfo - fields required for extra packages setup -type packageInfo struct { - Version string // Package version - Source string // URL to download the package from - Hash string // sha1sum of the package file -} - -// packageVersion - fields required for downloaded packages setup -type packageVersion struct { - Name string - - // Version is the version of docker, as specified in the kops - Version string - - // Source is the url where the package/tarfile can be found - Source string - - // 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 - - PackageVersion string - Distros []distributions.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 []architectures.Architecture - - // PlainBinary indicates that the Source is not an OS, but a "bare" tar.gz - PlainBinary bool - // MapFiles is the list of files to extract with corresponding directories for PlainBinary - MapFiles map[string]string - - // MarkImmutable is a list of files on which we should perform a `chattr +i ` - MarkImmutable []string -} - -// Match package version against configured values -func (d *packageVersion) matches(arch architectures.Architecture, packageVersion string, distro distributions.Distribution) bool { - if d.PackageVersion != packageVersion { - return false - } - foundDistro := false - if len(d.Distros) > 0 { - for _, d := range d.Distros { - if d == distro { - foundDistro = true - } - } - } else { - // Distro list is empty, assuming ANY - foundDistro = true - } - if !foundDistro { - return false - } - - foundArch := false - for _, a := range d.Architectures { - if a == arch { - foundArch = true - } - } - return foundArch -} diff --git a/pkg/apis/kops/validation/validation.go b/pkg/apis/kops/validation/validation.go index 5571fbd039..c258388aaf 100644 --- a/pkg/apis/kops/validation/validation.go +++ b/pkg/apis/kops/validation/validation.go @@ -185,6 +185,10 @@ func validateClusterSpec(spec *kops.ClusterSpec, c *kops.Cluster, fieldPath *fie allErrs = append(allErrs, validateContainerRuntime(&spec.ContainerRuntime, fieldPath.Child("containerRuntime"))...) } + if spec.Containerd != nil { + allErrs = append(allErrs, validateContainerdConfig(spec.Containerd, fieldPath.Child("containerd"))...) + } + if spec.Docker != nil { allErrs = append(allErrs, validateDockerConfig(spec.Docker, fieldPath.Child("docker"))...) } @@ -1055,16 +1059,37 @@ func validateContainerRuntime(runtime *string, fldPath *field.Path) field.ErrorL return allErrs } +func validateContainerdConfig(config *kops.ContainerdConfig, fldPath *field.Path) field.ErrorList { + allErrs := field.ErrorList{} + + if config.Version != nil { + sv, err := semver.ParseTolerant(*config.Version) + if err != nil { + allErrs = append(allErrs, field.Invalid(fldPath.Child("version"), config.Version, + fmt.Sprintf("unable to parse version string: %s", err.Error()))) + } + if sv.LT(semver.MustParse("1.2.6")) { + allErrs = append(allErrs, field.Invalid(fldPath.Child("version"), config.Version, "unsupported legacy version")) + } + } + + return allErrs +} + func validateDockerConfig(config *kops.DockerConfig, fldPath *field.Path) field.ErrorList { allErrs := field.ErrorList{} if config.Version != nil { - if strings.HasPrefix(*config.Version, "1.1") { + sv, err := semver.ParseTolerant(*config.Version) + if err != nil { allErrs = append(allErrs, field.Invalid(fldPath.Child("version"), config.Version, - "version is no longer available: https://www.docker.com/blog/changes-dockerproject-org-apt-yum-repositories/")) - } else { - valid := []string{"17.03.2", "17.09.0", "18.03.1", "18.06.1", "18.06.2", "18.06.3", "18.09.3", "18.09.9", "19.03.4", "19.03.8", "19.03.11", "19.03.13"} - allErrs = append(allErrs, IsValidValue(fldPath.Child("version"), config.Version, valid)...) + fmt.Sprintf("unable to parse version string: %s", err.Error()))) + } + if sv.LT(semver.MustParse("1.14.0")) { + allErrs = append(allErrs, field.Invalid(fldPath.Child("version"), config.Version, + "version is no longer available: https://www.docker.com/blog/changes-dockerproject-org-apt-yum-repositories")) + } else if sv.LT(semver.MustParse("17.3.0")) { + allErrs = append(allErrs, field.Invalid(fldPath.Child("version"), config.Version, "unsupported legacy version")) } } diff --git a/pkg/model/components/containerd.go b/pkg/model/components/containerd.go index 114144931a..8e6b41f9bd 100644 --- a/pkg/model/components/containerd.go +++ b/pkg/model/components/containerd.go @@ -20,6 +20,8 @@ import ( "fmt" "strings" + "github.com/blang/semver/v4" + "k8s.io/klog/v2" "k8s.io/kops/pkg/apis/kops" "k8s.io/kops/upup/pkg/fi" @@ -80,26 +82,17 @@ func (b *ContainerdOptionsBuilder) BuildOptions(o interface{}) error { } } else if clusterSpec.ContainerRuntime == "docker" { - if fi.StringValue(containerd.Version) == "" { - // Docker version should always be available - if fi.StringValue(clusterSpec.Docker.Version) == "" { - return fmt.Errorf("docker version is required") + // Docker version should always be available + dockerVersion := fi.StringValue(clusterSpec.Docker.Version) + if dockerVersion == "" { + return fmt.Errorf("docker version is required") + } else { + // Skip containerd setup for older versions without containerd service + sv, err := semver.ParseTolerant(dockerVersion) + if err != nil { + return fmt.Errorf("unable to parse version string: %q", dockerVersion) } - - // Set the containerd version for known Docker versions - switch fi.StringValue(clusterSpec.Docker.Version) { - case "19.03.13": - containerd.Version = fi.String("1.3.7") - case "19.03.8", "19.03.11": - containerd.Version = fi.String("1.2.13") - case "19.03.4": - containerd.Version = fi.String("1.2.10") - case "18.09.9": - containerd.Version = fi.String("1.2.10") - case "18.09.3": - containerd.Version = fi.String("1.2.4") - default: - // Old version of docker, single package + if sv.LT(semver.MustParse("18.9.0")) { containerd.SkipInstall = true return nil } diff --git a/upup/pkg/fi/cloudup/BUILD.bazel b/upup/pkg/fi/cloudup/BUILD.bazel index eb764e1311..88e7c9a726 100644 --- a/upup/pkg/fi/cloudup/BUILD.bazel +++ b/upup/pkg/fi/cloudup/BUILD.bazel @@ -5,8 +5,10 @@ go_library( srcs = [ "apply_cluster.go", "bootstrapchannelbuilder.go", + "containerd.go", "defaults.go", "dns.go", + "docker.go", "loader.go", "networking.go", "new_cluster.go", @@ -98,9 +100,11 @@ go_test( size = "small", srcs = [ "bootstrapchannelbuilder_test.go", + "containerd_test.go", "deepvalidate_test.go", "defaults_test.go", "dns_test.go", + "docker_test.go", "networking_test.go", "new_cluster_test.go", "populatecluster_test.go",