Skip to content

Commit

Permalink
Merge branch 'main' into feature-addregCodeWays
Browse files Browse the repository at this point in the history
  • Loading branch information
paragjain0910 authored Nov 28, 2024
2 parents 7bf5ca6 + d818538 commit 316503a
Show file tree
Hide file tree
Showing 14 changed files with 251 additions and 31 deletions.
11 changes: 9 additions & 2 deletions build/packaging/suseconnect-ng.changes
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
-------------------------------------------------------------------
Fri Sep 13 15:56:05 UTC 2024 - Miquel Sabate Sola <[email protected]>
Thu Nov 14 11:01:05 UTC 2024 - Miquel Sabaté Solà <[email protected]>

- IN PROGRESS: 1.13
- Update version to 1.13:
- Integrating uptime-tracker
- Honor auto-import-gpg-keys flag on migration (bsc#1231328)
- Only send labels if targetting SCC
- Skip the docker auth generation on RMT (bsc#1231185)
- Add --set-labels to register command to set labels at registration time on SCC
- Add a new function to display suse-uptime-tracker version
- Integrate with uptime-tracker ( https://github.com/SUSE/uptime-tracker/ )
- Add a command to show the info being gathered

-------------------------------------------------------------------
Fri Sep 13 14:11:22 UTC 2024 - Miquel Sabate Sola <[email protected]>
Expand Down
3 changes: 3 additions & 0 deletions cmd/suseconnect/connectUsage.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
35 changes: 24 additions & 11 deletions cmd/suseconnect/suseconnect.go
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ func main() {
fsRoot string
namespace string
token string
labels string
product singleStringFlag
instanceDataFile string
listExtensions bool
Expand Down Expand Up @@ -105,6 +106,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", "", "")
Expand Down Expand Up @@ -134,7 +136,7 @@ func main() {
fmt.Printf("URL \"%s\" not valid: %s\n", baseURL, err)
os.Exit(1)
}
connect.CFG.BaseURL = baseURL
connect.CFG.ChangeBaseURL(baseURL)
writeConfig = true
}
if fsRoot != "" {
Expand Down Expand Up @@ -258,11 +260,11 @@ func main() {

fmt.Print(string(out))
} else {
if instanceDataFile != "" && connect.URLDefault() {
if instanceDataFile != "" && connect.CFG.IsScc() {
fmt.Print("Please use --instance-data only in combination ")
fmt.Print("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() {
Expand All @@ -284,13 +286,24 @@ 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.Printf("Problem setting labels for this system: %s\n", err)
}
}
}
}
Expand All @@ -315,7 +328,7 @@ func parseRegistrationToken(token string) {
}

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.")
}
Expand Down
10 changes: 5 additions & 5 deletions cmd/zypper-migration/migration.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
Expand Down Expand Up @@ -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()
Expand All @@ -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
}

Expand Down Expand Up @@ -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
}
Expand Down
14 changes: 14 additions & 0 deletions internal/connect/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Up @@ -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
Expand Up @@ -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
}
Expand All @@ -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
Expand Down
53 changes: 49 additions & 4 deletions internal/connect/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ const (
defaultEnableSystemUptimeTracking = false
)

// Kinds of servers which are supported by SUSEConnect.
type ServerType uint64

const (
UnknownProvider ServerType = iota
SccProvider
RmtProvider
)

