From 16469a9fa6de7838928d372e20f1732302bb2b50 Mon Sep 17 00:00:00 2001
From: Nadica Rizova <nrizova@keitaro.com>
Date: Thu, 29 Jun 2023 13:47:18 +0200
Subject: [PATCH 1/7] psql-init dockerfile and scripts added

---
 images/ckan/2.9/psql-init/Dockerfile          |  17 ++
 .../ckan/2.9/psql-init/psql-init/psql-init.py | 277 ++++++++++++++++++
 .../psql-init/psql-init/set_permissions.sql   | 108 +++++++
 images/ckan/2.9/psql-init/requirements.txt    |   2 +
 4 files changed, 404 insertions(+)
 create mode 100644 images/ckan/2.9/psql-init/Dockerfile
 create mode 100644 images/ckan/2.9/psql-init/psql-init/psql-init.py
 create mode 100644 images/ckan/2.9/psql-init/psql-init/set_permissions.sql
 create mode 100644 images/ckan/2.9/psql-init/requirements.txt

diff --git a/images/ckan/2.9/psql-init/Dockerfile b/images/ckan/2.9/psql-init/Dockerfile
new file mode 100644
index 0000000..9fa996d
--- /dev/null
+++ b/images/ckan/2.9/psql-init/Dockerfile
@@ -0,0 +1,17 @@
+
+# Start with a lightweight base image
+FROM python:3.9-alpine
+
+# Set the working directory in the container
+WORKDIR /srv
+
+# Copy the requirements file to the container
+COPY requirements.txt .
+
+# Install the Python dependencies
+RUN pip install --no-cache-dir -r requirements.txt
+
+# Copy the rest of the application code to the container
+COPY psql-init/  .
+
+CMD ["sleep", "1000"]
\ No newline at end of file
diff --git a/images/ckan/2.9/psql-init/psql-init/psql-init.py b/images/ckan/2.9/psql-init/psql-init/psql-init.py
new file mode 100644
index 0000000..507b551
--- /dev/null
+++ b/images/ckan/2.9/psql-init/psql-init/psql-init.py
@@ -0,0 +1,277 @@
+"""
+Copyright (c) 2020 Keitaro AB
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    https://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+"""
+
+import os
+import sys
+import subprocess
+import re
+import psycopg2
+from psycopg2.extensions import ISOLATION_LEVEL_AUTOCOMMIT
+from psycopg2.extensions import AsIs
+from sqlalchemy.engine.url import make_url
+
+
+ckan_conn_str = os.environ.get('CKAN_SQLALCHEMY_URL', '')
+datastorerw_conn_str = os.environ.get('CKAN_DATASTORE_WRITE_URL', '')
+datastorero_conn_str = os.environ.get('CKAN_DATASTORE_READ_URL', '')
+
+master_user = os.environ.get('PSQL_MASTER', '')
+master_passwd = os.environ.get('PSQL_PASSWD', '')
+master_database = os.environ.get('PSQL_DB', '')
+
+
+class DB_Params:
+    def __init__(self, conn_str):
+        self.db_user = make_url(conn_str).username
+        self.db_passwd = make_url(conn_str).password
+        self.db_host = make_url(conn_str).host
+        self.db_name = make_url(conn_str).database
+
+
+def check_db_connection(db_params, retry=None):
+
+    print('Checking whether database is up...')
+
+    if retry is None:
+        retry = 20
+    elif retry == 0:
+        print('Giving up...')
+        sys.exit(1)
+
+    try:
+        con = psycopg2.connect(user=master_user,
+                               host=db_params.db_host,
+                               password=master_passwd,
+                               database=master_database)
+
+    except psycopg2.Error as e:
+        print((str(e)))
+        print('Unable to connect to the database...try again in a while.')
+        import time
+        time.sleep(30)
+        check_db_connection(db_params, retry=retry - 1)
+    else:
+        con.close()
+
+
+def create_user(db_params):
+    con = None
+    try:
+        con = psycopg2.connect(user=master_user,
+                               host=db_params.db_host,
+                               password=master_passwd,
+                               database=master_database)
+        con.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
+        cur = con.cursor()
+        print("Creating user " + db_params.db_user.split("@")[0])
+        cur.execute('CREATE ROLE "%s" ' +
+                    'WITH ' +
+                    'LOGIN NOSUPERUSER INHERIT ' +
+                    'CREATEDB NOCREATEROLE NOREPLICATION ' +
+                    'PASSWORD %s',
+                    (AsIs(db_params.db_user.split("@")[0]),
+                     db_params.db_passwd,))
+    except(Exception, psycopg2.DatabaseError) as error:
+        print("ERROR DB: ", error)
+    finally:
+        cur.close()
+        con.close()
+
+
+def create_db(db_params):
+    con = None
+    try:
+        con = psycopg2.connect(user=master_user,
+                               host=db_params.db_host,
+                               password=master_passwd,
+                               database=master_database)
+        con.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
+        cur = con.cursor()
+        cur.execute('GRANT "' + db_params.db_user.split("@")
+                    [0] + '" TO "' + master_user.split("@")[0] + '"')
+        print("Creating database " + db_params.db_name + " with owner " +
+              db_params.db_user.split("@")[0])
+        cur.execute('CREATE DATABASE ' + db_params.db_name + ' OWNER "' +
+                    db_params.db_user.split("@")[0] + '"')
+        cur.execute('GRANT ALL PRIVILEGES ON DATABASE ' +
+                    db_params.db_name + ' TO "' +
+                    db_params.db_user.split("@")[0] + '"')
+        if is_pg_buffercache_enabled(db_params) >= 1:
+            # FIXME: This is a known issue with pg_buffercache access
+            # For more info check this thread:
+            # https://www.postgresql.org/message-id/21009351582737086%40iva6-22e79380f52c.qloud-c.yandex.net
+            print("Granting privileges on pg_monitor to " +
+                  db_params.db_user.split("@")[0])
+            cur.execute('GRANT "pg_monitor" TO "' + db_params.db_user.split("@")[0] + '"')
+    except(Exception, psycopg2.DatabaseError) as error:
+        print("ERROR DB: ", error)
+    finally:
+        cur.close()
+        con.close()
+
+
+def is_pg_buffercache_enabled(db_params):
+    con = None
+    result = None
+    try:
+        con = psycopg2.connect(user=master_user,
+                               host=db_params.db_host,
+                               password=master_passwd,
+                               database=db_params.db_name)
+        con.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
+        cur = con.cursor()
+        cur.execute("SELECT count(*) FROM pg_extension " +
+                    "WHERE extname = 'pg_buffercache'")
+        result = cur.fetchone()
+    except(Exception, psycopg2.DatabaseError) as error:
+        print("ERROR DB: ", error)
+    finally:
+        cur.close()
+        con.close()
+    return result[0]
+
+
+def set_datastore_permissions(datastore_rw_params, datastore_ro_params, sql):
+    con = None
+    try:
+        con = psycopg2.connect(user=master_user,
+                               host=datastore_rw_params.db_host,
+                               password=master_passwd,
+                               database=datastore_rw_params.db_name)
+        con.set_isolation_level(ISOLATION_LEVEL_AUTOCOMMIT)
+        cur = con.cursor()
+        cur.execute('GRANT CONNECT ON DATABASE ' +
+                    datastore_rw_params.db_name +
+                    ' TO ' + datastore_ro_params.db_user.split("@")[0])
+        if is_pg_buffercache_enabled(datastore_rw_params) >= 1:
+            print("Granting privileges on pg_monitor to " +
+                  datastore_ro_params.db_user.split("@")[0])
+            cur.execute('GRANT ALL PRIVILEGES ON TABLE pg_monitor TO ' +
+                        datastore_ro_params.db_user.split("@")[0])
+        print("Setting datastore permissions\n")
+        print(sql)
+        cur.execute(sql)
+        print("Datastore permissions applied.")
+    except Exception as error:
+        print("ERROR DB: ", error)
+    finally:
+        cur.close()
+        con.close()
+
+
+if master_user == '' or master_passwd == '' or master_database == '':
+    print("No master postgresql user provided.")
+    print("Cannot initialize default CKAN db resources. Exiting!")
+    sys.exit(1)
+
+print("Master DB: " + master_database + " Master User: " + master_user)
+
+ckan_db = DB_Params(ckan_conn_str)
+datastorerw_db = DB_Params(datastorerw_conn_str)
+datastorero_db = DB_Params(datastorero_conn_str)
+
+
+# Check to see whether we can connect to the database, exit after 10 mins
+check_db_connection(ckan_db)
+
+try:
+    create_user(ckan_db)
+except(Exception, psycopg2.DatabaseError) as error:
+    print("ERROR DB: ", error)
+
+try:
+    create_user(datastorerw_db)
+except(Exception, psycopg2.DatabaseError) as error:
+    print("ERROR DB: ", error)
+
+try:
+    create_user(datastorero_db)
+except(Exception, psycopg2.DatabaseError) as error:
+    print("ERROR DB: ", error)
+
+try:
+    create_db(ckan_db)
+except(Exception, psycopg2.DatabaseError) as error:
+    print("ERROR DB: ", error)
+
+try:
+    create_db(datastorerw_db)
+except(Exception, psycopg2.DatabaseError) as error:
+    print("ERROR DB: ", error)
+
+
+def execute_sql_script(ckan_dbp, datastorero_dbp, datastorerw_dbp, script_path):
+    # Connect to the database
+    conn = psycopg2.connect(
+        user=master_user,
+        host=datastorerw_dbp.db_host,
+        password=master_passwd,
+        database=datastorerw_dbp.db_name
+    )
+
+    try:
+        # Create a cursor
+        cur = conn.cursor()
+
+        # Execute the SQL script
+        with open(script_path, 'r') as f:
+            sql_script = f.read()
+
+        # Replace placeholders with actual values
+        
+        sql_script = sql_script.replace('{datastoredb}', datastorerw_dbp.db_name)
+        sql_script = sql_script.replace('{readuser}', datastorero_dbp.db_user)
+        sql_script = sql_script.replace('{writeuser}', datastorerw_dbp.db_user)
+        sql_script = sql_script.replace('{mainuser}', ckan_dbp.db_user)
+        sql_script = sql_script.replace('{maindb}', ckan_dbp.db_name)
+
+        print("CKAN DB User:", ckan_dbp.db_user)
+
+        # Execute the SQL script
+        cur.execute(sql_script)
+
+        # Commit the changes
+        conn.commit()
+
+        print("SQL script executed successfully.")
+
+        # print("CKAN DB User:", ckan_dbp.db_user)
+        # print("read/write DB User:", datastorerw_dbp.db_user)
+        # print("read/write DB name:", datastorerw_dbp.db_name)
+        # print("read/write host:", datastorerw_dbp.db_host)
+        # print("read DB user:", datastorero_dbp.db_user)
+        # print("read DB name:", datastorero_dbp.db_name)
+
+    except psycopg2.Error as e:
+        print(f"Error executing SQL script: {str(e)}")
+
+    finally:
+        # Close the cursor and the connection
+        cur.close()
+        conn.close()
+
+set_permissions = './set_permissions.sql'
+
+# Print the current working directory
+print("Current working directory:", os.getcwd())
+
+# Check if the file exists
+if os.path.isfile(set_permissions):
+    print("File exists.")
+    # Call the execute_sql_script function with the appropriate arguments
+    execute_sql_script(ckan_db, datastorero_db, datastorerw_db, set_permissions)
+else:
+    print("File not found.")
diff --git a/images/ckan/2.9/psql-init/psql-init/set_permissions.sql b/images/ckan/2.9/psql-init/psql-init/set_permissions.sql
new file mode 100644
index 0000000..e7be428
--- /dev/null
+++ b/images/ckan/2.9/psql-init/psql-init/set_permissions.sql
@@ -0,0 +1,108 @@
+/*
+This script configures the permissions for the datastore.
+
+It ensures that the datastore read-only user will only be able to select from
+the datastore database but has no create/write/edit permission or any
+permissions on other databases. You must execute this script as a database
+superuser on the PostgreSQL server that hosts your datastore database.
+
+For example, if PostgreSQL is running locally and the "postgres" user has the
+appropriate permissions (as in the default Ubuntu PostgreSQL install), you can
+run:
+
+    ckan -c /etc/ckan/default/ckan.ini datastore set-permissions | sudo -u postgres psql
+
+Or, if your PostgreSQL server is remote, you can pipe the permissions script
+over SSH:
+
+    ckan -c /etc/ckan/default/ckan.ini datastore set-permissions | ssh dbserver sudo -u postgres psql
+
+*/
+
+-- Most of the following commands apply to an explicit database or to the whole
+-- 'public' schema, and could be executed anywhere. But ALTER DEFAULT
+-- PERMISSIONS applies to the current database, and so we must be connected to
+-- the datastore DB:
+--\connect {datastoredb}
+
+-- revoke permissions for the read-only user
+REVOKE CREATE ON SCHEMA public FROM PUBLIC;
+REVOKE USAGE ON SCHEMA public FROM PUBLIC;
+
+GRANT CREATE ON SCHEMA public TO {mainuser};
+GRANT USAGE ON SCHEMA public TO {mainuser};
+
+GRANT CREATE ON SCHEMA public TO {writeuser};
+GRANT USAGE ON SCHEMA public TO {writeuser};
+
+-- take connect permissions from main db
+REVOKE CONNECT ON DATABASE {maindb} FROM {readuser};
+
+-- grant select permissions for read-only user
+GRANT CONNECT ON DATABASE {datastoredb} TO {readuser};
+GRANT USAGE ON SCHEMA public TO {readuser};
+
+-- grant access to current tables and views to read-only user
+GRANT SELECT ON ALL TABLES IN SCHEMA public TO {readuser};
+
+-- grant access to new tables and views by default
+ALTER DEFAULT PRIVILEGES FOR USER {writeuser} IN SCHEMA public
+   GRANT SELECT ON TABLES TO {readuser};
+
+-- a view for listing valid table (resource id) and view names
+CREATE OR REPLACE VIEW "_table_metadata" AS
+    SELECT DISTINCT
+        substr(md5(dependee.relname || COALESCE(dependent.relname, '')), 0, 17) AS "_id",
+        dependee.relname AS name,
+        dependee.oid AS oid,
+        dependent.relname AS alias_of
+    FROM
+        pg_class AS dependee
+        LEFT OUTER JOIN pg_rewrite AS r ON r.ev_class = dependee.oid
+        LEFT OUTER JOIN pg_depend AS d ON d.objid = r.oid
+        LEFT OUTER JOIN pg_class AS dependent ON d.refobjid = dependent.oid
+    WHERE
+        (dependee.oid != dependent.oid OR dependent.oid IS NULL) AND
+        -- is a table (from pg_tables view definition)
+        -- or is a view (from pg_views view definition)
+        (dependee.relkind = 'r'::"char" OR dependee.relkind = 'v'::"char")
+        AND dependee.relnamespace = (
+            SELECT oid FROM pg_namespace WHERE nspname='public')
+    ORDER BY dependee.oid DESC;
+ALTER VIEW "_table_metadata" OWNER TO {writeuser};
+GRANT SELECT ON "_table_metadata" TO {readuser};
+
+-- _full_text fields are now updated by a trigger when set to NULL
+CREATE OR REPLACE FUNCTION populate_full_text_trigger() RETURNS trigger
+AS $body$
+    BEGIN
+        IF NEW._full_text IS NOT NULL THEN
+            RETURN NEW;
+        END IF;
+        NEW._full_text := (
+            SELECT to_tsvector(string_agg(value, ' '))
+            FROM json_each_text(row_to_json(NEW.*))
+            WHERE key NOT LIKE '\_%');
+        RETURN NEW;
+    END;
+$body$ LANGUAGE plpgsql;
+ALTER FUNCTION populate_full_text_trigger() OWNER TO {writeuser};
+
+-- migrate existing tables that don't have full text trigger applied
+DO $body$
+    BEGIN
+        EXECUTE coalesce(
+            (SELECT string_agg(
+                'CREATE TRIGGER zfulltext BEFORE INSERT OR UPDATE ON ' ||
+                quote_ident(relname) || ' FOR EACH ROW EXECUTE PROCEDURE ' ||
+                'populate_full_text_trigger();', ' ')
+            FROM pg_class
+            LEFT OUTER JOIN pg_trigger AS t
+                ON t.tgrelid = relname::regclass AND t.tgname = 'zfulltext'
+            WHERE relkind = 'r'::"char" AND t.tgname IS NULL
+                AND relnamespace = (
+                    SELECT oid FROM pg_namespace WHERE nspname='public')),
+            'SELECT 1;');
+    END;
+$body$;
+
diff --git a/images/ckan/2.9/psql-init/requirements.txt b/images/ckan/2.9/psql-init/requirements.txt
new file mode 100644
index 0000000..0662697
--- /dev/null
+++ b/images/ckan/2.9/psql-init/requirements.txt
@@ -0,0 +1,2 @@
+psycopg2-binary==2.9.3
+sqlalchemy==1.3.5  

From 2c021de9f6c89938826e7e3d4c6bfe04e639edf1 Mon Sep 17 00:00:00 2001
From: Nadica Rizova <nrizova@keitaro.com>
Date: Mon, 3 Jul 2023 09:44:59 +0200
Subject: [PATCH 2/7]  added step in workflow to automatic build psql-init
 image

---
 .github/workflows/master_merge.yml   | 46 ++++++++++++++++++++++++++++
 images/ckan/2.9/psql-init/Dockerfile |  4 ++-
 2 files changed, 49 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/master_merge.yml b/.github/workflows/master_merge.yml
index 37aaf2d..5461ef1 100644
--- a/.github/workflows/master_merge.yml
+++ b/.github/workflows/master_merge.yml
@@ -246,3 +246,49 @@ jobs:
             ghcr.io/keitaroinc/datapusher:${{ steps.datapusher.outputs.IMAGE_TAG }}
           cache-from: type=local,src=/tmp/.buildx-cache-datapusher
           cache-to: type=local,mode=max,dest=/tmp/.buildx-cache-datapusher
+    
+  build-psql-init:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+
+      - name: Set up Docker Buildx
+        uses: docker/setup-buildx-action@v1
+
+      - name: Login to DockerHub
+        uses: docker/login-action@v1 
+        with:
+          username: ${{ secrets.DOCKERHUB_USERNAME }}
+          password: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }}
+
+      - name: Login to GitHub Container Registry
+        uses: docker/login-action@v1 
+        with:
+          registry: ghcr.io
+          username: ${{ github.repository_owner }}
+          password: ${{ secrets.CR_PAT }}
+
+      - name: Cache Docker layers
+        uses: actions/cache@v2
+        with:
+          path: /tmp/.buildx-cache-psql-init
+          key: ${{ runner.os }}-buildx-psql-init-${{ github.sha }}
+          restore-keys: |
+            ${{ runner.os }}-buildx-psql-init
+
+      - name: Get docker tag for psql-init image
+        id: psql-init
+        run: |
+          echo "::set-output name=IMAGE_TAG::$(awk -F '=' '/IMAGE_TAG/{print $2}' ./images/ckan/2.9/psql-init/Dockerfile)"
+
+      - name: Build and push psql-init
+        uses: docker/build-push-action@v2
+        with:
+          context: ./images/ckan/2.9/psql-init
+          file: ./images/ckan/2.9/psql-init/Dockerfile
+          push: true
+          tags: |
+            keitaro/psql-init:${{ steps.psql-init.outputs.IMAGE_TAG }}
+            ghcr.io/keitaroinc/datapusher:${{ steps.psql-init.outputs.IMAGE_TAG }}
+          cache-from: type=local,src=/tmp/.buildx-cache-psql-init
+          cache-to: type=local,mode=max,dest=/tmp/.buildx-cache-psql-init
diff --git a/images/ckan/2.9/psql-init/Dockerfile b/images/ckan/2.9/psql-init/Dockerfile
index 9fa996d..e1727ec 100644
--- a/images/ckan/2.9/psql-init/Dockerfile
+++ b/images/ckan/2.9/psql-init/Dockerfile
@@ -2,6 +2,9 @@
 # Start with a lightweight base image
 FROM python:3.9-alpine
 
