-
Notifications
You must be signed in to change notification settings - Fork 30
feat(routing-table): provide rt and routes functionality #1023
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
5b9fe56 to
45f6702
Compare
|
Update waiting for go-sdk iaas api update ^ |
45f6702 to
c79ff62
Compare
e8c2521 to
826b579
Compare
e2b6a5f to
f4fd8d5
Compare
|
@rubenhoenle I’ve just implemented all of your suggestions and pushed an |
|
This PR was marked as stale after 7 days of inactivity and will be closed after another 7 days of further inactivity. If this PR should be kept open, just add a comment, remove the stale label or push new commits to it. |
424239c to
c2cadea
Compare
| } else { | ||
| params.Printer.Debug(print.ErrorLevel, "configure resource manager client: %v", err) | ||
| } | ||
| params.Printer.Outputf("No routing-tables found for organization %q\n", orgLabel) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Invalid JSON/YAML output in case -output-format flag is set to JSON/YAML output
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a user, I still want to be able to list routing tables for automation purposes, such as listing all routing tables, grepping the IDs, and deleting them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I also want users to be able to do that.
In fact running
stackit routing-table list --output-format json currently would output No routing-tables found for organization ... when there are no routing tables present, but as a user I would expect a valid JSON output which would be just [] (an empty list).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See #893 for reference please
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implemented a fix and also created automated tests for list both routing-table and route list call
| } | ||
|
|
||
| if items := response.Items; items == nil || len(*items) == 0 { | ||
| params.Printer.Outputf("No routes found for routing-table %q\n", model.RoutingTableId) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Invalid JSON/YAML output in case -output-format flag is set to JSON/YAML output
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're even handling that case in your outputResult func
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As a user, I still want to be able to list routes for automation purposes, such as listing all routes, grepping the IDs, and deleting them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see other conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Implemented a fix and also created automated tests for list both routing-table and route list call
| outputFormat string | ||
| route iaas.Route | ||
| wantErr bool | ||
| }{ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
test your table output...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These tests are kind of useless in their current state.
You must test edge cases, not only the happy paths for unit tests to be useful.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
good catch. I admit I forgot to implement them.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added more cases for all TestOutputResult functions
|
Test script: import subprocess
import sys
import yaml
from datetime import datetime
# Static variables
PROJECT_ID = "f28453cc-9c37-4948-b2c5-36c0bae0c47a"
NETWORK_AREA_ID = "f1ffef6c-078e-4580-8282-93b8ade6cb49"
ORG_ID = "03a34540-3c1a-4794-b2c6-7111ecf824ef"
# Dynamic variables initialized during test flow
NETWORK_ID = ""
ROUTING_TABLE_ID = ""
ROUTING_TABLE_ID_2 = ""
ROUTE_ID = ""
def log(msg: str):
print(f"[{datetime.now().strftime("%Y-%m-%d %H:%M:%S")}] {msg}", file=sys.stdout)
def run_command(description: str, _expected: str, *args):
log(f"{description}")
result = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
if result.returncode == 0:
log(f"Command succeeded: {description}")
if result.stdout.strip():
print("STDOUT:")
print(result.stdout.strip())
else:
log(f"Command failed: {description}")
if result.stderr.strip():
print("STDERR:")
print(result.stderr.strip())
elif result.stdout.strip():
# Some errors may go to stdout
print("STDOUT (unexpected):")
print(result.stdout.strip())
def extract_id(description: str, yq_path: str, *args) -> str:
full_args = list(args) + ["-o", "yaml"]
try:
result = subprocess.run(full_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True, text=True)
parsed_yaml = yaml.safe_load(result.stdout)
if isinstance(parsed_yaml, list):
first_item = parsed_yaml[0] if parsed_yaml else None
id_val = first_item.get("id") if first_item else None
elif isinstance(parsed_yaml, dict):
if yq_path.startswith(".items"):
items = parsed_yaml.get("items", [])
id_val = items[0].get("id") if items else None
elif yq_path.startswith("."):
id_val = parsed_yaml.get(yq_path.lstrip("."))
else:
id_val = parsed_yaml.get(yq_path)
else:
id_val = None
if not id_val:
raise ValueError("ID not found")
log(f"{description} ID: {id_val}")
return id_val
except Exception as e:
log(f"{description} Failed to extract ID: {e} {" ".join(full_args)}")
sys.exit(1)
def run():
global ROUTING_TABLE_ID, ROUTING_TABLE_ID_2, NETWORK_ID, ROUTE_ID
run_command("Set project ID", "success", "./bin/stackit", "config", "set", "--project-id", PROJECT_ID)
ROUTING_TABLE_ID = extract_id("Create routing-table rt_test", ".id",
"./bin/stackit", "routing-table", "create", "--network-area-id", NETWORK_AREA_ID,
"--organization-id", ORG_ID, "--name", "rt_test", "-y")
NETWORK_ID = extract_id("Create network with RT ID", ".id",
"./bin/stackit", "network", "create", "--name", "network-rt", "--routing-table-id", ROUTING_TABLE_ID, "-y")
run_command("List networks (check RT ID shown)", "success", "./bin/stackit", "network", "list", "-o", "pretty")
run_command("Describe network", "success", "./bin/stackit", "network", "describe", NETWORK_ID)
ROUTING_TABLE_ID_2 = extract_id("Create routing-table rt_test_2", ".id",
"./bin/stackit", "routing-table", "create", "--network-area-id", NETWORK_AREA_ID,
"--organization-id", ORG_ID, "--name", "rt_test_2", "-y")
run_command("Update network with RT 2 ID", "success",
"./bin/stackit", "network", "update", NETWORK_ID, "--routing-table-id", ROUTING_TABLE_ID_2, "-y")
run_command("Describe routing-table 1", "success",
"./bin/stackit", "routing-table", "describe", ROUTING_TABLE_ID,
"--network-area-id", NETWORK_AREA_ID, "--organization-id", ORG_ID, "-o", "pretty")
run_command("Describe routing-table 2", "success",
"./bin/stackit", "routing-table", "describe", ROUTING_TABLE_ID_2,
"--network-area-id", NETWORK_AREA_ID, "--organization-id", ORG_ID, "-o", "pretty")
# not working due to missing id
run_command("Describe routing-table 2", "fail",
"./bin/stackit", "routing-table", "describe", "",
"--network-area-id", NETWORK_AREA_ID, "--organization-id", ORG_ID, "-o", "pretty")
run_command("List routing-tables", "success",
"./bin/stackit", "routing-table", "list", "--network-area-id", NETWORK_AREA_ID,
"--organization-id", ORG_ID, "-o", "pretty")
run_command("Delete second routing-table", "success",
"./bin/stackit", "routing-table", "delete", ROUTING_TABLE_ID_2,
"--network-area-id", NETWORK_AREA_ID, "--organization-id", ORG_ID, "-y")
run_command("Update RT: disable dynamic-routes", "success",
"./bin/stackit", "routing-table", "update", ROUTING_TABLE_ID, "--network-area-id", NETWORK_AREA_ID,
"--organization-id", ORG_ID, "--description", "Test desc", "--non-dynamic-routes", "-y")
run_command("Update RT: re-enable dynamic-routes", "success",
"./bin/stackit", "routing-table", "update", ROUTING_TABLE_ID, "--network-area-id", NETWORK_AREA_ID,
"--organization-id", ORG_ID, "--description", "Test desc", "-y")
run_command("Update RT: name", "success",
"./bin/stackit", "routing-table", "update", ROUTING_TABLE_ID, "--network-area-id", NETWORK_AREA_ID,
"--organization-id", ORG_ID, "--name", "rt_test", "-y")
run_command("Update RT: labels + name", "success",
"./bin/stackit", "routing-table", "update", ROUTING_TABLE_ID, "--network-area-id", NETWORK_AREA_ID,
"--organization-id", ORG_ID, "--labels", "xxx=yyy,zzz=bbb", "--name", "rt_test", "-y")
ROUTE_ID = extract_id("Create route with next-hop IPv4", ".items.0.id",
"./bin/stackit", "routing-table", "route", "create", "--routing-table-id", ROUTING_TABLE_ID,
"--network-area-id", NETWORK_AREA_ID, "--organization-id", ORG_ID, "-y",
"--destination-type", "cidrv4", "--destination-value", "0.0.0.0/0",
"--nexthop-type", "ipv4", "--nexthop-value", "10.1.1.0")
run_command("Create route with next-hop blackhole", "success",
"./bin/stackit", "routing-table", "route", "create", "--routing-table-id", ROUTING_TABLE_ID,
"--network-area-id", NETWORK_AREA_ID, "--organization-id", ORG_ID, "-y",
"--destination-type", "cidrv4", "--destination-value", "0.0.0.0/0", "--nexthop-type", "blackhole")
run_command("Create route with next-hop internet", "success",
"./bin/stackit", "routing-table", "route", "create", "--routing-table-id", ROUTING_TABLE_ID,
"--network-area-id", NETWORK_AREA_ID, "--organization-id", ORG_ID, "-y",
"--destination-type", "cidrv4", "--destination-value", "0.0.0.0/0", "--nexthop-type", "internet")
run_command("Negative test: invalid next-hop", "fail",
"./bin/stackit", "routing-table", "route", "create", "--routing-table-id", ROUTING_TABLE_ID,
"--network-area-id", NETWORK_AREA_ID, "--organization-id", ORG_ID,
"--destination-type", "cidrv4", "--destination-value", "0.0.0.0/0", "--nexthop-type", "error")
run_command("Negative test: invalid destination-type", "fail",
"./bin/stackit", "routing-table", "route", "create", "--routing-table-id", ROUTING_TABLE_ID,
"--network-area-id", NETWORK_AREA_ID, "--organization-id", ORG_ID,
"--destination-type", "error", "--destination-value", "0.0.0.0/0", "--nexthop-type", "internet")
run_command("List all routing-table routes", "success",
"./bin/stackit", "routing-table", "route", "list", "--routing-table-id", ROUTING_TABLE_ID,
"--network-area-id", NETWORK_AREA_ID, "--organization-id", ORG_ID, "-o", "pretty")
run_command("Describe route", "success",
"./bin/stackit", "routing-table", "route", "describe", ROUTE_ID,
"--routing-table-id", ROUTING_TABLE_ID, "--network-area-id", NETWORK_AREA_ID,
"--organization-id", ORG_ID, "-o", "pretty")
# not working due to missing id
run_command("Describe route", "fail",
"./bin/stackit", "routing-table", "route", "describe", "",
"--routing-table-id", ROUTING_TABLE_ID, "--network-area-id", NETWORK_AREA_ID,
"--organization-id", ORG_ID, "-o", "pretty")
run_command("Update route labels", "success",
"./bin/stackit", "routing-table", "route", "update", ROUTE_ID, "--routing-table-id", ROUTING_TABLE_ID,
"--network-area-id", NETWORK_AREA_ID, "--organization-id", ORG_ID,
"--labels", "key=value,foo=bar", "-y")
run_command("Delete route", "success",
"./bin/stackit", "routing-table", "route", "delete", ROUTE_ID, "--routing-table-id", ROUTING_TABLE_ID,
"--network-area-id", NETWORK_AREA_ID, "--organization-id", ORG_ID, "-y")
log("Cleanup: Removing all routing-tables named rt_test or rt_test_2.")
cleanup_entities("routing-table", ["rt_test", "rt_test_2"],
["--organization-id", ORG_ID, "--network-area-id", NETWORK_AREA_ID])
log("Cleanup: Removing all networks named network-rt.")
cleanup_entities("network", ["network-rt"], [])
log("All tests finished successfully.")
def cleanup_entities(entity_type, name_list, extra_args):
result = subprocess.run(["./bin/stackit", entity_type, "list", "-o", "yaml"] + extra_args,
stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
items = yaml.safe_load(result.stdout)
for item in items:
if item.get("name") in name_list:
entity_id = item.get("id")
cmd = ["./bin/stackit", entity_type, "delete", entity_id] + extra_args + ["-y"]
run_command(f"Cleanup delete {entity_type} {item['name']}", "success", *cmd)
if __name__ == "__main__":
run() |
fd94802 to
4df31e5
Compare
25a8f19 to
75cddf2
Compare
|
|
||
| table := tables.NewTable() | ||
| table.SetHeader("ID", "NAME", "DESCRIPTION", "CREATED_AT", "UPDATED_AT", "DEFAULT", "LABELS", "SYSTEM_ROUTES", "DYNAMIC_ROUTES") | ||
| table.SetHeader("ID", "NAME", "DESCRIPTION", "DEFAULT", "LABELS", "SYSTEM ROUTES", "DYNAMIC ROUTES", "CREATED AT", "UPDATED AT") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I’ve reordered the table header. I thought it would be more readable to move created_at and updated_at to the end.
75cddf2 to
208163f
Compare
Description
This PR adds functionality to list and describe routing tables, as well as perform full CRUD operations on routes within those tables. It does not include support for creating or attaching routing tables, as those operations are currently intended to be managed exclusively through Terraform.
This implementation is primarily aimed at enabling users to inspect and debug routes created via Terraform. Once the routing table feature reaches GA, support for creating routing tables and attaching them to networks will be added to the CLI.
Checklist
make fmtmake generate-docs(will be checked by CI)make test(will be checked by CI)make lint(will be checked by CI)