- Added whitelist loopback quick toggle
- Fixed plugin exit stuck bug
This commit is contained in:
Toby Chui
2025-03-09 17:02:48 +08:00
parent 23d4df1ed7
commit 3e57a90bb6
17 changed files with 417 additions and 52 deletions

View File

@@ -151,6 +151,7 @@ func (m *Manager) handlePluginSTDOUT(pluginID string, line string) {
m.Log("["+thisPlugin.Spec.Name+":"+strconv.Itoa(processID)+"] "+line, nil)
}
// StopPlugin stops a plugin, it is garanteed that the plugin is stopped after this function
func (m *Manager) StopPlugin(pluginID string) error {
plugin, ok := m.LoadedPlugins.Load(pluginID)
if !ok {
@@ -224,15 +225,3 @@ func (m *Manager) PluginStillRunning(pluginID string) bool {
}
return plugin.(*Plugin).process.ProcessState == nil
}
// BlockUntilAllProcessExited blocks until all the plugins processes have exited
func (m *Manager) BlockUntilAllProcessExited() {
m.LoadedPlugins.Range(func(key, value interface{}) bool {
plugin := value.(*Plugin)
if m.PluginStillRunning(value.(*Plugin).Spec.ID) {
//Wait for the plugin to exit
plugin.process.Wait()
}
return true
})
}

View File

@@ -10,6 +10,7 @@ package plugins
*/
import (
"encoding/json"
"errors"
"fmt"
"net/http"
@@ -34,14 +35,24 @@ func NewPluginManager(options *ManagerOptions) *Manager {
os.MkdirAll(options.PluginDir, 0755)
}
//Create the plugin config file if not exists
if !utils.FileExists(options.PluginGroupsConfig) {
js, _ := json.Marshal(map[string][]string{})
err := os.WriteFile(options.PluginGroupsConfig, js, 0644)
if err != nil {
options.Logger.PrintAndLog("plugin-manager", "Failed to create plugin group config file", err)
}
}
//Create database table
options.Database.NewTable("plugins")
return &Manager{
LoadedPlugins: sync.Map{},
tagPluginMap: sync.Map{},
tagPluginList: make(map[string][]*Plugin),
Options: options,
LoadedPlugins: sync.Map{},
tagPluginMap: sync.Map{},
tagPluginListMutex: sync.RWMutex{},
tagPluginList: make(map[string][]*Plugin),
Options: options,
}
}
@@ -76,6 +87,14 @@ func (m *Manager) LoadPluginsFromDisk() error {
}
}
if m.Options.PluginGroupsConfig != "" {
//Load the plugin groups from the config file
err = m.LoadPluginGroupsFromConfig()
if err != nil {
m.Log("Failed to load plugin groups", err)
}
}
//Generate the static forwarder radix tree
m.UpdateTagsToPluginMaps()
@@ -156,9 +175,6 @@ func (m *Manager) Close() {
}
return true
})
//Wait until all loaded plugin process are terminated
m.BlockUntilAllProcessExited()
}
/* Plugin Functions */

View File

@@ -24,6 +24,7 @@ func (m *Manager) UpdateTagsToPluginMaps() {
}
//build the plugin list for each tag
m.tagPluginListMutex.Lock()
m.tagPluginList = make(map[string][]*Plugin)
for tag, pluginIds := range m.Options.PluginGroups {
for _, pluginId := range pluginIds {
@@ -35,6 +36,7 @@ func (m *Manager) UpdateTagsToPluginMaps() {
m.tagPluginList[tag] = append(m.tagPluginList[tag], plugin)
}
}
m.tagPluginListMutex.Unlock()
}
// GenerateForwarderRadixTree generates the radix tree for static forwarders

99
src/mod/plugins/tags.go Normal file
View File

