Merge pull request #1058 from dbungert/fr-1187-apt-components-v2
apt: disable_components
This commit is contained in:
commit
cde7150446
|
@ -281,6 +281,19 @@
|
|||
},
|
||||
"sources": {
|
||||
"type": "object"
|
||||
},
|
||||
"disable_components": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"universe",
|
||||
"multiverse",
|
||||
"restricted",
|
||||
"contrib",
|
||||
"non-free"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -60,7 +60,6 @@ class API:
|
|||
"""The API offered by the subiquity installer process."""
|
||||
identity = simple_endpoint(IdentityData)
|
||||
locale = simple_endpoint(str)
|
||||
mirror = simple_endpoint(str)
|
||||
proxy = simple_endpoint(str)
|
||||
ssh = simple_endpoint(SSHData)
|
||||
updates = simple_endpoint(str)
|
||||
|
@ -96,6 +95,14 @@ class API:
|
|||
class ssh_info:
|
||||
def GET() -> Optional[LiveSessionSSHInfo]: ...
|
||||
|
||||
class free_only:
|
||||
def GET() -> bool: ...
|
||||
|
||||
def POST(enable: bool) -> None:
|
||||
"""Enable or disable free-only mode. Currently only controlls
|
||||
the list of components. free-only choice must be made prior to
|
||||
confirmation of filesystem changes"""
|
||||
|
||||
class errors:
|
||||
class wait:
|
||||
def GET(error_ref: ErrorReportRef) -> ErrorReportRef:
|
||||
|
@ -297,6 +304,14 @@ class API:
|
|||
class shutdown:
|
||||
def POST(mode: ShutdownMode, immediate: bool = False): ...
|
||||
|
||||
class mirror:
|
||||
def GET() -> str: ...
|
||||
def POST(data: Payload[str]): ...
|
||||
|
||||
class disable_components:
|
||||
def GET() -> List[str]: ...
|
||||
def POST(data: Payload[List[str]]): ...
|
||||
|
||||
|
||||
class LinkAction(enum.Enum):
|
||||
NEW = enum.auto()
|
||||
|
|
|
@ -51,12 +51,18 @@ class MirrorModel(object):
|
|||
self.config = copy.deepcopy(DEFAULT)
|
||||
self.architecture = get_architecture()
|
||||
self.default_mirror = self.get_mirror()
|
||||
self.disable_components = set()
|
||||
|
||||
def is_default(self):
|
||||
def get_config(self):
|
||||
config = copy.deepcopy(self.config)
|
||||
config['disable_components'] = list(self.disable_components)
|
||||
return config
|
||||
|
||||
def mirror_is_default(self):
|
||||
return self.get_mirror() == self.default_mirror
|
||||
|
||||
def set_country(self, cc):
|
||||
if not self.is_default():
|
||||
if not self.mirror_is_default():
|
||||
return
|
||||
uri = self.get_mirror()
|
||||
parsed = parse.urlparse(uri)
|
||||
|
|
|
@ -42,3 +42,13 @@ class TestMirrorModel(unittest.TestCase):
|
|||
model.set_mirror("http://mymirror.invalid/")
|
||||
model.set_country("CC")
|
||||
self.assertEqual(model.get_mirror(), "http://mymirror.invalid/")
|
||||
|
||||
def test_default_disable_components(self):
|
||||
config = MirrorModel().get_config()
|
||||
self.assertEqual([], config['disable_components'])
|
||||
|
||||
def test_set_disable_components(self):
|
||||
model = MirrorModel()
|
||||
model.disable_components = set(['universe'])
|
||||
config = model.get_config()
|
||||
self.assertEqual(['universe'], config['disable_components'])
|
||||
|
|
|
@ -104,7 +104,7 @@ class AptConfigurer:
|
|||
config_upper = await self.setup_overlay(self.source, self.configured)
|
||||
|
||||
config = {
|
||||
'apt': self.app.base_model.mirror.config,
|
||||
'apt': self.app.base_model.mirror.get_config(),
|
||||
}
|
||||
config_location = os.path.join(
|
||||
self.app.root, 'var/log/installer/subiquity-curtin-apt.conf')
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
import asyncio
|
||||
import copy
|
||||
import logging
|
||||
from typing import List
|
||||
|
||||
from curtin.config import merge_config
|
||||
|
||||
|
@ -40,8 +41,16 @@ class MirrorController(SubiquityController):
|
|||
'primary': {'type': 'array'},
|
||||
'geoip': {'type': 'boolean'},
|
||||
'sources': {'type': 'object'},
|
||||
},
|
||||
'disable_components': {
|
||||
'type': 'array',
|
||||
'items': {
|
||||
'type': 'string',
|
||||
'enum': ['universe', 'multiverse', 'restricted',
|
||||
'contrib', 'non-free']
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
model_name = "mirror"
|
||||
|
||||
def __init__(self, app):
|
||||
|
@ -55,7 +64,7 @@ class MirrorController(SubiquityController):
|
|||
return
|
||||
geoip = data.pop('geoip', True)
|
||||
merge_config(self.model.config, data)
|
||||
self.geoip_enabled = geoip and self.model.is_default()
|
||||
self.geoip_enabled = geoip and self.model.mirror_is_default()
|
||||
|
||||
@with_context()
|
||||
async def apply_autoinstall_config(self, context):
|
||||
|
@ -89,3 +98,9 @@ class MirrorController(SubiquityController):
|
|||
async def POST(self, data: str):
|
||||
self.model.set_mirror(data)
|
||||
await self.configured()
|
||||
|
||||
async def disable_components_GET(self) -> List[str]:
|
||||
return list(self.model.disable_components)
|
||||
|
||||
async def disable_components_POST(self, data: List[str]):
|
||||
self.model.disable_components = set(data)
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
# Copyright 2021 Canonical, Ltd.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU Affero General Public License as
|
||||
# published by the Free Software Foundation, either version 3 of the
|
||||
# License, or (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU Affero General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU Affero General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import jsonschema
|
||||
import unittest
|
||||
|
||||
from subiquity.server.controllers.mirror import MirrorController
|
||||
|
||||
|
||||
class TestMirrorSchema(unittest.TestCase):
|
||||
def validate(self, data):
|
||||
jsonschema.validate(data, MirrorController.autoinstall_schema)
|
||||
|
||||
def test_empty(self):
|
||||
self.validate({})
|
||||
|
||||
def test_disable_components(self):
|
||||
self.validate({'disable_components': ['universe']})
|
||||
|
||||
def test_no_disable_main(self):
|
||||
with self.assertRaises(jsonschema.ValidationError):
|
||||
self.validate({'disable_components': ['main']})
|
||||
|
||||
def test_no_disable_random_junk(self):
|
||||
with self.assertRaises(jsonschema.ValidationError):
|
||||
self.validate({'disable_components': ['not-a-component']})
|
|
@ -86,6 +86,7 @@ class MetaController:
|
|||
def __init__(self, app):
|
||||
self.app = app
|
||||
self.context = app.context.child("Meta")
|
||||
self.free_only = False
|
||||
|
||||
async def status_GET(self, cur: Optional[ApplicationState] = None) \
|
||||
-> ApplicationStatus:
|
||||
|
@ -158,6 +159,18 @@ class MetaController:
|
|||
ips=ips,
|
||||
host_key_fingerprints=host_fingerprints)
|
||||
|
||||
async def free_only_GET(self) -> bool:
|
||||
return self.free_only
|
||||
|
||||
async def free_only_POST(self, enable: bool) -> None:
|
||||
self.free_only = enable
|
||||
to_disable = {'restricted', 'multiverse'}
|
||||
if enable:
|
||||
# enabling free only mode means disabling components
|
||||
self.app.base_model.mirror.disable_components |= to_disable
|
||||
else:
|
||||
self.app.base_model.mirror.disable_components -= to_disable
|
||||
|
||||
|
||||
def get_installer_password_from_cloudinit_log():
|
||||
try:
|
||||
|
|
|
@ -676,6 +676,25 @@ class TestInfo(TestAPI):
|
|||
self.assertEqual('/dev/sda', sda['path'])
|
||||
|
||||
|
||||
class TestFree(TestAPI):
|
||||
@timeout(5)
|
||||
async def test_free_only(self):
|
||||
async with start_server('examples/simple.json') as inst:
|
||||
await inst.post('/meta/free_only', enable=True)
|
||||
components = await inst.get('/mirror/disable_components')
|
||||
components.sort()
|
||||
self.assertEqual(['multiverse', 'restricted'], components)
|
||||
|
||||
@timeout(5)
|
||||
async def test_not_free_only(self):
|
||||
async with start_server('examples/simple.json') as inst:
|
||||
comps = ['universe', 'multiverse']
|
||||
await inst.post('/mirror/disable_components', comps)
|
||||
await inst.post('/meta/free_only', enable=False)
|
||||
components = await inst.get('/mirror/disable_components')
|
||||
self.assertEqual(['universe'], components)
|
||||
|
||||
|
||||
class TestRegression(TestAPI):
|
||||
@timeout()
|
||||
async def test_edit_not_trigger_boot_device(self):
|
||||
|
|
Loading…
Reference in New Issue