github.com/docker/app@v0.9.1-beta3.0.20210611140623-a48f773ab002/internal/cnab/cnab.go (about)

     1  package cnab
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"os"
     7  
     8  	"github.com/docker/app/internal"
     9  	"github.com/docker/app/internal/image"
    10  	"github.com/docker/app/internal/log"
    11  	"github.com/docker/app/internal/packager"
    12  	"github.com/docker/app/internal/store"
    13  	appstore "github.com/docker/app/internal/store"
    14  	"github.com/docker/cli/cli/command"
    15  	"github.com/docker/cnab-to-oci/remotes"
    16  	"github.com/docker/distribution/reference"
    17  	"github.com/sirupsen/logrus"
    18  )
    19  
    20  type nameKind uint
    21  
    22  const (
    23  	_ nameKind = iota
    24  	nameKindDir
    25  	nameKindReference
    26  )
    27  
    28  func getAppNameKind(name string) (string, nameKind) {
    29  	if name == "" {
    30  		return name, nameKindDir
    31  	}
    32  	// name can be a dockerapp directory
    33  	st, err := os.Stat(name)
    34  	if os.IsNotExist(err) {
    35  		// try with .dockerapp extension
    36  		st, err = os.Stat(name + internal.AppExtension)
    37  		if err == nil {
    38  			name += internal.AppExtension
    39  		}
    40  	}
    41  	if err != nil {
    42  		return name, nameKindReference
    43  	}
    44  	if st.IsDir() {
    45  		return name, nameKindDir
    46  	}
    47  	return name, nameKindReference
    48  }
    49  
    50  func extractAndLoadAppBasedBundle(dockerCli command.Cli, name string) (*image.AppImage, string, error) {
    51  	app, err := packager.Extract(name)
    52  	if err != nil {
    53  		return nil, "", err
    54  	}
    55  	defer app.Cleanup()
    56  	bndl, err := packager.MakeBundleFromApp(dockerCli, app, nil)
    57  	return image.FromBundle(bndl), "", err
    58  }
    59  
    60  // ResolveBundle looks for a CNAB bundle which can be in a Docker App Package format or
    61  // a bundle stored locally or in the bundle store. It returns a built or found bundle,
    62  // a reference to the bundle if it is found in the imageStore, and an error.
    63  func ResolveBundle(dockerCli command.Cli, imageStore appstore.ImageStore, name string) (*image.AppImage, string, error) {
    64  	// resolution logic:
    65  	// - if there is a docker-app package in working directory or if a directory is given use packager.Extract
    66  	// - pull the bundle from the registry and add it to the bundle store
    67  	name, kind := getAppNameKind(name)
    68  	switch kind {
    69  	case nameKindDir:
    70  		return extractAndLoadAppBasedBundle(dockerCli, name)
    71  	case nameKindReference:
    72  		bndl, tagRef, err := GetBundle(dockerCli, imageStore, name)
    73  		if err != nil {
    74  			return nil, "", err
    75  		}
    76  		return bndl, tagRef.String(), err
    77  	}
    78  	return nil, "", fmt.Errorf("could not resolve bundle %q", name)
    79  }
    80  
    81  // GetBundle searches for the bundle locally and tries to pull it if not found
    82  func GetBundle(dockerCli command.Cli, imageStore appstore.ImageStore, name string) (*image.AppImage, reference.Reference, error) {
    83  	bndl, ref, err := getBundleFromStore(imageStore, name)
    84  	if err != nil {
    85  		named, err := store.StringToNamedRef(name)
    86  		if err != nil {
    87  			return nil, nil, err
    88  		}
    89  		fmt.Fprintf(dockerCli.Err(), "Unable to find App image %q locally\n", reference.FamiliarString(named))
    90  		fmt.Fprintf(dockerCli.Out(), "Pulling from registry...\n")
    91  		bndl, err = PullBundle(dockerCli, imageStore, named)
    92  		if err != nil {
    93  			return nil, nil, err
    94  		}
    95  		ref = named
    96  	}
    97  	return bndl, ref, nil
    98  }
    99  
   100  func getBundleFromStore(imageStore appstore.ImageStore, name string) (*image.AppImage, reference.Reference, error) {
   101  	ref, err := imageStore.LookUp(name)
   102  	if err != nil {
   103  		logrus.Debugf("Unable to find reference %q in the bundle store", name)
   104  		return nil, nil, err
   105  	}
   106  	bndl, err := imageStore.Read(ref)
   107  	if err != nil {
   108  		logrus.Debugf("Unable to read bundle %q from store", reference.FamiliarString(ref))
   109  		return nil, nil, err
   110  	}
   111  	return bndl, ref, nil
   112  }
   113  
   114  // PullBundle pulls the bundle and stores it into the bundle store
   115  func PullBundle(dockerCli command.Cli, imageStore appstore.ImageStore, tagRef reference.Named) (*image.AppImage, error) {
   116  	insecureRegistries, err := internal.InsecureRegistriesFromEngine(dockerCli)
   117  	if err != nil {
   118  		return nil, fmt.Errorf("could not retrieve insecure registries: %v", err)
   119  	}
   120  
   121  	bndl, relocationMap, err := remotes.Pull(log.WithLogContext(context.Background()), reference.TagNameOnly(tagRef), remotes.CreateResolver(dockerCli.ConfigFile(), insecureRegistries...))
   122  	if err != nil {
   123  		return nil, err
   124  	}
   125  	relocatedBundle := &image.AppImage{Bundle: bndl, RelocationMap: relocationMap}
   126  	if _, err := imageStore.Store(relocatedBundle, tagRef); err != nil {
   127  		return nil, err
   128  	}
   129  	return relocatedBundle, nil
   130  }