@@ -0,0 +1,99 @@
package plugins
import (
"encoding/json"
"os"
)
/*
Plugin Tags
This file contains the tags that are used to match the plugin tag
to the one on HTTP proxy rule. Once the tag is matched, the plugin
will be enabled on that given rule.
*/
// LoadTagPluginMap loads the plugin map into the manager
// This will only load the plugin tags to option.PluginGroups map
// to push the changes to runtime, call UpdateTagsToPluginMaps()
func (m *Manager) LoadPluginGroupsFromConfig() error {
m.Options.pluginGroupsMutex.RLock()
defer m.Options.pluginGroupsMutex.RUnlock()
//Read the config file
rawConfig, err := os.ReadFile(m.Options.PluginGroupsConfig)
if err != nil {
return err
}
var config map[string][]string
err = json.Unmarshal(rawConfig, &config)
if err != nil {
return err
}
//Reset m.tagPluginList
m.Options.PluginGroups = config
return nil
}
// AddPluginToTag adds a plugin to a tag
func (m *Manager) AddPluginToTag(tag string, pluginID string) error {
m.Options.pluginGroupsMutex.RLock()
defer m.Options.pluginGroupsMutex.RUnlock()
//Check if the plugin exists
_, err := m.GetPluginByID(pluginID)
if err != nil {
return err
}
//Add to m.Options.PluginGroups
pluginList, ok := m.Options.PluginGroups[tag]
if !ok {
pluginList = []string{}
}
pluginList = append(pluginList, pluginID)
m.Options.PluginGroups[tag] = pluginList
//Update to runtime
m.UpdateTagsToPluginMaps()
//Save to file
return m.savePluginTagMap()
}
// RemovePluginFromTag removes a plugin from a tag
func (m *Manager) RemovePluginFromTag(tag string, pluginID string) error {
// Check if the plugin exists in Options.PluginGroups
m.Options.pluginGroupsMutex.RLock()
defer m.Options.pluginGroupsMutex.RUnlock()
pluginList, ok := m.Options.PluginGroups[tag]
if !ok {
return nil
}
// Remove the plugin from the list
for i, id := range pluginList {
if id == pluginID {
pluginList = append(pluginList[:i], pluginList[i+1:]...)
break
}
}
m.Options.PluginGroups[tag] = pluginList
// Update to runtime
m.UpdateTagsToPluginMaps()
// Save to file
return m.savePluginTagMap()
}
// savePluginTagMap saves the plugin tag map to the config file
func (m *Manager) savePluginTagMap() error {
m.Options.pluginGroupsMutex.RLock()
defer m.Options.pluginGroupsMutex.RUnlock()
js, _ := json.Marshal(m.Options.PluginGroups)
return os.WriteFile(m.Options.PluginGroupsConfig, js, 0644)
}

View File

@@ -45,6 +45,7 @@ func (m *Manager) HandleRoute(w http.ResponseWriter, r *http.Request, tags []str
wg.Add(1)
go func(thisTag string) {
defer wg.Done()
m.tagPluginListMutex.RLock()
for _, plugin := range m.tagPluginList[thisTag] {
if plugin.Enabled && plugin.Spec.DynamicCaptureSniff != "" && plugin.Spec.DynamicCaptureIngress != "" {
mutex.Lock()
@@ -52,6 +53,7 @@ func (m *Manager) HandleRoute(w http.ResponseWriter, r *http.Request, tags []str
mutex.Unlock()
}
}
m.tagPluginListMutex.RUnlock()
}(tag)
}
wg.Wait()

View File

@@ -29,19 +29,24 @@ type Plugin struct {
}
type ManagerOptions struct {
PluginDir string //The directory where the plugins are stored
PluginGroups map[string][]string //The plugin groups,key is the tag name and the value is an array of plugin IDs
PluginDir string //The directory where the plugins are stored
PluginGroups map[string][]string //The plugin groups,key is the tag name and the value is an array of plugin IDs
PluginGroupsConfig string //The group / tag configuration file, if set the plugin groups will be loaded from this file
/* Runtime */
SystemConst *zoraxyPlugin.RuntimeConstantValue
CSRFTokenGen func(*http.Request) string `json:"-"` //The CSRF token generator function
Database *database.Database `json:"-"`
Logger *logger.Logger `json:"-"`
/* Internal */
pluginGroupsMutex sync.RWMutex //Mutex for the pluginGroups
}
type Manager struct {
LoadedPlugins sync.Map //Storing *Plugin
tagPluginMap sync.Map //Storing *radix.Tree for each plugin tag
tagPluginList map[string][]*Plugin //Storing the plugin list for each tag, only concurrent READ is allowed
Options *ManagerOptions
LoadedPlugins sync.Map //Storing *Plugin
tagPluginMap sync.Map //Storing *radix.Tree for each plugin tag
tagPluginListMutex sync.RWMutex //Mutex for the tagPluginList
tagPluginList map[string][]*Plugin //Storing the plugin list for each tag, only concurrent READ is allowed
Options *ManagerOptions
}