30
30
using Newtonsoft . Json ;
31
31
using Newtonsoft . Json . Linq ;
32
32
using System . Collections . Immutable ;
33
+ using System . Configuration ;
34
+ using System . IO . Abstractions ;
33
35
34
36
namespace Microsoft . Azure . WebJobs . Script
35
37
{
@@ -42,7 +44,7 @@ public class ScriptHost : JobHost
42
44
private Action < FileSystemEventArgs > _restart ;
43
45
private AutoRecoveringFileSystemWatcher _scriptFileWatcher ;
44
46
private AutoRecoveringFileSystemWatcher _debugModeFileWatcher ;
45
- private ImmutableArray < string > _directorySnapshot ;
47
+ private ImmutableArray < string > _directorySnapshot ;
46
48
private BlobLeaseManager _blobLeaseManager ;
47
49
private static readonly TimeSpan MinTimeout = TimeSpan . FromSeconds ( 1 ) ;
48
50
private static readonly TimeSpan MaxTimeout = TimeSpan . FromMinutes ( 5 ) ;
@@ -270,7 +272,7 @@ protected virtual void Initialize()
270
272
271
273
_debugModeFileWatcher = new AutoRecoveringFileSystemWatcher ( hostLogPath , ScriptConstants . DebugSentinelFileName ,
272
274
includeSubdirectories : false , changeTypes : WatcherChangeTypes . Created | WatcherChangeTypes . Changed ) ;
273
-
275
+
274
276
_debugModeFileWatcher . Changed += OnDebugModeFileChanged ;
275
277
276
278
var storageString = AmbientConnectionStringProvider . Instance . GetConnectionString ( ConnectionStringNames . Storage ) ;
@@ -624,11 +626,10 @@ public static Collection<FunctionMetadata> ReadFunctionMetadata(ScriptHostConfig
624
626
string json = File . ReadAllText ( functionConfigPath ) ;
625
627
JObject functionConfig = JObject . Parse ( json ) ;
626
628
627
- Lazy < string [ ] > functionFiles = new Lazy < string [ ] > ( ( ) => Directory . EnumerateFiles ( scriptDir ) . Where ( p => Path . GetFileName ( p ) . ToLowerInvariant ( ) != ScriptConstants . FunctionMetadataFileName ) . ToArray ( ) ) ;
628
629
string functionError = null ;
629
630
FunctionMetadata functionMetadata = null ;
630
631
var mappedHttpFunctions = new Dictionary < string , HttpTriggerBindingMetadata > ( ) ;
631
- if ( ! TryParseFunctionMetadata ( functionName , functionConfig , mappedHttpFunctions , traceWriter , functionFiles , out functionMetadata , out functionError ) )
632
+ if ( ! TryParseFunctionMetadata ( functionName , functionConfig , mappedHttpFunctions , traceWriter , scriptDir , out functionMetadata , out functionError ) )
632
633
{
633
634
// for functions in error, log the error and don't
634
635
// add to the functions collection
@@ -650,8 +651,11 @@ public static Collection<FunctionMetadata> ReadFunctionMetadata(ScriptHostConfig
650
651
return functions ;
651
652
}
652
653
653
- internal static bool TryParseFunctionMetadata ( string functionName , JObject functionConfig , Dictionary < string , HttpTriggerBindingMetadata > mappedHttpFunctions , TraceWriter traceWriter , Lazy < string [ ] > functionFilesProvider , out FunctionMetadata functionMetadata , out string error )
654
+ internal static bool TryParseFunctionMetadata ( string functionName , JObject functionConfig , Dictionary < string , HttpTriggerBindingMetadata > mappedHttpFunctions ,
655
+ TraceWriter traceWriter , string scriptDirectory , out FunctionMetadata functionMetadata , out string error , IFileSystem fileSystem = null )
654
656
{
657
+ fileSystem = fileSystem ?? new FileSystem ( ) ;
658
+
655
659
error = null ;
656
660
functionMetadata = ParseFunctionMetadata ( functionName , functionConfig ) ;
657
661
@@ -667,22 +671,15 @@ internal static bool TryParseFunctionMetadata(string functionName, JObject funct
667
671
traceWriter . Info ( string . Format ( "Function '{0}' is disabled" , functionName ) ) ;
668
672
}
669
673
670
- // determine the primary script
671
- string [ ] functionFiles = functionFilesProvider . Value ;
672
- if ( functionFiles . Length == 0 )
674
+ try
673
675
{
674
- error = "No function script files present." ;
675
- return false ;
676
+ functionMetadata . ScriptFile = DeterminePrimaryScriptFile ( functionConfig , scriptDirectory , fileSystem ) ;
676
677
}
677
- string scriptFile = DeterminePrimaryScriptFile ( functionConfig , functionFiles ) ;
678
- if ( string . IsNullOrEmpty ( scriptFile ) )
678
+ catch ( ConfigurationErrorsException exc )
679
679
{
680
- error =
681
- "Unable to determine the primary function script. Try renaming your entry point script to 'run' (or 'index' in the case of Node), " +
682
- "or alternatively you can specify the name of the entry point script explicitly by adding a 'scriptFile' property to your function metadata." ;
680
+ error = exc . Message ;
683
681
return false ;
684
682
}
685
- functionMetadata . ScriptFile = scriptFile ;
686
683
687
684
// determine the script type based on the primary script file extension
688
685
functionMetadata . ScriptType = ParseScriptType ( functionMetadata . ScriptFile ) ;
@@ -754,35 +751,59 @@ internal static void ValidateFunctionName(string functionName)
754
751
/// <summary>
755
752
/// Determines which script should be considered the "primary" entry point script.
756
753
/// </summary>
757
- internal static string DeterminePrimaryScriptFile ( JObject functionConfig , string [ ] functionFiles )
754
+ /// <exception cref="ConfigurationErrorsException">Thrown if the function metadata points to an invalid script file, or no script files are present.</exception>
755
+ internal static string DeterminePrimaryScriptFile ( JObject functionConfig , string scriptDirectory , IFileSystem fileSystem = null )
758
756
{
759
- if ( functionFiles . Length == 1 )
757
+ fileSystem = fileSystem ?? new FileSystem ( ) ;
758
+
759
+ // First see if there is an explicit primary file indicated
760
+ // in config. If so use that.
761
+ string functionPrimary = null ;
762
+ string scriptFile = ( string ) functionConfig [ "scriptFile" ] ;
763
+
764
+ if ( ! string . IsNullOrEmpty ( scriptFile ) )
760
765
{
761
- // if there is only a single file, that file is primary
762
- return functionFiles [ 0 ] ;
766
+ string scriptPath = fileSystem . Path . Combine ( scriptDirectory , scriptFile ) ;
767
+ if ( ! fileSystem . File . Exists ( scriptPath ) )
768
+ {
769
+ throw new ConfigurationErrorsException ( "Invalid script file name configuration. The 'scriptFile' property is set to a file that does not exist." ) ;
770
+ }
771
+
772
+ functionPrimary = scriptPath ;
763
773
}
764
774
else
765
775
{
766
- // First see if there is an explicit primary file indicated
767
- // in config. If so use that.
768
- string functionPrimary = null ;
769
- string scriptFileName = ( string ) functionConfig [ "scriptFile" ] ;
770
- if ( ! string . IsNullOrEmpty ( scriptFileName ) )
776
+ string [ ] functionFiles = fileSystem . Directory . EnumerateFiles ( scriptDirectory )
777
+ . Where ( p => fileSystem . Path . GetFileName ( p ) . ToLowerInvariant ( ) != ScriptConstants . FunctionMetadataFileName )
778
+ . ToArray ( ) ;
779
+
780
+ if ( functionFiles . Length == 0 )
771
781
{
772
- functionPrimary = functionFiles . FirstOrDefault ( p =>
773
- string . Compare ( Path . GetFileName ( p ) , scriptFileName , StringComparison . OrdinalIgnoreCase ) == 0 ) ;
782
+ throw new ConfigurationErrorsException ( "No function script files present." ) ;
783
+ }
784
+
785
+ if ( functionFiles . Length == 1 )
786
+ {
787
+ // if there is only a single file, that file is primary
788
+ functionPrimary = functionFiles [ 0 ] ;
774
789
}
775
790
else
776
791
{
777
792
// if there is a "run" file, that file is primary,
778
793
// for Node, any index.js file is primary
779
794
functionPrimary = functionFiles . FirstOrDefault ( p =>
780
- Path . GetFileNameWithoutExtension ( p ) . ToLowerInvariant ( ) == "run" ||
781
- Path . GetFileName ( p ) . ToLowerInvariant ( ) == "index.js" ) ;
795
+ fileSystem . Path . GetFileNameWithoutExtension ( p ) . ToLowerInvariant ( ) == "run" ||
796
+ fileSystem . Path . GetFileName ( p ) . ToLowerInvariant ( ) == "index.js" ) ;
782
797
}
798
+ }
783
799
784
- return functionPrimary ;
800
+ if ( string . IsNullOrEmpty ( functionPrimary ) )
801
+ {
802
+ throw new ConfigurationErrorsException ( "Unable to determine the primary function script. Try renaming your entry point script to 'run' (or 'index' in the case of Node), " +
803
+ "or alternatively you can specify the name of the entry point script explicitly by adding a 'scriptFile' property to your function metadata." ) ;
785
804
}
805
+
806
+ return Path . GetFullPath ( functionPrimary ) ;
786
807
}
787
808
788
809
private static ScriptType ParseScriptType ( string scriptFilePath )
0 commit comments