Skip to content

Commit 0e281b2

Browse files
wangxinbotwangxinCopilot
authored
ansible: support pulling ceos_image from docker registry and enhance image discovery (#23254)
Cherry-pick of #22794 and #23299 to 202411 branch. Original PRs: - #22794 - #23299 Includes enhanced ceos image discovery: when ceos_registry is not defined, a consolidated 'docker images | grep' discovers pre-pulled registry images locally, avoiding unnecessary local image builds that trigger S360 alerts. Signed-off-by: Xin Wang <xiwang5@microsoft.com> Co-authored-by: Xin Wang <xiwang5@microsoft.com> Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 2ba07d0 commit 0e281b2

4 files changed

Lines changed: 225 additions & 87 deletions

File tree

ansible/group_vars/vm_host/ceos.yml

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,16 @@ ceos_image_url:
99
- "http://example1.com/cEOS64-lab-4.29.3M.tar"
1010
- "http://example2.com/cEOS64-lab-4.29.3M.tar"
1111
skip_ceos_image_downloading: false
12+
13+
# Registry settings for pulling ceos docker image. If ceos_registry is defined, the code will first
14+
# check whether ceos_image is already cached locally from the registry, and if not, try to pull it
15+
# from the registry before falling back to the download-and-build approach.
16+
#
17+
# Uncomment ceos_registry and set it to your registry URL to enable registry pulling.
18+
# If the registry requires authentication, also uncomment and set ceos_registry_username and
19+
# ceos_registry_password. If the registry is publicly accessible without authentication, leave the
20+
# username and password commented out.
21+
#
22+
# ceos_registry: "your-registry.example.com"
23+
# ceos_registry_username: "your-username"
24+
# ceos_registry_password: "your-password"

ansible/roles/eos/tasks/ceos.yml

Lines changed: 46 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,56 @@
1313
- include_tasks: ceos_config.yml
1414
- include_vars: group_vars/vm_host/ceos.yml
1515

16+
- name: Check if ceos_image from registry is available locally
17+
docker_image_info:
18+
name:
19+
- "{{ ceos_registry }}/{{ ceos_image }}"
20+
become: yes
21+
register: ceos_registry_image_info
22+
delegate_to: "{{ VM_host[0] }}"
23+
when: ceos_registry is defined
24+
25+
- name: Discover ceos_image locally (plain or from any registry)
26+
# Searches all local docker images for one matching ceos_image. This single grep covers:
27+
# - Plain local image: ceosimage:4.32.5M-1
28+
# - Image pre-pulled from any registry: soniccr1.azurecr.io/ceosimage:4.32.5M-1
29+
# When ceos_registry is defined, this task is skipped — the explicit registry check above is used.
30+
become: yes
31+
shell: >-
32+
docker images --format
33+
'{% raw %}{{.Repository}}:{{.Tag}}{% endraw %}'
34+
| grep -m1 '{{ ceos_image }}$'
35+
register: ceos_image_discovery_result
36+
delegate_to: "{{ VM_host[0] }}"
37+
ignore_errors: yes
38+
changed_when: no
39+
when: ceos_registry is not defined
40+
41+
- name: Set ceos_effective_image
42+
set_fact:
43+
# Priority order for determining the effective ceos image:
44+
# 1. When ceos_registry is defined and the registry image is available locally, use the
45+
# registry-prefixed image name. This ensures containers always reference the registry
46+
# image, satisfying security requirements (e.g. Microsoft ACR/MCR policy).
47+
# 2. When ceos_registry is not defined, use whatever local image matches ceos_image
48+
# (plain or from any registry). Preferring a pre-pulled registry image avoids building
49+
# a local image and triggering S360 alerts.
50+
# 3. Fall back to the plain ceos_image name when no image is discovered.
51+
ceos_effective_image: >-
52+
{{ ceos_registry + '/' + ceos_image
53+
if (ceos_registry is defined and ceos_registry_image_info.images | length > 0)
54+
else (ceos_image_discovery_result.stdout | trim)
55+
if (ceos_registry is not defined and
56+
ceos_image_discovery_result is not skipped and
57+
ceos_image_discovery_result.rc == 0 and
58+
ceos_image_discovery_result.stdout | trim != '')
59+
else ceos_image }}
60+
1661
- name: Create cEOS container
1762
become: yes
1863
docker_container:
1964
name: ceos_{{ vm_set_name }}_{{ inventory_hostname }}
20-
image: "{{ ceos_image }}"
65+
image: "{{ ceos_effective_image }}"
2166
command: /sbin/init systemd.setenv=INTFTYPE=eth systemd.setenv=ETBA=1 systemd.setenv=SKIP_ZEROTOUCH_BARRIER_IN_SYSDBINIT=1 systemd.setenv=CEOS=1 systemd.setenv=EOS_PLATFORM=ceoslab systemd.setenv=container=docker systemd.setenv=MGMT_INTF=eth0
2267
pull: no
2368
state: started

ansible/roles/eos/tasks/ceos_ensure_reachable.yml

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
- name: Set ceos_effective_image fallback if not already defined
2+
# ceos_effective_image is normally set by the caller (ceos.yml in the eos role) before this
3+
# file is included. This task provides a safe fallback in case this file is invoked
4+
# independently, so it does not fail with an undefined variable error.
5+
set_fact:
6+
ceos_effective_image: "{{ ceos_image }}"
7+
when: ceos_effective_image is not defined
8+
19
- block:
210
- name: set time out threshold
311
set_fact:
@@ -18,7 +26,7 @@
1826
become: yes
1927
docker_container:
2028
name: ceos_{{ vm_set_name }}_{{ inventory_hostname }}
21-
image: "{{ ceos_image }}"
29+
image: "{{ ceos_effective_image }}"
2230
command: /sbin/init systemd.setenv=INTFTYPE=eth systemd.setenv=ETBA=1 systemd.setenv=SKIP_ZEROTOUCH_BARRIER_IN_SYSDBINIT=1 systemd.setenv=CEOS=1 systemd.setenv=EOS_PLATFORM=ceoslab systemd.setenv=container=docker systemd.setenv=MGMT_INTF=eth0
2331
pull: no
2432
state: started

ansible/roles/vm_set/tasks/add_ceos_list.yml

Lines changed: 157 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -1,100 +1,172 @@
1-
- name: Check if cEOS docker image exists or not
1+
- name: Check if cEOS docker image from registry is already cached locally
22
docker_image_info:
33
name:
4-
- "{{ ceos_image }}"
4+
- "{{ ceos_registry }}/{{ ceos_image }}"
55
become: yes
6-
register: ceos_docker_image_stat
6+
register: ceos_registry_cached_image_stat
7+
when: ceos_registry is defined
8+
9+
- name: Discover cEOS docker image locally (plain or from any registry)
10+
# Searches all local docker images for one matching ceos_image. This single grep covers:
11+
# - Plain local image: ceosimage:4.32.5M-1
12+
# - Image pre-pulled from any registry: soniccr1.azurecr.io/ceosimage:4.32.5M-1
13+
# When ceos_registry is defined, this task is skipped — the explicit registry check above is used.
14+
become: yes
15+
shell: >-
16+
docker images --format
17+
'{% raw %}{{.Repository}}:{{.Tag}}{% endraw %}'
18+
| grep -m1 '{{ ceos_image }}$'
19+
register: ceos_image_discovery_result
20+
ignore_errors: yes
21+
changed_when: no
22+
when: ceos_registry is not defined
23+
24+
- name: Set fact for whether ceos_image is already available
25+
set_fact:
26+
# When registry is defined, only the registry-prefixed image is considered "found" - using
27+
# a local image in that case would bypass the registry requirement (e.g. Microsoft ACR/MCR).
28+
# When registry is not defined, the image is "found" if any local image (plain or from any
29+
# registry) matches ceos_image. This covers images pre-pulled during infrastructure
30+
# provisioning (e.g. VMSS setup) without exposing the registry URL in public code.
31+
# The | bool filter ensures this is stored as a Python boolean, not the string "True"/"False",
32+
# so that "when: not ceos_image_found" evaluates correctly.
33+
ceos_image_found: "{{ ((ceos_registry is not defined and
34+
ceos_image_discovery_result is not skipped and
35+
ceos_image_discovery_result.rc == 0 and
36+
ceos_image_discovery_result.stdout | trim != '') or
37+
(ceos_registry is defined and
38+
ceos_registry_cached_image_stat.images | length > 0)) | bool }}"
739

