diff --git a/.github/workflows/build-container-reuse.yaml b/.github/workflows/build-container-reuse.yaml new file mode 100644 index 000000000..36902d169 --- /dev/null +++ b/.github/workflows/build-container-reuse.yaml @@ -0,0 +1,121 @@ +name: Build Container Image + +on: + workflow_call: + inputs: + container_name: + required: true + type: string + description: 'Name of the container to build' + dockerfile_path: + required: false + type: string + default: 'Dockerfile' + description: 'Path to Dockerfile relative to container directory' + build_args: + required: false + type: string + default: '' + description: 'Build arguments as key=value pairs' + target: + required: false + type: string + default: '' + description: 'Build target stage' + latest_name: + required: false + type: string + default: 'latest' + description: 'Tag name to use for latest (e.g., "latest" or "2025.2-ubuntu_jammy")' + +jobs: + build: + if: github.event_name != 'pull_request' || (github.event_name == 'pull_request' && github.event.action != 'closed') + runs-on: ubuntu-latest + + permissions: + contents: read + packages: write + + steps: + - name: setup docker buildx + uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3 + + - name: login to ghcr.io + uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: image metadata + id: meta + uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5 + with: + images: ghcr.io/${{ github.repository }}/${{ inputs.container_name }} + tags: | + type=raw,value=${{ inputs.latest_name }},enable={{is_default_branch}} + type=raw,value=${{ inputs.latest_name }},enable=${{ github.event_name == 'workflow_dispatch' }} + type=ref,event=tag + type=ref,event=pr + env: + # Create the annotations at the index as well since this + # defaults to manifest only and we have to manually merge + # the container is multi-arch because of provenance creating + # an 'unknown/unknown' arch with data. We've got no annotations + # that are arch specific so populate them at the index as well. + DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index + + - name: build and push container image + uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6 + with: + file: ${{ inputs.dockerfile_path }} + build-args: ${{ inputs.build_args }} + pull: true + push: true + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + annotations: ${{ steps.meta.outputs.annotations }} + target: ${{ inputs.target }} + + clean: + if: github.event_name == 'pull_request' && github.event.action == 'closed' + runs-on: ubuntu-latest + + permissions: + packages: write + + steps: + - name: clean up PR container + uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 + env: + CONTAINER_NAME: '${{ inputs.container_name }}' + with: + script: | + const container_name = `${context.repo.repo}/${process.env.CONTAINER_NAME}`; + const response = await github.rest.packages.getAllPackageVersionsForPackageOwnedByOrg({ + package_type: "container", + package_name: container_name, + org: context.repo.owner, + }); + + const target_tag = `pr-${context.payload.pull_request.number}`; + console.log(`Looking for tag ${target_tag} for container ${container_name}`); + + const versions = response.data || []; + const matchingVersion = versions.find(version => + version.metadata.container.tags.includes(target_tag) + ); + + if (matchingVersion) { + console.log(`Found tag to delete "${target_tag}":`, matchingVersion.html_url); + await github.rest.packages.deletePackageVersionForOrg({ + package_type: "container", + package_name: container_name, + org: context.repo.owner, + package_version_id: matchingVersion.id, + }); + console.log("Tag deleted"); + } else { + console.log(`No package version found with the tag "${target_tag}".`); + } diff --git a/.github/workflows/containers-openstack.yaml b/.github/workflows/containers-openstack.yaml new file mode 100644 index 000000000..c3117d3a9 --- /dev/null +++ b/.github/workflows/containers-openstack.yaml @@ -0,0 +1,46 @@ +name: Build OpenStack containers + +on: + push: + tags: + - v* + branches: + - main + paths: + - "containers/**" + - ".github/workflows/containers-openstack.yaml" + - ".github/workflows/build-container-reuse.yaml" + - "python/**" + pull_request: + types: [opened, synchronize, reopened, closed] + paths: + - "containers/**" + - ".github/workflows/containers-openstack.yaml" + - ".github/workflows/build-container-reuse.yaml" + - "python/**" + workflow_dispatch: + merge_group: + types: [checks_requested] + +jobs: + openstack: + strategy: + matrix: + project: + - cinder + - glance + - horizon + - ironic + - keystone + - neutron + - nova + - octavia + - openstack-client + - placement + uses: ./.github/workflows/build-container-reuse.yaml + secrets: inherit + with: + container_name: ${{ matrix.project }} + dockerfile_path: containers/${{ matrix.project }}/Dockerfile + build_args: OPENSTACK_VERSION=2025.2 + latest_name: 2025.2-ubuntu_jammy diff --git a/.github/workflows/containers.yaml b/.github/workflows/containers.yaml index b4744d0b4..3720a9468 100644 --- a/.github/workflows/containers.yaml +++ b/.github/workflows/containers.yaml @@ -1,4 +1,4 @@ -name: container builds +name: Build non-OpenStack containers on: push: @@ -10,215 +10,30 @@ on: - "ansible/**" - "containers/**" - ".github/workflows/containers.yaml" - - "python/**" + - ".github/workflows/build-container-reuse.yaml" pull_request: types: [opened, synchronize, reopened, closed] paths: - "ansible/**" - "containers/**" - ".github/workflows/containers.yaml" - - "python/**" + - ".github/workflows/build-container-reuse.yaml" workflow_dispatch: merge_group: types: [checks_requested] -env: - OPENSTACK_VERSION: 2025.2 - jobs: - openstack: - if: github.event_name != 'pull_request' || (github.event_name == 'pull_request' && github.event.action != 'closed') - runs-on: ubuntu-latest - - strategy: - matrix: - # if you add a container here, add it to the 'clean_containers' job below - project: [ironic, neutron, keystone, nova, openstack-client, cinder, octavia] - - steps: - - name: setup docker buildx - uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3 - - name: login to ghcr.io - uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: image metadata - id: meta - uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5 - with: - images: ghcr.io/${{ github.repository }}/${{ matrix.project }} - tags: | - type=raw,value=${{ env.OPENSTACK_VERSION }}-ubuntu_jammy,enable={{is_default_branch}} - type=raw,value=${{ env.OPENSTACK_VERSION }}-ubuntu_jammy,enable=${{ github.event_name == 'workflow_dispatch' }} - type=ref,event=tag - type=ref,event=pr - env: - # Create the annotations at the index as well since this - # defaults to manifest only and we have to manually merge - # the container is multi-arch because of provenance creating - # an 'unknown/unknown' arch with data. We've got no annotations - # that are arch specific so populate them at the index as well. - DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index - - - name: build and deploy openstack container image to registry - uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6 - with: - file: containers/${{ matrix.project }}/Dockerfile - build-args: OPENSTACK_VERSION=${{ env.OPENSTACK_VERSION }} - pull: true # ensure we always have an up to date source - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - annotations: ${{ steps.meta.outputs.annotations }} - - dnsmasq: - if: github.event_name != 'pull_request' || (github.event_name == 'pull_request' && github.event.action != 'closed') - runs-on: ubuntu-latest - - steps: - - name: setup docker buildx - uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3 - - name: login to ghcr.io - uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: image metadata - id: meta - uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5 - with: - images: ghcr.io/${{ github.repository }}/dnsmasq - tags: | - type=raw,value=latest,enable={{is_default_branch}} - type=raw,value=latest,enable=${{ github.event_name == 'workflow_dispatch' }} - type=ref,event=tag - type=ref,event=pr - env: - # Create the annotations at the index as well since this - # defaults to manifest only and we have to manually merge - # the container is multi-arch because of provenance creating - # an 'unknown/unknown' arch with data. We've got no annotations - # that are arch specific so populate them at the index as well. - DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index - - - name: build and deploy dnsmasq container to registry - uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6 - with: - context: "{{defaultContext}}:containers/dnsmasq" - file: Dockerfile - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - - workflows: - if: github.event_name != 'pull_request' || (github.event_name == 'pull_request' && github.event.action != 'closed') - runs-on: ubuntu-latest - - strategy: - matrix: - # if you add a container here, add it to the 'clean_containers' job below - container: - - name: ironic-nautobot-client - - name: ansible - - name: understack-tests - - steps: - - name: setup docker buildx - uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3 - - name: login to ghcr.io - uses: docker/login-action@5e57cd118135c172c3672efd75eb46360885c0ef # v3 - with: - registry: ghcr.io - username: ${{ github.actor }} - password: ${{ secrets.GITHUB_TOKEN }} - - - name: image metadata - id: meta - uses: docker/metadata-action@c299e40c65443455700f0fdfc63efafe5b349051 # v5 - with: - images: ghcr.io/${{ github.repository }}/${{ matrix.container.name }} - tags: | - type=raw,value=latest,enable={{is_default_branch}} - type=raw,value=latest,enable=${{ github.event_name == 'workflow_dispatch' }} - type=ref,event=tag - type=ref,event=pr - env: - # Create the annotations at the index as well since this - # defaults to manifest only and we have to manually merge - # the container is multi-arch because of provenance creating - # an 'unknown/unknown' arch with data. We've got no annotations - # that are arch specific so populate them at the index as well. - DOCKER_METADATA_ANNOTATIONS_LEVELS: manifest,index - - - name: build and deploy container image to registry - uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6 - with: - file: containers/${{ matrix.container.name }}/Dockerfile - pull: true # ensure we always have an up to date source - push: true - tags: ${{ steps.meta.outputs.tags }} - labels: ${{ steps.meta.outputs.labels }} - annotations: ${{ steps.meta.outputs.annotations }} - # prod is the target that has the code installed - target: prod - - clean_containers: - if: github.event_name == 'pull_request' && github.event.action == 'closed' - runs-on: ubuntu-latest - - permissions: - packages: write - + build: strategy: matrix: container: - - ironic - - neutron - - keystone - - nova - - octavia - - openstack-client + - ansible - dnsmasq - ironic-nautobot-client - - ansible - understack-tests - - steps: - - name: clean up PR container - uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8 - env: - CONTAINER_NAME: '${{ matrix.container }}' - with: - script: | - const container_name = `${context.repo.repo}/${process.env.CONTAINER_NAME}`; - const response = await github.rest.packages.getAllPackageVersionsForPackageOwnedByOrg({ - package_type: "container", - package_name: container_name, - org: context.repo.owner, - }); - - const target_tag = `pr-${context.payload.pull_request.number}`; - console.log(`Looking for tag ${target_tag} for container ${container_name}`); - - const versions = response.data || []; - const matchingVersion = versions.find(version => - version.metadata.container.tags.includes(target_tag) - ); - - if (matchingVersion) { - console.log(`Found tag to delete "${target_tag}":`, matchingVersion.html_url); - await github.rest.packages.deletePackageVersionForOrg({ - package_type: "container", - package_name: container_name, - org: context.repo.owner, - package_version_id: matchingVersion.id, - }); - console.log("Tag deleted"); - } else { - console.log(`No package version found with the tag "${target_tag}".`); - } + uses: ./.github/workflows/build-container-reuse.yaml + secrets: inherit + with: + container_name: ${{ matrix.container }} + dockerfile_path: containers/${{ matrix.container }}/Dockerfile + target: prod diff --git a/components/images-openstack.yaml b/components/images-openstack.yaml index 441414df5..e73a37a71 100644 --- a/components/images-openstack.yaml +++ b/components/images-openstack.yaml @@ -64,8 +64,8 @@ images: nova_service_cleaner: "docker.io/openstackhelm/ceph-config-helper:latest-ubuntu_jammy" # placement - placement: "quay.io/airshipit/placement:2025.2-ubuntu_jammy" - placement_db_sync: "quay.io/airshipit/placement:2025.2-ubuntu_jammy" + placement: "ghcr.io/rackerlabs/understack/placement:2025.2-ubuntu_jammy" + placement_db_sync: "ghcr.io/rackerlabs/understack/placement:2025.2-ubuntu_jammy" # openvswitch openvswitch_db_server: "docker.io/openstackhelm/openvswitch:ubuntu_jammy-dpdk-20250127" @@ -78,13 +78,13 @@ images: ovn_controller: "docker.io/openstackhelm/ovn:ubuntu_jammy-20250111" # horizon - horizon: "quay.io/airshipit/horizon:2025.2-ubuntu_jammy" - horizon_db_sync: "quay.io/airshipit/horizon:2025.2-ubuntu_jammy" + horizon: "ghcr.io/rackerlabs/understack/horizon:2025.2-ubuntu_jammy" + horizon_db_sync: "ghcr.io/rackerlabs/understack/horizon:2025.2-ubuntu_jammy" # glance - glance_api: "quay.io/airshipit/glance:2025.2-ubuntu_jammy" - glance_db_sync: "quay.io/airshipit/glance:2025.2-ubuntu_jammy" - glance_metadefs_load: "quay.io/airshipit/glance:2025.2-ubuntu_jammy" + glance_api: "ghcr.io/rackerlabs/understack/glance:2025.2-ubuntu_jammy" + glance_db_sync: "ghcr.io/rackerlabs/understack/glance:2025.2-ubuntu_jammy" + glance_metadefs_load: "ghcr.io/rackerlabs/understack/glance:2025.2-ubuntu_jammy" glance_storage_init: "docker.io/openstackhelm/ceph-config-helper:latest-ubuntu_jammy" # skyline diff --git a/containers/dnsmasq/Dockerfile b/containers/dnsmasq/Dockerfile index ca0242e51..dbfa5ad34 100644 --- a/containers/dnsmasq/Dockerfile +++ b/containers/dnsmasq/Dockerfile @@ -22,7 +22,7 @@ WORKDIR /src RUN apt-get -y build-dep dnsmasq RUN apt-get -y source dnsmasq # copy in our patch -COPY dnsmasq/dhcp-allowed-srvids.patch /src/ +COPY containers/dnsmasq/dnsmasq/dhcp-allowed-srvids.patch /src/ # setup the patch to be built into the quilt file # set our version number to a local override suffixed by '.uc1' RUN cd /src/dnsmasq-* && \ @@ -51,10 +51,10 @@ RUN apt-get update && \ RUN mkdir -p /var/lib/openstack-helm/tftpboot && \ cp /usr/lib/ipxe/snponly.efi /var/lib/openstack-helm/tftpboot/ -COPY common/helpers.sh /helpers.sh -COPY dnsmasq/entry-point.sh /entry-point.sh +COPY containers/dnsmasq/common/helpers.sh /helpers.sh +COPY containers/dnsmasq/dnsmasq/entry-point.sh /entry-point.sh RUN chmod +x /entry-point.sh -COPY dnsmasq/dnsmasq.conf.j2 /etc/dnsmasq.conf.j2 +COPY containers/dnsmasq/dnsmasq/dnsmasq.conf.j2 /etc/dnsmasq.conf.j2 # let our entry point write out the script RUN ln -sf /etc/dnsmasq.d/dnsmasq.conf /etc/dnsmasq.conf diff --git a/containers/glance/Dockerfile b/containers/glance/Dockerfile new file mode 100644 index 000000000..eb503db08 --- /dev/null +++ b/containers/glance/Dockerfile @@ -0,0 +1,4 @@ +# syntax=docker/dockerfile:1 + +ARG OPENSTACK_VERSION="required_argument" +FROM quay.io/airshipit/glance:${OPENSTACK_VERSION}-ubuntu_jammy AS final diff --git a/containers/horizon/Dockerfile b/containers/horizon/Dockerfile new file mode 100644 index 000000000..3ab31d5e4 --- /dev/null +++ b/containers/horizon/Dockerfile @@ -0,0 +1,4 @@ +# syntax=docker/dockerfile:1 + +ARG OPENSTACK_VERSION="required_argument" +FROM quay.io/airshipit/horizon:${OPENSTACK_VERSION}-ubuntu_jammy AS final diff --git a/containers/placement/Dockerfile b/containers/placement/Dockerfile new file mode 100644 index 000000000..8039461ff --- /dev/null +++ b/containers/placement/Dockerfile @@ -0,0 +1,4 @@ +# syntax=docker/dockerfile:1 + +ARG OPENSTACK_VERSION="required_argument" +FROM quay.io/airshipit/placement:${OPENSTACK_VERSION}-ubuntu_jammy AS final