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

Changes to onboarding token api to include quota in body #2600

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

rchikatw
Copy link
Contributor

@rchikatw rchikatw commented May 8, 2024

  • Passing nil quota to function means unlimited quota
  • Quota will be sent as HTTP request body
  • Sample request: {"quota": "123Mi"} or no body

services/types.go Outdated Show resolved Hide resolved
@nb-ohad
Copy link
Contributor

nb-ohad commented May 8, 2024

@rchikatw Why use 0 or unlimited/unbound? why not have it a pointer and use nil instead?

@@ -19,14 +19,15 @@ import (

// GenerateOnboardingToken generates a token valid for a duration of "tokenLifetimeInHours".
// The token content is predefined and signed by the private key which'll be read from supplied "privateKeyPath".
func GenerateOnboardingToken(tokenLifetimeInHours int, privateKeyPath string) (string, error) {
func GenerateOnboardingToken(tokenLifetimeInHours int, privateKeyPath, quota string) (string, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Quota shouldn't be a string, it should be wither a number (assuming a specific unit) or a size object.
Also it should preferably be optional (a pointer) so we can generate a token that does not have a quota

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I have considered this as a string because in ClusterResourceQuota we also need to mention the unit. It will be better if we get that from the UI itself.

Copy link
Contributor

Choose a reason for hiding this comment

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

Even it that case this should not be a string but a k8s quantity object

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay will do the changes according

@@ -3,4 +3,5 @@ package services
type OnboardingTicket struct {
ID string `json:"id"`
ExpirationDate int64 `json:"expirationDate,string"`
Quota string `json:"quota"`
Copy link
Contributor

Choose a reason for hiding this comment

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

Quota shouldn't be a string, it should be wither a number (assuming a specific unit) or a size object.
Also it should preferably be optional (a pointer) so we can generate a token that does not have a quota

@@ -16,14 +16,15 @@ const (
func HandleMessage(w http.ResponseWriter, r *http.Request, tokenLifetimeInHours int) {
switch r.Method {
case "POST":
handlePost(w, tokenLifetimeInHours)
quota := r.URL.Query().Get("quota")
Copy link
Contributor

Choose a reason for hiding this comment

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

If this is a Post request the common practice is to use the request body to convey payload not the URL query line

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Okay. i will consider that.

Copy link
Contributor

openshift-ci bot commented May 14, 2024

[APPROVALNOTIFIER] This PR is NOT APPROVED

This pull-request has been approved by: rchikatw
Once this PR has been reviewed and has the lgtm label, please ask for approval from nb-ohad. For more information see the Kubernetes Code Review Process.

The full list of commands accepted by this bot can be found here.

Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@rchikatw rchikatw force-pushed the quotachanges branch 4 times, most recently from aabb7fd to 45e90bc Compare May 15, 2024 16:26
controllers/util/provider.go Show resolved Hide resolved
Comment on lines 21 to 24
type Message struct {
Quota string `json:"quota"`
}
var quota Message
Copy link
Contributor

Choose a reason for hiding this comment

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

  1. I don't think we should be processing the body before sending it to handlePost cause in the switch you are only deciding the method
  2. No need to create a type if we are not going to initialize it, can get away w/ var. Unrelated one, for encoding & decoding you only need fields to be exported not necessarily the struct.
Suggested change
type Message struct {
Quota string `json:"quota"`
}
var quota Message
var quota = struct { Quota string `json:"quota"`}{}

services/ux-backend/handlers/onboardingtokens/handler.go Outdated Show resolved Hide resolved
}
var quota Message
err := json.NewDecoder(r.Body).Decode(&quota)
if err != nil {
Copy link
Contributor

Choose a reason for hiding this comment

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

nit, can perform err handling in the same statement/line.

@rchikatw rchikatw force-pushed the quotachanges branch 3 times, most recently from 094ccac to f7803db Compare May 22, 2024 06:52
Comment on lines 72 to 74
// When length is 0 that means either request body is empty or
// quota parameter is not passed in that case quota is considerred
// to be unlimited
Copy link
Contributor

Choose a reason for hiding this comment

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

no, we already established that empty body represents unlimited quota and better not to club that w/ empty value for quota.

btw, as mentioned in the comment, I don't think quota is a param.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, correct, we are only checking with the request body only. I missed updating this comment.

default:
handleUnsupportedMethod(w, r)
}
}

func handlePost(w http.ResponseWriter, tokenLifetimeInHours int) {
if onboardingToken, err := util.GenerateOnboardingToken(tokenLifetimeInHours, onboardingPrivateKeyFilePath); err != nil {
func handlePost(w http.ResponseWriter, tokenLifetimeInHours int, r *http.Request) {
Copy link
Contributor

Choose a reason for hiding this comment

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

could you pls swap 2nd & 3rd param?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@rchikatw rchikatw changed the title Changes to onboarding token api to add quota parameter Changes to onboarding token api to include quota in body May 27, 2024
in body

Signed-off-by: rchikatw <rchikatw@redhat.com>
Copy link
Contributor

openshift-ci bot commented May 27, 2024

@rchikatw: The following test failed, say /retest to rerun all failed tests or /retest-required to rerun all mandatory failed tests:

Test name Commit Details Required Rerun command
ci/prow/ocs-operator-bundle-e2e-aws 7c796cb link true /test ocs-operator-bundle-e2e-aws

Full PR test history. Your PR dashboard.

Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes-sigs/prow repository. I understand the commands that are listed here.

tokenExpirationDate := time.Now().
Add(time.Duration(tokenLifetimeInHours) * time.Hour).
Unix()

payload, err := json.Marshal(services.OnboardingTicket{
ID: uuid.New().String(),
ExpirationDate: tokenExpirationDate,
StorageQuota: quantity,
Copy link
Contributor

Choose a reason for hiding this comment

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

@rchikatw How does the marshaled form of quantity look like? Is it just a string containing value and unit? is it unmarshalabe?.

Also, why do we need a complex data type in the token? why can we just normalize the quantity to an int representing the number of MiB? This will be beneficial for users who want to read the token content in other languages (e.g. Bash, Phyton, etc.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

After marshal quantity will look like <size><Unit> ex: 1Gi. Is it unmarshalabe? -> Yes it.

Why do we need a complex type for quantity? In your previous comment you suggested to using k8s quantity Refer: #2600 (comment)

Advantages of using quantity:

  1. we can validate quota unit is valid (using parseQuantity function) no need to add manual checks for the unit
  2. You have also suggested using a pointer so we can generate a token that does not have a quota. Refer Changes to onboarding token api to include quota in body #2600 (comment)

If we don't want to use complex data type then we should not allow the user to select the unit in the UI and make quantity int that should solve our problem not using complex data type.

Let me know which approach should I use k8s quantity or int

ExpirationDate int64 `json:"expirationDate,string"`
ID string `json:"id"`
ExpirationDate int64 `json:"expirationDate,string"`
StorageQuota *resource.Quantity `json:"storageQuota,omitempty"`
Copy link
Contributor

Choose a reason for hiding this comment

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

Can we please keep the token fields as non-complex types? in this case I would expect an int

Suggested change
StorageQuota *resource.Quantity `json:"storageQuota,omitempty"`
StorageQuotaInMiB *int `json:"storageQuotaInMiB,omitempty"`

func handlePost(w http.ResponseWriter, tokenLifetimeInHours int) {
if onboardingToken, err := util.GenerateOnboardingToken(tokenLifetimeInHours, onboardingPrivateKeyFilePath); err != nil {
func handlePost(w http.ResponseWriter, r *http.Request, tokenLifetimeInHours int) {
quantity, err := getQuantity(r)
Copy link
Contributor

Choose a reason for hiding this comment

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

Why Quantity? are we expecting the user to select both size and unit? or are we expecting a standardized unit?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes from the UI design slides user can select both size and unit.

Comment on lines +63 to +83
func getQuantity(r *http.Request) (*resource.Quantity, error) {
var quota = struct {
StorageQuota string `json:"storageQuota"`
}{}
err := json.NewDecoder(r.Body).Decode(&quota)
if err != nil && err.Error() != "EOF" {
return nil, err
}

// When length is 0 that means request body is empty and quota
// is unlimited
if len(quota.StorageQuota) == 0 {
return nil, nil
}
quantity, err := resource.ParseQuantity(quota.StorageQuota)
if err != nil {
return nil, fmt.Errorf("invalid StorageQuota value sent in request body: %v", err)
}
return &quantity, nil

}
Copy link
Contributor

Choose a reason for hiding this comment

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

I am not sure about the logic here. If the UI is passing us a quantity I would expect to see 2 fields in the body. One for the unit and one for the size. But if there is an assumption about the unit then there is no reason to parse it as a quantity, it should be just an int.

Anything else just add unnecessary complexity to the system

Copy link
Contributor Author

Choose a reason for hiding this comment

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

UI will be passed as a string in the body with the format 1Gi/1Mi/1Ti to indicate quantity. I will decode and parse it to validate the correctness of the passed value. Invalid values will look like 1/1gi/1ti.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants