mirror of
https://github.com/containrrr/watchtower.git
synced 2026-01-16 05:55:29 +01:00
http report wip
This commit is contained in:
parent
e3dd8d688a
commit
efaf7190ee
25 changed files with 350 additions and 284 deletions
|
|
@ -1,6 +1,9 @@
|
|||
package session
|
||||
|
||||
import wt "github.com/containrrr/watchtower/pkg/types"
|
||||
import (
|
||||
wt "github.com/containrrr/watchtower/pkg/types"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// State indicates what the current state is of the container
|
||||
type State int
|
||||
|
|
@ -19,51 +22,17 @@ const (
|
|||
|
||||
// ContainerStatus contains the container state during a session
|
||||
type ContainerStatus struct {
|
||||
containerID wt.ContainerID
|
||||
oldImage wt.ImageID
|
||||
newImage wt.ImageID
|
||||
containerName string
|
||||
imageName string
|
||||
error
|
||||
state State
|
||||
ID wt.ContainerID
|
||||
Name string
|
||||
OldImageID wt.ImageID
|
||||
NewImageID wt.ImageID
|
||||
ImageName string
|
||||
Error error
|
||||
State State
|
||||
}
|
||||
|
||||
// ID returns the container ID
|
||||
func (u *ContainerStatus) ID() wt.ContainerID {
|
||||
return u.containerID
|
||||
}
|
||||
|
||||
// Name returns the container name
|
||||
func (u *ContainerStatus) Name() string {
|
||||
return u.containerName
|
||||
}
|
||||
|
||||
// CurrentImageID returns the image ID that the container used when the session started
|
||||
func (u *ContainerStatus) CurrentImageID() wt.ImageID {
|
||||
return u.oldImage
|
||||
}
|
||||
|
||||
// LatestImageID returns the newest image ID found during the session
|
||||
func (u *ContainerStatus) LatestImageID() wt.ImageID {
|
||||
return u.newImage
|
||||
}
|
||||
|
||||
// ImageName returns the name:tag that the container uses
|
||||
func (u *ContainerStatus) ImageName() string {
|
||||
return u.imageName
|
||||
}
|
||||
|
||||
// Error returns the error (if any) that was encountered for the container during a session
|
||||
func (u *ContainerStatus) Error() string {
|
||||
if u.error == nil {
|
||||
return ""
|
||||
}
|
||||
return u.error.Error()
|
||||
}
|
||||
|
||||
// State returns the current State that the container is in
|
||||
func (u *ContainerStatus) State() string {
|
||||
switch u.state {
|
||||
func (state State) String() string {
|
||||
switch state {
|
||||
case SkippedState:
|
||||
return "Skipped"
|
||||
case ScannedState:
|
||||
|
|
@ -80,3 +49,12 @@ func (u *ContainerStatus) State() string {
|
|||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON marshals State as a string
|
||||
func (state State) MarshalJSON() ([]byte, error) {
|
||||
sb := strings.Builder{}
|
||||
sb.WriteString(`"`)
|
||||
sb.WriteString(state.String())
|
||||
sb.WriteString(`"`)
|
||||
return []byte(sb.String()), nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,19 +10,19 @@ type Progress map[types.ContainerID]*ContainerStatus
|
|||
// UpdateFromContainer sets various status fields from their corresponding container equivalents
|
||||
func UpdateFromContainer(cont types.Container, newImage types.ImageID, state State) *ContainerStatus {
|
||||
return &ContainerStatus{
|
||||
containerID: cont.ID(),
|
||||
containerName: cont.Name(),
|
||||
imageName: cont.ImageName(),
|
||||
oldImage: cont.SafeImageID(),
|
||||
newImage: newImage,
|
||||
state: state,
|
||||
ID: cont.ID(),
|
||||
Name: cont.Name(),
|
||||
ImageName: cont.ImageName(),
|
||||
OldImageID: cont.SafeImageID(),
|
||||
NewImageID: newImage,
|
||||
State: state,
|
||||
}
|
||||
}
|
||||
|
||||
// AddSkipped adds a container to the Progress with the state set as skipped
|
||||
func (m Progress) AddSkipped(cont types.Container, err error) {
|
||||
update := UpdateFromContainer(cont, cont.SafeImageID(), SkippedState)
|
||||
update.error = err
|
||||
update.Error = err
|
||||
m.Add(update)
|
||||
}
|
||||
|
||||
|
|
@ -35,22 +35,17 @@ func (m Progress) AddScanned(cont types.Container, newImage types.ImageID) {
|
|||
func (m Progress) UpdateFailed(failures map[types.ContainerID]error) {
|
||||
for id, err := range failures {
|
||||
update := m[id]
|
||||
update.error = err
|
||||
update.state = FailedState
|
||||
update.Error = err
|
||||
update.State = FailedState
|
||||
}
|
||||
}
|
||||
|
||||
// Add a container to the map using container ID as the key
|
||||
func (m Progress) Add(update *ContainerStatus) {
|
||||
m[update.containerID] = update
|
||||
m[update.ID] = update
|
||||
}
|
||||
|
||||
// MarkForUpdate marks the container identified by containerID for update
|
||||
func (m Progress) MarkForUpdate(containerID types.ContainerID) {
|
||||
m[containerID].state = UpdatedState
|
||||
}
|
||||
|
||||
// Report creates a new Report from a Progress instance
|
||||
func (m Progress) Report() types.Report {
|
||||
return NewReport(m)
|
||||
m[containerID].State = UpdatedState
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,90 +1,78 @@
|
|||
package session
|
||||
|
||||
import (
|
||||
"github.com/containrrr/watchtower/pkg/types"
|
||||
"sort"
|
||||
"time"
|
||||
)
|
||||
|
||||
type report struct {
|
||||
scanned []types.ContainerReport
|
||||
updated []types.ContainerReport
|
||||
failed []types.ContainerReport
|
||||
skipped []types.ContainerReport
|
||||
stale []types.ContainerReport
|
||||
fresh []types.ContainerReport
|
||||
}
|
||||
|
||||
func (r *report) Scanned() []types.ContainerReport {
|
||||
return r.scanned
|
||||
}
|
||||
func (r *report) Updated() []types.ContainerReport {
|
||||
return r.updated
|
||||
}
|
||||
func (r *report) Failed() []types.ContainerReport {
|
||||
return r.failed
|
||||
}
|
||||
func (r *report) Skipped() []types.ContainerReport {
|
||||
return r.skipped
|
||||
}
|
||||
func (r *report) Stale() []types.ContainerReport {
|
||||
return r.stale
|
||||
}
|
||||
func (r *report) Fresh() []types.ContainerReport {
|
||||
return r.fresh
|
||||
type Report struct {
|
||||
Started time.Time
|
||||
Ended time.Time
|
||||
Trigger Trigger
|
||||
Scanned []*ContainerStatus
|
||||
Updated []*ContainerStatus
|
||||
Failed []*ContainerStatus
|
||||
Skipped []*ContainerStatus
|
||||
Stale []*ContainerStatus
|
||||
Fresh []*ContainerStatus
|
||||
}
|
||||
|
||||
// NewReport creates a types.Report from the supplied Progress
|
||||
func NewReport(progress Progress) types.Report {
|
||||
report := &report{
|
||||
scanned: []types.ContainerReport{},
|
||||
updated: []types.ContainerReport{},
|
||||
failed: []types.ContainerReport{},
|
||||
skipped: []types.ContainerReport{},
|
||||
stale: []types.ContainerReport{},
|
||||
fresh: []types.ContainerReport{},
|
||||
// s.Started, time.Now().UTC(), s.Trigger, s.Progress
|
||||
func NewReport(started, ended time.Time, trigger Trigger, progress Progress) *Report {
|
||||
report := &Report{
|
||||
Started: started,
|
||||
Ended: ended,
|
||||
Trigger: trigger,
|
||||
Scanned: []*ContainerStatus{},
|
||||
Updated: []*ContainerStatus{},
|
||||
Failed: []*ContainerStatus{},
|
||||
Skipped: []*ContainerStatus{},
|
||||
Stale: []*ContainerStatus{},
|
||||
Fresh: []*ContainerStatus{},
|
||||
}
|
||||
|
||||
for _, update := range progress {
|
||||
if update.state == SkippedState {
|
||||
report.skipped = append(report.skipped, update)
|
||||
if update.State == SkippedState {
|
||||
report.Skipped = append(report.Skipped, update)
|
||||
continue
|
||||
}
|
||||
|
||||
report.scanned = append(report.scanned, update)
|
||||
if update.newImage == update.oldImage {
|
||||
update.state = FreshState
|
||||
report.fresh = append(report.fresh, update)
|
||||
report.Scanned = append(report.Scanned, update)
|
||||
if update.NewImageID == update.OldImageID {
|
||||
update.State = FreshState
|
||||
report.Fresh = append(report.Fresh, update)
|
||||
continue
|
||||
}
|
||||
|
||||
switch update.state {
|
||||
switch update.State {
|
||||
case UpdatedState:
|
||||
report.updated = append(report.updated, update)
|
||||
report.Updated = append(report.Updated, update)
|
||||
case FailedState:
|
||||
report.failed = append(report.failed, update)
|
||||
report.Failed = append(report.Failed, update)
|
||||
default:
|
||||
update.state = StaleState
|
||||
report.stale = append(report.stale, update)
|
||||
update.State = StaleState
|
||||
report.Stale = append(report.Stale, update)
|
||||
}
|
||||
}
|
||||
|
||||
sort.Sort(sortableContainers(report.scanned))
|
||||
sort.Sort(sortableContainers(report.updated))
|
||||
sort.Sort(sortableContainers(report.failed))
|
||||
sort.Sort(sortableContainers(report.skipped))
|
||||
sort.Sort(sortableContainers(report.stale))
|
||||
sort.Sort(sortableContainers(report.fresh))
|
||||
sort.Sort(sortableContainers(report.Scanned))
|
||||
sort.Sort(sortableContainers(report.Updated))
|
||||
sort.Sort(sortableContainers(report.Failed))
|
||||
sort.Sort(sortableContainers(report.Skipped))
|
||||
sort.Sort(sortableContainers(report.Stale))
|
||||
sort.Sort(sortableContainers(report.Fresh))
|
||||
|
||||
return report
|
||||
}
|
||||
|
||||
type sortableContainers []types.ContainerReport
|
||||
type sortableContainers []*ContainerStatus
|
||||
|
||||
// Len implements sort.Interface.Len
|
||||
func (s sortableContainers) Len() int { return len(s) }
|
||||
|
||||
// Less implements sort.Interface.Less
|
||||
func (s sortableContainers) Less(i, j int) bool { return s[i].ID() < s[j].ID() }
|
||||
func (s sortableContainers) Less(i, j int) bool { return s[i].ID < s[j].ID }
|
||||
|
||||
// Swap implements sort.Interface.Swap
|
||||
func (s sortableContainers) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
|
||||
|
|
|
|||
24
pkg/session/session.go
Normal file
24
pkg/session/session.go
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
package session
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
type Session struct {
|
||||
Trigger Trigger
|
||||
Started time.Time
|
||||
Progress Progress
|
||||
}
|
||||
|
||||
func New(trigger Trigger) *Session {
|
||||
return &Session{
|
||||
Started: time.Now().UTC(),
|
||||
Trigger: trigger,
|
||||
Progress: Progress{},
|
||||
}
|
||||
}
|
||||
|
||||
// Report creates a new Report from a Session instance
|
||||
func (s Session) Report() *Report {
|
||||
return NewReport(s.Started, time.Now().UTC(), s.Trigger, s.Progress)
|
||||
}
|
||||
34
pkg/session/trigger.go
Normal file
34
pkg/session/trigger.go
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
package session
|
||||
|
||||
import "strings"
|
||||
|
||||
type Trigger int
|
||||
|
||||
const (
|
||||
SchedulerTrigger Trigger = iota
|
||||
APITrigger
|
||||
StartupTrigger
|
||||
)
|
||||
|
||||
// String returns a string representation of the Trigger
|
||||
func (trigger Trigger) String() string {
|
||||
switch trigger {
|
||||
case SchedulerTrigger:
|
||||
return "Scheduler"
|
||||
case APITrigger:
|
||||
return "API"
|
||||
case StartupTrigger:
|
||||
return "Startup"
|
||||
default:
|
||||
return "Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
// MarshalJSON marshals Trigger as a quoted string
|
||||
func (trigger Trigger) MarshalJSON() ([]byte, error) {
|
||||
sb := strings.Builder{}
|
||||
sb.WriteString(`"`)
|
||||
sb.WriteString(trigger.String())
|
||||
sb.WriteString(`"`)
|
||||
return []byte(sb.String()), nil
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue