ARM64 support - Side-load multi-arch images

This commit is contained in:
Ciprian Hacman 2020-08-10 11:09:25 +03:00
parent c51a811c21
commit 331d223043
5 changed files with 62 additions and 50 deletions

View File

@ -56,7 +56,7 @@ func (t *ProtokubeBuilder) Build(c *fi.ModelBuilderContext) error {
return nil
}
if protokubeImage := t.NodeupConfig.ProtokubeImage; protokubeImage != nil {
if protokubeImage := t.NodeupConfig.ProtokubeImage[t.Architecture]; protokubeImage != nil {
c.AddTask(&nodetasks.LoadImageTask{
Name: "protokube",
Sources: protokubeImage.Sources,
@ -166,8 +166,8 @@ func (t *ProtokubeBuilder) buildSystemdService() (*nodetasks.Service, error) {
// ProtokubeImageName returns the docker image for protokube
func (t *ProtokubeBuilder) ProtokubeImageName() string {
name := ""
if t.NodeupConfig.ProtokubeImage != nil && t.NodeupConfig.ProtokubeImage.Name != "" {
name = t.NodeupConfig.ProtokubeImage.Name
if t.NodeupConfig.ProtokubeImage[t.Architecture] != nil && t.NodeupConfig.ProtokubeImage[t.Architecture].Name != "" {
name = t.NodeupConfig.ProtokubeImage[t.Architecture].Name
}
if name == "" {
// use current default corresponding to this version of nodeup
@ -179,8 +179,8 @@ func (t *ProtokubeBuilder) ProtokubeImageName() string {
// ProtokubeImagePullCommand returns the command to pull the image
func (t *ProtokubeBuilder) ProtokubeImagePullCommand() (string, error) {
var sources []string
if t.NodeupConfig.ProtokubeImage != nil {
sources = t.NodeupConfig.ProtokubeImage.Sources
if t.NodeupConfig.ProtokubeImage[t.Architecture] != nil {
sources = t.NodeupConfig.ProtokubeImage[t.Architecture].Sources
}
if len(sources) == 0 {
// Nothing to pull; return dummy value

View File

@ -21,6 +21,7 @@ import (
"k8s.io/kops/pkg/apis/nodeup"
"k8s.io/kops/upup/pkg/fi"
"k8s.io/kops/util/pkg/architectures"
)
func TestProtokubeBuilder_Docker(t *testing.T) {
@ -43,7 +44,10 @@ func populateImage(ctx *NodeupModelContext) {
if ctx.NodeupConfig == nil {
ctx.NodeupConfig = &nodeup.Config{}
}
ctx.NodeupConfig.ProtokubeImage = &nodeup.Image{
if ctx.NodeupConfig.ProtokubeImage == nil {
ctx.NodeupConfig.ProtokubeImage = make(map[architectures.Architecture]*nodeup.Image)
}
ctx.NodeupConfig.ProtokubeImage[architectures.ArchitectureAmd64] = &nodeup.Image{
Name: "protokube image name",
}
}

View File

@ -44,7 +44,7 @@ type Config struct {
// ClusterName is the name of the cluster
ClusterName string `json:",omitempty"`
// ProtokubeImage is the docker image to load for protokube (bootstrapping)
ProtokubeImage *Image `json:"protokubeImage,omitempty"`
ProtokubeImage map[architectures.Architecture]*Image `json:"protokubeImage,omitempty"`
// Channels is a list of channels that we should apply
Channels []string `json:"channels,omitempty"`
// ApiserverAdditionalIPs are additional IP address to put in the apiserver server cert.

View File

@ -1043,7 +1043,6 @@ func (c *ApplyClusterCmd) validateKubernetesVersion() error {
func (c *ApplyClusterCmd) addFileAssets(assetBuilder *assets.AssetBuilder) error {
var baseURL string
var err error
if components.IsBaseURL(c.Cluster.Spec.KubernetesVersion) {
baseURL = c.Cluster.Spec.KubernetesVersion
} else {
@ -1101,17 +1100,17 @@ func (c *ApplyClusterCmd) addFileAssets(assetBuilder *assets.AssetBuilder) error
}
c.NodeUpSource[arch] = strings.Join(asset.Locations, ",")
c.NodeUpHash[arch] = asset.Hash.Hex()
}
// TODO: Update Kops version in integration tests to 1.19.0 after it is released
// Integration tests fake the Kops version to 1.19.0-alpha.1 and will not be able to find Protokube
if kopsbase.Version != "1.19.0-alpha.1" {
// Explicitly add the protokube image,
// otherwise when the Target is DryRun this asset is not added
// Is there a better way to call this?
_, _, err = ProtokubeImageSource(assetBuilder)
if err != nil {
return err
// TODO: Update Kops version in integration tests to 1.19.0 after it is released
// Integration tests fake the Kops version to 1.19.0-alpha.1 and will not be able to find Protokube
if kopsbase.Version != "1.19.0-alpha.1" {
// Explicitly add the protokube image,
// otherwise when the Target is DryRun this asset is not added
// Is there a better way to call this?
_, _, err = ProtokubeImageSource(assetBuilder, arch)
if err != nil {
return err
}
}
}
@ -1155,7 +1154,7 @@ type nodeUpConfigBuilder struct {
cluster *kops.Cluster
etcdManifests map[kops.InstanceGroupRole][]string
images map[kops.InstanceGroupRole]map[architectures.Architecture][]*nodeup.Image
protokubeImage map[kops.InstanceGroupRole]*nodeup.Image
protokubeImage map[kops.InstanceGroupRole]map[architectures.Architecture]*nodeup.Image
}
func (c *ApplyClusterCmd) newNodeUpConfigBuilder(assetBuilder *assets.AssetBuilder) (model.NodeUpConfigBuilder, error) {
@ -1178,7 +1177,7 @@ func (c *ApplyClusterCmd) newNodeUpConfigBuilder(assetBuilder *assets.AssetBuild
etcdManifests := map[kops.InstanceGroupRole][]string{}
images := map[kops.InstanceGroupRole]map[architectures.Architecture][]*nodeup.Image{}
protokubeImage := map[kops.InstanceGroupRole]*nodeup.Image{}
protokubeImage := map[kops.InstanceGroupRole]map[architectures.Architecture]*nodeup.Image{}
for _, role := range kops.AllInstanceGroupRoles {
isMaster := role == kops.InstanceGroupRoleMaster
@ -1218,18 +1217,13 @@ func (c *ApplyClusterCmd) newNodeUpConfigBuilder(assetBuilder *assets.AssetBuild
// don't need to push/pull from a registry
if os.Getenv("KOPS_BASE_URL") != "" && isMaster {
for _, arch := range architectures.GetSupported() {
// TODO: Build multi-arch Kops images
if arch != architectures.ArchitectureAmd64 {
continue
}
for _, name := range []string{"kops-controller", "dns-controller", "kube-apiserver-healthcheck"} {
baseURL, err := url.Parse(os.Getenv("KOPS_BASE_URL"))
if err != nil {
return nil, err
}
baseURL.Path = path.Join(baseURL.Path, "/images/"+name+".tar.gz")
baseURL.Path = path.Join(baseURL.Path, "/images/"+name+"-"+string(arch)+".tar.gz")
u, hash, err := assetBuilder.RemapFileAndSHA(baseURL)
if err != nil {
@ -1249,17 +1243,20 @@ func (c *ApplyClusterCmd) newNodeUpConfigBuilder(assetBuilder *assets.AssetBuild
// TODO: Update Kops version in integration tests to 1.19.0 after it is released
// Integration tests fake the Kops version to 1.19.0-alpha.1 and will not be able to find Protokube
if kopsbase.Version != "1.19.0-alpha.1" {
u, hash, err := ProtokubeImageSource(assetBuilder)
if err != nil {
return nil, err
}
protokubeImage[role] = make(map[architectures.Architecture]*nodeup.Image)
for _, arch := range architectures.GetSupported() {
u, hash, err := ProtokubeImageSource(assetBuilder, arch)
if err != nil {
return nil, err
}
asset := BuildMirroredAsset(u, hash)
asset := BuildMirroredAsset(u, hash)
protokubeImage[role] = &nodeup.Image{
Name: kopsbase.DefaultProtokubeImageName(),
Sources: asset.Locations,
Hash: asset.Hash.Hex(),
protokubeImage[role][arch] = &nodeup.Image{
Name: kopsbase.DefaultProtokubeImageName(),
Sources: asset.Locations,
Hash: asset.Hash.Hex(),
}
}
}
}

View File

@ -61,10 +61,10 @@ var kopsBaseURL *url.URL
var nodeUpAsset map[architectures.Architecture]*MirroredAsset
// protokubeLocation caches the protokubeLocation url
var protokubeLocation *url.URL
var protokubeLocation map[architectures.Architecture]*url.URL
// protokubeHash caches the hash for protokube
var protokubeHash *hashing.Hash
var protokubeHash map[architectures.Architecture]*hashing.Hash
// BaseURL returns the base url for the distribution of kops - in particular for nodeup & docker images
func BaseURL() (*url.URL, error) {
@ -122,7 +122,8 @@ func SetKopsAssetsLocations(assetsBuilder *assets.AssetBuilder) error {
func NodeUpAsset(assetsBuilder *assets.AssetBuilder, arch architectures.Architecture) (*MirroredAsset, error) {
if nodeUpAsset == nil {
nodeUpAsset = make(map[architectures.Architecture]*MirroredAsset)
} else if nodeUpAsset[arch] != nil {
}
if nodeUpAsset[arch] != nil {
// Avoid repeated logging
klog.V(8).Infof("Using cached nodeup location for %s: %v", arch, nodeUpAsset[arch].Locations)
return nodeUpAsset[arch], nil
@ -168,34 +169,44 @@ func NodeUpAsset(assetsBuilder *assets.AssetBuilder, arch architectures.Architec
// ProtokubeImageSource returns the source for the docker image for protokube.
// Either a docker name (e.g. gcr.io/protokube:1.4), or a URL (https://...) in which case we download
// the contents of the url and docker load it
func ProtokubeImageSource(assetsBuilder *assets.AssetBuilder) (*url.URL, *hashing.Hash, error) {
// Avoid repeated logging
if protokubeLocation != nil && protokubeHash != nil {
klog.V(8).Infof("Using cached protokube location: %q", protokubeLocation)
return protokubeLocation, protokubeHash, nil
func ProtokubeImageSource(assetsBuilder *assets.AssetBuilder, arch architectures.Architecture) (*url.URL, *hashing.Hash, error) {
if protokubeLocation == nil {
protokubeLocation = make(map[architectures.Architecture]*url.URL)
}
if protokubeHash == nil {
protokubeHash = make(map[architectures.Architecture]*hashing.Hash)
}
if nodeUpAsset[arch] != nil && protokubeHash[arch] != nil {
// Avoid repeated logging
klog.V(8).Infof("Using cached protokube location for %s: %q", arch, protokubeLocation[arch])
return protokubeLocation[arch], protokubeHash[arch], nil
}
// Use multi-arch env var, but fall back to well known env var
env := os.Getenv(fmt.Sprintf("PROTOKUBE_IMAGE_%s", strings.ToUpper(string(arch))))
if env == "" {
env = os.Getenv("PROTOKUBE_IMAGE")
}
env := os.Getenv("PROTOKUBE_IMAGE")
var err error
if env == "" {
protokubeLocation, protokubeHash, err = KopsFileURL("images/protokube.tar.gz", assetsBuilder)
protokubeLocation[arch], protokubeHash[arch], err = KopsFileURL(fmt.Sprintf("images/protokube-%s.tar.gz", arch), assetsBuilder)
if err != nil {
return nil, nil, err
}
klog.V(8).Infof("Using default protokube location: %q", protokubeLocation)
klog.V(8).Infof("Using default protokube location: %q", protokubeLocation[arch])
} else {
protokubeImageSource, err := url.Parse(env)
if err != nil {
return nil, nil, fmt.Errorf("unable to parse env var PROTOKUBE_IMAGE %q as a url: %v", env, err)
return nil, nil, fmt.Errorf("unable to parse env var PROTOKUBE_IMAGE(_%s) %q as a url: %v", strings.ToUpper(string(arch)), env, err)
}
protokubeLocation, protokubeHash, err = assetsBuilder.RemapFileAndSHA(protokubeImageSource)
protokubeLocation[arch], protokubeHash[arch], err = assetsBuilder.RemapFileAndSHA(protokubeImageSource)
if err != nil {
return nil, nil, err
}
klog.Warningf("Using protokube location from PROTOKUBE_IMAGE env var: %q", protokubeLocation)
klog.Warningf("Using protokube location from PROTOKUBE_IMAGE(_%s) env var: %q", strings.ToUpper(string(arch)), protokubeLocation[arch])
}
return protokubeLocation, protokubeHash, nil
return protokubeLocation[arch], protokubeHash[arch], nil
}
// KopsFileURL returns the base url for the distribution of kops - in particular for nodeup & docker images