Class: Gizzard::Transformation

Inherits:
Object
  • Object
show all
Defined in:
lib/gizzard/transformation.rb

Defined Under Namespace

Modules: Op Classes: Scheduler

Constant Summary collapse

OP_NAMES =
{
  Op::RemoveForwarding => "remove_forwarding",
  Op::RemoveLink       => "remove_link",
  Op::DeleteShard      => "delete_shard",
  Op::CreateShard      => "create_shard",
  Op::AddLink          => "add_link",
  Op::SetForwarding    => "set_forwarding",
  Op::CopyShard        => "copy_shard",
  Op::RepairShards     => "repair_shards",
  Op::DiffShards       => "diff_shards"
}
OP_INVERSES =
{
  Op::AddLink       => Op::RemoveLink,
  Op::CreateShard   => Op::DeleteShard,
  Op::SetForwarding => Op::RemoveForwarding
}
OP_PRIORITIES =
{
  Op::CreateShard      => 1,
  Op::AddLink          => 2,
  Op::SetForwarding    => 3,
  Op::RemoveForwarding => 4,
  Op::RemoveLink       => 5,
  Op::DeleteShard      => 6,
  Op::CopyShard        => 7,
  Op::RepairShards     => 8,
  Op::DiffShards       => 9
}
DEFAULT_DEST_WRAPPER =
'BlockedShard'

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(from_template, to_template, copy_dest_wrapper = nil, skip_copies = false) ⇒ Transformation

Returns a new instance of Transformation.



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/gizzard/transformation.rb', line 44

def initialize(from_template, to_template, copy_dest_wrapper = nil, skip_copies = false)
  copy_dest_wrapper ||= DEFAULT_DEST_WRAPPER

  unless Shard::VIRTUAL_SHARD_TYPES.include? copy_dest_wrapper
    raise ArgumentError, "#{copy_dest_wrapper} is not a valid virtual shard type."
  end

  @from = from_template
  @to   = to_template
  @copy_dest_wrapper = copy_dest_wrapper
  @skip_copies = skip_copies

  if copies_required? && copy_source.nil?
    raise ArgumentError, "copy required without a valid copy source"
  end
end

Instance Attribute Details

#copy_dest_wrapperObject (readonly)

Returns the value of attribute copy_dest_wrapper.



42
43
44
# File 'lib/gizzard/transformation.rb', line 42

def copy_dest_wrapper
  @copy_dest_wrapper
end

#fromObject (readonly)

Returns the value of attribute from.



42
43
44
# File 'lib/gizzard/transformation.rb', line 42

def from
  @from
end

#skip_copiesObject (readonly)

Returns the value of attribute skip_copies.



42
43
44
# File 'lib/gizzard/transformation.rb', line 42

def skip_copies
  @skip_copies
end

#toObject (readonly)

Returns the value of attribute to.



42
43
44
# File 'lib/gizzard/transformation.rb', line 42

def to
  @to
end

Instance Method Details

#<=>(o) ⇒ Object



80
81
82
83
84
# File 'lib/gizzard/transformation.rb', line 80

def <=>(o)
  to_a = lambda {|t| [t.from, t.to, t.copy_dest_wrapper] }

  to_a.call(self) <=> to_a.call(o)
end

#bind(base_name, forwardings_to_shards) ⇒ Object

Raises:

  • (ArgumentError)


61
62
63
64
65
66
67
# File 'lib/gizzard/transformation.rb', line 61

def bind(base_name, forwardings_to_shards)
  raise ArgumentError unless forwardings_to_shards.is_a? Hash

  forwardings_to_shards.map do |forwarding, shard|
    BoundTransformation.new(self, base_name, forwarding, shard)
  end
end

#collapse_jobs(jobs) ⇒ Object



134
135
136
137
138
139
140
# File 'lib/gizzard/transformation.rb', line 134

def collapse_jobs(jobs)
  jobs.reject do |job1|
    jobs.find do |job2|
      job1.inverse? job2
    end
  end
end

#copies_required?Boolean

Returns:

  • (Boolean)


158
159
160
161
162
163
164
# File 'lib/gizzard/transformation.rb', line 158

def copies_required?
  return false if skip_copies
  return @copies_required unless  @copies_required.nil?

  @copies_required = !from.nil? &&
    to.concrete_descendants.select {|d| !from.shared_host? d }.length > 0
end

#copy_destination?(template) ⇒ Boolean

Returns:

  • (Boolean)


170
171
172
# File 'lib/gizzard/transformation.rb', line 170

def copy_destination?(template)
  copies_required? && template.concrete? && !from.shared_host?(template)
end

#copy_sourceObject



178
179
180
# File 'lib/gizzard/transformation.rb', line 178

def copy_source
  from.copy_sources.first if copies_required?
end

