1use log::{debug, error, trace, warn};
3use std::{
4 fs::{self, File},
5 io::{BufReader, Read},
6 path::Path,
7};
8
9use super::{
10 api::NetworkConfigList,
11 types::{NetConf, NetworkConfig},
12};
13
14pub struct ConfigFile {}
15
16impl ConfigFile {
17 pub fn config_files(dir: String, extensions: Vec<String>) -> Result<Vec<String>, String> {
18 debug!("Reading CNI config files from directory: {}", dir);
19 let mut conf_files = Vec::default();
20
21 match fs::read_dir(&dir) {
22 Ok(dir_entries) => {
23 for entry in dir_entries {
24 match entry {
25 Ok(file) => {
26 let file_path = file.path();
27 if let Some(ext) = file_path.extension() {
28 let ext_str = ext.to_string_lossy().to_string();
29 if extensions.contains(&ext_str) {
30 let path_str = file.path().to_string_lossy().to_string();
31 trace!("Found config file: {}", path_str);
32 conf_files.push(path_str);
33 }
34 }
35 }
36 Err(e) => {
37 warn!("Error reading directory entry: {}", e);
38 }
39 }
40 }
41
42 debug!("Found {} config files", conf_files.len());
43 Ok(conf_files)
44 }
45 Err(e) => {
46 error!("Failed to read CNI config directory {}: {}", dir, e);
47 Err(format!("Failed to read directory {}: {}", dir, e))
48 }
49 }
50 }
51
52 pub fn config_from_bytes(datas: &[u8]) -> Result<NetworkConfigList, String> {
53 trace!("Parsing CNI config from bytes: {} bytes", datas.len());
54
55 match serde_json::from_slice::<serde_json::Value>(datas) {
56 Ok(ncmaps) => {
57 let name = match ncmaps.get("name") {
59 Some(name_val) => match name_val.as_str() {
60 Some(name_str) => name_str.to_string(),
61 None => return Err("'name' is not a string".to_string()),
62 },
63 None => return Err("'name' field is required".to_string()),
64 };
65
66 let version = match ncmaps.get("cniVersion") {
67 Some(ver_val) => match ver_val.as_str() {
68 Some(ver_str) => ver_str.to_string(),
69 None => return Err("'cniVersion' is not a string".to_string()),
70 },
71 None => return Err("'cniVersion' field is required".to_string()),
72 };
73
74 let mut disable_check = false;
75 if let Some(check) = ncmaps.get("disableCheck") {
76 if let Some(check_bool) = check.as_bool() {
77 disable_check = check_bool;
78 }
79 }
80
81 let mut ncflist = NetworkConfigList::default();
82 let mut all_plugins = Vec::new();
83
84 if let Some(plugins) = ncmaps.get("plugins") {
85 if let Some(plugins_arr) = plugins.as_array() {
86 for plugin in plugins_arr {
87 let string_plugin = plugin.to_string();
88 let plg_bytes = string_plugin.as_bytes().to_vec();
89
90 match serde_json::from_str::<NetConf>(&string_plugin) {
91 Ok(tmp) => {
92 trace!("Parsed plugin: {}", tmp._type);
93 all_plugins.push(NetworkConfig {
94 network: tmp,
95 bytes: plg_bytes,
96 });
97 }
98 Err(e) => {
99 error!("Failed to parse plugin config: {}", e);
100 return Err(format!("Invalid plugin config: {}", e));
101 }
102 }
103 }
104 } else {
105 return Err("'plugins' must be an array".to_string());
106 }
107 } else {
108 return Err("'plugins' field is required".to_string());
109 }
110
111 ncflist.name = name;
112 ncflist.cni_version = version;
113 ncflist.bytes = datas.to_vec();
114 ncflist.disable_check = disable_check;
115 ncflist.plugins = all_plugins;
116 debug!("Successfully parsed NetworkConfigList: {}", ncflist.name);
117 Ok(ncflist)
118 }
119 Err(e) => {
120 error!("Failed to parse CNI config: {}", e);
121 Err(format!("Invalid JSON: {}", e))
122 }
123 }
124 }
125
126 pub fn read_configlist_file(file_path: String) -> Option<NetworkConfigList> {
127 debug!("Reading CNI config list from file: {}", file_path);
128 let path = Path::new(&file_path);
129
130 if !path.exists() {
131 error!("Config file does not exist: {}", file_path);
132 return None;
133 }
134
135 match File::open(path) {
136 Ok(file) => {
137 let mut file_bytes = Vec::default();
138 let mut reader = BufReader::new(file);
139
140 match reader.read_to_end(&mut file_bytes) {
141 Ok(_) => match Self::config_from_bytes(&file_bytes) {
142 Ok(ncflist) => {
143 debug!("Successfully read config list: {}", ncflist.name);
144 Some(ncflist)
145 }
146 Err(e) => {
147 error!("Failed to parse config list: {}", e);
148 None
149 }
150 },
151 Err(e) => {
152 error!("Failed to read config file {}: {}", file_path, e);
153 None
154 }
155 }
156 }
157 Err(e) => {
158 error!("Failed to open config file {}: {}", file_path, e);
159 None
160 }
161 }
162 }
163
164 pub fn read_config_file(file_path: String) -> Option<NetworkConfig> {
165 debug!("Reading CNI single config from file: {}", file_path);
166 let path = Path::new(&file_path);
167
168 if !path.exists() {
169 error!("Config file does not exist: {}", file_path);
170 return None;
171 }
172
173 match File::open(path) {
174 Ok(file) => {
175 let mut file_bytes = Vec::default();
176 let mut reader = BufReader::new(file);
177
178 match reader.read_to_end(&mut file_bytes) {
179 Ok(_) => match serde_json::from_slice::<NetConf>(&file_bytes) {
180 Ok(net_conf) => {
181 debug!("Successfully read config: {:?}", net_conf);
182 Some(NetworkConfig {
183 network: net_conf,
184 bytes: file_bytes,
185 })
186 }
187 Err(e) => {
188 error!("Failed to parse config: {}", e);
189 None
190 }
191 },
192 Err(e) => {
193 error!("Failed to read config file {}: {}", file_path, e);
194 None
195 }
196 }
197 }
198 Err(e) => {
199 error!("Failed to open config file {}: {}", file_path, e);
200 None
201 }
202 }
203 }
204
205 pub fn convert_to_config_list(config: NetworkConfig) -> NetworkConfigList {
206 debug!(
207 "Converting single config to config list: {}",
208 config.network.name
209 );
210 NetworkConfigList {
211 name: config.network.name.clone(),
212 cni_version: config.network.cni_version.clone(),
213 disable_check: false,
214 plugins: vec![config],
215 bytes: Vec::new(), }
217 }
218}