+# Used by Github Actions to tag the image with
+ENV IMAGE_TAG=0.0.1
+
 # Set the working directory in the container
 WORKDIR /srv
 
@@ -14,4 +17,3 @@ RUN pip install --no-cache-dir -r requirements.txt
 # Copy the rest of the application code to the container
 COPY psql-init/  .
 
-CMD ["sleep", "1000"]
\ No newline at end of file

From 47fe1b8cced9ce3ccd64c73b8ca7ea4e1d69ab3f Mon Sep 17 00:00:00 2001
From: Nadica Rizova <nrizova@keitaro.com>
Date: Mon, 3 Jul 2023 10:34:12 +0200
Subject: [PATCH 3/7] changed image name for psql

---
 .github/workflows/master_merge.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/master_merge.yml b/.github/workflows/master_merge.yml
index 5461ef1..0a56f4b 100644
--- a/.github/workflows/master_merge.yml
+++ b/.github/workflows/master_merge.yml
@@ -289,6 +289,6 @@ jobs:
           push: true
           tags: |
             keitaro/psql-init:${{ steps.psql-init.outputs.IMAGE_TAG }}
-            ghcr.io/keitaroinc/datapusher:${{ steps.psql-init.outputs.IMAGE_TAG }}
+            ghcr.io/keitaroinc/psql-init:${{ steps.psql-init.outputs.IMAGE_TAG }}
           cache-from: type=local,src=/tmp/.buildx-cache-psql-init
           cache-to: type=local,mode=max,dest=/tmp/.buildx-cache-psql-init

