Skip to content

Commit

Permalink
refactor(tag): Cleanup Tag structure
Browse files Browse the repository at this point in the history
(cherry picked from commit 0e2436d)
  • Loading branch information
ivanilves committed Aug 8, 2018
1 parent 0f27fcc commit 1c6c983
Show file tree
Hide file tree
Showing 6 changed files with 73 additions and 138 deletions.
2 changes: 0 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,6 @@ lstags -P /quay -r registry.company.io quay.io/coreos/hyperkube quay.io/coreos/f
* `ASSUMED` - **maybe** present in registry, not discovered by search, its presence assumed by user
* `LOCAL-ONLY` - present locally, absent in registry

There is also special `UNKNOWN` state, which means `lstags` failed to detect image state for some reason.

## Authentication
You can either:
* rely on `lstags` discovering credentials "automagically" :tophat:
Expand Down
4 changes: 2 additions & 2 deletions api/v1/collection/collection_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,8 @@ func makeTags() []*tag.Tag {

var tags = make([]*tag.Tag, 0)

for name, checksum := range seed {
tg, _ := tag.New(name, checksum)
for name, digest := range seed {
tg, _ := tag.New(name, digest, tag.Options{})

tags = append(tags, tg)
}
Expand Down
8 changes: 3 additions & 5 deletions tag/local/local.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,15 +30,13 @@ func FetchTags(repo *repository.Repository, dc *dockerclient.DockerClient) (map[
continue
}

tg, err := tag.New(tagName, repoDigest)
tagOptions := tag.Options{ImageID: imageSummary.ID, Created: imageSummary.Created}

tg, err := tag.New(tagName, repoDigest, tagOptions)
if err != nil {
return nil, err
}

tg.SetImageID(imageSummary.ID)

tg.SetCreated(imageSummary.Created)

tags[tg.Name()] = tg
}
}
Expand Down
14 changes: 5 additions & 9 deletions tag/remote/remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -366,11 +366,10 @@ func FetchTags(repo *repository.Repository, username, password string) (map[stri
digest, metadata, err := fetchDetails(repo, tagName, authorization)

ch <- detailResponse{
TagName: tagName,
Digest: digest,
Created: metadata.Created,
ContainerID: metadata.ContainerID,
Error: err,
TagName: tagName,
Digest: digest,
Created: metadata.Created,
Error: err,
}
}(repo, tagNames[tagIndex], authorization, ch)

Expand All @@ -392,14 +391,11 @@ func FetchTags(repo *repository.Repository, username, password string) (map[stri
return nil, dr.Error
}

tt, err := tag.New(dr.TagName, dr.Digest)
tt, err := tag.New(dr.TagName, dr.Digest, tag.Options{Created: dr.Created})
if err != nil {
return nil, err
}

tt.SetCreated(dr.Created)
tt.SetContainerID(dr.ContainerID)

tags[tt.Name()] = tt
}
}
Expand Down
75 changes: 25 additions & 50 deletions tag/tag.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,17 @@ import (

// Tag aggregates tag-related information: tag name, image digest etc
type Tag struct {
name string
digest string
imageID string
state string
created int64
containerID string
name string
digest string
imageID string
created int64
state string
}

// Options holds optional parameters for Tag creation
type Options struct {
ImageID string
Created int64
}

// SortKey returns a sort key (used to sort tags before process or display them)
Expand Down Expand Up @@ -65,8 +70,8 @@ func cutImageID(s string) string {
return id
}

// SetImageID sets local Docker image ID
func (tg *Tag) SetImageID(s string) {
// setImageID sets local Docker image ID
func (tg *Tag) setImageID(s string) {
tg.imageID = cutImageID(s)
}

Expand All @@ -80,8 +85,8 @@ func (tg *Tag) HasImageID() bool {
return len(tg.imageID) > 0
}

// SetState sets tag state (a difference between local tag and its remote counterpart)
func (tg *Tag) SetState(state string) {
// setState sets tag state (a difference between local tag and its remote counterpart)
func (tg *Tag) setState(state string) {
tg.state = state
}

Expand All @@ -108,31 +113,11 @@ func (tg *Tag) NeedsPush(doUpdate bool) bool {
return false
}

// SetCreated sets image creation timestamp
func (tg *Tag) SetCreated(created int64) {
tg.created = created
}

// GetCreated gets image creation timestamp
func (tg *Tag) GetCreated() int64 {
return tg.created
}

// SetContainerID sets "container ID": an OAF "image digest" generated locally
func (tg *Tag) SetContainerID(containerID string) {
tg.containerID = containerID
}

// GetContainerID gets "container ID": an OAF "image digest" generated locally
func (tg *Tag) GetContainerID() string {
return tg.containerID
}

// HasContainerID tells us if tag has "container ID"
func (tg *Tag) HasContainerID() bool {
return len(tg.containerID) > 0
}

// GetCreatedKey gets image creation timestamp in a string form (for a string sort e.g.)
func (tg *Tag) GetCreatedKey() string {
return strconv.FormatInt(tg.created, 10)
Expand All @@ -148,7 +133,7 @@ func (tg *Tag) GetCreatedString() string {
}

// New creates a new instance of Tag
func New(name, digest string) (*Tag, error) {
func New(name, digest string, options Options) (*Tag, error) {
if name == "" {
return nil, errors.New("Empty tag name not allowed")
}
Expand All @@ -158,8 +143,10 @@ func New(name, digest string) (*Tag, error) {
}

return &Tag{
name: name,
digest: digest,
name: name,
digest: digest,
imageID: cutImageID(options.ImageID),
created: options.Created,
},
nil
}
Expand All @@ -168,10 +155,6 @@ func calculateState(name string, remoteTags, localTags map[string]*Tag) string {
r, definedInRegistry := remoteTags[name]
l, definedLocally := localTags[name]

if !definedInRegistry && !definedLocally {
return "ASSUMED"
}

if definedInRegistry && !definedLocally {
return "ABSENT"
}
Expand All @@ -185,16 +168,10 @@ func calculateState(name string, remoteTags, localTags map[string]*Tag) string {
return "PRESENT"
}

if r.HasContainerID() && l.HasContainerID() {
if r.GetContainerID() == l.GetContainerID() {
return "PRESENT"
}
}

return "CHANGED"
}

return "UNKNOWN"
return "ASSUMED"
}

// Join joins local tags with ones from registry, performs state processing and returns:
Expand All @@ -219,9 +196,9 @@ func Join(

ltg, defined := localTags[name]
if defined && ltg.HasImageID() {
joinedTags[name].SetImageID(ltg.GetImageID())
joinedTags[name].setImageID(ltg.GetImageID())
} else {
joinedTags[name].SetImageID("n/a")
joinedTags[name].setImageID("n/a")
}
}

Expand All @@ -243,9 +220,7 @@ func Join(
_, definedLocally := localTags[name]

if !definedRemotely && !definedLocally {
joinedTags[name], _ = New(name, "n/a")

joinedTags[name].SetImageID("n/a")
joinedTags[name], _ = New(name, "n/a", Options{ImageID: "n/a"})

sortKey := joinedTags[name].SortKey()

Expand All @@ -256,7 +231,7 @@ func Join(
}

for name, jtg := range joinedTags {
jtg.SetState(
jtg.setState(
calculateState(
name,
remoteTags,
Expand Down
108 changes: 38 additions & 70 deletions tag/tag_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ func TestNew(t *testing.T) {
"digest": "sha256:c92260fe6357ac1cdd79e86e23fa287701c5edd2921d243a253fd21c9f0012ae",
}

tg, err := New(params["name"], params["digest"])
tg, err := New(params["name"], params["digest"], Options{})

if err != nil {
t.Fatalf("Unable to create new tag: %s", err.Error())
Expand All @@ -39,7 +39,7 @@ func TestNewWithShortDigest(t *testing.T) {
"digest": "csum:iamkindashort",
}

tg, err := New(params["name"], params["digest"])
tg, err := New(params["name"], params["digest"], Options{})

if err != nil {
t.Fatalf("Unable to create new tag with a short digest: %s", err.Error())
Expand All @@ -58,6 +58,7 @@ func TestNew_WithEmptyName(t *testing.T) {
_, err := New(
"",
"sha256:c92260fe6357ac1cdd79e86e23fa287701c5edd2921d243a253fd21c9f0012ae",
Options{},
)

if err == nil {
Expand All @@ -69,6 +70,7 @@ func TestNew_WithEmptyDigest(t *testing.T) {
_, err := New(
"latest",
"",
Options{},
)

if err == nil {
Expand All @@ -77,82 +79,48 @@ func TestNew_WithEmptyDigest(t *testing.T) {
}

//
// A pretty "Chinese" way of generating test data here (no pun intended here, no racism).
// But I like it because it's easy and doesn't require creation of extra data structures.
// Here we generate fake tags for our tests
//
func getRemoteTags() map[string]*Tag {
tags := make(map[string]*Tag, 0)

tg1, _ := New(
"latest",
"sha256:c92260fe6357ac1cdd79e86e23fa287701c5edd2921d243a253fd21c9f0012ae",
)
tags["latest"] = tg1

tg2, _ := New(
"v1.1",
"sha256:7abd16433f3bec5ee4c566ddbfc0e5255678498d5e7e2da8f41393bfe84bfcac",
)
tags["v1.1"] = tg2

tg3, _ := New(
"v1.2",
"sha256:7f7f94f26d23f7aca80a33732161af068f9f62fbe0e824a58cf3a39d209cfa77",
)
tags["v1.2"] = tg3
func getRemoteTags() map[string]*Tag {
seeds := []struct {
name string
digest string
}{
{"latest", "sha256:c92260fe6357ac1cdd79e86e23fa287701c5edd2921d243a253fd21c9f0012ae"},
{"v1.1", "sha256:7abd16433f3bec5ee4c566ddbfc0e5255678498d5e7e2da8f41393bfe84bfcac"},
{"v1.2", "sha256:7f7f94f26d23f7aca80a33732161af068f9f62fbe0e824a58cf3a39d209cfa77"},
{"v1.3.1", "sha256:9fb0e8a4f629b72a0a69aef357e637e4145b6588f04f1540a31a0d2e030ea7da"},
{"v1.3.2", "sha256:fc41473fc36c09222a29ffce9eaf5732fae91c3fabfa40aa878f600e13c7fed3"},
}

tg4, _ := New(
"v1.3.1",
"sha256:9fb0e8a4f629b72a0a69aef357e637e4145b6588f04f1540a31a0d2e030ea7da",
)
tags["v1.3.1"] = tg4
tags := make(map[string]*Tag, 0)

tg5, _ := New(
"v1.3.2",
"sha256:fc41473fc36c09222a29ffce9eaf5732fae91c3fabfa40aa878f600e13c7fed3",
)
tg5.SetContainerID("16dcde7895c73c98161aa6981c4ea0df027697cd")
tags["v1.3.2"] = tg5
for _, seed := range seeds {
tags[seed.name], _ = New(seed.name, seed.digest, Options{})
}

return tags
}

func getLocalTags() map[string]*Tag {
tags := make(map[string]*Tag, 0)

tg1, _ := New(
"latest",
"sha256:8ffc20b5be0e391f07f270bf79441fbea3c8b67200e5316bdefad9e0ca80277b",
)
tg1.SetImageID("sha256:883e3a5b24d7b46f81436bfc85564a676aa021a2c8adedc3ac6ab12ac06fdd95")
tags["latest"] = tg1

tg2, _ := New(
"v1.0",
"sha256:fe4286e7b852dc6aad6225239ecb32691f15f20b0d4354defb4ca4957958b2f0",
)
tg2.SetImageID("sha256:c9a69a36ff3ce76d7970df83bd438f0f1bc0363a3b4707b42542ea20ba4282f4")
tags["v1.0"] = tg2

tg3, _ := New(
"v1.2",
"sha256:7f7f94f26d23f7aca80a33732161af068f9f62fbe0e824a58cf3a39d209cfa77",
)
tg3.SetImageID("sha256:4c4ebb9614ef823bd04e5eba65e59286a4314d3a063e2eaa221d38fc21723cea")
tags["v1.2"] = tg3
seeds := []struct {
name string
digest string
imageID string
}{
{"latest", "sha256::8ffc20b5be0e391f07f270bf79441fbea3c8b67200e5316bdefad9e0ca80277b", "sha256:883e3a5b24d7b46f81436bfc85564a676aa021a2c8adedc3ac6ab12ac06fdd95"},
{"v1.0", "sha256:fe4286e7b852dc6aad6225239ecb32691f15f20b0d4354defb4ca4957958b2f0", "sha256:c9a69a36ff3ce76d7970df83bd438f0f1bc0363a3b4707b42542ea20ba4282f4"},
{"v1.2", "sha256:7f7f94f26d23f7aca80a33732161af068f9f62fbe0e824a58cf3a39d209cfa77", "4c4ebb9614ef823bd04e5eba65e59286a4314d3a063e2eaa221d38fc21723cea"},
{"v1.3.1", "sha256:7264ba7450b6be1bfba9ab29f506293bb324f4764c41ff32dcc04379c1a69c91", ""},
{"v1.3.2", "sha256:fc41473fc36c09222a29ffce9eaf5732fae91c3fabfa40aa878f600e13c7fed3", ""},
}

tg4, _ := New(
"v1.3.1",
"sha256:7264ba7450b6be1bfba9ab29f506293bb324f4764c41ff32dcc04379c1a69c91",
)
tags["v1.3.1"] = tg4
tags := make(map[string]*Tag, 0)

tg5, _ := New(
"v1.3.2",
"sha256:70fbfacca0ab3ec01258b1787b02de77474c6f120b86bb8743b81b7dc37d4aab",
)
tg5.SetContainerID("16dcde7895c73c98161aa6981c4ea0df027697cd")
tags["v1.3.2"] = tg5
for _, seed := range seeds {
tags[seed.name], _ = New(seed.name, seed.digest, Options{ImageID: seed.imageID})
}

return tags
}
Expand Down Expand Up @@ -392,10 +360,10 @@ func TestCutImageID(t *testing.T) {
}

func TestCreated(t *testing.T) {
tg, _ := New("latest", "csum:something")

expectedTimestamp := time.Now().Unix()
tg.SetCreated(expectedTimestamp)

tg, _ := New("latest", "csum:something", Options{Created: expectedTimestamp})

timestamp := tg.GetCreated()
if timestamp != expectedTimestamp {
t.Fatalf(
Expand Down

0 comments on commit 1c6c983

Please sign in to comment.