840
- name: Prepare ceos_image if it does not exist
941
block:
1042

11-
- name: Check if ceos_image_orig exists or not
12-
docker_image_info:
13-
name:
14-
- "{{ ceos_image_orig }}"
15-
become: yes
16-
register: ceos_image_orig_stat
43+
- name: Try to get ceos_image from registry
44+
block:
1745

18-
- name: Prepare ceos_image_orig if it does not exist
46+
- name: Login to ceos registry if credentials are defined
47+
become: yes
48+
docker_login:
49+
registry_url: "{{ ceos_registry }}"
50+
username: "{{ ceos_registry_username }}"
51+
password: "{{ ceos_registry_password }}"
52+
when: >
53+
ceos_registry_username is defined and
54+
ceos_registry_password is defined
55+
56+
- name: Pull ceos_image from registry
57+
become: yes
58+
docker_image:
59+
name: "{{ ceos_registry }}/{{ ceos_image }}"
60+
source: pull
61+
register: ceos_registry_pull_result
62+
ignore_errors: yes
63+
64+
- name: Warn that registry pull failed, will fall back to download and build
65+
debug:
66+
msg: >-
67+
WARNING: Failed to pull {{ ceos_registry }}/{{ ceos_image }} from registry.
68+
Will fall back to the download-and-build path. Check ceos_registry and credentials.
69+
when: ceos_registry_pull_result is failed
70+
71+
when: ceos_registry is defined
72+
73+
- name: Prepare ceos_image via download and build if registry pull failed or registry not defined
1974
block:
20-
- name: Check if local ceos image file exists or not
21-
stat:
22-
path: "{{ root_path }}/images/{{ ceos_image_filename }}"
23-
register: ceos_image_file_stat
2475