From 80a4a849f56b4de97c2b87f68c8f8c3b678eaf05 Mon Sep 17 00:00:00 2001
From: Nadica Rizova <nrizova@keitaro.com>
Date: Mon, 3 Jul 2023 10:41:35 +0200
Subject: [PATCH 4/7] added cmd in Dockerfile

---
 images/ckan/2.9/psql-init/Dockerfile             |  2 ++
 images/ckan/2.9/psql-init/psql-init/psql-init.py | 12 ++++++------
 2 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/images/ckan/2.9/psql-init/Dockerfile b/images/ckan/2.9/psql-init/Dockerfile
index e1727ec..65fdf5e 100644
--- a/images/ckan/2.9/psql-init/Dockerfile
+++ b/images/ckan/2.9/psql-init/Dockerfile
@@ -17,3 +17,5 @@ RUN pip install --no-cache-dir -r requirements.txt
 # Copy the rest of the application code to the container
 COPY psql-init/  .
 
+CMD ["python", "/srv/psql-init.py"]
+
diff --git a/images/ckan/2.9/psql-init/psql-init/psql-init.py b/images/ckan/2.9/psql-init/psql-init/psql-init.py
index 507b551..6eb25cb 100644
--- a/images/ckan/2.9/psql-init/psql-init/psql-init.py
+++ b/images/ckan/2.9/psql-init/psql-init/psql-init.py
@@ -248,12 +248,12 @@ def execute_sql_script(ckan_dbp, datastorero_dbp, datastorerw_dbp, script_path):
 
         print("SQL script executed successfully.")
 
