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

feat: register with content templates #168

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ BUILDROOT
RPMS
SOURCES
SRPMS
.idea
5 changes: 5 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,11 @@ func main() {
Usage: "register with `KEY`",
Aliases: []string{"a"},
},
&cli.StringSliceFlag{
Name: "content-template",
Usage: "register with `CONTENT_TEMPLATE`",
Aliases: []string{"c"},
},
&cli.StringFlag{
Name: "server",
Hidden: true,
Expand Down
34 changes: 27 additions & 7 deletions rhsm.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import (
"github.com/godbus/dbus/v5"
)

const EnvTypeContentTemplate = "content-template"

func getConsumerUUID() (string, error) {
conn, err := dbus.SystemBus()
if err != nil {
Expand Down Expand Up @@ -85,7 +87,7 @@ func unpackOrgs(s string) ([]string, error) {
// registerUsernamePassword tries to register system against candlepin server (Red Hat Management Service)
// username and password are mandatory. When organization is not obtained, then this method
// returns list of available organization and user can select one organization from the list.
func registerUsernamePassword(username, password, organization, serverURL string) ([]string, error) {
func registerUsernamePassword(username, password, organization string, environments []string, environmentType string, serverURL string) ([]string, error) {
var orgs []string
if serverURL != "" {
if err := configureRHSM(serverURL); err != nil {
Expand Down Expand Up @@ -132,6 +134,14 @@ func registerUsernamePassword(username, password, organization, serverURL string
return orgs, err
}

options := make(map[string]string)
if len(environments) != 0 {
options["environment_names"] = strings.Join(environments, ",")
options["environment_type"] = environmentType

}
options["enable_content"] = "true"

if err := privConn.Object(
"com.redhat.RHSM1",
"/com/redhat/RHSM1/Register").Call(
Expand All @@ -140,7 +150,7 @@ func registerUsernamePassword(username, password, organization, serverURL string
organization,
username,
password,
map[string]string{"enable_content": "true"},
options,
map[string]string{},
locale).Err; err != nil {

Expand Down Expand Up @@ -183,7 +193,7 @@ func registerUsernamePassword(username, password, organization, serverURL string
return orgs, nil
}

func registerActivationKey(orgID string, activationKeys []string, serverURL string) error {
func registerActivationKey(orgID string, activationKeys []string, environments []string, environmentType string, serverURL string) error {
if serverURL != "" {
if err := configureRHSM(serverURL); err != nil {
return fmt.Errorf("cannot configure RHSM: %w", err)
Expand Down Expand Up @@ -229,14 +239,21 @@ func registerActivationKey(orgID string, activationKeys []string, serverURL stri
return err
}

options := make(map[string]string)
if len(environments) != 0 {
options["environment_names"] = strings.Join(environments, ",")
options["environment_type"] = environmentType

}

if err := privConn.Object(
"com.redhat.RHSM1",
"/com/redhat/RHSM1/Register").Call(
"com.redhat.RHSM1.Register.RegisterWithActivationKeys",
dbus.Flags(0),
orgID,
activationKeys,
map[string]string{},
options,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The registration using activation key & org & content templates does not work.

You cannot do it so simple, when activation key(s) are used for registration. You cannot use environment names as environment IDs. You have to get IDs from environment names in the same way as you do it for registration using username & password.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The GetEnvironments() method requires username and password, so the intention here was to assume that the given values are the IDs.

But now I am wondering if using activation keys with templates should be prevented? The ID is not really usable in the case of content templates. @ptoscano wrote the ticket he want to provide some input.

Copy link
Contributor

@jirihnidek jirihnidek Dec 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, you are right. Getting environments requires username and password...

Hmm, It would be easy to simply say that content templates are supported only with username and password authentication, but user experience would not be the best.

Other approach: requiring environment name for username & password and requiring environment ID (typically some UUID) for activation key authentication would be very very confusing.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The problem here is due to the difference in behaviour of two types of "credentials": username & password, activation key(s) (AKs) + org id:

  • username & password are real credentials: they are used when authenticating to Candlepin (via HTTP Basic Auth), and thus the identity of the caller is immediately known even before the code of the actual endpoint is run
  • AKs are "credentials", yes, however they are actually metadata passed when calling the POST /consumers endpoint. They currently cannot be used for anything else than that

Environments have been historically handled in a "special way": so far they are Katello-only feature, and for reasons I'm not even sure about they were implemented in Katello directly (since Katello proxies the communication to its Candlepin instance) as part of AKs. When connecting directly to Candlepin there is (or better, used to be) no possibility to specify environments with AKs. Hence, if the IDs and names of AKs are different, they don't generally matter in a Katello setup.

Strictly speaking, the org ID has the same inconsistency as environments:

  • sub-man does resolve the org name from the available ones when registering with username & password, or even what is passed via --org
  • sub-man uses --org as-is when registering with AKs

The above inconsistency for org ID practically has not been an issue because:

  • when registering to Hosted, the org ID and names are the same (numeric strings)
  • when registering to Katello, the automated registration ways such as the global registration take care of pre-filling the script to run (and the script uses AKs)
  • registration to Katello via username & password is not commonly done

Because of the above, IMHO there is not much that can be done to avoid the inconsistencies from Candlepin; my recommendation is to:

  • resolve content templates names -> IDs when registering via username & password, as that is usually done manually (and thus the user better expect user-facing bits)
  • assume that the specified content templates are IDs when registering via AKs

Copy link
Contributor

@jirihnidek jirihnidek Dec 4, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because of the above, IMHO there is not much that can be done to avoid the inconsistencies from Candlepin; my recommendation is to:

  • resolve content templates names -> IDs when registering via username & password, as that is usually done
    manually (and thus the user better expect user-facing bits)
  • assume that the specified content templates are IDs when registering via AKs

No, I will never accept anything like this! Because it would very confusing user experience. If this proposal was implemented, then we would see many customer cases related to this inconsistency (environment names for username & password, environment IDs for activation-keys & orgs).

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have been thinking about this problem and I think that cleanest solution is to use environment IDs in both cases (authentication using username & password, authentication using activation-key & org). Could environment IDs be some e.g. UUID. Yes, it could happen. Do users care? Not really, because if environment names were supported, then they would probably copy paste environment names to command line anyway. So, it does not matter, what they copy paste.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A candlepin dev pointed out that candlepin can natively accept the environment name:

Candlepin supports using either environment names or ids during registration. Just pass it like this in the consumer payload:
{
  "environments": [
     {
       "name": "my_environment_name"
       }
    ]
 }

Any reason to not go that route?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is probably doable, but something like this would be necessary for subscription-manager first: candlepin/subscription-manager#3480

map[string]string{},
locale).Err; err != nil {
return unpackRHSMError(err)
Expand Down Expand Up @@ -407,6 +424,7 @@ func registerRHSM(ctx *cli.Context) (string, error) {
password := ctx.String("password")
organization := ctx.String("organization")
activationKeys := ctx.StringSlice("activation-key")
contentTemplates := ctx.StringSlice("content-template")

if len(activationKeys) == 0 {
if username == "" {
Expand Down Expand Up @@ -440,13 +458,15 @@ func registerRHSM(ctx *cli.Context) (string, error) {
err = registerActivationKey(
organization,
ctx.StringSlice("activation-key"),
contentTemplates,
EnvTypeContentTemplate,
ctx.String("server"))
} else {
var orgs []string
if organization != "" {
_, err = registerUsernamePassword(username, password, organization, ctx.String("server"))
_, err = registerUsernamePassword(username, password, organization, contentTemplates, EnvTypeContentTemplate, ctx.String("server"))
} else {
orgs, err = registerUsernamePassword(username, password, "", ctx.String("server"))
orgs, err = registerUsernamePassword(username, password, "", contentTemplates, EnvTypeContentTemplate, ctx.String("server"))
/* When organization was not specified using CLI option --organization, and it is
required, because user is member of more than one organization, then ask for
the organization. */
Expand Down Expand Up @@ -481,7 +501,7 @@ func registerRHSM(ctx *cli.Context) (string, error) {
}

// Try to register once again with given organization
_, err = registerUsernamePassword(username, password, organization, ctx.String("server"))
_, err = registerUsernamePassword(username, password, organization, contentTemplates, EnvTypeContentTemplate, ctx.String("server"))
}
}
}
Expand Down
Loading