github.com/cozy/cozy-stack@v0.0.0-20240603063001-31110fa4cae1/model/app/fetcher_registry.go (about) 1 package app 2 3 import ( 4 "bytes" 5 "encoding/hex" 6 "io" 7 "net/url" 8 "regexp" 9 "strings" 10 11 "github.com/cozy/cozy-stack/pkg/appfs" 12 "github.com/cozy/cozy-stack/pkg/logger" 13 "github.com/cozy/cozy-stack/pkg/registry" 14 ) 15 16 type registryFetcher struct { 17 log logger.Logger 18 registries []*url.URL 19 version *registry.Version 20 } 21 22 func newRegistryFetcher(registries []*url.URL, log logger.Logger) Fetcher { 23 return ®istryFetcher{log: log, registries: registries} 24 } 25 26 func (f *registryFetcher) FetchManifest(src *url.URL) (io.ReadCloser, error) { 27 slug := src.Host 28 if slug == "" { 29 return nil, ErrManifestNotReachable 30 } 31 32 var version *registry.Version 33 var err error 34 35 channel, vnumber := getRegistryChannel(src) 36 if vnumber != "" { 37 version, err = registry.GetVersion(slug, vnumber, f.registries) 38 } else { 39 version, err = registry.GetLatestVersion(slug, channel, f.registries) 40 } 41 42 if err != nil { 43 f.log.Infof("Could not fetch manifest for %s: %s", src.String(), err.Error()) 44 return nil, ErrManifestNotReachable 45 } 46 f.version = version 47 return io.NopCloser(bytes.NewBuffer(version.Manifest)), nil 48 } 49 50 func (f *registryFetcher) appVersion() string { 51 return f.version.Version 52 } 53 54 func (f *registryFetcher) Fetch(src *url.URL, fs appfs.Copier, man Manifest) error { 55 v := f.version 56 shasum, err := hex.DecodeString(v.Sha256) 57 if err != nil { 58 return err 59 } 60 u, err := url.Parse(v.URL) 61 if err != nil { 62 return err 63 } 64 man.SetVersion(v.Version) 65 man.SetChecksum(v.Sha256) 66 return fetchHTTP(u, shasum, fs, man, v.TarPrefix) 67 } 68 69 func getRegistryChannel(src *url.URL) (string, string) { 70 var channel, version string 71 channel = "stable" 72 splittedPath := strings.Split(src.String(), "/") 73 switch len(splittedPath) { 74 case 4: // Either channel or version 75 channelOrVersion := splittedPath[3] 76 77 // If it starts with a number, it is the version 78 versionRegex := "^\\d" 79 matched, err := regexp.MatchString(versionRegex, channelOrVersion) 80 if err != nil { 81 log := logger.WithNamespace("fetcher_registry") 82 log.Errorf("fetcher_registry: Bad format for %s", src.String()) 83 return "", "" 84 } 85 if matched { 86 version = channelOrVersion 87 } else { 88 channel = channelOrVersion 89 } 90 case 5: // Channel and version 91 channel = splittedPath[3] 92 version = splittedPath[4] 93 } 94 95 if version == "latest" { 96 version = "" 97 } 98 99 return channel, version 100 }