diff --git a/pkg/cloud/aws/aws.go b/pkg/cloud/aws/aws.go index f9b04bfa9..023180dcd 100644 --- a/pkg/cloud/aws/aws.go +++ b/pkg/cloud/aws/aws.go @@ -5,25 +5,35 @@ import ( "github.com/openshift/cluster-cloud-controller-manager-operator/pkg/cloud/common" appsv1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" utilruntime "k8s.io/apimachinery/pkg/util/runtime" "sigs.k8s.io/controller-runtime/pkg/client" ) var ( //go:embed assets/* - awsFS embed.FS - awsSources = []common.ObjectSource{ + awsFS embed.FS + //go:embed bootstrap/* + awsBootstrapFS embed.FS + + awsResources, awsBootstrapResoureces []client.Object + awsSources = []common.ObjectSource{ {Object: &appsv1.Deployment{}, Path: "assets/deployment.yaml"}, } - awsResources []client.Object + awsBootstrapSources = []common.ObjectSource{ + {Object: &corev1.Pod{}, Path: "bootstrap/pod.yaml"}, + } ) func init() { var err error awsResources, err = common.ReadResources(awsFS, awsSources) utilruntime.Must(err) + awsBootstrapResoureces, err = common.ReadResources(awsBootstrapFS, awsBootstrapSources) + utilruntime.Must(err) } +// GetResources returns a list of AWS resources for provisioning CCM in running cluster func GetResources() []client.Object { resources := make([]client.Object, len(awsResources)) for i := range awsResources { @@ -32,3 +42,13 @@ func GetResources() []client.Object { return resources } + +// GetBootstrapResources returns a list static pods for provisioning CCM on bootstrap node for AWS +func GetBootstrapResources() []client.Object { + resources := make([]client.Object, len(awsBootstrapResoureces)) + for i := range awsBootstrapResoureces { + resources[i] = awsBootstrapResoureces[i].DeepCopyObject().(client.Object) + } + + return resources +} diff --git a/pkg/cloud/aws/aws_test.go b/pkg/cloud/aws/aws_test.go index c41055307..bb1f4f8c5 100644 --- a/pkg/cloud/aws/aws_test.go +++ b/pkg/cloud/aws/aws_test.go @@ -19,3 +19,17 @@ func TestGetResources(t *testing.T) { assert.Contains(t, names, "aws-cloud-controller-manager") assert.Contains(t, kinds, "Deployment") } + +func TestGetBootstrapResources(t *testing.T) { + resources := GetBootstrapResources() + assert.Len(t, resources, 1) + + var names, kinds []string + for _, r := range resources { + names = append(names, r.GetName()) + kinds = append(kinds, r.GetObjectKind().GroupVersionKind().Kind) + } + + assert.Contains(t, names, "aws-cloud-controller-manager") + assert.Contains(t, kinds, "Pod") +} diff --git a/pkg/cloud/aws/bootstrap/pod.yaml b/pkg/cloud/aws/bootstrap/pod.yaml new file mode 100644 index 000000000..a4d459bb7 --- /dev/null +++ b/pkg/cloud/aws/bootstrap/pod.yaml @@ -0,0 +1,27 @@ +apiVersion: v1 +kind: Pod +metadata: + name: aws-cloud-controller-manager + namespace: kube-system +spec: + priorityClassName: system-cluster-critical + containers: + - args: + - --cloud-provider=aws + - --use-service-account-credentials=false + - --controllers=cloud-node # run only cloud-node controller required to bootstrap master nodes + - --kubeconfig=/etc/kubernetes/secrets/kubeconfig + - --leader-elect=false + - -v=2 + image: gcr.io/k8s-staging-provider-aws/cloud-controller-manager:v1.19.0-alpha.1 + imagePullPolicy: IfNotPresent + name: cloud-controller-manager + volumeMounts: + - mountPath: /etc/kubernetes/secrets + name: secrets + readOnly: true + hostNetwork: true + volumes: + - hostPath: + path: /etc/kubernetes/bootstrap-secrets + name: secrets diff --git a/pkg/cloud/cloud.go b/pkg/cloud/cloud.go index 76a92a42e..cd3f499b2 100644 --- a/pkg/cloud/cloud.go +++ b/pkg/cloud/cloud.go @@ -8,6 +8,13 @@ import ( "sigs.k8s.io/controller-runtime/pkg/client" ) +// GetResources selectively returns a list resources required for +// provisioning CCM instance in the cluster for the given platform type +// +// These resources will be actively maintained by the operator, preventing +// changes in their spec. However you can extend any resource spec with +// values not specified in the provided source resource. These changes +// would be preserved. func GetResources(platform configv1.PlatformType) []client.Object { switch platform { case configv1.AWSPlatformType: @@ -19,3 +26,19 @@ func GetResources(platform configv1.PlatformType) []client.Object { return nil } } + +// GetBootstrapResources selectively returns a list static pods required for +// provisioning CCM on bootstrap node for the given platform type +// +// This pod is required for platforms that allow multiple Node initialization from +// a single CCM instance which is not bound to link-local VM IP address and node name. +// Allows to initalize master Nodes immediately after they are created by the installer. +func GetBootstrapResources(platform configv1.PlatformType) []client.Object { + switch platform { + case configv1.AWSPlatformType: + return aws.GetBootstrapResources() + default: + klog.Warning("No recognized cloud provider platform found in infrastructure") + return nil + } +} diff --git a/pkg/cloud/cloud_test.go b/pkg/cloud/cloud_test.go index 31a95ed35..82d3b28be 100644 --- a/pkg/cloud/cloud_test.go +++ b/pkg/cloud/cloud_test.go @@ -71,3 +71,66 @@ func TestGetResources(t *testing.T) { }) } } + +func TestGetBootstrapResources(t *testing.T) { + tc := []struct { + name string + platform configv1.PlatformType + expected []client.Object + }{{ + name: "AWS resources returned as expected", + platform: configv1.AWSPlatformType, + expected: aws.GetBootstrapResources(), + }, { + name: "OpenStack resources are empty, as the platform is not yet supported", + platform: configv1.OpenStackPlatformType, + }, { + name: "GCP resources are empty, as the platform is not yet supported", + platform: configv1.GCPPlatformType, + }, { + name: "Azure resources are empty, as the platform is not yet supported", + platform: configv1.AzurePlatformType, + }, { + name: "VSphere resources are empty, as the platform is not yet supported", + platform: configv1.VSpherePlatformType, + }, { + name: "OVirt resources are empty, as the platform is not yet supported", + platform: configv1.OvirtPlatformType, + }, { + name: "IBMCloud resources are empty, as the platform is not yet supported", + platform: configv1.IBMCloudPlatformType, + }, { + name: "Libvirt resources are empty", + platform: configv1.LibvirtPlatformType, + }, { + name: "Kubevirt resources are empty", + platform: configv1.KubevirtPlatformType, + }, { + name: "BareMetal resources are empty", + platform: configv1.BareMetalPlatformType, + }, { + name: "None platform resources are empty", + platform: configv1.NonePlatformType, + }} + + for _, tc := range tc { + t.Run(tc.name, func(t *testing.T) { + resources := GetBootstrapResources(tc.platform) + + assert.Equal(t, len(tc.expected), len(resources)) + assert.EqualValues(t, tc.expected, resources) + + if len(resources) > 0 { + // Edit and repeat procedure to ensure modification in place is not present + for _, resource := range resources { + resource.SetName("different") + } + newResources := GetBootstrapResources(tc.platform) + + assert.Equal(t, len(tc.expected), len(newResources)) + assert.EqualValues(t, tc.expected, newResources) + assert.NotEqualValues(t, resources, newResources) + } + }) + } +} diff --git a/pkg/cloud/common/sources.go b/pkg/cloud/common/sources.go index 63582a70c..5c37457f4 100644 --- a/pkg/cloud/common/sources.go +++ b/pkg/cloud/common/sources.go @@ -8,11 +8,17 @@ import ( "sigs.k8s.io/yaml" ) +// ObjectSource is a holder for the path of the object to read from +// and typed Object, where the resource content would be unserialized type ObjectSource struct { Object client.Object Path string } +// ReadResources is responsible for reading resources from embed path in the binary. +// This method is for internal use only, and a should use this method +// only in init() and later provide a copy of aquired objects. See /pkg/cloud for +// examples func ReadResources(f embed.FS, sources []ObjectSource) ([]client.Object, error) { ret := []client.Object{} for _, source := range sources { diff --git a/pkg/cloud/openstack/openstack.go b/pkg/cloud/openstack/openstack.go index 21fb51517..0a72a52b3 100644 --- a/pkg/cloud/openstack/openstack.go +++ b/pkg/cloud/openstack/openstack.go @@ -26,6 +26,7 @@ func init() { utilruntime.Must(err) } +// GetResources returns a list of OpenStack resources for provisioning CCM in running cluster func GetResources() []client.Object { resources := make([]client.Object, len(openStackResources)) for i := range openStackResources {