diff --git a/pkg/api/v1alpha1/cluster.go b/pkg/api/v1alpha1/cluster.go index 9fd9869a38e8..147c3c538f4a 100644 --- a/pkg/api/v1alpha1/cluster.go +++ b/pkg/api/v1alpha1/cluster.go @@ -311,6 +311,29 @@ func (c *Cluster) ClearManagedByCLIAnnotation() { } } +// AddTinkerbellIPAnnotation adds the managed-by-cli annotation to the cluster. +func (c *Cluster) AddTinkerbellIPAnnotation(tinkerbellIP string) { + if c.Annotations == nil { + c.Annotations = map[string]string{} + } + c.Annotations[tinkerbellIPAnnotation] = tinkerbellIP +} + +// ClearTinkerbellIPAnnotation removes the managed-by-cli annotation from the cluster. +func (c *Cluster) ClearTinkerbellIPAnnotation() { + if c.Annotations != nil { + delete(c.Annotations, tinkerbellIPAnnotation) + } +} + +// HasTinkerbellIPAnnotation returns the tinkerbell IP value if the annotation exists. +func (c *Cluster) HasTinkerbellIPAnnotation() (string, bool) { + if tinkerbellIP, ok := c.Annotations[tinkerbellIPAnnotation]; ok { + return tinkerbellIP, true + } + return "", false +} + // RegistryAuth returns whether registry requires authentication or not. func (c *Cluster) RegistryAuth() bool { if c.Spec.RegistryMirrorConfiguration == nil { diff --git a/pkg/api/v1alpha1/cluster_test.go b/pkg/api/v1alpha1/cluster_test.go index 61bb4cdcae7c..c845e57aa5b3 100644 --- a/pkg/api/v1alpha1/cluster_test.go +++ b/pkg/api/v1alpha1/cluster_test.go @@ -1616,6 +1616,24 @@ func TestCluster_AddRemoveManagedByCLIAnnotation(t *testing.T) { g.Expect(ok).To(BeFalse()) } +func TestClusterClearTinkerbellIPAnnotation(t *testing.T) { + g := NewWithT(t) + c := &Cluster{ + ObjectMeta: metav1.ObjectMeta{ + Name: "cluster_test", + }, + } + c.AddTinkerbellIPAnnotation("1.1.1.1") + val, ok := c.HasTinkerbellIPAnnotation() + + g.Expect(ok).To(BeTrue()) + g.Expect(val).To(ContainSubstring("1.1.1.1")) + + c.ClearTinkerbellIPAnnotation() + _, ok = c.Annotations[tinkerbellIPAnnotation] + g.Expect(ok).To(BeFalse()) +} + func TestGitOpsEquals(t *testing.T) { tests := []struct { name string diff --git a/pkg/api/v1alpha1/cluster_types.go b/pkg/api/v1alpha1/cluster_types.go index 4412e2eb633c..640dd1d394bb 100644 --- a/pkg/api/v1alpha1/cluster_types.go +++ b/pkg/api/v1alpha1/cluster_types.go @@ -25,6 +25,10 @@ const ( // the controller will remove the finalizer and let the cluster be deleted. ManagedByCLIAnnotation = "anywhere.eks.amazonaws.com/managed-by-cli" + // tinkerbellIPAnnotation can be applied to an EKS-A Cluster to convey the tinkerbell bootstrap ip to the + // EKSA controller. When marked for deletion, the controller will remove the IP annotation. + tinkerbellIPAnnotation = "anywhere.eks.amazonaws.com/tinkerbell-bootstrap-ip" + // ControlPlaneAnnotation is an annotation that can be applied to EKS-A machineconfig // object to prevent a controller from making changes to that resource. controlPlaneAnnotation = "anywhere.eks.amazonaws.com/control-plane" diff --git a/pkg/providers/tinkerbell/create.go b/pkg/providers/tinkerbell/create.go index 8db90a1bd2e9..24c5187a9a79 100644 --- a/pkg/providers/tinkerbell/create.go +++ b/pkg/providers/tinkerbell/create.go @@ -28,6 +28,8 @@ func (p *Provider) BootstrapClusterOpts(_ *cluster.Spec) ([]bootstrapper.Bootstr func (p *Provider) PreCAPIInstallOnBootstrap(ctx context.Context, cluster *types.Cluster, clusterSpec *cluster.Spec) error { logger.V(4).Info("Installing Tinkerbell stack on bootstrap cluster") + logger.V(4).Info("Adding annotation for tinkerbell ip on bootstrap cluster") + clusterSpec.Cluster.AddTinkerbellIPAnnotation(p.tinkerbellIP) versionsBundle := clusterSpec.RootVersionsBundle() err := p.stackInstaller.Install( diff --git a/pkg/providers/tinkerbell/template.go b/pkg/providers/tinkerbell/template.go index aca7710ea941..e95053e1f6a9 100644 --- a/pkg/providers/tinkerbell/template.go +++ b/pkg/providers/tinkerbell/template.go @@ -71,6 +71,10 @@ func (tb *TemplateBuilder) GenerateCAPISpecControlPlane(clusterSpec *cluster.Spe } var OSImageURL string + if tinkerbellIP, ok := clusterSpec.Cluster.HasTinkerbellIPAnnotation(); ok { + tb.tinkerbellIP = tinkerbellIP + } + if cpTemplateConfig == nil { OSImageURL = clusterSpec.TinkerbellDatacenter.Spec.OSImageURL if tb.controlPlaneMachineSpec.OSImageURL != "" { @@ -123,6 +127,10 @@ func (tb *TemplateBuilder) GenerateCAPISpecWorkers(clusterSpec *cluster.Spec, wo bundle := clusterSpec.RootVersionsBundle() OSImageURL := clusterSpec.TinkerbellDatacenter.Spec.OSImageURL + if tinkerbellIP, ok := clusterSpec.Cluster.HasTinkerbellIPAnnotation(); ok { + tb.tinkerbellIP = tinkerbellIP + } + for _, workerNodeGroupConfiguration := range clusterSpec.Cluster.Spec.WorkerNodeGroupConfigurations { workerNodeMachineSpec := tb.WorkerNodeGroupMachineSpecs[workerNodeGroupConfiguration.MachineGroupRef.Name] wTemplateConfig := clusterSpec.TinkerbellTemplateConfigs[workerNodeMachineSpec.TemplateRef.Name] diff --git a/pkg/providers/tinkerbell/template_test.go b/pkg/providers/tinkerbell/template_test.go index 93864d24e93d..58dc084b40fa 100644 --- a/pkg/providers/tinkerbell/template_test.go +++ b/pkg/providers/tinkerbell/template_test.go @@ -164,3 +164,34 @@ func TestTemplateBuilder_CertSANs(t *testing.T) { } } + +func TestTemplateBuilder(t *testing.T) { + for _, tc := range []struct { + Input string + Output string + }{ + { + Input: "testdata/cluster_tinkerbell_api_server_cert_san_ip.yaml", + Output: "testdata/expected_cluster_tinkerbell_api_server_cert_san_ip.yaml", + }, + } { + g := NewWithT(t) + clusterSpec := test.NewFullClusterSpec(t, tc.Input) + clusterSpec.Cluster.AddTinkerbellIPAnnotation("1.1.1.1") + + cpMachineCfg, err := getControlPlaneMachineSpec(clusterSpec) + g.Expect(err).ToNot(HaveOccurred()) + + wngMachineCfgs, err := getWorkerNodeGroupMachineSpec(clusterSpec) + g.Expect(err).ToNot(HaveOccurred()) + + tinkIPBefore := "0.0.0.0" + bldr := NewTemplateBuilder(&clusterSpec.TinkerbellDatacenter.Spec, cpMachineCfg, nil, wngMachineCfgs, tinkIPBefore, time.Now) + + data, err := bldr.GenerateCAPISpecControlPlane(clusterSpec) + g.Expect(err).ToNot(HaveOccurred()) + + test.AssertContentToFile(t, string(data), tc.Output) + + } +} diff --git a/pkg/workflows/management/create_install_eksa.go b/pkg/workflows/management/create_install_eksa.go index 1c0b2e36ae48..10676f472731 100644 --- a/pkg/workflows/management/create_install_eksa.go +++ b/pkg/workflows/management/create_install_eksa.go @@ -3,6 +3,7 @@ package management import ( "context" + "github.com/aws/eks-anywhere/pkg/api/v1alpha1" "github.com/aws/eks-anywhere/pkg/cluster" "github.com/aws/eks-anywhere/pkg/logger" "github.com/aws/eks-anywhere/pkg/task" @@ -38,6 +39,11 @@ func (s *installEksaComponentsOnBootstrapTask) Checkpoint() *task.CompletedTask type installEksaComponentsOnWorkloadTask struct{} func (s *installEksaComponentsOnWorkloadTask) Run(ctx context.Context, commandContext *task.CommandContext) task.Task { + if commandContext.ClusterSpec.Cluster.Spec.DatacenterRef.Kind == v1alpha1.TinkerbellDatacenterKind { + logger.Info("Removing Tinkerbell IP annotation") + commandContext.ClusterSpec.Cluster.ClearTinkerbellIPAnnotation() + } + logger.Info("Installing EKS-A custom components on workload cluster") err := installEKSAComponents(ctx, commandContext, commandContext.WorkloadCluster)