Skip to content

Commit

Permalink
[stack-deploy] Change public interface
Browse files Browse the repository at this point in the history
  • Loading branch information
mbj committed Jan 2, 2024
1 parent 498241b commit d5a50df
Show file tree
Hide file tree
Showing 33 changed files with 1,239 additions and 1,001 deletions.
43 changes: 19 additions & 24 deletions aws-secrets/src/AWS/Secrets.hs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module AWS.Secrets
, IsSecret(..)
, SecretConfig(..)
, arnValue
, envSpecEntries
, envSpec
, externalTemplate
, fetchStackSecretArn
, getSecretValuePolicy
Expand All @@ -29,9 +29,9 @@ import qualified Data.Text as Text
import qualified MIO.Amazonka as AWS
import qualified MIO.Log as Log
import qualified StackDeploy.Component as StackDeploy
import qualified StackDeploy.EnvSpec
import qualified StackDeploy.Template as StackDeploy
import qualified StackDeploy.Utils
import qualified StackDeploy.EnvSpec as StackDeploy
import qualified StackDeploy.NamedTemplate as StackDeploy
import qualified StackDeploy.Stack as StackDeploy
import qualified Stratosphere as CFT
import qualified Stratosphere.IAM.Role as IAM.Role
import qualified Stratosphere.SecretsManager.Secret as SecretsManager
Expand Down Expand Up @@ -70,18 +70,12 @@ internalComponent = mempty

externalTemplate
:: forall a . IsSecret a
=> StackDeploy.Template
externalTemplate =
StackDeploy.Template
{ name = fromType @"secrets-external"
, ..
}
=> StackDeploy.NamedTemplate
externalTemplate
= StackDeploy.mkNamedTemplate (fromType @"secrets-external")
$ (CFT.mkTemplate . CFT.Resources $ mkSecretResource <$> externalSecrets)
{ CFT.outputs = pure . CFT.Outputs $ mkExternalOutput <$> externalSecrets }
where
stratosphere :: CFT.Template
stratosphere =
(CFT.mkTemplate . CFT.Resources $ mkSecretResource <$> externalSecrets)
{ CFT.outputs = pure . CFT.Outputs $ mkExternalOutput <$> externalSecrets }

