sigs.k8s.io/cluster-api-provider-aws@v1.5.5/pkg/cloud/scope/machine_test.go (about)

     1  /*
     2  Copyright 2020 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8  	http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package scope
    18  
    19  import (
    20  	"encoding/base64"
    21  	"testing"
    22  
    23  	corev1 "k8s.io/api/core/v1"
    24  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    25  	"k8s.io/apimachinery/pkg/runtime"
    26  	"k8s.io/utils/pointer"
    27  	"sigs.k8s.io/controller-runtime/pkg/client"
    28  	"sigs.k8s.io/controller-runtime/pkg/client/fake"
    29  
    30  	infrav1 "sigs.k8s.io/cluster-api-provider-aws/api/v1beta1"
    31  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    32  )
    33  
    34  func setupScheme() (*runtime.Scheme, error) {
    35  	scheme := runtime.NewScheme()
    36  	if err := infrav1.AddToScheme(scheme); err != nil {
    37  		return nil, err
    38  	}
    39  	if err := clusterv1.AddToScheme(scheme); err != nil {
    40  		return nil, err
    41  	}
    42  	if err := corev1.AddToScheme(scheme); err != nil {
    43  		return nil, err
    44  	}
    45  	return scheme, nil
    46  }
    47  
    48  func newMachine(clusterName, machineName string) *clusterv1.Machine {
    49  	return &clusterv1.Machine{
    50  		ObjectMeta: metav1.ObjectMeta{
    51  			Labels: map[string]string{
    52  				clusterv1.ClusterLabelName: clusterName,
    53  			},
    54  			Name:      machineName,
    55  			Namespace: "default",
    56  		},
    57  		Spec: clusterv1.MachineSpec{
    58  			Bootstrap: clusterv1.Bootstrap{
    59  				DataSecretName: pointer.StringPtr(machineName),
    60  			},
    61  		},
    62  	}
    63  }
    64  
    65  func newCluster(name string) *clusterv1.Cluster {
    66  	return &clusterv1.Cluster{
    67  		ObjectMeta: metav1.ObjectMeta{
    68  			Name:      name,
    69  			Namespace: "default",
    70  		},
    71  	}
    72  }
    73  
    74  func newAWSCluster(name string) *infrav1.AWSCluster {
    75  	return &infrav1.AWSCluster{
    76  		ObjectMeta: metav1.ObjectMeta{
    77  			Name:      name,
    78  			Namespace: "default",
    79  		},
    80  	}
    81  }
    82  
    83  func newAWSMachine(clusterName, machineName string) *infrav1.AWSMachine {
    84  	return &infrav1.AWSMachine{
    85  		ObjectMeta: metav1.ObjectMeta{
    86  			Labels: map[string]string{
    87  				clusterv1.ClusterLabelName: clusterName,
    88  			},
    89  			Name:      machineName,
    90  			Namespace: "default",
    91  		},
    92  	}
    93  }
    94  
    95  func newBootstrapSecret(clusterName, machineName string) *corev1.Secret {
    96  	return &corev1.Secret{
    97  		ObjectMeta: metav1.ObjectMeta{
    98  			Labels: map[string]string{
    99  				clusterv1.ClusterLabelName: clusterName,
   100  			},
   101  			Name:      machineName,
   102  			Namespace: "default",
   103  		},
   104  		Data: map[string][]byte{
   105  			"value": []byte("user data"),
   106  		},
   107  	}
   108  }
   109  
   110  func setupMachineScope() (*MachineScope, error) {
   111  	scheme, err := setupScheme()
   112  	if err != nil {
   113  		return nil, err
   114  	}
   115  	clusterName := "my-cluster"
   116  	cluster := newCluster(clusterName)
   117  	machine := newMachine(clusterName, "my-machine-0")
   118  	secret := newBootstrapSecret(clusterName, "my-machine-0")
   119  	awsMachine := newAWSMachine(clusterName, "my-machine-0")
   120  	awsCluster := newAWSCluster(clusterName)
   121  
   122  	initObjects := []client.Object{
   123  		cluster, machine, secret, awsMachine, awsCluster,
   124  	}
   125  
   126  	client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjects...).Build()
   127  	return NewMachineScope(
   128  		MachineScopeParams{
   129  			Client:  client,
   130  			Machine: machine,
   131  			Cluster: cluster,
   132  			InfraCluster: &ClusterScope{
   133  				AWSCluster: awsCluster,
   134  			},
   135  			AWSMachine: awsMachine,
   136  		},
   137  	)
   138  }
   139  
   140  func TestGetBootstrapDataIsBase64Encoded(t *testing.T) {
   141  	scope, err := setupMachineScope()
   142  	if err != nil {
   143  		t.Fatal(err)
   144  	}
   145  
   146  	userdata, err := scope.GetBootstrapData()
   147  	if err != nil {
   148  		t.Fatal(err)
   149  	}
   150  	_, err = base64.StdEncoding.DecodeString(userdata)
   151  	if err != nil {
   152  		t.Fatalf("GetBootstrapData isn't base 64 encoded: %+v", err)
   153  	}
   154  }
   155  
   156  func TestGetRawBootstrapDataIsNotBase64Encoded(t *testing.T) {
   157  	scope, err := setupMachineScope()
   158  	if err != nil {
   159  		t.Fatal(err)
   160  	}
   161  
   162  	userdata, err := scope.GetRawBootstrapData()
   163  	if err != nil {
   164  		t.Fatal(err)
   165  	}
   166  	_, err = base64.StdEncoding.DecodeString(string(userdata))
   167  	if err == nil {
   168  		t.Fatalf("GetBootstrapData is base 64 encoded: %+v", err)
   169  	}
   170  }
   171  
   172  func Test_GetRawBootstrapDataWithFormat(t *testing.T) {
   173  	t.Run("returns_empty_format_when_format_is_not_set_in_bootstrap_data", func(t *testing.T) {
   174  		scope, err := setupMachineScope()
   175  		if err != nil {
   176  			t.Fatal(err)
   177  		}
   178  
   179  		_, format, err := scope.GetRawBootstrapDataWithFormat()
   180  		if err != nil {
   181  			t.Fatalf("Getting raw bootstrap data with format: %v", err)
   182  		}
   183  
   184  		if format != "" {
   185  			t.Fatalf("Fromat should be empty when it's not defined in bootstrap data, got: %q", format)
   186  		}
   187  	})
   188  
   189  	t.Run("returns_format_defined_in_bootstrap_data_when_available", func(t *testing.T) {
   190  		scheme, err := setupScheme()
   191  		if err != nil {
   192  			t.Fatalf("Configuring schema: %v", err)
   193  		}
   194  
   195  		clusterName := "my-cluster"
   196  		machineName := "my-machine-0"
   197  		cluster := newCluster(clusterName)
   198  		machine := newMachine(clusterName, machineName)
   199  		awsMachine := newAWSMachine(clusterName, machineName)
   200  		awsCluster := newAWSCluster(clusterName)
   201  
   202  		expectedBootstrapDataFormat := "ignition"
   203  
   204  		secret := &corev1.Secret{
   205  			ObjectMeta: metav1.ObjectMeta{
   206  				Labels: map[string]string{
   207  					clusterv1.ClusterLabelName: clusterName,
   208  				},
   209  				Name:      machineName,
   210  				Namespace: "default",
   211  			},
   212  			Data: map[string][]byte{
   213  				"value":  []byte("user data"),
   214  				"format": []byte(expectedBootstrapDataFormat),
   215  			},
   216  		}
   217  
   218  		initObjects := []client.Object{
   219  			cluster, machine, secret, awsMachine, awsCluster,
   220  		}
   221  
   222  		client := fake.NewClientBuilder().WithScheme(scheme).WithObjects(initObjects...).Build()
   223  
   224  		machineScope, err := NewMachineScope(
   225  			MachineScopeParams{
   226  				Client:  client,
   227  				Machine: machine,
   228  				Cluster: cluster,
   229  				InfraCluster: &ClusterScope{
   230  					AWSCluster: awsCluster,
   231  				},
   232  				AWSMachine: awsMachine,
   233  			},
   234  		)
   235  		if err != nil {
   236  			t.Fatalf("Creating machine scope: %v", err)
   237  		}
   238  
   239  		_, format, err := machineScope.GetRawBootstrapDataWithFormat()
   240  		if err != nil {
   241  			t.Fatalf("Getting raw bootstrap data with format: %v", err)
   242  		}
   243  
   244  		if format != expectedBootstrapDataFormat {
   245  			t.Fatalf("Unexpected bootstrap data format, expected %q, got %q", expectedBootstrapDataFormat, format)
   246  		}
   247  	})
   248  }
   249  
   250  func TestUseSecretsManagerTrue(t *testing.T) {
   251  	scope, err := setupMachineScope()
   252  	if err != nil {
   253  		t.Fatal(err)
   254  	}
   255  
   256  	if !scope.UseSecretsManager("cloud-config") {
   257  		t.Fatalf("UseSecretsManager should be true")
   258  	}
   259  }
   260  
   261  func Test_UseIgnition(t *testing.T) {
   262  	t.Run("returns_true_when_given_bootstrap_data_format_is_ignition", func(t *testing.T) {
   263  		scope, err := setupMachineScope()
   264  		if err != nil {
   265  			t.Fatal(err)
   266  		}
   267  
   268  		if !scope.UseIgnition("ignition") {
   269  			t.Fatalf("UseIgnition should be true")
   270  		}
   271  	})
   272  
   273  	// To retain backward compatibility, where KAPBK does not produce format field.
   274  	t.Run("returns_false_when_given_bootstrap_data_format_is_empty", func(t *testing.T) {
   275  		scope, err := setupMachineScope()
   276  		if err != nil {
   277  			t.Fatal(err)
   278  		}
   279  
   280  		if scope.UseIgnition("") {
   281  			t.Fatalf("UseIgnition should be false")
   282  		}
   283  	})
   284  }
   285  
   286  func Test_CompressUserData(t *testing.T) {
   287  	// Ignition does not support compressed data in S3.
   288  	t.Run("returns_false_when_bootstrap_data_is_in_ignition_format", func(t *testing.T) {
   289  		scope, err := setupMachineScope()
   290  		if err != nil {
   291  			t.Fatal(err)
   292  		}
   293  
   294  		if scope.CompressUserData("ignition") {
   295  			t.Fatalf("User data would be compressed despite Ignition format")
   296  		}
   297  	})
   298  }
   299  
   300  func TestGetSecretARNDefaultIsNil(t *testing.T) {
   301  	scope, err := setupMachineScope()
   302  	if err != nil {
   303  		t.Fatal(err)
   304  	}
   305  
   306  	if scope.GetSecretPrefix() != "" {
   307  		t.Fatalf("GetSecretPrefix should be empty string")
   308  	}
   309  }
   310  
   311  func TestSetSecretARN(t *testing.T) {
   312  	prefix := "secret"
   313  	scope, err := setupMachineScope()
   314  	if err != nil {
   315  		t.Fatal(err)
   316  	}
   317  
   318  	scope.SetSecretPrefix(prefix)
   319  	if val := scope.GetSecretPrefix(); val != prefix {
   320  		t.Fatalf("prefix does not equal %s: %s", prefix, val)
   321  	}
   322  }
   323  
   324  func TestSetProviderID(t *testing.T) {
   325  	scope, err := setupMachineScope()
   326  	if err != nil {
   327  		t.Fatal(err)
   328  	}
   329  
   330  	scope.SetProviderID("test-id", "test-zone-1a")
   331  	providerID := *scope.AWSMachine.Spec.ProviderID
   332  	expectedProviderID := "aws:///test-zone-1a/test-id"
   333  	if providerID != expectedProviderID {
   334  		t.Fatalf("Expected providerID %s, got %s", expectedProviderID, providerID)
   335  	}
   336  
   337  	scope.SetProviderID("test-id", "")
   338  	providerID = *scope.AWSMachine.Spec.ProviderID
   339  	expectedProviderID = "aws:////test-id"
   340  	if providerID != expectedProviderID {
   341  		t.Fatalf("Expected providerID %s, got %s", expectedProviderID, providerID)
   342  	}
   343  }