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

chore(cmp): refactor and test plugin discovery #20217

Open
wants to merge 7 commits into
base: master
Choose a base branch
from

Conversation

crenshaw-dev
Copy link
Member

@crenshaw-dev crenshaw-dev commented Oct 3, 2024

Plugin discovery logic is complicated and has resulted in difficult to diagnose bugs.

This PR makes a few changes:

  1. Deprecate RepositoryResponse.IsDiscoveryEnabled field. No code actually uses that field anymore since we do a pre-flight CMP request to determine whether it has discovery enabled.
  2. Break cmpSupports into namedCMPSupports and unnamedCMPSupports. Reading cmpSupports was difficult, because the namedPlugin parameter implied more than the name suggested. Specifically, when namedPlugin == false, we could assume that the app spec did not contain a plugin.name. By using two different functions with two different names, we make it very clear that one is for use when the plugin name is specified, and the other is used when the plugin name is not specified.
  3. Make the CMP client constructor mock-able. It requires some code overhead, but discovery is so deeply entangled with CMP server API calls that I think it's worth it.
  4. Test the behavior of both unnamedCMPSupports and namedCMPSupports. I'd love to test all the way up to DetectConfigManagementPlugin, but I think that would require more mock logic than I'm comfortable with.
  5. Use securejoin instead of our internal path traversal detection logic - it's more robust.

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Copy link

bunnyshell bot commented Oct 3, 2024

🔴 Preview Environment stopped on Bunnyshell

See: Environment Details | Pipeline Logs

Available commands (reply to this comment):

  • 🔵 /bns:start to start the environment
  • 🚀 /bns:deploy to redeploy the environment
  • /bns:delete to remove the environment

Copy link

bunnyshell bot commented Oct 3, 2024

✅ Preview Environment created on Bunnyshell but will not be auto-deployed

See: Environment Details

Available commands (reply to this comment):

  • 🚀 /bns:deploy to deploy the environment

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
Copy link

codecov bot commented Oct 3, 2024

Codecov Report

Attention: Patch coverage is 79.48718% with 16 lines in your changes missing coverage. Please review.

Please upload report for BASE (master@b2091e3). Learn more about missing BASE report.
Report is 1 commits behind head on master.

Files with missing lines Patch % Lines
util/app/discovery/discovery.go 76.47% 11 Missing and 5 partials ⚠️
Additional details and impacted files
@@            Coverage Diff            @@
##             master   #20217   +/-   ##
=========================================
  Coverage          ?   55.98%           
=========================================
  Files             ?      322           
  Lines             ?    44569           
  Branches          ?        0           
=========================================
  Hits              ?    24951           
  Misses            ?    17047           
  Partials          ?     2571           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@crenshaw-dev crenshaw-dev marked this pull request as ready for review October 3, 2024 19:11
@crenshaw-dev crenshaw-dev requested a review from a team as a code owner October 3, 2024 19:11
@crenshaw-dev
Copy link
Member Author

@CodiumAI-Agent /review

@CodiumAI-Agent
Copy link

CodiumAI-Agent commented Oct 3, 2024

PR Reviewer Guide 🔍

(Review updated until commit cf553e6)

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Refactoring
The refactoring of the DetectConfigManagementPlugin function to improve modularity and testability is significant. Ensure that the new structure does not introduce any regressions or misunderstandings about how plugins are detected and managed.

Logic Change
The changes in how discovery is handled (removing isDiscoveryEnabled and using isSupported only) could affect existing functionality. Verify that these changes align with the intended use cases and do not negatively impact existing workflows.

