dry-run: add configuration object to control dry-run behavior

Oftentimes, we want to simulate a specific behavior of the application
when running in dry-run mode. To do so, we use either command line
parameters or environment variables.

This patch introduces a configuration object for dry-run executions
only. The object can be automatically loaded from a JSON file specified
via the --dry-run-config CLI argument.

Such a configuration object should help us cover way more test cases.
Going forward, I would like to use this object for things like:

 * drivers - to instruct Subiquity what third-party drivers it should
   suggest ; or if Subiquity should run ubuntu-drivers on the host
   instead.
 * ubuntu-pro - to specify the ua-contracts test environment URL - or
   predefined automatic replies for the server
 * to assume that /var/lib/snapd/seed/systems directory exists on the
   source (or not).
 * to specify the Ubuntu release that is returned by lsb_release ; can
   be used to test behavior on LTS vs non LTS releases.
 *
 * ...

Signed-off-by: Olivier Gayot <olivier.gayot@canonical.com>
This commit is contained in:
Olivier Gayot 2022-11-24 17:31:17 +01:00
parent 9eea4cf220
commit 93f06eeb0f
4 changed files with 32 additions and 0 deletions

View File

@ -159,6 +159,7 @@ for answers in examples/answers*.yaml; do
if echo $answers|grep -vq system-setup; then
config=$(sed -n 's/^#machine-config: \(.*\)/\1/p' $answers || true)
catalog=$(sed -n 's/^#source-catalog: \(.*\)/\1/p' $answers || true)
dr_config=$(sed -n 's/^#dr-config: \(.*\)/\1/p' "$answers" || true)
if [ -z "$config" ]; then
config=examples/simple.json
fi
@ -170,6 +171,9 @@ for answers in examples/answers*.yaml; do
if [ -n "$serial" ]; then
opts+=(--serial)
fi
if [ -n "$dr_config" ]; then
opts+=(--dry-run-config "$dr_config")
fi
# The --foreground is important to avoid subiquity getting SIGTTOU-ed.
LANG=C.UTF-8 timeout --foreground 60 \
python3 -m subiquity.cmd.tui < "$tty" \

View File

@ -62,6 +62,7 @@ def make_server_args_parser():
parser.add_argument('--dry-run', action='store_true',
dest='dry_run',
help='menu-only, do not call installer function')
parser.add_argument('--dry-run-config', type=argparse.FileType())
parser.add_argument('--socket')
parser.add_argument('--machine-config', metavar='CONFIG',
dest='machine_config',
@ -118,6 +119,7 @@ def main():
# setup_environment sets $APPORT_DATA_DIR which must be set before
# apport is imported, which is done by this import:
from subiquity.server.server import SubiquityServer
from subiquity.server.dryrun import DRConfig
parser = make_server_args_parser()
opts = parser.parse_args(sys.argv[1:])
if opts.storage_version is None:
@ -125,9 +127,16 @@ def main():
'subiquity-storage-version', 1))
logdir = LOGDIR
if opts.dry_run:
if opts.dry_run_config:
dr_cfg = DRConfig.load(opts.dry_run_config)
else:
dr_cfg = DRConfig()
if opts.snaps_from_examples is None:
opts.snaps_from_examples = True
logdir = opts.output_base
else:
dr_cfg = None
if opts.socket is None:
if opts.dry_run:
opts.socket = opts.output_base + '/socket'
@ -157,6 +166,7 @@ def main():
async def run_with_loop():
server = SubiquityServer(opts, block_log_dir)
server.dr_cfg = dr_cfg
server.note_file_for_apport(
"InstallerServerLog", logfiles['debug'])
server.note_file_for_apport(

View File

@ -13,6 +13,10 @@
# 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 yaml
import attr
class DryRunController:
@ -22,3 +26,15 @@ class DryRunController:
async def crash_GET(self) -> None:
1/0
@attr.s(auto_attribs=True)
class DRConfig:
""" Configuration for dry-run-only executions.
All variables here should have default values ; to indicate the behavior we
want by default in dry-run mode. """
@classmethod
def load(cls, stream):
data = yaml.safe_load(stream)
return cls(**data)

View File

@ -68,6 +68,7 @@ from subiquity.models.subiquity import (
ModelNames,
SubiquityModel,
)
from subiquity.server.dryrun import DRConfig
from subiquity.server.controller import SubiquityController
from subiquity.server.geoip import (
GeoIP,
@ -274,6 +275,7 @@ class SubiquityServer(Application):
def __init__(self, opts, block_log_dir):
super().__init__(opts)
self.dr_cfg: Optional[DRConfig] = None
self.set_source_variant(self.supported_variants[0])
self.block_log_dir = block_log_dir
self.cloud = None