@@ -656,6 +656,8 @@ def get(opt, default, **kwargs):
656
656
environ_str = get ('environment' , '' )
657
657
environ_str = expand (environ_str , expansions , 'environment' )
658
658
section .environment = dict_of_key_value_pairs (environ_str )
659
+ section .environment_file = get ('environment_file' , None )
660
+ section .environment_loader = get ('environment_loader' , None )
659
661
660
662
# extend expansions for global from [supervisord] environment definition
661
663
for k , v in section .environment .items ():
@@ -674,6 +676,13 @@ def get(opt, default, **kwargs):
674
676
env = section .environment .copy ()
675
677
env .update (proc .environment )
676
678
proc .environment = env
679
+
680
+ # set the environment file/loader on the process configs but let them override it
681
+ if not proc .environment_file and not proc .environment_loader :
682
+ if section .environment_file :
683
+ proc .environment_file = section .environment_file
684
+ elif section .environment_loader :
685
+ proc .environment_loader = section .environment_loader
677
686
section .server_configs = self .server_configs_from_parser (parser )
678
687
section .profile_options = None
679
688
return section
@@ -925,6 +934,8 @@ def get(section, opt, *args, **kwargs):
925
934
numprocs = integer (get (section , 'numprocs' , 1 ))
926
935
numprocs_start = integer (get (section , 'numprocs_start' , 0 ))
927
936
environment_str = get (section , 'environment' , '' , do_expand = False )
937
+ environment_file = get (section , 'environment_file' , '' , do_expand = False )
938
+ environment_loader = get (section , 'environment_loader' , '' , do_expand = False )
928
939
stdout_cmaxbytes = byte_size (get (section ,'stdout_capture_maxbytes' ,'0' ))
929
940
stdout_events = boolean (get (section , 'stdout_events_enabled' ,'false' ))
930
941
stderr_cmaxbytes = byte_size (get (section ,'stderr_capture_maxbytes' ,'0' ))
@@ -1057,6 +1068,8 @@ def get(section, opt, *args, **kwargs):
1057
1068
exitcodes = exitcodes ,
1058
1069
redirect_stderr = redirect_stderr ,
1059
1070
environment = environment ,
1071
+ environment_file = environment_file ,
1072
+ environment_loader = environment_loader ,
1060
1073
serverurl = serverurl )
1061
1074
1062
1075
programs .append (pconfig )
@@ -1875,7 +1888,7 @@ class ProcessConfig(Config):
1875
1888
'stderr_events_enabled' , 'stderr_syslog' ,
1876
1889
'stopsignal' , 'stopwaitsecs' , 'stopasgroup' , 'killasgroup' ,
1877
1890
'exitcodes' , 'redirect_stderr' ]
1878
- optional_param_names = [ 'environment' , 'serverurl' ]
1891
+ optional_param_names = [ 'environment' , 'environment_file' , 'environment_loader' , ' serverurl' ]
1879
1892
1880
1893
def __init__ (self , options , ** params ):
1881
1894
self .options = options
@@ -1939,6 +1952,57 @@ def make_dispatchers(self, proc):
1939
1952
dispatchers [stdin_fd ] = PInputDispatcher (proc , 'stdin' , stdin_fd )
1940
1953
return dispatchers , p
1941
1954
1955
+ def load_external_environment_definition (self ):
1956
+ return self .load_external_environment_definition_for_config (self )
1957
+
1958
+ # this is separated out in order to make it easier to test
1959
+ @classmethod
1960
+ def load_external_environment_definition_for_config (cls , config ):
1961
+ # lazily load extra env vars before we drop privileges so that this can be used to load a secrets file
1962
+ # or execute a program to get more env configuration. It doesn't have to be secrets, just config that
1963
+ # needs to be separate from the supervisor config for whatever reason. The supervisor config interpolation
1964
+ # is not supported here. The data format is just plain text, with one k=v value per line. Lines starting
1965
+ # with '#' are ignored.
1966
+ env = {}
1967
+ envdata = None
1968
+ if config .environment_file :
1969
+ if os .path .exists (config .environment_file ):
1970
+ try :
1971
+ with open (config .environment_file , 'r' ) as f :
1972
+ envdata = f .read ()
1973
+
1974
+ except Exception as e :
1975
+ raise ProcessException ("environment_file read failure on %s: %s" % (config .environment_file , e ))
1976
+
1977
+ elif config .environment_loader :
1978
+ try :
1979
+ from subprocess import check_output , CalledProcessError
1980
+ kwargs = dict (shell = True )
1981
+ if not PY2 :
1982
+ kwargs ['text' ] = True
1983
+
1984
+ envdata = check_output (config .environment_loader , ** kwargs )
1985
+
1986
+ except CalledProcessError as e :
1987
+ raise ProcessException ("environment_loader failure with %s: %d, %s" % (config .environment_loader , e .returncode , e .output ))
1988
+
1989
+ if envdata :
1990
+ extra_env = {}
1991
+
1992
+ for line in envdata .splitlines ():
1993
+ line = line .strip ()
1994
+ if line .startswith ('#' ): # ignore comments
1995
+ continue
1996
+
1997
+ key , val = [s .strip () for s in line .split ('=' , 1 )]
1998
+ if key :
1999
+ extra_env [key .upper ()] = val
2000
+
2001
+ if extra_env :
2002
+ env .update (extra_env )
2003
+
2004
+ return env
2005
+
1942
2006
class EventListenerConfig (ProcessConfig ):
1943
2007
def make_dispatchers (self , proc ):
1944
2008
# always use_stderr=True for eventlisteners because mixing stderr
0 commit comments