| 
									
										
										
										
											2015-02-27 20:45:00 +08:00
										 |  |  | package plugins | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							|  |  |  | 	"encoding/json" | 
					
						
							| 
									
										
										
										
											2023-02-28 02:03:55 +08:00
										 |  |  | 	"errors" | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 	"fmt" | 
					
						
							| 
									
										
										
										
											2023-05-08 16:58:47 +08:00
										 |  |  | 	"io" | 
					
						
							| 
									
										
										
										
											2022-12-02 20:46:55 +08:00
										 |  |  | 	"io/fs" | 
					
						
							| 
									
										
										
										
											2023-03-07 23:47:02 +08:00
										 |  |  | 	"path" | 
					
						
							| 
									
										
										
										
											2022-12-02 20:46:55 +08:00
										 |  |  | 	"runtime" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							| 
									
										
										
										
											2023-08-16 16:46:00 +08:00
										 |  |  | 	"sync" | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	"github.com/grafana/grafana-plugin-sdk-go/backend" | 
					
						
							| 
									
										
										
										
											2023-02-28 23:10:27 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-28 18:18:09 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/plugins/auth" | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/plugins/backendplugin" | 
					
						
							|  |  |  | 	"github.com/grafana/grafana/pkg/plugins/backendplugin/pluginextensionv2" | 
					
						
							| 
									
										
										
										
											2023-02-28 23:10:27 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/plugins/log" | 
					
						
							| 
									
										
										
										
											2022-08-10 17:56:48 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/services/org" | 
					
						
							| 
									
										
										
										
											2022-12-02 20:46:55 +08:00
										 |  |  | 	"github.com/grafana/grafana/pkg/util" | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 21:35:49 +08:00
										 |  |  | var ( | 
					
						
							| 
									
										
										
										
											2023-04-20 17:52:59 +08:00
										 |  |  | 	ErrFileNotExist              = errors.New("file does not exist") | 
					
						
							|  |  |  | 	ErrPluginFileRead            = errors.New("file could not be read") | 
					
						
							|  |  |  | 	ErrUninstallInvalidPluginDir = errors.New("cannot recognize as plugin folder") | 
					
						
							| 
									
										
										
										
											2023-05-08 16:58:47 +08:00
										 |  |  | 	ErrInvalidPluginJSON         = errors.New("did not find valid type or id properties in plugin.json") | 
					
						
							| 
									
										
										
										
											2023-09-29 23:20:37 +08:00
										 |  |  | 	ErrUnsupportedAlias          = errors.New("can not set alias in plugin.json") | 
					
						
							| 
									
										
										
										
											2023-03-20 21:35:49 +08:00
										 |  |  | ) | 
					
						
							| 
									
										
										
										
											2022-12-02 20:46:55 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | type Plugin struct { | 
					
						
							|  |  |  | 	JSONData | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-07 23:47:02 +08:00
										 |  |  | 	FS    FS | 
					
						
							|  |  |  | 	Class Class | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// App fields
 | 
					
						
							|  |  |  | 	IncludedInAppID string | 
					
						
							|  |  |  | 	DefaultNavURL   string | 
					
						
							|  |  |  | 	Pinned          bool | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// Signature fields
 | 
					
						
							| 
									
										
										
										
											2024-04-11 22:18:04 +08:00
										 |  |  | 	Signature     SignatureStatus | 
					
						
							|  |  |  | 	SignatureType SignatureType | 
					
						
							|  |  |  | 	SignatureOrg  string | 
					
						
							|  |  |  | 	Parent        *Plugin | 
					
						
							|  |  |  | 	Children      []*Plugin | 
					
						
							|  |  |  | 	Error         *Error | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// SystemJS fields
 | 
					
						
							|  |  |  | 	Module  string | 
					
						
							|  |  |  | 	BaseURL string | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-10 18:44:54 +08:00
										 |  |  | 	Angular AngularMeta | 
					
						
							| 
									
										
										
										
											2023-05-12 18:51:11 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-28 18:18:09 +08:00
										 |  |  | 	ExternalService *auth.ExternalService | 
					
						
							| 
									
										
										
										
											2023-06-26 22:38:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-20 18:00:59 +08:00
										 |  |  | 	Renderer pluginextensionv2.RendererPlugin | 
					
						
							|  |  |  | 	client   backendplugin.Plugin | 
					
						
							|  |  |  | 	log      log.Logger | 
					
						
							| 
									
										
										
										
											2023-06-03 01:46:12 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-17 22:12:05 +08:00
										 |  |  | 	SkipHostEnvVars bool | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-08-16 16:46:00 +08:00
										 |  |  | 	mu sync.Mutex | 
					
						
							| 
									
										
										
										
											2025-03-31 12:38:38 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	Translations map[string]string | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-24 23:45:16 +08:00
										 |  |  | var ( | 
					
						
							|  |  |  | 	_ = backend.CollectMetricsHandler(&Plugin{}) | 
					
						
							|  |  |  | 	_ = backend.CheckHealthHandler(&Plugin{}) | 
					
						
							|  |  |  | 	_ = backend.QueryDataHandler(&Plugin{}) | 
					
						
							|  |  |  | 	_ = backend.CallResourceHandler(&Plugin{}) | 
					
						
							|  |  |  | 	_ = backend.StreamHandler(&Plugin{}) | 
					
						
							|  |  |  | 	_ = backend.AdmissionHandler(&Plugin{}) | 
					
						
							| 
									
										
										
										
											2024-08-16 04:02:21 +08:00
										 |  |  | 	_ = backend.ConversionHandler(&Plugin{}) | 
					
						
							| 
									
										
										
										
											2024-05-24 23:45:16 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-11-10 18:44:54 +08:00
										 |  |  | type AngularMeta struct { | 
					
						
							| 
									
										
										
										
											2025-09-08 18:39:01 +08:00
										 |  |  | 	Detected bool `json:"detected"` | 
					
						
							| 
									
										
										
										
											2023-11-10 18:44:54 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | // JSONData represents the plugin's plugin.json
 | 
					
						
							|  |  |  | type JSONData struct { | 
					
						
							|  |  |  | 	// Common settings
 | 
					
						
							|  |  |  | 	ID           string       `json:"id"` | 
					
						
							|  |  |  | 	Type         Type         `json:"type"` | 
					
						
							|  |  |  | 	Name         string       `json:"name"` | 
					
						
							| 
									
										
										
										
											2023-09-29 23:20:37 +08:00
										 |  |  | 	AliasIDs     []string     `json:"aliasIDs,omitempty"` | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 	Info         Info         `json:"info"` | 
					
						
							|  |  |  | 	Dependencies Dependencies `json:"dependencies"` | 
					
						
							|  |  |  | 	Includes     []*Includes  `json:"includes"` | 
					
						
							|  |  |  | 	State        ReleaseState `json:"state,omitempty"` | 
					
						
							|  |  |  | 	Category     string       `json:"category"` | 
					
						
							|  |  |  | 	HideFromList bool         `json:"hideFromList,omitempty"` | 
					
						
							|  |  |  | 	Preload      bool         `json:"preload"` | 
					
						
							|  |  |  | 	Backend      bool         `json:"backend"` | 
					
						
							|  |  |  | 	Routes       []*Route     `json:"routes"` | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-07 18:30:45 +08:00
										 |  |  | 	// AccessControl settings
 | 
					
						
							| 
									
										
										
										
											2024-07-19 23:16:23 +08:00
										 |  |  | 	Roles      []RoleRegistration `json:"roles,omitempty"` | 
					
						
							|  |  |  | 	ActionSets []ActionSet        `json:"actionSets,omitempty"` | 
					
						
							| 
									
										
										
										
											2022-11-07 18:30:45 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 	// Panel settings
 | 
					
						
							|  |  |  | 	SkipDataQuery bool `json:"skipDataQuery"` | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	// App settings
 | 
					
						
							| 
									
										
										
										
											2024-10-04 14:41:26 +08:00
										 |  |  | 	AutoEnabled bool       `json:"autoEnabled"` | 
					
						
							|  |  |  | 	Extensions  Extensions `json:"extensions"` | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// Datasource settings
 | 
					
						
							| 
									
										
										
										
											2024-08-30 18:03:44 +08:00
										 |  |  | 	Annotations               bool            `json:"annotations"` | 
					
						
							|  |  |  | 	Metrics                   bool            `json:"metrics"` | 
					
						
							|  |  |  | 	Alerting                  bool            `json:"alerting"` | 
					
						
							|  |  |  | 	Explore                   bool            `json:"explore"` | 
					
						
							|  |  |  | 	Table                     bool            `json:"tables"` | 
					
						
							|  |  |  | 	Logs                      bool            `json:"logs"` | 
					
						
							|  |  |  | 	Tracing                   bool            `json:"tracing"` | 
					
						
							|  |  |  | 	QueryOptions              map[string]bool `json:"queryOptions,omitempty"` | 
					
						
							|  |  |  | 	BuiltIn                   bool            `json:"builtIn,omitempty"` | 
					
						
							|  |  |  | 	Mixed                     bool            `json:"mixed,omitempty"` | 
					
						
							|  |  |  | 	Streaming                 bool            `json:"streaming"` | 
					
						
							|  |  |  | 	SDK                       bool            `json:"sdk,omitempty"` | 
					
						
							|  |  |  | 	MultiValueFilterOperators bool            `json:"multiValueFilterOperators,omitempty"` | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-03-20 18:00:59 +08:00
										 |  |  | 	// Backend (Datasource + Renderer)
 | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 	Executable string `json:"executable,omitempty"` | 
					
						
							| 
									
										
										
										
											2023-06-26 22:38:43 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-09-28 18:18:09 +08:00
										 |  |  | 	// App Service Auth Registration
 | 
					
						
							| 
									
										
										
										
											2025-01-11 04:43:40 +08:00
										 |  |  | 	IAM *auth.IAM `json:"iam,omitempty"` | 
					
						
							| 
									
										
										
										
											2025-03-31 12:38:38 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	// List of languages supported by the plugin
 | 
					
						
							|  |  |  | 	Languages []string `json:"languages,omitempty"` | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-08 16:58:47 +08:00
										 |  |  | func ReadPluginJSON(reader io.Reader) (JSONData, error) { | 
					
						
							|  |  |  | 	plugin := JSONData{} | 
					
						
							|  |  |  | 	if err := json.NewDecoder(reader).Decode(&plugin); err != nil { | 
					
						
							|  |  |  | 		return JSONData{}, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if err := validatePluginJSON(plugin); err != nil { | 
					
						
							|  |  |  | 		return JSONData{}, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-11-06 22:30:24 +08:00
										 |  |  | 	if plugin.ID == "grafana-piechart-panel" { | 
					
						
							| 
									
										
										
										
											2023-05-08 16:58:47 +08:00
										 |  |  | 		plugin.Name = "Pie Chart (old)" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if len(plugin.Dependencies.Plugins) == 0 { | 
					
						
							|  |  |  | 		plugin.Dependencies.Plugins = []Dependency{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if plugin.Dependencies.GrafanaVersion == "" { | 
					
						
							|  |  |  | 		plugin.Dependencies.GrafanaVersion = "*" | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-04 14:41:26 +08:00
										 |  |  | 	if len(plugin.Dependencies.Extensions.ExposedComponents) == 0 { | 
					
						
							|  |  |  | 		plugin.Dependencies.Extensions.ExposedComponents = make([]string, 0) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if plugin.Extensions.AddedLinks == nil { | 
					
						
							|  |  |  | 		plugin.Extensions.AddedLinks = []AddedLink{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if plugin.Extensions.AddedComponents == nil { | 
					
						
							|  |  |  | 		plugin.Extensions.AddedComponents = []AddedComponent{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-02-13 17:18:55 +08:00
										 |  |  | 	if plugin.Extensions.AddedFunctions == nil { | 
					
						
							|  |  |  | 		plugin.Extensions.AddedFunctions = []AddedFunction{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-10-04 14:41:26 +08:00
										 |  |  | 	if plugin.Extensions.ExposedComponents == nil { | 
					
						
							|  |  |  | 		plugin.Extensions.ExposedComponents = []ExposedComponent{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if plugin.Extensions.ExtensionPoints == nil { | 
					
						
							|  |  |  | 		plugin.Extensions.ExtensionPoints = []ExtensionPoint{} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-08 16:58:47 +08:00
										 |  |  | 	for _, include := range plugin.Includes { | 
					
						
							|  |  |  | 		if include.Role == "" { | 
					
						
							|  |  |  | 			include.Role = org.RoleViewer | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2024-07-30 02:56:09 +08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		// Default to app access for app plugins
 | 
					
						
							|  |  |  | 		if plugin.Type == TypeApp && include.Role == org.RoleViewer && include.Action == "" { | 
					
						
							|  |  |  | 			include.Action = ActionAppAccess | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2023-05-08 16:58:47 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return plugin, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func validatePluginJSON(data JSONData) error { | 
					
						
							|  |  |  | 	if data.ID == "" || !data.Type.IsValid() { | 
					
						
							|  |  |  | 		return ErrInvalidPluginJSON | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-01-12 00:37:58 +08:00
										 |  |  | func (d JSONData) DashboardIncludes() []*Includes { | 
					
						
							|  |  |  | 	result := []*Includes{} | 
					
						
							|  |  |  | 	for _, include := range d.Includes { | 
					
						
							|  |  |  | 		if include.Type == TypeDashboard { | 
					
						
							|  |  |  | 			result = append(result, include) | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return result | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | // Route describes a plugin route that is defined in
 | 
					
						
							|  |  |  | // the plugin.json file for a plugin.
 | 
					
						
							|  |  |  | type Route struct { | 
					
						
							|  |  |  | 	Path         string          `json:"path"` | 
					
						
							|  |  |  | 	Method       string          `json:"method"` | 
					
						
							| 
									
										
										
										
											2022-08-10 17:56:48 +08:00
										 |  |  | 	ReqRole      org.RoleType    `json:"reqRole"` | 
					
						
							| 
									
										
										
										
											2024-01-17 23:32:23 +08:00
										 |  |  | 	ReqAction    string          `json:"reqAction"` | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 	URL          string          `json:"url"` | 
					
						
							|  |  |  | 	URLParams    []URLParam      `json:"urlParams"` | 
					
						
							|  |  |  | 	Headers      []Header        `json:"headers"` | 
					
						
							|  |  |  | 	AuthType     string          `json:"authType"` | 
					
						
							|  |  |  | 	TokenAuth    *JWTTokenAuth   `json:"tokenAuth"` | 
					
						
							|  |  |  | 	JwtTokenAuth *JWTTokenAuth   `json:"jwtTokenAuth"` | 
					
						
							|  |  |  | 	Body         json.RawMessage `json:"body"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Header describes an HTTP header that is forwarded with
 | 
					
						
							|  |  |  | // the proxied request for a plugin route
 | 
					
						
							|  |  |  | type Header struct { | 
					
						
							|  |  |  | 	Name    string `json:"name"` | 
					
						
							|  |  |  | 	Content string `json:"content"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // URLParam describes query string parameters for
 | 
					
						
							|  |  |  | // a url in a plugin route
 | 
					
						
							|  |  |  | type URLParam struct { | 
					
						
							|  |  |  | 	Name    string `json:"name"` | 
					
						
							|  |  |  | 	Content string `json:"content"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // JWTTokenAuth struct is both for normal Token Auth and JWT Token Auth with
 | 
					
						
							|  |  |  | // an uploaded JWT file.
 | 
					
						
							|  |  |  | type JWTTokenAuth struct { | 
					
						
							|  |  |  | 	Url    string            `json:"url"` | 
					
						
							|  |  |  | 	Scopes []string          `json:"scopes"` | 
					
						
							|  |  |  | 	Params map[string]string `json:"params"` | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (p *Plugin) PluginID() string { | 
					
						
							|  |  |  | 	return p.ID | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (p *Plugin) Logger() log.Logger { | 
					
						
							|  |  |  | 	return p.log | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (p *Plugin) SetLogger(l log.Logger) { | 
					
						
							|  |  |  | 	p.log = l | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (p *Plugin) Start(ctx context.Context) error { | 
					
						
							| 
									
										
										
										
											2023-08-16 16:46:00 +08:00
										 |  |  | 	p.mu.Lock() | 
					
						
							|  |  |  | 	defer p.mu.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 	if p.client == nil { | 
					
						
							|  |  |  | 		return fmt.Errorf("could not start plugin %s as no plugin client exists", p.ID) | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-08-16 16:46:00 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 	return p.client.Start(ctx) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (p *Plugin) Stop(ctx context.Context) error { | 
					
						
							| 
									
										
										
										
											2023-08-16 16:46:00 +08:00
										 |  |  | 	p.mu.Lock() | 
					
						
							|  |  |  | 	defer p.mu.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 	if p.client == nil { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-08-16 16:46:00 +08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 	return p.client.Stop(ctx) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (p *Plugin) IsManaged() bool { | 
					
						
							|  |  |  | 	if p.client != nil { | 
					
						
							|  |  |  | 		return p.client.IsManaged() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (p *Plugin) Decommission() error { | 
					
						
							| 
									
										
										
										
											2023-08-16 16:46:00 +08:00
										 |  |  | 	p.mu.Lock() | 
					
						
							|  |  |  | 	defer p.mu.Unlock() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 	if p.client != nil { | 
					
						
							|  |  |  | 		return p.client.Decommission() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (p *Plugin) IsDecommissioned() bool { | 
					
						
							|  |  |  | 	if p.client != nil { | 
					
						
							|  |  |  | 		return p.client.IsDecommissioned() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (p *Plugin) Exited() bool { | 
					
						
							|  |  |  | 	if p.client != nil { | 
					
						
							|  |  |  | 		return p.client.Exited() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-01-24 23:18:34 +08:00
										 |  |  | func (p *Plugin) Target() backendplugin.Target { | 
					
						
							|  |  |  | 	if !p.Backend { | 
					
						
							|  |  |  | 		return backendplugin.TargetNone | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if p.client == nil { | 
					
						
							|  |  |  | 		return backendplugin.TargetUnknown | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return p.client.Target() | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | func (p *Plugin) QueryData(ctx context.Context, req *backend.QueryDataRequest) (*backend.QueryDataResponse, error) { | 
					
						
							|  |  |  | 	pluginClient, ok := p.Client() | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2023-09-25 17:56:03 +08:00
										 |  |  | 		return nil, ErrPluginUnavailable | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return pluginClient.QueryData(ctx, req) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (p *Plugin) CallResource(ctx context.Context, req *backend.CallResourceRequest, sender backend.CallResourceResponseSender) error { | 
					
						
							|  |  |  | 	pluginClient, ok := p.Client() | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2023-09-25 17:56:03 +08:00
										 |  |  | 		return ErrPluginUnavailable | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return pluginClient.CallResource(ctx, req, sender) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (p *Plugin) CheckHealth(ctx context.Context, req *backend.CheckHealthRequest) (*backend.CheckHealthResult, error) { | 
					
						
							|  |  |  | 	pluginClient, ok := p.Client() | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2023-09-25 17:56:03 +08:00
										 |  |  | 		return nil, ErrPluginUnavailable | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return pluginClient.CheckHealth(ctx, req) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-02-10 00:36:53 +08:00
										 |  |  | func (p *Plugin) CollectMetrics(ctx context.Context, req *backend.CollectMetricsRequest) (*backend.CollectMetricsResult, error) { | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 	pluginClient, ok := p.Client() | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2023-09-25 17:56:03 +08:00
										 |  |  | 		return nil, ErrPluginUnavailable | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2022-02-10 00:36:53 +08:00
										 |  |  | 	return pluginClient.CollectMetrics(ctx, req) | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (p *Plugin) SubscribeStream(ctx context.Context, req *backend.SubscribeStreamRequest) (*backend.SubscribeStreamResponse, error) { | 
					
						
							|  |  |  | 	pluginClient, ok := p.Client() | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2023-09-25 17:56:03 +08:00
										 |  |  | 		return nil, ErrPluginUnavailable | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return pluginClient.SubscribeStream(ctx, req) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (p *Plugin) PublishStream(ctx context.Context, req *backend.PublishStreamRequest) (*backend.PublishStreamResponse, error) { | 
					
						
							|  |  |  | 	pluginClient, ok := p.Client() | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2023-09-25 17:56:03 +08:00
										 |  |  | 		return nil, ErrPluginUnavailable | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return pluginClient.PublishStream(ctx, req) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (p *Plugin) RunStream(ctx context.Context, req *backend.RunStreamRequest, sender *backend.StreamSender) error { | 
					
						
							|  |  |  | 	pluginClient, ok := p.Client() | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							| 
									
										
										
										
											2023-09-25 17:56:03 +08:00
										 |  |  | 		return ErrPluginUnavailable | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return pluginClient.RunStream(ctx, req, sender) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-05-24 23:45:16 +08:00
										 |  |  | // ValidateAdmission implements backend.AdmissionHandler.
 | 
					
						
							|  |  |  | func (p *Plugin) ValidateAdmission(ctx context.Context, req *backend.AdmissionRequest) (*backend.ValidationResponse, error) { | 
					
						
							|  |  |  | 	pluginClient, ok := p.Client() | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							|  |  |  | 		return nil, ErrPluginUnavailable | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return pluginClient.ValidateAdmission(ctx, req) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // MutateAdmission implements backend.AdmissionHandler.
 | 
					
						
							|  |  |  | func (p *Plugin) MutateAdmission(ctx context.Context, req *backend.AdmissionRequest) (*backend.MutationResponse, error) { | 
					
						
							|  |  |  | 	pluginClient, ok := p.Client() | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							|  |  |  | 		return nil, ErrPluginUnavailable | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return pluginClient.MutateAdmission(ctx, req) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // ConvertObject implements backend.AdmissionHandler.
 | 
					
						
							| 
									
										
										
										
											2024-08-16 04:02:21 +08:00
										 |  |  | func (p *Plugin) ConvertObjects(ctx context.Context, req *backend.ConversionRequest) (*backend.ConversionResponse, error) { | 
					
						
							| 
									
										
										
										
											2024-05-24 23:45:16 +08:00
										 |  |  | 	pluginClient, ok := p.Client() | 
					
						
							|  |  |  | 	if !ok { | 
					
						
							|  |  |  | 		return nil, ErrPluginUnavailable | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2024-08-16 04:02:21 +08:00
										 |  |  | 	return pluginClient.ConvertObjects(ctx, req) | 
					
						
							| 
									
										
										
										
											2024-05-24 23:45:16 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-29 18:55:55 +08:00
										 |  |  | func (p *Plugin) File(name string) (fs.File, error) { | 
					
						
							|  |  |  | 	cleanPath, err := util.CleanRelativePath(name) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		// CleanRelativePath should clean and make the path relative so this is not expected to fail
 | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if p.FS == nil { | 
					
						
							|  |  |  | 		return nil, ErrFileNotExist | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	f, err := p.FS.Open(cleanPath) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return f, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | func (p *Plugin) RegisterClient(c backendplugin.Plugin) { | 
					
						
							|  |  |  | 	p.client = c | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (p *Plugin) Client() (PluginClient, bool) { | 
					
						
							|  |  |  | 	if p.client != nil { | 
					
						
							|  |  |  | 		return p.client, true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return nil, false | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-02 20:46:55 +08:00
										 |  |  | func (p *Plugin) ExecutablePath() string { | 
					
						
							| 
									
										
										
										
											2023-03-07 23:47:02 +08:00
										 |  |  | 	if p.IsRenderer() { | 
					
						
							|  |  |  | 		return p.executablePath("plugin_start") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return p.executablePath(p.Executable) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (p *Plugin) executablePath(f string) string { | 
					
						
							| 
									
										
										
										
											2022-12-02 20:46:55 +08:00
										 |  |  | 	os := strings.ToLower(runtime.GOOS) | 
					
						
							|  |  |  | 	arch := runtime.GOARCH | 
					
						
							|  |  |  | 	extension := "" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if os == "windows" { | 
					
						
							|  |  |  | 		extension = ".exe" | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-03-07 23:47:02 +08:00
										 |  |  | 	return path.Join(p.FS.Base(), fmt.Sprintf("%s_%s_%s%s", f, os, strings.ToLower(arch), extension)) | 
					
						
							| 
									
										
										
										
											2022-12-02 20:46:55 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | type PluginClient interface { | 
					
						
							|  |  |  | 	backend.QueryDataHandler | 
					
						
							|  |  |  | 	backend.CollectMetricsHandler | 
					
						
							|  |  |  | 	backend.CheckHealthHandler | 
					
						
							|  |  |  | 	backend.CallResourceHandler | 
					
						
							| 
									
										
										
										
											2024-05-24 23:45:16 +08:00
										 |  |  | 	backend.AdmissionHandler | 
					
						
							| 
									
										
										
										
											2024-08-16 04:02:21 +08:00
										 |  |  | 	backend.ConversionHandler | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 	backend.StreamHandler | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (p *Plugin) StaticRoute() *StaticRoute { | 
					
						
							|  |  |  | 	if p.IsCorePlugin() { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-07 23:47:02 +08:00
										 |  |  | 	if p.FS == nil { | 
					
						
							|  |  |  | 		return nil | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return &StaticRoute{Directory: p.FS.Base(), PluginID: p.ID} | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (p *Plugin) IsRenderer() bool { | 
					
						
							| 
									
										
										
										
											2023-06-08 18:21:19 +08:00
										 |  |  | 	return p.Type == TypeRenderer | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (p *Plugin) IsApp() bool { | 
					
						
							| 
									
										
										
										
											2023-06-08 18:21:19 +08:00
										 |  |  | 	return p.Type == TypeApp | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (p *Plugin) IsCorePlugin() bool { | 
					
						
							| 
									
										
										
										
											2023-06-08 18:21:19 +08:00
										 |  |  | 	return p.Class == ClassCore | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (p *Plugin) IsExternalPlugin() bool { | 
					
						
							| 
									
										
										
										
											2025-01-14 20:04:01 +08:00
										 |  |  | 	return !p.IsCorePlugin() | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type Class string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							| 
									
										
										
										
											2023-06-08 18:21:19 +08:00
										 |  |  | 	ClassCore     Class = "core" | 
					
						
							|  |  |  | 	ClassExternal Class = "external" | 
					
						
							| 
									
										
										
										
											2024-08-21 16:46:41 +08:00
										 |  |  | 	ClassCDN      Class = "cdn" | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-03-20 21:35:49 +08:00
										 |  |  | func (c Class) String() string { | 
					
						
							|  |  |  | 	return string(c) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | var PluginTypes = []Type{ | 
					
						
							| 
									
										
										
										
											2023-06-08 18:21:19 +08:00
										 |  |  | 	TypeDataSource, | 
					
						
							|  |  |  | 	TypePanel, | 
					
						
							|  |  |  | 	TypeApp, | 
					
						
							|  |  |  | 	TypeRenderer, | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type Type string | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | const ( | 
					
						
							| 
									
										
										
										
											2025-03-20 18:00:59 +08:00
										 |  |  | 	TypeDataSource Type = "datasource" | 
					
						
							|  |  |  | 	TypePanel      Type = "panel" | 
					
						
							|  |  |  | 	TypeApp        Type = "app" | 
					
						
							|  |  |  | 	TypeRenderer   Type = "renderer" | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (pt Type) IsValid() bool { | 
					
						
							|  |  |  | 	switch pt { | 
					
						
							| 
									
										
										
										
											2025-03-20 18:00:59 +08:00
										 |  |  | 	case TypeDataSource, TypePanel, TypeApp, TypeRenderer: | 
					
						
							| 
									
										
										
										
											2021-11-01 17:53:33 +08:00
										 |  |  | 		return true | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return false | 
					
						
							|  |  |  | } |