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: experimental plugin setup #269

Merged
merged 9 commits into from
Sep 19, 2023

Conversation

coopernetes
Copy link
Contributor

@coopernetes coopernetes commented Aug 27, 2023

This is a working PR for adding experimental plugin support to git-proxy.

  • ability to define custom behaviour as part of the proxied requests. This can be both runtime (user specified) as well as pre-defined or hard-coded plugins
  • define a minimum viable interface or method for defining this custom behaviour without breaking existing code too much
  • experiment to see if we can use this same method to integrate a call to an Open Policy Agent as described in Open Policy Agent checks #99

Related #47

Misc:

@coopernetes coopernetes force-pushed the experiment/plugin-opa branch from 100e337 to 33b96cd Compare August 27, 2023 16:48
@maoo
Copy link
Member

maoo commented Aug 27, 2023

Quickly reviewed the code, I like the solution .

I'd be curious to see the implementation of a simple hello world plugin, and it seems that you just need one line to invoke a certain function (with a standard signature) to close the loop, if I'm not mistaken.

At that point we should already have a working solution for modularity at application level.

@coopernetes
Copy link
Contributor Author

I'd be curious to see the implementation of a simple hello world plugin, and it seems that you just need one line to invoke a certain function (with a standard signature) to close the loop, if I'm not mistaken.

As part of this PR, I intend to create a trivial example plugin which the PluginManager can detect, load and present for git-proxy to execute against.

