OpenKeel: the sovereign French Cloud solution of the inherent group, with a European reach Discover inherent

Reverse-spec · Services

skyline-console — Summary

Reverse-specification of skyline-console — branch stable/2026.2

  • Repository: https://opendev.org/openstack/skyline-console — specified branch: stable/2026.2 @ 806d200392ee (9.0.0-1-g806d2003) — analysed on: 2026-10-29
  • Scope: the whole single-page application: UI modules, pages and actions; outbound REST calls with microversions; error handling; polling; build, packaging and deployment on Kubernetes. Rewrite pack (step 8) delivered as a deployment and connection contract: in OpenKeel the console is kept as is and is not rewritten in Go. Change history (step 7) not requested.
  • Other repositories cited: skyline-apiserver stable/2026.2 @ 4af72bfd4577 (prefix skyline-apiserver:), microversion-parse @ 77ea32a (prefix microversion-parse:), cinder stable/2026.2 @ c117f9cf0 (prefix cinder:), nova stable/2026.2 @ 8df1922926 (prefix nova:). Companion report: [../reverse-spec-skyline-apiserver/](../reverse-spec-skyline-apiserver/00-summary_stable-2026.2.md).
FileContent
01-ui-modules_stable-2026.2.mdmodules, routes, pages, actions and the operations they trigger (replaces the REST API of a server)
02-errors_stable-2026.2.mdhow errors are handled and shown; error bodies the console parses
03-interactions_stable-2026.2.mdrequest layer, URL and microversion rules, calls to skyline-apiserver and to each OpenStack API, browser-side state
04-scenarios_stable-2026.2.mdlogin, instance creation, state action, volume extension, project switch, session expiry
05-async-processing_stable-2026.2.mdauto refresh, transition polling, session restore
07-rewrite-pack_stable-2026.2.mdbuild, Kubernetes deployment, connection contract, requirements on the OpenStack APIs, smoke test, checklist
_rewrite/ui-catalogue_stable-2026.2.md, _rewrite/outbound-calls_stable-2026.2.mdgenerated exhaustive tables (every route, page, action and outbound call with file:line)

Overview

skyline-console is the React 16 / MobX / Ant Design web UI of OpenStack Skyline. It is a pure client: no server process, no database, no bus. It is built with webpack into static files (skyline_console/static/) and shipped as a Python package. skyline-apiserver's container installs that package and serves the files with nginx (skyline-apiserver:container/Dockerfile:23). In the browser it talks only to its own origin:

  • /api/openstack/skyline/api/v1/… for skyline-apiserver: session, profile, UI policies, aggregated lists, Prometheus, settings;
  • /api/openstack/<region>/<service>/… for the OpenStack APIs: Nova, Neutron, Cinder, Glance, Keystone, Manila, Octavia, Ironic, Heat, Trove, Zun, Magnum, Masakari, Designate, Barbican, Swift, Placement. nginx proxies these to the catalog endpoints, carrying the user's Keystone token.

The token comes from skyline-apiserver's login answer. The console keeps it in localStorage and sends it as X-Auth-Token.

Architecture

flowchart LR
  user[Browser: skyline-console SPA] -->|HTTPS, same origin| nginx[nginx in the skyline pod]
  nginx -->|/ static files, SPA fallback| static[(skyline_console/static)]
  nginx -->|/api/openstack/skyline/ → unix socket| api[skyline-apiserver]
  nginx -->|/api/openstack/regionone/&lt;service&gt;/<br/>X-Auth-Token + microversion headers| os[OpenStack APIs]
  api -->|REST| ks[Keystone and services]
  user -.->|noVNC / Zun console URLs, direct| consoles[console proxies]

Processes

ProcessEntry pointRoleListens to / consumes
browser applicationsrc/core/index.jsx:65 (session restore, then render)the whole UIuser events; HTTP responses
build (not at runtime)config/webpack.prod.js:34produces skyline_console/static/—
development serverconfig/webpack.dev.js:46, config/server.dev.js:23hot reload, proxies /api/ to server of config/config.yaml0.0.0.0:8088 (config/config.yaml:1)
Python packageskyline_console/__init__.py:17exposes static_path to skyline-apiserver's nginx generator—

External dependencies

SystemProtocolLibraryConfigurationUsage
skyline-apiserverHTTPS (same origin)axios 0.21.4fixed prefix /api/openstack/skyline/api/v1 (src/client/client/constants.js:60)login, profile, policies, aggregated lists, Prometheus, settings
OpenStack APIs (17)HTTPS through nginxaxiosprofile.endpoints (src/client/client/constants.js:46)everything else
Nova noVNC / serial, Zun WebSocketHTTPS / WSS, directwindow.open, WebSocketURLs returned by the servicesconsoles
browser storagecookies, localStorage——session (time_expired), Keystone token, language

