Skip to content

Commit a4d53cb

Browse files
committed
chore: initial commit
0 parents  commit a4d53cb

File tree

112 files changed

+20153
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

112 files changed

+20153
-0
lines changed

.github/CODEOWNERS

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
* @skyleague/oss
2+
package-lock.json

.github/workflows/tfsec.yml

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
name: tfsec
2+
on: push
3+
4+
jobs:
5+
tfsec:
6+
uses: skyleague/node-standards/.github/workflows/reusable-tfsec.yml@main
7+
secrets:
8+
GITHUB_APP_ID: ${{ secrets.STARCHART_MODULES_APP_ID }}
9+
GITHUB_APP_PEM: ${{ secrets.STARCHART_MODULES_APP_PEM }}
10+
with:
11+
terraform-version: '1.4.6'
12+
working-directory: './'

.gitignore

+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
.*
2+
__pycache__
3+
node_modules
4+
5+
!.github
6+
!.vscode
7+
!.npmrc
8+
!.husky
9+
10+
!.gitignore
11+
!.gitkeep

.husky/commit-msg

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
npx --no -- commitlint --edit $1

.husky/pre-commit

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
npx --no -- lint-staged --concurrent=true --allow-empty

.npmrc

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
//registry.npmjs.org/:_authToken=${NPM_TOKEN}

.vscode/extensions.json

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"recommendations": ["biomejs.biome"],
3+
"unwantedRecommendations": ["esbenp.prettier-vscode", "dbaeumer.vscode-eslint"]
4+
}

.vscode/settings.json

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
{
2+
"debug.javascript.terminalOptions": {
3+
"remoteRoot": null,
4+
"skipFiles": ["<node_internals>/**"],
5+
"sourceMaps": true
6+
},
7+
"editor.codeActionsOnSave": {
8+
"source.organizeImports.biome": "explicit",
9+
"quickfix.biome": "explicit"
10+
},
11+
"editor.defaultFormatter": "biomejs.biome",
12+
"editor.formatOnSave": true,
13+
"editor.wordSeparators": "`~!@#%^&*()-=+[{]}\\|;:'\",.<>/?",
14+
"files.eol": "\n",
15+
"files.exclude": {
16+
"**/.coverage": true,
17+
"**/.dist": true,
18+
"**/.DS_Store": true,
19+
"**/.git": true,
20+
"**/.hg": true,
21+
"**/.husky": true,
22+
"**/.svn": true,
23+
"**/CVS": true,
24+
"**/node_modules": true
25+
},
26+
"files.watcherExclude": {
27+
"**/.coverage/*/**": true,
28+
"**/.dist/**": true,
29+
"**/.git/objects/**": true,
30+
"**/.git/subtree-cache/**": true,
31+
"**/node_modules/*/**": true,
32+
"src/**/*.d.ts": true
33+
},
34+
"typescript.implementationsCodeLens.enabled": true,
35+
"typescript.preferences.autoImportFileExcludePatterns": ["**/index.ts"],
36+
"typescript.referencesCodeLens.enabled": true,
37+
"typescript.tsdk": "./node_modules/typescript/lib",
38+
"typescript.enablePromptUseWorkspaceTsdk": true
39+
}

