Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add atmos components validation using JSON Schema and OPA policies #207

Merged
merged 43 commits into from Oct 3, 2022
Merged
Show file tree
Hide file tree
Changes from 37 commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
57b452e
Updates
aknysh Sep 24, 2022
45a7d19
Add schames and validation
aknysh Sep 24, 2022
b64209d
Add schames and validation
aknysh Sep 24, 2022
6c737ee
Add schemas and validation
aknysh Sep 24, 2022
aa3d36a
Add schemas and validation
aknysh Sep 24, 2022
34bc3c0
Add schemas and validation
aknysh Sep 24, 2022
78594c3
Add schemas and validation
aknysh Sep 24, 2022
d39a19d
Add schemas and validation
aknysh Sep 24, 2022
98fbf34
Add schemas and validation
aknysh Sep 26, 2022
a2b1997
Add schemas and validation
aknysh Sep 26, 2022
29329d7
Add schemas and validation
aknysh Sep 26, 2022
05f2763
Add schemas and validation
aknysh Sep 26, 2022
29599ea
Add schemas and validation
aknysh Sep 26, 2022
1e83e26
Add schemas and validation
aknysh Sep 26, 2022
07742f5
Add schemas and validation
aknysh Sep 26, 2022
195e50e
Add schemas and validation
aknysh Sep 26, 2022
d82f2aa
Add schemas and validation
aknysh Sep 26, 2022
7c8491e
Add schemas and validation
aknysh Sep 27, 2022
e3e77b8
Add schemas and validation
aknysh Sep 27, 2022
97ee1f5
Add schemas and validation
aknysh Sep 27, 2022
edd74df
Add schemas and validation
aknysh Sep 27, 2022
1c0fef7
Add schemas and validation
aknysh Sep 27, 2022
8bb05ed
Add schemas and validation
aknysh Sep 28, 2022
d10a3d9
Add schemas and validation
aknysh Sep 28, 2022
28fa417
Add schemas and validation
aknysh Sep 28, 2022
3754df4
Add schemas and validation
aknysh Sep 28, 2022
c712e05
Add schemas and validation
aknysh Sep 28, 2022
2ec0518
Add schemas and validation
aknysh Sep 29, 2022
6643a58
Add schemas and validation
aknysh Sep 29, 2022
99b505c
Add schemas and validation
aknysh Sep 29, 2022
6a3bee9
Add schemas and validation
aknysh Sep 29, 2022
2437f46
Add schemas and validation
aknysh Sep 29, 2022
1604800
Add schemas and validation
aknysh Sep 29, 2022
fd528e2
Add schemas and validation
aknysh Sep 29, 2022
b0a3905
Add schemas and validation
aknysh Sep 29, 2022
c17b1d5
Add schemas and validation
aknysh Sep 29, 2022
fdfc193
Add schemas and validation
aknysh Sep 29, 2022
bc44eef
Update VPC component config
aknysh Sep 30, 2022
9610d84
Add validation
aknysh Oct 1, 2022
7defcfb
Add validation
aknysh Oct 2, 2022
ba42244
Add validation
aknysh Oct 2, 2022
d680093
Add validation
aknysh Oct 2, 2022
3987536
Add validation
aknysh Oct 2, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
20 changes: 19 additions & 1 deletion atmos.yaml
Expand Up @@ -185,9 +185,27 @@ integrations:
steps:
- run: terraform init -input=false
# When using workspaces, you need to select the workspace using the $WORKSPACE environment variable
- run: terraform workspace select $WORKSPACE
- run: terraform workspace select $WORKSPACE || terraform workspace new $WORKSPACE
# You must output the plan using `-out $PLANFILE` because Atlantis expects plans to be in a specific location
- run: terraform plan -input=false -refresh -out $PLANFILE -var-file varfiles/$PROJECT_NAME.tfvars.json
apply:
steps:
- run: terraform apply $PLANFILE

# Validation schemas (for validating atmos stacks and components)
schemas:
# https://json-schema.org
jsonschema:
# Can also be set using `ATMOS_SCHEMAS_JSONSCHEMA_BASE_PATH` ENV var, or `--schemas-jsonschema-dir` command-line arguments
# Supports both absolute and relative paths
base_path: "stacks/schemas/jsonschema"
# https://www.openpolicyagent.org
opa:
# Can also be set using `ATMOS_SCHEMAS_OPA_BASE_PATH` ENV var, or `--schemas-opa-dir` command-line arguments
# Supports both absolute and relative paths
base_path: "stacks/schemas/opa"
# https://cuelang.org
cue:
# Can also be set using `ATMOS_SCHEMAS_CUE_BASE_PATH` ENV var, or `--schemas-cue-dir` command-line arguments
# Supports both absolute and relative paths
base_path: "stacks/schemas/cue"
9 changes: 5 additions & 4 deletions cmd/cmd_utils.go
Expand Up @@ -17,14 +17,15 @@ var (
// All custom top-level commands will be checked against this map in order to not override `atmos` top-level commands,
// but just add subcommands to them
existingTopLevelCommands = map[string]*cobra.Command{
"terraform": terraformCmd,
"helmfile": helmfileCmd,
"describe": describeCmd,
"atlantis": atlantisCmd,
"aws": awsCmd,
"describe": describeCmd,
"helmfile": helmfileCmd,
"terraform": terraformCmd,
"validate": validateCmd,
"vendor": vendorCmd,
"workflow": workflowCmd,
"version": versionCmd,
"workflow": workflowCmd,
}
)

Expand Down
36 changes: 36 additions & 0 deletions cmd/validate_component.go
@@ -0,0 +1,36 @@
package cmd

import (
e "github.com/cloudposse/atmos/internal/exec"
u "github.com/cloudposse/atmos/pkg/utils"
"github.com/spf13/cobra"
)

// validateComponentCmd validates atmos components
var validateComponentCmd = &cobra.Command{
Use: "component",
Short: "Execute 'validate component' command",
Long: `This command validates an atmos component in a stack using Json Schema, OPA or CUE policies: atmos validate component <component> -s <stack> --schema-path <schema_path> --schema-type <jsonschema|opa|cue>`,
FParseErrWhitelist: struct{ UnknownFlags bool }{UnknownFlags: false},
Run: func(cmd *cobra.Command, args []string) {
err := e.ExecuteValidateComponentCmd(cmd, args)
if err != nil {
u.PrintErrorToStdErrorAndExit(err)
}
},
}

func init() {
validateComponentCmd.DisableFlagParsing = false

validateComponentCmd.PersistentFlags().StringP("stack", "s", "", "atmos validate component <component> -s <stack> --schema-path <schema_path> --schema-type <jsonschema|opa|cue>")
validateComponentCmd.PersistentFlags().String("schema-path", "", "atmos validate component <component> -s <stack> --schema-path <schema_path> --schema-type <jsonschema|opa|cue>")
validateComponentCmd.PersistentFlags().String("schema-type", "jsonschema", "atmos validate component <component> -s <stack> --schema-path <schema_path> --schema-type <jsonschema|opa|cue>")

err := validateComponentCmd.MarkPersistentFlagRequired("stack")
if err != nil {
u.PrintErrorToStdErrorAndExit(err)
}

validateCmd.AddCommand(validateComponentCmd)
}
6 changes: 3 additions & 3 deletions examples/complete/Dockerfile
@@ -1,10 +1,10 @@
# Geodesic: https://github.com/cloudposse/geodesic/
ARG GEODESIC_VERSION=1.3.1
ARG GEODESIC_VERSION=1.3.2
ARG GEODESIC_OS=debian
# atmos: https://github.com/cloudposse/atmos
ARG ATMOS_VERSION=1.8.2
ARG ATMOS_VERSION=1.9.0
# Terraform: https://github.com/hashicorp/terraform/releases
ARG TF_VERSION=1.2.9
ARG TF_VERSION=1.3.0

FROM cloudposse/geodesic:${GEODESIC_VERSION}-${GEODESIC_OS}

Expand Down
20 changes: 19 additions & 1 deletion examples/complete/atmos.yaml
Expand Up @@ -185,9 +185,27 @@ integrations:
steps:
- run: terraform init -input=false
# When using workspaces, you need to select the workspace using the $WORKSPACE environment variable
- run: terraform workspace select $WORKSPACE
- run: terraform workspace select $WORKSPACE || terraform workspace new $WORKSPACE
# You must output the plan using `-out $PLANFILE` because Atlantis expects plans to be in a specific location
- run: terraform plan -input=false -refresh -out $PLANFILE -var-file varfiles/$PROJECT_NAME.tfvars.json
apply:
steps:
- run: terraform apply $PLANFILE

# Validation schemas (for validating atmos stacks and components)
schemas:
# https://json-schema.org
jsonschema:
# Can also be set using `ATMOS_SCHEMAS_JSONSCHEMA_BASE_PATH` ENV var, or `--schemas-jsonschema-dir` command-line arguments
# Supports both absolute and relative paths
base_path: "stacks/schemas/jsonschema"
# https://www.openpolicyagent.org
opa:
# Can also be set using `ATMOS_SCHEMAS_OPA_BASE_PATH` ENV var, or `--schemas-opa-dir` command-line arguments
# Supports both absolute and relative paths
base_path: "stacks/schemas/opa"
# https://cuelang.org
cue:
# Can also be set using `ATMOS_SCHEMAS_CUE_BASE_PATH` ENV var, or `--schemas-cue-dir` command-line arguments
# Supports both absolute and relative paths
base_path: "stacks/schemas/cue"

This file was deleted.

15 changes: 10 additions & 5 deletions examples/complete/components/terraform/infra/vpc/main.tf
Expand Up @@ -26,9 +26,14 @@ locals {

module "vpc" {
source = "cloudposse/vpc/aws"
version = "0.25.0"
version = "1.1.1"

cidr_block = var.cidr_block
ipv4_primary_cidr_block = var.cidr_block
instance_tenancy = var.instance_tenancy
dns_hostnames_enabled = var.dns_hostnames_enabled
dns_support_enabled = var.dns_support_enabled
classiclink_enabled = var.classiclink_enabled
classiclink_dns_support_enabled = var.classiclink_dns_support_enabled

tags = local.tags

Expand All @@ -37,11 +42,11 @@ module "vpc" {

module "subnets" {
source = "cloudposse/dynamic-subnets/aws"
version = "0.39.3"
version = "2.0.3"

availability_zones = local.availability_zones
cidr_block = module.vpc.vpc_cidr_block
igw_id = module.vpc.igw_id
ipv4_cidr_block = [module.vpc.vpc_cidr_block]
igw_id = [module.vpc.igw_id]
map_public_ip_on_launch = var.map_public_ip_on_launch
max_subnet_count = local.max_subnet_count
nat_gateway_enabled = var.nat_gateway_enabled
Expand Down
35 changes: 35 additions & 0 deletions examples/complete/components/terraform/infra/vpc/variables.tf
Expand Up @@ -44,6 +44,7 @@ variable "map_public_ip_on_launch" {

variable "subnet_type_tag_key" {
type = string
default = "cp.io/subnet/type"
description = "Key for subnet type tag to provide information about the type of subnets, e.g. `cpco/subnet/type=private` or `cpcp/subnet/type=public`"
}

Expand All @@ -58,3 +59,37 @@ variable "max_subnet_count" {
default = 0
description = "Sets the maximum amount of subnets to deploy. 0 will deploy a subnet for every provided availability zone (in `region_availability_zones` variable) within the region"
}

variable "instance_tenancy" {
type = string
description = "A tenancy option for instances launched into the VPC"
default = "default"
validation {
condition = contains(["default", "dedicated", "host"], var.instance_tenancy)
error_message = "Instance tenancy must be one of \"default\", \"dedicated\", or \"host\"."
}
}

variable "dns_hostnames_enabled" {
type = bool
description = "A boolean flag to enable/disable DNS hostnames in the VPC"
default = true
}

variable "dns_support_enabled" {
type = bool
description = "A boolean flag to enable/disable DNS support in the VPC"
default = true
}

variable "classiclink_enabled" {
type = bool
description = "A boolean flag to enable/disable ClassicLink for the VPC"
default = false
}

variable "classiclink_dns_support_enabled" {
type = bool
description = "A boolean flag to enable/disable ClassicLink DNS Support for the VPC"
default = false
}

This file was deleted.

20 changes: 19 additions & 1 deletion examples/complete/rootfs/usr/local/etc/atmos/atmos.yaml
Expand Up @@ -185,9 +185,27 @@ integrations:
steps:
- run: terraform init -input=false
# When using workspaces, you need to select the workspace using the $WORKSPACE environment variable
- run: terraform workspace select $WORKSPACE
- run: terraform workspace select $WORKSPACE || terraform workspace new $WORKSPACE
# You must output the plan using `-out $PLANFILE` because Atlantis expects plans to be in a specific location
- run: terraform plan -input=false -refresh -out $PLANFILE -var-file varfiles/$PROJECT_NAME.tfvars.json
apply:
steps:
- run: terraform apply $PLANFILE

# Validation schemas (for validating atmos stacks and components)
schemas:
# https://json-schema.org
jsonschema:
# Can also be set using `ATMOS_SCHEMAS_JSONSCHEMA_BASE_PATH` ENV var, or `--schemas-jsonschema-dir` command-line arguments
# Supports both absolute and relative paths
base_path: "stacks/schemas/jsonschema"
# https://www.openpolicyagent.org
opa:
# Can also be set using `ATMOS_SCHEMAS_OPA_BASE_PATH` ENV var, or `--schemas-opa-dir` command-line arguments
# Supports both absolute and relative paths
base_path: "stacks/schemas/opa"
# https://cuelang.org
cue:
# Can also be set using `ATMOS_SCHEMAS_CUE_BASE_PATH` ENV var, or `--schemas-cue-dir` command-line arguments
# Supports both absolute and relative paths
base_path: "stacks/schemas/cue"
18 changes: 18 additions & 0 deletions examples/complete/stacks/catalog/terraform/vpc.yaml
@@ -1,16 +1,34 @@
components:
terraform:
"infra/vpc":
metadata:
component: infra/vpc
backend:
s3:
workspace_key_prefix: infra-vpc
settings:
spacelift:
workspace_enabled: true
# Validation
# Supports JSON Schema and OPA policies
# All validation steps must succeed to allow the component to be provisioned
validation:
validate-infra-vpc-component-with-jsonschema:
schema_type: jsonschema
# 'schema_path' can be an absolute path or a path relative to 'schemas.jsonschema.base_path' defined in `atmos.yaml`
schema_path: validate-infra-vpc-component.json
description: Validate 'infra/vpc' component variables using JSON Schema
check-infra-vpc-component-config-with-opa-policy:
schema_type: opa
# 'schema_path' can be an absolute path or a path relative to 'schemas.opa.base_path' defined in `atmos.yaml`
schema_path: validate-infra-vpc-component.rego
description: Check 'infra/vpc' component configuration using OPA policy
vars:
enabled: true
name: "common"
subnet_type_tag_key: cp.io/subnet/type
aknysh marked this conversation as resolved.
Show resolved Hide resolved
nat_gateway_enabled: true
nat_instance_enabled: false
max_subnet_count: 3
map_public_ip_on_launch: true
dns_hostnames_enabled: true
Empty file.
@@ -0,0 +1,29 @@
{
"$id": "infra-vpc-component",
"$schema": "https://json-schema.org/draft/2020-12/schema",
"title": "infra/vpc component validation",
"description": "JSON Schema for infra/vpc atmos component.",
"type": "object",
"properties": {
"vars": {
"type": "object",
"properties": {
"region": {
"type": "string"
},
"cidr_block": {
"type": "string"
},
"map_public_ip_on_launch": {
"type": "boolean"
}
},
"additionalProperties": true,
"required": [
"region",
"cidr_block",
"map_public_ip_on_launch"
]
}
}
}
@@ -0,0 +1,23 @@
# https://www.openpolicyagent.org/docs/latest/policy-language
# https://www.openpolicyagent.org/
# https://blog.openpolicyagent.org/rego-design-principle-1-syntax-should-reflect-real-world-policies-e1a801ab8bfb
# https://github.com/open-policy-agent/library
# https://github.com/open-policy-agent/example-api-authz-go
# https://github.com/open-policy-agent/opa/issues/2104
# https://www.fugue.co/blog/5-tips-for-using-the-rego-language-for-open-policy-agent-opa

# 'package atmos' is required in all `atmos` OPA policies
package atmos

default allow := true

# In production, don't allow mapping public IPs on launch
deny_map_public_ip_on_launch_in_prod {
input.vars.stage == "prod"
input.vars.map_public_ip_on_launch == true
}

# 'atmos' looks for the 'allow' output (boolean) from OPA policies
allow := false {
deny_map_public_ip_on_launch_in_prod
}