Skip to content

Commit 3ee8036

Browse files
committed
libartifact: introduce handling of artifact store events
Signed-off-by: Byounguk Lee <nimdrak@gmail.com>
1 parent bca05bd commit 3ee8036

2 files changed

Lines changed: 114 additions & 6 deletions

File tree

common/pkg/libartifact/events.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
//go:build !remote
2+
3+
package libartifact
4+
5+
import (
6+
"time"
7+
8+
"github.com/sirupsen/logrus"
9+
)
10+
11+
// EventType indicates the type of an event.
12+
type EventType int
13+
14+
const (
15+
// EventTypeUnknown is an uninitialized EventType.
16+
EventTypeUnknown EventType = iota
17+
// EventTypeArtifactPull represents an artifact pull.
18+
EventTypeArtifactPull
19+
// EventTypeArtifactPullError represents a failed artifact pull.
20+
EventTypeArtifactPullError
21+
// EventTypeArtifactPush represents an artifact push.
22+
EventTypeArtifactPush
23+
// EventTypeArtifactPushError represents a failed artifact push.
24+
EventTypeArtifactPushError
25+
// EventTypeArtifactRemove represents an artifact removal.
26+
EventTypeArtifactRemove
27+
// EventTypeArtifactRemoveError represents a failed artifact removal.
28+
EventTypeArtifactRemoveError
29+
// EventTypeArtifactAdd represents an artifact being added.
30+
EventTypeArtifactAdd
31+
// EventTypeArtifactAddError represents a failed artifact add.
32+
EventTypeArtifactAddError
33+
)
34+
35+
// Event represents an event such as an artifact pull or push.
36+
type Event struct {
37+
// ID of the object (e.g., artifact digest).
38+
ID string
39+
// Name of the object (e.g., artifact name "quay.io/foobar/artifact:special")
40+
Name string
41+
// Time of the event.
42+
Time time.Time
43+
// Type of the event.
44+
Type EventType
45+
// Error in case of failure.
46+
Error error
47+
}
48+
49+
// writeEvent writes the specified event to the store's event channel. The
50+
// event is discarded if no event channel has been registered (yet).
51+
func (as *ArtifactStore) writeEvent(event *Event) {
52+
select {
53+
case as.eventChannel <- event:
54+
// Done
55+
case <-time.After(2 * time.Second):
56+
// The store's event channel has a buffer of size 100 which
57+
// should be enough even under high load. However, we
58+
// shouldn't block too long in case the buffer runs full (could
59+
// be an honest user error or bug).
60+
logrus.Warnf("Discarding libartifact event which was not read within 2 seconds: %v", event)
61+
}
62+
}

common/pkg/libartifact/store.go

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ type ArtifactStore struct {
4141
SystemContext *types.SystemContext
4242
storePath string
4343
lock *lockfile.LockFile
44+
eventChannel chan *Event
4445
}
4546

4647
// NewArtifactStore is a constructor for artifact stores. Most artifact dealings depend on this. Store path is
@@ -81,6 +82,18 @@ func NewArtifactStore(storePath string, sc *types.SystemContext) (*ArtifactStore
8182
return artifactStore, nil
8283
}
8384

85+
// EventChannel creates a buffered channel for events that the ArtifactStore will use
86+
// to write events to. Callers are expected to read from the channel in a
87+
// timely manner.
88+
// Can be called once for a given ArtifactStore.
89+
func (as *ArtifactStore) EventChannel() chan *Event {
90+
if as.eventChannel != nil {
91+
return as.eventChannel
92+
}
93+
as.eventChannel = make(chan *Event, 100)
94+
return as.eventChannel
95+
}
96+
8497
// lookupArtifactLocked looks up an artifact by fully qualified name,
8598
// or name@digest, full ID, or partial ID.
8699
// note: lookupArtifactLocked must be called while under a store lock
@@ -146,19 +159,30 @@ func (as ArtifactStore) lookupArtifactLocked(ctx context.Context, asr ArtifactSt
146159
}
147160

