diff --git a/buildpack_store.go b/buildpack_store.go index 12c97b8..95602c6 100644 --- a/buildpack_store.go +++ b/buildpack_store.go @@ -32,6 +32,11 @@ type CacheManager interface { Close() error } +//go:generate faux --interface BPStoreDockerPull --output fakes/docker_pull.go +type BPStoreDockerPull interface { + Execute(string) error +} + type BuildpackStore struct { Get BuildpackStoreGet } @@ -71,6 +76,11 @@ func (bs BuildpackStore) WithRemoteFetcher(fetcher RemoteFetcher) BuildpackStore return bs } +func (bs BuildpackStore) WithOCIFetcher(fetcher BPStoreDockerPull) BuildpackStore { + bs.Get.oci = fetcher + return bs +} + func (bs BuildpackStore) WithCacheManager(manager CacheManager) BuildpackStore { bs.Get.cacheManager = manager return bs @@ -86,12 +96,21 @@ type BuildpackStoreGet struct { cacheManager CacheManager local LocalFetcher remote RemoteFetcher + oci BPStoreDockerPull offline bool version string } func (g BuildpackStoreGet) Execute(url string) (string, error) { + if strings.HasPrefix(url, "docker://") { + if g.oci == nil { + return "", fmt.Errorf("must provide OCI fetcher to fetch OCI images") + } + url := strings.TrimPrefix(url, "docker://") + return url, g.oci.Execute(url) + } + err := g.cacheManager.Open() if err != nil { return "", fmt.Errorf("failed to open cacheManager: %s", err) diff --git a/buildpack_store_test.go b/buildpack_store_test.go index a04d4d1..750f61f 100644 --- a/buildpack_store_test.go +++ b/buildpack_store_test.go @@ -21,21 +21,79 @@ func testBuildpackStore(t *testing.T, when spec.G, it spec.S) { fakeRemoteFetcher *fakes.RemoteFetcher fakeLocalFetcher *fakes.LocalFetcher fakeCacheManager *fakes.CacheManager + fakeDockerPull *fakes.BPStoreDockerPull ) it.Before(func() { fakeRemoteFetcher = &fakes.RemoteFetcher{} fakeLocalFetcher = &fakes.LocalFetcher{} fakeCacheManager = &fakes.CacheManager{} + fakeDockerPull = &fakes.BPStoreDockerPull{} buildpackStore = occam.NewBuildpackStore() buildpackStore = buildpackStore.WithLocalFetcher(fakeLocalFetcher). WithRemoteFetcher(fakeRemoteFetcher). - WithCacheManager(fakeCacheManager) + WithCacheManager(fakeCacheManager). + WithOCIFetcher(fakeDockerPull) }) when("getting an online buildpack", func() { + when("from a docker uri", func() { + it("returns the URI to the OCI image, stripping the docker:// protocol", func() { + local_url, err := buildpackStore.Get. + Execute("docker://some-image:tag") + Expect(err).NotTo(HaveOccurred()) + + Expect(local_url).To(Equal("some-image:tag")) + + Expect(fakeDockerPull.ExecuteCall.CallCount).To(Equal(1)) + Expect(fakeDockerPull.ExecuteCall.Receives.String).To(Equal("some-image:tag")) + + Expect(fakeCacheManager.OpenCall.CallCount).To(Equal(0)) + Expect(fakeCacheManager.CloseCall.CallCount).To(Equal(0)) + + Expect(fakeRemoteFetcher.GetCall.CallCount).To(Equal(0)) + Expect(fakeLocalFetcher.GetCall.CallCount).To(Equal(0)) + }) + + it("ignores the online/offline flags", func() { + local_url, err := buildpackStore.Get. + WithOfflineDependencies(). + Execute("docker://some-image:tag") + Expect(err).NotTo(HaveOccurred()) + + Expect(local_url).To(Equal("some-image:tag")) + + Expect(fakeDockerPull.ExecuteCall.CallCount).To(Equal(1)) + Expect(fakeDockerPull.ExecuteCall.Receives.String).To(Equal("some-image:tag")) + + Expect(fakeCacheManager.OpenCall.CallCount).To(Equal(0)) + Expect(fakeCacheManager.CloseCall.CallCount).To(Equal(0)) + + Expect(fakeRemoteFetcher.GetCall.CallCount).To(Equal(0)) + Expect(fakeLocalFetcher.GetCall.CallCount).To(Equal(0)) + }) + + it("ignores the version flag", func() { + local_url, err := buildpackStore.Get. + WithVersion("some-version"). + Execute("docker://some-image:tag") + Expect(err).NotTo(HaveOccurred()) + + Expect(local_url).To(Equal("some-image:tag")) + + Expect(fakeDockerPull.ExecuteCall.CallCount).To(Equal(1)) + Expect(fakeDockerPull.ExecuteCall.Receives.String).To(Equal("some-image:tag")) + + Expect(fakeCacheManager.OpenCall.CallCount).To(Equal(0)) + Expect(fakeCacheManager.CloseCall.CallCount).To(Equal(0)) + + Expect(fakeRemoteFetcher.GetCall.CallCount).To(Equal(0)) + Expect(fakeLocalFetcher.GetCall.CallCount).To(Equal(0)) + }) + }) + when("from a local uri", func() { it.Before(func() { fakeLocalFetcher.GetCall.Returns.String = "/path/to/cool-buildpack/" @@ -151,6 +209,18 @@ func testBuildpackStore(t *testing.T, when spec.G, it spec.S) { }) when("failure cases", func() { + when("attempting to fetch OCI image without OCI fetcher", func() { + it.Before(func() { + buildpackStore = occam.NewBuildpackStore() + }) + + it("returns an error", func() { + _, err := buildpackStore.Get.Execute("docker://some-image:tag") + + Expect(err).To(MatchError("must provide OCI fetcher to fetch OCI images")) + }) + }) + when("unable to open cacheManager", func() { it.Before(func() { fakeCacheManager.OpenCall.Returns.Error = errors.New("bad bad error") diff --git a/fakes/docker_pull.go b/fakes/docker_pull.go new file mode 100644 index 0000000..c9fb965 --- /dev/null +++ b/fakes/docker_pull.go @@ -0,0 +1,28 @@ +package fakes + +import "sync" + +type BPStoreDockerPull struct { + ExecuteCall struct { + mutex sync.Mutex + CallCount int + Receives struct { + String string + } + Returns struct { + Error error + } + Stub func(string) error + } +} + +func (f *BPStoreDockerPull) Execute(param1 string) error { + f.ExecuteCall.mutex.Lock() + defer f.ExecuteCall.mutex.Unlock() + f.ExecuteCall.CallCount++ + f.ExecuteCall.Receives.String = param1 + if f.ExecuteCall.Stub != nil { + return f.ExecuteCall.Stub(param1) + } + return f.ExecuteCall.Returns.Error +}