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

Allow full profile updates through the PATCH handler #2990

Merged
merged 5 commits into from
Apr 22, 2024

Conversation

jhrozek
Copy link
Contributor

@jhrozek jhrozek commented Apr 8, 2024

Summary

We used to use a bespoke PATCH handler to update profiles but the PATCH
method only allowed changing alert and remediate. We can make the code
simpler and allow more updates by modifying the existing profile using
the UpdateMask and then just relaying the update to the existing
UpdateProfile method of the Profile service.

Fixes: #2971

Change Type

Mark the type of change your PR introduces:

  • Bug fix (resolves an issue without affecting existing features)
  • Feature (adds new functionality without breaking changes)
  • Breaking change (may impact existing functionalities or require documentation updates)
  • Documentation (updates or additions to documentation)
  • Refactoring or test improvements (no bug fixes or new functionality)

Testing

There are unit tests but you can also test with curl:

Adding a rule to a profile:

curl -XPATCH -H "Authorization: Bearer $MINDER_BEARER_TOKEN"
'http://localhost:8080/api/v1/profile/ce746798-c30c-46f3-8a12-0654747b49ec?context.project=1900c9f6-ef0f-43e4-8af7-108beb72de0e&context.provider=github'
-d '{"name":"dependabot-enabled-profile", "labels":[], "buildEnvironment":[], "repository":[{"type":"dependabot_configured","params":null,"def":{"apply_if_file":"requirements.txt","package_ecosystem":"pypi","schedule_interval":"weekly"},"name":"dependabot_configured"},{"type":"dependabot_configured","params":null,"def":{"apply_if_file":"go.mod","package_ecosystem":"gomod","schedule_interval":"weekly"},"name":"dependabot_configured_go"}], "pullRequest":[], "remediate ":"on", "alert":"off", "type":"", "version":"", "displayName":"gah" }

Remove the rule back:

curl -XPATCH -H "Authorization: Bearer $MINDER_BEARER_TOKEN"
'http://localhost:8080/api/v1/profile/ce746798-c30c-46f3-8a12-0654747b49ec?context.project=1900c9f6-ef0f-43e4-8af7-108beb72de0e&context.provider=github'
-d '{"name":"dependabot-enabled-profile", "labels":[], "buildEnvironment":[], "repository":[{"type":"dependabot_configured","params":null,"def":{"apply_if_file":"requirements.txt","package_ecosystem":"pypi","schedule_interval":"weekly"},"name":"dependabot_configured"}], "pullRequest":[], "remediate":"on", "alert":"off", "type":"", "version":"", "displayName":"gah"}}'

Review Checklist:

  • Reviewed my own code for quality and clarity.
  • Added comments to complex or tricky code sections.
  • Updated any affected documentation.
  • Included tests that validate the fix or feature.
  • Checked that related changes are merged.

@jhrozek jhrozek requested a review from a team as a code owner April 8, 2024 08:33
if err != nil {
return nil, util.UserVisibleError(codes.Internal, "error updating profile: %v", err)
}
for _, attr := range updateMask.Paths {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

should we explicitly disallow patching some fields here? I think it's OK to leave that to the Update but wanted to check explicitly...

Copy link
Contributor

Choose a reason for hiding this comment

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

Subscription ID is one which we do not want to allow to be changed.

@coveralls
Copy link

coveralls commented Apr 8, 2024

Coverage Status

coverage: 49.69% (+1.8%) from 47.861%
when pulling ebc3a37 on jhrozek:patch
into 5964281 on stacklok:main.

Copy link
Contributor

@dmjb dmjb left a comment

Choose a reason for hiding this comment

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

Leaving this as "Request changes" until the question around editing bundle profiles is settled.

@dmjb
Copy link
Contributor

dmjb commented Apr 8, 2024

After talking to @jhrozek offline, I understand why that check can be deleted. I would like to see a test added for the bundled subscription ID case though.

jhrozek added a commit to jhrozek/minder that referenced this pull request Apr 9, 2024
I forgot to issue a profile update message on patching a profile. Since
the profile update message is currently nicely encapsulated in
`sendNewProfileEvent` which is a private method of the Profile service
and we already have PR mindersec#2990 that would move all of the Patch
functionality to that service and remove the current Patch handler
implementation, I decided to create a bespoke message for now.

This code will be removed once we merge PR mindersec#2990.
jhrozek added a commit to jhrozek/minder that referenced this pull request Apr 9, 2024
I forgot to issue a profile update message on patching a profile. Since
the profile update message is currently nicely encapsulated in
`sendNewProfileEvent` which is a private method of the Profile service
and we already have PR mindersec#2990 that would move all of the Patch
functionality to that service and remove the current Patch handler
implementation, I decided to create a bespoke message for now.

This code will be removed once we merge PR mindersec#2990.
jhrozek added a commit that referenced this pull request Apr 9, 2024
I forgot to issue a profile update message on patching a profile. Since
the profile update message is currently nicely encapsulated in
`sendNewProfileEvent` which is a private method of the Profile service
and we already have PR #2990 that would move all of the Patch
functionality to that service and remove the current Patch handler
implementation, I decided to create a bespoke message for now.

This code will be removed once we merge PR #2990.
@jhrozek
Copy link
Contributor Author

jhrozek commented Apr 9, 2024

After talking to @jhrozek offline, I understand why that check can be deleted. I would like to see a test added for the bundled subscription ID case though.

I added a test and moved a bunch of the patch handler to profile service.

btw this PR is NOT important to merge at this moment

evankanderson
evankanderson previously approved these changes Apr 22, 2024
Copy link
Member

@evankanderson evankanderson left a comment

Choose a reason for hiding this comment

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

Only nits; I like the new code much better! Thanks!

ctx context.Context,
projectID uuid.UUID,
profileID uuid.UUID,
subscriptionID uuid.UUID,
Copy link
Member

Choose a reason for hiding this comment

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

Do we want to allow PATCH with subscriptions? If not, could we drop this argument?

Copy link
Member

Choose a reason for hiding this comment

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

(It looks like we pass uuid.Nil as the argument to UpdateProfile from the other gRPC endpoint, so it looks like we the PATCH-on-Subscription codepath is effectively dead...)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

probably not, you're right. We'll likely to only update subscription profiles through a replace, not patch.

Comment on lines 320 to 321
oldReflect := oldProfilePb.ProtoReflect()
patchReflect := patchPb.ProtoReflect()
Copy link
Member

Choose a reason for hiding this comment

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

Do you want to do these inside the for loop?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

thanks, fixed

updateMask *fieldmaskpb.FieldMask,
qtx db.Querier,
) (*minderv1.Profile, error) {
oldProfilePb, err := getProfilePBFromDB(ctx, profileID, projectID, qtx)
Copy link
Member

Choose a reason for hiding this comment

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

Do you want to call old "base" here? It's slightly confusing, because we go through the steps and then pass old to the Update method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

thanks, renamed

}

