Skip to content
7 changes: 7 additions & 0 deletions docs/source/markdown/podman-events.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,13 @@ The *secret* type reports the following statuses:
* create
* remove

The *artifact* type reports the following statuses:
* add
* extract
* pull
* push
* remove

#### Verbose Create Events

Setting `events_container_create_inspect_data=true` in containers.conf(5) instructs Podman to create more verbose container-create events which include a JSON payload with detailed information about the containers. The JSON payload is identical to the one of podman-container-inspect(1). The associated field in journald is named `PODMAN_CONTAINER_INSPECT_DATA`.
Expand Down
12 changes: 12 additions & 0 deletions libpod/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,18 @@ func (r *Runtime) NewSecretEvent(status events.Status, secretID string) {
}
}

// NewArtifactEvent creates a new event for a libpod artifact
func (r *Runtime) NewArtifactEvent(status events.Status, name, digest string, attr map[string]string) {
e := events.NewEvent(status)
e.Type = events.Artifact
e.Name = name
e.ID = digest
e.Attributes = attr
if err := r.eventer.Write(e); err != nil {
logrus.Errorf("Unable to write artifact event: %q", err)
}
}

// Events is a wrapper function for everyone to begin tailing the events log
// with options
func (r *Runtime) Events(ctx context.Context, options events.ReadOptions) error {
Expand Down
6 changes: 6 additions & 0 deletions libpod/events/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,11 @@ const (
Machine Type = "machine"
// Secret - event is related to secrets
Secret Type = "secret"
// Artifact - event is related to artifacts
Artifact Type = "artifact"

// Add ...
Add Status = "add"
// Attach ...
Attach Status = "attach"
// AutoUpdate ...
Expand All @@ -156,6 +160,8 @@ const (
ExecDied Status = "exec_died"
// Exited indicates that a container's process died
Exited Status = "died"
// Extract ...
Extract Status = "extract"
// Export ...
Export Status = "export"
// HealthStatus ...
Expand Down
14 changes: 14 additions & 0 deletions libpod/events/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,14 @@ func (e *Event) ToHumanReadable(truncate bool) string {
humanFormat = fmt.Sprintf("%s %s %s %s", e.Time, e.Type, e.Status, e.Name)
case Secret:
humanFormat = fmt.Sprintf("%s %s %s %s", e.Time, e.Type, e.Status, id)
case Artifact:
humanFormat = fmt.Sprintf("%s %s %s %s (name=%s", e.Time, e.Type, e.Status, id, e.Name)
if len(e.Attributes) > 0 {
for k, v := range e.Attributes {
humanFormat += fmt.Sprintf(", %s=%s", k, v)
}
}
humanFormat += ")"
}
return humanFormat
}
Expand Down Expand Up @@ -127,6 +135,8 @@ func StringToType(name string) (Type, error) {
return Volume, nil
case Secret.String():
return Secret, nil
case Artifact.String():
return Artifact, nil
case "":
return "", ErrEventTypeBlank
}
Expand All @@ -136,6 +146,8 @@ func StringToType(name string) (Type, error) {
// StringToStatus converts a string to an Event Status
func StringToStatus(name string) (Status, error) {
switch name {
case Add.String():
return Add, nil
case Attach.String():
return Attach, nil
case AutoUpdate.String():
Expand All @@ -156,6 +168,8 @@ func StringToStatus(name string) (Status, error) {
return ExecDied, nil
case Exited.String():
return Exited, nil
case Extract.String():
return Extract, nil
case Export.String():
return Export, nil
case HealthStatus.String():
Expand Down
11 changes: 11 additions & 0 deletions libpod/events/journal_linux.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,12 @@ func (e EventJournalD) Write(ee Event) error {
}
case Volume:
m["PODMAN_NAME"] = ee.Name
case Artifact:
m["PODMAN_NAME"] = ee.Name
m["PODMAN_ID"] = ee.ID
if err := addLabelsToJournal(m, ee.Details.Attributes); err != nil {
return err
}
}

// starting with commit 7e6e267329 we set LogLevel=notice for the systemd healthcheck unit
Expand Down Expand Up @@ -275,6 +281,11 @@ func newEventFromJournalEntry(entry *sdjournal.JournalEntry) (*Event, error) {
if val, ok := entry.Fields["ERROR"]; ok {
newEvent.Error = val
}
case Artifact:
newEvent.ID = entry.Fields["PODMAN_ID"]
if err := getLabelsFromJournal(entry, &newEvent); err != nil {
return nil, err
}
}
return &newEvent, nil
}
Expand Down
2 changes: 1 addition & 1 deletion libpod/events/logfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ func (e EventLogFile) Read(ctx context.Context, options ReadOptions) error {
continue
}
switch event.Type {
case Image, Volume, Pod, Container, Network, Secret:
case Image, Volume, Pod, Container, Network, Secret, Artifact:
// no-op
case System:
begin, end, err := e.readRotateEvent(event)
Expand Down
10 changes: 9 additions & 1 deletion libpod/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,15 @@ func makeRuntime(ctx context.Context, runtime *Runtime) (retErr error) {

// Using sync once value to only init the store exactly once and only when it will be actually be used.
runtime.ArtifactStore = sync.OnceValues(func() (*artStore.ArtifactStore, error) {
return artStore.NewArtifactStore(filepath.Join(runtime.storageConfig.GraphRoot, "artifacts"), runtime.SystemContext())
artifactEventCallBack := func(status, name, digest string, attributes map[string]string) {
eventStatus, err := events.StringToStatus(status)
if err != nil {
logrus.Errorf("Unknown artifact event status %q: %v", status, err)
return
}
runtime.NewArtifactEvent(eventStatus, name, digest, attributes)
}
return artStore.NewArtifactStoreWithEventCallback(filepath.Join(runtime.storageConfig.GraphRoot, "artifacts"), runtime.SystemContext(), artifactEventCallBack)
})
}

Expand Down
50 changes: 50 additions & 0 deletions test/e2e/events_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,9 @@ package integration
import (
"encoding/json"
"fmt"
"sort"
"strconv"
"strings"
"sync"
"time"

Expand Down Expand Up @@ -309,4 +311,52 @@ var _ = Describe("Podman events", func() {
Expect(result.OutputToStringArray()).ToNot(BeEmpty(), "Number of health_status events")
})

It("podman events artifacts", func() {
lock, port, err := setupRegistry(nil)
Expect(err).ToNot(HaveOccurred())
defer lock.Unlock()

artifactFile, err := createArtifactFile(4192)
Expect(err).ToNot(HaveOccurred())
artifactName := fmt.Sprintf("localhost:%s/test/artifact1", port)

podmanTest.PodmanExitCleanly("artifact", "add", artifactName, artifactFile)

podmanTest.PodmanExitCleanly("artifact", "extract", artifactName, artifactFile)

podmanTest.PodmanExitCleanly("artifact", "push", "-q", "--tls-verify=false", artifactName)

podmanTest.PodmanExitCleanly("artifact", "remove", artifactName)

podmanTest.PodmanExitCleanly("artifact", "pull", "--tls-verify=false", artifactName)

result := podmanTest.PodmanExitCleanly("events", "--stream=false", "--filter", "type=artifact")
events := result.OutputToStringArray()

// sort for remote
sort.Slice(events, func(i, j int) bool {
getStatus := func(log string) string {
status := strings.Fields(log)
return status[5]
}
return getStatus(events[i]) < getStatus(events[j])
})

Expect(events).To(HaveLen(5))
Expect(events[0]).To(And(
ContainSubstring("artifact add"),
ContainSubstring(fmt.Sprintf("(name=%s, files=%s)", artifactName, "1"))))
Expect(events[1]).To(And(
ContainSubstring("artifact extract"),
ContainSubstring(fmt.Sprintf("(name=%s, target=%s)", artifactName, artifactFile))))
Expect(events[2]).To(And(
ContainSubstring("artifact pull"),
ContainSubstring(fmt.Sprintf("(name=%s)", artifactName))))
Expect(events[3]).To(And(
ContainSubstring("artifact push"),
ContainSubstring(fmt.Sprintf("(name=%s)", artifactName))))
Expect(events[4]).To(And(
ContainSubstring("artifact remove"),
ContainSubstring(fmt.Sprintf("(name=%s)", artifactName))))
})
})