Key figures

  • 251 routes, 274 list/detail pages, 392 actions (95 % resolved statically to their HTTP calls).
  • 18 client modules declaring 1 555 operations; 577 calls actually reached: neutron 119, manila 60, keystone 58, cinder 52, nova 50, trove 31, zun 30, ironic 27, octavia 27, skyline 24, glance 19, magnum 16, masakari 14, designate 13, swift 12, heat 11, barbican 10, placement 4.
  • Microversions: Nova 2.79 (actions up to 2.93), Cinder 3.59, Placement 1.28, Ironic 1.58, Manila 2.55, Zun 1.40.
  • Timers: list refresh 30 s, 10 s during transitions, stops after 20 idle refreshes; no other background work.
  • Build: Node 16, 1 min 44 s, 45 files / 16 MB. Smoke test: 35 recorded requests, including a deep link through login to the first lists.
  • Cluster run in kind (Keystone stable/2026.2 + skyline-apiserver container + Neutron stand-in): Cypress login, error and identity specs, 31 of 34 tests passing. The 3 failures need Nova or Cinder, which were not deployed (07 § 6.3).

Points of attention

  1. Stale bundle committed in skyline_console/static/ (2022, rc1). Installing from git without make package ships an old UI. The apiserver image also downloads the console's master tarball by default (skyline-apiserver:Makefile:20). Build from stable/2026.2 explicitly (07 § 2).
  2. Same origin at the root path only: absolute paths everywhere (src/client/client/request.js:159, config/webpack.prod.js:46). No sub-path deployment, no separate API host.
  3. Microversion headers are chosen by substring of the URL (src/client/client/constants.js:150). They depend on the nginx location names, which come from skyline-apiserver's service_mapping. A non-default mapping silently drops the headers (03 § 2).
  4. Two case-variant microversion headers are merged by the browser into compute 2.79, compute 2.42 (recorded). Nova takes the last value, Cinder the first: Cinder never receives the 3.60 (os-extend) and 3.29 (/messages) the console intends (cinder:cinder/api/openstack/wsgi.py:292). The Go rewrites of Nova and Cinder must reproduce these two parsings.
  5. Any 401 logs the user out, from any service (src/client/client/request.js:42). A Go service answering 401 for a policy refusal would log users out.
  6. The token is readable by page scripts (localStorage, src/stores/root.js:117) and stays usable locally for 10 days, bounded by the time_expired cookie. It is compared with the browser clock, so clock skew matters.
  7. UI policy defaults to allow for rules missing from skyline-apiserver's list, and to deny everything when the list is empty (src/resources/skyline/policy.js:141, src/resources/skyline/policy.js:176).
  8. Consoles bypass Skyline: the noVNC and Zun URLs must be publicly reachable (src/pages/compute/containers/Instance/actions/Console.jsx:69).
  9. Polling load: each open list re-reads every 30 s. For instances and volumes, each refresh is a skyline aggregation that fans out to several services, and admin lists add per-project Keystone calls (05).
  10. Nova error body parsed: forbidden.message containing Quota exceeded (src/pages/compute/containers/Instance/actions/StepCreate/index.jsx:794).
  11. Found in the kind run (07 § 3.1):
    • No login without Neutron. Login answers 401 "Endpoint not found" when the catalog has no network endpoint (skyline-apiserver:skyline_apiserver/client/openstack/system.py:79).
    • Admin pages need a system-scope role. Administrator pages require a system-scope admin or reader role (src/stores/root.js:155).
    • The pod crash-loops while Keystone is down, because nginx is generated from the catalog at start.
    • **A probe on / is too early.** It reports ready while the API still returns 502.
    • Upstream race on admin deep links. After login, an administrator deep link lands on the overview, because the system-roles request is not awaited (src/stores/root.js:195).

Blind spots / [inferred]

  • The extraction is static (AST). Calls built dynamically or behind conditions are approximated, and 18 actions have no resolved call; the generated catalogue is an upper bound.
  • The real skyline-apiserver container ran in kind against Keystone and a Neutron stand-in only. Compute, network and storage screens, and microversions through the real nginx, were checked only against the recording stand-in of the smoke test. Cypress ran the login, error and identity specs, not the compute, network and storage ones. No OpenKeel Go service was in the cluster.
  • Header merging was observed in Chromium only; other browsers [inferred] behave the same (Fetch specification).
  • Pages of optional services (Trove, Zun, Magnum, Masakari, Designate, Barbican) were catalogued but not described in sheets.
  • The development server, i18n and theming are out of scope.

Tool changes made for this report

  • ~/.claude/skills/generate-reverse-spec/scripts/inventory.py gained small, generic JS support: .jsx/.tsx files, minified bundles skipped, SPA routes (ui_route), client timers (setTimeout) and XMLHttpRequest/WebSocket/EventSource occurrences, and React/Vue/Angular/MobX detection. Files analysed went from 271 to 1 223.
  • Console-specific tools are kept with the report in _rewrite/tools/ (extract_console.js, build_catalogue.py, smoke_server.py); rewrite_pack.py and conformance_capture.py import a Python project and do not apply to a JavaScript client.

Source: reverse-spec-skyline-console/00-summary_stable-2026.2.md. The _rewrite/ and _inventory/ files are not published on the site.