diff --git a/Dockerfile.dapper b/Dockerfile.dapper index 65c12ae4..14743322 100644 --- a/Dockerfile.dapper +++ b/Dockerfile.dapper @@ -4,7 +4,7 @@ RUN zypper ref ARG DAPPER_HOST_ARCH ENV ARCH $DAPPER_HOST_ARCH -RUN zypper in -y bash git gcc docker vim less file curl wget ca-certificates make mkisofs go1.16 +RUN zypper in -y bash git gcc docker vim less file curl wget ca-certificates make mkisofs go1.16 qemu-tools RUN go get golang.org/x/tools/cmd/goimports RUN if [ "${ARCH}" == "amd64" ]; then \ curl -sL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s v1.40.1; \ diff --git a/Makefile b/Makefile index 009ba569..4bb64a53 100644 --- a/Makefile +++ b/Makefile @@ -68,6 +68,10 @@ ami: run: ./scripts/run +.PHONY: run +pxe: + ./scripts/run pxe + all-amis: \ ami-us-west-1 \ ami-us-west-2 diff --git a/cmd/ros-installer/main.go b/cmd/ros-installer/main.go index a52fc7aa..ad5128fb 100644 --- a/cmd/ros-installer/main.go +++ b/cmd/ros-installer/main.go @@ -2,18 +2,36 @@ package main import ( "flag" + "os" + "github.com/rancher/os/pkg/config" "github.com/rancher/os/pkg/install" "github.com/sirupsen/logrus" + "sigs.k8s.io/yaml" ) var ( - output = flag.Bool("automatic", false, "Check for and run automatic installation") + output = flag.Bool("automatic", false, "Check for and run automatic installation") + printConfig = flag.Bool("print-config", false, "Print effective configuration and exit") + configFile = flag.String("config-file", "", "Config file to use, local file or http/tftp URL") ) func main() { flag.Parse() - if err := install.Run(*output); err != nil { + if *printConfig { + cfg, err := config.ReadConfig(*configFile) + if err != nil { + logrus.Fatal(err) + } + data, err := yaml.Marshal(cfg) + if err != nil { + logrus.Fatal(err) + } + os.Stdout.Write(data) + return + } + + if err := install.Run(*output, *configFile); err != nil { logrus.Fatal(err) } } diff --git a/framework/files/etc/systemd/system/multi-user.target.wants/ros-installer.service b/framework/files/etc/systemd/system/multi-user.target.wants/ros-installer.service new file mode 120000 index 00000000..c80e1a31 --- /dev/null +++ b/framework/files/etc/systemd/system/multi-user.target.wants/ros-installer.service @@ -0,0 +1 @@ +/usr/lib/systemd/system/ros-installer.service \ No newline at end of file diff --git a/framework/files/usr/lib/systemd/system/ros-installer.service b/framework/files/usr/lib/systemd/system/ros-installer.service new file mode 100644 index 00000000..89f781d3 --- /dev/null +++ b/framework/files/usr/lib/systemd/system/ros-installer.service @@ -0,0 +1,17 @@ +[Unit] +Description=RancherOS Automatic Installation +Documentation=https://github.com/rancher/os +Wants=network-online.target +After=network-online.target +Before=getty-pre.target serial-getty@ttyS0.service + +[Install] +WantedBy=multi-user.target + +[Service] +Type=oneshot +EnvironmentFile=-/etc/default/%N +EnvironmentFile=-/etc/sysconfig/%N +TimeoutStartSec=0 +StandardOutput=journal+console +ExecStart=/usr/sbin/ros-installer -automatic diff --git a/go.mod b/go.mod index c811709a..dfede308 100644 --- a/go.mod +++ b/go.mod @@ -4,12 +4,13 @@ go 1.16 require ( github.com/mattn/go-isatty v0.0.10 + github.com/pin/tftp v2.1.0+incompatible // indirect github.com/pkg/errors v0.9.1 github.com/rancher/wrangler v0.8.3 github.com/sirupsen/logrus v1.4.2 - github.com/stretchr/testify v1.4.0 github.com/tredoe/osutil v1.0.5 golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2 + gopkg.in/pin/tftp.v2 v2.1.0 sigs.k8s.io/yaml v1.2.0 ) diff --git a/go.sum b/go.sum index 891120cd..bd06e27d 100644 --- a/go.sum +++ b/go.sum @@ -244,6 +244,8 @@ github.com/paulmach/orb v0.1.3/go.mod h1:VFlX/8C+IQ1p6FTRRKzKoOPJnvEtA5G0Veuqwbu github.com/pborman/uuid v1.2.0/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pin/tftp v2.1.0+incompatible h1:Yng4J7jv6lOc6IF4XoB5mnd3P7ZrF60XQq+my3FAMus= +github.com/pin/tftp v2.1.0+incompatible/go.mod h1:xVpZOMCXTy+A5QMjEVN0Glwa1sUvaJhFXbr/aAxuxGY= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= @@ -473,6 +475,8 @@ gopkg.in/inf.v0 v0.9.0/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/pin/tftp.v2 v2.1.0 h1:QbLeBeg/UoYf1dkjJS5nLj/x9t8gFQRG06fq5PEOWDw= +gopkg.in/pin/tftp.v2 v2.1.0/go.mod h1:OptUw+bblWIH7jYabWNBP0CAH4/sBtD2N0oBeXcRG98= gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= diff --git a/pkg/config/config.go b/pkg/config/config.go index 60bbae87..f4e5fef8 100644 --- a/pkg/config/config.go +++ b/pkg/config/config.go @@ -1,6 +1,6 @@ package config -type Rancher struct { +type RancherOS struct { Install Install `json:"install,omitempty"` } @@ -15,31 +15,22 @@ type Install struct { NoFormat bool `json:"noFormat,omitempty"` Debug bool `json:"debug,omitempty"` TTY string `json:"tty,omitempty"` - ServerURL string `json:"serverUrl,omitempty"` - Token string `json:"token,omitempty"` Password string `json:"password,omitempty"` } type Config struct { - SSHAuthorizedKeys []string `json:"sshAuthorizedKeys,omitempty"` - Rancher Rancher `json:"rancher,omitempty"` + SSHAuthorizedKeys []string `json:"sshAuthorizedKeys,omitempty"` + RancherOS RancherOS `json:"rancheros,omitempty"` } type YipConfig struct { - Stages map[string][]Stage `json:"stages,omitempty"` - Rancherd Rancherd `json:"rancherd,omitempty"` + Stages map[string][]Stage `json:"stages,omitempty"` } type Stage struct { Users map[string]User `json:"users,omitempty"` } -type Rancherd struct { - Server string `json:"server,omitempty"` - Role string `json:"role,omitempty"` - Token string `json:"token,omitempty"` -} - type User struct { Name string `json:"name,omitempty"` PasswordHash string `json:"passwd,omitempty"` diff --git a/pkg/config/read.go b/pkg/config/read.go index ac60ada1..202d1c0a 100644 --- a/pkg/config/read.go +++ b/pkg/config/read.go @@ -3,6 +3,7 @@ package config import ( "fmt" "io/ioutil" + "net/http" "os" "regexp" "strings" @@ -10,18 +11,20 @@ import ( values "github.com/rancher/wrangler/pkg/data" "github.com/rancher/wrangler/pkg/data/convert" schemas2 "github.com/rancher/wrangler/pkg/schemas" + "sigs.k8s.io/yaml" ) var ( + defaultMappers = schemas2.Mappers{ + NewToMap(), + NewToSlice(), + NewToBool(), + &FuzzyNames{}, + } schemas = schemas2.EmptySchemas().Init(func(s *schemas2.Schemas) *schemas2.Schemas { - s.DefaultMapper = func() schemas2.Mapper { - return schemas2.Mappers{ - NewToMap(), - NewToSlice(), - NewToBool(), - &FuzzyNames{}, - } - } + s.AddMapper("config", defaultMappers) + s.AddMapper("rancherOS", defaultMappers) + s.AddMapper("install", defaultMappers) return s }).MustImport(Config{}) schema = schemas.Schema("config") @@ -40,7 +43,7 @@ func mapToEnv(prefix string, data map[string]interface{}) []string { var result []string for k, v := range data { keyName := strings.ToUpper(prefix + convert.ToYAMLKey(k)) - keyName = strings.ReplaceAll(keyName, "RANCHER_", "COS_") + keyName = strings.ReplaceAll(keyName, "RANCHEROS_", "COS_") if data, ok := v.(map[string]interface{}); ok { subResult := mapToEnv(keyName+"_", data) result = append(result, subResult...) @@ -51,14 +54,86 @@ func mapToEnv(prefix string, data map[string]interface{}) []string { return result } -func ReadConfig() (Config, error) { - result := Config{} +func readFile(path string) (result map[string]interface{}, _ error) { + result = map[string]interface{}{} + defer func() { + if v, ok := result["install"]; ok { + values.PutValue(result, v, "rancheros", "install") + } + }() - data, err := readCmdline() - if err != nil { - return result, err + switch { + case strings.HasPrefix(path, "http://"): + fallthrough + case strings.HasPrefix(path, "https://"): + resp, err := http.Get(path) + if err != nil { + return nil, err + } + defer resp.Body.Close() + + buffer, err := ioutil.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("read %s: %w", path, err) + } + + return result, yaml.Unmarshal(buffer, &result) + case strings.HasPrefix(path, "tftp://"): + return tftpGet(path) } - if err := schema.Mapper.ToInternal(data); err != nil { + + f, err := ioutil.ReadFile(path) + if os.IsNotExist(err) { + return nil, nil + } else if err != nil { + return nil, err + } + + data := map[string]interface{}{} + if err := yaml.Unmarshal(f, &data); err != nil { + return nil, err + } + + return data, nil +} + +type reader func() (map[string]interface{}, error) + +func merge(readers ...reader) (map[string]interface{}, error) { + d := map[string]interface{}{} + for _, r := range readers { + newData, err := r() + if err != nil { + return nil, err + } + if err := schema.Mapper.ToInternal(newData); err != nil { + return nil, err + } + d = values.MergeMapsConcatSlice(d, newData) + } + return d, nil +} + +func readConfigMap(cfg string) (map[string]interface{}, error) { + var ( + err error + data map[string]interface{} + ) + return merge(func() (map[string]interface{}, error) { + data, err = merge(readCmdline, + func() (map[string]interface{}, error) { + return readFile(cfg) + }, + ) + return data, err + }, func() (map[string]interface{}, error) { + return readFile(convert.ToString(values.GetValueN(data, "rancheros", "install", "configUrl"))) + }) +} + +func ReadConfig(cfg string) (result Config, err error) { + data, err := readConfigMap(cfg) + if err != nil { return result, err } @@ -72,7 +147,11 @@ func readCmdline() (map[string]interface{}, error) { return nil, nil } - bytes, err := ioutil.ReadFile("/proc/cmdline") + procCmdLine := os.Getenv("PROC_CMDLINE") + if procCmdLine == "" { + procCmdLine = "/proc/cmdline" + } + bytes, err := ioutil.ReadFile(procCmdLine) if os.IsNotExist(err) { return nil, nil } else if err != nil { diff --git a/pkg/config/rename.go b/pkg/config/rename.go index 0a0176da..6201fe22 100644 --- a/pkg/config/rename.go +++ b/pkg/config/rename.go @@ -30,7 +30,9 @@ func (f *FuzzyNames) addName(name, toName string) { } func (f *FuzzyNames) ModifySchema(schema *schemas2.Schema, schemas *schemas2.Schemas) error { - f.names = map[string]string{} + if f.names == nil { + f.names = map[string]string{} + } for name := range schema.ResourceFields { if strings.HasSuffix(name, "s") && len(name) > 1 { diff --git a/pkg/config/tftpget.go b/pkg/config/tftpget.go new file mode 100644 index 00000000..c6a8f769 --- /dev/null +++ b/pkg/config/tftpget.go @@ -0,0 +1,41 @@ +package config + +import ( + "bytes" + "fmt" + "net" + "net/url" + + "gopkg.in/pin/tftp.v2" + "sigs.k8s.io/yaml" +) + +func tftpGet(tftpURL string) (map[string]interface{}, error) { + u, err := url.Parse(tftpURL) + if err != nil { + return nil, err + } + + host, _, err := net.SplitHostPort(u.Host) + if err != nil { + host = u.Host + ":69" + } + + fmt.Printf("Downloading config from host %s, file %s\n", host, u.Path) + client, err := tftp.NewClient(host) + if err != nil { + return nil, err + } + writerTo, err := client.Receive(u.Path, "octet") + if err != nil { + return nil, err + } + + buf := &bytes.Buffer{} + if _, err := writerTo.WriteTo(buf); err != nil { + return nil, err + } + + result := map[string]interface{}{} + return result, yaml.Unmarshal(buf.Bytes(), &result) +} diff --git a/pkg/config/write.go b/pkg/config/write.go index 8adf7944..e036b62c 100644 --- a/pkg/config/write.go +++ b/pkg/config/write.go @@ -6,10 +6,10 @@ import ( ) func PrintInstall(cfg Config) ([]byte, error) { - if cfg.Rancher.Install.Password != "" { - cfg.Rancher.Install.Password = "******" + if cfg.RancherOS.Install.Password != "" { + cfg.RancherOS.Install.Password = "******" } - data, err := convert.EncodeToMap(cfg.Rancher.Install) + data, err := convert.EncodeToMap(cfg.RancherOS.Install) if err != nil { return nil, err } diff --git a/pkg/install/ask.go b/pkg/install/ask.go index 09b06c17..7554f1d0 100644 --- a/pkg/install/ask.go +++ b/pkg/install/ask.go @@ -10,10 +10,6 @@ import ( ) func Ask(cfg *config.Config) error { - if cfg.Rancher.Install.Silent { - return nil - } - if err := AskInstallDevice(cfg); err != nil { return err } @@ -22,7 +18,7 @@ func Ask(cfg *config.Config) error { return err } - if cfg.Rancher.Install.ConfigURL == "" { + if cfg.RancherOS.Install.ConfigURL == "" { if err := AskGithub(cfg); err != nil { return err } @@ -30,17 +26,13 @@ func Ask(cfg *config.Config) error { if err := AskPassword(cfg); err != nil { return err } - - if err := AskServerAgent(cfg); err != nil { - return err - } } return nil } func AskInstallDevice(cfg *config.Config) error { - if cfg.Rancher.Install.Device != "" { + if cfg.RancherOS.Install.Device != "" { return nil } @@ -54,69 +46,22 @@ func AskInstallDevice(cfg *config.Config) error { return err } - cfg.Rancher.Install.Device = "/dev/" + fields[i] + cfg.RancherOS.Install.Device = "/dev/" + fields[i] return nil } -func AskToken(cfg *config.Config, server bool) error { - var ( - token string - err error - ) - - if cfg.Rancher.Install.Token != "" { - return nil - } - - msg := "Token or cluster secret" - if server { - msg += " (optional)" - } - if server { - token, err = questions.PromptOptional(msg+": ", "") - } else { - token, err = questions.Prompt(msg+": ", "") - } - cfg.Rancher.Install.Token = token - - return err -} - -func isServer(cfg *config.Config) (bool, error) { - opts := []string{"server", "agent"} +func isServer(cfg *config.Config) (bool, bool, error) { + opts := []string{"server", "agent", "none"} i, err := questions.PromptFormattedOptions("Run as server or agent?", 0, opts...) if err != nil { - return false, err + return false, false, err } - return i == 0, nil -} - -func AskServerAgent(cfg *config.Config) error { - if cfg.Rancher.Install.ServerURL != "" { - return nil - } - - server, err := isServer(cfg) - if err != nil { - return err - } - - if server { - return AskToken(cfg, true) - } - - url, err := questions.Prompt("URL of server: ", "") - if err != nil { - return err - } - cfg.Rancher.Install.ServerURL = url - - return AskToken(cfg, false) + return i == 0, i == 1, nil } func AskPassword(cfg *config.Config) error { - if cfg.Rancher.Install.Silent || cfg.Rancher.Install.Password != "" { + if cfg.RancherOS.Install.Silent || cfg.RancherOS.Install.Password != "" { return nil } @@ -140,12 +85,12 @@ func AskPassword(cfg *config.Config) error { } } - cfg.Rancher.Install.Password = pass + cfg.RancherOS.Install.Password = pass return nil } func AskGithub(cfg *config.Config) error { - if len(cfg.SSHAuthorizedKeys) > 0 || cfg.Rancher.Install.Password != "" { + if len(cfg.SSHAuthorizedKeys) > 0 || cfg.RancherOS.Install.Password != "" || cfg.RancherOS.Install.Silent { return nil } @@ -167,7 +112,7 @@ func AskGithub(cfg *config.Config) error { } func AskConfigURL(cfg *config.Config) error { - if cfg.Rancher.Install.ConfigURL != "" { + if cfg.RancherOS.Install.ConfigURL != "" || cfg.RancherOS.Install.Silent { return nil } @@ -185,6 +130,6 @@ func AskConfigURL(cfg *config.Config) error { return err } - cfg.Rancher.Install.ConfigURL = str + cfg.RancherOS.Install.ConfigURL = str return nil } diff --git a/pkg/install/install.go b/pkg/install/install.go index 7c5fdccf..0c37dfa3 100644 --- a/pkg/install/install.go +++ b/pkg/install/install.go @@ -1,6 +1,7 @@ package install import ( + "fmt" "io/ioutil" "os" "os/exec" @@ -10,16 +11,16 @@ import ( "sigs.k8s.io/yaml" ) -func Run(automatic bool) error { - cfg, err := config.ReadConfig() +func Run(automatic bool, configFile string) error { + cfg, err := config.ReadConfig(configFile) if err != nil { return err } - if automatic && !cfg.Rancher.Install.Automatic { + if automatic && !cfg.RancherOS.Install.Automatic { return nil } else if automatic { - cfg.Rancher.Install.Silent = true + cfg.RancherOS.Install.Silent = true } err = Ask(&cfg) @@ -44,7 +45,7 @@ func runInstall(cfg config.Config, output string) error { return err } - if !cfg.Rancher.Install.Silent { + if !cfg.RancherOS.Install.Silent { val, err := questions.PromptBool("\nConfiguration\n"+"-------------\n\n"+ string(installBytes)+ "\nYour disk will be formatted and installed with the above configuration.\nContinue?", false) @@ -53,30 +54,20 @@ func runInstall(cfg config.Config, output string) error { } } - if cfg.Rancher.Install.ConfigURL == "" { - yip := config.YipConfig{ - Rancherd: config.Rancherd{ - Server: cfg.Rancher.Install.ServerURL, - Token: cfg.Rancher.Install.Token, - }, - } - if cfg.Rancher.Install.ServerURL == "" { - yip.Rancherd.Role = "cluster-init" - } else { - yip.Rancherd.Role = "agent" - } - if cfg.Rancher.Install.Password != "" || len(cfg.SSHAuthorizedKeys) > 0 { + if cfg.RancherOS.Install.ConfigURL == "" && !cfg.RancherOS.Install.Silent { + yip := config.YipConfig{} + if cfg.RancherOS.Install.Password != "" || len(cfg.SSHAuthorizedKeys) > 0 { yip.Stages = map[string][]config.Stage{ "network": {{ Users: map[string]config.User{ "root": { Name: "root", - PasswordHash: cfg.Rancher.Install.Password, + PasswordHash: cfg.RancherOS.Install.Password, SSHAuthorizedKeys: cfg.SSHAuthorizedKeys, }, }}, }} - cfg.Rancher.Install.Password = "" + cfg.RancherOS.Install.Password = "" } data, err := yaml.Marshal(yip) @@ -87,7 +78,7 @@ func runInstall(cfg config.Config, output string) error { if err := ioutil.WriteFile(output+".yip", data, 0600); err != nil { return err } - cfg.Rancher.Install.ConfigURL = output + ".yip" + cfg.RancherOS.Install.ConfigURL = output + ".yip" } ev, err := config.ToEnv(cfg) @@ -95,6 +86,8 @@ func runInstall(cfg config.Config, output string) error { return err } + printEnv(cfg) + cmd := exec.Command("cos-installer") cmd.Env = append(os.Environ(), ev...) cmd.Stdout = os.Stdout @@ -102,3 +95,19 @@ func runInstall(cfg config.Config, output string) error { cmd.Stderr = os.Stderr return cmd.Run() } + +func printEnv(cfg config.Config) { + if cfg.RancherOS.Install.Password != "" { + cfg.RancherOS.Install.Password = "" + } + + ev2, err := config.ToEnv(cfg) + if err != nil { + return + } + + fmt.Println("Install environment:") + for _, ev := range ev2 { + fmt.Println(ev) + } +} diff --git a/scripts/package b/scripts/package index ca5caa49..ad2c21e9 100755 --- a/scripts/package +++ b/scripts/package @@ -32,6 +32,30 @@ isoinfo -x /rootfs.squashfs -R -i build/output.iso > build/output.squashfs isoinfo -x /boot/kernel.xz -R -i build/output.iso > build/output-kernel isoinfo -x /boot/rootfs.xz -R -i build/output.iso > build/output-initrd + +RELEASE_URL=${RELEASE_URL:-https://github.com/rancher/os/releases/download} +INSTALL_CFG=${RELEASE_URL}/${VERSION}/example-cloud-init +PXE_ASSET_VERSION="\${version}-\${arch}" +if [ "${TAG}" = "dev" ]; then + RELEASE_URL=tftp://10.0.2.2 + INSTALL_CFG= + VERSION=${TAG} + PXE_ASSET_VERSION=${TAG} +fi + +cat > build/output.ipxe << EOF +#!ipxe +set arch ${ARCH} +set version ${VERSION} +set url ${RELEASE_URL}/\${version} +set kernel rancheros-${PXE_ASSET_VERSION}-kernel +set initrd rancheros-${PXE_ASSET_VERSION}-initrd +set rootfs rancheros-${PXE_ASSET_VERSION}.squashfs +kernel \${url}/\${kernel} initrd=\${initrd} ip=dhcp rd.cos.disable root=live:\${url}/\${rootfs} rancheros.install.automatic=true rancheros.install.config_url=\${config} console=tty1 console=ttyS0 +initrd \${url}/\${initrd} +boot +EOF + mkdir -p dist/artifacts for i in build/output*; do mv -f $i dist/artifacts/rancheros-${TAG}${i##build/output} diff --git a/scripts/run b/scripts/run index f40122ba..0249616e 100755 --- a/scripts/run +++ b/scripts/run @@ -11,10 +11,34 @@ fi touch meta-data touch user-data rm -f seed.iso -genisoimage -output seed.iso -volid cidata -joliet -rock user-data meta-data +mkisofs -output seed.iso -volid cidata -joliet -rock user-data meta-data +BOOT= + +if [ "$1" = "pxe" ]; then + shift 1 + if [ ! -e ipxe.cfg ]; then + if [ ! -e ../dist/artifacts/rancheros-dev.ipxe ]; then + make -C .. package + fi + if [ -e ../dist/artifacts/rancheros-dev.ipxe ]; then + cp ../dist/artifacts/rancheros-dev.ipxe ipxe.cfg + fi + fi + BOOT="-boot cn" + + if [ ! -e dev ]; then + ln -s ../dist/artifacts dev + fi +fi + + +if [ "$1" == "" ] && [ ! -e output.iso ]; then + make -C .. build iso +fi #-bios /usr/share/qemu/OVMF.fd \ qemu-system-x86_64 \ + $BOOT \ -enable-kvm \ -m ${MEMORY:=4096} \ -machine accel=${ACCEL:="kvm"} \ @@ -23,6 +47,7 @@ qemu-system-x86_64 \ -serial mon:stdio \ -rtc base=utc,clock=rt \ -chardev socket,path=qga.sock,server,nowait,id=qga0 \ + -nic user,tftp=./,bootfile=/ipxe.cfg \ -device virtio-serial \ -device virtserialport,chardev=qga0,name=org.qemu.guest_agent.0 \ -drive if=virtio,media=disk,file=disk.img \