// Config holds the config!
type Config struct {
Path string
Expand All @@ -40,10 +49,10 @@ type Config struct {
Email string `json:"email"`
AutoAgreeEULA bool
EnableSystemUptimeTracking bool

NoZypperRefresh bool
AutoImportRepoKeys bool
SkipServiceInstall bool
ServerType ServerType
NoZypperRefresh bool
AutoImportRepoKeys bool
SkipServiceInstall bool
}

// NewConfig returns a Config with defaults
Expand Down Expand Up @@ -84,6 +93,12 @@ func (c Config) Save() error {
func (c *Config) Load() {
f, err := os.Open(c.Path)
if err != nil {
// If we failed at parsing the configuration, we can make further
// assumptions based on the base URL being used.
if c.BaseURL == defaultBaseURL {
c.ServerType = SccProvider
}

util.Debug.Println(err)
return
}
Expand All @@ -92,6 +107,22 @@ func (c *Config) Load() {
util.Debug.Printf("Config after parsing: %+v", c)
}

// Change the base url to be used when talking to the server to the one being
// provided.
func (c *Config) ChangeBaseURL(baseUrl string) {
c.BaseURL = baseUrl

// When making an explicit change of the URL, we can further detect which
// kind of server we are dealing with. For now, let's keep it simple, and if
// it's the defaultBaseURL then we assume it to be SccProvider, otherwise
// RmtProvider.
if c.BaseURL == defaultBaseURL {
c.ServerType = SccProvider
} else {
c.ServerType = RmtProvider
}
}

func parseConfig(r io.Reader, c *Config) {
scanner := bufio.NewScanner(r)
for scanner.Scan() {
Expand Down Expand Up @@ -123,6 +154,11 @@ func parseConfig(r io.Reader, c *Config) {
util.Debug.Printf("Cannot parse line \"%s\" from %s", line, c.Path)
}
}

// Set the server type depending on what we parsed from the configuration.
if c.BaseURL == defaultBaseURL {
c.ServerType = SccProvider
}
}

// MergeJSON merges attributes of jsn that match Config fields
Expand All @@ -131,3 +167,12 @@ func (c *Config) MergeJSON(jsn string) error {
util.Debug.Printf("Merged options: %+v", c)
return err
}

// Returns true if we detected that the configuration points to SCC.
//
// NOTE: this will be reliable if the configuration file already pointed to SCC,
// but it might need to be filled in upon HTTP requests to further guess if it's
// a Glue instance running on localhost or similar developer-only scenarios.
func (c *Config) IsScc() bool {
return c.ServerType == SccProvider
}
2 changes: 2 additions & 0 deletions internal/connect/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,14 @@ func TestSaveLoad(t *testing.T) {
c1 := NewConfig()
c1.Path = path
c1.AutoAgreeEULA = true
c1.ServerType = UnknownProvider
if err := c1.Save(); err != nil {
t.Fatalf("Unable to write config: %s", err)
}
c2 := NewConfig()
c2.Path = path
c2.Load()
c2.ServerType = UnknownProvider
if !reflect.DeepEqual(c1, c2) {
t.Errorf("got %+v, expected %+v", c2, c1)
}
Expand Down
12 changes: 12 additions & 0 deletions internal/connect/connection.go
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,18 @@ func callHTTP(verb, path string, body []byte, query map[string]string, auth auth
}
defer resp.Body.Close()

// If we failed to detect which server type was being used when loading the
// configuration, we can actually further inspect it via some of the headers
// that are returned by Glue vs RMT. Hence, if the server type is unknown,
// make an educated guess now.
if CFG.ServerType == UnknownProvider {
if api := resp.Header.Get("Scc-Api-Version"); api == sccAPIVersion {
CFG.ServerType = SccProvider
} else {
CFG.ServerType = RmtProvider
}
}

// For each request SCC might update the System token for a given system.
// This will be given through the `System-Token` header, so we have to grab
// this here and store it for the next request.
Expand Down
27 changes: 27 additions & 0 deletions internal/connect/labels.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package connect

import (
"github.com/SUSE/connect-ng/internal/util"
"strings"
)

var (
localSetLabels = setLabels
)

type Label struct {
Name string `json:"name"`
Description string `json:"description,omitempty"`
}

func AssignAndCreateLabels(labels []string) error {
collection := []Label{}

for _, name := range labels {
name = strings.TrimSpace(name)
collection = append(collection, Label{Name: name})
}

util.Debug.Printf(util.Bold("Setting Labels %s"), strings.Join(labels, ","))
return localSetLabels(collection)
}
Loading

0 comments on commit 316503a

Please sign in to comment.