1%% This Source Code Form is subject to the terms of the Mozilla Public 2%% License, v. 2.0. If a copy of the MPL was not distributed with this 3%% file, You can obtain one at https://mozilla.org/MPL/2.0/. 4%% 5%% Copyright (c) 2007-2021 VMware, Inc. or its affiliates. All rights reserved. 6%% 7 8-module(rabbit_definitions_import_local_filesystem). 9-include_lib("rabbit_common/include/rabbit.hrl"). 10 11-export([ 12 is_enabled/0, 13 %% definition source options 14 load/1, 15 %% classic arguments specific to this source 16 load/2, 17 location/0 18]). 19 20 21 22-import(rabbit_misc, [pget/2, pget/3]). 23-import(rabbit_data_coercion, [to_binary/1]). 24-import(rabbit_definitions, [import_raw/1]). 25 26%% 27%% API 28%% 29 30-spec is_enabled() -> boolean(). 31is_enabled() -> 32 is_enabled_via_classic_option() or is_enabled_via_modern_option(). 33 34load(Proplist) when is_list(Proplist) -> 35 case pget(local_path, Proplist, undefined) of 36 undefined -> {error, "local definition file path is not configured: local_path is not set"}; 37 Path -> 38 rabbit_log:debug("Asked to import definitions from a local file or directory at '~s'", [Path]), 39 case file:read_file_info(Path) of 40 {ok, FileInfo} -> 41 %% same check is used by Cuttlefish validation, this is to be extra defensive 42 IsReadable = (element(4, FileInfo) == read) or (element(4, FileInfo) == read_write), 43 case IsReadable of 44 true -> 45 load_from_single_file(Path); 46 false -> 47 Msg = rabbit_misc:format("local definition file '~s' does not exist or cannot be read by the node", [Path]), 48 {error, Msg} 49 end; 50 _ -> 51 Msg = rabbit_misc:format("local definition file '~s' does not exist or cannot be read by the node", [Path]), 52 {error, {could_not_read_defs, Msg}} 53 end 54 end. 55 56load(IsDir, Path) -> 57 load_from_local_path(IsDir, Path). 58 59location() -> 60 case location_from_classic_option() of 61 undefined -> location_from_modern_option(); 62 Value -> Value 63 end. 64 65load_from_local_path(true, Dir) -> 66 rabbit_log:info("Applying definitions from directory ~s", [Dir]), 67 load_from_files(file:list_dir(Dir), Dir); 68load_from_local_path(false, File) -> 69 load_from_single_file(File). 70 71%% 72%% Implementation 73%% 74 75-spec is_enabled_via_classic_option() -> boolean(). 76is_enabled_via_classic_option() -> 77 %% Classic way of defining a local filesystem definition source 78 case application:get_env(rabbit, load_definitions) of 79 undefined -> false; 80 {ok, none} -> false; 81 {ok, _Path} -> true 82 end. 83 84-spec is_enabled_via_modern_option() -> boolean(). 85is_enabled_via_modern_option() -> 86 %% Modern way of defining a local filesystem definition source 87 case application:get_env(rabbit, definitions) of 88 undefined -> false; 89 {ok, none} -> false; 90 {ok, []} -> false; 91 {ok, Proplist} -> 92 case pget(import_backend, Proplist, undefined) of 93 undefined -> false; 94 ?MODULE -> true; 95 _ -> false 96 end 97 end. 98 99location_from_classic_option() -> 100 case application:get_env(rabbit, load_definitions) of 101 undefined -> undefined; 102 {ok, none} -> undefined; 103 {ok, Path} -> Path 104 end. 105 106location_from_modern_option() -> 107 case application:get_env(rabbit, definitions) of 108 undefined -> undefined; 109 {ok, none} -> undefined; 110 {ok, Proplist} -> 111 pget(local_path, Proplist) 112 end. 113 114 115load_from_files({ok, Filenames0}, Dir) -> 116 Filenames1 = lists:sort(Filenames0), 117 Filenames2 = [filename:join(Dir, F) || F <- Filenames1], 118 load_from_multiple_files(Filenames2); 119load_from_files({error, E}, Dir) -> 120 rabbit_log:error("Could not read definitions from directory ~s, Error: ~p", [Dir, E]), 121 {error, {could_not_read_defs, E}}. 122 123load_from_multiple_files([]) -> 124 ok; 125load_from_multiple_files([File|Rest]) -> 126 case load_from_single_file(File) of 127 ok -> load_from_multiple_files(Rest); 128 {error, E} -> {error, {failed_to_import_definitions, File, E}} 129 end. 130 131load_from_single_file(Path) -> 132 rabbit_log:debug("Will try to load definitions from a local file or directory at '~s'", [Path]), 133 case file:read_file(Path) of 134 {ok, Body} -> 135 rabbit_log:info("Applying definitions from file at '~s'", [Path]), 136 import_raw(Body); 137 {error, E} -> 138 rabbit_log:error("Could not read definitions from file at '~s', error: ~p", [Path, E]), 139 {error, {could_not_read_defs, {Path, E}}} 140 end. 141