-
Notifications
You must be signed in to change notification settings - Fork 5
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
Enable user profile hce 266 #137
Conversation
config/hcp.go
Outdated
if c.profile.GetOrganizationID() == "" || c.profile.GetProjectID() == "" { | ||
return fmt.Errorf("organization ID and project ID must be non-empty") | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is it best to make organizationID and projectID optional as I'm doing here? Or would it be best to throw an error on client initialization to enforce those fields as a requirement on the config struct? Would it be appropriate as well to allow the environment field to be optional or otherwise assign a default value for the field in the profile if not assigned?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's keep these optional. But I would add some validation that ensures that if one of them has a value, the other one also has a value. Since they don't make sense without each other.
config/env.go
Outdated
|
||
envVarHCPProjectID = "HCP_PROJECT_ID" | ||
|
||
envVarHCPEnvironment = "HCP_ENVIRONMENT" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
After some discussion today, let's remove the HCP environment setting from the profile.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent work on this! I had a few comments to address, but it's very close.
config/env.go
Outdated
@@ -52,6 +58,18 @@ const ( | |||
// It will not fail if no or only part of the variables are present. | |||
func FromEnv() HCPConfigOption { | |||
return func(config *hcpConfig) error { | |||
// Read user profile information from the environment, the values will only be |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would move this block to the bottom of FromEnv()
. The client credentials/authURL checks are more critical to the SDK to function and should fail early, whereas profile variables should always be optional.
config/new.go
Outdated
@@ -74,6 +75,7 @@ func NewHCPConfig(opts ...HCPConfigOption) (HCPConfig, error) { | |||
Scopes: []string{"openid", "offline_access"}, | |||
}, | |||
session: &auth.UserSession{}, | |||
profile: profile.UserProfile{}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's make this a pointer, so we're always mutating the same object if the profile needs to change.
profile: profile.UserProfile{}, | |
profile: &profile.UserProfile{}, |
config/with.go
Outdated
// WithProfile is an option that can be used to provide a custom Profile struct. | ||
// | ||
// This should only be necessary for development purposes. | ||
func WithProfile(organizationID, projectID, hcpEnvironment string) HCPConfigOption { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like a good opportunity for using the Profile interface as a single parameter, so we don't have to keep updating this function signature every time we change what's in the profile.
profile/profile.go
Outdated
hcpEnvironment string | ||
} | ||
|
||
//TODO: Do we need to include comments here or is it covered since we comment the function definitions in the interface? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the comments might be more helpful next to their actual function definition, rather than within the interface.
profile/profile.go
Outdated
"os" | ||
) | ||
|
||
type Profile interface { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One more question, more philosophical so I'll leave it up to you. Right now I'm not sure I see the need for a Profile interface. I think we could get away with just the UserProfile struct with the methods defined below. It's true that we could keep the interface and add a mock implementation, the way we did with Session. But where would we use that mock implementation? Would it provide anything useful? This package just seems straightforward enough to not require much code 🤔
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmmm, I'd think it might be best as well to stick with just using the struct since I'm finding myself maneuvering a bit to find utility for a mock implementation. I agree that since the methods are mostly managing the struct properties, the interface might be a bit overkill. I can go ahead and make that tweak then!
profile/profile.go
Outdated
} | ||
|
||
func (p *UserProfile) SetOrganizationID(id string) error { | ||
err := os.Setenv(envVarHCPOrganizationID, id) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
RE: our earlier discussion, we don't want to overwrite environment variables, only read them. Setting the org ID should only involve setting the value in the UserProfile struct.
profile/profile.go
Outdated
package profile | ||
|
||
const ( | ||
envVarHCPOrganizationID = "HCP_ORGANIZATION_ID" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These constants are already defined in env.go
config/env.go
Outdated
if hcpOrganizationIDOK && hcpProjectIDOK { | ||
userProfile := profile.UserProfile{OrganizationID: hcpOrganizationID, ProjectID: hcpProjectID} | ||
if err := apply(config, WithProfile(&userProfile)); err != nil { | ||
return fmt.Errorf("failed to set profile fields from environment variables (%s, %s): %w", envVarHCPOrganizationID, envVarHCPProjectID, err) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For consistency with the errors above:
return fmt.Errorf("failed to set profile fields from environment variables (%s, %s): %w", envVarHCPOrganizationID, envVarHCPProjectID, err) | |
return fmt.Errorf("failed to configure profile based on environment variables (%s, %s): %w", envVarHCPOrganizationID, envVarHCPProjectID, err) |
(bonus ask: fix the basd
typo I just noticed in those other errors 😅)
@@ -84,6 +85,13 @@ type hcpConfig struct { | |||
// session is responsible for getting an access token fron our identity provider. | |||
// A mock can be used in tests. | |||
session auth.Session | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
a bonus for this PR or a follow-up: let's use a Session pointer
config/hcp.go
Outdated
@@ -84,6 +85,13 @@ type hcpConfig struct { | |||
// session is responsible for getting an access token fron our identity provider. | |||
// A mock can be used in tests. | |||
session auth.Session | |||
|
|||
// profile is user's the org id, project id, and application environment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// profile is user's the org id, project id, and application environment | |
// profile is the user's organization ID and project ID |
config/new_test.go
Outdated
@@ -36,6 +37,8 @@ func TestNew_Options(t *testing.T) { | |||
WithPortalURL("https://my-portal:1234"), | |||
WithAPI("my-api:2345", &tls.Config{}), | |||
WithSCADA("my-scada:3456", &tls.Config{}), | |||
// considering that some fields now are optional...should that mean I should be passing in the Profile struct instance directly here (empty or otherwise)? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the purpose of this test is to show all the options that are available to be used, so personally I would populate the UserProfile with a project ID and org ID and then assert that the values are set correctly down below.
config/with.go
Outdated
|
||
// WithProfile is an option that can be used to provide a custom UserProfile struct. | ||
// | ||
// This should only be necessary for development purposes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// This should only be necessary for development purposes. |
profile/profile.go
Outdated
// UserProfile implements the Profile interface. | ||
type UserProfile struct { | ||
|
||
// hcpOrganizationID is the user's organization ID. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// hcpOrganizationID is the user's organization ID. | |
// OrganizationID is the user's organization ID. |
profile/profile.go
Outdated
// hcpOrganizationID is the user's organization ID. | ||
OrganizationID string | ||
|
||
// hcpProjectID is the user's project ID. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// hcpProjectID is the user's project ID. | |
// ProjectID is the user's project ID. |
profile/profile.go
Outdated
ProjectID string | ||
} | ||
|
||
// getOrganizationID() retrieves a user's organization ID. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// getOrganizationID() retrieves a user's organization ID. | |
// GetOrganizationID() retrieves a user's organization ID. |
profile/profile.go
Outdated
return p.OrganizationID | ||
} | ||
|
||
// getProjectID() retrieves the user's project ID. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// getProjectID() retrieves the user's project ID. | |
// GetProjectID() retrieves the user's project ID. |
profile/profile.go
Outdated
envVarHCPProjectID = "HCP_PROJECT_ID" | ||
) | ||
|
||
// UserProfile implements the Profile interface. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't really true anymore, so let's make this comment more descriptive
🛠️ Description
This PR includes a profile library to enable users setting organization ID, project ID, and development environment on their client configuration instance. With the current changes, users may set profile values via environment variables. Next steps include introducing support for saving profile values optionally in a
profile.json
file saved in a location chosen by the user.🔗 External Links
HCE-266 - 'profile' library to handle setting org ID, project ID, environment
👍 Definition of Done