func bootstrapToRootDir(sto Store, model *asserts.Model, opts *Options, local *localInfos) error { // FIXME: try to avoid doing this if opts.RootDir != "" { dirs.SetRootDir(opts.RootDir) defer dirs.SetRootDir("/") } // sanity check target if osutil.FileExists(dirs.SnapStateFile) { return fmt.Errorf("cannot bootstrap over existing system") } // TODO: developer database in home or use snapd (but need // a bit more API there, potential issues when crossing stores/series) db, err := asserts.OpenDatabase(&asserts.DatabaseConfig{ Backstore: asserts.NewMemoryBackstore(), Trusted: sysdb.Trusted(), }) if err != nil { return err } f := makeFetcher(sto, &DownloadOptions{}, db) if err := f.Save(model); err != nil { if !osutil.GetenvBool("UBUNTU_IMAGE_SKIP_COPY_UNVERIFIED_MODEL") { return fmt.Errorf("cannot fetch and check prerequisites for the model assertion: %v", err) } else { logger.Noticef("Cannot fetch and check prerequisites for the model assertion, it will not be copied into the image: %v", err) f.addedRefs = nil } } // put snaps in place if err := os.MkdirAll(dirs.SnapBlobDir, 0755); err != nil { return err } snapSeedDir := filepath.Join(dirs.SnapSeedDir, "snaps") assertSeedDir := filepath.Join(dirs.SnapSeedDir, "assertions") dlOpts := &DownloadOptions{ TargetDir: snapSeedDir, Channel: opts.Channel, DevMode: false, // XXX: should this be true? } for _, d := range []string{snapSeedDir, assertSeedDir} { if err := os.MkdirAll(d, 0755); err != nil { return err } } snaps := []string{} // core,kernel,gadget first snaps = append(snaps, local.PreferLocal(defaultCore)) snaps = append(snaps, local.PreferLocal(model.Kernel())) snaps = append(snaps, local.PreferLocal(model.Gadget())) // then required and the user requested stuff for _, snapName := range model.RequiredSnaps() { snaps = append(snaps, local.PreferLocal(snapName)) } snaps = append(snaps, opts.Snaps...) seen := make(map[string]bool) downloadedSnapsInfo := map[string]*snap.Info{} var seedYaml snap.Seed for _, snapName := range snaps { name := local.Name(snapName) if seen[name] { fmt.Fprintf(Stdout, "%s already prepared, skipping\n", name) continue } if name != snapName { fmt.Fprintf(Stdout, "Copying %q (%s)\n", snapName, name) } else { fmt.Fprintf(Stdout, "Fetching %s\n", snapName) } fn, info, err := acquireSnap(sto, name, dlOpts, local) if err != nil { return err } seen[name] = true // if it comes from the store fetch the snap assertions too // TODO: support somehow including available assertions // also for local snaps if info.SnapID != "" { err = FetchAndCheckSnapAssertions(fn, info, f, db) if err != nil { return err } } typ := info.Type // kernel/os are required for booting if typ == snap.TypeKernel || typ == snap.TypeOS { dst := filepath.Join(dirs.SnapBlobDir, filepath.Base(fn)) if err := osutil.CopyFile(fn, dst, 0); err != nil { return err } // store the snap.Info for kernel/os so // that the bootload can DTRT downloadedSnapsInfo[dst] = info } // set seed.yaml seedYaml.Snaps = append(seedYaml.Snaps, &snap.SeedSnap{ Name: info.Name(), SnapID: info.SnapID, // cross-ref Channel: info.Channel, File: filepath.Base(fn), DevMode: info.NeedsDevMode(), // no assertions for this snap were put in the seed Unasserted: info.SnapID == "", }) } for _, aRef := range f.addedRefs { var afn string // the names don't matter in practice as long as they don't conflict if aRef.Type == asserts.ModelType { afn = "model" } else { afn = fmt.Sprintf("%s.%s", strings.Join(aRef.PrimaryKey, ","), aRef.Type.Name) } a, err := aRef.Resolve(db.Find) if err != nil { return fmt.Errorf("internal error: lost saved assertion") } err = ioutil.WriteFile(filepath.Join(assertSeedDir, afn), asserts.Encode(a), 0644) if err != nil { return err } } // TODO: add the refs as an assertions list of maps section to seed.yaml seedFn := filepath.Join(dirs.SnapSeedDir, "seed.yaml") if err := seedYaml.Write(seedFn); err != nil { return fmt.Errorf("cannot write seed.yaml: %s", err) } // now do the bootloader stuff if err := partition.InstallBootConfig(opts.GadgetUnpackDir); err != nil { return err } if err := setBootvars(downloadedSnapsInfo); err != nil { return err } // and the cloud-init things if err := installCloudConfig(opts.GadgetUnpackDir); err != nil { return err } return nil }