github.com/buildpacks/pack@v0.33.3-0.20240516162812-884dd1837311/pkg/cache/volume_cache_test.go (about)

     1  package cache_test
     2  
     3  import (
     4  	"context"
     5  	"strings"
     6  	"testing"
     7  
     8  	"github.com/buildpacks/pack/pkg/cache"
     9  
    10  	"github.com/docker/docker/api/types/filters"
    11  	"github.com/docker/docker/api/types/volume"
    12  	"github.com/docker/docker/client"
    13  	"github.com/docker/docker/daemon/names"
    14  	"github.com/google/go-containerregistry/pkg/name"
    15  	"github.com/heroku/color"
    16  	"github.com/sclevine/spec"
    17  	"github.com/sclevine/spec/report"
    18  
    19  	h "github.com/buildpacks/pack/testhelpers"
    20  )
    21  
    22  func TestVolumeCache(t *testing.T) {
    23  	h.RequireDocker(t)
    24  	color.Disable(true)
    25  	defer color.Disable(false)
    26  
    27  	spec.Run(t, "VolumeCache", testCache, spec.Parallel(), spec.Report(report.Terminal{}))
    28  }
    29  
    30  func testCache(t *testing.T, when spec.G, it spec.S) {
    31  	var dockerClient client.CommonAPIClient
    32  
    33  	it.Before(func() {
    34  		var err error
    35  		dockerClient, err = client.NewClientWithOpts(client.FromEnv, client.WithVersion("1.38"))
    36  		h.AssertNil(t, err)
    37  	})
    38  	when("#NewVolumeCache", func() {
    39  		when("volume cache name is empty", func() {
    40  			it("adds suffix to calculated name", func() {
    41  				ref, err := name.ParseReference("my/repo", name.WeakValidation)
    42  				h.AssertNil(t, err)
    43  				subject := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient)
    44  				if !strings.HasSuffix(subject.Name(), ".some-suffix") {
    45  					t.Fatalf("Calculated volume name '%s' should end with '.some-suffix'", subject.Name())
    46  				}
    47  			})
    48  
    49  			it("reusing the same cache for the same repo name", func() {
    50  				ref, err := name.ParseReference("my/repo", name.WeakValidation)
    51  				h.AssertNil(t, err)
    52  
    53  				subject := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient)
    54  				expected := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient)
    55  				if subject.Name() != expected.Name() {
    56  					t.Fatalf("The same repo name should result in the same volume")
    57  				}
    58  			})
    59  
    60  			it("supplies different volumes for different tags", func() {
    61  				ref, err := name.ParseReference("my/repo:other-tag", name.WeakValidation)
    62  				h.AssertNil(t, err)
    63  
    64  				subject := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient)
    65  
    66  				ref, err = name.ParseReference("my/repo", name.WeakValidation)
    67  				h.AssertNil(t, err)
    68  				notExpected := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient)
    69  				if subject.Name() == notExpected.Name() {
    70  					t.Fatalf("Different image tags should result in different volumes")
    71  				}
    72  			})
    73  
    74  			it("supplies different volumes for different registries", func() {
    75  				ref, err := name.ParseReference("registry.com/my/repo:other-tag", name.WeakValidation)
    76  				h.AssertNil(t, err)
    77  
    78  				subject := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient)
    79  
    80  				ref, err = name.ParseReference("my/repo", name.WeakValidation)
    81  				h.AssertNil(t, err)
    82  				notExpected := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient)
    83  				if subject.Name() == notExpected.Name() {
    84  					t.Fatalf("Different image registries should result in different volumes")
    85  				}
    86  			})
    87  
    88  			it("resolves implied tag", func() {
    89  				ref, err := name.ParseReference("my/repo:latest", name.WeakValidation)
    90  				h.AssertNil(t, err)
    91  
    92  				subject := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient)
    93  
    94  				ref, err = name.ParseReference("my/repo", name.WeakValidation)
    95  				h.AssertNil(t, err)
    96  				expected := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient)
    97  				h.AssertEq(t, subject.Name(), expected.Name())
    98  			})
    99  
   100  			it("resolves implied registry", func() {
   101  				ref, err := name.ParseReference("index.docker.io/my/repo", name.WeakValidation)
   102  				h.AssertNil(t, err)
   103  
   104  				subject := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient)
   105  
   106  				ref, err = name.ParseReference("my/repo", name.WeakValidation)
   107  				h.AssertNil(t, err)
   108  				expected := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient)
   109  				h.AssertEq(t, subject.Name(), expected.Name())
   110  			})
   111  
   112  			it("includes human readable information", func() {
   113  				ref, err := name.ParseReference("myregistryhost:5000/fedora/httpd:version1.0", name.WeakValidation)
   114  				h.AssertNil(t, err)
   115  
   116  				subject := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient)
   117  
   118  				h.AssertContains(t, subject.Name(), "fedora_httpd_version1.0")
   119  				h.AssertTrue(t, names.RestrictedNamePattern.MatchString(subject.Name()))
   120  			})
   121  		})
   122  
   123  		when("volume cache name is not empty", func() {
   124  			volumeName := "test-volume-name"
   125  			cacheInfo := cache.CacheInfo{
   126  				Format: cache.CacheVolume,
   127  				Source: volumeName,
   128  			}
   129  
   130  			it("named volume created without suffix", func() {
   131  				ref, err := name.ParseReference("my/repo", name.WeakValidation)
   132  				h.AssertNil(t, err)
   133  
   134  				subject := cache.NewVolumeCache(ref, cacheInfo, "some-suffix", dockerClient)
   135  
   136  				if volumeName != subject.Name() {
   137  					t.Fatalf("Volume name '%s' should be same as the name specified '%s'", subject.Name(), volumeName)
   138  				}
   139  			})
   140  
   141  			it("reusing the same cache for the same repo name", func() {
   142  				ref, err := name.ParseReference("my/repo", name.WeakValidation)
   143  				h.AssertNil(t, err)
   144  
   145  				subject := cache.NewVolumeCache(ref, cacheInfo, "some-suffix", dockerClient)
   146  
   147  				expected := cache.NewVolumeCache(ref, cacheInfo, "some-suffix", dockerClient)
   148  				if subject.Name() != expected.Name() {
   149  					t.Fatalf("The same repo name should result in the same volume")
   150  				}
   151  			})
   152  
   153  			it("supplies different volumes for different registries", func() {
   154  				ref, err := name.ParseReference("registry.com/my/repo:other-tag", name.WeakValidation)
   155  				h.AssertNil(t, err)
   156  
   157  				subject := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient)
   158  
   159  				ref, err = name.ParseReference("my/repo", name.WeakValidation)
   160  				h.AssertNil(t, err)
   161  				notExpected := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient)
   162  				if subject.Name() == notExpected.Name() {
   163  					t.Fatalf("Different image registries should result in different volumes")
   164  				}
   165  			})
   166  
   167  			it("resolves implied tag", func() {
   168  				ref, err := name.ParseReference("my/repo:latest", name.WeakValidation)
   169  				h.AssertNil(t, err)
   170  
   171  				subject := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient)
   172  
   173  				ref, err = name.ParseReference("my/repo", name.WeakValidation)
   174  				h.AssertNil(t, err)
   175  				expected := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient)
   176  				h.AssertEq(t, subject.Name(), expected.Name())
   177  			})
   178  
   179  			it("resolves implied registry", func() {
   180  				ref, err := name.ParseReference("index.docker.io/my/repo", name.WeakValidation)
   181  				h.AssertNil(t, err)
   182  
   183  				subject := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient)
   184  
   185  				ref, err = name.ParseReference("my/repo", name.WeakValidation)
   186  				h.AssertNil(t, err)
   187  				expected := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient)
   188  				h.AssertEq(t, subject.Name(), expected.Name())
   189  			})
   190  
   191  			it("includes human readable information", func() {
   192  				ref, err := name.ParseReference("myregistryhost:5000/fedora/httpd:version1.0", name.WeakValidation)
   193  				h.AssertNil(t, err)
   194  
   195  				subject := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient)
   196  
   197  				h.AssertContains(t, subject.Name(), "fedora_httpd_version1.0")
   198  				h.AssertTrue(t, names.RestrictedNamePattern.MatchString(subject.Name()))
   199  			})
   200  		})
   201  	})
   202  
   203  	when("#Clear", func() {
   204  		var (
   205  			volumeName   string
   206  			dockerClient client.CommonAPIClient
   207  			subject      *cache.VolumeCache
   208  			ctx          context.Context
   209  		)
   210  
   211  		it.Before(func() {
   212  			var err error
   213  			dockerClient, err = client.NewClientWithOpts(client.FromEnv, client.WithVersion("1.38"))
   214  			h.AssertNil(t, err)
   215  			ctx = context.TODO()
   216  
   217  			ref, err := name.ParseReference(h.RandString(10), name.WeakValidation)
   218  			h.AssertNil(t, err)
   219  
   220  			subject = cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient)
   221  			volumeName = subject.Name()
   222  		})
   223  
   224  		when("there is a cache volume", func() {
   225  			it.Before(func() {
   226  				dockerClient.VolumeCreate(context.TODO(), volume.CreateOptions{
   227  					Name: volumeName,
   228  				})
   229  			})
   230  
   231  			it("removes the volume", func() {
   232  				err := subject.Clear(ctx)
   233  				h.AssertNil(t, err)
   234  
   235  				volumes, err := dockerClient.VolumeList(context.TODO(), volume.ListOptions{
   236  					Filters: filters.NewArgs(filters.KeyValuePair{
   237  						Key:   "name",
   238  						Value: volumeName,
   239  					}),
   240  				})
   241  				h.AssertNil(t, err)
   242  				h.AssertEq(t, len(volumes.Volumes), 0)
   243  			})
   244  		})
   245  
   246  		when("there is no cache volume", func() {
   247  			it("does not fail", func() {
   248  				err := subject.Clear(ctx)
   249  				h.AssertNil(t, err)
   250  			})
   251  		})
   252  	})
   253  
   254  	when("#Type", func() {
   255  		it("returns the cache type", func() {
   256  			ref, err := name.ParseReference("my/repo", name.WeakValidation)
   257  			h.AssertNil(t, err)
   258  			subject := cache.NewVolumeCache(ref, cache.CacheInfo{}, "some-suffix", dockerClient)
   259  			expected := cache.Volume
   260  			h.AssertEq(t, subject.Type(), expected)
   261  		})
   262  	})
   263  }