diff --git a/cluster/plan.go b/cluster/plan.go index ea344a355..5a750484e 100644 --- a/cluster/plan.go +++ b/cluster/plan.go @@ -2,6 +2,7 @@ package cluster import ( "context" + "crypto/md5" "fmt" "path" "strconv" @@ -29,6 +30,7 @@ const ( CoreOS = "CoreOS" CoreOSPrefixPath = "/opt/rke" ContainerNameLabel = "io.rancher.rke.container.name" + CloudConfigSumEnv = "RKE_CLOUD_CONFIG_CHECKSUM" ) func GeneratePlan(ctx context.Context, rkeConfig *v3.RancherKubernetesEngineConfig, hostsInfoMap map[string]types.Info) (v3.RKEPlan, error) { @@ -132,6 +134,11 @@ func (c *Cluster) BuildKubeAPIProcess(prefixPath string) v3.Process { if len(c.CloudProvider.Name) > 0 && c.CloudProvider.Name != aws.AWSCloudProviderName { CommandArgs["cloud-config"] = CloudConfigPath } + if len(c.CloudProvider.Name) > 0 { + c.Services.KubeAPI.ExtraEnv = append( + c.Services.KubeAPI.ExtraEnv, + fmt.Sprintf("%s=%s", CloudConfigSumEnv, getCloudConfigChecksum(c.CloudProvider))) + } // check if our version has specific options for this component serviceOptions := c.GetKubernetesServicesOptions() if serviceOptions.KubeAPI != nil { @@ -192,7 +199,7 @@ func (c *Cluster) BuildKubeAPIProcess(prefixPath string) v3.Process { Args: args, VolumesFrom: VolumesFrom, Binds: Binds, - Env: c.Services.KubeAPI.ExtraEnv, + Env: getUniqStringList(c.Services.KubeAPI.ExtraEnv), NetworkMode: "host", RestartPolicy: "always", Image: c.Services.KubeAPI.Image, @@ -230,7 +237,11 @@ func (c *Cluster) BuildKubeControllerProcess(prefixPath string) v3.Process { if len(c.CloudProvider.Name) > 0 && c.CloudProvider.Name != aws.AWSCloudProviderName { CommandArgs["cloud-config"] = CloudConfigPath } - + if len(c.CloudProvider.Name) > 0 { + c.Services.KubeController.ExtraEnv = append( + c.Services.KubeController.ExtraEnv, + fmt.Sprintf("%s=%s", CloudConfigSumEnv, getCloudConfigChecksum(c.CloudProvider))) + } // check if our version has specific options for this component serviceOptions := c.GetKubernetesServicesOptions() if serviceOptions.KubeController != nil { @@ -274,7 +285,7 @@ func (c *Cluster) BuildKubeControllerProcess(prefixPath string) v3.Process { Args: args, VolumesFrom: VolumesFrom, Binds: Binds, - Env: c.Services.KubeController.ExtraEnv, + Env: getUniqStringList(c.Services.KubeController.ExtraEnv), NetworkMode: "host", RestartPolicy: "always", Image: c.Services.KubeController.Image, @@ -323,7 +334,11 @@ func (c *Cluster) BuildKubeletProcess(host *hosts.Host, prefixPath string) v3.Pr if len(c.CloudProvider.Name) > 0 && c.CloudProvider.Name != aws.AWSCloudProviderName { CommandArgs["cloud-config"] = CloudConfigPath } - + if len(c.CloudProvider.Name) > 0 { + c.Services.Kubelet.ExtraEnv = append( + c.Services.Kubelet.ExtraEnv, + fmt.Sprintf("%s=%s", CloudConfigSumEnv, getCloudConfigChecksum(c.CloudProvider))) + } // check if our version has specific options for this component serviceOptions := c.GetKubernetesServicesOptions() if serviceOptions.Kubelet != nil { @@ -379,7 +394,7 @@ func (c *Cluster) BuildKubeletProcess(host *hosts.Host, prefixPath string) v3.Pr Command: Command, VolumesFrom: VolumesFrom, Binds: Binds, - Env: c.Services.Kubelet.ExtraEnv, + Env: getUniqStringList(c.Services.Kubelet.ExtraEnv), NetworkMode: "host", RestartPolicy: "always", Image: c.Services.Kubelet.Image, @@ -705,3 +720,20 @@ func getTagMajorVersion(tag string) string { } return strings.Join(splitTag[:2], ".") } + +func getCloudConfigChecksum(config v3.CloudProvider) string { + configByteSum := md5.Sum([]byte(fmt.Sprintf("%s", config))) + return fmt.Sprintf("%x", configByteSum) +} + +func getUniqStringList(l []string) []string { + m := map[string]bool{} + ul := []string{} + for _, k := range l { + if _, ok := m[k]; !ok { + m[k] = true + ul = append(ul, k) + } + } + return ul +} diff --git a/docker/docker.go b/docker/docker.go index d61ff0a87..ca21c140c 100644 --- a/docker/docker.go +++ b/docker/docker.go @@ -299,7 +299,8 @@ func IsContainerUpgradable(ctx context.Context, dClient *client.Client, imageCfg } if containerInspect.Config.Image != imageCfg.Image || !sliceEqualsIgnoreOrder(containerInspect.Config.Entrypoint, imageCfg.Entrypoint) || - !sliceEqualsIgnoreOrder(containerInspect.Config.Cmd, imageCfg.Cmd) { + !sliceEqualsIgnoreOrder(containerInspect.Config.Cmd, imageCfg.Cmd) || + !isContainerRKEEnvChanged(containerInspect.Config.Env, imageCfg.Env) { logrus.Debugf("[%s] Container [%s] is eligible for upgrade on host [%s]", plane, containerName, hostname) return true, nil } @@ -390,3 +391,21 @@ func convertToSemver(version string) (*semver.Version, error) { compVersion[2] = "0" return semver.NewVersion(strings.Join(compVersion, ".")) } + +func isContainerRKEEnvChanged(containerEnv, imageConfigEnv []string) bool { + // remove PATH env from the container env + cleanedContainerEnv := getRKEEnvVars(containerEnv) + cleanedImageConfigEnv := getRKEEnvVars(imageConfigEnv) + + return sliceEqualsIgnoreOrder(cleanedContainerEnv, cleanedImageConfigEnv) +} + +func getRKEEnvVars(env []string) []string { + tmp := []string{} + for _, e := range env { + if strings.HasPrefix("RKE_", e) { + tmp = append(tmp, e) + } + } + return tmp +}