Skip to content

Commit

Permalink
developing file service interface
Browse files Browse the repository at this point in the history
  • Loading branch information
nruth committed Sep 26, 2011
1 parent f5b9ef6 commit 3b99eb6
Show file tree
Hide file tree
Showing 8 changed files with 205 additions and 25 deletions.
35 changes: 15 additions & 20 deletions demo.rb
Expand Up @@ -6,9 +6,19 @@

num_pieces = 4

input_file = Eraser::File.new File.join(File.dirname(__FILE__), *%w[media test.jpg])
encoder = Eraser::Encoder.new(input_file, num_pieces)
pieces = encoder.encode
#encode
service = Eraser::Service.new
service.put File.join(File.dirname(__FILE__), *%w[media test.jpg])

raise "Stop"

# input_file = Eraser::File.new
# encoder = Eraser::Encoder.new(input_file, num_pieces)
# pieces = encoder.encode

#Distribute pieces
# nodes = (1..5).map { |id|Node.new(id) }


#use nodes 1 and 2 to rebuild something on node 4
#pick a file to delete & store a hash of its contents
Expand All @@ -19,31 +29,16 @@
`rm #{removed_filename}`
available_pieces = pieces.reject {|p| p.bitmask == 0b0001}
raise "fail" if available_pieces == pieces
code = Eraser::Code.new

pieces_to_decode_with = Eraser::Code.new.basis_vectors_for_node(1)
pieces_to_decode_with += Eraser::Code.new.basis_vectors_for_node(2)
pieces_to_decode_with = Eraser::Code.basis_vectors_for_node(1)
pieces_to_decode_with += Eraser::Code.basis_vectors_for_node(2)
pieces_to_decode_with = pieces_to_decode_with.map {|code| Eraser::Piece.new(input_file.name, code)}

decoder = Eraser::Decoder.new(pieces_to_decode_with)
wanted_piece = Eraser::Piece.new(input_file.name, 0b0001)

# Decode and check contents are as they were before deleting
decoder.decode [wanted_piece]
reassembled_hash = Digest::SHA1.hexdigest(wanted_piece.content)
puts original_hash == reassembled_hash ? "SUCCESS" : "FAILURE"


# removed = pieces.pop
# original_hash = Digest::SHA1.hexdigest(File.read(removed.filename))
# puts "deleting #{removed}"
# FileUtils.rm removed.filename
# code = Eraser::Code.new
# available_pieces = code.basis_vectors_for_node(2) + code.basis_vectors_for_node(4)
# available_pieces = available_pieces.map {|code| Eraser::Piece.new input_file.name, code}
# decoder = Eraser::Decoder.new(available_pieces)
# decoded_pieces = decoder.decode([Eraser::Piece.new(input_file.name, removed.bitmask)])
# reassembled_hash = Digest::SHA1.hexdigest(decoded_pieces.first.content)

reassembled = Eraser::Decoder.build_from_pieces(input_file.name, num_pieces)
File.open(input_file.name, 'w') {|f| f.print reassembled}
Expand Down
3 changes: 0 additions & 3 deletions eraser.rb
@@ -1,4 +1 @@
#!/usr/bin/env ruby
Dir[File.expand_path(File.join(File.dirname(__FILE__),'lib','**','*.rb'))].each {|f| require f}

N_PIECES = 4
27 changes: 27 additions & 0 deletions lib/eraser/node.rb
@@ -0,0 +1,27 @@
module Eraser
class Node
attr_reader :id
attr_reader :root_path
def initialize(id)
@id = id
end

def self.spawn_nodes(n)
(1..n).map {|id| Node.new(id)}
end

def copy_pieces(pieces)
puts "copying #{pieces.join(',')}"
pieces.each{|p| copy_piece(p)}
end

def copy_piece(piece)
p = Piece.new(piece.original_filename, piece.bitmask, storage_path)
p.overwrite(piece.content)
end

def storage_path
"#{id}"
end
end
end
13 changes: 11 additions & 2 deletions lib/eraser/piece.rb
Expand Up @@ -2,17 +2,19 @@ module Eraser
class Piece
attr_accessor :original_filename
attr_accessor :bitmask
def initialize(original_filename, bitmask)
def initialize(original_filename, bitmask, storage_path=nil)
self.original_filename = ::File.basename(original_filename)
self.bitmask = bitmask
@storage_path = storage_path
end

def to_s
filename
end

def filename
File.bitmask_appended_filename(original_filename, bitmask)
filename = File.bitmask_appended_filename(original_filename, bitmask)
@storage_path ? ::File.join(@storage_path, filename) : filename
end

def ^(piece)
Expand Down Expand Up @@ -42,11 +44,13 @@ def open_file
end

def overwrite(content)
FileUtils.mkpath(@storage_path) if @storage_path && !::File.exist?(@storage_path)
::File.open(filename, 'wb') {|f| f.print content}
self
end

def append(content)
FileUtils.mkpath(@storage_path) if @storage_path && !::File.exist?(@storage_path)
::File.open(filename, 'ab') {|f| f.print content}
self
end
Expand All @@ -56,9 +60,14 @@ def content
end

def reset_content!
FileUtils.mkpath(@storage_path) if @storage_path && !::File.exist?(@storage_path)
::File.open(filename, 'w') do
#truncate file
end
end

