Do implement a real cache

Instead of merely storing files into disk use a real cache.

This also makes possible finally to reference artifacts in the cache with the
package checksum, which solves the cache hit checksum failures we had
previously.
This commit is contained in:
Ettore Di Giacinto
2021-10-19 17:06:48 +02:00
parent 2eeb464946
commit 5b4e930fc3
26 changed files with 328 additions and 212 deletions

View File

@@ -22,8 +22,8 @@ import (
"github.com/ghodss/yaml" "github.com/ghodss/yaml"
helpers "github.com/mudler/luet/cmd/helpers" helpers "github.com/mudler/luet/cmd/helpers"
"github.com/mudler/luet/cmd/util" "github.com/mudler/luet/cmd/util"
"github.com/mudler/luet/pkg/api/core/types/artifact"
"github.com/mudler/luet/pkg/compiler" "github.com/mudler/luet/pkg/compiler"
"github.com/mudler/luet/pkg/compiler/types/artifact"
compilerspec "github.com/mudler/luet/pkg/compiler/types/spec" compilerspec "github.com/mudler/luet/pkg/compiler/types/spec"
"github.com/mudler/luet/pkg/installer" "github.com/mudler/luet/pkg/installer"

View File

@@ -19,7 +19,7 @@ import (
"io/ioutil" "io/ioutil"
"github.com/mudler/luet/cmd/util" "github.com/mudler/luet/cmd/util"
artifact "github.com/mudler/luet/pkg/compiler/types/artifact" artifact "github.com/mudler/luet/pkg/api/core/types/artifact"
. "github.com/mudler/luet/pkg/logger" . "github.com/mudler/luet/pkg/logger"
pkg "github.com/mudler/luet/pkg/package" pkg "github.com/mudler/luet/pkg/package"

View File

@@ -20,7 +20,7 @@ import (
"time" "time"
helpers "github.com/mudler/luet/cmd/helpers" helpers "github.com/mudler/luet/cmd/helpers"
"github.com/mudler/luet/pkg/compiler/types/artifact" "github.com/mudler/luet/pkg/api/core/types/artifact"
"github.com/mudler/luet/pkg/compiler/types/compression" "github.com/mudler/luet/pkg/compiler/types/compression"
compilerspec "github.com/mudler/luet/pkg/compiler/types/spec" compilerspec "github.com/mudler/luet/pkg/compiler/types/spec"
. "github.com/mudler/luet/pkg/config" . "github.com/mudler/luet/pkg/config"

1
go.mod
View File

@@ -62,6 +62,7 @@ require (
github.com/pelletier/go-toml v1.9.4 // indirect github.com/pelletier/go-toml v1.9.4 // indirect
github.com/philopon/go-toposort v0.0.0-20170620085441-9be86dbd762f github.com/philopon/go-toposort v0.0.0-20170620085441-9be86dbd762f
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/rancher-sandbox/gofilecache v0.0.0-20210330135715-becdeff5df15
github.com/schollz/progressbar/v3 v3.7.1 github.com/schollz/progressbar/v3 v3.7.1
github.com/sirupsen/logrus v1.8.1 github.com/sirupsen/logrus v1.8.1
github.com/spf13/cast v1.4.1 // indirect github.com/spf13/cast v1.4.1 // indirect

View File

@@ -50,7 +50,7 @@ import (
yaml "gopkg.in/yaml.v2" yaml "gopkg.in/yaml.v2"
) )
// When compiling, we write also a fingerprint.metadata.yaml file with PackageArtifact. In this way we can have another command to create the repository // When compiling, we write also a fingerprint.metadata.yaml file with PackageArtifact. In this way we can have another command to create the repository
// which will consist in just of an repository.yaml which is just the repository structure with the list of package artifact. // which will consist in just of an repository.yaml which is just the repository structure with the list of package artifact.
// In this way a generic client can fetch the packages and, after unpacking the tree, performing queries to install packages. // In this way a generic client can fetch the packages and, after unpacking the tree, performing queries to install packages.
type PackageArtifact struct { type PackageArtifact struct {

View File

@@ -20,10 +20,10 @@ import (
"os" "os"
"path/filepath" "path/filepath"
. "github.com/mudler/luet/pkg/api/core/types/artifact"
"github.com/mudler/luet/pkg/compiler" "github.com/mudler/luet/pkg/compiler"
. "github.com/mudler/luet/pkg/compiler/backend" . "github.com/mudler/luet/pkg/compiler/backend"
backend "github.com/mudler/luet/pkg/compiler/backend" backend "github.com/mudler/luet/pkg/compiler/backend"
. "github.com/mudler/luet/pkg/compiler/types/artifact"
compression "github.com/mudler/luet/pkg/compiler/types/compression" compression "github.com/mudler/luet/pkg/compiler/types/compression"
compilerspec "github.com/mudler/luet/pkg/compiler/types/spec" compilerspec "github.com/mudler/luet/pkg/compiler/types/spec"

View File

@@ -0,0 +1,63 @@
// Copyright © 2021 Ettore Di Giacinto <mudler@mocaccino.org>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, see <http://www.gnu.org/licenses/>.
package artifact
import (
"crypto/sha512"
"fmt"
"os"
"path/filepath"
"github.com/pkg/errors"
"github.com/rancher-sandbox/gofilecache"
)
type ArtifactCache struct {
gofilecache.Cache
}
func NewCache(dir string) *ArtifactCache {
return &ArtifactCache{Cache: *gofilecache.InitCache(dir)}
}
func (c *ArtifactCache) cacheID(a *PackageArtifact) [64]byte {
fingerprint := filepath.Base(a.Path)
if a.CompileSpec != nil && a.CompileSpec.Package != nil {
fingerprint = a.CompileSpec.Package.GetFingerPrint()
}
if len(a.Checksums) > 0 {
for _, cs := range a.Checksums.List() {
t := cs[0]
result := cs[1]
fingerprint += fmt.Sprintf("+%s:%s", t, result)
}
}
return sha512.Sum512([]byte(fingerprint))
}
func (c *ArtifactCache) Get(a *PackageArtifact) (string, error) {
fileName, _, err := c.Cache.GetFile(c.cacheID(a))
return fileName, err
}
func (c *ArtifactCache) Put(a *PackageArtifact) (gofilecache.OutputID, int64, error) {
file, err := os.Open(a.Path)
if err != nil {
return [64]byte{}, 0, errors.Wrapf(err, "failed opening %s", a.Path)
}
defer file.Close()
return c.Cache.Put(c.cacheID(a), file)
}

View File

@@ -0,0 +1,89 @@
// Copyright © 2019 Ettore Di Giacinto <mudler@gentoo.org>
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License along
// with this program; if not, see <http://www.gnu.org/licenses/>.
package artifact_test
import (
"io/ioutil"
"os"
"path/filepath"
. "github.com/mudler/luet/pkg/api/core/types/artifact"
compilerspec "github.com/mudler/luet/pkg/compiler/types/spec"
fileHelper "github.com/mudler/luet/pkg/helpers/file"
pkg "github.com/mudler/luet/pkg/package"
. "github.com/onsi/ginkgo"
. "github.com/onsi/gomega"
)
var _ = Describe("Cache", func() {
Context("CacheID", func() {
It("Get and retrieve files", func() {
tmpdir, err := ioutil.TempDir(os.TempDir(), "test")
Expect(err).ToNot(HaveOccurred())
defer os.RemoveAll(tmpdir) // clean up
tmpdirartifact, err := ioutil.TempDir(os.TempDir(), "testartifact")
Expect(err).ToNot(HaveOccurred())
defer os.RemoveAll(tmpdirartifact) // clean up
err = ioutil.WriteFile(filepath.Join(tmpdirartifact, "foo"), []byte(string("foo")), os.ModePerm)
Expect(err).ToNot(HaveOccurred())
a := NewPackageArtifact(filepath.Join(tmpdir, "foo.tar.gz"))
err = a.Compress(tmpdirartifact, 1)
Expect(err).ToNot(HaveOccurred())
cache := NewCache(tmpdir)
// Put an artifact in the cache and retrieve it later
// the artifact is NOT hashed so it is referenced just by the path in the cache
_, _, err = cache.Put(a)
Expect(err).ToNot(HaveOccurred())
path, err := cache.Get(a)
Expect(err).ToNot(HaveOccurred())
b := NewPackageArtifact(path)
err = b.Unpack(tmpdir, false)
Expect(err).ToNot(HaveOccurred())
Expect(fileHelper.Exists(filepath.Join(tmpdir, "foo"))).To(BeTrue())
bb, err := ioutil.ReadFile(filepath.Join(tmpdir, "foo"))
Expect(err).ToNot(HaveOccurred())
Expect(string(bb)).To(Equal("foo"))
// After the artifact is hashed, the fingerprint mutates so the cache doesn't see it hitting again
// the test we did above fails as we expect to.
a.Hash()
_, err = cache.Get(a)
Expect(err).To(HaveOccurred())
a.CompileSpec = &compilerspec.LuetCompilationSpec{Package: &pkg.DefaultPackage{Name: "foo", Category: "bar"}}
_, _, err = cache.Put(a)
Expect(err).ToNot(HaveOccurred())
c := NewPackageArtifact(filepath.Join(tmpdir, "foo.tar.gz"))
c.Hash()
c.CompileSpec = &compilerspec.LuetCompilationSpec{Package: &pkg.DefaultPackage{Name: "foo", Category: "bar"}}
_, err = cache.Get(c)
Expect(err).ToNot(HaveOccurred())
})
})
})

View File

@@ -24,6 +24,7 @@ import (
"hash" "hash"
"io" "io"
"os" "os"
"sort"
// . "github.com/mudler/luet/pkg/logger" // . "github.com/mudler/luet/pkg/logger"
"github.com/pkg/errors" "github.com/pkg/errors"
@@ -42,6 +43,18 @@ type HashOptions struct {
Type HashImplementation Type HashImplementation
} }
func (c Checksums) List() (res [][]string) {
keys := make([]string, 0)
for k, _ := range c {
keys = append(keys, k)
}
sort.Strings(keys)
for _, k := range keys {
res = append(res, []string{k, c[k]})
}
return
}
// Generate generates all Checksums supported for the artifact // Generate generates all Checksums supported for the artifact
func (c *Checksums) Generate(a *PackageArtifact) error { func (c *Checksums) Generate(a *PackageArtifact) error {
return c.generateSHA256(a) return c.generateSHA256(a)

View File

@@ -19,7 +19,7 @@ import (
"io/ioutil" "io/ioutil"
"os" "os"
. "github.com/mudler/luet/pkg/compiler/types/artifact" . "github.com/mudler/luet/pkg/api/core/types/artifact"
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"
. "github.com/onsi/gomega" . "github.com/onsi/gomega"

View File

@@ -7,7 +7,7 @@ import (
"path/filepath" "path/filepath"
"strings" "strings"
artifact "github.com/mudler/luet/pkg/compiler/types/artifact" artifact "github.com/mudler/luet/pkg/api/core/types/artifact"
"github.com/mudler/luet/pkg/compiler/backend" "github.com/mudler/luet/pkg/compiler/backend"
"github.com/mudler/luet/pkg/config" "github.com/mudler/luet/pkg/config"

View File

@@ -16,11 +16,11 @@
package backend_test package backend_test
import ( import (
"github.com/mudler/luet/pkg/api/core/types/artifact"
"github.com/mudler/luet/pkg/compiler" "github.com/mudler/luet/pkg/compiler"
. "github.com/mudler/luet/pkg/compiler" . "github.com/mudler/luet/pkg/compiler"
"github.com/mudler/luet/pkg/compiler/backend" "github.com/mudler/luet/pkg/compiler/backend"
. "github.com/mudler/luet/pkg/compiler/backend" . "github.com/mudler/luet/pkg/compiler/backend"
"github.com/mudler/luet/pkg/compiler/types/artifact"
fileHelper "github.com/mudler/luet/pkg/helpers/file" fileHelper "github.com/mudler/luet/pkg/helpers/file"
"io/ioutil" "io/ioutil"

View File

@@ -30,9 +30,9 @@ import (
"time" "time"
"github.com/imdario/mergo" "github.com/imdario/mergo"
artifact "github.com/mudler/luet/pkg/api/core/types/artifact"
bus "github.com/mudler/luet/pkg/bus" bus "github.com/mudler/luet/pkg/bus"
"github.com/mudler/luet/pkg/compiler/backend" "github.com/mudler/luet/pkg/compiler/backend"
artifact "github.com/mudler/luet/pkg/compiler/types/artifact"
"github.com/mudler/luet/pkg/compiler/types/options" "github.com/mudler/luet/pkg/compiler/types/options"
compilerspec "github.com/mudler/luet/pkg/compiler/types/spec" compilerspec "github.com/mudler/luet/pkg/compiler/types/spec"
"github.com/mudler/luet/pkg/helpers" "github.com/mudler/luet/pkg/helpers"

View File

@@ -1,4 +1,4 @@
// Copyright © 2020 Ettore Di Giacinto <mudler@mocaccino.org> // Copyright © 2020-2021 Ettore Di Giacinto <mudler@mocaccino.org>
// //
// This program is free software; you can redistribute it and/or modify // This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@@ -26,7 +26,7 @@ import (
"github.com/docker/go-units" "github.com/docker/go-units"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/mudler/luet/pkg/compiler/types/artifact" "github.com/mudler/luet/pkg/api/core/types/artifact"
"github.com/mudler/luet/pkg/config" "github.com/mudler/luet/pkg/config"
"github.com/mudler/luet/pkg/helpers/docker" "github.com/mudler/luet/pkg/helpers/docker"
fileHelper "github.com/mudler/luet/pkg/helpers/file" fileHelper "github.com/mudler/luet/pkg/helpers/file"
@@ -41,6 +41,7 @@ type DockerClient struct {
RepoData RepoData RepoData RepoData
auth *types.AuthConfig auth *types.AuthConfig
verify bool verify bool
Cache *artifact.ArtifactCache
} }
func NewDockerClient(r RepoData) *DockerClient { func NewDockerClient(r RepoData) *DockerClient {
@@ -49,25 +50,22 @@ func NewDockerClient(r RepoData) *DockerClient {
dat, _ := json.Marshal(r.Authentication) dat, _ := json.Marshal(r.Authentication)
json.Unmarshal(dat, auth) json.Unmarshal(dat, auth)
return &DockerClient{RepoData: r, auth: auth} return &DockerClient{RepoData: r, auth: auth,
Cache: artifact.NewCache(config.LuetCfg.GetSystem().GetSystemPkgsCacheDirPath()),
}
} }
func (c *DockerClient) DownloadArtifact(a *artifact.PackageArtifact) (*artifact.PackageArtifact, error) { func (c *DockerClient) DownloadArtifact(a *artifact.PackageArtifact) (*artifact.PackageArtifact, error) {
//var u *url.URL = nil //var u *url.URL = nil
var err error var err error
var temp string
Spinner(22) Spinner(22)
defer SpinnerStop() defer SpinnerStop()
var resultingArtifact *artifact.PackageArtifact resultingArtifact := a.ShallowCopy()
artifactName := path.Base(a.Path) artifactName := path.Base(a.Path)
cacheFile := filepath.Join(config.LuetCfg.GetSystem().GetSystemPkgsCacheDirPath(), artifactName)
Debug("Cache file", cacheFile) downloaded := false
if err := fileHelper.EnsureDir(cacheFile); err != nil {
return nil, errors.Wrapf(err, "could not create cache folder %s for %s", config.LuetCfg.GetSystem().GetSystemPkgsCacheDirPath(), cacheFile)
}
ok := false
// TODO: // TODO:
// Files are in URI/packagename:version (GetPackageImageName() method) // Files are in URI/packagename:version (GetPackageImageName() method)
@@ -77,19 +75,26 @@ func (c *DockerClient) DownloadArtifact(a *artifact.PackageArtifact) (*artifact.
// is done in such cases (see repository.go) // is done in such cases (see repository.go)
// Check if file is already in cache // Check if file is already in cache
if fileHelper.Exists(cacheFile) { fileName, err := c.Cache.Get(a)
Debug("Cache hit for artifact", artifactName) // Check if file is already in cache
if err == nil {
resultingArtifact = a resultingArtifact = a
resultingArtifact.Path = cacheFile resultingArtifact.Path = fileName
resultingArtifact.Checksums = artifact.Checksums{} resultingArtifact.Checksums = artifact.Checksums{}
Debug("Use artifact", artifactName, "from cache.")
} else { } else {
temp, err = config.LuetCfg.GetSystem().TempDir("tree") temp, err := config.LuetCfg.GetSystem().TempDir("image")
if err != nil { if err != nil {
return nil, err return nil, err
} }
defer os.RemoveAll(temp) defer os.RemoveAll(temp)
tempArtifact, err := config.LuetCfg.GetSystem().TempFile("artifact")
if err != nil {
return nil, err
}
defer os.RemoveAll(tempArtifact.Name())
for _, uri := range c.RepoData.Urls { for _, uri := range c.RepoData.Urls {
imageName := fmt.Sprintf("%s:%s", uri, a.CompileSpec.GetPackage().ImageID()) imageName := fmt.Sprintf("%s:%s", uri, a.CompileSpec.GetPackage().ImageID())
@@ -110,26 +115,37 @@ func (c *DockerClient) DownloadArtifact(a *artifact.PackageArtifact) (*artifact.
Info(fmt.Sprintf("Pulled: %s", info.Target.Digest)) Info(fmt.Sprintf("Pulled: %s", info.Target.Digest))
Info(fmt.Sprintf("Size: %s", units.BytesSize(float64(info.Target.Size)))) Info(fmt.Sprintf("Size: %s", units.BytesSize(float64(info.Target.Size))))
Debug("\nCompressing result ", filepath.Join(temp), "to", cacheFile) Debug("\nCompressing result ", filepath.Join(temp), "to", tempArtifact.Name())
newart := a
// We discard checksum, that are checked while during pull and unpack // We discard checksum, that are checked while during pull and unpack
newart.Checksums = artifact.Checksums{} resultingArtifact.Checksums = artifact.Checksums{}
newart.Path = cacheFile // First set to cache file resultingArtifact.Path = tempArtifact.Name() // First set to cache file
newart.Path = newart.GetUncompressedName() // Calculate the real path from cacheFile err = resultingArtifact.Compress(temp, 1)
err = newart.Compress(temp, 1)
if err != nil { if err != nil {
Error(fmt.Sprintf("Failed compressing package %s: %s", imageName, err.Error())) Error(fmt.Sprintf("Failed compressing package %s: %s", imageName, err.Error()))
continue continue
} }
resultingArtifact = newart
ok = true _, _, err = c.Cache.Put(resultingArtifact)
if err != nil {
Error(fmt.Sprintf("Failed storing package %s from cache: %s", imageName, err.Error()))
continue
}
fileName, err := c.Cache.Get(resultingArtifact)
if err != nil {
Error(fmt.Sprintf("Failed getting package %s from cache: %s", imageName, err.Error()))
continue
}
resultingArtifact.Path = fileName // Cache is persistent. tempArtifact is not
downloaded = true
break break
} }
if !ok { if !downloaded {
return nil, err return nil, errors.Wrap(err, "no image available from repositories")
} }
} }

View File

@@ -20,7 +20,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"github.com/mudler/luet/pkg/compiler/types/artifact" "github.com/mudler/luet/pkg/api/core/types/artifact"
compilerspec "github.com/mudler/luet/pkg/compiler/types/spec" compilerspec "github.com/mudler/luet/pkg/compiler/types/spec"
fileHelper "github.com/mudler/luet/pkg/helpers/file" fileHelper "github.com/mudler/luet/pkg/helpers/file"

View File

@@ -1,4 +1,4 @@
// Copyright © 2019 Ettore Di Giacinto <mudler@gentoo.org> // Copyright © 2019-2021 Ettore Di Giacinto <mudler@gentoo.org>
// //
// This program is free software; you can redistribute it and/or modify // This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@@ -26,9 +26,9 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/mudler/luet/pkg/compiler/types/artifact" "github.com/mudler/luet/pkg/api/core/types/artifact"
fileHelper "github.com/mudler/luet/pkg/helpers/file"
. "github.com/mudler/luet/pkg/logger" . "github.com/mudler/luet/pkg/logger"
"github.com/pkg/errors"
"github.com/cavaliercoder/grab" "github.com/cavaliercoder/grab"
"github.com/mudler/luet/pkg/config" "github.com/mudler/luet/pkg/config"
@@ -38,14 +38,18 @@ import (
type HttpClient struct { type HttpClient struct {
RepoData RepoData RepoData RepoData
Cache *artifact.ArtifactCache
} }
func NewHttpClient(r RepoData) *HttpClient { func NewHttpClient(r RepoData) *HttpClient {
return &HttpClient{RepoData: r} return &HttpClient{
RepoData: r,
Cache: artifact.NewCache(config.LuetCfg.GetSystem().GetSystemPkgsCacheDirPath()),
}
} }
func NewGrabClient() *grab.Client { func NewGrabClient() *grab.Client {
httpTimeout := 120 httpTimeout := 360
timeout := os.Getenv("HTTP_TIMEOUT") timeout := os.Getenv("HTTP_TIMEOUT")
if timeout != "" { if timeout != "" {
timeoutI, err := strconv.Atoi(timeout) timeoutI, err := strconv.Atoi(timeout)
@@ -65,7 +69,7 @@ func NewGrabClient() *grab.Client {
} }
} }
func (c *HttpClient) PrepareReq(dst, url string) (*grab.Request, error) { func (c *HttpClient) prepareReq(dst, url string) (*grab.Request, error) {
req, err := grab.NewRequest(dst, url) req, err := grab.NewRequest(dst, url)
if err != nil { if err != nil {
@@ -88,169 +92,114 @@ func Round(input float64) float64 {
return math.Floor(input + 0.5) return math.Floor(input + 0.5)
} }
func (c *HttpClient) DownloadArtifact(a *artifact.PackageArtifact) (*artifact.PackageArtifact, error) { func (c *HttpClient) DownloadFile(p string) (string, error) {
var u *url.URL = nil
var err error
var req *grab.Request
var temp string
artifactName := path.Base(a.Path)
cacheFile := filepath.Join(config.LuetCfg.GetSystem().GetSystemPkgsCacheDirPath(), artifactName)
ok := false
// Check if file is already in cache
if fileHelper.Exists(cacheFile) {
Debug("Use artifact", artifactName, "from cache.")
} else {
temp, err = config.LuetCfg.GetSystem().TempDir("tree")
if err != nil {
return nil, err
}
defer os.RemoveAll(temp)
client := NewGrabClient()
for _, uri := range c.RepoData.Urls {
Debug("Downloading artifact", artifactName, "from", uri)
u, err = url.Parse(uri)
if err != nil {
continue
}
u.Path = path.Join(u.Path, artifactName)
req, err = c.PrepareReq(temp, u.String())
if err != nil {
continue
}
resp := client.Do(req)
bar := progressbar.NewOptions64(
resp.Size(),
progressbar.OptionSetDescription(
fmt.Sprintf("[cyan] %s - [reset]",
filepath.Base(resp.Request.HTTPRequest.URL.RequestURI()))),
progressbar.OptionSetRenderBlankState(true),
progressbar.OptionEnableColorCodes(config.LuetCfg.GetLogging().Color),
progressbar.OptionClearOnFinish(),
progressbar.OptionShowBytes(true),
progressbar.OptionShowCount(),
progressbar.OptionSetPredictTime(true),
progressbar.OptionFullWidth(),
progressbar.OptionSetTheme(progressbar.Theme{
Saucer: "[white]=[reset]",
SaucerHead: "[white]>[reset]",
SaucerPadding: " ",
BarStart: "[",
BarEnd: "]",
}))
bar.Reset()
// start download loop
t := time.NewTicker(500 * time.Millisecond)
defer t.Stop()
download_loop:
for {
select {
case <-t.C:
bar.Set64(resp.BytesComplete())
case <-resp.Done:
// download is complete
break download_loop
}
}
if err = resp.Err(); err != nil {
continue
}
if err != nil {
continue
}
Info("\nDownloaded", artifactName, "of",
fmt.Sprintf("%.2f", (float64(resp.BytesComplete())/1000)/1000), "MB (",
fmt.Sprintf("%.2f", (float64(resp.BytesPerSecond())/1024)/1024), "MiB/s )")
Debug("\nCopying file ", filepath.Join(temp, artifactName), "to", cacheFile)
err = fileHelper.CopyFile(filepath.Join(temp, artifactName), cacheFile)
bar.Finish()
ok = true
break
}
if !ok {
return nil, err
}
}
newart := a
newart.Path = cacheFile
return newart, nil
}
func (c *HttpClient) DownloadFile(name string) (string, error) {
var file *os.File = nil var file *os.File = nil
var u *url.URL = nil var downloaded bool
var err error temp, err := config.LuetCfg.GetSystem().TempDir("download")
var req *grab.Request
var temp string
ok := false
temp, err = config.LuetCfg.GetSystem().TempDir("tree")
if err != nil { if err != nil {
return "", err return "", err
} }
defer os.RemoveAll(temp)
client := NewGrabClient() client := NewGrabClient()
for _, uri := range c.RepoData.Urls { for _, uri := range c.RepoData.Urls {
file, err = config.LuetCfg.GetSystem().TempFile("HttpClient") file, err = config.LuetCfg.GetSystem().TempFile("HttpClient")
if err != nil { if err != nil {
Debug("Failed downloading", p, "from", uri)
continue continue
} }
Debug("Downloading artifact", p, "from", uri)
u, err = url.Parse(uri) u, err := url.Parse(uri)
if err != nil { if err != nil {
continue continue
} }
u.Path = path.Join(u.Path, name) u.Path = path.Join(u.Path, p)
Debug("Downloading", u.String()) req, err := c.prepareReq(file.Name(), u.String())
req, err = c.PrepareReq(temp, u.String())
if err != nil { if err != nil {
continue continue
} }
resp := client.Do(req) resp := client.Do(req)
bar := progressbar.NewOptions64(
resp.Size(),
progressbar.OptionSetDescription(
fmt.Sprintf("[cyan] %s - [reset]",
filepath.Base(resp.Request.HTTPRequest.URL.RequestURI()))),
progressbar.OptionSetRenderBlankState(true),
progressbar.OptionEnableColorCodes(config.LuetCfg.GetLogging().Color),
progressbar.OptionClearOnFinish(),
progressbar.OptionShowBytes(true),
progressbar.OptionShowCount(),
progressbar.OptionSetPredictTime(true),
progressbar.OptionFullWidth(),
progressbar.OptionSetTheme(progressbar.Theme{
Saucer: "[white]=[reset]",
SaucerHead: "[white]>[reset]",
SaucerPadding: " ",
BarStart: "[",
BarEnd: "]",
}))
bar.Reset()
// start download loop
t := time.NewTicker(500 * time.Millisecond)
defer t.Stop()
download_loop:
for {
select {
case <-t.C:
bar.Set64(resp.BytesComplete())
case <-resp.Done:
// download is complete
break download_loop
}
}
if err = resp.Err(); err != nil { if err = resp.Err(); err != nil {
continue continue
} }
Info("Downloaded", filepath.Base(resp.Filename), "of", Info("\nDownloaded", p, "of",
fmt.Sprintf("%.2f", (float64(resp.BytesComplete())/1000)/1000), "MB (", fmt.Sprintf("%.2f", (float64(resp.BytesComplete())/1000)/1000), "MB (",
fmt.Sprintf("%.2f", (float64(resp.BytesPerSecond())/1024)/1024), "MiB/s )") fmt.Sprintf("%.2f", (float64(resp.BytesPerSecond())/1024)/1024), "MiB/s )")
err = fileHelper.CopyFile(filepath.Join(temp, name), file.Name()) bar.Finish()
if err != nil { downloaded = true
continue
}
ok = true
break break
} }
if !ok { if !downloaded {
return "", err return "", errors.Wrap(err, "artifact not available in any of the specified url locations")
}
return file.Name(), nil
}
func (c *HttpClient) DownloadArtifact(a *artifact.PackageArtifact) (*artifact.PackageArtifact, error) {
newart := a.ShallowCopy()
artifactName := path.Base(a.Path)
fileName, err := c.Cache.Get(a)
// Check if file is already in cache
if err == nil {
newart.Path = fileName
Debug("Use artifact", artifactName, "from cache.")
} else {
d, err := c.DownloadFile(artifactName)
if err != nil {
return nil, errors.Wrapf(err, "failed downloading %s", artifactName)
}
newart.Path = d
c.Cache.Put(newart)
} }
return file.Name(), err return newart, nil
} }

View File

@@ -22,7 +22,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"github.com/mudler/luet/pkg/compiler/types/artifact" "github.com/mudler/luet/pkg/api/core/types/artifact"
fileHelper "github.com/mudler/luet/pkg/helpers/file" fileHelper "github.com/mudler/luet/pkg/helpers/file"
. "github.com/mudler/luet/pkg/installer/client" . "github.com/mudler/luet/pkg/installer/client"
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"

View File

@@ -1,4 +1,4 @@
// Copyright © 2019 Ettore Di Giacinto <mudler@gentoo.org> // Copyright © 2020-2021 Ettore Di Giacinto <mudler@gentoo.org>
// //
// This program is free software; you can redistribute it and/or modify // This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by // it under the terms of the GNU General Public License as published by
@@ -20,61 +20,46 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"github.com/mudler/luet/pkg/compiler/types/artifact" "github.com/mudler/luet/pkg/api/core/types/artifact"
"github.com/mudler/luet/pkg/config" "github.com/mudler/luet/pkg/config"
fileHelper "github.com/mudler/luet/pkg/helpers/file" fileHelper "github.com/mudler/luet/pkg/helpers/file"
. "github.com/mudler/luet/pkg/logger" . "github.com/mudler/luet/pkg/logger"
"github.com/pkg/errors"
) )
type LocalClient struct { type LocalClient struct {
RepoData RepoData RepoData RepoData
Cache *artifact.ArtifactCache
} }
func NewLocalClient(r RepoData) *LocalClient { func NewLocalClient(r RepoData) *LocalClient {
return &LocalClient{RepoData: r} return &LocalClient{
Cache: artifact.NewCache(config.LuetCfg.GetSystem().GetSystemPkgsCacheDirPath()),
RepoData: r,
}
} }
func (c *LocalClient) DownloadArtifact(a *artifact.PackageArtifact) (*artifact.PackageArtifact, error) { func (c *LocalClient) DownloadArtifact(a *artifact.PackageArtifact) (*artifact.PackageArtifact, error) {
var err error var err error
rootfs := ""
artifactName := path.Base(a.Path) artifactName := path.Base(a.Path)
cacheFile := filepath.Join(config.LuetCfg.GetSystem().GetSystemPkgsCacheDirPath(), artifactName) newart := a.ShallowCopy()
if !config.LuetCfg.ConfigFromHost {
rootfs, err = config.LuetCfg.GetSystem().GetRootFsAbs()
if err != nil {
return nil, err
}
}
fileName, err := c.Cache.Get(a)
// Check if file is already in cache // Check if file is already in cache
if fileHelper.Exists(cacheFile) { if err == nil {
newart.Path = fileName
Debug("Use artifact", artifactName, "from cache.") Debug("Use artifact", artifactName, "from cache.")
} else { } else {
ok := false d, err := c.DownloadFile(artifactName)
for _, uri := range c.RepoData.Urls { if err != nil {
return nil, errors.Wrapf(err, "failed downloading %s", artifactName)
uri = filepath.Join(rootfs, uri)
Info("Downloading artifact", artifactName, "from", uri)
//defer os.Remove(file.Name())
err = fileHelper.CopyFile(filepath.Join(uri, artifactName), cacheFile)
if err != nil {
continue
}
ok = true
break
} }
if !ok { newart.Path = d
return nil, err c.Cache.Put(newart)
}
} }
newart := a
newart.Path = cacheFile
return newart, nil return newart, nil
} }

View File

@@ -20,7 +20,7 @@ import (
"os" "os"
"path/filepath" "path/filepath"
"github.com/mudler/luet/pkg/compiler/types/artifact" "github.com/mudler/luet/pkg/api/core/types/artifact"
fileHelper "github.com/mudler/luet/pkg/helpers/file" fileHelper "github.com/mudler/luet/pkg/helpers/file"
. "github.com/mudler/luet/pkg/installer/client" . "github.com/mudler/luet/pkg/installer/client"
. "github.com/onsi/ginkgo" . "github.com/onsi/ginkgo"

View File

@@ -25,8 +25,8 @@ import (
"sync" "sync"
"github.com/mudler/luet/pkg/api/core/types" "github.com/mudler/luet/pkg/api/core/types"
artifact "github.com/mudler/luet/pkg/api/core/types/artifact"
"github.com/mudler/luet/pkg/bus" "github.com/mudler/luet/pkg/bus"
artifact "github.com/mudler/luet/pkg/compiler/types/artifact"
"github.com/mudler/luet/pkg/config" "github.com/mudler/luet/pkg/config"
"github.com/mudler/luet/pkg/helpers" "github.com/mudler/luet/pkg/helpers"
fileHelper "github.com/mudler/luet/pkg/helpers/file" fileHelper "github.com/mudler/luet/pkg/helpers/file"

View File

@@ -16,7 +16,7 @@
package installer package installer
import ( import (
artifact "github.com/mudler/luet/pkg/compiler/types/artifact" artifact "github.com/mudler/luet/pkg/api/core/types/artifact"
//"github.com/mudler/luet/pkg/solver" //"github.com/mudler/luet/pkg/solver"
) )

View File

@@ -27,7 +27,7 @@ import (
"strings" "strings"
"time" "time"
artifact "github.com/mudler/luet/pkg/compiler/types/artifact" artifact "github.com/mudler/luet/pkg/api/core/types/artifact"
compression "github.com/mudler/luet/pkg/compiler/types/compression" compression "github.com/mudler/luet/pkg/compiler/types/compression"
fileHelper "github.com/mudler/luet/pkg/helpers/file" fileHelper "github.com/mudler/luet/pkg/helpers/file"
"go.uber.org/multierr" "go.uber.org/multierr"

View File

@@ -24,10 +24,10 @@ import (
"strings" "strings"
"time" "time"
artifact "github.com/mudler/luet/pkg/api/core/types/artifact"
"github.com/mudler/luet/pkg/bus" "github.com/mudler/luet/pkg/bus"
compiler "github.com/mudler/luet/pkg/compiler" compiler "github.com/mudler/luet/pkg/compiler"
"github.com/mudler/luet/pkg/compiler/backend" "github.com/mudler/luet/pkg/compiler/backend"
artifact "github.com/mudler/luet/pkg/compiler/types/artifact"
"github.com/mudler/luet/pkg/config" "github.com/mudler/luet/pkg/config"
"github.com/mudler/luet/pkg/helpers" "github.com/mudler/luet/pkg/helpers"
"github.com/mudler/luet/pkg/helpers/docker" "github.com/mudler/luet/pkg/helpers/docker"

View File

@@ -24,7 +24,7 @@ import (
"strings" "strings"
"time" "time"
artifact "github.com/mudler/luet/pkg/compiler/types/artifact" artifact "github.com/mudler/luet/pkg/api/core/types/artifact"
. "github.com/mudler/luet/pkg/logger" . "github.com/mudler/luet/pkg/logger"
pkg "github.com/mudler/luet/pkg/package" pkg "github.com/mudler/luet/pkg/package"

View File

@@ -25,9 +25,9 @@ import (
"path/filepath" "path/filepath"
"github.com/mudler/luet/pkg/api/core/types" "github.com/mudler/luet/pkg/api/core/types"
artifact "github.com/mudler/luet/pkg/api/core/types/artifact"
"github.com/mudler/luet/pkg/compiler" "github.com/mudler/luet/pkg/compiler"
backend "github.com/mudler/luet/pkg/compiler/backend" backend "github.com/mudler/luet/pkg/compiler/backend"
artifact "github.com/mudler/luet/pkg/compiler/types/artifact"
compilerspec "github.com/mudler/luet/pkg/compiler/types/spec" compilerspec "github.com/mudler/luet/pkg/compiler/types/spec"
"github.com/mudler/luet/pkg/helpers" "github.com/mudler/luet/pkg/helpers"
fileHelper "github.com/mudler/luet/pkg/helpers/file" fileHelper "github.com/mudler/luet/pkg/helpers/file"