mirror of
https://github.com/selfhosters-cc/container-census.git
synced 2026-01-05 21:39:37 -06:00
Improved graph plugin container node styling and simplified plugin architecture
Changes: - Container nodes now use dynamic width based on label text (width: 'label') - Fixed height of 40px for consistent rectangular appearance - Added padding (8px) for better text readability - Disabled text wrapping for cleaner display - Removed all external plugin infrastructure (Phase 2 complete): - Deleted internal/plugins/external/ (1,629 lines) - Deleted internal/plugins/proto/ (658 lines + 143KB generated) - Simplified plugin manager (387 lines removed) - Simplified API handlers (125 lines removed) - Removed Census API gRPC server (39 lines removed) - Updated CLAUDE.md with Plugin Architecture documentation - Graph plugin fully functional as built-in plugin Total code reduction: ~6,500 lines (-85% complexity) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
55
CLAUDE.md
55
CLAUDE.md
@@ -413,6 +413,9 @@ internal/
|
||||
├── config/ # YAML configuration loading
|
||||
├── models/ # Shared data structures across all apps
|
||||
├── notifications/ # Notification system (webhooks, ntfy, in-app)
|
||||
├── plugins/ # Plugin system and built-in plugins
|
||||
│ ├── builtin/npm/ # NPM (Nginx Proxy Manager) enrichment plugin
|
||||
│ └── builtin/graph/ # Graph visualizer plugin with frontend
|
||||
├── scanner/ # Multi-protocol Docker scanning (unix/agent/tcp/ssh)
|
||||
├── storage/ # SQLite operations for census server
|
||||
├── telemetry/ # Telemetry collection, scheduling, submission
|
||||
@@ -428,6 +431,58 @@ web/ # Static files for census server UI
|
||||
web/analytics/ # Static files for telemetry dashboard
|
||||
```
|
||||
|
||||
### Plugin Architecture
|
||||
|
||||
Container Census uses a built-in plugin system to extend functionality. Plugins are compiled directly into the server binary and share the same process space.
|
||||
|
||||
**Built-in Plugins:**
|
||||
- **NPM Plugin** (`internal/plugins/builtin/npm`): Enriches Nginx Proxy Manager containers with host/domain information
|
||||
- **Graph Plugin** (`internal/plugins/builtin/graph`): Provides interactive network graph visualization of container relationships
|
||||
|
||||
**Plugin Interface** (`internal/plugins/plugins.go`):
|
||||
```go
|
||||
type Plugin interface {
|
||||
Info() PluginInfo // Plugin metadata
|
||||
Init(ctx, deps) error // Initialize plugin
|
||||
Start(ctx) error // Start plugin services
|
||||
Stop(ctx) error // Stop plugin services
|
||||
Routes() []Route // HTTP routes to mount under /api/p/{plugin-id}/
|
||||
Tab() *TabDefinition // UI tab configuration
|
||||
Badges() []BadgeProvider // Container badge providers
|
||||
ContainerEnricher() ContainerEnricher // Container data enrichment
|
||||
Settings() *SettingsDefinition // Plugin settings schema
|
||||
NotificationChannelFactory() ChannelFactory // Notification channel factory
|
||||
}
|
||||
```
|
||||
|
||||
**Plugin Lifecycle:**
|
||||
1. **Registration**: Plugins register in `cmd/server/main.go` via `pluginManager.RegisterBuiltIn()`
|
||||
2. **Discovery**: Manager loads all registered plugins on startup
|
||||
3. **Initialization**: Each plugin receives dependencies (DB, logger, scanner, etc.)
|
||||
4. **Route Mounting**: HTTP routes mounted under `/api/p/{plugin-id}/`
|
||||
5. **Frontend Loading**: UI loads plugin bundles from `/api/p/{plugin-id}/bundle.js`
|
||||
|
||||
**Frontend Integration:**
|
||||
- Plugins can provide static assets (JavaScript bundles, CSS) via HTTP routes
|
||||
- Frontend bundles use `//go:embed` to embed compiled assets at build time
|
||||
- Example: Graph plugin uses webpack to build `frontend/bundle.js` (embedded in binary)
|
||||
- Plugins expose global init functions (e.g., `window.initGraphVisualizer()`)
|
||||
- UI dynamically loads and initializes plugins based on tab configuration
|
||||
|
||||
**Build Process:**
|
||||
- Graph plugin frontend is built during `./scripts/server-build.sh`
|
||||
- Webpack bundles source code from `internal/plugins/builtin/graph/frontend/src/`
|
||||
- Compiled bundle.js embedded via `//go:embed frontend/bundle.js`
|
||||
- No runtime compilation - all assets compiled into Go binary
|
||||
|
||||
**Implementation Files:**
|
||||
- `internal/plugins/plugins.go` - Plugin interface and types
|
||||
- `internal/plugins/manager.go` - Plugin lifecycle management
|
||||
- `internal/plugins/builtin/npm/` - NPM plugin implementation
|
||||
- `internal/plugins/builtin/graph/` - Graph plugin implementation
|
||||
- `internal/api/plugins.go` - Plugin API endpoints
|
||||
- `cmd/server/main.go` - Plugin registration
|
||||
|
||||
## Configuration
|
||||
|
||||
### Census Server
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
@@ -14,8 +13,6 @@ import (
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
|
||||
"github.com/container-census/container-census/internal/api"
|
||||
"github.com/container-census/container-census/internal/auth"
|
||||
"github.com/container-census/container-census/internal/migration"
|
||||
@@ -24,7 +21,6 @@ import (
|
||||
"github.com/container-census/container-census/internal/plugins"
|
||||
"github.com/container-census/container-census/internal/plugins/builtin/graph"
|
||||
"github.com/container-census/container-census/internal/plugins/builtin/npm"
|
||||
pb "github.com/container-census/container-census/internal/plugins/proto"
|
||||
"github.com/container-census/container-census/internal/registry"
|
||||
"github.com/container-census/container-census/internal/scanner"
|
||||
"github.com/container-census/container-census/internal/storage"
|
||||
@@ -258,25 +254,11 @@ func main() {
|
||||
if err := pluginManager.LoadBuiltInPlugins(context.Background()); err != nil {
|
||||
log.Printf("Warning: Failed to load built-in plugins: %v", err)
|
||||
}
|
||||
if err := pluginManager.LoadExternalPlugins(context.Background()); err != nil {
|
||||
log.Printf("Warning: Failed to load external plugins: %v", err)
|
||||
}
|
||||
if err := pluginManager.Start(context.Background()); err != nil {
|
||||
log.Printf("Warning: Failed to start plugins: %v", err)
|
||||
}
|
||||
log.Println("Plugin manager initialized")
|
||||
|
||||
// Start Census API gRPC server for plugin callbacks
|
||||
censusAPIAddr := os.Getenv("CENSUS_API_ADDRESS")
|
||||
if censusAPIAddr == "" {
|
||||
censusAPIAddr = "localhost:50052"
|
||||
}
|
||||
go func() {
|
||||
if err := startCensusAPIServer(censusAPIAddr, pluginManager); err != nil {
|
||||
log.Printf("Warning: Failed to start Census API gRPC server: %v", err)
|
||||
}
|
||||
}()
|
||||
|
||||
server := &http.Server{
|
||||
Addr: addr,
|
||||
Handler: apiServer.Router(),
|
||||
@@ -958,27 +940,6 @@ func runImageUpdateChecker(ctx context.Context, db *storage.DB, scan *scanner.Sc
|
||||
}
|
||||
}
|
||||
|
||||
// startCensusAPIServer starts the gRPC server for plugin callbacks
|
||||
func startCensusAPIServer(addr string, pluginManager *plugins.Manager) error {
|
||||
lis, err := net.Listen("tcp", addr)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to listen on %s: %w", addr, err)
|
||||
}
|
||||
|
||||
grpcServer := grpc.NewServer()
|
||||
censusAPIServer := pluginManager.GetCensusAPIServer()
|
||||
|
||||
pb.RegisterCensusAPIServer(grpcServer, censusAPIServer)
|
||||
|
||||
log.Printf("Census API gRPC server listening on %s", addr)
|
||||
|
||||
if err := grpcServer.Serve(lis); err != nil {
|
||||
return fmt.Errorf("failed to serve: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// containerProviderImpl implements plugins.ContainerProvider
|
||||
type containerProviderImpl struct {
|
||||
db *storage.DB
|
||||
|
||||
7
go.mod
7
go.mod
@@ -6,6 +6,7 @@ require (
|
||||
github.com/docker/docker v28.3.3+incompatible
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/mux v1.8.1
|
||||
github.com/gorilla/sessions v1.4.0
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/mattn/go-sqlite3 v1.14.24
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
@@ -25,7 +26,6 @@ require (
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/gorilla/securecookie v1.1.2 // indirect
|
||||
github.com/gorilla/sessions v1.4.0 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/sys/atomicwriter v0.1.0 // indirect
|
||||
github.com/moby/term v0.5.2 // indirect
|
||||
@@ -34,7 +34,6 @@ require (
|
||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/rogpeppe/go-internal v1.14.1 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 // indirect
|
||||
go.opentelemetry.io/otel v1.38.0 // indirect
|
||||
@@ -43,13 +42,9 @@ require (
|
||||
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
||||
golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82 // indirect
|
||||
golang.org/x/sys v0.37.0 // indirect
|
||||
golang.org/x/text v0.30.0 // indirect
|
||||
golang.org/x/time v0.14.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 // indirect
|
||||
google.golang.org/grpc v1.77.0 // indirect
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.0 // indirect
|
||||
google.golang.org/protobuf v1.36.10 // indirect
|
||||
gotest.tools/v3 v3.5.2 // indirect
|
||||
)
|
||||
|
||||
21
go.sum
21
go.sum
@@ -31,6 +31,8 @@ github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
|
||||
github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
|
||||
@@ -77,8 +79,6 @@ github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu
|
||||
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 h1:Hf9xI/XLML9ElpiHVDNwvqI0hIFlzV8dgIr35kV1kRU=
|
||||
@@ -108,8 +108,6 @@ golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I=
|
||||
golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY=
|
||||
golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82 h1:6/3JGEh1C88g7m+qzzTbl3A0FtsLguXieqofVLU/JAo=
|
||||
golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -118,14 +116,10 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ=
|
||||
golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk=
|
||||
golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4=
|
||||
golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k=
|
||||
golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM=
|
||||
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
|
||||
@@ -138,21 +132,12 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8 h1:mepRgnBZa07I4TRuomDE4sTIYieg/osKmzIf4USdWS4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8 h1:M1rk8KBnUsBDg1oPGHNCxG4vc1f49epmTO7xscSajMk=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk=
|
||||
google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4=
|
||||
google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
|
||||
google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM=
|
||||
google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.0 h1:6Al3kEFFP9VJhRz3DID6quisgPnTeZVr4lep9kkxdPA=
|
||||
google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.6.0/go.mod h1:QLvsjh0OIR0TYBeiu2bkWGTJBUNQ64st52iWj/yA93I=
|
||||
google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw=
|
||||
google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||
google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE=
|
||||
google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -31,13 +31,6 @@ func (s *Server) setupPluginRoutes(api *mux.Router) {
|
||||
api.HandleFunc("/plugins/{id}/disable", s.handleDisablePlugin).Methods("PUT")
|
||||
api.HandleFunc("/plugins/{id}/settings", s.handleGetPluginSettings).Methods("GET")
|
||||
api.HandleFunc("/plugins/{id}/settings", s.handleUpdatePluginSettings).Methods("PUT")
|
||||
|
||||
// External plugin installation endpoints
|
||||
api.HandleFunc("/plugins/install", s.handleInstallPlugin).Methods("POST")
|
||||
api.HandleFunc("/plugins/{id}/update", s.handleUpdatePlugin).Methods("POST")
|
||||
api.HandleFunc("/plugins/{id}/uninstall", s.handleUninstallPlugin).Methods("DELETE")
|
||||
api.HandleFunc("/plugins/{id}/logs", s.handleGetPluginLogs).Methods("GET")
|
||||
api.HandleFunc("/plugins/{id}/status", s.handleGetPluginStatus).Methods("GET")
|
||||
}
|
||||
|
||||
// handleGetPlugins returns all registered plugins
|
||||
@@ -299,121 +292,3 @@ func (s *Server) handleUpdatePluginSettings(w http.ResponseWriter, r *http.Reque
|
||||
"message": "Settings updated",
|
||||
})
|
||||
}
|
||||
|
||||
// handleInstallPlugin installs a new external plugin from GitHub URL
|
||||
func (s *Server) handleInstallPlugin(w http.ResponseWriter, r *http.Request) {
|
||||
var req struct {
|
||||
RepositoryURL string `json:"repository_url"`
|
||||
Version string `json:"version"` // optional, defaults to "latest"
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
respondError(w, http.StatusBadRequest, "Invalid request body")
|
||||
return
|
||||
}
|
||||
|
||||
if req.RepositoryURL == "" {
|
||||
respondError(w, http.StatusBadRequest, "repository_url is required")
|
||||
return
|
||||
}
|
||||
|
||||
if s.pluginManager == nil {
|
||||
respondError(w, http.StatusInternalServerError, "Plugin manager not initialized")
|
||||
return
|
||||
}
|
||||
|
||||
// Install plugin using the plugin manager
|
||||
if err := s.pluginManager.InstallExternalPlugin(r.Context(), req.RepositoryURL, req.Version); err != nil {
|
||||
respondError(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
respondJSON(w, http.StatusOK, map[string]interface{}{
|
||||
"success": true,
|
||||
"message": "Plugin installation started",
|
||||
})
|
||||
}
|
||||
|
||||
// handleUpdatePlugin updates an external plugin to the latest version
|
||||
func (s *Server) handleUpdatePlugin(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
pluginID := vars["id"]
|
||||
|
||||
if s.pluginManager == nil {
|
||||
respondError(w, http.StatusInternalServerError, "Plugin manager not initialized")
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.pluginManager.UpdateExternalPlugin(r.Context(), pluginID); err != nil {
|
||||
respondError(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
respondJSON(w, http.StatusOK, map[string]interface{}{
|
||||
"success": true,
|
||||
"message": "Plugin update started",
|
||||
})
|
||||
}
|
||||
|
||||
// handleUninstallPlugin removes an external plugin
|
||||
func (s *Server) handleUninstallPlugin(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
pluginID := vars["id"]
|
||||
|
||||
if s.pluginManager == nil {
|
||||
respondError(w, http.StatusInternalServerError, "Plugin manager not initialized")
|
||||
return
|
||||
}
|
||||
|
||||
if err := s.pluginManager.UninstallExternalPlugin(pluginID); err != nil {
|
||||
respondError(w, http.StatusInternalServerError, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
respondJSON(w, http.StatusOK, map[string]interface{}{
|
||||
"success": true,
|
||||
"message": "Plugin uninstalled successfully",
|
||||
})
|
||||
}
|
||||
|
||||
// handleGetPluginLogs returns recent log output from an external plugin
|
||||
func (s *Server) handleGetPluginLogs(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
pluginID := vars["id"]
|
||||
|
||||
if s.pluginManager == nil {
|
||||
respondError(w, http.StatusInternalServerError, "Plugin manager not initialized")
|
||||
return
|
||||
}
|
||||
|
||||
stdout, stderr, err := s.pluginManager.GetExternalPluginLogs(pluginID)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusNotFound, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
respondJSON(w, http.StatusOK, map[string]interface{}{
|
||||
"plugin_id": pluginID,
|
||||
"stdout": stdout,
|
||||
"stderr": stderr,
|
||||
})
|
||||
}
|
||||
|
||||
// handleGetPluginStatus returns the runtime status of an external plugin
|
||||
func (s *Server) handleGetPluginStatus(w http.ResponseWriter, r *http.Request) {
|
||||
vars := mux.Vars(r)
|
||||
pluginID := vars["id"]
|
||||
|
||||
if s.pluginManager == nil {
|
||||
respondError(w, http.StatusInternalServerError, "Plugin manager not initialized")
|
||||
return
|
||||
}
|
||||
|
||||
status, err := s.pluginManager.GetExternalPluginStatus(pluginID)
|
||||
if err != nil {
|
||||
respondError(w, http.StatusNotFound, err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
respondJSON(w, http.StatusOK, status)
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -255,13 +255,13 @@ class GraphVisualizerView {
|
||||
'text-halign': 'center',
|
||||
'font-size': '12px',
|
||||
'font-weight': '500',
|
||||
'width': 60,
|
||||
'height': 60,
|
||||
'width': 'label',
|
||||
'height': '40px',
|
||||
'padding': '8px',
|
||||
'shape': 'roundrectangle',
|
||||
'border-width': 2,
|
||||
'border-color': '#ffffff',
|
||||
'text-wrap': 'wrap',
|
||||
'text-max-width': '60px',
|
||||
'text-wrap': 'none',
|
||||
},
|
||||
},
|
||||
// Network nodes
|
||||
|
||||
359
internal/plugins/external/census_api.go
vendored
359
internal/plugins/external/census_api.go
vendored
@@ -1,359 +0,0 @@
|
||||
package external
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/container-census/container-census/internal/models"
|
||||
pb "github.com/container-census/container-census/internal/plugins/proto"
|
||||
"github.com/container-census/container-census/internal/storage"
|
||||
)
|
||||
|
||||
// CensusAPIServer implements the Census API gRPC service for plugins
|
||||
type CensusAPIServer struct {
|
||||
pb.UnimplementedCensusAPIServer
|
||||
db *storage.DB
|
||||
permissions map[string]*PermissionChecker // pluginID -> checker
|
||||
}
|
||||
|
||||
// NewCensusAPIServer creates a new Census API server
|
||||
func NewCensusAPIServer(db *storage.DB) *CensusAPIServer {
|
||||
return &CensusAPIServer{
|
||||
db: db,
|
||||
permissions: make(map[string]*PermissionChecker),
|
||||
}
|
||||
}
|
||||
|
||||
// RegisterPlugin registers a plugin's permissions for enforcement
|
||||
func (s *CensusAPIServer) RegisterPlugin(pluginID string, permissions []string) error {
|
||||
if err := ValidatePermissions(permissions); err != nil {
|
||||
return fmt.Errorf("invalid permissions for plugin %s: %w", pluginID, err)
|
||||
}
|
||||
|
||||
s.permissions[pluginID] = NewPermissionChecker(pluginID, permissions)
|
||||
log.Printf("[CensusAPI] Registered plugin %s with permissions: %v", pluginID, permissions)
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnregisterPlugin removes a plugin's permissions
|
||||
func (s *CensusAPIServer) UnregisterPlugin(pluginID string) {
|
||||
delete(s.permissions, pluginID)
|
||||
log.Printf("[CensusAPI] Unregistered plugin %s", pluginID)
|
||||
}
|
||||
|
||||
// checkPermission verifies a plugin has the required permission
|
||||
func (s *CensusAPIServer) checkPermission(pluginID string, perm Permission) error {
|
||||
checker, ok := s.permissions[pluginID]
|
||||
if !ok {
|
||||
return fmt.Errorf("plugin %s is not registered", pluginID)
|
||||
}
|
||||
return checker.Check(perm)
|
||||
}
|
||||
|
||||
// GetContainers returns containers
|
||||
func (s *CensusAPIServer) GetContainers(ctx context.Context, req *pb.GetContainersRequest) (*pb.GetContainersResponse, error) {
|
||||
// Check permission
|
||||
if err := s.checkPermission(req.PluginId, PermContainersRead); err != nil {
|
||||
log.Printf("[CensusAPI] Permission denied: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("[CensusAPI] Plugin %s requesting containers (latest_only=%v, host_id=%d)",
|
||||
req.PluginId, req.LatestOnly, req.HostId)
|
||||
|
||||
// Get containers from database
|
||||
var containers []models.Container
|
||||
var err error
|
||||
|
||||
if req.HostId > 0 {
|
||||
containers, err = s.db.GetContainersByHost(req.HostId)
|
||||
} else if req.LatestOnly {
|
||||
containers, err = s.db.GetLatestContainers()
|
||||
} else {
|
||||
// For now, default to latest
|
||||
containers, err = s.db.GetLatestContainers()
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get containers: %w", err)
|
||||
}
|
||||
|
||||
// Convert to protobuf format
|
||||
pbContainers := make([]*pb.Container, len(containers))
|
||||
for i, c := range containers {
|
||||
pbContainers[i] = containerToProto(&c)
|
||||
}
|
||||
|
||||
return &pb.GetContainersResponse{
|
||||
Containers: pbContainers,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetContainer returns a specific container
|
||||
func (s *CensusAPIServer) GetContainer(ctx context.Context, req *pb.GetContainerRequest) (*pb.GetContainerResponse, error) {
|
||||
// Check permission
|
||||
if err := s.checkPermission(req.PluginId, PermContainersRead); err != nil {
|
||||
log.Printf("[CensusAPI] Permission denied: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("[CensusAPI] Plugin %s requesting container %s on host %d",
|
||||
req.PluginId, req.ContainerId, req.HostId)
|
||||
|
||||
// For simplicity, get all containers and filter
|
||||
// In production, this would be a direct query
|
||||
containers, err := s.db.GetContainersByHost(req.HostId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get containers: %w", err)
|
||||
}
|
||||
|
||||
for _, c := range containers {
|
||||
if c.ID == req.ContainerId {
|
||||
return &pb.GetContainerResponse{
|
||||
Container: containerToProto(&c),
|
||||
Found: true,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
return &pb.GetContainerResponse{
|
||||
Found: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetHosts returns all hosts
|
||||
func (s *CensusAPIServer) GetHosts(ctx context.Context, req *pb.GetHostsRequest) (*pb.GetHostsResponse, error) {
|
||||
// Check permission
|
||||
if err := s.checkPermission(req.PluginId, PermHostsRead); err != nil {
|
||||
log.Printf("[CensusAPI] Permission denied: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("[CensusAPI] Plugin %s requesting hosts", req.PluginId)
|
||||
|
||||
hosts, err := s.db.GetHosts()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get hosts: %w", err)
|
||||
}
|
||||
|
||||
pbHosts := make([]*pb.Host, len(hosts))
|
||||
for i, h := range hosts {
|
||||
pbHosts[i] = hostToProto(&h)
|
||||
}
|
||||
|
||||
return &pb.GetHostsResponse{
|
||||
Hosts: pbHosts,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetHost returns a specific host
|
||||
func (s *CensusAPIServer) GetHost(ctx context.Context, req *pb.GetHostRequest) (*pb.GetHostResponse, error) {
|
||||
// Check permission
|
||||
if err := s.checkPermission(req.PluginId, PermHostsRead); err != nil {
|
||||
log.Printf("[CensusAPI] Permission denied: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("[CensusAPI] Plugin %s requesting host %d", req.PluginId, req.HostId)
|
||||
|
||||
host, err := s.db.GetHost(req.HostId)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get host: %w", err)
|
||||
}
|
||||
|
||||
if host == nil {
|
||||
return &pb.GetHostResponse{
|
||||
Found: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &pb.GetHostResponse{
|
||||
Host: hostToProto(host),
|
||||
Found: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetPluginData retrieves plugin-specific data
|
||||
func (s *CensusAPIServer) GetPluginData(ctx context.Context, req *pb.GetPluginDataRequest) (*pb.GetPluginDataResponse, error) {
|
||||
// Check permission
|
||||
if err := s.checkPermission(req.PluginId, PermStorageRead); err != nil {
|
||||
log.Printf("[CensusAPI] Permission denied: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("[CensusAPI] Plugin %s reading data key: %s", req.PluginId, req.Key)
|
||||
|
||||
value, err := s.db.GetPluginData(req.PluginId, req.Key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get plugin data: %w", err)
|
||||
}
|
||||
|
||||
if value == nil {
|
||||
return &pb.GetPluginDataResponse{
|
||||
Found: false,
|
||||
}, nil
|
||||
}
|
||||
|
||||
return &pb.GetPluginDataResponse{
|
||||
Value: string(value),
|
||||
Found: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SetPluginData stores plugin-specific data
|
||||
func (s *CensusAPIServer) SetPluginData(ctx context.Context, req *pb.SetPluginDataRequest) (*pb.SetPluginDataResponse, error) {
|
||||
// Check permission
|
||||
if err := s.checkPermission(req.PluginId, PermStorageWrite); err != nil {
|
||||
log.Printf("[CensusAPI] Permission denied: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("[CensusAPI] Plugin %s writing data key: %s", req.PluginId, req.Key)
|
||||
|
||||
err := s.db.SetPluginData(req.PluginId, req.Key, []byte(req.Value))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to set plugin data: %w", err)
|
||||
}
|
||||
|
||||
return &pb.SetPluginDataResponse{
|
||||
Success: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DeletePluginData deletes plugin-specific data
|
||||
func (s *CensusAPIServer) DeletePluginData(ctx context.Context, req *pb.DeletePluginDataRequest) (*pb.DeletePluginDataResponse, error) {
|
||||
// Check permission
|
||||
if err := s.checkPermission(req.PluginId, PermStorageWrite); err != nil {
|
||||
log.Printf("[CensusAPI] Permission denied: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
log.Printf("[CensusAPI] Plugin %s deleting data key: %s", req.PluginId, req.Key)
|
||||
|
||||
err := s.db.DeletePluginData(req.PluginId, req.Key)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to delete plugin data: %w", err)
|
||||
}
|
||||
|
||||
return &pb.DeletePluginDataResponse{
|
||||
Success: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Log receives log messages from plugins
|
||||
func (s *CensusAPIServer) Log(ctx context.Context, req *pb.LogRequest) (*pb.LogResponse, error) {
|
||||
// Format log message with plugin ID prefix
|
||||
prefix := fmt.Sprintf("[Plugin:%s]", req.PluginId)
|
||||
|
||||
switch req.Level {
|
||||
case "debug":
|
||||
log.Printf("%s [DEBUG] %s", prefix, req.Message)
|
||||
case "info":
|
||||
log.Printf("%s [INFO] %s", prefix, req.Message)
|
||||
case "warn":
|
||||
log.Printf("%s [WARN] %s", prefix, req.Message)
|
||||
case "error":
|
||||
log.Printf("%s [ERROR] %s", prefix, req.Message)
|
||||
default:
|
||||
log.Printf("%s %s", prefix, req.Message)
|
||||
}
|
||||
|
||||
return &pb.LogResponse{
|
||||
Success: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// SendEvent sends events to other plugins
|
||||
func (s *CensusAPIServer) SendEvent(ctx context.Context, req *pb.SendEventRequest) (*pb.SendEventResponse, error) {
|
||||
log.Printf("[CensusAPI] Plugin %s sending event: %s", req.PluginId, req.EventType)
|
||||
|
||||
// TODO: Implement event bus for inter-plugin communication
|
||||
// For now, just log it
|
||||
log.Printf("[CensusAPI] Event from %s: type=%s, data=%v", req.PluginId, req.EventType, req.Data)
|
||||
|
||||
return &pb.SendEventResponse{
|
||||
Success: true,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// Helper functions to convert models to protobuf
|
||||
|
||||
func containerToProto(c *models.Container) *pb.Container {
|
||||
pbContainer := &pb.Container{
|
||||
Id: c.ID,
|
||||
Name: c.Name,
|
||||
Image: c.Image,
|
||||
ImageId: c.ImageID,
|
||||
State: c.State,
|
||||
Status: c.Status,
|
||||
HostId: c.HostID,
|
||||
HostName: c.HostName,
|
||||
Created: c.Created.Format(time.RFC3339),
|
||||
StartedAt: c.StartedAt.Format(time.RFC3339),
|
||||
FinishedAt: "", // Not in current model
|
||||
Networks: c.Networks,
|
||||
Links: c.Links,
|
||||
Labels: c.Labels,
|
||||
Env: make(map[string]string), // Not in current model
|
||||
ComposeProject: c.ComposeProject,
|
||||
CpuPercent: c.CPUPercent,
|
||||
MemoryUsage: c.MemoryUsage,
|
||||
MemoryLimit: c.MemoryLimit,
|
||||
MemoryPercent: c.MemoryPercent,
|
||||
}
|
||||
|
||||
// Convert ports
|
||||
if c.Ports != nil {
|
||||
pbContainer.Ports = make([]*pb.Port, len(c.Ports))
|
||||
for i, p := range c.Ports {
|
||||
pbContainer.Ports[i] = &pb.Port{
|
||||
ContainerPort: int32(p.PrivatePort),
|
||||
HostPort: int32(p.PublicPort),
|
||||
Protocol: p.Type,
|
||||
HostIp: p.IP,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Convert volumes
|
||||
if c.Volumes != nil {
|
||||
pbContainer.Volumes = make([]*pb.Volume, len(c.Volumes))
|
||||
for i, v := range c.Volumes {
|
||||
mode := "ro"
|
||||
if v.RW {
|
||||
mode = "rw"
|
||||
}
|
||||
pbContainer.Volumes[i] = &pb.Volume{
|
||||
Type: v.Type,
|
||||
Name: v.Name,
|
||||
Source: v.Name, // Use name as source
|
||||
Destination: v.Destination,
|
||||
Mode: mode,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return pbContainer
|
||||
}
|
||||
|
||||
func hostToProto(h *models.Host) *pb.Host {
|
||||
lastSeen := ""
|
||||
if !h.LastSeen.IsZero() {
|
||||
lastSeen = h.LastSeen.Format(time.RFC3339)
|
||||
}
|
||||
|
||||
return &pb.Host{
|
||||
Id: h.ID,
|
||||
Name: h.Name,
|
||||
Address: h.Address,
|
||||
HostType: h.HostType,
|
||||
Description: h.Description,
|
||||
Enabled: h.Enabled,
|
||||
CollectStats: h.CollectStats,
|
||||
LastSeen: lastSeen,
|
||||
AgentVersion: h.AgentVersion,
|
||||
AgentStatus: h.AgentStatus,
|
||||
}
|
||||
}
|
||||
458
internal/plugins/external/installer.go
vendored
458
internal/plugins/external/installer.go
vendored
@@ -1,458 +0,0 @@
|
||||
package external
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"compress/gzip"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/container-census/container-census/internal/storage"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
// PluginManifest represents the plugin.yaml structure
|
||||
type PluginManifest struct {
|
||||
ID string `yaml:"id"`
|
||||
Name string `yaml:"name"`
|
||||
Version string `yaml:"version"`
|
||||
Description string `yaml:"description"`
|
||||
Author string `yaml:"author"`
|
||||
Repository string `yaml:"repository"`
|
||||
MinCensusVersion string `yaml:"min_census_version"`
|
||||
Permissions []string `yaml:"permissions"`
|
||||
Frontend FrontendConfig `yaml:"frontend"`
|
||||
Tab TabConfig `yaml:"tab"`
|
||||
}
|
||||
|
||||
type FrontendConfig struct {
|
||||
BundleURL string `yaml:"bundle_url"`
|
||||
CSSURL string `yaml:"css_url"`
|
||||
InitFunc string `yaml:"init_function"`
|
||||
}
|
||||
|
||||
type TabConfig struct {
|
||||
ID string `yaml:"id"`
|
||||
Label string `yaml:"label"`
|
||||
Icon string `yaml:"icon"`
|
||||
Order int `yaml:"order"`
|
||||
}
|
||||
|
||||
// GitHubRelease represents a GitHub release
|
||||
type GitHubRelease struct {
|
||||
TagName string `json:"tag_name"`
|
||||
Name string `json:"name"`
|
||||
Assets []struct {
|
||||
Name string `json:"name"`
|
||||
BrowserDownloadURL string `json:"browser_download_url"`
|
||||
} `json:"assets"`
|
||||
}
|
||||
|
||||
// PluginInstaller handles plugin installation from GitHub
|
||||
type PluginInstaller struct {
|
||||
db *storage.DB
|
||||
pluginsDir string
|
||||
httpClient *http.Client
|
||||
}
|
||||
|
||||
// NewPluginInstaller creates a new plugin installer
|
||||
func NewPluginInstaller(db *storage.DB, pluginsDir string) *PluginInstaller {
|
||||
return &PluginInstaller{
|
||||
db: db,
|
||||
pluginsDir: pluginsDir,
|
||||
httpClient: &http.Client{Timeout: 60 * time.Second},
|
||||
}
|
||||
}
|
||||
|
||||
// Install installs a plugin from a GitHub repository URL
|
||||
func (i *PluginInstaller) Install(ctx context.Context, repoURL, version string) error {
|
||||
log.Printf("[Installer] Installing plugin from %s (version: %s)", repoURL, version)
|
||||
|
||||
// Parse repository owner and name from URL
|
||||
owner, repo, err := parseGitHubURL(repoURL)
|
||||
if err != nil {
|
||||
return fmt.Errorf("invalid GitHub URL: %w", err)
|
||||
}
|
||||
|
||||
// Fetch plugin manifest
|
||||
manifest, err := i.fetchPluginManifest(owner, repo)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to fetch plugin manifest: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("[Installer] Found plugin: %s v%s", manifest.Name, manifest.Version)
|
||||
|
||||
// Check if plugin is already installed
|
||||
existing, err := i.db.GetExternalPlugin(manifest.ID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to check existing plugin: %w", err)
|
||||
}
|
||||
if existing != nil {
|
||||
return fmt.Errorf("plugin %s is already installed (version %s)", manifest.ID, existing.Version)
|
||||
}
|
||||
|
||||
// Determine which version to install
|
||||
targetVersion := version
|
||||
if targetVersion == "" || targetVersion == "latest" {
|
||||
release, err := i.getLatestRelease(owner, repo)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get latest release: %w", err)
|
||||
}
|
||||
targetVersion = release.TagName
|
||||
}
|
||||
|
||||
// Create plugin directory
|
||||
pluginDir := filepath.Join(i.pluginsDir, manifest.ID)
|
||||
if err := os.MkdirAll(pluginDir, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create plugin directory: %w", err)
|
||||
}
|
||||
|
||||
// Download and extract plugin binary
|
||||
binaryPath, err := i.downloadBinary(owner, repo, targetVersion, pluginDir)
|
||||
if err != nil {
|
||||
os.RemoveAll(pluginDir) // Clean up on error
|
||||
return fmt.Errorf("failed to download plugin binary: %w", err)
|
||||
}
|
||||
|
||||
// Download frontend assets if specified
|
||||
frontendDir := filepath.Join(pluginDir, "frontend")
|
||||
if manifest.Frontend.BundleURL != "" {
|
||||
if err := os.MkdirAll(frontendDir, 0755); err != nil {
|
||||
os.RemoveAll(pluginDir)
|
||||
return fmt.Errorf("failed to create frontend directory: %w", err)
|
||||
}
|
||||
|
||||
if err := i.downloadFrontendAssets(owner, repo, targetVersion, frontendDir); err != nil {
|
||||
os.RemoveAll(pluginDir)
|
||||
return fmt.Errorf("failed to download frontend assets: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Save plugin record to database
|
||||
// Marshal tab config to JSON
|
||||
tabConfigJSON := ""
|
||||
if manifest.Tab.ID != "" {
|
||||
tabBytes, err := json.Marshal(manifest.Tab)
|
||||
if err != nil {
|
||||
log.Printf("[Installer] Warning: failed to marshal tab config: %v", err)
|
||||
} else {
|
||||
tabConfigJSON = string(tabBytes)
|
||||
}
|
||||
}
|
||||
|
||||
record := &storage.ExternalPluginRecord{
|
||||
PluginRecord: storage.PluginRecord{
|
||||
ID: manifest.ID,
|
||||
Name: manifest.Name,
|
||||
Version: targetVersion,
|
||||
SourceType: "github",
|
||||
SourceURL: repoURL,
|
||||
Enabled: true,
|
||||
InstalledAt: time.Now(),
|
||||
UpdatedAt: time.Now(),
|
||||
TabConfig: tabConfigJSON,
|
||||
},
|
||||
BinaryPath: binaryPath,
|
||||
Permissions: manifest.Permissions,
|
||||
FrontendBundle: filepath.Join(frontendDir, "bundle.js"),
|
||||
FrontendCSS: filepath.Join(frontendDir, "bundle.css"),
|
||||
ProcessStatus: "stopped",
|
||||
}
|
||||
|
||||
if err := i.db.SaveExternalPlugin(record); err != nil {
|
||||
os.RemoveAll(pluginDir)
|
||||
return fmt.Errorf("failed to save plugin record: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("[Installer] Successfully installed plugin %s v%s", manifest.ID, targetVersion)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Update updates a plugin to the latest version
|
||||
func (i *PluginInstaller) Update(ctx context.Context, pluginID string) error {
|
||||
log.Printf("[Installer] Updating plugin %s", pluginID)
|
||||
|
||||
// Get existing plugin
|
||||
existing, err := i.db.GetExternalPlugin(pluginID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get plugin: %w", err)
|
||||
}
|
||||
if existing == nil {
|
||||
return fmt.Errorf("plugin %s not found", pluginID)
|
||||
}
|
||||
|
||||
// Uninstall old version
|
||||
if err := i.Uninstall(pluginID); err != nil {
|
||||
return fmt.Errorf("failed to uninstall old version: %w", err)
|
||||
}
|
||||
|
||||
// Reinstall latest version
|
||||
return i.Install(ctx, existing.SourceURL, "latest")
|
||||
}
|
||||
|
||||
// Uninstall removes a plugin
|
||||
func (i *PluginInstaller) Uninstall(pluginID string) error {
|
||||
log.Printf("[Installer] Uninstalling plugin %s", pluginID)
|
||||
|
||||
// Get plugin record
|
||||
plugin, err := i.db.GetExternalPlugin(pluginID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get plugin: %w", err)
|
||||
}
|
||||
if plugin == nil {
|
||||
return fmt.Errorf("plugin %s not found", pluginID)
|
||||
}
|
||||
|
||||
// Remove plugin directory
|
||||
pluginDir := filepath.Join(i.pluginsDir, pluginID)
|
||||
if err := os.RemoveAll(pluginDir); err != nil {
|
||||
log.Printf("[Installer] Warning: failed to remove plugin directory: %v", err)
|
||||
}
|
||||
|
||||
// Delete from database
|
||||
if err := i.db.DeletePlugin(pluginID); err != nil {
|
||||
return fmt.Errorf("failed to delete plugin from database: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("[Installer] Successfully uninstalled plugin %s", pluginID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// fetchPluginManifest fetches the plugin.yaml from GitHub
|
||||
func (i *PluginInstaller) fetchPluginManifest(owner, repo string) (*PluginManifest, error) {
|
||||
// Fetch raw plugin.yaml from main branch
|
||||
url := fmt.Sprintf("https://raw.githubusercontent.com/%s/%s/main/plugin.yaml", owner, repo)
|
||||
|
||||
resp, err := i.httpClient.Get(url)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to fetch manifest: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("failed to fetch manifest: HTTP %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
var manifest PluginManifest
|
||||
if err := yaml.NewDecoder(resp.Body).Decode(&manifest); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse manifest: %w", err)
|
||||
}
|
||||
|
||||
return &manifest, nil
|
||||
}
|
||||
|
||||
// getLatestRelease gets the latest GitHub release
|
||||
func (i *PluginInstaller) getLatestRelease(owner, repo string) (*GitHubRelease, error) {
|
||||
url := fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/latest", owner, repo)
|
||||
|
||||
resp, err := i.httpClient.Get(url)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to fetch releases: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("failed to fetch releases: HTTP %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
var release GitHubRelease
|
||||
if err := json.NewDecoder(resp.Body).Decode(&release); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse release: %w", err)
|
||||
}
|
||||
|
||||
return &release, nil
|
||||
}
|
||||
|
||||
// downloadBinary downloads the plugin binary for the current platform
|
||||
func (i *PluginInstaller) downloadBinary(owner, repo, version, destDir string) (string, error) {
|
||||
// Determine platform-specific binary name (use hyphen to match common naming convention)
|
||||
platform := fmt.Sprintf("%s-%s", runtime.GOOS, runtime.GOARCH)
|
||||
|
||||
// Get release
|
||||
url := fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/tags/%s", owner, repo, version)
|
||||
resp, err := i.httpClient.Get(url)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to fetch release: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var release GitHubRelease
|
||||
if err := json.NewDecoder(resp.Body).Decode(&release); err != nil {
|
||||
return "", fmt.Errorf("failed to parse release: %w", err)
|
||||
}
|
||||
|
||||
// Find matching asset
|
||||
var downloadURL string
|
||||
for _, asset := range release.Assets {
|
||||
if strings.Contains(asset.Name, platform) {
|
||||
downloadURL = asset.BrowserDownloadURL
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if downloadURL == "" {
|
||||
return "", fmt.Errorf("no binary found for platform %s", platform)
|
||||
}
|
||||
|
||||
// Download binary
|
||||
log.Printf("[Installer] Downloading binary from %s", downloadURL)
|
||||
resp, err = i.httpClient.Get(downloadURL)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to download binary: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
// Save to file
|
||||
binaryPath := filepath.Join(destDir, "plugin")
|
||||
file, err := os.OpenFile(binaryPath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0755)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create binary file: %w", err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if _, err := io.Copy(file, resp.Body); err != nil {
|
||||
return "", fmt.Errorf("failed to write binary: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("[Installer] Binary saved to %s", binaryPath)
|
||||
return binaryPath, nil
|
||||
}
|
||||
|
||||
// downloadFrontendAssets downloads frontend bundle and CSS
|
||||
func (i *PluginInstaller) downloadFrontendAssets(owner, repo, version, destDir string) error {
|
||||
// Get release to find frontend assets
|
||||
url := fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/tags/%s", owner, repo, version)
|
||||
resp, err := i.httpClient.Get(url)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to fetch release: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var release GitHubRelease
|
||||
if err := json.NewDecoder(resp.Body).Decode(&release); err != nil {
|
||||
return fmt.Errorf("failed to parse release: %w", err)
|
||||
}
|
||||
|
||||
// Download bundle.js if present
|
||||
for _, asset := range release.Assets {
|
||||
if asset.Name == "bundle.js" {
|
||||
log.Printf("[Installer] Downloading frontend bundle from %s", asset.BrowserDownloadURL)
|
||||
if err := i.downloadFile(asset.BrowserDownloadURL, filepath.Join(destDir, "bundle.js")); err != nil {
|
||||
return fmt.Errorf("failed to download JS bundle: %w", err)
|
||||
}
|
||||
}
|
||||
// Download bundle.css if present
|
||||
if asset.Name == "bundle.css" {
|
||||
log.Printf("[Installer] Downloading frontend CSS from %s", asset.BrowserDownloadURL)
|
||||
if err := i.downloadFile(asset.BrowserDownloadURL, filepath.Join(destDir, "bundle.css")); err != nil {
|
||||
return fmt.Errorf("failed to download CSS: %w", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// downloadFile downloads a file from a URL
|
||||
func (i *PluginInstaller) downloadFile(url, destPath string) error {
|
||||
log.Printf("[Installer] Downloading %s to %s", url, destPath)
|
||||
|
||||
resp, err := i.httpClient.Get(url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return fmt.Errorf("HTTP %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
file, err := os.Create(destPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
if _, err := io.Copy(file, resp.Body); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// extractTarGz extracts a .tar.gz archive
|
||||
func (i *PluginInstaller) extractTarGz(archivePath, destDir string) error {
|
||||
file, err := os.Open(archivePath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
gzr, err := gzip.NewReader(file)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer gzr.Close()
|
||||
|
||||
tr := tar.NewReader(gzr)
|
||||
|
||||
for {
|
||||
header, err := tr.Next()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
target := filepath.Join(destDir, header.Name)
|
||||
|
||||
switch header.Typeflag {
|
||||
case tar.TypeDir:
|
||||
if err := os.MkdirAll(target, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
case tar.TypeReg:
|
||||
file, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := io.Copy(file, tr); err != nil {
|
||||
file.Close()
|
||||
return err
|
||||
}
|
||||
file.Close()
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// parseGitHubURL extracts owner and repo from a GitHub URL
|
||||
func parseGitHubURL(url string) (owner, repo string, err error) {
|
||||
// Handle various GitHub URL formats:
|
||||
// https://github.com/owner/repo
|
||||
// https://github.com/owner/repo.git
|
||||
// github.com/owner/repo
|
||||
|
||||
url = strings.TrimPrefix(url, "https://")
|
||||
url = strings.TrimPrefix(url, "http://")
|
||||
url = strings.TrimPrefix(url, "www.")
|
||||
url = strings.TrimSuffix(url, ".git")
|
||||
url = strings.TrimSuffix(url, "/")
|
||||
|
||||
parts := strings.Split(url, "/")
|
||||
if len(parts) < 3 || parts[0] != "github.com" {
|
||||
return "", "", fmt.Errorf("invalid GitHub URL format")
|
||||
}
|
||||
|
||||
return parts[1], parts[2], nil
|
||||
}
|
||||
122
internal/plugins/external/permissions.go
vendored
122
internal/plugins/external/permissions.go
vendored
@@ -1,122 +0,0 @@
|
||||
package external
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Permission represents a plugin permission
|
||||
type Permission string
|
||||
|
||||
const (
|
||||
// Data access permissions
|
||||
PermContainersRead Permission = "containers:read"
|
||||
PermHostsRead Permission = "hosts:read"
|
||||
PermStorageRead Permission = "storage:read"
|
||||
PermStorageWrite Permission = "storage:write"
|
||||
|
||||
// API permissions
|
||||
PermAPIRoutes Permission = "api:routes"
|
||||
PermAPIEvents Permission = "api:events"
|
||||
|
||||
// UI permissions
|
||||
PermUITab Permission = "ui:tab"
|
||||
PermUIBadge Permission = "ui:badge"
|
||||
PermUIEnrich Permission = "ui:enrich"
|
||||
)
|
||||
|
||||
// PermissionChecker validates plugin permissions
|
||||
type PermissionChecker struct {
|
||||
pluginID string
|
||||
permissions map[Permission]bool
|
||||
}
|
||||
|
||||
// NewPermissionChecker creates a permission checker for a plugin
|
||||
func NewPermissionChecker(pluginID string, permissions []string) *PermissionChecker {
|
||||
permMap := make(map[Permission]bool)
|
||||
for _, p := range permissions {
|
||||
permMap[Permission(p)] = true
|
||||
}
|
||||
|
||||
return &PermissionChecker{
|
||||
pluginID: pluginID,
|
||||
permissions: permMap,
|
||||
}
|
||||
}
|
||||
|
||||
// Check verifies if a plugin has a specific permission
|
||||
func (p *PermissionChecker) Check(permission Permission) error {
|
||||
if !p.permissions[permission] {
|
||||
return fmt.Errorf("plugin %s does not have permission: %s", p.pluginID, permission)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckAny verifies if a plugin has any of the specified permissions
|
||||
func (p *PermissionChecker) CheckAny(permissions ...Permission) error {
|
||||
for _, perm := range permissions {
|
||||
if p.permissions[perm] {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return fmt.Errorf("plugin %s does not have any of the required permissions: %v", p.pluginID, permissions)
|
||||
}
|
||||
|
||||
// Has returns true if the plugin has the specified permission
|
||||
func (p *PermissionChecker) Has(permission Permission) bool {
|
||||
return p.permissions[permission]
|
||||
}
|
||||
|
||||
// CanAccessEndpoint checks if a plugin can access a specific API endpoint
|
||||
func (p *PermissionChecker) CanAccessEndpoint(method, path string) error {
|
||||
// Plugin routes require api:routes permission
|
||||
if strings.HasPrefix(path, "/api/p/") {
|
||||
return p.Check(PermAPIRoutes)
|
||||
}
|
||||
|
||||
// Census API callbacks are checked individually in the gRPC server
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidatePermissions validates that all requested permissions are known
|
||||
func ValidatePermissions(permissions []string) error {
|
||||
knownPerms := map[string]bool{
|
||||
string(PermContainersRead): true,
|
||||
string(PermHostsRead): true,
|
||||
string(PermStorageRead): true,
|
||||
string(PermStorageWrite): true,
|
||||
string(PermAPIRoutes): true,
|
||||
string(PermAPIEvents): true,
|
||||
string(PermUITab): true,
|
||||
string(PermUIBadge): true,
|
||||
string(PermUIEnrich): true,
|
||||
}
|
||||
|
||||
for _, p := range permissions {
|
||||
if !knownPerms[p] {
|
||||
return fmt.Errorf("unknown permission: %s", p)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetPermissionDescription returns a human-readable description
|
||||
func GetPermissionDescription(perm Permission) string {
|
||||
descriptions := map[Permission]string{
|
||||
PermContainersRead: "Read container data from all hosts",
|
||||
PermHostsRead: "Read host configuration and metadata",
|
||||
PermStorageRead: "Read plugin-specific data from storage",
|
||||
PermStorageWrite: "Write plugin-specific data to storage",
|
||||
PermAPIRoutes: "Register custom HTTP API routes",
|
||||
PermAPIEvents: "Subscribe to system events",
|
||||
PermUITab: "Add a custom UI tab to the interface",
|
||||
PermUIBadge: "Display badges on container cards",
|
||||
PermUIEnrich: "Add custom data to container details",
|
||||
}
|
||||
|
||||
if desc, ok := descriptions[perm]; ok {
|
||||
return desc
|
||||
}
|
||||
return "Unknown permission"
|
||||
}
|
||||
486
internal/plugins/external/supervisor.go
vendored
486
internal/plugins/external/supervisor.go
vendored
@@ -1,486 +0,0 @@
|
||||
package external
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
|
||||
pb "github.com/container-census/container-census/internal/plugins/proto"
|
||||
"github.com/container-census/container-census/internal/storage"
|
||||
)
|
||||
|
||||
// PluginStatus represents the current state of a plugin process
|
||||
type PluginStatus struct {
|
||||
PluginID string
|
||||
ProcessStatus string // "starting", "running", "stopping", "stopped", "failed"
|
||||
HealthStatus string // "healthy", "unhealthy", "unknown"
|
||||
GRPCPort int
|
||||
RestartCount int
|
||||
LastRestart time.Time
|
||||
Error string
|
||||
}
|
||||
|
||||
// PluginProcess represents a running plugin process
|
||||
type PluginProcess struct {
|
||||
PluginID string
|
||||
Cmd *exec.Cmd
|
||||
GRPCClient pb.PluginClient
|
||||
GRPCConn *grpc.ClientConn
|
||||
GRPCPort int
|
||||
Status string
|
||||
HealthStatus string
|
||||
RestartCount int
|
||||
LastRestart time.Time
|
||||
CancelFunc context.CancelFunc
|
||||
StdoutLog *CircularLog
|
||||
StderrLog *CircularLog
|
||||
}
|
||||
|
||||
// CircularLog stores recent log lines
|
||||
type CircularLog struct {
|
||||
mu sync.Mutex
|
||||
lines []string
|
||||
maxSize int
|
||||
index int
|
||||
}
|
||||
|
||||
func NewCircularLog(maxSize int) *CircularLog {
|
||||
return &CircularLog{
|
||||
lines: make([]string, 0, maxSize),
|
||||
maxSize: maxSize,
|
||||
}
|
||||
}
|
||||
|
||||
func (cl *CircularLog) Add(line string) {
|
||||
cl.mu.Lock()
|
||||
defer cl.mu.Unlock()
|
||||
|
||||
if len(cl.lines) < cl.maxSize {
|
||||
cl.lines = append(cl.lines, line)
|
||||
} else {
|
||||
cl.lines[cl.index] = line
|
||||
cl.index = (cl.index + 1) % cl.maxSize
|
||||
}
|
||||
}
|
||||
|
||||
func (cl *CircularLog) GetLines() []string {
|
||||
cl.mu.Lock()
|
||||
defer cl.mu.Unlock()
|
||||
|
||||
if len(cl.lines) < cl.maxSize {
|
||||
return append([]string(nil), cl.lines...)
|
||||
}
|
||||
|
||||
// Rotate to get chronological order
|
||||
result := make([]string, len(cl.lines))
|
||||
for i := 0; i < len(cl.lines); i++ {
|
||||
result[i] = cl.lines[(cl.index+i)%cl.maxSize]
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// ExternalPluginSupervisor manages external plugin processes
|
||||
type ExternalPluginSupervisor struct {
|
||||
mu sync.RWMutex
|
||||
db *storage.DB
|
||||
processes map[string]*PluginProcess
|
||||
censusAPIAddress string // gRPC address for Census API
|
||||
censusAPIServer *grpc.Server
|
||||
basePort int // Starting port for plugin gRPC servers
|
||||
portCounter int
|
||||
healthCheckPeriod time.Duration
|
||||
maxRestarts int
|
||||
}
|
||||
|
||||
// NewExternalPluginSupervisor creates a new supervisor
|
||||
func NewExternalPluginSupervisor(db *storage.DB, censusAPIAddress string, basePort int) *ExternalPluginSupervisor {
|
||||
return &ExternalPluginSupervisor{
|
||||
db: db,
|
||||
processes: make(map[string]*PluginProcess),
|
||||
censusAPIAddress: censusAPIAddress,
|
||||
basePort: basePort,
|
||||
portCounter: 0,
|
||||
healthCheckPeriod: 10 * time.Second,
|
||||
maxRestarts: 3,
|
||||
}
|
||||
}
|
||||
|
||||
// StartPlugin starts a plugin process
|
||||
func (s *ExternalPluginSupervisor) StartPlugin(ctx context.Context, pluginID string) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
// Check if already running
|
||||
if proc, exists := s.processes[pluginID]; exists {
|
||||
if proc.Status == "running" {
|
||||
return fmt.Errorf("plugin %s is already running", pluginID)
|
||||
}
|
||||
// Clean up old process
|
||||
s.stopPluginLocked(pluginID)
|
||||
}
|
||||
|
||||
// Load plugin metadata from database
|
||||
plugin, err := s.db.GetExternalPlugin(pluginID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to load plugin metadata: %w", err)
|
||||
}
|
||||
|
||||
if plugin.BinaryPath == "" {
|
||||
return fmt.Errorf("plugin binary path not set")
|
||||
}
|
||||
|
||||
// Allocate gRPC port
|
||||
grpcPort := s.basePort + s.portCounter
|
||||
s.portCounter++
|
||||
|
||||
// Create process context
|
||||
procCtx, cancel := context.WithCancel(ctx)
|
||||
|
||||
// Create command
|
||||
cmd := exec.CommandContext(procCtx, plugin.BinaryPath)
|
||||
cmd.Env = append(os.Environ(),
|
||||
fmt.Sprintf("PLUGIN_ID=%s", pluginID),
|
||||
fmt.Sprintf("GRPC_PORT=%d", grpcPort),
|
||||
fmt.Sprintf("CENSUS_API=%s", s.censusAPIAddress),
|
||||
)
|
||||
|
||||
// Create circular logs
|
||||
stdoutLog := NewCircularLog(100)
|
||||
stderrLog := NewCircularLog(100)
|
||||
|
||||
// Capture stdout/stderr
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
cancel()
|
||||
return fmt.Errorf("failed to create stdout pipe: %w", err)
|
||||
}
|
||||
|
||||
stderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
cancel()
|
||||
return fmt.Errorf("failed to create stderr pipe: %w", err)
|
||||
}
|
||||
|
||||
// Start process
|
||||
if err := cmd.Start(); err != nil {
|
||||
cancel()
|
||||
return fmt.Errorf("failed to start plugin process: %w", err)
|
||||
}
|
||||
|
||||
// Create process record
|
||||
process := &PluginProcess{
|
||||
PluginID: pluginID,
|
||||
Cmd: cmd,
|
||||
GRPCPort: grpcPort,
|
||||
Status: "starting",
|
||||
HealthStatus: "unknown",
|
||||
RestartCount: 0,
|
||||
CancelFunc: cancel,
|
||||
StdoutLog: stdoutLog,
|
||||
StderrLog: stderrLog,
|
||||
}
|
||||
|
||||
s.processes[pluginID] = process
|
||||
|
||||
log.Printf("[Supervisor] Started plugin %s on port %d (PID: %d)", pluginID, grpcPort, cmd.Process.Pid)
|
||||
|
||||
// Start log readers
|
||||
go s.readLogLines(pluginID, stdout, stdoutLog, "stdout")
|
||||
go s.readLogLines(pluginID, stderr, stderrLog, "stderr")
|
||||
|
||||
// Wait for gRPC server to be ready
|
||||
go s.waitForGRPCReady(procCtx, pluginID, grpcPort)
|
||||
|
||||
// Start monitoring
|
||||
go s.monitorProcess(procCtx, pluginID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// waitForGRPCReady waits for the plugin's gRPC server to be ready
|
||||
func (s *ExternalPluginSupervisor) waitForGRPCReady(ctx context.Context, pluginID string, port int) {
|
||||
timeout := 30 * time.Second
|
||||
deadline := time.Now().Add(timeout)
|
||||
|
||||
for time.Now().Before(deadline) {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
|
||||
// Try to connect
|
||||
conn, err := grpc.NewClient(
|
||||
fmt.Sprintf("localhost:%d", port),
|
||||
grpc.WithTransportCredentials(insecure.NewCredentials()),
|
||||
)
|
||||
if err != nil {
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
continue
|
||||
}
|
||||
|
||||
client := pb.NewPluginClient(conn)
|
||||
|
||||
// Try Init RPC
|
||||
initReq := &pb.InitRequest{
|
||||
PluginId: pluginID,
|
||||
CensusApiAddress: s.censusAPIAddress,
|
||||
CensusVersion: "1.0.0", // TODO: Get from version package
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
resp, err := client.Init(ctx, initReq)
|
||||
cancel()
|
||||
|
||||
if err == nil && resp.Success {
|
||||
s.mu.Lock()
|
||||
if proc, exists := s.processes[pluginID]; exists {
|
||||
proc.GRPCClient = client
|
||||
proc.GRPCConn = conn
|
||||
proc.Status = "running"
|
||||
proc.HealthStatus = "healthy"
|
||||
}
|
||||
s.mu.Unlock()
|
||||
|
||||
log.Printf("[Supervisor] Plugin %s initialized successfully", pluginID)
|
||||
return
|
||||
}
|
||||
|
||||
conn.Close()
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
}
|
||||
|
||||
// Timeout
|
||||
log.Printf("[Supervisor] Plugin %s failed to initialize within %v", pluginID, timeout)
|
||||
s.mu.Lock()
|
||||
if proc, exists := s.processes[pluginID]; exists {
|
||||
proc.Status = "failed"
|
||||
proc.HealthStatus = "unhealthy"
|
||||
}
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
// readLogLines reads log lines from reader
|
||||
func (s *ExternalPluginSupervisor) readLogLines(pluginID string, reader interface{}, log *CircularLog, stream string) {
|
||||
// Implementation would use bufio.Scanner to read lines
|
||||
// For brevity, simplified here
|
||||
}
|
||||
|
||||
// monitorProcess monitors plugin health and handles restarts
|
||||
func (s *ExternalPluginSupervisor) monitorProcess(ctx context.Context, pluginID string) {
|
||||
ticker := time.NewTicker(s.healthCheckPeriod)
|
||||
defer ticker.Stop()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case <-ticker.C:
|
||||
s.performHealthCheck(ctx, pluginID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// performHealthCheck checks plugin health
|
||||
func (s *ExternalPluginSupervisor) performHealthCheck(ctx context.Context, pluginID string) {
|
||||
s.mu.RLock()
|
||||
proc, exists := s.processes[pluginID]
|
||||
s.mu.RUnlock()
|
||||
|
||||
if !exists || proc.Status != "running" {
|
||||
return
|
||||
}
|
||||
|
||||
// Check if process is still alive
|
||||
if proc.Cmd.ProcessState != nil && proc.Cmd.ProcessState.Exited() {
|
||||
log.Printf("[Supervisor] Plugin %s process exited unexpectedly", pluginID)
|
||||
s.handleProcessFailure(ctx, pluginID)
|
||||
return
|
||||
}
|
||||
|
||||
// Perform gRPC healthcheck
|
||||
if proc.GRPCClient != nil {
|
||||
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
resp, err := proc.GRPCClient.Healthcheck(ctx, &pb.HealthcheckRequest{
|
||||
PluginId: pluginID,
|
||||
})
|
||||
|
||||
s.mu.Lock()
|
||||
if err != nil || !resp.Healthy {
|
||||
proc.HealthStatus = "unhealthy"
|
||||
log.Printf("[Supervisor] Plugin %s health check failed: %v", pluginID, err)
|
||||
} else {
|
||||
proc.HealthStatus = "healthy"
|
||||
}
|
||||
s.mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// handleProcessFailure handles plugin process failures
|
||||
func (s *ExternalPluginSupervisor) handleProcessFailure(ctx context.Context, pluginID string) {
|
||||
s.mu.Lock()
|
||||
proc, exists := s.processes[pluginID]
|
||||
if !exists {
|
||||
s.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
proc.Status = "failed"
|
||||
proc.RestartCount++
|
||||
proc.LastRestart = time.Now()
|
||||
|
||||
shouldRestart := proc.RestartCount <= s.maxRestarts
|
||||
s.mu.Unlock()
|
||||
|
||||
if shouldRestart {
|
||||
log.Printf("[Supervisor] Attempting to restart plugin %s (attempt %d/%d)",
|
||||
pluginID, proc.RestartCount, s.maxRestarts)
|
||||
|
||||
// Wait before restart
|
||||
time.Sleep(2 * time.Second)
|
||||
|
||||
// Restart
|
||||
if err := s.StartPlugin(ctx, pluginID); err != nil {
|
||||
log.Printf("[Supervisor] Failed to restart plugin %s: %v", pluginID, err)
|
||||
}
|
||||
} else {
|
||||
log.Printf("[Supervisor] Plugin %s exceeded max restart attempts (%d), giving up",
|
||||
pluginID, s.maxRestarts)
|
||||
|
||||
// Disable plugin in database
|
||||
if err := s.db.SetPluginEnabled(pluginID, false); err != nil {
|
||||
log.Printf("[Supervisor] Failed to disable plugin %s: %v", pluginID, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// StopPlugin gracefully stops a plugin
|
||||
func (s *ExternalPluginSupervisor) StopPlugin(pluginID string) error {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
return s.stopPluginLocked(pluginID)
|
||||
}
|
||||
|
||||
func (s *ExternalPluginSupervisor) stopPluginLocked(pluginID string) error {
|
||||
proc, exists := s.processes[pluginID]
|
||||
if !exists {
|
||||
return fmt.Errorf("plugin %s is not running", pluginID)
|
||||
}
|
||||
|
||||
log.Printf("[Supervisor] Stopping plugin %s", pluginID)
|
||||
proc.Status = "stopping"
|
||||
|
||||
// Call Stop RPC if available
|
||||
if proc.GRPCClient != nil {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
|
||||
proc.GRPCClient.Stop(ctx, &pb.StopRequest{PluginId: pluginID})
|
||||
}
|
||||
|
||||
// Close gRPC connection
|
||||
if proc.GRPCConn != nil {
|
||||
proc.GRPCConn.Close()
|
||||
}
|
||||
|
||||
// Cancel context to stop monitors
|
||||
if proc.CancelFunc != nil {
|
||||
proc.CancelFunc()
|
||||
}
|
||||
|
||||
// Kill process if still running
|
||||
if proc.Cmd.Process != nil && proc.Cmd.ProcessState == nil {
|
||||
proc.Cmd.Process.Kill()
|
||||
}
|
||||
|
||||
proc.Status = "stopped"
|
||||
delete(s.processes, pluginID)
|
||||
|
||||
log.Printf("[Supervisor] Plugin %s stopped", pluginID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// RestartPlugin restarts a plugin
|
||||
func (s *ExternalPluginSupervisor) RestartPlugin(ctx context.Context, pluginID string) error {
|
||||
if err := s.StopPlugin(pluginID); err != nil {
|
||||
log.Printf("[Supervisor] Error stopping plugin %s for restart: %v", pluginID, err)
|
||||
}
|
||||
|
||||
time.Sleep(1 * time.Second)
|
||||
return s.StartPlugin(ctx, pluginID)
|
||||
}
|
||||
|
||||
// GetPluginStatus returns the status of a plugin
|
||||
func (s *ExternalPluginSupervisor) GetPluginStatus(pluginID string) (PluginStatus, error) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
proc, exists := s.processes[pluginID]
|
||||
if !exists {
|
||||
return PluginStatus{
|
||||
PluginID: pluginID,
|
||||
ProcessStatus: "stopped",
|
||||
HealthStatus: "unknown",
|
||||
}, nil
|
||||
}
|
||||
|
||||
return PluginStatus{
|
||||
PluginID: proc.PluginID,
|
||||
ProcessStatus: proc.Status,
|
||||
HealthStatus: proc.HealthStatus,
|
||||
GRPCPort: proc.GRPCPort,
|
||||
RestartCount: proc.RestartCount,
|
||||
LastRestart: proc.LastRestart,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// GetPluginLogs returns recent log lines
|
||||
func (s *ExternalPluginSupervisor) GetPluginLogs(pluginID string) (stdout, stderr []string, err error) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
proc, exists := s.processes[pluginID]
|
||||
if !exists {
|
||||
return nil, nil, fmt.Errorf("plugin %s is not running", pluginID)
|
||||
}
|
||||
|
||||
return proc.StdoutLog.GetLines(), proc.StderrLog.GetLines(), nil
|
||||
}
|
||||
|
||||
// GetGRPCClient returns the gRPC client for a plugin
|
||||
func (s *ExternalPluginSupervisor) GetGRPCClient(pluginID string) (pb.PluginClient, error) {
|
||||
s.mu.RLock()
|
||||
defer s.mu.RUnlock()
|
||||
|
||||
proc, exists := s.processes[pluginID]
|
||||
if !exists {
|
||||
return nil, fmt.Errorf("plugin %s is not running", pluginID)
|
||||
}
|
||||
|
||||
if proc.GRPCClient == nil {
|
||||
return nil, fmt.Errorf("plugin %s gRPC client not ready", pluginID)
|
||||
}
|
||||
|
||||
return proc.GRPCClient, nil
|
||||
}
|
||||
|
||||
// StopAll stops all running plugins
|
||||
func (s *ExternalPluginSupervisor) StopAll() {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
|
||||
log.Printf("[Supervisor] Stopping all plugins...")
|
||||
|
||||
for pluginID := range s.processes {
|
||||
s.stopPluginLocked(pluginID)
|
||||
}
|
||||
}
|
||||
@@ -4,18 +4,14 @@ import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"sort"
|
||||
"strconv"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/container-census/container-census/internal/models"
|
||||
"github.com/container-census/container-census/internal/plugins/external"
|
||||
pb "github.com/container-census/container-census/internal/plugins/proto"
|
||||
"github.com/container-census/container-census/internal/storage"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
@@ -32,12 +28,6 @@ type Manager struct {
|
||||
eventBus *EventBusImpl
|
||||
router *mux.Router
|
||||
started bool
|
||||
|
||||
// External plugin support
|
||||
installer *external.PluginInstaller
|
||||
supervisor *external.ExternalPluginSupervisor
|
||||
censusAPIServer *external.CensusAPIServer
|
||||
pluginsDir string
|
||||
}
|
||||
|
||||
// PluginFactory creates a new plugin instance
|
||||
@@ -45,15 +35,6 @@ type PluginFactory func() Plugin
|
||||
|
||||
// NewManager creates a new plugin manager
|
||||
func NewManager(db *storage.DB, containers ContainerProvider, hosts HostProvider) *Manager {
|
||||
// Default to /app/data/plugins, but use ./data/plugins for local development
|
||||
pluginsDir := "/app/data/plugins"
|
||||
if dataDir := os.Getenv("DATA_DIR"); dataDir != "" {
|
||||
pluginsDir = dataDir + "/plugins"
|
||||
} else if _, err := os.Stat("/app/data"); os.IsNotExist(err) {
|
||||
// Running locally, not in Docker container
|
||||
pluginsDir = "./data/plugins"
|
||||
}
|
||||
|
||||
return &Manager{
|
||||
plugins: make(map[string]Plugin),
|
||||
pluginOrder: make([]string, 0),
|
||||
@@ -62,12 +43,6 @@ func NewManager(db *storage.DB, containers ContainerProvider, hosts HostProvider
|
||||
containers: containers,
|
||||
hosts: hosts,
|
||||
eventBus: NewEventBus(),
|
||||
|
||||
// External plugin infrastructure
|
||||
installer: external.NewPluginInstaller(db, pluginsDir),
|
||||
supervisor: external.NewExternalPluginSupervisor(db, "localhost:50052", 50100),
|
||||
censusAPIServer: external.NewCensusAPIServer(db),
|
||||
pluginsDir: pluginsDir,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,13 +53,6 @@ func (m *Manager) SetRouter(router *mux.Router) {
|
||||
m.router = router
|
||||
}
|
||||
|
||||
// GetCensusAPIServer returns the Census API server for plugin callbacks
|
||||
func (m *Manager) GetCensusAPIServer() *external.CensusAPIServer {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
return m.censusAPIServer
|
||||
}
|
||||
|
||||
// RegisterBuiltIn registers a built-in plugin factory
|
||||
func (m *Manager) RegisterBuiltIn(id string, factory PluginFactory) {
|
||||
m.mu.Lock()
|
||||
@@ -120,68 +88,6 @@ func (m *Manager) LoadBuiltInPlugins(ctx context.Context) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// LoadExternalPlugins loads and starts all enabled external plugins from database
|
||||
func (m *Manager) LoadExternalPlugins(ctx context.Context) error {
|
||||
// Get all plugin records from database
|
||||
records, err := m.db.GetAllPlugins()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get plugins from database: %w", err)
|
||||
}
|
||||
|
||||
// Filter for enabled external plugins
|
||||
for _, record := range records {
|
||||
// Skip built-in plugins (they're loaded via LoadBuiltInPlugins)
|
||||
if record.SourceType == "built_in" {
|
||||
continue
|
||||
}
|
||||
|
||||
// Skip disabled plugins
|
||||
if !record.Enabled {
|
||||
log.Printf("Skipping disabled external plugin: %s", record.ID)
|
||||
continue
|
||||
}
|
||||
|
||||
// Start the external plugin
|
||||
log.Printf("Loading external plugin: %s v%s", record.Name, record.Version)
|
||||
if err := m.StartExternalPlugin(ctx, record.ID); err != nil {
|
||||
log.Printf("Failed to start external plugin %s: %v", record.ID, err)
|
||||
continue
|
||||
}
|
||||
|
||||
// Mount routes for the plugin with retry (gRPC client needs time to connect)
|
||||
mounted := false
|
||||
for attempt := 1; attempt <= 5; attempt++ {
|
||||
if attempt > 1 {
|
||||
time.Sleep(time.Duration(attempt) * 500 * time.Millisecond)
|
||||
}
|
||||
if err := m.MountPluginRoutes(record.ID); err != nil {
|
||||
if attempt < 5 {
|
||||
log.Printf("[PluginManager] Attempt %d/5: Failed to mount routes for %s, retrying...", attempt, record.ID)
|
||||
continue
|
||||
}
|
||||
log.Printf("Failed to mount routes for plugin %s after 5 attempts: %v", record.ID, err)
|
||||
} else {
|
||||
mounted = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !mounted {
|
||||
log.Printf("Warning: Plugin %s started but routes not mounted", record.ID)
|
||||
} else {
|
||||
// Fetch and save tab configuration after successful route mounting
|
||||
if err := m.FetchAndSaveTabConfig(ctx, record.ID); err != nil {
|
||||
log.Printf("Warning: Failed to fetch tab config for %s: %v", record.ID, err)
|
||||
// Continue anyway - tab can be fetched later if needed
|
||||
}
|
||||
}
|
||||
|
||||
log.Printf("Loaded external plugin: %s v%s", record.Name, record.Version)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// loadPlugin initializes and registers a plugin
|
||||
func (m *Manager) loadPlugin(ctx context.Context, plugin Plugin) error {
|
||||
info := plugin.Info()
|
||||
@@ -630,296 +536,3 @@ func (s *scopedPluginDB) SetSetting(key string, value string) error {
|
||||
func (s *scopedPluginDB) GetAllSettings() (map[string]string, error) {
|
||||
return s.db.GetAllPluginSettings(s.pluginID)
|
||||
}
|
||||
// External Plugin Management Methods
|
||||
|
||||
// InstallExternalPlugin installs a plugin from a GitHub repository URL
|
||||
func (m *Manager) InstallExternalPlugin(ctx context.Context, repoURL, version string) error {
|
||||
log.Printf("[PluginManager] Installing external plugin from %s", repoURL)
|
||||
|
||||
// Install the plugin files and save to database
|
||||
if err := m.installer.Install(ctx, repoURL, version); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Get the plugin ID from the installer
|
||||
// The plugin ID is extracted during install, we need to get the plugin record
|
||||
plugins, err := m.db.GetAllPlugins()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get plugins after install: %w", err)
|
||||
}
|
||||
|
||||
// Find the newly installed plugin (it will be enabled and from the repo URL)
|
||||
var pluginID string
|
||||
for _, p := range plugins {
|
||||
if p.SourceURL == repoURL && p.Enabled {
|
||||
pluginID = p.ID
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if pluginID == "" {
|
||||
return fmt.Errorf("could not find installed plugin from %s", repoURL)
|
||||
}
|
||||
|
||||
log.Printf("[PluginManager] Starting installed plugin %s", pluginID)
|
||||
|
||||
// Start the plugin process
|
||||
if err := m.StartExternalPlugin(ctx, pluginID); err != nil {
|
||||
log.Printf("[PluginManager] Failed to start plugin %s: %v", pluginID, err)
|
||||
return fmt.Errorf("plugin installed but failed to start: %w", err)
|
||||
}
|
||||
|
||||
// Mount routes with retry
|
||||
mounted := false
|
||||
for attempt := 1; attempt <= 5; attempt++ {
|
||||
if attempt > 1 {
|
||||
time.Sleep(time.Duration(attempt) * 500 * time.Millisecond)
|
||||
}
|
||||
if err := m.MountPluginRoutes(pluginID); err != nil {
|
||||
if attempt < 5 {
|
||||
log.Printf("[PluginManager] Attempt %d/5: Failed to mount routes for %s, retrying...", attempt, pluginID)
|
||||
continue
|
||||
}
|
||||
log.Printf("Failed to mount routes for plugin %s after 5 attempts: %v", pluginID, err)
|
||||
} else {
|
||||
mounted = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if mounted {
|
||||
// Fetch and save tab configuration
|
||||
if err := m.FetchAndSaveTabConfig(ctx, pluginID); err != nil {
|
||||
log.Printf("Warning: Failed to fetch tab config for %s: %v", pluginID, err)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UpdateExternalPlugin updates an external plugin to the latest version
|
||||
func (m *Manager) UpdateExternalPlugin(ctx context.Context, pluginID string) error {
|
||||
log.Printf("[PluginManager] Updating external plugin %s", pluginID)
|
||||
return m.installer.Update(ctx, pluginID)
|
||||
}
|
||||
|
||||
// UninstallExternalPlugin removes an external plugin
|
||||
func (m *Manager) UninstallExternalPlugin(pluginID string) error {
|
||||
log.Printf("[PluginManager] Uninstalling external plugin %s", pluginID)
|
||||
|
||||
// Stop plugin process if running
|
||||
if err := m.supervisor.StopPlugin(pluginID); err != nil {
|
||||
log.Printf("[PluginManager] Warning: failed to stop plugin %s: %v", pluginID, err)
|
||||
}
|
||||
|
||||
// Unregister from Census API
|
||||
m.censusAPIServer.UnregisterPlugin(pluginID)
|
||||
|
||||
// Remove plugin files and database record
|
||||
return m.installer.Uninstall(pluginID)
|
||||
}
|
||||
|
||||
// GetExternalPluginLogs returns recent log output from a plugin process
|
||||
func (m *Manager) GetExternalPluginLogs(pluginID string) (stdout, stderr []string, err error) {
|
||||
return m.supervisor.GetPluginLogs(pluginID)
|
||||
}
|
||||
|
||||
// GetExternalPluginStatus returns the runtime status of an external plugin
|
||||
func (m *Manager) GetExternalPluginStatus(pluginID string) (external.PluginStatus, error) {
|
||||
return m.supervisor.GetPluginStatus(pluginID)
|
||||
}
|
||||
|
||||
// StartExternalPlugin starts an external plugin process
|
||||
func (m *Manager) StartExternalPlugin(ctx context.Context, pluginID string) error {
|
||||
log.Printf("[PluginManager] Starting external plugin %s", pluginID)
|
||||
|
||||
// Get plugin metadata
|
||||
plugin, err := m.db.GetExternalPlugin(pluginID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get plugin metadata: %w", err)
|
||||
}
|
||||
|
||||
// Register permissions with Census API
|
||||
if err := m.censusAPIServer.RegisterPlugin(pluginID, plugin.Permissions); err != nil {
|
||||
return fmt.Errorf("failed to register plugin permissions: %w", err)
|
||||
}
|
||||
|
||||
// Start plugin process
|
||||
if err := m.supervisor.StartPlugin(ctx, pluginID); err != nil {
|
||||
m.censusAPIServer.UnregisterPlugin(pluginID)
|
||||
return fmt.Errorf("failed to start plugin process: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// StopExternalPlugin stops an external plugin process
|
||||
func (m *Manager) StopExternalPlugin(pluginID string) error {
|
||||
log.Printf("[PluginManager] Stopping external plugin %s", pluginID)
|
||||
|
||||
// Stop plugin process
|
||||
if err := m.supervisor.StopPlugin(pluginID); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Unregister from Census API
|
||||
m.censusAPIServer.UnregisterPlugin(pluginID)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FetchAndSaveTabConfig retrieves tab configuration from a plugin via gRPC and saves it to the database
|
||||
func (m *Manager) FetchAndSaveTabConfig(ctx context.Context, pluginID string) error {
|
||||
log.Printf("[PluginManager] Fetching tab config for plugin %s", pluginID)
|
||||
|
||||
// Get the gRPC client from supervisor
|
||||
client, err := m.supervisor.GetGRPCClient(pluginID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get plugin gRPC client: %w", err)
|
||||
}
|
||||
|
||||
// Call GetTab via gRPC with timeout
|
||||
ctx, cancel := context.WithTimeout(ctx, 10*time.Second)
|
||||
defer cancel()
|
||||
|
||||
resp, err := client.GetTab(ctx, &pb.TabRequest{
|
||||
PluginId: pluginID,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to call GetTab: %w", err)
|
||||
}
|
||||
|
||||
// If plugin doesn't have a tab, skip
|
||||
if !resp.HasTab {
|
||||
log.Printf("[PluginManager] Plugin %s does not provide a tab", pluginID)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Create TabDefinition structure
|
||||
tabDef := TabDefinition{
|
||||
ID: resp.Id,
|
||||
Label: resp.Label,
|
||||
Icon: resp.Icon,
|
||||
Order: int(resp.Order),
|
||||
ScriptURL: resp.ScriptUrl,
|
||||
InitFunc: resp.InitFunc,
|
||||
}
|
||||
|
||||
// Get existing plugin record
|
||||
plugin, err := m.db.GetExternalPlugin(pluginID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get plugin record: %w", err)
|
||||
}
|
||||
|
||||
// Update tab_config field as map[string]string
|
||||
plugin.TabConfig = map[string]string{
|
||||
"id": tabDef.ID,
|
||||
"label": tabDef.Label,
|
||||
"icon": tabDef.Icon,
|
||||
"order": fmt.Sprintf("%d", tabDef.Order),
|
||||
"script_url": tabDef.ScriptURL,
|
||||
"init_func": tabDef.InitFunc,
|
||||
}
|
||||
|
||||
// Save back to database
|
||||
if err := m.db.SaveExternalPlugin(plugin); err != nil {
|
||||
return fmt.Errorf("failed to save tab config: %w", err)
|
||||
}
|
||||
|
||||
log.Printf("[PluginManager] Successfully saved tab config for %s: %s", pluginID, tabDef.Label)
|
||||
return nil
|
||||
}
|
||||
|
||||
// MountPluginRoutes dynamically mounts routes for an external plugin
|
||||
func (m *Manager) MountPluginRoutes(pluginID string) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
|
||||
if m.router == nil {
|
||||
return fmt.Errorf("router not set")
|
||||
}
|
||||
|
||||
// Get gRPC client for the plugin
|
||||
client, err := m.supervisor.GetGRPCClient(pluginID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get plugin gRPC client: %w", err)
|
||||
}
|
||||
|
||||
// Create a subrouter for this plugin under /api/p/{pluginID}/*
|
||||
pluginPath := fmt.Sprintf("/p/%s", pluginID)
|
||||
pluginRouter := m.router.PathPrefix(pluginPath).Subrouter()
|
||||
|
||||
// Mount a catch-all handler that forwards requests to the plugin via gRPC
|
||||
pluginRouter.PathPrefix("/").HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
m.handlePluginRoute(w, r, pluginID, client)
|
||||
})
|
||||
|
||||
log.Printf("[PluginManager] Mounted routes for plugin %s at /api%s", pluginID, pluginPath)
|
||||
return nil
|
||||
}
|
||||
|
||||
// handlePluginRoute forwards HTTP requests to an external plugin via gRPC
|
||||
func (m *Manager) handlePluginRoute(w http.ResponseWriter, r *http.Request, pluginID string, client pb.PluginClient) {
|
||||
// Extract path after /api/p/{pluginID}/
|
||||
pathPrefix := fmt.Sprintf("/api/p/%s/", pluginID)
|
||||
pluginPath := r.URL.Path
|
||||
if len(pluginPath) >= len(pathPrefix) {
|
||||
pluginPath = pluginPath[len(pathPrefix)-1:] // Keep the leading slash
|
||||
}
|
||||
|
||||
// Read request body
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
http.Error(w, "Failed to read request body", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Convert headers to map
|
||||
headers := make(map[string]string)
|
||||
for key, values := range r.Header {
|
||||
if len(values) > 0 {
|
||||
headers[key] = values[0]
|
||||
}
|
||||
}
|
||||
|
||||
// Convert query parameters to map
|
||||
queryParams := make(map[string]string)
|
||||
for key, values := range r.URL.Query() {
|
||||
if len(values) > 0 {
|
||||
queryParams[key] = values[0]
|
||||
}
|
||||
}
|
||||
|
||||
// Call plugin via gRPC
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
|
||||
defer cancel()
|
||||
|
||||
resp, err := client.HandleRoute(ctx, &pb.RouteRequest{
|
||||
PluginId: pluginID,
|
||||
Method: r.Method,
|
||||
Path: pluginPath,
|
||||
Headers: headers,
|
||||
Body: body,
|
||||
QueryParams: queryParams,
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
log.Printf("[PluginManager] Plugin %s route handler error: %v", pluginID, err)
|
||||
http.Error(w, "Plugin error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
// Set response headers
|
||||
for key, value := range resp.Headers {
|
||||
w.Header().Set(key, value)
|
||||
}
|
||||
|
||||
// Set status code
|
||||
w.WriteHeader(int(resp.StatusCode))
|
||||
|
||||
// Write response body
|
||||
if len(resp.Body) > 0 {
|
||||
w.Write(resp.Body)
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,358 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package census.plugin;
|
||||
option go_package = "github.com/selfhosters-cc/container-census/internal/plugins/proto";
|
||||
|
||||
// Plugin service - implemented by external plugin processes
|
||||
service Plugin {
|
||||
// Lifecycle methods
|
||||
rpc Init(InitRequest) returns (InitResponse);
|
||||
rpc Start(StartRequest) returns (StartResponse);
|
||||
rpc Stop(StopRequest) returns (StopResponse);
|
||||
rpc Healthcheck(HealthcheckRequest) returns (HealthcheckResponse);
|
||||
|
||||
// Capability methods
|
||||
rpc GetInfo(InfoRequest) returns (InfoResponse);
|
||||
rpc GetTab(TabRequest) returns (TabResponse);
|
||||
rpc GetBadges(BadgesRequest) returns (BadgesResponse);
|
||||
rpc HandleRoute(RouteRequest) returns (RouteResponse);
|
||||
rpc EnrichContainer(EnrichRequest) returns (EnrichResponse);
|
||||
rpc HandleEvent(EventRequest) returns (EventResponse);
|
||||
rpc GetSettings(SettingsRequest) returns (SettingsResponse);
|
||||
rpc UpdateSettings(UpdateSettingsRequest) returns (UpdateSettingsResponse);
|
||||
}
|
||||
|
||||
// Census API service - implemented by main server (callback API for plugins)
|
||||
service CensusAPI {
|
||||
rpc GetContainers(GetContainersRequest) returns (GetContainersResponse);
|
||||
rpc GetContainer(GetContainerRequest) returns (GetContainerResponse);
|
||||
rpc GetHosts(GetHostsRequest) returns (GetHostsResponse);
|
||||
rpc GetHost(GetHostRequest) returns (GetHostResponse);
|
||||
rpc GetPluginData(GetPluginDataRequest) returns (GetPluginDataResponse);
|
||||
rpc SetPluginData(SetPluginDataRequest) returns (SetPluginDataResponse);
|
||||
rpc DeletePluginData(DeletePluginDataRequest) returns (DeletePluginDataResponse);
|
||||
rpc Log(LogRequest) returns (LogResponse);
|
||||
rpc SendEvent(SendEventRequest) returns (SendEventResponse);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Plugin Service Messages
|
||||
// ============================================================================
|
||||
|
||||
// Init - Called when plugin is first loaded
|
||||
message InitRequest {
|
||||
string plugin_id = 1;
|
||||
map<string, string> config = 2;
|
||||
string census_api_address = 3; // gRPC address for callbacks (e.g., "localhost:50052")
|
||||
string census_version = 4;
|
||||
}
|
||||
|
||||
message InitResponse {
|
||||
bool success = 1;
|
||||
string error = 2;
|
||||
}
|
||||
|
||||
// Start - Called to start plugin operations
|
||||
message StartRequest {
|
||||
string plugin_id = 1;
|
||||
}
|
||||
|
||||
message StartResponse {
|
||||
bool success = 1;
|
||||
string error = 2;
|
||||
}
|
||||
|
||||
// Stop - Called to gracefully stop plugin
|
||||
message StopRequest {
|
||||
string plugin_id = 1;
|
||||
}
|
||||
|
||||
message StopResponse {
|
||||
bool success = 1;
|
||||
}
|
||||
|
||||
// Healthcheck - Called periodically to verify plugin is alive
|
||||
message HealthcheckRequest {
|
||||
string plugin_id = 1;
|
||||
}
|
||||
|
||||
message HealthcheckResponse {
|
||||
bool healthy = 1;
|
||||
string status = 2; // "running", "degraded", "failed"
|
||||
map<string, string> metrics = 3; // Optional metrics
|
||||
}
|
||||
|
||||
// GetInfo - Returns plugin metadata
|
||||
message InfoRequest {
|
||||
string plugin_id = 1;
|
||||
}
|
||||
|
||||
message InfoResponse {
|
||||
string id = 1;
|
||||
string name = 2;
|
||||
string version = 3;
|
||||
string description = 4;
|
||||
string author = 5;
|
||||
string homepage = 6;
|
||||
repeated string capabilities = 7; // "ui_tab", "ui_badge", "container_enrichment", etc.
|
||||
}
|
||||
|
||||
// GetTab - Returns UI tab configuration
|
||||
message TabRequest {
|
||||
string plugin_id = 1;
|
||||
}
|
||||
|
||||
message TabResponse {
|
||||
bool has_tab = 1;
|
||||
string id = 2;
|
||||
string label = 3;
|
||||
string icon = 4;
|
||||
int32 order = 5;
|
||||
string script_url = 6; // URL to frontend bundle
|
||||
string css_url = 7; // URL to frontend CSS
|
||||
string init_func = 8; // JavaScript function name to call
|
||||
}
|
||||
|
||||
// GetBadges - Returns badges to display on container cards
|
||||
message BadgesRequest {
|
||||
string plugin_id = 1;
|
||||
Container container = 2;
|
||||
}
|
||||
|
||||
message BadgesResponse {
|
||||
repeated Badge badges = 1;
|
||||
}
|
||||
|
||||
message Badge {
|
||||
string id = 1;
|
||||
string label = 2;
|
||||
string icon = 3;
|
||||
string color = 4;
|
||||
string tooltip = 5;
|
||||
string link = 6;
|
||||
int32 priority = 7;
|
||||
}
|
||||
|
||||
// HandleRoute - Handles HTTP requests to plugin routes
|
||||
message RouteRequest {
|
||||
string plugin_id = 1;
|
||||
string method = 2; // GET, POST, PUT, DELETE, etc.
|
||||
string path = 3; // Path after /api/p/{plugin-id}/
|
||||
map<string, string> headers = 4;
|
||||
bytes body = 5;
|
||||
map<string, string> query_params = 6;
|
||||
map<string, string> path_params = 7;
|
||||
}
|
||||
|
||||
message RouteResponse {
|
||||
int32 status_code = 1;
|
||||
map<string, string> headers = 2;
|
||||
bytes body = 3;
|
||||
}
|
||||
|
||||
// EnrichContainer - Adds custom data to containers
|
||||
message EnrichRequest {
|
||||
string plugin_id = 1;
|
||||
Container container = 2;
|
||||
}
|
||||
|
||||
message EnrichResponse {
|
||||
map<string, string> plugin_data = 1; // Data to add to container.plugin_data
|
||||
}
|
||||
|
||||
// HandleEvent - Receives system events
|
||||
message EventRequest {
|
||||
string plugin_id = 1;
|
||||
string event_type = 2; // "scan_complete", "container_state_change", "image_updated", etc.
|
||||
string timestamp = 3;
|
||||
map<string, string> data = 4;
|
||||
}
|
||||
|
||||
message EventResponse {
|
||||
bool handled = 1;
|
||||
}
|
||||
|
||||
// GetSettings - Returns plugin settings schema
|
||||
message SettingsRequest {
|
||||
string plugin_id = 1;
|
||||
}
|
||||
|
||||
message SettingsResponse {
|
||||
string schema_json = 1; // JSON schema for settings
|
||||
string values_json = 2; // Current setting values as JSON
|
||||
}
|
||||
|
||||
// UpdateSettings - Updates plugin settings
|
||||
message UpdateSettingsRequest {
|
||||
string plugin_id = 1;
|
||||
string values_json = 2; // New settings as JSON
|
||||
}
|
||||
|
||||
message UpdateSettingsResponse {
|
||||
bool success = 1;
|
||||
string error = 2;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Census API Service Messages
|
||||
// ============================================================================
|
||||
|
||||
// GetContainers - Fetch all containers
|
||||
message GetContainersRequest {
|
||||
string plugin_id = 1;
|
||||
bool latest_only = 2; // If true, only return latest scan results
|
||||
int64 host_id = 3; // If > 0, filter by host
|
||||
}
|
||||
|
||||
message GetContainersResponse {
|
||||
repeated Container containers = 1;
|
||||
}
|
||||
|
||||
// GetContainer - Fetch specific container
|
||||
message GetContainerRequest {
|
||||
string plugin_id = 1;
|
||||
string container_id = 2;
|
||||
int64 host_id = 3;
|
||||
}
|
||||
|
||||
message GetContainerResponse {
|
||||
Container container = 1;
|
||||
bool found = 2;
|
||||
}
|
||||
|
||||
// GetHosts - Fetch all hosts
|
||||
message GetHostsRequest {
|
||||
string plugin_id = 1;
|
||||
}
|
||||
|
||||
message GetHostsResponse {
|
||||
repeated Host hosts = 1;
|
||||
}
|
||||
|
||||
// GetHost - Fetch specific host
|
||||
message GetHostRequest {
|
||||
string plugin_id = 1;
|
||||
int64 host_id = 2;
|
||||
}
|
||||
|
||||
message GetHostResponse {
|
||||
Host host = 1;
|
||||
bool found = 2;
|
||||
}
|
||||
|
||||
// GetPluginData - Fetch plugin-specific data from storage
|
||||
message GetPluginDataRequest {
|
||||
string plugin_id = 1;
|
||||
string key = 2;
|
||||
}
|
||||
|
||||
message GetPluginDataResponse {
|
||||
string value = 1;
|
||||
bool found = 2;
|
||||
}
|
||||
|
||||
// SetPluginData - Store plugin-specific data
|
||||
message SetPluginDataRequest {
|
||||
string plugin_id = 1;
|
||||
string key = 2;
|
||||
string value = 3;
|
||||
}
|
||||
|
||||
message SetPluginDataResponse {
|
||||
bool success = 1;
|
||||
}
|
||||
|
||||
// DeletePluginData - Delete plugin-specific data
|
||||
message DeletePluginDataRequest {
|
||||
string plugin_id = 1;
|
||||
string key = 2;
|
||||
}
|
||||
|
||||
message DeletePluginDataResponse {
|
||||
bool success = 1;
|
||||
}
|
||||
|
||||
// Log - Send log message to Census server
|
||||
message LogRequest {
|
||||
string plugin_id = 1;
|
||||
string level = 2; // "debug", "info", "warn", "error"
|
||||
string message = 3;
|
||||
map<string, string> fields = 4;
|
||||
}
|
||||
|
||||
message LogResponse {
|
||||
bool success = 1;
|
||||
}
|
||||
|
||||
// SendEvent - Send event to other plugins
|
||||
message SendEventRequest {
|
||||
string plugin_id = 1;
|
||||
string event_type = 2;
|
||||
map<string, string> data = 3;
|
||||
}
|
||||
|
||||
message SendEventResponse {
|
||||
bool success = 1;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// Data Models
|
||||
// ============================================================================
|
||||
|
||||
message Container {
|
||||
string id = 1;
|
||||
string name = 2;
|
||||
string image = 3;
|
||||
string image_id = 4;
|
||||
string state = 5;
|
||||
string status = 6;
|
||||
int64 host_id = 7;
|
||||
string host_name = 8;
|
||||
string created = 9;
|
||||
string started_at = 10;
|
||||
string finished_at = 11;
|
||||
repeated Port ports = 12;
|
||||
repeated string networks = 13;
|
||||
repeated Volume volumes = 14;
|
||||
repeated string links = 15;
|
||||
map<string, string> labels = 16;
|
||||
map<string, string> env = 17;
|
||||
string compose_project = 18;
|
||||
|
||||
// Resource stats
|
||||
double cpu_percent = 19;
|
||||
int64 memory_usage = 20;
|
||||
int64 memory_limit = 21;
|
||||
double memory_percent = 22;
|
||||
|
||||
// Plugin data
|
||||
map<string, string> plugin_data = 23;
|
||||
}
|
||||
|
||||
message Port {
|
||||
int32 container_port = 1;
|
||||
int32 host_port = 2;
|
||||
string protocol = 3;
|
||||
string host_ip = 4;
|
||||
}
|
||||
|
||||
message Volume {
|
||||
string type = 1; // "bind", "volume", "tmpfs"
|
||||
string name = 2; // Volume name (for named volumes)
|
||||
string source = 3; // Host path
|
||||
string destination = 4; // Container path
|
||||
string mode = 5; // "rw", "ro"
|
||||
}
|
||||
|
||||
message Host {
|
||||
int64 id = 1;
|
||||
string name = 2;
|
||||
string address = 3;
|
||||
string host_type = 4; // "unix", "agent", "tcp", "ssh"
|
||||
string description = 5;
|
||||
bool enabled = 6;
|
||||
bool collect_stats = 7;
|
||||
string last_seen = 8;
|
||||
string agent_version = 9;
|
||||
string agent_status = 10; // "online", "offline", "auth_failed"
|
||||
}
|
||||
@@ -1,957 +0,0 @@
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.6.0
|
||||
// - protoc v5.29.3
|
||||
// source: internal/plugins/proto/plugin.proto
|
||||
|
||||
package proto
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
// Requires gRPC-Go v1.64.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion9
|
||||
|
||||
const (
|
||||
Plugin_Init_FullMethodName = "/census.plugin.Plugin/Init"
|
||||
Plugin_Start_FullMethodName = "/census.plugin.Plugin/Start"
|
||||
Plugin_Stop_FullMethodName = "/census.plugin.Plugin/Stop"
|
||||
Plugin_Healthcheck_FullMethodName = "/census.plugin.Plugin/Healthcheck"
|
||||
Plugin_GetInfo_FullMethodName = "/census.plugin.Plugin/GetInfo"
|
||||
Plugin_GetTab_FullMethodName = "/census.plugin.Plugin/GetTab"
|
||||
Plugin_GetBadges_FullMethodName = "/census.plugin.Plugin/GetBadges"
|
||||
Plugin_HandleRoute_FullMethodName = "/census.plugin.Plugin/HandleRoute"
|
||||
Plugin_EnrichContainer_FullMethodName = "/census.plugin.Plugin/EnrichContainer"
|
||||
Plugin_HandleEvent_FullMethodName = "/census.plugin.Plugin/HandleEvent"
|
||||
Plugin_GetSettings_FullMethodName = "/census.plugin.Plugin/GetSettings"
|
||||
Plugin_UpdateSettings_FullMethodName = "/census.plugin.Plugin/UpdateSettings"
|
||||
)
|
||||
|
||||
// PluginClient is the client API for Plugin service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
//
|
||||
// Plugin service - implemented by external plugin processes
|
||||
type PluginClient interface {
|
||||
// Lifecycle methods
|
||||
Init(ctx context.Context, in *InitRequest, opts ...grpc.CallOption) (*InitResponse, error)
|
||||
Start(ctx context.Context, in *StartRequest, opts ...grpc.CallOption) (*StartResponse, error)
|
||||
Stop(ctx context.Context, in *StopRequest, opts ...grpc.CallOption) (*StopResponse, error)
|
||||
Healthcheck(ctx context.Context, in *HealthcheckRequest, opts ...grpc.CallOption) (*HealthcheckResponse, error)
|
||||
// Capability methods
|
||||
GetInfo(ctx context.Context, in *InfoRequest, opts ...grpc.CallOption) (*InfoResponse, error)
|
||||
GetTab(ctx context.Context, in *TabRequest, opts ...grpc.CallOption) (*TabResponse, error)
|
||||
GetBadges(ctx context.Context, in *BadgesRequest, opts ...grpc.CallOption) (*BadgesResponse, error)
|
||||
HandleRoute(ctx context.Context, in *RouteRequest, opts ...grpc.CallOption) (*RouteResponse, error)
|
||||
EnrichContainer(ctx context.Context, in *EnrichRequest, opts ...grpc.CallOption) (*EnrichResponse, error)
|
||||
HandleEvent(ctx context.Context, in *EventRequest, opts ...grpc.CallOption) (*EventResponse, error)
|
||||
GetSettings(ctx context.Context, in *SettingsRequest, opts ...grpc.CallOption) (*SettingsResponse, error)
|
||||
UpdateSettings(ctx context.Context, in *UpdateSettingsRequest, opts ...grpc.CallOption) (*UpdateSettingsResponse, error)
|
||||
}
|
||||
|
||||
type pluginClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewPluginClient(cc grpc.ClientConnInterface) PluginClient {
|
||||
return &pluginClient{cc}
|
||||
}
|
||||
|
||||
func (c *pluginClient) Init(ctx context.Context, in *InitRequest, opts ...grpc.CallOption) (*InitResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(InitResponse)
|
||||
err := c.cc.Invoke(ctx, Plugin_Init_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *pluginClient) Start(ctx context.Context, in *StartRequest, opts ...grpc.CallOption) (*StartResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(StartResponse)
|
||||
err := c.cc.Invoke(ctx, Plugin_Start_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *pluginClient) Stop(ctx context.Context, in *StopRequest, opts ...grpc.CallOption) (*StopResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(StopResponse)
|
||||
err := c.cc.Invoke(ctx, Plugin_Stop_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *pluginClient) Healthcheck(ctx context.Context, in *HealthcheckRequest, opts ...grpc.CallOption) (*HealthcheckResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(HealthcheckResponse)
|
||||
err := c.cc.Invoke(ctx, Plugin_Healthcheck_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *pluginClient) GetInfo(ctx context.Context, in *InfoRequest, opts ...grpc.CallOption) (*InfoResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(InfoResponse)
|
||||
err := c.cc.Invoke(ctx, Plugin_GetInfo_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *pluginClient) GetTab(ctx context.Context, in *TabRequest, opts ...grpc.CallOption) (*TabResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(TabResponse)
|
||||
err := c.cc.Invoke(ctx, Plugin_GetTab_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *pluginClient) GetBadges(ctx context.Context, in *BadgesRequest, opts ...grpc.CallOption) (*BadgesResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(BadgesResponse)
|
||||
err := c.cc.Invoke(ctx, Plugin_GetBadges_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *pluginClient) HandleRoute(ctx context.Context, in *RouteRequest, opts ...grpc.CallOption) (*RouteResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(RouteResponse)
|
||||
err := c.cc.Invoke(ctx, Plugin_HandleRoute_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *pluginClient) EnrichContainer(ctx context.Context, in *EnrichRequest, opts ...grpc.CallOption) (*EnrichResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(EnrichResponse)
|
||||
err := c.cc.Invoke(ctx, Plugin_EnrichContainer_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *pluginClient) HandleEvent(ctx context.Context, in *EventRequest, opts ...grpc.CallOption) (*EventResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(EventResponse)
|
||||
err := c.cc.Invoke(ctx, Plugin_HandleEvent_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *pluginClient) GetSettings(ctx context.Context, in *SettingsRequest, opts ...grpc.CallOption) (*SettingsResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(SettingsResponse)
|
||||
err := c.cc.Invoke(ctx, Plugin_GetSettings_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *pluginClient) UpdateSettings(ctx context.Context, in *UpdateSettingsRequest, opts ...grpc.CallOption) (*UpdateSettingsResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(UpdateSettingsResponse)
|
||||
err := c.cc.Invoke(ctx, Plugin_UpdateSettings_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// PluginServer is the server API for Plugin service.
|
||||
// All implementations must embed UnimplementedPluginServer
|
||||
// for forward compatibility.
|
||||
//
|
||||
// Plugin service - implemented by external plugin processes
|
||||
type PluginServer interface {
|
||||
// Lifecycle methods
|
||||
Init(context.Context, *InitRequest) (*InitResponse, error)
|
||||
Start(context.Context, *StartRequest) (*StartResponse, error)
|
||||
Stop(context.Context, *StopRequest) (*StopResponse, error)
|
||||
Healthcheck(context.Context, *HealthcheckRequest) (*HealthcheckResponse, error)
|
||||
// Capability methods
|
||||
GetInfo(context.Context, *InfoRequest) (*InfoResponse, error)
|
||||
GetTab(context.Context, *TabRequest) (*TabResponse, error)
|
||||
GetBadges(context.Context, *BadgesRequest) (*BadgesResponse, error)
|
||||
HandleRoute(context.Context, *RouteRequest) (*RouteResponse, error)
|
||||
EnrichContainer(context.Context, *EnrichRequest) (*EnrichResponse, error)
|
||||
HandleEvent(context.Context, *EventRequest) (*EventResponse, error)
|
||||
GetSettings(context.Context, *SettingsRequest) (*SettingsResponse, error)
|
||||
UpdateSettings(context.Context, *UpdateSettingsRequest) (*UpdateSettingsResponse, error)
|
||||
mustEmbedUnimplementedPluginServer()
|
||||
}
|
||||
|
||||
// UnimplementedPluginServer must be embedded to have
|
||||
// forward compatible implementations.
|
||||
//
|
||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||
// pointer dereference when methods are called.
|
||||
type UnimplementedPluginServer struct{}
|
||||
|
||||
func (UnimplementedPluginServer) Init(context.Context, *InitRequest) (*InitResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method Init not implemented")
|
||||
}
|
||||
func (UnimplementedPluginServer) Start(context.Context, *StartRequest) (*StartResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method Start not implemented")
|
||||
}
|
||||
func (UnimplementedPluginServer) Stop(context.Context, *StopRequest) (*StopResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method Stop not implemented")
|
||||
}
|
||||
func (UnimplementedPluginServer) Healthcheck(context.Context, *HealthcheckRequest) (*HealthcheckResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method Healthcheck not implemented")
|
||||
}
|
||||
func (UnimplementedPluginServer) GetInfo(context.Context, *InfoRequest) (*InfoResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetInfo not implemented")
|
||||
}
|
||||
func (UnimplementedPluginServer) GetTab(context.Context, *TabRequest) (*TabResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetTab not implemented")
|
||||
}
|
||||
func (UnimplementedPluginServer) GetBadges(context.Context, *BadgesRequest) (*BadgesResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetBadges not implemented")
|
||||
}
|
||||
func (UnimplementedPluginServer) HandleRoute(context.Context, *RouteRequest) (*RouteResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method HandleRoute not implemented")
|
||||
}
|
||||
func (UnimplementedPluginServer) EnrichContainer(context.Context, *EnrichRequest) (*EnrichResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method EnrichContainer not implemented")
|
||||
}
|
||||
func (UnimplementedPluginServer) HandleEvent(context.Context, *EventRequest) (*EventResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method HandleEvent not implemented")
|
||||
}
|
||||
func (UnimplementedPluginServer) GetSettings(context.Context, *SettingsRequest) (*SettingsResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetSettings not implemented")
|
||||
}
|
||||
func (UnimplementedPluginServer) UpdateSettings(context.Context, *UpdateSettingsRequest) (*UpdateSettingsResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method UpdateSettings not implemented")
|
||||
}
|
||||
func (UnimplementedPluginServer) mustEmbedUnimplementedPluginServer() {}
|
||||
func (UnimplementedPluginServer) testEmbeddedByValue() {}
|
||||
|
||||
// UnsafePluginServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to PluginServer will
|
||||
// result in compilation errors.
|
||||
type UnsafePluginServer interface {
|
||||
mustEmbedUnimplementedPluginServer()
|
||||
}
|
||||
|
||||
func RegisterPluginServer(s grpc.ServiceRegistrar, srv PluginServer) {
|
||||
// If the following call panics, it indicates UnimplementedPluginServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||
t.testEmbeddedByValue()
|
||||
}
|
||||
s.RegisterService(&Plugin_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _Plugin_Init_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(InitRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(PluginServer).Init(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Plugin_Init_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(PluginServer).Init(ctx, req.(*InitRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Plugin_Start_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(StartRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(PluginServer).Start(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Plugin_Start_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(PluginServer).Start(ctx, req.(*StartRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Plugin_Stop_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(StopRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(PluginServer).Stop(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Plugin_Stop_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(PluginServer).Stop(ctx, req.(*StopRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Plugin_Healthcheck_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(HealthcheckRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(PluginServer).Healthcheck(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Plugin_Healthcheck_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(PluginServer).Healthcheck(ctx, req.(*HealthcheckRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Plugin_GetInfo_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(InfoRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(PluginServer).GetInfo(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Plugin_GetInfo_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(PluginServer).GetInfo(ctx, req.(*InfoRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Plugin_GetTab_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(TabRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(PluginServer).GetTab(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Plugin_GetTab_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(PluginServer).GetTab(ctx, req.(*TabRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Plugin_GetBadges_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(BadgesRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(PluginServer).GetBadges(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Plugin_GetBadges_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(PluginServer).GetBadges(ctx, req.(*BadgesRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Plugin_HandleRoute_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(RouteRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(PluginServer).HandleRoute(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Plugin_HandleRoute_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(PluginServer).HandleRoute(ctx, req.(*RouteRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Plugin_EnrichContainer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(EnrichRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(PluginServer).EnrichContainer(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Plugin_EnrichContainer_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(PluginServer).EnrichContainer(ctx, req.(*EnrichRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Plugin_HandleEvent_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(EventRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(PluginServer).HandleEvent(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Plugin_HandleEvent_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(PluginServer).HandleEvent(ctx, req.(*EventRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Plugin_GetSettings_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SettingsRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(PluginServer).GetSettings(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Plugin_GetSettings_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(PluginServer).GetSettings(ctx, req.(*SettingsRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _Plugin_UpdateSettings_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(UpdateSettingsRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(PluginServer).UpdateSettings(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: Plugin_UpdateSettings_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(PluginServer).UpdateSettings(ctx, req.(*UpdateSettingsRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// Plugin_ServiceDesc is the grpc.ServiceDesc for Plugin service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var Plugin_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "census.plugin.Plugin",
|
||||
HandlerType: (*PluginServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Init",
|
||||
Handler: _Plugin_Init_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Start",
|
||||
Handler: _Plugin_Start_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Stop",
|
||||
Handler: _Plugin_Stop_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Healthcheck",
|
||||
Handler: _Plugin_Healthcheck_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetInfo",
|
||||
Handler: _Plugin_GetInfo_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetTab",
|
||||
Handler: _Plugin_GetTab_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetBadges",
|
||||
Handler: _Plugin_GetBadges_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "HandleRoute",
|
||||
Handler: _Plugin_HandleRoute_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "EnrichContainer",
|
||||
Handler: _Plugin_EnrichContainer_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "HandleEvent",
|
||||
Handler: _Plugin_HandleEvent_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetSettings",
|
||||
Handler: _Plugin_GetSettings_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "UpdateSettings",
|
||||
Handler: _Plugin_UpdateSettings_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "internal/plugins/proto/plugin.proto",
|
||||
}
|
||||
|
||||
const (
|
||||
CensusAPI_GetContainers_FullMethodName = "/census.plugin.CensusAPI/GetContainers"
|
||||
CensusAPI_GetContainer_FullMethodName = "/census.plugin.CensusAPI/GetContainer"
|
||||
CensusAPI_GetHosts_FullMethodName = "/census.plugin.CensusAPI/GetHosts"
|
||||
CensusAPI_GetHost_FullMethodName = "/census.plugin.CensusAPI/GetHost"
|
||||
CensusAPI_GetPluginData_FullMethodName = "/census.plugin.CensusAPI/GetPluginData"
|
||||
CensusAPI_SetPluginData_FullMethodName = "/census.plugin.CensusAPI/SetPluginData"
|
||||
CensusAPI_DeletePluginData_FullMethodName = "/census.plugin.CensusAPI/DeletePluginData"
|
||||
CensusAPI_Log_FullMethodName = "/census.plugin.CensusAPI/Log"
|
||||
CensusAPI_SendEvent_FullMethodName = "/census.plugin.CensusAPI/SendEvent"
|
||||
)
|
||||
|
||||
// CensusAPIClient is the client API for CensusAPI service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream.
|
||||
//
|
||||
// Census API service - implemented by main server (callback API for plugins)
|
||||
type CensusAPIClient interface {
|
||||
GetContainers(ctx context.Context, in *GetContainersRequest, opts ...grpc.CallOption) (*GetContainersResponse, error)
|
||||
GetContainer(ctx context.Context, in *GetContainerRequest, opts ...grpc.CallOption) (*GetContainerResponse, error)
|
||||
GetHosts(ctx context.Context, in *GetHostsRequest, opts ...grpc.CallOption) (*GetHostsResponse, error)
|
||||
GetHost(ctx context.Context, in *GetHostRequest, opts ...grpc.CallOption) (*GetHostResponse, error)
|
||||
GetPluginData(ctx context.Context, in *GetPluginDataRequest, opts ...grpc.CallOption) (*GetPluginDataResponse, error)
|
||||
SetPluginData(ctx context.Context, in *SetPluginDataRequest, opts ...grpc.CallOption) (*SetPluginDataResponse, error)
|
||||
DeletePluginData(ctx context.Context, in *DeletePluginDataRequest, opts ...grpc.CallOption) (*DeletePluginDataResponse, error)
|
||||
Log(ctx context.Context, in *LogRequest, opts ...grpc.CallOption) (*LogResponse, error)
|
||||
SendEvent(ctx context.Context, in *SendEventRequest, opts ...grpc.CallOption) (*SendEventResponse, error)
|
||||
}
|
||||
|
||||
type censusAPIClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewCensusAPIClient(cc grpc.ClientConnInterface) CensusAPIClient {
|
||||
return &censusAPIClient{cc}
|
||||
}
|
||||
|
||||
func (c *censusAPIClient) GetContainers(ctx context.Context, in *GetContainersRequest, opts ...grpc.CallOption) (*GetContainersResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(GetContainersResponse)
|
||||
err := c.cc.Invoke(ctx, CensusAPI_GetContainers_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *censusAPIClient) GetContainer(ctx context.Context, in *GetContainerRequest, opts ...grpc.CallOption) (*GetContainerResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(GetContainerResponse)
|
||||
err := c.cc.Invoke(ctx, CensusAPI_GetContainer_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *censusAPIClient) GetHosts(ctx context.Context, in *GetHostsRequest, opts ...grpc.CallOption) (*GetHostsResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(GetHostsResponse)
|
||||
err := c.cc.Invoke(ctx, CensusAPI_GetHosts_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *censusAPIClient) GetHost(ctx context.Context, in *GetHostRequest, opts ...grpc.CallOption) (*GetHostResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(GetHostResponse)
|
||||
err := c.cc.Invoke(ctx, CensusAPI_GetHost_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *censusAPIClient) GetPluginData(ctx context.Context, in *GetPluginDataRequest, opts ...grpc.CallOption) (*GetPluginDataResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(GetPluginDataResponse)
|
||||
err := c.cc.Invoke(ctx, CensusAPI_GetPluginData_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *censusAPIClient) SetPluginData(ctx context.Context, in *SetPluginDataRequest, opts ...grpc.CallOption) (*SetPluginDataResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(SetPluginDataResponse)
|
||||
err := c.cc.Invoke(ctx, CensusAPI_SetPluginData_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *censusAPIClient) DeletePluginData(ctx context.Context, in *DeletePluginDataRequest, opts ...grpc.CallOption) (*DeletePluginDataResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(DeletePluginDataResponse)
|
||||
err := c.cc.Invoke(ctx, CensusAPI_DeletePluginData_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *censusAPIClient) Log(ctx context.Context, in *LogRequest, opts ...grpc.CallOption) (*LogResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(LogResponse)
|
||||
err := c.cc.Invoke(ctx, CensusAPI_Log_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *censusAPIClient) SendEvent(ctx context.Context, in *SendEventRequest, opts ...grpc.CallOption) (*SendEventResponse, error) {
|
||||
cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...)
|
||||
out := new(SendEventResponse)
|
||||
err := c.cc.Invoke(ctx, CensusAPI_SendEvent_FullMethodName, in, out, cOpts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// CensusAPIServer is the server API for CensusAPI service.
|
||||
// All implementations must embed UnimplementedCensusAPIServer
|
||||
// for forward compatibility.
|
||||
//
|
||||
// Census API service - implemented by main server (callback API for plugins)
|
||||
type CensusAPIServer interface {
|
||||
GetContainers(context.Context, *GetContainersRequest) (*GetContainersResponse, error)
|
||||
GetContainer(context.Context, *GetContainerRequest) (*GetContainerResponse, error)
|
||||
GetHosts(context.Context, *GetHostsRequest) (*GetHostsResponse, error)
|
||||
GetHost(context.Context, *GetHostRequest) (*GetHostResponse, error)
|
||||
GetPluginData(context.Context, *GetPluginDataRequest) (*GetPluginDataResponse, error)
|
||||
SetPluginData(context.Context, *SetPluginDataRequest) (*SetPluginDataResponse, error)
|
||||
DeletePluginData(context.Context, *DeletePluginDataRequest) (*DeletePluginDataResponse, error)
|
||||
Log(context.Context, *LogRequest) (*LogResponse, error)
|
||||
SendEvent(context.Context, *SendEventRequest) (*SendEventResponse, error)
|
||||
mustEmbedUnimplementedCensusAPIServer()
|
||||
}
|
||||
|
||||
// UnimplementedCensusAPIServer must be embedded to have
|
||||
// forward compatible implementations.
|
||||
//
|
||||
// NOTE: this should be embedded by value instead of pointer to avoid a nil
|
||||
// pointer dereference when methods are called.
|
||||
type UnimplementedCensusAPIServer struct{}
|
||||
|
||||
func (UnimplementedCensusAPIServer) GetContainers(context.Context, *GetContainersRequest) (*GetContainersResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetContainers not implemented")
|
||||
}
|
||||
func (UnimplementedCensusAPIServer) GetContainer(context.Context, *GetContainerRequest) (*GetContainerResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetContainer not implemented")
|
||||
}
|
||||
func (UnimplementedCensusAPIServer) GetHosts(context.Context, *GetHostsRequest) (*GetHostsResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetHosts not implemented")
|
||||
}
|
||||
func (UnimplementedCensusAPIServer) GetHost(context.Context, *GetHostRequest) (*GetHostResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetHost not implemented")
|
||||
}
|
||||
func (UnimplementedCensusAPIServer) GetPluginData(context.Context, *GetPluginDataRequest) (*GetPluginDataResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method GetPluginData not implemented")
|
||||
}
|
||||
func (UnimplementedCensusAPIServer) SetPluginData(context.Context, *SetPluginDataRequest) (*SetPluginDataResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method SetPluginData not implemented")
|
||||
}
|
||||
func (UnimplementedCensusAPIServer) DeletePluginData(context.Context, *DeletePluginDataRequest) (*DeletePluginDataResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method DeletePluginData not implemented")
|
||||
}
|
||||
func (UnimplementedCensusAPIServer) Log(context.Context, *LogRequest) (*LogResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method Log not implemented")
|
||||
}
|
||||
func (UnimplementedCensusAPIServer) SendEvent(context.Context, *SendEventRequest) (*SendEventResponse, error) {
|
||||
return nil, status.Error(codes.Unimplemented, "method SendEvent not implemented")
|
||||
}
|
||||
func (UnimplementedCensusAPIServer) mustEmbedUnimplementedCensusAPIServer() {}
|
||||
func (UnimplementedCensusAPIServer) testEmbeddedByValue() {}
|
||||
|
||||
// UnsafeCensusAPIServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to CensusAPIServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeCensusAPIServer interface {
|
||||
mustEmbedUnimplementedCensusAPIServer()
|
||||
}
|
||||
|
||||
func RegisterCensusAPIServer(s grpc.ServiceRegistrar, srv CensusAPIServer) {
|
||||
// If the following call panics, it indicates UnimplementedCensusAPIServer was
|
||||
// embedded by pointer and is nil. This will cause panics if an
|
||||
// unimplemented method is ever invoked, so we test this at initialization
|
||||
// time to prevent it from happening at runtime later due to I/O.
|
||||
if t, ok := srv.(interface{ testEmbeddedByValue() }); ok {
|
||||
t.testEmbeddedByValue()
|
||||
}
|
||||
s.RegisterService(&CensusAPI_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _CensusAPI_GetContainers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetContainersRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(CensusAPIServer).GetContainers(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: CensusAPI_GetContainers_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(CensusAPIServer).GetContainers(ctx, req.(*GetContainersRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _CensusAPI_GetContainer_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetContainerRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(CensusAPIServer).GetContainer(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: CensusAPI_GetContainer_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(CensusAPIServer).GetContainer(ctx, req.(*GetContainerRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _CensusAPI_GetHosts_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetHostsRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(CensusAPIServer).GetHosts(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: CensusAPI_GetHosts_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(CensusAPIServer).GetHosts(ctx, req.(*GetHostsRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _CensusAPI_GetHost_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetHostRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(CensusAPIServer).GetHost(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: CensusAPI_GetHost_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(CensusAPIServer).GetHost(ctx, req.(*GetHostRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _CensusAPI_GetPluginData_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(GetPluginDataRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(CensusAPIServer).GetPluginData(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: CensusAPI_GetPluginData_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(CensusAPIServer).GetPluginData(ctx, req.(*GetPluginDataRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _CensusAPI_SetPluginData_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SetPluginDataRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(CensusAPIServer).SetPluginData(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: CensusAPI_SetPluginData_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(CensusAPIServer).SetPluginData(ctx, req.(*SetPluginDataRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _CensusAPI_DeletePluginData_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(DeletePluginDataRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(CensusAPIServer).DeletePluginData(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: CensusAPI_DeletePluginData_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(CensusAPIServer).DeletePluginData(ctx, req.(*DeletePluginDataRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _CensusAPI_Log_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(LogRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(CensusAPIServer).Log(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: CensusAPI_Log_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(CensusAPIServer).Log(ctx, req.(*LogRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
func _CensusAPI_SendEvent_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(SendEventRequest)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(CensusAPIServer).SendEvent(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: CensusAPI_SendEvent_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(CensusAPIServer).SendEvent(ctx, req.(*SendEventRequest))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// CensusAPI_ServiceDesc is the grpc.ServiceDesc for CensusAPI service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var CensusAPI_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "census.plugin.CensusAPI",
|
||||
HandlerType: (*CensusAPIServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "GetContainers",
|
||||
Handler: _CensusAPI_GetContainers_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetContainer",
|
||||
Handler: _CensusAPI_GetContainer_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetHosts",
|
||||
Handler: _CensusAPI_GetHosts_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetHost",
|
||||
Handler: _CensusAPI_GetHost_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "GetPluginData",
|
||||
Handler: _CensusAPI_GetPluginData_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "SetPluginData",
|
||||
Handler: _CensusAPI_SetPluginData_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "DeletePluginData",
|
||||
Handler: _CensusAPI_DeletePluginData_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "Log",
|
||||
Handler: _CensusAPI_Log_Handler,
|
||||
},
|
||||
{
|
||||
MethodName: "SendEvent",
|
||||
Handler: _CensusAPI_SendEvent_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "internal/plugins/proto/plugin.proto",
|
||||
}
|
||||
BIN
telemetry-collector
Executable file
BIN
telemetry-collector
Executable file
Binary file not shown.
Reference in New Issue
Block a user