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
8 changes: 8 additions & 0 deletions deploy-edpm.yml
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,14 @@
environment:
PATH: "{{ cifmw_path }}"

- name: Resolve S2I content set
when: cifmw_s2i_content_set | default(false) | bool
ansible.builtin.import_role:
name: s2i_content_set
tags:
- edpm
- s2i-content-set

- name: Deploy EDPM
ansible.builtin.import_role:
name: cifmw_setup
Expand Down
63 changes: 63 additions & 0 deletions roles/s2i_content_set/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# s2i_content_set

Resolve a promoted S2I service content set once and export
`cifmw_set_containers_images` for `edpm_prepare` /
`cifmw.general.set_containers` to patch `OpenStackVersion`.

The set is identified by `s2iCommit` plus image **digests**, not the floating
`:master-latest` tag. Operator manager images stay on
`openstack-k8s-operators-content-provider`; this role only injects service
containers from `quay.io/openstack-s2i-containers`.

## Privilege escalation

None.

## Parameters

* `cifmw_s2i_content_set_commit`: (String) Explicit s2i-openstack-containers
git SHA. Wins over the pin file and over inspecting `:master-latest`.
Default: empty.
* `cifmw_s2i_content_set_pin_file`: (String) Optional YAML with `s2iCommit`.
Default: `openstack-operator/config/s2i-content-set.yaml` in the Zuul
checkout.
* `cifmw_s2i_content_set_catalog_file`: (String) Optional digest catalog
(`content-set.yaml`). Used when it exists and its `s2iCommit` matches the
resolved commit (or no commit is set yet). Default: `containers/content-set.yaml`
in the s2i checkout.
* `cifmw_s2i_content_set_mappings_file`: (String) `image-mappings.yaml` used
when inspecting Quay. Default: s2i `containers/image-mappings.yaml`.
* `cifmw_s2i_content_set_src`: Checkout of `s2i-openstack-containers`. Default:
`{{ ansible_user_dir }}/src/github.com/openstack-k8s-operators/s2i-openstack-containers`.
* `cifmw_s2i_content_set_registry` / `cifmw_s2i_content_set_namespace` /
`cifmw_s2i_content_set_stream` / `cifmw_s2i_content_set_prefix`: Promoted
registry and image name prefix. Defaults: `quay.io` /
`openstack-s2i-containers` / `master` / `openstack`.
* `cifmw_s2i_content_set_probe_image`: Image inspected at `:master-latest` to
read `org.opencontainers.image.revision` when no pin is set. Default:
`openstack-keystone`.
* `cifmw_s2i_content_set_skip_images`: (List) OpenStackVersion field names to
omit so `preserve_unlisted` keeps payload defaults.
* `cifmw_s2i_content_set_ignore_missing`: (Boolean) Continue when an inspect
fails. Default: `false`.
* `cifmw_s2i_content_set_dest_path`: Artifact YAML. Default:
`{{ cifmw_basedir }}/artifacts/s2i-content-set.yaml`.
* `cifmw_s2i_content_set_backend_images`: Cinder volume / Manila share map
entries (not expressible as scalars in `image-mappings.yaml`).

Existing `cifmw_set_containers_images` entries win (speculative overlay).
When the role runs, `cifmw_set_containers_preserve_unlisted` defaults to
`true` if unset.

## Examples

Enable from a Zuul job (and from `deploy-edpm.yml`):

```yaml
vars:
cifmw_s2i_content_set: true
cifmw_s2i_content_set_skip_images:
- neutronAPIImage
- edpmNeutronMetadataAgentImage
- mariadbImage
```
102 changes: 102 additions & 0 deletions roles/s2i_content_set/defaults/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
---
# Copyright Red Hat, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.


# All variables intended for modification should be placed in this file.
# All variables within this role should have a prefix of "cifmw_s2i_content_set"

# Explicit s2i-openstack-containers git SHA. Wins over the pin file and
# over inspecting :master-latest.
cifmw_s2i_content_set_commit: ""
Comment thread
rebtoor marked this conversation as resolved.

cifmw_s2i_content_set_registry: quay.io
cifmw_s2i_content_set_namespace: openstack-s2i-containers
cifmw_s2i_content_set_stream: master
cifmw_s2i_content_set_prefix: openstack
Comment thread
rebtoor marked this conversation as resolved.

# Well-known image used to read org.opencontainers.image.revision when no
# pin or explicit commit is provided.
cifmw_s2i_content_set_probe_image: openstack-keystone

