Class: Chef::Knife::ScribeAdjust

Inherits:
Chef::Knife show all
Includes:
Mixin::DeepMerge, Mixin::ShellOut
Defined in:
lib/chef/knife/scribe_adjust.rb

Constant Summary collapse

TEMPLATE_HASH =
{ "author_name" => "",
  "author_email" => "",
  "description" => "",
  "adjustments" => []
}
ENVIRONMENT_ADJUSTMENT_TEMPLATE =
{
  "action" => "merge",
  "type" => "environment",
  "search" => "name:my_environment_name",
  "adjustment" => { "default_attributes" => { },
    "override_attributes" => { },
    "cookbook_versions" => { }
  }
}
ROLE_ADJUSTMENT_TEMPLATE =
{
  "action" => "merge",
  "type" => "role",
  "search" => "name:my_role_name",
  "adjustment" => { "default_attributes" => { },
    "override_attributes" => { },
    "run_list" => [ ]
  }
}
NODE_ADJUSTMENT_TEMPLATE =
{
  "action" => "merge",
  "type" => "node",
  "search" => "name:my_node_name",
  "adjustment" => { "normal" => { },
    "run_list" => [ ],
  }
}

Instance Method Summary collapse

Methods inherited from Chef::Knife

#deep_delete, #deep_delete!, #diff

Instance Method Details

#action_overwrite(base, overwrite_with) ⇒ Object



297
298
299
300
301
302
303
304
305
# File 'lib/chef/knife/scribe_adjust.rb', line 297

def action_overwrite(base, overwrite_with)
  if base.kind_of?(Hash) && overwrite_with.kind_of?(Hash)
    base.merge(overwrite_with)
  elsif overwrite_with.nil?
    base
  else
    overwrite_with
  end
end

#adjustment_file_valid?(adjustment_file) ⇒ Boolean



235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/chef/knife/scribe_adjust.rb', line 235

def adjustment_file_valid? adjustment_file
  unless adjustment_file.kind_of?(Hash)
    errors.last["general"] = "Adjustment file must contain a JSON hash!"
    return false
  end

  unless adjustment_file["adjustments"].kind_of?(Array)
    errors.last["general"] = "Adjustment file must contain an array of adjustments!"
    return false
  end
  true
end

#adjustment_valid?(adjustment, index) ⇒ Boolean



248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
# File 'lib/chef/knife/scribe_adjust.rb', line 248

def adjustment_valid?(adjustment, index)
  unless adjustment.kind_of?(Hash)
    errors.last["adjustments"].store(index,"Adjustment must be a JSON hash!")
    return false
  end

  ["action", "type", "search", "adjustment"].each do |required_key|
    unless adjustment.has_key?(required_key)
      errors.last["adjustments"].store(index, "Adjustment hash must contain " + required_key + "!")
      return false
    end
  end

  unless respond_to?("action_" + adjustment["action"])
    errors.last["adjustments"].store(index, "Incorrect action!")
    return false
  end
  true
end

#apply_adjustment(adjustment) ⇒ Object



201
202
203
204
205
206
207
208
# File 'lib/chef/knife/scribe_adjust.rb', line 201

def apply_adjustment(adjustment)
  query = adjustment["search"].include?(":") ? adjustment["search"] : "name:" + adjustment["search"]
  Chef::Search::Query.new.search(adjustment["type"], query ) do |result|
    key = result.class.name.downcase + ":" + result.name
    prepared_hash = prepare_adjustment_subject(key, result)
    changes[key].store("adjusted", send(("action_" + adjustment["action"]).to_sym, prepared_hash, adjustment["adjustment"]))
  end
end

#changesObject



119
120
121
# File 'lib/chef/knife/scribe_adjust.rb', line 119

def changes
  @changes ||= {}
end

#descriptionsObject



123
124
125
# File 'lib/chef/knife/scribe_adjust.rb', line 123

def descriptions
  @descriptions ||= []
end

#errorsObject



127
128
129
# File 'lib/chef/knife/scribe_adjust.rb', line 127

def errors
  @errors ||= []
end

#errors?Boolean



283
284
285
286
# File 'lib/chef/knife/scribe_adjust.rb', line 283

def errors?
  errors.each { |err| return true if !err["general"].nil? || (err["adjustments"].size > 0) }
  false
end

#generate_template(filename) ⇒ Object



170
171
172
173
174
175
176
177
# File 'lib/chef/knife/scribe_adjust.rb', line 170

def generate_template(filename)
  unless ["environment", "role", "node"].include?(config[:type])
    ui.fatal("Incorrect adjustment type! Only 'node', 'environment' or 'role' allowed.")
    exit 1
  end
  TEMPLATE_HASH["adjustments"] = [self.class.class_eval(config[:type].upcase + "_ADJUSTMENT_TEMPLATE")]
  File.open(filename, "w") { |file| file.write(JSON.pretty_generate(TEMPLATE_HASH)) }
end

#generate_templatesObject



145
146
147
# File 'lib/chef/knife/scribe_adjust.rb', line 145

def generate_templates

end

#hireObject



268
269
270
271
272
# File 'lib/chef/knife/scribe_adjust.rb', line 268

def hire
  hired_scribe = Chef::Knife::ScribeHire.new
  [:chronicle_path, :remote_url, :remote_name].each { |key| hired_scribe.config[key] = config[key] }
  hired_scribe.run
end

#parse_adjustment_file(filename) ⇒ Object



179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'lib/chef/knife/scribe_adjust.rb', line 179

def parse_adjustment_file(filename)
  if !File.exists?(filename)
    errors.last["general"] = "File does not exist!"
  else
    begin
      adjustment_file = File.open(filename, "r") { |file| JSON.load(file) }
      if adjustment_file_valid? adjustment_file
        adjustment_file["adjustments"].each_with_index do |adjustment, index|
          apply_adjustment(adjustment) if adjustment_valid?(adjustment, index)
        end
      end
      if adjustment_file["adjustments"].length > errors.last["adjustments"].length
        description = adjustment_file["description"]
        description += "[with errors]" if errors.last["adjustments"].size > 0
        descriptions.push(description)
      end
    rescue JSON::ParserError
      errors.last["general"] = "Malformed JSON!"
    end
  end
end

#parse_adjustmentsObject



149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# File 'lib/chef/knife/scribe_adjust.rb', line 149

def parse_adjustments
  @name_args.each do |filename|
    errors.push({ "name" => filename, "general" => nil, "adjustments" => {} })
    parse_adjustment_file(filename)
  end
  if errors?
    print_errors
    exit 1 unless config[:dryrun]
  end
  if config[:dryrun]
    diff
  else
    if config[:document] == true
      hire
      record_state
    end
    write_adjustments
    record_state(descriptions.join("\n").strip) if config[:document] == true
  end
end

#prepare_adjustment_subject(key, chef_object) ⇒ Object



210
211
212
213
214
215
216
217
218
# File 'lib/chef/knife/scribe_adjust.rb', line 210

def prepare_adjustment_subject(key, chef_object)
  prepared_hash = prepare_object_hash(chef_object)
  if changes.has_key? key
    prepared_hash = changes[key]["adjusted"]
  else
    changes.store(key, { "original" => prepared_hash })
  end
  prepared_hash
end

#prepare_object_hash(chef_object) ⇒ Object



220
221
222
223
224
225
226
227
# File 'lib/chef/knife/scribe_adjust.rb', line 220

def prepare_object_hash(chef_object)
  prepared_hash = chef_object.to_hash
  if prepared_hash["chef_type"] == "node"
    prepared_hash.reject! { |key, value| !(["name","chef_type","chef_environment","run_list"].include? key) }
    prepared_hash.merge!({ "normal" => chef_object.normal_attrs })
  end
  prepared_hash
end


288
289
290
291
292
293
294
295
# File 'lib/chef/knife/scribe_adjust.rb', line 288

def print_errors
  ui.error("ERRORS OCCURED:")
  errors.each do |err|
    ui.error(err["name"]) if !err["general"].nil? || (err["adjustments"].size > 0)
    ui.error("\t" + err["general"]) if !err["general"].nil?
    err["adjustments"].each { |num, adj_err| ui.error("\t[Adjustment #{num}]: #{adj_err}") }
  end
end

#record_state(message = nil) ⇒ Object



274
275
276
277
278
279
280
281
# File 'lib/chef/knife/scribe_adjust.rb', line 274

def record_state(message = nil)
  if @copyist.nil?
    @copyist = Chef::Knife::ScribeCopy.new
    [:chronicle_path, :remote_name, :branch].each { |key| @copyist.config[key] = config[key] }
  end
  @copyist.config[:message] = message
  @copyist.run
end

#runObject



132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/chef/knife/scribe_adjust.rb', line 132

def run
  if @name_args[0].nil?
    show_usage
    ui.fatal("At least one adjustment file needs to be specified!")
    exit 1
  end
  if config[:generate] == true
    @name_args.each { |filename| generate_template(filename) }
  else
    parse_adjustments
  end
end

#write_adjustmentsObject



229
230
231
232
233
# File 'lib/chef/knife/scribe_adjust.rb', line 229

def write_adjustments
  changes.values.each do |change|
    Chef.const_get(change["adjusted"]["chef_type"].capitalize).json_create(change["adjusted"]).save
  end
end