Ways to add one more quote nesting level (docker + bash + psql) - bash

I need to modify a value in postgreSQL table in docker using a single command. I want to run something like this inside a container:
su - postgres -c "psql -c \"\c database_name\" -c \"UPDATE table_name SET value_name = 'value';\""
It turned out to be the only way to do this because echo "\c database_name \\ UPDATE table_name SET value_name = 'value';" | psql doesn't work in my postgreSQL resulting in
invalid command \
Is there anything I can do to run it in a container via command sudo docker exec -it container_name bash -c "*command above*"? None of the quote types surrounding command above make it work. Thank you in advance!

You should be able to avoid using su by telling docker exec itself which user should run psql. You can also run psql directly, rather than executing a bash shell that executes it.
sudo docker exec --user postgres \
psql -c "\c database_name" -c "UPDATE table_name SET value_name = 'value';"

Related

Docker: "pg_restore input file does not appear to be a valid archive" error

I backup a PostgreSql database using the following command and it creates an sql file:
cmd /c 'docker exec -t <container-name> pg_dump <db_name> -U postgres -c
-v > C:\\backup\\<db_name>.sql'
However, I cannot restore the sql backup file using the following command:
first I drop and create an empty db:
docker exec <container-name> bash -c "dropdb -U postgres <db_name>"
docker exec <container-name> bash -c "createdb -U postgres <db_name>"
then restore:
cmd /c "docker exec -i <container-name> pg_restore -C -U postgres -d
<db_name> -v < C:\\<db_name>.sql"
gives "pg_restore: error: input file does not appear to be a valid archive" error. So,
1. how can I restore the database with sql file?
2. how can I backup PostgreSql db in Docker on Windows?
A plain-text pg_dump is restored with psql, not with pg_restore.
I don't understand why you want to run that inside the container. Install a PostgreSQL client on the host operating system and simplify the procedure. Besides, a backup should be on a different machine than the database.

Unable to run queries from a file using psql command line with docker exec

I have a bash file should bring the postgres docker container online and then run a .sql file to create the databases. But it's throwing the error.
psql: error: provision-db.sql: No such file or directory
I have checked the path and the file exists at the same level of this bash script. Following is the content of my bash file.
#!/usr/bin/env bash
docker-compose up -d db
# Ensure the Postgres server is online and usable
until docker exec -i boohoo.postgres pg_isready --host="${POSTGRES_HOST}" --username="${POSTGRES_USER}"
do
echo "."
sleep 1
done
docker exec -i boohoo.postgres psql -h "${POSTGRES_HOST}" -U "${POSTGRES_USER}" -a -q -f provision-db.sql
And this is the provision-db.sql file.
DROP DATABASE "boo-hoo";
CREATE DATABASE "boo-hoo";
GRANT ALL PRIVILEGES ON DATABASE "boo-hoo" TO postgres;
This is the part of docker-compose.yml
version: '3.3'
services:
db:
container_name: boohoo.postgres
hostname: postgres.boohoo
image: postgres
ports:
- "15432:5432"
environment:
POSTGRES_USER: "postgres"
POSTGRES_PASSWORD: "postgres"
The short version
This works
cat provision-db.sql | docker exec -i boohoo.postgres bash -c 'psql -U ${POSTGRES_USER} -w -a -q -f -'
The long version
multiple things here
1) why does following command not find the provision-db.sql?
docker exec -i boohoo.postgres psql -h "${POSTGRES_HOST}" -U "${POSTGRES_USER}" -a -q -f provision-db.sql
because the provision-db.sql is on your host and not in your container. Therefore, when you execute the psql command inside the container it can not find the file
2) Why didn't my first solution work?
cat provision-db.sql | docker exec -i boohoo.postgres psql -h "${POSTGRES_HOST}" -U "${POSTGRES_USER}" -a -q -f - should do the trick asuming provision-db.sql
That is due to the fact, that the variables ${POSTGRES_USER} and ${POSTGRES_PASSWORD} get evaluated on your host machine and I guess they are not set there. In addition, I forgot to specify the -w flag to avoid the password prompt
3) Why does that work?
cat provision-db.sql | docker exec -i boohoo.postgres bash -c 'psql -U ${POSTGRES_USER} -w -a -q -f -'
Well, let's go through it step by step.
First, we print the content of provision-db.sql, which resides on the host machine to stdout and pipe it to the next command via |.
docker-exec executes a command in the container specified (boohoo.postgres). By specifying the -i flag we allow the stdin from your host to go to stdin in the container <- that's important.
In the container, we execute bash -c which is just a wrapper to avoid evaluating the bash variables on the host. We want the variables from the container and by putting it into single quotes we can do that.
docker-exec boohoo.postgres bash -c "echo $POSTGRES_USER"
evaluates the host env variable named POSTGRES_USER.
docker-exec boohoo.postgres bash -c "echo $POSTGRES_USER"
evaluates the container env variable named POSTGRES_USER.
Next we just have to get our postgres command in order.
psql -U ${POSTGRES_USER} -w -a -q -f -
-U specifies the user
-w does not ask for password
-q do it quietly
-f - process whatever you get from stdin
-f is an option for psql and not for docker exec, and psql is running inside the container, so it can only access the file if it is inside the container as well.

