1
1
use std:: {
2
+ collections:: HashMap ,
2
3
io:: { Error , ErrorKind } ,
3
4
path:: PathBuf ,
4
5
process:: ExitStatus ,
@@ -61,6 +62,17 @@ pub struct ExecConfig {
61
62
#[ configurable( metadata( docs:: examples = "echo" , docs:: examples = "Hello World!" ) ) ]
62
63
pub command : Vec < String > ,
63
64
65
+ /// Custom environment variables to set or update when running the command.
66
+ /// If a variable name already exists in the environment, its value is replaced.
67
+ #[ serde( default ) ]
68
+ #[ configurable( metadata( docs:: additional_props_description = "An environment variable." ) ) ]
69
+ #[ configurable( metadata( docs:: examples = "environment_examples()" ) ) ]
70
+ pub environment : Option < HashMap < String , String > > ,
71
+
72
+ /// Whether or not to clear the environment before setting custom environment variables.
73
+ #[ serde( default = "default_clear_environment" ) ]
74
+ pub clear_environment : bool ,
75
+
64
76
/// The directory in which to run the command.
65
77
pub working_directory : Option < PathBuf > ,
66
78
@@ -141,6 +153,8 @@ impl Default for ExecConfig {
141
153
} ) ,
142
154
streaming : None ,
143
155
command : vec ! [ "echo" . to_owned( ) , "Hello World!" . to_owned( ) ] ,
156
+ environment : None ,
157
+ clear_environment : default_clear_environment ( ) ,
144
158
working_directory : None ,
145
159
include_stderr : default_include_stderr ( ) ,
146
160
maximum_buffer_size_bytes : default_maximum_buffer_size ( ) ,
@@ -168,10 +182,25 @@ const fn default_respawn_on_exit() -> bool {
168
182
true
169
183
}
170
184
185
+ const fn default_clear_environment ( ) -> bool {
186
+ false
187
+ }
188
+
171
189
const fn default_include_stderr ( ) -> bool {
172
190
true
173
191
}
174
192
193
+ fn environment_examples ( ) -> HashMap < String , String > {
194
+ HashMap :: < _ , _ > :: from_iter (
195
+ [
196
+ ( "LANG" . to_owned ( ) , "es_ES.UTF-8" . to_owned ( ) ) ,
197
+ ( "TZ" . to_owned ( ) , "Etc/UTC" . to_owned ( ) ) ,
198
+ ( "PATH" . to_owned ( ) , "/bin:/usr/bin:/usr/local/bin" . to_owned ( ) ) ,
199
+ ]
200
+ . into_iter ( ) ,
201
+ )
202
+ }
203
+
175
204
fn get_hostname ( ) -> Option < String > {
176
205
crate :: get_hostname ( ) . ok ( )
177
206
}
@@ -610,6 +639,16 @@ fn build_command(config: &ExecConfig) -> Command {
610
639
611
640
command. kill_on_drop ( true ) ;
612
641
642
+ // Clear environment variables if needed
643
+ if config. clear_environment {
644
+ command. env_clear ( ) ;
645
+ }
646
+
647
+ // Configure environment variables if needed
648
+ if let Some ( envs) = & config. environment {
649
+ command. envs ( envs) ;
650
+ }
651
+
613
652
// Explicitly set the current dir if needed
614
653
if let Some ( current_dir) = & config. working_directory {
615
654
command. current_dir ( current_dir) ;
@@ -726,6 +765,7 @@ mod tests {
726
765
use super :: * ;
727
766
use crate :: { event:: LogEvent , test_util:: trace_init} ;
728
767
use bytes:: Bytes ;
768
+ use std:: ffi:: OsStr ;
729
769
use std:: io:: Cursor ;
730
770
use vector_core:: event:: EventMetadata ;
731
771
use vrl:: value;
@@ -900,6 +940,8 @@ mod tests {
900
940
respawn_interval_secs : default_respawn_interval_secs ( ) ,
901
941
} ) ,
902
942
command : vec ! [ "./runner" . to_owned( ) , "arg1" . to_owned( ) , "arg2" . to_owned( ) ] ,
943
+ environment : None ,
944
+ clear_environment : default_clear_environment ( ) ,
903
945
working_directory : Some ( PathBuf :: from ( "/tmp" ) ) ,
904
946
include_stderr : default_include_stderr ( ) ,
905
947
maximum_buffer_size_bytes : default_maximum_buffer_size ( ) ,
@@ -922,6 +964,64 @@ mod tests {
922
964
assert_eq ! ( expected_command_string, command_string) ;
923
965
}
924
966
967
+ #[ test]
968
+ fn test_build_command_custom_environment ( ) {
969
+ let config = ExecConfig {
970
+ mode : Mode :: Streaming ,
971
+ scheduled : None ,
972
+ streaming : Some ( StreamingConfig {
973
+ respawn_on_exit : default_respawn_on_exit ( ) ,
974
+ respawn_interval_secs : default_respawn_interval_secs ( ) ,
975
+ } ) ,
976
+ command : vec ! [ "./runner" . to_owned( ) , "arg1" . to_owned( ) , "arg2" . to_owned( ) ] ,
977
+ environment : Some ( HashMap :: from ( [ ( "FOO" . to_owned ( ) , "foo" . to_owned ( ) ) ] ) ) ,
978
+ clear_environment : default_clear_environment ( ) ,
979
+ working_directory : Some ( PathBuf :: from ( "/tmp" ) ) ,
980
+ include_stderr : default_include_stderr ( ) ,
981
+ maximum_buffer_size_bytes : default_maximum_buffer_size ( ) ,
982
+ framing : None ,
983
+ decoding : default_decoding ( ) ,
984
+ log_namespace : None ,
985
+ } ;
986
+
987
+ let command = build_command ( & config) ;
988
+ let cmd = command. as_std ( ) ;
989
+
990
+ let idx = cmd
991
+ . get_envs ( )
992
+ . position ( |v| v == ( OsStr :: new ( "FOO" ) , Some ( OsStr :: new ( "foo" ) ) ) ) ;
993
+
994
+ assert_ne ! ( idx, None ) ;
995
+ }
996
+
997
+ #[ test]
998
+ fn test_build_command_clear_environment ( ) {
999
+ let config = ExecConfig {
1000
+ mode : Mode :: Streaming ,
1001
+ scheduled : None ,
1002
+ streaming : Some ( StreamingConfig {
1003
+ respawn_on_exit : default_respawn_on_exit ( ) ,
1004
+ respawn_interval_secs : default_respawn_interval_secs ( ) ,
1005
+ } ) ,
1006
+ command : vec ! [ "./runner" . to_owned( ) , "arg1" . to_owned( ) , "arg2" . to_owned( ) ] ,
1007
+ environment : Some ( HashMap :: from ( [ ( "FOO" . to_owned ( ) , "foo" . to_owned ( ) ) ] ) ) ,
1008
+ clear_environment : true ,
1009
+ working_directory : Some ( PathBuf :: from ( "/tmp" ) ) ,
1010
+ include_stderr : default_include_stderr ( ) ,
1011
+ maximum_buffer_size_bytes : default_maximum_buffer_size ( ) ,
1012
+ framing : None ,
1013
+ decoding : default_decoding ( ) ,
1014
+ log_namespace : None ,
1015
+ } ;
1016
+
1017
+ let command = build_command ( & config) ;
1018
+ let cmd = command. as_std ( ) ;
1019
+
1020
+ let envs: Vec < _ > = cmd. get_envs ( ) . collect ( ) ;
1021
+
1022
+ assert_eq ! ( envs. len( ) , 1 ) ;
1023
+ }
1024
+
925
1025
#[ tokio:: test]
926
1026
async fn test_spawn_reader_thread ( ) {
927
1027
trace_init ( ) ;
@@ -1112,6 +1212,8 @@ mod tests {
1112
1212
respawn_interval_secs : default_respawn_interval_secs ( ) ,
1113
1213
} ) ,
1114
1214
command : vec ! [ "yes" . to_owned( ) ] ,
1215
+ environment : None ,
1216
+ clear_environment : default_clear_environment ( ) ,
1115
1217
working_directory : None ,
1116
1218
include_stderr : default_include_stderr ( ) ,
1117
1219
maximum_buffer_size_bytes : default_maximum_buffer_size ( ) ,
0 commit comments