From 8d7b8e0f0fdda6f0fde21967eaee6c77a8f218f3 Mon Sep 17 00:00:00 2001
From: Doug Lagemann <douglagemann@seed-innovations.com>
Date: Fri, 15 Nov 2024 13:07:27 -0700
Subject: [PATCH] WIP

---
 docker/Dockerfile.node-vite-ui.dev            |   2 +-
 .../docker-compose-dockerfile-lint.yml        |  15 ++
 scripts/dockerfile-lint/entrypoint.sh         |  13 ++
 scripts/dockerfile-lint/monitorstatus.sh      | 141 ++++++++++++++++++
 4 files changed, 170 insertions(+), 1 deletion(-)
 create mode 100644 docker/pipeline-jobs/docker-compose-dockerfile-lint.yml
 create mode 100644 scripts/dockerfile-lint/entrypoint.sh
 create mode 100644 scripts/dockerfile-lint/monitorstatus.sh

diff --git a/docker/Dockerfile.node-vite-ui.dev b/docker/Dockerfile.node-vite-ui.dev
index 6588c33..a8f6c8d 100644
--- a/docker/Dockerfile.node-vite-ui.dev
+++ b/docker/Dockerfile.node-vite-ui.dev
@@ -10,7 +10,7 @@ USER node
 COPY --chown=node:node ./<<projectName>>-ui/package.json ./
 
 # install dependencies
-# TODO: It would be vest to ensure npm install has been run before creating the image for the first time, then the if check can be removed
+# TODO: It would be best to ensure npm install has been run before creating the image for the first time, then the if check can be removed
 RUN if test -f package-lock.json ; then npm ci ; else npm i ; fi
 
 EXPOSE 8080
