Skip to content
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

autoinstall! #625

Merged
merged 12 commits into from
Feb 4, 2020
Merged

autoinstall! #625

merged 12 commits into from
Feb 4, 2020

Conversation

mwhudson
Copy link
Collaborator

The branch implements the first cut of autoinstall for subiquity. What is there:

  • support for fully non-interactive and partially interactive installs
  • support for early and late commands

What is not there:

  • any sort of decent error handling
    • this includes any sort of sensible data validation. Get your autoinstall.yaml wrong and things will just blow up in a mess
  • support for the packages, debconf-selections, snaplist, user-data sections of the config file
  • sensible behaviour when more than one subiquity is running (e.g. on tty1 and serial)
  • generation of an autoinstall file at the end of install
  • any kind of sensible storage configuration, it just supports lvm or direct installation on the largest disk in the system

But I have plans for all of these things :)

@mwhudson
Copy link
Collaborator Author

To test this, try something like:

$ wget https://people.canonical.com/~mwh/subiquity_autoinstall.snap
$ sudo ~/src/subiquity/scripts/inject-subiquity-snap.sh \
    -c 'cp /home/mwhudson/src/subiquity/examples/autoinstall.yaml new_installer/' \
    ~/isos/focal-live-server-amd64.iso  subiquity_autoinstall.snap ~/isos/custom.iso

Copy link
Contributor

@raharper raharper left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see anything that would affect existing interactive installs. I've left some review comments for the autoinstall bits.

from curtin.config import merge_config


def merge_autoinstall_configs(source_paths, target_path):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The order of the source_paths controls what the final config will look like due to how merge_config works with dictionaries and lists. I suggest documenting that behavior. Additionally it's important to retain the order of the configs, or the order of the expected precedence of autoinstall file locations. For example, would you expect the initrd-autoinstall.yaml to override other paths?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, we need to make some decisions and document how all this works. I'm going to post to discource, probably tomorrow, about this.

for path in source_paths:
with open(path) as fp:
c = yaml.safe_load(fp)
merge_config(config, c)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would include a comment or a value in the merged config indicating the source yamls and the order of their merging so that when debugging a resulting autoinstall we can sort out how a value was modified after merging.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good idea.

self.load_autoinstall_data(
app.autoinstall_config.get(
self.autoinstall_key,
self.autoinstall_default))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pre-populating the autoinstall_config dictionary with keys which may be None (due to the default value being set) means that checking for presence of a key w.r.t to whether subiquity uses a default value or the user provided can be difficult.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, there are a few different ways of looking at this. This API seems reasonably ok. load_install_data has access to self.app.autoinstall_config if it needs it...

self.cmds = data

async def run(self):
for i, cmd in enumerate(self.cmds):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to expect that self.cmds will return a list like object; if an autoinstall config does not have any early commands, wont the load_autoinstall_data from the base controller set this to a None which would fail to enumerate?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"autoinstall_default = []" handles this

class CmdListController(NoUIController):

autoinstall_default = []
cmds = ()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a list instead of tuple?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps autoinstall_default should be ()? I don't think it matters a great deal although having them different is indeed odd.

if data is None:
return
self.ai_data = data
if 'mirror' in data:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check fails if users don't provide 'mirror' info in the autoinstall, as the loader sets data['mirror'] = None.
Either the loader shouldn't set default value to None so you can do key checks like this, or this needs to be
if data['mirror'] is not None:

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't understand your comment but I don't think it is correct.

This all needs to change anyway because I want to redo how mirrors are described, see #626 and https://wiki.ubuntu.com/FoundationsTeam/AutomatedServerInstalls/ConfigReference?action=diff&rev2=40&rev1=38

self.active = self.interactive()

def load_autoinstall_data(self, data):
if data is not None and data.get('refresh'):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

data.get('refresh-installer') or 'refresh' ?

Copy link
Collaborator Author

@mwhudson mwhudson Jan 30, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'refresh'. The format of the yaml is:

refresh-installer:
   refresh: yes

self.model.authorized_keys = self.autoinstall_data.get(
'authorized-keys', [])
self.model.pwauth = self.autoinstall_data.get(
'allow-pw', not self.model.authorized_keys)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Policy decision? Is this documented that one cannot set password and import ssh keys?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Uh, the idea is only that the default of allow-pw is False iff a key is supplied. And I think that's what this code does but I've been thinking about udev too much to be sure.

@@ -25,7 +25,11 @@

class WelcomeController(SubiquityController):

model_name = "locale"
autoinstall_key = model_name = "locale"
autoinstall_default = 'en_US.UTF-8'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C.UTF-8 is the default in cloud-images;

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. subiquity is more targeted towards humans, but maybe not for autoinstall cases. Easy to change later in any case.

realname: ''
username: ubuntu
password: '$6$wdAcoXrU039hKYPd$508Qvbe7ObUnxoj15DRCkzC3qO7edjH0VV7BPNRDYK4QR8ofJaEEF2heacn0QgD.f8pO8SNp83XNdWG6tocBM1'
hostname: ubuntu
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this example include all possible keys? We might want to expand this with comments or examples of other accepted autoinstall keys.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No it certainly does not, and yes providing a more complete example would make sense.

@mwhudson
Copy link
Collaborator Author

I don't see anything that would affect existing interactive installs. I've left some review comments for the autoinstall bits.

Thanks for the review. I don't plan to make many changes to this branch in response to them, but I'll keep them in mind for later. I'll probably make some tweaks and merge this tomorrow.

@mwhudson mwhudson merged commit e2410a0 into canonical:master Feb 4, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants