anchor.diff:  %% -*- erlang-indent-level: 4;indent-tabs-mode: nil; fill-column: 92-*-
   %% ex: ts=4 sw=4 et
   %% @author Seth Falcon <seth@opscode.com>
  +%% @author Seth Chisamore <schisamo@opscode.com>
   %% @doc A utility module for manipulating OSC Chef objects from the
   %% console.  Sked is Swedish for spoon. Does not work for OPC/OHC.
   %%
  
positive.code: 
  -module(chef_sked).
  
  -include_lib("chef_objects/include/chef_types.hrl").
  -include_lib("chef_objects/include/chef_osc_defaults.hrl").
  -include_lib("chef_certgen/include/chef_certgen.hrl").
  
  -export([create_client/4,
           create_default_environment/0]).
  
negative.code:     end;
  %% @doc Create a client with the specified `PublicKey'.
  create_client(Name, IsValidator, IsAdmin, PublicKey) ->
      Ejson = {[{<<"name">>, Name},
                {<<"validator">>, IsValidator =:= true},
                {<<"admin">>, IsAdmin =:= true},
                {<<"public_key">>, PublicKey}]},
      create_from_json(chef_client, Ejson).
  
  %% @doc Create the _default environment
  create_default_environment() ->
      Ejson = {[{<<"name">>, <<"_default">>},
                {<<"description">>, <<"The default Chef environment">>},
                {<<"cookbook_versions">>, {[]}},
                {<<"json_class">>, <<"Chef::Environment">>},
                {<<"chef_type">>, <<"environment">>},
                {<<"default_attributes">>, {[]}},
                {<<"override_attributes">>, {[]}}]},
      create_from_json(chef_environment, Ejson).
  
  %% @doc Thin wrapper used to create objects from ejson tuples.
  %% FIXME: It would be nice to deal stricly with records!
  create_from_json(RecType, ObjectEjson) ->
      DbContext = chef_db:make_context(make_req_id()),
      %% ObjectEjson should already be normalized. Record creation does minimal work and does
      %% not add or update any fields.
      ObjectRec = chef_object:new_record(RecType, ?OSC_ORG_ID, unset, ObjectEjson),
      Id = chef_object:id(ObjectRec),
      TypeName = chef_object:type_name(ObjectRec),
      %% We send the object data to solr for indexing *first*. If it fails, we'll error out on
      %% a 500 and client can retry. If we succeed and the db call fails or conflicts, we can
      %% safely send a delete to solr since this is a new object with a unique ID unknown to
      %% the world.
      ok = chef_object_db:add_to_solr(TypeName, Id, ?OSC_ORG_ID,
                                   chef_object:ejson_for_indexing(ObjectRec, ObjectEjson)),
      CreateFun = chef_db:create_fun(ObjectRec),
  
      case chef_db:CreateFun(DbContext, ObjectRec, ?CHEF_SKED_AUTHZ_ID) of
          {conflict, Msg} ->
              %% ignore return value of solr delete, this is best effort.
              chef_object_db:delete_from_solr(ObjectRec),
              {conflict, Msg};
          ok ->
              ok;
          What ->
              %% ignore return value of solr delete, this is best effort.
              chef_object_db:delete_from_solr(ObjectRec),
              What
      end.
  
  %% Create a binary string used as the request identifier.
  make_req_id() ->
  