using cred helper from watcher

feature/ecr_registry_auth
Karolis Rusenas 2018-04-28 22:01:44 +01:00
parent 286508d60d
commit 52c5101646
5 changed files with 181 additions and 154 deletions

View File

@ -40,6 +40,7 @@ import (
// credentials helpers
_ "github.com/keel-hq/keel/extension/credentialshelper/aws"
secretsCredentialsHelper "github.com/keel-hq/keel/extension/credentialshelper/secrets"
// bots
_ "github.com/keel-hq/keel/bot/hipchat"
@ -171,8 +172,14 @@ func main() {
// setting up providers
providers := setupProviders(implementer, sender, approvalsManager, &t.GenericResourceCache)
// registering secrets based credentials helper
secretsGetter := secrets.NewGetter(implementer)
teardownTriggers := setupTriggers(ctx, providers, secretsGetter, approvalsManager)
ch := secretsCredentialsHelper.New(secretsGetter)
credentialshelper.RegisterCredentialsHelper("secrets", ch)
// trigger setup
teardownTriggers := setupTriggers(ctx, providers, approvalsManager)
bot.Run(implementer, approvalsManager)
@ -235,7 +242,7 @@ func setupProviders(k8sImplementer kubernetes.Implementer, sender notification.S
// setupTriggers - setting up triggers. New triggers should be added to this function. Each trigger
// should go through all providers (or not if there is a reason) and submit events)
func setupTriggers(ctx context.Context, providers provider.Providers, secretsGetter secrets.Getter, approvalsManager approvals.Manager) (teardown func()) {
func setupTriggers(ctx context.Context, providers provider.Providers, approvalsManager approvals.Manager) (teardown func()) {
// setting up generic http webhook server
whs := http.NewTriggerServer(&http.Opts{
@ -273,7 +280,7 @@ func setupTriggers(ctx context.Context, providers provider.Providers, secretsGet
registryClient := registry.New()
watcher := poll.NewRepositoryWatcher(providers, registryClient)
pollManager := poll.NewPollManager(providers, watcher, secretsGetter, credentialshelper.New())
pollManager := poll.NewPollManager(providers, watcher)
// start poll manager, will finish with ctx
go watcher.Start(ctx)

View File

@ -5,9 +5,7 @@ import (
"sync"
"time"
"github.com/keel-hq/keel/extension/credentialshelper"
"github.com/keel-hq/keel/provider"
"github.com/keel-hq/keel/secrets"
"github.com/keel-hq/keel/types"
"github.com/prometheus/client_golang/prometheus"
@ -31,10 +29,6 @@ func init() {
type DefaultManager struct {
providers provider.Providers
secretsGetter secrets.Getter
credentialsHelper credentialshelper.CredentialsHelper
// repository watcher
watcher Watcher
@ -48,14 +42,12 @@ type DefaultManager struct {
}
// NewPollManager - new default poller
func NewPollManager(providers provider.Providers, watcher Watcher, secretsGetter secrets.Getter, credentialsHelper credentialshelper.CredentialsHelper) *DefaultManager {
func NewPollManager(providers provider.Providers, watcher Watcher) *DefaultManager {
return &DefaultManager{
providers: providers,
secretsGetter: secretsGetter,
credentialsHelper: credentialsHelper,
watcher: watcher,
mu: &sync.Mutex{},
scanTick: 1,
providers: providers,
watcher: watcher,
mu: &sync.Mutex{},
scanTick: 1,
}
}
@ -93,59 +85,25 @@ func (s *DefaultManager) Start(ctx context.Context) error {
}
func (s *DefaultManager) scan(ctx context.Context) error {
log.Info("performing scan")
log.Debug("trigger.poll.manager: performing scan")
trackedImages, err := s.providers.TrackedImages()
if err != nil {
return err
}
var tracked float64
for _, trackedImage := range trackedImages {
if trackedImage.Trigger != types.TriggerTypePoll {
continue
}
tracked++
var imageCreds *types.Credentials
// anonymous credentials
creds := &types.Credentials{}
imageCreds, err = s.secretsGetter.Get(trackedImage)
if err != nil {
log.WithFields(log.Fields{
"error": err,
"secrets": trackedImage.Secrets,
"image": trackedImage.Image.Remote(),
}).Error("trigger.poll.manager: failed to get authentication credentials")
} else {
creds = imageCreds
}
// TODO: refactor to either recreate it every 10 hours (12 hours expiration) or better to retrieve creds
// just before quering the registry
if imageCreds.Username == "" && imageCreds.Password == "" {
registryCreds, err := s.credentialsHelper.GetCredentials(trackedImage.Image.Registry())
if err != nil {
log.WithFields(log.Fields{
"error": err,
"registry": trackedImage.Image.Registry(),
"image": trackedImage.Image.Remote(),
}).Error("trigger.poll.manager: failed to get registry credentials")
} else {
creds = registryCreds
}
}
err = s.watcher.Watch(trackedImage.Image.Remote(), trackedImage.PollSchedule, creds.Username, creds.Password)
err = s.watcher.Watch(trackedImage, trackedImage.PollSchedule)
if err != nil {
log.WithFields(log.Fields{
"error": err,
"schedule": trackedImage.PollSchedule,
"image": trackedImage.Image.Remote(),
}).Error("trigger.poll.manager: failed to start watching repository")
// continue processing other images
}
}

View File

@ -13,7 +13,7 @@ import (
"github.com/keel-hq/keel/util/codecs"
"github.com/keel-hq/keel/util/image"
"github.com/keel-hq/keel/extension/credentialshelper"
// "github.com/keel-hq/keel/extension/credentialshelper"
_ "github.com/keel-hq/keel/extension/credentialshelper/aws"
"testing"
@ -59,7 +59,7 @@ func TestCheckDeployment(t *testing.T) {
watcher := NewRepositoryWatcher(providers, frc)
pm := NewPollManager(providers, watcher, &FakeSecretsGetter{}, credentialshelper.New())
pm := NewPollManager(providers, watcher)
imageA := "gcr.io/v2-namespace/hello-world:1.1.1"
imageB := "gcr.io/v2-namespace/greetings-world:1.1.1"
@ -80,11 +80,11 @@ func TestCheckDeployment(t *testing.T) {
if watcher.watched[keyA].schedule != types.KeelPollDefaultSchedule {
t.Errorf("unexpected schedule: %s", watcher.watched[keyA].schedule)
}
if watcher.watched[keyA].imageRef.Remote() != ref.Remote() {
t.Errorf("unexpected remote remote: %s", watcher.watched[keyA].imageRef.Remote())
if watcher.watched[keyA].trackedImage.Image.Remote() != ref.Remote() {
t.Errorf("unexpected remote remote: %s", watcher.watched[keyA].trackedImage.Image.Remote())
}
if watcher.watched[keyA].imageRef.Tag() != ref.Tag() {
t.Errorf("unexpected tag: %s", watcher.watched[keyA].imageRef.Tag())
if watcher.watched[keyA].trackedImage.Image.Tag() != ref.Tag() {
t.Errorf("unexpected tag: %s", watcher.watched[keyA].trackedImage.Image.Tag())
}
refB, _ := image.Parse(imageB)
@ -95,11 +95,11 @@ func TestCheckDeployment(t *testing.T) {
if watcher.watched[keyB].schedule != types.KeelPollDefaultSchedule {
t.Errorf("unexpected schedule: %s", watcher.watched[keyB].schedule)
}
if watcher.watched[keyB].imageRef.Remote() != refB.Remote() {
t.Errorf("unexpected remote remote: %s", watcher.watched[keyB].imageRef.Remote())
if watcher.watched[keyB].trackedImage.Image.Remote() != refB.Remote() {
t.Errorf("unexpected remote remote: %s", watcher.watched[keyB].trackedImage.Image.Remote())
}
if watcher.watched[keyB].imageRef.Tag() != refB.Tag() {
t.Errorf("unexpected tag: %s", watcher.watched[keyB].imageRef.Tag())
if watcher.watched[keyB].trackedImage.Image.Tag() != refB.Tag() {
t.Errorf("unexpected tag: %s", watcher.watched[keyB].trackedImage.Image.Tag())
}
}
@ -132,7 +132,7 @@ func TestCheckECRDeployment(t *testing.T) {
watcher := NewRepositoryWatcher(providers, rc)
pm := NewPollManager(providers, watcher, &FakeSecretsGetter{}, credentialshelper.New())
pm := NewPollManager(providers, watcher)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
@ -157,10 +157,10 @@ func TestCheckECRDeployment(t *testing.T) {
if watcher.watched[keyA].schedule != types.KeelPollDefaultSchedule {
t.Errorf("unexpected schedule: %s", watcher.watched[keyA].schedule)
}
if watcher.watched[keyA].imageRef.Remote() != imgA.Remote() {
t.Errorf("unexpected remote remote: %s", watcher.watched[keyA].imageRef.Remote())
if watcher.watched[keyA].trackedImage.Image.Remote() != imgA.Remote() {
t.Errorf("unexpected remote remote: %s", watcher.watched[keyA].trackedImage.Image.Remote())
}
if watcher.watched[keyA].imageRef.Tag() != imgA.Tag() {
t.Errorf("unexpected tag: %s", watcher.watched[keyA].imageRef.Tag())
if watcher.watched[keyA].trackedImage.Image.Tag() != imgA.Tag() {
t.Errorf("unexpected tag: %s", watcher.watched[keyA].trackedImage.Image.Tag())
}
}

View File

@ -4,6 +4,7 @@ import (
"context"
"fmt"
"github.com/keel-hq/keel/extension/credentialshelper"
"github.com/keel-hq/keel/provider"
"github.com/keel-hq/keel/registry"
"github.com/keel-hq/keel/types"
@ -30,17 +31,15 @@ func init() {
// Watcher - generic watcher interface
type Watcher interface {
Watch(imageName, registryUsername, registryPassword, schedule string) error
Watch(image *types.TrackedImage, schedule string) error
Unwatch(image string) error
}
type watchDetails struct {
imageRef *image.Reference
registryUsername string // "" for anonymous
registryPassword string // "" for anonymous
digest string // image digest
latest string // latest tag
schedule string
trackedImage *types.TrackedImage
digest string // image digest
latest string // latest tag
schedule string
}
// RepositoryWatcher - repository watcher cron
@ -115,7 +114,7 @@ func (w *RepositoryWatcher) Unwatch(imageName string) error {
// 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 {
func (w *RepositoryWatcher) Watch(image *types.TrackedImage, schedule string) error {
if schedule == "" {
return fmt.Errorf("cron schedule cannot be empty")
@ -125,32 +124,23 @@ func (w *RepositoryWatcher) Watch(imageName, schedule, registryUsername, registr
if err != nil {
log.WithFields(log.Fields{
"error": err,
"image": imageName,
"image": image.String(),
"schedule": schedule,
}).Error("trigger.poll.RepositoryWatcher.addJob: invalid cron schedule")
return fmt.Errorf("invalid cron schedule: %s", err)
}
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)
key := getImageIdentifier(image.Image)
// checking whether it's already being watched
details, ok := w.watched[key]
if !ok {
err = w.addJob(imageRef, registryUsername, registryPassword, schedule)
// err = w.addJob(imageRef, registryUsername, registryPassword, schedule)
err = w.addJob(image, schedule)
if err != nil {
log.WithFields(log.Fields{
"error": err,
"image_name": imageName,
"registry_username": registryUsername,
"error": err,
"image": image.String(),
}).Error("trigger.poll.RepositoryWatcher.Watch: failed to add image watch job")
}
@ -162,53 +152,38 @@ func (w *RepositoryWatcher) Watch(imageName, schedule, registryUsername, registr
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 {
func (w *RepositoryWatcher) addJob(ti *types.TrackedImage, schedule string) error {
// getting initial digest
reg := ref.Scheme() + "://" + ref.Registry()
reg := ti.Image.Scheme() + "://" + ti.Image.Registry()
creds := credentialshelper.GetCredentials(ti)
digest, err := w.registryClient.Digest(registry.Opts{
Registry: reg,
Name: ref.ShortName(),
Tag: ref.Tag(),
Username: registryUsername,
Password: registryPassword,
Name: ti.Image.ShortName(),
Tag: ti.Image.Tag(),
Username: creds.Username,
Password: creds.Password,
})
if err != nil {
log.WithFields(log.Fields{
"error": err,
"image": ref.Remote(),
"image": ti.Image.String(),
}).Error("trigger.poll.RepositoryWatcher.addJob: failed to get image digest")
return err
}
key := getImageIdentifier(ref)
key := getImageIdentifier(ti.Image)
details := &watchDetails{
imageRef: ref,
digest: digest, // current image digest
latest: ref.Tag(),
registryUsername: registryUsername,
registryPassword: registryPassword,
schedule: schedule,
trackedImage: ti,
digest: digest, // current image digest
latest: ti.Image.Tag(),
schedule: schedule,
}
// adding job to internal map
@ -217,13 +192,13 @@ func (w *RepositoryWatcher) addJob(ref *image.Reference, registryUsername, regis
// checking tag type, for versioned (semver) tags we setup a watch all tags job
// and for non-semver types we create a single tag watcher which
// checks digest
_, err = version.GetVersion(ref.Tag())
_, err = version.GetVersion(ti.Image.Tag())
if err != nil {
// adding new job
job := NewWatchTagJob(w.providers, w.registryClient, details)
log.WithFields(log.Fields{
"job_name": key,
"image": ref.Remote(),
"image": ti.Image.String(),
"digest": digest,
"schedule": schedule,
}).Info("trigger.poll.RepositoryWatcher: new watch tag digest job added")
@ -234,7 +209,7 @@ func (w *RepositoryWatcher) addJob(ref *image.Reference, registryUsername, regis
job := NewWatchRepositoryTagsJob(w.providers, w.registryClient, details)
log.WithFields(log.Fields{
"job_name": key,
"image": ref.Remote(),
"image": ti.Image.String(),
"digest": digest,
"schedule": schedule,
}).Info("trigger.poll.RepositoryWatcher: new watch repository tags job added")
@ -261,21 +236,22 @@ func NewWatchTagJob(providers provider.Providers, registryClient registry.Client
// Run - main function to check schedule
func (j *WatchTagJob) Run() {
reg := j.details.imageRef.Scheme() + "://" + j.details.imageRef.Registry()
creds := credentialshelper.GetCredentials(j.details.trackedImage)
reg := j.details.trackedImage.Image.Scheme() + "://" + j.details.trackedImage.Image.Registry()
currentDigest, err := j.registryClient.Digest(registry.Opts{
Registry: reg,
Name: j.details.imageRef.ShortName(),
Tag: j.details.imageRef.Tag(),
Username: j.details.registryUsername,
Password: j.details.registryPassword,
Name: j.details.trackedImage.Image.ShortName(),
Tag: j.details.trackedImage.Image.Tag(),
Username: creds.Username,
Password: creds.Password,
})
registriesScannedCounter.With(prometheus.Labels{"registry": j.details.imageRef.Registry(), "image": j.details.imageRef.Name()}).Inc()
registriesScannedCounter.With(prometheus.Labels{"registry": j.details.trackedImage.Image.Registry(), "image": j.details.trackedImage.Image.Name()}).Inc()
if err != nil {
log.WithFields(log.Fields{
"error": err,
"image": j.details.imageRef.Remote(),
"image": j.details.trackedImage.Image.String(),
}).Error("trigger.poll.WatchTagJob: failed to check digest")
return
}
@ -283,7 +259,7 @@ func (j *WatchTagJob) Run() {
log.WithFields(log.Fields{
"current_digest": j.details.digest,
"new_digest": currentDigest,
"image_name": j.details.imageRef.Remote(),
"image": j.details.trackedImage.Image.String(),
}).Debug("trigger.poll.WatchTagJob: checking digest")
// checking whether image digest has changed
@ -293,14 +269,14 @@ func (j *WatchTagJob) Run() {
event := types.Event{
Repository: types.Repository{
Name: j.details.imageRef.Repository(),
Tag: j.details.imageRef.Tag(),
Name: j.details.trackedImage.Image.Repository(),
Tag: j.details.trackedImage.Image.Tag(),
Digest: currentDigest,
},
TriggerName: types.TriggerTypePoll.String(),
}
log.WithFields(log.Fields{
"repository": j.details.imageRef.Repository(),
"image": j.details.trackedImage.Image.String(),
"new_digest": currentDigest,
}).Info("trigger.poll.WatchTagJob: digest change detected, submiting event to providers")
@ -327,32 +303,35 @@ func NewWatchRepositoryTagsJob(providers provider.Providers, registryClient regi
// Run - main function to check schedule
func (j *WatchRepositoryTagsJob) Run() {
reg := j.details.imageRef.Scheme() + "://" + j.details.imageRef.Registry()
creds := credentialshelper.GetCredentials(j.details.trackedImage)
// reg := j.details.imageRef.Scheme() + "://" + j.details.imageRef.Registry()
reg := j.details.trackedImage.Image.Scheme() + "://" + j.details.trackedImage.Image.Registry()
if j.details.latest == "" {
j.details.latest = j.details.imageRef.Tag()
j.details.latest = j.details.trackedImage.Image.Tag()
}
repository, err := j.registryClient.Get(registry.Opts{
Registry: reg,
Name: j.details.imageRef.ShortName(),
Name: j.details.trackedImage.Image.ShortName(),
Tag: j.details.latest,
Username: j.details.registryUsername,
Password: j.details.registryPassword,
Username: creds.Username,
Password: creds.Password,
})
if err != nil {
log.WithFields(log.Fields{
"error": err,
"image": j.details.imageRef.Remote(),
"image": j.details.trackedImage.Image.String(),
}).Error("trigger.poll.WatchRepositoryTagsJob: failed to get repository")
return
}
log.WithFields(log.Fields{
"current_tag": j.details.imageRef.Tag(),
"current_tag": j.details.trackedImage.Image.Tag(),
"repository_tags": repository.Tags,
"image_name": j.details.imageRef.Remote(),
"image_name": j.details.trackedImage.Image.Remote(),
}).Debug("trigger.poll.WatchRepositoryTagsJob: checking tags")
latestVersion, newAvailable, err := version.NewAvailable(j.details.latest, repository.Tags)
@ -360,7 +339,8 @@ func (j *WatchRepositoryTagsJob) Run() {
log.WithFields(log.Fields{
"error": err,
"repository_tags": repository.Tags,
"image": j.details.imageRef.Remote(),
// "image": j.details.imageRef.Remote(),
"image": j.details.trackedImage.Image.String(),
}).Error("trigger.poll.WatchRepositoryTagsJob: failed to get latest version from tags")
return
}
@ -372,13 +352,13 @@ func (j *WatchRepositoryTagsJob) Run() {
j.details.latest = latestVersion
event := types.Event{
Repository: types.Repository{
Name: j.details.imageRef.Repository(),
Name: j.details.trackedImage.Image.Repository(),
Tag: latestVersion,
},
TriggerName: types.TriggerTypePoll.String(),
}
log.WithFields(log.Fields{
"repository": j.details.imageRef.Repository(),
"repository": j.details.trackedImage.Image.Repository(),
"new_tag": latestVersion,
}).Info("trigger.poll.WatchRepositoryTagsJob: submiting event to providers")
j.providers.Submit(event)

View File

@ -6,6 +6,7 @@ import (
"github.com/keel-hq/keel/approvals"
"github.com/keel-hq/keel/cache/memory"
"github.com/keel-hq/keel/extension/credentialshelper"
"github.com/keel-hq/keel/provider"
"github.com/keel-hq/keel/registry"
"github.com/keel-hq/keel/types"
@ -13,6 +14,16 @@ import (
"github.com/keel-hq/keel/util/image"
)
func mustParse(img string) *types.TrackedImage {
ref, err := image.Parse(img)
if err != nil {
panic(err)
}
return &types.TrackedImage{
Image: ref,
}
}
// ======== fake registry client for testing =======
type fakeRegistryClient struct {
opts registry.Opts // opts set if anything called Digest(opts Opts)
@ -23,6 +34,7 @@ type fakeRegistryClient struct {
}
func (c *fakeRegistryClient) Get(opts registry.Opts) (*registry.Repository, error) {
c.opts = opts
return &registry.Repository{
Name: opts.Name,
Tags: c.tagsToReturn,
@ -30,6 +42,7 @@ func (c *fakeRegistryClient) Get(opts registry.Opts) (*registry.Repository, erro
}
func (c *fakeRegistryClient) Digest(opts registry.Opts) (digest string, err error) {
c.opts = opts
return c.digestToReturn, nil
}
@ -68,8 +81,10 @@ func TestWatchTagJob(t *testing.T) {
reference, _ := image.Parse("foo/bar:1.1")
details := &watchDetails{
imageRef: reference,
digest: "sha256:123123123",
trackedImage: &types.TrackedImage{
Image: reference,
},
digest: "sha256:123123123",
}
job := NewWatchTagJob(providers, frc, details)
@ -113,8 +128,10 @@ func TestWatchTagJobLatest(t *testing.T) {
reference, _ := image.Parse("foo/bar:latest")
details := &watchDetails{
imageRef: reference,
digest: "sha256:123123123",
trackedImage: &types.TrackedImage{
Image: reference,
},
digest: "sha256:123123123",
}
job := NewWatchTagJob(providers, frc, details)
@ -158,7 +175,9 @@ func TestWatchAllTagsJob(t *testing.T) {
reference, _ := image.Parse("foo/bar:1.1.0")
details := &watchDetails{
imageRef: reference,
trackedImage: &types.TrackedImage{
Image: reference,
},
}
job := NewWatchRepositoryTagsJob(providers, frc, details)
@ -192,7 +211,9 @@ func TestWatchAllTagsJobCurrentLatest(t *testing.T) {
reference, _ := image.Parse("foo/bar:latest")
details := &watchDetails{
imageRef: reference,
trackedImage: &types.TrackedImage{
Image: reference,
},
}
job := NewWatchRepositoryTagsJob(providers, frc, details)
@ -257,10 +278,10 @@ func TestWatchMultipleTags(t *testing.T) {
watcher := NewRepositoryWatcher(providers, frc)
watcher.Watch("gcr.io/v2-namespace/hello-world:1.1.1", "@every 10m", "", "")
watcher.Watch("gcr.io/v2-namespace/greetings-world:1.1.1", "@every 10m", "", "")
watcher.Watch("gcr.io/v2-namespace/greetings-world:alpha", "@every 10m", "", "")
watcher.Watch("gcr.io/v2-namespace/greetings-world:master", "@every 10m", "", "")
watcher.Watch(mustParse("gcr.io/v2-namespace/hello-world:1.1.1"), "@every 10m")
watcher.Watch(mustParse("gcr.io/v2-namespace/greetings-world:1.1.1"), "@every 10m")
watcher.Watch(mustParse("gcr.io/v2-namespace/greetings-world:alpha"), "@every 10m")
watcher.Watch(mustParse("gcr.io/v2-namespace/greetings-world:master"), "@every 10m")
if len(watcher.watched) != 4 {
t.Errorf("expected to find watching 4 entries, found: %d", len(watcher.watched))
@ -287,3 +308,64 @@ func TestWatchMultipleTags(t *testing.T) {
}
}
}
type fakeCredentialsHelper struct {
// set by the caller
getImageRequest *types.TrackedImage
// credentials to return
creds *types.Credentials
}
func (fch *fakeCredentialsHelper) GetCredentials(image *types.TrackedImage) (*types.Credentials, error) {
fch.getImageRequest = image
return fch.creds, nil
}
func (fch *fakeCredentialsHelper) IsEnabled() bool { return true }
func TestWatchTagJobCheckCredentials(t *testing.T) {
fakeHelper := &fakeCredentialsHelper{
creds: &types.Credentials{
Username: "user-xx",
Password: "pass-xx",
},
}
credentialshelper.RegisterCredentialsHelper("fake", fakeHelper)
defer credentialshelper.UnregisterCredentialsHelper("fake")
fp := &fakeProvider{}
mem := memory.NewMemoryCache(100*time.Millisecond, 100*time.Millisecond, 10*time.Millisecond)
am := approvals.New(mem, codecs.DefaultSerializer())
providers := provider.New([]provider.Provider{fp}, am)
frc := &fakeRegistryClient{
digestToReturn: "sha256:0604af35299dd37ff23937d115d103532948b568a9dd8197d14c256a8ab8b0bb",
}
reference, _ := image.Parse("foo/bar:1.1")
details := &watchDetails{
trackedImage: &types.TrackedImage{
Image: reference,
},
digest: "sha256:123123123",
}
job := NewWatchTagJob(providers, frc, details)
job.Run()
// checking whether new job was submitted
if frc.opts.Password != "pass-xx" {
t.Errorf("unexpected password for registry: %s", frc.opts.Password)
}
if frc.opts.Username != "user-xx" {
t.Errorf("unexpected username for registry: %s", frc.opts.Username)
}
}