Compare commits
11 Commits
86c2fd5ce7
...
5b33b76184
Author | SHA1 | Date | |
---|---|---|---|
|
5b33b76184 | ||
|
4e06ed4a5a | ||
|
2a3a08e328 | ||
|
d3c48c17f8 | ||
|
ce7d977df4 | ||
|
42567b8b1c | ||
|
075d5725bf | ||
|
eea81cef1e | ||
|
fb8377af6e | ||
|
ef9de18d34 | ||
|
948228e460 |
39
.zuul.yaml
39
.zuul.yaml
@ -25,40 +25,13 @@
|
|||||||
tox_install_siblings: false
|
tox_install_siblings: false
|
||||||
tox_envlist: functional
|
tox_envlist: functional
|
||||||
|
|
||||||
- job:
|
|
||||||
name: skyline-tox-py38
|
|
||||||
parent: openstack-tox-py38
|
|
||||||
description: |
|
|
||||||
Skyline py38 unittest tests
|
|
||||||
vars:
|
|
||||||
python_version: 3.8
|
|
||||||
tox_envlist: py38
|
|
||||||
|
|
||||||
- job:
|
|
||||||
name: skyline-tox-pep8-py38
|
|
||||||
parent: openstack-tox-py38
|
|
||||||
description: |
|
|
||||||
Skyline code static analysis
|
|
||||||
vars:
|
|
||||||
python_version: 3.8
|
|
||||||
tox_envlist: pep8
|
|
||||||
|
|
||||||
- project:
|
- project:
|
||||||
templates:
|
templates:
|
||||||
|
- openstack-python3-jobs
|
||||||
- release-notes-jobs-python3
|
- release-notes-jobs-python3
|
||||||
- publish-openstack-docs-pti
|
- publish-openstack-docs-pti
|
||||||
check:
|
check:
|
||||||
jobs:
|
jobs:
|
||||||
- skyline-tox-py38:
|
|
||||||
irrelevant-files:
|
|
||||||
- ^container/.*$
|
|
||||||
- ^devstack/.*$
|
|
||||||
- ^docs/.*$
|
|
||||||
- ^tools/.*$
|
|
||||||
- ^.*\.rst$
|
|
||||||
- ^.*\.md$
|
|
||||||
- ^kolla/.*$
|
|
||||||
- skyline-tox-pep8-py38
|
|
||||||
- skyline-functional-devstack:
|
- skyline-functional-devstack:
|
||||||
irrelevant-files:
|
irrelevant-files:
|
||||||
- ^container/.*$
|
- ^container/.*$
|
||||||
@ -71,16 +44,6 @@
|
|||||||
- ^releasenotes/.*$
|
- ^releasenotes/.*$
|
||||||
gate:
|
gate:
|
||||||
jobs:
|
jobs:
|
||||||
- skyline-tox-py38:
|
|
||||||
irrelevant-files:
|
|
||||||
- ^container/.*$
|
|
||||||
- ^devstack/.*$
|
|
||||||
- ^docs/.*$
|
|
||||||
- ^tools/.*$
|
|
||||||
- ^.*\.rst$
|
|
||||||
- ^.*\.md$
|
|
||||||
- ^kolla/.*$
|
|
||||||
- skyline-tox-pep8-py38
|
|
||||||
- skyline-functional-devstack:
|
- skyline-functional-devstack:
|
||||||
irrelevant-files:
|
irrelevant-files:
|
||||||
- ^container/.*$
|
- ^container/.*$
|
||||||
|
4
Makefile
4
Makefile
@ -91,14 +91,14 @@ else
|
|||||||
endif
|
endif
|
||||||
build: skyline_console/skyline_console.tar.gz skyline_console/commit_id
|
build: skyline_console/skyline_console.tar.gz skyline_console/commit_id
|
||||||
$(build_cmd) --no-cache --pull --force-rm \
|
$(build_cmd) --no-cache --pull --force-rm \
|
||||||
--build-arg GIT_CONSOLE_COMMIT=$(file < skyline_console/commit_id) \
|
--build-arg GIT_CONSOLE_COMMIT=$(shell cat skyline_console/commit_id) \
|
||||||
--build-arg GIT_BRANCH=$(GIT_BRANCH) \
|
--build-arg GIT_BRANCH=$(GIT_BRANCH) \
|
||||||
--build-arg GIT_COMMIT=$(GIT_COMMIT) \
|
--build-arg GIT_COMMIT=$(GIT_COMMIT) \
|
||||||
--build-arg RELEASE_VERSION=$(RELEASE_VERSION) \
|
--build-arg RELEASE_VERSION=$(RELEASE_VERSION) \
|
||||||
$(BUILD_ARGS) -f $(DOCKER_FILE) -t $(IMAGE):$(IMAGE_TAG) $(BUILD_CONTEXT)
|
$(BUILD_ARGS) -f $(DOCKER_FILE) -t $(IMAGE):$(IMAGE_TAG) $(BUILD_CONTEXT)
|
||||||
devbuild: skyline_console/skyline_console.tar.gz skyline_console/commit_id
|
devbuild: skyline_console/skyline_console.tar.gz skyline_console/commit_id
|
||||||
$(build_cmd) \
|
$(build_cmd) \
|
||||||
--build-arg GIT_CONSOLE_COMMIT=$(file < skyline_console/commit_id) \
|
--build-arg GIT_CONSOLE_COMMIT=$(shell cat skyline_console/commit_id) \
|
||||||
--build-arg GIT_BRANCH=devbuild \
|
--build-arg GIT_BRANCH=devbuild \
|
||||||
--build-arg GIT_COMMIT=devbuild \
|
--build-arg GIT_COMMIT=devbuild \
|
||||||
--build-arg RELEASE_VERSION=devbuild \
|
--build-arg RELEASE_VERSION=devbuild \
|
||||||
|
6
releasenotes/source/2024.1.rst
Normal file
6
releasenotes/source/2024.1.rst
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
===========================
|
||||||
|
2024.1 Series Release Notes
|
||||||
|
===========================
|
||||||
|
|
||||||
|
.. release-notes::
|
||||||
|
:branch: stable/2024.1
|
6
releasenotes/source/2024.2.rst
Normal file
6
releasenotes/source/2024.2.rst
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
===========================
|
||||||
|
2024.2 Series Release Notes
|
||||||
|
===========================
|
||||||
|
|
||||||
|
.. release-notes::
|
||||||
|
:branch: stable/2024.2
|
@ -6,6 +6,8 @@ Skyline APIServer Release Notes
|
|||||||
:maxdepth: 1
|
:maxdepth: 1
|
||||||
|
|
||||||
unreleased
|
unreleased
|
||||||
|
2024.2
|
||||||
|
2024.1
|
||||||
2023.2
|
2023.2
|
||||||
2023.1
|
2023.1
|
||||||
zed
|
zed
|
||||||
|
@ -3,4 +3,4 @@ Zed Series Release Notes
|
|||||||
========================
|
========================
|
||||||
|
|
||||||
.. release-notes::
|
.. release-notes::
|
||||||
:branch: stable/zed
|
:branch: unmaintained/zed
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
pbr>=5.8.0 # Apache-2.0
|
pbr>=5.8.0 # Apache-2.0
|
||||||
fastapi<=0.58.1 # MIT
|
fastapi<=0.58.1 # MIT
|
||||||
pydantic<=1.8.2 # MIT
|
pydantic<=1.10.0 # MIT
|
||||||
uvicorn<=0.17.6 # BSD License (3 clause)
|
uvicorn<=0.17.6 # BSD License (3 clause)
|
||||||
gunicorn>=20.1.0 # MIT
|
gunicorn>=20.1.0 # MIT
|
||||||
python-jose<=3.3.0 # MIT
|
python-jose<=3.3.0 # MIT
|
||||||
databases<=0.8.0 # BSD License (3 clause)
|
databases>=0.9.0 # BSD License (3 clause)
|
||||||
aiomysql<=0.1.1 # MIT
|
aiomysql>=0.2.0 # MIT
|
||||||
aiosqlite<=0.17.0 # MIT
|
aiosqlite<=0.17.0 # MIT
|
||||||
loguru<=0.5.3 # MIT
|
loguru<=0.5.3 # MIT
|
||||||
PyYAML>=5.4.1 # MIT
|
PyYAML>=5.4.1 # MIT
|
||||||
|
@ -19,6 +19,8 @@ classifier =
|
|||||||
Programming Language :: Python :: 3
|
Programming Language :: Python :: 3
|
||||||
Programming Language :: Python :: 3.8
|
Programming Language :: Python :: 3.8
|
||||||
Programming Language :: Python :: 3.9
|
Programming Language :: Python :: 3.9
|
||||||
|
Programming Language :: Python :: 3.10
|
||||||
|
Programming Language :: Python :: 3.11
|
||||||
project_urls:
|
project_urls:
|
||||||
Source=https://opendev.org/openstack/skyline-apiserver
|
Source=https://opendev.org/openstack/skyline-apiserver
|
||||||
Tracker=https://bugs.launchpad.net/skyline-apiserver
|
Tracker=https://bugs.launchpad.net/skyline-apiserver
|
||||||
|
@ -165,7 +165,7 @@ async def list_servers(
|
|||||||
sort_dirs=[sort_dirs.value] if sort_dirs else None,
|
sort_dirs=[sort_dirs.value] if sort_dirs else None,
|
||||||
)
|
)
|
||||||
|
|
||||||
result = []
|
result: List = []
|
||||||
server_ids = []
|
server_ids = []
|
||||||
image_ids = []
|
image_ids = []
|
||||||
root_device_ids = []
|
root_device_ids = []
|
||||||
@ -752,13 +752,13 @@ async def list_volume_snapshots(
|
|||||||
)
|
)
|
||||||
except NotFound as ex:
|
except NotFound as ex:
|
||||||
LOG.debug(f"Not found volume snapshot with id '{uuid}': {ex}")
|
LOG.debug(f"Not found volume snapshot with id '{uuid}': {ex}")
|
||||||
return schemas.VolumeSnapshotsResponse(**{"count": 0, "volume_snapshots": []})
|
return schemas.VolumeSnapshotsResponse(count=0, volume_snapshots=[])
|
||||||
if volume_snapshot.project_id != profile.project.id:
|
if volume_snapshot.project_id != profile.project.id:
|
||||||
LOG.debug(
|
LOG.debug(
|
||||||
f"Volume snapshot with id '{uuid}' is in project "
|
f"Volume snapshot with id '{uuid}' is in project "
|
||||||
f"'{volume_snapshot.project_id}', not in '{profile.project.id}'"
|
f"'{volume_snapshot.project_id}', not in '{profile.project.id}'"
|
||||||
)
|
)
|
||||||
return schemas.VolumeSnapshotsResponse(**{"count": 0, "volume_snapshots": []})
|
return schemas.VolumeSnapshotsResponse(count=0, volume_snapshots=[])
|
||||||
snapshot_session = get_system_session()
|
snapshot_session = get_system_session()
|
||||||
search_opts["all_tenants"] = True
|
search_opts["all_tenants"] = True
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from pathlib import PurePath
|
from pathlib import PurePath
|
||||||
from typing import Any, List, Optional, Tuple, Union
|
from typing import Any, Dict, List, Optional, Tuple, Union
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, Form, Header, HTTPException, Request, Response, status
|
from fastapi import APIRouter, Depends, Form, Header, HTTPException, Request, Response, status
|
||||||
from fastapi.responses import RedirectResponse
|
from fastapi.responses import RedirectResponse
|
||||||
@ -173,9 +173,10 @@ async def login(
|
|||||||
regex=constants.INBOUND_HEADER_REGEX,
|
regex=constants.INBOUND_HEADER_REGEX,
|
||||||
),
|
),
|
||||||
) -> schemas.Profile:
|
) -> schemas.Profile:
|
||||||
|
region = credential.region or CONF.openstack.default_region
|
||||||
try:
|
try:
|
||||||
project_scope, unscope_token, default_project_id = await _get_projects_and_unscope_token(
|
project_scope, unscope_token, default_project_id = await _get_projects_and_unscope_token(
|
||||||
region=credential.region,
|
region=region,
|
||||||
domain=credential.domain,
|
domain=credential.domain,
|
||||||
username=credential.username,
|
username=credential.username,
|
||||||
password=credential.password,
|
password=credential.password,
|
||||||
@ -186,13 +187,13 @@ async def login(
|
|||||||
default_project_id = None
|
default_project_id = None
|
||||||
project_scope_token = await get_project_scope_token(
|
project_scope_token = await get_project_scope_token(
|
||||||
keystone_token=unscope_token,
|
keystone_token=unscope_token,
|
||||||
region=credential.region,
|
region=region,
|
||||||
project_id=default_project_id or project_scope[0].id,
|
project_id=default_project_id or project_scope[0].id,
|
||||||
)
|
)
|
||||||
|
|
||||||
profile = await generate_profile(
|
profile = await generate_profile(
|
||||||
keystone_token=project_scope_token,
|
keystone_token=project_scope_token,
|
||||||
region=credential.region,
|
region=region,
|
||||||
)
|
)
|
||||||
|
|
||||||
profile = await _patch_profile(profile, x_openstack_request_id)
|
profile = await _patch_profile(profile, x_openstack_request_id)
|
||||||
@ -218,12 +219,12 @@ async def login(
|
|||||||
response_description="OK",
|
response_description="OK",
|
||||||
)
|
)
|
||||||
async def get_sso(request: Request) -> schemas.SSO:
|
async def get_sso(request: Request) -> schemas.SSO:
|
||||||
sso = {
|
sso: Dict = {
|
||||||
"enable_sso": False,
|
"enable_sso": False,
|
||||||
"protocols": [],
|
"protocols": [],
|
||||||
}
|
}
|
||||||
if CONF.openstack.sso_enabled:
|
if CONF.openstack.sso_enabled:
|
||||||
protocols = []
|
protocols: List = []
|
||||||
|
|
||||||
ks_url = CONF.openstack.keystone_url.rstrip("/")
|
ks_url = CONF.openstack.keystone_url.rstrip("/")
|
||||||
url_scheme = "https" if CONF.default.ssl_enabled else "http"
|
url_scheme = "https" if CONF.default.ssl_enabled else "http"
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
from typing import Dict
|
from typing import Dict, List
|
||||||
|
|
||||||
from fastapi import APIRouter, Depends, HTTPException, status
|
from fastapi import APIRouter, Depends, HTTPException, status
|
||||||
from keystoneauth1.exceptions.http import (
|
from keystoneauth1.exceptions.http import (
|
||||||
@ -113,7 +113,7 @@ async def list_policies(
|
|||||||
LOG.debug("Keystone is not reachable. No privilege to access system scope.")
|
LOG.debug("Keystone is not reachable. No privilege to access system scope.")
|
||||||
target = _generate_target(profile)
|
target = _generate_target(profile)
|
||||||
|
|
||||||
results = []
|
results: List = []
|
||||||
services = constants.SUPPORTED_SERVICE_EPS.keys()
|
services = constants.SUPPORTED_SERVICE_EPS.keys()
|
||||||
for service in services:
|
for service in services:
|
||||||
try:
|
try:
|
||||||
@ -176,7 +176,7 @@ async def check_policies(
|
|||||||
target = _generate_target(profile)
|
target = _generate_target(profile)
|
||||||
target.update(policy_rules.target if policy_rules.target else {})
|
target.update(policy_rules.target if policy_rules.target else {})
|
||||||
try:
|
try:
|
||||||
result = []
|
result: List = []
|
||||||
for policy_rule in policy_rules.rules:
|
for policy_rule in policy_rules.rules:
|
||||||
service = policy_rule.split(":", 1)[0]
|
service = policy_rule.split(":", 1)[0]
|
||||||
rule = policy_rule.split(":", 1)[1]
|
rule = policy_rule.split(":", 1)[1]
|
||||||
|
@ -16,9 +16,9 @@ from __future__ import annotations
|
|||||||
|
|
||||||
import time
|
import time
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
from typing import Any
|
from typing import Any, Union
|
||||||
|
|
||||||
from sqlalchemy import delete, func, insert, select, update
|
from sqlalchemy import Insert, Update, delete, func, insert, select, update
|
||||||
|
|
||||||
from skyline_apiserver.types import Fn
|
from skyline_apiserver.types import Fn
|
||||||
|
|
||||||
@ -41,7 +41,7 @@ def check_db_connected(fn: Fn) -> Any:
|
|||||||
async def check_token(token_id: str) -> bool:
|
async def check_token(token_id: str) -> bool:
|
||||||
count_label = "revoked_count"
|
count_label = "revoked_count"
|
||||||
query = (
|
query = (
|
||||||
select([func.count(RevokedToken.c.uuid).label(count_label)])
|
select(func.count(RevokedToken.c.uuid).label(count_label))
|
||||||
.select_from(RevokedToken)
|
.select_from(RevokedToken)
|
||||||
.where(RevokedToken.c.uuid == token_id)
|
.where(RevokedToken.c.uuid == token_id)
|
||||||
)
|
)
|
||||||
@ -76,7 +76,7 @@ async def purge_revoked_token() -> Any:
|
|||||||
|
|
||||||
@check_db_connected
|
@check_db_connected
|
||||||
async def list_settings() -> Any:
|
async def list_settings() -> Any:
|
||||||
query = select([Settings])
|
query = select(Settings)
|
||||||
db = DB.get()
|
db = DB.get()
|
||||||
async with db.transaction():
|
async with db.transaction():
|
||||||
result = await db.fetch_all(query)
|
result = await db.fetch_all(query)
|
||||||
@ -86,7 +86,7 @@ async def list_settings() -> Any:
|
|||||||
|
|
||||||
@check_db_connected
|
@check_db_connected
|
||||||
async def get_setting(key: str) -> Any:
|
async def get_setting(key: str) -> Any:
|
||||||
query = select([Settings]).where(Settings.c.key == key)
|
query = select(Settings).where(Settings.c.key == key)
|
||||||
db = DB.get()
|
db = DB.get()
|
||||||
async with db.transaction():
|
async with db.transaction():
|
||||||
result = await db.fetch_one(query)
|
result = await db.fetch_one(query)
|
||||||
@ -97,17 +97,17 @@ async def get_setting(key: str) -> Any:
|
|||||||
@check_db_connected
|
@check_db_connected
|
||||||
async def update_setting(key: str, value: Any) -> Any:
|
async def update_setting(key: str, value: Any) -> Any:
|
||||||
get_query = (
|
get_query = (
|
||||||
select([Settings.c.key, Settings.c.value]).where(Settings.c.key == key).with_for_update()
|
select(Settings.c.key, Settings.c.value).where(Settings.c.key == key).with_for_update()
|
||||||
)
|
)
|
||||||
db = DB.get()
|
db = DB.get()
|
||||||
async with db.transaction():
|
async with db.transaction():
|
||||||
is_exist = await db.fetch_one(get_query)
|
is_exist = await db.fetch_one(get_query)
|
||||||
|
stmt: Union[Insert, Update]
|
||||||
if is_exist is None:
|
if is_exist is None:
|
||||||
query = insert(Settings)
|
stmt = insert(Settings).values(key=key, value=value)
|
||||||
await db.execute(query, {"key": key, "value": value})
|
|
||||||
else:
|
else:
|
||||||
query = update(Settings).where(Settings.c.key == key)
|
stmt = update(Settings).where(Settings.c.key == key).values(value=value)
|
||||||
await db.execute(query, {"value": value})
|
await db.execute(stmt)
|
||||||
result = await db.fetch_one(get_query)
|
result = await db.fetch_one(get_query)
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
@ -24,7 +24,7 @@ from skyline_apiserver.types import constants
|
|||||||
|
|
||||||
|
|
||||||
class Credential(BaseModel):
|
class Credential(BaseModel):
|
||||||
region: str = Field(..., description="Credential user region")
|
region: Optional[str] = Field(None, description="Credential identity service region")
|
||||||
domain: str = Field(..., description="Credential user domain")
|
domain: str = Field(..., description="Credential user domain")
|
||||||
username: str = Field(..., description="Credential username")
|
username: str = Field(..., description="Credential username")
|
||||||
password: str = Field(..., description="Credential password for user")
|
password: str = Field(..., description="Credential password for user")
|
||||||
|
@ -79,7 +79,7 @@ class TestOpt:
|
|||||||
{
|
{
|
||||||
"name": FAKER.text.word(),
|
"name": FAKER.text.word(),
|
||||||
"description": FAKER.text.word(),
|
"description": FAKER.text.word(),
|
||||||
"schema": object,
|
"schema": RuntimeError,
|
||||||
},
|
},
|
||||||
RuntimeError,
|
RuntimeError,
|
||||||
),
|
),
|
||||||
|
@ -2229,7 +2229,6 @@
|
|||||||
"Credential": {
|
"Credential": {
|
||||||
"title": "Credential",
|
"title": "Credential",
|
||||||
"required": [
|
"required": [
|
||||||
"region",
|
|
||||||
"domain",
|
"domain",
|
||||||
"username",
|
"username",
|
||||||
"password"
|
"password"
|
||||||
@ -2239,7 +2238,7 @@
|
|||||||
"region": {
|
"region": {
|
||||||
"title": "Region",
|
"title": "Region",
|
||||||
"type": "string",
|
"type": "string",
|
||||||
"description": "Credential user region"
|
"description": "Credential identity service region"
|
||||||
},
|
},
|
||||||
"domain": {
|
"domain": {
|
||||||
"title": "Domain",
|
"title": "Domain",
|
||||||
|
15
tox.ini
15
tox.ini
@ -2,19 +2,17 @@
|
|||||||
minversion = 3.18.0
|
minversion = 3.18.0
|
||||||
requires = virtualenv>=20.4.2
|
requires = virtualenv>=20.4.2
|
||||||
skipsdist = True
|
skipsdist = True
|
||||||
# python runtimes: https://governance.openstack.org/tc/reference/project-testing-interface.html#tested-runtimes
|
envlist = pep8,py3,functional
|
||||||
envlist = pep8,py38,functional
|
|
||||||
# this allows tox to infer the base python from the environment name
|
# this allows tox to infer the base python from the environment name
|
||||||
# and override any basepython configured in this file
|
# and override any basepython configured in this file
|
||||||
ignore_basepython_conflict=true
|
ignore_basepython_conflict=true
|
||||||
|
|
||||||
[testenv]
|
[testenv]
|
||||||
basepython = python3
|
usedevelop = true
|
||||||
setenv = VIRTUAL_ENV={envdir}
|
setenv =
|
||||||
PYTHONWARNINGS=default::DeprecationWarning
|
VIRTUAL_ENV={envdir}
|
||||||
OS_STDOUT_CAPTURE=1
|
OS_STDOUT_CAPTURE=1
|
||||||
OS_STDERR_CAPTURE=1
|
OS_STDERR_CAPTURE=1
|
||||||
usedevelop = True
|
|
||||||
|
|
||||||
deps =
|
deps =
|
||||||
-c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
|
-c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master}
|
||||||
@ -68,10 +66,9 @@ commands =
|
|||||||
black --check --diff --color skyline_apiserver --line-length 98
|
black --check --diff --color skyline_apiserver --line-length 98
|
||||||
flake8 {posargs} .
|
flake8 {posargs} .
|
||||||
|
|
||||||
[testenv:py38]
|
[testenv:py{3,38,39,310,311}]
|
||||||
description =
|
description =
|
||||||
Run pytest.
|
Run pytest.
|
||||||
envdir = {toxworkdir}/shared
|
|
||||||
deps =
|
deps =
|
||||||
{[testenv]deps}
|
{[testenv]deps}
|
||||||
extras =
|
extras =
|
||||||
@ -109,6 +106,7 @@ commands =
|
|||||||
description =
|
description =
|
||||||
Generate swagger files.
|
Generate swagger files.
|
||||||
envdir = {toxworkdir}/shared
|
envdir = {toxworkdir}/shared
|
||||||
|
allowlist_externals = skyline-swagger-generator
|
||||||
commands =
|
commands =
|
||||||
skyline-swagger-generator -o swagger.json
|
skyline-swagger-generator -o swagger.json
|
||||||
|
|
||||||
@ -116,6 +114,7 @@ commands =
|
|||||||
description =
|
description =
|
||||||
Generate config files.
|
Generate config files.
|
||||||
envdir = {toxworkdir}/shared
|
envdir = {toxworkdir}/shared
|
||||||
|
allowlist_externals = skyline-sample-config-generator
|
||||||
commands =
|
commands =
|
||||||
skyline-sample-config-generator -o etc/skyline.yaml.sample
|
skyline-sample-config-generator -o etc/skyline.yaml.sample
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user