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
38 changes: 17 additions & 21 deletions roles/cloud-scaleway/tasks/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,25 +3,21 @@
import_tasks: prompts.yml

- block:
- name: Gather Scaleway organizations facts
scaleway_organization_info:
register: scaleway_org

- name: Get images
scaleway_image_info:
region: "{{ algo_region }}"
register: scaleway_image
- 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: 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:
Expand All @@ -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
Expand All @@ -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
Expand Down
15 changes: 15 additions & 0 deletions roles/cloud-scaleway/tasks/prompts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,26 @@
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) }}"
algo_region: >-
{% 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 changes: 136 additions & 0 deletions tests/unit/test_scaleway_fix.py
Original file line number Diff line number Diff line change
@@ -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"

Check failure

Code scanning / CodeQL

Incomplete URL substring sanitization High test

The string
api-marketplace.scaleway.com
may be at an arbitrary position in the sanitized URL.

Copilot Autofix

AI 20 days ago

Copilot could not generate an autofix suggestion

Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.


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"

Check failure

Code scanning / CodeQL

Incomplete URL substring sanitization High test

The string
console.scaleway.com
may be at an arbitrary position in the sanitized URL.

Copilot Autofix

AI 20 days ago

Copilot could not generate an autofix suggestion

Copilot could not generate an autofix suggestion for this alert. Try pushing a new commit or if the problem persists contact support.


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!")
Loading