Can't connect to MySQL docker container launched via ansible - mysql

I get an "Access denied" error trying to connect to the MySQL Docker container that I start through my ansible script. If I create the container with the Docker CLI tool, everything works. I'm wondering if this has something to do with the environment variables.
This works
$ docker run --name database -e MYSQL_ROOT_PASSWORD=hunter2 -d mysql:5.7
$ mysql -h $CONTAINER_IP_ADDRESS -u root -phunter2
$ mysql>
This is broken
playbook.yml
- name: Start new MySQL container
docker:
name: database
image: mysql:5.7
state: running
env:
MYSQL_ROOT_PASSWORD=hunter2
Then the connection fails:
$ mysql -h $CONTAINER_IP_ADDRESS -u root -phunter2
ERROR 1045 (28000): Access denied for user 'root'#'$CONTAINER_IP_ADDRESS' (using password: YES)
Any idea what I'm missing here?

The env definition for the docker module should be a dict object, not a string
env:
MYSQL_ROOT_PASSWORD: hunter2

After some time I found that ansible does not play well with Docker. I've been moving some infraestructure from ansible to Docker, and keeping some legacy code like adding users to mysql or dumping dbs didn't work from Ansible.
The solution is to use shell instead:
- name: Drop/delete mysql table {{ mysql_table }}
shell: mysqladmin -h 127.0.0.1 -p{{ mysql_password }} -u{{ mysql_user }} drop {{ mysql_table }} -f
# If it fails it means db was already deleted, so continue.
ignore_errors: True
tags:
- mysql
- mysql_rebuild_db
- mysql_rebuild_db_quick
- name: Add mysql table {{ mysql_table }}
shell: mysqladmin -h 127.0.0.1 -p{{ mysql_password }} -u{{ mysql_user }} create {{ mysql_table }} -f
tags:
- mysql
- mysql_rebuild_db
- mysql_rebuild_db_quick
- name: Create mysql user {{ mysql_user }} in {{ mysql_table }}
shell: echo "CREATE USER {{ mysql_user }}#'localhost' IDENTIFIED BY '{{ mysql_password }}';" | mysql -h 127.0.0.1 -proot -uroot
# The user may already exists in a previous run
ignore_errors: True
tags:
- mysql
- mysql_rebuild_db
- mysql_rebuild_db_quick

Related

How to run sql scripts in GitHub Actions

I'm currently building application and want to test on GitHub actions.
I'm using mysql for database and need to insert test data(csv format) into GitHub action's container, but it's not working well.
Here is the steps.
Create mysql container. → OK
Run script to create table. → NG.
Insert test data into table created on step2. → NG.
In every step, there are no errors, but it seems I can't run scripts in step2 and step3.
Please tell me which part of my code is wrong.
Codes here.
ci.yml
name: sample-ci
on:
push:
branches: [ "main" ]
pull_request:
branches: [ "main" ]
jobs:
job-with-mysql-8_0:
runs-on: ubuntu-latest
services:
db:
image: mysql:8
ports:
- 3306:3306
env:
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: sampleDB
options: >-
--health-cmd "mysqladmin ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- name: setup-go
uses: actions/setup-go#v3
with:
go-version: 1.16
- name: checkout
uses: actions/checkout#v3
- name: Show Docker containers
run: docker ps -a
- name: Show databases for root user
run: mysql --protocol=tcp -h localhost -P 3306 -u root -ppassword -e "SHOW DATABASES"
- name: Set up MySQL
run: sudo /lib/systemd/systemd-sysv-install enable mysql
sudo systemctl enable mysql.service
sudo systemctl start mysql.service
- name: Run sample.sql
run: |
mysql --protocol=tcp -h localhost -P 3306 -u root -ppassword -e "$(cat $(find ./ -name sample.sql))"
- name: Show created tables
run: mysql --protocol=tcp -h localhost -P 3306 -u root -ppassword -e "USE sampleDB"
mysql --protocol=tcp -h localhost -P 3306 -u root -ppassword -e "SHOW TABLES"
- name: Insert data into user table
run: sudo /lib/systemd/systemd-sysv-install enable mysql
sudo systemctl enable mysql.service
sudo systemctl start mysql.service
sh ./.github/scripts/sample.sh
- name: Check inserted data
run: mysql --protocol=tcp -h localhost -P 3306 -u root -ppassword -e "USE sampleDB"
mysql --protocol=tcp -h localhost -P 3306 -u root -ppassword -e "SELECT * FROM user"
sample.sql
USE sampleDB;
CREATE TABLE user(
id INT,
name VARCHAR(100) NOT NULL,
PRIMARY KEY(id),
UNIQUE(name)
);
sample.sh
mysql -uroot -ppassword --local-infile=1 sampleDB -e "LOAD DATA LOCAL INFILE '/docker-entrypoint-initdb.d/user.csv' INTO TABLE user FIELDS TERMINATED BY ',' LINES TERMINATED BY '\n' IGNORE 1 LINES"
user.csv
id,name
1,aaa
2,bbb
3,ccc