25-
- name: Download cEOS image file if no local ceos image file exists
76+
- name: Check if ceos_image_orig exists or not
77+
docker_image_info:
78+
name:
79+
- "{{ ceos_image_orig }}"
80+
become: yes
81+
register: ceos_image_orig_stat
82+
83+
- name: Prepare ceos_image_orig if it does not exist
2684
block:
27-
- name: Fail if skip_ceos_image_downloading is true
28-
fail:
29-
msg: [
30-
"Failed, no ceos docker image, no ceos image file and skip_ceos_image_downloading is true",
31-
"Please manually put cEOS image to {{ root_path }}/images/{{ ceos_image_filename }}"
32-
]
33-
when: skip_ceos_image_downloading == true
34-
35-
- name: Init ceos_image_urls when ceos_image_url value type is string
36-
set_fact:
37-
ceos_image_urls:
38-
- "{{ ceos_image_url }}"
39-
when: ceos_image_url | type_debug == 'string'
40-
41-
- name: Init ceos_image_urls when ceos_image_url value type is list
42-
set_fact:
43-
ceos_image_urls: "{{ ceos_image_url }}"
44-
when: ceos_image_url | type_debug == 'list'
45-
46-
- name: Init working_image_urls list
47-
set_fact:
48-
working_image_urls: []
49-
50-
- name: Loop ceos_image_urls to find out working URLs
51-
include_tasks: probe_image_url.yml
52-
loop: "{{ ceos_image_urls }}"
53-
54-
- name: Fail if no working ceos image download url is found
55-
fail:
56-
msg: [
57-
"Failed, no working ceos image download URL is found. There are 2 options to fix it:",
58-
" 1. Fix ceos_image_url defined in ansible/group_vars/vm_host/ceos.yml",
59-
" 2. Manually put cEOS image to {{ root_path }}/images/{{ ceos_image_filename }}",
60-
]
61-
when: working_image_urls | length == 0
62-
63-
- name: Download cEOS image file from working ceos_image_urls using the first working URL
64-
get_url:
65-
url: "{{ working_image_urls[0] }}"
66-
dest: "{{ root_path }}/images/{{ ceos_image_filename }}"
67-
environment: "{{ proxy_env | default({}) }}"
68-
register: ceos_image_download_result
69-
70-
when: ceos_image_file_stat.stat.exists == false
71-
72-
- name: Import ceos_image_orig docker image
85+
- name: Check if local ceos image file exists or not
86+
stat:
87+
path: "{{ root_path }}/images/{{ ceos_image_filename }}"
88+
register: ceos_image_file_stat
89+
90+
- name: Download cEOS image file if no local ceos image file exists
91+
block:
92+
- name: Fail if skip_ceos_image_downloading is true
93+
fail:
94+
msg: [
95+
"Failed, no ceos docker image, no ceos image file and skip_ceos_image_downloading is true",
96+
"Please manually put cEOS image to {{ root_path }}/images/{{ ceos_image_filename }}"
97+
]
98+
when: skip_ceos_image_downloading == true
99+
100+
- name: Init ceos_image_urls when ceos_image_url value type is string
101+
set_fact:
102+
ceos_image_urls:
103+
- "{{ ceos_image_url }}"
104+
when: ceos_image_url | type_debug == 'string'
105+
106+
- name: Init ceos_image_urls when ceos_image_url value type is list
107+
set_fact:
108+
ceos_image_urls: "{{ ceos_image_url }}"
109+
when: ceos_image_url | type_debug == 'list'
110+
111+
- name: Init working_image_urls list
112+
set_fact:
113+
working_image_urls: []
114+
115+
- name: Loop ceos_image_urls to find out working URLs
116+
include_tasks: probe_image_url.yml
117+
loop: "{{ ceos_image_urls }}"
118+
119+
- name: Fail if no working ceos image download url is found
120+
fail:
121+
msg: [
122+
"Failed, no working ceos image download URL is found. There are 2 options to fix it:",
123+
" 1. Fix ceos_image_url defined in ansible/group_vars/vm_host/ceos.yml",
124+
" 2. Manually put cEOS image to {{ root_path }}/images/{{ ceos_image_filename }}",
125+
]
126+
when: working_image_urls | length == 0
127+
128+
- name: Ensure {{ root_path }}/images exists
129+
file: path={{ root_path }}/images state=directory
130+
131+
- name: Download cEOS image file from working ceos_image_urls using the first working URL
132+
get_url:
133+
url: "{{ working_image_urls[0] }}"
134+
dest: "{{ root_path }}/images/{{ ceos_image_filename }}"
135+
environment: "{{ proxy_env | default({}) }}"
136+
register: ceos_image_download_result
137+
138+
when: ceos_image_file_stat.stat.exists == false
139+
140+
- name: Import ceos_image_orig docker image
141+
become: yes
142+
shell: "docker import {{ root_path }}/images/{{ ceos_image_filename }} {{ ceos_image_orig }}"
143+
144+
when: ceos_image_orig_stat.images | length == 0
145+
146+
- name: Create directory for building ceos docker image
147+
become: yes
148+
file:
149+
path: "/tmp/ceosimage"
150+
state: directory
151+
152+
- name: Copy the ceos image template
153+
become: yes
154+
template: src=ceos_dockerfile.j2 dest=/tmp/ceosimage/Dockerfile mode=0644
155+
156+
- name: Build the ceos image with increasing inotify limit
73157
become: yes
74-
shell: "docker import {{ root_path }}/images/{{ ceos_image_filename }} {{ ceos_image_orig }}"
75-
76-
when: ceos_image_orig_stat.images | length == 0
77-
78-
- name: Create directory for building ceos docker image
79-
become: yes
80-
file:
81-
path: "/tmp/ceosimage"
82-
state: directory
83-
84-
- name: Copy the ceos image template
85-
become: yes
86-
template: src=ceos_dockerfile.j2 dest=/tmp/ceosimage/Dockerfile mode=0644
87-
88-
- name: Build the ceos image with increasing inotify limit
89-
become: yes
90-
docker_image:
91-
name: "{{ ceos_image }}"
92-
build:
93-
path: "/tmp/ceosimage"
94-
pull: no
95-
source: build
96-
97-
when: ceos_docker_image_stat.images | length == 0
158+
docker_image:
159+
name: "{{ ceos_image }}"
160+
build:
161+
path: "/tmp/ceosimage"
162+
pull: no
163+
source: build
164+
165+
when: >
166+
ceos_registry is not defined or
167+
(ceos_registry_pull_result is defined and ceos_registry_pull_result is failed)
168+
169+
when: not ceos_image_found
98170

99171
- name: Create VMs network
100172
become: yes

0 commit comments

Comments
 (0)