packages feed

stackctl 1.6.1.2 → 1.7.0.0

raw patch · 6 files changed

+128/−91 lines, 6 files

Files

CHANGELOG.md view
@@ -1,4 +1,13 @@-## [_Unreleased_](https://github.com/freckle/stackctl/compare/v1.6.1.2...main)+## [_Unreleased_](https://github.com/freckle/stackctl/compare/v1.7.0.0...main)++## [v1.7.0.0](https://github.com/freckle/stackctl/compare/v1.6.1.2...v1.7.0.0)++- Retain numeric parameter types, and add boolean parameter handling, in our own+  yaml generation++  This required changing the `Generate` to use `ParametersYaml` instead of+  `[Parameter]`, and ultimately led us to removing it, hence the major version+  bump.  ## [v1.6.1.2](https://github.com/freckle/stackctl/compare/v1.6.1.1...v1.6.1.2) 
src/Stackctl/Spec/Capture.hs view
@@ -13,6 +13,13 @@ import Stackctl.DirectoryOption (HasDirectoryOption) import Stackctl.Spec.Generate import Stackctl.StackSpec+import Stackctl.StackSpecYaml+  ( StackSpecYaml (..)+  , TagYaml (..)+  , parameterYaml+  , parametersYaml+  , tagsYaml+  ) import System.FilePath.Glob  data CaptureOptions = CaptureOptions@@ -91,7 +98,7 @@     setScopeName scope =       maybe scope (\name -> scope {awsAccountName = name}) scoAccountName -    generate' stack template path templatePath = do+    generate' stack template mPath mTemplatePath = do       let         stackName = StackName $ stack ^. stack_stackName         templateBody = templateBodyFromValue template@@ -99,21 +106,26 @@       void         $ local (awsScopeL %~ setScopeName)         $ generate-          Generate-            { gDescription = stackDescription stack-            , gDepends = scoDepends-            , gActions = Nothing-            , gParameters = parameters stack-            , gCapabilities = capabilities stack-            , gTags = tags stack-            , 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-            }+          False+          ( case mPath of+              Nothing -> GenerateSpec stackName+              Just sp -> GenerateSpecTo stackName sp+          )+          ( case mTemplatePath of+              Nothing -> GenerateTemplate templateBody scoTemplateFormat+              Just tp -> GenerateTemplateTo templateBody tp+          )+          ( \templatePath ->+              StackSpecYaml+                { ssyDescription = stackDescription stack+                , ssyTemplate = templatePath+                , ssyDepends = scoDepends+                , ssyActions = Nothing+                , ssyParameters = parametersYaml . mapMaybe parameterYaml <$> parameters stack+                , ssyCapabilities = capabilities stack+                , ssyTags = tagsYaml . map TagYaml <$> tags stack+                }+          )    results <- awsCloudFormationGetStackNamesMatching scoStackName 
src/Stackctl/Spec/Generate.hs view
@@ -1,6 +1,5 @@ module Stackctl.Spec.Generate-  ( Generate (..)-  , GenerateSpec (..)+  ( GenerateSpec (..)   , GenerateTemplate (..)   , generate   , TemplateFormat (..)@@ -10,7 +9,6 @@  import Stackctl.AWS import Stackctl.AWS.Scope-import Stackctl.Action import Stackctl.Config (HasConfig) import Stackctl.DirectoryOption import Stackctl.Spec.Discover (buildSpecPath)@@ -18,18 +16,6 @@ import Stackctl.StackSpecPath import Stackctl.StackSpecYaml -data Generate = Generate-  { gDescription :: Maybe StackDescription-  , gDepends :: Maybe [StackName]-  , gActions :: Maybe [Action]-  , gParameters :: Maybe [Parameter]-  , gCapabilities :: Maybe [Capability]-  , gTags :: Maybe [Tag]-  , gSpec :: GenerateSpec-  , gTemplate :: GenerateTemplate-  , gOverwrite :: Bool-  }- data GenerateSpec   = -- | Generate at an inferred name     GenerateSpec StackName@@ -57,15 +43,18 @@      , HasAwsScope env      , HasDirectoryOption env      )-  => Generate+  => Bool+  -> GenerateSpec+  -> GenerateTemplate+  -> (FilePath -> StackSpecYaml)   -> m FilePath-generate Generate {..} = do+generate overwrite spec template toStackSpecYaml = do   let-    (stackName, stackPath) = case gSpec of+    (stackName, stackPath) = case spec of       GenerateSpec name -> (name, unpack (unStackName name) <> ".yaml")       GenerateSpecTo name path -> (name, path) -    (mTemplateBody, templatePath) = case gTemplate of+    (mTemplateBody, templatePath) = case template of       GenerateTemplate body format ->         ( Just body         , case format of@@ -75,21 +64,12 @@       GenerateTemplateTo body path -> (Just body, path)       UseExistingTemplate path -> (Nothing, path) -    specYaml =-      StackSpecYaml-        { ssyDescription = gDescription-        , ssyTemplate = templatePath-        , ssyDepends = gDepends-        , ssyActions = gActions-        , ssyParameters = parametersYaml . mapMaybe parameterYaml <$> gParameters-        , ssyCapabilities = gCapabilities-        , ssyTags = tagsYaml . map TagYaml <$> gTags-        }+    specYaml = toStackSpecYaml templatePath    dir <- view $ directoryOptionL . to unDirectoryOption   specPath <- buildSpecPath stackName stackPath   stackSpec <- buildStackSpec dir specPath specYaml    withThreadContext ["stackName" .= stackSpecStackName stackSpec] $ do-    writeStackSpec gOverwrite stackSpec mTemplateBody+    writeStackSpec overwrite stackSpec mTemplateBody     pure $ stackSpecPathFilePath specPath
src/Stackctl/StackSpecYaml.hs view
@@ -4,18 +4,24 @@ -- Template: <path> -- -- Depends:--- - <string>+--   - <string> -- -- Parameters:--- - ParameterKey: <string>---   ParameterValue: <string>+--   - ParameterKey: <string>+--     ParameterValue: <string|number|boolean> --+--   # Or+--   <key>: <string|number|boolean>+-- -- Capabilities:--- - <capability>+--   - <capability> -- -- Tags:--- - Key: <string>---   Value: <string>+--   - Key: <string>+--     Value: <string>+--+--   # Or+--   <key>: <string> -- @ module Stackctl.StackSpecYaml   ( StackSpecYaml (..)@@ -24,7 +30,11 @@   , unParametersYaml   , ParameterYaml   , parameterYaml+  , mkParameterYaml   , unParameterYaml+  , ParameterValue+  , parameterValueFromText+  , parameterValueTemplate   , TagsYaml   , tagsYaml   , unTagsYaml@@ -39,6 +49,7 @@ import qualified Data.Aeson.KeyMap as KeyMap import Data.Aeson.Types (typeMismatch) import qualified Data.HashMap.Strict as HashMap+import Data.List.Extra (dropSuffix) import Data.Monoid (Last (..)) import qualified Data.Text as T import Stackctl.AWS@@ -117,31 +128,81 @@ parameterYamlPair :: KeyValue kv => ParameterYaml -> kv parameterYamlPair ParameterYaml {..} = pyKey .= pyValue -mkParameterYaml :: Text -> Maybe ParameterValue -> ParameterYaml-mkParameterYaml k = ParameterYaml (Key.fromText k) . Last- parameterYaml :: Parameter -> Maybe ParameterYaml parameterYaml p = do   k <- p ^. parameter_parameterKey   let mv = p ^. parameter_parameterValue-  pure $ mkParameterYaml k $ ParameterValue <$> mv+  pure $ mkParameterYaml k $ parameterValueFromText <$> mv +mkParameterYaml :: Text -> Maybe ParameterValue -> ParameterYaml+mkParameterYaml k = ParameterYaml (Key.fromText k) . Last+ unParameterYaml :: ParameterYaml -> Parameter unParameterYaml (ParameterYaml k v) =-  makeParameter (Key.toText k) $ unParameterValue <$> getLast v+  makeParameter (Key.toText k) $ parameterValueToText <$> getLast v -newtype ParameterValue = ParameterValue-  { unParameterValue :: Text-  }+data ParameterValue+  = StringParameter Text+  | NumberParameter Double+  | -- | Encodes as String True|False+    BooleanParameter Bool   deriving stock (Eq, Show)-  deriving newtype (Semigroup, ToJSON)  instance FromJSON ParameterValue where   parseJSON = \case-    String x -> pure $ ParameterValue x-    Number x -> pure $ ParameterValue $ dropSuffix ".0" $ pack $ show x-    x -> fail $ "Expected String or Number, got: " <> show x+    String t -> pure $ StringParameter t+    Number s -> pure $ NumberParameter $ realToFrac s+    Bool b -> pure $ BooleanParameter b+    x -> typeMismatch "String, Number or Bool" x +instance ToJSON ParameterValue where+  toJSON = \case+    StringParameter t -> toJSON t+    NumberParameter d -> toJSON d+    BooleanParameter b -> toJSON $ pack $ show b+  toEncoding = \case+    StringParameter t -> toEncoding t+    NumberParameter d -> toEncoding d+    BooleanParameter b -> toEncoding $ pack $ show b++parameterValueToText :: ParameterValue -> Text+parameterValueToText = \case+  StringParameter t -> t+  NumberParameter d -> pack $ dropSuffix ".0" $ show d+  BooleanParameter b -> pack $ show b++parameterValueFromText :: Text -> ParameterValue+parameterValueFromText = \case+  v | T.toLower v == "true" -> BooleanParameter True+  v | T.toLower v == "false" -> BooleanParameter False+  v | Just d <- readMaybe (unpack v) -> NumberParameter d+  v -> StringParameter v++-- | For use as the value in a @Parameters@ object of a CFN Template+parameterValueTemplate :: ParameterValue -> Value+parameterValueTemplate v =+  object+    $ catMaybes+      [ Just $ "Type" .= parameterValueType v+      , Just $ "Default" .= parameterValueDefault v+      , ("AllowedValues" .=) <$> parameterValueAllowedValues v+      ]++parameterValueType :: ParameterValue -> Text+parameterValueType = \case+  StringParameter {} -> "String"+  NumberParameter {} -> "Number"+  BooleanParameter {} -> "String"++parameterValueDefault :: ParameterValue -> Value+parameterValueDefault = toJSON++parameterValueAllowedValues :: ParameterValue -> Maybe Value+parameterValueAllowedValues = \case+  StringParameter {} -> Nothing+  NumberParameter {} -> Nothing+  BooleanParameter {} -> Just $ toJSON [String "True", String "False"]+ newtype TagsYaml = TagsYaml   { unTagsYaml :: [TagYaml]   }@@ -194,6 +255,3 @@  tagYamlPair :: KeyValue kv => TagYaml -> kv tagYamlPair (TagYaml t) = Key.fromText (t ^. tag_key) .= (t ^. tag_value)--dropSuffix :: Text -> Text -> Text-dropSuffix suffix t = fromMaybe t $ T.stripSuffix suffix t
stackctl.cabal view
@@ -1,6 +1,6 @@ cabal-version:   1.18 name:            stackctl-version:         1.6.1.2+version:         1.7.0.0 license:         MIT license-file:    LICENSE copyright:       2022 Renaissance Learning Inc
test/Stackctl/StackSpecYamlSpec.hs view
@@ -80,28 +80,6 @@       param ^. parameter_parameterKey `shouldBe` Just "Pie"       param ^. parameter_parameterValue `shouldBe` Just "3.14" -    it "has informative errors" $ do-      let Left ex =-            Yaml.decodeEither' @StackSpecYaml-              $ mconcat-                [ "Template: foo.yaml\n"-                , "Parameters:\n"-                , "  - ParameterKey: Norway\n"-                , "    ParameterValue: no\n"-                ]--      show ex-        `shouldBe` "AesonException \"Error in $.Parameters[0].ParameterValue: Expected String or Number, got: Bool False\""--    it "has informative errors in Object form" $ do-      let Left ex =-            Yaml.decodeEither' @StackSpecYaml-              $ mconcat-                ["Template: foo.yaml\n", "Parameters:\n", "  Norway: no\n"]--      show ex-        `shouldBe` "AesonException \"Error in $.Parameters.Norway: Expected String or Number, got: Bool False\""-     it "handles null Value" $ do       StackSpecYaml {..} <-         Yaml.decodeThrow