Skip to content

Latest commit

 

History

History
900 lines (817 loc) · 17.2 KB

README.md

File metadata and controls

900 lines (817 loc) · 17.2 KB

AIRLOCK

Go Report Card License

Overview

Translate between JSON Schema and common IaC languages (opentofu, helm, bicep)

Getting Started

Prerequisites

  • Go version 1.22

Installation

To install this package, simply run:

go get -u github.com/massdriver-cloud/airlock

Usage

OpenTofu

OpenTofu -> JSON Schema:

airlock opentofu input /path/to/module
Example

main.tf:

provider "aws" {
  region = var.region
}

variable "bucket_name" {
  description = "The name of the S3 bucket."
  type        = string
}

variable "region" {
  description = "The AWS region to create the S3 bucket in."
  type        = string
  default     = "us-east-1"
}

variable "enable_versioning" {
  description = "Enable versioning on the S3 bucket."
  type        = bool
  default     = false
}

variable "acl" {
  description = "The access control list for the S3 bucket."
  type        = string
  default     = "private"
}

resource "aws_s3_bucket" "example" {
  bucket = var.bucket_name
  acl    = var.acl

  versioning {
    enabled = var.enable_versioning
  }

  lifecycle_rule {
    id      = "delete-old-versions"
    enabled = true

    noncurrent_version_expiration {
      days = 30
    }
  }

  tags = {
    Name        = var.bucket_name
    Environment = "Dev"
  }
}

output "bucket_id" {
  value = aws_s3_bucket.example.id
}

output "bucket_arn" {
  value = aws_s3_bucket.example.arn
}

JSON output:

{
  "properties": {
    "bucket_name": {
      "type": "string",
      "title": "bucket_name",
      "description": "The name of the S3 bucket."
    },
    "region": {
      "type": "string",
      "title": "region",
      "description": "The AWS region to create the S3 bucket in.",
      "default": "us-east-1"
    },
    "enable_versioning": {
      "type": "boolean",
      "title": "enable_versioning",
      "description": "Enable versioning on the S3 bucket.",
      "default": false
    },
    "acl": {
      "type": "string",
      "title": "acl",
      "description": "The access control list for the S3 bucket.",
      "default": "private"
    }
  },
  "required": [
    "acl",
    "bucket_name",
    "enable_versioning",
    "region"
  ]
}

JSON Schema -> OpenTofu:

airlock opentofu output /path/to/schema.json
Example

schema.json:

{
  "properties": {
    "form": {
      "title": "Form",
      "type": "object",
      "required": [
        "firstName",
        "lastName"
      ],
      "properties": {
        "firstName": {
          "type": "string",
          "title": "First name"
        },
        "lastName": {
          "type": "string",
          "title": "Last name"
        },
        "age": {
          "type": "integer",
          "title": "Age"
        },
        "bio": {
          "type": "string",
          "title": "Bio"
        },
        "password": {
          "type": "string",
          "title": "Password",
          "minLength": 3
        },
        "telephone": {
          "type": "string",
          "title": "Telephone",
          "minLength": 10
        }
      }
    }
  }
}

OpenTofu output:

variable "form" {
  type = object({
    firstName = string
    lastName  = string
    age       = optional(number)
    bio       = optional(string)
    password  = optional(string)
    telephone = optional(string)
  })
  default = null
}

Helm

Helm -> JSON Schema:

airlock helm input /path/to/values.yaml
Example

values.yaml:

name: my-app

image:
  repository: my-app-repo/my-app
  tag: latest
  pullPolicy: IfNotPresent

service:
  enabled: true
  type: ClusterIP
  port: 80
  targetPort: 8080

ingress:
  enabled: false
  path: /
  hosts:
    - host: my-app.local
      paths:
        - /
  tls: []

replicaCount: 1

resources:
  requests:
    cpu: "100m"
    memory: "256Mi"
  limits:
    cpu: "500m"
    memory: "512Mi"

env:
  - name: DATABASE_URL
    value: postgres://user:password@postgres:5432/mydb
  - name: APP_SECRET
    value: supersecretkey

logging:
  level: info

persistence:
  enabled: false
  storageClass: "standard"
  accessModes:
    - ReadWriteOnce
  size: 1Gi

annotations: {}

nodeSelector: {}

tolerations: []

affinity: {}

JSON Schema output:

{
  "properties": {
    "name": {
      "type": "string",
      "title": "name",
      "description": "Application name",
      "default": "my-app"
    },
    "image": {
      "properties": {
        "repository": {
          "type": "string",
          "title": "repository",
          "default": "my-app-repo/my-app"
        },
        "tag": {
          "type": "string",
          "title": "tag",
          "default": "latest"
        },
        "pullPolicy": {
          "type": "string",
          "title": "pullPolicy",
          "default": "IfNotPresent"
        }
      },
      "type": "object",
      "required": [
        "repository",
        "tag",
        "pullPolicy"
      ],
      "title": "image",
      "description": "Image configuration"
    },
    "service": {
      "properties": {
        "enabled": {
          "type": "boolean",
          "title": "enabled",
          "default": true
        },
        "type": {
          "type": "string",
          "title": "type",
          "default": "ClusterIP"
        },
        "port": {
          "type": "integer",
          "title": "port",
          "default": 80
        },
        "targetPort": {
          "type": "integer",
          "title": "targetPort",
          "default": 8080
        }
      },
      "type": "object",
      "required": [
        "enabled",
        "type",
        "port",
        "targetPort"
      ],
      "title": "service",
      "description": "Service configuration"
    },
    "ingress": {
      "properties": {
        "enabled": {
          "type": "boolean",
          "title": "enabled",
          "default": false
        },
        "path": {
          "type": "string",
          "title": "path",
          "default": "/"
        },
        "hosts": {
          "items": {
            "properties": {
              "host": {
                "type": "string",
                "title": "host",
                "default": "my-app.local"
              },
              "paths": {
                "items": {
                  "type": "string",
                  "default": "/"
                },
                "type": "array",
                "title": "paths",
                "default": [
                  "/"
                ]
              }
            },
            "type": "object",
            "required": [
              "host",
              "paths"
            ]
          },
          "type": "array",
          "title": "hosts",
          "default": [
            {
              "host": "my-app.local",
              "paths": [
                "/"
              ]
            }
          ]
        },
        "tls": {
          "items": {
            "properties": {
              "secretName": {
                "type": "string",
                "title": "secretName",
                "default": "my-app-tls"
              },
              "hosts": {
                "items": {
                  "type": "string",
                  "default": "my-app.local"
                },
                "type": "array",
                "title": "hosts",
                "default": [
                  "my-app.local"
                ]
              }
            },
            "type": "object",
            "required": [
              "secretName",
              "hosts"
            ]
          },
          "type": "array",
          "title": "tls",
          "default": [
            {
              "hosts": [
                "my-app.local"
              ],
              "secretName": "my-app-tls"
            }
          ]
        }
      },
      "type": "object",
      "required": [
        "enabled",
        "path",
        "hosts",
        "tls"
      ],
      "title": "ingress",
      "description": "Ingress configuration"
    },
    "replicaCount": {
      "type": "integer",
      "title": "replicaCount",
      "description": "Replicas configuration",
      "default": 1
    },
    "resources": {
      "properties": {
        "requests": {
          "properties": {
            "cpu": {
              "type": "string",
              "title": "cpu",
              "default": "100m"
            },
            "memory": {
              "type": "string",
              "title": "memory",
              "default": "256Mi"
            }
          },
          "type": "object",
          "required": [
            "cpu",
            "memory"
          ],
          "title": "requests"
        },
        "limits": {
          "properties": {
            "cpu": {
              "type": "string",
              "title": "cpu",
              "default": "500m"
            },
            "memory": {
              "type": "string",
              "title": "memory",
              "default": "512Mi"
            }
          },
          "type": "object",
          "required": [
            "cpu",
            "memory"
          ],
          "title": "limits"
        }
      },
      "type": "object",
      "required": [
        "requests",
        "limits"
      ],
      "title": "resources",
      "description": "Resource requests and limits"
    },
    "env": {
      "items": {
        "properties": {
          "name": {
            "type": "string",
            "title": "name",
            "default": "DATABASE_URL"
          },
          "value": {
            "type": "string",
            "title": "value",
            "default": "postgres://user:password@postgres:5432/mydb"
          }
        },
        "type": "object",
        "required": [
          "name",
          "value"
        ]
      },
      "type": "array",
      "title": "env",
      "description": "Environment variables",
      "default": [
        {
          "name": "DATABASE_URL",
          "value": "postgres://user:password@postgres:5432/mydb"
        },
        {
          "name": "APP_SECRET",
          "value": "supersecretkey"
        }
      ]
    },
    "logging": {
      "properties": {
        "level": {
          "type": "string",
          "title": "level",
          "default": "info"
        }
      },
      "type": "object",
      "required": [
        "level"
      ],
      "title": "logging",
      "description": "Logging configuration"
    },
    "persistence": {
      "properties": {
        "enabled": {
          "type": "boolean",
          "title": "enabled",
          "default": false
        },
        "storageClass": {
          "type": "string",
          "title": "storageClass",
          "default": "standard"
        },
        "accessModes": {
          "items": {
            "type": "string",
            "default": "ReadWriteOnce"
          },
          "type": "array",
          "title": "accessModes",
          "default": [
            "ReadWriteOnce"
          ]
        },
        "size": {
          "type": "string",
          "title": "size",
          "default": "1Gi"
        }
      },
      "type": "object",
      "required": [
        "enabled",
        "storageClass",
        "accessModes",
        "size"
      ],
      "title": "persistence",
      "description": "Persistent storage configuration"
    },
    "annotations": {
      "properties": {},
      "type": "object",
      "title": "annotations",
      "description": "Custom annotations"
    },
    "nodeSelector": {
      "properties": {},
      "type": "object",
      "title": "nodeSelector",
      "description": "Node selector"
    },
    "tolerations": {
      "items": {
        "properties": {
          "key": {
            "type": "string",
            "title": "key",
            "default": "key1"
          },
          "operator": {
            "type": "string",
            "title": "operator",
            "default": "Equal"
          },
          "value": {
            "type": "string",
            "title": "value",
            "default": "value1"
          },
          "effect": {
            "type": "string",
            "title": "effect",
            "default": "NoSchedule"
          }
        },
        "type": "object",
        "required": [
          "key",
          "operator",
          "value",
          "effect"
        ]
      },
      "type": "array",
      "title": "tolerations",
      "description": "Tolerations",
      "default": [
        {
          "effect": "NoSchedule",
          "key": "key1",
          "operator": "Equal",
          "value": "value1"
        },
        {
          "effect": "NoExecute",
          "key": "key2",
          "operator": "Exists"
        }
      ]
    },
    "affinity": {
      "properties": {},
      "type": "object",
      "title": "affinity",
      "description": "Affinity settings"
    }
  },
  "type": "object",
  "required": [
    "name",
    "image",
    "service",
    "ingress",
    "replicaCount",
    "resources",
    "env",
    "logging",
    "persistence",
    "annotations",
    "nodeSelector",
    "tolerations",
    "affinity"
  ]
}

