actionFinally not running cmd invoked inside handler - haskell

I have the following code fragment:
withContainer :: String -> (String -> Action a) -> Action a
cid <= cmd "docker" [ "run" , "--rm", "-d", my_image ]
...
actionFinally (action containerName) $ do
cmd_ "docker" [ "rm", "-f", filter (/= '\n') cid ]
which supposedly kill containers whether or not the action succeeds. However I noticed containers are still left up and running when the action fails, which is annoying. What am I doing wrong?

This sample of code looks correct, and testing variations of it in isolation does indeed work. A bug has been raised to figure out the precise reason it doesn't work in your case at https://github.com/ndmitchell/shake/issues/731.

Related

Use CMD inside Python

Good evening everyone, this is the first time I post here.
I have many codes I need to run in CMD but I'm not good at coding with CMD, so I want to use python to modify the code that I have to repeat often
for example
temp1 = ["C:/dir1", "C:/dir2", "C:/dir3"]
temp2 = ["1", "2", "3"]
for i, p in zip(temp1, temp2):
os.system('7z -a "{}" C:/target/output"{}" -m5'.format(i, p))
With my code, I also need to see the Shell prompt, because some of the codes I do show a very long terminal data output that I want to monitor actively, I prefer to see it in the python terminal as an output, or print or something similar.
Thanks in advance, it is my first post hope it's clear.
os.system() implicitly uses cmd.exe on Windows (by passing the specified command line to cmd /c) and implicitly passes the command's output through to stdout.
Therefore, your command should work in principle, though I recommend double-quoting the target path in full ("C:/target/output{}"), not just the substituted part (C:/target/output"{}")[1]:
import os
temp1 = ["C:/dir1", "C:/dir2", "C:/dir3"]
temp2 = ["1", "2", "3"]
for i, p in zip(temp1, temp2):
os.system('7z -a "{}" "C:/target/output{}" -m5'.format(i, p))
If you're running this code from a GUI script (a script invoked via pythonw.exe / pyw.exe), the console window created for each os.system() call automatically closes when the command finishes, which doesn't allow you to inspect the output after the fact. To address this, you have two options:
Append & pause to the command, which waits for keypress before closing the console window:
import os
temp1 = ["C:/dir1", "C:/dir2", "C:/dir3"]
temp2 = ["1", "2", "3"]
for i, p in zip(temp1, temp2):
os.system('7z -a "{}" "C:/target/output{}" -m5 & pause'.format(i, p))
Invoke your command via cmd /k, which starts an interactive cmd.exe session that stays open after the command finishes; note that each such session then blocks further execution of your Python script until you manually close the console window in which the persistent cmd.exe session runs.
import os
temp1 = ["C:/dir1", "C:/dir2", "C:/dir3"]
temp2 = ["1", "2", "3"]
for i, p in zip(temp1, temp2):
os.system('cmd /k " 7z -a "{}" "C:/target/output{}" -m5 "'.format(i, p))
Note the unusual quoting (an outer "..." string in which embedded " aren't escaped), which is what cmd.exe supports, however.
[1] Typically, this shouldn't make a difference, but you never know how programs parse their command line on Windows, and double-quoting an argument in full is more likely to work.

Getting no response from ProcessBuilder's input stream in some specific case

So I am trying to get battery status from linux, and so far the first command (path variable) returns perfectly and I am able to get its response in form of Sequence from the input stream, but unfortunately the second command (of result variable) returns empty sequence.
fun getLinuxBatteryStatus(): Nothing? {
val path = """upower --enumerate""".runCommand() ?: return null
val parameters = listOf("present", "state", "energy-full", "energy", "energy-rate", "time to empty", "percentage")
val result = """upower -i ${path.first { "battery_BAT" in it }} | grep -E "${parameters.joinToString("|")}""""
.also { println(it) }
.runCommand() ?: return null
result.forEach(::println) // <- no ouput
// println(result.count()) // <- 0
/* Do other thing and return something (that is not related to problem) */
}
Ouput:
upower -i /org/freedesktop/UPower/devices/battery_BAT1 | grep -E "present|state|energy-full|energy|energy-rate|time to empty|percentage"
The above output is from the also block in the last command, just to preview the command's string for debugging. And if I run the above command directly into the terminal I am successfully getting the responses as follows:
present: yes
state: charging
energy: 47.903 Wh
energy-empty: 0 Wh
energy-full: 50.299 Wh
energy-full-design: 48.004 Wh
energy-rate: 17.764 W
percentage: 95%
Why is the last command not working (not returning any response) with the ProcessBuilder?
Note: the extension function runCommand has been taken from here
private fun String.runCommand(
workingDir: File = File("."),
timeoutAmount: Long = 60,
timeoutUnit: TimeUnit = TimeUnit.SECONDS
): Sequence<String>? = try {
ProcessBuilder(split("\\s".toRegex()))
.directory(workingDir)
.redirectOutput(ProcessBuilder.Redirect.PIPE)
.redirectError(ProcessBuilder.Redirect.PIPE)
.start()
.apply { waitFor(timeoutAmount, timeoutUnit) }
.inputStream.bufferedReader().lineSequence()
} catch (e: IOException) {
e.printStackTrace()
null
}
The problem here is the pipe.
You're trying to run a pipeline — a construction involving running multiple programs, that needs a shell to interpret.
But ProcessBuilder runs a single program.  In this case, it's running the program upower and passing it the parameters -i, /org/freedesktop/UPower/devices/battery_BAT1, |, grep, -E, and "present|state|energy-full|energy|energy-rate|time to empty|percentage".  Obviously upower won't know what to do with the | parameter or those after it.
You could use ProcessBuilder to run up a shell instance, which could then run your pipeline; see this answer.
But it would probably be simpler, safer, and more efficient to do the filtering in your own code, and avoid calling grep entirely.
I recommend capturing the process's error output, which would very probably have made the problem clear.

Snap framework - Restrict access to the whole website including its subsnaplets

I have a simple snaplet, which has its own routing and pretty much independent of the whole application behavior. However, as for most of the application, I want to hide the new simple snaplet under the restricted area, where only logged in users can enter.
For the root snaplet the problem solved by using simple function restricted which accepts a handler and checks if the user logged in, proceeding further with the given handler or redirecting to the login screen.
Here is the whole configuration:
appInit :: SnapletInit App App
appInit = makeSnaplet "myapp" "My Example Application" Nothing $ do
fs <- nestSnaplet "foo" foo fooInit
ss <- nestSnaplet "sess" sess $
initCookieSessionManager "site_key.txt" "sess" (Just 3600)
as <- nestSnaplet "auth" auth $
initJsonFileAuthManager defAuthSettings sess "users.json"
addRoutes [("content", restricted $ render "content"),
("login", login)]
return $ App ss as fs
restricted :: Handler App App () -> Handler App App ()
restricted = requireUser auth (redirect "/login")
fooInit :: SnapletInit b Foo
fooInit = makeSnaplet "foo" "A nested snaplet" Nothing $ do
addRoutes [("info", writeText "Only registered users can have acess to it")]
return Foo
If I enter http://mywebsite/foo/info, I will be able to see the content of the subsnaplet without logging it. It seems to me, that I cannot protect all of the handlers implemented inside of my new Foo without changing that snaplet and modifying its routing. Or am I wrong?
P.S.: There is an option to use weapSite and check the request URL, but since it implies verification based on URL, not on the recourse, (handler in this case) it doesn't seem right to me.
The answer here is to use the wrapSite function. It takes an argument (Handler b v () -> Handler b v ()), which is exactly the type signature of your restricted function.

"Could not find module ‘Test.HUnit’" Error when executing Haskell's unittest (HUnit) in CodeRunner

I have simple unit test code for Haskell's HUnit. I use Mac OS X 10.10, and I installed HUnit with cabal install hunit.
module TestSafePrelude where
import SafePrelude( safeHead )
import Test.HUnit
testSafeHeadForEmptyList :: Test
testSafeHeadForEmptyList =
TestCase $ assertEqual "Should return Nothing for empty list"
Nothing (safeHead ([]::[Int]))
testSafeHeadForNonEmptyList :: Test
testSafeHeadForNonEmptyList =
TestCase $ assertEqual "Should return (Just head) for non empty list" (Just 1)
(safeHead ([1]::[Int]))
main :: IO Counts
main = runTestTT $ TestList [testSafeHeadForEmptyList, testSafeHeadForNonEmptyList]
I can execute it with runhaskell TestSafePrelude.hs to get the results:
Cases: 2 Tried: 2 Errors: 0 Failures: 0
Counts {cases = 2, tried = 2, errors = 0, failures = 0}
However, when I run it in Code Runner, I have error message that can't find the HUnit module.
CodeRunner launches the test on a different shell environment, and this seems to be the issue. If so, what environment variables need to be added? If not, what might be causing the problem?
I also find that ghc-pkg list from the CodeRunner does not search for the directories in ~/.ghc which contains the HUnit.
/usr/local/Cellar/ghc/7.8.3/lib/ghc-7.8.3/package.conf.d:
Cabal-1.18.1.4
array-0.5.0.0
...
xhtml-3000.2.1
This is the results when executed in shell:
/usr/local/Cellar/ghc/7.8.3/lib/ghc-7.8.3/package.conf.d
Cabal-1.18.1.4
array-0.5.0.0
...
/Users/smcho/.ghc/x86_64-darwin-7.8.3/package.conf.d
...
HUnit-1.2.5.2
...
zlib-0.5.4.2
I added both ~/.cabal and ~/.ghc in the path, but it doesn't work.
The problem was the $HOME setup change. I used different $HOME for CodeRunner, but Haskell searches for $HOME/.cabal and $HOME/.ghc for installed package.
After my resetting $HOME to correct location, everything works fine.

Extending command line options with Haskell Snap

I have an acid-state backend that complements my snap website. It is running in its own process and my snap web server requires an IP address to connect to it. For debugging and deployment purposes I would like to be able to pass in the IP address as a command line argument when running my compiled snap application. This IP address would be accessible inside the SnapletInit monad where the acid state handler gets called.
How can I extend the command line parameter system in Snap to account for this?
Ideally, I'd like something like.
./app -ip 192.168.0.2 -p 8080 -e prod +RTS -I0 -A4M -qg1
Then apply it like this.
app :: SnapletInit App App
app = makeSnaplet "app" "Snapplication" Nothing $ do
ip <- getConfig "ip"
d <- nestSnaplet "acid" acid $ acidInitRemote ip
return $ App d
I would recommend changing the Acid State snaplet to read it's IP from a config instead of the command line. The configs in Snap are set up so that it'll load whatever you pass as the -e argument on the command line. For example, starting with -e prod will load snaplet/acidstate/prod.conf and starting with no -e or -e devel will load snaplet/acidstate/devel.conf. This helps keep all your environmental settings together instead of allowing any possible combination of command line flags.
Here's an example from one of my snaplets:
initStripe :: SnapletInit b StripeState
initStripe = makeSnaplet "stripe" "Stripe credit card payment" Nothing $ do
config <- getSnapletUserConfig
(stripeState, errors) <- runWriterT $ do
secretKey <- logErr "Must specify Strip secret key" $ C.lookup config "secret_key"
publicKey <- logErr "Must specify Strip public key" $ C.lookup config "public_key"
clientId <- logErr "Must specify Strip client ID" $ C.lookup config "client_id"
version <- Just . maybe V20110915d OtherVersion <$> liftIO (C.lookup config "version")
let caFilePath = Just "" -- This is unused by Stripe but vestigial in the Haskell library.
return $ StripeState <$> (StripeConfig <$> (SecretKey <$> secretKey) <*> caFilePath <*> version) <*> (PublicKey <$> publicKey) <*> clientId
return $ fromMaybe (error $ intercalate "\n" errors) stripeState
Check out snap-server's Config module. Specifically, extendedCommandLineConfig.
You could use getEnv, lookupEnv or getArgs from System.Environment
Personally, I'd go with the ENV variables approach.

Resources