Skip to content

Commit 70e9a26

Browse files
committed
Improve the error messages when reading the configuration files
1 parent 5de591d commit 70e9a26

File tree

4 files changed

+187
-6
lines changed

4 files changed

+187
-6
lines changed

src/cmd/root.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ func preRun(cmd *cobra.Command, args []string) error {
174174
return err
175175
}
176176

177-
if err := utils.SetUpConfiguration(); err != nil {
177+
if err := setUpConfiguration(); err != nil {
178178
return err
179179
}
180180

src/cmd/utils.go

+164
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,49 @@ func askForConfirmation(prompt string) bool {
6161
return retVal
6262
}
6363

64+
func createErrorConfiguration(file string) error {
65+
var builder strings.Builder
66+
fmt.Fprintf(&builder, "failed to read file %s\n", file)
67+
fmt.Fprintf(&builder, "Run '%s --help' for usage.", executableBase)
68+
69+
errMsg := builder.String()
70+
return errors.New(errMsg)
71+
}
72+
73+
func createErrorConfigurationIsDirectory(file string) error {
74+
var builder strings.Builder
75+
fmt.Fprintf(&builder, "failed to read file %s\n", file)
76+
fmt.Fprintf(&builder, "Configuration file must be a regular file.\n")
77+
fmt.Fprintf(&builder, "Run '%s --help' for usage.", executableBase)
78+
79+
errMsg := builder.String()
80+
return errors.New(errMsg)
81+
}
82+
83+
func createErrorConfigurationPermission(file string) error {
84+
var builder strings.Builder
85+
fmt.Fprintf(&builder, "failed to read file %s\n", file)
86+
fmt.Fprintf(&builder, "Configuration file must be readable.\n")
87+
fmt.Fprintf(&builder, "Run '%s --help' for usage.", executableBase)
88+
89+
errMsg := builder.String()
90+
return errors.New(errMsg)
91+
}
92+
93+
func createErrorConfigurationSyntax(file string) error {
94+
var builder strings.Builder
95+
fmt.Fprintf(&builder, "failed to parse file %s\n", file)
96+
fmt.Fprintf(&builder, "Configuration file must be valid TOML.\n")
97+
fmt.Fprintf(&builder, "Run '%s --help' for usage.", executableBase)
98+
99+
errMsg := builder.String()
100+
return errors.New(errMsg)
101+
}
102+
103+
func createErrorConfigurationUserConfigDir() error {
104+
return errors.New("neither $XDG_CONFIG_HOME nor $HOME are defined")
105+
}
106+
64107
func createErrorContainerNotFound(container string) error {
65108
var builder strings.Builder
66109
fmt.Fprintf(&builder, "container %s not found\n", container)
@@ -81,6 +124,16 @@ func createErrorDistroWithoutReleaseForCLI(distro string) error {
81124
return errors.New(errMsg)
82125
}
83126

127+
func createErrorDistroWithoutReleaseForConfig(distro string) error {
128+
var builder strings.Builder
129+
fmt.Fprintf(&builder, "option 'release' is needed in configuration file\n")
130+
fmt.Fprintf(&builder, "Distribution %s doesn't match the host.\n", distro)
131+
fmt.Fprintf(&builder, "Run '%s --help' for usage.", executableBase)
132+
133+
errMsg := builder.String()
134+
return errors.New(errMsg)
135+
}
136+
84137
func createErrorInvalidContainer(containerArg string) error {
85138
var builder strings.Builder
86139
fmt.Fprintf(&builder, "invalid argument for '%s'\n", containerArg)
@@ -101,6 +154,16 @@ func createErrorInvalidDistroForCLI(distro string) error {
101154
return errors.New(errMsg)
102155
}
103156

157+
func createErrorInvalidDistroForConfig(distro string) error {
158+
var builder strings.Builder
159+
fmt.Fprintf(&builder, "invalid argument for 'distro' in configuration file\n")
160+
fmt.Fprintf(&builder, "Distribution %s is unsupported.\n", distro)
161+
fmt.Fprintf(&builder, "Run '%s --help' for usage.", executableBase)
162+
163+
errMsg := builder.String()
164+
return errors.New(errMsg)
165+
}
166+
104167
func createErrorInvalidImageForContainerNameForCLI(container string) error {
105168
var builder strings.Builder
106169
fmt.Fprintf(&builder, "invalid argument for '--image'\n")
@@ -112,6 +175,17 @@ func createErrorInvalidImageForContainerNameForCLI(container string) error {
112175
return errors.New(errMsg)
113176
}
114177

178+
func createErrorInvalidImageForContainerNameForConfig() error {
179+
var builder strings.Builder
180+
fmt.Fprintf(&builder, "invalid argument for 'image' in configuration file\n")
181+
fmt.Fprintf(&builder, "Image gives an invalid container name.\n")
182+
fmt.Fprintf(&builder, "Container names must match '%s'.\n", utils.ContainerNameRegexp)
183+
fmt.Fprintf(&builder, "Run '%s --help' for usage.", executableBase)
184+
185+
errMsg := builder.String()
186+
return errors.New(errMsg)
187+
}
188+
115189
func createErrorInvalidImageWithoutBasenameForCLI() error {
116190
var builder strings.Builder
117191
fmt.Fprintf(&builder, "invalid argument for '--image'\n")
@@ -122,6 +196,16 @@ func createErrorInvalidImageWithoutBasenameForCLI() error {
122196
return errors.New(errMsg)
123197
}
124198

199+
func createErrorInvalidImageWithoutBasenameForConfig() error {
200+
var builder strings.Builder
201+
fmt.Fprintf(&builder, "invalid argument for 'image' in configuration file\n")
202+
fmt.Fprintf(&builder, "Images must have basenames.\n")
203+
fmt.Fprintf(&builder, "Run '%s --help' for usage.", executableBase)
204+
205+
errMsg := builder.String()
206+
return errors.New(errMsg)
207+
}
208+
125209
func createErrorInvalidReleaseForCLI(hint string) error {
126210
var builder strings.Builder
127211
fmt.Fprintf(&builder, "invalid argument for '--release'\n")
@@ -132,6 +216,16 @@ func createErrorInvalidReleaseForCLI(hint string) error {
132216
return errors.New(errMsg)
133217
}
134218

219+
func createErrorInvalidReleaseForConfig(hint string) error {
220+
var builder strings.Builder
221+
fmt.Fprintf(&builder, "invalid argument for 'release'\n")
222+
fmt.Fprintf(&builder, "%s\n", hint)
223+
fmt.Fprintf(&builder, "Run '%s --help' for usage.", executableBase)
224+
225+
errMsg := builder.String()
226+
return errors.New(errMsg)
227+
}
228+
135229
func getUsageForCommonCommands() string {
136230
var builder strings.Builder
137231
fmt.Fprintf(&builder, "create Create a new toolbox container\n")
@@ -202,6 +296,76 @@ func resolveContainerAndImageNames(container, containerArg, distroCLI, imageCLI,
202296
return container, image, release, nil
203297
}
204298

299+
func setUpConfiguration() error {
300+
if err := utils.SetUpConfiguration(); err != nil {
301+
if errors.Is(err, utils.ErrContainerNameInvalid) {
302+
panicMsg := fmt.Sprintf("invalid container when reading configuration: %s", err)
303+
panic(panicMsg)
304+
}
305+
306+
var errConfiguration *utils.ConfigurationError
307+
var errContainer *utils.ContainerError
308+
var errDistro *utils.DistroError
309+
var errImage *utils.ImageError
310+
var errParseRelease *utils.ParseReleaseError
311+
312+
if errors.As(err, &errConfiguration) {
313+
EISDIR := errors.New("is a directory")
314+
315+
if errors.Is(err, EISDIR) {
316+
err := createErrorConfigurationIsDirectory(errConfiguration.File)
317+
return err
318+
} else if errors.Is(err, os.ErrPermission) {
319+
err := createErrorConfigurationPermission(errConfiguration.File)
320+
return err
321+
} else if errors.Is(err, utils.ErrConfigurationSyntax) {
322+
err := createErrorConfigurationSyntax(errConfiguration.File)
323+
return err
324+
} else if errors.Is(err, utils.ErrConfigurationUserConfigDir) {
325+
err := createErrorConfigurationUserConfigDir()
326+
return err
327+
} else {
328+
err := createErrorConfiguration(errConfiguration.File)
329+
return err
330+
}
331+
} else if errors.As(err, &errContainer) {
332+
if errors.Is(err, utils.ErrContainerNameFromImageInvalid) {
333+
err := createErrorInvalidImageForContainerNameForConfig()
334+
return err
335+
} else {
336+
panicMsg := fmt.Sprintf("unexpected %T: %s", err, err)
337+
panic(panicMsg)
338+
}
339+
} else if errors.As(err, &errDistro) {
340+
if errors.Is(err, utils.ErrDistroUnsupported) {
341+
err := createErrorInvalidDistroForConfig(errDistro.Distro)
342+
return err
343+
} else if errors.Is(err, utils.ErrDistroWithoutRelease) {
344+
err := createErrorDistroWithoutReleaseForConfig(errDistro.Distro)
345+
return err
346+
} else {
347+
panicMsg := fmt.Sprintf("unexpected %T: %s", err, err)
348+
panic(panicMsg)
349+
}
350+
} else if errors.As(err, &errImage) {
351+
if errors.Is(err, utils.ErrImageWithoutBasename) {
352+
err := createErrorInvalidImageWithoutBasenameForConfig()
353+
return err
354+
} else {
355+
panicMsg := fmt.Sprintf("unexpected %T: %s", err, err)
356+
panic(panicMsg)
357+
}
358+
} else if errors.As(err, &errParseRelease) {
359+
err := createErrorInvalidReleaseForConfig(errParseRelease.Hint)
360+
return err
361+
} else {
362+
return err
363+
}
364+
}
365+
366+
return nil
367+
}
368+
205369
// showManual tries to open the specified manual page using man on stdout
206370
func showManual(manual string) error {
207371
manBinary, err := exec.LookPath("man")

src/pkg/utils/errors.go

+14
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ import (
2020
"fmt"
2121
)
2222

23+
type ConfigurationError struct {
24+
File string
25+
Err error
26+
}
27+
2328
type ContainerError struct {
2429
Container string
2530
Image string
@@ -40,6 +45,15 @@ type ParseReleaseError struct {
4045
Hint string
4146
}
4247

48+
func (err *ConfigurationError) Error() string {
49+
errMsg := fmt.Sprintf("%s: %s", err.File, err.Err)
50+
return errMsg
51+
}
52+
53+
func (err *ConfigurationError) Unwrap() error {
54+
return err.Err
55+
}
56+
4357
func (err *ContainerError) Error() string {
4458
errMsg := fmt.Sprintf("%s: %s", err.Container, err.Err)
4559
return errMsg

src/pkg/utils/utils.go

+8-5
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,10 @@ var (
118118
var (
119119
ContainerNameDefault string
120120

121+
ErrConfigurationSyntax = errors.New("failed to parse")
122+
123+
ErrConfigurationUserConfigDir = errors.New("neither $XDG_CONFIG_HOME nor $HOME are defined")
124+
121125
ErrContainerNameFromImageInvalid = errors.New("container name generated from image is invalid")
122126

123127
ErrContainerNameInvalid = errors.New("container name is invalid")
@@ -558,7 +562,7 @@ func SetUpConfiguration() error {
558562
userConfigDir, err := os.UserConfigDir()
559563
if err != nil {
560564
logrus.Debugf("Setting up configuration: failed to get the user config directory: %s", err)
561-
return errors.New("failed to get the user config directory")
565+
return &ConfigurationError{"", ErrConfigurationUserConfigDir}
562566
}
563567

564568
userConfigPath := userConfigDir + "/containers/toolbox.conf"
@@ -580,18 +584,17 @@ func SetUpConfiguration() error {
580584
continue
581585
} else if errors.As(err, &errConfigParse) {
582586
logrus.Debugf("Setting up configuration: failed to parse file %s: %s", configFile, err)
583-
return fmt.Errorf("failed to parse file %s", configFile)
587+
return &ConfigurationError{configFile, ErrConfigurationSyntax}
584588
} else {
585589
logrus.Debugf("Setting up configuration: failed to read file %s: %s", configFile, err)
586-
return fmt.Errorf("failed to read file %s", configFile)
590+
return &ConfigurationError{configFile, err}
587591
}
588592
}
589593
}
590594

591595
container, _, _, err := ResolveContainerAndImageNames("", "", "", "")
592596
if err != nil {
593-
logrus.Debugf("Setting up configuration: failed to resolve container name: %s", err)
594-
return errors.New("failed to resolve container name")
597+
return err
595598
}
596599

597600
ContainerNameDefault = container

0 commit comments

Comments
 (0)