The second command doesn't run as root

I have created a Jenkins job today, what it does is the Jenkins user should log into another server and run two commands separated by &&:
ssh -i /creds/jenkins jenkins#servername.com "sh -c 'sudo su && lxc exec containername bash'"
The logging part works fine, then it runs the sudo su command and becomes root but it never runs the second command.
I even did this manually and from the Jenkins machine logged into the other server (servername). Then ran sh -c "sudo su && lxc exec containername bash" with no luck.
Try to execute the second command as a parameter for the sudo su command, like this:
ssh -i /creds/jenkins jenkins#servername.com "sh -c 'sudo su -c "lxc exec containername bash"'"

how to periodically dump a set of database tables to a local machine from a psql database in a single bash script run on Mac OS X?

I tried this but it does not work.
#!/bin/bash
TABLENAMES="user_stats"
ssh -t railsapps#xxx.xxx.xxx.xx -p xxx bash -c "'
for TABLENAME in $TABLENAMES
do
psql -d mydb -P format=unaligned -P tuples_only -P fieldsep=\, -c "SELECT * FROM $TABLENAME" > /tmp/$TABLENAME
done
'"
You should use the Postgres COPY command, and stuff them all in to a single sql file, that you then run with psql. Then you can take that script and either feed it to cron on Mac OS X, or launchd to run the script periodically.

bash script returns error "ERROR: syntax error at end of input LINE 1: SELECT" for psql request to copy the table to an external file

What should I do for making it work?
#!/bin/bash
TABLENAMES="user_stats"
ssh -t railsapps#xxx.xxx.xxx.xx -p xxx bash -c "'
for TABLENAME in $TABLENAMES
do
psql -d mydb -P format=unaligned -P tuples_only -P fieldsep=\, -c "SELECT * FROM $TABLENAME" > /tmp/$TABLENAME
done
'"
General problem: how to periodically dump the database tables to a local machine from a psql database in a single bash script run on Mac OS X?
Firstly, you should test your SQL and bash scripts remotely (do SSH interactively).
I think your problem is caused by a bad mix of quote / double-quote. I think the star (*) and $TABLENAME are expensed before the SSH call, so too early. Try to put a backslash before the $ sign.
You should use the verbose or the debug option, to help to understand what is really executed:
ssh -t railsapps#xxx.xxx.xxx.xx -p xxx bash -vxc "'
for TABLENAME in \$TABLENAMES; do
psql -d mydb -P format=unaligned -P tuples_only -P fieldsep=\, -c "SELECT \* FROM \$TABLENAME" > /tmp/\$TABLENAME
done
'"

Resources