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  }