1008 test implementation

Signed-off-by: Guillaume de Rouville <guillaume.derouville@gmail.com>
This commit is contained in:
Guillaume de Rouville 2021-08-13 04:18:56 +02:00
parent 6744045ce7
commit dc865bf2be
12 changed files with 379 additions and 290 deletions

View File

@ -66,68 +66,7 @@ The idea here is to follow best practices in [S3 buckets](https://docs.aws.amazo
Create a file named `template.cue` and add the following configuration to it.
```cue title="todoapp/cloudformation/template.cue"
package cloudformation
// inlined s3 cloudformation template as a string
template: """
{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"S3Bucket": {
"Type": "AWS::S3::Bucket",
"Properties": {
"AccessControl": "PublicRead",
"WebsiteConfiguration": {
"IndexDocument": "index.html",
"ErrorDocument": "error.html"
}
},
"DeletionPolicy": "Retain"
},
"BucketPolicy": {
"Type": "AWS::S3::BucketPolicy",
"Properties": {
"PolicyDocument": {
"Id": "MyPolicy",
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadForGetBucketObjects",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": {
"Fn::Join": [
"",
[
"arn:aws:s3:::",
{
"Ref": "S3Bucket"
},
"/*"
]
]
}
}
]
},
"Bucket": {
"Ref": "S3Bucket"
}
}
}
},
"Outputs": {
"Name": {
"Value": {
"Fn::GetAtt": ["S3Bucket", "Arn"]
},
"Description": "Name S3 Bucket"
}
}
}
"""
```cue file=./tests/cloudformation/template.cue title="todoapp/cloudformation/template.cue"
```
##### 2. Cloudformation relay
@ -164,15 +103,7 @@ The config values are all part of the `aws` relay. Regarding this package, as yo
Let's implement the first step, use the `aws.#Config` relay, and request its first inputs: the region to deploy and the AWS credentials.
```cue title="todoapp/cloudformation/source.cue"
package cloudformation
import (
"alpha.dagger.io/aws"
)
// AWS account: credentials and region
awsConfig: aws.#Config
```cue file=./tests/cloudformation/source-begin.cue title="todoapp/cloudformation/source.cue"
```
This defines:
@ -214,33 +145,7 @@ dagger up -e cloudformation # Try to run the plan. As expected, we encounter a f
Now that we have the `config` definition properly configured, let's modify the Cloudformation one:
```cue title="todoapp/cloudformation/source.cue"
package cloudformation
import (
"alpha.dagger.io/aws"
"alpha.dagger.io/dagger"
"alpha.dagger.io/random"
"alpha.dagger.io/aws/cloudformation"
)
// AWS account: credentials and region
awsConfig: aws.#Config
// Create a random suffix
suffix: random.#String & {
seed: ""
}
// Query the Cloudformation stackname, or create one with a random suffix to keep unicity
cfnStackName: *"stack-\(suffix.out)" | string & dagger.#Input
// AWS Cloudformation stdlib
cfnStack: cloudformation.#Stack & {
config: awsConfig
stackName: cfnStackName
source: template
}
```cue file=./tests/cloudformation/source-end.cue title="todoapp/cloudformation/source.cue"
```
This defines:
@ -359,35 +264,9 @@ We will create a new `convert.cue` file to process the conversion
import Tabs from "@theme/Tabs";
import TabItem from "@theme/TabItem";
<Tabs
defaultValue="sv"
values={[
{ label: 'JSON Generic Code', value: 'sv', },
{ label: 'YAML Generic Code', value: 'yv', },
]
}>
<TabItem value="sv">
```cue title="todoapp/cloudformation/convert.cue"
package cloudformation
import "encoding/json"
s3Template: json.Unmarshal(template)
```cue file=./tests/cloudformation/template/convert.cue title="todoapp/cloudformation/convert.cue"
```
</TabItem>
<TabItem value="yv">
```cue title="todoapp/cloudformation/convert.cue"
package cloudformation
import "encoding/yaml"
s3Template: yaml.Unmarshal(template)
```
</TabItem>
</Tabs>
This defines:
- `s3Template`: contains the unmarshalled template.
@ -428,74 +307,7 @@ rm cloudformation/convert.cue
Open `cloudformation/template.cue` and append below elements with copied Cue definition of the JSON:
```cue title="todoapp/cloudformation/template.cue"
// Add this line, to make it part to the cloudformation template
package cloudformation
import "encoding/json"
// Wrap exported Cue in previous point inside the `s3` value
s3: {
"AWSTemplateFormatVersion": "2010-09-09",
"Outputs": {
"Name": {
"Description": "Name S3 Bucket",
"Value": {
"Fn::GetAtt": [
"S3Bucket",
"Arn"
]
}
}
},
"Resources": {
"BucketPolicy": {
"Properties": {
"Bucket": {
"Ref": "S3Bucket"
},
"PolicyDocument": {
"Id": "MyPolicy",
"Statement": [
{
"Action": "s3:GetObject",
"Effect": "Allow",
"Principal": "*",
"Resource": {
"Fn::Join": [
"",
[
"arn:aws:s3:::",
{
"Ref": "S3Bucket"
},
"/*"
]
]
},
"Sid": "PublicReadForGetBucketObjects"
}
],
"Version": "2012-10-17"
}
},
"Type": "AWS::S3::BucketPolicy"
},
"S3Bucket": {
"DeletionPolicy": "Retain",
"Properties": {
"AccessControl": "PublicRead",
"WebsiteConfiguration": {
"ErrorDocument": "error.html",
"IndexDocument": "index.html"
}
},
"Type": "AWS::S3::Bucket"
}
}
}
// Template contains the marshalled value of the s3 template
template: json.Marshal(s3)
```cue file=./tests/cloudformation/template/template-begin.cue title="todoapp/cloudformation/template.cue"
```
We're using the built-in `json.Marshal` function to convert CUE back to JSON, so Cloudformation still receives the same template.
@ -510,104 +322,12 @@ Now that the template is defined in CUE, we can use the language to add more fle
Let's define a re-usable `#Deployment` definition in `todoapp/cloudformation/deployment.cue`:
```cue title="todoapp/cloudformation/deployment.cue"
package cloudformation
#Deployment: {
// Bucket's output description
description: string
// index file
indexDocument: *"index.html" | string
// error file
errorDocument: *"error.html" | string
// Bucket policy version
version: *"2012-10-17" | string
// Retain as default deletion policy. Delete is also accepted but requires the s3 bucket to be empty
deletionPolicy: *"Retain" | "Delete"
// Canned access control list (ACL) that grants predefined permissions to the bucket
accessControl: *"PublicRead" | "Private" | "PublicReadWrite" | "AuthenticatedRead" | "LogDeliveryWrite" | "BucketOwnerRead" | "BucketOwnerFullControl" | "AwsExecRead"
// Modified copy of s3 value in `todoapp/cloudformation/template.cue`
template: {
"AWSTemplateFormatVersion": "2010-09-09",
"Outputs": {
"Name": {
"Description": description,
"Value": {
"Fn::GetAtt": [
"S3Bucket",
"Arn"
]
}
}
},
"Resources": {
"BucketPolicy": {
"Properties": {
"Bucket": {
"Ref": "S3Bucket"
},
"PolicyDocument": {
"Id": "MyPolicy",
"Statement": [
{
"Action": "s3:GetObject",
"Effect": "Allow",
"Principal": "*",
"Resource": {
"Fn::Join": [
"",
[
"arn:aws:s3:::",
{
"Ref": "S3Bucket"
},
"/*"
]
]
},
"Sid": "PublicReadForGetBucketObjects"
}
],
"Version": version
}
},
"Type": "AWS::S3::BucketPolicy"
},
"S3Bucket": {
"DeletionPolicy": deletionPolicy,
"Properties": {
"AccessControl": "PublicRead",
"WebsiteConfiguration": {
"ErrorDocument": errorDocument,
"IndexDocument": indexDocument
}
},
"Type": "AWS::S3::Bucket"
}
}
}
}
```cue file=./tests/cloudformation/template/deployment.cue title="todoapp/cloudformation/deployment.cue"
```
`template.cue` can be rewritten as follows:
```cue title="todoapp/cloudformation/template.cue"
package cloudformation
import "encoding/json"
s3: #Deployment & {
description: "Name S3 Bucket"
}
// Template contains the marshalled value of the s3 template
template: json.Marshal(s3.template)
```cue file=./tests/cloudformation/template/template-end.cue title="todoapp/cloudformation/template.cue"
```
Verify template
@ -637,10 +357,9 @@ dagger query template -f text -e cloudformation
# "Value": {
```
You need to move back the `source.cue` for Dagger to instanciate a bucket:
Reimplement `source.cue`:
```shell
mv ~/tmp/source.cue cloudformation/source.cue
```cue file=./tests/cloudformation/source-end.cue title="todoapp/cloudformation/source.cue"
```
And we can now deploy it:

View File

@ -0,0 +1,2 @@
# dagger state
state/**

View File

@ -0,0 +1,32 @@
plan:
package: ./cloudformation
name: cloudformation
inputs:
awsConfig.accessKey:
secret: ENC[AES256_GCM,data:MJSSjaXpMawrpM5trPQzZR2Cg/M=,iv:D8Ff3Uy8hpzFPKg+okFkVM5DRam9Dyk31GjIizklvP0=,tag:J/7/+xjHdy94mYTQmB0sUw==,type:str]
awsConfig.region:
text: us-east-2
awsConfig.secretKey:
secret: ENC[AES256_GCM,data:BnBc29SIbLj3DDWwpem7mcLQSPoP6a/opqIYxLZyQPdSJojL/Jy9jA==,iv:nK3mj7jOgJdAwcO2i5OYj1FxHsYnoodyXMih9eqLnOQ=,tag:1zaXCkwL8d8ilyxCGzPN6Q==,type:str]
cfnStack.onFailure:
text: DELETE
sops:
kms: []
gcp_kms: []
azure_kv: []
hc_vault: []
age:
- recipient: age1gxwmtwahzwdmrskhf90ppwlnze30lgpm056kuesrxzeuyclrwvpsupwtpk
enc: |
-----BEGIN AGE ENCRYPTED FILE-----
YWdlLWVuY3J5cHRpb24ub3JnL3YxCi0+IFgyNTUxOSB0TW4xNVMzQkZueGY1NC91
WUplK09CRXpPSGMyTGhZYjZubDI1Sk96YVRFClJhVlNGT2VMMEtjeTcxbThaMHNq
MTgwdlFObTc3VVYxU3FFZ1lpMDAxVWcKLS0tIFJMQ2F6bWo4OEY2VnZoajJMMDVq
Lzd0bU1qMXF0dzIzTG05cmlzZURxUncKp2wNXD/HKXgGv16pM/yvaYfWL5lsQyWo
CDsT4rHHMZ21XtS9W+7oL9IqMMmDUQf6RZgDgoqLVmTjdBeA8yB2yg==
-----END AGE ENCRYPTED FILE-----
lastmodified: "2021-08-13T02:12:09Z"
mac: ENC[AES256_GCM,data:tZ35vWWlepyQOuKtzO1pau51YQZZ+Lg2LKK9PlSJvJZYzp+QaUVMCsoTAkZXgMH12C9ced6Jo1pfV9kQjxG1/g+5JmjgClFCWCMTIRa8Z9iI3hGu35SjxG4KVRlmeZsjstEW+78JBOekBUWYzkX0vAlKq44qLLug3FDc+sxFetQ=,iv:yB85C7b5X4y/koB3zN3ktUoYrYdB7N07FvJigwjxao0=,tag:3PSTd39bLyPRa4uZzHycjg==,type:str]
pgp: []
encrypted_suffix: secret
version: 3.7.1

View File

@ -0,0 +1,24 @@
package main
import (
"alpha.dagger.io/os"
"alpha.dagger.io/aws"
"alpha.dagger.io/dagger"
)
// Remove Cloudformation Stack
stackRemoval: {
// Cloudformation Stackname
stackName: string & dagger.#Input
ctr: os.#Container & {
image: aws.#CLI & {
config: awsConfig
}
always: true
env: STACK_NAME: stackName
command: """
aws cloudformation delete-stack --stack-name $STACK_NAME
"""
}
}

View File

@ -0,0 +1,8 @@
package main
import (
"alpha.dagger.io/aws"
)
// AWS account: credentials and region
awsConfig: aws.#Config

View File

@ -0,0 +1,26 @@
package main
import (
"alpha.dagger.io/aws"
"alpha.dagger.io/dagger"
"alpha.dagger.io/random"
"alpha.dagger.io/aws/cloudformation"
)
// AWS account: credentials and region
awsConfig: aws.#Config
// Create a random suffix
suffix: random.#String & {
seed: ""
}
// Query the Cloudformation stackname, or create one with a random suffix to keep unicity
cfnStackName: *"stack-\(suffix.out)" | string & dagger.#Input
// AWS Cloudformation stdlib
cfnStack: cloudformation.#Stack & {
config: awsConfig
stackName: cfnStackName
source: template
}

View File

@ -0,0 +1,61 @@
package main
// inlined s3 cloudformation template as a string
template: """
{
"AWSTemplateFormatVersion": "2010-09-09",
"Resources": {
"S3Bucket": {
"Type": "AWS::S3::Bucket",
"Properties": {
"AccessControl": "PublicRead",
"WebsiteConfiguration": {
"IndexDocument": "index.html",
"ErrorDocument": "error.html"
}
},
"DeletionPolicy": "Retain"
},
"BucketPolicy": {
"Type": "AWS::S3::BucketPolicy",
"Properties": {
"PolicyDocument": {
"Id": "MyPolicy",
"Version": "2012-10-17",
"Statement": [
{
"Sid": "PublicReadForGetBucketObjects",
"Effect": "Allow",
"Principal": "*",
"Action": "s3:GetObject",
"Resource": {
"Fn::Join": [
"",
[
"arn:aws:s3:::",
{
"Ref": "S3Bucket"
},
"/*"
]
]
}
}
]
},
"Bucket": {
"Ref": "S3Bucket"
}
}
}
},
"Outputs": {
"Name": {
"Value": {
"Fn::GetAtt": ["S3Bucket", "Arn"]
},
"Description": "Name S3 Bucket"
}
}
}
"""

View File

@ -0,0 +1,5 @@
package main
import "encoding/json"
s3Template: json.Unmarshal(template)

View File

@ -0,0 +1,75 @@
package main
#Deployment: {
// Bucket's output description
description: string
// index file
indexDocument: *"index.html" | string
// error file
errorDocument: *"error.html" | string
// Bucket policy version
version: *"2012-10-17" | string
// Retain as default deletion policy. Delete is also accepted but requires the s3 bucket to be empty
deletionPolicy: *"Retain" | "Delete"
// Canned access control list (ACL) that grants predefined permissions to the bucket
accessControl: *"PublicRead" | "Private" | "PublicReadWrite" | "AuthenticatedRead" | "LogDeliveryWrite" | "BucketOwnerRead" | "BucketOwnerFullControl" | "AwsExecRead"
// Modified copy of s3 value in `todoapp/cloudformation/template.cue`
template: {
AWSTemplateFormatVersion: "2010-09-09"
Outputs: Name: {
Description: description
Value: "Fn::GetAtt": [
"S3Bucket",
"Arn",
]
}
Resources: {
BucketPolicy: {
Properties: {
Bucket: Ref: "S3Bucket"
PolicyDocument: {
Id: "MyPolicy"
Statement: [
{
Action: "s3:GetObject"
Effect: "Allow"
Principal: "*"
Resource: "Fn::Join": [
"",
[
"arn:aws:s3:::",
{
Ref: "S3Bucket"
},
"/*",
],
]
Sid: "PublicReadForGetBucketObjects"
},
]
Version: version
}
}
Type: "AWS::S3::BucketPolicy"
}
S3Bucket: {
DeletionPolicy: deletionPolicy
Properties: {
AccessControl: "PublicRead"
WebsiteConfiguration: {
ErrorDocument: errorDocument
IndexDocument: indexDocument
}
}
Type: "AWS::S3::Bucket"
}
}
}
}

View File

@ -0,0 +1,60 @@
// Add this line, to make it part to the cloudformation template
package main
import "encoding/json"
// Wrap exported Cue in previous point inside the `s3` value
s3: {
AWSTemplateFormatVersion: "2010-09-09"
Outputs: Name: {
Description: "Name S3 Bucket"
Value: "Fn::GetAtt": [
"S3Bucket",
"Arn",
]
}
Resources: {
BucketPolicy: {
Properties: {
Bucket: Ref: "S3Bucket"
PolicyDocument: {
Id: "MyPolicy"
Statement: [
{
Action: "s3:GetObject"
Effect: "Allow"
Principal: "*"
Resource: "Fn::Join": [
"",
[
"arn:aws:s3:::",
{
Ref: "S3Bucket"
},
"/*",
],
]
Sid: "PublicReadForGetBucketObjects"
},
]
Version: "2012-10-17"
}
}
Type: "AWS::S3::BucketPolicy"
}
S3Bucket: {
DeletionPolicy: "Retain"
Properties: {
AccessControl: "PublicRead"
WebsiteConfiguration: {
ErrorDocument: "error.html"
IndexDocument: "index.html"
}
}
Type: "AWS::S3::Bucket"
}
}
}
// Template contains the marshalled value of the s3 template
template: json.Marshal(s3)

View File

@ -0,0 +1,10 @@
package main
import "encoding/json"
s3: #Deployment & {
description: "Name S3 Bucket"
}
// Template contains the marshalled value of the s3 template
template: json.Marshal(s3.template)

View File

@ -77,4 +77,71 @@ setup() {
# Run test
run dagger -e gcpcloudrun up
assert_success
}
@test "doc-1008-aws-cloudformation" {
setup_example_sandbox "doc"
### Create a basic plan
## Construct
mkdir cloudformation
cp $CODEBLOC_SRC/cloudformation/template.cue cloudformation
# Cloudformation relay
dagger doc alpha.dagger.io/aws/cloudformation
cp $CODEBLOC_SRC/cloudformation/source-begin.cue cloudformation/source.cue
# Initialize new env
dagger new 'cloudformation' -p cloudformation
# Finish template setup
cp $CODEBLOC_SRC/cloudformation/source-end.cue cloudformation/source.cue
# Copy corresponding env
cp -r $CODEBLOC_SRC/.dagger/env/cloudformation .dagger/env/
# Run test
dagger -e cloudformation up
stackName=$(dagger -e cloudformation query cfnStackName -f text)
## Cleanup
# Place back empty source
cp $CODEBLOC_SRC/cloudformation/source-begin.cue cloudformation/source.cue
cp $CODEBLOC_SRC/cloudformation/deletion.cue cloudformation/deletion.cue
# Prepare and run cloudformation cleanup
dagger -e cloudformation input text stackRemoval.stackName $stackName
dagger -e cloudformation up
### Template part
## Create convert.cue
cp $CODEBLOC_SRC/cloudformation/template/convert.cue cloudformation/convert.cue
rm cloudformation/source.cue cloudformation/deletion.cue
## Retrieve Unmarshalled JSON
dagger query -e cloudformation s3Template
## Remove convert.cue
rm cloudformation/convert.cue
## Store the output
cp $CODEBLOC_SRC/cloudformation/template/template-begin.cue cloudformation/template.cue
# Inspect conf
dagger query -e cloudformation template -f text
cp $CODEBLOC_SRC/cloudformation/template/deployment.cue cloudformation/deployment.cue
cp $CODEBLOC_SRC/cloudformation/template/template-end.cue cloudformation/template.cue
cp $CODEBLOC_SRC/cloudformation/source-end.cue cloudformation/source.cue
# Deploy again
dagger -e cloudformation query template -f text
dagger -e cloudformation up
dagger -e cloudformation output list
## Cleanup again
stackName=$(dagger -e cloudformation query cfnStackName -f text)
rm -rf cloudformation/*
# Place back empty source
cp $CODEBLOC_SRC/cloudformation/source-begin.cue cloudformation/source.cue
cp $CODEBLOC_SRC/cloudformation/deletion.cue cloudformation/deletion.cue
# Prepare and run cloudformation cleanup
dagger -e cloudformation input text stackRemoval.stackName $stackName
dagger -e cloudformation up
}