From e274032d476419b004ce91cf3dac539841f64038 Mon Sep 17 00:00:00 2001
From: Floppy Disk <kklinch0@gmail.com>
Date: Tue, 15 Oct 2024 14:56:11 +0300
Subject: [PATCH 1/2] add e2e for apps

---
 .pre-commit-config.yaml                       | 15 ++++-
 hack/Makefile                                 | 16 +++++
 hack/e2e.applications.sh                      | 54 +++++++++++++++
 hack/modules/check_helmrelease_status.sh      | 32 +++++++++
 hack/modules/colors.sh                        |  6 ++
 hack/modules/create_git_repo.sh               | 33 ++++++++++
 hack/modules/ignored_charts                   |  6 ++
 hack/modules/install_all_apps.sh              | 66 +++++++++++++++++++
 hack/modules/install_chart.sh                 | 60 +++++++++++++++++
 hack/modules/install_tenant.sh                | 11 ++++
 hack/values/tenant.yaml                       |  6 ++
 packages/apps/ferretdb/Chart.yaml             |  2 +-
 .../apps/ferretdb/templates/init-script.yaml  |  7 +-
 packages/apps/versions_map                    |  3 +-
 14 files changed, 310 insertions(+), 7 deletions(-)
 create mode 100644 hack/Makefile
 create mode 100755 hack/e2e.applications.sh
 create mode 100755 hack/modules/check_helmrelease_status.sh
 create mode 100755 hack/modules/colors.sh
 create mode 100644 hack/modules/create_git_repo.sh
 create mode 100644 hack/modules/ignored_charts
 create mode 100755 hack/modules/install_all_apps.sh
 create mode 100755 hack/modules/install_chart.sh
 create mode 100755 hack/modules/install_tenant.sh
 create mode 100644 hack/values/tenant.yaml

diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
index c669497fb..994cecaa1 100644
--- a/.pre-commit-config.yaml
+++ b/.pre-commit-config.yaml
@@ -7,10 +7,19 @@ repos:
     - id: mixed-line-ending
       args: [--fix=lf]
     - id: check-yaml
-      exclude: packages/apps/postgres/templates/init-script.yaml
+      exclude: .*/init-script\.yaml$
       args: [--unsafe]
 - repo: https://github.com/igorshubovych/markdownlint-cli
   rev: v0.41.0
   hooks:
