github.com/uber/kraken@v0.1.4/origin/blobserver/testutils_test.go (about) 1 // Copyright (c) 2016-2019 Uber Technologies, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 package blobserver 15 16 import ( 17 "bytes" 18 "testing" 19 "time" 20 21 "go.uber.org/zap" 22 23 "github.com/andres-erbsen/clock" 24 "github.com/golang/mock/gomock" 25 "github.com/stretchr/testify/require" 26 "github.com/uber-go/tally" 27 28 "github.com/uber/kraken/core" 29 "github.com/uber/kraken/lib/backend" 30 "github.com/uber/kraken/lib/blobrefresh" 31 "github.com/uber/kraken/lib/hashring" 32 "github.com/uber/kraken/lib/healthcheck" 33 "github.com/uber/kraken/lib/hostlist" 34 "github.com/uber/kraken/lib/metainfogen" 35 "github.com/uber/kraken/lib/store" 36 "github.com/uber/kraken/mocks/lib/backend" 37 "github.com/uber/kraken/mocks/lib/persistedretry" 38 "github.com/uber/kraken/mocks/origin/blobclient" 39 "github.com/uber/kraken/origin/blobclient" 40 "github.com/uber/kraken/utils/log" 41 "github.com/uber/kraken/utils/stringset" 42 "github.com/uber/kraken/utils/testutil" 43 ) 44 45 const ( 46 master1 = "dummy-origin-master01-zone2:80" 47 master2 = "dummy-origin-master02-zone2:80" 48 master3 = "dummy-origin-master03-zone2:80" 49 ) 50 51 func init() { 52 zapConfig := zap.NewProductionConfig() 53 zapConfig.OutputPaths = []string{} 54 log.ConfigureLogger(zapConfig) 55 } 56 57 func newHashRing(maxReplica int) hashring.Ring { 58 return hashring.New( 59 hashring.Config{MaxReplica: maxReplica}, 60 hostlist.Fixture(master1, master2, master3), 61 healthcheck.IdentityFilter{}) 62 } 63 64 func hashRingNoReplica() hashring.Ring { return newHashRing(1) } 65 func hashRingSomeReplica() hashring.Ring { return newHashRing(2) } 66 func hashRingMaxReplica() hashring.Ring { return newHashRing(3) } 67 68 // testClientProvider implements blobclient.ClientProvider. It maps origin hostnames to 69 // the local addresses they are running on, such that Provide("dummy-origin") 70 // can resolve a real address. 71 type testClientProvider struct { 72 clients map[string]blobclient.Client 73 } 74 75 func newTestClientProvider() *testClientProvider { 76 return &testClientProvider{make(map[string]blobclient.Client)} 77 } 78 79 func (p *testClientProvider) register(host string, client blobclient.Client) { 80 p.clients[host] = client 81 } 82 83 func (p *testClientProvider) Provide(host string) blobclient.Client { 84 c, ok := p.clients[host] 85 if !ok { 86 log.Panicf("host %q not found", host) 87 } 88 return c 89 } 90 91 // testServer is a convenience wrapper around the underlying components of a 92 // Server and faciliates restarting Servers with new configuration. 93 type testServer struct { 94 ctrl *gomock.Controller 95 host string 96 addr string 97 cas *store.CAStore 98 cp *testClientProvider 99 clusterProvider *mockblobclient.MockClusterProvider 100 pctx core.PeerContext 101 backendManager *backend.Manager 102 writeBackManager *mockpersistedretry.MockManager 103 clk *clock.Mock 104 cleanup func() 105 } 106 107 func newTestServer( 108 t *testing.T, host string, ring hashring.Ring, cp *testClientProvider) *testServer { 109 110 var cleanup testutil.Cleanup 111 defer cleanup.Recover() 112 113 ctrl := gomock.NewController(t) 114 cleanup.Add(ctrl.Finish) 115 116 clusterProvider := mockblobclient.NewMockClusterProvider(ctrl) 117 118 pctx := core.PeerContextFixture() 119 120 cas, c := store.CAStoreFixture() 121 cleanup.Add(c) 122 123 bm := backend.ManagerFixture() 124 125 writeBackManager := mockpersistedretry.NewMockManager(ctrl) 126 127 mg := metainfogen.Fixture(cas, 4) 128 129 br := blobrefresh.New(blobrefresh.Config{}, tally.NoopScope, cas, bm, mg) 130 131 clk := clock.NewMock() 132 clk.Set(time.Now()) 133 134 s, err := New( 135 Config{}, tally.NoopScope, clk, host, ring, cas, cp, clusterProvider, pctx, 136 bm, br, mg, writeBackManager) 137 if err != nil { 138 panic(err) 139 } 140 141 addr, stop := testutil.StartServer(s.Handler()) 142 cleanup.Add(stop) 143 144 cp.register(host, blobclient.New(addr, blobclient.WithChunkSize(16))) 145 146 return &testServer{ 147 ctrl: ctrl, 148 host: host, 149 addr: addr, 150 cas: cas, 151 cp: cp, 152 clusterProvider: clusterProvider, 153 pctx: pctx, 154 backendManager: bm, 155 writeBackManager: writeBackManager, 156 clk: clk, 157 cleanup: cleanup.Run, 158 } 159 } 160 161 func (s *testServer) backendClient(namespace string) *mockbackend.MockClient { 162 client := mockbackend.NewMockClient(s.ctrl) 163 if err := s.backendManager.Register(namespace, client); err != nil { 164 panic(err) 165 } 166 return client 167 } 168 169 func (s *testServer) expectRemoteCluster(dns string) *mockblobclient.MockClusterClient { 170 cc := mockblobclient.NewMockClusterClient(s.ctrl) 171 s.clusterProvider.EXPECT().Provide(dns).Return(cc, nil).MinTimes(1) 172 return cc 173 } 174 175 // computeBlobForHosts generates a random digest / content which shards to hosts. 176 func computeBlobForHosts(ring hashring.Ring, hosts ...string) *core.BlobFixture { 177 want := stringset.New(hosts...) 178 for { 179 blob := core.SizedBlobFixture(32, 4) 180 got := stringset.New(ring.Locations(blob.Digest)...) 181 if stringset.Equal(want, got) { 182 return blob 183 } 184 } 185 } 186 187 func ensureHasBlob(t *testing.T, c blobclient.Client, namespace string, blob *core.BlobFixture) { 188 var buf bytes.Buffer 189 require.NoError(t, c.DownloadBlob(namespace, blob.Digest, &buf)) 190 require.Equal(t, string(blob.Content), buf.String()) 191 }