summaryrefslogtreecommitdiffstats
path: root/lib/puppet/file_bucket/file.rb
blob: 96fd8e22563cc402f37144b511ccbd697ccf3eed (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
require 'puppet/file_bucket'
require 'puppet/indirector'
require 'puppet/util/checksums'

class Puppet::FileBucket::File
  include Puppet::Util::Checksums

  # This class handles the abstract notion of a file in a filebucket.
  # There are mechanisms to save and load this file locally and remotely in puppet/indirector/filebucketfile/*
  # There is a compatibility class that emulates pre-indirector filebuckets in Puppet::FileBucket::Dipper
  extend Puppet::Indirector
  require 'puppet/file_bucket/file/indirection_hooks'
  indirects :file_bucket_file, :terminus_class => :file, :extend => Puppet::FileBucket::File::IndirectionHooks

  attr :path, true
  attr :paths, true
  attr :contents, true
  attr :checksum_type
  attr :bucket_path, true

  def self.default_checksum_type
    "md5"
  end

  def initialize( contents, options = {} )
    @bucket_path   = options[:bucket_path]
    @path          = options[:path]
    @paths         = options[:paths] || []

    @checksum      = options[:checksum]
    @checksum_type = options[:checksum_type]

    self.contents  = contents

    yield(self) if block_given?

    validate!
  end

  def validate!
    validate_checksum_type!(checksum_type)
    validate_checksum!(checksum) if checksum
  end

  def contents=(str)
    raise "You may not change the contents of a FileBucket File" if @contents
    validate_content!(str)
    @contents = str
  end

  def checksum
    return @checksum if @checksum
    @checksum = calculate_checksum if contents
    @checksum
  end

  def checksum=(checksum)
    validate_checksum!(checksum)
    @checksum = checksum
  end

  def checksum_type=( new_checksum_type )
    @checksum = nil
    @checksum_type = new_checksum_type
  end

  def checksum_type
    unless @checksum_type
      if @checksum
        @checksum_type = sumtype(checksum)
      else
        @checksum_type = self.class.default_checksum_type
      end
    end
    @checksum_type
  end

  def checksum_data
    sumdata(checksum)
  end

  def to_s
    contents
  end

  def name
    [checksum_type, checksum_data, path].compact.join('/')
  end

  def name=(name)
    data = name.split('/',3)
    self.path = data.pop
    @checksum_type = nil
    self.checksum = "{#{data[0]}}#{data[1]}"
  end

  def conflict_check?
    true
  end

  def self.from_s( contents )
    self.new( contents )
  end

  def to_pson
    hash = { "contents" => contents }
    hash["path"] = @path if @path
    hash.to_pson
  end

  def self.from_pson( pson )
    self.new( pson["contents"], :path => pson["path"] )
  end

  private

  def calculate_checksum
    "{#{checksum_type}}" + send(checksum_type, contents)
  end

  def validate_content!(content)
    raise ArgumentError, "Contents must be a string" if content and ! content.is_a?(String)
  end

  def validate_checksum!(new_checksum)
    newtype = sumtype(new_checksum)

    unless sumdata(new_checksum) == (calc_sum = send(newtype, contents))
      raise Puppet::Error, "Checksum #{new_checksum} does not match contents #{calc_sum}"
    end
  end

  def validate_checksum_type!(type)
    raise ArgumentError, "Invalid checksum type #{type}" unless respond_to?(type)
  end
end