Skip to content
This repository has been archived by the owner on Feb 24, 2020. It is now read-only.

Commit

Permalink
cas: add a GetACI function.
Browse files Browse the repository at this point in the history
GetACI returns the best ACI that matches the required app name and the provided
labels.
If there are multiple matching ACIs choose the latest one (defined as the
last one imported in the store).
If no version label is requested, ACIs marked as latest in the ACIInfo are
preferred.
  • Loading branch information
sgotti committed Feb 5, 2015
1 parent 8a1b95b commit 2b97fe8
Show file tree
Hide file tree
Showing 2 changed files with 217 additions and 0 deletions.
71 changes: 71 additions & 0 deletions cas/cas.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ import (

"github.com/coreos/rocket/Godeps/_workspace/src/github.com/appc/spec/aci"
"github.com/coreos/rocket/Godeps/_workspace/src/github.com/appc/spec/schema"
"github.com/coreos/rocket/Godeps/_workspace/src/github.com/appc/spec/schema/types"

"github.com/coreos/rocket/Godeps/_workspace/src/github.com/peterbourgon/diskv"
)
Expand Down Expand Up @@ -256,6 +257,76 @@ func (ds Store) GetImageManifest(key string) (*schema.ImageManifest, error) {
return im, nil
}

// Get the best ACI that matches app name and the provided labels. It returns
// the blob store key of the given ACI.
// If there are multiple matching ACIs choose the latest one (defined as the
// last one imported in the store).
// If no version label is requested, ACIs marked as latest in the ACIInfo are
// preferred.
func (ds Store) GetACI(name types.ACName, labels types.Labels) (string, error) {
var curaciinfo *ACIInfo
versionRequested := false
if _, ok := labels.Get("version"); ok {
versionRequested = true
}

var aciinfos []*ACIInfo
err := ds.db.Do(func(tx *sql.Tx) error {
var err error
aciinfos, _, err = GetACIInfosWithAppName(tx, name.String())
return err
})
if err != nil {
return "", err
}

nextKey:
for _, aciinfo := range aciinfos {
im, err := ds.GetImageManifest(aciinfo.BlobKey)
if err != nil {
return "", fmt.Errorf("error getting image manifest: %v", err)
}

// The image manifest must have all the requested labels
for _, l := range labels {
ok := false
for _, rl := range im.Labels {
if l.Name == rl.Name && l.Value == rl.Value {
ok = true
break
}
}
if !ok {
continue nextKey
}
}

if curaciinfo != nil {
// If no version is requested prefer the acis marked as latest
if !versionRequested {
if !curaciinfo.Latest && aciinfo.Latest {
curaciinfo = aciinfo
continue nextKey
}
if curaciinfo.Latest && !aciinfo.Latest {
continue nextKey
}
}
// If multiple matching image manifests are found, choose the latest imported in the cas.
if aciinfo.ImportTime.After(curaciinfo.ImportTime) {
curaciinfo = aciinfo
}
} else {
curaciinfo = aciinfo
}
}

if curaciinfo != nil {
return curaciinfo.BlobKey, nil
}
return "", fmt.Errorf("aci not found")
}

func (ds Store) Dump(hex bool) {
for _, s := range ds.stores {
var keyCount int
Expand Down
146 changes: 146 additions & 0 deletions cas/cas_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -244,3 +244,149 @@ func TestGetImageManifest(t *testing.T) {
t.Fatalf("expected non-nil error!")
}
}

// Test an image with 1 dep. The parent provides a dir not provided by the image.
func TestGetAci(t *testing.T) {
type test struct {
name types.ACName
labels types.Labels
expected int // the aci index to expect or -1 if not result expected,
}

type acidef struct {
imj string
latest bool
}

dir, err := ioutil.TempDir("", tstprefix)
if err != nil {
t.Fatalf("error creating tempdir: %v", err)
}
defer os.RemoveAll(dir)
ds, err := NewStore(dir)
if err != nil {
t.Fatalf("unexpected error %v", err)
}

tests := []struct {
acidefs []acidef
tests []test
}{
{
[]acidef{
{
`{
"acKind": "ImageManifest",
"acVersion": "0.1.1",
"name": "example.com/test01"
}`,
false,
},
{
`{
"acKind": "ImageManifest",
"acVersion": "0.1.1",
"name": "example.com/test02",
"labels": [
{
"name": "version",
"value": "1.0.0"
}
]
}`,
true,
},
{
`{
"acKind": "ImageManifest",
"acVersion": "0.1.1",
"name": "example.com/test02",
"labels": [
{
"name": "version",
"value": "2.0.0"
}
]
}`,
false,
},
},
[]test{
{
"example.com/unexistentaci",
types.Labels{},
-1,
},
{
"example.com/test01",
types.Labels{},
0,
},
{
"example.com/test02",
types.Labels{
{
Name: "version",
Value: "1.0.0",
},
},
1,
},
{
"example.com/test02",
types.Labels{
{
Name: "version",
Value: "2.0.0",
},
},
2,
},
{
"example.com/test02",
types.Labels{},
1,
},
},
},
}

for _, tt := range tests {
keys := []string{}
// Create ACIs
for _, ad := range tt.acidefs {
aci, err := aci.NewACI(dir, ad.imj, nil)
if err != nil {
t.Fatalf("error creating test tar: %v", err)
}

// Rewind the ACI
if _, err := aci.Seek(0, 0); err != nil {
t.Fatalf("unexpected error %v", err)
}

key, err := ds.WriteACI(aci, ad.latest)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
keys = append(keys, key)
}

for _, test := range tt.tests {
key, err := ds.GetACI(test.name, test.labels)
if test.expected == -1 {
if err == nil {
t.Fatalf("Expected no key for appName %s, got %s", test.name, key)
}

} else {
if err != nil {
t.Fatalf("unexpected error on GetACI for name %s, labels: %v: %v", test.name, test.labels, err)
}
if keys[test.expected] != key {
t.Errorf("expected key: %s, got %s. GetACI with name: %s, labels: %v", key, keys[test.expected], test.name, test.labels)
}
}
}
}
}

0 comments on commit 2b97fe8

Please sign in to comment.