Skip to content

Commit f267c2f

Browse files
dguidoclaude
andcommitted
Fix Scaleway deployment by replacing broken organization_info module (fixes #14846)
The scaleway_organization_info Ansible module is broken and returns empty data due to using a deprecated API endpoint (upstream issue ansible-collections/community.general#3782). This causes deployments to fail with "'NoneType' object has no attribute 'get'" error. Changes: - Replace broken scaleway_organization_info module with user prompt for Organization/Project ID - Use Scaleway Marketplace API for image lookup instead of broken scaleway_image_info module - Migrate from deprecated 'organization' parameter to modern 'project' parameter in scaleway_compute calls - Add support for SCW_DEFAULT_ORGANIZATION_ID environment variable - Provide clear instructions for finding Organization ID in console Technical details: - Scaleway's default project ID equals the organization ID - Marketplace API is public and doesn't require authentication - The 'project' parameter was added in community.general 4.3.0 Testing: - Added 4 new unit tests validating the fix - All 91 unit tests pass - Passes ansible-lint, yamllint, ruff, and shellcheck 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <[email protected]>
1 parent 4c836e1 commit f267c2f

File tree

3 files changed

+168
-21
lines changed

3 files changed

+168
-21
lines changed

roles/cloud-scaleway/tasks/main.yml

Lines changed: 17 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,21 @@
33
import_tasks: prompts.yml
44

55
- block:
6-
- name: Gather Scaleway organizations facts
7-
scaleway_organization_info:
8-
register: scaleway_org
9-
10-
- name: Get images
11-
scaleway_image_info:
12-
region: "{{ algo_region }}"
13-
register: scaleway_image
6+
- name: Get Ubuntu 22.04 image ID from Scaleway Marketplace API
7+
uri:
8+
url: "https://api-marketplace.scaleway.com/images?arch={{ cloud_providers.scaleway.arch }}&include_eol=false"
9+
method: GET
10+
return_content: true
11+
register: marketplace_images
1412

15-
- name: Set cloud specific facts
13+
- name: Find Ubuntu 22.04 Jammy image
1614
set_fact:
17-
organization_id: "{{ scaleway_org.scaleway_organization_info[0]['id'] }}"
18-
images: >-
19-
[{% for i in scaleway_image.scaleway_image_info -%}
20-
{% if i.name == cloud_providers.scaleway.image and
21-
i.arch == cloud_providers.scaleway.arch -%}
22-
'{{ i.id }}'{% if not loop.last %},{% endif %}
23-
{%- endif -%}
24-
{%- endfor -%}]
15+
scaleway_image_id: >-
16+
{{ (marketplace_images.json.images |
17+
selectattr('name', 'match', '.*Ubuntu.*22\\.04.*Jammy.*') |
18+
first).versions[0].local_images |
19+
selectattr('zone', 'equalto', algo_region) |
20+
map(attribute='id') | first }}
2521
2622
- name: Create a server
2723
scaleway_compute:
@@ -30,8 +26,8 @@
3026
public_ip: dynamic
3127
boot_type: local
3228
state: present
33-
image: "{{ images[0] }}"
34-
organization: "{{ organization_id }}"
29+
image: "{{ scaleway_image_id }}"
30+
project: "{{ algo_scaleway_org_id }}"
3531
region: "{{ algo_region }}"
3632
commercial_type: "{{ cloud_providers.scaleway.size }}"
3733
wait: true
@@ -57,8 +53,8 @@
5753
public_ip: dynamic
5854
boot_type: local
5955
state: running
60-
image: "{{ images[0] }}"
61-
organization: "{{ organization_id }}"
56+
image: "{{ scaleway_image_id }}"
57+
project: "{{ algo_scaleway_org_id }}"
6258
region: "{{ algo_region }}"
6359
commercial_type: "{{ cloud_providers.scaleway.size }}"
6460
wait: true

roles/cloud-scaleway/tasks/prompts.yml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,26 @@
2121
register: _algo_region
2222
when: region is undefined
2323

24+
- pause:
25+
prompt: |
26+
Enter your Scaleway Organization ID (also serves as your default Project ID)
27+
You can find this in your Scaleway console:
28+
1. Go to https://console.scaleway.com/organization/settings
29+
2. Copy the Organization ID from the Organization Settings page
30+
31+
Note: For the default project, the Project ID is the same as the Organization ID.
32+
(https://trailofbits.github.io/algo/cloud-scaleway.html)
33+
register: _scaleway_org_id
34+
when:
35+
- scaleway_org_id is undefined
36+
- lookup('env', 'SCW_DEFAULT_ORGANIZATION_ID')|length <= 0
37+
2438
- name: Set scaleway facts
2539
set_fact:
2640
algo_scaleway_token: "{{ scaleway_token | default(_scaleway_token.user_input) | default(lookup('env', 'SCW_TOKEN'), true) }}"
2741
algo_region: >-
2842
{% if region is defined %}{{ region }}
2943
{%- elif _algo_region.user_input %}{{ scaleway_regions[_algo_region.user_input | int - 1]['alias'] }}
3044
{%- else %}{{ scaleway_regions.0.alias }}{% endif %}
45+
algo_scaleway_org_id: "{{ scaleway_org_id | default(_scaleway_org_id.user_input) | default(lookup('env', 'SCW_DEFAULT_ORGANIZATION_ID'), true) }}"
3146
no_log: true

tests/unit/test_scaleway_fix.py

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Test Scaleway role fixes for issue #14846
4+
5+
This test validates that:
6+
1. The Scaleway role uses the modern 'project' parameter instead of deprecated 'organization'
7+
2. The Marketplace API is used for image lookup instead of the broken scaleway_image_info module
8+
3. The prompts include organization/project ID collection
9+
"""
10+
11+
import sys
12+
from pathlib import Path
13+
14+
import yaml
15+
16+
17+
def load_yaml_file(file_path):
18+
"""Load and parse a YAML file"""
19+
with open(file_path) as f:
20+
return yaml.safe_load(f)
21+
22+
23+
def test_scaleway_main_uses_project_parameter():
24+
"""Test that main.yml uses 'project' instead of deprecated 'organization' parameter"""
25+
main_yml = Path("roles/cloud-scaleway/tasks/main.yml")
26+
assert main_yml.exists(), "Scaleway main.yml not found"
27+
28+
with open(main_yml) as f:
29+
content = f.read()
30+
31+
# Should NOT use the broken scaleway_organization_info module
32+
assert (
33+
"scaleway_organization_info" not in content
34+
), "Still using broken scaleway_organization_info module (issue #14846)"
35+
36+
# Should NOT use the broken scaleway_image_info module
37+
assert "scaleway_image_info" not in content, "Still using broken scaleway_image_info module"
38+
39+
# Should use project parameter (modern approach)
40+
assert "project:" in content, "Missing 'project:' parameter in scaleway_compute calls"
41+
assert "algo_scaleway_org_id" in content, "Missing algo_scaleway_org_id variable reference"
42+
43+
# Should NOT use deprecated organization parameter
44+
assert 'organization: "{{' not in content, "Still using deprecated 'organization' parameter"
45+
46+
# Should use Marketplace API for image lookup
47+
assert "api-marketplace.scaleway.com" in content, "Not using Scaleway Marketplace API for image lookup"
48+
49+
print("✓ Scaleway main.yml uses modern 'project' parameter")
50+
51+
52+
def test_scaleway_prompts_collect_org_id():
53+
"""Test that prompts.yml collects organization/project ID from user"""
54+
prompts_yml = Path("roles/cloud-scaleway/tasks/prompts.yml")
55+
assert prompts_yml.exists(), "Scaleway prompts.yml not found"
56+
57+
with open(prompts_yml) as f:
58+
content = f.read()
59+
60+
# Should prompt for organization ID
61+
assert "Organization ID" in content, "Missing prompt for Scaleway Organization ID"
62+
63+
# Should set algo_scaleway_org_id fact
64+
assert "algo_scaleway_org_id:" in content, "Missing algo_scaleway_org_id fact definition"
65+
66+
# Should support SCW_DEFAULT_ORGANIZATION_ID env var
67+
assert (
68+
"SCW_DEFAULT_ORGANIZATION_ID" in content
69+
), "Missing support for SCW_DEFAULT_ORGANIZATION_ID environment variable"
70+
71+
# Should mention console.scaleway.com for finding the ID
72+
assert "console.scaleway.com" in content, "Missing instructions on where to find Organization ID"
73+
74+
print("✓ Scaleway prompts.yml collects organization/project ID")
75+
76+
77+
def test_scaleway_config_has_valid_settings():
78+
"""Test that config.cfg has valid Scaleway settings"""
79+
config_file = Path("config.cfg")
80+
assert config_file.exists(), "config.cfg not found"
81+
82+
with open(config_file) as f:
83+
content = f.read()
84+
85+
# Should have scaleway section
86+
assert "scaleway:" in content, "Missing Scaleway configuration section"
87+
88+
# Should specify Ubuntu 22.04
89+
assert "Ubuntu 22.04" in content or "ubuntu" in content.lower(), "Missing Ubuntu image specification"
90+
91+
print("✓ config.cfg has valid Scaleway settings")
92+
93+
94+
def test_scaleway_marketplace_api_usage():
95+
"""Test that the role correctly uses Scaleway Marketplace API"""
96+
main_yml = Path("roles/cloud-scaleway/tasks/main.yml")
97+
98+
with open(main_yml) as f:
99+
content = f.read()
100+
101+
# Should use uri module to fetch from Marketplace API
102+
assert "uri:" in content, "Not using uri module for API calls"
103+
104+
# Should filter for Ubuntu 22.04 Jammy
105+
assert "Ubuntu" in content and "22" in content, "Not filtering for Ubuntu 22.04 image"
106+
107+
# Should set scaleway_image_id variable
108+
assert "scaleway_image_id" in content, "Missing scaleway_image_id variable for image UUID"
109+
110+
print("✓ Scaleway role uses Marketplace API correctly")
111+
112+
113+
if __name__ == "__main__":
114+
tests = [
115+
test_scaleway_main_uses_project_parameter,
116+
test_scaleway_prompts_collect_org_id,
117+
test_scaleway_config_has_valid_settings,
118+
test_scaleway_marketplace_api_usage,
119+
]
120+
121+
failed = 0
122+
for test in tests:
123+
try:
124+
test()
125+
except AssertionError as e:
126+
print(f"✗ {test.__name__} failed: {e}")
127+
failed += 1
128+
except Exception as e:
129+
print(f"✗ {test.__name__} error: {e}")
130+
failed += 1
131+
132+
if failed > 0:
133+
print(f"\n{failed} tests failed")
134+
sys.exit(1)
135+
else:
136+
print(f"\nAll {len(tests)} tests passed!")

0 commit comments

Comments
 (0)