# OCI Env Deep Dive Pulpcon 2022
By David Newswanger
---
### Agenda
1. What is OCI Env
2. How does OCI Env Work?
3. OCI Env Profile system
---
### What is OCI Env?
- A pulp developer environment to develop pulp using the pulp all in one images
- Based off of the developer environment for galaxy_ng
- Features:
- Fast load times (sub 2 minutes to spin up)
- Auto code reloading
- Out of the box support for running tests
- Helper functions for working on pulp
- Podman and Docker support
- Multi environment support
---
### Philosophy
Ansible: Make the easy things easy and the hard things possible.
- Everything must work out of the box with the least possible number of configurations and commands for developers to go from forking a pulp plugin and writing code.
- The system must be flexible enough to allow for a wide variety of development modes.
---
### A quick demo of the basics
- Setting up your environment
- Tip: use `OCI_ENV_PATH`
- Starting pulp
- Running tests
- Resetting the db
- Accessing the shell
---
### Running multiple environments
```bash
API_PORT=4002
COMPOSE_PROJECT_NAME=my_second_env
SRC_DIR=~/code/tmp/pulpcon/src_path_2
COMPOSE_PROFILE=
DEV_SOURCE_PATH=pulp_file
COMPOSE_BINARY=docker
```
---
### How does oci env work?
- Builds a custom image off of the pulp all in one container that contains the utilities necessary to run the dev environment
- Generates a set of docker/podman compose files for launching the environment
- Uses the generated compose files to start and interact with the containers
---
### Containerfile
`base/Dockerfile`
```Dockerfile
FROM ghcr.io/pulp/pulp-ci-centos:latest
# configure S6 to use env variables
ENV S6_KEEP_ENV=1
COPY dev_requirements.txt /dev_requirements.txt
RUN pip3 install -r dev_requirements.txt
COPY settings.py /etc/pulp/settings.py
COPY s6-rc.d/oci-env-prepare/ /etc/s6-overlay/s6-rc.d/oci-env-prepare/
COPY s6-rc.d/oci-env-profiles /etc/s6-overlay/s6-rc.d/oci-env-profiles/
RUN cd /etc/s6-overlay/s6-rc.d/user/contents.d && touch oci-env-prepare oci-env-profiles
# Modify postgres so it starts after oci-env gets a chance to initialize the plugins from source
COPY s6-rc-modifications/postgres-prepare/dependencies.d /etc/s6-overlay/s6-rc.d/postgres-prepare/dependencies.d
# Modify pulpcore-api so it auto reloads
COPY s6-rc-modifications/pulpcore-api/run /etc/s6-overlay/s6-rc.d/pulpcore-api/run
COPY utils.sh /utils.sh
```
---
### Base compose file
```yaml
---
version: "3.7"
services:
_base:
build:
context: "{OCI_ENV_DIR}/base/"
dockerfile: "Dockerfile"
image: "localhost/oci_env/pulp:base"
entrypoint: "/bin/true"
pulp:
image: "localhost/oci_env/pulp:base"
cap_add:
- SYS_PTRACE
env_file:
- "{OCI_ENV_DIR}/.compiled/{COMPOSE_PROJECT_NAME}/combined.env"
- "{OCI_ENV_CONFIG_FILE}"
environment:
- "DEV_SOURCE_PATH={DEV_SOURCE_PATH}"
- "DJANGO_SUPERUSER_USERNAME={DJANGO_SUPERUSER_USERNAME}"
- "DJANGO_SUPERUSER_PASSWORD={DJANGO_SUPERUSER_PASSWORD}"
- "API_HOST={API_HOST}"
- "API_PORT={API_PORT}"
- "API_PROTOCOL={API_PROTOCOL}"
- "NGINX_PORT={NGINX_PORT}"
- "NGINX_SSL_PORT={NGINX_SSL_PORT}"
- "COMPOSE_PROJECT_NAME={COMPOSE_PROJECT_NAME}"
- S6_BEHAVIOUR_IF_STAGE2_FAILS=0
ports:
- "{API_PORT}:{NGINX_PORT}"
depends_on:
- _base
volumes:
- "{SRC_DIR}:/src:z"
- "{OCI_ENV_DIR}:/opt/oci_env:z"
- "oci_pulp:/var/lib/pulp"
- "oci_redis_data:/data"
- "oci_pg_data:/var/lib/pgsql"
volumes:
oci_pulp:
name: "{COMPOSE_PROJECT_NAME}_pulp"
oci_pg_data:
name: "{COMPOSE_PROJECT_NAME}_pg_data"
oci_redis_data:
name: "{COMPOSE_PROJECT_NAME}_redis_data"
```
---
### Compose file generation
1. Load the environment configuration from your compose.env file
2. Parse the profiles defined in the env file
3. Compile the profiles to be used by the container runtime
- Perform variable substitution
- Parse pulp settings from profiles
- Create init.sh script
---
### Container Launch
```shell
# Inspect the container command with -v
$ oci-env -v compose up
Running command in container: docker-compose -p oci_env -f /Users/dnewswan/code/tmp/pulpcon/oci_env/.compiled/oci_env/base_compose.yaml up
Starting oci_env__base_1 ... done
Starting oci_env_pulp_1 ... done
$ oci-env -v shell
Running command in container: docker exec -it oci_env_pulp_1 bash
```
---
### oci-env CLI
- Executes podman/docker compose commands
```shell
oci-env compose <args>
# Is a wrapper on top of
podman-comopose <args>
```
---
### oci-env CLI
- Executes commands inside the container
- Can be a script in the container found in base/container_scripts/.
- Can be a direct command.
```shell
# Run the base/container_scripts/database_reset.sh script in the container
oci-env -v db reset
Running command in container: docker exec -it oci_env_pulp_1 bash /opt/oci_env/base/container_scripts/database_reset.sh
# Runs podman exec -it bash
oci-env -v shell
Running command in container: docker exec -it oci_env_pulp_1 bash
```
---
### oci-env CLI
- Executes commands on the host machine
- Currently just the `base/local_scripts/generate_client.sh` for the `oci-env generate-client` command
```shell
# runs base/local_scripts/generate_client.sh
oci-env -v generate-client
Running local command: bash /Users/dnewswan/code/tmp/pulpcon/oci_env/base/local_scripts/generate_client.sh pulpcore
```
---
## Profiles
---
### What are profiles?
- A system to easily configure the developer environment to operate in different deployment modes.
- Goes beyond just changing settings.py
- Can define extra services to run alongside pulp
- S3 Storage
- Keycloak authentication
- Web UI
- Can define specific configurations for pulp
- Can run arbitrary bash scripts to set up objects in the database or install extra dependencies.
---
### Let's write a new profile!
Goal: launch a fileserver alongside pulp file that we can use to test pulp file syncs with.
---
### Create the profile
```shell
$ oci-env profile init -p pulp_file fs
New profile "pulp_file/fs" successfully created at: /Users/dnewswan/code/tmp/pulpcon/pulp_file/profiles/fs
To use it set "COMPOSE_PROFILE=pulp_file/fs"
$ tree ../pulp_file/profiles
../pulp_file/profiles
└── fs
├── README.md
├── compose.yaml
├── init.sh
├── profile_requirements.txt
└── pulp_config.env
```
---
- README.md: documentation for using the profile.
- compose.yaml: compose file to define any new services that the profile provides.
- init.sh: shell script that runs when the container starts for the first time to initialize any extra stuff the profile needs
- profile_requirements.txt: a list of other profiles that this profile may need to function
- pulp_config.env: a set of environment variables that the profile requires to run
---
### Our compose.yaml
```yaml
---
version: "3.7"
services:
fileserver:
build:
context: "{SRC_DIR}/pulp_file/profiles/fs/"
dockerfile: "containerfile"
ports:
- "8001:80"
volumes:
- "{WEBSERVER_DIR}:/usr/local/apache2/htdocs/"
```
---
### containerfile for our service
containerfile
```Dockerfile
FROM registry.hub.docker.com/library/httpd:alpine
# INSTALL PYTHON IN THE HTTPD IMAGE
ENV PYTHONUNBUFFERED=1
RUN apk add --update --no-cache python3 git && ln -sf python3 /usr/bin/python
RUN python3 -m ensurepip
RUN pip3 install --no-cache --upgrade pip setuptools
# INSTALL PULP MANIFEST
RUN pip install git+https://github.com/pulp/pulp-manifest.git
COPY cmd.sh /cmd.sh
RUN chmod u+x /cmd.sh
CMD /cmd.sh
```
---
### cmd.sh to launch our service
cmd.sh
```bash
# /bin/bash
pulp-manifest /usr/local/apache2/htdocs/
httpd-foreground
```
---
### Our init.sh file
```bash
#!/bin/bash
sleep 10
pulp file remote create --name my_test_remote --url http://fileserver:80/PULP_MANIFEST
pulp file repository create --name demo_repo --remote my_test_remote
```
---
### Our pulp_config.env
```
PULP_ALLOWED_CONTENT_CHECKSUMS=['sha1', 'sha224', 'sha256', 'sha384', 'sha512']
```
---
### Configuring our environment
```
COMPOSE_PROFILE=pulp_file/fs
DEV_SOURCE_PATH=pulpcore:pulp_file
COMPOSE_BINARY=docker
WEBSERVER_DIR=~/code/tmp/pulpcon/webserver
```
---
### Shipping profiles with plugins
- Profiles can (and should!) be shipped with plugins
- oci-env can load any profile from the `<plugin>/profiles/` directory
```shell
$ oci-env profile ls
Plugin: oci_env
galaxy_base
pulp_rpm_base
pulp_ansible_base
galaxy_ui
Plugin: pulp_file
pulp_file/fs
```
---
### Multiple profiles
- Multiple profiles can be specified with the `:` separator
- Example: configure pulp file to run with an s3 backend and our new fileserver profile
```
COMPOSE_PROFILE=s3:pulp_file/fs
DEV_SOURCE_PATH=pulpcore:pulp_file
```
---
### Multiple compose files
`docker-compose` and `podman-compose` both allow for the `-f` flag that allows a user to specify as many compose files as you want.
```shell
docker-compose -f compose1.yaml -f compose2.yaml -f compose3.yaml
```
This allows for multiple compose yaml files to be combined into one. oci-env leverages this to combine all of the compose.yaml files in each profile into one compose file.
---
### Compose file tricks
- Any compose file can modify any compose file that comes before it.
```yaml
---
version: "3.7"
services:
pulp:
image: "my_custom_image:latest"
```
---
# Q and A
---