diff --git a/cmd/build-iso.go b/cmd/build-iso.go index 5a67f691ee8..2b1aa4cd670 100644 --- a/cmd/build-iso.go +++ b/cmd/build-iso.go @@ -27,6 +27,7 @@ import ( "github.com/rancher/elemental-cli/cmd/config" "github.com/rancher/elemental-cli/pkg/action" "github.com/rancher/elemental-cli/pkg/constants" + elementalError "github.com/rancher/elemental-cli/pkg/error" v1 "github.com/rancher/elemental-cli/pkg/types/v1" "github.com/rancher/elemental-cli/pkg/utils" ) @@ -52,19 +53,20 @@ func NewBuildISO(root *cobra.Command, addCheckRoot bool) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { path, err := exec.LookPath("mount") if err != nil { - return err + return elementalError.NewFromError(err, elementalError.StatFile) } mounter := mount.New(path) cfg, err := config.ReadConfigBuild(viper.GetString("config-dir"), cmd.Flags(), mounter) if err != nil { cfg.Logger.Errorf("Error reading config: %s\n", err) + return elementalError.NewFromError(err, elementalError.ReadingBuildConfig) } flags := cmd.Flags() err = validateCosignFlags(cfg.Logger, flags) if err != nil { - return err + return elementalError.NewFromError(err, elementalError.CosignWrongFlags) } // Set this after parsing of the flags, so it fails on parsing and prints usage properly @@ -73,20 +75,20 @@ func NewBuildISO(root *cobra.Command, addCheckRoot bool) *cobra.Command { spec, err := config.ReadBuildISO(cfg, flags) if err != nil { cfg.Logger.Errorf("invalid install command setup %v", err) - return err + return elementalError.NewFromError(err, elementalError.ReadingSpecConfig) } if len(args) == 1 { imgSource, err := v1.NewSrcFromURI(args[0]) if err != nil { cfg.Logger.Errorf("not a valid rootfs source image argument: %s", args[0]) - return err + return elementalError.NewFromError(err, elementalError.IdentifySource) } spec.RootFS = []*v1.ImageSource{imgSource} } else if len(spec.RootFS) == 0 { errmsg := "rootfs source image for building ISO was not provided" cfg.Logger.Errorf(errmsg) - return fmt.Errorf(errmsg) + return elementalError.New(errmsg, elementalError.NoSourceProvided) } // Repos and overlays can't be unmarshaled directly as they require @@ -100,24 +102,27 @@ func NewBuildISO(root *cobra.Command, addCheckRoot bool) *cobra.Command { if ok, err := utils.Exists(cfg.Fs, oRootfs); ok { spec.RootFS = append(spec.RootFS, v1.NewDirSrc(oRootfs)) } else { - cfg.Logger.Errorf("Invalid value for overlay-rootfs") - return fmt.Errorf("Invalid path '%s': %v", oRootfs, err) + msg := fmt.Sprintf("Invalid path '%s': %v", oRootfs, err) + cfg.Logger.Errorf(msg) + return elementalError.New(msg, elementalError.StatFile) } } if oUEFI != "" { if ok, err := utils.Exists(cfg.Fs, oUEFI); ok { spec.UEFI = append(spec.UEFI, v1.NewDirSrc(oUEFI)) } else { - cfg.Logger.Errorf("Invalid value for overlay-uefi") - return fmt.Errorf("Invalid path '%s': %v", oUEFI, err) + msg := fmt.Sprintf("Invalid path '%s': %v", oUEFI, err) + cfg.Logger.Errorf(msg) + return elementalError.New(msg, elementalError.StatFile) } } if oISO != "" { if ok, err := utils.Exists(cfg.Fs, oISO); ok { spec.Image = append(spec.Image, v1.NewDirSrc(oISO)) } else { - cfg.Logger.Errorf("Invalid value for overlay-iso") - return fmt.Errorf("Invalid path '%s': %v", oISO, err) + msg := fmt.Sprintf("Invalid path '%s': %v", oISO, err) + cfg.Logger.Errorf(msg) + return elementalError.New(msg, elementalError.StatFile) } } @@ -126,13 +131,7 @@ func NewBuildISO(root *cobra.Command, addCheckRoot bool) *cobra.Command { } buildISO := action.NewBuildISOAction(cfg, spec) - err = buildISO.ISORun() - if err != nil { - cfg.Logger.Errorf(err.Error()) - return err - } - - return nil + return buildISO.ISORun() }, } diff --git a/cmd/cloud-init.go b/cmd/cloud-init.go index c212b508dbf..168481070c2 100644 --- a/cmd/cloud-init.go +++ b/cmd/cloud-init.go @@ -23,6 +23,7 @@ import ( "k8s.io/mount-utils" "github.com/rancher/elemental-cli/cmd/config" + elementalError "github.com/rancher/elemental-cli/pkg/error" "github.com/mudler/yip/pkg/schema" "github.com/spf13/cobra" @@ -40,8 +41,9 @@ func NewCloudInitCmd(root *cobra.Command) *cobra.Command { RunE: func(cmd *cobra.Command, args []string) error { cfg, err := config.ReadConfigRun(viper.GetString("config-dir"), cmd.Flags(), &mount.FakeMounter{}) if err != nil { - return err + return elementalError.NewFromError(err, elementalError.ReadingRunConfig) } + stage, _ := cmd.Flags().GetString("stage") dot, _ := cmd.Flags().GetBool("dotnotation") @@ -54,13 +56,14 @@ func NewCloudInitCmd(root *cobra.Command) *cobra.Command { if fromStdin { std, err := ioutil.ReadAll(os.Stdin) if err != nil { - return err + return elementalError.NewFromError(err, elementalError.ReadFile) } args = []string{string(std)} } - return cfg.CloudInitRunner.Run(stage, args...) + err = cfg.CloudInitRunner.Run(stage, args...) + return elementalError.NewFromError(err, elementalError.CloudInitRunStage) }, } root.AddCommand(c) diff --git a/cmd/convert-disk.go b/cmd/convert-disk.go index 83ac6286cf2..ed1a0c77a34 100644 --- a/cmd/convert-disk.go +++ b/cmd/convert-disk.go @@ -20,12 +20,14 @@ import ( "fmt" "strings" - "github.com/rancher/elemental-cli/cmd/config" - "github.com/rancher/elemental-cli/pkg/action" - "github.com/rancher/elemental-cli/pkg/utils" "github.com/spf13/cobra" "github.com/spf13/viper" mountUtils "k8s.io/mount-utils" + + "github.com/rancher/elemental-cli/cmd/config" + "github.com/rancher/elemental-cli/pkg/action" + elementalError "github.com/rancher/elemental-cli/pkg/error" + "github.com/rancher/elemental-cli/pkg/utils" ) var outputAllowed = []string{"azure", "gce"} @@ -49,7 +51,7 @@ func NewConvertDisk(root *cobra.Command, addCheckRoot bool) *cobra.Command { cfg, err := config.ReadConfigBuild(viper.GetString("config-dir"), cmd.Flags(), mounter) if err != nil { - return err + return elementalError.NewFromError(err, elementalError.ReadingBuildConfig) } // Set this after parsing of the flags, so it fails on parsing and prints usage properly @@ -61,8 +63,9 @@ func NewConvertDisk(root *cobra.Command, addCheckRoot bool) *cobra.Command { rawDisk := args[0] if exists, _ := utils.Exists(cfg.Fs, rawDisk); !exists { - cfg.Logger.Errorf("Raw image %s doesnt exist", rawDisk) - return fmt.Errorf("raw image %s doesnt exist", rawDisk) + msg := fmt.Sprintf("Raw image %s doesnt exist", rawDisk) + cfg.Logger.Errorf(msg) + return elementalError.New(msg, elementalError.StatFile) } switch imgType { diff --git a/cmd/new.go b/cmd/new.go index 5ba670383b2..4c41d86d0bf 100644 --- a/cmd/new.go +++ b/cmd/new.go @@ -18,14 +18,16 @@ package cmd import ( "context" - "errors" "fmt" + "strings" "github.com/hashicorp/go-getter" - "github.com/rancher/elemental-cli/cmd/config" "github.com/spf13/cobra" "github.com/spf13/viper" "k8s.io/mount-utils" + + "github.com/rancher/elemental-cli/cmd/config" + elementalError "github.com/rancher/elemental-cli/pkg/error" ) func NewDerivativeCmd(root *cobra.Command) *cobra.Command { @@ -37,34 +39,36 @@ func NewDerivativeCmd(root *cobra.Command) *cobra.Command { SilenceErrors: true, // Do not propagate errors down the line, we control them RunE: func(cmd *cobra.Command, args []string) error { cfg, err := config.ReadConfigRun(viper.GetString("config-dir"), cmd.Flags(), &mount.FakeMounter{}) - if err != nil { cfg.Logger.Errorf("Error reading config: %s\n", err) + return elementalError.NewFromError(err, elementalError.ReadingRunConfig) } - flavor := args[0] + flavor := strings.ToLower(args[0]) if flavor != "opensuse" && flavor != "ubuntu" && flavor != "fedora" { cfg.Logger.Errorf("Unsupported flavor") - return errors.New("unsupported flavor") + return elementalError.New("unsupported flavor", elementalError.UnsupportedFlavor) } client := &getter.Client{ Ctx: context.Background(), Dst: fmt.Sprintf("derivatives/%s", flavor), Dir: true, - Src: "github.com/rancher-sandbox/cOS-toolkit//examples/standard", + Src: "github.com/rancher/elemental-toolkit/examples/standard", Mode: getter.ClientModeDir, Detectors: []getter.Detector{ &getter.GitHubDetector{}, }, } + cfg.Logger.Infof("Downloading template...") err = client.Get() if err != nil { cfg.Logger.Errorf("Unable to create derivative") - return err + return elementalError.NewFromError(err, elementalError.DownloadFile) } + cfg.Logger.Infof("New derivative created successfully") return nil }, } diff --git a/cmd/pull-image.go b/cmd/pull-image.go index f62ce00a7c4..a93b3671e22 100644 --- a/cmd/pull-image.go +++ b/cmd/pull-image.go @@ -25,6 +25,7 @@ import ( "k8s.io/mount-utils" "github.com/rancher/elemental-cli/cmd/config" + elementalError "github.com/rancher/elemental-cli/pkg/error" "github.com/rancher/elemental-cli/pkg/luet" ) @@ -41,16 +42,16 @@ func NewPullImageCmd(root *cobra.Command, addCheckRoot bool) *cobra.Command { }, RunE: func(cmd *cobra.Command, args []string) error { cfg, err := config.ReadConfigRun(viper.GetString("config-dir"), cmd.Flags(), &mount.FakeMounter{}) - if err != nil { cfg.Logger.Errorf("Error reading config: %s\n", err) + return elementalError.NewFromError(err, elementalError.ReadingRunConfig) } image := args[0] destination, err := filepath.Abs(args[1]) if err != nil { cfg.Logger.Errorf("Invalid path %s", destination) - return err + return elementalError.NewFromError(err, elementalError.StatFile) } // Set this after parsing of the flags, so it fails on parsing and prints usage properly @@ -82,7 +83,7 @@ func NewPullImageCmd(root *cobra.Command, addCheckRoot bool) *cobra.Command { if err != nil { cfg.Logger.Error(err.Error()) - return err + return elementalError.NewFromError(err, elementalError.UnpackImage) } return nil diff --git a/cmd/run-stage.go b/cmd/run-stage.go index 3852c40fbdc..e89ce1a91b5 100644 --- a/cmd/run-stage.go +++ b/cmd/run-stage.go @@ -22,6 +22,7 @@ import ( "k8s.io/mount-utils" "github.com/rancher/elemental-cli/cmd/config" + elementalError "github.com/rancher/elemental-cli/pkg/error" "github.com/rancher/elemental-cli/pkg/utils" ) @@ -35,12 +36,13 @@ func NewRunStage(root *cobra.Command) *cobra.Command { }, RunE: func(cmd *cobra.Command, args []string) error { cfg, err := config.ReadConfigRun(viper.GetString("config-dir"), cmd.Flags(), &mount.FakeMounter{}) - if err != nil { cfg.Logger.Errorf("Error reading config: %s\n", err) + return elementalError.NewFromError(err, elementalError.ReadingRunConfig) } - return utils.RunStage(&cfg.Config, args[0], cfg.Strict) + err = utils.RunStage(&cfg.Config, args[0], cfg.Strict) + return elementalError.NewFromError(err, elementalError.CloudInitRunStage) }, } root.AddCommand(c) diff --git a/docs/elemental_exit-codes.md b/docs/elemental_exit-codes.md index fa1962c0f7b..1e8dd37888e 100644 --- a/docs/elemental_exit-codes.md +++ b/docs/elemental_exit-codes.md @@ -38,7 +38,6 @@ | 42 | Error occurred trying to reboot| | 43 | Error occurred trying to shutdown| | 44 | Error occurred when labeling partition| -| 44 | Error occurred when unmounting image| | 45 | Error setting default grub entry| | 46 | Error occurred during selinux relabeling| | 47 | Error invalid device specified| @@ -58,4 +57,12 @@ | 61 | Error during before-reset hook| | 62 | Error during after-reset-chroot hook| | 63 | Error during after-reset hook| +| 64 | Unsupported flavor| +| 65 | Error encountered during cloud-init run-stage| +| 66 | Error unpacking image| +| 67 | Error reading file| +| 68 | No source was provided for the command| +| 69 | Error removing a file| +| 70 | Error calculating checksum| +| 71 | Error occurred when unmounting image| | 255 | Unknown error| diff --git a/docs/generate_docs.go b/docs/generate_docs.go index c5596133413..4d1c3d77b1b 100644 --- a/docs/generate_docs.go +++ b/docs/generate_docs.go @@ -27,9 +27,10 @@ import ( "strconv" "strings" - "github.com/rancher/elemental-cli/cmd" "github.com/spf13/cobra" "github.com/spf13/cobra/doc" + + "github.com/rancher/elemental-cli/cmd" ) func main() { @@ -56,10 +57,14 @@ func main() { os.Exit(1) } } - generateExitCodes() + + if err := generateExitCodes(); err != nil { + fmt.Printf("error generating exit-codes: %v\n", err) + os.Exit(1) + } } -func generateExitCodes() { +func generateExitCodes() error { fset := token.NewFileSet() files := []*ast.File{ mustParse(fset, "../pkg/error/exit-codes.go"), @@ -68,13 +73,24 @@ func generateExitCodes() { if err != nil { panic(err) } - var exitCodes []*ErrorCode + var ( + exitCodes []*ErrorCode + used map[int]bool + ) + + used = make(map[int]bool) for _, c := range p.Consts { // Cast it, its safe as these are constants v := c.Decl.Specs[0].(*ast.ValueSpec) val := v.Values[0].(*ast.BasicLit) code, _ := strconv.Atoi(val.Value) + + if _, ok := used[code]; ok { + return fmt.Errorf("duplicate exit-code found: %v", code) + } + + used[code] = true exitCodes = append(exitCodes, &ErrorCode{code: code, doc: c.Doc}) } @@ -86,7 +102,7 @@ func generateExitCodes() { if err != nil { fmt.Print(err) - return + return err } defer func() { @@ -99,10 +115,11 @@ func generateExitCodes() { for _, code := range exitCodes { _, err = exitCodesFile.WriteString(fmt.Sprintf("| %d | %s|\n", code.code, strings.Replace(code.doc, "\n", "", 1))) if err != nil { - return + return err } } + return nil } func mustParse(fset *token.FileSet, filename string) *ast.File { diff --git a/pkg/action/build-iso.go b/pkg/action/build-iso.go index 53bf96d4f7f..fc5d1f119d0 100644 --- a/pkg/action/build-iso.go +++ b/pkg/action/build-iso.go @@ -23,6 +23,7 @@ import ( "github.com/rancher/elemental-cli/pkg/constants" "github.com/rancher/elemental-cli/pkg/elemental" + elementalError "github.com/rancher/elemental-cli/pkg/error" "github.com/rancher/elemental-cli/pkg/live" v1 "github.com/rancher/elemental-cli/pkg/types/v1" "github.com/rancher/elemental-cli/pkg/utils" @@ -62,39 +63,43 @@ func NewBuildISOAction(cfg *v1.BuildConfig, spec *v1.LiveISO, opts ...BuildISOAc } // BuildISORun will install the system from a given configuration -func (b *BuildISOAction) ISORun() (err error) { +func (b *BuildISOAction) ISORun() error { cleanup := utils.NewCleanStack() + var err error defer func() { err = cleanup.Cleanup(err) }() isoTmpDir, err := utils.TempDir(b.cfg.Fs, "", "elemental-iso") if err != nil { - return err + return elementalError.NewFromError(err, elementalError.CreateTempDir) } cleanup.Push(func() error { return b.cfg.Fs.RemoveAll(isoTmpDir) }) rootDir := filepath.Join(isoTmpDir, "rootfs") err = utils.MkdirAll(b.cfg.Fs, rootDir, constants.DirPerm) if err != nil { - return err + b.cfg.Logger.Errorf("Failed creating rootfs dir: %s", rootDir) + return elementalError.NewFromError(err, elementalError.CreateDir) } uefiDir := filepath.Join(isoTmpDir, "uefi") err = utils.MkdirAll(b.cfg.Fs, uefiDir, constants.DirPerm) if err != nil { - return err + b.cfg.Logger.Errorf("Failed creating uefi dir: %s", uefiDir) + return elementalError.NewFromError(err, elementalError.CreateDir) } isoDir := filepath.Join(isoTmpDir, "iso") err = utils.MkdirAll(b.cfg.Fs, isoDir, constants.DirPerm) if err != nil { - return err + b.cfg.Logger.Errorf("Failed creating iso dir: %s", isoDir) + return elementalError.NewFromError(err, elementalError.CreateDir) } if b.cfg.OutDir != "" { err = utils.MkdirAll(b.cfg.Fs, b.cfg.OutDir, constants.DirPerm) if err != nil { - b.cfg.Logger.Errorf("Failed creating output folder: %s", b.cfg.OutDir) - return err + b.cfg.Logger.Errorf("Failed creating output dir: %s", b.cfg.OutDir) + return elementalError.NewFromError(err, elementalError.CreateDir) } } @@ -107,7 +112,7 @@ func (b *BuildISOAction) ISORun() (err error) { err = utils.CreateDirStructure(b.cfg.Fs, rootDir) if err != nil { b.cfg.Logger.Errorf("Failed creating root directory structure: %v", err) - return err + return elementalError.NewFromError(err, elementalError.CreateDir) } if b.spec.Firmware == v1.EFI { @@ -116,7 +121,7 @@ func (b *BuildISOAction) ISORun() (err error) { err = b.liveBoot.PrepareEFI(rootDir, uefiDir) if err != nil { b.cfg.Logger.Errorf("Failed fetching EFI data: %v", err) - return err + return elementalError.NewFromError(err, elementalError.CopyData) } } err = b.applySources(uefiDir, b.spec.UEFI...) @@ -131,7 +136,7 @@ func (b *BuildISOAction) ISORun() (err error) { err = b.liveBoot.PrepareISO(rootDir, isoDir) if err != nil { b.cfg.Logger.Errorf("Failed fetching bootloader binaries: %v", err) - return err + return elementalError.NewFromError(err, elementalError.CreateFile) } } err = b.applySources(isoDir, b.spec.Image...) @@ -168,33 +173,29 @@ func (b BuildISOAction) prepareISORoot(isoDir string, rootDir string) error { kernel, initrd, err := b.e.FindKernelInitrd(rootDir) if err != nil { b.cfg.Logger.Error("Could not find kernel and/or initrd") - return err + return elementalError.NewFromError(err, elementalError.StatFile) } err = utils.MkdirAll(b.cfg.Fs, filepath.Join(isoDir, "boot"), constants.DirPerm) if err != nil { - return err + return elementalError.NewFromError(err, elementalError.CreateDir) } //TODO document boot/kernel and boot/initrd expectation in bootloader config b.cfg.Logger.Debugf("Copying Kernel file %s to iso root tree", kernel) err = utils.CopyFile(b.cfg.Fs, kernel, filepath.Join(isoDir, constants.IsoKernelPath)) if err != nil { - return err + return elementalError.NewFromError(err, elementalError.CopyFile) } b.cfg.Logger.Debugf("Copying initrd file %s to iso root tree", initrd) err = utils.CopyFile(b.cfg.Fs, initrd, filepath.Join(isoDir, constants.IsoInitrdPath)) if err != nil { - return err + return elementalError.NewFromError(err, elementalError.CopyFile) } b.cfg.Logger.Info("Creating squashfs...") squashOptions := append(constants.GetDefaultSquashfsOptions(), b.cfg.SquashFsCompressionConfig...) err = utils.CreateSquashFS(b.cfg.Runner, b.cfg.Logger, rootDir, filepath.Join(isoDir, constants.IsoRootFile), squashOptions) - if err != nil { - return err - } - - return nil + return elementalError.NewFromError(err, elementalError.MKFSCall) } func (b BuildISOAction) createEFI(root string, img string) error { @@ -253,7 +254,7 @@ func (b BuildISOAction) burnISO(root, efiImg string) error { b.cfg.Logger.Warnf("Overwriting already existing %s", outputFile) err := b.cfg.Fs.Remove(outputFile) if err != nil { - return err + return elementalError.NewFromError(err, elementalError.RemoveFile) } } @@ -266,16 +267,18 @@ func (b BuildISOAction) burnISO(root, efiImg string) error { out, err := b.cfg.Runner.Run(cmd, args...) b.cfg.Logger.Debugf("Xorriso: %s", string(out)) if err != nil { - return err + return elementalError.NewFromError(err, elementalError.CommandRun) } checksum, err := utils.CalcFileChecksum(b.cfg.Fs, outputFile) if err != nil { - return fmt.Errorf("checksum computation failed: %w", err) + b.cfg.Logger.Errorf("checksum computation failed: %v", err) + return elementalError.NewFromError(err, elementalError.CalculateChecksum) } err = b.cfg.Fs.WriteFile(fmt.Sprintf("%s.sha256", outputFile), []byte(fmt.Sprintf("%s %s\n", checksum, isoFileName)), 0644) if err != nil { - return fmt.Errorf("cannot write checksum file: %w", err) + b.cfg.Logger.Errorf("cannot write checksum file: %v", err) + return elementalError.NewFromError(err, elementalError.CreateFile) } return nil @@ -285,7 +288,7 @@ func (b BuildISOAction) applySources(target string, sources ...*v1.ImageSource) for _, src := range sources { _, err := b.e.DumpSource(target, src) if err != nil { - return err + return elementalError.NewFromError(err, elementalError.DumpSource) } } return nil diff --git a/pkg/error/exit-codes.go b/pkg/error/exit-codes.go index c049e3b50f5..507507e578a 100644 --- a/pkg/error/exit-codes.go +++ b/pkg/error/exit-codes.go @@ -133,9 +133,6 @@ const PowerOff = 43 // Error occurred when labeling partition const LabelImage = 44 -// Error occurred when unmounting image -const UnmountImage = 44 - // Error setting default grub entry const SetDefaultGrubEntry = 45 @@ -193,5 +190,29 @@ const HookAfterResetChroot = 62 // Error during after-reset hook const HookAfterReset = 63 +// Unsupported flavor +const UnsupportedFlavor = 64 + +// Error encountered during cloud-init run-stage +const CloudInitRunStage = 65 + +// Error unpacking image +const UnpackImage = 66 + +// Error reading file +const ReadFile = 67 + +// No source was provided for the command +const NoSourceProvided = 68 + +// Error removing a file +const RemoveFile = 69 + +// Error calculating checksum +const CalculateChecksum = 70 + +// Error occurred when unmounting image +const UnmountImage = 71 + // Unknown error const Unknown int = 255