In terms of how plugins should expose their functionality, defining it via a function is fine as a first draft. However, I anticipate this can much more complex if we want to start modularizing specific layers such as the database/persistence, audit logs or events and different server types (HTTPS, SSH, email via #61). A single callable function can work for trivial use cases (particularly around checking commit information against a list of "good values" such as repo names, authors, etc).

For the purposes of externalizing the static configuration-based checks (resources/config.json) and providing an OPA option via #99 , we can stick with a simple request/response function that a plugin author can pass in. I do think we want to provide much more facilities for custom behaviour within git-proxy but we can iterate on that.

@coopernetes coopernetes force-pushed the experiment/plugin-opa branch from 33b96cd to f252a6d Compare August 31, 2023 04:48
@coopernetes

This comment was marked as outdated.

@coopernetes coopernetes force-pushed the experiment/plugin-opa branch from 6024ac6 to cc4321c Compare September 4, 2023 15:11
@coopernetes
Copy link
Contributor Author

@JamieSlome I had to bump the nodejs workflow to Node 16.x as an FYI - adding this new dependency requires >= 14

@maoo working "hello world" example plugin as of c381bc5 :

App startup

$ npm run server

> @finos/git-proxy@1.0.0 server
> node index.js

Initializing plugin manager...
Found 1 plugin modules
Loaded 1 plugins
Service Listening on 8080
Listening on 8000

on push, plugin executes

/coopernetes/test-repo.git/info/refs?service=git-receive-pack
recieved
action processed
/coopernetes/test-repo.git/info/refs?service=git-receive-pack
recieved
action processed
/coopernetes/test-repo.git/git-receive-pack
recieved
Found 1, inserting into proxy chain
Inserting plugin [object Object] into chain
This is a message from the helloPlugin!

the remaining steps (checkRepoInAuthorisedList)

[
  {
    project: 'finos',
    name: 'git-proxy',
    url: 'https://github.com/finos/git-proxy.git',
    users: { canPush: [Array], canAuthorise: [Array] },
    _id: 'CoEwvG0trXnXkEfP'
  }
]
coopernetes/test-repo = finos/git-proxy
undefined
not found
checkRepoInAuthorisedList - repo coopernetes/test-repo.git is not in the authorisedList, ending
setting error
checkRepoInAuthorisedList - Rejecting repo coopernetes/test-repo.git not in the authorisedList
action processed
Rejecting repo coopernetes/test-repo.git not in the authorisedList

@JamieSlome
Copy link
Member

@coopernetes - great PR 🎉 The Node bump should be fine, checks and tests are passing 👍

I have reviewed the code and as a first estimation, this is great. As you mention in your code, we can re-think the entry/loading point of the functions into the "chain".

I still need to play around with the code by running this locally, but from your proof of concept above this looks easy enough.

I don't think we need to strive for perfection on how the plugin manager works, so long as it gives us enough room for extensibility and modularity, i.e. expose custom (user-defined) and pre-defined (official/recommended) plugins to the "chain". We can always look to improve the sophistication later on.

src/plugin.js Outdated
Comment on lines 126 to 128
localPlugins = localPlugins.concat(p);
} else {
packagePlugins = packagePlugins.concat(p);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

TODO: simply push a new item into the plugin list instead of concat.

Suggested change
localPlugins = localPlugins.concat(p);
} else {
packagePlugins = packagePlugins.concat(p);
localPlugins.push(p);
} else {
packagePlugins.push(p);

* add Dockerfile & entrypoint script for container deployments
* on proxy startup, read plugin names via environment variable
  `GITPROXY_LOAD_PLUGINS` as a comma-separated list
* on startup, initialize PluginManager which sets a list of plugins
  and loads them via `load-plugin`. Note, this is a no-op atm since
  the actual proxy processors are not calling into plugin functions yet
* add lerna to support multiple npm packages in a single workspace.
  This is necessary to start breaking apart the monolithic git-proxy
  package code into separate packages.
* add HttpPlugin class which takes in a request & response function.
  This class should implement those methods. Note, this is better suited
  to TypeScript which has built-in interfaces.

This doesn't "do" anything yet.

feat: plugin system

re-ordered hardcoded values

work in progress
- fixed bug with async execution of PluginManager initialization.
  Plugins by name or package are first resolved & loaded then we
  do the ProxyPlugin object casting once those run to completion
  via a Promise.all()
- remove list of Node module objects (this.pluginModules) in
  PluginManager. It's never used outside of loadPlugins() so its
  fine to convert to a local var
- pluginManager.plugins was capturing a nested Array ([][]ProxyPlugin)
  so fixed that up.
- working through a bug in parsePackFile action
- upgrade nodejs workflow to 16.x. New dependencies won't build on <14
- pass in the ActionPlugin.exec function as part of the proxy chain
- update JSDoc on ActionPlugin
- add test for creating the PluginLoader
@coopernetes coopernetes marked this pull request as ready for review September 15, 2023 07:46
@coopernetes
Copy link
Contributor Author

Test coverage isn't complete and some of the bits in the PluginLoader (renamed from PluginManager) would be pretty difficult to test since we're dynamically bringing in Node modules directly. Some additional JSDoc notes added and I've removed the hard-coded plugin that would be loaded on startup so that plugins are "opt-in" via the GITPROXY_PLUGIN_* environment variables. Confirmed this is working as intended behaviourally:

PluginLoader isn't invoked

$ npm run server

> @finos/git-proxy@1.0.0 server
> node index.js

Service Listening on 8080
Listening on 8000

PluginLoader running with "hello world" example

$ GITPROXY_PLUGIN_FILES='packages/git-proxy-notify-hello/index.js' npm run server

> @finos/git-proxy@1.0.0 server
> node index.js

Found 1 plugin modules
Loaded 1 plugins
Service Listening on 8080
Listening on 8000
/coopernetes/test-repo.git/info/refs?service=git-receive-pack
recieved
action processed
/coopernetes/test-repo.git/info/refs?service=git-receive-pack
recieved
action processed
/coopernetes/test-repo.git/git-receive-pack
recieved
Found 1, inserting into proxy chain
Inserting plugin [object Object] into chain
This is a message from the HelloPlugin!
[
  {
    project: 'finos',
    name: 'git-proxy',
    url: 'https://github.com/finos/git-proxy.git',
    users: { canPush: [Array], canAuthorise: [Array] },
    _id: 'CoEwvG0trXnXkEfP'
  },
  {
    project: 'finos',
    name: 'test-repo',
    url: 'https://github.com/finos/test-repo.git',
    users: { canPush: [Array], canAuthorise: [Array] },
    _id: 'zTmvyqXUda83ivbA'
  }
]
coopernetes/test-repo = finos/git-proxy
coopernetes/test-repo = finos/test-repo
undefined
not found
checkRepoInAuthorisedList - repo coopernetes/test-repo.git is not in the authorisedList, ending
setting error
checkRepoInAuthorisedList - Rejecting repo coopernetes/test-repo.git not in the authorisedList
action processed
Rejecting repo coopernetes/test-repo.git not in the authorisedList

Ready to merge as a prototype.

- closing in favour of finos#282
- unused lerna config deleted. This would be re-added only if Lerna is
  the tool of choice and when refactoring the existing src into discreet
  packages & workspaces
@JamieSlome
Copy link
Member

@coopernetes - fabulous work! 🎉

Thanks for including some tests. I'll have a play around with the code at some point today but generally looks good to merge.

Copy link
Member

@JamieSlome JamieSlome left a comment

Choose a reason for hiding this comment

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

LGTM! 🍰

@JamieSlome JamieSlome merged commit ea14ce8 into finos:main Sep 19, 2023
@maoo
Copy link
Member

maoo commented Sep 19, 2023

This is huge! Thanks @coopernetes !

coopernetes pushed a commit to coopernetes/git-proxy that referenced this pull request Oct 13, 2023
@coopernetes coopernetes deleted the experiment/plugin-opa branch January 24, 2024 05:20
Psingle20 pushed a commit to Psingle20/git-proxy that referenced this pull request Nov 27, 2024
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.

feat: Add Dockerfile for container-based deploys
3 participants