module Lib where import Types import JSONInstances import AWSCommands import TreeUtils import Data.Tree import Data.Tree.Pretty import Data.List import Data.Maybe import Data.Aeson showDeletionPlan :: String -> IO () showDeletionPlan stackName = do putStrLn $ "Retrieving dependencies of " ++ stackName ++ "..." tree <- buildDependencyGraph (StackName stackName) putStrLn "Done. Delete these stacks in postorder traversal:\n" putStrLn $ drawTree (dependencyToTree tree) putStrLn "Or, delete manually in this order:\n" mapM_ putStrLn $ postorder (dependencyToTree tree) putStrLn "\nIf you trust this app you can execute:" putStrLn $ "forest-fire \"" ++ stackName ++ "\" --delete\n" actuallyDoTheDelete :: String -> IO () actuallyDoTheDelete stackName = do putStrLn $ "Retrieving dependencies of " ++ stackName ++ "..." tree <- buildDependencyGraph (StackName stackName) putStrLn "Deleting dependencies and stack..." mapM_ (doDeletionWait . StackName) $ postorder (dependencyToTree tree) findExportsByStack :: StackName -> IO [ExportName] findExportsByStack s = do json <- eitherDecode <$> jsonForDescribeStacks s either error (pure . map eName . concatMap sExports . sStacks) json whoImportsThisValue :: ExportName -> IO [StackName] whoImportsThisValue e = do json <- eitherDecode <$> jsonForListImports e :: IO (Either String Imports) either (const (pure [])) (pure . iStackNames) json buildDependencyGraph :: StackName -> IO Dependency buildDependencyGraph = buildDependencyGraph' [] -- the empty list is because we haven't yet queried any stacks where buildDependencyGraph' :: [StackName] -> StackName -> IO Dependency buildDependencyGraph' alreadySeen name = do outputs <- findExportsByStack name importers <- mapM whoImportsThisValue outputs let downstreams = nub $ filter (`notElem` alreadySeen) $ concat importers downstreamDeps <- mapM (buildDependencyGraph' (alreadySeen ++ downstreams)) downstreams pure $ Dependency name downstreamDeps