Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Write diagnostics/error messages to stderr #272

Open
wants to merge 12 commits into
base: main
Choose a base branch
from
Open
3 changes: 3 additions & 0 deletions cmd/suseconnect/connectUsage.txt
Original file line number Diff line number Diff line change
@@ -15,6 +15,9 @@ Manage subscriptions at https://scc.suse.com
be registered.
Relates that product to the specified subscription,
and enables software repositories for that product.
--set-labels [LABELS]
Set labels in SCC when the product is registered.
To add multiple labels, separate them with commas.
-d, --de-register De-registers the system and base product, or in
conjunction with --product, a single extension, and
removes all its services installed by SUSEConnect.
95 changes: 54 additions & 41 deletions cmd/suseconnect/suseconnect.go
Original file line number Diff line number Diff line change
@@ -68,6 +68,7 @@ func main() {
fsRoot string
namespace string
token string
labels string
product singleStringFlag
instanceDataFile string
listExtensions bool
@@ -104,6 +105,7 @@ func main() {
flag.StringVar(&namespace, "namespace", "", "")
flag.StringVar(&token, "regcode", "", "")
flag.StringVar(&token, "r", "", "")
flag.StringVar(&labels, "set-labels", "", "")
flag.StringVar(&instanceDataFile, "instance-data", "", "")
flag.StringVar(&email, "email", "", "")
flag.StringVar(&email, "e", "", "")
@@ -130,15 +132,15 @@ func main() {
connect.CFG.Load()
if baseURL != "" {
if err := validateURL(baseURL); err != nil {
fmt.Printf("URL \"%s\" not valid: %s\n", baseURL, err)
fmt.Fprintf(os.Stderr, "URL \"%s\" not valid: %s\n", baseURL, err)
os.Exit(1)
}
connect.CFG.BaseURL = baseURL
connect.CFG.ChangeBaseURL(baseURL)
writeConfig = true
}
if fsRoot != "" {
if fsRoot[0] != '/' {
fmt.Println("The path specified in the --root option must be absolute.")
fmt.Fprintln(os.Stderr, "The path specified in the --root option must be absolute.")
os.Exit(1)
}
connect.CFG.FsRoot = fsRoot
@@ -153,9 +155,9 @@ func main() {
}
if product.isSet {
if p, err := connect.SplitTriplet(product.value); err != nil {
fmt.Print("Please provide the product identifier in this format: ")
fmt.Print("<internal name>/<version>/<architecture>. You can find ")
fmt.Print("these values by calling: 'SUSEConnect --list-extensions'\n")
fmt.Fprint(os.Stderr, "Please provide the product identifier in this format: ")
fmt.Fprint(os.Stderr, "<internal name>/<version>/<architecture>. You can find ")
fmt.Fprint(os.Stderr, "these values by calling: 'SUSEConnect --list-extensions'\n")
os.Exit(1)
} else {
connect.CFG.Product = p
@@ -259,15 +261,15 @@ func main() {

fmt.Print(string(out))
} else {
if instanceDataFile != "" && connect.URLDefault() {
fmt.Print("Please use --instance-data only in combination ")
fmt.Print("with --url pointing to your RMT or SMT server\n")
if instanceDataFile != "" && connect.CFG.IsScc() {
fmt.Fprint(os.Stderr, "Please use --instance-data only in combination ")
fmt.Fprint(os.Stderr, "with --url pointing to your RMT or SMT server\n")
os.Exit(1)
} else if connect.URLDefault() && token == "" && product.value == "" {
} else if connect.CFG.IsScc() && token == "" && product.value == "" {
flag.Usage()
os.Exit(1)
} else if isSumaManaged() {
fmt.Println("This system is managed by SUSE Manager / Uyuni, do not use SUSEconnect.")
fmt.Fprintln(os.Stderr, "This system is managed by SUSE Manager / Uyuni, do not use SUSEconnect.")
os.Exit(1)
} else {

@@ -285,26 +287,37 @@ func main() {
}

err := connect.Register(jsonFlag)
if jsonFlag && err != nil {
out := connect.RegisterOut{Success: false, Message: err.Error()}
str, _ := json.Marshal(&out)
fmt.Println(string(str))
os.Exit(1)
} else {
exitOnError(err)
if err != nil {
if jsonFlag {
out := connect.RegisterOut{Success: false, Message: err.Error()}
str, _ := json.Marshal(&out)
fmt.Println(string(str))
os.Exit(1)
} else {
exitOnError(err)
}
}

// After successful registration we try to set labels if we are
// targetting SCC.
if connect.CFG.IsScc() && len(labels) > 0 {
err := connect.AssignAndCreateLabels(strings.Split(labels, ","))
if err != nil && !jsonFlag {
fmt.Fprintf(os.Stderr, "Problem setting labels for this system: %s\n", err)
}
}
}
}
if writeConfig {
if err := connect.CFG.Save(); err != nil {
fmt.Printf("Problem writing configuration: %s\n", err)
fmt.Fprintf(os.Stderr, "Problem writing configuration: %s\n", err)
os.Exit(1)
}
}
}

func maybeBrokenSMTError() error {
if !connect.URLDefault() && !connect.UpToDate() {
if !connect.CFG.IsScc() && !connect.UpToDate() {
return fmt.Errorf("Your Registration Proxy server doesn't support this function. " +
"Please update it and try again.")
}
@@ -316,55 +329,55 @@ func exitOnError(err error) {
return
}
if ze, ok := err.(zypper.ZypperError); ok {
fmt.Println(ze)
fmt.Fprintln(os.Stderr, ze)
os.Exit(ze.ExitCode)
}
if ue, ok := err.(*url.Error); ok && errors.Is(ue, syscall.ECONNREFUSED) {
fmt.Println("Error:", err)
fmt.Fprintln(os.Stderr, "Error:", err)
os.Exit(64)
}
if je, ok := err.(connect.JSONError); ok {
if err := maybeBrokenSMTError(); err != nil {
fmt.Println(err)
fmt.Fprintln(os.Stderr, err)
} else {
fmt.Print("Error: Cannot parse response from server\n")
fmt.Println(je)
fmt.Fprint(os.Stderr, "Error: Cannot parse response from server\n")
fmt.Fprintln(os.Stderr, je)
}
os.Exit(66)
}
if ae, ok := err.(connect.APIError); ok {
if ae.Code == http.StatusUnauthorized && connect.IsRegistered() {
fmt.Print("Error: Invalid system credentials, probably because the ")
fmt.Print("registered system was deleted in SUSE Customer Center. ")
fmt.Print("Check ", connect.CFG.BaseURL, " whether your system appears there. ")
fmt.Print("If it does not, please call SUSEConnect --cleanup and re-register this system.\n")
fmt.Fprint(os.Stderr, "Error: Invalid system credentials, probably because the ")
fmt.Fprint(os.Stderr, "registered system was deleted in SUSE Customer Center. ")
fmt.Fprint(os.Stderr, "Check ", connect.CFG.BaseURL, " whether your system appears there. ")
fmt.Fprint(os.Stderr, "If it does not, please call SUSEConnect --cleanup and re-register this system.\n")
} else if err := maybeBrokenSMTError(); err != nil {
fmt.Println(err)
fmt.Fprintln(os.Stderr, err)
} else {
fmt.Println(ae)
fmt.Fprintln(os.Stderr, ae)
}
os.Exit(67)
}
switch err {
case connect.ErrSystemNotRegistered:
fmt.Print("Deregistration failed. Check if the system has been ")
fmt.Print("registered using the --status-text option or use the ")
fmt.Print("--regcode parameter to register it.\n")
fmt.Fprint(os.Stderr, "Deregistration failed. Check if the system has been ")
fmt.Fprint(os.Stderr, "registered using the --status-text option or use the ")
fmt.Fprint(os.Stderr, "--regcode parameter to register it.\n")
os.Exit(69)
case connect.ErrListExtensionsUnregistered:
fmt.Print("To list extensions, you must first register the base product, ")
fmt.Print("using: SUSEConnect -r <registration code>\n")
fmt.Fprint(os.Stderr, "To list extensions, you must first register the base product, ")
fmt.Fprint(os.Stderr, "using: SUSEConnect -r <registration code>\n")
os.Exit(1)
case connect.ErrBaseProductDeactivation:
fmt.Print("Can not deregister base product. Use SUSEConnect -d to deactivate ")
fmt.Print("the whole system.\n")
fmt.Fprint(os.Stderr, "Can not deregister base product. Use SUSEConnect -d to deactivate ")
fmt.Fprint(os.Stderr, "the whole system.\n")
os.Exit(70)
case connect.ErrPingFromUnregistered:
fmt.Print("Error sending keepalive: ")
fmt.Print("System is not registered. Use the --regcode parameter to register it.\n")
fmt.Fprint(os.Stderr, "Error sending keepalive: ")
fmt.Fprint(os.Stderr, "System is not registered. Use the --regcode parameter to register it.\n")
os.Exit(71)
default:
fmt.Printf("SUSEConnect error: %s\n", err)
fmt.Fprintf(os.Stderr, "SUSEConnect error: %s\n", err)
os.Exit(1)
}
}
10 changes: 5 additions & 5 deletions cmd/zypper-migration/migration.go
Original file line number Diff line number Diff line change
@@ -497,8 +497,8 @@ func compareEditions(left, right string) int {
return 0
}

func cleanupProductRepos(p connect.Product, force bool) error {
productPackages, err := zypper.FindProductPackages(p.Name)
func cleanupProductRepos(p connect.Product, force, autoImportRepoKeys bool) error {
productPackages, err := zypper.FindProductPackages(p.Name, autoImportRepoKeys)
if err != nil {
return err
}
@@ -564,7 +564,7 @@ func isSUSEService(service zypper.ZypperService) bool {
// adds/removes services to match target state
// disables obsolete repos
// returns base product version string
func migrateSystem(migration connect.MigrationPath, forceDisableRepos bool) (string, error) {
func migrateSystem(migration connect.MigrationPath, forceDisableRepos, autoImportRepoKeys bool) (string, error) {
var baseProductVersion string

systemServices, _ := zypper.InstalledServices()
@@ -587,7 +587,7 @@ func migrateSystem(migration connect.MigrationPath, forceDisableRepos bool) (str
}
}

if err := cleanupProductRepos(p, forceDisableRepos); err != nil {
if err := cleanupProductRepos(p, forceDisableRepos, autoImportRepoKeys); err != nil {
return baseProductVersion, err
}

@@ -678,7 +678,7 @@ func applyMigration(migration connect.MigrationPath, systemProducts []connect.Pr
}
}

baseProductVersion, err := migrateSystem(migration, nonInteractive || forceDisableRepos)
baseProductVersion, err := migrateSystem(migration, nonInteractive || forceDisableRepos, autoImportRepoKeys)
if err != nil {
return fsInconsistent, err
}
14 changes: 14 additions & 0 deletions internal/connect/api.go
Original file line number Diff line number Diff line change
@@ -339,3 +339,17 @@ func installerUpdates(product Product) ([]zypper.Repository, error) {
}
return repos, nil
}

func setLabels(labels []Label) error {
var payload struct {
Labels []Label `json:"labels"`
}
payload.Labels = labels
body, err := json.Marshal(payload)

if err != nil {
return err
}
_, err = callHTTP("POST", "/connect/systems/labels", body, nil, authSystem)
return err
}
43 changes: 43 additions & 0 deletions internal/connect/api_test.go
Original file line number Diff line number Diff line change
@@ -375,3 +375,46 @@ func TestMakeSysInfoBody(t *testing.T) {
assert.NoError(err)
assert.Equal(expectedBody, string(body))
}

func TestSetLabelsOk(t *testing.T) {
assert := assert.New(t)

testLabels := []Label{
Label{Name: "label1"},
Label{Name: "label2"},
}

setRootToTmp()
credentials.CreateTestCredentials("", "", CFG.FsRoot, t)

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Write(util.ReadTestFile("set_labels.json", t))
}))
defer ts.Close()

CFG.BaseURL = ts.URL

err := setLabels(testLabels)
assert.NoError(err)
}

func TestSetLabelsError(t *testing.T) {
assert := assert.New(t)
testLabels := []Label{
Label{Name: "label1"},
Label{Name: "label2"},
}

setRootToTmp()
credentials.CreateTestCredentials("", "", CFG.FsRoot, t)

ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
errMsg := "{\"status\":422,\"error\":\"Cannot set more than 10 labels on system: test-system\",\"type\":\"error\",\"localized_error\":\"Es können nicht mehr als 10 labels per System gesetzt werden: test-system\"}"
http.Error(w, errMsg, http.StatusUnprocessableEntity)
}))
defer ts.Close()
CFG.BaseURL = ts.URL

err := setLabels(testLabels)
assert.ErrorContains(err, "Es können")
}
17 changes: 9 additions & 8 deletions internal/connect/client.go
Original file line number Diff line number Diff line change
@@ -387,8 +387,14 @@ func announceOrUpdate(quiet bool) error {
}

if err = cred.CreateCredentials(login, password, "", cred.SystemCredentialsPath(CFG.FsRoot)); err == nil {
util.Debug.Print("\nAdding SUSE registry system authentication configuration ...")
setupRegistryAuthentication(login, password)
// If the user is authenticated against the SCC, then setup the Docker
// Registry configuration for the system. Otherwise, if the system is
// behind a proxy (e.g. RMT), this step might fail and it's best to
// avoid it (see bsc#1231185).
if CFG.IsScc() {
util.Debug.Print("\nAdding SUSE registry system authentication configuration ...")
setupRegistryAuthentication(login, password)
}
}
return err
}
@@ -405,14 +411,9 @@ func UpToDate() bool {
return upToDate()
}

// URLDefault returns true if using https://scc.suse.com
func URLDefault() bool {
return CFG.BaseURL == defaultBaseURL
}

func printInformation(action string, jsonOutput bool) {
var server string
if URLDefault() {
if CFG.IsScc() {
server = "SUSE Customer Center"
} else {
server = "registration proxy " + CFG.BaseURL
Loading