stackctl 1.3.0.2 → 1.4.0.0
raw patch · 6 files changed
+110/−41 lines, 6 files
Files
- CHANGELOG.md +7/−1
- src/Stackctl/AWS/Core.hs +27/−0
- src/Stackctl/Spec/Capture.hs +13/−10
- src/Stackctl/Spec/Generate.hs +38/−23
- src/Stackctl/StackSpec.hs +24/−6
- stackctl.cabal +1/−1
CHANGELOG.md view
@@ -1,4 +1,10 @@-## [_Unreleased_](https://github.com/freckle/stackctl/compare/v1.3.0.2...main)+## [_Unreleased_](https://github.com/freckle/stackctl/compare/v1.4.0.0...main)++## [v1.4.0.0](https://github.com/freckle/stackctl/compare/v1.3.0.2...v1.4.0.0)++- Add `awsAssumeRole` for running an action as an assumed role+- Refactor `Generate` interface to better support generating stacks with+ pre-existing templates ## [v1.3.0.2](https://github.com/freckle/stackctl/compare/v1.3.0.1...v1.3.0.2)
src/Stackctl/AWS/Core.hs view
@@ -6,6 +6,7 @@ , awsSend , awsPaginate , awsAwait+ , awsAssumeRole -- * Modifiers on 'AwsEnv' , awsWithin@@ -25,6 +26,8 @@ import Amazonka hiding (LogLevel(..)) import qualified Amazonka as AWS+import Amazonka.Auth.Keys (fromSession)+import Amazonka.STS.AssumeRole import Conduit (ConduitM) import Control.Monad.Logger (defaultLoc, toLogStr) import Control.Monad.Trans.Resource (MonadResource)@@ -103,6 +106,30 @@ awsAwait w req = do AwsEnv env <- view awsEnvL await env w req++awsAssumeRole+ :: (MonadResource m, MonadReader env m, HasAwsEnv env)+ => Text+ -- ^ Role ARN+ -> Text+ -- ^ Session name+ -> m a+ -- ^ Action to run as the assumed role+ -> m a+awsAssumeRole role sessionName f = do+ let req = newAssumeRole role sessionName++ assumeEnv <- awsSimple "sts:AssumeRole" req $ \resp -> do+ creds <- resp ^. assumeRoleResponse_credentials+ token <- creds ^. authSessionToken++ let+ accessKeyId = creds ^. authAccessKeyId+ secretAccessKey = creds ^. authSecretAccessKey++ pure $ fromSession accessKeyId secretAccessKey token++ local (awsEnvL . unL %~ assumeEnv) f awsWithin :: (MonadReader env m, HasAwsEnv env) => Region -> m a -> m a awsWithin r = local $ over (awsEnvL . unL) (within r)
src/Stackctl/Spec/Capture.hs view
@@ -10,7 +10,7 @@ import Stackctl.AWS import Stackctl.AWS.Scope import Stackctl.Config (HasConfig)-import Stackctl.DirectoryOption (HasDirectoryOption(..), unDirectoryOption)+import Stackctl.DirectoryOption (HasDirectoryOption) import Stackctl.Spec.Generate import Stackctl.StackSpec import System.FilePath.Glob@@ -74,26 +74,29 @@ => CaptureOptions -> m () runCapture CaptureOptions {..} = do- dir <- unDirectoryOption <$> view directoryOptionL- let setScopeName scope = maybe scope (\name -> scope { awsAccountName = name }) scoAccountName generate' stack template path templatePath = do+ let+ stackName = StackName $ stack ^. stack_stackName+ templateBody = templateBodyFromValue template+ void $ local (awsScopeL %~ setScopeName) $ generate Generate- { gOutputDirectory = dir- , gTemplatePath = templatePath- , gTemplateFormat = scoTemplateFormat- , gStackPath = path- , gStackName = StackName $ stack ^. stack_stackName- , gDescription = stackDescription stack+ { gDescription = stackDescription stack , gDepends = scoDepends , gActions = Nothing , gParameters = parameters stack , gCapabilities = capabilities stack , gTags = tags stack- , gTemplateBody = templateBodyFromValue template+ , gSpec = case path of+ Nothing -> GenerateSpec stackName+ Just sp -> GenerateSpecTo stackName sp+ , gTemplate = case templatePath of+ Nothing -> GenerateTemplate templateBody scoTemplateFormat+ Just tp -> GenerateTemplateTo templateBody tp+ , gOverwrite = False } results <- awsCloudFormationGetStackNamesMatching scoStackName
src/Stackctl/Spec/Generate.hs view
@@ -1,5 +1,7 @@ module Stackctl.Spec.Generate ( Generate(..)+ , GenerateSpec(..)+ , GenerateTemplate(..) , generate , TemplateFormat(..) ) where@@ -10,29 +12,38 @@ import Stackctl.AWS import Stackctl.AWS.Scope import Stackctl.Config (HasConfig)+import Stackctl.DirectoryOption import Stackctl.Spec.Discover (buildSpecPath) import Stackctl.StackSpec import Stackctl.StackSpecPath import Stackctl.StackSpecYaml data Generate = Generate- { gOutputDirectory :: FilePath- , gTemplatePath :: Maybe FilePath- -- ^ If not given, will use @{stack-name}.(yaml|json)@- , gTemplateFormat :: TemplateFormat- -- ^ Ignored if 'gTemplatePath' is given- , gStackPath :: Maybe FilePath- -- ^ If not given, will use @{stack-name}.yaml@- , gStackName :: StackName- , gDescription :: Maybe StackDescription+ { gDescription :: Maybe StackDescription , gDepends :: Maybe [StackName] , gActions :: Maybe [Action] , gParameters :: Maybe [Parameter] , gCapabilities :: Maybe [Capability] , gTags :: Maybe [Tag]- , gTemplateBody :: TemplateBody+ , gSpec :: GenerateSpec+ , gTemplate :: GenerateTemplate+ , gOverwrite :: Bool } +data GenerateSpec+ = GenerateSpec StackName+ -- ^ Generate at an inferred name+ | GenerateSpecTo StackName FilePath+ -- ^ Generate to a given path++data GenerateTemplate+ = GenerateTemplate TemplateBody TemplateFormat+ -- ^ Generate at an inferred name+ | GenerateTemplateTo TemplateBody FilePath+ -- ^ Generate to the given path+ | UseExistingTemplate FilePath+ -- ^ Assume template exists+ data TemplateFormat = TemplateFormatYaml | TemplateFormatJson@@ -44,23 +55,26 @@ , MonadReader env m , HasConfig env , HasAwsScope env+ , HasDirectoryOption env ) => Generate -> m FilePath generate Generate {..} = do let- defaultStackPath = unpack (unStackName gStackName) <.> "yaml"- defaultTemplatePath =- unpack (unStackName gStackName) <.> case gTemplateFormat of- TemplateFormatYaml -> "yaml"- TemplateFormatJson -> "json"-- stackPath = fromMaybe defaultStackPath gStackPath+ (stackName, stackPath) = case gSpec of+ GenerateSpec name -> (name, unpack (unStackName name) <> ".yaml")+ GenerateSpecTo name path -> (name, path) - specPath <- buildSpecPath gStackName stackPath+ (mTemplateBody, templatePath) = case gTemplate of+ GenerateTemplate body format ->+ ( Just body+ , case format of+ TemplateFormatYaml -> unpack (unStackName stackName) <> ".yaml"+ TemplateFormatJson -> unpack (unStackName stackName) <> ".json"+ )+ GenerateTemplateTo body path -> (Just body, path)+ UseExistingTemplate path -> (Nothing, path) - let- templatePath = fromMaybe defaultTemplatePath gTemplatePath specYaml = StackSpecYaml { ssyDescription = gDescription , ssyTemplate = templatePath@@ -71,9 +85,10 @@ , ssyTags = tagsYaml . map TagYaml <$> gTags } - stackSpec <- buildStackSpec gOutputDirectory specPath specYaml+ dir <- view $ directoryOptionL . to unDirectoryOption+ specPath <- buildSpecPath stackName stackPath+ stackSpec <- buildStackSpec dir specPath specYaml withThreadContext ["stackName" .= stackSpecStackName stackSpec] $ do- logInfo "Generating specification"- writeStackSpec stackSpec gTemplateBody+ writeStackSpec gOverwrite stackSpec mTemplateBody pure $ stackSpecPathFilePath specPath
src/Stackctl/StackSpec.hs view
@@ -34,7 +34,7 @@ import Stackctl.StackSpecYaml import qualified System.FilePath as FilePath import System.FilePath (takeExtension)-import UnliftIO.Directory (createDirectoryIfMissing)+import UnliftIO.Directory (createDirectoryIfMissing, doesFileExist) data StackSpec = StackSpec { ssSpecRoot :: FilePath@@ -135,11 +135,29 @@ dir = takeDirectory path ext = takeExtension path -writeStackSpec :: MonadUnliftIO m => StackSpec -> TemplateBody -> m ()-writeStackSpec stackSpec templateBody = do- writeTemplateBody templatePath templateBody- createDirectoryIfMissing True $ takeDirectory specPath- liftIO $ Yaml.encodeFile specPath $ stackSpecSpecBody stackSpec+writeStackSpec+ :: (MonadUnliftIO m, MonadLogger m)+ => Bool+ -> StackSpec+ -> Maybe TemplateBody+ -> m ()+writeStackSpec overwrite stackSpec mTemplateBody = do+ for_ mTemplateBody $ \templateBody -> do+ logInfo $ "Writing template" :# ["path" .= templatePath]+ writeTemplateBody templatePath templateBody++ exists <- doesFileExist specPath++ if exists && not overwrite+ then do+ let+ reason :: Text+ reason = "file exists and overwrite not set"+ logInfo $ "Skipping" :# ["path" .= specPath, "reason" .= reason]+ else do+ logInfo $ "Writing specification" :# ["path" .= specPath]+ createDirectoryIfMissing True $ takeDirectory specPath+ liftIO $ Yaml.encodeFile specPath $ stackSpecSpecBody stackSpec where templatePath = unStackTemplate $ stackSpecTemplate stackSpec specPath =
stackctl.cabal view
@@ -1,6 +1,6 @@ cabal-version: 1.18 name: stackctl-version: 1.3.0.2+version: 1.4.0.0 license: MIT license-file: LICENSE copyright: 2022 Renaissance Learning Inc