Skip to content

Commit

Permalink
Rework standalone API (#703)
Browse files Browse the repository at this point in the history
* rework standalone API

* return err

* update comment

* address from env func

* more test coverage

* fix linter

* fix debug

* remove env var

* remove -debug

* add err log
  • Loading branch information
wbrowne authored Jun 16, 2023
1 parent e8cf32a commit d40c8ed
Show file tree
Hide file tree
Showing 4 changed files with 375 additions and 219 deletions.
88 changes: 38 additions & 50 deletions backend/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package backend

import (
"context"
"errors"
"fmt"
"net"
"os"
Expand Down Expand Up @@ -108,55 +109,48 @@ func Serve(opts ServeOpts) error {
return grpcplugin.Serve(pluginOpts)
}

// StandaloneServe starts a gRPC server that is not managed by hashicorp.
// Deprecated: use GracefulStandaloneServe instead.
func StandaloneServe(dsopts ServeOpts, address string) error {
// GracefulStandaloneServe has a new signature, this function keeps the old
// signature for existing plugins for backwards compatibility.
// Create a new standalone.Args and disable all the standalone-file-related features.
return GracefulStandaloneServe(dsopts, standalone.Args{Address: address})
}

// GracefulStandaloneServe starts a gRPC server that is not managed by hashicorp.
// The provided standalone.Args must have an Address set, or the function returns an error.
// The function handles creating/cleaning up the standalone address file, and graceful GRPC server termination.
// The function returns after the GRPC server has been terminated.
func GracefulStandaloneServe(dsopts ServeOpts, info standalone.Args) error {
func GracefulStandaloneServe(dsopts ServeOpts, info standalone.ServerSettings) error {
// We must have an address if we want to run the plugin in standalone mode
if info.Address == "" {
return fmt.Errorf("standalone address must be specified")
return errors.New("standalone address must be specified")
}

// Write the address to the local file
if info.Debugger {
log.DefaultLogger.Info("Creating standalone address and pid files")
if err := standalone.CreateStandaloneAddressFile(info); err != nil {
return fmt.Errorf("create standalone address file: %w", err)
if info.Dir == "" {
return errors.New("directory must be specified")
}

// Write the address and PID to local files
log.DefaultLogger.Info("Creating standalone address and pid files", "dir", info.Dir)
if err := standalone.CreateStandaloneAddressFile(info.Address, info.Dir); err != nil {
return fmt.Errorf("create standalone address file: %w", err)
}
if err := standalone.CreateStandalonePIDFile(os.Getpid(), info.Dir); err != nil {
return fmt.Errorf("create standalone pid file: %w", err)
}

// sadly vs-code can not listen to shutdown events
// https://github.com/golang/vscode-go/issues/120

// Cleanup function that deletes standalone.txt and pid.txt, if it exists. Fails silently.
// This is so the address file is deleted when the plugin shuts down gracefully, if possible.
defer func() {
log.DefaultLogger.Info("Cleaning up standalone address and pid files")
if err := standalone.CleanupStandaloneAddressFile(info); err != nil {
log.DefaultLogger.Error("Error while cleaning up standalone address file", "error", err)
}
if err := standalone.CreateStandalonePIDFile(info); err != nil {
return fmt.Errorf("create standalone pid file: %w", err)
if err := standalone.CleanupStandalonePIDFile(info); err != nil {
log.DefaultLogger.Error("Error while cleaning up standalone pid file", "error", err)
}

// sadly vs-code can not listen to shutdown events
// https://github.com/golang/vscode-go/issues/120

// Cleanup function that deletes standalone.txt and pid.txt, if it exists. Fails silently.
// This is so the address file is deleted when the plugin shuts down gracefully, if possible.
defer func() {
log.DefaultLogger.Info("Cleaning up standalone address and pid files")
if err := standalone.CleanupStandaloneAddressFile(info); err != nil {
log.DefaultLogger.Error("Error while cleaning up standalone address file", "error", err)
}
if err := standalone.CleanupStandalonePIDFile(info); err != nil {
log.DefaultLogger.Error("Error while cleaning up standalone pid file", "error", err)
}
// Kill the dummy locator so Grafana reloads the plugin
standalone.FindAndKillCurrentPlugin(info.Dir)
}()

// When debugging, be sure to kill the running instances, so we reconnect
// Kill the dummy locator so Grafana reloads the plugin
standalone.FindAndKillCurrentPlugin(info.Dir)
}
}()

// When debugging, be sure to kill the running instances, so that we can reconnect
standalone.FindAndKillCurrentPlugin(info.Dir)

// Start GRPC server
pluginOpts := asGRPCServeOpts(dsopts)
Expand Down Expand Up @@ -242,21 +236,15 @@ func Manage(pluginID string, serveOpts ServeOpts) error {
}
}()

info, err := standalone.GetInfo(pluginID)
if err != nil {
return err
}

if info.Standalone {
if s, enabled := standalone.ServerModeEnabled(pluginID); enabled {
// Run the standalone GRPC server
return GracefulStandaloneServe(serveOpts, info)
return GracefulStandaloneServe(serveOpts, s)
}

if info.Address != "" && standalone.CheckPIDIsRunning(info.PID) {
// Grafana is trying to run the dummy plugin locator to connect to the standalone
// GRPC server (separate process)
Logger.Debug("Running dummy plugin locator", "addr", info.Address, "pid", strconv.Itoa(info.PID))
standalone.RunDummyPluginLocator(info.Address)
if s, enabled := standalone.ClientModeEnabled(pluginID); enabled {
// Grafana is trying to run the dummy plugin locator to connect to the standalone GRPC server (separate process)
Logger.Debug("Running dummy plugin locator", "addr", s.TargetAddress, "pid", strconv.Itoa(s.TargetPID))
standalone.RunDummyPluginLocator(s.TargetAddress)
return nil
}

Expand Down
Loading

0 comments on commit d40c8ed

Please sign in to comment.