-        # print("CKAN DB User:", ckan_dbp.db_user)
-        # print("read/write DB User:", datastorerw_dbp.db_user)
-        # print("read/write DB name:", datastorerw_dbp.db_name)
-        # print("read/write host:", datastorerw_dbp.db_host)
-        # print("read DB user:", datastorero_dbp.db_user)
-        # print("read DB name:", datastorero_dbp.db_name)
+        print("CKAN DB User:", ckan_dbp.db_user)
+        print("read/write DB User:", datastorerw_dbp.db_user)
+        print("read/write DB name:", datastorerw_dbp.db_name)
+        print("read/write host:", datastorerw_dbp.db_host)
+        print("read DB user:", datastorero_dbp.db_user)
+        print("read DB name:", datastorero_dbp.db_name)
 
     except psycopg2.Error as e:
         print(f"Error executing SQL script: {str(e)}")

From 53cb2363d6549d33044c148e0a7e1471febbf6d4 Mon Sep 17 00:00:00 2001
From: Nadica Rizova <nrizova@keitaro.com>
Date: Mon, 3 Jul 2023 10:50:57 +0200
Subject: [PATCH 5/7]  changed location of psql-init folder

---
 .github/workflows/master_merge.yml                          | 6 +++---
 images/{ckan/2.9 => }/psql-init/Dockerfile                  | 0
 images/{ckan/2.9 => }/psql-init/psql-init/psql-init.py      | 0
 .../{ckan/2.9 => }/psql-init/psql-init/set_permissions.sql  | 0
 images/{ckan/2.9 => }/psql-init/requirements.txt            | 0
 5 files changed, 3 insertions(+), 3 deletions(-)
 rename images/{ckan/2.9 => }/psql-init/Dockerfile (100%)
 rename images/{ckan/2.9 => }/psql-init/psql-init/psql-init.py (100%)
 rename images/{ckan/2.9 => }/psql-init/psql-init/set_permissions.sql (100%)
 rename images/{ckan/2.9 => }/psql-init/requirements.txt (100%)