LICENSE.md

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
Copyright (c) 2024, SkyLeague Technologies B.V.. 'SkyLeague' and the astronaut logo are trademarks of SkyLeague Technologies, registered at Chamber of Commerce in The Netherlands under number 86650564.
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4+
5+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6+
7+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# SkyLeague StarChart: Streamlined Serverless Deployment on AWS
2+
3+
StarChart is a collection of Terraform modules designed to simplify the development of serverless microservices using AWS Lambda and other AWS services, such as SQS, EventBridge, DynamoDB, S3, and more.
4+
5+
## Streamlined Function Deployment
6+
7+
With StarChart, managing multiple Lambda functions in a single repository becomes easy. Simply create `handler.yml` files alongside your handler definitions (e.g., `index.ts`). Specify the location of the `handler.yml` files, and StarChart handles the rest! The basic function definition resembles the Serverless framework, but with added opinionated syntax to accelerate development. It adheres to the Principle of Least Privilege, eliminating the need to manually create a policy for each Lambda Function you deploy.
8+
9+
## Deployment Options: Managed or Modular
10+
11+
StarChart offers two ways to deploy your microservice:
12+
13+
1. **Fully-Managed Module**: Deploy your microservice based on the `handler.yml` files next to your functions.
14+
2. **Modular Components**: Customize your deployment by selecting from several modular components that StarChart defines.
15+
16+
## Effortless Deployment with Low Configuration
17+
18+
Deploying a new Lambda Function with StarChart is seamless. Apart from the initial setup, you rarely need to modify the Terraform code. StarChart requires minimal configuration for basic deployments but allows additional customization if necessary. For example, you can configure extra IAM policies for Lambda Functions, add environment variables, or adjust settings for other resources such as SQS queues and API Gateway properties. StarChart's flexible and modular design lets you adapt it to suit your specific needs.
19+
20+
## Deployment Best Practices
21+
22+
### Separate Runtime and Persistent Components
23+
24+
While StarChart handles deploying the runtime components of your application, such as AWS Lambda and SQS, it is recommended to define the persistent deployment of your stack as a separate Terraform deployment. The persistent part of the stack refers to anything that holds data or plays a crucial role in your AWS account's infrastructure.
25+
26+
### Two-Stage Microservice Deployment
27+
28+
For example, a central repository could deploy EventBridge and an S3 bucket that holds Lambda Function artifacts, along with centrally managed resources such as an API Gateway logging role. Then, several microservice repositories could deploy in two stages: a `persistent` stage and a `runtime` stage. In the `persistent` stage, define resources like S3 or DynamoDB, Textract, or Secrets Manager Secret placeholders. In the `runtime` stage, deploy all your Lambda Functions, SQS and EventBridge rules that connect them, an API Gateway with Lambda behind it, etc., – anything that can be safely discarded.
29+
30+
## Example Reference Project
31+
32+
To better understand how to use StarChart, refer to the [`skyleague-standards`](https://github.com/skyleague-internal/standards) project. This project provides a complete example of deploying a Lambda Function with TypeScript using StarChart, and demonstrates how to use the StarChart module as a starter template.

biome.json

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"extends": ["@skyleague/node-standards/biome"]
3+
}

commitlint.config.ts

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
export default { extends: ['@commitlint/config-conventional'] }

lint-staged.config.cjs

+3
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
module.exports = {
2+
'*': ['biome check --no-errors-on-unmatched --files-ignore-unknown=true --write --unsafe'],
3+
}

modules/_common/config.tf

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
variable "config" {
2+
type = object({
3+
project_name = string
4+
project_identifier = string
5+
environment = string
6+
stack = string
7+
domain = string
8+
repo_root = string
9+
})
10+
description = "The configuration for the project"
11+
nullable = false
12+
}
13+
14+
module "config" {
15+
source = "../_config"
16+
17+
project_name = var.config.project_name
18+
project_identifier = var.config.project_identifier
19+
environment = var.config.environment
20+
stack = var.config.stack
21+
domain = var.config.domain
22+
repo_root = var.config.repo_root
23+
}
24+
25+
output "config" {
26+
value = module.config
27+
}

modules/_common/group.tf

+28
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
resource "aws_resourcegroups_group" "stack" {
2+
count = var.default_tags["StackType"] != null ? 1 : 0
3+
name = "${var.config.stack}-${lower(var.default_tags["StackType"])}"
4+
tags = {
5+
Name = var.config.project_name
6+
Stack = ""
7+
}
8+
9+
resource_query {
10+
query = jsonencode({
11+
ResourceTypeFilters = ["AWS::AllSupported"],
12+
TagFilters = [
13+
{
14+
Key = "Name"
15+
Values = [var.config.project_name]
16+
},
17+
{
18+
Key = "Stack"
19+
Values = [var.config.stack]
20+
},
21+
{
22+
Key = "StackType"
23+
Values = [var.default_tags["StackType"]]
24+
}
25+
]
26+
})
27+
}
28+
}

modules/_common/variables.tf

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
variable "default_tags" {
2+
type = map(string)
3+
default = {}
4+
}

modules/_config/output.tf

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
output "project_name" {
2+
value = var.project_name
3+
}
4+
5+
output "project_identifier" {
6+
value = var.project_identifier
7+
}
8+
9+
output "environment" {
10+
value = var.environment
11+
}
12+
13+
output "stack" {
14+
value = var.stack
15+
}
16+
17+
output "domain" {
18+
value = var.domain
19+
}
20+
21+
output "repo_root" {
22+
value = var.repo_root
23+
}
24+
25+
output "stack_prefix" {
26+
value = var.stack
27+
}
28+
29+
output "resource_prefix" {
30+
value = "${var.environment}-${var.project_identifier}-${var.stack}"
31+
}

modules/_config/variables.tf