func patchProfilePb(oldProfilePb, patchPb *minderv1.Profile, updateMask *fieldmaskpb.FieldMask) {
// if there is no update mask, return the old profile. The grpc-rest gateway always sets the update mask
Copy link
Member

Choose a reason for hiding this comment

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

This is confusing, as it says "return", but there's no return value. Maybe you mean "an empty field mask means apply no changes"?

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 think the original version of the code was trying to return a modified profile instead and I didn't update the comment.
Thanks, fixed.

We used to use a bespoke PATCH handler to update profiles but the PATCH
method only allowed changing alert and remediate. We can make the code
simpler and allow more updates by modifying the existing profile using
the UpdateMask and then just relaying the update to the existing
UpdateProfile method of the Profile service.

Examples:

Adding a rule to a profile:
```
curl -XPATCH -H "Authorization: Bearer $MINDER_BEARER_TOKEN"
'http://localhost:8080/api/v1/profile/ce746798-c30c-46f3-8a12-0654747b49ec?context.project=1900c9f6-ef0f-43e4-8af7-108beb72de0e&context.provider=github'
-d '{"name":"dependabot-enabled-profile", "labels":[], "buildEnvironment":[], "repository":[{"type":"dependabot_configured","params":null,"def":{"apply_if_file":"requirements.txt","package_ecosystem":"pypi","schedule_interval":"weekly"},"name":"dependabot_configured"},{"type":"dependabot_configured","params":null,"def":{"apply_if_file":"go.mod","package_ecosystem":"gomod","schedule_interval":"weekly"},"name":"dependabot_configured_go"}], "pullRequest":[], "remediate ":"on", "alert":"off", "type":"", "version":"", "displayName":"gah" }
```

Remove the rule back:
```
curl -XPATCH -H "Authorization: Bearer $MINDER_BEARER_TOKEN"
'http://localhost:8080/api/v1/profile/ce746798-c30c-46f3-8a12-0654747b49ec?context.project=1900c9f6-ef0f-43e4-8af7-108beb72de0e&context.provider=github'
-d '{"name":"dependabot-enabled-profile", "labels":[], "buildEnvironment":[], "repository":[{"type":"dependabot_configured","params":null,"def":{"apply_if_file":"requirements.txt","package_ecosystem":"pypi","schedule_interval":"weekly"},"name":"dependabot_configured"}], "pullRequest":[], "remediate":"on", "alert":"off", "type":"", "version":"", "displayName":"gah"}}'
```

Fixes: mindersec#2971
@jhrozek
Copy link
Contributor Author

jhrozek commented Apr 22, 2024

@evankanderson thanks for the review, I fixed your comments in separate patches for easier review.

@jhrozek jhrozek merged commit 17412a2 into mindersec:main Apr 22, 2024
20 checks passed
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.

Support full profile updates with PATCH
4 participants