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 &registryFetcher{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  }