+75
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
variable "project_name" {
2+
type = string
3+
description = "Name of the project, used for tagging and naming resources"
4+
nullable = false
5+
6+
validation {
7+
condition = 0 < length(var.project_name) && length(var.project_name) <= 36
8+
error_message = "The project name must be non empty and smaller or equal to 36 characters."
9+
}
10+
11+
validation {
12+
condition = can(regex("^[0-9A-Za-z-]+$", var.project_name))
13+
error_message = "For the project name value only a-z, A-Z and 0-9, and - are allowed."
14+
}
15+
}
16+
17+
variable "project_identifier" {
18+
type = string
19+
description = "Short prefix of the project, used for tagging and naming resources"
20+
nullable = false
21+
22+
validation {
23+
condition = 0 < length(var.project_identifier) && length(var.project_identifier) <= 6
24+
error_message = "The project prefix must be non empty and smaller or equal to 6 characters."
25+
}
26+
27+
validation {
28+
condition = can(regex("^[0-9A-Za-z]+$", var.project_identifier))
29+
error_message = "For the project prefix value only a-z, A-Z and 0-9 are allowed."
30+
}
31+
}
32+
33+
variable "environment" {
34+
type = string
35+
description = "value of the environment variable"
36+
nullable = false
37+
38+
validation {
39+
condition = 0 < length(var.environment) && length(var.environment) <= 6
40+
error_message = "The project prefix must be non empty and smaller or equal to 6 characters."
41+
}
42+
43+
validation {
44+
condition = can(regex("^[0-9A-Za-z]+$", var.environment))
45+
error_message = "For the environment value only a-z, A-Z and 0-9 are allowed."
46+
}
47+
}
48+
49+
variable "stack" {
50+
type = string
51+
description = "Name of the stack used for logical separation of resources"
52+
nullable = false
53+
54+
validation {
55+
condition = 0 < length(var.stack) && length(var.stack) <= 36
56+
error_message = "The stack name must be non empty and smaller or equal to 36 characters."
57+
}
58+
59+
validation {
60+
condition = can(regex("^[0-9A-Za-z-]+$", var.stack))
61+
error_message = "For the stack name value only a-z, A-Z and 0-9, and - are allowed."
62+
}
63+
}
64+
65+
variable "domain" {
66+
type = string
67+
description = "Domain of the project, used for tagging and naming resources"
68+
nullable = false
69+
}
70+
71+
variable "repo_root" {
72+
type = string
73+
description = "Root of the repository"
74+
nullable = false
75+
}

modules/appsync-function/function.tf

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
resource "aws_appsync_function" "function" {
2+
for_each = var.function
3+
data_source = var.datasource
4+
api_id = var.api_id
5+
6+
max_batch_size = each.value.max_batch_size
7+
name = each.value.name
8+
description = each.value.description
9+
code = var.function_code[each.key]
10+
11+
dynamic "runtime" {
12+
for_each = each.value.runtime != null ? [each.value.runtime] : []
13+
content {
14+
name = runtime.value.name
15+
runtime_version = runtime.value.runtime_version
16+
}
17+
}
18+
}
19+
20+
output "function" {
21+
value = aws_appsync_function.function
22+
}

modules/appsync-function/iam.tf

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
resource "aws_iam_role_policy" "this" {
2+
for_each = { for name, policy in var.policy : name => policy.json if policy != null }
3+
role = var.role.id
4+
policy = each.value
5+
}

modules/appsync-function/variables.tf

+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
variable "datasource" {
2+
type = string
3+
description = "The AppSync datasource to attach the resolvers to."
4+
}
5+
6+
variable "role" {
7+
type = object({ id = string })
8+
description = "The IAM role to be attached to the datasources."
9+
}
10+
11+
variable "policy" {
12+
type = map(object({ json = string }))
13+
description = "The policies to be attached to the datasources."
14+
}
15+
16+
variable "function" {
17+
type = map(object({
18+
# datasource_id = string
19+
# artifact_dir = string
20+
# function_id = string
21+
22+
max_batch_size = optional(number)
23+
name = optional(string)
24+
description = optional(string)
25+
# sync_config = optional(any)
26+
27+
runtime = optional(object({
28+
name = string
29+
runtime_version = string
30+
}))
31+
}))
32+
description = "The AppSync functions to be attached to datasources. Keys are the datasource IDs."
33+
}
34+
35+
variable "function_code" {
36+
type = map(string)
37+
sensitive = true
38+
description = "The code to be executed by the function."
39+
}
40+
41+
variable "api_id" {
42+
type = string
43+
description = "The AppSync API ID."
44+
}

modules/appsync-resolver/iam.tf

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
resource "aws_iam_role_policy" "this" {
2+
for_each = { for name, policy in var.policy : name => policy.json if policy != null }
3+
role = var.role.id
4+
policy = each.value
5+
}

0 commit comments

Comments
 (0)