mirror of
https://github.com/mudler/luet.git
synced 2025-09-02 07:45:02 +00:00
Add salted method for assertion hashing
- Add the spec Hash as salt for image hashes - Add tests and adapt existing ones - Use a signature to build a spec hash Fixes: #207
This commit is contained in:
1
go.mod
1
go.mod
@@ -32,6 +32,7 @@ require (
|
|||||||
github.com/kyokomi/emoji v2.1.0+incompatible
|
github.com/kyokomi/emoji v2.1.0+incompatible
|
||||||
github.com/logrusorgru/aurora v0.0.0-20190417123914-21d75270181e
|
github.com/logrusorgru/aurora v0.0.0-20190417123914-21d75270181e
|
||||||
github.com/marcsauter/single v0.0.0-20181104081128-f8bf46f26ec0
|
github.com/marcsauter/single v0.0.0-20181104081128-f8bf46f26ec0
|
||||||
|
github.com/mitchellh/hashstructure/v2 v2.0.1
|
||||||
github.com/moby/buildkit v0.7.2
|
github.com/moby/buildkit v0.7.2
|
||||||
github.com/moby/sys/mount v0.2.0 // indirect
|
github.com/moby/sys/mount v0.2.0 // indirect
|
||||||
github.com/mudler/cobra-extensions v0.0.0-20200612154940-31a47105fe3d
|
github.com/mudler/cobra-extensions v0.0.0-20200612154940-31a47105fe3d
|
||||||
|
2
go.sum
2
go.sum
@@ -728,6 +728,8 @@ github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS4
|
|||||||
github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
|
github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
|
||||||
github.com/mitchellh/hashstructure v1.0.0 h1:ZkRJX1CyOoTkar7p/mLS5TZU4nJ1Rn/F8u9dGS02Q3Y=
|
github.com/mitchellh/hashstructure v1.0.0 h1:ZkRJX1CyOoTkar7p/mLS5TZU4nJ1Rn/F8u9dGS02Q3Y=
|
||||||
github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
|
github.com/mitchellh/hashstructure v1.0.0/go.mod h1:QjSHrPWS+BGUVBYkbTZWEnOh3G1DutKwClXU/ABz6AQ=
|
||||||
|
github.com/mitchellh/hashstructure/v2 v2.0.1 h1:L60q1+q7cXE4JeEJJKMnh2brFIe3rZxCihYAB61ypAY=
|
||||||
|
github.com/mitchellh/hashstructure/v2 v2.0.1/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
|
||||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||||
github.com/mitchellh/mapstructure v0.0.0-20150613213606-2caf8efc9366/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v0.0.0-20150613213606-2caf8efc9366/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||||
|
@@ -109,13 +109,29 @@ func (ht *ImageHashTree) resolve(cs *LuetCompiler, p *compilerspec.LuetCompilati
|
|||||||
return nil, errors.Wrap(err, "While computing a solution for "+p.GetPackage().HumanReadableString())
|
return nil, errors.Wrap(err, "While computing a solution for "+p.GetPackage().HumanReadableString())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Get hash from buildpsecs
|
||||||
|
salts := map[string]string{}
|
||||||
|
for _, assertion := range dependencies { //highly dependent on the order
|
||||||
|
if assertion.Value {
|
||||||
|
spec, err := cs.FromPackage(assertion.Package)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "while computing hash buildspecs")
|
||||||
|
}
|
||||||
|
hash, err := spec.Hash()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.Wrap(err, "failed computing hash")
|
||||||
|
}
|
||||||
|
salts[assertion.Package.GetFingerPrint()] = hash
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
assertions := solver.PackagesAssertions{}
|
assertions := solver.PackagesAssertions{}
|
||||||
for _, assertion := range dependencies { //highly dependent on the order
|
for _, assertion := range dependencies { //highly dependent on the order
|
||||||
if assertion.Value {
|
if assertion.Value {
|
||||||
nthsolution := dependencies.Cut(assertion.Package)
|
nthsolution := dependencies.Cut(assertion.Package)
|
||||||
assertion.Hash = solver.PackageHash{
|
assertion.Hash = solver.PackageHash{
|
||||||
BuildHash: nthsolution.HashFrom(assertion.Package),
|
BuildHash: nthsolution.SaltedHashFrom(assertion.Package, salts),
|
||||||
PackageHash: nthsolution.AssertionHash(),
|
PackageHash: nthsolution.SaltedAssertionHash(salts),
|
||||||
}
|
}
|
||||||
assertion.Package.SetTreeDir(p.Package.GetTreeDir())
|
assertion.Package.SetTreeDir(p.Package.GetTreeDir())
|
||||||
assertions = append(assertions, assertion)
|
assertions = append(assertions, assertion)
|
||||||
|
@@ -46,12 +46,15 @@ var _ = Describe("ImageHashTree", func() {
|
|||||||
|
|
||||||
packageHash, err := hashtree.Query(compiler, spec)
|
packageHash, err := hashtree.Query(compiler, spec)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(packageHash.Target.Hash.BuildHash).To(Equal("6490e800fe443b99328fc363529aee74bda513930fb27ce6ab814d692bba068e"))
|
|
||||||
Expect(packageHash.Target.Hash.PackageHash).To(Equal("79d7107d13d578b362e6a7bf10ec850efce26316405b8d732ce8f9e004d64281"))
|
Expect(packageHash.Target.Hash.BuildHash).To(Equal("ec62e3e2cfb4c520c8b2561797c005d248c2659295f3660fa1a66582fc4dc280"))
|
||||||
Expect(packageHash.BuilderImageHash).To(Equal("builder-79462b60bf899ad79db63f194a3c9c2a"))
|
Expect(packageHash.Target.Hash.PackageHash).To(Equal("5fa15a0eb0534eaa78ef1b4e32fe72704effaa5e54399b7cab6d630aa0aeac5c"))
|
||||||
|
Expect(packageHash.BuilderImageHash).To(Equal("builder-96e0c42b5741376ebcf0a47c8ec1c481"))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
expectedPackageHash := "bc6d354e8b9480b70c6f17eafa34cef387b8443ad150b7c9528fb7e94b764e90"
|
||||||
|
|
||||||
Context("complex package definition", func() {
|
Context("complex package definition", func() {
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
generalRecipe = tree.NewCompilerRecipe(pkg.NewInMemoryDatabase(false))
|
generalRecipe = tree.NewCompilerRecipe(pkg.NewInMemoryDatabase(false))
|
||||||
@@ -69,25 +72,75 @@ var _ = Describe("ImageHashTree", func() {
|
|||||||
packageHash, err := hashtree.Query(compiler, spec)
|
packageHash, err := hashtree.Query(compiler, spec)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
Expect(packageHash.Dependencies[len(packageHash.Dependencies)-1].Hash.PackageHash).To(Equal("c46e653125d71ee3fd696b3941ec1ed6e8a0268f896204c7a222a5aa03eb9982"))
|
Expect(packageHash.Dependencies[len(packageHash.Dependencies)-1].Hash.PackageHash).To(Equal(expectedPackageHash))
|
||||||
Expect(packageHash.SourceHash).To(Equal("c46e653125d71ee3fd696b3941ec1ed6e8a0268f896204c7a222a5aa03eb9982"))
|
Expect(packageHash.SourceHash).To(Equal(expectedPackageHash))
|
||||||
Expect(packageHash.BuilderImageHash).To(Equal("builder-37f4d05ba8a39525742ca364f69b4090"))
|
Expect(packageHash.BuilderImageHash).To(Equal("builder-9b2bc16985446c41eca8f7922ec98078"))
|
||||||
|
|
||||||
//Expect(packageHash.Target.Hash.BuildHash).To(Equal("79d7107d13d578b362e6a7bf10ec850efce26316405b8d732ce8f9e004d64281"))
|
//Expect(packageHash.Target.Hash.BuildHash).To(Equal("79d7107d13d578b362e6a7bf10ec850efce26316405b8d732ce8f9e004d64281"))
|
||||||
Expect(packageHash.Target.Hash.PackageHash).To(Equal("bb1d9a99c0c309a297c75b436504e664a42121fadbb4e035bda403cd418117aa"))
|
Expect(packageHash.Target.Hash.PackageHash).To(Equal("bb84a30ced857725fcb575e87fe33d4aefe911abfdd5f9063bbaeb9e4b94e9e2"))
|
||||||
a := &pkg.DefaultPackage{Name: "a", Category: "test", Version: "1.1"}
|
a := &pkg.DefaultPackage{Name: "a", Category: "test", Version: "1.1"}
|
||||||
hash, err := packageHash.DependencyBuildImage(a)
|
hash, err := packageHash.DependencyBuildImage(a)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(hash).To(Equal("79d7107d13d578b362e6a7bf10ec850efce26316405b8d732ce8f9e004d64281"))
|
|
||||||
|
Expect(hash).To(Equal("484f14294d96fd3b51cec1f2db37a269b7b903f3516b74b0cb0771b65d85b799"))
|
||||||
|
|
||||||
assertionA := packageHash.Dependencies.Search(a.GetFingerPrint())
|
assertionA := packageHash.Dependencies.Search(a.GetFingerPrint())
|
||||||
Expect(assertionA.Hash.PackageHash).To(Equal("c46e653125d71ee3fd696b3941ec1ed6e8a0268f896204c7a222a5aa03eb9982"))
|
Expect(assertionA.Hash.PackageHash).To(Equal(expectedPackageHash))
|
||||||
b := &pkg.DefaultPackage{Name: "b", Category: "test", Version: "1.0"}
|
b := &pkg.DefaultPackage{Name: "b", Category: "test", Version: "1.0"}
|
||||||
assertionB := packageHash.Dependencies.Search(b.GetFingerPrint())
|
assertionB := packageHash.Dependencies.Search(b.GetFingerPrint())
|
||||||
Expect(assertionB.Hash.PackageHash).To(Equal("79d7107d13d578b362e6a7bf10ec850efce26316405b8d732ce8f9e004d64281"))
|
Expect(assertionB.Hash.PackageHash).To(Equal("484f14294d96fd3b51cec1f2db37a269b7b903f3516b74b0cb0771b65d85b799"))
|
||||||
hashB, err := packageHash.DependencyBuildImage(b)
|
hashB, err := packageHash.DependencyBuildImage(b)
|
||||||
Expect(err).ToNot(HaveOccurred())
|
Expect(err).ToNot(HaveOccurred())
|
||||||
Expect(hashB).To(Equal("6490e800fe443b99328fc363529aee74bda513930fb27ce6ab814d692bba068e"))
|
Expect(hashB).To(Equal("828c983e755353190540565a29e71c9eb4c48d6303e1fd2c523235b7c2339c73"))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
Context("complex package definition, with small change in build.yaml", func() {
|
||||||
|
BeforeEach(func() {
|
||||||
|
generalRecipe = tree.NewCompilerRecipe(pkg.NewInMemoryDatabase(false))
|
||||||
|
|
||||||
|
//Definition of A here is slightly changed in the steps build.yaml file (1 character only)
|
||||||
|
err := generalRecipe.Load("../../tests/fixtures/upgrade_old_repo_revision_content_changed")
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
compiler = NewLuetCompiler(sd.NewSimpleDockerBackend(), generalRecipe.GetDatabase(), options.Concurrency(2))
|
||||||
|
hashtree = NewHashTree(generalRecipe.GetDatabase())
|
||||||
|
|
||||||
|
})
|
||||||
|
It("Calculates the hash correctly", func() {
|
||||||
|
spec, err := compiler.FromPackage(&pkg.DefaultPackage{Name: "c", Category: "test", Version: "1.0"})
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
packageHash, err := hashtree.Query(compiler, spec)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
Expect(packageHash.Dependencies[len(packageHash.Dependencies)-1].Hash.PackageHash).ToNot(Equal(expectedPackageHash))
|
||||||
|
sourceHash := "ed1bd90e696904982a1f51998646a335067329e1a262994b5ae15c579106ac81"
|
||||||
|
Expect(packageHash.Dependencies[len(packageHash.Dependencies)-1].Hash.PackageHash).To(Equal(sourceHash))
|
||||||
|
Expect(packageHash.SourceHash).To(Equal(sourceHash))
|
||||||
|
Expect(packageHash.SourceHash).ToNot(Equal(expectedPackageHash))
|
||||||
|
|
||||||
|
Expect(packageHash.BuilderImageHash).To(Equal("builder-f4b0e366e0a42774428fbdc9aa325648"))
|
||||||
|
|
||||||
|
//Expect(packageHash.Target.Hash.BuildHash).To(Equal("79d7107d13d578b362e6a7bf10ec850efce26316405b8d732ce8f9e004d64281"))
|
||||||
|
Expect(packageHash.Target.Hash.PackageHash).To(Equal("2618f12851a596f6801e2665e07147da98a0a151f44500a54ca8b76b869e378d"))
|
||||||
|
a := &pkg.DefaultPackage{Name: "a", Category: "test", Version: "1.1"}
|
||||||
|
hash, err := packageHash.DependencyBuildImage(a)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(hash).To(Equal("484f14294d96fd3b51cec1f2db37a269b7b903f3516b74b0cb0771b65d85b799"))
|
||||||
|
|
||||||
|
assertionA := packageHash.Dependencies.Search(a.GetFingerPrint())
|
||||||
|
|
||||||
|
Expect(assertionA.Hash.PackageHash).To(Equal("ed1bd90e696904982a1f51998646a335067329e1a262994b5ae15c579106ac81"))
|
||||||
|
Expect(assertionA.Hash.PackageHash).ToNot(Equal(expectedPackageHash))
|
||||||
|
|
||||||
|
b := &pkg.DefaultPackage{Name: "b", Category: "test", Version: "1.0"}
|
||||||
|
assertionB := packageHash.Dependencies.Search(b.GetFingerPrint())
|
||||||
|
|
||||||
|
Expect(assertionB.Hash.PackageHash).To(Equal("484f14294d96fd3b51cec1f2db37a269b7b903f3516b74b0cb0771b65d85b799"))
|
||||||
|
hashB, err := packageHash.DependencyBuildImage(b)
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
Expect(hashB).To(Equal("828c983e755353190540565a29e71c9eb4c48d6303e1fd2c523235b7c2339c73"))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
@@ -20,6 +20,7 @@ import (
|
|||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/mitchellh/hashstructure/v2"
|
||||||
options "github.com/mudler/luet/pkg/compiler/types/options"
|
options "github.com/mudler/luet/pkg/compiler/types/options"
|
||||||
|
|
||||||
pkg "github.com/mudler/luet/pkg/package"
|
pkg "github.com/mudler/luet/pkg/package"
|
||||||
@@ -226,6 +227,44 @@ func (cs *LuetCompilationSpec) HasImageSource() bool {
|
|||||||
return (cs.Package != nil && len(cs.GetPackage().GetRequires()) != 0) || cs.GetImage() != ""
|
return (cs.Package != nil && len(cs.GetPackage().GetRequires()) != 0) || cs.GetImage() != ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Signature is a portion of the spec that yields a signature for the hash
|
||||||
|
type Signature struct {
|
||||||
|
Image string
|
||||||
|
Steps []string
|
||||||
|
PackageDir string
|
||||||
|
Prelude []string
|
||||||
|
Seed string
|
||||||
|
Env []string
|
||||||
|
Retrieve []string
|
||||||
|
Unpack bool
|
||||||
|
Includes []string
|
||||||
|
Excludes []string
|
||||||
|
Copy []CopyField
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *LuetCompilationSpec) signature() Signature {
|
||||||
|
return Signature{
|
||||||
|
Image: cs.Image,
|
||||||
|
Steps: cs.Steps,
|
||||||
|
PackageDir: cs.PackageDir,
|
||||||
|
Prelude: cs.Prelude,
|
||||||
|
Seed: cs.Seed,
|
||||||
|
Env: cs.Env,
|
||||||
|
Retrieve: cs.Retrieve,
|
||||||
|
Unpack: cs.Unpack,
|
||||||
|
Includes: cs.Includes,
|
||||||
|
Excludes: cs.Excludes,
|
||||||
|
Copy: cs.Copy,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cs *LuetCompilationSpec) Hash() (string, error) {
|
||||||
|
// build a signature, we want to be part of the hash only the fields that are relevant for build purposes
|
||||||
|
signature := cs.signature()
|
||||||
|
h, err := hashstructure.Hash(signature, hashstructure.FormatV2, nil)
|
||||||
|
return fmt.Sprint(h), err
|
||||||
|
}
|
||||||
|
|
||||||
func (cs *LuetCompilationSpec) CopyRetrieves(dest string) error {
|
func (cs *LuetCompilationSpec) CopyRetrieves(dest string) error {
|
||||||
var err error
|
var err error
|
||||||
if len(cs.Retrieve) > 0 {
|
if len(cs.Retrieve) > 0 {
|
||||||
|
@@ -20,6 +20,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
||||||
|
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/compiler"
|
. "github.com/mudler/luet/pkg/compiler"
|
||||||
@@ -74,6 +75,62 @@ var _ = Describe("Spec", func() {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
Context("Image hashing", func() {
|
||||||
|
It("is stable", func() {
|
||||||
|
spec1 := &compilerspec.LuetCompilationSpec{
|
||||||
|
Image: "foo",
|
||||||
|
BuildOptions: &options.Compiler{BuildValues: []map[string]interface{}{{"foo": "bar", "baz": true}}},
|
||||||
|
|
||||||
|
Package: &pkg.DefaultPackage{
|
||||||
|
Name: "foo",
|
||||||
|
Category: "Bar",
|
||||||
|
Labels: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
"baz": "foo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
spec2 := &compilerspec.LuetCompilationSpec{
|
||||||
|
Image: "foo",
|
||||||
|
BuildOptions: &options.Compiler{BuildValues: []map[string]interface{}{{"foo": "bar", "baz": true}}},
|
||||||
|
Package: &pkg.DefaultPackage{
|
||||||
|
Name: "foo",
|
||||||
|
Category: "Bar",
|
||||||
|
Labels: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
"baz": "foo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
spec3 := &compilerspec.LuetCompilationSpec{
|
||||||
|
Image: "foo",
|
||||||
|
Steps: []string{"foo"},
|
||||||
|
Package: &pkg.DefaultPackage{
|
||||||
|
Name: "foo",
|
||||||
|
Category: "Bar",
|
||||||
|
Labels: map[string]string{
|
||||||
|
"foo": "bar",
|
||||||
|
"baz": "foo",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
hash, err := spec1.Hash()
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
hash2, err := spec2.Hash()
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
hash3, err := spec3.Hash()
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
|
||||||
|
Expect(hash).To(Equal(hash2))
|
||||||
|
hashagain, err := spec2.Hash()
|
||||||
|
Expect(err).ToNot(HaveOccurred())
|
||||||
|
Expect(hash).ToNot(Equal(hash3))
|
||||||
|
Expect(hash).To(Equal(hashagain))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
Context("Simple package build definition", func() {
|
Context("Simple package build definition", func() {
|
||||||
It("Loads it correctly", func() {
|
It("Loads it correctly", func() {
|
||||||
generalRecipe := tree.NewGeneralRecipe(pkg.NewInMemoryDatabase(false))
|
generalRecipe := tree.NewGeneralRecipe(pkg.NewInMemoryDatabase(false))
|
||||||
|
@@ -118,6 +118,8 @@ type Package interface {
|
|||||||
SetTreeDir(s string)
|
SetTreeDir(s string)
|
||||||
GetTreeDir() string
|
GetTreeDir() string
|
||||||
|
|
||||||
|
Mark() Package
|
||||||
|
|
||||||
JSON() ([]byte, error)
|
JSON() ([]byte, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -492,6 +494,12 @@ func (p *DefaultPackage) Matches(m Package) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *DefaultPackage) Mark() Package {
|
||||||
|
marked := p.Clone()
|
||||||
|
marked.SetName("@@" + marked.GetName())
|
||||||
|
return marked
|
||||||
|
}
|
||||||
|
|
||||||
func (p *DefaultPackage) Expand(definitiondb PackageDatabase) (Packages, error) {
|
func (p *DefaultPackage) Expand(definitiondb PackageDatabase) (Packages, error) {
|
||||||
var versionsInWorld Packages
|
var versionsInWorld Packages
|
||||||
|
|
||||||
|
@@ -260,24 +260,42 @@ func (a PackagesAssertions) TrueLen() int {
|
|||||||
// and checks it's not the only one. if it's unique it marks it specially - so the hash
|
// and checks it's not the only one. if it's unique it marks it specially - so the hash
|
||||||
// which is generated is unique for the selected package
|
// which is generated is unique for the selected package
|
||||||
func (assertions PackagesAssertions) HashFrom(p pkg.Package) string {
|
func (assertions PackagesAssertions) HashFrom(p pkg.Package) string {
|
||||||
|
return assertions.SaltedHashFrom(p, map[string]string{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (assertions PackagesAssertions) AssertionHash() string {
|
||||||
|
return assertions.SaltedAssertionHash(map[string]string{})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (assertions PackagesAssertions) SaltedHashFrom(p pkg.Package, salts map[string]string) string {
|
||||||
var assertionhash string
|
var assertionhash string
|
||||||
|
|
||||||
// When we don't have any solution to hash for, we need to generate an UUID by ourselves
|
// When we don't have any solution to hash for, we need to generate an UUID by ourselves
|
||||||
latestsolution := assertions.Drop(p)
|
latestsolution := assertions.Drop(p)
|
||||||
if latestsolution.TrueLen() == 0 {
|
if latestsolution.TrueLen() == 0 {
|
||||||
assertionhash = assertions.Mark(p).AssertionHash()
|
// Preserve the hash if supplied of marked packages
|
||||||
|
marked := p.Mark()
|
||||||
|
if markedHash, exists := salts[p.GetFingerPrint()]; exists {
|
||||||
|
salts[marked.GetFingerPrint()] = markedHash
|
||||||
|
}
|
||||||
|
assertionhash = assertions.Mark(p).SaltedAssertionHash(salts)
|
||||||
} else {
|
} else {
|
||||||
assertionhash = latestsolution.AssertionHash()
|
assertionhash = latestsolution.SaltedAssertionHash(salts)
|
||||||
}
|
}
|
||||||
return assertionhash
|
return assertionhash
|
||||||
}
|
}
|
||||||
|
|
||||||
func (assertions PackagesAssertions) AssertionHash() string {
|
func (assertions PackagesAssertions) SaltedAssertionHash(salts map[string]string) string {
|
||||||
var fingerprint string
|
var fingerprint string
|
||||||
for _, assertion := range assertions { // Note: Always order them first!
|
for _, assertion := range assertions { // Note: Always order them first!
|
||||||
if assertion.Value { // Tke into account only dependencies installed (get fingerprint of subgraph)
|
if assertion.Value { // Tke into account only dependencies installed (get fingerprint of subgraph)
|
||||||
fingerprint += assertion.ToString() + "\n"
|
salt, exists := salts[assertion.Package.GetFingerPrint()]
|
||||||
|
if exists {
|
||||||
|
fingerprint += assertion.ToString() + salt + "\n"
|
||||||
|
|
||||||
|
} else {
|
||||||
|
fingerprint += assertion.ToString() + "\n"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
hash := sha256.Sum256([]byte(fingerprint))
|
hash := sha256.Sum256([]byte(fingerprint))
|
||||||
@@ -316,8 +334,7 @@ func (assertions PackagesAssertions) Mark(p pkg.Package) PackagesAssertions {
|
|||||||
|
|
||||||
for _, a := range assertions {
|
for _, a := range assertions {
|
||||||
if a.Package.Matches(p) {
|
if a.Package.Matches(p) {
|
||||||
marked := a.Package.Clone()
|
marked := a.Package.Mark()
|
||||||
marked.SetName("@@" + marked.GetName())
|
|
||||||
a = PackageAssert{Package: marked.(*pkg.DefaultPackage), Value: a.Value, Hash: a.Hash}
|
a = PackageAssert{Package: marked.(*pkg.DefaultPackage), Value: a.Value, Hash: a.Hash}
|
||||||
}
|
}
|
||||||
ass = append(ass, a)
|
ass = append(ass, a)
|
||||||
|
@@ -382,6 +382,9 @@ var _ = Describe("Decoder", func() {
|
|||||||
|
|
||||||
Expect(solution.HashFrom(X)).ToNot(Equal(solution2.HashFrom(F)))
|
Expect(solution.HashFrom(X)).ToNot(Equal(solution2.HashFrom(F)))
|
||||||
Expect(solution3.HashFrom(D)).To(Equal(solution.HashFrom(X)))
|
Expect(solution3.HashFrom(D)).To(Equal(solution.HashFrom(X)))
|
||||||
|
Expect(solution3.SaltedHashFrom(D, map[string]string{D.GetFingerPrint(): "foo"})).ToNot(Equal(solution3.HashFrom(D)))
|
||||||
|
|
||||||
|
Expect(solution4.SaltedHashFrom(Y, map[string]string{X.GetFingerPrint(): "foo"})).ToNot(Equal(solution4.HashFrom(Y)))
|
||||||
|
|
||||||
Expect(empty.AssertionHash()).ToNot(Equal(solution3.HashFrom(D)))
|
Expect(empty.AssertionHash()).ToNot(Equal(solution3.HashFrom(D)))
|
||||||
Expect(empty.AssertionHash()).ToNot(Equal(solution2.HashFrom(F)))
|
Expect(empty.AssertionHash()).ToNot(Equal(solution2.HashFrom(F)))
|
||||||
|
10
tests/fixtures/upgrade_old_repo_revision_content_changed/c/build.yaml
vendored
Normal file
10
tests/fixtures/upgrade_old_repo_revision_content_changed/c/build.yaml
vendored
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
prelude:
|
||||||
|
- echo foo > /test
|
||||||
|
- echo bar > /test2
|
||||||
|
steps:
|
||||||
|
- echo c > /c
|
||||||
|
- echo c > /cd
|
||||||
|
requires:
|
||||||
|
- category: "test"
|
||||||
|
name: "a"
|
||||||
|
version: ">=1.0"
|
3
tests/fixtures/upgrade_old_repo_revision_content_changed/c/definition.yaml
vendored
Normal file
3
tests/fixtures/upgrade_old_repo_revision_content_changed/c/definition.yaml
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
category: "test"
|
||||||
|
name: "c"
|
||||||
|
version: "1.0"
|
11
tests/fixtures/upgrade_old_repo_revision_content_changed/cat/a/a/build.yaml
vendored
Normal file
11
tests/fixtures/upgrade_old_repo_revision_content_changed/cat/a/a/build.yaml
vendored
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
image: "alpine"
|
||||||
|
prelude:
|
||||||
|
- echo fozo > /test
|
||||||
|
- echo bar > /test2
|
||||||
|
steps:
|
||||||
|
- echo artifact3 > /test3
|
||||||
|
- echo artifact4 > /test4
|
||||||
|
requires:
|
||||||
|
- category: "test"
|
||||||
|
name: "b"
|
||||||
|
version: "1.0"
|
8
tests/fixtures/upgrade_old_repo_revision_content_changed/cat/a/a/definition.yaml
vendored
Normal file
8
tests/fixtures/upgrade_old_repo_revision_content_changed/cat/a/a/definition.yaml
vendored
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
category: "test"
|
||||||
|
name: "a"
|
||||||
|
version: "1.1"
|
||||||
|
requires:
|
||||||
|
- category: "test2"
|
||||||
|
name: "b"
|
||||||
|
version: "1.0"
|
||||||
|
|
9
tests/fixtures/upgrade_old_repo_revision_content_changed/cat/b-1.1/build.yaml
vendored
Normal file
9
tests/fixtures/upgrade_old_repo_revision_content_changed/cat/b-1.1/build.yaml
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
image: "alpine"
|
||||||
|
prelude:
|
||||||
|
- echo foo > /test
|
||||||
|
- echo bar > /test2
|
||||||
|
steps:
|
||||||
|
- echo artifact5 > /newc
|
||||||
|
- echo artifact6 > /newnewc
|
||||||
|
- chmod +x generate.sh
|
||||||
|
- ./generate.sh
|
3
tests/fixtures/upgrade_old_repo_revision_content_changed/cat/b-1.1/definition.yaml
vendored
Normal file
3
tests/fixtures/upgrade_old_repo_revision_content_changed/cat/b-1.1/definition.yaml
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
category: "test"
|
||||||
|
name: "b"
|
||||||
|
version: "1.0"
|
1
tests/fixtures/upgrade_old_repo_revision_content_changed/cat/b-1.1/generate.sh
vendored
Normal file
1
tests/fixtures/upgrade_old_repo_revision_content_changed/cat/b-1.1/generate.sh
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
echo generated > /sonewc
|
@@ -51,9 +51,9 @@ testBuild() {
|
|||||||
assertTrue 'create package' "[ -e '$tmpdir/testbuild/c-test-1.0.package.tar.zst' ]"
|
assertTrue 'create package' "[ -e '$tmpdir/testbuild/c-test-1.0.package.tar.zst' ]"
|
||||||
assertTrue 'create package Z' "[ -e '$tmpdir/testbuild/z-test-1.0+2.package.tar.zst' ]"
|
assertTrue 'create package Z' "[ -e '$tmpdir/testbuild/z-test-1.0+2.package.tar.zst' ]"
|
||||||
assertTrue 'create package interpolated' "[ -e '$tmpdir/testbuild/interpolated-test-1.0+2.package.tar.zst' ]"
|
assertTrue 'create package interpolated' "[ -e '$tmpdir/testbuild/interpolated-test-1.0+2.package.tar.zst' ]"
|
||||||
assertContains 'Does use the upstream cache without specifying it test/c' "$build_output" "Images available for test/c-1.0 generating artifact from remote images: quay.io/mocaccinoos/integration-test-cache:d620e573c81eab36a9dc5cc314e80fd7b6e04aeff26127de4225bf24fe1f8e71"
|
assertContains 'Does use the upstream cache without specifying it test/c' "$build_output" "Images available for test/c-1.0 generating artifact from remote images: quay.io/mocaccinoos/integration-test-cache:7dd6062f45e78c1fe36d0f48fc21bc8c8219edfc9759f117527677a15ae42717"
|
||||||
assertContains 'Does use the upstream cache without specifying it test/z' "$build_output" "Images available for test/z-1.0+2 generating artifact from remote images: quay.io/mocaccinoos/integration-test-cache:b0f34b0d2d271f0f2619324476b2857b3b39ca895bddc2474a741f3c8c1acbbc"
|
assertContains 'Does use the upstream cache without specifying it test/z' "$build_output" "Images available for test/z-1.0+2 generating artifact from remote images: quay.io/mocaccinoos/integration-test-cache:2338dc4dc4b3d9657eb26597ad79569fe60f5529dd05676df266ad982ba2b1ba"
|
||||||
assertContains 'Does use the upstream cache without specifying it test/interpolated' "$build_output" "Images available for test/interpolated-1.0+2 generating artifact from remote images: quay.io/mocaccinoos/integration-test-cache:c1f11f48113cd71d8795a06c7b49e1558bd7211d2aa88f5d79a3334f0393c64d"
|
assertContains 'Does use the upstream cache without specifying it test/interpolated' "$build_output" "Images available for test/interpolated-1.0+2 generating artifact from remote images: quay.io/mocaccinoos/integration-test-cache:2edc4b6f8fc08a0fc958132dfcf2813e1ab220c2bbf60b988f1039082d61ff3e"
|
||||||
}
|
}
|
||||||
|
|
||||||
testRepo() {
|
testRepo() {
|
||||||
|
@@ -45,7 +45,7 @@ EOF
|
|||||||
mkdir $tmpdir/testbuild
|
mkdir $tmpdir/testbuild
|
||||||
mkdir $tmpdir/empty
|
mkdir $tmpdir/empty
|
||||||
|
|
||||||
# With --rebuild, the package gets ignored
|
# Without --rebuild, the package gets ignored
|
||||||
build_output=$(luet build --pull --tree "$tmpdir/empty" \
|
build_output=$(luet build --pull --tree "$tmpdir/empty" \
|
||||||
--config $tmpdir/luet.yaml --values $tmpdir/default.yaml --concurrency 1 \
|
--config $tmpdir/luet.yaml --values $tmpdir/default.yaml --concurrency 1 \
|
||||||
--from-repositories --destination $tmpdir/testbuild --compression zstd test/c@1.0 test/z test/interpolated)
|
--from-repositories --destination $tmpdir/testbuild --compression zstd test/c@1.0 test/z test/interpolated)
|
||||||
@@ -56,7 +56,8 @@ EOF
|
|||||||
assertTrue 'create package' "[ -e '$tmpdir/testbuild/c-test-1.0.package.tar.zst' ]"
|
assertTrue 'create package' "[ -e '$tmpdir/testbuild/c-test-1.0.package.tar.zst' ]"
|
||||||
assertTrue 'create package Z' "[ -e '$tmpdir/testbuild/z-test-1.0+2.package.tar.zst' ]"
|
assertTrue 'create package Z' "[ -e '$tmpdir/testbuild/z-test-1.0+2.package.tar.zst' ]"
|
||||||
assertTrue 'create package interpolated' "[ -e '$tmpdir/testbuild/interpolated-test-1.0+2.package.tar.zst' ]"
|
assertTrue 'create package interpolated' "[ -e '$tmpdir/testbuild/interpolated-test-1.0+2.package.tar.zst' ]"
|
||||||
assertContains 'Does use the upstream cache without specifying it' "$build_output" "Images available for test/interpolated-1.0+2 generating artifact from remote images: quay.io/mocaccinoos/integration-test-cache:c1f11f48113cd71d8795a06c7b49e1558bd7211d2aa88f5d79a3334f0393c64d"
|
assertNotContains 'Does NOT use the upstream cache without specifying it' "$build_output" "Images available for test/interpolated-1.0+2 generating artifact from remote images: quay.io/mocaccinoos/integration-test-cache:2edc4b6f8fc08a0fc958132dfcf2813e1ab220c2bbf60b988f1039082d61ff3e"
|
||||||
|
assertContains 'Does generate a new hash as values changed build.yaml' "$build_output" "Building image luet/cache:a249d16764168baf32b592dc05c33e499fa3685edda72dfb188a51191709de5a done"
|
||||||
}
|
}
|
||||||
|
|
||||||
testRepo() {
|
testRepo() {
|
||||||
@@ -113,7 +114,7 @@ testInstall() {
|
|||||||
assertTrue 'package installed' "[ -e '$tmpdir/testrootfs/c' ]"
|
assertTrue 'package installed' "[ -e '$tmpdir/testrootfs/c' ]"
|
||||||
assertTrue 'package Z installed' "[ -e '$tmpdir/testrootfs/z' ]"
|
assertTrue 'package Z installed' "[ -e '$tmpdir/testrootfs/z' ]"
|
||||||
ls -liah $tmpdir/testrootfs/
|
ls -liah $tmpdir/testrootfs/
|
||||||
assertTrue 'package interpolated installed' "[ -e '$tmpdir/testrootfs/interpolated-baz-bar' ]"
|
assertTrue 'package interpolated installed' "[ -e '$tmpdir/testrootfs/interpolated-baz-an' ]"
|
||||||
}
|
}
|
||||||
|
|
||||||
testReInstall() {
|
testReInstall() {
|
||||||
|
@@ -56,7 +56,7 @@ EOF
|
|||||||
assertTrue 'create package' "[ -e '$tmpdir/testbuild/c-test-1.0.package.tar.zst' ]"
|
assertTrue 'create package' "[ -e '$tmpdir/testbuild/c-test-1.0.package.tar.zst' ]"
|
||||||
assertTrue 'create package Z' "[ -e '$tmpdir/testbuild/z-test-1.0+2.package.tar.zst' ]"
|
assertTrue 'create package Z' "[ -e '$tmpdir/testbuild/z-test-1.0+2.package.tar.zst' ]"
|
||||||
assertTrue 'create package interpolated' "[ -e '$tmpdir/testbuild/interpolated-test-1.0+2.package.tar.zst' ]"
|
assertTrue 'create package interpolated' "[ -e '$tmpdir/testbuild/interpolated-test-1.0+2.package.tar.zst' ]"
|
||||||
assertContains 'Does use the upstream cache without specifying it' "$build_output" "Downloading image quay.io/mocaccinoos/integration-test-cache:6490e800fe443b99328fc363529aee74bda513930fb27ce6ab814d692bba068e"
|
assertContains 'Does use the upstream cache without specifying it' "$build_output" "Downloading image quay.io/mocaccinoos/integration-test-cache:ec62e3e2cfb4c520c8b2561797c005d248c2659295f3660fa1a66582fc4dc280"
|
||||||
}
|
}
|
||||||
|
|
||||||
testRepo() {
|
testRepo() {
|
||||||
|
@@ -1,6 +1,6 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
set -e
|
set -e
|
||||||
|
export LUET_NO_SPINNER=true
|
||||||
export LUET_YES=true
|
export LUET_YES=true
|
||||||
export ROOT_DIR="$(git rev-parse --show-toplevel)"
|
export ROOT_DIR="$(git rev-parse --show-toplevel)"
|
||||||
|
|
||||||
|
3
vendor/modules.txt
vendored
3
vendor/modules.txt
vendored
@@ -376,6 +376,9 @@ github.com/mitchellh/colorstring
|
|||||||
github.com/mitchellh/copystructure
|
github.com/mitchellh/copystructure
|
||||||
# github.com/mitchellh/hashstructure v1.0.0
|
# github.com/mitchellh/hashstructure v1.0.0
|
||||||
github.com/mitchellh/hashstructure
|
github.com/mitchellh/hashstructure
|
||||||
|
# github.com/mitchellh/hashstructure/v2 v2.0.1
|
||||||
|
## explicit
|
||||||
|
github.com/mitchellh/hashstructure/v2
|
||||||
# github.com/mitchellh/mapstructure v1.1.2
|
# github.com/mitchellh/mapstructure v1.1.2
|
||||||
github.com/mitchellh/mapstructure
|
github.com/mitchellh/mapstructure
|
||||||
# github.com/mitchellh/reflectwalk v1.0.0
|
# github.com/mitchellh/reflectwalk v1.0.0
|
||||||
|
Reference in New Issue
Block a user