github.com/uber/kraken@v0.1.4/lib/persistedretry/writeback/executor_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 writeback 15 16 import ( 17 "bytes" 18 "errors" 19 "testing" 20 21 "github.com/uber/kraken/core" 22 "github.com/uber/kraken/lib/backend" 23 "github.com/uber/kraken/lib/backend/backenderrors" 24 "github.com/uber/kraken/lib/store" 25 "github.com/uber/kraken/lib/store/metadata" 26 "github.com/uber/kraken/mocks/lib/backend" 27 "github.com/uber/kraken/utils/mockutil" 28 "github.com/uber/kraken/utils/testutil" 29 30 "github.com/golang/mock/gomock" 31 "github.com/stretchr/testify/require" 32 "github.com/uber-go/tally" 33 ) 34 35 type executorMocks struct { 36 ctrl *gomock.Controller 37 cas *store.CAStore 38 backends *backend.Manager 39 } 40 41 func newExecutorMocks(t *testing.T) (*executorMocks, func()) { 42 var cleanup testutil.Cleanup 43 defer cleanup.Recover() 44 45 ctrl := gomock.NewController(t) 46 cleanup.Add(ctrl.Finish) 47 48 cas, c := store.CAStoreFixture() 49 cleanup.Add(c) 50 51 return &executorMocks{ 52 ctrl: ctrl, 53 cas: cas, 54 backends: backend.ManagerFixture(), 55 }, cleanup.Run 56 } 57 58 func (m *executorMocks) new() *Executor { 59 return NewExecutor(tally.NoopScope, m.cas, m.backends) 60 } 61 62 func (m *executorMocks) client(namespace string) *mockbackend.MockClient { 63 client := mockbackend.NewMockClient(m.ctrl) 64 if err := m.backends.Register(namespace, client); err != nil { 65 panic(err) 66 } 67 return client 68 } 69 70 func setupBlob(t *testing.T, cas *store.CAStore, blob *core.BlobFixture) { 71 t.Helper() 72 require.NoError(t, cas.CreateCacheFile(blob.Digest.Hex(), bytes.NewReader(blob.Content))) 73 _, err := cas.SetCacheFileMetadata(blob.Digest.Hex(), metadata.NewPersist(true)) 74 require.NoError(t, err) 75 } 76 77 func TestExec(t *testing.T) { 78 require := require.New(t) 79 80 mocks, cleanup := newExecutorMocks(t) 81 defer cleanup() 82 83 blob := core.NewBlobFixture() 84 85 setupBlob(t, mocks.cas, blob) 86 87 task := NewTask(core.TagFixture(), blob.Digest.Hex(), 0) 88 89 client := mocks.client(task.Namespace) 90 client.EXPECT().Stat(task.Namespace, blob.Digest.Hex()).Return(nil, backenderrors.ErrBlobNotFound) 91 client.EXPECT().Upload(task.Namespace, blob.Digest.Hex(), mockutil.MatchReader(blob.Content)).Return(nil) 92 93 executor := mocks.new() 94 95 require.NoError(executor.Exec(task)) 96 97 // Should be safe to delete the file. 98 require.NoError(mocks.cas.DeleteCacheFile(blob.Digest.Hex())) 99 } 100 101 func TestExecNoopWhenFileAlreadyUploaded(t *testing.T) { 102 require := require.New(t) 103 104 mocks, cleanup := newExecutorMocks(t) 105 defer cleanup() 106 107 blob := core.NewBlobFixture() 108 109 setupBlob(t, mocks.cas, blob) 110 111 require.NoError(mocks.cas.CreateCacheFile(blob.Digest.Hex(), bytes.NewReader(blob.Content))) 112 113 task := NewTask(core.TagFixture(), blob.Digest.Hex(), 0) 114 115 client := mocks.client(task.Namespace) 116 client.EXPECT().Stat(task.Namespace, blob.Digest.Hex()).Return(core.NewBlobInfo(blob.Length()), nil) 117 118 executor := mocks.new() 119 120 require.NoError(executor.Exec(task)) 121 122 // Should be safe to delete the file. 123 require.NoError(mocks.cas.DeleteCacheFile(blob.Digest.Hex())) 124 } 125 126 func TestExecNoopWhenFileMissing(t *testing.T) { 127 require := require.New(t) 128 129 mocks, cleanup := newExecutorMocks(t) 130 defer cleanup() 131 132 blob := core.NewBlobFixture() 133 134 task := NewTask(core.TagFixture(), blob.Digest.Hex(), 0) 135 136 client := mocks.client(task.Namespace) 137 client.EXPECT().Stat(task.Namespace, blob.Digest.Hex()).Return(nil, backenderrors.ErrBlobNotFound) 138 139 executor := mocks.new() 140 141 require.NoError(executor.Exec(task)) 142 } 143 144 func TestExecNoopWhenNamespaceNotFound(t *testing.T) { 145 require := require.New(t) 146 147 mocks, cleanup := newExecutorMocks(t) 148 defer cleanup() 149 150 blob := core.NewBlobFixture() 151 152 setupBlob(t, mocks.cas, blob) 153 154 require.NoError(mocks.cas.CreateCacheFile(blob.Digest.Hex(), bytes.NewReader(blob.Content))) 155 156 task := NewTask(core.TagFixture(), blob.Digest.Hex(), 0) 157 158 executor := mocks.new() 159 160 require.NoError(executor.Exec(task)) 161 162 // Should be safe to delete the file. 163 require.NoError(mocks.cas.DeleteCacheFile(blob.Digest.Hex())) 164 } 165 166 func TestExecUploadFailure(t *testing.T) { 167 require := require.New(t) 168 169 mocks, cleanup := newExecutorMocks(t) 170 defer cleanup() 171 172 blob := core.NewBlobFixture() 173 174 setupBlob(t, mocks.cas, blob) 175 176 task := NewTask(core.TagFixture(), blob.Digest.Hex(), 0) 177 178 client := mocks.client(task.Namespace) 179 client.EXPECT().Stat(task.Namespace, blob.Digest.Hex()).Return(nil, backenderrors.ErrBlobNotFound) 180 client.EXPECT().Upload(task.Namespace, 181 blob.Digest.Hex(), mockutil.MatchReader(blob.Content)).Return(errors.New("some error")) 182 183 executor := mocks.new() 184 185 require.Error(executor.Exec(task)) 186 187 // Since upload failed, deletion of the file should fail since persist 188 // metadata is still present. 189 require.Error(mocks.cas.DeleteCacheFile(blob.Digest.Hex())) 190 }