github.com/safing/portbase@v0.19.5/updater/get.go (about)

     1  package updater
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"fmt"
     7  	"net/http"
     8  
     9  	"github.com/safing/portbase/log"
    10  )
    11  
    12  // Errors returned by the updater package.
    13  var (
    14  	ErrNotFound                  = errors.New("the requested file could not be found")
    15  	ErrNotAvailableLocally       = errors.New("the requested file is not available locally")
    16  	ErrVerificationNotConfigured = errors.New("verification not configured for this resource")
    17  )
    18  
    19  // GetFile returns the selected (mostly newest) file with the given
    20  // identifier or an error, if it fails.
    21  func (reg *ResourceRegistry) GetFile(identifier string) (*File, error) {
    22  	reg.RLock()
    23  	res, ok := reg.resources[identifier]
    24  	reg.RUnlock()
    25  	if !ok {
    26  		return nil, ErrNotFound
    27  	}
    28  
    29  	file := res.GetFile()
    30  	// check if file is available locally
    31  	if file.version.Available {
    32  		file.markActiveWithLocking()
    33  
    34  		// Verify file, if configured.
    35  		_, err := file.Verify()
    36  		if err != nil && !errors.Is(err, ErrVerificationNotConfigured) {
    37  			// TODO: If verification is required, try deleting the resource and downloading it again.
    38  			return nil, fmt.Errorf("failed to verify file: %w", err)
    39  		}
    40  
    41  		return file, nil
    42  	}
    43  
    44  	// check if online
    45  	if !reg.Online {
    46  		return nil, ErrNotAvailableLocally
    47  	}
    48  
    49  	// check download dir
    50  	err := reg.tmpDir.Ensure()
    51  	if err != nil {
    52  		return nil, fmt.Errorf("could not prepare tmp directory for download: %w", err)
    53  	}
    54  
    55  	// Start registry operation.
    56  	reg.state.StartOperation(StateFetching)
    57  	defer reg.state.EndOperation()
    58  
    59  	// download file
    60  	log.Tracef("%s: starting download of %s", reg.Name, file.versionedPath)
    61  	client := &http.Client{}
    62  	for tries := 0; tries < 5; tries++ {
    63  		err = reg.fetchFile(context.TODO(), client, file.version, tries)
    64  		if err != nil {
    65  			log.Tracef("%s: failed to download %s: %s, retrying (%d)", reg.Name, file.versionedPath, err, tries+1)
    66  		} else {
    67  			file.markActiveWithLocking()
    68  
    69  			// TODO: We just download the file - should we verify it again?
    70  			return file, nil
    71  		}
    72  	}
    73  	log.Warningf("%s: failed to download %s: %s", reg.Name, file.versionedPath, err)
    74  	return nil, err
    75  }
    76  
    77  // GetVersion returns the selected version of the given identifier.
    78  // The returned resource version may not be modified.
    79  func (reg *ResourceRegistry) GetVersion(identifier string) (*ResourceVersion, error) {
    80  	reg.RLock()
    81  	res, ok := reg.resources[identifier]
    82  	reg.RUnlock()
    83  	if !ok {
    84  		return nil, ErrNotFound
    85  	}
    86  
    87  	res.Lock()
    88  	defer res.Unlock()
    89  
    90  	return res.SelectedVersion, nil
    91  }