156 lines
3.6 KiB
Go
156 lines
3.6 KiB
Go
|
package azure
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"io/ioutil"
|
||
|
|
||
|
azure "github.com/MSOpenTech/azure-sdk-for-go/clients/storage"
|
||
|
)
|
||
|
|
||
|
type StorageSimulator struct {
|
||
|
blobs map[string]*BlockBlob
|
||
|
}
|
||
|
|
||
|
type BlockBlob struct {
|
||
|
blocks map[string]*DataBlock
|
||
|
blockList []string
|
||
|
}
|
||
|
|
||
|
type DataBlock struct {
|
||
|
data []byte
|
||
|
committed bool
|
||
|
}
|
||
|
|
||
|
func (s *StorageSimulator) path(container, blob string) string {
|
||
|
return fmt.Sprintf("%s/%s", container, blob)
|
||
|
}
|
||
|
|
||
|
func (s *StorageSimulator) BlobExists(container, blob string) (bool, error) {
|
||
|
_, ok := s.blobs[s.path(container, blob)]
|
||
|
return ok, nil
|
||
|
}
|
||
|
|
||
|
func (s *StorageSimulator) GetBlob(container, blob string) (io.ReadCloser, error) {
|
||
|
bb, ok := s.blobs[s.path(container, blob)]
|
||
|
if !ok {
|
||
|
return nil, fmt.Errorf("blob not found")
|
||
|
}
|
||
|
|
||
|
var readers []io.Reader
|
||
|
for _, bID := range bb.blockList {
|
||
|
readers = append(readers, bytes.NewReader(bb.blocks[bID].data))
|
||
|
}
|
||
|
return ioutil.NopCloser(io.MultiReader(readers...)), nil
|
||
|
}
|
||
|
|
||
|
func (s *StorageSimulator) GetSectionReader(container, blob string, start, length int64) (io.ReadCloser, error) {
|
||
|
r, err := s.GetBlob(container, blob)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
b, err := ioutil.ReadAll(r)
|
||
|
if err != nil {
|
||
|
return nil, err
|
||
|
}
|
||
|
return ioutil.NopCloser(bytes.NewReader(b[start : start+length])), nil
|
||
|
}
|
||
|
|
||
|
func (s *StorageSimulator) CreateBlockBlob(container, blob string) error {
|
||
|
path := s.path(container, blob)
|
||
|
bb := &BlockBlob{
|
||
|
blocks: make(map[string]*DataBlock),
|
||
|
blockList: []string{},
|
||
|
}
|
||
|
s.blobs[path] = bb
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (s *StorageSimulator) PutBlock(container, blob, blockID string, chunk []byte) error {
|
||
|
path := s.path(container, blob)
|
||
|
bb, ok := s.blobs[path]
|
||
|
if !ok {
|
||
|
return fmt.Errorf("blob not found")
|
||
|
}
|
||
|
data := make([]byte, len(chunk))
|
||
|
copy(data, chunk)
|
||
|
bb.blocks[blockID] = &DataBlock{data: data, committed: false} // add block to blob
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func (s *StorageSimulator) GetBlockList(container, blob string, blockType azure.BlockListType) (azure.BlockListResponse, error) {
|
||
|
resp := azure.BlockListResponse{}
|
||
|
bb, ok := s.blobs[s.path(container, blob)]
|
||
|
if !ok {
|
||
|
return resp, fmt.Errorf("blob not found")
|
||
|
}
|
||
|
|
||
|
// Iterate committed blocks (in order)
|
||
|
if blockType == azure.BlockListTypeAll || blockType == azure.BlockListTypeCommitted {
|
||
|
for _, blockID := range bb.blockList {
|
||
|
b := bb.blocks[blockID]
|
||
|
block := azure.BlockResponse{
|
||
|
Name: blockID,
|
||
|
Size: int64(len(b.data)),
|
||
|
}
|
||
|
resp.CommittedBlocks = append(resp.CommittedBlocks, block)
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// Iterate uncommitted blocks (in no order)
|
||
|
if blockType == azure.BlockListTypeAll || blockType == azure.BlockListTypeCommitted {
|
||
|
for blockID, b := range bb.blocks {
|
||
|
block := azure.BlockResponse{
|
||
|
Name: blockID,
|
||
|
Size: int64(len(b.data)),
|
||
|
}
|
||
|
if !b.committed {
|
||
|
resp.UncommittedBlocks = append(resp.UncommittedBlocks, block)
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return resp, nil
|
||
|
}
|
||
|
|
||
|
func (s *StorageSimulator) PutBlockList(container, blob string, blocks []azure.Block) error {
|
||
|
bb, ok := s.blobs[s.path(container, blob)]
|
||
|
if !ok {
|
||
|
return fmt.Errorf("blob not found")
|
||
|
}
|
||
|
|
||
|
var blockIDs []string
|
||
|
for _, v := range blocks {
|
||
|
bl, ok := bb.blocks[v.Id]
|
||
|
if !ok { // check if block ID exists
|
||
|
return fmt.Errorf("Block id '%s' not found", v.Id)
|
||
|
}
|
||
|
bl.committed = true
|
||
|
blockIDs = append(blockIDs, v.Id)
|
||
|
}
|
||
|
|
||
|
// Mark all other blocks uncommitted
|
||
|
for k, b := range bb.blocks {
|
||
|
inList := false
|
||
|
for _, v := range blockIDs {
|
||
|
if k == v {
|
||
|
inList = true
|
||
|
break
|
||
|
}
|
||
|
}
|
||
|
if !inList {
|
||
|
b.committed = false
|
||
|
}
|
||
|
}
|
||
|
|
||
|
bb.blockList = blockIDs
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func NewStorageSimulator() StorageSimulator {
|
||
|
return StorageSimulator{
|
||
|
blobs: make(map[string]*BlockBlob),
|
||
|
}
|
||
|
}
|