diff --git a/pkg/cnab/cnab-to-oci/helpers.go b/pkg/cnab/cnab-to-oci/helpers.go index 41769186c..0278df52d 100644 --- a/pkg/cnab/cnab-to-oci/helpers.go +++ b/pkg/cnab/cnab-to-oci/helpers.go @@ -11,6 +11,7 @@ type TestRegistry struct { MockPullBundle func(ref cnab.OCIReference, insecureRegistry bool) (cnab.BundleReference, error) MockPushBundle func(bundleRef cnab.BundleReference, insecureRegistry bool) (bundleReference cnab.BundleReference, err error) MockPushInvocationImage func(invocationImage string) (imageDigest digest.Digest, err error) + MockIsImageCached func(invocationImage string) (bool, error) } func NewTestRegistry() *TestRegistry { @@ -39,3 +40,11 @@ func (t TestRegistry) PushInvocationImage(invocationImage string) (digest.Digest } return "", nil } + +func (t TestRegistry) IsImageCached(invocationImage string) (bool, error) { + if t.MockIsImageCached != nil { + return t.MockIsImageCached(invocationImage) + } + + return true, nil +} diff --git a/pkg/cnab/cnab-to-oci/provider.go b/pkg/cnab/cnab-to-oci/provider.go index bca54972b..839fb1c77 100644 --- a/pkg/cnab/cnab-to-oci/provider.go +++ b/pkg/cnab/cnab-to-oci/provider.go @@ -17,4 +17,7 @@ type RegistryProvider interface { // the expected format of the invocationImage is REGISTRY/NAME:TAG. // Returns the image digest from the registry. PushInvocationImage(invocationImage string) (digest.Digest, error) + + // IsImageCached checks whether a particular invocation image exists in the local image cache. + IsImageCached(invocationImage string) (bool, error) } diff --git a/pkg/cnab/cnab-to-oci/registry.go b/pkg/cnab/cnab-to-oci/registry.go index dd702eb28..885731f41 100644 --- a/pkg/cnab/cnab-to-oci/registry.go +++ b/pkg/cnab/cnab-to-oci/registry.go @@ -13,6 +13,7 @@ import ( dockerconfig "github.com/docker/cli/cli/config" cliflags "github.com/docker/cli/cli/flags" "github.com/docker/docker/api/types" + "github.com/docker/docker/api/types/filters" "github.com/docker/docker/pkg/jsonmessage" "github.com/docker/docker/pkg/term" "github.com/opencontainers/go-digest" @@ -188,3 +189,25 @@ func (r *Registry) getDockerClient() (*command.DockerCli, error) { } return cli, nil } + +func (r *Registry) IsImageCached(invocationImage string) (bool, error) { + ctx := context.Background() + + cli, err := r.getDockerClient() + if err != nil { + return false, err + } + + imageListOpts := types.ImageListOptions{All: true, Filters: filters.NewArgs(filters.KeyValuePair{Key: "reference", Value: invocationImage})} + + imageSummaries, err := cli.Client().ImageList(ctx, imageListOpts) + if err != nil { + return false, errors.Wrapf(err, "could not list images") + } + + if len(imageSummaries) == 0 { + return false, nil + } + + return true, nil +} diff --git a/pkg/porter/stamp.go b/pkg/porter/stamp.go index d1497e049..42aa144ed 100644 --- a/pkg/porter/stamp.go +++ b/pkg/porter/stamp.go @@ -60,6 +60,21 @@ func (p *Porter) IsBundleUpToDate(opts bundleFileOptions) (bool, error) { return false, errors.Wrapf(err, "could not marshal data from %s", opts.CNABFile) } + // Check whether invocation images exist in host registry. + for _, invocationImage := range bun.InvocationImages { + isImageCached, err := p.Registry.IsImageCached(invocationImage.Image) + if err != nil { + return false, err + } + + if !isImageCached { + if p.Debug { + fmt.Fprintln(p.Err, errors.New(fmt.Sprintf("Invocation image %s doesn't exist in the local image cache, will need to build first", invocationImage.Image))) + } + return false, nil + } + } + oldStamp, err := configadapter.LoadStamp(bun) if err != nil { return false, errors.Wrapf(err, "could not load stamp from %s", opts.CNABFile)