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 }