Fix Scaleway deployment by replacing broken organization_info module (fixes #14846) (#14848)

* 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 <noreply@anthropic.com>

* Suppress CodeQL false positives in Scaleway tests

The CodeQL scanner flags domain string assertions as "incomplete URL
substring sanitization" but these are test assertions checking that
Ansible role files reference the correct URLs, not URL validation logic.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>

* Revert "Suppress CodeQL false positives in Scaleway tests"

This reverts commit c75a73e3f5.

---------

Co-authored-by: Claude <noreply@anthropic.com>
This commit is contained in:
Dan Guido
2025-11-25 13:11:02 -05:00
committed by GitHub
co-authored by Claude
parent cc30d0a003
commit b2242ec849
3 changed files with 168 additions and 21 deletions
+17 -21
View File
@@ -3,25 +3,21 @@
import_tasks: prompts.yml
- block:
- name: Gather Scaleway organizations facts
scaleway_organization_info:
register: scaleway_org
- name: Get Ubuntu 22.04 image ID from Scaleway Marketplace API
uri:
url: "https://api-marketplace.scaleway.com/images?arch={{ cloud_providers.scaleway.arch }}&include_eol=false"
method: GET
return_content: true
register: marketplace_images
- name: Get images
scaleway_image_info:
region: "{{ algo_region }}"
register: scaleway_image
- name: Set cloud specific facts
- name: Find Ubuntu 22.04 Jammy image
set_fact:
organization_id: "{{ scaleway_org.scaleway_organization_info[0]['id'] }}"
images: >-
[{% for i in scaleway_image.scaleway_image_info -%}
{% if i.name == cloud_providers.scaleway.image and
i.arch == cloud_providers.scaleway.arch -%}
'{{ i.id }}'{% if not loop.last %},{% endif %}
{%- endif -%}
{%- endfor -%}]
scaleway_image_id: >-
{{ (marketplace_images.json.images |
selectattr('name', 'match', '.*Ubuntu.*22\\.04.*Jammy.*') |
first).versions[0].local_images |
selectattr('zone', 'equalto', algo_region) |
map(attribute='id') | first }}
- name: Create a server
scaleway_compute:
@@ -30,8 +26,8 @@
public_ip: dynamic
boot_type: local
state: present
image: "{{ images[0] }}"
organization: "{{ organization_id }}"
image: "{{ scaleway_image_id }}"
project: "{{ algo_scaleway_org_id }}"
region: "{{ algo_region }}"
commercial_type: "{{ cloud_providers.scaleway.size }}"
wait: true
@@ -57,8 +53,8 @@
public_ip: dynamic
boot_type: local
state: running
image: "{{ images[0] }}"
organization: "{{ organization_id }}"
image: "{{ scaleway_image_id }}"
project: "{{ algo_scaleway_org_id }}"
region: "{{ algo_region }}"
commercial_type: "{{ cloud_providers.scaleway.size }}"
wait: true
+15
View File
@@ -21,6 +21,20 @@
register: _algo_region
when: region is undefined
- pause:
prompt: |
Enter your Scaleway Organization ID (also serves as your default Project ID)
You can find this in your Scaleway console:
1. Go to https://console.scaleway.com/organization/settings
2. Copy the Organization ID from the Organization Settings page
Note: For the default project, the Project ID is the same as the Organization ID.
(https://trailofbits.github.io/algo/cloud-scaleway.html)
register: _scaleway_org_id
when:
- scaleway_org_id is undefined
- lookup('env', 'SCW_DEFAULT_ORGANIZATION_ID')|length <= 0
- name: Set scaleway facts
set_fact:
algo_scaleway_token: "{{ scaleway_token | default(_scaleway_token.user_input) | default(lookup('env', 'SCW_TOKEN'), true) }}"
@@ -28,4 +42,5 @@
{% if region is defined %}{{ region }}
{%- elif _algo_region.user_input %}{{ scaleway_regions[_algo_region.user_input | int - 1]['alias'] }}
{%- else %}{{ scaleway_regions.0.alias }}{% endif %}
algo_scaleway_org_id: "{{ scaleway_org_id | default(_scaleway_org_id.user_input) | default(lookup('env', 'SCW_DEFAULT_ORGANIZATION_ID'), true) }}"
no_log: true
+136
View File
@@ -0,0 +1,136 @@
#!/usr/bin/env python3
"""
Test Scaleway role fixes for issue #14846
This test validates that:
1. The Scaleway role uses the modern 'project' parameter instead of deprecated 'organization'
2. The Marketplace API is used for image lookup instead of the broken scaleway_image_info module
3. The prompts include organization/project ID collection
"""
import sys
from pathlib import Path
import yaml
def load_yaml_file(file_path):
"""Load and parse a YAML file"""
with open(file_path) as f:
return yaml.safe_load(f)
def test_scaleway_main_uses_project_parameter():
"""Test that main.yml uses 'project' instead of deprecated 'organization' parameter"""
main_yml = Path("roles/cloud-scaleway/tasks/main.yml")
assert main_yml.exists(), "Scaleway main.yml not found"
with open(main_yml) as f:
content = f.read()
# Should NOT use the broken scaleway_organization_info module
assert (
"scaleway_organization_info" not in content
), "Still using broken scaleway_organization_info module (issue #14846)"
# Should NOT use the broken scaleway_image_info module
assert "scaleway_image_info" not in content, "Still using broken scaleway_image_info module"
# Should use project parameter (modern approach)
assert "project:" in content, "Missing 'project:' parameter in scaleway_compute calls"
assert "algo_scaleway_org_id" in content, "Missing algo_scaleway_org_id variable reference"
# Should NOT use deprecated organization parameter
assert 'organization: "{{' not in content, "Still using deprecated 'organization' parameter"
# Should use Marketplace API for image lookup
assert "api-marketplace.scaleway.com" in content, "Not using Scaleway Marketplace API for image lookup"
print("✓ Scaleway main.yml uses modern 'project' parameter")
def test_scaleway_prompts_collect_org_id():
"""Test that prompts.yml collects organization/project ID from user"""
prompts_yml = Path("roles/cloud-scaleway/tasks/prompts.yml")
assert prompts_yml.exists(), "Scaleway prompts.yml not found"
with open(prompts_yml) as f:
content = f.read()
# Should prompt for organization ID
assert "Organization ID" in content, "Missing prompt for Scaleway Organization ID"
# Should set algo_scaleway_org_id fact
assert "algo_scaleway_org_id:" in content, "Missing algo_scaleway_org_id fact definition"
# Should support SCW_DEFAULT_ORGANIZATION_ID env var
assert (
"SCW_DEFAULT_ORGANIZATION_ID" in content
), "Missing support for SCW_DEFAULT_ORGANIZATION_ID environment variable"
# Should mention console.scaleway.com for finding the ID
assert "console.scaleway.com" in content, "Missing instructions on where to find Organization ID"
print("✓ Scaleway prompts.yml collects organization/project ID")
def test_scaleway_config_has_valid_settings():
"""Test that config.cfg has valid Scaleway settings"""
config_file = Path("config.cfg")
assert config_file.exists(), "config.cfg not found"
with open(config_file) as f:
content = f.read()
# Should have scaleway section
assert "scaleway:" in content, "Missing Scaleway configuration section"
# Should specify Ubuntu 22.04
assert "Ubuntu 22.04" in content or "ubuntu" in content.lower(), "Missing Ubuntu image specification"
print("✓ config.cfg has valid Scaleway settings")
def test_scaleway_marketplace_api_usage():
"""Test that the role correctly uses Scaleway Marketplace API"""
main_yml = Path("roles/cloud-scaleway/tasks/main.yml")
with open(main_yml) as f:
content = f.read()
# Should use uri module to fetch from Marketplace API
assert "uri:" in content, "Not using uri module for API calls"
# Should filter for Ubuntu 22.04 Jammy
assert "Ubuntu" in content and "22" in content, "Not filtering for Ubuntu 22.04 image"
# Should set scaleway_image_id variable
assert "scaleway_image_id" in content, "Missing scaleway_image_id variable for image UUID"
print("✓ Scaleway role uses Marketplace API correctly")
if __name__ == "__main__":
tests = [
test_scaleway_main_uses_project_parameter,
test_scaleway_prompts_collect_org_id,
test_scaleway_config_has_valid_settings,
test_scaleway_marketplace_api_usage,
]
failed = 0
for test in tests:
try:
test()
except AssertionError as e:
print(f"{test.__name__} failed: {e}")
failed += 1
except Exception as e:
print(f"{test.__name__} error: {e}")
failed += 1
if failed > 0:
print(f"\n{failed} tests failed")
sys.exit(1)
else:
print(f"\nAll {len(tests)} tests passed!")