diff --git a/cmd/container-suseconnect/main.go b/cmd/container-suseconnect/main.go index 12331a4..e667927 100644 --- a/cmd/container-suseconnect/main.go +++ b/cmd/container-suseconnect/main.go @@ -33,7 +33,7 @@ func actionWrapper(action func(*cli.Context) error) func(*cli.Context) error { if err := action(ctx); err != nil { switch err.(type) { case *cs.SuseConnectError: - if err.(*cs.SuseConnectError).ErrorCode == cs.GetCredentialsError { + if err.(*cs.SuseConnectError).Code == cs.GetCredentialsError { if ctx.Bool("log-credentials-errors") { return err } diff --git a/internal/configuration.go b/internal/configuration.go index 3f6b596..14bbbd0 100644 --- a/internal/configuration.go +++ b/internal/configuration.go @@ -68,12 +68,12 @@ func ReadConfiguration(config Configuration) error { if config.onLocationsNotFound() { return nil } - return loggedError(GetCredentialsError, "Warning: SUSE credentials not found: %v - automatic handling of repositories not done.", config.locations()) + return NewSuseConnectError(GetCredentialsError, "Warning: SUSE credentials not found: %v - automatic handling of repositories not done.", config.locations()) } file, err := os.Open(path) if err != nil { - return loggedError(GetCredentialsError, "Can't open %s file: %v", path, err.Error()) + return NewSuseConnectError(GetCredentialsError, "Can't open %s file: %v", path, err.Error()) } defer file.Close() @@ -96,7 +96,7 @@ func parse(config Configuration, reader io.Reader) error { line := scanner.Text() parts := strings.SplitN(line, string(config.separator()), 2) if len(parts) != 2 { - return loggedError(GetCredentialsError, "Can't parse line: %v", line) + return NewSuseConnectError(GetCredentialsError, "Can't parse line: %v", line) } // And finally trim the key and the value and pass it to the config. @@ -106,7 +106,7 @@ func parse(config Configuration, reader io.Reader) error { // Final checks. if err := scanner.Err(); err != nil { - return loggedError(GetCredentialsError, "Error when scanning configuration: %v", err) + return NewSuseConnectError(GetCredentialsError, "Error when scanning configuration: %v", err) } if err := config.afterParseCheck(); err != nil { return err diff --git a/internal/configuration_test.go b/internal/configuration_test.go index cd75aa4..edcdb4b 100644 --- a/internal/configuration_test.go +++ b/internal/configuration_test.go @@ -66,7 +66,6 @@ func TestNotFound(t *testing.T) { if err == nil || err.Error() != "Warning: SUSE credentials not found: [] - automatic handling of repositories not done." { t.Fatalf("Wrong error: %v", err) } - shouldHaveLogged(t, "Warning: SUSE credentials not found: [] - automatic handling of repositories not done.") } type NotAllowedConfiguration struct{} @@ -99,7 +98,6 @@ func TestNotAllowed(t *testing.T) { if err == nil || err.Error() != msg { t.Fatal("Wrong error") } - shouldHaveLogged(t, msg) } func TestParseInvalid(t *testing.T) { @@ -116,7 +114,6 @@ func TestParseInvalid(t *testing.T) { if err == nil || err.Error() != msg { t.Fatal("Wrong error") } - shouldHaveLogged(t, msg) } type ErrorAfterParseConfiguration struct{} @@ -160,5 +157,4 @@ func TestParseFailNoSeparator(t *testing.T) { if err == nil || err.Error() != msg { t.Fatal("Wrong error") } - shouldHaveLogged(t, msg) } diff --git a/internal/credentials.go b/internal/credentials.go index 8ff42a5..6615842 100644 --- a/internal/credentials.go +++ b/internal/credentials.go @@ -71,10 +71,10 @@ func (cr *Credentials) setValues(key, value string) { func (cr *Credentials) afterParseCheck() error { if cr.Username == "" { - return loggedError(GetCredentialsError, "Can't find username") + return NewSuseConnectError(GetCredentialsError, "Can't find username") } if cr.Password == "" { - return loggedError(GetCredentialsError, "Can't find password") + return NewSuseConnectError(GetCredentialsError, "Can't find password") } return nil } diff --git a/internal/credentials_test.go b/internal/credentials_test.go index 7b62295..8d90858 100644 --- a/internal/credentials_test.go +++ b/internal/credentials_test.go @@ -33,7 +33,6 @@ func TestCredentials(t *testing.T) { if err == nil || err.Error() != msg { t.Fatal("Wrong error") } - shouldHaveLogged(t, msg) cr.setValues("username", "suse") prepareLogger() @@ -42,7 +41,6 @@ func TestCredentials(t *testing.T) { if err == nil || err.Error() != msg { t.Fatal("Wrong error") } - shouldHaveLogged(t, msg) cr.setValues("password", "1234") err = cr.afterParseCheck() diff --git a/internal/error.go b/internal/error.go index c308ae8..45a3759 100644 --- a/internal/error.go +++ b/internal/error.go @@ -14,6 +14,10 @@ package containersuseconnect +import ( + "fmt" +) + const ( // GetCredentialsError indicates a failure to retrieve or parse // credentials @@ -36,10 +40,22 @@ const ( // SuseConnectError is a custom error type allowing us to distinguish between // different error kinds via the `ErrorCode` field type SuseConnectError struct { - ErrorCode int - message string + Code int + Err error } func (s *SuseConnectError) Error() string { - return s.message + return s.Err.Error() +} + +func (s *SuseConnectError) Unwrap() error { + return s.Err +} + +func NewSuseConnectError(errorCode int, format string, params ...interface{}) *SuseConnectError { + err := fmt.Errorf(format, params...) + return &SuseConnectError{ + Code: errorCode, + Err: err, + } } diff --git a/internal/installed_product.go b/internal/installed_product.go index 4912d62..1d3ca96 100644 --- a/internal/installed_product.go +++ b/internal/installed_product.go @@ -61,7 +61,7 @@ func parseInstalledProduct(reader io.Reader) (InstalledProduct, error) { err := xml.Unmarshal(xmlData, &p) if err != nil { return InstalledProduct{}, - loggedError(InstalledProductError, "Can't parse base product file: %v", err.Error()) + NewSuseConnectError(InstalledProductError, "Can't parse base product file: %v", err.Error()) } return p, nil } @@ -69,13 +69,13 @@ func parseInstalledProduct(reader io.Reader) (InstalledProduct, error) { // Read the product file from the standard location func readInstalledProduct(provider ProductProvider) (InstalledProduct, error) { if _, err := os.Stat(provider.Location()); os.IsNotExist(err) { - return InstalledProduct{}, loggedError(InstalledProductError, "No base product detected") + return InstalledProduct{}, NewSuseConnectError(InstalledProductError, "No base product detected") } xmlFile, err := os.Open(provider.Location()) if err != nil { return InstalledProduct{}, - loggedError(InstalledProductError, "Can't open base product file: %v", err.Error()) + NewSuseConnectError(InstalledProductError, "Can't open base product file: %v", err.Error()) } defer xmlFile.Close() diff --git a/internal/logger.go b/internal/logger.go index c0bf734..09c1f44 100644 --- a/internal/logger.go +++ b/internal/logger.go @@ -15,8 +15,6 @@ package containersuseconnect import ( - "fmt" - "log" "os" ) @@ -47,14 +45,3 @@ func GetLoggerFile() *os.File { } return os.Stderr } - -// Log the given formatted string with its parameters, and return it -// as a new error. -func loggedError(errorCode int, format string, params ...interface{}) *SuseConnectError { - msg := fmt.Sprintf(format, params...) - log.Print(msg) - return &SuseConnectError{ - ErrorCode: errorCode, - message: msg, - } -} diff --git a/internal/products.go b/internal/products.go index d92a3ae..dac1295 100644 --- a/internal/products.go +++ b/internal/products.go @@ -55,8 +55,7 @@ func fixRepoUrlsForRMT(p *Product) error { for i := range p.Repositories { repourl, err := url.Parse(p.Repositories[i].URL) if err != nil { - loggedError(RepositoryError, "Unable to parse repository URL: %s - %v", p.Repositories[i].URL, err) - return err + return NewSuseConnectError(RepositoryError, "Unable to parse repository URL: %s - %v", p.Repositories[i].URL, err) } params := repourl.Query() if params.Get("credentials") == "" { @@ -83,7 +82,7 @@ func parseProducts(reader io.Reader) ([]Product, error) { data, err := io.ReadAll(reader) if err != nil { return products, - loggedError(RepositoryError, "Can't read product information: %v", err.Error()) + NewSuseConnectError(RepositoryError, "Can't read product information: %v", err.Error()) } // Depending on which API was used the JSON we get passed contains @@ -106,7 +105,7 @@ func parseProducts(reader io.Reader) ([]Product, error) { } if err != nil { return products, - loggedError(RepositoryError, "Can't read product information: %v - %s", err.Error(), data) + NewSuseConnectError(RepositoryError, "Can't read product information: %v - %s", err.Error(), data) } return products, nil } @@ -129,7 +128,7 @@ func requestProductsFromRegCodeOrSystem(data SUSEConnectData, regCode string, req, err := http.NewRequest("GET", data.SccURL, nil) if err != nil { return products, - loggedError(NetworkError, "Could not connect with registration server: %v\n", err) + NewSuseConnectError(NetworkError, "Could not connect with registration server: %v\n", err) } values := req.URL.Query() @@ -164,7 +163,7 @@ func requestProductsFromRegCodeOrSystem(data SUSEConnectData, regCode string, } } return products, - loggedError(SubscriptionServerError, "Unexpected error while retrieving products with regCode %s: %s", regCode, resp.Status) + NewSuseConnectError(SubscriptionServerError, "Unexpected error while retrieving products with regCode %s: %s", regCode, resp.Status) } return parseProducts(resp.Body) diff --git a/internal/setup_test.go b/internal/setup_test.go index 9142213..5f60d02 100644 --- a/internal/setup_test.go +++ b/internal/setup_test.go @@ -17,9 +17,6 @@ package containersuseconnect import ( "bytes" "log" - "regexp" - "strings" - "testing" ) // Handy functions to be used by the test suite. @@ -33,19 +30,3 @@ func prepareLogger() { logged = bytes.NewBuffer([]byte{}) log.SetOutput(logged) } - -// Make sure that the logged string matches the given expected string. -func shouldHaveLogged(t *testing.T, str string) { - original := logged.String() - if strings.TrimSpace(original) == "" { - t.Fatal("Nothing has been logged.\n") - } - - // The logged string includes the timestamp, get rid of it. - re := regexp.MustCompile("^\\d{4}/\\d{2}/\\d{2} \\d{2}:\\d{2}:\\d{2}\\s") - logStr := strings.TrimSpace(re.ReplaceAllString(original, "")) - - if strings.TrimSpace(str) != logStr { - t.Fatalf("Should have logged: %v, not %v\n", str, logStr) - } -} diff --git a/internal/subscriptions.go b/internal/subscriptions.go index 5ff772c..ad22de6 100644 --- a/internal/subscriptions.go +++ b/internal/subscriptions.go @@ -45,7 +45,7 @@ func requestRegcodes(data SUSEConnectData, credentials Credentials) ([]string, e req, err := http.NewRequest("GET", data.SccURL, nil) if err != nil { return codes, - loggedError(NetworkError, "Could not connect with registration server: %v\n", err) + NewSuseConnectError(NetworkError, "Could not connect with registration server: %v\n", err) } req.URL.Path = "/connect/systems/subscriptions" @@ -71,7 +71,7 @@ func requestRegcodes(data SUSEConnectData, credentials Credentials) ([]string, e if resp.StatusCode != 200 { return codes, - loggedError(SubscriptionServerError, "Unexpected error while retrieving regcode: %s", resp.Status) + NewSuseConnectError(SubscriptionServerError, "Unexpected error while retrieving regcode: %s", resp.Status) } subscriptions, err := parseSubscriptions(resp.Body) @@ -83,9 +83,10 @@ func requestRegcodes(data SUSEConnectData, credentials Credentials) ([]string, e if strings.ToUpper(subscription.Status) != "EXPIRED" { codes = append(codes, subscription.RegCode) } else { - loggedError(SubscriptionServerError, "Skipping regCode: %s -- expired.", subscription.RegCode) + log.Println(NewSuseConnectError(SubscriptionServerError, "Skipping regCode: %s -- expired.", subscription.RegCode)) } } + return codes, err } @@ -96,15 +97,15 @@ func parseSubscriptions(reader io.Reader) ([]Subscription, error) { data, err := io.ReadAll(reader) if err != nil { - return subscriptions, loggedError(SubscriptionError, "Can't read subscriptions information: %v", err.Error()) + return subscriptions, NewSuseConnectError(SubscriptionError, "Can't read subscriptions information: %v", err.Error()) } err = json.Unmarshal(data, &subscriptions) if err != nil { - return subscriptions, loggedError(SubscriptionError, "Can't read subscription: %v", err.Error()) + return subscriptions, NewSuseConnectError(SubscriptionError, "Can't read subscription: %v", err.Error()) } if len(subscriptions) == 0 { - return subscriptions, loggedError(SubscriptionError, "Got 0 subscriptions") + return subscriptions, NewSuseConnectError(SubscriptionError, "Got 0 subscriptions") } return subscriptions, nil }