Skip to content

IaC

TIP

Одна из главных особенностей - это использование единой JSON-схемы не только для генерации кода, но и для описания схемы таблицы в DynamoDB используя IaC инструменты.

WARNING

Ниже представлены варианты как можно работать с JSON-схемой и IaC-инструментами.

Это лишь несколько вариантов, а конкретная реализация может иметь совершенно другие способы и структуры в зависимости от конкретных нужд и соглашений внутри ваших проектов.

GoDyno никак не регламентирует использование IaC инструментов.

🌍 Terraform

Пример модуля для описания DynamoDB таблицы

bash
project/
  ├── main.tf  
  └── modules/
       └── dynamodb/
            ├── dynamo.tf 
            ├── variables.tf
            └── outputs.tf
hcl
terraform {
  required_version = ">= 1.0"
  required_providers {
    aws = {
      source  = "hashicorp/aws"
      version = "~> 5.0"
    }
  }
}

provider "aws" {
  region = "us-east-1"
}

# --- >

module "schema_table" {
  source = "./modules/dynamodb"
  schema = jsondecode(file("${path.module}/schema.json"))
}

# --- >

output "table_name" {
  value = module.schema_table.table_name
}

output "table_arn" {
  value = module.schema_table.table_arn
}
hcl
locals {
  key_attributes = var.schema.attributes
      
  gsi_indexes = [
    for idx in var.schema.secondary_indexes : idx
    if try(idx.type, "GSI") == "GSI"
  ]

  lsi_indexes = [
    for idx in var.schema.secondary_indexes : idx
    if try(idx.type, "LSI") == "LSI"
  ]
}

# --- >

resource "aws_dynamodb_table" "this" {
  name           = var.schema.table_name
  billing_mode   = "PAY_PER_REQUEST"
  hash_key       = var.schema.hash_key
  range_key      = var.schema.range_key

  dynamic "attribute" {
    for_each = local.key_attributes
    content {
      name = attribute.value.name
      type = attribute.value.type
    }
  }

  dynamic "global_secondary_index" {
    for_each = local.gsi_indexes
    content {
      name               = global_secondary_index.value.name
      hash_key           = global_secondary_index.value.hash_key
      range_key          = try(global_secondary_index.value.range_key, null)
      projection_type    = global_secondary_index.value.projection_type
      non_key_attributes = global_secondary_index.value.projection_type == "INCLUDE" ? global_secondary_index.value.non_key_attributes : null
    }
  }

  dynamic "local_secondary_index" {
    for_each = local.lsi_indexes
    content {
      name               = local_secondary_index.value.name
      range_key          = local_secondary_index.value.range_key
      projection_type    = local_secondary_index.value.projection_type
      non_key_attributes = local_secondary_index.value.projection_type == "INCLUDE" ? local_secondary_index.value.non_key_attributes : null
    }
  }

  tags = {
    Name      = var.schema.table_name
    ManagedBy = "go-dyno"
  }
}
hcl
variable "schema" {
  type = object({
    table_name           = string
    hash_key             = string
    range_key            = optional(string)
    attributes           = list(object({
      name = string
      type = string
    }))
    common_attributes = optional(list(object({
      name = string
      type = string
    })), [])
    secondary_indexes = optional(list(object({
      name               = string
      type               = optional(string, "GSI")
      hash_key           = optional(string)
      range_key          = optional(string)
      projection_type    = string
      non_key_attributes = optional(list(string), [])
      read_capacity      = optional(number)
      write_capacity     = optional(number)
    })), [])
  })
  description = "DynamoDB table schema from go-dyno JSON"
}
hcl
output "table_name" {
  description = "Name of the DynamoDB table"
  value       = aws_dynamodb_table.this.name
}

output "table_arn" {
  description = "ARN of the DynamoDB table"
  value       = aws_dynamodb_table.this.arn
}

output "table_id" {
  description = "ID of the DynamoDB table"
  value       = aws_dynamodb_table.this.id
}

output "hash_key" {
  description = "Hash key of the table"
  value       = aws_dynamodb_table.this.hash_key
}

output "range_key" {
  description = "Range key of the table"
  value       = aws_dynamodb_table.this.range_key
}

Применение

bash
terraform init && terraform apply

Пример в один файл

variable "schema" {
  type = object({
    table_name           = string
    hash_key             = string
    range_key            = optional(string)
    attributes           = list(object({
      name = string
      type = string
    }))
    secondary_indexes = optional(list(object({
      name               = string
      type               = optional(string, "GSI")
      hash_key           = optional(string)
      range_key          = optional(string)
      projection_type    = string
      non_key_attributes = optional(list(string))
      read_capacity      = optional(number)
      write_capacity     = optional(number)
    })))
  })
}

locals {
  gsi_indexes = [
    for idx in coalesce(var.schema.secondary_indexes, []) : idx
    if lookup(idx, "type", "GSI") == "GSI"
  ]

  lsi_indexes = [
    for idx in coalesce(var.schema.secondary_indexes, []) : idx
    if lookup(idx, "type", "LSI") == "LSI"
  ]
}

resource "aws_dynamodb_table" "this" {
  name         = var.schema.table_name
  billing_mode = "PAY_PER_REQUEST"
  hash_key     = var.schema.hash_key
  range_key    = var.schema.range_key

  dynamic "attribute" {
    for_each = var.schema.attributes
    content {
      name = attribute.value.name
      type = attribute.value.type
    }
  }

  dynamic "global_secondary_index" {
    for_each = local.gsi_indexes
    content {
      name               = global_secondary_index.value.name
      hash_key           = global_secondary_index.value.hash_key
      range_key          = lookup(global_secondary_index.value, "range_key", null)
      projection_type    = global_secondary_index.value.projection_type
      read_capacity      = global_secondary_index.value.read_capacity
      write_capacity     = global_secondary_index.value.write_capacity
      non_key_attributes = global_secondary_index.value.projection_type == "INCLUDE" ? global_secondary_index.value.non_key_attributes : null
    }
  }

  dynamic "local_secondary_index" {
    for_each = local.lsi_indexes
    content {
      name               = local_secondary_index.value.name
      range_key          = local_secondary_index.value.range_key
      projection_type    = local_secondary_index.value.projection_type
      non_key_attributes = local_secondary_index.value.projection_type == "INCLUDE" ? local_secondary_index.value.non_key_attributes : null
    }
  }
}

Применение

bash
# как пример, динамически задаем шаблон через EnvVar
export TF_VAR_schema=$(cat schema.json)

terraform init && terraform apply

Выпущено под лицензией MIT.