def destroy
::File.delete(filename) if ::File.exists?(filename)
end
end
end
37 changes: 37 additions & 0 deletions lib/eraser/service.rb
@@ -0,0 +1,37 @@
module Eraser
class Service
def num_pieces
4
end

def num_nodes
5
end

def put(file)
input_file = Eraser::File.new ::File.expand_path(file)
encoder = Eraser::Encoder.new(input_file, num_pieces)
pieces = encoder.encode
# distribute_pieces(pieces)
end

def distribute_pieces(pieces)
puts "distributing #{pieces.join(',')}"
nodes = Node.spawn_nodes(5)
nodes.each do |node|
puts "node #{node.id}"
nodes_pieces = pieces.select do |p|
bitmasks = Eraser::Code.basis_vectors_for_node(node.id)
puts "bitmasks: #{bitmasks.join(',')}"
bitmasks.include?(p.bitmask)
end
node.copy_pieces(nodes_pieces)
end
pieces.each(&:destroy)
end

def retrieve(id)

end
end
end
29 changes: 29 additions & 0 deletions spec/eraser/node_spec.rb
@@ -0,0 +1,29 @@
require File.join(File.dirname(__FILE__), *%w[.. .. eraser])
describe Eraser::Node do
let(:node_id) {mock}
let(:node) {Eraser::Node.new(node_id)}
subject {node}

describe "copy_piece" do
it "makes a new piece with the contents of the given piece" do
piece = mock
content, filename, bitmask = mock, mock, mock
piece.stub(:content).and_return content
piece.stub(:original_filename).and_return filename
piece.stub(:bitmask).and_return bitmask
node.stub(:storage_path).and_return storage_path = mock
Eraser::Piece.should_receive(:new).with(filename, bitmask, storage_path).and_return new_piece = mock
new_piece.should_receive(:overwrite).with(content)
node.copy_piece piece
end
end

describe "copy_pieces(pieces)" do
it "copies the pieces" do
m1, m2 = mock, mock
subject.should_receive(:copy_piece).with(m1).ordered
subject.should_receive(:copy_piece).with(m2).ordered
subject.copy_pieces [m1, m2]
end
end
end
8 changes: 8 additions & 0 deletions spec/eraser/piece_spec.rb
Expand Up @@ -69,6 +69,14 @@
subject.content.should == 'new content'
end
end
describe "changing path" do
it "writes to the new directory" do
Eraser::Piece.new('foo.jpg', 0b0101, 'node1').overwrite 'data'
File.should exist('node1/foo.jpg.0101')
File.read('node1/foo.jpg.0101').should == 'data'
end
end
end
end
end
78 changes: 78 additions & 0 deletions spec/eraser/service_spec.rb
@@ -0,0 +1,78 @@
require File.join(File.dirname(__FILE__), *%w[.. .. eraser])
describe Eraser::Service do
let(:service) {Eraser::Service.new}
describe "put(filepath_string)" do
let(:filepath) {mock}
let(:encoder) {mock}
let(:wrapped_input_file) {mock}
before(:each) do
Eraser::File.stub(:new).and_return wrapped_input_file
Eraser::Encoder.stub(:new).and_return encoder
service.stub(:distribute_pieces)
encoder.stub(:encode)
end

it "normalises the path & wraps the file in an Eraser::File" do
File.should_receive(:expand_path).with(filepath).and_return filepath
Eraser::File.should_receive(:new).with(filepath).and_return wrapped_input_file
service.put(filepath)
end

it "encodes the file in 4 pieces" do
Eraser::Encoder.should_receive(:new).with(wrapped_input_file, 4)
encoder.should_receive(:encode)
service.put(filepath)
end

it "distributes the pieces across 5 nodes" do
pieces = mock
encoder.should_receive(:encode).and_return pieces
service.should_receive(:distribute_pieces).with(pieces)
service.put(filepath)
end
end

describe "distribute_pieces(pieces)" do

let(:node) {mock}
let(:nodes) {[node]}
let(:bitmask) {0b0010}
let(:piece) {mock(:bitmask => bitmask)}
let(:pieces) {[piece]}
before(:each) do
Node.stub(:spawn_nodes).and_return nodes
Eraser::Code.stub(:basis_vectors_for_node).and_return [bitmask]
piece.stub(:destroy)
node.stub(:copy_pieces)
end

it "spawns 5 nodes" do
Node.should_receive(:spawn_nodes).with(5).and_return nodes
service.distribute_pieces(pieces)
end

it "sends the basis vector decided pieces to each node" do
node_id = mock
node.should_receive(:id).and_return node_id
Eraser::Code.should_receive(:basis_vectors_for_node).with(node_id).and_return [bitmask]
node.should_receive(:copy_pieces).with([piece])
service.distribute_pieces(pieces)
end

it "deletes local pieces generated during encoding" do
piece.should_receive(:destroy)
service.distribute_pieces(pieces)
end
end

describe "retrieve" do
it "takes a filename and returns the file contents"
end

describe "regenerate" do
context "when 1 node has failed (and its pieces are lost)" do
it "finds the missing pieces"
it "reconstructs the missing node and its pieces"
end
end
end

0 comments on commit 3b99eb6

Please sign in to comment.