Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

client: invalidate simple streams cache #1424

Merged
merged 1 commit into from
Nov 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions client/simplestreams_images.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package incus
import (
"context"
"crypto/sha256"
"errors"
"fmt"
"io"
"net/http"
Expand All @@ -13,6 +14,7 @@ import (
"time"

"github.com/lxc/incus/v6/shared/api"
"github.com/lxc/incus/v6/shared/logger"
"github.com/lxc/incus/v6/shared/subprocess"
"github.com/lxc/incus/v6/shared/util"
)
Expand Down Expand Up @@ -121,6 +123,11 @@ func (r *ProtocolSimpleStreams) GetImageFile(fingerprint string, req ImageFileRe

size, err = util.DownloadFileHash(context.TODO(), &httpClient, r.httpUserAgent, req.ProgressHandler, req.Canceler, filename, uri, hash, sha256.New(), target)
if err != nil {
if errors.Is(err, util.ErrNotFound) {
logger.Info("Unable to download file by hash, invalidate potentially outdated cache", logger.Ctx{"filename": filename, "uri": uri, "hash": hash})
r.ssClient.InvalidateCache()
}

return -1, err
}
}
Expand Down
10 changes: 8 additions & 2 deletions shared/simplestreams/simplestreams.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,11 @@ func (s *SimpleStreams) readCache(path string) ([]byte, bool) {
return body, expired
}

// InvalidateCache removes the on-disk cache for the SimpleStreams remote.
func (s *SimpleStreams) InvalidateCache() {
_ = os.RemoveAll(s.cachePath)
}

func (s *SimpleStreams) cachedDownload(path string) ([]byte, error) {
fields := strings.Split(path, "/")
fileName := fields[len(fields)-1]
Expand Down Expand Up @@ -165,7 +170,7 @@ func (s *SimpleStreams) cachedDownload(path string) ([]byte, error) {
if s.cachePath != "" {
cacheName := filepath.Join(s.cachePath, fileName)
_ = os.Remove(cacheName)
_ = os.WriteFile(cacheName, body, 0644)
_ = os.WriteFile(cacheName, body, 0o644)
}

return body, nil
Expand Down Expand Up @@ -375,7 +380,8 @@ func (s *SimpleStreams) GetFiles(fingerprint string) (map[string]DownloadableFil
files[path[2]] = DownloadableFile{
Path: path[0],
Sha256: path[1],
Size: size}
Size: size,
}
}

return files, nil
Expand Down
9 changes: 9 additions & 0 deletions shared/util/net.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package util

import (
"context"
"errors"
"fmt"
"hash"
"io"
Expand All @@ -12,6 +13,10 @@ import (
"github.com/lxc/incus/v6/shared/units"
)

// ErrNotFound is used to explicitly signal error cases, where a resource
// can not be found (404 HTTP status code).
var ErrNotFound = errors.New("resource not found")

func DownloadFileHash(ctx context.Context, httpClient *http.Client, useragent string, progress func(progress ioprogress.ProgressData), canceler *cancel.HTTPRequestCanceller, filename string, url string, hash string, hashFunc hash.Hash, target io.WriteSeeker) (int64, error) {
// Always seek to the beginning
_, _ = target.Seek(0, io.SeekStart)
Expand Down Expand Up @@ -44,6 +49,10 @@ func DownloadFileHash(ctx context.Context, httpClient *http.Client, useragent st
defer close(doneCh)

if r.StatusCode != http.StatusOK {
if r.StatusCode == http.StatusNotFound {
return -1, fmt.Errorf("Unable to fetch %s: %w", url, ErrNotFound)
}

return -1, fmt.Errorf("Unable to fetch %s: %s", url, r.Status)
}

Expand Down