flancy icon indicating copy to clipboard operation
flancy copied to clipboard

Can't run latest Flancy as Job anymore

Open beatcracker opened this issue 9 years ago • 12 comments

Repro script:

Start-Job -Name 'Flancy' -ScriptBlock {
    Import-Module -Name 'Flancy' -ErrorAction Stop
    New-Flancy -WebSchema @{
        Method = 'Get'
        Path= '/'
        Script = {'Hello, world!'}
    }
}

Wait-Job -Name 'Flancy' | Receive-Job

Output:

PS > .\Flancy-AsJob.ps1

Id     Name            PSJobTypeName   State         HasMoreData     Location             Command
--     ----            -------------   -----         -----------     --------             -------
2      Flancy          BackgroundJob   Running       True            localhost            ...

Exception calling ".ctor" with "1" argument(s): "Unable to resolve type: Nancy.ViewEngines.ViewEngineApplicationStartup"

    + CategoryInfo          : InvalidOperation: (:) [New-Object], MethodInvocationException
    + FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand
    + PSComputerName        : localhost

You cannot call a method on a null-valued expression.

At .\flancy\flancy.psm1:395 char:9
+         $flancy.start()
+         ~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidOperation: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvokeMethodOnNull

PowerShell version:

Name             : Windows PowerShell ISE Host
Version          : 5.0.10018.0
InstanceId       : 03eac871-dcbd-4e6a-8c7c-b5c018dbcda8
UI               : System.Management.Automation.Internal.Host.InternalHostUserInterface
CurrentCulture   : ru-RU
CurrentUICulture : en-US
PrivateData      : Microsoft.PowerShell.Host.ISE.ISEOptions
DebuggerEnabled  : True
IsRunspacePushed : False
Runspace         : System.Management.Automation.Runspaces.LocalRunspace

This doesn't happen with this version of Flancy.

beatcracker avatar Nov 29 '15 15:11 beatcracker

I think this is the same as issue #32

h0rnman avatar Dec 02 '15 14:12 h0rnman

@h0rnman Agree. Curious, that I have no issues while running Flancy in PS console/ISE, only as job.

beatcracker avatar Dec 03 '15 09:12 beatcracker

Is your ISE environment importing a different build of the module perhaps? I am seeing the error in both ISE and console.

h0rnman avatar Dec 03 '15 19:12 h0rnman

I've nailed it! It all comes down to the Flancy's Path parameter. When Flancy is run in the interactive PowerShell session and Path parameter is not specified, it will be set to the current PowerShell's directory. But if you run it as a job, $PWD points to the current user's My Documents folder (C:\Users\Administrator\Documents in my case).

Here is the code in the Flancy.psm1 that sets it:

if (!$path) {
    $path = ''
    if ($MyInvocation.MyCommand.Path) {
        $path = Split-Path $MyInvocation.MyCommand.Path
    } else {
        $path = $pwd -replace '^\S+::',''
    }
}

And that's where weird things start to happen: Nancy tries to access folder C:\Users\Administrator\Documents\My Music which doesn't exist!

