module Stackctl.Spec.Capture
( CaptureOptions (..)
, parseCaptureOptions
, runCapture
) where
import Stackctl.Prelude
import Options.Applicative
import Stackctl.AWS
import Stackctl.AWS.Scope
import Stackctl.Config (HasConfig)
import Stackctl.DirectoryOption (HasDirectoryOption)
import Stackctl.Spec.Generate
import Stackctl.StackSpec
import System.FilePath.Glob
data CaptureOptions = CaptureOptions
{ CaptureOptions -> Maybe Text
scoAccountName :: Maybe Text
, CaptureOptions -> Maybe FilePath
scoTemplatePath :: Maybe FilePath
, CaptureOptions -> Maybe FilePath
scoStackPath :: Maybe FilePath
, CaptureOptions -> Maybe [StackName]
scoDepends :: Maybe [StackName]
, CaptureOptions -> TemplateFormat
scoTemplateFormat :: TemplateFormat
, CaptureOptions -> Pattern
scoStackName :: Pattern
}
parseCaptureOptions :: Parser CaptureOptions
parseCaptureOptions :: Parser CaptureOptions
parseCaptureOptions =
Maybe Text
-> Maybe FilePath
-> Maybe FilePath
-> Maybe [StackName]
-> TemplateFormat
-> Pattern
-> CaptureOptions
CaptureOptions
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall (f :: * -> *) a. Alternative f => f a -> f (Maybe a)
optional
( forall s. IsString s => Mod OptionFields s -> Parser s
strOption
( forall (f :: * -> *) a. HasName f => Char -> Mod f a
short Char
'n'
forall a. Semigroup a => a -> a -> a
<> forall (f :: * -> *) a. HasName f => FilePath -> Mod f a
long FilePath
"account-name"
forall a. Semigroup a => a -> a -> a
<> forall (f :: * -> *) a. HasMetavar f => FilePath -> Mod f a
metavar FilePath
"NAME"
forall a. Semigroup a => a -> a -> a
<> forall (f :: * -> *) a. FilePath -> Mod f a
help FilePath
"Account name to use in generated files"
)
)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall (f :: * -> *) a. Alternative f => f a -> f (Maybe a)
optional
( forall s. IsString s => Mod OptionFields s -> Parser s
strOption
( forall (f :: * -> *) a. HasName f => Char -> Mod f a
short Char
't'
forall a. Semigroup a => a -> a -> a
<> forall (f :: * -> *) a. HasName f => FilePath -> Mod f a
long FilePath
"template-path"
forall a. Semigroup a => a -> a -> a
<> forall (f :: * -> *) a. HasMetavar f => FilePath -> Mod f a
metavar FilePath
"PATH"
forall a. Semigroup a => a -> a -> a
<> forall (f :: * -> *) a. FilePath -> Mod f a
help FilePath
"Write Template to PATH. Default is based on STACK"
)
)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall (f :: * -> *) a. Alternative f => f a -> f (Maybe a)
optional
( forall s. IsString s => Mod OptionFields s -> Parser s
strOption
( forall (f :: * -> *) a. HasName f => Char -> Mod f a
short Char
'p'
forall a. Semigroup a => a -> a -> a
<> forall (f :: * -> *) a. HasName f => FilePath -> Mod f a
long FilePath
"path"
forall a. Semigroup a => a -> a -> a
<> forall (f :: * -> *) a. HasMetavar f => FilePath -> Mod f a
metavar FilePath
"PATH"
forall a. Semigroup a => a -> a -> a
<> forall (f :: * -> *) a. FilePath -> Mod f a
help FilePath
"Write specification to PATH. Default is based on STACK"
)
)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall (f :: * -> *) a. Alternative f => f a -> f (Maybe a)
optional
( forall (f :: * -> *) a. Alternative f => f a -> f [a]
some
( Text -> StackName
StackName
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> forall s. IsString s => Mod OptionFields s -> Parser s
strOption
( forall (f :: * -> *) a. HasName f => FilePath -> Mod f a
long FilePath
"depend"
forall a. Semigroup a => a -> a -> a
<> forall (f :: * -> *) a. HasMetavar f => FilePath -> Mod f a
metavar FilePath
"STACK"
forall a. Semigroup a => a -> a -> a
<> forall (f :: * -> *) a. FilePath -> Mod f a
help FilePath
"Add a dependency on STACK"
)
)
)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall a. a -> a -> Mod FlagFields a -> Parser a
flag
TemplateFormat
TemplateFormatYaml
TemplateFormat
TemplateFormatJson
( forall (f :: * -> *) a. HasName f => FilePath -> Mod f a
long FilePath
"no-flip"
forall a. Semigroup a => a -> a -> a
<> forall (f :: * -> *) a. FilePath -> Mod f a
help FilePath
"Don't flip JSON templates to Yaml"
)
forall (f :: * -> *) a b. Applicative f => f (a -> b) -> f a -> f b
<*> forall s. IsString s => Mod ArgumentFields s -> Parser s
strArgument
( forall (f :: * -> *) a. HasMetavar f => FilePath -> Mod f a
metavar FilePath
"STACK"
forall a. Semigroup a => a -> a -> a
<> forall (f :: * -> *) a. FilePath -> Mod f a
help FilePath
"Name of deployed Stack to capture"
)
runCapture
:: ( MonadMask m
, MonadUnliftIO m
, MonadResource m
, MonadLogger m
, MonadReader env m
, HasAwsScope env
, HasAwsEnv env
, HasConfig env
, HasDirectoryOption env
)
=> CaptureOptions
-> m ()
runCapture :: forall (m :: * -> *) env.
(MonadMask m, MonadUnliftIO m, MonadResource m, MonadLogger m,
MonadReader env m, HasAwsScope env, HasAwsEnv env, HasConfig env,
HasDirectoryOption env) =>
CaptureOptions -> m ()
runCapture CaptureOptions {Maybe FilePath
Maybe [StackName]
Maybe Text
Pattern
TemplateFormat
scoStackName :: Pattern
scoTemplateFormat :: TemplateFormat
scoDepends :: Maybe [StackName]
scoStackPath :: Maybe FilePath
scoTemplatePath :: Maybe FilePath
scoAccountName :: Maybe Text
scoStackName :: CaptureOptions -> Pattern
scoTemplateFormat :: CaptureOptions -> TemplateFormat
scoDepends :: CaptureOptions -> Maybe [StackName]
scoStackPath :: CaptureOptions -> Maybe FilePath
scoTemplatePath :: CaptureOptions -> Maybe FilePath
scoAccountName :: CaptureOptions -> Maybe Text
..} = do
let
setScopeName :: AwsScope -> AwsScope
setScopeName AwsScope
scope =
forall b a. b -> (a -> b) -> Maybe a -> b
maybe AwsScope
scope (\Text
name -> AwsScope
scope {awsAccountName :: Text
awsAccountName = Text
name}) Maybe Text
scoAccountName
generate' :: Stack -> Value -> Maybe FilePath -> Maybe FilePath -> m ()
generate' Stack
stack Value
template Maybe FilePath
path Maybe FilePath
templatePath = do
let
stackName :: StackName
stackName = Text -> StackName
StackName forall a b. (a -> b) -> a -> b
$ Stack
stack forall s a. s -> Getting a s a -> a
^. Lens' Stack Text
stack_stackName
templateBody :: TemplateBody
templateBody = Value -> TemplateBody
templateBodyFromValue Value
template
forall (f :: * -> *) a. Functor f => f a -> f ()
void
forall a b. (a -> b) -> a -> b
$ forall r (m :: * -> *) a. MonadReader r m => (r -> r) -> m a -> m a
local (forall env. HasAwsScope env => Lens' env AwsScope
awsScopeL forall s t a b. ASetter s t a b -> (a -> b) -> s -> t
%~ AwsScope -> AwsScope
setScopeName)
forall a b. (a -> b) -> a -> b
$ forall (m :: * -> *) env.
(MonadMask m, MonadUnliftIO m, MonadLogger m, MonadReader env m,
HasConfig env, HasAwsScope env, HasDirectoryOption env) =>
Generate -> m FilePath
generate
Generate
{ gDescription :: Maybe StackDescription
gDescription = Stack -> Maybe StackDescription
stackDescription Stack
stack
, gDepends :: Maybe [StackName]
gDepends = Maybe [StackName]
scoDepends
, gActions :: Maybe [Action]
gActions = forall a. Maybe a
Nothing
, gParameters :: Maybe [Parameter]
gParameters = Stack -> Maybe [Parameter]
parameters Stack
stack
, gCapabilities :: Maybe [Capability]
gCapabilities = Stack -> Maybe [Capability]
capabilities Stack
stack
, gTags :: Maybe [Tag]
gTags = Stack -> Maybe [Tag]
tags Stack
stack
, gSpec :: GenerateSpec
gSpec = case Maybe FilePath
path of
Maybe FilePath
Nothing -> StackName -> GenerateSpec
GenerateSpec StackName
stackName
Just FilePath
sp -> StackName -> FilePath -> GenerateSpec
GenerateSpecTo StackName
stackName FilePath
sp
, gTemplate :: GenerateTemplate
gTemplate = case Maybe FilePath
templatePath of
Maybe FilePath
Nothing -> TemplateBody -> TemplateFormat -> GenerateTemplate
GenerateTemplate TemplateBody
templateBody TemplateFormat
scoTemplateFormat
Just FilePath
tp -> TemplateBody -> FilePath -> GenerateTemplate
GenerateTemplateTo TemplateBody
templateBody FilePath
tp
, gOverwrite :: Bool
gOverwrite = Bool
False
}
[StackName]
results <- forall (m :: * -> *) env.
(MonadResource m, MonadReader env m, HasAwsEnv env) =>
Pattern -> m [StackName]
awsCloudFormationGetStackNamesMatching Pattern
scoStackName
case [StackName]
results of
[] -> do
forall (m :: * -> *).
(HasCallStack, MonadLogger m) =>
Message -> m ()
logError
forall a b. (a -> b) -> a -> b
$ Text
"No Active Stacks match "
forall a. Semigroup a => a -> a -> a
<> FilePath -> Text
pack (Pattern -> FilePath
decompile Pattern
scoStackName)
Text -> [SeriesElem] -> Message
:# []
forall (m :: * -> *) a. MonadIO m => m a
exitFailure
[StackName
stackName] -> do
Stack
stack <- forall (m :: * -> *) env.
(MonadResource m, MonadReader env m, HasAwsEnv env) =>
StackName -> m Stack
awsCloudFormationDescribeStack StackName
stackName
Value
template <- forall (m :: * -> *) env.
(MonadResource m, MonadReader env m, HasAwsEnv env) =>
StackName -> m Value
awsCloudFormationGetTemplate StackName
stackName
Stack -> Value -> Maybe FilePath -> Maybe FilePath -> m ()
generate' Stack
stack Value
template Maybe FilePath
scoStackPath Maybe FilePath
scoTemplatePath
[StackName]
stackNames -> do
forall (m :: * -> *).
(HasCallStack, MonadLogger m) =>
Message -> m ()
logInfo Message
"Capturing multiple matching Stacks"
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
t a -> (a -> f b) -> f ()
for_ Maybe FilePath
scoStackPath forall a b. (a -> b) -> a -> b
$ \FilePath
_ -> forall (m :: * -> *).
(HasCallStack, MonadLogger m) =>
Message -> m ()
logWarn Message
"--path option ignored"
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
t a -> (a -> f b) -> f ()
for_ Maybe FilePath
scoTemplatePath forall a b. (a -> b) -> a -> b
$ \FilePath
_ -> forall (m :: * -> *).
(HasCallStack, MonadLogger m) =>
Message -> m ()
logWarn Message
"--template-path option ignored"
forall (t :: * -> *) (f :: * -> *) a b.
(Foldable t, Applicative f) =>
t a -> (a -> f b) -> f ()
for_ [StackName]
stackNames forall a b. (a -> b) -> a -> b
$ \StackName
stackName -> do
Stack
stack <- forall (m :: * -> *) env.
(MonadResource m, MonadReader env m, HasAwsEnv env) =>
StackName -> m Stack
awsCloudFormationDescribeStack StackName
stackName
Value
template <- forall (m :: * -> *) env.
(MonadResource m, MonadReader env m, HasAwsEnv env) =>
StackName -> m Value
awsCloudFormationGetTemplate StackName
stackName
Stack -> Value -> Maybe FilePath -> Maybe FilePath -> m ()
generate' Stack
stack Value
template forall a. Maybe a
Nothing forall a. Maybe a
Nothing