stackctl 1.1.0.5 → 1.1.1.0
raw patch · 9 files changed
+101/−25 lines, 9 files
Files
- CHANGELOG.md +6/−1
- src/Stackctl/AWS/CloudFormation.hs +19/−1
- src/Stackctl/DirectoryOption.hs +1/−0
- src/Stackctl/ParameterOption.hs +25/−0
- src/Stackctl/Sort.hs +26/−0
- src/Stackctl/Spec/Changes.hs +6/−2
- src/Stackctl/Spec/Deploy.hs +8/−4
- src/Stackctl/StackSpec.hs +7/−16
- stackctl.cabal +3/−1
CHANGELOG.md view
@@ -1,4 +1,9 @@-## [_Unreleased_](https://github.com/freckle/stackctl/compare/v1.1.0.5...main)+## [_Unreleased_](https://github.com/freckle/stackctl/compare/v1.1.1.0...main)++## [v1.1.1.0](https://github.com/freckle/stackctl/compare/v1.1.0.5...v1.1.1.0)++- Add `--parameter` to `changes` and `deploy`+- Sort changes by causing-before-caused ## [v1.1.0.5](https://github.com/freckle/stackctl/compare/v1.1.0.4...v1.1.0.5)
src/Stackctl/AWS/CloudFormation.hs view
@@ -94,6 +94,7 @@ import qualified Data.UUID as UUID import qualified Data.UUID.V4 as UUID import Stackctl.AWS.Core+import Stackctl.Sort import UnliftIO.Exception.Lens (handling_, trying) newtype StackId = StackId@@ -375,7 +376,7 @@ awsSimple "DescribeChangeSet" req $ \resp -> ChangeSet <$> (resp ^. describeChangeSetResponse_creationTime)- <*> pure (resp ^. describeChangeSetResponse_changes)+ <*> pure (fmap sortChanges $ resp ^. describeChangeSetResponse_changes) <*> (ChangeSetName <$> resp ^. describeChangeSetResponse_changeSetName) <*> (resp ^. describeChangeSetResponse_executionStatus) <*> (ChangeSetId <$> resp ^. describeChangeSetResponse_changeSetId)@@ -387,6 +388,23 @@ <*> pure (resp ^. describeChangeSetResponse_status) <*> pure (resp ^. describeChangeSetResponse_statusReason) <*> pure resp++sortChanges :: [Change] -> [Change]+sortChanges = sortByDependencies changeName changeCausedBy++changeName :: Change -> Text+changeName c = fromMaybe "" $ do+ ResourceChange' {..} <- resourceChange c+ logicalResourceId++changeCausedBy :: Change -> [Text]+changeCausedBy c = fromMaybe [] $ do+ ResourceChange' {..} <- resourceChange c+ mapMaybe detailCausingLogicalResourceId <$> details++detailCausingLogicalResourceId :: ResourceChangeDetail -> Maybe Text+detailCausingLogicalResourceId ResourceChangeDetail' {..} =+ T.takeWhile (/= '.') <$> causingEntity awsCloudFormationExecuteChangeSet :: (MonadResource m, MonadReader env m, HasAwsEnv env) => ChangeSetId -> m ()
src/Stackctl/DirectoryOption.hs view
@@ -21,4 +21,5 @@ , help "Operate on specifications in PATH" , value "." , showDefault+ , action "directory" ]
+ src/Stackctl/ParameterOption.hs view
@@ -0,0 +1,25 @@+module Stackctl.ParameterOption+ ( parameterOption+ ) where++import Stackctl.Prelude++import qualified Data.Text as T+import Options.Applicative+import Stackctl.AWS.CloudFormation (Parameter, makeParameter)++parameterOption :: Parser Parameter+parameterOption = option (eitherReader readParameter) $ mconcat+ [ short 'p'+ , long "parameter"+ , metavar "KEY=[VALUE]"+ , help "Override the given Parameter for this operation"+ ]++readParameter :: String -> Either String Parameter+readParameter s = case T.breakOn "=" t of+ (_, v) | T.null v -> Left $ "No '=' found (" <> s <> ")"+ (k, _) | T.null k -> Left $ "Empty key (" <> s <> ")"+ (k, "=") -> Right $ makeParameter k $ Just ""+ (k, v) -> Right $ makeParameter k $ Just $ T.drop 1 v+ where t = pack s
+ src/Stackctl/Sort.hs view
@@ -0,0 +1,26 @@+module Stackctl.Sort+ ( sortByDependencies+ ) where++import Stackctl.Prelude++import Data.Graph (graphFromEdges, topSort)++sortByDependencies+ :: Ord k+ => (a -> k)+ -- ^ How to identify a given item+ -> (a -> [k])+ -- ^ How to get the given item's dependencies+ -> [a]+ -> [a]+sortByDependencies toName toDepends specs =+ map nodeFromVertex $ reverse $ topSort graph+ where+ (graph, tripleFromVertex, _) = graphFromEdges $ map tripleFromNode specs++ nodeFromVertex = nodeFromTriple . tripleFromVertex++ tripleFromNode n = (n, toName n, toDepends n)++ nodeFromTriple (n, _, _) = n
src/Stackctl/Spec/Changes.hs view
@@ -8,11 +8,12 @@ import qualified Data.Text.IO as T import Options.Applicative-import Stackctl.AWS+import Stackctl.AWS hiding (action) import Stackctl.AWS.Scope import Stackctl.Colors import Stackctl.DirectoryOption (HasDirectoryOption) import Stackctl.FilterOption (HasFilterOption)+import Stackctl.ParameterOption import Stackctl.Spec.Changes.Format import Stackctl.Spec.Discover import Stackctl.StackSpec@@ -20,6 +21,7 @@ data ChangesOptions = ChangesOptions { scoFormat :: Format+ , scoParameters :: [Parameter] , scoOutput :: FilePath } @@ -28,9 +30,11 @@ runChangesOptions :: Parser ChangesOptions runChangesOptions = ChangesOptions <$> formatOption+ <*> many parameterOption <*> argument str ( metavar "PATH" <> help "Where to write the changes summary"+ <> action "file" ) runChanges@@ -55,7 +59,7 @@ for_ specs $ \spec -> do withThreadContext ["stackName" .= stackSpecStackName spec] $ do- emChangeSet <- createChangeSet spec+ emChangeSet <- createChangeSet spec scoParameters case emChangeSet of Left err -> do
src/Stackctl/Spec/Deploy.hs view
@@ -11,12 +11,13 @@ import qualified Data.Text as T import Data.Time (defaultTimeLocale, formatTime, utcToLocalZonedTime) import Options.Applicative-import Stackctl.AWS+import Stackctl.AWS hiding (action) import Stackctl.AWS.Scope import Stackctl.Action import Stackctl.Colors import Stackctl.DirectoryOption (HasDirectoryOption) import Stackctl.FilterOption (HasFilterOption)+import Stackctl.ParameterOption import Stackctl.Prompt import Stackctl.Spec.Changes.Format import Stackctl.Spec.Discover@@ -25,7 +26,8 @@ import UnliftIO.Directory (createDirectoryIfMissing) data DeployOptions = DeployOptions- { sdoSaveChangeSets :: Maybe FilePath+ { sdoParameters :: [Parameter]+ , sdoSaveChangeSets :: Maybe FilePath , sdoDeployConfirmation :: DeployConfirmation , sdoClean :: Bool }@@ -34,10 +36,12 @@ runDeployOptions :: Parser DeployOptions runDeployOptions = DeployOptions- <$> optional (strOption+ <$> many parameterOption+ <*> optional (strOption ( long "save-change-sets" <> metavar "DIRECTORY" <> help "Save executed changesets to DIRECTORY"+ <> action "directory" )) <*> flag DeployWithConfirmation DeployWithoutConfirmation ( long "no-confirm"@@ -70,7 +74,7 @@ withThreadContext ["stackName" .= stackSpecStackName spec] $ do handleRollbackComplete sdoDeployConfirmation $ stackSpecStackName spec - emChangeSet <- createChangeSet spec+ emChangeSet <- createChangeSet spec sdoParameters case emChangeSet of Left err -> do
src/Stackctl/StackSpec.hs view
@@ -18,10 +18,11 @@ import qualified CfnFlip import Data.Aeson-import Data.Graph (graphFromEdges, topSort)+import Data.List.Extra (nubOrdOn) import qualified Data.Yaml as Yaml import Stackctl.AWS import Stackctl.Action+import Stackctl.Sort import Stackctl.StackSpecPath import Stackctl.StackSpecYaml import UnliftIO.Directory (createDirectoryIfMissing)@@ -99,10 +100,6 @@ throwString $ path <> " is invalid: " <> Yaml.prettyPrintParseException e -- | Create a Change Set between a Stack Specification and deployed state------ We use @aws cloudformation deploy --no-execute-changeset@ because it handles--- the create-or-update logic for us.--- createChangeSet :: ( MonadUnliftIO m , MonadResource m@@ -111,21 +108,15 @@ , HasAwsEnv env ) => StackSpec+ -> [Parameter] -> m (Either Text (Maybe ChangeSet))-createChangeSet spec = awsCloudFormationCreateChangeSet+createChangeSet spec parameters = awsCloudFormationCreateChangeSet (stackSpecStackName spec) (stackSpecTemplateFile spec)- (stackSpecParameters spec)+ (nubOrdOn (^. parameter_parameterKey) $ parameters <> stackSpecParameters spec+ ) (stackSpecCapabilities spec) (stackSpecTags spec) sortStackSpecs :: [StackSpec] -> [StackSpec]-sortStackSpecs specs = map nodeFromVertex $ reverse $ topSort graph- where- (graph, tripleFromVertex, _) = graphFromEdges $ map tripleFromNode specs-- nodeFromVertex = nodeFromTriple . tripleFromVertex-- tripleFromNode n = (n, stackSpecStackName n, stackSpecDepends n)-- nodeFromTriple (n, _, _) = n+sortStackSpecs = sortByDependencies stackSpecStackName stackSpecDepends
stackctl.cabal view
@@ -1,6 +1,6 @@ cabal-version: 1.18 name: stackctl-version: 1.1.0.5+version: 1.1.1.0 license: MIT license-file: LICENSE copyright: 2022 Renaissance Learning Inc@@ -36,8 +36,10 @@ Stackctl.DirectoryOption Stackctl.FilterOption Stackctl.Options+ Stackctl.ParameterOption Stackctl.Prelude Stackctl.Prompt+ Stackctl.Sort Stackctl.Spec.Capture Stackctl.Spec.Cat Stackctl.Spec.Changes