📄 release_handler.erl
字号:
%% Check that file exists at all masters.check_file_masters(FileName, Type, [Master|Masters]) -> do_check_file(Master, FileName, Type), check_file_masters(FileName, Type, Masters);check_file_masters(_FileName, _Type, []) -> ok.%% Type == regular | directorydo_check_file(FileName, Type) -> case file:read_file_info(FileName) of {ok, Info} when Info#file_info.type==Type -> ok; {error, _Reason} -> throw({error, {no_such_file, FileName}}) end.do_check_file(Master, FileName, Type) -> case rpc:call(Master, file, file_info, [FileName]) of {ok,{_,Type,_,_,_,_,_}} -> ok; _ -> throw({error, {no_such_file, {Master, FileName}}}) end.%%-----------------------------------------------------------------%% If Rel doesn't exists in tar it could have been created%% by the user in another way, i.e. ignore this here.%%-----------------------------------------------------------------extract_rel_file(Rel, Tar, Root) -> erl_tar:extract(Tar, [{files, [Rel]}, {cwd, Root}, compressed]).extract_tar(Root, Tar) -> case erl_tar:extract(Tar, [keep_old_files, {cwd, Root}, compressed]) of ok -> ok; {error, Reason, Name} -> % Old erl_tar. throw({error, {cannot_extract_file, Name, Reason}}); {error, {Name, Reason}} -> % New erl_tar (R3A). throw({error, {cannot_extract_file, Name, Reason}}) end.write_releases(Dir, NewReleases, false) -> case do_write_release(Dir, "RELEASES", NewReleases) of ok -> ok; Error -> throw(Error) end;write_releases(Dir, NewReleases, Masters) -> all_masters(Masters), write_releases_m(Dir, NewReleases, Masters).do_write_release(Dir, RELEASES, NewReleases) -> case file:open(filename:join(Dir, RELEASES), write) of {ok, Fd} -> ok = io:format(Fd, "~p.~n", [NewReleases]), file:close(Fd), ok; {error, Reason} -> {error, Reason} end.%%-----------------------------------------------------------------%% Write the "RELEASES" file at all master nodes.%% 1. Save "RELEASES.backup" at all nodes.%% 2. Save "RELEASES.change" at all nodes.%% 3. Update the "RELEASES.change" file at all nodes.%% 4. Move "RELEASES.change" to "RELEASES".%% 5. Remove "RELEASES.backup" at all nodes.%%%% If one of the steps above fails, all steps is recovered from%% (as long as possible), except for 5 which is allowed to fail.%%-----------------------------------------------------------------write_releases_m(Dir, NewReleases, Masters) -> RelFile = filename:join(Dir, "RELEASES"), Backup = filename:join(Dir, "RELEASES.backup"), Change = filename:join(Dir, "RELEASES.change"), ensure_RELEASES_exists(Masters, RelFile), case at_all_masters(Masters, ?MODULE, do_copy_files, [RelFile, [Backup, Change]]) of ok -> case at_all_masters(Masters, ?MODULE, do_write_release, [Dir, "RELEASES.change", NewReleases]) of ok -> case at_all_masters(Masters, file, rename, [Change, RelFile]) of ok -> remove_files(all, [Backup, Change], Masters), ok; {error, {Master, R}} -> takewhile(Master, Masters, file, rename, [Backup, RelFile]), remove_files(all, [Backup, Change], Masters), throw({error, {Master, R, move_releases}}) end; {error, {Master, R}} -> remove_files(all, [Backup, Change], Masters), throw({error, {Master, R, update_releases}}) end; {error, {Master, R}} -> remove_files(Master, [Backup, Change], Masters), throw({error, {Master, R, backup_releases}}) end.ensure_RELEASES_exists(Masters, RelFile) -> case at_all_masters(Masters, ?MODULE, do_ensure_RELEASES, [RelFile]) of ok -> ok; {error, {Master, R}} -> throw({error, {Master, R, ensure_RELEASES_exists}}) end.copy_file(File, Dir, false) -> case do_copy_file(File, Dir) of ok -> ok; Error -> throw(Error) end;copy_file(File, Dir, Masters) -> all_masters(Masters), copy_file_m(File, Dir, Masters).%%-----------------------------------------------------------------%% copy File to Dir at every master node.%% If an error occurs at a node, the total copy failed.%% We do not have to cleanup in case of failure as this%% copy_file is harmless.%%-----------------------------------------------------------------copy_file_m(File, Dir, [Master|Masters]) -> case rpc:call(Master, ?MODULE, do_copy_file, [File, Dir]) of ok -> copy_file_m(File, Dir, Masters); {error, {Reason, F}} -> throw({error, {Master, Reason, F}}); Other -> throw({error, {Master, Other, File}}) end;copy_file_m(_File, _Dir, []) -> ok.do_copy_file(File, Dir) -> File2 = filename:join(Dir, filename:basename(File)), do_copy_file1(File, File2).do_copy_file1(File, File2) -> case file:read_file(File) of {ok, Bin} -> case file:write_file(File2, Bin) of ok -> ok; {error, Reason} -> {error, {Reason, File2}} end; {error, Reason} -> {error, {Reason, File}} end.%%-----------------------------------------------------------------%% Copy File to a list of files.%%-----------------------------------------------------------------do_copy_files(File, [ToFile|ToFiles]) -> case do_copy_file1(File, ToFile) of ok -> do_copy_files(File, ToFiles); Error -> Error end;do_copy_files(_, []) -> ok.%%-----------------------------------------------------------------%% Copy each Src file to Dest file in the list of files.%%-----------------------------------------------------------------do_copy_files([{Src, Dest}|Files]) -> case do_copy_file1(Src, Dest) of ok -> do_copy_files(Files); Error -> Error end;do_copy_files([]) -> ok.%%-----------------------------------------------------------------%% Rename each Src file to Dest file in the list of files.%%-----------------------------------------------------------------do_rename_files([{Src, Dest}|Files]) -> case file:rename(Src, Dest) of ok -> do_rename_files(Files); Error -> Error end;do_rename_files([]) -> ok.%%-----------------------------------------------------------------%% Remove a list of files. Ignore failure.%%-----------------------------------------------------------------do_remove_files([File|Files]) -> file:delete(File), do_remove_files(Files);do_remove_files([]) -> ok.%%-----------------------------------------------------------------%% Ensure that the RELEASES file exists.%% If not create an empty RELEASES file.%%-----------------------------------------------------------------do_ensure_RELEASES(RelFile) -> case file:read_file_info(RelFile) of {ok, _} -> ok; _ -> do_write_file(RelFile, "[]. ") end.%%-----------------------------------------------------------------%% Make a directory, ignore failures (captured later).%%-----------------------------------------------------------------make_dir(Dir, false) -> file:make_dir(Dir);make_dir(Dir, Masters) -> lists:foreach(fun(Master) -> rpc:call(Master, file, make_dir, [Dir]) end, Masters).%%-----------------------------------------------------------------%% Check that all masters are alive.%%-----------------------------------------------------------------all_masters(Masters) -> case rpc:multicall(Masters, erlang, info, [version]) of {_, []} -> ok; {_, BadNodes} -> throw({error, {bad_masters, BadNodes}}) end.%%-----------------------------------------------------------------%% Evaluate {M,F,A} at all masters.%% {M,F,A} is supposed to return ok. Otherwise at_all_masters%% returns {error, {Master, Other}}.%%-----------------------------------------------------------------at_all_masters([Master|Masters], M, F, A) -> case rpc:call(Master, M, F, A) of ok -> at_all_masters(Masters, M, F, A); Error -> {error, {Master, Error}} end;at_all_masters([], _, _, _) -> ok.%%-----------------------------------------------------------------%% Evaluate {M,F,A} at all masters until Master is found.%% Ignore {M,F,A} return value.%%-----------------------------------------------------------------takewhile(Master, Masters, M, F, A) -> lists:takewhile(fun(Ma) when Ma == Master -> false; (Ma) -> rpc:call(Ma, M, F, A), true end, Masters), ok.consult(File, false) -> file:consult(File);consult(File, Masters) -> consult_master(Masters, File).%%-----------------------------------------------------------------%% consult the File at any master node.%% If the file does not exist at one node it should%% not exist at any other node either.%%-----------------------------------------------------------------consult_master([Master|Ms], File) -> case rpc:call(Master, file, consult, [File]) of {badrpc, _} -> consult_master(Ms, File); Res -> Res end;consult_master([], _File) -> {error, no_master}.read_file(File, false) -> file:read_file(File);read_file(File, Masters) -> read_master(Masters, File).%% Ignore status of each delete !remove_files(Master, Files, Masters) -> takewhile(Master, Masters, ?MODULE, do_remove_files, [Files]).%%-----------------------------------------------------------------%% read the File at any master node.%% If the file does not exist at one node it should%% not exist at any other node either.%%-----------------------------------------------------------------read_master([Master|Ms], File) -> case rpc:call(Master, file, read_file, [File]) of {badrpc, _} -> read_master(Ms, File); Res -> Res end;read_master([], _File) -> {error, no_master}.%%-----------------------------------------------------------------%% Write start_erl.data.%%-----------------------------------------------------------------write_start(File, Data, false) -> case do_write_file(File, Data) of ok -> ok; Error -> throw(Error) end;write_start(File, Data, Masters) -> all_masters(Masters), write_start_m(File, Data, Masters).%%-----------------------------------------------------------------%% Write the "start_erl.data" file at all master nodes.%% 1. Save "start_erl.backup" at all nodes.%% 2. Write the "start_erl.change" file at all nodes.%% 3. Move "start_erl.change" to "start_erl.data".%% 4. Remove "start_erl.backup" at all nodes.%%%% If one of the steps above fails, all steps is recovered from%% (as long as possible), except for 4 which is allowed to fail.%%-----------------------------------------------------------------write_start_m(File, Data, Masters) -> Dir = filename:dirname(File), Backup = filename:join(Dir, "start_erl.backup"), Change = filename:join(Dir, "start_erl.change"), case at_all_masters(Masters, ?MODULE, do_copy_files, [File, [Backup]]) of ok -> case at_all_masters(Masters, ?MODULE, do_write_file, [Change, Data]) of ok -> case at_all_masters(Masters, file, rename, [Change, File]) of ok -> remove_files(all, [Backup, Change], Masters), ok; {error, {Master, R}} -> takewhile(Master, Masters, file, rename, [Backup, File]), remove_files(all, [Backup, Change], Masters), throw({error, {Master, R, move_start_erl}}) end; {error, {Master, R}} -> remove_files(all, [Backup, Change], Masters), throw({error, {Master, R, write_start_erl}}) end; {error, {Master, R}} -> remove_files(Master, [Backup], Masters), throw({error, {Master, R, backup_start_erl}}) end.%%-----------------------------------------------------------------%% Copy the "start.boot" and "sys.config" from SrcDir to DestDir at all%% master nodes.%% 1. Save DestDir/"start.backup" and DestDir/"sys.backup" at all nodes.%% 2. Copy files at all nodes.%% 3. Remove backup files at all nodes.%%%% If one of the steps above fails, all steps is recovered from%% (as long as possible), except for 3 which is allowed to fail.%%-----------------------------------------------------------------set_static_files(SrcDir, DestDir, Masters) -> all_masters(Masters), Boot = "start.boot", Config = "sys.config", SrcBoot = filename:join(SrcDir, Boot), DestBoot = filename:join(DestDir, Boot), BackupBoot = filename:join(DestDir, "start.backup"), SrcConf = filename:join(SrcDir, Config), DestConf = filename:join(DestDir, Config), BackupConf = filename:join(DestDir, "sys.backup"), case at_all_masters(Masters, ?MODULE, do_copy_files, [[{DestBoot, BackupBoot}, {DestConf, BackupConf}]]) of ok -> case at_all_masters(Masters, ?MODULE, do_copy_files, [[{SrcBoot, DestBoot}, {SrcConf, DestConf}]]) of ok -> remove_files(all, [BackupBoot, BackupConf], Masters), ok; {error, {Master, R}} -> takewhile(Master, Masters, ?MODULE, do_rename_files, [{BackupBoot, DestBoot}, {BackupConf, DestConf}]), remove_files(all, [BackupBoot, BackupConf], Masters), throw({error, {Master, R, copy_start_config}}) end; {error, {Master, R}} -> remove_files(Master, [BackupBoot, BackupConf], Masters), throw({error, {Master, R, backup_start_config}}) end.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -