From fd94e51838d57d8bc78bfd7c01ac09c7ab5e64f9 Mon Sep 17 00:00:00 2001 From: Maxim Likhachev Date: Tue, 19 Apr 2022 19:19:10 +0500 Subject: [PATCH] ++terraform-compliance-generator --- README.md | 3 +- scripts/terraform-compliance-generator | 186 +++++++++++++++++++++++++++++++++ 2 files changed, 188 insertions(+), 1 deletion(-) create mode 100755 scripts/terraform-compliance-generator diff --git a/README.md b/README.md index 4f27563..d8a3588 100644 --- a/README.md +++ b/README.md @@ -100,9 +100,10 @@ will create the symbolic link for each script in the \ directory. - [gitlab-ci-linter](scripts/gitlab-ci-linter) checks Gitlab CI Pipeline syntax via GitLab API. - [how](scripts/how) shows dev cheat sheets using [cheat.sh](https://cheat.sh). - [robodoc2html](scripts/robodoc2html) converts a documentation in the robodoc format into html. +- [terraform-compliance-generator](scripts/terraform-compliance-generator) makes a [terraform-compliance](https://terraform-compliance.com/) compatible BDD testing scenario. - [todolist](scripts/todolist) shows all `TODO` notes in current git repository. -- [vault-kv-tree](scripts/vault-kv-tree) shows all HashiCorp Vault's paths and keys recursively. - [utable](scripts/utable) shows a Unicode table. +- [vault-kv-tree](scripts/vault-kv-tree) shows all HashiCorp Vault's paths and keys recursively. ### Git diff --git a/scripts/terraform-compliance-generator b/scripts/terraform-compliance-generator new file mode 100755 index 0000000..585e354 --- /dev/null +++ b/scripts/terraform-compliance-generator @@ -0,0 +1,186 @@ +#!/usr/bin/env racket + +;;; +;;; Copyright (C) 2022, Maxim Lihachev, +;;; +;;; This program is free software: you can redistribute it and/or modify it +;;; under the terms of the GNU General Public License as published by the Free +;;; Software Foundation, version 3. +;;; +;;; This program is distributed in the hope that it will be useful, but WITHOUT +;;; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +;;; FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for +;;; more details. +;;; +;;; You should have received a copy of the GNU General Public License along with +;;; this program. If not, see . +;;; + +;;; terraform-compliance-generator can be used to generate +;;; https://terraform-compliance.com/ compatible BDD scenario. + +#lang racket/base + +(require + (for-syntax racket/base) + json + racket/format + racket/function + racket/list + racket/path + racket/port + racket/string + racket/stxparam + racket/vector) + +;;; ---------------------------------------------------------------------------- +;; +;; Terraform plan JSON: +;; +;; { +;; "resource_changes": [ +;; { +;; "type": "helm_release", +;; "change": { +;; "after": { +;; "key": value, +;; ... +;; } +;; } +;; }, +;; ... +;; ] +;; } +;; + +(define (usage) + (display-lines + (list + "terraform-bdd generates terraform-compliance acceptable BDD tests.\n" + (format "USAGE: ~a [terraform.plan.json]~%" script-file) + "Command line options:" + " --help Show this help."))) + +;;; ---------------------------------------------------------------------------- + +(define-syntax-parameter + (λ (stx) (raise-syntax-error ' "Used outside of macro." stx))) + +(define-syntax-parameter + (λ (stx) (raise-syntax-error ' "Used outside of macro." stx))) + +(define (to-string x) + (format "~a" x)) + +(define (read-terraform-plan plan-file) + (if (file-exists? plan-file) + (with-handlers + ((exn:fail? + (λ (exn) + (fprintf (current-error-port) + "ERROR: Unable to read JSON data: ~a~%" (exn-message exn)) + (exit 1)))) + (with-input-from-file plan-file + (λ () + (hash-ref (read-json) 'resource_changes)))) + (printf "ERROR: file ~a does not exist.~%" plan-file))) + +(define (fields l) + (filter (λ (xs) + (let ((x (cdr xs))) + (not (or (null? x) + (eq? (json-null) x) + (hash? x) + (list? x))))) + l)) + +(define (get-properties change) + (sort (fields + (hash->list + (hash-ref (hash-ref change 'change) 'after))) + #:key car (λ (a b) (string" "")) + (map row properties)))) + +(define-syntax-rule (GIVEN resource expr ...) + (λ (short-list properties) + (list + (format " Given I have ~a defined" (string-replace (hash-ref resource 'type) "_" " ")) + (if short-list + (map (curry expr ...) properties) + (list + (expr ... (cons (with-quotes "") (with-quotes ""))) + (EXAMPLES properties)))))) + +(define-syntax-rule (THEN _ _) + (λ (x) + (syntax-parameterize + (( (make-rename-transformer #'x))) + (format " Then it must have ~a~% And its value must be ~a" (key x) (value x))))) + +(define-syntax-rule (SCENARIO _ expr ...) + (λ (x) + (syntax-parameterize + (( (make-rename-transformer #'x))) + (let* ((properties (get-properties x)) + (long-list (<= (length properties) 3)) + (resource-type (hash-ref x 'type)) + (scenario-type (if long-list "" " Outline"))) + (list + (format "~%Scenario~a: Ensure that the ~a resource exists" scenario-type resource-type) + (expr ... long-list properties)))))) + +;;; ---------------------------------------------------------------------------- + +(define script-file (file-name-from-path (find-system-path 'run-file))) +(define args (current-command-line-arguments)) + +(when (or (vector-empty? args) + (equal? (vector-ref args 0) "--help")) + (usage) + (exit)) + +(define terraform-plan + (read-terraform-plan (vector-ref args 0))) + +;;; ---------------------------------------------------------------------------- + +(FEATURE terraform-plan + (SCENARIO + (GIVEN + (THEN CHECK )))) +