#copy_source?(template) ⇒ Boolean

Returns:

  • (Boolean)


174
175
176
# File 'lib/gizzard/transformation.rb', line 174

def copy_source?(template)
  copies_required? && !!from.copy_sources.find {|s| s.shard_eql? template }
end

#create_tree(root) ⇒ Object



182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
# File 'lib/gizzard/transformation.rb', line 182

def create_tree(root)
  get_wrapper_type = Proc.new { |template, wrapper_type|
    if wrapper_type.nil?
      nil
    elsif template.contains_shard_type? wrapper_type 
      nil
    else
      wrapper_type
    end
  }
  jobs = visit_collect(root, get_wrapper_type, @copy_dest_wrapper) do |parent, child, wrapper|
    [Op::CreateShard.new(child, wrapper), Op::AddLink.new(parent, child, wrapper)]
  end
  [Op::CreateShard.new(root, @copy_dest_wrapper)].concat jobs << Op::SetForwarding.new(root, @copy_dest_wrapper)
end

#destroy_tree(root) ⇒ Object



198
199
200
201
202
203
# File 'lib/gizzard/transformation.rb', line 198

def destroy_tree(root)
  jobs = visit_collect(root) do |parent, child|
    [Op::RemoveLink.new(parent, child), Op::DeleteShard.new(child)]
  end
  [Op::RemoveForwarding.new(root)].concat jobs << Op::DeleteShard.new(root)
end

#eql?(o) ⇒ Boolean

Returns:

  • (Boolean)


73
74
75
76
77
78
# File 'lib/gizzard/transformation.rb', line 73

def eql?(o)
  o.is_a?(self.class) &&
  from.eql?(o.from) &&
  to.eql?(o.to) &&
  copy_dest_wrapper.eql?(o.copy_dest_wrapper)
end

#expand_jobs(jobs) ⇒ Object



142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
# File 'lib/gizzard/transformation.rb', line 142

def expand_jobs(jobs)
  expanded = jobs.inject({:prepare => [], :copy => [], :repair => [], :cleanup => [], :diff => []}) do |ops, job|
    job_ops = job.expand(self.copy_source, involved_in_copy?(job.template))
    ops.update(job_ops) {|k,a,b| a + b }
  end

  # if there are no copies that need to take place, we can do all
  # nameserver changes in one step
  if expanded[:copy].empty?
    expanded[:prepare].concat expanded[:cleanup]
    expanded[:cleanup] = []
  end

  expanded
end

#hashObject



86
87
88
# File 'lib/gizzard/transformation.rb', line 86

def hash
  from.hash + to.hash + copy_dest_wrapper.hash
end

#inspectObject



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
# File 'lib/gizzard/transformation.rb', line 90

def inspect
  # TODO: Need to limit this to e.g. 10 ops in the list, and show a total
  # count instead of showing the whole thing.
  op_inspect = operations.inject({}) do |h, (phase, ops)|
    h.update phase => ops.map {|job| "    #{job.inspect}" }.join("\n")
  end

  # TODO: This seems kind of daft to copy around these long strings.
  # Loop over it once just for display?
  prepare_inspect = op_inspect[:prepare].empty? ? "" : "  PREPARE\n#{op_inspect[:prepare]}\n"
  copy_inspect    = op_inspect[:copy].empty?    ? "" : "  COPY\n#{op_inspect[:copy]}\n"
  repair_inspect  = op_inspect[:repair].empty?  ? "" : "  REPAIR\n#{op_inspect[:repair]}\n"
  diff_inspect    = op_inspect[:diff].empty?  ? "" : "  DIFF\n#{op_inspect[:diff]}\n"
  cleanup_inspect = op_inspect[:cleanup].empty? ? "" : "  CLEANUP\n#{op_inspect[:cleanup]}\n"

  op_inspect = [
    prepare_inspect,
    copy_inspect,
    repair_inspect,
    diff_inspect,
    cleanup_inspect,
  ].join

  "#{from.inspect} => #{to.inspect} :\n#{op_inspect}"
end

#involved_in_copy?(template) ⇒ Boolean

Returns:

  • (Boolean)


166
167
168
# File 'lib/gizzard/transformation.rb', line 166

def involved_in_copy?(template)
  copy_source?(template) || copy_destination?(template)
end

#noop?Boolean

Returns:

  • (Boolean)


69
70
71
# File 'lib/gizzard/transformation.rb', line 69

def noop?
  from.eql? to
end

#operationsObject



116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/gizzard/transformation.rb', line 116

def operations
  return @operations if @operations

  log = []
  log.concat destroy_tree(from) if from
  log.concat create_tree(to) if to

  # compact
  log = collapse_jobs(log)
  @operations = expand_jobs(log)

  @operations.each do |(phase, jobs)|
    jobs.sort!
  end

  @operations
end