util/app/discovery/discovery.go Outdated Show resolved Hide resolved
// check if the given plugin supports the repo
conn, cmpClient, connFound = cmpSupports(ctx, pluginSockFilePath, appPath, repoPath, fmt.Sprintf("%v.sock", pluginName), env, tarExcludedGlobs, true)
if !connFound {
c := newSocketCMPClientConstructorForPluginName(pluginSockFilePath, pluginName)
Copy link
Collaborator

Choose a reason for hiding this comment

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

Not sure if I am understanding it correctly but this PR introduces the new CMPClientConstructor interface however it seems that it is impossible to use a different implementation than the socketCMPClientConstructor. Not sure if this was intentional, but it would be great if we could enable the flexibility to use different CMPClientConstructor implementations. This could help simplifying the CMP as a Service PR.

Copy link
Member Author

Choose a reason for hiding this comment

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

it seems that it is impossible to use a different implementation

The only other implementation is the mock implementation used for testing.

The other option would have been to have (un)namedCMPSupports accept a getClient function, but the code just looked ickier. I could go either way.

util/app/discovery/discovery.go Outdated Show resolved Hide resolved
Copy link
Contributor

@pradithya pradithya left a comment

Choose a reason for hiding this comment

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

I wonder if the e2e test include plugin by default?

@@ -297,11 +297,11 @@ func (s *Service) matchRepositoryGeneric(stream MatchRepositoryStream) error {
return fmt.Errorf("match repository error receiving stream: %w", err)
}

isSupported, isDiscoveryEnabled, err := s.matchRepository(bufferedCtx, workDir, metadata.GetEnv(), metadata.GetAppRelPath())
Copy link
Contributor

Choose a reason for hiding this comment

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

This is nice! I felt isDiscoveryEnabled and cfg.IsDiscoveryConfigured were redundant upon reading these code the first time.

// If discovery isn't configured, assume the CMP supports the plugin since it was explicitly named.
return cmpClient, closer
}
usePlugin := cmpSupportsForClient(ctx, cmpClient, appPath, repoPath, env, tarExcludedGlobs)
Copy link
Contributor

Choose a reason for hiding this comment

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

For named plugin, do you think we should still invoke matchRepositoryCMP? For efficiency, should we just invoke matchRepositoryCMP for unnamed plugin that has discovery functionality configured?

Copy link
Member Author

Choose a reason for hiding this comment

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

We have to. Even if a plugin is named, if it failed any configured discovery rule, we'll refuse to use it. We did this when introducing plugin.name because some people may have used discovery rules as a security mechanism, and we wouldn't want to allow users to bypass that check.

Copy link
Contributor

Choose a reason for hiding this comment

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

Noted!

matchRepoStream, err := client.MatchRepository(ctx, grpc_retry.Disable())
// namedCMPSupports will return a client for the named plugin if it supports the given repository. It will also return
// a closer for the connection.
func namedCMPSupports(ctx context.Context, c CMPClientConstructor, appPath, repoPath string, env, tarExcludedGlobs []string) (pluginclient.ConfigManagementPluginServiceClient, io.Closer) {
Copy link
Contributor

Choose a reason for hiding this comment

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

nitpick on the consistency of return types ordering, DetectConfigManagementPlugin return io.Closer, pluginclient.ConfigManagementPluginServiceClient but the newly introduced functions (namedCMPSupports , unnamedCMPSupports, and getClientForFilepath) return pluginclient.ConfigManagementPluginServiceClient, io.Closer .

Copy link
Member Author

Choose a reason for hiding this comment

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

Updated DetectConfigManagementPlugin. I think client, closer is the more common convention in the code base.


isSupported, isDiscoveryEnabled, err := matchRepositoryCMP(ctx, appPath, repoPath, cmpClient, env, tarExcludedGlobs)
// matchRepositoryCMP will send the repoPath to the cmp-server. The cmp-server will
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this docs is for matchRepositoryCMP function

Signed-off-by: Michael Crenshaw <350466+crenshaw-dev@users.noreply.github.com>
@pradithya
Copy link
Contributor

LGTM

@CodiumAI-Agent
Copy link

Persistent review updated to latest commit cf553e6

// Deprecated: Use the IsDiscoveryConfigured field from the CheckPluginConfigurationResponse instead. Argo CD no
// longer uses an expensive MatchRepository service to determine if discovery is enabled. Instead, we
// make a relatively cheap pre-flight request to the CheckPluginConfiguration service. We'll leave this
// field here to avoid errors on mismatched repo-server and cmp-server versions.
bool isDiscoveryEnabled = 2;
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we use a strict parsing mode for protobuf where unrecognized fields would trigger error?

@AJChandler AJChandler mentioned this pull request Nov 20, 2024
14 tasks
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.

5 participants