github.com/openshift/installer@v1.4.17/pkg/asset/machines/aws/awsmachines_test.go (about)

     1  // Package aws generates Machine objects for aws.
     2  package aws
     3  
     4  import (
     5  	"fmt"
     6  	"strings"
     7  	"testing"
     8  
     9  	"github.com/google/go-cmp/cmp"
    10  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    11  	"k8s.io/utils/ptr"
    12  	capa "sigs.k8s.io/cluster-api-provider-aws/v2/api/v1beta2"
    13  
    14  	"github.com/openshift/installer/pkg/asset"
    15  	"github.com/openshift/installer/pkg/types"
    16  	awstypes "github.com/openshift/installer/pkg/types/aws"
    17  )
    18  
    19  var stubMachineInputManagedVpc = &MachineInput{
    20  	Role: "master",
    21  	Pool: &types.MachinePool{
    22  		Name:     "master",
    23  		Replicas: ptr.To(int64(3)),
    24  		Platform: types.MachinePoolPlatform{
    25  			AWS: &awstypes.MachinePool{
    26  				Zones: []string{"A", "B", "C"},
    27  			},
    28  		},
    29  	},
    30  	Subnets:  make(map[string]string, 0),
    31  	Tags:     capa.Tags{},
    32  	PublicIP: false,
    33  	Ignition: &capa.Ignition{
    34  		StorageType: capa.IgnitionStorageTypeOptionUnencryptedUserData,
    35  	},
    36  }
    37  
    38  func stubDeepCopyMachineInput(in *MachineInput) *MachineInput {
    39  	out := &MachineInput{
    40  		Role:     in.Role,
    41  		PublicIP: in.PublicIP,
    42  	}
    43  	if in.Pool != nil {
    44  		out.Pool = &types.MachinePool{}
    45  		*out.Pool = *in.Pool
    46  	}
    47  	if len(in.Subnets) > 0 {
    48  		out.Subnets = make(map[string]string, len(in.Subnets))
    49  		for k, v := range in.Subnets {
    50  			out.Subnets[k] = v
    51  		}
    52  	}
    53  	if len(in.Tags) > 0 {
    54  		out.Tags = in.Tags.DeepCopy()
    55  	}
    56  	if in.Ignition != nil {
    57  		out.Ignition = in.Ignition.DeepCopy()
    58  	}
    59  	return out
    60  }
    61  
    62  func stubGetMachineManagedVpc() *MachineInput {
    63  	return stubDeepCopyMachineInput(stubMachineInputManagedVpc)
    64  }
    65  
    66  func TestGenerateMachines(t *testing.T) {
    67  	stubClusterID := "vpc-zr2-m2"
    68  	tests := []struct {
    69  		name           string
    70  		clusterID      string
    71  		input          *MachineInput
    72  		want           []*asset.RuntimeFile
    73  		wantInfraFiles []*asset.RuntimeFile
    74  		wantErr        string
    75  	}{
    76  		{
    77  			name:      "topology ha, managed vpc, default zones region, 2 zones A and B, 3 machines should be in A, B and A private subnet",
    78  			clusterID: stubClusterID,
    79  			input: func() *MachineInput {
    80  				in := stubGetMachineManagedVpc()
    81  				in.Pool.Platform.AWS.Zones = []string{"A", "B"}
    82  				return in
    83  			}(),
    84  			// generate 3 AWSMachine manifests for control plane nodes in two zones
    85  			wantInfraFiles: func() []*asset.RuntimeFile {
    86  				machineZoneMap := map[int]string{0: "A", 1: "B", 2: "A"}
    87  				infraMachineFiles := []*asset.RuntimeFile{}
    88  				for mid := 0; mid < 3; mid++ {
    89  					machineName := fmt.Sprintf("%s-%s-%d", stubClusterID, "master", mid)
    90  					machineZone := machineZoneMap[mid]
    91  					machine := &capa.AWSMachine{
    92  						TypeMeta: metav1.TypeMeta{
    93  							APIVersion: "infrastructure.cluster.x-k8s.io/v1beta2",
    94  							Kind:       "AWSMachine",
    95  						},
    96  						ObjectMeta: metav1.ObjectMeta{
    97  							Name:   machineName,
    98  							Labels: map[string]string{"cluster.x-k8s.io/control-plane": ""},
    99  						},
   100  						Spec: capa.AWSMachineSpec{
   101  							InstanceMetadataOptions: &capa.InstanceMetadataOptions{
   102  								HTTPEndpoint: capa.InstanceMetadataEndpointStateEnabled,
   103  								HTTPTokens:   capa.HTTPTokensStateOptional,
   104  							},
   105  							AMI: capa.AMIReference{
   106  								ID: ptr.To(""),
   107  							},
   108  							IAMInstanceProfile: fmt.Sprintf("%s-%s-profile", stubClusterID, "master"),
   109  							PublicIP:           ptr.To(false),
   110  							Subnet: &capa.AWSResourceReference{
   111  								Filters: []capa.Filter{{Name: "tag:Name", Values: []string{
   112  									fmt.Sprintf("%s-subnet-private-%s", stubClusterID, machineZone),
   113  								}}},
   114  							},
   115  							SSHKeyName: ptr.To(""),
   116  							RootVolume: &capa.Volume{
   117  								Encrypted: ptr.To(true),
   118  							},
   119  							UncompressedUserData: ptr.To(true),
   120  							Ignition: &capa.Ignition{
   121  								StorageType: capa.IgnitionStorageTypeOptionUnencryptedUserData,
   122  							},
   123  						},
   124  					}
   125  					infraMachineFiles = append(infraMachineFiles, &asset.RuntimeFile{
   126  						File:   asset.File{Filename: fmt.Sprintf("10_inframachine_%s.yaml", machineName)},
   127  						Object: machine,
   128  					})
   129  				}
   130  				return infraMachineFiles
   131  			}(),
   132  		},
   133  		{
   134  			name:      "topology ha, byo vpc, two zones subnets A and B, 3 machines should be in A, B and A private subnets",
   135  			clusterID: stubClusterID,
   136  			input: func() *MachineInput {
   137  				in := stubGetMachineManagedVpc()
   138  				in.Pool.Platform.AWS.Zones = []string{"A", "B"}
   139  				in.Subnets = map[string]string{"A": "subnet-id-A", "B": "subnet-id-B"}
   140  				return in
   141  			}(),
   142  			// generate 3 AWSMachine manifests for control plane nodes in two subnets/zones
   143  			wantInfraFiles: func() []*asset.RuntimeFile {
   144  				machineZoneMap := map[int]string{0: "subnet-id-A", 1: "subnet-id-B", 2: "subnet-id-A"}
   145  				infraMachineFiles := []*asset.RuntimeFile{}
   146  				for mid := 0; mid < 3; mid++ {
   147  					machineName := fmt.Sprintf("%s-%s-%d", stubClusterID, "master", mid)
   148  					machineSubnet := machineZoneMap[mid]
   149  					machine := &capa.AWSMachine{
   150  						TypeMeta: metav1.TypeMeta{
   151  							APIVersion: "infrastructure.cluster.x-k8s.io/v1beta2",
   152  							Kind:       "AWSMachine",
   153  						},
   154  						ObjectMeta: metav1.ObjectMeta{
   155  							Name:   machineName,
   156  							Labels: map[string]string{"cluster.x-k8s.io/control-plane": ""},
   157  						},
   158  						Spec: capa.AWSMachineSpec{
   159  							InstanceMetadataOptions: &capa.InstanceMetadataOptions{
   160  								HTTPEndpoint: capa.InstanceMetadataEndpointStateEnabled,
   161  								HTTPTokens:   capa.HTTPTokensStateOptional,
   162  							},
   163  							AMI: capa.AMIReference{
   164  								ID: ptr.To(""),
   165  							},
   166  							IAMInstanceProfile: fmt.Sprintf("%s-%s-profile", stubClusterID, "master"),
   167  							PublicIP:           ptr.To(false),
   168  							Subnet: &capa.AWSResourceReference{
   169  								ID: ptr.To(machineSubnet),
   170  							},
   171  							SSHKeyName: ptr.To(""),
   172  							RootVolume: &capa.Volume{
   173  								Encrypted: ptr.To(true),
   174  							},
   175  							UncompressedUserData: ptr.To(true),
   176  							Ignition: &capa.Ignition{
   177  								StorageType: capa.IgnitionStorageTypeOptionUnencryptedUserData,
   178  							},
   179  						},
   180  					}
   181  					infraMachineFiles = append(infraMachineFiles, &asset.RuntimeFile{
   182  						File:   asset.File{Filename: fmt.Sprintf("10_inframachine_%s.yaml", machineName)},
   183  						Object: machine,
   184  					})
   185  				}
   186  				return infraMachineFiles
   187  			}(),
   188  		},
   189  		// Error's scenarios
   190  		{
   191  			name:      "error topology ha, byo vpc, no subnet for zones",
   192  			clusterID: stubClusterID,
   193  			input: func() *MachineInput {
   194  				in := stubGetMachineManagedVpc()
   195  				in.Pool.Platform.AWS.Zones = []string{"A", "B"}
   196  				in.Subnets = map[string]string{"C": "subnet-id-C", "D": "subnet-id-D"}
   197  				return in
   198  			}(),
   199  			wantErr: `no subnet for zone A`,
   200  		},
   201  		{
   202  			name:      "error topology ha, managed vpc, empty subnet zone",
   203  			clusterID: stubClusterID,
   204  			input: func() *MachineInput {
   205  				in := stubGetMachineManagedVpc()
   206  				in.Pool.Platform.AWS.Zones = []string{"A", "B"}
   207  				in.Subnets = map[string]string{"A": "subnet-id-A", "B": ""}
   208  				return in
   209  			}(),
   210  			wantErr: `invalid subnet ID for zone B`,
   211  		},
   212  		// TODO: add more use cases.
   213  		// {
   214  		// 	name: "managed vpc, default zones region, 5 zones A to E, 3 machines should be in A, B and C private subnet",
   215  		// },
   216  		// {
   217  		// 	name: "managed vpc, default zones region, 5 zones A to E, 3 machines should be in A, B and C private subnet",
   218  		// },
   219  		// {
   220  		// 	name: "byo vpc, 2 zones subnets A and B, 3 machines' subnet should be in A, B and A",
   221  		// },
   222  		// {
   223  		// 	name: "managed vpc, default zones region, 5 zones A to E, bootstrap should be in zone A public subnet",
   224  		// },
   225  		// {
   226  		// 	name: "topology ha, managed vpc, two zones subnets A and B, bootstrap node using public subnet A",
   227  		// },
   228  	}
   229  	for _, tt := range tests {
   230  		t.Run(tt.name, func(t *testing.T) {
   231  			files, err := GenerateMachines(tt.clusterID, tt.input)
   232  			if err != nil {
   233  				if len(tt.wantErr) > 0 {
   234  					if got := err.Error(); !cmp.Equal(got, tt.wantErr) {
   235  						t.Errorf("GenerateMachines() unexpected error message: %v", cmp.Diff(got, tt.wantErr))
   236  					}
   237  					return
   238  				}
   239  				t.Errorf("GenerateMachines() unexpected error: %v", err)
   240  				return
   241  			}
   242  			// TODO: support the CAPA v1beta1.Machine manifest check.
   243  			// Support only comparing manifest file for CAPA v1beta2.AWSMachine.
   244  			if len(tt.wantInfraFiles) > 0 {
   245  				got := []*asset.RuntimeFile{}
   246  				for _, file := range files {
   247  					if !strings.HasPrefix(file.Filename, "10_inframachine") {
   248  						continue
   249  					}
   250  					got = append(got, file)
   251  				}
   252  				if !cmp.Equal(got, tt.wantInfraFiles) {
   253  					t.Errorf("GenerateMachines() Got unexpected results:\n%v", cmp.Diff(got, tt.wantInfraFiles))
   254  				}
   255  			}
   256  		})
   257  	}
   258  }