Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 41 additions & 4 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,43 @@
demo/assets/keys/gpg/
demo/assets/certs/
# Version control
.git/
.gitignore
.github/

# Development environment
.devcontainer/
.claude/
.coverage
venv/
__pycache__/

# Tests and coverage
**/tests/
**/unit_tests/
.pytest_cache/
.coverage
htmlcov/
pytest.ini

# Development configs
pylint.rc
Makefile

# Documentation
*.md
!README.md
docs/

# Demo files
demo/

# Python cache
__pycache__/
*.pyc
*.pyo
*.pyd
.Python
*.egg-info/

# IDE files
.vscode/
.idea/
*.swp
.DS_Store
140 changes: 140 additions & 0 deletions .github/workflows/docker-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
name: Build and Push Docker Images

on:
push:
branches:
- main
tags:
- 'v*.*.*'
- '[0-9]+.[0-9]+.[0-9]+'
pull_request:
branches:
- main
workflow_dispatch:
workflow_call:

env:
IMAGE_NAME: pulp-manager

jobs:
build-and-push:
name: Build and Push Docker Image
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
submodules: recursive

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Determine registries to push to
id: registries
run: |
# Always include ghcr.io
registries="ghcr.io"

# Add docker.io and quay.io for release tags
if [[ "${{ github.ref }}" == refs/tags/* ]]; then
registries="ghcr.io docker.io quay.io"
fi

echo "registries=${registries}" >> $GITHUB_OUTPUT
echo "Will push to: ${registries}"

- name: Log in to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Log in to Docker Hub
if: startsWith(github.ref, 'refs/tags/')
uses: docker/login-action@v3
with:
registry: docker.io
username: ${{ secrets.DOCKER_BOT_USERNAME }}
password: ${{ secrets.DOCKER_BOT_PASSWORD }}

- name: Log in to Quay.io
if: startsWith(github.ref, 'refs/tags/')
uses: docker/login-action@v3
with:
registry: quay.io
username: ${{ secrets.QUAY_BOT_USERNAME }}
password: ${{ secrets.QUAY_BOT_PASSWORD }}

- name: Determine tags
id: tags
run: |
tags=""

if [[ "${{ github.ref }}" == refs/tags/* ]]; then
# Release tag (e.g., v1.2.3)
version="${{ github.ref_name }}"
version="${version#v}" # Remove 'v' prefix
major=$(echo $version | cut -d. -f1)
minor=$(echo $version | cut -d. -f1-2)
tags="${version} ${minor} ${major} latest"
elif [ "${{ github.ref_name }}" == "main" ]; then
# Main branch
tags="main latest"
elif [ "${{ github.event_name }}" == "pull_request" ]; then
# PR
tags="pr-${{ github.event.pull_request.number }}"
else
# Other branches
tags="${{ github.ref_name }}"
fi

# Add SHA tag for traceability
sha_short=$(echo ${{ github.sha }} | cut -c1-7)
tags="${tags} sha-${sha_short}"

echo "tags=${tags}" >> $GITHUB_OUTPUT
echo "Will use tags: ${tags}"

- name: Build Docker image
uses: docker/build-push-action@v5
with:
context: .
file: ./Dockerfile
push: false
load: true
tags: pulp/${{ env.IMAGE_NAME }}:ci
labels: |
org.opencontainers.image.title=Pulp Manager
org.opencontainers.image.description=FastAPI-based orchestration and management for multiple Pulp 3 servers
org.opencontainers.image.vendor=Pulp
org.opencontainers.image.source=${{ github.server_url }}/${{ github.repository }}
org.opencontainers.image.revision=${{ github.sha }}
cache-from: type=gha
cache-to: type=gha,mode=max
platforms: linux/amd64

- name: Push to registries
if: github.event_name != 'pull_request'
run: |
for registry in ${{ steps.registries.outputs.registries }}; do
echo "Pushing to ${registry}..."
for tag in ${{ steps.tags.outputs.tags }}; do
echo " Tagging and pushing ${registry}/pulp/${{ env.IMAGE_NAME }}:${tag}"
docker tag pulp/${{ env.IMAGE_NAME }}:ci ${registry}/pulp/${{ env.IMAGE_NAME }}:${tag}
docker push ${registry}/pulp/${{ env.IMAGE_NAME }}:${tag}
done
done

- name: Generate artifact attestation
if: github.event_name != 'pull_request'
uses: actions/attest-build-provenance@v1
with:
subject-name: ghcr.io/pulp/${{ env.IMAGE_NAME }}
subject-digest: ${{ hashFiles('Dockerfile') }}
push-to-registry: true
100 changes: 100 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
name: Release

on:
push:
tags:
- 'v*.*.*'
- '[0-9]+.[0-9]+.[0-9]+'
workflow_dispatch:
inputs:
tag:
description: 'Tag to release'
required: true
type: string

jobs:
build-docker:
name: Build Docker Image
uses: ./.github/workflows/docker-build.yml
permissions:
contents: read
packages: write
id-token: write
secrets: inherit

create-release:
name: Create GitHub Release
runs-on: ubuntu-latest
needs: build-docker
permissions:
contents: write

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0

- name: Get version from tag
id: get_version
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
VERSION="${{ inputs.tag }}"
else
VERSION=${GITHUB_REF#refs/tags/}
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Version: $VERSION"

- name: Generate changelog
id: changelog
run: |
# Get the previous tag
PREV_TAG=$(git tag --sort=-v:refname | grep -E '^v?[0-9]+\.[0-9]+\.[0-9]+$' | sed -n '2p')

if [ -z "$PREV_TAG" ]; then
echo "No previous tag found, using all commits"
PREV_TAG=$(git rev-list --max-parents=0 HEAD)
fi

echo "## What's Changed" > changelog.md
echo "" >> changelog.md

# Generate commit log
git log ${PREV_TAG}..HEAD --pretty=format:"* %s (%h)" --no-merges >> changelog.md

echo "" >> changelog.md
echo "" >> changelog.md
echo "**Full Changelog**: https://github.com/${{ github.repository }}/compare/${PREV_TAG}...${{ steps.get_version.outputs.version }}" >> changelog.md

cat changelog.md

- name: Create Release
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs');
const changelog = fs.readFileSync('changelog.md', 'utf8');

const release = await github.rest.repos.createRelease({
owner: context.repo.owner,
repo: context.repo.repo,
tag_name: '${{ steps.get_version.outputs.version }}',
name: 'Release ${{ steps.get_version.outputs.version }}',
body: changelog,
draft: false,
prerelease: false,
make_latest: 'true'
});

console.log(`Created release ${release.data.html_url}`);

// Add Docker image information to release
const dockerImage = `ghcr.io/${{ github.repository }}:${{ steps.get_version.outputs.version }}`;
await github.rest.repos.updateRelease({
owner: context.repo.owner,
repo: context.repo.repo,
release_id: release.data.id,
body: changelog + `\n\n## Docker Image\n\n\`\`\`bash\ndocker pull ${dockerImage}\n\`\`\``
});
20 changes: 18 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,27 @@ RUN apt-get update && apt-get install -y netcat-openbsd git make python3-dev lib
COPY --from=builder /opt/venv /opt/venv
# Copy requirements file
COPY --from=builder /pulp_manager/requirements.txt ./
# Copy the entire project
COPY . .

# Copy application code
COPY pulp_manager ./pulp_manager/
COPY pulp3_bindings ./pulp3_bindings/
COPY hashi_vault_client ./hashi_vault_client/

# Copy database migration files
COPY alembic ./alembic/
COPY alembic.ini ./

# Copy entrypoint script
COPY pulp-manager.sh ./

# Install default configs to /etc/pulp_manager/
RUN mkdir -p /etc/pulp_manager
COPY config-examples/config.ini /etc/pulp_manager/config.ini
COPY config-examples/pulp_config.yml /etc/pulp_manager/pulp_config.yml

# Ensure correct permissions
RUN chown -R pulp_manager:pulp_manager /pulp_manager \
&& chown -R pulp_manager:pulp_manager /etc/pulp_manager \
&& ln -s /pulp_manager/pulp-manager.sh /usr/local/bin/pulp-manager

USER 10001
70 changes: 70 additions & 0 deletions config-examples/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# Configuration Examples

This directory contains example configuration files that are copied into the Docker image at `/etc/pulp_manager/`.

## Files

- `config.ini` - Main application configuration
- `pulp_config.yml` - Pulp server and sync schedule configuration

## Usage

### Docker Image Default Configs

These configs are automatically copied to `/etc/pulp_manager/` during image build and serve as working defaults for testing and CI.

### Production Deployment

For production use, you have two options:

#### Option 1: Mount custom configs at the default location

```bash
docker run -v /path/to/your/config.ini:/etc/pulp_manager/config.ini \
-v /path/to/your/pulp_config.yml:/etc/pulp_manager/pulp_config.yml \
pulp/pulp-manager
```

#### Option 2: Mount configs anywhere and use environment variables

```bash
docker run -v /path/to/configs:/configs \
-e PULP_MANAGER_CONFIG_PATH=/configs/my-config.ini \
-e PULP_SYNC_CONFIG_PATH=/configs/my-pulp-config.yml \
pulp/pulp-manager
```

### Environment Variable Overrides

The default `config.ini` supports environment variable substitution for common settings:

- `DB_HOSTNAME` - Database host (default: localhost)
- `DB_PORT` - Database port (default: 3306)
- `DB_NAME` - Database name (default: pulp_manager)
- `DB_USER` - Database user (default: pulp-manager)
- `DB_PASSWORD` - Database password (default: pulp-manager)
- `REDIS_HOST` - Redis host (default: localhost)
- `REDIS_PORT` - Redis port (default: 6379)
- `REDIS_DB` - Redis database number (default: 0)
- `PULP_ADMIN_PASSWORD` - Pulp admin password (default: password)

Example using environment variables:

```bash
docker run -e DB_HOSTNAME=mysql.example.com \
-e DB_PASSWORD=secret \
-e REDIS_HOST=redis.example.com \
pulp/pulp-manager
```

## Configuration Locations

The application looks for configs in this order:

1. Environment variable `PULP_MANAGER_CONFIG_PATH` (if set)
2. Default location `/etc/pulp_manager/config.ini`

And for Pulp sync config:

1. Environment variable `PULP_SYNC_CONFIG_PATH` (if set)
2. Default location `/etc/pulp_manager/pulp_config.yml`
Loading