Bicep

Bicep -> JSON Schema:

airlock bicep input /path/to/template.bicep
Example

template.bicep:

@description('The name of the resource group.')
param resourceGroupName string

@description('The location where the storage account will be deployed.')
param location string = resourceGroup().location

@description('The name of the storage account.')
@secure()
param storageAccountName string

@description('The SKU for the storage account.')
@allowed([
  'Standard_LRS'
  'Standard_GRS'
  'Standard_RAGRS'
  'Standard_ZRS'
  'Premium_LRS'
])
param sku string = 'Standard_LRS'

@description('The kind of storage account.')
@allowed([
  'StorageV2'
  'Storage'
  'BlobStorage'
  'FileStorage'
  'BlockBlobStorage'
])
param kind string = 'StorageV2'

resource storageAccount 'Microsoft.Storage/storageAccounts@2021-09-01' = {
  name: storageAccountName
  location: location
  sku: {
    name: sku
  }
  kind: kind
  properties: {
    supportsHttpsTrafficOnly: true
  }
}

output storageAccountId string = storageAccount.id
output storageAccountPrimaryEndpoints object = storageAccount.properties.primaryEndpoints

JSON Schema output:

{
  "properties": {
    "storageAccountName": {
      "type": "string",
      "format": "password",
      "title": "storageAccountName",
      "description": "The name of the storage account."
    },
    "kind": {
      "type": "string",
      "enum": [
        "StorageV2",
        "Storage",
        "BlobStorage",
        "FileStorage",
        "BlockBlobStorage"
      ],
      "title": "kind",
      "description": "The kind of storage account.",
      "default": "StorageV2"
    },
    "location": {
      "type": "string",
      "title": "location",
      "description": "The location where the storage account will be deployed.",
      "default": "[resourceGroup().location]"
    },
    "resourceGroupName": {
      "type": "string",
      "title": "resourceGroupName",
      "description": "The name of the resource group."
    },
    "sku": {
      "type": "string",
      "enum": [
        "Standard_LRS",
        "Standard_GRS",
        "Standard_RAGRS",
        "Standard_ZRS",
        "Premium_LRS"
      ],
      "title": "sku",
      "description": "The SKU for the storage account.",
      "default": "Standard_LRS"
    }
  },
  "type": "object",
  "required": [
    "kind",
    "location",
    "resourceGroupName",
    "sku",
    "storageAccountName"
  ]
}

JSON Schema -> Bicep:

airlock bicep output /path/to/schema.json
Example

schema.json:

{
  "properties": {
    "firstName": {
      "title": "First name",
      "type": "string"
    },
    "lastName": {
      "title": "Last name",
      "type": "string"
    },
    "phoneNumber": {
      "title": "Phone number",
      "type": "string",
      "minLength": 9,
      "maxLength": 12
    },
    "email": {
      "title": "Email",
      "type": "string",
      "minLength": 3
    },
    "age": {
      "title": "Age",
      "type": "integer",
      "minimum": 1
    },
    "ssn": {
      "title": "SSN",
      "type": "string",
      "format": "password",
      "minLength": 9,
      "maxLength": 9
    },
    "color": {
      "title": "Favorite color",
      "type": "string",
      "enum": [
        "Blue",
        "Red",
        "Yellow",
        "Other"
      ]
    },
    "active": {
      "title": "User is active",
      "description": "Is the user currently active?",
      "type": "boolean",
      "default": false
    }
  }
}

Bicep output:

param firstName string
param lastName string
@minLength(9)
@maxLength(12)
param phoneNumber string
@minLength(3)
param email string
@minValue(1)
param age int
@minLength(9)
@maxLength(9)
@secure()
param ssn string
@allowed([
  'Blue'
  'Red'
  'Yellow'
  'Other'
])
param color string
@sys.description('Is the user currently active?')
param active bool = false