github.com/canonical/ubuntu-image@v0.0.0-20240430122802-2202fe98b290/internal/imagedefinition/image_definition_test.go (about) 1 package imagedefinition 2 3 import ( 4 "strings" 5 "testing" 6 7 "github.com/google/go-cmp/cmp" 8 "github.com/xeipuuv/gojsonschema" 9 10 "github.com/canonical/ubuntu-image/internal/helper" 11 ) 12 13 func TestGeneratePocketList(t *testing.T) { 14 t.Parallel() 15 asserter := helper.Asserter{T: t} 16 type args struct { 17 series string 18 components []string 19 mirror string 20 securityMirror string 21 pocket string 22 } 23 24 testCases := []struct { 25 name string 26 imageDef ImageDefinition 27 args args 28 expectedSourcesList string 29 }{ 30 { 31 name: "release", 32 args: args{ 33 series: "jammy", 34 components: []string{"main", "universe"}, 35 mirror: "http://archive.ubuntu.com/ubuntu/", 36 securityMirror: "http://security.ubuntu.com/ubuntu/", 37 pocket: "release", 38 }, 39 expectedSourcesList: `# See http://help.ubuntu.com/community/UpgradeNotes for how to upgrade to 40 # newer versions of the distribution. 41 deb http://archive.ubuntu.com/ubuntu/ jammy main universe 42 `, 43 }, 44 { 45 name: "security", 46 args: args{ 47 series: "jammy", 48 components: []string{"main"}, 49 mirror: "http://archive.ubuntu.com/ubuntu/", 50 pocket: "security", 51 securityMirror: "http://security.ubuntu.com/ubuntu/", 52 }, 53 expectedSourcesList: `# See http://help.ubuntu.com/community/UpgradeNotes for how to upgrade to 54 # newer versions of the distribution. 55 deb http://archive.ubuntu.com/ubuntu/ jammy main 56 deb http://security.ubuntu.com/ubuntu/ jammy-security main 57 `, 58 }, 59 { 60 name: "updates", 61 args: args{ 62 series: "jammy", 63 components: []string{"main", "universe", "multiverse"}, 64 mirror: "http://ports.ubuntu.com/", 65 securityMirror: "http://ports.ubuntu.com/", 66 pocket: "updates", 67 }, 68 expectedSourcesList: `# See http://help.ubuntu.com/community/UpgradeNotes for how to upgrade to 69 # newer versions of the distribution. 70 deb http://ports.ubuntu.com/ jammy main universe multiverse 71 deb http://ports.ubuntu.com/ jammy-security main universe multiverse 72 ## Major bug fix updates produced after the final release of the 73 ## distribution. 74 deb http://ports.ubuntu.com/ jammy-updates main universe multiverse 75 `, 76 }, 77 { 78 name: "proposed", 79 args: args{ 80 series: "jammy", 81 components: []string{"main", "universe", "multiverse", "restricted"}, 82 mirror: "http://archive.ubuntu.com/ubuntu/", 83 securityMirror: "http://security.ubuntu.com/ubuntu/", 84 pocket: "proposed", 85 }, 86 expectedSourcesList: `# See http://help.ubuntu.com/community/UpgradeNotes for how to upgrade to 87 # newer versions of the distribution. 88 deb http://archive.ubuntu.com/ubuntu/ jammy main universe multiverse restricted 89 deb http://security.ubuntu.com/ubuntu/ jammy-security main universe multiverse restricted 90 ## Major bug fix updates produced after the final release of the 91 ## distribution. 92 deb http://archive.ubuntu.com/ubuntu/ jammy-updates main universe multiverse restricted 93 deb http://archive.ubuntu.com/ubuntu/ jammy-proposed main universe multiverse restricted 94 `, 95 }, 96 } 97 for _, tc := range testCases { 98 t.Run(tc.name, func(t *testing.T) { 99 gotSourcesList := generateLegacySourcesList( 100 tc.args.series, 101 tc.args.components, 102 tc.args.mirror, 103 tc.args.securityMirror, 104 tc.args.pocket, 105 ) 106 107 asserter.AssertEqual(tc.expectedSourcesList, gotSourcesList) 108 }) 109 } 110 } 111 112 // TestCustomErrors tests the custom json schema errors that we define 113 func TestCustomErrors(t *testing.T) { 114 t.Parallel() 115 jsonContext := gojsonschema.NewJsonContext("testContext", nil) 116 errDetail := gojsonschema.ErrorDetails{ 117 "key": "testKey", 118 "value": "testValue", 119 } 120 missingURLErr := NewMissingURLError( 121 gojsonschema.NewJsonContext("testMissingURL", jsonContext), 122 52, 123 errDetail, 124 ) 125 // spot check the description format 126 if !strings.Contains(missingURLErr.DescriptionFormat(), 127 "When key {{.key}} is specified as {{.value}}, a URL must be provided") { 128 t.Errorf("missingURLError description format \"%s\" is invalid", 129 missingURLErr.DescriptionFormat()) 130 } 131 132 invalidPPAErr := NewInvalidPPAError( 133 gojsonschema.NewJsonContext("testInvalidPPA", jsonContext), 134 52, 135 errDetail, 136 ) 137 // spot check the description format 138 if !strings.Contains(invalidPPAErr.DescriptionFormat(), 139 "Fingerprint is required for private PPAs") { 140 t.Errorf("invalidPPAError description format \"%s\" is invalid", 141 invalidPPAErr.DescriptionFormat()) 142 } 143 144 pathNotAbsoluteErr := NewPathNotAbsoluteError( 145 gojsonschema.NewJsonContext("testPathNotAbsolute", jsonContext), 146 52, 147 errDetail, 148 ) 149 // spot check the description format 150 if !strings.Contains(pathNotAbsoluteErr.DescriptionFormat(), 151 "Key {{.key}} needs to be an absolute path ({{.value}})") { 152 t.Errorf("pathNotAbsoluteError description format \"%s\" is invalid", 153 pathNotAbsoluteErr.DescriptionFormat()) 154 } 155 dependentKeyErr := NewDependentKeyError( 156 gojsonschema.NewJsonContext("testDependentKey", jsonContext), 157 52, 158 errDetail, 159 ) 160 // spot check the description format 161 if !strings.Contains(dependentKeyErr.DescriptionFormat(), 162 "Key {{.key1}} cannot be used without key {{.key2}}") { 163 t.Errorf("dependentKeyError description format \"%s\" is invalid", 164 dependentKeyErr.DescriptionFormat()) 165 } 166 } 167 168 // TestImageDefinition_SetDefaults make sure we do not add a boolean field 169 // with a default value (because we cannot properly apply the default value) 170 func TestImageDefinition_SetDefaults(t *testing.T) { 171 t.Parallel() 172 173 tests := []struct { 174 name string 175 imageDef *ImageDefinition 176 want *ImageDefinition 177 }{ 178 { 179 name: "full", 180 imageDef: &ImageDefinition{ 181 Gadget: &Gadget{}, 182 Rootfs: &Rootfs{ 183 Seed: &Seed{}, 184 Tarball: &Tarball{}, 185 }, 186 Customization: &Customization{ 187 Installer: &Installer{}, 188 CloudInit: &CloudInit{}, 189 ExtraPPAs: []*PPA{{}}, 190 ExtraPackages: []*Package{{}}, 191 ExtraSnaps: []*Snap{{}}, 192 Fstab: []*Fstab{{}}, 193 Manual: &Manual{ 194 AddUser: []*AddUser{ 195 {}, 196 }, 197 }, 198 }, 199 Artifacts: &Artifact{ 200 Img: &[]Img{{}}, 201 Iso: &[]Iso{{}}, 202 Qcow2: &[]Qcow2{{}}, 203 Manifest: &Manifest{}, 204 Filelist: &Filelist{}, 205 Changelog: &Changelog{}, 206 RootfsTar: &RootfsTar{}, 207 }, 208 }, 209 want: &ImageDefinition{ 210 Gadget: &Gadget{}, 211 Rootfs: &Rootfs{ 212 Seed: &Seed{ 213 Vcs: helper.BoolPtr(true), 214 }, 215 Tarball: &Tarball{}, 216 Components: []string{"main", "restricted"}, 217 Archive: "ubuntu", 218 Flavor: "ubuntu", 219 Mirror: "http://archive.ubuntu.com/ubuntu/", 220 Pocket: "release", 221 SourcesListDeb822: helper.BoolPtr(false), 222 }, 223 Customization: &Customization{ 224 Components: []string{"main", "restricted", "universe"}, 225 Pocket: "release", 226 Installer: &Installer{}, 227 CloudInit: &CloudInit{}, 228 ExtraPPAs: []*PPA{{ 229 KeepEnabled: helper.BoolPtr(true), 230 }}, 231 ExtraPackages: []*Package{{}}, 232 ExtraSnaps: []*Snap{{ 233 Store: "canonical", 234 Channel: "stable", 235 }}, 236 Fstab: []*Fstab{{ 237 MountOptions: "defaults", 238 }}, 239 Manual: &Manual{ 240 AddUser: []*AddUser{ 241 { 242 PasswordType: "hash", 243 }, 244 }, 245 }, 246 }, 247 Artifacts: &Artifact{ 248 Img: &[]Img{{}}, 249 Iso: &[]Iso{{}}, 250 Qcow2: &[]Qcow2{{}}, 251 Manifest: &Manifest{}, 252 Filelist: &Filelist{}, 253 Changelog: &Changelog{}, 254 RootfsTar: &RootfsTar{ 255 Compression: "uncompressed", 256 }, 257 }, 258 }, 259 }, 260 { 261 name: "minimal conf", 262 imageDef: &ImageDefinition{ 263 Gadget: &Gadget{}, 264 Rootfs: &Rootfs{ 265 Seed: &Seed{}, 266 Tarball: &Tarball{}, 267 }, 268 }, 269 want: &ImageDefinition{ 270 Gadget: &Gadget{}, 271 Rootfs: &Rootfs{ 272 Seed: &Seed{ 273 Vcs: helper.BoolPtr(true), 274 }, 275 Tarball: &Tarball{}, 276 Components: []string{"main", "restricted"}, 277 Archive: "ubuntu", 278 Flavor: "ubuntu", 279 Mirror: "http://archive.ubuntu.com/ubuntu/", 280 Pocket: "release", 281 SourcesListDeb822: helper.BoolPtr(false), 282 }, 283 }, 284 }, 285 } 286 for _, tt := range tests { 287 t.Run(tt.name, func(t *testing.T) { 288 asserter := helper.Asserter{T: t} 289 err := helper.SetDefaults(tt.imageDef) 290 asserter.AssertErrNil(err, true) 291 292 asserter.AssertEqual(tt.want, tt.imageDef, cmp.AllowUnexported(ImageDefinition{})) 293 }) 294 } 295 } 296 297 func TestImageDefinition_securityMirror(t *testing.T) { 298 type fields struct { 299 Architecture string 300 Rootfs *Rootfs 301 } 302 tests := []struct { 303 name string 304 fields fields 305 want string 306 }{ 307 { 308 name: "amd64", 309 fields: fields{ 310 Architecture: "amd64", 311 Rootfs: &Rootfs{ 312 Mirror: "http://archive.ubuntu.com/ubuntu/", 313 }, 314 }, 315 want: "http://security.ubuntu.com/ubuntu/", 316 }, 317 { 318 name: "i386", 319 fields: fields{ 320 Architecture: "i386", 321 Rootfs: &Rootfs{ 322 Mirror: "http://archive.ubuntu.com/ubuntu/", 323 }, 324 }, 325 want: "http://security.ubuntu.com/ubuntu/", 326 }, 327 { 328 name: "arm64", 329 fields: fields{ 330 Architecture: "arm64", 331 Rootfs: &Rootfs{ 332 Mirror: "http://archive.ubuntu.com/ubuntu/", 333 }, 334 }, 335 want: "http://archive.ubuntu.com/ubuntu/", 336 }, 337 { 338 name: "no arch", 339 fields: fields{ 340 Rootfs: &Rootfs{ 341 Mirror: "http://archive.ubuntu.com/ubuntu/", 342 }, 343 }, 344 want: "http://archive.ubuntu.com/ubuntu/", 345 }, 346 } 347 for _, tt := range tests { 348 t.Run(tt.name, func(t *testing.T) { 349 imageDef := ImageDefinition{ 350 Architecture: tt.fields.Architecture, 351 Rootfs: tt.fields.Rootfs, 352 } 353 if got := imageDef.securityMirror(); got != tt.want { 354 t.Errorf("ImageDefinition.securityMirror() = %v, want %v", got, tt.want) 355 } 356 }) 357 } 358 } 359 360 func Test_generateDeb822Section(t *testing.T) { 361 asserter := helper.Asserter{T: t} 362 type args struct { 363 mirror string 364 series string 365 components []string 366 pocket string 367 } 368 tests := []struct { 369 name string 370 args args 371 want string 372 }{ 373 { 374 name: "release", 375 args: args{ 376 mirror: "http://archive.ubuntu.com/ubuntu/", 377 series: "jammy", 378 components: []string{"main", "restricted"}, 379 pocket: "release", 380 }, 381 want: `Types: deb 382 URIs: http://archive.ubuntu.com/ubuntu/ 383 Suites: jammy 384 Components: main restricted 385 Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg 386 387 `, 388 }, 389 { 390 name: "security", 391 args: args{ 392 mirror: "http://security.ubuntu.com/ubuntu/", 393 series: "jammy", 394 components: []string{"main", "restricted"}, 395 pocket: "security", 396 }, 397 want: `Types: deb 398 URIs: http://security.ubuntu.com/ubuntu/ 399 Suites: jammy-security 400 Components: main restricted 401 Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg 402 403 `, 404 }, 405 { 406 name: "proposed", 407 args: args{ 408 mirror: "http://archive.ubuntu.com/ubuntu/", 409 series: "jammy", 410 components: []string{"main", "restricted"}, 411 pocket: "proposed", 412 }, 413 want: `Types: deb 414 URIs: http://archive.ubuntu.com/ubuntu/ 415 Suites: jammy jammy-updates jammy-proposed 416 Components: main restricted 417 Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg 418 419 `, 420 }, 421 { 422 name: "updates", 423 args: args{ 424 mirror: "http://archive.ubuntu.com/ubuntu/", 425 series: "jammy", 426 components: []string{"main", "restricted"}, 427 pocket: "updates", 428 }, 429 want: `Types: deb 430 URIs: http://archive.ubuntu.com/ubuntu/ 431 Suites: jammy jammy-updates 432 Components: main restricted 433 Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg 434 435 `, 436 }, 437 { 438 name: "no pocket", 439 args: args{ 440 mirror: "http://archive.ubuntu.com/ubuntu/", 441 series: "jammy", 442 components: []string{"main", "restricted"}, 443 pocket: "", 444 }, 445 want: `Types: deb 446 URIs: http://archive.ubuntu.com/ubuntu/ 447 Suites: 448 Components: main restricted 449 Signed-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg 450 451 `, 452 }, 453 } 454 for _, tc := range tests { 455 t.Run(tc.name, func(t *testing.T) { 456 got := generateDeb822Section(tc.args.mirror, tc.args.series, tc.args.components, tc.args.pocket) 457 asserter.AssertEqual(tc.want, got) 458 }) 459 } 460 }