diff --git a/.github/workflows/master_merge.yml b/.github/workflows/master_merge.yml
index 0a56f4b..5d5d03e 100644
--- a/.github/workflows/master_merge.yml
+++ b/.github/workflows/master_merge.yml
@@ -279,13 +279,13 @@ jobs:
       - name: Get docker tag for psql-init image
         id: psql-init
         run: |
-          echo "::set-output name=IMAGE_TAG::$(awk -F '=' '/IMAGE_TAG/{print $2}' ./images/ckan/2.9/psql-init/Dockerfile)"
+          echo "::set-output name=IMAGE_TAG::$(awk -F '=' '/IMAGE_TAG/{print $2}' ./images/psql-init/Dockerfile)"
 
       - name: Build and push psql-init
         uses: docker/build-push-action@v2
         with:
-          context: ./images/ckan/2.9/psql-init
-          file: ./images/ckan/2.9/psql-init/Dockerfile
+          context: ./images/psql-init
+          file: ./images/psql-init/Dockerfile
           push: true
           tags: |
             keitaro/psql-init:${{ steps.psql-init.outputs.IMAGE_TAG }}
diff --git a/images/ckan/2.9/psql-init/Dockerfile b/images/psql-init/Dockerfile
similarity index 100%
rename from images/ckan/2.9/psql-init/Dockerfile
rename to images/psql-init/Dockerfile
diff --git a/images/ckan/2.9/psql-init/psql-init/psql-init.py b/images/psql-init/psql-init/psql-init.py
similarity index 100%
rename from images/ckan/2.9/psql-init/psql-init/psql-init.py
rename to images/psql-init/psql-init/psql-init.py
diff --git a/images/ckan/2.9/psql-init/psql-init/set_permissions.sql b/images/psql-init/psql-init/set_permissions.sql
similarity index 100%
rename from images/ckan/2.9/psql-init/psql-init/set_permissions.sql
rename to images/psql-init/psql-init/set_permissions.sql
diff --git a/images/ckan/2.9/psql-init/requirements.txt b/images/psql-init/requirements.txt
similarity index 100%
rename from images/ckan/2.9/psql-init/requirements.txt
rename to images/psql-init/requirements.txt