System.UnauthorizedAccessException: Access to the path 'C:\Users\Administrator\Documents\My Music' is denied.
    at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
    at System.IO.FileSystemEnumerableIterator`1.AddSearchableDirsToStack(SearchData localSearchData)
    at System.IO.FileSystemEnumerableIterator`1.MoveNext()
    at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
    at System.IO.Directory.InternalGetFileDirectoryNames(String path, String userPathOriginal, String searchPattern, Boolean includeFiles, Boolean includeDirs, SearchOption searchOption, Boolean checkHost)
    at System.IO.Directory.InternalGetFiles(String path, String searchPattern, SearchOption searchOption)
    at Nancy.ViewEngines.DefaultFileSystemReader.GetFilenames(String path, String extension)
    at System.Linq.Enumerable.<SelectManyIterator>d__14`2.MoveNext()
    at System.Linq.Enumerable.<DistinctIterator>d__81`1.MoveNext()
    at System.Linq.Enumerable.WhereSelectEnumerableIterator`2.MoveNext()
    at System.Collections.Generic.List`1..ctor(IEnumerable`1 collection)
    at System.Linq.Enumerable.ToList[TSource](IEnumerable`1 source)
    at Nancy.ViewEngines.DefaultFileSystemReader.GetViewsWithSupportedExtensions(String path, IEnumerable`1 supportedViewExtensions)
    at Nancy.ViewEngines.FileSystemViewLocationProvider.GetViewsFromPath(String path, IEnumerable`1 supportedViewExtensions)
    at Nancy.ViewEngines.DefaultViewLocator..ctor(IViewLocationProvider viewLocationProvider, IEnumerable`1 viewEngines)
    at lambda_method(Closure , Object[] )
    at Nancy.TinyIoc.TinyIoCContainer.ConstructType(Type requestedType, Type implementationType, ConstructorInfo constructor, NamedParameterOverloads parameters, ResolveOptions options)

I'm also able to reproduce this in the PowerShell session (not job). It looks like Nancy is trying to traverse down the given path, because when I set Path to the root of my drive (E:\), exception is thrown about E:\$RECYCLE.BIN\S-1-5-18.

To get this details I've modified Flancy.psm1 to catch and unwind inner exceptions when creating new Flancy object. This gives you the full list of the exceptions, instead of the outer one.

Before:

$flancy = new-object "flancy.flancy" -argumentlist $url

After:

try
{
    $flancy = new-object "flancy.flancy" -argumentlist $url
}
catch
{
    function Unwind-Exception
    {
        Param($Exception)

        if($Exception.InnerException)
        {
            $Exception.InnerException.PsObject.Properties | Select-Object -Property Name, Value
            Unwind-Exception $Exception.InnerException
        }
    }

    Unwind-Exception $_.Exception.InnerException
}

beatcracker avatar Dec 06 '15 00:12 beatcracker

I will include the exception unwinding in flancy.psm1 in the devel branch. Doing that now. If you set your directory to something else in the job, is it working? I'd like to understand how to make it work. I'd also like to understand what's causing it.

toenuff avatar Dec 06 '15 02:12 toenuff

never mind - I duplicated and was able to get it to work with this:

Start-Job -InitializationScript {import-module c:\flancy\flancy.psd1} -ScriptBlock {cd c:\flancy; new-flancy;while ($true) {sleep 60}}

I'm thinking the best way to fix this would be to create an asjob parameter. I could then do the following:

  1. load the module in the initialization script from get-module -listavailable flancy |select -expandproperty modulebase
  2. Set the current directory in the job to the current directory
  3. Pass any arguments as a single argument to the scriptblock
  4. Pass the arguments to the scriptblock to new-flancy
  5. Create the loop with a 60 minute sleep

toenuff avatar Dec 06 '15 03:12 toenuff

Confirmed, this appears to be a bug in Nancy, not flancy. You cannot seem to set the root path to c:\users\username\documents. I attempted to compile it with that root path and it errors. Actually, I found that any directory (such as root drives due to the recycle bin) where a subfolder gives access denied appears to error. i'm unsure whether or not I can use the following to validate the rootdir just now.

Get-ChildItem c:\users\tome\documents -Depth 1 -force -Recurse -ea stop

It will work for my docs and for c-drive, but I'm unsure if the depth goes further in Nancy. For now, I wiil try adding the validation of the path with the above and catch the error to notify people that they cannot use that directory and to use the -path parameter to override the path.

I opened this in Nancy's issue list: https://github.com/NancyFx/Nancy/issues/2146

toenuff avatar Dec 06 '15 03:12 toenuff

OK - We should be able to close this pending validation that @beatcracker can implement a workaround with -Path and that he now receives a better error message when not using -Path in a job (or changing directory in the job itself prior to calling new-flancy). Once confirmed, we'll close this, and then we'll implement asjob per Issue #25

toenuff avatar Dec 06 '15 04:12 toenuff

I'm able to run Flancy as job if I specify path without inaccessible directories and I receive a new error message if path contains such inaccessible directories.

But we have to do something about the validation code, because it's very slow - it tries to enumerate all directories/subdirectories in given path. In my case it takes 3 seconds to start Flancy as job when Path targets an empty directory and 25 seconds when Path is set to the drive root (+ heavy HDD activity).

beatcracker avatar Dec 06 '15 16:12 beatcracker

Well, we could opt for an easier way - I think it might be better anyway: Let's ensure that we are not in the root of a drive or that we are not in $env:userprofile\documents or $enf:userprofile\my documents. As long as path doesn't match those 3, chances are the person is in an ok directory. AND if they are not, they will get the normal access denied errors thanks to your update. Plus, we now have this documented in the parameter and we can update the examples and readme.md to call it out.

I think that's fair.

toenuff avatar Dec 06 '15 22:12 toenuff

Yes, I was thinking along those lines. In fact, I don't even think that we should block user from supplying those paths, just check and Write-Warning if they match.

beatcracker avatar Dec 06 '15 22:12 beatcracker

Just figured out why accessing My Musicgenerates exception in Nancy (of course, it's a permissions issue, but a curious one): https://github.com/NancyFx/Nancy/issues/2146#issuecomment-162368570

beatcracker avatar Dec 06 '15 23:12 beatcracker