mkExternalOutput :: a -> CFT.Output
mkExternalOutput secret = (mkInternalOutput secret)
{ CFT.export
Expand Down Expand Up @@ -147,13 +141,14 @@ secretNameValue namespace name

fetchStackSecretArn :: IsSecret a => CF.Stack -> a -> MIO env Text
fetchStackSecretArn stack
= liftIO . StackDeploy.Utils.fetchOutput stack . mkInternalOutput
= liftIO . StackDeploy.fetchStackOutput stack . mkInternalOutput

readStackSecretValue :: Env env => IsSecret a => CF.Stack -> a -> MIO env Text
readStackSecretValue stack secret = readSecretsManagerSecret =<< fetchStackSecretArn stack secret

readEnvSecretValue :: (Env env, IsSecret a) => a -> MIO env Text
readEnvSecretValue = readSecretsManagerSecret . convert <=< Environment.getEnv . convert . envArnName
readEnvSecretValue =
readSecretsManagerSecret . convert <=< Environment.getEnv . convertVia @Text . envSpecName

readSecretsManagerSecret :: Env env => Text -> MIO env Text
readSecretsManagerSecret arn = do
Expand All @@ -173,14 +168,14 @@ rdsGeneratePostgresqlPassword
& CFT.set @"ExcludeCharacters" "/\"@"
& CFT.set @"PasswordLength" (CFT.Literal 48)
envArnName :: IsSecret a => a -> Text
envArnName = (<> "_ARN") . Text.toUpper . convert . JSON.camelTo2 '_' . show
envSpecName :: IsSecret a => a -> StackDeploy.EnvSpecName
envSpecName = convertImpure . (<> "_ARN") . Text.toUpper . convert . JSON.camelTo2 '_' . show
envSpecEntries :: IsSecret a => [a] -> [StackDeploy.EnvSpec.Entry]
envSpecEntries = fmap $ \secret ->
StackDeploy.EnvSpec.Entry
{ envName = envArnName secret
, envValue = StackDeploy.EnvSpec.StackOutput $ case secretConfig secret of
envSpec :: IsSecret a => [a] -> [StackDeploy.EnvSpec]
envSpec = fmap $ \secret ->
StackDeploy.EnvSpec
{ name = envSpecName secret
, value = StackDeploy.EnvSpecStackOutput $ case secretConfig secret of
Internal{} -> mkInternalOutput secret
External{} -> mkInternalExternalOutput secret
}
Expand Down
14 changes: 9 additions & 5 deletions aws-secrets/src/AWS/Secrets/CLI.hs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import qualified Options.Applicative as CLI
import qualified StackDeploy.CLI.Utils as StackDeploy.CLI
import qualified StackDeploy.Stack as StackDeploy
import qualified System.Exit as System
import qualified UnliftIO.Exception as UnliftIO

parserInfo :: forall a env . (Env env, IsSecret a) => CLI.ParserInfo (MIO env System.ExitCode)
parserInfo = CLI.info (CLI.helper <*> subcommands) CLI.idm
Expand Down Expand Up @@ -45,12 +46,15 @@ parserInfo = CLI.info (CLI.helper <*> subcommands) CLI.idm
list = traverse_ (liftIO . IO.putStrLn . snakeCase) (secrets @a)

printStackSecret action =
evaluate <$> StackDeploy.CLI.instanceSpecNameOption <*> secretNameOption
evaluate <$> StackDeploy.CLI.instanceNameOption <*> secretNameOption
where
evaluate instanceSpecName secret = do
stack <- StackDeploy.getExistingStack instanceSpecName
putStrLn =<< action stack secret
pure System.ExitSuccess
evaluate instanceName secret = do
maybe absent present =<< StackDeploy.readCloudFormationStack instanceName
where
absent = UnliftIO.throwString $ "Stack does not exist: " <> convertVia @Text instanceName
present stack = do
putStrLn =<< action stack secret
pure System.ExitSuccess

putStrLn :: MonadIO m => Text -> m ()
putStrLn = liftIO . IO.putStrLn
Expand Down
16 changes: 8 additions & 8 deletions aws-secrets/test/Test.hs
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ import AWS.Secrets.Prelude
import qualified Devtools
import qualified StackDeploy.Component as StackDeploy
import qualified StackDeploy.EnvSpec as StackDeploy
import qualified StackDeploy.Template as StackDeploy
import qualified StackDeploy.Utils as StackDeploy
import qualified StackDeploy.NamedTemplate as StackDeploy
import qualified StackDeploy.Stratosphere as CFT
import qualified Stratosphere as CFT
import qualified Stratosphere.IAM.Role as IAM
import qualified Stratosphere.Lambda.Function as Lambda
Expand Down Expand Up @@ -43,35 +43,35 @@ main =
liftIO . Tasty.defaultMain .
Tasty.testGroup "aws-secrets" $
[ Devtools.testTree $$(Devtools.readDependencies [Devtools.Target "aws-secrets"])
, StackDeploy.testTree
, StackDeploy.namedTemplateTestTree $ StackDeploy.namedTemplateMapFromList
[ internalTemplate
, internalTemplateNoExternal
, externalTemplate @TestSecret
]
]
where
internalTemplate
= StackDeploy.mkTemplate (fromType @"secrets-internal")
= StackDeploy.namedTemplateFromComponents (fromType @"secrets-internal")
[ internalComponent @TestSecret
, lambdaComponent
]

internalTemplateNoExternal
= StackDeploy.mkTemplate (fromType @"secrets-internal-no-external")
= StackDeploy.namedTemplateFromComponents (fromType @"secrets-internal-no-external")
[internalComponent @TestSecretNoExternal]

lambdaComponent = mempty
{ StackDeploy.resources = [lambdaFunction, lambdaRole] }
where
lambdaFunction
= CFT.resource "TestLambdaFunction"
$ Lambda.mkFunction lambdaFunctionCode (StackDeploy.getAttArn lambdaRole)
$ Lambda.mkFunction lambdaFunctionCode (CFT.getAttArn lambdaRole)
& CFT.set @"Environment"
(StackDeploy.lambdaEnvironment $ envSpecEntries [TestExternal, TestInternal])
(StackDeploy.envSpecToLambdaEnvironment $ envSpec [TestExternal, TestInternal])

lambdaRole
= CFT.resource "TestLambdaRole"
$ IAM.mkRole (StackDeploy.assumeRole "lambda.amazonaws.com")
$ IAM.mkRole (CFT.assumeRole "lambda.amazonaws.com")
& CFT.set @"Policies" [getSecretValuePolicy [TestExternal, TestInternal]]

lambdaFunctionCode :: Lambda.CodeProperty
Expand Down
2 changes: 1 addition & 1 deletion aws-secrets/test/stack-9.4-dependencies.txt
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,7 @@ socks 0.6.1
source-constraints 0.0.5
split 0.2.3.5
splitmix 0.1.0.5
stack-deploy 0.0.9
stack-deploy 0.1.0
stm 2.5.1.0
stratosphere 1.0.0
stratosphere-ecs 1.0.0
Expand Down
65 changes: 34 additions & 31 deletions aws-temporary-ingress-rule/src/AWS/TemporaryIngressRule.hs
Original file line number Diff line number Diff line change
Expand Up @@ -42,10 +42,10 @@ import qualified Network.IP.Addr as Network
import qualified Options.Applicative as CLI
import qualified StackDeploy.CLI.Utils as StackDeploy.CLI
import qualified StackDeploy.Component as StackDeploy
import qualified StackDeploy.EnvSpec as EnvSpec
import qualified StackDeploy.InstanceSpec
import qualified StackDeploy.EnvSpec as StackDeploy
import qualified StackDeploy.InstanceSpec as StackDeploy
import qualified StackDeploy.Stack as StackDeploy
import qualified StackDeploy.Utils
import qualified StackDeploy.Stratosphere as CFT
import qualified Stratosphere as CFT
import qualified Stratosphere.Events.Rule as Events
import qualified Stratosphere.IAM.Role as IAM
Expand Down Expand Up @@ -90,8 +90,8 @@ authorizeIngressRule IngressConfig{..} stack = do
readRequest = do
netAddr <- AWS.Checkip.readNetAddr
now <- liftIO Time.getCurrentTime
groupId <- liftIO (StackDeploy.Utils.fetchOutput stack securityGroupIdOutput)
port <- read . convert <$> liftIO (StackDeploy.Utils.fetchOutput stack portOutput)
groupId <- liftIO (StackDeploy.fetchStackOutput stack securityGroupIdOutput)
port <- read . convert <$> liftIO (StackDeploy.fetchStackOutput stack portOutput)
pure Request{cidr = convert @Text @String $ Network.printNetAddr netAddr, ..}

revokeExpiredIngressRules :: forall env . Env env => [Text] -> MIO env ()
Expand Down Expand Up @@ -291,35 +291,39 @@ parserInfo configs = CLI.info (CLI.helper <*> subcommands) CLI.idm
"show configuration"
<> mkCommand
"authorize-all"
(runStack authorizeAll <$> StackDeploy.CLI.instanceSpecNameOption)
(withStack authorizeAll <$> StackDeploy.CLI.instanceNameOption)
"authorize all ingress configurations"
<> mkCommand
"revoke-expired"
(runGroupIds revokeExpiredIngressRules <$> StackDeploy.CLI.instanceSpecNameOption)
(runGroupIds revokeExpiredIngressRules <$> StackDeploy.CLI.instanceNameOption)
"revoke expired ingress rules"
<> mkCommand
"list-expired"
(runGroupIds listExpiredIngressRules <$> StackDeploy.CLI.instanceSpecNameOption)
(runGroupIds listExpiredIngressRules <$> StackDeploy.CLI.instanceNameOption)
"list expired ingress rules"

printIngressConfigurations = traverse_ (liftIO . IO.putStrLn . convert . show) configs

runStack
withStack
:: (CloudFormation.Stack -> MIO env ())
-> StackDeploy.InstanceSpec.Name
-> StackDeploy.InstanceName
-> MIO env System.ExitCode
runStack action instanceSpecName = do
action =<< StackDeploy.getExistingStack instanceSpecName
pure System.ExitSuccess
withStack action instanceName = do
maybe absent present =<< StackDeploy.readCloudFormationStack instanceName
where
absent = UnliftIO.throwString $ "Stack does not exist: " <> convertVia @Text instanceName
present stack = do
action stack
pure System.ExitSuccess

runGroupIds
:: ([Text] -> MIO env ())
-> StackDeploy.InstanceSpec.Name
-> StackDeploy.InstanceName
-> MIO env System.ExitCode
runGroupIds action =
runStack $ \stack -> do
withStack $ \stack -> do
groupIds <- traverse
(liftIO . StackDeploy.Utils.fetchOutput stack . (.securityGroupIdOutput)) configs
(liftIO . StackDeploy.fetchStackOutput stack . (.securityGroupIdOutput)) configs
action groupIds

authorizeAll :: CloudFormation.Stack -> MIO env ()
Expand Down Expand Up @@ -380,7 +384,7 @@ instance AWS.HasResourceMap Environment where
bootLambdaExpire :: [IngressConfig] -> IO ()
bootLambdaExpire ingressConfigurations = do
withEnvironment $ do
groupIds <- Text.split (== ',') <$> EnvSpec.loadEnv (groupIdsEnvSpec ingressConfigurations)
groupIds <- Text.split (== ',') <$> StackDeploy.readEnvSpecFromEnvironment (groupIdsEnvSpec ingressConfigurations)
AWS.Lambda.Runtime.run (const $ revokeExpiredIngressRules groupIds $> JSON.Null)

withEnvironment :: MIO Environment a -> IO a
Expand All @@ -403,12 +407,11 @@ withEnvironment action = do
{ Amazonka.logger = logger
}

groupIdsEnvSpec :: [IngressConfig] -> EnvSpec.Entry
groupIdsEnvSpec :: [IngressConfig] -> StackDeploy.EnvSpec
groupIdsEnvSpec ingressConfigurations
= EnvSpec.Entry
{ envName = "TEMPORARY_INGRESS_RULE_GROUP_IDS"
, envValue = EnvSpec.StackOutput $ mkGroupIdsOutput ingressConfigurations
}
= StackDeploy.EnvSpec
(fromType @"TEMPORARY_INGRESS_RULE_GROUP_IDS")
(StackDeploy.EnvSpecStackOutput $ mkGroupIdsOutput ingressConfigurations)

mkGroupIdsOutput :: [IngressConfig] -> CFT.Output
mkGroupIdsOutput ingressConfigurations
Expand All @@ -431,45 +434,45 @@ component StackConfig{..} ingressConfigurations = mempty

lambdaFunction
= CFT.resource (prefix <> "LambdaFunction")
$ Lambda.mkFunction lambdaCode (StackDeploy.Utils.getAttArn lambdaRole)
& CFT.set @"Environment" (EnvSpec.lambdaEnvironment [groupIdsEnvSpec ingressConfigurations])
$ Lambda.mkFunction lambdaCode (CFT.getAttArn lambdaRole)
& CFT.set @"Environment" (StackDeploy.envSpecToLambdaEnvironment [groupIdsEnvSpec ingressConfigurations])
& CFT.set @"FunctionName" lambdaFunctionName
& CFT.set @"Handler" lambdaFunctionHandler
& CFT.set @"Runtime" "provided.al2"
& CFT.set @"Timeout" (CFT.Literal 60)

lambdaLogGroup
= CFT.resource (prefix <> "LogGroup")
$ StackDeploy.Utils.mkLambdaLogGroup lambdaFunctionName
$ CFT.mkLambdaLogGroup lambdaFunctionName

lambdaRole
= CFT.resource (prefix <> "LambdaRule")
$ IAM.mkRole (StackDeploy.Utils.assumeRole "lambda.amazonaws.com")
& CFT.set @"Policies" [StackDeploy.Utils.mkLambdaLogsPolicy lambdaFunctionName, ingressRulePolicy]
$ IAM.mkRole (CFT.assumeRole "lambda.amazonaws.com")
& CFT.set @"Policies" [CFT.mkLambdaLogsPolicy lambdaFunctionName, ingressRulePolicy]

lambdaFunctionName :: CFT.Value Text
lambdaFunctionName = StackDeploy.Utils.mkName $ CFT.Literal prefix
lambdaFunctionName = CFT.mkName $ CFT.Literal prefix

lambdaPermission :: CFT.Resource
lambdaPermission
= CFT.resource (prefix <> "LambdaPermission")
$ Lambda.mkPermission "lambda:InvokeFunction" (CFT.toRef lambdaFunction) "events.amazonaws.com"
& CFT.set @"SourceArn" (StackDeploy.Utils.getAttArn eventsRule)
& CFT.set @"SourceArn" (CFT.getAttArn eventsRule)

eventsRule :: CFT.Resource
eventsRule
= CFT.resource logicalName
$ Events.mkRule
& CFT.set @"Description" (CFT.Literal ("Trigger rule for " <> prefix))
& CFT.set @"Name" (StackDeploy.Utils.mkName (CFT.Literal logicalName))
& CFT.set @"Name" (CFT.mkName (CFT.Literal logicalName))
& CFT.set @"ScheduleExpression" (CFT.Literal "rate(1 minute)")
& CFT.set @"State" "ENABLED"
& CFT.set @"Targets" [lambdaTarget]
where
logicalName = prefix <> "EventsRule"

lambdaTarget =
Events.mkTargetProperty (StackDeploy.Utils.getAttArn lambdaFunction) (CFT.Literal logicalName)
Events.mkTargetProperty (CFT.getAttArn lambdaFunction) (CFT.Literal logicalName)

ingressRulePolicy
= IAM.mkPolicyProperty
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
module AWS.TemporaryIngressRule.Prelude (module Exports) where

import Data.Conversions as Exports
import MIO.Core as Exports
import MPrelude as Exports
import Data.Conversions as Exports
import Data.Conversions.FromType as Exports
import MIO.Core as Exports
import MPrelude as Exports
2 changes: 1 addition & 1 deletion aws-temporary-ingress-rule/test/stack-9.4-dependencies.txt
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ socks 0.6.1
source-constraints 0.0.5
split 0.2.3.5
splitmix 0.1.0.5
stack-deploy 0.0.9
stack-deploy 0.1.0
stm 2.5.1.0
stratosphere 1.0.0
stratosphere-ecs 1.0.0
Expand Down
2 changes: 1 addition & 1 deletion stack-9.4.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ extra-deps:
- amazonka-sts-2.0
- mprelude
- github: mbj/stratosphere
commit: 7c462f2de4369a76df22d4b34b2f3a9e7754c731
commit: d1b638820ca9fd90f7d5afba535415da47252af5
subdirs:
- .
- services/ecs
Expand Down
Loading

0 comments on commit d5a50df

Please sign in to comment.