diff --git a/.drone.jsonnet b/.drone.jsonnet index 0f16af831..f0cfb2a29 100644 --- a/.drone.jsonnet +++ b/.drone.jsonnet @@ -220,6 +220,7 @@ local talos = Step("talos", depends_on=[installer], environment={"REGISTRY": loc local lint = Step("lint", depends_on=[check_dirty]); local talosctl_cni_bundle = Step('talosctl-cni-bundle', depends_on=[lint]); local images = Step("images", depends_on=[installer], environment={"REGISTRY": local_registry}); +local iso = Step('iso', depends_on=[images], environment={"REGISTRY": local_registry}); local unit_tests = Step("unit-tests", depends_on=[initramfs]); local unit_tests_race = Step("unit-tests-race", depends_on=[initramfs]); local e2e_docker = Step("e2e-docker-short", depends_on=[talos, talosctl_linux, unit_tests, unit_tests_race], target="e2e-docker", environment={"SHORT_INTEGRATION_TEST": "yes", "REGISTRY": local_registry}); @@ -299,6 +300,7 @@ local default_steps = [ lint, talosctl_cni_bundle, images, + iso, unit_tests, unit_tests_race, coverage, @@ -489,6 +491,7 @@ local release = { '_out/gcp.tar.gz', '_out/initramfs-amd64.xz', '_out/initramfs-arm64.xz', + '_out/talos-amd64.iso', '_out/talosctl-cni-bundle-amd64.tar.gz', '_out/talosctl-cni-bundle-arm64.tar.gz', '_out/talosctl-darwin-amd64', @@ -504,7 +507,7 @@ local release = { when: { event: ['tag'], }, - depends_on: [kernel.name, boot.name, talosctl_cni_bundle.name, images.name, push.name, release_notes.name] + depends_on: [kernel.name, iso.name, boot.name, talosctl_cni_bundle.name, images.name, push.name, release_notes.name] }; local release_steps = default_steps + [ diff --git a/Dockerfile b/Dockerfile index dc6e43867..c3602cd04 100644 --- a/Dockerfile +++ b/Dockerfile @@ -447,16 +447,22 @@ WORKDIR /src/cmd/installer RUN --mount=type=cache,target=/.cache/go-build go build -ldflags "-s -w -X ${VERSION_PKG}.Name=Talos -X ${VERSION_PKG}.SHA=${SHA} -X ${VERSION_PKG}.Tag=${TAG} -X ${VERSION_PKG}.PkgsVersion=${PKGS} -X ${VERSION_PKG}.ExtrasVersion=${EXTRAS} -X ${IMAGES_PKGS}.Username=${USERNAME} -X ${IMAGES_PKGS}.Registry=${REGISTRY}" -o /installer RUN chmod +x /installer +FROM alpine:3.11 AS unicode-pf2 +RUN apk add --no-cache --update grub + FROM alpine:3.11 AS installer RUN apk add --no-cache --update \ bash \ ca-certificates \ - cdrkit \ efibootmgr \ + mtools \ qemu-img \ util-linux \ - xfsprogs + xfsprogs \ + xorriso \ + xz COPY --from=pkg-grub / / +COPY --from=unicode-pf2 /usr/share/grub/unicode.pf2 /usr/share/grub/unicode.pf2 ARG TARGETARCH COPY --from=kernel /vmlinuz-${TARGETARCH} /usr/install/vmlinuz COPY --from=initramfs /initramfs-${TARGETARCH}.xz /usr/install/initramfs.xz diff --git a/Makefile b/Makefile index f2f6a81c5..ee2cb8c60 100644 --- a/Makefile +++ b/Makefile @@ -175,6 +175,10 @@ image-%: ## Builds the specified image. Valid options are aws, azure, digital-oc images: image-aws image-azure image-digital-ocean image-gcp image-vmware ## Builds all known images (AWS, Azure, Digital Ocean, GCP, and VMware). +.PHONY: iso +iso: ## Builds the ISO and outputs it to the artifact directory. + @docker run --rm -i -v $(PWD)/$(ARTIFACTS):/out $(REGISTRY)/$(USERNAME)/installer:$(TAG) iso + .PHONY: boot boot: ## Creates a compressed tarball that includes vmlinuz-{amd64,arm64} and initramfs-{amd64,arm64}.xz. Note that these files must already be present in the artifacts directory. @for platform in $(subst $(,),$(space),$(PLATFORM)); do \ diff --git a/cmd/installer/cmd/iso.go b/cmd/installer/cmd/iso.go new file mode 100644 index 000000000..a6d87ca5f --- /dev/null +++ b/cmd/installer/cmd/iso.go @@ -0,0 +1,126 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package cmd + +import ( + "fmt" + "io" + "io/ioutil" + "log" + "os" + "path/filepath" + "runtime" + + "github.com/spf13/cobra" + + "github.com/talos-systems/talos/cmd/installer/pkg" +) + +var cfg = []byte(`set default=0 +set timeout=0 + +insmod all_video + +terminal_input console +terminal_output console + +menuentry "Talos Interactive Installer" { + set gfxmode=auto + set gfxpayload=text + linux /boot/vmlinuz page_poison=1 slab_nomerge slub_debug=P pti=on panic=0 consoleblank=0 earlyprintk=ttyS0 console=tty0 console=ttyS0 talos.platform=metal + initrd /boot/initramfs.xz +}`) + +// isoCmd represents the iso command. +var isoCmd = &cobra.Command{ + Use: "iso", + Short: "", + Long: ``, + Run: func(cmd *cobra.Command, args []string) { + if err := runISOCmd(); err != nil { + log.Fatal(err) + } + }, +} + +func init() { + rootCmd.AddCommand(isoCmd) +} + +// nolint: gocyclo +func runISOCmd() error { + files := map[string]string{ + "/usr/install/vmlinuz": "/mnt/boot/vmlinuz", + "/usr/install/initramfs.xz": "/mnt/boot/initramfs.xz", + } + + for src, dest := range files { + if err := os.MkdirAll(filepath.Dir(dest), 0o755); err != nil { + return err + } + + log.Printf("copying %s to %s", src, dest) + + from, err := os.Open(src) + if err != nil { + return err + } + // nolint: errcheck + defer from.Close() + + to, err := os.OpenFile(dest, os.O_RDWR|os.O_CREATE, 0o666) + if err != nil { + return err + } + // nolint: errcheck + defer to.Close() + + _, err = io.Copy(to, from) + if err != nil { + return err + } + } + + log.Println("creating grub.cfg") + + cfgPath := "/mnt/boot/grub/grub.cfg" + + if err := os.MkdirAll(filepath.Dir(cfgPath), 0o755); err != nil { + return err + } + + if err := ioutil.WriteFile(cfgPath, cfg, 0o666); err != nil { + return err + } + + log.Println("creating ISO") + + out := fmt.Sprintf("/tmp/talos-%s.iso", runtime.GOARCH) + + if err := pkg.CreateISO(out, "/mnt"); err != nil { + return err + } + + from, err := os.Open(out) + if err != nil { + log.Fatal(err) + } + // nolint: errcheck + defer from.Close() + + to, err := os.OpenFile(filepath.Join(outputArg, filepath.Base(out)), os.O_RDWR|os.O_CREATE, 0o666) + if err != nil { + log.Fatal(err) + } + // nolint: errcheck + defer to.Close() + + _, err = io.Copy(to, from) + if err != nil { + log.Fatal(err) + } + + return nil +} diff --git a/cmd/installer/pkg/iso.go b/cmd/installer/pkg/iso.go new file mode 100644 index 000000000..bb31f4429 --- /dev/null +++ b/cmd/installer/pkg/iso.go @@ -0,0 +1,27 @@ +// This Source Code Form is subject to the terms of the Mozilla Public +// License, v. 2.0. If a copy of the MPL was not distributed with this +// file, You can obtain one at http://mozilla.org/MPL/2.0/. + +package pkg + +import ( + "fmt" + + "github.com/talos-systems/talos/pkg/cmd" +) + +// CreateISO creates an iso by invoking the `grub-mkrescue` command. +func CreateISO(iso, dir string) (err error) { + _, err = cmd.Run( + "grub-mkrescue", + "--compress=xz", + "--output="+iso, + dir, + ) + + if err != nil { + return fmt.Errorf("failed to create ISO: %w", err) + } + + return nil +} diff --git a/internal/app/machined/internal/install/install.go b/internal/app/machined/internal/install/install.go index 744429eea..d52f1ef2a 100644 --- a/internal/app/machined/internal/install/install.go +++ b/internal/app/machined/internal/install/install.go @@ -69,7 +69,8 @@ func RunInstallerContainer(disk, platform, ref string, reg config.Registries, op // platform name, this should be determined in the installer container. var config *string if config = procfs.ProcCmdline().Get(constants.KernelParamConfig).First(); config == nil { - return fmt.Errorf("no config option was found") + c := "none" + config = &c } upgrade := strconv.FormatBool(options.Upgrade) diff --git a/internal/app/machined/pkg/runtime/v1alpha1/platform/vmware/vmware_amd64.go b/internal/app/machined/pkg/runtime/v1alpha1/platform/vmware/vmware_amd64.go index 32d8411a7..ff3b85979 100644 --- a/internal/app/machined/pkg/runtime/v1alpha1/platform/vmware/vmware_amd64.go +++ b/internal/app/machined/pkg/runtime/v1alpha1/platform/vmware/vmware_amd64.go @@ -35,7 +35,7 @@ func (v *VMware) Name() string { func (v *VMware) Configuration(context.Context) ([]byte, error) { var option *string if option = procfs.ProcCmdline().Get(constants.KernelParamConfig).First(); option == nil { - return nil, fmt.Errorf("no config option was found") + return nil, fmt.Errorf("%s not found", constants.KernelParamConfig) } if *option == constants.ConfigGuestInfo {