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 }