github.com/uber/kraken@v0.1.4/lib/torrent/storage/agentstorage/torrent_archive.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 agentstorage
    15  
    16  import (
    17  	"fmt"
    18  	"os"
    19  
    20  	"github.com/uber-go/tally"
    21  	"github.com/willf/bitset"
    22  
    23  	"github.com/uber/kraken/core"
    24  	"github.com/uber/kraken/lib/store"
    25  	"github.com/uber/kraken/lib/store/metadata"
    26  	"github.com/uber/kraken/lib/torrent/storage"
    27  	"github.com/uber/kraken/tracker/metainfoclient"
    28  )
    29  
    30  // TorrentArchive is capable of initializing torrents in the download directory
    31  // and serving torrents from either the download or cache directory.
    32  type TorrentArchive struct {
    33  	stats          tally.Scope
    34  	cads           *store.CADownloadStore
    35  	metaInfoClient metainfoclient.Client
    36  }
    37  
    38  // NewTorrentArchive creates a new TorrentArchive.
    39  func NewTorrentArchive(
    40  	stats tally.Scope,
    41  	cads *store.CADownloadStore,
    42  	mic metainfoclient.Client) *TorrentArchive {
    43  
    44  	stats = stats.Tagged(map[string]string{
    45  		"module": "agenttorrentarchive",
    46  	})
    47  
    48  	return &TorrentArchive{stats, cads, mic}
    49  }
    50  
    51  // Stat returns TorrentInfo for the given digest. Returns os.ErrNotExist if the
    52  // file does not exist. Ignores namespace.
    53  func (a *TorrentArchive) Stat(namespace string, d core.Digest) (*storage.TorrentInfo, error) {
    54  	var tm metadata.TorrentMeta
    55  	if err := a.cads.Any().GetMetadata(d.Hex(), &tm); err != nil {
    56  		return nil, err
    57  	}
    58  	var psm pieceStatusMetadata
    59  	if err := a.cads.Any().GetMetadata(d.Hex(), &psm); err != nil {
    60  		return nil, err
    61  	}
    62  	b := bitset.New(uint(len(psm.pieces)))
    63  	for i, p := range psm.pieces {
    64  		if p.status == _complete {
    65  			b.Set(uint(i))
    66  		}
    67  	}
    68  	return storage.NewTorrentInfo(tm.MetaInfo, b), nil
    69  }
    70  
    71  // CreateTorrent returns a Torrent for either an existing metainfo / file on
    72  // disk, or downloads metainfo and initializes the file. Returns ErrNotFound
    73  // if no metainfo was found.
    74  func (a *TorrentArchive) CreateTorrent(namespace string, d core.Digest) (storage.Torrent, error) {
    75  	var tm metadata.TorrentMeta
    76  	if err := a.cads.Any().GetMetadata(d.Hex(), &tm); os.IsNotExist(err) {
    77  		downloadTimer := a.stats.Timer("metainfo_download").Start()
    78  		mi, err := a.metaInfoClient.Download(namespace, d)
    79  		if err != nil {
    80  			if err == metainfoclient.ErrNotFound {
    81  				return nil, storage.ErrNotFound
    82  			}
    83  			return nil, fmt.Errorf("download metainfo: %s", err)
    84  		}
    85  		downloadTimer.Stop()
    86  
    87  		// There's a race condition here, but it's "okay"... Basically, we could
    88  		// initialize a download file with metainfo that is rejected by file store,
    89  		// because someone else beats us to it. However, we catch a lucky break
    90  		// because the only piece of metainfo we use is file length -- which digest
    91  		// is derived from, so it's "okay".
    92  		createErr := a.cads.CreateDownloadFile(mi.Digest().Hex(), mi.Length())
    93  		if createErr != nil &&
    94  			!(a.cads.InDownloadError(createErr) || a.cads.InCacheError(createErr)) {
    95  			return nil, fmt.Errorf("create download file: %s", createErr)
    96  		}
    97  		tm.MetaInfo = mi
    98  		if err := a.cads.Any().GetOrSetMetadata(d.Hex(), &tm); err != nil {
    99  			return nil, fmt.Errorf("get or set metainfo: %s", err)
   100  		}
   101  	} else if err != nil {
   102  		return nil, fmt.Errorf("get metainfo: %s", err)
   103  	}
   104  	t, err := NewTorrent(a.cads, tm.MetaInfo)
   105  	if err != nil {
   106  		return nil, fmt.Errorf("initialize torrent: %s", err)
   107  	}
   108  	return t, nil
   109  }
   110  
   111  // GetTorrent returns a Torrent for an existing metainfo / file on disk. Ignores namespace.
   112  func (a *TorrentArchive) GetTorrent(namespace string, d core.Digest) (storage.Torrent, error) {
   113  	var tm metadata.TorrentMeta
   114  	if err := a.cads.Any().GetMetadata(d.Hex(), &tm); err != nil {
   115  		return nil, fmt.Errorf("get metainfo: %s", err)
   116  	}
   117  	t, err := NewTorrent(a.cads, tm.MetaInfo)
   118  	if err != nil {
   119  		return nil, fmt.Errorf("initialize torrent: %s", err)
   120  	}
   121  	return t, nil
   122  }
   123  
   124  // DeleteTorrent deletes a torrent from disk.
   125  func (a *TorrentArchive) DeleteTorrent(d core.Digest) error {
   126  	if err := a.cads.Any().DeleteFile(d.Hex()); err != nil && !os.IsNotExist(err) {
   127  		return err
   128  	}
   129  	return nil
   130  }