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), } }