diff --git a/cmd/fluxd/main.go b/cmd/fluxd/main.go index cd6e1b27f..05b73d89b 100644 --- a/cmd/fluxd/main.go +++ b/cmd/fluxd/main.go @@ -90,8 +90,8 @@ func main() { memcachedHostname = fs.String("memcached-hostname", "", "Hostname for memcached service to use when caching chunks. If empty, no memcached will be used.") memcachedTimeout = fs.Duration("memcached-timeout", time.Second, "Maximum time to wait before giving up on memcached requests.") memcachedService = fs.String("memcached-service", "memcached", "SRV service used to discover memcache servers.") - registryCacheExpiry = fs.Duration("registry-cache-expiry", 20*time.Minute, "Duration to keep cached registry tag info. Must be < 1 month.") - registryPollInterval = fs.Duration("registry-poll-interval", 5*time.Minute, "period at which to poll registry for new images") + registryCacheExpiry = fs.Duration("registry-cache-expiry", 1*time.Hour, "Duration to keep cached image info. Must be < 1 month.") + registryPollInterval = fs.Duration("registry-poll-interval", 5*time.Minute, "period at which to check for updated images") registryRPS = fs.Int("registry-rps", 200, "maximum registry requests per second per host") registryBurst = fs.Int("registry-burst", defaultRemoteConnections, "maximum number of warmer connections to remote and memcache") @@ -240,6 +240,7 @@ func main() { memcacheClient := registryMemcache.NewMemcacheClient(registryMemcache.MemcacheConfig{ Host: *memcachedHostname, Service: *memcachedService, + Expiry: *registryCacheExpiry, Timeout: *memcachedTimeout, UpdateInterval: 1 * time.Minute, Logger: log.With(logger, "component", "memcached"), @@ -270,7 +271,6 @@ func main() { cacheWarmer = &cache.Warmer{ Logger: warmerLogger, ClientFactory: remoteFactory, - Expiry: *registryCacheExpiry, Cache: cacheClient, Burst: *registryBurst, } diff --git a/daemon/daemon_test.go b/daemon/daemon_test.go index f4df28ff1..95d9b5aee 100644 --- a/daemon/daemon_test.go +++ b/daemon/daemon_test.go @@ -25,6 +25,7 @@ import ( "github.com/weaveworks/flux/job" "github.com/weaveworks/flux/policy" "github.com/weaveworks/flux/registry" + registryMock "github.com/weaveworks/flux/registry/mock" "github.com/weaveworks/flux/remote" "github.com/weaveworks/flux/resource" "github.com/weaveworks/flux/update" @@ -405,11 +406,13 @@ func mockDaemon(t *testing.T) (*Daemon, func(), *cluster.Mock, *mockEventWriter) img1 := makeImageInfo(currentHelloImage, time.Now()) img2 := makeImageInfo(newHelloImage, time.Now().Add(1*time.Second)) img3 := makeImageInfo("another/service:latest", time.Now().Add(1*time.Second)) - imageRegistry = registry.NewMockRegistry([]image.Info{ - img1, - img2, - img3, - }, nil) + imageRegistry = ®istryMock.Registry{ + Images: []image.Info{ + img1, + img2, + img3, + }, + } } events := &mockEventWriter{} diff --git a/daemon/loop_test.go b/daemon/loop_test.go index 445717921..e99f11f03 100644 --- a/daemon/loop_test.go +++ b/daemon/loop_test.go @@ -21,7 +21,7 @@ import ( "github.com/weaveworks/flux/git" "github.com/weaveworks/flux/git/gittest" "github.com/weaveworks/flux/job" - "github.com/weaveworks/flux/registry" + registryMock "github.com/weaveworks/flux/registry/mock" "github.com/weaveworks/flux/resource" ) @@ -66,7 +66,7 @@ func daemon(t *testing.T) (*Daemon, func()) { d := &Daemon{ Cluster: k8s, Manifests: k8s, - Registry: registry.NewMockRegistry(nil, nil), + Registry: ®istryMock.Registry{}, Checkout: working, Jobs: jobs, JobStatusCache: &job.StatusCache{Size: 100}, diff --git a/registry/integration_test.go b/registry/cache/memcached/integration_test.go similarity index 62% rename from registry/integration_test.go rename to registry/cache/memcached/integration_test.go index fdbdda609..ea209d2d8 100644 --- a/registry/integration_test.go +++ b/registry/cache/memcached/integration_test.go @@ -1,9 +1,8 @@ // +build integration -package registry +package memcached import ( - "flag" "os" "strings" "sync" @@ -13,20 +12,19 @@ import ( "github.com/go-kit/kit/log" "github.com/weaveworks/flux/image" + "github.com/weaveworks/flux/registry" "github.com/weaveworks/flux/registry/cache" "github.com/weaveworks/flux/registry/middleware" ) -var ( - memcachedIPs = flag.String("memcached-ips", "127.0.0.1:11211", "space-separated host:port values for memcached to connect to") -) +// memcachedIPs flag from memcached_test.go -// This tests a real memcache cache and a request to a real docker -// repository. It is intended to be an end-to-end integration test -// for the warmer since I had a few bugs introduced when -// refactoring. This should cover against these bugs. +// This tests a real memcached cache and a request to a real docker +// repository. It is intended to be an end-to-end integration test for +// the warmer since I had a few bugs introduced when refactoring. This +// should cover against these bugs. func TestWarming_WarmerWriteCacheRead(t *testing.T) { - mc := cache.NewFixedServerMemcacheClient(cache.MemcacheConfig{ + mc := NewFixedServerMemcacheClient(MemcacheConfig{ Timeout: time.Second, UpdateInterval: 1 * time.Minute, Logger: log.With(log.NewLogfmtLogger(os.Stderr), "component", "memcached"), @@ -36,30 +34,18 @@ func TestWarming_WarmerWriteCacheRead(t *testing.T) { logger := log.NewLogfmtLogger(os.Stderr) - remote := NewRemoteClientFactory( - log.With(logger, "component", "client"), - middleware.RateLimiterConfig{200, 10}, - ) - - cache := NewCacheClientFactory( - log.With(logger, "component", "cache"), - mc, - time.Hour, - ) + remote := ®istry.RemoteClientFactory{ + Logger: log.With(logger, "component", "client"), + Limiters: &middleware.RateLimiters{RPS: 200, Burst: 10}, + Trace: true, + } - r := NewRegistry( - cache, - log.With(logger, "component", "registry"), - 512, - ) + r := &cache.Cache{mc} - w := Warmer{ + w := &cache.Warmer{ Logger: log.With(logger, "component", "warmer"), ClientFactory: remote, - Creds: NoCredentials(), - Expiry: time.Hour, - Reader: mc, - Writer: mc, + Cache: mc, Burst: 125, } @@ -71,9 +57,9 @@ func TestWarming_WarmerWriteCacheRead(t *testing.T) { }() shutdownWg.Add(1) - go w.Loop(shutdown, shutdownWg, func() ImageCreds { - return ImageCreds{ - id.Name: NoCredentials(), + go w.Loop(shutdown, shutdownWg, func() registry.ImageCreds { + return registry.ImageCreds{ + id.Name: registry.NoCredentials(), } }) diff --git a/registry/cache/memcached/memcached.go b/registry/cache/memcached/memcached.go index b1dfe1e8a..adbbddfce 100644 --- a/registry/cache/memcached/memcached.go +++ b/registry/cache/memcached/memcached.go @@ -16,7 +16,7 @@ import ( ) const ( - expiry = time.Hour + DefaultExpiry = time.Hour ) // MemcacheClient is a memcache client that gets its server list from SRV @@ -26,6 +26,7 @@ type MemcacheClient struct { serverList *memcache.ServerList hostname string service string + ttl time.Duration logger log.Logger quit chan struct{} @@ -36,6 +37,7 @@ type MemcacheClient struct { type MemcacheConfig struct { Host string Service string + Expiry time.Duration Timeout time.Duration UpdateInterval time.Duration Logger log.Logger @@ -53,10 +55,15 @@ func NewMemcacheClient(config MemcacheConfig) *MemcacheClient { serverList: &servers, hostname: config.Host, service: config.Service, + ttl: config.Expiry, logger: config.Logger, quit: make(chan struct{}), } + if newClient.ttl == 0 { + newClient.ttl = DefaultExpiry + } + err := newClient.updateMemcacheServers() if err != nil { config.Logger.Log("err", errors.Wrapf(err, "Error setting memcache servers to '%v'", config.Host)) @@ -79,10 +86,15 @@ func NewFixedServerMemcacheClient(config MemcacheConfig, addresses ...string) *M serverList: &servers, hostname: config.Host, service: config.Service, + ttl: config.Expiry, logger: config.Logger, quit: make(chan struct{}), } + if newClient.ttl == 0 { + newClient.ttl = DefaultExpiry + } + return newClient } @@ -104,19 +116,19 @@ func (c *MemcacheClient) GetKey(k cache.Keyer) ([]byte, time.Time, error) { return []byte{}, time.Time{}, err } } - expiry := binary.BigEndian.Uint32(cacheItem.Value) - return cacheItem.Value[4:], time.Unix(int64(expiry), 0), nil + exTime := binary.BigEndian.Uint32(cacheItem.Value) + return cacheItem.Value[4:], time.Unix(int64(exTime), 0), nil } // SetKey sets the value at a key. func (c *MemcacheClient) SetKey(k cache.Keyer, v []byte) error { - expiry := time.Now().Add(expiry).Unix() - expiryBytes := make([]byte, 4, 4) - binary.BigEndian.PutUint32(expiryBytes, uint32(expiry)) + exTime := time.Now().Add(c.ttl).Unix() + exBytes := make([]byte, 4, 4) + binary.BigEndian.PutUint32(exBytes, uint32(exTime)) if err := c.client.Set(&memcache.Item{ Key: k.Key(), - Value: append(expiryBytes, v...), - Expiration: int32(expiry), + Value: append(exBytes, v...), + Expiration: int32(exTime), }); err != nil { c.logger.Log("err", errors.Wrap(err, "storing in memcache")) return err diff --git a/registry/cache/memcached/memcached_test.go b/registry/cache/memcached/memcached_test.go index 4c2ff1f33..9c0b6d408 100644 --- a/registry/cache/memcached/memcached_test.go +++ b/registry/cache/memcached/memcached_test.go @@ -1,4 +1,5 @@ // +build integration + package memcached import ( @@ -38,8 +39,7 @@ func TestMemcache_ExpiryReadWrite(t *testing.T) { t.Fatal(err) } - // Get the expiry - expiry, err := mc.GetExpiration(key) + cached, expiry, err := mc.GetKey(key) if err != nil { t.Fatal(err) } @@ -49,27 +49,7 @@ func TestMemcache_ExpiryReadWrite(t *testing.T) { if expiry.Before(time.Now()) { t.Fatal("Expiry should be in the future") } -} - -func TestMemcache_ReadWrite(t *testing.T) { - // Memcache client - mc := NewFixedServerMemcacheClient(MemcacheConfig{ - Timeout: time.Second, - UpdateInterval: 1 * time.Minute, - Logger: log.With(log.NewLogfmtLogger(os.Stderr), "component", "memcached"), - }, strings.Fields(*memcachedIPs)...) - - // Set some dummy data - err := mc.SetKey(key, val) - if err != nil { - t.Fatal(err) - } - // Get the data - cached, err := mc.GetKey(key) - if err != nil { - t.Fatal(err) - } if string(cached) != string(val) { t.Fatalf("Should have returned %q, but got %q", string(val), string(cached)) } diff --git a/registry/cache/warming.go b/registry/cache/warming.go index 9d9a0aa9f..f80bfd882 100644 --- a/registry/cache/warming.go +++ b/registry/cache/warming.go @@ -21,8 +21,7 @@ const askForNewImagesInterval = time.Minute // registries. type Warmer struct { Logger log.Logger - ClientFactory *registry.RemoteClientFactory - Expiry time.Duration + ClientFactory registry.ClientFactory Cache Client Burst int Priority chan image.Name @@ -40,7 +39,7 @@ type backlogItem struct { func (w *Warmer) Loop(stop <-chan struct{}, wg *sync.WaitGroup, imagesToFetchFunc func() registry.ImageCreds) { defer wg.Done() - if w.Logger == nil || w.ClientFactory == nil || w.Expiry == 0 || w.Cache == nil { + if w.Logger == nil || w.ClientFactory == nil || w.Cache == nil { panic("registry.Warmer fields are nil") } diff --git a/registry/cache/warming_test.go b/registry/cache/warming_test.go index df205d9df..829e5896e 100644 --- a/registry/cache/warming_test.go +++ b/registry/cache/warming_test.go @@ -2,34 +2,83 @@ package cache import ( "context" + "sync" "testing" "time" - "github.com/pkg/errors" + "github.com/go-kit/kit/log" + + "github.com/weaveworks/flux/image" + "github.com/weaveworks/flux/registry" + "github.com/weaveworks/flux/registry/mock" ) -func TestWarming_ExpiryBuffer(t *testing.T) { - testTime := time.Now() - for _, x := range []struct { - expiresIn, buffer time.Duration - expectedResult bool - }{ - {time.Minute, time.Second, false}, - {time.Second, time.Minute, true}, - } { - if withinExpiryBuffer(testTime.Add(x.expiresIn), x.buffer) != x.expectedResult { - t.Fatalf("Should return %t", x.expectedResult) - } +type mem struct { + kv map[string][]byte + mx sync.Mutex +} + +func (c *mem) SetKey(k Keyer, v []byte) error { + c.mx.Lock() + defer c.mx.Unlock() + if c.kv == nil { + c.kv = make(map[string][]byte) + } + c.kv[k.Key()] = v + return nil +} + +func (c *mem) GetKey(k Keyer) ([]byte, time.Time, error) { + c.mx.Lock() + defer c.mx.Unlock() + if c.kv == nil { + c.kv = make(map[string][]byte) } + + if v, ok := c.kv[k.Key()]; ok { + return v, time.Now().Add(time.Hour), nil + } + return nil, time.Time{}, ErrNotCached } -func TestName(t *testing.T) { - err := errors.Wrap(context.DeadlineExceeded, "getting remote manifest") - t.Log(err.Error()) - err = errors.Cause(err) - if err == context.DeadlineExceeded { - t.Log("OK") +// WarmTest effectively checks that the cache.Warmer and +// cache.Registry work together as intended: that is, if you ask the +// warmer to fetch information, the cached gets populated, and the +// Registry implementation will see it. +func TestWarm(t *testing.T) { + ref, _ := image.ParseRef("example.com/path/image:tag") + repo := ref.Name + + client := &mock.Client{ + TagsFn: func() ([]string, error) { + return []string{"tag"}, nil + }, + ManifestFn: func(tag string) (image.Info, error) { + if tag != "tag" { + t.Errorf("remote client was asked for %q instead of %q", tag, "tag") + } + return image.Info{ + ID: ref, + CreatedAt: time.Now(), + }, nil + }, + } + factory := &mock.ClientFactory{Client: client} + c := &mem{} + warmer := &Warmer{Logger: log.NewNopLogger(), ClientFactory: factory, Cache: c, Burst: 10} + warmer.warm(context.TODO(), repo, registry.NoCredentials()) + + registry := &Cache{Reader: c} + repoInfo, err := registry.GetRepository(ref.Name) + if err != nil { + t.Error(err) + } + // Otherwise, we should get what we put in ... + if len(repoInfo) != 1 { + t.Errorf("expected an image.Info item; got %#v", repoInfo) } else { - t.Log("Not OK") + if got := repoInfo[0].ID.String(); got != ref.String() { + t.Errorf("expected image %q from registry cache; got %q", ref.String(), got) + } } } diff --git a/registry/client.go b/registry/client.go index 24f73d050..d7a3f8ab5 100644 --- a/registry/client.go +++ b/registry/client.go @@ -27,6 +27,13 @@ type Client interface { Manifest(ctx context.Context, ref string) (image.Info, error) } +// ClientFactory supplies Client implementations for a given repo, +// with credentials. This is an interface so we can provide fake +// implementations. +type ClientFactory interface { + ClientFor(image.CanonicalName, Credentials) (Client, error) +} + type Remote struct { transport http.RoundTripper repo image.CanonicalName diff --git a/registry/credentials.go b/registry/credentials.go index 240bbe2ab..5aeb433ba 100644 --- a/registry/credentials.go +++ b/registry/credentials.go @@ -55,7 +55,7 @@ func ParseCredentials(b []byte) (Credentials, error) { // Some users were passing in credentials in the form of // http://docker.io and http://docker.io/v1/, etc. - // So strip everything down to it's base host. + // So strip everything down to the host. // Also, the registry might be local and on a different port. // So we need to check for that because url.Parse won't parse the ip:port format very well. u, err := url.Parse(host) diff --git a/registry/middleware/rate_limiter_test.go b/registry/middleware/rate_limiter_test.go deleted file mode 100644 index 816b44c8e..000000000 --- a/registry/middleware/rate_limiter_test.go +++ /dev/null @@ -1,58 +0,0 @@ -package middleware - -import ( - "context" - "fmt" - "net/http" - "net/http/httptest" - "testing" - "time" -) - -const requestTimeout = 10 * time.Second - -// We shouldn't share roundtrippers in the ratelimiter because the context will be stale -func TestRateLimiter_WithContext(t *testing.T) { - ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintln(w, "Hello, client") - })) - defer ts.Close() - - rateLimit := 100 - - // A context we'll use to cancel - ctx, cancel := context.WithCancel(context.Background()) - rt := &ContextRoundTripper{Transport: http.DefaultTransport, Ctx: ctx} - rl := RateLimitedRoundTripper(rt, RateLimiterConfig{ - RPS: rateLimit, - Burst: 1, - }, ts.URL) - - client := &http.Client{ - Transport: rl, - Timeout: requestTimeout, - } - _, err := client.Get(ts.URL) - if err != nil { - t.Fatal(err) - } - cancel() // Perform the cancel. If the RL is sharing contexts, then if we use it again it will fail. - - // Now do that again, it should use a new context - // A context we'll use to cancel requests on error - ctx, cancel = context.WithCancel(context.Background()) - rt = &ContextRoundTripper{Transport: http.DefaultTransport, Ctx: ctx} - rl = RateLimitedRoundTripper(rt, RateLimiterConfig{ - RPS: rateLimit, - Burst: 1, - }, ts.URL) - client = &http.Client{ - Transport: rl, - Timeout: requestTimeout, - } - _, err = client.Get(ts.URL) - if err != nil { - t.Fatal(err) // It will fail here if it is sharing contexts - } - cancel() -} diff --git a/registry/mock.go b/registry/mock.go deleted file mode 100644 index a873e2fc0..000000000 --- a/registry/mock.go +++ /dev/null @@ -1,89 +0,0 @@ -package registry - -import ( - "context" - - "github.com/pkg/errors" - - "github.com/weaveworks/flux/image" -) - -type mockClientAdapter struct { - imgs []image.Info - err error -} - -type mockRemote struct { - img image.Info - tags []string - err error -} - -type ManifestFunc func(ref string) (image.Info, error) -type TagsFunc func() ([]string, error) -type mockDockerClient struct { - manifest ManifestFunc - tags TagsFunc -} - -func NewMockClient(manifest ManifestFunc, tags TagsFunc) Client { - return &mockDockerClient{ - manifest: manifest, - tags: tags, - } -} - -func (m *mockDockerClient) Manifest(ctx context.Context, tag string) (image.Info, error) { - return m.manifest(tag) -} - -func (m *mockDockerClient) Tags(context.Context) ([]string, error) { - return m.tags() -} - -func (*mockDockerClient) Cancel() { - return -} - -type mockRemoteFactory struct { - c Client - err error -} - -func (m *mockRemoteFactory) ClientFor(repository image.CanonicalName, creds Credentials) (Client, error) { - return m.c, m.err -} - -type mockRegistry struct { - imgs []image.Info - err error -} - -func NewMockRegistry(images []image.Info, err error) Registry { - return &mockRegistry{ - imgs: images, - err: err, - } -} - -func (m *mockRegistry) GetRepository(id image.Name) ([]image.Info, error) { - var imgs []image.Info - for _, i := range m.imgs { - // include only if it's the same repository in the same place - if i.ID.Image == id.Image { - imgs = append(imgs, i) - } - } - return imgs, m.err -} - -func (m *mockRegistry) GetImage(id image.Ref) (image.Info, error) { - if len(m.imgs) > 0 { - for _, i := range m.imgs { - if i.ID.String() == id.String() { - return i, nil - } - } - } - return image.Info{}, errors.New("not found") -} diff --git a/registry/mock/mock.go b/registry/mock/mock.go new file mode 100644 index 000000000..9e2fd9814 --- /dev/null +++ b/registry/mock/mock.go @@ -0,0 +1,66 @@ +package mock + +import ( + "context" + + "github.com/pkg/errors" + + "github.com/weaveworks/flux/image" + "github.com/weaveworks/flux/registry" +) + +type ManifestFunc func(ref string) (image.Info, error) +type TagsFunc func() ([]string, error) + +type Client struct { + ManifestFn ManifestFunc + TagsFn TagsFunc +} + +func (m *Client) Manifest(ctx context.Context, tag string) (image.Info, error) { + return m.ManifestFn(tag) +} + +func (m *Client) Tags(context.Context) ([]string, error) { + return m.TagsFn() +} + +var _ registry.Client = &Client{} + +type ClientFactory struct { + Client registry.Client + Err error +} + +func (m *ClientFactory) ClientFor(repository image.CanonicalName, creds registry.Credentials) (registry.Client, error) { + return m.Client, m.Err +} + +var _ registry.ClientFactory = &ClientFactory{} + +type Registry struct { + Images []image.Info + Err error +} + +func (m *Registry) GetRepository(id image.Name) ([]image.Info, error) { + var imgs []image.Info + for _, i := range m.Images { + // include only if it's the same repository in the same place + if i.ID.Image == id.Image { + imgs = append(imgs, i) + } + } + return imgs, m.Err +} + +func (m *Registry) GetImage(id image.Ref) (image.Info, error) { + for _, i := range m.Images { + if i.ID.String() == id.String() { + return i, nil + } + } + return image.Info{}, errors.New("not found") +} + +var _ registry.Registry = &Registry{} diff --git a/registry/registry_test.go b/registry/registry_test.go deleted file mode 100644 index 0ab7bd80b..000000000 --- a/registry/registry_test.go +++ /dev/null @@ -1,171 +0,0 @@ -package registry - -import ( - "errors" - "testing" - "time" - - "github.com/docker/distribution/manifest/schema1" - "github.com/go-kit/kit/log" - - fluxerr "github.com/weaveworks/flux/errors" - "github.com/weaveworks/flux/image" - "github.com/weaveworks/flux/registry/cache" - "github.com/weaveworks/flux/registry/middleware" -) - -const testTagStr = "latest" -const testImageStr = "alpine:" + testTagStr -const constTime = "2017-01-13T16:22:58.009923189Z" - -var ( - id, _ = image.ParseRef(testImageStr) - man = schema1.SignedManifest{ - Manifest: schema1.Manifest{ - History: []schema1.History{ - { - V1Compatibility: `{"created":"` + constTime + `"}`, - }, - }, - }, - } -) - -var ( - testTags = []string{testTagStr, "anotherTag"} - mClient = NewMockClient( - func(_ image.Ref) (image.Info, error) { - return image.Info{ID: id, CreatedAt: time.Time{}}, nil - }, - func(repository image.Name) ([]string, error) { - return testTags, nil - }, - ) -) - -func TestRegistry_GetRepository(t *testing.T) { - fact := NewMockClientFactory(mClient, nil) - reg := NewRegistry(fact, log.NewNopLogger(), 512) - imgs, err := reg.GetRepository(id.Name) - if err != nil { - t.Fatal(err) - } - // Dev note, the tags will look the same because we are returning the same - // Image from the mock remote. But they are distinct images. - if len(testTags) != len(imgs) { - t.Fatal("Expecting %v images, but got %v", len(testTags), len(imgs)) - } -} - -func TestRegistry_GetRepositoryFactoryError(t *testing.T) { - errFact := NewMockClientFactory(mClient, errors.New("")) - reg := NewRegistry(errFact, nil, 512) - _, err := reg.GetRepository(id.Name) - if err == nil { - t.Fatal("Expecting error") - } -} - -func TestRegistry_GetRepositoryManifestError(t *testing.T) { - errClient := NewMockClient( - func(repository image.Ref) (image.Info, error) { - return image.Info{}, errors.New("") - }, - func(repository image.Name) ([]string, error) { - return testTags, nil - }, - ) - errFact := NewMockClientFactory(errClient, nil) - reg := NewRegistry(errFact, log.NewNopLogger(), 512) - _, err := reg.GetRepository(id.Name) - if err == nil { - t.Fatal("Expecting error") - } -} - -// Note: This actually goes off to docker hub to find the Image. -// It will fail if there is not internet connection -func TestRemoteFactory_RawClient(t *testing.T) { - // No credentials required for public Image - fact := NewRemoteClientFactory(log.NewNopLogger(), middleware.RateLimiterConfig{ - RPS: 200, - Burst: 1, - }) - - // Refresh tags first - var tags []string - client, err := fact.ClientFor(id.Registry(), Credentials{}) - if err != nil { - t.Fatal(err) - } - - tags, err = client.Tags(id.Name) - if err != nil { - t.Fatal(err) - } - client.Cancel() - if len(tags) == 0 { - t.Fatal("Should have some tags") - } - - client, err = fact.ClientFor(id.Registry(), Credentials{}) - if err != nil { - t.Fatal(err) - } - id.Tag = tags[0] - newImg, err := client.Manifest(id) - if err != nil { - t.Fatal(err) - } - if newImg.ID.String() == "" { - t.Fatal("Should image ") - } - if newImg.CreatedAt.IsZero() { - t.Fatal("CreatedAt time was 0") - } - client.Cancel() -} - -func TestRemoteFactory_InvalidHost(t *testing.T) { - fact := NewRemoteClientFactory(log.NewNopLogger(), middleware.RateLimiterConfig{}) - invalidId, err := image.ParseRef("invalid.host/library/alpine:latest") - if err != nil { - t.Fatal(err) - } - client, err := fact.ClientFor(invalidId.Registry(), Credentials{}) - if err != nil { - return - } - _, err = client.Manifest(invalidId) - if err == nil { - t.Fatal("Expected error due to invalid host but got none.") - } -} - -func TestRemote_BetterError(t *testing.T) { - errClient := NewMockClient( - func(repository image.Ref) (image.Info, error) { - return image.Info{}, cache.ErrNotCached - }, - func(repository image.Name) ([]string, error) { - return []string{}, cache.ErrNotCached - }, - ) - - fact := NewMockClientFactory(errClient, nil) - reg := NewRegistry(fact, log.NewNopLogger(), 512) - _, err := reg.GetRepository(id.Name) - if err == nil { - t.Fatal("Should have errored") - } - if !fluxerr.IsMissing(err) { - t.Fatalf("Should not be bespoke error, got %q", err.Error()) - } - _, err = reg.GetImage(id) - if err == nil { - t.Fatal("Should have errored") - } - if !fluxerr.IsMissing(err) { - t.Fatalf("Should not be bespoke error, got %q", err.Error()) - } -} diff --git a/release/releaser_test.go b/release/releaser_test.go index 1eeb1bda6..b646aff64 100644 --- a/release/releaser_test.go +++ b/release/releaser_test.go @@ -13,7 +13,7 @@ import ( "github.com/weaveworks/flux/git" "github.com/weaveworks/flux/git/gittest" "github.com/weaveworks/flux/image" - "github.com/weaveworks/flux/registry" + registryMock "github.com/weaveworks/flux/registry/mock" "github.com/weaveworks/flux/update" ) @@ -80,16 +80,18 @@ var ( } newRef, _ = image.ParseRef("quay.io/weaveworks/helloworld:master-a000002") timeNow = time.Now() - mockRegistry = registry.NewMockRegistry([]image.Info{ - image.Info{ - ID: newRef, - CreatedAt: timeNow, - }, - image.Info{ - ID: newLockedID, - CreatedAt: timeNow, + mockRegistry = ®istryMock.Registry{ + Images: []image.Info{ + { + ID: newRef, + CreatedAt: timeNow, + }, + { + ID: newLockedID, + CreatedAt: timeNow, + }, }, - }, nil) + } mockManifests = &kubernetes.Manifests{} ) @@ -316,16 +318,18 @@ func Test_ImageStatus(t *testing.T) { }, } - upToDateRegistry := registry.NewMockRegistry([]image.Info{ - image.Info{ - ID: oldRef, - CreatedAt: timeNow, - }, - image.Info{ - ID: sidecarRef, - CreatedAt: timeNow, + upToDateRegistry := ®istryMock.Registry{ + Images: []image.Info{ + { + ID: oldRef, + CreatedAt: timeNow, + }, + { + ID: sidecarRef, + CreatedAt: timeNow, + }, }, - }, nil) + } testSvcSpec, _ := update.ParseResourceSpec(testSvc.ID.String()) for _, tst := range []struct {