AB#2076: version specific images (#288)

KubernetesVersion sent by the init command now controls
all downloaded binaries, if they depend on the k8s version.

* Move all download links into /internal/versions.
* Unify files in /internal/versions package
* Move image download links into VersionConfigs
and thus make them dependant on the k8s version,
where the image version is specific to the k8s version.
* Don't specify patch version in k8sVersion
This commit is contained in:
Otto Bittner 2022-07-21 14:41:07 +02:00 committed by GitHub
parent b57e9cf92a
commit 741384158a
22 changed files with 137 additions and 112 deletions

View file

@ -3,11 +3,12 @@ package azure
import (
"context"
"encoding/json"
"fmt"
"github.com/edgelesssys/constellation/bootstrapper/cloudprovider"
"github.com/edgelesssys/constellation/bootstrapper/internal/kubernetes/k8sapi/resources"
"github.com/edgelesssys/constellation/internal/azureshared"
"github.com/edgelesssys/constellation/internal/cloud/metadata"
"github.com/edgelesssys/constellation/internal/versions"
k8s "k8s.io/api/core/v1"
meta "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@ -29,8 +30,11 @@ func NewCloudControllerManager(metadata ccmMetadata) *CloudControllerManager {
}
// Image returns the container image used to provide cloud-controller-manager for the cloud-provider.
func (c *CloudControllerManager) Image() string {
return cloudprovider.CloudControllerManagerImageAzure
func (c *CloudControllerManager) Image(k8sVersion string) (string, error) {
if !versions.IsSupportedK8sVersion(k8sVersion) {
return "", fmt.Errorf("received unsupported k8sVersion: %s", k8sVersion)
}
return versions.VersionConfigs[k8sVersion].CloudControllerManagerImageAzure, nil
}
// Path returns the path used by cloud-controller-manager executable within the container image.

View file

@ -88,7 +88,7 @@ func TestTrivialCCMFunctions(t *testing.T) {
assert := assert.New(t)
cloud := CloudControllerManager{}
assert.NotEmpty(cloud.Image())
assert.NotEmpty(cloud.Image("1.23"))
assert.NotEmpty(cloud.Path())
assert.NotEmpty(cloud.Name())
assert.NotEmpty(cloud.ExtraArgs())

View file

@ -1,14 +1,21 @@
package azure
import "github.com/edgelesssys/constellation/bootstrapper/cloudprovider"
import (
"fmt"
"github.com/edgelesssys/constellation/internal/versions"
)
// CloudNodeManager holds the Azure cloud-node-manager configuration.
// reference: https://raw.githubusercontent.com/kubernetes-sigs/cloud-provider-azure/master/examples/out-of-tree/cloud-node-manager.yaml .
type CloudNodeManager struct{}
// Image returns the container image used to provide cloud-node-manager for the cloud-provider.
func (c *CloudNodeManager) Image() string {
return cloudprovider.CloudNodeManagerImageAzure
func (c *CloudNodeManager) Image(k8sVersion string) (string, error) {
if !versions.IsSupportedK8sVersion(k8sVersion) {
return "", fmt.Errorf("received unsupported k8sVersion: %s", k8sVersion)
}
return versions.VersionConfigs[k8sVersion].CloudControllerManagerImageAzure, nil
}
// Path returns the path used by cloud-node-manager executable within the container image.

View file

@ -10,7 +10,7 @@ func TestTrivialCNMFunctions(t *testing.T) {
assert := assert.New(t)
cloud := CloudNodeManager{}
assert.NotEmpty(cloud.Image())
assert.NotEmpty(cloud.Image("1.23"))
assert.NotEmpty(cloud.Path())
assert.NotEmpty(cloud.ExtraArgs())
assert.True(cloud.Supported())

View file

@ -6,10 +6,10 @@ import (
"fmt"
"strings"
"github.com/edgelesssys/constellation/bootstrapper/cloudprovider"
"github.com/edgelesssys/constellation/bootstrapper/internal/kubernetes/k8sapi/resources"
"github.com/edgelesssys/constellation/internal/cloud/metadata"
"github.com/edgelesssys/constellation/internal/gcpshared"
"github.com/edgelesssys/constellation/internal/versions"
k8s "k8s.io/api/core/v1"
v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)
@ -18,8 +18,11 @@ import (
type CloudControllerManager struct{}
// Image returns the container image used to provide cloud-controller-manager for the cloud-provider.
func (c *CloudControllerManager) Image() string {
return cloudprovider.CloudControllerManagerImageGCP
func (c *CloudControllerManager) Image(k8sVersion string) (string, error) {
if !versions.IsSupportedK8sVersion(k8sVersion) {
return "", fmt.Errorf("received unsupported k8sVersion: %s", k8sVersion)
}
return versions.VersionConfigs[k8sVersion].CloudControllerManagerImageAzure, nil
}
// Path returns the path used by cloud-controller-manager executable within the container image.

View file

@ -132,7 +132,7 @@ func TestTrivialCCMFunctions(t *testing.T) {
assert := assert.New(t)
cloud := CloudControllerManager{}
assert.NotEmpty(cloud.Image())
assert.NotEmpty(cloud.Image("1.23"))
assert.NotEmpty(cloud.Path())
assert.NotEmpty(cloud.Name())
assert.NotEmpty(cloud.ExtraArgs())

View file

@ -5,8 +5,8 @@ type CloudNodeManager struct{}
// Image returns the container image used to provide cloud-node-manager for the cloud-provider.
// Not used on GCP.
func (c *CloudNodeManager) Image() string {
return ""
func (c *CloudNodeManager) Image(k8sVersion string) (string, error) {
return "", nil
}
// Path returns the path used by cloud-node-manager executable within the container image.

View file

@ -10,7 +10,7 @@ func TestTrivialCNMFunctions(t *testing.T) {
assert := assert.New(t)
cloud := CloudNodeManager{}
assert.Empty(cloud.Image())
assert.Empty(cloud.Image(""))
assert.Empty(cloud.Path())
assert.Empty(cloud.ExtraArgs())
assert.False(cloud.Supported())

View file

@ -1,13 +0,0 @@
package cloudprovider
const (
// CloudControllerManagerImageAWS is the CCM image used on AWS.
CloudControllerManagerImageAWS = "us.gcr.io/k8s-artifacts-prod/provider-aws/cloud-controller-manager:v1.22.0-alpha.0"
// CloudControllerManagerImageGCP is the CCM image used on GCP.
// TODO: use newer "cloud-provider-gcp" from https://github.com/kubernetes/cloud-provider-gcp when newer releases are available.
CloudControllerManagerImageGCP = "ghcr.io/edgelesssys/cloud-provider-gcp:sha-2f6a5b07fc2d37f24f8ff725132f87584d627d8f"
// CloudControllerManagerImageAzure is the CCM image used on Azure.
CloudControllerManagerImageAzure = "mcr.microsoft.com/oss/kubernetes/azure-cloud-controller-manager:v1.23.11"
// CloudNodeManagerImageAzure is the cloud-node-manager image used on Azure.
CloudNodeManagerImageAzure = "mcr.microsoft.com/oss/kubernetes/azure-cloud-node-manager:v1.23.11"
)

View file

@ -12,8 +12,8 @@ import (
type CloudControllerManager struct{}
// Image returns the container image used to provide cloud-controller-manager for the cloud-provider.
func (c CloudControllerManager) Image() string {
return ""
func (c CloudControllerManager) Image(k8sVersion string) (string, error) {
return "", nil
}
// Path returns the path used by cloud-controller-manager executable within the container image.

View file

@ -5,8 +5,8 @@ type CloudNodeManager struct{}
// Image returns the container image used to provide cloud-node-manager for the cloud-provider.
// Not used on QEMU.
func (c *CloudNodeManager) Image() string {
return ""
func (c *CloudNodeManager) Image(k8sVersion string) (string, error) {
return "", nil
}
// Path returns the path used by cloud-node-manager executable within the container image.

View file

@ -29,7 +29,7 @@ type ProviderMetadata interface {
// CloudControllerManager implementers provide configuration for the k8s cloud-controller-manager.
type CloudControllerManager interface {
// Image returns the container image used to provide cloud-controller-manager for the cloud-provider.
Image() string
Image(k8sVersion string) (string, error)
// Path returns the path used by cloud-controller-manager executable within the container image.
Path() string
// Name returns the cloud-provider name as used by k8s cloud-controller-manager (k8s.gcr.io/cloud-controller-manager).
@ -56,7 +56,7 @@ type CloudControllerManager interface {
// CloudNodeManager implementers provide configuration for the k8s cloud-node-manager.
type CloudNodeManager interface {
// Image returns the container image used to provide cloud-node-manager for the cloud-provider.
Image() string
Image(k8sVersion string) (string, error)
// Path returns the path used by cloud-node-manager executable within the container image.
Path() string
// ExtraArgs returns a list of arguments to append to the cloud-node-manager command.
@ -133,8 +133,8 @@ type stubCloudControllerManager struct {
SupportedResp bool
}
func (m *stubCloudControllerManager) Image() string {
return "stub-image:latest"
func (m *stubCloudControllerManager) Image(k8sVersion string) (string, error) {
return "stub-image:latest", nil
}
func (m *stubCloudControllerManager) Path() string {
@ -181,8 +181,8 @@ type stubCloudNodeManager struct {
ExtraArgsResp []string
}
func (m *stubCloudNodeManager) Image() string {
return m.ImageResp
func (m *stubCloudNodeManager) Image(k8sVersion string) (string, error) {
return m.ImageResp, nil
}
func (m *stubCloudNodeManager) Path() string {

View file

@ -245,7 +245,7 @@ func TestGetObjects(t *testing.T) {
wantErr bool
}{
"GetObjects works on cluster-autoscaler deployment": {
wantResources: resources.NewDefaultAutoscalerDeployment(nil, nil, nil),
wantResources: resources.NewDefaultAutoscalerDeployment(nil, nil, nil, ""),
resourcesYAML: string(nginxDeplYAML),
wantErr: false,
},

View file

@ -23,7 +23,7 @@ type autoscalerDeployment struct {
}
// NewDefaultAutoscalerDeployment creates a new *autoscalerDeployment, customized for the CSP.
func NewDefaultAutoscalerDeployment(extraVolumes []k8s.Volume, extraVolumeMounts []k8s.VolumeMount, env []k8s.EnvVar) *autoscalerDeployment {
func NewDefaultAutoscalerDeployment(extraVolumes []k8s.Volume, extraVolumeMounts []k8s.VolumeMount, env []k8s.EnvVar, k8sVersion string) *autoscalerDeployment {
return &autoscalerDeployment{
PodDisruptionBudget: policy.PodDisruptionBudget{
TypeMeta: v1.TypeMeta{
@ -435,7 +435,7 @@ func NewDefaultAutoscalerDeployment(extraVolumes []k8s.Volume, extraVolumeMounts
Containers: []k8s.Container{
{
Name: "cluster-autoscaler",
Image: versions.ClusterAutoscalerImage,
Image: versions.VersionConfigs[k8sVersion].ClusterAutoscalerImage,
ImagePullPolicy: k8s.PullIfNotPresent,
LivenessProbe: &k8s.Probe{
ProbeHandler: k8s.ProbeHandler{

View file

@ -11,7 +11,7 @@ func TestAutoscalerDeploymentMarshalUnmarshal(t *testing.T) {
require := require.New(t)
assert := assert.New(t)
autoscalerDepl := NewDefaultAutoscalerDeployment(nil, nil, nil)
autoscalerDepl := NewDefaultAutoscalerDeployment(nil, nil, nil, "")
data, err := autoscalerDepl.Marshal()
require.NoError(err)
@ -27,7 +27,7 @@ func TestAutoscalerDeploymentWithCommandMarshalUnmarshal(t *testing.T) {
require := require.New(t)
assert := assert.New(t)
autoscalerDepl := NewDefaultAutoscalerDeployment(nil, nil, nil)
autoscalerDepl := NewDefaultAutoscalerDeployment(nil, nil, nil, "")
autoscalerDepl.SetAutoscalerCommand("someProvider", []string{"group1", "group2"})
data, err := autoscalerDepl.Marshal()

View file

@ -84,6 +84,8 @@ func (k *KubeWrapper) InitCluster(
id attestationtypes.ID, kmsConfig KMSConfig, sshUsers map[string]string, log *logger.Logger,
) ([]byte, error) {
log.With(zap.String("version", k8sVersion)).Infof("Installing Kubernetes components")
// InstallComponents validates the k8sVersion as it's first action and returns if not supported.
// This implicitly makes k8sVersion safe to use in this function.
if err := k.clusterUtil.InstallComponents(ctx, k8sVersion); err != nil {
return nil, err
}
@ -194,14 +196,14 @@ func (k *KubeWrapper) InitCluster(
return nil, fmt.Errorf("setting up join service failed: %w", err)
}
if err := k.setupCCM(ctx, subnetworkPodCIDR, cloudServiceAccountURI, instance); err != nil {
if err := k.setupCCM(ctx, subnetworkPodCIDR, cloudServiceAccountURI, instance, k8sVersion); err != nil {
return nil, fmt.Errorf("setting up cloud controller manager: %w", err)
}
if err := k.setupCloudNodeManager(); err != nil {
if err := k.setupCloudNodeManager(k8sVersion); err != nil {
return nil, fmt.Errorf("setting up cloud node manager: %w", err)
}
if err := k.setupClusterAutoscaler(instance, cloudServiceAccountURI, autoscalingNodeGroups); err != nil {
if err := k.setupClusterAutoscaler(instance, cloudServiceAccountURI, autoscalingNodeGroups, k8sVersion); err != nil {
return nil, fmt.Errorf("setting up cluster autoscaler: %w", err)
}
@ -309,7 +311,7 @@ func (k *KubeWrapper) setupJoinService(csp string, measurementsJSON []byte, id a
return k.clusterUtil.SetupJoinService(k.client, joinConfiguration)
}
func (k *KubeWrapper) setupCCM(ctx context.Context, subnetworkPodCIDR, cloudServiceAccountURI string, instance metadata.InstanceMetadata) error {
func (k *KubeWrapper) setupCCM(ctx context.Context, subnetworkPodCIDR, cloudServiceAccountURI string, instance metadata.InstanceMetadata, k8sVersion string) error {
if !k.cloudControllerManager.Supported() {
return nil
}
@ -321,9 +323,13 @@ func (k *KubeWrapper) setupCCM(ctx context.Context, subnetworkPodCIDR, cloudServ
if err != nil {
return fmt.Errorf("defining Secrets for CCM failed: %w", err)
}
ccmImage, err := k.cloudControllerManager.Image(k8sVersion)
if err != nil {
return fmt.Errorf("defining Image for CCM failed: %w", err)
}
cloudControllerManagerConfiguration := resources.NewDefaultCloudControllerManagerDeployment(
k.cloudControllerManager.Name(), k.cloudControllerManager.Image(), k.cloudControllerManager.Path(), subnetworkPodCIDR,
k.cloudControllerManager.Name(), ccmImage, k.cloudControllerManager.Path(), subnetworkPodCIDR,
k.cloudControllerManager.ExtraArgs(), k.cloudControllerManager.Volumes(), k.cloudControllerManager.VolumeMounts(), k.cloudControllerManager.Env(),
)
if err := k.clusterUtil.SetupCloudControllerManager(k.client, cloudControllerManagerConfiguration, ccmConfigMaps, ccmSecrets); err != nil {
@ -333,12 +339,17 @@ func (k *KubeWrapper) setupCCM(ctx context.Context, subnetworkPodCIDR, cloudServ
return nil
}
func (k *KubeWrapper) setupCloudNodeManager() error {
func (k *KubeWrapper) setupCloudNodeManager(k8sVersion string) error {
if !k.cloudNodeManager.Supported() {
return nil
}
nodeManagerImage, err := k.cloudNodeManager.Image(k8sVersion)
if err != nil {
return fmt.Errorf("defining Image for Node Manager failed: %w", err)
}
cloudNodeManagerConfiguration := resources.NewDefaultCloudNodeManagerDeployment(
k.cloudNodeManager.Image(), k.cloudNodeManager.Path(), k.cloudNodeManager.ExtraArgs(),
nodeManagerImage, k.cloudNodeManager.Path(), k.cloudNodeManager.ExtraArgs(),
)
if err := k.clusterUtil.SetupCloudNodeManager(k.client, cloudNodeManagerConfiguration); err != nil {
return fmt.Errorf("failed to setup cloud-node-manager: %w", err)
@ -347,7 +358,7 @@ func (k *KubeWrapper) setupCloudNodeManager() error {
return nil
}
func (k *KubeWrapper) setupClusterAutoscaler(instance metadata.InstanceMetadata, cloudServiceAccountURI string, autoscalingNodeGroups []string) error {
func (k *KubeWrapper) setupClusterAutoscaler(instance metadata.InstanceMetadata, cloudServiceAccountURI string, autoscalingNodeGroups []string, k8sVersion string) error {
if !k.clusterAutoscaler.Supported() {
return nil
}
@ -356,7 +367,7 @@ func (k *KubeWrapper) setupClusterAutoscaler(instance metadata.InstanceMetadata,
return fmt.Errorf("defining Secrets for cluster-autoscaler failed: %w", err)
}
clusterAutoscalerConfiguration := resources.NewDefaultAutoscalerDeployment(k.clusterAutoscaler.Volumes(), k.clusterAutoscaler.VolumeMounts(), k.clusterAutoscaler.Env())
clusterAutoscalerConfiguration := resources.NewDefaultAutoscalerDeployment(k.clusterAutoscaler.Volumes(), k.clusterAutoscaler.VolumeMounts(), k.clusterAutoscaler.Env(), k8sVersion)
clusterAutoscalerConfiguration.SetAutoscalerCommand(k.clusterAutoscaler.Name(), autoscalingNodeGroups)
if err := k.clusterUtil.SetupAutoscaling(k.client, clusterAutoscalerConfiguration, caSecrets); err != nil {
return fmt.Errorf("failed to setup cluster-autoscaler: %w", err)

View file

@ -70,7 +70,7 @@ func TestInitCluster(t *testing.T) {
},
ClusterConfiguration: kubeadm.ClusterConfiguration{},
},
k8sVersion: "1.23.6",
k8sVersion: "1.23",
},
"kubeadm init works with metadata and loadbalancer": {
clusterUtil: stubClusterUtil{},
@ -110,7 +110,7 @@ func TestInitCluster(t *testing.T) {
},
},
wantErr: false,
k8sVersion: "1.23.6",
k8sVersion: "1.23",
},
"kubeadm init fails when retrieving metadata self": {
clusterUtil: stubClusterUtil{},
@ -125,7 +125,7 @@ func TestInitCluster(t *testing.T) {
CloudNodeManager: &stubCloudNodeManager{},
ClusterAutoscaler: &stubClusterAutoscaler{},
wantErr: true,
k8sVersion: "1.23.6",
k8sVersion: "1.23",
},
"kubeadm init fails when retrieving metadata subnetwork cidr": {
clusterUtil: stubClusterUtil{},
@ -140,7 +140,7 @@ func TestInitCluster(t *testing.T) {
CloudNodeManager: &stubCloudNodeManager{},
ClusterAutoscaler: &stubClusterAutoscaler{},
wantErr: true,
k8sVersion: "1.23.6",
k8sVersion: "1.23",
},
"kubeadm init fails when retrieving metadata loadbalancer ip": {
clusterUtil: stubClusterUtil{},
@ -156,7 +156,7 @@ func TestInitCluster(t *testing.T) {
CloudNodeManager: &stubCloudNodeManager{},
ClusterAutoscaler: &stubClusterAutoscaler{},
wantErr: true,
k8sVersion: "1.23.6",
k8sVersion: "1.23",
},
"kubeadm init fails when applying the init config": {
clusterUtil: stubClusterUtil{initClusterErr: someErr},
@ -168,7 +168,7 @@ func TestInitCluster(t *testing.T) {
CloudNodeManager: &stubCloudNodeManager{},
ClusterAutoscaler: &stubClusterAutoscaler{},
wantErr: true,
k8sVersion: "1.23.6",
k8sVersion: "1.23",
},
"kubeadm init fails when setting up the pod network": {
clusterUtil: stubClusterUtil{setupPodNetworkErr: someErr},
@ -180,7 +180,7 @@ func TestInitCluster(t *testing.T) {
CloudNodeManager: &stubCloudNodeManager{},
ClusterAutoscaler: &stubClusterAutoscaler{},
wantErr: true,
k8sVersion: "1.23.6",
k8sVersion: "1.23",
},
"kubeadm init fails when setting up the join service": {
clusterUtil: stubClusterUtil{setupJoinServiceError: someErr},
@ -192,7 +192,7 @@ func TestInitCluster(t *testing.T) {
CloudNodeManager: &stubCloudNodeManager{},
ClusterAutoscaler: &stubClusterAutoscaler{},
wantErr: true,
k8sVersion: "1.23.6",
k8sVersion: "1.23",
},
"kubeadm init fails when setting the cloud contoller manager": {
clusterUtil: stubClusterUtil{setupCloudControllerManagerError: someErr},
@ -204,7 +204,7 @@ func TestInitCluster(t *testing.T) {
CloudNodeManager: &stubCloudNodeManager{},
ClusterAutoscaler: &stubClusterAutoscaler{},
wantErr: true,
k8sVersion: "1.23.6",
k8sVersion: "1.23",
},
"kubeadm init fails when setting the cloud node manager": {
clusterUtil: stubClusterUtil{setupCloudNodeManagerError: someErr},
@ -216,7 +216,7 @@ func TestInitCluster(t *testing.T) {
CloudNodeManager: &stubCloudNodeManager{SupportedResp: true},
ClusterAutoscaler: &stubClusterAutoscaler{},
wantErr: true,
k8sVersion: "1.23.6",
k8sVersion: "1.23",
},
"kubeadm init fails when setting the cluster autoscaler": {
clusterUtil: stubClusterUtil{setupAutoscalingError: someErr},
@ -228,7 +228,7 @@ func TestInitCluster(t *testing.T) {
CloudNodeManager: &stubCloudNodeManager{},
ClusterAutoscaler: &stubClusterAutoscaler{SupportedResp: true},
wantErr: true,
k8sVersion: "1.23.6",
k8sVersion: "1.23",
},
"kubeadm init fails when reading kubeconfig": {
clusterUtil: stubClusterUtil{},
@ -240,7 +240,7 @@ func TestInitCluster(t *testing.T) {
CloudNodeManager: &stubCloudNodeManager{},
ClusterAutoscaler: &stubClusterAutoscaler{},
wantErr: true,
k8sVersion: "1.23.6",
k8sVersion: "1.23",
},
"kubeadm init fails when setting up the kms": {
clusterUtil: stubClusterUtil{setupKMSError: someErr},
@ -252,7 +252,7 @@ func TestInitCluster(t *testing.T) {
CloudNodeManager: &stubCloudNodeManager{SupportedResp: false},
ClusterAutoscaler: &stubClusterAutoscaler{},
wantErr: true,
k8sVersion: "1.23.6",
k8sVersion: "1.23",
},
"kubeadm init fails when setting up verification service": {
clusterUtil: stubClusterUtil{setupVerificationServiceErr: someErr},
@ -264,7 +264,7 @@ func TestInitCluster(t *testing.T) {
CloudNodeManager: &stubCloudNodeManager{SupportedResp: false},
ClusterAutoscaler: &stubClusterAutoscaler{},
wantErr: true,
k8sVersion: "1.23.6",
k8sVersion: "1.23",
},
"unsupported k8sVersion fails cluster creation": {
clusterUtil: stubClusterUtil{},