Add a "quick" command for fast booting of various installers.

The quick commands are just aliases for the `boot` command with
various pre-cooked kernel/initrds/cmdline combinations. Currently
supported are Debian, Ubuntu, Fedora and Centos. There's also code
for Arch, but it's currently disabled as I'm failing to get the
HTTP rootfs to work.
This commit is contained in:
David Anderson 2017-02-22 04:21:07 -08:00
parent 0d99a2ad1f
commit 34da4bdc44
3 changed files with 291 additions and 11 deletions

View File

@ -63,6 +63,5 @@ var bootCmd = &cobra.Command{
func init() { func init() {
rootCmd.AddCommand(bootCmd) rootCmd.AddCommand(bootCmd)
serverConfigFlags(bootCmd) serverConfigFlags(bootCmd)
bootCmd.Flags().String("cmdline", "", "Kernel commandline arguments") staticConfigFlags(bootCmd)
bootCmd.Flags().String("bootmsg", "", "Message to print on machines before booting")
} }

View File

@ -67,6 +67,11 @@ func todo(msg string, args ...interface{}) {
fatalf("TODO: "+msg, args...) fatalf("TODO: "+msg, args...)
} }
func staticConfigFlags(cmd *cobra.Command) {
cmd.Flags().String("cmdline", "", "Kernel commandline arguments")
cmd.Flags().String("bootmsg", "", "Message to print on machines before booting")
}
func serverConfigFlags(cmd *cobra.Command) { func serverConfigFlags(cmd *cobra.Command) {
cmd.Flags().BoolP("debug", "d", false, "Log more things that aren't directly related to booting a recognized client") cmd.Flags().BoolP("debug", "d", false, "Log more things that aren't directly related to booting a recognized client")
cmd.Flags().BoolP("log-timestamps", "t", false, "Add a timestamp to each log line") cmd.Flags().BoolP("log-timestamps", "t", false, "Add a timestamp to each log line")
@ -92,6 +97,40 @@ func mustFile(path string) []byte {
return bs return bs
} }
func staticFromFlags(cmd *cobra.Command, kernel string, initrds []string, extraCmdline string) *pixiecore.Server {
cmdline, err := cmd.Flags().GetString("cmdline")
if err != nil {
fatalf("Error reading flag: %s", err)
}
bootmsg, err := cmd.Flags().GetString("bootmsg")
if err != nil {
fatalf("Error reading flag: %s", err)
}
if extraCmdline != "" {
cmdline = fmt.Sprintf("%s %s", extraCmdline, cmdline)
}
spec := &pixiecore.Spec{
Kernel: pixiecore.ID(kernel),
Cmdline: cmdline,
Message: bootmsg,
}
for _, initrd := range initrds {
spec.Initrd = append(spec.Initrd, pixiecore.ID(initrd))
}
booter, err := pixiecore.StaticBooter(spec)
if err != nil {
fatalf("Couldn't make static booter: %s", err)
}
s := serverFromFlags(cmd)
s.Booter = booter
return s
}
func serverFromFlags(cmd *cobra.Command) *pixiecore.Server { func serverFromFlags(cmd *cobra.Command) *pixiecore.Server {
debug, err := cmd.Flags().GetBool("debug") debug, err := cmd.Flags().GetBool("debug")
if err != nil { if err != nil {

View File

@ -14,7 +14,12 @@
package cli package cli
import "github.com/spf13/cobra" import (
"fmt"
"strings"
"github.com/spf13/cobra"
)
var quickCmd = &cobra.Command{ var quickCmd = &cobra.Command{
Use: "quick recipe [settings...]", Use: "quick recipe [settings...]",
@ -24,17 +29,254 @@ you having to find the kernels and ramdisks for popular OSes.
TODO: better help here TODO: better help here
`, `,
Run: func(cmd *cobra.Command, args []string) { }
if len(args) < 1 {
fatalf("you must specify at least a recipe") func debianRecipe(parent *cobra.Command) {
} versions := []string{
recipe := args[0] "oldstable",
todo("run in quick mode with recipe=%s", recipe) "stable",
}, "testing",
"unstable",
"wheezy",
"jessie",
"stretch",
"sid",
}
debCmd := &cobra.Command{
Use: "debian version",
Short: "Boot a Debian installer",
Long: fmt.Sprintf("Boot a Debian installer for the given version (one of %s)", strings.Join(versions, ",")),
Run: func(cmd *cobra.Command, args []string) {
if len(args) < 1 {
fatalf("you must specify a Debian version")
}
var version string
for _, v := range versions {
if args[0] == v {
version = v
break
}
}
if version == "" {
fatalf("Unknown Debian version %q", version)
}
arch, err := cmd.Flags().GetString("arch")
if err != nil {
fatalf("Error reading flag: %s", err)
}
mirror, err := cmd.Flags().GetString("mirror")
if err != nil {
fatalf("Error reading flag: %s", err)
}
kernel := fmt.Sprintf("%s/dists/%s/main/installer-%s/current/images/netboot/debian-installer/%s/linux", mirror, version, arch, arch)
initrd := fmt.Sprintf("%s/dists/%s/main/installer-%s/current/images/netboot/debian-installer/%s/initrd.gz", mirror, version, arch, arch)
fmt.Println(staticFromFlags(cmd, kernel, []string{initrd}, "").Serve())
},
}
debCmd.Flags().String("arch", "amd64", "CPU architecture of the Debian installer files")
debCmd.Flags().String("mirror", "https://mirrors.kernel.org/debian", "Root of the debian mirror to use")
serverConfigFlags(debCmd)
staticConfigFlags(debCmd)
parent.AddCommand(debCmd)
}
func ubuntuRecipe(parent *cobra.Command) {
versions := []string{
"precise",
"trusty",
"vivid",
"wily",
"xenial",
"yakkety",
"zesty",
}
ubuntuCmd := &cobra.Command{
Use: "ubuntu version",
Short: "Boot an Ubuntu installer",
Long: fmt.Sprintf("Boot an Ubuntu installer for the given version (one of %s)", strings.Join(versions, ",")),
Run: func(cmd *cobra.Command, args []string) {
if len(args) < 1 {
fatalf("you must specify an Ubuntu version")
}
var version string
for _, v := range versions {
if args[0] == v {
version = v
break
}
}
if version == "" {
fatalf("Unknown Ubuntu version %q", version)
}
arch, err := cmd.Flags().GetString("arch")
if err != nil {
fatalf("Error reading flag: %s", err)
}
mirror, err := cmd.Flags().GetString("mirror")
if err != nil {
fatalf("Error reading flag: %s", err)
}
kernel := fmt.Sprintf("%s/dists/%s/main/installer-%s/current/images/netboot/ubuntu-installer/%s/linux", mirror, version, arch, arch)
initrd := fmt.Sprintf("%s/dists/%s/main/installer-%s/current/images/netboot/ubuntu-installer/%s/initrd.gz", mirror, version, arch, arch)
fmt.Println(staticFromFlags(cmd, kernel, []string{initrd}, "").Serve())
},
}
ubuntuCmd.Flags().String("arch", "amd64", "CPU architecture of the Ubuntu installer files")
ubuntuCmd.Flags().String("mirror", "https://mirrors.kernel.org/ubuntu", "Root of the ubuntu mirror to use")
serverConfigFlags(ubuntuCmd)
staticConfigFlags(ubuntuCmd)
parent.AddCommand(ubuntuCmd)
}
func fedoraRecipe(parent *cobra.Command) {
versions := []string{
"22",
"23",
"24",
"25",
}
fedoraCmd := &cobra.Command{
Use: "fedora version",
Short: "Boot a Fedora installer",
Long: fmt.Sprintf(`Boot a Fedora installer for the given version (one of %s)`, strings.Join(versions, ",")),
Run: func(cmd *cobra.Command, args []string) {
if len(args) < 1 {
fatalf("you must specify a Fedora version")
}
var version string
for _, v := range versions {
if args[0] == v {
version = v
break
}
}
if version == "" {
fatalf("Unknown Fedora version %q", version)
}
arch, err := cmd.Flags().GetString("arch")
if err != nil {
fatalf("Error reading flag: %s", err)
}
mirror, err := cmd.Flags().GetString("mirror")
if err != nil {
fatalf("Error reading flag: %s", err)
}
kernel := fmt.Sprintf("%s/releases/%s/Server/%s/os/images/pxeboot/vmlinuz", mirror, version, arch)
initrd := fmt.Sprintf("%s/releases/%s/Server/%s/os/images/pxeboot/initrd.img", mirror, version, arch)
stage2 := fmt.Sprintf("inst.stage2=%s/releases/%s/Server/%s/os/", mirror, version, arch)
fmt.Println(staticFromFlags(cmd, kernel, []string{initrd}, stage2).Serve())
},
}
fedoraCmd.Flags().String("arch", "x86_64", "CPU architecture of the Fedora installer files")
// TODO: workstation/server variant
fedoraCmd.Flags().String("mirror", "https://mirrors.kernel.org/fedora", "Root of the fedora mirror to use")
serverConfigFlags(fedoraCmd)
staticConfigFlags(fedoraCmd)
parent.AddCommand(fedoraCmd)
}
func centosRecipe(parent *cobra.Command) {
versions := []string{
"5",
"6",
"7",
}
centosCmd := &cobra.Command{
Use: "centos version",
Short: "Boot a Centos installer",
Long: fmt.Sprintf(`Boot a Centos installer for the given version (one of %s)`, strings.Join(versions, ",")),
Run: func(cmd *cobra.Command, args []string) {
if len(args) < 1 {
fatalf("you must specify a Centos version")
}
var version string
for _, v := range versions {
if args[0] == v {
version = v
break
}
}
if version == "" {
fatalf("Unknown Centos version %q", version)
}
arch, err := cmd.Flags().GetString("arch")
if err != nil {
fatalf("Error reading flag: %s", err)
}
mirror, err := cmd.Flags().GetString("mirror")
if err != nil {
fatalf("Error reading flag: %s", err)
}
kernel := fmt.Sprintf("%s/%s/os/%s/images/pxeboot/vmlinuz", mirror, version, arch)
initrd := fmt.Sprintf("%s/%s/os/%s/images/pxeboot/initrd.img", mirror, version, arch)
stage2 := fmt.Sprintf("inst.stage2=%s/%s/os/%s/", mirror, version, arch)
fmt.Println(staticFromFlags(cmd, kernel, []string{initrd}, stage2).Serve())
},
}
centosCmd.Flags().String("arch", "x86_64", "CPU architecture of the Centos installer files")
centosCmd.Flags().String("mirror", "https://mirrors.kernel.org/centos", "Root of the centos mirror to use")
serverConfigFlags(centosCmd)
staticConfigFlags(centosCmd)
parent.AddCommand(centosCmd)
}
func archRecipe(parent *cobra.Command) {
archCmd := &cobra.Command{
Use: "arch",
Short: "Boot an Arch Linux installer",
Run: func(cmd *cobra.Command, args []string) {
arch, err := cmd.Flags().GetString("arch")
if err != nil {
fatalf("Error reading flag: %s", err)
}
mirror, err := cmd.Flags().GetString("mirror")
if err != nil {
fatalf("Error reading flag: %s", err)
}
kernel := fmt.Sprintf("%s/iso/latest/arch/boot/%s/vmlinuz", mirror, arch)
initrd := fmt.Sprintf("%s/iso/latest/arch/boot/%s/archiso.img", mirror, arch)
stage2 := fmt.Sprintf("archisobasedir=arch archiso_http_srv=%s/iso/latest/", mirror)
fmt.Println(staticFromFlags(cmd, kernel, []string{initrd}, stage2).Serve())
},
}
archCmd.Flags().String("arch", "x86_64", "CPU architecture of the Debian installer files")
archCmd.Flags().String("mirror", "https://mirrors.kernel.org/archlinux", "Root of the debian mirror to use")
serverConfigFlags(archCmd)
staticConfigFlags(archCmd)
parent.AddCommand(archCmd)
} }
func init() { func init() {
//rootCmd.AddCommand(quickCmd) rootCmd.AddCommand(quickCmd)
debianRecipe(quickCmd)
ubuntuRecipe(quickCmd)
fedoraRecipe(quickCmd)
centosRecipe(quickCmd)
//archRecipe(quickCmd)
// TODO: some kind of caching support where quick OSes get // TODO: some kind of caching support where quick OSes get
// downloaded locally, so you don't have to fetch from a remote // downloaded locally, so you don't have to fetch from a remote