keel/trigger/poll/watcher.go

159 lines
4.0 KiB
Go
Raw Normal View History

2017-07-01 12:55:26 +00:00
package poll
import (
"github.com/rusenask/cron"
2017-07-02 07:29:15 +00:00
"github.com/rusenask/keel/image"
2017-07-01 12:55:26 +00:00
"github.com/rusenask/keel/provider"
"github.com/rusenask/keel/types"
)
type Watcher interface {
2017-07-02 07:29:15 +00:00
Watch(image string) error
Unwatch(image string) error
2017-07-01 12:55:26 +00:00
List() ([]types.Repository, error)
}
type RepositoryWatcher struct {
providers provider.Providers
cron *cron.Cron
}
2017-07-02 12:31:01 +00:00
// Watch - starts watching repository for changes, if it's already watching - ignores,
// if details changed - updates details
func (w *RepositoryWatcher) Watch(imageName, schedule, registryUsername, registryPassword string) error {
imageRef, err := image.Parse(imageName)
if err != nil {
log.WithFields(log.Fields{
"error": err,
"image_name": imageName,
}).Error("trigger.poll.RepositoryWatcher.Watch: failed to parse image")
return err
}
key := getImageIdentifier(imageRef)
// checking whether it's already being watched
details, ok := w.watched[key]
if !ok {
err = w.addJob(imageRef, registryUsername, registryPassword, schedule)
if err != nil {
log.WithFields(log.Fields{
"error": err,
"image_name": imageName,
"registry_username": registryUsername,
}).Error("trigger.poll.RepositoryWatcher.Watch: failed to add image watch job")
}
return err
}
// checking schedule
if details.schedule != schedule {
w.cron.UpdateJob(key, schedule)
}
// checking auth details, if changed - need to update
if details.registryPassword != registryPassword || details.registryUsername != registryUsername {
// recreating job
w.cron.DeleteJob(key)
err = w.addJob(imageRef, registryUsername, registryPassword, schedule)
if err != nil {
log.WithFields(log.Fields{
"error": err,
"image_name": imageName,
"registry_username": registryUsername,
}).Error("trigger.poll.RepositoryWatcher.Watch: failed to add image watch job")
}
return err
}
// nothing to do
return nil
}
func (w *RepositoryWatcher) addJob(ref *image.Reference, registryUsername, registryPassword, schedule string) error {
// getting initial digest
digest, err := w.registryClient.Digest(registry.Opts{
Registry: ref.Registry(),
Name: ref.ShortName(),
Tag: ref.Tag(),
})
if err != nil {
log.WithFields(log.Fields{
"error": err,
"image": ref.Remote(),
}).Error("trigger.poll.RepositoryWatcher.addJob: failed to get image digest")
return err
}
key := getImageIdentifier(ref)
details := &watchDetails{
imageRef: ref,
digest: digest, // current image digest
registryUsername: registryUsername,
registryPassword: registryPassword,
schedule: schedule,
}
// adding new job
job := NewWatchTagJob(w.providers, w.registryClient, details)
log.WithFields(log.Fields{
"job_name": key,
"image": ref.Remote(),
"schedule": schedule,
}).Info("trigger.poll.RepositoryWatcher: new job added")
return w.cron.AddJob(key, schedule, job)
}
2017-07-02 12:30:36 +00:00
// Watch specific tag job
type WatchTagJob struct {
providers provider.Providers
registryClient registry.Client
details *watchDetails
}
func NewWatchTagJob(providers provider.Providers, registryClient registry.Client, details *watchDetails) *WatchTagJob {
return &WatchTagJob{
providers: providers,
registryClient: registryClient,
details: details,
}
}
func (j *WatchTagJob) Run() {
currentDigest, err := j.registryClient.Digest(registry.Opts{
Registry: j.details.imageRef.Registry(),
Name: j.details.imageRef.ShortName(),
Tag: j.details.imageRef.Tag(),
})
if err != nil {
log.WithFields(log.Fields{
"error": err,
"image": j.details.imageRef.Remote(),
}).Error("trigger.poll.WatchTagJob: failed to check digest")
return
}
// checking whether image digest has changed
if j.details.digest != currentDigest {
// updating digest
j.details.digest = currentDigest
event := types.Event{
Repository: types.Repository{
Name: j.details.imageRef.Remote(),
Tag: j.details.imageRef.Tag(),
Digest: currentDigest,
},
TriggerName: types.TriggerTypePoll.String(),
}
j.providers.Submit(event)
}
}