Search code examples
pythondockerdocker-composepyyaml

Editing docker-compose.yml with PyYAML


I have a pretty standard docker-compose.yml and I need to edit the password of the database programmatically.

Since its a YAML file, I thought it would be simple to edit and dump the content. So far I tried the PyYAML and it just mess the docker-compose file and I don't know why.

Loading and dumping the same content, it breaks the structure.

Content of the docker-compose.yml:

version: '2'
services:
  web:
    container_name: xxx
    ports:
     - "80:80"
    volumes:
      - .:/xxx
    depends_on:
      - mysql
    build: .
  mysql:
    ports:
     - "32768:32768"
     - "3306:3306"
    container_name: xxx-mysql
    restart: always
    image: mariadb:latest
    environment:
      MYSQL_ROOT_PASSWORD: 'thiswillbechangeonsetupscript'
      MYSQL_DATABASE: 'xxxdb'
    volumes:
     - ./database:/var/lib/mysql
    ports:
      - "3306:3306"

This is how I'm loading and dumping the content:

import yaml

with open("docker-compose.yml", 'r') as ymlfile:
    docker_config = yaml.load(ymlfile)

with open("docker-compose.yml", 'w') as newconf:
    yaml.dump(docker_config, newconf)

And this is how the file is saved.

services:
  mysql:
    container_name: xxx-mysql
    environment: {MYSQL_DATABASE: xxxdb, MYSQL_ROOT_PASSWORD: thiswillbechangeonsetupscript}
    image: mariadb:latest
    ports: ['3306:3306']
    restart: always
    volumes: ['./database:/var/lib/mysql']
  web:
    build: .
    container_name: xxx
    depends_on: [mysql]
    ports: ['80:80']
    volumes: ['.:/xxx']
version: '2'

Is there any better way to do this?! What I'm missing?


Solution

  • You need to add default_flow_style=False when you write the yaml:

    import yaml
    
    with open("docker-compose.yml", 'r') as ymlfile:
        docker_config = yaml.load(ymlfile)
    
    with open("docker-compose_new.yml", 'w') as newconf:
        yaml.dump(docker_config, newconf, default_flow_style=False)
    

    Then you will get the following as output which, except using alphabetic order to write the lines, is similar to your input:

    services:
      mysql:
        container_name: xxx-mysql
        environment:
          MYSQL_DATABASE: xxxdb
          MYSQL_ROOT_PASSWORD: thiswillbechangeonsetupscript
        image: mariadb:latest
        ports:
        - 3306:3306
        restart: always
        volumes:
        - ./database:/var/lib/mysql
      web:
        build: .
        container_name: xxx
        depends_on:
        - mysql
        ports:
        - 80:80
        volumes:
        - .:/xxx
    version: '2'
    

    Please note that, in your original docker-compose.yaml you are declaring the ports variable twice, so yaml parser will only consider the last variable. To fix that, remove the following lines:

    ports:
      - "3306:3306"
    

    Then, running the write operation as explained above gives the following ouput:

    services:
      mysql:
        container_name: xxx-mysql
        environment:
          MYSQL_DATABASE: xxxdb
          MYSQL_ROOT_PASSWORD: thiswillbechangeonsetupscript
        image: mariadb:latest
        ports:
        - 32768:32768
        - 3306:3306
        restart: always
        volumes:
        - ./database:/var/lib/mysql
      web:
        build: .
        container_name: xxx
        depends_on:
        - mysql
        ports:
        - 80:80
        volumes:
        - .:/xxx
    version: '2'