Kubernetes Job can't reach service right away

I have a Job that connects to a running MySQL database and executes some commands:
apiVersion: batch/v1
kind: Job
metadata:
name: do-db-stuff
spec:
template:
spec:
containers:
- name: do-db-stuff
image: mysql:5
command: ["/bin/sh"]
args: ["-c", "mysql -h db -uroot -pmypassword mydb -Bse 'show tables;'"]
restartPolicy: Never
backoffLimit: 10
This job spins on a crash loop, with each pod reporting: ERROR 2005 (HY000): Unknown MySQL server host 'db' (11). If I change the command to ["/bin/sh", "-c", "sleep 3600"] and log in, I can connect from the Job's pod to the database with the command mysql -h db -uroot -pmypassword mydb -Bse 'show tables;' just fine. Based on this, I changed the command in the job to
args: ["-c", "sleep 3 && mysql -h db -uroot -pmypassword mydb -Bse 'show tables;'"]`
and now it works (i.e. correctly prints out the db tables instead of the unknown host error above). My question is why does the sleep 3 do anything? The service and database are not changing in the background at all, so why does waiting in the Job's pod make it connect?

Ansible Install MySql 5.7 - Set Root User Password

I've recently upgraded my vagrant from ubuntu/trusty-64 to bento/ubuntu-16.04. With that MySQL was updated to 5.7. I've made several updates to my playbook, but I keep getting stuck when setting the root user's password.
In the past (before 5.7) the following was sufficient:
- name: MySQL | Set the root password.
mysql_user:
name=root
host=localhost
password={{ mysql_root_password }}
become: true
In my playbook this is tested by attempting to delete an anonymous user.
- name: MySQL | Delete anonymous MySQL server user for {{ server_hostname }}
mysql_user:
name=""
host="{{ server_hostname }}"
state="absent"
login_user=root
login_password={{ mysql_root_password }}
However, now my playbook fails at this step, returning:
"Access denied for user 'root'#'localhost'"
TASK [mysql : MySQL | Delete anonymous MySQL server user for vagrant] **********
task path: /Users/jonrobinson/vagrant/survey/playbooks/roles/mysql/tasks/mysql.yml:51
fatal: [vagrant]: FAILED! => {"changed": false, "failed": true, "msg": "unable to connect to database, check login_user and login_password are correct or /home/vagrant/.my.cnf has the credentials. Exception message: (1698, \"Access denied for user 'root'#'localhost'\")"}
I've tried several things:
Setting the password blank for root user mysql_root_password=""
Attempting to delete the root user then recreate it with Ansible. I get same error probably because it's trying to act at the root user.
Manually updating the root password in mysql. - This also doesn't appear to work (password isn't recognized) unless I delete the root user and recreate it with all the permissions. Just updating the root user password appears to have no change.
My Full MySQL YAML:
---
- name: MySQL | install mysql packages
apt: pkg={{ item }} state=installed
become: true
with_items:
- mysql-client
- mysql-common
- mysql-server
- python-mysqldb
- name: MySQL | create MySQL configuration file
template:
src=my.cnf.j2
dest=/etc/mysql/my.cnf
backup=yes
owner=root
group=root
mode=0644
become: true
- name: MySQL | create MySQLD configuration file
template:
src=mysqld.cnf.j2
dest=/etc/mysql/conf.d/mysqld.cnf
backup=yes
owner=root
group=root
mode=0644
become: true
- name: MySQL | restart mysql
service: name=mysql state=restarted
become: true
- name: MySQL | Set the root password.
mysql_user:
name=root
host=localhost
password={{ mysql_root_password }}
become: true
- name: MySQL | Config for easy access as root user
template: src=mysql_root.my.cnf.j2 dest=/root/.my.cnf
become: true
- name: MySQL | Config for easy access as root user
template: src=mysql_root.my.cnf.j2 dest={{ home_dir }}/.my.cnf
when: "'{{ user }}' != 'root'"
- name: MySQL | Delete anonymous MySQL server user for {{ server_hostname }}
mysql_user: name="" host="{{ server_hostname }}" state="absent" login_user=root login_password={{ mysql_root_password }}
- name: MySQL | Delete anonymous MySQL server user for localhost
mysql_user: name="" state="absent" host=localhost login_user=root login_password={{ mysql_root_password }}
- name: MySQL | Secure the MySQL root user for IPV6 localhost (::1)
mysql_user: name="root" password="{{ mysql_root_password }}" host="::1" login_user=root login_password={{ mysql_root_password }}
- name: MySQL | Secure the MySQL root user for IPV4 localhost (127.0.0.1)
mysql_user: name="root" password="{{ mysql_root_password }}" host="127.0.0.1" login_user=root login_password={{ mysql_root_password }}
- name: MySQL | Secure the MySQL root user for localhost domain (localhost)
mysql_user: name="root" password="{{ mysql_root_password }}" host="localhost" login_user=root login_password={{ mysql_root_password }}
- name: MySQL | Secure the MySQL root user for {{ server_hostname }} domain
mysql_user: name="root" password="{{ mysql_root_password }}" host="{{ server_hostname }}" login_user=root login_password={{ mysql_root_password }}
- name: MySQL | Remove the MySQL test database
mysql_db: db=test state=absent login_user=root login_password={{ mysql_root_password }}
- name: MySQL | create application database user
mysql_user: name={{ dbuser }} password={{ dbpass }} priv=*.*:ALL host='%' state=present login_password={{ mysql_root_password }} login_user=root
- name: MySQL | restart mysql
service: name=mysql state=restarted
become: true
I was able to figure it out. The gist of the problem had to do with mysql 5.7 using auth_socket for the root user when no password is provided. See the following: "That plugin doesn’t care and doesn’t need a password. It just checks if the user is connecting using a UNIX socket and then compares the username. "
When this is the case you cannot update the password using:
SET PASSWORD FOR 'root'#'localhost' = PASSWORD('test');
And instead must use:
ALTER USER 'root'#'localhost' IDENTIFIED WITH mysql_native_password='test';
Solution 1: However, Ansible, as of version 2.0.2 didn't account for this. I was able to get around this by setting the password before MySql is installed
- name: Specify MySQL root password before installing
debconf: name='mysql-server' question='mysql-server/root_password' value='{{mysql_root_password | quote}}' vtype='password'
become: true
- name: Confirm MySQL root password before installing
debconf: name='mysql-server' question='mysql-server/root_password_again' value='{{mysql_root_password | quote}}' vtype='password'
become: true
- name: MySQL | install mysql packages
apt: pkg={{ item }} state=installed
become: true
with_items:
- mysql-client
- mysql-common
- mysql-server
- python-mysqldb
...
However, this has also since been addressed by Ansible
Solution 2: The easiest solution is just to upgrade Ansible to 2.2.1
From what I understand, in MySQL, changing the root password needs to be done for localhost, the server's hostname and 127.0.0.1 and also needs full privileges. Something along the these lines may help (Note: I've only tested this on MariaDB, and not MySQL):
tasks:
- name: Set a new root password
mysql_user: check_implicit_admin=yes
login_user=root
login_password={{ mysql_root_password }}
user=root
password={{ NEW_mysql_root_password }}
host={{ item }}
priv='*.*:ALL,GRANT'
with_items:
- localhost
- 127.0.0.1
- {{ server_hostname }}
notify:
- restart_mariadb
handlers:
- name: restart_mariadb
service: name=mariadb
state=restarted

make ansible check if database is present on a remote host

To make sure that host A can connect to the database of the host B, I try to use mysql_db on a remote host
- name: Make sure A can connect to B database
mysql_db:
login_user=root
login_password=password
login_host=B_address
login_port=B_port
name=B_database
state=present
and I get that error message even if the login/pass is right
msg: unable to connect, check login_user and login_password are correct,
or alternatively check ~/.my.cnf contains credentials
Am i missing something? can I set login_host with a specific ansible host?
Did you configure the mysql to accept the connection from Host A because
by default mysql only accept connection from localhost.
If you have configured that the mysql accept the connection from Host A then you can verify that the database exist
- name: check if DB exists
shell: mysql -e 'SHOW DATABASES;' | grep {{ B_database }}
register: dbstatus
failed_when: dbstatus.rc == 2
Then you can run your task, if the B_database exist
- name: Make sure A can connect to B database
mysql_db:
login_user=root
login_password=password
login_host=B_address
login_port=B_port
name=B_database
state=present
when: dbstatus.rc == 0
no_log: yes # You can disable this, if you want to print the stdout
If you are sure that the above cases are true and you are still getting error, then please do this:
Add this task inside your task/main.yml
- name: Copy the root credentials as .my.cnf file
template:
src: root.cnf.j2
dest: "~/.my.cnf"
mode: 0600
and this will be your root.cnf.j2
[client]
user=root
password={{ password }}
What it will do is, to connect the mysql from the root user without password and perform these task. You can remove it after running all the task or leave it like this because it is under root and have tight permission.
In my situation I only wanted to import an SQL file once, when the database gets created. Here's how to achieve that, the Ansible way:
---
- name: Test playbook
hosts: localhost
connection: local
tasks:
- name: Database exists
mysql_db:
name: some_db
state: present
register: database_exists
- name: Import database schema
mysql_db:
name: some_db
state: import
target: database.sql.gz
when: database_exists.changed
Using combination of above answers
- name: check if DB exists
shell: mysql --host={{ db_host }} --user={{ db_username }} --password={{ db_password }} -e 'SHOW DATABASES;' | grep -c {{ db_name }}
register: dbstatus
failed_when: dbstatus.rc == 2
- name: Create database
mysql_db: name={{db_name}} collation=utf8mb4_unicode_ci state=present login_host={{ db_host }} login_user={{ db_root_username }} login_password={{ db_root_password }}
when: dbstatus.stdout == "0"
- name: Create application user in the database
mysql_user: name={{ db_username }} password={{ db_password }} host={{ db_connection_host }} append_privs=true priv={{ db_name }}.*:ALL state=present login_host={{ db_host }} login_user={{ db_root_username }} login_password={{ db_root_password }}
when: dbstatus.stdout == "0"
useful to note you can get the output of dbstatus by adding -vvv when you run the playbook
Because ansible was failing on the grep exit code when it was not found, I found a different way to filter the databases:
> mysql -e "SHOW DATABASES LIKE '<database>'" -sN
and then you can do the following:
when: dbstatus.stdout_lines
because python will treat an empty array as false
keep in mind that there's actually 3 hosts involved here:
the "ansible execution host", the host on which you're running ansible;
the "ansible target host", the host(s) your task target;
the mysql host, that is the databse server.
In this case, the target host needs to be able to connect to the mysql host as the login_user with the login_password.
Check :
that the provider user and password are granted connectivity to the mysql host from the target host
that the firewalls between the target and mysql hosts allow mysql connections (typically port 3306). If these things work, so should the ansible module.
Use config_file: to connect, and check mode to figure out if database exists or not:
- name: Make sure A can connect to B database
mysql_db:
config_file: /etc/mysql/debian.cnf
name=B_database
state=present
check_mode: yes
register: database_exists
- debug: var=database_exists.changed
I'm working with postgresql and a much later version of ansible, so using the above answers I pulled together this solution.
- name: find out if db exists
postgresql_query:
query: |
SELECT datname FROM pg_database where datname = '{{ dbname }}'
register: result
become: yes
become_user: postgres
- name: look at results {{ result.rowcount }}.....
debug:
var: result
{{ dbname }} is a variable which can be replaced with a literal. The second task shows that you can look at the result.rowcount and if it is 0 then the database does not exist. If it is 1, the database exist. Now you can use result.rowcount with when: to determine if a task is executed or not. For example, in my case I'm trying to determine if I should create the database or not, such as:
- name: message if db exists....
debug:
msg: "yes it exist"
when: "{{ result.rowcount }} == 1"

ansible mysql configuration in one file for ubuntu 14.04

I'm trying to write a single page ansible script for mysql installation and setup a new user and create a empty DB. what I've tried so far -
hosts file
[mysql]
webapp ansible_ssh_host=xxx.xxx.xxx.xxx
mysql.yml (The single file for all tasks/vars/handelers) (both hosts and mysql.yml in same directory) and I can login in remote system using ssh
---
- hosts: mysql
vars:
- system_packages:
- build-essential
- python-dev
- python-pip
- libmysqlclient-dev
- mysql-server
- python-mysqldb
- root_password: root
tasks:
- name: Install MySQL
apt: pkg={{ item }} state=installed update-cache=yes
with_items: system_packages
tags:
- setup
- name: Start MySQL service
action: service name=mysql state=started enabled=yes
- name: Update mysql password for root account
mysql_user: name=root host={{ item }} password={{root_password}}
with_items:
- 127.0.0.1 #In case of distributed system how should I place Ip addr of this system
- localhost
- name: create db 'mydb'
action: mysql_db db=mydb state=present
- name: Creates database user 'eric' with password 'eric' for 'mydb' and grant all priveleges
action: mysql_user state=present name=eric password=eric priv=mydb.*:ALL
handlers:
- name: start mysql
service: name=mysql state=started
when I run this script I get this output(+error)
failed: [webapp] => (item=127.0.0.1) => {"failed": true, "item": "127.0.0.1"}
msg: unable to connect to database, check login_user and login_password are correct or ~/.my.cnf has the credentials
You can't connect to mysql server before you will set root password. To do this use the following:
- name: Set password for root user
shell:
mysqladmin password "{{ root_password }}" -u root
After a default installation on ubuntu, you only can login to your mysql as root with password "root". So to change your password you will have to set your mysql login user to root (as you did in the next task)
- name: Update mysql password for root account
mysql_user:
name=root host={{ item }}
password={{root_password}}
login_user=root
login_password="root"
sudo: yes
sudo_user: root
There is also a ready-to-go solution in ansible galaxy
Please add this task inside your task/main.yml
- name: Copy the root credentials as .my.cnf file
template: src=root.cnf.j2 dest=~/.my.cnf mode=0600
and this will be your root.cnf.j2
[client]
user=root
password={{ root_password }}
What it will do is, to connect the mysql from the root user without password and perform these task. You can remove it after running all the task or leave it like this because it is under root and have tight permission.