148161
// Remove an artifact from the local artifact store.
149-
func (as ArtifactStore) Remove(ctx context.Context, asr ArtifactStoreReference) (*digest.Digest, error) {
162+
func (as ArtifactStore) Remove(ctx context.Context, asr ArtifactStoreReference) (_ *digest.Digest, removeErr error) {
150163
as.lock.Lock()
151164
defer as.lock.Unlock()
152165

153166
arty, err := as.lookupArtifactLocked(ctx, asr)
154167
if err != nil {
155168
return nil, err
156169
}
170+
171+
defer func() {
172+
if removeErr != nil {
173+
as.writeEvent(&Event{ID: arty.Digest.String(), Name: arty.Name, Time: time.Now(), Type: EventTypeArtifactRemoveError, Error: removeErr})
174+
}
175+
}()
176+
157177
ir, err := layout.NewReference(as.storePath, arty.Name)
158178
if err != nil {
159179
return nil, err
160180
}
161-
return &arty.Digest, ir.DeleteImage(ctx, as.SystemContext)
181+
if err := ir.DeleteImage(ctx, as.SystemContext); err != nil {
182+
return nil, err
183+
}
184+
as.writeEvent(&Event{ID: arty.Digest.String(), Name: arty.Name, Time: time.Now(), Type: EventTypeArtifactRemove})
185+
return &arty.Digest, nil
162186
}
163187

164188
// Inspect an artifact in a local store.
@@ -178,7 +202,13 @@ func (as ArtifactStore) List(ctx context.Context) (ArtifactList, error) {
178202
}
179203

180204
// Pull an artifact from an image registry to a local store.
181-
func (as ArtifactStore) Pull(ctx context.Context, ref ArtifactReference, opts libimage.CopyOptions) (digest.Digest, error) {
205+
func (as ArtifactStore) Pull(ctx context.Context, ref ArtifactReference, opts libimage.CopyOptions) (_ digest.Digest, pullErr error) {
206+
defer func() {
207+
if pullErr != nil {
208+
as.writeEvent(&Event{Name: ref.String(), Time: time.Now(), Type: EventTypeArtifactPullError, Error: pullErr})
209+
}
210+
}()
211+
182212
srcRef, err := docker.NewReference(ref.ref)
183213
if err != nil {
184214
return "", err
@@ -202,11 +232,19 @@ func (as ArtifactStore) Pull(ctx context.Context, ref ArtifactReference, opts li
202232
if err != nil {
203233
return "", err
204234
}
205-
return digest.FromBytes(artifactBytes), nil
235+
artifactDigest := digest.FromBytes(artifactBytes)
236+
as.writeEvent(&Event{ID: artifactDigest.String(), Name: ref.String(), Time: time.Now(), Type: EventTypeArtifactPull})
237+
return artifactDigest, nil
206238
}
207239

208240
// Push an artifact to an image registry.
209-
func (as ArtifactStore) Push(ctx context.Context, src, dest ArtifactReference, opts libimage.CopyOptions) (digest.Digest, error) {
241+
func (as ArtifactStore) Push(ctx context.Context, src, dest ArtifactReference, opts libimage.CopyOptions) (_ digest.Digest, pushErr error) {
242+
defer func() {
243+
if pushErr != nil {
244+
as.writeEvent(&Event{Name: dest.String(), Time: time.Now(), Type: EventTypeArtifactPushError, Error: pushErr})
245+
}
246+
}()
247+
210248
destRef, err := docker.NewReference(dest.ref)
211249
if err != nil {
212250
return "", err
@@ -233,6 +271,7 @@ func (as ArtifactStore) Push(ctx context.Context, src, dest ArtifactReference, o
233271
return "", err
234272
}
235273
artifactDigest := digest.FromBytes(artifactBytes)
274+
as.writeEvent(&Event{ID: artifactDigest.String(), Name: dest.String(), Time: time.Now(), Type: EventTypeArtifactPush})
236275
return artifactDigest, nil
237276
}
238277

@@ -279,7 +318,13 @@ func cleanupAfterAppend(ctx context.Context, oldDigest digest.Digest, as Artifac
279318

280319
// Add takes one or more artifact blobs and add them to the local artifact store. The empty
281320
// string input is for possible custom artifact types.
282-
func (as ArtifactStore) Add(ctx context.Context, dest ArtifactReference, artifactBlobs []libartTypes.ArtifactBlob, options *libartTypes.AddOptions) (*digest.Digest, error) {
321+
func (as ArtifactStore) Add(ctx context.Context, dest ArtifactReference, artifactBlobs []libartTypes.ArtifactBlob, options *libartTypes.AddOptions) (_ *digest.Digest, addErr error) {
322+
defer func() {
323+
if addErr != nil {
324+
as.writeEvent(&Event{Name: dest.String(), Time: time.Now(), Type: EventTypeArtifactAddError, Error: addErr})
325+
}
326+
}()
327+
283328
if options.Append && len(options.ArtifactMIMEType) > 0 {
284329
return nil, errors.New("append option is not compatible with type option")
285330
}
@@ -447,6 +492,7 @@ func (as ArtifactStore) Add(ctx context.Context, dest ArtifactReference, artifac
447492
return nil, err
448493
}
449494
}
495+
as.writeEvent(&Event{ID: artifactManifestDigest.String(), Name: dest.String(), Time: time.Now(), Type: EventTypeArtifactAdd})
450496
return &artifactManifestDigest, nil
451497
}
452498

0 commit comments

Comments
 (0)