zotregistry.io/zot@v1.4.4-0.20231124084042-02a8ed785457/pkg/cli/client/image_cmd_internal_test.go (about) 1 //go:build search 2 // +build search 3 4 package client 5 6 import ( 7 "bytes" 8 "context" 9 "errors" 10 "fmt" 11 "log" 12 "os" 13 "path" 14 "path/filepath" 15 "regexp" 16 "strings" 17 "sync" 18 "testing" 19 "time" 20 21 godigest "github.com/opencontainers/go-digest" 22 ispec "github.com/opencontainers/image-spec/specs-go/v1" 23 . "github.com/smartystreets/goconvey/convey" 24 25 zerr "zotregistry.io/zot/errors" 26 "zotregistry.io/zot/pkg/api" 27 "zotregistry.io/zot/pkg/api/config" 28 "zotregistry.io/zot/pkg/common" 29 extconf "zotregistry.io/zot/pkg/extensions/config" 30 stypes "zotregistry.io/zot/pkg/storage/types" 31 test "zotregistry.io/zot/pkg/test/common" 32 . "zotregistry.io/zot/pkg/test/image-utils" 33 ) 34 35 func TestSearchImageCmd(t *testing.T) { 36 Convey("Test image help", t, func() { 37 args := []string{"--help"} 38 configPath := makeConfigFile("") 39 defer os.Remove(configPath) 40 cmd := NewImageCommand(new(mockService)) 41 buff := bytes.NewBufferString("") 42 cmd.SetOut(buff) 43 cmd.SetErr(buff) 44 cmd.SetArgs(args) 45 err := cmd.Execute() 46 So(buff.String(), ShouldContainSubstring, "Usage") 47 So(err, ShouldBeNil) 48 Convey("with the shorthand", func() { 49 args[0] = "-h" 50 configPath := makeConfigFile("") 51 defer os.Remove(configPath) 52 cmd := NewImageCommand(new(mockService)) 53 buff := bytes.NewBufferString("") 54 cmd.SetOut(buff) 55 cmd.SetErr(buff) 56 cmd.SetArgs(args) 57 err := cmd.Execute() 58 So(buff.String(), ShouldContainSubstring, "Usage") 59 So(err, ShouldBeNil) 60 }) 61 }) 62 63 Convey("Test image no url", t, func() { 64 args := []string{"name", "dummyIdRandom", "--config", "imagetest"} 65 configPath := makeConfigFile(`{"configs":[{"_name":"imagetest","showspinner":false}]}`) 66 defer os.Remove(configPath) 67 cmd := NewImageCommand(new(mockService)) 68 buff := bytes.NewBufferString("") 69 cmd.SetOut(buff) 70 cmd.SetErr(buff) 71 cmd.SetArgs(args) 72 err := cmd.Execute() 73 So(err, ShouldNotBeNil) 74 So(errors.Is(err, zerr.ErrNoURLProvided), ShouldBeTrue) 75 }) 76 77 Convey("Test image invalid home directory", t, func() { 78 args := []string{"name", "dummyImageName", "--config", "imagetest"} 79 80 configPath := makeConfigFile(`{"configs":[{"_name":"imagetest","url":"https://test-url.com","showspinner":false}]}`) 81 defer os.Remove(configPath) 82 83 err := os.Setenv("HOME", "nonExistentDirectory") 84 if err != nil { 85 panic(err) 86 } 87 88 cmd := NewImageCommand(new(mockService)) 89 buff := bytes.NewBufferString("") 90 cmd.SetOut(buff) 91 cmd.SetErr(buff) 92 cmd.SetArgs(args) 93 err = cmd.Execute() 94 So(err, ShouldNotBeNil) 95 96 home, err := os.UserHomeDir() 97 if err != nil { 98 panic(err) 99 } 100 err = os.Setenv("HOME", home) 101 if err != nil { 102 log.Fatal(err) 103 } 104 }) 105 106 Convey("Test image no params", t, func() { 107 args := []string{"--url", "someUrl"} 108 configPath := makeConfigFile(`{"configs":[{"_name":"imagetest","showspinner":false}]}`) 109 defer os.Remove(configPath) 110 cmd := NewImageCommand(new(mockService)) 111 buff := bytes.NewBufferString("") 112 cmd.SetOut(buff) 113 cmd.SetErr(buff) 114 cmd.SetArgs(args) 115 err := cmd.Execute() 116 So(err, ShouldBeNil) 117 }) 118 119 Convey("Test image invalid url", t, func() { 120 args := []string{"name", "dummyImageName", "--url", "invalidUrl"} 121 configPath := makeConfigFile(`{"configs":[{"_name":"imagetest","showspinner":false}]}`) 122 defer os.Remove(configPath) 123 cmd := NewImageCommand(new(searchService)) 124 buff := bytes.NewBufferString("") 125 cmd.SetOut(buff) 126 cmd.SetErr(buff) 127 cmd.SetArgs(args) 128 err := cmd.Execute() 129 So(err, ShouldNotBeNil) 130 So(strings.Contains(err.Error(), zerr.ErrInvalidURL.Error()), ShouldBeTrue) 131 So(buff.String(), ShouldContainSubstring, "invalid URL format") 132 }) 133 134 Convey("Test image invalid url port", t, func() { 135 args := []string{"name", "dummyImageName", "--url", "http://localhost:99999"} 136 configPath := makeConfigFile(`{"configs":[{"_name":"imagetest","showspinner":false}]}`) 137 defer os.Remove(configPath) 138 cmd := NewImageCommand(new(searchService)) 139 buff := bytes.NewBufferString("") 140 cmd.SetOut(buff) 141 cmd.SetErr(buff) 142 cmd.SetArgs(args) 143 err := cmd.Execute() 144 So(err, ShouldNotBeNil) 145 So(buff.String(), ShouldContainSubstring, "invalid port") 146 147 Convey("without flags", func() { 148 args := []string{"list", "--url", "http://localhost:99999"} 149 configPath := makeConfigFile(`{"configs":[{"_name":"imagetest","showspinner":false}]}`) 150 defer os.Remove(configPath) 151 cmd := NewImageCommand(new(searchService)) 152 buff := bytes.NewBufferString("") 153 cmd.SetOut(buff) 154 cmd.SetErr(buff) 155 cmd.SetArgs(args) 156 err = cmd.Execute() 157 So(err, ShouldNotBeNil) 158 So(buff.String(), ShouldContainSubstring, "invalid port") 159 }) 160 }) 161 162 Convey("Test image unreachable", t, func() { 163 args := []string{"name", "dummyImageName", "--url", "http://localhost:9999"} 164 configPath := makeConfigFile(`{"configs":[{"_name":"imagetest","showspinner":false}]}`) 165 defer os.Remove(configPath) 166 cmd := NewImageCommand(new(searchService)) 167 buff := bytes.NewBufferString("") 168 cmd.SetOut(buff) 169 cmd.SetErr(buff) 170 cmd.SetArgs(args) 171 err := cmd.Execute() 172 So(err, ShouldNotBeNil) 173 }) 174 175 Convey("Test image url from config", t, func() { 176 args := []string{"name", "dummyImageName", "--config", "imagetest"} 177 configPath := makeConfigFile(`{"configs":[{"_name":"imagetest","url":"https://test-url.com","showspinner":false}]}`) 178 defer os.Remove(configPath) 179 cmd := NewImageCommand(new(mockService)) 180 buff := bytes.NewBufferString("") 181 cmd.SetOut(buff) 182 cmd.SetErr(buff) 183 cmd.SetArgs(args) 184 err := cmd.Execute() 185 space := regexp.MustCompile(`\s+`) 186 str := space.ReplaceAllString(buff.String(), " ") 187 So(strings.TrimSpace(str), ShouldEqual, 188 "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE dummyImageName tag os/arch 6e2f80bf false 123kB") 189 So(err, ShouldBeNil) 190 }) 191 192 Convey("Test image by name", t, func() { 193 args := []string{"name", "dummyImageName", "--url", "http://127.0.0.1:8080"} 194 configPath := makeConfigFile(`{"configs":[{"_name":"imagetest","showspinner":false}]}`) 195 defer os.Remove(configPath) 196 imageCmd := NewImageCommand(new(mockService)) 197 buff := &bytes.Buffer{} 198 imageCmd.SetOut(buff) 199 imageCmd.SetErr(buff) 200 imageCmd.SetArgs(args) 201 err := imageCmd.Execute() 202 space := regexp.MustCompile(`\s+`) 203 str := space.ReplaceAllString(buff.String(), " ") 204 So(strings.TrimSpace(str), ShouldEqual, 205 "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE dummyImageName tag os/arch 6e2f80bf false 123kB") 206 So(err, ShouldBeNil) 207 }) 208 209 Convey("Test image by digest", t, func() { 210 searchConfig := getTestSearchConfig("http://127.0.0.1:8080", new(mockService)) 211 buff := &bytes.Buffer{} 212 searchConfig.ResultWriter = buff 213 err := SearchImagesByDigest(searchConfig, "6e2f80bf") 214 So(err, ShouldBeNil) 215 216 space := regexp.MustCompile(`\s+`) 217 str := space.ReplaceAllString(buff.String(), " ") 218 So(strings.TrimSpace(str), ShouldEqual, 219 "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE anImage tag os/arch 6e2f80bf false 123kB") 220 So(err, ShouldBeNil) 221 }) 222 } 223 224 func TestListRepos(t *testing.T) { 225 searchConfig := getTestSearchConfig("https://test-url.com", new(mockService)) 226 227 Convey("Test listing repositories", t, func() { 228 buff := &bytes.Buffer{} 229 searchConfig.ResultWriter = buff 230 err := SearchRepos(searchConfig) 231 So(err, ShouldBeNil) 232 }) 233 234 Convey("Test listing repositories with debug flag", t, func() { 235 args := []string{"list", "--config", "config-test", "--debug"} 236 configPath := makeConfigFile(`{"configs":[{"_name":"config-test","url":"https://test-url.com","showspinner":false}]}`) 237 defer os.Remove(configPath) 238 cmd := NewRepoCommand(new(searchService)) 239 240 buff := bytes.NewBufferString("") 241 cmd.SetOut(buff) 242 cmd.SetErr(buff) 243 cmd.SetArgs(args) 244 err := cmd.Execute() 245 So(err, ShouldNotBeNil) 246 space := regexp.MustCompile(`\s+`) 247 str := space.ReplaceAllString(buff.String(), " ") 248 actual := strings.TrimSpace(str) 249 So(actual, ShouldContainSubstring, "GET") 250 }) 251 252 Convey("Test error on home directory", t, func() { 253 args := []string{"list", "--config", "config-test"} 254 255 configPath := makeConfigFile(`{"configs":[{"_name":"config-test","url":"https://test-url.com","showspinner":false}]}`) 256 defer os.Remove(configPath) 257 258 err := os.Setenv("HOME", "nonExistentDirectory") 259 if err != nil { 260 panic(err) 261 } 262 263 cmd := NewRepoCommand(new(mockService)) 264 buff := bytes.NewBufferString("") 265 cmd.SetOut(buff) 266 cmd.SetErr(buff) 267 cmd.SetArgs(args) 268 err = cmd.Execute() 269 So(err, ShouldNotBeNil) 270 271 home, err := os.UserHomeDir() 272 if err != nil { 273 panic(err) 274 } 275 err = os.Setenv("HOME", home) 276 if err != nil { 277 log.Fatal(err) 278 } 279 }) 280 281 Convey("Test listing repositories error", t, func() { 282 args := []string{"list", "--config", "config-test"} 283 configPath := makeConfigFile(`{"configs":[{"_name":"config-test", 284 "url":"https://invalid.invalid","showspinner":false}]}`) 285 defer os.Remove(configPath) 286 cmd := NewRepoCommand(new(searchService)) 287 buff := bytes.NewBufferString("") 288 cmd.SetOut(buff) 289 cmd.SetErr(buff) 290 cmd.SetArgs(args) 291 err := cmd.Execute() 292 So(err, ShouldNotBeNil) 293 }) 294 295 Convey("Test unable to get config value", t, func() { 296 args := []string{"list", "--config", "config-test-nonexistent"} 297 configPath := makeConfigFile(`{"configs":[{"_name":"config-test","url":"https://test-url.com","showspinner":false}]}`) 298 defer os.Remove(configPath) 299 cmd := NewRepoCommand(new(mockService)) 300 buff := bytes.NewBufferString("") 301 cmd.SetOut(buff) 302 cmd.SetErr(buff) 303 cmd.SetArgs(args) 304 err := cmd.Execute() 305 So(err, ShouldNotBeNil) 306 }) 307 308 Convey("Test error - no url provided", t, func() { 309 args := []string{"list", "--config", "config-test"} 310 configPath := makeConfigFile(`{"configs":[{"_name":"config-test","url":"","showspinner":false}]}`) 311 defer os.Remove(configPath) 312 cmd := NewRepoCommand(new(mockService)) 313 buff := bytes.NewBufferString("") 314 cmd.SetOut(buff) 315 cmd.SetErr(buff) 316 cmd.SetArgs(args) 317 err := cmd.Execute() 318 So(err, ShouldNotBeNil) 319 }) 320 321 Convey("Test error - spinner config invalid", t, func() { 322 args := []string{"list", "--config", "config-test"} 323 configPath := makeConfigFile(`{"configs":[{"_name":"config-test", 324 "url":"https://test-url.com","showspinner":invalid}]}`) 325 defer os.Remove(configPath) 326 cmd := NewRepoCommand(new(mockService)) 327 buff := bytes.NewBufferString("") 328 cmd.SetOut(buff) 329 cmd.SetErr(buff) 330 cmd.SetArgs(args) 331 err := cmd.Execute() 332 So(err, ShouldNotBeNil) 333 }) 334 335 Convey("Test error - verifyTLSConfig fails", t, func() { 336 args := []string{"list", "--config", "config-test"} 337 configPath := makeConfigFile(`{"configs":[{"_name":"config-test", 338 "verify-tls":"invalid", "url":"https://test-url.com","showspinner":false}]}`) 339 defer os.Remove(configPath) 340 cmd := NewRepoCommand(new(mockService)) 341 buff := bytes.NewBufferString("") 342 cmd.SetOut(buff) 343 cmd.SetErr(buff) 344 cmd.SetArgs(args) 345 err := cmd.Execute() 346 So(err, ShouldNotBeNil) 347 }) 348 } 349 350 func TestOutputFormat(t *testing.T) { 351 Convey("Test text", t, func() { 352 args := []string{"name", "dummyImageName", "--config", "imagetest", "-f", "text"} 353 configPath := makeConfigFile(`{"configs":[{"_name":"imagetest","url":"https://test-url.com","showspinner":false}]}`) 354 defer os.Remove(configPath) 355 cmd := NewImageCommand(new(mockService)) 356 buff := bytes.NewBufferString("") 357 cmd.SetOut(buff) 358 cmd.SetErr(buff) 359 cmd.SetArgs(args) 360 err := cmd.Execute() 361 space := regexp.MustCompile(`\s+`) 362 str := space.ReplaceAllString(buff.String(), " ") 363 So(strings.TrimSpace(str), ShouldEqual, 364 "REPOSITORY TAG OS/ARCH DIGEST SIGNED SIZE dummyImageName tag os/arch 6e2f80bf false 123kB") 365 So(err, ShouldBeNil) 366 }) 367 368 Convey("Test json", t, func() { 369 args := []string{"name", "dummyImageName", "--config", "imagetest", "-f", "json"} 370 configPath := makeConfigFile(`{"configs":[{"_name":"imagetest","url":"https://test-url.com","showspinner":false}]}`) 371 defer os.Remove(configPath) 372 cmd := NewImageCommand(new(mockService)) 373 buff := bytes.NewBufferString("") 374 cmd.SetOut(buff) 375 cmd.SetErr(buff) 376 cmd.SetArgs(args) 377 err := cmd.Execute() 378 // Output is supposed to be in json lines format, keep all spaces as is for verification 379 So(buff.String(), ShouldEqual, `{"repoName":"dummyImageName","tag":"tag",`+ 380 `"digest":"sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08",`+ 381 `"mediaType":"application/vnd.oci.image.manifest.v1+json",`+ 382 `"manifests":[{"digest":"sha256:6e2f80bf9cfaabad474fbaf8ad68fdb652f776ea80b63492ecca404e5f6446a6",`+ 383 `"configDigest":"sha256:4c10985c40365538426f2ba8cf0c21384a7769be502a550dcc0601b3736625e0",`+ 384 `"lastUpdated":"0001-01-01T00:00:00Z","size":"123445","platform":{"os":"os","arch":"arch",`+ 385 `"variant":""},"isSigned":false,"downloadCount":0,`+ 386 `"layers":[{"size":"","digest":"sha256:c122a146f0d02349be211bb95cc2530f4a5793f96edbdfa00860f741e5d8c0e6",`+ 387 `"score":0}],"history":null,"vulnerabilities":{"maxSeverity":"","count":0},`+ 388 `"referrers":null,"artifactType":"","signatureInfo":null}],"size":"123445",`+ 389 `"downloadCount":0,"lastUpdated":"0001-01-01T00:00:00Z","description":"","isSigned":false,"licenses":"",`+ 390 `"labels":"","title":"","source":"","documentation":"","authors":"","vendor":"",`+ 391 `"vulnerabilities":{"maxSeverity":"","count":0},"referrers":null,"signatureInfo":null}`+"\n") 392 So(err, ShouldBeNil) 393 }) 394 395 Convey("Test yaml", t, func() { 396 args := []string{"name", "dummyImageName", "--config", "imagetest", "-f", "yaml"} 397 configPath := makeConfigFile(`{"configs":[{"_name":"imagetest","url":"https://test-url.com","showspinner":false}]}`) 398 defer os.Remove(configPath) 399 cmd := NewImageCommand(new(mockService)) 400 buff := bytes.NewBufferString("") 401 cmd.SetOut(buff) 402 cmd.SetErr(buff) 403 cmd.SetArgs(args) 404 err := cmd.Execute() 405 space := regexp.MustCompile(`\s+`) 406 str := space.ReplaceAllString(buff.String(), " ") 407 So( 408 strings.TrimSpace(str), 409 ShouldEqual, 410 `--- reponame: dummyImageName tag: tag `+ 411 `digest: sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08 `+ 412 `mediatype: application/vnd.oci.image.manifest.v1+json manifests: - `+ 413 `digest: sha256:6e2f80bf9cfaabad474fbaf8ad68fdb652f776ea80b63492ecca404e5f6446a6 `+ 414 `configdigest: sha256:4c10985c40365538426f2ba8cf0c21384a7769be502a550dcc0601b3736625e0 `+ 415 `lastupdated: 0001-01-01T00:00:00Z size: "123445" platform: os: os arch: arch variant: "" `+ 416 `issigned: false downloadcount: 0 layers: - size: "" `+ 417 `digest: sha256:c122a146f0d02349be211bb95cc2530f4a5793f96edbdfa00860f741e5d8c0e6 score: 0 `+ 418 `history: [] vulnerabilities: maxseverity: "" count: 0 referrers: [] artifacttype: "" `+ 419 `signatureinfo: [] size: "123445" downloadcount: 0 `+ 420 `lastupdated: 0001-01-01T00:00:00Z description: "" issigned: false licenses: "" labels: "" `+ 421 `title: "" source: "" documentation: "" authors: "" vendor: "" vulnerabilities: maxseverity: "" `+ 422 `count: 0 referrers: [] signatureinfo: []`, 423 ) 424 So(err, ShouldBeNil) 425 426 Convey("Test yml", func() { 427 args := []string{"name", "dummyImageName", "--config", "imagetest", "-f", "yml"} 428 configPath := makeConfigFile( 429 `{"configs":[{"_name":"imagetest",` + 430 `"url":"https://test-url.com","showspinner":false}]}`, 431 ) 432 defer os.Remove(configPath) 433 cmd := NewImageCommand(new(mockService)) 434 buff := bytes.NewBufferString("") 435 cmd.SetOut(buff) 436 cmd.SetErr(buff) 437 cmd.SetArgs(args) 438 err := cmd.Execute() 439 space := regexp.MustCompile(`\s+`) 440 str := space.ReplaceAllString(buff.String(), " ") 441 So( 442 strings.TrimSpace(str), 443 ShouldEqual, 444 `--- reponame: dummyImageName tag: tag `+ 445 `digest: sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08 `+ 446 `mediatype: application/vnd.oci.image.manifest.v1+json `+ 447 `manifests: - digest: sha256:6e2f80bf9cfaabad474fbaf8ad68fdb652f776ea80b63492ecca404e5f6446a6 `+ 448 `configdigest: sha256:4c10985c40365538426f2ba8cf0c21384a7769be502a550dcc0601b3736625e0 `+ 449 `lastupdated: 0001-01-01T00:00:00Z size: "123445" platform: os: os arch: arch variant: "" `+ 450 `issigned: false downloadcount: 0 layers: - size: "" `+ 451 `digest: sha256:c122a146f0d02349be211bb95cc2530f4a5793f96edbdfa00860f741e5d8c0e6 score: 0 `+ 452 `history: [] vulnerabilities: maxseverity: "" count: 0 referrers: [] artifacttype: "" `+ 453 `signatureinfo: [] size: "123445" downloadcount: 0 `+ 454 `lastupdated: 0001-01-01T00:00:00Z description: "" issigned: false licenses: "" labels: "" `+ 455 `title: "" source: "" documentation: "" authors: "" vendor: "" vulnerabilities: maxseverity: `+ 456 `"" count: 0 referrers: [] signatureinfo: []`, 457 ) 458 So(err, ShouldBeNil) 459 }) 460 }) 461 462 Convey("Test invalid", t, func() { 463 args := []string{"name", "dummyImageName", "--config", "imagetest", "-f", "random"} 464 configPath := makeConfigFile(`{"configs":[{"_name":"imagetest","url":"https://test-url.com","showspinner":false}]}`) 465 defer os.Remove(configPath) 466 cmd := NewImageCommand(new(mockService)) 467 buff := bytes.NewBufferString("") 468 cmd.SetOut(buff) 469 cmd.SetErr(buff) 470 cmd.SetArgs(args) 471 err := cmd.Execute() 472 So(err, ShouldNotBeNil) 473 So(buff.String(), ShouldContainSubstring, "invalid output format") 474 }) 475 } 476 477 func TestImagesCommandGQL(t *testing.T) { 478 port := test.GetFreePort() 479 baseURL := test.GetBaseURL(port) 480 conf := config.New() 481 conf.HTTP.Port = port 482 483 defaultVal := true 484 conf.Extensions = &extconf.ExtensionConfig{ 485 Search: &extconf.SearchConfig{ 486 BaseConfig: extconf.BaseConfig{Enable: &defaultVal}, 487 }, 488 } 489 ctlr := api.NewController(conf) 490 ctlr.Config.Storage.RootDirectory = t.TempDir() 491 cm := test.NewControllerManager(ctlr) 492 493 cm.StartAndWait(conf.HTTP.Port) 494 defer cm.StopServer() 495 496 Convey("commands with gql", t, func() { 497 err := removeLocalStorageContents(ctlr.StoreController.DefaultStore) 498 So(err, ShouldBeNil) 499 500 Convey("base and derived command", func() { 501 baseImage := CreateImageWith().LayerBlobs( 502 [][]byte{{1, 2, 3}, {11, 22, 33}}, 503 ).DefaultConfig().Build() 504 505 derivedImage := CreateImageWith().LayerBlobs( 506 [][]byte{{1, 2, 3}, {11, 22, 33}, {44, 55, 66}}, 507 ).DefaultConfig().Build() 508 509 err := UploadImage(baseImage, baseURL, "repo", "base") 510 So(err, ShouldBeNil) 511 512 err = UploadImage(derivedImage, baseURL, "repo", "derived") 513 So(err, ShouldBeNil) 514 515 configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, 516 baseURL)) 517 defer os.Remove(configPath) 518 args := []string{"base", "repo:derived", "--config", "imagetest"} 519 520 cmd := NewImageCommand(NewSearchService()) 521 buff := bytes.NewBufferString("") 522 cmd.SetOut(buff) 523 cmd.SetErr(buff) 524 cmd.SetArgs(args) 525 err = cmd.Execute() 526 So(err, ShouldBeNil) 527 space := regexp.MustCompile(`\s+`) 528 str := space.ReplaceAllString(buff.String(), " ") 529 actual := strings.TrimSpace(str) 530 So(actual, ShouldContainSubstring, "repo base linux/amd64 df554ddd false 699B") 531 args = []string{"derived", "repo:base", "--config", "imagetest"} 532 533 cmd = NewImageCommand(NewSearchService()) 534 buff = bytes.NewBufferString("") 535 cmd.SetOut(buff) 536 cmd.SetErr(buff) 537 cmd.SetArgs(args) 538 err = cmd.Execute() 539 So(err, ShouldBeNil) 540 str = space.ReplaceAllString(buff.String(), " ") 541 actual = strings.TrimSpace(str) 542 So(actual, ShouldContainSubstring, "repo derived linux/amd64 79f4b82e false 854B") 543 }) 544 545 Convey("base and derived command errors", func() { 546 // too many parameters 547 buff := bytes.NewBufferString("") 548 args := []string{"too", "many", "args", "--config", "imagetest"} 549 cmd := NewImageBaseCommand(NewSearchService()) 550 cmd.SetOut(buff) 551 cmd.SetErr(buff) 552 cmd.SetArgs(args) 553 err := cmd.Execute() 554 So(err, ShouldNotBeNil) 555 556 cmd = NewImageDerivedCommand(NewSearchService()) 557 cmd.SetOut(buff) 558 cmd.SetErr(buff) 559 cmd.SetArgs(args) 560 err = cmd.Execute() 561 So(err, ShouldNotBeNil) 562 563 // bad input 564 buff = bytes.NewBufferString("") 565 args = []string{"only-repo"} 566 cmd = NewImageBaseCommand(NewSearchService()) 567 cmd.SetOut(buff) 568 cmd.SetErr(buff) 569 cmd.SetArgs(args) 570 err = cmd.Execute() 571 So(err, ShouldNotBeNil) 572 573 cmd = NewImageDerivedCommand(NewSearchService()) 574 cmd.SetOut(buff) 575 cmd.SetErr(buff) 576 cmd.SetArgs(args) 577 err = cmd.Execute() 578 So(err, ShouldNotBeNil) 579 580 // no url 581 buff = bytes.NewBufferString("") 582 args = []string{"repo:tag"} 583 cmd = NewImageBaseCommand(NewSearchService()) 584 cmd.SetOut(buff) 585 cmd.SetErr(buff) 586 cmd.SetArgs(args) 587 err = cmd.Execute() 588 So(err, ShouldNotBeNil) 589 590 cmd = NewImageDerivedCommand(NewSearchService()) 591 cmd.SetOut(buff) 592 cmd.SetErr(buff) 593 cmd.SetArgs(args) 594 err = cmd.Execute() 595 So(err, ShouldNotBeNil) 596 }) 597 598 Convey("digest command", func() { 599 image := CreateImageWith().RandomLayers(1, 10).DefaultConfig().Build() 600 601 err := UploadImage(image, baseURL, "repo", "img") 602 So(err, ShouldBeNil) 603 604 configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, 605 baseURL)) 606 defer os.Remove(configPath) 607 args := []string{"digest", image.DigestStr(), "--config", "imagetest"} 608 609 cmd := NewImageCommand(NewSearchService()) 610 buff := bytes.NewBufferString("") 611 cmd.SetOut(buff) 612 cmd.SetErr(buff) 613 cmd.SetArgs(args) 614 err = cmd.Execute() 615 So(err, ShouldBeNil) 616 space := regexp.MustCompile(`\s+`) 617 str := space.ReplaceAllString(buff.String(), " ") 618 actual := strings.TrimSpace(str) 619 So(actual, ShouldContainSubstring, fmt.Sprintf("repo img linux/amd64 %s false 552B", 620 image.DigestStr()[7:7+8])) 621 }) 622 623 Convey("digest command errors", func() { 624 // too many parameters 625 buff := bytes.NewBufferString("") 626 args := []string{"too", "many", "args", "--config", "imagetest"} 627 cmd := NewImageDigestCommand(NewSearchService()) 628 cmd.SetOut(buff) 629 cmd.SetErr(buff) 630 cmd.SetArgs(args) 631 err := cmd.Execute() 632 So(err, ShouldNotBeNil) 633 634 // bad input 635 buff = bytes.NewBufferString("") 636 args = []string{"bad-digest"} 637 cmd = NewImageDigestCommand(NewSearchService()) 638 cmd.SetOut(buff) 639 cmd.SetErr(buff) 640 cmd.SetArgs(args) 641 err = cmd.Execute() 642 So(err, ShouldNotBeNil) 643 644 // no url 645 buff = bytes.NewBufferString("") 646 args = []string{godigest.FromString("str").String()} 647 cmd = NewImageDigestCommand(NewSearchService()) 648 cmd.SetOut(buff) 649 cmd.SetErr(buff) 650 cmd.SetArgs(args) 651 err = cmd.Execute() 652 So(err, ShouldNotBeNil) 653 }) 654 655 Convey("list command", func() { 656 image := CreateImageWith().RandomLayers(1, 10).DefaultConfig().Build() 657 658 err := UploadImage(image, baseURL, "repo", "img") 659 So(err, ShouldBeNil) 660 661 configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, 662 baseURL)) 663 defer os.Remove(configPath) 664 args := []string{"list", "--config", "imagetest"} 665 666 cmd := NewImageCommand(NewSearchService()) 667 buff := bytes.NewBufferString("") 668 cmd.SetOut(buff) 669 cmd.SetErr(buff) 670 cmd.SetArgs(args) 671 err = cmd.Execute() 672 So(err, ShouldBeNil) 673 space := regexp.MustCompile(`\s+`) 674 str := space.ReplaceAllString(buff.String(), " ") 675 actual := strings.TrimSpace(str) 676 fmt.Println(actual) 677 So(actual, ShouldContainSubstring, fmt.Sprintf("repo img linux/amd64 %s false 552B", 678 image.DigestStr()[7:7+8])) 679 fmt.Println(actual) 680 }) 681 682 Convey("list command errors", func() { 683 // too many parameters 684 buff := bytes.NewBufferString("") 685 args := []string{"repo:img", "arg", "--config", "imagetest"} 686 cmd := NewImageListCommand(NewSearchService()) 687 cmd.SetOut(buff) 688 cmd.SetErr(buff) 689 cmd.SetArgs(args) 690 err := cmd.Execute() 691 So(err, ShouldNotBeNil) 692 693 // no url 694 buff = bytes.NewBufferString("") 695 args = []string{} 696 cmd = NewImageListCommand(NewSearchService()) 697 cmd.SetOut(buff) 698 cmd.SetErr(buff) 699 cmd.SetArgs(args) 700 err = cmd.Execute() 701 So(err, ShouldNotBeNil) 702 }) 703 704 Convey("name command", func() { 705 image := CreateImageWith().RandomLayers(1, 10).DefaultConfig().Build() 706 707 err := UploadImage(image, baseURL, "repo", "img") 708 So(err, ShouldBeNil) 709 710 err = UploadImage(CreateRandomImage(), baseURL, "repo", "img2") 711 So(err, ShouldBeNil) 712 713 configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, 714 baseURL)) 715 defer os.Remove(configPath) 716 args := []string{"name", "repo:img", "--config", "imagetest"} 717 718 cmd := NewImageCommand(NewSearchService()) 719 buff := bytes.NewBufferString("") 720 cmd.SetOut(buff) 721 cmd.SetErr(buff) 722 cmd.SetArgs(args) 723 err = cmd.Execute() 724 So(err, ShouldBeNil) 725 space := regexp.MustCompile(`\s+`) 726 str := space.ReplaceAllString(buff.String(), " ") 727 actual := strings.TrimSpace(str) 728 fmt.Println(actual) 729 So(actual, ShouldContainSubstring, fmt.Sprintf("repo img linux/amd64 %s false 552B", 730 image.DigestStr()[7:7+8])) 731 fmt.Println(actual) 732 }) 733 734 Convey("name command errors", func() { 735 // too many parameters 736 buff := bytes.NewBufferString("") 737 args := []string{"repo:img", "arg", "--config", "imagetest"} 738 cmd := NewImageNameCommand(NewSearchService()) 739 cmd.SetOut(buff) 740 cmd.SetErr(buff) 741 cmd.SetArgs(args) 742 err := cmd.Execute() 743 So(err, ShouldNotBeNil) 744 745 // bad input 746 buff = bytes.NewBufferString("") 747 args = []string{":tag"} 748 cmd = NewImageNameCommand(NewSearchService()) 749 cmd.SetOut(buff) 750 cmd.SetErr(buff) 751 cmd.SetArgs(args) 752 err = cmd.Execute() 753 So(err, ShouldNotBeNil) 754 755 // no url 756 buff = bytes.NewBufferString("") 757 args = []string{"repo:tag"} 758 cmd = NewImageNameCommand(NewSearchService()) 759 cmd.SetOut(buff) 760 cmd.SetErr(buff) 761 cmd.SetArgs(args) 762 err = cmd.Execute() 763 So(err, ShouldNotBeNil) 764 }) 765 766 Convey("CVE", func() { 767 vulnImage := CreateDefaultVulnerableImage() 768 err := UploadImage(vulnImage, baseURL, "repo", "vuln") 769 So(err, ShouldBeNil) 770 771 configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, 772 baseURL)) 773 defer os.Remove(configPath) 774 775 args := []string{"cve", "repo:vuln", "--config", "imagetest"} 776 cmd := NewImageCommand(mockService{}) 777 buff := bytes.NewBufferString("") 778 cmd.SetOut(buff) 779 cmd.SetErr(buff) 780 cmd.SetArgs(args) 781 err = cmd.Execute() 782 So(err, ShouldBeNil) 783 space := regexp.MustCompile(`\s+`) 784 str := space.ReplaceAllString(buff.String(), " ") 785 actual := strings.TrimSpace(str) 786 So(actual, ShouldContainSubstring, "dummyCVEID HIGH Title of that CVE") 787 }) 788 789 Convey("CVE errors", func() { 790 count := 0 791 configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, 792 baseURL)) 793 defer os.Remove(configPath) 794 args := []string{"cve", "repo:vuln", "--config", "imagetest"} 795 cmd := NewImageCommand(mockService{ 796 getCveByImageGQLFn: func(ctx context.Context, config SearchConfig, username, password, 797 imageName, searchedCVE string) (*cveResult, error, 798 ) { 799 if count == 0 { 800 count++ 801 fmt.Println("Count:", count) 802 803 return &cveResult{}, zerr.ErrCVEDBNotFound 804 } 805 806 return &cveResult{}, zerr.ErrInjected 807 }, 808 }) 809 buff := bytes.NewBufferString("") 810 cmd.SetOut(buff) 811 cmd.SetErr(buff) 812 cmd.SetArgs(args) 813 err = cmd.Execute() 814 So(err, ShouldNotBeNil) 815 space := regexp.MustCompile(`\s+`) 816 str := space.ReplaceAllString(buff.String(), " ") 817 actual := strings.TrimSpace(str) 818 So(actual, ShouldContainSubstring, "[warning] CVE DB is not ready") 819 }) 820 }) 821 822 Convey("Config error", t, func() { 823 args := []string{"base", "repo:derived", "--config", "imagetest"} 824 cmd := NewImageCommand(NewSearchService()) 825 buff := bytes.NewBufferString("") 826 cmd.SetOut(buff) 827 cmd.SetErr(buff) 828 cmd.SetArgs(args) 829 err := cmd.Execute() 830 So(err, ShouldNotBeNil) 831 So(err, ShouldNotBeNil) 832 833 args = []string{"derived", "repo:base"} 834 cmd = NewImageCommand(NewSearchService()) 835 buff = bytes.NewBufferString("") 836 cmd.SetOut(buff) 837 cmd.SetErr(buff) 838 cmd.SetArgs(args) 839 err = cmd.Execute() 840 So(err, ShouldNotBeNil) 841 842 args = []string{"digest", ispec.DescriptorEmptyJSON.Digest.String()} 843 cmd = NewImageCommand(NewSearchService()) 844 buff = bytes.NewBufferString("") 845 cmd.SetOut(buff) 846 cmd.SetErr(buff) 847 cmd.SetArgs(args) 848 err = cmd.Execute() 849 So(err, ShouldNotBeNil) 850 851 args = []string{"list"} 852 cmd = NewImageCommand(NewSearchService()) 853 buff = bytes.NewBufferString("") 854 cmd.SetOut(buff) 855 cmd.SetErr(buff) 856 cmd.SetArgs(args) 857 err = cmd.Execute() 858 So(err, ShouldNotBeNil) 859 860 args = []string{"name", "repo:img"} 861 cmd = NewImageCommand(NewSearchService()) 862 buff = bytes.NewBufferString("") 863 cmd.SetOut(buff) 864 cmd.SetErr(buff) 865 cmd.SetArgs(args) 866 err = cmd.Execute() 867 So(err, ShouldNotBeNil) 868 869 args = []string{"cve", "repo:vuln"} 870 cmd = NewImageCommand(mockService{}) 871 buff = bytes.NewBufferString("") 872 cmd.SetOut(buff) 873 cmd.SetErr(buff) 874 cmd.SetArgs(args) 875 err = cmd.Execute() 876 So(err, ShouldNotBeNil) 877 }) 878 } 879 880 func TestImageCommandREST(t *testing.T) { 881 port := test.GetFreePort() 882 baseURL := test.GetBaseURL(port) 883 conf := config.New() 884 conf.HTTP.Port = port 885 886 ctlr := api.NewController(conf) 887 ctlr.Config.Storage.RootDirectory = t.TempDir() 888 cm := test.NewControllerManager(ctlr) 889 890 cm.StartAndWait(conf.HTTP.Port) 891 defer cm.StopServer() 892 893 Convey("commands without gql", t, func() { 894 err := removeLocalStorageContents(ctlr.StoreController.DefaultStore) 895 So(err, ShouldBeNil) 896 897 Convey("base and derived command", func() { 898 baseImage := CreateImageWith().LayerBlobs( 899 [][]byte{{1, 2, 3}, {11, 22, 33}}, 900 ).DefaultConfig().Build() 901 902 derivedImage := CreateImageWith().LayerBlobs( 903 [][]byte{{1, 2, 3}, {11, 22, 33}, {44, 55, 66}}, 904 ).DefaultConfig().Build() 905 906 err := UploadImage(baseImage, baseURL, "repo", "base") 907 So(err, ShouldBeNil) 908 909 err = UploadImage(derivedImage, baseURL, "repo", "derived") 910 So(err, ShouldBeNil) 911 912 configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, 913 baseURL)) 914 defer os.Remove(configPath) 915 916 args := []string{"base", "repo:derived", "--config", "imagetest"} 917 cmd := NewImageCommand(NewSearchService()) 918 buff := bytes.NewBufferString("") 919 cmd.SetOut(buff) 920 cmd.SetErr(buff) 921 cmd.SetArgs(args) 922 err = cmd.Execute() 923 So(err, ShouldNotBeNil) 924 925 args = []string{"derived", "repo:base"} 926 cmd = NewImageCommand(NewSearchService()) 927 buff = bytes.NewBufferString("") 928 cmd.SetOut(buff) 929 cmd.SetErr(buff) 930 cmd.SetArgs(args) 931 err = cmd.Execute() 932 So(err, ShouldNotBeNil) 933 }) 934 935 Convey("digest command", func() { 936 image := CreateRandomImage() 937 938 err := UploadImage(image, baseURL, "repo", "img") 939 So(err, ShouldBeNil) 940 941 configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, 942 baseURL)) 943 defer os.Remove(configPath) 944 945 args := []string{"digest", image.DigestStr(), "--config", "imagetest"} 946 cmd := NewImageCommand(NewSearchService()) 947 buff := bytes.NewBufferString("") 948 cmd.SetOut(buff) 949 cmd.SetErr(buff) 950 cmd.SetArgs(args) 951 err = cmd.Execute() 952 So(err, ShouldNotBeNil) 953 }) 954 955 Convey("list command", func() { 956 image := CreateRandomImage() 957 958 err := UploadImage(image, baseURL, "repo", "img") 959 So(err, ShouldBeNil) 960 961 configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, 962 baseURL)) 963 defer os.Remove(configPath) 964 965 args := []string{"list", "--config", "imagetest"} 966 cmd := NewImageCommand(NewSearchService()) 967 buff := bytes.NewBufferString("") 968 cmd.SetOut(buff) 969 cmd.SetErr(buff) 970 cmd.SetArgs(args) 971 err = cmd.Execute() 972 So(err, ShouldBeNil) 973 fmt.Println(buff.String()) 974 fmt.Println() 975 }) 976 977 Convey("name command", func() { 978 image := CreateRandomImage() 979 980 err := UploadImage(image, baseURL, "repo", "img") 981 So(err, ShouldBeNil) 982 983 err = UploadImage(CreateRandomImage(), baseURL, "repo", "img2") 984 So(err, ShouldBeNil) 985 986 configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, 987 baseURL)) 988 defer os.Remove(configPath) 989 990 args := []string{"name", "repo:img", "--config", "imagetest"} 991 cmd := NewImageCommand(NewSearchService()) 992 buff := bytes.NewBufferString("") 993 cmd.SetOut(buff) 994 cmd.SetErr(buff) 995 cmd.SetArgs(args) 996 err = cmd.Execute() 997 So(err, ShouldBeNil) 998 fmt.Println(buff.String()) 999 fmt.Println() 1000 }) 1001 1002 Convey("CVE", func() { 1003 vulnImage := CreateDefaultVulnerableImage() 1004 err := UploadImage(vulnImage, baseURL, "repo", "vuln") 1005 So(err, ShouldBeNil) 1006 1007 configPath := makeConfigFile(fmt.Sprintf(`{"configs":[{"_name":"imagetest","url":"%s","showspinner":false}]}`, 1008 baseURL)) 1009 args := []string{"cve", "repo:vuln", "--config", "imagetest"} 1010 defer os.Remove(configPath) 1011 cmd := NewImageCommand(mockService{}) 1012 buff := bytes.NewBufferString("") 1013 cmd.SetOut(buff) 1014 cmd.SetErr(buff) 1015 cmd.SetArgs(args) 1016 err = cmd.Execute() 1017 So(err, ShouldNotBeNil) 1018 }) 1019 }) 1020 } 1021 1022 type mockService struct { 1023 getAllImagesFn func(ctx context.Context, config SearchConfig, username, password string, 1024 channel chan stringResult, wtgrp *sync.WaitGroup) 1025 1026 getImagesGQLFn func(ctx context.Context, config SearchConfig, username, password string, 1027 imageName string) (*common.ImageListResponse, error) 1028 1029 getImageByNameFn func(ctx context.Context, config SearchConfig, 1030 username, password, imageName string, channel chan stringResult, wtgrp *sync.WaitGroup, 1031 ) 1032 1033 getImagesByDigestFn func(ctx context.Context, config SearchConfig, username, 1034 password, digest string, rch chan stringResult, wtgrp *sync.WaitGroup, 1035 ) 1036 1037 getReferrersFn func(ctx context.Context, config SearchConfig, username, password string, 1038 repo, digest string, 1039 ) (referrersResult, error) 1040 1041 globalSearchGQLFn func(ctx context.Context, config SearchConfig, username, password string, 1042 query string, 1043 ) (*common.GlobalSearch, error) 1044 1045 getReferrersGQLFn func(ctx context.Context, config SearchConfig, username, password string, 1046 repo, digest string, 1047 ) (*common.ReferrersResp, error) 1048 1049 getDerivedImageListGQLFn func(ctx context.Context, config SearchConfig, username, password string, 1050 derivedImage string, 1051 ) (*common.DerivedImageListResponse, error) 1052 1053 getBaseImageListGQLFn func(ctx context.Context, config SearchConfig, username, password string, 1054 derivedImage string, 1055 ) (*common.BaseImageListResponse, error) 1056 1057 getImagesForDigestGQLFn func(ctx context.Context, config SearchConfig, username, password string, 1058 digest string, 1059 ) (*common.ImagesForDigest, error) 1060 1061 getCveByImageGQLFn func(ctx context.Context, config SearchConfig, username, password, 1062 imageName, searchedCVE string, 1063 ) (*cveResult, error) 1064 1065 getTagsForCVEGQLFn func(ctx context.Context, config SearchConfig, username, password, 1066 imageName, cveID string, 1067 ) (*common.ImagesForCve, error) 1068 1069 getFixedTagsForCVEGQLFn func(ctx context.Context, config SearchConfig, username, password, 1070 imageName, cveID string, 1071 ) (*common.ImageListWithCVEFixedResponse, error) 1072 } 1073 1074 func (service mockService) getRepos(ctx context.Context, config SearchConfig, username, 1075 password string, channel chan stringResult, wtgrp *sync.WaitGroup, 1076 ) { 1077 defer wtgrp.Done() 1078 defer close(channel) 1079 1080 fmt.Fprintln(config.ResultWriter, "\n\nREPOSITORY NAME") 1081 1082 fmt.Fprintln(config.ResultWriter, "repo1") 1083 fmt.Fprintln(config.ResultWriter, "repo2") 1084 } 1085 1086 func (service mockService) getReferrers(ctx context.Context, config SearchConfig, username, password string, 1087 repo, digest string, 1088 ) (referrersResult, error) { 1089 if service.getReferrersFn != nil { 1090 return service.getReferrersFn(ctx, config, username, password, repo, digest) 1091 } 1092 1093 return referrersResult{ 1094 common.Referrer{ 1095 ArtifactType: "art.type", 1096 Digest: ispec.DescriptorEmptyJSON.Digest.String(), 1097 MediaType: ispec.MediaTypeImageManifest, 1098 Size: 100, 1099 }, 1100 }, nil 1101 } 1102 1103 func (service mockService) globalSearchGQL(ctx context.Context, config SearchConfig, username, password string, 1104 query string, 1105 ) (*common.GlobalSearch, error) { 1106 if service.globalSearchGQLFn != nil { 1107 return service.globalSearchGQLFn(ctx, config, username, password, query) 1108 } 1109 1110 return &common.GlobalSearch{ 1111 Images: []common.ImageSummary{ 1112 { 1113 RepoName: "repo", 1114 MediaType: ispec.MediaTypeImageManifest, 1115 Size: "100", 1116 Manifests: []common.ManifestSummary{ 1117 { 1118 Digest: godigest.FromString("str").String(), 1119 Size: "100", 1120 ConfigDigest: ispec.DescriptorEmptyJSON.Digest.String(), 1121 }, 1122 }, 1123 }, 1124 }, 1125 Repos: []common.RepoSummary{ 1126 { 1127 Name: "repo", 1128 Size: "100", 1129 LastUpdated: time.Date(2010, 1, 1, 1, 1, 1, 0, time.UTC), 1130 }, 1131 }, 1132 }, nil 1133 } 1134 1135 func (service mockService) getReferrersGQL(ctx context.Context, config SearchConfig, username, password string, 1136 repo, digest string, 1137 ) (*common.ReferrersResp, error) { 1138 if service.getReferrersGQLFn != nil { 1139 return service.getReferrersGQLFn(ctx, config, username, password, repo, digest) 1140 } 1141 1142 return &common.ReferrersResp{ 1143 ReferrersResult: common.ReferrersResult{ 1144 Referrers: []common.Referrer{ 1145 { 1146 MediaType: "MediaType", 1147 ArtifactType: "ArtifactType", 1148 Size: 100, 1149 Digest: "Digest", 1150 }, 1151 }, 1152 }, 1153 }, nil 1154 } 1155 1156 func (service mockService) getDerivedImageListGQL(ctx context.Context, config SearchConfig, username, password string, 1157 derivedImage string, 1158 ) (*common.DerivedImageListResponse, error) { 1159 if service.getDerivedImageListGQLFn != nil { 1160 return service.getDerivedImageListGQLFn(ctx, config, username, password, derivedImage) 1161 } 1162 1163 imageListGQLResponse := &common.DerivedImageListResponse{} 1164 imageListGQLResponse.DerivedImageList.Results = []common.ImageSummary{ 1165 { 1166 RepoName: "dummyImageName", 1167 Tag: "tag", 1168 Manifests: []common.ManifestSummary{ 1169 { 1170 Digest: godigest.FromString("Digest").String(), 1171 ConfigDigest: godigest.FromString("ConfigDigest").String(), 1172 Size: "123445", 1173 Layers: []common.LayerSummary{{Digest: godigest.FromString("LayerDigest").String()}}, 1174 Platform: common.Platform{Os: "os", Arch: "arch"}, 1175 }, 1176 }, 1177 Size: "123445", 1178 }, 1179 } 1180 1181 return imageListGQLResponse, nil 1182 } 1183 1184 func (service mockService) getBaseImageListGQL(ctx context.Context, config SearchConfig, username, password string, 1185 baseImage string, 1186 ) (*common.BaseImageListResponse, error) { 1187 if service.getBaseImageListGQLFn != nil { 1188 return service.getBaseImageListGQLFn(ctx, config, username, password, baseImage) 1189 } 1190 1191 imageListGQLResponse := &common.BaseImageListResponse{} 1192 imageListGQLResponse.BaseImageList.Results = []common.ImageSummary{ 1193 { 1194 RepoName: "dummyImageName", 1195 Tag: "tag", 1196 Manifests: []common.ManifestSummary{ 1197 { 1198 Digest: godigest.FromString("Digest").String(), 1199 ConfigDigest: godigest.FromString("ConfigDigest").String(), 1200 Size: "123445", 1201 Layers: []common.LayerSummary{{Digest: godigest.FromString("LayerDigest").String()}}, 1202 Platform: common.Platform{Os: "os", Arch: "arch"}, 1203 }, 1204 }, 1205 Size: "123445", 1206 }, 1207 } 1208 1209 return imageListGQLResponse, nil 1210 } 1211 1212 func (service mockService) getImagesGQL(ctx context.Context, config SearchConfig, username, password string, 1213 imageName string, 1214 ) (*common.ImageListResponse, error) { 1215 if service.getImagesGQLFn != nil { 1216 return service.getImagesGQLFn(ctx, config, username, password, imageName) 1217 } 1218 1219 imageListGQLResponse := &common.ImageListResponse{} 1220 imageListGQLResponse.PaginatedImagesResult.Results = []common.ImageSummary{ 1221 { 1222 RepoName: "dummyImageName", 1223 Tag: "tag", 1224 MediaType: ispec.MediaTypeImageManifest, 1225 Digest: godigest.FromString("test").String(), 1226 Manifests: []common.ManifestSummary{ 1227 { 1228 Digest: godigest.FromString("Digest").String(), 1229 ConfigDigest: godigest.FromString("ConfigDigest").String(), 1230 Size: "123445", 1231 Layers: []common.LayerSummary{{Digest: godigest.FromString("LayerDigest").String()}}, 1232 Platform: common.Platform{Os: "os", Arch: "arch"}, 1233 }, 1234 }, 1235 Size: "123445", 1236 }, 1237 } 1238 1239 return imageListGQLResponse, nil 1240 } 1241 1242 func (service mockService) getImagesForDigestGQL(ctx context.Context, config SearchConfig, username, password string, 1243 digest string, 1244 ) (*common.ImagesForDigest, error) { 1245 if service.getImagesForDigestGQLFn != nil { 1246 return service.getImagesForDigestGQLFn(ctx, config, username, password, digest) 1247 } 1248 1249 imageListGQLResponse := &common.ImagesForDigest{} 1250 imageListGQLResponse.Results = []common.ImageSummary{ 1251 { 1252 RepoName: "randomimageName", 1253 Tag: "tag", 1254 MediaType: ispec.MediaTypeImageManifest, 1255 Digest: godigest.FromString("test").String(), 1256 Manifests: []common.ManifestSummary{ 1257 { 1258 Digest: godigest.FromString("Digest").String(), 1259 ConfigDigest: godigest.FromString("ConfigDigest").String(), 1260 Layers: []common.LayerSummary{{Digest: godigest.FromString("LayerDigest").String()}}, 1261 Size: "123445", 1262 Platform: common.Platform{Os: "os", Arch: "arch"}, 1263 }, 1264 }, 1265 Size: "123445", 1266 }, 1267 } 1268 1269 return imageListGQLResponse, nil 1270 } 1271 1272 func (service mockService) getTagsForCVEGQL(ctx context.Context, config SearchConfig, username, password, 1273 imageName, cveID string, 1274 ) (*common.ImagesForCve, error) { 1275 if service.getTagsForCVEGQLFn != nil { 1276 return service.getTagsForCVEGQLFn(ctx, config, username, password, imageName, cveID) 1277 } 1278 1279 images := &common.ImagesForCve{ 1280 Errors: nil, 1281 ImagesForCVEList: struct { 1282 common.PaginatedImagesResult `json:"ImageListForCVE"` //nolint:tagliatelle // graphQL schema 1283 }{}, 1284 } 1285 1286 if imageName == "" { 1287 imageName = "image-name" 1288 } 1289 1290 images.Errors = nil 1291 1292 mockedImage := service.getMockedImageByName(imageName) 1293 images.Results = []common.ImageSummary{common.ImageSummary(mockedImage)} 1294 1295 return images, nil 1296 } 1297 1298 func (service mockService) getFixedTagsForCVEGQL(ctx context.Context, config SearchConfig, username, password, 1299 imageName, cveID string, 1300 ) (*common.ImageListWithCVEFixedResponse, error) { 1301 if service.getFixedTagsForCVEGQLFn != nil { 1302 return service.getFixedTagsForCVEGQLFn(ctx, config, username, password, imageName, cveID) 1303 } 1304 1305 fixedTags := &common.ImageListWithCVEFixedResponse{ 1306 Errors: nil, 1307 ImageListWithCVEFixed: struct { 1308 common.PaginatedImagesResult `json:"ImageListWithCVEFixed"` //nolint:tagliatelle // graphQL schema 1309 }{}, 1310 } 1311 1312 fixedTags.Errors = nil 1313 1314 mockedImage := service.getMockedImageByName(imageName) 1315 fixedTags.Results = []common.ImageSummary{common.ImageSummary(mockedImage)} 1316 1317 return fixedTags, nil 1318 } 1319 1320 func (service mockService) getCveByImageGQL(ctx context.Context, config SearchConfig, username, password, 1321 imageName, searchedCVE string, 1322 ) (*cveResult, error) { 1323 if service.getCveByImageGQLFn != nil { 1324 return service.getCveByImageGQLFn(ctx, config, username, password, imageName, searchedCVE) 1325 } 1326 cveRes := &cveResult{} 1327 cveRes.Data = cveData{ 1328 CVEListForImage: cveListForImage{ 1329 Tag: imageName, 1330 CVEList: []cve{ 1331 { 1332 ID: "dummyCVEID", 1333 Description: "Description of the CVE", 1334 Title: "Title of that CVE", 1335 Severity: "HIGH", 1336 PackageList: []packageList{ 1337 { 1338 Name: "packagename", 1339 FixedVersion: "fixedver", 1340 InstalledVersion: "installedver", 1341 }, 1342 }, 1343 }, 1344 }, 1345 }, 1346 } 1347 1348 return cveRes, nil 1349 } 1350 1351 //nolint:goconst 1352 func (service mockService) getMockedImageByName(imageName string) imageStruct { 1353 image := imageStruct{} 1354 image.RepoName = imageName 1355 image.Tag = "tag" 1356 image.MediaType = ispec.MediaTypeImageManifest 1357 image.Manifests = []common.ManifestSummary{ 1358 { 1359 Digest: godigest.FromString("Digest").String(), 1360 ConfigDigest: godigest.FromString("ConfigDigest").String(), 1361 Layers: []common.LayerSummary{{Digest: godigest.FromString("LayerDigest").String()}}, 1362 Size: "123445", 1363 Platform: common.Platform{Os: "os", Arch: "arch"}, 1364 }, 1365 } 1366 image.Size = "123445" 1367 1368 return image 1369 } 1370 1371 func (service mockService) getAllImages(ctx context.Context, config SearchConfig, username, password string, 1372 channel chan stringResult, wtgrp *sync.WaitGroup, 1373 ) { 1374 defer wtgrp.Done() 1375 defer close(channel) 1376 1377 if service.getAllImagesFn != nil { 1378 service.getAllImagesFn(ctx, config, username, password, channel, wtgrp) 1379 1380 return 1381 } 1382 1383 image := &imageStruct{} 1384 image.RepoName = "randomimageName" 1385 image.Tag = "tag" 1386 image.Digest = godigest.FromString("test").String() 1387 image.MediaType = ispec.MediaTypeImageManifest 1388 image.Manifests = []common.ManifestSummary{ 1389 { 1390 Digest: godigest.FromString("Digest").String(), 1391 ConfigDigest: godigest.FromString("ConfigDigest").String(), 1392 Layers: []common.LayerSummary{{Digest: godigest.FromString("LayerDigest").String()}}, 1393 Size: "123445", 1394 Platform: common.Platform{Os: "os", Arch: "arch"}, 1395 }, 1396 } 1397 image.Size = "123445" 1398 1399 str, err := image.string(config.OutputFormat, len(image.RepoName), len(image.Tag), len("os/Arch"), config.Verbose) 1400 if err != nil { 1401 channel <- stringResult{"", err} 1402 1403 return 1404 } 1405 1406 channel <- stringResult{str, nil} 1407 } 1408 1409 func (service mockService) getImageByName(ctx context.Context, config SearchConfig, 1410 username, password, imageName string, channel chan stringResult, wtgrp *sync.WaitGroup, 1411 ) { 1412 defer wtgrp.Done() 1413 defer close(channel) 1414 1415 if service.getImageByNameFn != nil { 1416 service.getImageByNameFn(ctx, config, username, password, imageName, channel, wtgrp) 1417 1418 return 1419 } 1420 1421 image := &imageStruct{} 1422 image.RepoName = imageName 1423 image.Tag = "tag" 1424 image.Digest = godigest.FromString("test").String() 1425 image.MediaType = ispec.MediaTypeImageManifest 1426 image.Manifests = []common.ManifestSummary{ 1427 { 1428 Digest: godigest.FromString("Digest").String(), 1429 ConfigDigest: godigest.FromString("ConfigDigest").String(), 1430 Layers: []common.LayerSummary{{Digest: godigest.FromString("LayerDigest").String()}}, 1431 Size: "123445", 1432 Platform: common.Platform{Os: "os", Arch: "arch"}, 1433 }, 1434 } 1435 image.Size = "123445" 1436 1437 str, err := image.string(config.OutputFormat, len(image.RepoName), len(image.Tag), len("os/Arch"), config.Verbose) 1438 if err != nil { 1439 channel <- stringResult{"", err} 1440 1441 return 1442 } 1443 1444 channel <- stringResult{str, nil} 1445 } 1446 1447 func (service mockService) getImagesByDigest(ctx context.Context, config SearchConfig, username, 1448 password, digest string, rch chan stringResult, wtgrp *sync.WaitGroup, 1449 ) { 1450 if service.getImagesByDigestFn != nil { 1451 defer wtgrp.Done() 1452 defer close(rch) 1453 1454 service.getImagesByDigestFn(ctx, config, username, password, digest, rch, wtgrp) 1455 1456 return 1457 } 1458 1459 service.getImageByName(ctx, config, username, password, "anImage", rch, wtgrp) 1460 } 1461 1462 func makeConfigFile(content string) string { 1463 os.Setenv("HOME", os.TempDir()) 1464 1465 home, err := os.UserHomeDir() 1466 if err != nil { 1467 panic(err) 1468 } 1469 1470 configPath := path.Join(home, "/.zot") 1471 1472 if err := os.WriteFile(configPath, []byte(content), 0o600); err != nil { 1473 panic(err) 1474 } 1475 1476 return configPath 1477 } 1478 1479 func getTestSearchConfig(url string, searchService SearchService) SearchConfig { 1480 var ( 1481 user string 1482 outputFormat string 1483 verbose bool 1484 debug bool 1485 verifyTLS bool 1486 ) 1487 1488 return SearchConfig{ 1489 SearchService: searchService, 1490 SortBy: "alpha-asc", 1491 ServURL: url, 1492 User: user, 1493 OutputFormat: outputFormat, 1494 Verbose: verbose, 1495 Debug: debug, 1496 VerifyTLS: verifyTLS, 1497 ResultWriter: nil, 1498 } 1499 } 1500 1501 func removeLocalStorageContents(imageStore stypes.ImageStore) error { 1502 repos, err := imageStore.GetRepositories() 1503 if err != nil { 1504 return err 1505 } 1506 1507 for _, repo := range repos { 1508 // take just the first path 1509 err = os.RemoveAll(filepath.Join(imageStore.RootDir(), filepath.SplitList(repo)[0])) 1510 if err != nil { 1511 return err 1512 } 1513 } 1514 1515 return nil 1516 }