-  - id: markdownlint
-    args: [--fix, --disable, MD013, MD041, --]
+    - id: markdownlint
+      args: [--fix, --disable, MD013, MD041, --]
+-   repo: local
+    hooks:
+    - id: gen-versions-map
+      name: Generate versions map and check for changes
+      entry: bash -c 'cd packages/apps && make check-version-map'
+      language: system
+      types: [file]
+      pass_filenames: false
+      description: Run the script and fail if it generates changes
diff --git a/hack/Makefile b/hack/Makefile
new file mode 100644
index 000000000..c77632555
--- /dev/null
+++ b/hack/Makefile
@@ -0,0 +1,16 @@
+.PHONY: test clean help
+
+SCRIPT=./e2e.applications.sh
+PRECHECKS=./pre-checks.sh
+
+help:
+	@echo "Usage: make {test|clean}"
+	@echo "  test  - Run the end-to-end tests."
+	@echo "  clean - Clean up resources."
+
+test:
+	@bash $(PRECHECKS) test
+	@bash $(SCRIPT) test
+
+clean:
+	@bash $(SCRIPT) clean
diff --git a/hack/e2e.applications.sh b/hack/e2e.applications.sh
new file mode 100755
index 000000000..d5f7dde3a
--- /dev/null
+++ b/hack/e2e.applications.sh
@@ -0,0 +1,54 @@
+for file in ./modules/*.sh; do
+    source "$file"
+done
+
+ROOT_NS="tenant-root"
+TEST_TENANT="tenant-e2e"
+
+FLUX_NS="cozy-fluxcd"
+GITREPO_NAME="e2e-repo"
+BRANCH="main"
+
+function test() {
+    create_git_repo $GITREPO_NAME $FLUX_NS $BRANCH
+
+    install_tenant $TEST_TENANT $ROOT_NS $GITREPO_NAME $FLUX_NS
+    check_helmrelease_status $TEST_TENANT $ROOT_NS
+
+    install_all_apps "../packages/apps" "$TEST_TENANT" $GITREPO_NAME $FLUX_NS
+
+    if true; then
+        echo -e "${GREEN}All tests passed!${RESET}"
+        return 0
+    else
+        echo -e "${RED}Some tests failed!${RESET}"
+        return 1
+    fi
+}
+
+function clean() {
+    kubectl delete gitrepository.source.toolkit.fluxcd.io $GITREPO_NAME -n $FLUX_NS
+    kubectl delete helmrelease.helm.toolkit.fluxcd.io $TEST_TENANT -n $ROOT_NS
+    if true; then
+        echo -e "${GREEN}Cleanup successful!${RESET}"
+        return 0
+    else
+        echo -e "${RED}Cleanup failed!${RESET}"
+        return 1
+    fi
+}
+
+case "$1" in
+    test)
+        echo -e "${YELLOW}Running tests...${RESET}"
+        test
+        ;;
+    clean)
+        echo -e "${YELLOW}Cleaning up...${RESET}"
+        clean
+        ;;
+    *)
+        echo -e "${RED}Usage: $0 {test|clean}${RESET}"
+        exit 1
+        ;;
+esac
diff --git a/hack/modules/check_helmrelease_status.sh b/hack/modules/check_helmrelease_status.sh
new file mode 100755
index 000000000..b4178fb5e
--- /dev/null
+++ b/hack/modules/check_helmrelease_status.sh
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+source ./modules/colors.sh
+
+function check_helmrelease_status() {
+    local release_name="$1"
+    local namespace="$2"
+    local timeout=300  # Timeout in seconds
+    local interval=5   # Interval between checks in seconds
+    local elapsed=0
+
+    while [[ $elapsed -lt $timeout ]]; do
+        local status_output
+        status_output=$(kubectl get helmrelease "$release_name" -n "$namespace" -o json | jq -r '.status.conditions[-1].reason')
+
+        if [[ "$status_output" == "InstallSucceeded" ]]; then
+            echo -e "${GREEN}Helm release '$release_name' is ready.${RESET}"
+            return 0
+        elif [[ "$status_output" == "InstallFailed" ]]; then
+          echo -e "${RED}Helm release '$release_name': InstallFailed${RESET}"
+          exit 1
+        else
+            echo -e "${YELLOW}Helm release '$release_name' is not ready. Current status: $status_output${RESET}"
+        fi
+
+        sleep "$interval"
+        elapsed=$((elapsed + interval))
+    done
+
+    echo -e "${RED}Timeout reached. Helm release '$release_name' is still not ready after $timeout seconds.${RESET}"
+    exit 1
+}
diff --git a/hack/modules/colors.sh b/hack/modules/colors.sh
new file mode 100755
index 000000000..ed80c40dc
--- /dev/null
+++ b/hack/modules/colors.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+RED='\033[0;31m'
+GREEN='\033[0;32m'
+RESET='\033[0m'
+YELLOW='\033[0;33m'
diff --git a/hack/modules/create_git_repo.sh b/hack/modules/create_git_repo.sh
new file mode 100644
index 000000000..7715cd03b
--- /dev/null
+++ b/hack/modules/create_git_repo.sh
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+function create_git_repo() {
+    local repo_name="$1"
+    local namespace="$2"
+    local branch="$3"
+
+    if [[ -z "$repo_name" || -z "$namespace" || -z "$branch" ]]; then
+        echo "Usage: create_git_repo <repo_name> <namespace> <branch>"
+        return 1
+    fi
+
+    local gitrepo_file=$(mktemp /tmp/GitRepository.XXXXXX.yaml)
+    {
+        echo "apiVersion: source.toolkit.fluxcd.io/v1"
+        echo "kind: GitRepository"
+        echo "metadata:"
+        echo "  name: \"$repo_name\""
+        echo "  namespace: \"$namespace\""
+        echo "spec:"
+        echo "  interval: 1m"
+        echo "  url: https://github.com/aenix-io/cozystack"
+        echo "  ref:"
+        echo "    branch: \"$branch\""
+        echo "  ignore: |"
+        echo "    !/packages/apps/ "
+
+    } > "$gitrepo_file"
+
+    kubectl apply -f "$gitrepo_file"
+
+    rm -f "$gitrepo_file"
+}
diff --git a/hack/modules/ignored_charts b/hack/modules/ignored_charts
new file mode 100644
index 000000000..916dad6a5
--- /dev/null
+++ b/hack/modules/ignored_charts
@@ -0,0 +1,6 @@
+tenant
+http-cache
+mysql
+rabbitmq
+virtual-machine
+vpn
diff --git a/hack/modules/install_all_apps.sh b/hack/modules/install_all_apps.sh
new file mode 100755
index 000000000..9faf35e27
--- /dev/null
+++ b/hack/modules/install_all_apps.sh
@@ -0,0 +1,66 @@
+#!/bin/bash
+
+source ./modules/colors.sh
+
+# Function to load ignored charts from a file
+function load_ignored_charts() {
+    local ignore_file="$1"
+    local ignored_charts=()
+
+    if [[ -f "$ignore_file" ]]; then
+        while IFS= read -r chart; do
+            ignored_charts+=("$chart")
+        done < "$ignore_file"
+    else
+        echo "Ignore file not found: $ignore_file"
+    fi
+
+    # Return the array of ignored charts
+    echo "${ignored_charts[@]}"
+}
+
+# Function to check if a chart is in the ignored list
+function is_chart_ignored() {
+    local chart_name="$1"
+    shift
+    local ignored_charts=("$@")
+
+    for ignored_chart in "${ignored_charts[@]}"; do
+        if [[ "$ignored_chart" == "$chart_name" ]]; then
+            return 0
+        fi
+    done
+    return 1
+}
+
+function install_all_apps() {
+    local charts_dir="$1"
+    local namespace="$2"
+    local gitrepo_name="$3"
+    local flux_ns="$4"
+
+    local ignore_file="./modules/ignored_charts"
+    local ignored_charts
+    ignored_charts=($(load_ignored_charts "$ignore_file"))
+
+    for chart_path in "$charts_dir"/*; do
+        if [[ -d "$chart_path" ]]; then
+            local chart_name
+            chart_name=$(basename "$chart_path")
+            # Check if the chart is in the ignored list
+            if is_chart_ignored "$chart_name" "${ignored_charts[@]}"; then
+                echo "Skipping chart: $chart_name (listed in ignored charts)"
+                continue
+            fi
+
+            chart_name="$chart_name-e2e"
+            echo "Installing chart: $chart_name"
+            install_helmrelease "$chart_name" "$namespace" "$chart_path" "$gitrepo_name" "$flux_ns"
+
+            echo "Checking status for HelmRelease: $chart_name"
+            check_helmrelease_status "$chart_name" "$namespace"
+        else
+            echo "$chart_path is not a directory. Skipping."
+        fi
+    done
+}
diff --git a/hack/modules/install_chart.sh b/hack/modules/install_chart.sh
new file mode 100755
index 000000000..7fe53be17
--- /dev/null
+++ b/hack/modules/install_chart.sh
@@ -0,0 +1,60 @@
+#!/bin/bash
+
+source ./modules/colors.sh
+
+function install_helmrelease() {
+    local release_name="$1"
+    local namespace="$2"
+    local chart_path="$3"
+    local gitrepo_name="$4"
+    local flux_ns="$5"
+    local values_file="$6"
+
+    if [[ -z "$release_name" ]]; then
+        echo -e "${RED}Error: Release name is required.${RESET}"
+        exit 1
+    fi
+
+    if [[ -z "$namespace" ]]; then
+        echo -e "${RED}Error: Namespace name is required.${RESET}"
+        exit 1
+    fi
+
+    if [[ -z "$chart_path" ]]; then
+        echo -e "${RED}Error: Chart path name is required.${RESET}"
+        exit 1
+    fi
+
+    local helmrelease_file=$(mktemp /tmp/HelmRelease.XXXXXX.yaml)
+
+    {
+        echo "apiVersion: helm.toolkit.fluxcd.io/v2"
+        echo "kind: HelmRelease"
+        echo "metadata:"
+        echo "  labels:"
+        echo "    cozystack.io/ui: \"true\""
+        echo "  name: \"$release_name\""
+        echo "  namespace: \"$namespace\""
+        echo "spec:"
+        echo "  chart:"
+        echo "    spec:"
+        echo "      chart: \"$chart_path\""
+        echo "      reconcileStrategy: Revision"
+        echo "      sourceRef:"
+        echo "        kind: GitRepository"
+        echo "        name: \"$gitrepo_name\""
+        echo "        namespace: \"$flux_ns\""
+        echo "      version: '*'"
+        echo "  interval: 1m0s"
+        echo "  timeout: 5m0s"
+
+        if [[ -n "$values_file" && -f "$values_file" ]]; then
+            echo "  values:"
+            cat "$values_file" | sed 's/^/    /'
+        fi
+    } > "$helmrelease_file"
+
+    kubectl apply -f "$helmrelease_file"
+
+    rm -f "$helmrelease_file"
+}
diff --git a/hack/modules/install_tenant.sh b/hack/modules/install_tenant.sh
new file mode 100755
index 000000000..ed4c3291c
--- /dev/null
+++ b/hack/modules/install_tenant.sh
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+function install_tenant (){
+    local release_name="$1"
+    local namespace="$2"
+    local gitrepo_name="$3"
+    local flux_ns="$4"
+    local values_file="${5:-tenant.yaml}"
+
+    install_helmrelease "$release_name" "$namespace" "../../packages/apps/tenant" "$gitrepo_name" "$flux_ns" "$values_file"
+}
diff --git a/hack/values/tenant.yaml b/hack/values/tenant.yaml
new file mode 100644
index 000000000..1a7862d7b
--- /dev/null
+++ b/hack/values/tenant.yaml
@@ -0,0 +1,6 @@
+host: ""
+etcd: false
+monitoring: true
+ingress: false
+seaweedfs: true
+isolated: true
diff --git a/packages/apps/ferretdb/Chart.yaml b/packages/apps/ferretdb/Chart.yaml
index d23dfe5e3..0b8fc9935 100644
--- a/packages/apps/ferretdb/Chart.yaml
+++ b/packages/apps/ferretdb/Chart.yaml
@@ -16,7 +16,7 @@ type: application
 # This is the chart version. This version number should be incremented each time you make changes
 # to the chart and its templates, including the app version.
 # Versions are expected to follow Semantic Versioning (https://semver.org/)
-version: 0.4.0
+version: 0.4.1
 
 # This is the version number of the application being deployed. This version number should be
 # incremented each time you make changes to the application. Versions are not expected to
diff --git a/packages/apps/ferretdb/templates/init-script.yaml b/packages/apps/ferretdb/templates/init-script.yaml
index beef5a5aa..35723edee 100644
--- a/packages/apps/ferretdb/templates/init-script.yaml
+++ b/packages/apps/ferretdb/templates/init-script.yaml
@@ -34,6 +34,9 @@ stringData:
   init.sh: |
     #!/bin/bash
     set -e
+
+    until pg_isready ; do sleep 5; done
+
     echo "== create users"
     {{- if .Values.users }}
     psql -v ON_ERROR_STOP=1 <<\EOT
@@ -60,7 +63,7 @@ stringData:
     DROP USER $user;
     EOT
     done
-    
+
     echo "== create roles"
     psql -v ON_ERROR_STOP=1 --echo-all <<\EOT
     SELECT 'CREATE ROLE app_admin NOINHERIT;'
@@ -80,7 +83,7 @@ stringData:
         FOR schema_record IN SELECT schema_name FROM information_schema.schemata WHERE schema_name NOT IN ('pg_catalog', 'information_schema') LOOP
             -- Changing Schema Ownership
             EXECUTE format('ALTER SCHEMA %I OWNER TO %I', schema_record.schema_name, 'app_admin');
-    
+
             -- Add rights for the admin role
             EXECUTE format('GRANT ALL ON SCHEMA %I TO %I', schema_record.schema_name, 'app_admin');
             EXECUTE format('GRANT ALL ON ALL TABLES IN SCHEMA %I TO %I', schema_record.schema_name, 'app_admin');
diff --git a/packages/apps/versions_map b/packages/apps/versions_map
index 5e06c3325..a88b0f9e9 100644
--- a/packages/apps/versions_map
+++ b/packages/apps/versions_map
@@ -9,7 +9,8 @@ ferretdb 0.1.0 4ffa8615
 ferretdb 0.1.1 5ca8823
 ferretdb 0.2.0 adaf603
 ferretdb 0.3.0 aa2f553
-ferretdb 0.4.0 HEAD
+ferretdb 0.4.0 def2eb0f
+ferretdb 0.4.1 HEAD
 http-cache 0.1.0 a956713
 http-cache 0.2.0 5ca8823
 http-cache 0.3.0 fab5940

From 03206eb14690c8cef079531c971ecac0aac3f59a Mon Sep 17 00:00:00 2001
From: Andrei Kvapil <kvapss@gmail.com>
Date: Fri, 18 Oct 2024 10:43:05 +0200
Subject: [PATCH 2/2] Refactor e2e tests for applications

---
 hack/e2e.applications.sh         | 11 ++---------
 hack/modules/create_git_repo.sh  | 33 --------------------------------
 hack/modules/install_all_apps.sh | 14 +++++++-------
 hack/modules/install_chart.sh    | 10 +++++-----
 hack/modules/install_tenant.sh   |  8 ++++----
 hack/pre-checks.sh               |  2 +-
 6 files changed, 19 insertions(+), 59 deletions(-)
 delete mode 100644 hack/modules/create_git_repo.sh

diff --git a/hack/e2e.applications.sh b/hack/e2e.applications.sh
index d5f7dde3a..fae502c94 100755
--- a/hack/e2e.applications.sh
+++ b/hack/e2e.applications.sh
@@ -5,17 +5,11 @@ done
 ROOT_NS="tenant-root"
 TEST_TENANT="tenant-e2e"
 
-FLUX_NS="cozy-fluxcd"
-GITREPO_NAME="e2e-repo"
-BRANCH="main"
-
 function test() {
-    create_git_repo $GITREPO_NAME $FLUX_NS $BRANCH
-
-    install_tenant $TEST_TENANT $ROOT_NS $GITREPO_NAME $FLUX_NS
+    install_tenant $TEST_TENANT $ROOT_NS
     check_helmrelease_status $TEST_TENANT $ROOT_NS
 
-    install_all_apps "../packages/apps" "$TEST_TENANT" $GITREPO_NAME $FLUX_NS
+    install_all_apps "../packages/apps" "$TEST_TENANT" cozystack-apps cozy-public
 
     if true; then
         echo -e "${GREEN}All tests passed!${RESET}"
@@ -27,7 +21,6 @@ function test() {
 }
 
 function clean() {
-    kubectl delete gitrepository.source.toolkit.fluxcd.io $GITREPO_NAME -n $FLUX_NS
     kubectl delete helmrelease.helm.toolkit.fluxcd.io $TEST_TENANT -n $ROOT_NS
     if true; then
         echo -e "${GREEN}Cleanup successful!${RESET}"
diff --git a/hack/modules/create_git_repo.sh b/hack/modules/create_git_repo.sh
deleted file mode 100644
index 7715cd03b..000000000
--- a/hack/modules/create_git_repo.sh
+++ /dev/null
@@ -1,33 +0,0 @@
-#!/bin/bash
-
-function create_git_repo() {
-    local repo_name="$1"
-    local namespace="$2"
-    local branch="$3"
-
-    if [[ -z "$repo_name" || -z "$namespace" || -z "$branch" ]]; then
-        echo "Usage: create_git_repo <repo_name> <namespace> <branch>"
-        return 1
-    fi
-
-    local gitrepo_file=$(mktemp /tmp/GitRepository.XXXXXX.yaml)
-    {
-        echo "apiVersion: source.toolkit.fluxcd.io/v1"
-        echo "kind: GitRepository"
-        echo "metadata:"
-        echo "  name: \"$repo_name\""
-        echo "  namespace: \"$namespace\""
-        echo "spec:"
-        echo "  interval: 1m"
-        echo "  url: https://github.com/aenix-io/cozystack"
-        echo "  ref:"
-        echo "    branch: \"$branch\""
-        echo "  ignore: |"
-        echo "    !/packages/apps/ "
-
-    } > "$gitrepo_file"
-
-    kubectl apply -f "$gitrepo_file"
-
-    rm -f "$gitrepo_file"
-}
diff --git a/hack/modules/install_all_apps.sh b/hack/modules/install_all_apps.sh
index 9faf35e27..a697fd092 100755
--- a/hack/modules/install_all_apps.sh
+++ b/hack/modules/install_all_apps.sh
@@ -36,8 +36,8 @@ function is_chart_ignored() {
 function install_all_apps() {
     local charts_dir="$1"
     local namespace="$2"
-    local gitrepo_name="$3"
-    local flux_ns="$4"
+    local repo_name="$3"
+    local repo_ns="$4"
 
     local ignore_file="./modules/ignored_charts"
     local ignored_charts
@@ -53,12 +53,12 @@ function install_all_apps() {
                 continue
             fi
 
-            chart_name="$chart_name-e2e"
-            echo "Installing chart: $chart_name"
-            install_helmrelease "$chart_name" "$namespace" "$chart_path" "$gitrepo_name" "$flux_ns"
+            release_name="$chart_name-e2e"
+            echo "Installing release: $release_name"
+            install_helmrelease "$release_name" "$namespace" "$chart_name" "$repo_name" "$repo_ns"
 
-            echo "Checking status for HelmRelease: $chart_name"
-            check_helmrelease_status "$chart_name" "$namespace"
+            echo "Checking status for HelmRelease: $release_name"
+            check_helmrelease_status "$release_name" "$namespace"
         else
             echo "$chart_path is not a directory. Skipping."
         fi
diff --git a/hack/modules/install_chart.sh b/hack/modules/install_chart.sh
index 7fe53be17..5e45ba408 100755
--- a/hack/modules/install_chart.sh
+++ b/hack/modules/install_chart.sh
@@ -6,8 +6,8 @@ function install_helmrelease() {
     local release_name="$1"
     local namespace="$2"
     local chart_path="$3"
-    local gitrepo_name="$4"
-    local flux_ns="$5"
+    local repo_name="$4"
+    local repo_ns="$5"
     local values_file="$6"
 
     if [[ -z "$release_name" ]]; then
@@ -41,9 +41,9 @@ function install_helmrelease() {
         echo "      chart: \"$chart_path\""
         echo "      reconcileStrategy: Revision"
         echo "      sourceRef:"
-        echo "        kind: GitRepository"
-        echo "        name: \"$gitrepo_name\""
-        echo "        namespace: \"$flux_ns\""
+        echo "        kind: HelmRepository"
+        echo "        name: \"$repo_name\""
+        echo "        namespace: \"$repo_ns\""
         echo "      version: '*'"
         echo "  interval: 1m0s"
         echo "  timeout: 5m0s"
diff --git a/hack/modules/install_tenant.sh b/hack/modules/install_tenant.sh
index ed4c3291c..f1cab14d6 100755
--- a/hack/modules/install_tenant.sh
+++ b/hack/modules/install_tenant.sh
@@ -3,9 +3,9 @@
 function install_tenant (){
     local release_name="$1"
     local namespace="$2"
-    local gitrepo_name="$3"
-    local flux_ns="$4"
-    local values_file="${5:-tenant.yaml}"
+    local values_file="${3:-tenant.yaml}"
+    local repo_name="cozystack-apps"
+    local repo_ns="cozy-public"
 
-    install_helmrelease "$release_name" "$namespace" "../../packages/apps/tenant" "$gitrepo_name" "$flux_ns" "$values_file"
+    install_helmrelease "$release_name" "$namespace" "tenant" "$repo_name" "$repo_ns" "$values_file"
 }
diff --git a/hack/pre-checks.sh b/hack/pre-checks.sh
index be0e2a675..d8fc94e0e 100755
--- a/hack/pre-checks.sh
+++ b/hack/pre-checks.sh
@@ -5,7 +5,7 @@ RED='\033[31m'
 RESET='\033[0m'
 
 check-yq-version() {
-    current_version=$(yq -V | grep -oP 'v[0-9]+\.[0-9]+\.[0-9]+')
+    current_version=$(yq -V | awk '$(NF-1) == "version" {print $NF}')
     if [ -z "$current_version" ]; then
         echo "yq is not installed or version cannot be determined."
         exit 1