cifmw_s2i_content_set_src: >-
{{
[
ansible_user_dir,
'src',
'github.com',
'openstack-k8s-operators',
's2i-openstack-containers'
] | path_join
}}

cifmw_s2i_content_set_mappings_file: >-
{{
[
cifmw_s2i_content_set_src,
'containers',
'image-mappings.yaml'
] | path_join
}}

cifmw_s2i_content_set_catalog_file: >-
{{
[
cifmw_s2i_content_set_src,
'containers',
'content-set.yaml'
] | path_join
}}

cifmw_s2i_content_set_pin_file: >-
{{
[
ansible_user_dir,
'src',
'github.com',
'openstack-k8s-operators',
'openstack-operator',
'config',
's2i-content-set.yaml'
] | path_join
}}

cifmw_s2i_content_set_dest_path: >-
{{
[
cifmw_basedir | default([ansible_user_dir, 'ci-framework-data'] | path_join),
'artifacts',
's2i-content-set.yaml'
] | path_join
}}

# OpenStackVersion field names to omit so preserve_unlisted keeps payload
# defaults. Empty here; jobs that must match s2i-openstack-deploy-validation
# pass the unready keys.
cifmw_s2i_content_set_skip_images: []

# Continue when an individual image inspect fails (partial set).
cifmw_s2i_content_set_ignore_missing: false

# Backend maps are not expressible in image-mappings.yaml scalars.
cifmw_s2i_content_set_backend_images:
Comment thread
evallesp marked this conversation as resolved.
- name: cinderVolumeImages
image: openstack-cinder-volume
backends:
- volume1
- name: manilaShareImages
image: openstack-manila-share
backends:
- share1
28 changes: 28 additions & 0 deletions roles/s2i_content_set/meta/main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
# Copyright Red Hat, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.


galaxy_info:
author: CI Framework
description: CI Framework Role -- s2i_content_set
company: Red Hat
license: Apache-2.0
min_ansible_version: "2.14"
namespace: cifmw
galaxy_tags:
- cifmw

dependencies: []
92 changes: 92 additions & 0 deletions roles/s2i_content_set/molecule/default/converge.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
---
# Copyright Red Hat, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.


- name: Resolve from a local catalog
hosts: all
vars:
cifmw_basedir: /tmp/ci-framework-data
cifmw_s2i_content_set_catalog_file: "{{ playbook_dir }}/files/content-set.yaml"
cifmw_s2i_content_set_pin_file: /tmp/cifmw-s2i-missing-pin.yaml
cifmw_s2i_content_set_skip_images:
- neutronAPIImage
cifmw_set_containers_images:
- name: glanceAPIImage
full_registry: ci.example/overlay-glance:ci
roles:
- role: s2i_content_set
tasks:
- name: Catalog commit is recorded
ansible.builtin.assert:
that:
- cifmw_s2i_content_set_commit == 'cafebabedeadbeefcafebabedeadbeefcafebabe'
- cifmw_s2i_content_set_from_catalog | bool
- cifmw_set_containers_preserve_unlisted | bool

- name: Overlay wins and skip list is honored
ansible.builtin.assert:
that:
- cifmw_set_containers_images | selectattr('name', 'equalto', 'glanceAPIImage') | map(attribute='full_registry') | first == 'ci.example/overlay-glance:ci'
- cifmw_set_containers_images | selectattr('name', 'equalto', 'keystoneAPIImage') | list | length == 1
- cifmw_set_containers_images | selectattr('name', 'equalto', 'neutronAPIImage') | list | length == 0
- cifmw_set_containers_images | selectattr('name', 'equalto', 'cinderVolumeImages') | list | length == 1

- name: Artifact was written
ansible.builtin.stat:
path: "{{ cifmw_s2i_content_set_dest_path }}"
register: cifmw_s2i_content_set_artifact_stat

- name: Artifact exists
ansible.builtin.assert:
that:
- cifmw_s2i_content_set_artifact_stat.stat.exists

- name: Cleanup catalog artifact
ansible.builtin.include_role:
name: s2i_content_set
tasks_from: cleanup.yml

- name: Resolve by inspecting Quay
hosts: all
environment:
PATH: "{{ playbook_dir }}/files:{{ ansible_env.PATH }}"
vars:
cifmw_basedir: /tmp/ci-framework-data
cifmw_s2i_content_set_catalog_file: /tmp/cifmw-s2i-missing-catalog.yaml
cifmw_s2i_content_set_pin_file: /tmp/cifmw-s2i-missing-pin.yaml
cifmw_s2i_content_set_mappings_file: "{{ playbook_dir }}/files/image-mappings.yaml"
cifmw_s2i_content_set_backend_images: []
cifmw_s2i_content_set_skip_images: []
cifmw_set_containers_images: []
tasks:
- name: Run role against fake skopeo
ansible.builtin.include_role:
name: s2i_content_set

