sigs.k8s.io/cluster-api@v1.7.1/bootstrap/kubeadm/internal/ignition/ignition_test.go (about) 1 /* 2 Copyright 2021 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 ignition_test 18 19 import ( 20 "encoding/json" 21 "fmt" 22 "net/url" 23 "strings" 24 "testing" 25 26 bootstrapv1 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/api/v1beta1" 27 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/internal/cloudinit" 28 "sigs.k8s.io/cluster-api/bootstrap/kubeadm/internal/ignition" 29 ) 30 31 const testString = "foo bar baz" 32 33 func Test_NewNode(t *testing.T) { 34 t.Parallel() 35 36 t.Run("returns error when", func(t *testing.T) { 37 t.Parallel() 38 39 cases := map[string]*ignition.NodeInput{ 40 "nil input is given": nil, 41 "nil node input is given": {}, 42 } 43 44 for name, input := range cases { 45 input := input 46 47 t.Run(name, func(t *testing.T) { 48 t.Parallel() 49 50 ignitionData, _, err := ignition.NewNode(input) 51 if err == nil { 52 t.Fatalf("Expected error") 53 } 54 55 if ignitionData != nil { 56 t.Fatalf("Unexpected data returned %v", ignitionData) 57 } 58 }) 59 } 60 }) 61 62 t.Run("returns JSON data without error", func(t *testing.T) { 63 t.Parallel() 64 65 input := &ignition.NodeInput{ 66 NodeInput: &cloudinit.NodeInput{}, 67 Ignition: &bootstrapv1.IgnitionSpec{}, 68 } 69 70 ignitionData, _, err := ignition.NewNode(input) 71 if err != nil { 72 t.Fatalf("Unexpected error: %v", err) 73 } 74 75 if ignitionData == nil { 76 t.Fatalf("Returned data is nil") 77 } 78 79 decodedValue := map[string]interface{}{} 80 81 if err := json.Unmarshal(ignitionData, &decodedValue); err != nil { 82 t.Fatalf("Decoding received Ignition data as JSON: %v", err) 83 } 84 }) 85 86 t.Run("returns Ignition with user-specified snippet", func(t *testing.T) { 87 t.Parallel() 88 89 input := &ignition.NodeInput{ 90 NodeInput: &cloudinit.NodeInput{}, 91 Ignition: &bootstrapv1.IgnitionSpec{ 92 ContainerLinuxConfig: &bootstrapv1.ContainerLinuxConfig{ 93 AdditionalConfig: fmt.Sprintf(`storage: 94 files: 95 - path: /etc/foo 96 mode: 0644 97 contents: 98 inline: | 99 %s 100 `, testString), 101 }, 102 }, 103 } 104 105 ignitionData, _, err := ignition.NewNode(input) 106 if err != nil { 107 t.Fatalf("Unexpected error: %v", err) 108 } 109 110 // Ignition stores content URL-encoded. 111 u := url.URL{Path: testString} 112 113 if !strings.Contains(string(ignitionData), u.String()) { 114 t.Fatalf("Expected %q to be included in %q", testString, string(ignitionData)) 115 } 116 }) 117 118 t.Run("returns warnings if any", func(t *testing.T) { 119 t.Parallel() 120 121 input := &ignition.NodeInput{ 122 NodeInput: &cloudinit.NodeInput{}, 123 Ignition: &bootstrapv1.IgnitionSpec{ 124 ContainerLinuxConfig: &bootstrapv1.ContainerLinuxConfig{ 125 AdditionalConfig: fmt.Sprintf(`storage: 126 files: 127 - path: /etc/foo 128 contents: 129 inline: | 130 %s 131 `, testString), 132 }, 133 }, 134 } 135 136 ignitionData, warnings, err := ignition.NewNode(input) 137 if err != nil { 138 t.Fatalf("Unexpected error: %v", err) 139 } 140 141 if warnings == "" { 142 t.Fatalf("Expected warnings") 143 } 144 145 if len(ignitionData) == 0 { 146 t.Fatalf("Data should be returned with warnings but no errors") 147 } 148 }) 149 } 150 151 func Test_NewJoinControlPlane(t *testing.T) { 152 t.Parallel() 153 154 t.Run("returns error when", func(t *testing.T) { 155 t.Parallel() 156 157 cases := map[string]*ignition.ControlPlaneJoinInput{ 158 "nil input is given": nil, 159 "nil node input is given": {}, 160 } 161 162 for name, input := range cases { 163 input := input 164 165 t.Run(name, func(t *testing.T) { 166 t.Parallel() 167 168 ignitionData, _, err := ignition.NewJoinControlPlane(input) 169 if err == nil { 170 t.Fatalf("Expected error") 171 } 172 173 if ignitionData != nil { 174 t.Fatalf("Unexpected data returned %v", ignitionData) 175 } 176 }) 177 } 178 }) 179 180 t.Run("returns JSON data without error", func(t *testing.T) { 181 t.Parallel() 182 183 input := &ignition.ControlPlaneJoinInput{ 184 ControlPlaneJoinInput: &cloudinit.ControlPlaneJoinInput{}, 185 Ignition: &bootstrapv1.IgnitionSpec{}, 186 } 187 188 ignitionData, _, err := ignition.NewJoinControlPlane(input) 189 if err != nil { 190 t.Fatalf("Unexpected error: %v", err) 191 } 192 193 if ignitionData == nil { 194 t.Fatalf("Returned data is nil") 195 } 196 197 decodedValue := map[string]interface{}{} 198 199 if err := json.Unmarshal(ignitionData, &decodedValue); err != nil { 200 t.Fatalf("Decoding received Ignition data as JSON: %v", err) 201 } 202 }) 203 204 t.Run("returns Ignition with user specified snippet", func(t *testing.T) { 205 t.Parallel() 206 207 input := &ignition.ControlPlaneJoinInput{ 208 ControlPlaneJoinInput: &cloudinit.ControlPlaneJoinInput{}, 209 Ignition: &bootstrapv1.IgnitionSpec{ 210 ContainerLinuxConfig: &bootstrapv1.ContainerLinuxConfig{ 211 AdditionalConfig: fmt.Sprintf(`storage: 212 files: 213 - path: /etc/foo 214 mode: 0644 215 contents: 216 inline: | 217 %s 218 `, testString), 219 }, 220 }, 221 } 222 223 ignitionData, _, err := ignition.NewJoinControlPlane(input) 224 if err != nil { 225 t.Fatalf("Unexpected error: %v", err) 226 } 227 228 // Ignition stores content URL-encoded. 229 u := url.URL{Path: testString} 230 231 if !strings.Contains(string(ignitionData), u.String()) { 232 t.Fatalf("Expected %q to be included in %q", testString, string(ignitionData)) 233 } 234 }) 235 236 t.Run("returns warnings if any", func(t *testing.T) { 237 t.Parallel() 238 239 input := &ignition.ControlPlaneJoinInput{ 240 ControlPlaneJoinInput: &cloudinit.ControlPlaneJoinInput{}, 241 Ignition: &bootstrapv1.IgnitionSpec{ 242 ContainerLinuxConfig: &bootstrapv1.ContainerLinuxConfig{ 243 AdditionalConfig: fmt.Sprintf(`storage: 244 files: 245 - path: /etc/foo 246 contents: 247 inline: | 248 %s 249 `, testString), 250 }, 251 }, 252 } 253 254 ignitionData, warnings, err := ignition.NewJoinControlPlane(input) 255 if err != nil { 256 t.Fatalf("Unexpected error: %v", err) 257 } 258 259 if warnings == "" { 260 t.Fatalf("Expected to get some warnings") 261 } 262 263 if len(ignitionData) == 0 { 264 t.Fatalf("Data should be returned with warnings but no errors") 265 } 266 }) 267 } 268 269 func Test_NewInitControlPlane(t *testing.T) { 270 t.Parallel() 271 272 t.Run("returns error when", func(t *testing.T) { 273 t.Parallel() 274 275 cases := map[string]*ignition.ControlPlaneInput{ 276 "nil input is given": nil, 277 "nil node input is given": {}, 278 } 279 280 for name, input := range cases { 281 input := input 282 283 t.Run(name, func(t *testing.T) { 284 t.Parallel() 285 286 ignitionData, _, err := ignition.NewInitControlPlane(input) 287 if err == nil { 288 t.Fatalf("Expected error") 289 } 290 291 if ignitionData != nil { 292 t.Fatalf("Unexpected data returned %v", ignitionData) 293 } 294 }) 295 } 296 }) 297 298 t.Run("returns without error", func(t *testing.T) { 299 t.Parallel() 300 301 input := &ignition.ControlPlaneInput{ 302 ControlPlaneInput: &cloudinit.ControlPlaneInput{}, 303 } 304 305 ignitionData, _, err := ignition.NewInitControlPlane(input) 306 if err != nil { 307 t.Fatalf("Unexpected error: %v", err) 308 } 309 310 if ignitionData == nil { 311 t.Fatalf("Returned data is nil") 312 } 313 314 t.Run("valid_JSON_data", func(t *testing.T) { 315 decodedValue := map[string]interface{}{} 316 317 if err := json.Unmarshal(ignitionData, &decodedValue); err != nil { 318 t.Fatalf("Decoding received Ignition data as JSON: %v", err) 319 } 320 }) 321 }) 322 323 t.Run("returns Ignition with user-specified snippet", func(t *testing.T) { 324 t.Parallel() 325 326 input := &ignition.ControlPlaneInput{ 327 ControlPlaneInput: &cloudinit.ControlPlaneInput{}, 328 Ignition: &bootstrapv1.IgnitionSpec{ 329 ContainerLinuxConfig: &bootstrapv1.ContainerLinuxConfig{ 330 AdditionalConfig: fmt.Sprintf(`storage: 331 files: 332 - path: /etc/foo 333 mode: 0644 334 contents: 335 inline: | 336 %s 337 `, testString), 338 }, 339 }, 340 } 341 342 ignitionData, _, err := ignition.NewInitControlPlane(input) 343 if err != nil { 344 t.Fatalf("Unexpected error: %v", err) 345 } 346 347 // Ignition stores content URL-encoded. 348 u := url.URL{Path: testString} 349 350 if !strings.Contains(string(ignitionData), u.String()) { 351 t.Fatalf("Expected %q to be included in %q", testString, string(ignitionData)) 352 } 353 }) 354 355 t.Run("returns warnings if any", func(t *testing.T) { 356 t.Parallel() 357 358 input := &ignition.ControlPlaneInput{ 359 ControlPlaneInput: &cloudinit.ControlPlaneInput{}, 360 Ignition: &bootstrapv1.IgnitionSpec{ 361 ContainerLinuxConfig: &bootstrapv1.ContainerLinuxConfig{ 362 AdditionalConfig: fmt.Sprintf(`storage: 363 files: 364 - path: /etc/foo 365 contents: 366 inline: | 367 %s 368 `, testString), 369 }, 370 }, 371 } 372 373 ignitionData, warnings, err := ignition.NewInitControlPlane(input) 374 if err != nil { 375 t.Fatalf("Unexpected error: %v", err) 376 } 377 378 if warnings == "" { 379 t.Fatalf("Expected warnings") 380 } 381 382 if len(ignitionData) == 0 { 383 t.Fatalf("Data should be returned with warnings but no errors") 384 } 385 }) 386 }