From 4f770582e92cafe9fdb459a76ebdb4d5de81ec04 Mon Sep 17 00:00:00 2001
From: Nadica Rizova <nrizova@keitaro.com>
Date: Mon, 3 Jul 2023 11:24:28 +0200
Subject: [PATCH 6/7]  pre-checks added for psql init

---
 .github/workflows/pr_checks.yml | 31 +++++++++++++++++++++++++++++++
 1 file changed, 31 insertions(+)

diff --git a/.github/workflows/pr_checks.yml b/.github/workflows/pr_checks.yml
index 4619032..fe7da0f 100644
--- a/.github/workflows/pr_checks.yml
+++ b/.github/workflows/pr_checks.yml
@@ -182,3 +182,34 @@ jobs:
           tags: keitaro/ckandatapusher:${{ steps.datapusher.outputs.IMAGE_TAG }}
           cache-from: type=local,src=/tmp/.buildx-cache-datapusher
           cache-to: type=local,mode=max,dest=/tmp/.buildx-cache-datapusher
+
+  build-psql-init:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@v2
+
+      - name: Set up Docker Buildx
+        uses: docker/setup-buildx-action@v1
+
+      - name: Cache Docker layers
+        uses: actions/cache@v2
+        with:
+          path: /tmp/.buildx-cache-psql-init
+          key: ${{ runner.os }}-buildx-psql-init-${{ github.sha }}
+          restore-keys: |
+            ${{ runner.os }}-buildx-psql-init
+
+      - name: Get docker tag for psql-init image
+        id: psql-init
+        run: |
+          echo "::set-output name=IMAGE_TAG::$(awk -F '=' '/IMAGE_TAG/{print $2}' ./images/psql-init/Dockerfile)"
+
+      - name: Build psql-init
+        uses: docker/build-push-action@v2
+        with:
+          context: ./images/psgl-init
+          file: ./images/psql-init/Dockerfile
+          push: false
+          tags: keitaro/psql-init:${{ steps.psql-init.outputs.IMAGE_TAG }}
+          cache-from: type=local,src=/tmp/.buildx-cache-psql-init
+          cache-to: type=local,mode=max,dest=/tmp/.buildx-cache-psql-init
\ No newline at end of file

From 640b0fa09c07e12d0ec06203ebdbea38ae08766b Mon Sep 17 00:00:00 2001
From: Nadica Rizova <nrizova@keitaro.com>
Date: Mon, 3 Jul 2023 11:28:46 +0200
Subject: [PATCH 7/7] syntax changed psql

---
 .github/workflows/pr_checks.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.github/workflows/pr_checks.yml b/.github/workflows/pr_checks.yml
index fe7da0f..c37d6e9 100644
--- a/.github/workflows/pr_checks.yml
+++ b/.github/workflows/pr_checks.yml
@@ -207,7 +207,7 @@ jobs:
       - name: Build psql-init
         uses: docker/build-push-action@v2
         with:
-          context: ./images/psgl-init
+          context: ./images/psql-init
           file: ./images/psql-init/Dockerfile
           push: false
           tags: keitaro/psql-init:${{ steps.psql-init.outputs.IMAGE_TAG }}