- name: Inspect path records revision and digest pins
ansible.builtin.assert:
that:
- not (cifmw_s2i_content_set_from_catalog | bool)
- cifmw_s2i_content_set_commit == 'cafebabedeadbeefcafebabedeadbeefcafebabe'
- cifmw_set_containers_images | selectattr('name', 'equalto', 'keystoneAPIImage') | list | length == 1
- cifmw_set_containers_images | selectattr('name', 'equalto', 'glanceAPIImage') | list | length == 1
- cifmw_set_containers_images | selectattr('name', 'equalto', 'neutronAPIImage') | list | length == 1
- "'@sha256:' in (cifmw_set_containers_images | selectattr('name', 'equalto', 'keystoneAPIImage') | map(attribute='full_registry') | first)"

- name: Cleanup inspect artifact
ansible.builtin.include_role:
name: s2i_content_set
tasks_from: cleanup.yml
19 changes: 19 additions & 0 deletions roles/s2i_content_set/molecule/default/files/content-set.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
schemaVersion: 1
stream: master
s2iCommit: "cafebabedeadbeefcafebabedeadbeefcafebabe"
tag: master-cafebabedeadbeefcafebabedeadbeefcafebabe
registry: quay.io
namespace: openstack-s2i-containers
created: "2026-09-08T00:00:00Z"
images:
keystone/keystone: quay.io/openstack-s2i-containers/openstack-keystone@sha256:1111111111111111111111111111111111111111111111111111111111111111
glance/glance-api: quay.io/openstack-s2i-containers/openstack-glance-api@sha256:2222222222222222222222222222222222222222222222222222222222222222
neutron/neutron-server: quay.io/openstack-s2i-containers/openstack-neutron-server@sha256:3333333333333333333333333333333333333333333333333333333333333333
openstackVersion:
customContainerImages:
keystoneAPIImage: quay.io/openstack-s2i-containers/openstack-keystone@sha256:1111111111111111111111111111111111111111111111111111111111111111
glanceAPIImage: quay.io/openstack-s2i-containers/openstack-glance-api@sha256:2222222222222222222222222222222222222222222222222222222222222222
neutronAPIImage: quay.io/openstack-s2i-containers/openstack-neutron-server@sha256:3333333333333333333333333333333333333333333333333333333333333333
cinderVolumeImages:
volume1: quay.io/openstack-s2i-containers/openstack-cinder-volume@sha256:4444444444444444444444444444444444444444444444444444444444444444
Comment thread
evallesp marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
openstack_version:
custom_container_images:
keystone/keystone:
- keystoneAPIImage
glance/glance-api:
- glanceAPIImage
neutron/neutron-server:
- neutronAPIImage
22 changes: 22 additions & 0 deletions roles/s2i_content_set/molecule/default/files/skopeo
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#!/usr/bin/env bash
Comment thread
evallesp marked this conversation as resolved.
# Fake skopeo inspect for molecule. Prints a Digest and optional revision label.
set -euo pipefail

ref="${!#}"
ref="${ref#docker://}"
image="${ref##*/}"
name="${image%%:*}"

digest="sha256:$(printf '%s' "${name}" | sha256sum | awk '{print $1}')"
revision="cafebabedeadbeefcafebabedeadbeefcafebabe"

python3 - <<PY
import json
print(json.dumps({
"Digest": "${digest}",
"Labels": {
"org.opencontainers.image.revision": "${revision}",
"s2i.openstack.org/stream": "master",
},
}))
PY
9 changes: 9 additions & 0 deletions roles/s2i_content_set/molecule/default/molecule.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
# Mainly used to override the defaults set in .config/molecule/
log: true

provisioner:
name: ansible
log: true
env:
ANSIBLE_STDOUT_CALLBACK: yaml
26 changes: 26 additions & 0 deletions roles/s2i_content_set/molecule/default/prepare.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
---
# Copyright Red Hat, Inc.
# All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.


- name: Prepare
hosts: all
roles:
- role: test_deps
tasks:
- name: Make fake skopeo executable
ansible.builtin.file:
path: "{{ playbook_dir }}/files/skopeo"
mode: "0755"
Loading
Loading