diff --git a/docker/pipeline-jobs/docker-compose-dockerfile-lint.yml b/docker/pipeline-jobs/docker-compose-dockerfile-lint.yml
new file mode 100644
index 0000000..49146b2
--- /dev/null
+++ b/docker/pipeline-jobs/docker-compose-dockerfile-lint.yml
@@ -0,0 +1,15 @@
+services:
+
+  dockerfile-lint<<subProject>>:
+    image: registry1.dso.mil/ironbank/opensource/hadolint/hadolint:v2.12.0
+    container_name: dockerfile-lint<<subProject>>
+    entrypoint: ["${BASE_SCRIPTS_DIR}/dockerfile-lint/entrypoint.sh"]
+    working_dir: /root
+    environment:
+      - REPORTS_DIR=${BASE_REPORTS_DIR}/dockerfile-lint<<subProject>>
+      - SCRIPTS_DIR=${BASE_SCRIPTS_DIR}/dockerfile-lint
+      - SCAN_DIR=/app
+      - CI_JOB_NAME=dockerfile-lint<<subProject>>
+    volumes:
+      - ./:/root
+      - ./<<projectName>><<subProject>>:/app
diff --git a/scripts/dockerfile-lint/entrypoint.sh b/scripts/dockerfile-lint/entrypoint.sh
new file mode 100644
index 0000000..d27b44c
--- /dev/null
+++ b/scripts/dockerfile-lint/entrypoint.sh
@@ -0,0 +1,13 @@
+'${SCRIPTS_DIR}/monitor/monitorstatus.sh -j ${CI_JOB_NAME} -s fail -r config -l "Job URL: ${CI_JOB_URL}"'
+echo "removing any existing hadolint config."
+rm -rf .config/hadolint.yml .config/hadolint.yaml
+hadolint $APPROVED_REGISTRY $DOCKERFILE_LOC --failure-threshold warning | tee ${REPORTS_DIR}/${CI_JOB_NAME}.out
+
+if [ "$?" == "0" ]; then
+  ${SCRIPTS_DIR}/monitorstatus.sh -j ${CI_JOB_NAME} -s pass -r pass
+else
+  if [ -s "${REPORTS_DIR}/${CI_JOB_NAME}.out" ]; then
+    ${SCRIPTS_DIR}/monitor/monitorstatus.sh -j ${CI_JOB_NAME} -r findings -l "${CI_JOB_NAME} process found findings, check job for details"
+  fi
+  exit 1
+fi
diff --git a/scripts/dockerfile-lint/monitorstatus.sh b/scripts/dockerfile-lint/monitorstatus.sh
new file mode 100644
index 0000000..abfb5ce
--- /dev/null
+++ b/scripts/dockerfile-lint/monitorstatus.sh
@@ -0,0 +1,141 @@
+#!/bin/bash
+
+LOG_DIR="${REPORTS_DIR}"
+LOG_NAME="monitor_status"
+LOG_EXT=".yaml"
+LOG_FILE="${LOG_DIR}${LOG_NAME}${LOG_EXT}"
+STATUS_VALUES=("pass" "fail")
+REASON_VALUES=("defect" "config" "findings" "pass")
+
+job_name="${LOG_NAME}"
+status_file="${LOG_FILE}"
+status=""
+reason=""
+log=""
+
+function help_message() {
+  IFS="|"
+  echo "Usage: $0 [-h] [-s arg] [-r arg] [-l arg] [-f arg] [-j arg]" >&2
+  echo "  -h  Display this message" >&2
+  echo "  -s  Job status with value from (${STATUS_VALUES[*]})" >&2
+  echo "  -r  Job reason with value from (${REASON_VALUES[*]})" >&2
+  echo "  -l  Log message appended to list" >&2
+  echo "  -f  Path to log file, current value: ${LOG_DIR}" >&2
+  echo "  -j  Job name for status file creation, current value: ${LOG_NAME}" >&2
+  echo "      File extension for log file: ${LOG_EXT}" >&2
+}
+
+function error_message() {
+    echo "$1" >&2
+    exit 1
+}
+
+function check_string_in_list() {
+  local string="$1"
+  local list=("${@:2}")
+
+  for value in "${list[@]}"; do
+    if [[ "$string" == "$value" ]]; then
+      return 1
+    fi
+  done
+
+  return 0
+}
+
+USE_YQ=
+if command -v yq 2>&1 >/dev/null; then
+  USE_YQ="true"
+fi
+
+while getopts :hj:s:r:l:f: flag
+do 
+  case "${flag}" in 
+    h)
+      help_message
+      exit 0
+      ;;
+    s)
+      status=${OPTARG}
+      if check_string_in_list "$status" "${STATUS_VALUES[@]}"; then
+        error_message "Status value not in (${STATUS_VALUES[*]})"
+      fi 
+      ;;
+    r)
+      reason=${OPTARG}
+      if check_string_in_list "$reason" "${REASON_VALUES[@]}"; then
+        error_message "Reason value not in (${REASON_VALUES[*]})"
+      fi
+      ;;
+    l)
+      log=${OPTARG}
+      ;;
+    j)
+      job_name=${OPTARG}
+      status_file="${LOG_DIR}${job_name}${LOG_EXT}"
+      ;;
+    f)
+      LOG_FILE=${OPTARG}
+      ;;
+    :)
+      echo "Option -${OPTARG} requires an argument." >&2
+      echo ""
+      help_message
+      exit 1
+      ;;
+    ?)
+      echo "Invalid option: -${OPTARG}." >&2
+      echo ""
+      help_message
+      exit 1
+      ;;
+  esac
+done
+
+if [ -f "$status_file" ]; then
+  if [ -n "$status" ]; then
+    if [ -n "$USE_YQ" ]; then
+      yq -Yi ".status=\"$status\"" "$status_file"
+    else
+      tmpfile=$(mktemp)
+      cp "$status_file" "$tmpfile"
+      awk "{sub(/status: .*/,\"status: ${status}\"); print}" "$tmpfile" > "$status_file"
+      rm "$tmpfile"
+    fi
+  fi 
+
+  if [ -n "$reason" ]; then
+    if [ -n "$USE_YQ" ]; then
+      yq -Yi ".reason=\"$reason\"" "$status_file"
+    else
+      tmpfile=$(mktemp)
+      cp "$status_file" "$tmpfile"
+      awk "{sub(/reason: .*/,\"reason: ${reason}\"); print}" "$tmpfile" > "$status_file"
+      rm "$tmpfile"
+    fi
+  fi 
+else
+  if [ -z "$status" ]; then
+    error_message "Status code is required from (${STATUS_VALUES[*]}) during initial log creation"
+  fi 
+
+  if [ -z "$reason" ]; then
+    error_message "Reason code is required from (${REASON_VALUES[*]}) during initial log creation"
+  fi 
+
+  cat <<EOF > "$status_file"
+status: "$status"
+reason: "$reason"
+log:
+EOF
+fi
+
+if [ -n "$log" ]; then
+  if [ -f "$status_file" ]; then
+    echo "  - \"${log}\"" >> "$status_file"
+  else 
+    error_message "No log file exists (${status_file}) to append log message"
+  fi 
+fi 
+
+exit 0
-- 
GitLab