mirror of
https://github.com/opencloud-eu/opencloud.git
synced 2026-05-02 08:49:29 -05:00
Bump reva deps (#8412)
* bump dependencies Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> * bump reva and add config options Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de> --------- Signed-off-by: Jörn Friedrich Dreyer <jfd@butonic.de>
This commit is contained in:
committed by
GitHub
parent
c92ebf4b46
commit
5ed57cc09a
+1
-3
@@ -1,6 +1,4 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 HashiCorp
|
||||
Copyright (c) 2017 HashiCorp, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
+3
-2
@@ -140,9 +140,10 @@ log.Printf("[DEBUG] %d", 42)
|
||||
... [DEBUG] my-app: 42
|
||||
```
|
||||
|
||||
Notice that if `appLogger` is initialized with the `INFO` log level _and_ you
|
||||
Notice that if `appLogger` is initialized with the `INFO` log level, _and_ you
|
||||
specify `InferLevels: true`, you will not see any output here. You must change
|
||||
`appLogger` to `DEBUG` to see output. See the docs for more information.
|
||||
|
||||
If the log lines start with a timestamp you can use the
|
||||
`InferLevelsWithTimestamp` option to try and ignore them.
|
||||
`InferLevelsWithTimestamp` option to try and ignore them. Please note that in order
|
||||
for `InferLevelsWithTimestamp` to be relevant, `InferLevels` must be set to `true`.
|
||||
|
||||
+25
-10
@@ -1,3 +1,6 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
@@ -7,23 +10,35 @@ import (
|
||||
"github.com/mattn/go-isatty"
|
||||
)
|
||||
|
||||
// hasFD is used to check if the writer has an Fd value to check
|
||||
// if it's a terminal.
|
||||
type hasFD interface {
|
||||
Fd() uintptr
|
||||
}
|
||||
|
||||
// setColorization will mutate the values of this logger
|
||||
// to appropriately configure colorization options. It provides
|
||||
// a wrapper to the output stream on Windows systems.
|
||||
func (l *intLogger) setColorization(opts *LoggerOptions) {
|
||||
switch opts.Color {
|
||||
case ColorOff:
|
||||
fallthrough
|
||||
case ForceColor:
|
||||
if opts.Color != AutoColor {
|
||||
return
|
||||
case AutoColor:
|
||||
fi := l.checkWriterIsFile()
|
||||
isUnixTerm := isatty.IsTerminal(fi.Fd())
|
||||
isCygwinTerm := isatty.IsCygwinTerminal(fi.Fd())
|
||||
isTerm := isUnixTerm || isCygwinTerm
|
||||
if !isTerm {
|
||||
}
|
||||
|
||||
if sc, ok := l.writer.w.(SupportsColor); ok {
|
||||
if !sc.SupportsColor() {
|
||||
l.headerColor = ColorOff
|
||||
l.writer.color = ColorOff
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
fi, ok := l.writer.w.(hasFD)
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
if !isatty.IsTerminal(fi.Fd()) {
|
||||
l.headerColor = ColorOff
|
||||
l.writer.color = ColorOff
|
||||
}
|
||||
}
|
||||
|
||||
+22
-19
@@ -1,3 +1,6 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
@@ -7,32 +10,32 @@ import (
|
||||
"os"
|
||||
|
||||
colorable "github.com/mattn/go-colorable"
|
||||
"github.com/mattn/go-isatty"
|
||||
)
|
||||
|
||||
// setColorization will mutate the values of this logger
|
||||
// to appropriately configure colorization options. It provides
|
||||
// a wrapper to the output stream on Windows systems.
|
||||
func (l *intLogger) setColorization(opts *LoggerOptions) {
|
||||
switch opts.Color {
|
||||
case ColorOff:
|
||||
if opts.Color == ColorOff {
|
||||
return
|
||||
case ForceColor:
|
||||
fi := l.checkWriterIsFile()
|
||||
l.writer.w = colorable.NewColorable(fi)
|
||||
case AutoColor:
|
||||
fi := l.checkWriterIsFile()
|
||||
isUnixTerm := isatty.IsTerminal(os.Stdout.Fd())
|
||||
isCygwinTerm := isatty.IsCygwinTerminal(os.Stdout.Fd())
|
||||
isTerm := isUnixTerm || isCygwinTerm
|
||||
if !isTerm {
|
||||
l.writer.color = ColorOff
|
||||
l.headerColor = ColorOff
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if l.headerColor == ColorOff {
|
||||
l.writer.w = colorable.NewColorable(fi)
|
||||
}
|
||||
fi, ok := l.writer.w.(*os.File)
|
||||
if !ok {
|
||||
l.writer.color = ColorOff
|
||||
l.headerColor = ColorOff
|
||||
return
|
||||
}
|
||||
|
||||
cfi := colorable.NewColorable(fi)
|
||||
|
||||
// NewColorable detects if color is possible and if it's not, then it
|
||||
// returns the original value. So we can test if we got the original
|
||||
// value back to know if color is possible.
|
||||
if cfi == fi {
|
||||
l.writer.color = ColorOff
|
||||
l.headerColor = ColorOff
|
||||
} else {
|
||||
l.writer.w = cfi
|
||||
}
|
||||
}
|
||||
|
||||
+3
@@ -1,3 +1,6 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package hclog
|
||||
|
||||
import (
|
||||
|
||||
+3
@@ -1,3 +1,6 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package hclog
|
||||
|
||||
import (
|
||||
|
||||
+3
@@ -1,3 +1,6 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package hclog
|
||||
|
||||
import (
|
||||
|
||||
+3
@@ -1,3 +1,6 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package hclog
|
||||
|
||||
import (
|
||||
|
||||
+119
-29
@@ -1,3 +1,6 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package hclog
|
||||
|
||||
import (
|
||||
@@ -8,7 +11,6 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"sort"
|
||||
@@ -53,11 +55,25 @@ var (
|
||||
|
||||
faintBoldColor = color.New(color.Faint, color.Bold)
|
||||
faintColor = color.New(color.Faint)
|
||||
faintMultiLinePrefix = faintColor.Sprint(" | ")
|
||||
faintFieldSeparator = faintColor.Sprint("=")
|
||||
faintFieldSeparatorWithNewLine = faintColor.Sprint("=\n")
|
||||
faintMultiLinePrefix string
|
||||
faintFieldSeparator string
|
||||
faintFieldSeparatorWithNewLine string
|
||||
)
|
||||
|
||||
func init() {
|
||||
// Force all the colors to enabled because we do our own detection of color usage.
|
||||
for _, c := range _levelToColor {
|
||||
c.EnableColor()
|
||||
}
|
||||
|
||||
faintBoldColor.EnableColor()
|
||||
faintColor.EnableColor()
|
||||
|
||||
faintMultiLinePrefix = faintColor.Sprint(" | ")
|
||||
faintFieldSeparator = faintColor.Sprint("=")
|
||||
faintFieldSeparatorWithNewLine = faintColor.Sprint("=\n")
|
||||
}
|
||||
|
||||
// Make sure that intLogger is a Logger
|
||||
var _ Logger = &intLogger{}
|
||||
|
||||
@@ -77,6 +93,19 @@ type intLogger struct {
|
||||
writer *writer
|
||||
level *int32
|
||||
|
||||
// The value of curEpoch when our level was set
|
||||
setEpoch uint64
|
||||
|
||||
// The value of curEpoch the last time we performed the level sync process
|
||||
ownEpoch uint64
|
||||
|
||||
// Shared amongst all the loggers created in this hierachy, used to determine
|
||||
// if the level sync process should be run by comparing it with ownEpoch
|
||||
curEpoch *uint64
|
||||
|
||||
// The logger this one was created from. Only set when syncParentLevel is set
|
||||
parent *intLogger
|
||||
|
||||
headerColor ColorOption
|
||||
fieldColor ColorOption
|
||||
|
||||
@@ -86,6 +115,9 @@ type intLogger struct {
|
||||
|
||||
// create subloggers with their own level setting
|
||||
independentLevels bool
|
||||
syncParentLevel bool
|
||||
|
||||
subloggerHook func(sub Logger) Logger
|
||||
}
|
||||
|
||||
// New returns a configured logger.
|
||||
@@ -125,9 +157,9 @@ func newLogger(opts *LoggerOptions) *intLogger {
|
||||
}
|
||||
|
||||
var (
|
||||
primaryColor ColorOption = ColorOff
|
||||
headerColor ColorOption = ColorOff
|
||||
fieldColor ColorOption = ColorOff
|
||||
primaryColor = ColorOff
|
||||
headerColor = ColorOff
|
||||
fieldColor = ColorOff
|
||||
)
|
||||
switch {
|
||||
case opts.ColorHeaderOnly:
|
||||
@@ -148,10 +180,13 @@ func newLogger(opts *LoggerOptions) *intLogger {
|
||||
mutex: mutex,
|
||||
writer: newWriter(output, primaryColor),
|
||||
level: new(int32),
|
||||
curEpoch: new(uint64),
|
||||
exclude: opts.Exclude,
|
||||
independentLevels: opts.IndependentLevels,
|
||||
syncParentLevel: opts.SyncParentLevel,
|
||||
headerColor: headerColor,
|
||||
fieldColor: fieldColor,
|
||||
subloggerHook: opts.SubloggerHook,
|
||||
}
|
||||
if opts.IncludeLocation {
|
||||
l.callerOffset = offsetIntLogger + opts.AdditionalLocationOffset
|
||||
@@ -167,6 +202,10 @@ func newLogger(opts *LoggerOptions) *intLogger {
|
||||
l.timeFormat = opts.TimeFormat
|
||||
}
|
||||
|
||||
if l.subloggerHook == nil {
|
||||
l.subloggerHook = identityHook
|
||||
}
|
||||
|
||||
l.setColorization(opts)
|
||||
|
||||
atomic.StoreInt32(l.level, int32(level))
|
||||
@@ -174,6 +213,10 @@ func newLogger(opts *LoggerOptions) *intLogger {
|
||||
return l
|
||||
}
|
||||
|
||||
func identityHook(logger Logger) Logger {
|
||||
return logger
|
||||
}
|
||||
|
||||
// offsetIntLogger is the stack frame offset in the call stack for the caller to
|
||||
// one of the Warn, Info, Log, etc methods.
|
||||
const offsetIntLogger = 3
|
||||
@@ -181,7 +224,7 @@ const offsetIntLogger = 3
|
||||
// Log a message and a set of key/value pairs if the given level is at
|
||||
// or more severe that the threshold configured in the Logger.
|
||||
func (l *intLogger) log(name string, level Level, msg string, args ...interface{}) {
|
||||
if level < Level(atomic.LoadInt32(l.level)) {
|
||||
if level < l.GetLevel() {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -261,7 +304,6 @@ func needsQuoting(str string) bool {
|
||||
// 2. Color the whole log line, based on the level.
|
||||
// 3. Color only the header (level) part of the log line.
|
||||
// 4. Color both the header and fields of the log line.
|
||||
//
|
||||
func (l *intLogger) logPlain(t time.Time, name string, level Level, msg string, args ...interface{}) {
|
||||
|
||||
if !l.disableTime {
|
||||
@@ -585,7 +627,7 @@ func (l *intLogger) logJSON(t time.Time, name string, level Level, msg string, a
|
||||
vals := l.jsonMapEntry(t, name, level, msg)
|
||||
args = append(l.implied, args...)
|
||||
|
||||
if args != nil && len(args) > 0 {
|
||||
if len(args) > 0 {
|
||||
if len(args)%2 != 0 {
|
||||
cs, ok := args[len(args)-1].(CapturedStacktrace)
|
||||
if ok {
|
||||
@@ -706,27 +748,27 @@ func (l *intLogger) Error(msg string, args ...interface{}) {
|
||||
|
||||
// Indicate that the logger would emit TRACE level logs
|
||||
func (l *intLogger) IsTrace() bool {
|
||||
return Level(atomic.LoadInt32(l.level)) == Trace
|
||||
return l.GetLevel() == Trace
|
||||
}
|
||||
|
||||
// Indicate that the logger would emit DEBUG level logs
|
||||
func (l *intLogger) IsDebug() bool {
|
||||
return Level(atomic.LoadInt32(l.level)) <= Debug
|
||||
return l.GetLevel() <= Debug
|
||||
}
|
||||
|
||||
// Indicate that the logger would emit INFO level logs
|
||||
func (l *intLogger) IsInfo() bool {
|
||||
return Level(atomic.LoadInt32(l.level)) <= Info
|
||||
return l.GetLevel() <= Info
|
||||
}
|
||||
|
||||
// Indicate that the logger would emit WARN level logs
|
||||
func (l *intLogger) IsWarn() bool {
|
||||
return Level(atomic.LoadInt32(l.level)) <= Warn
|
||||
return l.GetLevel() <= Warn
|
||||
}
|
||||
|
||||
// Indicate that the logger would emit ERROR level logs
|
||||
func (l *intLogger) IsError() bool {
|
||||
return Level(atomic.LoadInt32(l.level)) <= Error
|
||||
return l.GetLevel() <= Error
|
||||
}
|
||||
|
||||
const MissingKey = "EXTRA_VALUE_AT_END"
|
||||
@@ -776,7 +818,7 @@ func (l *intLogger) With(args ...interface{}) Logger {
|
||||
sl.implied = append(sl.implied, MissingKey, extra)
|
||||
}
|
||||
|
||||
return sl
|
||||
return l.subloggerHook(sl)
|
||||
}
|
||||
|
||||
// Create a new sub-Logger that a name decending from the current name.
|
||||
@@ -790,7 +832,7 @@ func (l *intLogger) Named(name string) Logger {
|
||||
sl.name = name
|
||||
}
|
||||
|
||||
return sl
|
||||
return l.subloggerHook(sl)
|
||||
}
|
||||
|
||||
// Create a new sub-Logger with an explicit name. This ignores the current
|
||||
@@ -801,7 +843,7 @@ func (l *intLogger) ResetNamed(name string) Logger {
|
||||
|
||||
sl.name = name
|
||||
|
||||
return sl
|
||||
return l.subloggerHook(sl)
|
||||
}
|
||||
|
||||
func (l *intLogger) ResetOutput(opts *LoggerOptions) error {
|
||||
@@ -842,7 +884,63 @@ func (l *intLogger) resetOutput(opts *LoggerOptions) error {
|
||||
// Update the logging level on-the-fly. This will affect all subloggers as
|
||||
// well.
|
||||
func (l *intLogger) SetLevel(level Level) {
|
||||
atomic.StoreInt32(l.level, int32(level))
|
||||
if !l.syncParentLevel {
|
||||
atomic.StoreInt32(l.level, int32(level))
|
||||
return
|
||||
}
|
||||
|
||||
nsl := new(int32)
|
||||
*nsl = int32(level)
|
||||
|
||||
l.level = nsl
|
||||
|
||||
l.ownEpoch = atomic.AddUint64(l.curEpoch, 1)
|
||||
l.setEpoch = l.ownEpoch
|
||||
}
|
||||
|
||||
func (l *intLogger) searchLevelPtr() *int32 {
|
||||
p := l.parent
|
||||
|
||||
ptr := l.level
|
||||
|
||||
max := l.setEpoch
|
||||
|
||||
for p != nil {
|
||||
if p.setEpoch > max {
|
||||
max = p.setEpoch
|
||||
ptr = p.level
|
||||
}
|
||||
|
||||
p = p.parent
|
||||
}
|
||||
|
||||
return ptr
|
||||
}
|
||||
|
||||
// Returns the current level
|
||||
func (l *intLogger) GetLevel() Level {
|
||||
// We perform the loads immediately to keep the CPU pipeline busy, which
|
||||
// effectively makes the second load cost nothing. Once loaded into registers
|
||||
// the comparison returns the already loaded value. The comparison is almost
|
||||
// always true, so the branch predictor should hit consistently with it.
|
||||
var (
|
||||
curEpoch = atomic.LoadUint64(l.curEpoch)
|
||||
level = Level(atomic.LoadInt32(l.level))
|
||||
own = l.ownEpoch
|
||||
)
|
||||
|
||||
if curEpoch == own {
|
||||
return level
|
||||
}
|
||||
|
||||
// Perform the level sync process. We'll avoid doing this next time by seeing the
|
||||
// epoch as current.
|
||||
|
||||
ptr := l.searchLevelPtr()
|
||||
l.level = ptr
|
||||
l.ownEpoch = curEpoch
|
||||
|
||||
return Level(atomic.LoadInt32(ptr))
|
||||
}
|
||||
|
||||
// Create a *log.Logger that will send it's data through this Logger. This
|
||||
@@ -872,16 +970,6 @@ func (l *intLogger) StandardWriter(opts *StandardLoggerOptions) io.Writer {
|
||||
}
|
||||
}
|
||||
|
||||
// checks if the underlying io.Writer is a file, and
|
||||
// panics if not. For use by colorization.
|
||||
func (l *intLogger) checkWriterIsFile() *os.File {
|
||||
fi, ok := l.writer.w.(*os.File)
|
||||
if !ok {
|
||||
panic("Cannot enable coloring of non-file Writers")
|
||||
}
|
||||
return fi
|
||||
}
|
||||
|
||||
// Accept implements the SinkAdapter interface
|
||||
func (i *intLogger) Accept(name string, level Level, msg string, args ...interface{}) {
|
||||
i.log(name, level, msg, args...)
|
||||
@@ -905,6 +993,8 @@ func (l *intLogger) copy() *intLogger {
|
||||
if l.independentLevels {
|
||||
sl.level = new(int32)
|
||||
*sl.level = *l.level
|
||||
} else if l.syncParentLevel {
|
||||
sl.parent = l
|
||||
}
|
||||
|
||||
return &sl
|
||||
|
||||
+39
@@ -1,3 +1,6 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package hclog
|
||||
|
||||
import (
|
||||
@@ -89,6 +92,13 @@ const (
|
||||
ForceColor
|
||||
)
|
||||
|
||||
// SupportsColor is an optional interface that can be implemented by the output
|
||||
// value. If implemented and SupportsColor() returns true, then AutoColor will
|
||||
// enable colorization.
|
||||
type SupportsColor interface {
|
||||
SupportsColor() bool
|
||||
}
|
||||
|
||||
// LevelFromString returns a Level type for the named log level, or "NoLevel" if
|
||||
// the level string is invalid. This facilitates setting the log level via
|
||||
// config or environment variable by name in a predictable way.
|
||||
@@ -198,6 +208,9 @@ type Logger interface {
|
||||
// implementation cannot update the level on the fly, it should no-op.
|
||||
SetLevel(level Level)
|
||||
|
||||
// Returns the current level
|
||||
GetLevel() Level
|
||||
|
||||
// Return a value that conforms to the stdlib log.Logger interface
|
||||
StandardLogger(opts *StandardLoggerOptions) *log.Logger
|
||||
|
||||
@@ -220,6 +233,7 @@ type StandardLoggerOptions struct {
|
||||
// [DEBUG] and strip it off before reapplying it.
|
||||
// The timestamp detection may result in false positives and incomplete
|
||||
// string outputs.
|
||||
// InferLevelsWithTimestamp is only relevant if InferLevels is true.
|
||||
InferLevelsWithTimestamp bool
|
||||
|
||||
// ForceLevel is used to force all output from the standard logger to be at
|
||||
@@ -289,6 +303,31 @@ type LoggerOptions struct {
|
||||
// logger will not affect any subloggers, and SetLevel on any subloggers
|
||||
// will not affect the parent or sibling loggers.
|
||||
IndependentLevels bool
|
||||
|
||||
// When set, changing the level of a logger effects only it's direct sub-loggers
|
||||
// rather than all sub-loggers. For example:
|
||||
// a := logger.Named("a")
|
||||
// a.SetLevel(Error)
|
||||
// b := a.Named("b")
|
||||
// c := a.Named("c")
|
||||
// b.GetLevel() => Error
|
||||
// c.GetLevel() => Error
|
||||
// b.SetLevel(Info)
|
||||
// a.GetLevel() => Error
|
||||
// b.GetLevel() => Info
|
||||
// c.GetLevel() => Error
|
||||
// a.SetLevel(Warn)
|
||||
// a.GetLevel() => Warn
|
||||
// b.GetLevel() => Warn
|
||||
// c.GetLevel() => Warn
|
||||
SyncParentLevel bool
|
||||
|
||||
// SubloggerHook registers a function that is called when a sublogger via
|
||||
// Named, With, or ResetNamed is created. If defined, the function is passed
|
||||
// the newly created Logger and the returned Logger is returned from the
|
||||
// original function. This option allows customization via interception and
|
||||
// wrapping of Logger instances.
|
||||
SubloggerHook func(sub Logger) Logger
|
||||
}
|
||||
|
||||
// InterceptLogger describes the interface for using a logger
|
||||
|
||||
+5
@@ -1,3 +1,6 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package hclog
|
||||
|
||||
import (
|
||||
@@ -49,6 +52,8 @@ func (l *nullLogger) ResetNamed(name string) Logger { return l }
|
||||
|
||||
func (l *nullLogger) SetLevel(level Level) {}
|
||||
|
||||
func (l *nullLogger) GetLevel() Level { return NoLevel }
|
||||
|
||||
func (l *nullLogger) StandardLogger(opts *StandardLoggerOptions) *log.Logger {
|
||||
return log.New(l.StandardWriter(opts), "", log.LstdFlags)
|
||||
}
|
||||
|
||||
+3
@@ -1,3 +1,6 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package hclog
|
||||
|
||||
import (
|
||||
|
||||
+3
@@ -1,3 +1,6 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
package hclog
|
||||
|
||||
import (
|
||||
|
||||
+85
-2
@@ -1,3 +1,88 @@
|
||||
## v1.6.0
|
||||
|
||||
CHANGES:
|
||||
|
||||
* plugin: Plugins written in other languages can optionally start to advertise whether they support gRPC broker multiplexing.
|
||||
If the environment variable `PLUGIN_MULTIPLEX_GRPC` is set, it is safe to include a seventh field containing a boolean
|
||||
value in the `|`-separated protocol negotiation line.
|
||||
|
||||
ENHANCEMENTS:
|
||||
|
||||
* Support muxing gRPC broker connections over a single listener [[GH-288](https://github.com/hashicorp/go-plugin/pull/288)]
|
||||
* client: Configurable buffer size for reading plugin log lines [[GH-265](https://github.com/hashicorp/go-plugin/pull/265)]
|
||||
* Use `buf` for proto generation [[GH-286](https://github.com/hashicorp/go-plugin/pull/286)]
|
||||
* deps: bump golang.org/x/net to v0.17.0 [[GH-285](https://github.com/hashicorp/go-plugin/pull/285)]
|
||||
* deps: bump golang.org/x/sys to v0.13.0 [[GH-285](https://github.com/hashicorp/go-plugin/pull/285)]
|
||||
* deps: bump golang.org/x/text to v0.13.0 [[GH-285](https://github.com/hashicorp/go-plugin/pull/285)]
|
||||
|
||||
## v1.5.2
|
||||
|
||||
ENHANCEMENTS:
|
||||
|
||||
client: New `UnixSocketConfig.TempDir` option allows setting the directory to use when creating plugin-specific Unix socket directories [[GH-282](https://github.com/hashicorp/go-plugin/pull/282)]
|
||||
|
||||
## v1.5.1
|
||||
|
||||
BUGS:
|
||||
|
||||
* server: `PLUGIN_UNIX_SOCKET_DIR` is consistently used for gRPC broker sockets as well as the initial socket [[GH-277](https://github.com/hashicorp/go-plugin/pull/277)]
|
||||
|
||||
ENHANCEMENTS:
|
||||
|
||||
* client: New `UnixSocketConfig` option in `ClientConfig` to support making the client's Unix sockets group-writable [[GH-277](https://github.com/hashicorp/go-plugin/pull/277)]
|
||||
|
||||
## v1.5.0
|
||||
|
||||
ENHANCEMENTS:
|
||||
|
||||
* client: New `runner.Runner` interface to support clients providing custom plugin command runner implementations [[GH-270](https://github.com/hashicorp/go-plugin/pull/270)]
|
||||
* Accessible via new `ClientConfig` field `RunnerFunc`, which is mutually exclusive with `Cmd` and `Reattach`
|
||||
* Reattaching support via `ReattachConfig` field `ReattachFunc`
|
||||
* client: New `ClientConfig` field `SkipHostEnv` allows omitting the client process' own environment variables from the plugin command's environment [[GH-270](https://github.com/hashicorp/go-plugin/pull/270)]
|
||||
* client: Add `ID()` method to `Client` for retrieving the pid or other unique ID of a running plugin [[GH-272](https://github.com/hashicorp/go-plugin/pull/272)]
|
||||
* server: Support setting the directory to create Unix sockets in with the env var `PLUGIN_UNIX_SOCKET_DIR` [[GH-270](https://github.com/hashicorp/go-plugin/pull/270)]
|
||||
* server: Support setting group write permission and a custom group name or gid owner with the env var `PLUGIN_UNIX_SOCKET_GROUP` [[GH-270](https://github.com/hashicorp/go-plugin/pull/270)]
|
||||
|
||||
## v1.4.11-rc1
|
||||
|
||||
ENHANCEMENTS:
|
||||
|
||||
* deps: bump protoreflect to v1.15.1 [[GH-264](https://github.com/hashicorp/go-plugin/pull/264)]
|
||||
|
||||
## v1.4.10
|
||||
|
||||
BUG FIXES:
|
||||
|
||||
* additional notes: ensure to close files [[GH-241](https://github.com/hashicorp/go-plugin/pull/241)]
|
||||
|
||||
ENHANCEMENTS:
|
||||
|
||||
* deps: Remove direct dependency on golang.org/x/net [[GH-240](https://github.com/hashicorp/go-plugin/pull/240)]
|
||||
|
||||
## v1.4.9
|
||||
|
||||
ENHANCEMENTS:
|
||||
|
||||
* client: Remove log warning introduced in 1.4.5 when SecureConfig is nil. [[GH-238](https://github.com/hashicorp/go-plugin/pull/238)]
|
||||
|
||||
## v1.4.8
|
||||
|
||||
BUG FIXES:
|
||||
|
||||
* Fix windows build: [[GH-227](https://github.com/hashicorp/go-plugin/pull/227)]
|
||||
|
||||
## v1.4.7
|
||||
|
||||
ENHANCEMENTS:
|
||||
|
||||
* More detailed error message on plugin start failure: [[GH-223](https://github.com/hashicorp/go-plugin/pull/223)]
|
||||
|
||||
## v1.4.6
|
||||
|
||||
BUG FIXES:
|
||||
|
||||
* server: Prevent gRPC broker goroutine leak when using `GRPCServer` type `GracefulStop()` or `Stop()` methods [[GH-220](https://github.com/hashicorp/go-plugin/pull/220)]
|
||||
|
||||
## v1.4.5
|
||||
|
||||
ENHANCEMENTS:
|
||||
@@ -15,5 +100,3 @@ BUG FIXES:
|
||||
|
||||
* Bidirectional communication: fix bidirectional communication when AutoMTLS is enabled [[GH-193](https://github.com/hashicorp/go-plugin/pull/193)]
|
||||
* RPC: Trim a spurious log message for plugins using RPC [[GH-186](https://github.com/hashicorp/go-plugin/pull/186)]
|
||||
|
||||
|
||||
|
||||
+2
@@ -1,3 +1,5 @@
|
||||
Copyright (c) 2016 HashiCorp, Inc.
|
||||
|
||||
Mozilla Public License, version 2.0
|
||||
|
||||
1. Definitions
|
||||
|
||||
+3
-2
@@ -4,8 +4,9 @@
|
||||
that has been in use by HashiCorp tooling for over 4 years. While initially
|
||||
created for [Packer](https://www.packer.io), it is additionally in use by
|
||||
[Terraform](https://www.terraform.io), [Nomad](https://www.nomadproject.io),
|
||||
[Vault](https://www.vaultproject.io), and
|
||||
[Boundary](https://www.boundaryproject.io).
|
||||
[Vault](https://www.vaultproject.io),
|
||||
[Boundary](https://www.boundaryproject.io),
|
||||
and [Waypoint](https://www.waypointproject.io).
|
||||
|
||||
While the plugin system is over RPC, it is currently only designed to work
|
||||
over a local [reliable] network. Plugins over a real network are not supported
|
||||
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
# Copyright (c) HashiCorp, Inc.
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
version: v1
|
||||
plugins:
|
||||
- plugin: buf.build/protocolbuffers/go
|
||||
out: .
|
||||
opt:
|
||||
- paths=source_relative
|
||||
- plugin: buf.build/grpc/go:v1.3.0
|
||||
out: .
|
||||
opt:
|
||||
- paths=source_relative
|
||||
- require_unimplemented_servers=false
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
# Copyright (c) HashiCorp, Inc.
|
||||
# SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
version: v1
|
||||
build:
|
||||
excludes:
|
||||
- examples/
|
||||
+287
-103
@@ -1,3 +1,6 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package plugin
|
||||
|
||||
import (
|
||||
@@ -23,6 +26,9 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-hclog"
|
||||
"github.com/hashicorp/go-plugin/internal/cmdrunner"
|
||||
"github.com/hashicorp/go-plugin/internal/grpcmux"
|
||||
"github.com/hashicorp/go-plugin/runner"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
@@ -41,7 +47,7 @@ var managedClientsLock sync.Mutex
|
||||
var (
|
||||
// ErrProcessNotFound is returned when a client is instantiated to
|
||||
// reattach to an existing process and it isn't found.
|
||||
ErrProcessNotFound = errors.New("Reattachment process not found")
|
||||
ErrProcessNotFound = cmdrunner.ErrProcessNotFound
|
||||
|
||||
// ErrChecksumsDoNotMatch is returned when binary's checksum doesn't match
|
||||
// the one provided in the SecureConfig.
|
||||
@@ -58,8 +64,18 @@ var (
|
||||
// ErrSecureConfigAndReattach is returned when both Reattach and
|
||||
// SecureConfig are set.
|
||||
ErrSecureConfigAndReattach = errors.New("only one of Reattach or SecureConfig can be set")
|
||||
|
||||
// ErrGRPCBrokerMuxNotSupported is returned when the client requests
|
||||
// multiplexing over the gRPC broker, but the plugin does not support the
|
||||
// feature. In most cases, this should be resolvable by updating and
|
||||
// rebuilding the plugin, or restarting the plugin with
|
||||
// ClientConfig.GRPCBrokerMultiplex set to false.
|
||||
ErrGRPCBrokerMuxNotSupported = errors.New("client requested gRPC broker multiplexing but plugin does not support the feature")
|
||||
)
|
||||
|
||||
// defaultPluginLogBufferSize is the default size of the buffer used to read from stderr for plugin log lines.
|
||||
const defaultPluginLogBufferSize = 64 * 1024
|
||||
|
||||
// Client handles the lifecycle of a plugin application. It launches
|
||||
// plugins, connects to them, dispenses interface implementations, and handles
|
||||
// killing the process.
|
||||
@@ -76,7 +92,7 @@ type Client struct {
|
||||
exited bool
|
||||
l sync.Mutex
|
||||
address net.Addr
|
||||
process *os.Process
|
||||
runner runner.AttachedRunner
|
||||
client ClientProtocol
|
||||
protocol Protocol
|
||||
logger hclog.Logger
|
||||
@@ -95,6 +111,11 @@ type Client struct {
|
||||
// processKilled is used for testing only, to flag when the process was
|
||||
// forcefully killed.
|
||||
processKilled bool
|
||||
|
||||
unixSocketCfg UnixSocketConfig
|
||||
|
||||
grpcMuxerOnce sync.Once
|
||||
grpcMuxer *grpcmux.GRPCClientMuxer
|
||||
}
|
||||
|
||||
// NegotiatedVersion returns the protocol version negotiated with the server.
|
||||
@@ -103,6 +124,19 @@ func (c *Client) NegotiatedVersion() int {
|
||||
return c.negotiatedVersion
|
||||
}
|
||||
|
||||
// ID returns a unique ID for the running plugin. By default this is the process
|
||||
// ID (pid), but it could take other forms if RunnerFunc was provided.
|
||||
func (c *Client) ID() string {
|
||||
c.l.Lock()
|
||||
defer c.l.Unlock()
|
||||
|
||||
if c.runner != nil {
|
||||
return c.runner.ID()
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// ClientConfig is the configuration used to initialize a new
|
||||
// plugin client. After being used to initialize a plugin client,
|
||||
// that configuration must not be modified again.
|
||||
@@ -130,6 +164,13 @@ type ClientConfig struct {
|
||||
Cmd *exec.Cmd
|
||||
Reattach *ReattachConfig
|
||||
|
||||
// RunnerFunc allows consumers to provide their own implementation of
|
||||
// runner.Runner and control the context within which a plugin is executed.
|
||||
// The cmd argument will have been copied from the config and populated with
|
||||
// environment variables that a go-plugin server expects to read such as
|
||||
// AutoMTLS certs and the magic cookie key.
|
||||
RunnerFunc func(l hclog.Logger, cmd *exec.Cmd, tmpDir string) (runner.Runner, error)
|
||||
|
||||
// SecureConfig is configuration for verifying the integrity of the
|
||||
// executable. It can not be used with Reattach.
|
||||
SecureConfig *SecureConfig
|
||||
@@ -182,6 +223,10 @@ type ClientConfig struct {
|
||||
// it will default to hclog's default logger.
|
||||
Logger hclog.Logger
|
||||
|
||||
// PluginLogBufferSize is the buffer size(bytes) to read from stderr for plugin log lines.
|
||||
// If this is 0, then the default of 64KB is used.
|
||||
PluginLogBufferSize int
|
||||
|
||||
// AutoMTLS has the client and server automatically negotiate mTLS for
|
||||
// transport authentication. This ensures that only the original client will
|
||||
// be allowed to connect to the server, and all other connections will be
|
||||
@@ -209,6 +254,44 @@ type ClientConfig struct {
|
||||
// to create gRPC connections. This only affects plugins using the gRPC
|
||||
// protocol.
|
||||
GRPCDialOptions []grpc.DialOption
|
||||
|
||||
// GRPCBrokerMultiplex turns on multiplexing for the gRPC broker. The gRPC
|
||||
// broker will multiplex all brokered gRPC servers over the plugin's original
|
||||
// listener socket instead of making a new listener for each server. The
|
||||
// go-plugin library currently only includes a Go implementation for the
|
||||
// server (i.e. plugin) side of gRPC broker multiplexing.
|
||||
//
|
||||
// Does not support reattaching.
|
||||
//
|
||||
// Multiplexed gRPC streams MUST be established sequentially, i.e. after
|
||||
// calling AcceptAndServe from one side, wait for the other side to Dial
|
||||
// before calling AcceptAndServe again.
|
||||
GRPCBrokerMultiplex bool
|
||||
|
||||
// SkipHostEnv allows plugins to run without inheriting the parent process'
|
||||
// environment variables.
|
||||
SkipHostEnv bool
|
||||
|
||||
// UnixSocketConfig configures additional options for any Unix sockets
|
||||
// that are created. Not normally required. Not supported on Windows.
|
||||
UnixSocketConfig *UnixSocketConfig
|
||||
}
|
||||
|
||||
type UnixSocketConfig struct {
|
||||
// If set, go-plugin will change the owner of any Unix sockets created to
|
||||
// this group, and set them as group-writable. Can be a name or gid. The
|
||||
// client process must be a member of this group or chown will fail.
|
||||
Group string
|
||||
|
||||
// TempDir specifies the base directory to use when creating a plugin-specific
|
||||
// temporary directory. It is expected to already exist and be writable. If
|
||||
// not set, defaults to the directory chosen by os.MkdirTemp.
|
||||
TempDir string
|
||||
|
||||
// The directory to create Unix sockets in. Internally created and managed
|
||||
// by go-plugin and deleted when the plugin is killed. Will be created
|
||||
// inside TempDir if specified.
|
||||
socketDir string
|
||||
}
|
||||
|
||||
// ReattachConfig is used to configure a client to reattach to an
|
||||
@@ -220,6 +303,11 @@ type ReattachConfig struct {
|
||||
Addr net.Addr
|
||||
Pid int
|
||||
|
||||
// ReattachFunc allows consumers to provide their own implementation of
|
||||
// runner.AttachedRunner and attach to something other than a plain process.
|
||||
// At least one of Pid or ReattachFunc must be set.
|
||||
ReattachFunc runner.ReattachFunc
|
||||
|
||||
// Test is set to true if this is reattaching to to a plugin in "test mode"
|
||||
// (see ServeConfig.Test). In this mode, client.Kill will NOT kill the
|
||||
// process and instead will rely on the plugin to terminate itself. This
|
||||
@@ -295,11 +383,11 @@ func CleanupClients() {
|
||||
wg.Wait()
|
||||
}
|
||||
|
||||
// Creates a new plugin client which manages the lifecycle of an external
|
||||
// NewClient creates a new plugin client which manages the lifecycle of an external
|
||||
// plugin and gets the address for the RPC connection.
|
||||
//
|
||||
// The client must be cleaned up at some point by calling Kill(). If
|
||||
// the client is a managed client (created with NewManagedClient) you
|
||||
// the client is a managed client (created with ClientConfig.Managed) you
|
||||
// can just call CleanupClients at the end of your program and they will
|
||||
// be properly cleaned.
|
||||
func NewClient(config *ClientConfig) (c *Client) {
|
||||
@@ -317,10 +405,10 @@ func NewClient(config *ClientConfig) (c *Client) {
|
||||
}
|
||||
|
||||
if config.SyncStdout == nil {
|
||||
config.SyncStdout = ioutil.Discard
|
||||
config.SyncStdout = io.Discard
|
||||
}
|
||||
if config.SyncStderr == nil {
|
||||
config.SyncStderr = ioutil.Discard
|
||||
config.SyncStderr = io.Discard
|
||||
}
|
||||
|
||||
if config.AllowedProtocols == nil {
|
||||
@@ -335,6 +423,10 @@ func NewClient(config *ClientConfig) (c *Client) {
|
||||
})
|
||||
}
|
||||
|
||||
if config.PluginLogBufferSize == 0 {
|
||||
config.PluginLogBufferSize = defaultPluginLogBufferSize
|
||||
}
|
||||
|
||||
c = &Client{
|
||||
config: config,
|
||||
logger: config.Logger,
|
||||
@@ -407,12 +499,13 @@ func (c *Client) killed() bool {
|
||||
func (c *Client) Kill() {
|
||||
// Grab a lock to read some private fields.
|
||||
c.l.Lock()
|
||||
process := c.process
|
||||
runner := c.runner
|
||||
addr := c.address
|
||||
hostSocketDir := c.unixSocketCfg.socketDir
|
||||
c.l.Unlock()
|
||||
|
||||
// If there is no process, there is nothing to kill.
|
||||
if process == nil {
|
||||
// If there is no runner or ID, there is nothing to kill.
|
||||
if runner == nil || runner.ID() == "" {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -420,10 +513,14 @@ func (c *Client) Kill() {
|
||||
// Wait for the all client goroutines to finish.
|
||||
c.clientWaitGroup.Wait()
|
||||
|
||||
if hostSocketDir != "" {
|
||||
os.RemoveAll(hostSocketDir)
|
||||
}
|
||||
|
||||
// Make sure there is no reference to the old process after it has been
|
||||
// killed.
|
||||
c.l.Lock()
|
||||
c.process = nil
|
||||
c.runner = nil
|
||||
c.l.Unlock()
|
||||
}()
|
||||
|
||||
@@ -466,14 +563,16 @@ func (c *Client) Kill() {
|
||||
|
||||
// If graceful exiting failed, just kill it
|
||||
c.logger.Warn("plugin failed to exit gracefully")
|
||||
process.Kill()
|
||||
if err := runner.Kill(context.Background()); err != nil {
|
||||
c.logger.Debug("error killing plugin", "error", err)
|
||||
}
|
||||
|
||||
c.l.Lock()
|
||||
c.processKilled = true
|
||||
c.l.Unlock()
|
||||
}
|
||||
|
||||
// Starts the underlying subprocess, communicating with it to negotiate
|
||||
// Start the underlying subprocess, communicating with it to negotiate
|
||||
// a port for RPC connections, and returning the address to connect via RPC.
|
||||
//
|
||||
// This method is safe to call multiple times. Subsequent calls have no effect.
|
||||
@@ -491,16 +590,27 @@ func (c *Client) Start() (addr net.Addr, err error) {
|
||||
// this in a {} for scoping reasons, and hopeful that the escape
|
||||
// analysis will pop the stack here.
|
||||
{
|
||||
cmdSet := c.config.Cmd != nil
|
||||
attachSet := c.config.Reattach != nil
|
||||
secureSet := c.config.SecureConfig != nil
|
||||
if cmdSet == attachSet {
|
||||
return nil, fmt.Errorf("Only one of Cmd or Reattach must be set")
|
||||
var mutuallyExclusiveOptions int
|
||||
if c.config.Cmd != nil {
|
||||
mutuallyExclusiveOptions += 1
|
||||
}
|
||||
if c.config.Reattach != nil {
|
||||
mutuallyExclusiveOptions += 1
|
||||
}
|
||||
if c.config.RunnerFunc != nil {
|
||||
mutuallyExclusiveOptions += 1
|
||||
}
|
||||
if mutuallyExclusiveOptions != 1 {
|
||||
return nil, fmt.Errorf("exactly one of Cmd, or Reattach, or RunnerFunc must be set")
|
||||
}
|
||||
|
||||
if secureSet && attachSet {
|
||||
if c.config.SecureConfig != nil && c.config.Reattach != nil {
|
||||
return nil, ErrSecureConfigAndReattach
|
||||
}
|
||||
|
||||
if c.config.GRPCBrokerMultiplex && c.config.Reattach != nil {
|
||||
return nil, fmt.Errorf("gRPC broker multiplexing is not supported with Reattach config")
|
||||
}
|
||||
}
|
||||
|
||||
if c.config.Reattach != nil {
|
||||
@@ -532,24 +642,24 @@ func (c *Client) Start() (addr net.Addr, err error) {
|
||||
fmt.Sprintf("PLUGIN_MAX_PORT=%d", c.config.MaxPort),
|
||||
fmt.Sprintf("PLUGIN_PROTOCOL_VERSIONS=%s", strings.Join(versionStrings, ",")),
|
||||
}
|
||||
if c.config.GRPCBrokerMultiplex {
|
||||
env = append(env, fmt.Sprintf("%s=true", envMultiplexGRPC))
|
||||
}
|
||||
|
||||
cmd := c.config.Cmd
|
||||
cmd.Env = append(cmd.Env, os.Environ()...)
|
||||
if cmd == nil {
|
||||
// It's only possible to get here if RunnerFunc is non-nil, but we'll
|
||||
// still use cmd as a spec to populate metadata for the external
|
||||
// implementation to consume.
|
||||
cmd = exec.Command("")
|
||||
}
|
||||
if !c.config.SkipHostEnv {
|
||||
cmd.Env = append(cmd.Env, os.Environ()...)
|
||||
}
|
||||
cmd.Env = append(cmd.Env, env...)
|
||||
cmd.Stdin = os.Stdin
|
||||
|
||||
cmdStdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cmdStderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if c.config.SecureConfig == nil {
|
||||
c.logger.Warn("plugin configured with a nil SecureConfig")
|
||||
} else {
|
||||
if c.config.SecureConfig != nil {
|
||||
if ok, err := c.config.SecureConfig.Check(cmd.Path); err != nil {
|
||||
return nil, fmt.Errorf("error verifying checksum: %s", err)
|
||||
} else if !ok {
|
||||
@@ -582,26 +692,62 @@ func (c *Client) Start() (addr net.Addr, err error) {
|
||||
}
|
||||
}
|
||||
|
||||
c.logger.Debug("starting plugin", "path", cmd.Path, "args", cmd.Args)
|
||||
err = cmd.Start()
|
||||
if err != nil {
|
||||
return
|
||||
if c.config.UnixSocketConfig != nil {
|
||||
c.unixSocketCfg = *c.config.UnixSocketConfig
|
||||
}
|
||||
|
||||
// Set the process
|
||||
c.process = cmd.Process
|
||||
c.logger.Debug("plugin started", "path", cmd.Path, "pid", c.process.Pid)
|
||||
if c.unixSocketCfg.Group != "" {
|
||||
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", EnvUnixSocketGroup, c.unixSocketCfg.Group))
|
||||
}
|
||||
|
||||
var runner runner.Runner
|
||||
switch {
|
||||
case c.config.RunnerFunc != nil:
|
||||
c.unixSocketCfg.socketDir, err = os.MkdirTemp(c.unixSocketCfg.TempDir, "plugin-dir")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// os.MkdirTemp creates folders with 0o700, so if we have a group
|
||||
// configured we need to make it group-writable.
|
||||
if c.unixSocketCfg.Group != "" {
|
||||
err = setGroupWritable(c.unixSocketCfg.socketDir, c.unixSocketCfg.Group, 0o770)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", EnvUnixSocketDir, c.unixSocketCfg.socketDir))
|
||||
c.logger.Trace("created temporary directory for unix sockets", "dir", c.unixSocketCfg.socketDir)
|
||||
|
||||
runner, err = c.config.RunnerFunc(c.logger, cmd, c.unixSocketCfg.socketDir)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
default:
|
||||
runner, err = cmdrunner.NewCmdRunner(c.logger, cmd)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
c.runner = runner
|
||||
startCtx, startCtxCancel := context.WithTimeout(context.Background(), c.config.StartTimeout)
|
||||
defer startCtxCancel()
|
||||
err = runner.Start(startCtx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Make sure the command is properly cleaned up if there is an error
|
||||
defer func() {
|
||||
r := recover()
|
||||
rErr := recover()
|
||||
|
||||
if err != nil || r != nil {
|
||||
cmd.Process.Kill()
|
||||
if err != nil || rErr != nil {
|
||||
runner.Kill(context.Background())
|
||||
}
|
||||
|
||||
if r != nil {
|
||||
panic(r)
|
||||
if rErr != nil {
|
||||
panic(rErr)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -612,7 +758,7 @@ func (c *Client) Start() (addr net.Addr, err error) {
|
||||
c.clientWaitGroup.Add(1)
|
||||
c.stderrWaitGroup.Add(1)
|
||||
// logStderr calls Done()
|
||||
go c.logStderr(cmdStderr)
|
||||
go c.logStderr(runner.Name(), runner.Stderr())
|
||||
|
||||
c.clientWaitGroup.Add(1)
|
||||
go func() {
|
||||
@@ -621,29 +767,17 @@ func (c *Client) Start() (addr net.Addr, err error) {
|
||||
|
||||
defer c.clientWaitGroup.Done()
|
||||
|
||||
// get the cmd info early, since the process information will be removed
|
||||
// in Kill.
|
||||
pid := c.process.Pid
|
||||
path := cmd.Path
|
||||
|
||||
// wait to finish reading from stderr since the stderr pipe reader
|
||||
// will be closed by the subsequent call to cmd.Wait().
|
||||
c.stderrWaitGroup.Wait()
|
||||
|
||||
// Wait for the command to end.
|
||||
err := cmd.Wait()
|
||||
|
||||
msgArgs := []interface{}{
|
||||
"path", path,
|
||||
"pid", pid,
|
||||
}
|
||||
err := runner.Wait(context.Background())
|
||||
if err != nil {
|
||||
msgArgs = append(msgArgs,
|
||||
[]interface{}{"error", err.Error()}...)
|
||||
c.logger.Error("plugin process exited", msgArgs...)
|
||||
c.logger.Error("plugin process exited", "plugin", runner.Name(), "id", runner.ID(), "error", err.Error())
|
||||
} else {
|
||||
// Log and make sure to flush the logs right away
|
||||
c.logger.Info("plugin process exited", msgArgs...)
|
||||
c.logger.Info("plugin process exited", "plugin", runner.Name(), "id", runner.ID())
|
||||
}
|
||||
|
||||
os.Stderr.Sync()
|
||||
@@ -662,10 +796,13 @@ func (c *Client) Start() (addr net.Addr, err error) {
|
||||
defer c.clientWaitGroup.Done()
|
||||
defer close(linesCh)
|
||||
|
||||
scanner := bufio.NewScanner(cmdStdout)
|
||||
scanner := bufio.NewScanner(runner.Stdout())
|
||||
for scanner.Scan() {
|
||||
linesCh <- scanner.Text()
|
||||
}
|
||||
if scanner.Err() != nil {
|
||||
c.logger.Error("error encountered while scanning stdout", "error", scanner.Err())
|
||||
}
|
||||
}()
|
||||
|
||||
// Make sure after we exit we read the lines from stdout forever
|
||||
@@ -685,22 +822,27 @@ func (c *Client) Start() (addr net.Addr, err error) {
|
||||
timeout := time.After(c.config.StartTimeout)
|
||||
|
||||
// Start looking for the address
|
||||
c.logger.Debug("waiting for RPC address", "path", cmd.Path)
|
||||
c.logger.Debug("waiting for RPC address", "plugin", runner.Name())
|
||||
select {
|
||||
case <-timeout:
|
||||
err = errors.New("timeout while waiting for plugin to start")
|
||||
case <-c.doneCtx.Done():
|
||||
err = errors.New("plugin exited before we could connect")
|
||||
case line := <-linesCh:
|
||||
case line, ok := <-linesCh:
|
||||
// Trim the line and split by "|" in order to get the parts of
|
||||
// the output.
|
||||
line = strings.TrimSpace(line)
|
||||
parts := strings.SplitN(line, "|", 6)
|
||||
parts := strings.Split(line, "|")
|
||||
if len(parts) < 4 {
|
||||
err = fmt.Errorf(
|
||||
"Unrecognized remote plugin message: %s\n\n"+
|
||||
"This usually means that the plugin is either invalid or simply\n"+
|
||||
"needs to be recompiled to support the latest protocol.", line)
|
||||
errText := fmt.Sprintf("Unrecognized remote plugin message: %s", line)
|
||||
if !ok {
|
||||
errText += "\n" + "Failed to read any lines from plugin's stdout"
|
||||
}
|
||||
additionalNotes := runner.Diagnose(context.Background())
|
||||
if additionalNotes != "" {
|
||||
errText += "\n" + additionalNotes
|
||||
}
|
||||
err = errors.New(errText)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -735,13 +877,18 @@ func (c *Client) Start() (addr net.Addr, err error) {
|
||||
c.negotiatedVersion = version
|
||||
c.logger.Debug("using plugin", "version", version)
|
||||
|
||||
switch parts[2] {
|
||||
network, address, err := runner.PluginToHost(parts[2], parts[3])
|
||||
if err != nil {
|
||||
return addr, err
|
||||
}
|
||||
|
||||
switch network {
|
||||
case "tcp":
|
||||
addr, err = net.ResolveTCPAddr("tcp", parts[3])
|
||||
addr, err = net.ResolveTCPAddr("tcp", address)
|
||||
case "unix":
|
||||
addr, err = net.ResolveUnixAddr("unix", parts[3])
|
||||
addr, err = net.ResolveUnixAddr("unix", address)
|
||||
default:
|
||||
err = fmt.Errorf("Unknown address type: %s", parts[3])
|
||||
err = fmt.Errorf("Unknown address type: %s", address)
|
||||
}
|
||||
|
||||
// If we have a server type, then record that. We default to net/rpc
|
||||
@@ -773,6 +920,18 @@ func (c *Client) Start() (addr net.Addr, err error) {
|
||||
return nil, fmt.Errorf("error parsing server cert: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
if c.config.GRPCBrokerMultiplex && c.protocol == ProtocolGRPC {
|
||||
if len(parts) <= 6 {
|
||||
return nil, fmt.Errorf("%w; for Go plugins, you will need to update the "+
|
||||
"github.com/hashicorp/go-plugin dependency and recompile", ErrGRPCBrokerMuxNotSupported)
|
||||
}
|
||||
if muxSupported, err := strconv.ParseBool(parts[6]); err != nil {
|
||||
return nil, fmt.Errorf("error parsing %q as a boolean for gRPC broker multiplexing support", parts[6])
|
||||
} else if !muxSupported {
|
||||
return nil, ErrGRPCBrokerMuxNotSupported
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c.address = addr
|
||||
@@ -802,39 +961,30 @@ func (c *Client) loadServerCert(cert string) error {
|
||||
}
|
||||
|
||||
func (c *Client) reattach() (net.Addr, error) {
|
||||
// Verify the process still exists. If not, then it is an error
|
||||
p, err := os.FindProcess(c.config.Reattach.Pid)
|
||||
if err != nil {
|
||||
// On Unix systems, FindProcess never returns an error.
|
||||
// On Windows, for non-existent pids it returns:
|
||||
// os.SyscallError - 'OpenProcess: the paremter is incorrect'
|
||||
return nil, ErrProcessNotFound
|
||||
reattachFunc := c.config.Reattach.ReattachFunc
|
||||
// For backwards compatibility default to cmdrunner.ReattachFunc
|
||||
if reattachFunc == nil {
|
||||
reattachFunc = cmdrunner.ReattachFunc(c.config.Reattach.Pid, c.config.Reattach.Addr)
|
||||
}
|
||||
|
||||
// Attempt to connect to the addr since on Unix systems FindProcess
|
||||
// doesn't actually return an error if it can't find the process.
|
||||
conn, err := net.Dial(
|
||||
c.config.Reattach.Addr.Network(),
|
||||
c.config.Reattach.Addr.String())
|
||||
r, err := reattachFunc()
|
||||
if err != nil {
|
||||
p.Kill()
|
||||
return nil, ErrProcessNotFound
|
||||
return nil, err
|
||||
}
|
||||
conn.Close()
|
||||
|
||||
// Create a context for when we kill
|
||||
c.doneCtx, c.ctxCancel = context.WithCancel(context.Background())
|
||||
|
||||
c.clientWaitGroup.Add(1)
|
||||
// Goroutine to mark exit status
|
||||
go func(pid int) {
|
||||
go func(r runner.AttachedRunner) {
|
||||
defer c.clientWaitGroup.Done()
|
||||
|
||||
// ensure the context is cancelled when we're done
|
||||
defer c.ctxCancel()
|
||||
|
||||
// Wait for the process to die
|
||||
pidWait(pid)
|
||||
r.Wait(context.Background())
|
||||
|
||||
// Log so we can see it
|
||||
c.logger.Debug("reattached plugin process exited")
|
||||
@@ -843,7 +993,7 @@ func (c *Client) reattach() (net.Addr, error) {
|
||||
c.l.Lock()
|
||||
defer c.l.Unlock()
|
||||
c.exited = true
|
||||
}(p.Pid)
|
||||
}(r)
|
||||
|
||||
// Set the address and protocol
|
||||
c.address = c.config.Reattach.Addr
|
||||
@@ -855,13 +1005,12 @@ func (c *Client) reattach() (net.Addr, error) {
|
||||
|
||||
if c.config.Reattach.Test {
|
||||
c.negotiatedVersion = c.config.Reattach.ProtocolVersion
|
||||
}
|
||||
|
||||
// If we're in test mode, we do NOT set the process. This avoids the
|
||||
// process being killed (the only purpose we have for c.process), since
|
||||
// in test mode the process is responsible for exiting on its own.
|
||||
if !c.config.Reattach.Test {
|
||||
c.process = p
|
||||
} else {
|
||||
// If we're in test mode, we do NOT set the runner. This avoids the
|
||||
// runner being killed (the only purpose we have for setting c.runner
|
||||
// when reattaching), since in test mode the process is responsible for
|
||||
// exiting on its own.
|
||||
c.runner = r
|
||||
}
|
||||
|
||||
return c.address, nil
|
||||
@@ -900,6 +1049,9 @@ func (c *Client) checkProtoVersion(protoVersion string) (int, PluginSet, error)
|
||||
//
|
||||
// If this returns nil then the process hasn't been started yet. Please
|
||||
// call Start or Client before calling this.
|
||||
//
|
||||
// Clients who specified a RunnerFunc will need to populate their own
|
||||
// ReattachFunc in the returned ReattachConfig before it can be used.
|
||||
func (c *Client) ReattachConfig() *ReattachConfig {
|
||||
c.l.Lock()
|
||||
defer c.l.Unlock()
|
||||
@@ -917,11 +1069,16 @@ func (c *Client) ReattachConfig() *ReattachConfig {
|
||||
return c.config.Reattach
|
||||
}
|
||||
|
||||
return &ReattachConfig{
|
||||
reattach := &ReattachConfig{
|
||||
Protocol: c.protocol,
|
||||
Addr: c.address,
|
||||
Pid: c.config.Cmd.Process.Pid,
|
||||
}
|
||||
|
||||
if c.config.Cmd != nil && c.config.Cmd.Process != nil {
|
||||
reattach.Pid = c.config.Cmd.Process.Pid
|
||||
}
|
||||
|
||||
return reattach
|
||||
}
|
||||
|
||||
// Protocol returns the protocol of server on the remote end. This will
|
||||
@@ -957,11 +1114,24 @@ func netAddrDialer(addr net.Addr) func(string, time.Duration) (net.Conn, error)
|
||||
// dialer is compatible with grpc.WithDialer and creates the connection
|
||||
// to the plugin.
|
||||
func (c *Client) dialer(_ string, timeout time.Duration) (net.Conn, error) {
|
||||
conn, err := netAddrDialer(c.address)("", timeout)
|
||||
muxer, err := c.getGRPCMuxer(c.address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var conn net.Conn
|
||||
if muxer.Enabled() {
|
||||
conn, err = muxer.Dial()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
} else {
|
||||
conn, err = netAddrDialer(c.address)("", timeout)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// If we have a TLS config we wrap our connection. We only do this
|
||||
// for net/rpc since gRPC uses its own mechanism for TLS.
|
||||
if c.protocol == ProtocolNetRPC && c.config.TLSConfig != nil {
|
||||
@@ -971,14 +1141,28 @@ func (c *Client) dialer(_ string, timeout time.Duration) (net.Conn, error) {
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
var stdErrBufferSize = 64 * 1024
|
||||
func (c *Client) getGRPCMuxer(addr net.Addr) (*grpcmux.GRPCClientMuxer, error) {
|
||||
if c.protocol != ProtocolGRPC || !c.config.GRPCBrokerMultiplex {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (c *Client) logStderr(r io.Reader) {
|
||||
var err error
|
||||
c.grpcMuxerOnce.Do(func() {
|
||||
c.grpcMuxer, err = grpcmux.NewGRPCClientMuxer(c.logger, addr)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.grpcMuxer, nil
|
||||
}
|
||||
|
||||
func (c *Client) logStderr(name string, r io.Reader) {
|
||||
defer c.clientWaitGroup.Done()
|
||||
defer c.stderrWaitGroup.Done()
|
||||
l := c.logger.Named(filepath.Base(c.config.Cmd.Path))
|
||||
l := c.logger.Named(filepath.Base(name))
|
||||
|
||||
reader := bufio.NewReaderSize(r, stdErrBufferSize)
|
||||
reader := bufio.NewReaderSize(r, c.config.PluginLogBufferSize)
|
||||
// continuation indicates the previous line was a prefix
|
||||
continuation := false
|
||||
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package plugin
|
||||
|
||||
const (
|
||||
// EnvUnixSocketDir specifies the directory that _plugins_ should create unix
|
||||
// sockets in. Does not affect client behavior.
|
||||
EnvUnixSocketDir = "PLUGIN_UNIX_SOCKET_DIR"
|
||||
|
||||
// EnvUnixSocketGroup specifies the owning, writable group to set for Unix
|
||||
// sockets created by _plugins_. Does not affect client behavior.
|
||||
EnvUnixSocketGroup = "PLUGIN_UNIX_SOCKET_GROUP"
|
||||
|
||||
envMultiplexGRPC = "PLUGIN_MULTIPLEX_GRPC"
|
||||
)
|
||||
+3
@@ -1,3 +1,6 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package plugin
|
||||
|
||||
import (
|
||||
|
||||
+3
@@ -1,3 +1,6 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package plugin
|
||||
|
||||
// This is a type that wraps error types so that they can be messaged
|
||||
|
||||
+222
-25
@@ -1,3 +1,6 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package plugin
|
||||
|
||||
import (
|
||||
@@ -11,7 +14,9 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-plugin/internal/grpcmux"
|
||||
"github.com/hashicorp/go-plugin/internal/plugin"
|
||||
"github.com/hashicorp/go-plugin/runner"
|
||||
|
||||
"github.com/oklog/run"
|
||||
"google.golang.org/grpc"
|
||||
@@ -36,6 +41,8 @@ type sendErr struct {
|
||||
// connection information to/from the plugin. Implements GRPCBrokerServer and
|
||||
// streamer interfaces.
|
||||
type gRPCBrokerServer struct {
|
||||
plugin.UnimplementedGRPCBrokerServer
|
||||
|
||||
// send is used to send connection info to the gRPC stream.
|
||||
send chan *sendErr
|
||||
|
||||
@@ -259,25 +266,41 @@ func (s *gRPCBrokerClientImpl) Close() {
|
||||
type GRPCBroker struct {
|
||||
nextId uint32
|
||||
streamer streamer
|
||||
streams map[uint32]*gRPCBrokerPending
|
||||
tls *tls.Config
|
||||
doneCh chan struct{}
|
||||
o sync.Once
|
||||
|
||||
clientStreams map[uint32]*gRPCBrokerPending
|
||||
serverStreams map[uint32]*gRPCBrokerPending
|
||||
|
||||
unixSocketCfg UnixSocketConfig
|
||||
addrTranslator runner.AddrTranslator
|
||||
|
||||
dialMutex sync.Mutex
|
||||
|
||||
muxer grpcmux.GRPCMuxer
|
||||
|
||||
sync.Mutex
|
||||
}
|
||||
|
||||
type gRPCBrokerPending struct {
|
||||
ch chan *plugin.ConnInfo
|
||||
doneCh chan struct{}
|
||||
once sync.Once
|
||||
}
|
||||
|
||||
func newGRPCBroker(s streamer, tls *tls.Config) *GRPCBroker {
|
||||
func newGRPCBroker(s streamer, tls *tls.Config, unixSocketCfg UnixSocketConfig, addrTranslator runner.AddrTranslator, muxer grpcmux.GRPCMuxer) *GRPCBroker {
|
||||
return &GRPCBroker{
|
||||
streamer: s,
|
||||
streams: make(map[uint32]*gRPCBrokerPending),
|
||||
tls: tls,
|
||||
doneCh: make(chan struct{}),
|
||||
|
||||
clientStreams: make(map[uint32]*gRPCBrokerPending),
|
||||
serverStreams: make(map[uint32]*gRPCBrokerPending),
|
||||
muxer: muxer,
|
||||
|
||||
unixSocketCfg: unixSocketCfg,
|
||||
addrTranslator: addrTranslator,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,15 +308,59 @@ func newGRPCBroker(s streamer, tls *tls.Config) *GRPCBroker {
|
||||
//
|
||||
// This should not be called multiple times with the same ID at one time.
|
||||
func (b *GRPCBroker) Accept(id uint32) (net.Listener, error) {
|
||||
listener, err := serverListener()
|
||||
if b.muxer.Enabled() {
|
||||
p := b.getServerStream(id)
|
||||
go func() {
|
||||
err := b.listenForKnocks(id)
|
||||
if err != nil {
|
||||
log.Printf("[ERR]: error listening for knocks, id: %d, error: %s", id, err)
|
||||
}
|
||||
}()
|
||||
|
||||
ln, err := b.muxer.Listener(id, p.doneCh)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ln = &rmListener{
|
||||
Listener: ln,
|
||||
close: func() error {
|
||||
// We could have multiple listeners on the same ID, so use sync.Once
|
||||
// for closing doneCh to ensure we don't get a panic.
|
||||
p.once.Do(func() {
|
||||
close(p.doneCh)
|
||||
})
|
||||
|
||||
b.Lock()
|
||||
defer b.Unlock()
|
||||
|
||||
// No longer need to listen for knocks once the listener is closed.
|
||||
delete(b.serverStreams, id)
|
||||
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
return ln, nil
|
||||
}
|
||||
|
||||
listener, err := serverListener(b.unixSocketCfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
advertiseNet := listener.Addr().Network()
|
||||
advertiseAddr := listener.Addr().String()
|
||||
if b.addrTranslator != nil {
|
||||
advertiseNet, advertiseAddr, err = b.addrTranslator.HostToPlugin(advertiseNet, advertiseAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
err = b.streamer.Send(&plugin.ConnInfo{
|
||||
ServiceId: id,
|
||||
Network: listener.Addr().Network(),
|
||||
Address: listener.Addr().String(),
|
||||
Network: advertiseNet,
|
||||
Address: advertiseAddr,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -309,20 +376,20 @@ func (b *GRPCBroker) Accept(id uint32) (net.Listener, error) {
|
||||
// connection is opened every call, these calls should be used sparingly.
|
||||
// Multiple gRPC server implementations can be registered to a single
|
||||
// AcceptAndServe call.
|
||||
func (b *GRPCBroker) AcceptAndServe(id uint32, s func([]grpc.ServerOption) *grpc.Server) {
|
||||
listener, err := b.Accept(id)
|
||||
func (b *GRPCBroker) AcceptAndServe(id uint32, newGRPCServer func([]grpc.ServerOption) *grpc.Server) {
|
||||
ln, err := b.Accept(id)
|
||||
if err != nil {
|
||||
log.Printf("[ERR] plugin: plugin acceptAndServe error: %s", err)
|
||||
return
|
||||
}
|
||||
defer listener.Close()
|
||||
defer ln.Close()
|
||||
|
||||
var opts []grpc.ServerOption
|
||||
if b.tls != nil {
|
||||
opts = []grpc.ServerOption{grpc.Creds(credentials.NewTLS(b.tls))}
|
||||
}
|
||||
|
||||
server := s(opts)
|
||||
server := newGRPCServer(opts)
|
||||
|
||||
// Here we use a run group to close this goroutine if the server is shutdown
|
||||
// or the broker is shutdown.
|
||||
@@ -330,7 +397,7 @@ func (b *GRPCBroker) AcceptAndServe(id uint32, s func([]grpc.ServerOption) *grpc
|
||||
{
|
||||
// Serve on the listener, if shutting down call GracefulStop.
|
||||
g.Add(func() error {
|
||||
return server.Serve(listener)
|
||||
return server.Serve(ln)
|
||||
}, func(err error) {
|
||||
server.GracefulStop()
|
||||
})
|
||||
@@ -363,12 +430,108 @@ func (b *GRPCBroker) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *GRPCBroker) listenForKnocks(id uint32) error {
|
||||
p := b.getServerStream(id)
|
||||
for {
|
||||
select {
|
||||
case msg := <-p.ch:
|
||||
// Shouldn't be possible.
|
||||
if msg.ServiceId != id {
|
||||
return fmt.Errorf("knock received with wrong service ID; expected %d but got %d", id, msg.ServiceId)
|
||||
}
|
||||
|
||||
// Also shouldn't be possible.
|
||||
if msg.Knock == nil || !msg.Knock.Knock || msg.Knock.Ack {
|
||||
return fmt.Errorf("knock received for service ID %d with incorrect values; knock=%+v", id, msg.Knock)
|
||||
}
|
||||
|
||||
// Successful knock, open the door for the given ID.
|
||||
var ackError string
|
||||
err := b.muxer.AcceptKnock(id)
|
||||
if err != nil {
|
||||
ackError = err.Error()
|
||||
}
|
||||
|
||||
// Send back an acknowledgement to allow the client to start dialling.
|
||||
err = b.streamer.Send(&plugin.ConnInfo{
|
||||
ServiceId: id,
|
||||
Knock: &plugin.ConnInfo_Knock{
|
||||
Knock: true,
|
||||
Ack: true,
|
||||
Error: ackError,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("error sending back knock acknowledgement: %w", err)
|
||||
}
|
||||
case <-p.doneCh:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (b *GRPCBroker) knock(id uint32) error {
|
||||
// Send a knock.
|
||||
err := b.streamer.Send(&plugin.ConnInfo{
|
||||
ServiceId: id,
|
||||
Knock: &plugin.ConnInfo_Knock{
|
||||
Knock: true,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Wait for the ack.
|
||||
p := b.getClientStream(id)
|
||||
select {
|
||||
case msg := <-p.ch:
|
||||
if msg.ServiceId != id {
|
||||
return fmt.Errorf("handshake failed for multiplexing on id %d; got response for %d", id, msg.ServiceId)
|
||||
}
|
||||
if msg.Knock == nil || !msg.Knock.Knock || !msg.Knock.Ack {
|
||||
return fmt.Errorf("handshake failed for multiplexing on id %d; expected knock and ack, but got %+v", id, msg.Knock)
|
||||
}
|
||||
if msg.Knock.Error != "" {
|
||||
return fmt.Errorf("failed to knock for id %d: %s", id, msg.Knock.Error)
|
||||
}
|
||||
case <-time.After(5 * time.Second):
|
||||
return fmt.Errorf("timeout waiting for multiplexing knock handshake on id %d", id)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *GRPCBroker) muxDial(id uint32) func(string, time.Duration) (net.Conn, error) {
|
||||
return func(string, time.Duration) (net.Conn, error) {
|
||||
b.dialMutex.Lock()
|
||||
defer b.dialMutex.Unlock()
|
||||
|
||||
// Tell the other side the listener ID it should give the next stream to.
|
||||
err := b.knock(id)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to knock before dialling client: %w", err)
|
||||
}
|
||||
|
||||
conn, err := b.muxer.Dial()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Dial opens a connection by ID.
|
||||
func (b *GRPCBroker) Dial(id uint32) (conn *grpc.ClientConn, err error) {
|
||||
if b.muxer.Enabled() {
|
||||
return dialGRPCConn(b.tls, b.muxDial(id))
|
||||
}
|
||||
|
||||
var c *plugin.ConnInfo
|
||||
|
||||
// Open the stream
|
||||
p := b.getStream(id)
|
||||
p := b.getClientStream(id)
|
||||
select {
|
||||
case c = <-p.ch:
|
||||
close(p.doneCh)
|
||||
@@ -376,12 +539,20 @@ func (b *GRPCBroker) Dial(id uint32) (conn *grpc.ClientConn, err error) {
|
||||
return nil, fmt.Errorf("timeout waiting for connection info")
|
||||
}
|
||||
|
||||
network, address := c.Network, c.Address
|
||||
if b.addrTranslator != nil {
|
||||
network, address, err = b.addrTranslator.PluginToHost(network, address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var addr net.Addr
|
||||
switch c.Network {
|
||||
switch network {
|
||||
case "tcp":
|
||||
addr, err = net.ResolveTCPAddr("tcp", c.Address)
|
||||
addr, err = net.ResolveTCPAddr("tcp", address)
|
||||
case "unix":
|
||||
addr, err = net.ResolveUnixAddr("unix", c.Address)
|
||||
addr, err = net.ResolveUnixAddr("unix", address)
|
||||
default:
|
||||
err = fmt.Errorf("Unknown address type: %s", c.Address)
|
||||
}
|
||||
@@ -408,37 +579,63 @@ func (m *GRPCBroker) NextId() uint32 {
|
||||
// the plugin host/client.
|
||||
func (m *GRPCBroker) Run() {
|
||||
for {
|
||||
stream, err := m.streamer.Recv()
|
||||
msg, err := m.streamer.Recv()
|
||||
if err != nil {
|
||||
// Once we receive an error, just exit
|
||||
break
|
||||
}
|
||||
|
||||
// Initialize the waiter
|
||||
p := m.getStream(stream.ServiceId)
|
||||
var p *gRPCBrokerPending
|
||||
if msg.Knock != nil && msg.Knock.Knock && !msg.Knock.Ack {
|
||||
p = m.getServerStream(msg.ServiceId)
|
||||
// The server side doesn't close the channel immediately as it needs
|
||||
// to continuously listen for knocks.
|
||||
} else {
|
||||
p = m.getClientStream(msg.ServiceId)
|
||||
go m.timeoutWait(msg.ServiceId, p)
|
||||
}
|
||||
select {
|
||||
case p.ch <- stream:
|
||||
case p.ch <- msg:
|
||||
default:
|
||||
}
|
||||
|
||||
go m.timeoutWait(stream.ServiceId, p)
|
||||
}
|
||||
}
|
||||
|
||||
func (m *GRPCBroker) getStream(id uint32) *gRPCBrokerPending {
|
||||
// getClientStream is a buffer to receive new connection info and knock acks
|
||||
// by stream ID.
|
||||
func (m *GRPCBroker) getClientStream(id uint32) *gRPCBrokerPending {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
p, ok := m.streams[id]
|
||||
p, ok := m.clientStreams[id]
|
||||
if ok {
|
||||
return p
|
||||
}
|
||||
|
||||
m.streams[id] = &gRPCBrokerPending{
|
||||
m.clientStreams[id] = &gRPCBrokerPending{
|
||||
ch: make(chan *plugin.ConnInfo, 1),
|
||||
doneCh: make(chan struct{}),
|
||||
}
|
||||
return m.streams[id]
|
||||
return m.clientStreams[id]
|
||||
}
|
||||
|
||||
// getServerStream is a buffer to receive knocks to a multiplexed stream ID
|
||||
// that its side is listening on. Not used unless multiplexing is enabled.
|
||||
func (m *GRPCBroker) getServerStream(id uint32) *gRPCBrokerPending {
|
||||
m.Lock()
|
||||
defer m.Unlock()
|
||||
|
||||
p, ok := m.serverStreams[id]
|
||||
if ok {
|
||||
return p
|
||||
}
|
||||
|
||||
m.serverStreams[id] = &gRPCBrokerPending{
|
||||
ch: make(chan *plugin.ConnInfo, 1),
|
||||
doneCh: make(chan struct{}),
|
||||
}
|
||||
return m.serverStreams[id]
|
||||
}
|
||||
|
||||
func (m *GRPCBroker) timeoutWait(id uint32, p *gRPCBrokerPending) {
|
||||
@@ -453,5 +650,5 @@ func (m *GRPCBroker) timeoutWait(id uint32, p *gRPCBrokerPending) {
|
||||
defer m.Unlock()
|
||||
|
||||
// Delete the stream so no one else can grab it
|
||||
delete(m.streams, id)
|
||||
delete(m.clientStreams, id)
|
||||
}
|
||||
|
||||
+10
-2
@@ -1,6 +1,10 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"math"
|
||||
@@ -8,7 +12,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-plugin/internal/plugin"
|
||||
"golang.org/x/net/context"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/health/grpc_health_v1"
|
||||
@@ -58,9 +61,14 @@ func newGRPCClient(doneCtx context.Context, c *Client) (*GRPCClient, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
muxer, err := c.getGRPCMuxer(c.address)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Start the broker.
|
||||
brokerGRPCClient := newGRPCBrokerClient(conn)
|
||||
broker := newGRPCBroker(brokerGRPCClient, c.config.TLSConfig)
|
||||
broker := newGRPCBroker(brokerGRPCClient, c.config.TLSConfig, c.unixSocketCfg, c.runner, muxer)
|
||||
go broker.Run()
|
||||
go brokerGRPCClient.StartStream()
|
||||
|
||||
|
||||
+3
@@ -1,3 +1,6 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package plugin
|
||||
|
||||
import (
|
||||
|
||||
+21
-3
@@ -1,3 +1,6 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package plugin
|
||||
|
||||
import (
|
||||
@@ -9,6 +12,7 @@ import (
|
||||
"net"
|
||||
|
||||
hclog "github.com/hashicorp/go-hclog"
|
||||
"github.com/hashicorp/go-plugin/internal/grpcmux"
|
||||
"github.com/hashicorp/go-plugin/internal/plugin"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
@@ -58,6 +62,8 @@ type GRPCServer struct {
|
||||
stdioServer *grpcStdioServer
|
||||
|
||||
logger hclog.Logger
|
||||
|
||||
muxer *grpcmux.GRPCServerMuxer
|
||||
}
|
||||
|
||||
// ServerProtocol impl.
|
||||
@@ -81,7 +87,7 @@ func (s *GRPCServer) Init() error {
|
||||
// Register the broker service
|
||||
brokerServer := newGRPCBrokerServer()
|
||||
plugin.RegisterGRPCBrokerServer(s.server, brokerServer)
|
||||
s.broker = newGRPCBroker(brokerServer, s.TLS)
|
||||
s.broker = newGRPCBroker(brokerServer, s.TLS, unixSocketConfigFromEnv(), nil, s.muxer)
|
||||
go s.broker.Run()
|
||||
|
||||
// Register the controller
|
||||
@@ -107,14 +113,26 @@ func (s *GRPCServer) Init() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Stop calls Stop on the underlying grpc.Server
|
||||
// Stop calls Stop on the underlying grpc.Server and Close on the underlying
|
||||
// grpc.Broker if present.
|
||||
func (s *GRPCServer) Stop() {
|
||||
s.server.Stop()
|
||||
|
||||
if s.broker != nil {
|
||||
s.broker.Close()
|
||||
s.broker = nil
|
||||
}
|
||||
}
|
||||
|
||||
// GracefulStop calls GracefulStop on the underlying grpc.Server
|
||||
// GracefulStop calls GracefulStop on the underlying grpc.Server and Close on
|
||||
// the underlying grpc.Broker if present.
|
||||
func (s *GRPCServer) GracefulStop() {
|
||||
s.server.GracefulStop()
|
||||
|
||||
if s.broker != nil {
|
||||
s.broker.Close()
|
||||
s.broker = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Config is the GRPCServerConfig encoded as JSON then base64.
|
||||
|
||||
+3
@@ -1,3 +1,6 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package plugin
|
||||
|
||||
import (
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package cmdrunner
|
||||
|
||||
// addrTranslator implements stateless identity functions, as the host and plugin
|
||||
// run in the same context wrt Unix and network addresses.
|
||||
type addrTranslator struct{}
|
||||
|
||||
func (*addrTranslator) PluginToHost(pluginNet, pluginAddr string) (string, string, error) {
|
||||
return pluginNet, pluginAddr, nil
|
||||
}
|
||||
|
||||
func (*addrTranslator) HostToPlugin(hostNet, hostAddr string) (string, string, error) {
|
||||
return hostNet, hostAddr, nil
|
||||
}
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package cmdrunner
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
|
||||
"github.com/hashicorp/go-plugin/runner"
|
||||
)
|
||||
|
||||
// ReattachFunc returns a function that allows reattaching to a plugin running
|
||||
// as a plain process. The process may or may not be a child process.
|
||||
func ReattachFunc(pid int, addr net.Addr) runner.ReattachFunc {
|
||||
return func() (runner.AttachedRunner, error) {
|
||||
p, err := os.FindProcess(pid)
|
||||
if err != nil {
|
||||
// On Unix systems, FindProcess never returns an error.
|
||||
// On Windows, for non-existent pids it returns:
|
||||
// os.SyscallError - 'OpenProcess: the paremter is incorrect'
|
||||
return nil, ErrProcessNotFound
|
||||
}
|
||||
|
||||
// Attempt to connect to the addr since on Unix systems FindProcess
|
||||
// doesn't actually return an error if it can't find the process.
|
||||
conn, err := net.Dial(addr.Network(), addr.String())
|
||||
if err != nil {
|
||||
p.Kill()
|
||||
return nil, ErrProcessNotFound
|
||||
}
|
||||
conn.Close()
|
||||
|
||||
return &CmdAttachedRunner{
|
||||
pid: pid,
|
||||
process: p,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
// CmdAttachedRunner is mostly a subset of CmdRunner, except the Wait function
|
||||
// does not assume the process is a child of the host process, and so uses a
|
||||
// different implementation to wait on the process.
|
||||
type CmdAttachedRunner struct {
|
||||
pid int
|
||||
process *os.Process
|
||||
|
||||
addrTranslator
|
||||
}
|
||||
|
||||
func (c *CmdAttachedRunner) Wait(_ context.Context) error {
|
||||
return pidWait(c.pid)
|
||||
}
|
||||
|
||||
func (c *CmdAttachedRunner) Kill(_ context.Context) error {
|
||||
return c.process.Kill()
|
||||
}
|
||||
|
||||
func (c *CmdAttachedRunner) ID() string {
|
||||
return fmt.Sprintf("%d", c.pid)
|
||||
}
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package cmdrunner
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"github.com/hashicorp/go-hclog"
|
||||
"github.com/hashicorp/go-plugin/runner"
|
||||
)
|
||||
|
||||
var (
|
||||
_ runner.Runner = (*CmdRunner)(nil)
|
||||
|
||||
// ErrProcessNotFound is returned when a client is instantiated to
|
||||
// reattach to an existing process and it isn't found.
|
||||
ErrProcessNotFound = errors.New("Reattachment process not found")
|
||||
)
|
||||
|
||||
const unrecognizedRemotePluginMessage = `This usually means
|
||||
the plugin was not compiled for this architecture,
|
||||
the plugin is missing dynamic-link libraries necessary to run,
|
||||
the plugin is not executable by this process due to file permissions, or
|
||||
the plugin failed to negotiate the initial go-plugin protocol handshake
|
||||
%s`
|
||||
|
||||
// CmdRunner implements the runner.Runner interface. It mostly just passes through
|
||||
// to exec.Cmd methods.
|
||||
type CmdRunner struct {
|
||||
logger hclog.Logger
|
||||
cmd *exec.Cmd
|
||||
|
||||
stdout io.ReadCloser
|
||||
stderr io.ReadCloser
|
||||
|
||||
// Cmd info is persisted early, since the process information will be removed
|
||||
// after Kill is called.
|
||||
path string
|
||||
pid int
|
||||
|
||||
addrTranslator
|
||||
}
|
||||
|
||||
// NewCmdRunner returns an implementation of runner.Runner for running a plugin
|
||||
// as a subprocess. It must be passed a cmd that hasn't yet been started.
|
||||
func NewCmdRunner(logger hclog.Logger, cmd *exec.Cmd) (*CmdRunner, error) {
|
||||
stdout, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stderr, err := cmd.StderrPipe()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &CmdRunner{
|
||||
logger: logger,
|
||||
cmd: cmd,
|
||||
stdout: stdout,
|
||||
stderr: stderr,
|
||||
path: cmd.Path,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *CmdRunner) Start(_ context.Context) error {
|
||||
c.logger.Debug("starting plugin", "path", c.cmd.Path, "args", c.cmd.Args)
|
||||
err := c.cmd.Start()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
c.pid = c.cmd.Process.Pid
|
||||
c.logger.Debug("plugin started", "path", c.path, "pid", c.pid)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CmdRunner) Wait(_ context.Context) error {
|
||||
return c.cmd.Wait()
|
||||
}
|
||||
|
||||
func (c *CmdRunner) Kill(_ context.Context) error {
|
||||
if c.cmd.Process != nil {
|
||||
err := c.cmd.Process.Kill()
|
||||
// Swallow ErrProcessDone, we support calling Kill multiple times.
|
||||
if !errors.Is(err, os.ErrProcessDone) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *CmdRunner) Stdout() io.ReadCloser {
|
||||
return c.stdout
|
||||
}
|
||||
|
||||
func (c *CmdRunner) Stderr() io.ReadCloser {
|
||||
return c.stderr
|
||||
}
|
||||
|
||||
func (c *CmdRunner) Name() string {
|
||||
return c.path
|
||||
}
|
||||
|
||||
func (c *CmdRunner) ID() string {
|
||||
return fmt.Sprintf("%d", c.pid)
|
||||
}
|
||||
|
||||
// peTypes is a list of Portable Executable (PE) machine types from https://learn.microsoft.com/en-us/windows/win32/debug/pe-format
|
||||
// mapped to GOARCH types. It is not comprehensive, and only includes machine types that Go supports.
|
||||
var peTypes = map[uint16]string{
|
||||
0x14c: "386",
|
||||
0x1c0: "arm",
|
||||
0x6264: "loong64",
|
||||
0x8664: "amd64",
|
||||
0xaa64: "arm64",
|
||||
}
|
||||
|
||||
func (c *CmdRunner) Diagnose(_ context.Context) string {
|
||||
return fmt.Sprintf(unrecognizedRemotePluginMessage, additionalNotesAboutCommand(c.cmd.Path))
|
||||
}
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package cmdrunner
|
||||
|
||||
import (
|
||||
"debug/elf"
|
||||
"debug/macho"
|
||||
"debug/pe"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/user"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
// additionalNotesAboutCommand tries to get additional information about a command that might help diagnose
|
||||
// why it won't run correctly. It runs as a best effort only.
|
||||
func additionalNotesAboutCommand(path string) string {
|
||||
notes := ""
|
||||
stat, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return notes
|
||||
}
|
||||
|
||||
notes += "\nAdditional notes about plugin:\n"
|
||||
notes += fmt.Sprintf(" Path: %s\n", path)
|
||||
notes += fmt.Sprintf(" Mode: %s\n", stat.Mode())
|
||||
statT, ok := stat.Sys().(*syscall.Stat_t)
|
||||
if ok {
|
||||
currentUsername := "?"
|
||||
if u, err := user.LookupId(strconv.FormatUint(uint64(os.Getuid()), 10)); err == nil {
|
||||
currentUsername = u.Username
|
||||
}
|
||||
currentGroup := "?"
|
||||
if g, err := user.LookupGroupId(strconv.FormatUint(uint64(os.Getgid()), 10)); err == nil {
|
||||
currentGroup = g.Name
|
||||
}
|
||||
username := "?"
|
||||
if u, err := user.LookupId(strconv.FormatUint(uint64(statT.Uid), 10)); err == nil {
|
||||
username = u.Username
|
||||
}
|
||||
group := "?"
|
||||
if g, err := user.LookupGroupId(strconv.FormatUint(uint64(statT.Gid), 10)); err == nil {
|
||||
group = g.Name
|
||||
}
|
||||
notes += fmt.Sprintf(" Owner: %d [%s] (current: %d [%s])\n", statT.Uid, username, os.Getuid(), currentUsername)
|
||||
notes += fmt.Sprintf(" Group: %d [%s] (current: %d [%s])\n", statT.Gid, group, os.Getgid(), currentGroup)
|
||||
}
|
||||
|
||||
if elfFile, err := elf.Open(path); err == nil {
|
||||
defer elfFile.Close()
|
||||
notes += fmt.Sprintf(" ELF architecture: %s (current architecture: %s)\n", elfFile.Machine, runtime.GOARCH)
|
||||
} else if machoFile, err := macho.Open(path); err == nil {
|
||||
defer machoFile.Close()
|
||||
notes += fmt.Sprintf(" MachO architecture: %s (current architecture: %s)\n", machoFile.Cpu, runtime.GOARCH)
|
||||
} else if peFile, err := pe.Open(path); err == nil {
|
||||
defer peFile.Close()
|
||||
machine, ok := peTypes[peFile.Machine]
|
||||
if !ok {
|
||||
machine = "unknown"
|
||||
}
|
||||
notes += fmt.Sprintf(" PE architecture: %s (current architecture: %s)\n", machine, runtime.GOARCH)
|
||||
}
|
||||
return notes
|
||||
}
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//go:build windows
|
||||
// +build windows
|
||||
|
||||
package cmdrunner
|
||||
|
||||
import (
|
||||
"debug/elf"
|
||||
"debug/macho"
|
||||
"debug/pe"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
)
|
||||
|
||||
// additionalNotesAboutCommand tries to get additional information about a command that might help diagnose
|
||||
// why it won't run correctly. It runs as a best effort only.
|
||||
func additionalNotesAboutCommand(path string) string {
|
||||
notes := ""
|
||||
stat, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return notes
|
||||
}
|
||||
|
||||
notes += "\nAdditional notes about plugin:\n"
|
||||
notes += fmt.Sprintf(" Path: %s\n", path)
|
||||
notes += fmt.Sprintf(" Mode: %s\n", stat.Mode())
|
||||
|
||||
if elfFile, err := elf.Open(path); err == nil {
|
||||
defer elfFile.Close()
|
||||
notes += fmt.Sprintf(" ELF architecture: %s (current architecture: %s)\n", elfFile.Machine, runtime.GOARCH)
|
||||
} else if machoFile, err := macho.Open(path); err == nil {
|
||||
defer machoFile.Close()
|
||||
notes += fmt.Sprintf(" MachO architecture: %s (current architecture: %s)\n", machoFile.Cpu, runtime.GOARCH)
|
||||
} else if peFile, err := pe.Open(path); err == nil {
|
||||
defer peFile.Close()
|
||||
machine, ok := peTypes[peFile.Machine]
|
||||
if !ok {
|
||||
machine = "unknown"
|
||||
}
|
||||
notes += fmt.Sprintf(" PE architecture: %s (current architecture: %s)\n", machine, runtime.GOARCH)
|
||||
}
|
||||
return notes
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package cmdrunner
|
||||
|
||||
import "time"
|
||||
|
||||
// pidAlive checks whether a pid is alive.
|
||||
func pidAlive(pid int) bool {
|
||||
return _pidAlive(pid)
|
||||
}
|
||||
|
||||
// pidWait blocks for a process to exit.
|
||||
func pidWait(pid int) error {
|
||||
ticker := time.NewTicker(1 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for range ticker.C {
|
||||
if !pidAlive(pid) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Generated
Vendored
+4
-1
@@ -1,7 +1,10 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
//go:build !windows
|
||||
// +build !windows
|
||||
|
||||
package plugin
|
||||
package cmdrunner
|
||||
|
||||
import (
|
||||
"os"
|
||||
Generated
Vendored
+4
-1
@@ -1,4 +1,7 @@
|
||||
package plugin
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package cmdrunner
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
Generated
Vendored
+51
@@ -0,0 +1,51 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package grpcmux
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
|
||||
"github.com/hashicorp/yamux"
|
||||
)
|
||||
|
||||
var _ net.Listener = (*blockedClientListener)(nil)
|
||||
|
||||
// blockedClientListener accepts connections for a specific gRPC broker stream
|
||||
// ID on the client (host) side of the connection.
|
||||
type blockedClientListener struct {
|
||||
session *yamux.Session
|
||||
waitCh chan struct{}
|
||||
doneCh <-chan struct{}
|
||||
}
|
||||
|
||||
func newBlockedClientListener(session *yamux.Session, doneCh <-chan struct{}) *blockedClientListener {
|
||||
return &blockedClientListener{
|
||||
waitCh: make(chan struct{}, 1),
|
||||
doneCh: doneCh,
|
||||
session: session,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *blockedClientListener) Accept() (net.Conn, error) {
|
||||
select {
|
||||
case <-b.waitCh:
|
||||
return b.session.Accept()
|
||||
case <-b.doneCh:
|
||||
return nil, io.EOF
|
||||
}
|
||||
}
|
||||
|
||||
func (b *blockedClientListener) Addr() net.Addr {
|
||||
return b.session.Addr()
|
||||
}
|
||||
|
||||
func (b *blockedClientListener) Close() error {
|
||||
// We don't close the session, the client muxer is responsible for that.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (b *blockedClientListener) unblock() {
|
||||
b.waitCh <- struct{}{}
|
||||
}
|
||||
Generated
Vendored
+49
@@ -0,0 +1,49 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package grpcmux
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net"
|
||||
)
|
||||
|
||||
var _ net.Listener = (*blockedServerListener)(nil)
|
||||
|
||||
// blockedServerListener accepts connections for a specific gRPC broker stream
|
||||
// ID on the server (plugin) side of the connection.
|
||||
type blockedServerListener struct {
|
||||
addr net.Addr
|
||||
acceptCh chan acceptResult
|
||||
doneCh <-chan struct{}
|
||||
}
|
||||
|
||||
type acceptResult struct {
|
||||
conn net.Conn
|
||||
err error
|
||||
}
|
||||
|
||||
func newBlockedServerListener(addr net.Addr, doneCh <-chan struct{}) *blockedServerListener {
|
||||
return &blockedServerListener{
|
||||
addr: addr,
|
||||
acceptCh: make(chan acceptResult),
|
||||
doneCh: doneCh,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *blockedServerListener) Accept() (net.Conn, error) {
|
||||
select {
|
||||
case accept := <-b.acceptCh:
|
||||
return accept.conn, accept.err
|
||||
case <-b.doneCh:
|
||||
return nil, io.EOF
|
||||
}
|
||||
}
|
||||
|
||||
func (b *blockedServerListener) Addr() net.Addr {
|
||||
return b.addr
|
||||
}
|
||||
|
||||
func (b *blockedServerListener) Close() error {
|
||||
return nil
|
||||
}
|
||||
+105
@@ -0,0 +1,105 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package grpcmux
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
|
||||
"github.com/hashicorp/go-hclog"
|
||||
"github.com/hashicorp/yamux"
|
||||
)
|
||||
|
||||
var _ GRPCMuxer = (*GRPCClientMuxer)(nil)
|
||||
|
||||
// GRPCClientMuxer implements the client (host) side of the gRPC broker's
|
||||
// GRPCMuxer interface for multiplexing multiple gRPC broker connections over
|
||||
// a single net.Conn.
|
||||
//
|
||||
// The client dials the initial net.Conn eagerly, and creates a yamux.Session
|
||||
// as the implementation for multiplexing any additional connections.
|
||||
//
|
||||
// Each net.Listener returned from Listener will block until the client receives
|
||||
// a knock that matches its gRPC broker stream ID. There is no default listener
|
||||
// on the client, as it is a client for the gRPC broker's control services. (See
|
||||
// GRPCServerMuxer for more details).
|
||||
type GRPCClientMuxer struct {
|
||||
logger hclog.Logger
|
||||
session *yamux.Session
|
||||
|
||||
acceptMutex sync.Mutex
|
||||
acceptListeners map[uint32]*blockedClientListener
|
||||
}
|
||||
|
||||
func NewGRPCClientMuxer(logger hclog.Logger, addr net.Addr) (*GRPCClientMuxer, error) {
|
||||
// Eagerly establish the underlying connection as early as possible.
|
||||
logger.Debug("making new client mux initial connection", "addr", addr)
|
||||
conn, err := net.Dial(addr.Network(), addr.String())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tcpConn, ok := conn.(*net.TCPConn); ok {
|
||||
// Make sure to set keep alive so that the connection doesn't die
|
||||
_ = tcpConn.SetKeepAlive(true)
|
||||
}
|
||||
|
||||
cfg := yamux.DefaultConfig()
|
||||
cfg.Logger = logger.Named("yamux").StandardLogger(&hclog.StandardLoggerOptions{
|
||||
InferLevels: true,
|
||||
})
|
||||
cfg.LogOutput = nil
|
||||
sess, err := yamux.Client(conn, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
logger.Debug("client muxer connected", "addr", addr)
|
||||
m := &GRPCClientMuxer{
|
||||
logger: logger,
|
||||
session: sess,
|
||||
acceptListeners: make(map[uint32]*blockedClientListener),
|
||||
}
|
||||
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m *GRPCClientMuxer) Enabled() bool {
|
||||
return m != nil
|
||||
}
|
||||
|
||||
func (m *GRPCClientMuxer) Listener(id uint32, doneCh <-chan struct{}) (net.Listener, error) {
|
||||
ln := newBlockedClientListener(m.session, doneCh)
|
||||
|
||||
m.acceptMutex.Lock()
|
||||
m.acceptListeners[id] = ln
|
||||
m.acceptMutex.Unlock()
|
||||
|
||||
return ln, nil
|
||||
}
|
||||
|
||||
func (m *GRPCClientMuxer) AcceptKnock(id uint32) error {
|
||||
m.acceptMutex.Lock()
|
||||
defer m.acceptMutex.Unlock()
|
||||
|
||||
ln, ok := m.acceptListeners[id]
|
||||
if !ok {
|
||||
return fmt.Errorf("no listener for id %d", id)
|
||||
}
|
||||
ln.unblock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *GRPCClientMuxer) Dial() (net.Conn, error) {
|
||||
stream, err := m.session.Open()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error dialling new client stream: %w", err)
|
||||
}
|
||||
|
||||
return stream, nil
|
||||
}
|
||||
|
||||
func (m *GRPCClientMuxer) Close() error {
|
||||
return m.session.Close()
|
||||
}
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package grpcmux
|
||||
|
||||
import (
|
||||
"net"
|
||||
)
|
||||
|
||||
// GRPCMuxer enables multiple implementations of net.Listener to accept
|
||||
// connections over a single "main" multiplexed net.Conn, and dial multiple
|
||||
// client connections over the same multiplexed net.Conn.
|
||||
//
|
||||
// The first multiplexed connection is used to serve the gRPC broker's own
|
||||
// control services: plugin.GRPCBroker, plugin.GRPCController, plugin.GRPCStdio.
|
||||
//
|
||||
// Clients must "knock" before dialling, to tell the server side that the
|
||||
// next net.Conn should be accepted onto a specific stream ID. The knock is a
|
||||
// bidirectional streaming message on the plugin.GRPCBroker service.
|
||||
type GRPCMuxer interface {
|
||||
// Enabled determines whether multiplexing should be used. It saves users
|
||||
// of the interface from having to compare an interface with nil, which
|
||||
// is a bit awkward to do correctly.
|
||||
Enabled() bool
|
||||
|
||||
// Listener returns a multiplexed listener that will wait until AcceptKnock
|
||||
// is called with a matching ID before its Accept function returns.
|
||||
Listener(id uint32, doneCh <-chan struct{}) (net.Listener, error)
|
||||
|
||||
// AcceptKnock unblocks the listener with the matching ID, and returns an
|
||||
// error if it hasn't been created yet.
|
||||
AcceptKnock(id uint32) error
|
||||
|
||||
// Dial makes a new multiplexed client connection. To dial a specific ID,
|
||||
// a knock must be sent first.
|
||||
Dial() (net.Conn, error)
|
||||
|
||||
// Close closes connections and releases any resources associated with the
|
||||
// muxer.
|
||||
Close() error
|
||||
}
|
||||
+190
@@ -0,0 +1,190 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package grpcmux
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/hashicorp/go-hclog"
|
||||
"github.com/hashicorp/yamux"
|
||||
)
|
||||
|
||||
var _ GRPCMuxer = (*GRPCServerMuxer)(nil)
|
||||
var _ net.Listener = (*GRPCServerMuxer)(nil)
|
||||
|
||||
// GRPCServerMuxer implements the server (plugin) side of the gRPC broker's
|
||||
// GRPCMuxer interface for multiplexing multiple gRPC broker connections over
|
||||
// a single net.Conn.
|
||||
//
|
||||
// The server side needs a listener to serve the gRPC broker's control services,
|
||||
// which includes the service we will receive knocks on. That means we always
|
||||
// accept the first connection onto a "default" main listener, and if we accept
|
||||
// any further connections without receiving a knock first, they are also given
|
||||
// to the default listener.
|
||||
//
|
||||
// When creating additional multiplexed listeners for specific stream IDs, we
|
||||
// can't control the order in which gRPC servers will call Accept() on each
|
||||
// listener, but we do need to control which gRPC server accepts which connection.
|
||||
// As such, each multiplexed listener blocks waiting on a channel. It will be
|
||||
// unblocked when a knock is received for the matching stream ID.
|
||||
type GRPCServerMuxer struct {
|
||||
addr net.Addr
|
||||
logger hclog.Logger
|
||||
|
||||
sessionErrCh chan error
|
||||
sess *yamux.Session
|
||||
|
||||
knockCh chan uint32
|
||||
|
||||
acceptMutex sync.Mutex
|
||||
acceptChannels map[uint32]chan acceptResult
|
||||
}
|
||||
|
||||
func NewGRPCServerMuxer(logger hclog.Logger, ln net.Listener) *GRPCServerMuxer {
|
||||
m := &GRPCServerMuxer{
|
||||
addr: ln.Addr(),
|
||||
logger: logger,
|
||||
|
||||
sessionErrCh: make(chan error),
|
||||
|
||||
knockCh: make(chan uint32, 1),
|
||||
acceptChannels: make(map[uint32]chan acceptResult),
|
||||
}
|
||||
|
||||
go m.acceptSession(ln)
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
// acceptSessionAndMuxAccept is responsible for establishing the yamux session,
|
||||
// and then kicking off the acceptLoop function.
|
||||
func (m *GRPCServerMuxer) acceptSession(ln net.Listener) {
|
||||
defer close(m.sessionErrCh)
|
||||
|
||||
m.logger.Debug("accepting initial connection", "addr", m.addr)
|
||||
conn, err := ln.Accept()
|
||||
if err != nil {
|
||||
m.sessionErrCh <- err
|
||||
return
|
||||
}
|
||||
|
||||
m.logger.Debug("initial server connection accepted", "addr", m.addr)
|
||||
cfg := yamux.DefaultConfig()
|
||||
cfg.Logger = m.logger.Named("yamux").StandardLogger(&hclog.StandardLoggerOptions{
|
||||
InferLevels: true,
|
||||
})
|
||||
cfg.LogOutput = nil
|
||||
m.sess, err = yamux.Server(conn, cfg)
|
||||
if err != nil {
|
||||
m.sessionErrCh <- err
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
func (m *GRPCServerMuxer) session() (*yamux.Session, error) {
|
||||
select {
|
||||
case err := <-m.sessionErrCh:
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
case <-time.After(5 * time.Second):
|
||||
return nil, errors.New("timed out waiting for connection to be established")
|
||||
}
|
||||
|
||||
// Should never happen.
|
||||
if m.sess == nil {
|
||||
return nil, errors.New("no connection established and no error received")
|
||||
}
|
||||
|
||||
return m.sess, nil
|
||||
}
|
||||
|
||||
// Accept accepts all incoming connections and routes them to the correct
|
||||
// stream ID based on the most recent knock received.
|
||||
func (m *GRPCServerMuxer) Accept() (net.Conn, error) {
|
||||
session, err := m.session()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error establishing yamux session: %w", err)
|
||||
}
|
||||
|
||||
for {
|
||||
conn, acceptErr := session.Accept()
|
||||
|
||||
select {
|
||||
case id := <-m.knockCh:
|
||||
m.acceptMutex.Lock()
|
||||
acceptCh, ok := m.acceptChannels[id]
|
||||
m.acceptMutex.Unlock()
|
||||
|
||||
if !ok {
|
||||
if conn != nil {
|
||||
_ = conn.Close()
|
||||
}
|
||||
return nil, fmt.Errorf("received knock on ID %d that doesn't have a listener", id)
|
||||
}
|
||||
m.logger.Debug("sending conn to brokered listener", "id", id)
|
||||
acceptCh <- acceptResult{
|
||||
conn: conn,
|
||||
err: acceptErr,
|
||||
}
|
||||
default:
|
||||
m.logger.Debug("sending conn to default listener")
|
||||
return conn, acceptErr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (m *GRPCServerMuxer) Addr() net.Addr {
|
||||
return m.addr
|
||||
}
|
||||
|
||||
func (m *GRPCServerMuxer) Close() error {
|
||||
session, err := m.session()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return session.Close()
|
||||
}
|
||||
|
||||
func (m *GRPCServerMuxer) Enabled() bool {
|
||||
return m != nil
|
||||
}
|
||||
|
||||
func (m *GRPCServerMuxer) Listener(id uint32, doneCh <-chan struct{}) (net.Listener, error) {
|
||||
sess, err := m.session()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ln := newBlockedServerListener(sess.Addr(), doneCh)
|
||||
m.acceptMutex.Lock()
|
||||
m.acceptChannels[id] = ln.acceptCh
|
||||
m.acceptMutex.Unlock()
|
||||
|
||||
return ln, nil
|
||||
}
|
||||
|
||||
func (m *GRPCServerMuxer) Dial() (net.Conn, error) {
|
||||
sess, err := m.session()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
stream, err := sess.OpenStream()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("error dialling new server stream: %w", err)
|
||||
}
|
||||
|
||||
return stream, nil
|
||||
}
|
||||
|
||||
func (m *GRPCServerMuxer) AcceptKnock(id uint32) error {
|
||||
m.knockCh <- id
|
||||
return nil
|
||||
}
|
||||
-3
@@ -1,3 +0,0 @@
|
||||
//go:generate protoc -I ./ ./grpc_broker.proto ./grpc_controller.proto ./grpc_stdio.proto --go_out=plugins=grpc:.
|
||||
|
||||
package plugin
|
||||
+222
-161
@@ -1,203 +1,264 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: grpc_broker.proto
|
||||
// versions:
|
||||
// protoc-gen-go v1.31.0
|
||||
// protoc (unknown)
|
||||
// source: internal/plugin/grpc_broker.proto
|
||||
|
||||
package plugin
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
|
||||
import (
|
||||
context "golang.org/x/net/context"
|
||||
grpc "google.golang.org/grpc"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type ConnInfo struct {
|
||||
ServiceId uint32 `protobuf:"varint,1,opt,name=service_id,json=serviceId,proto3" json:"service_id,omitempty"`
|
||||
Network string `protobuf:"bytes,2,opt,name=network,proto3" json:"network,omitempty"`
|
||||
Address string `protobuf:"bytes,3,opt,name=address,proto3" json:"address,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
ServiceId uint32 `protobuf:"varint,1,opt,name=service_id,json=serviceId,proto3" json:"service_id,omitempty"`
|
||||
Network string `protobuf:"bytes,2,opt,name=network,proto3" json:"network,omitempty"`
|
||||
Address string `protobuf:"bytes,3,opt,name=address,proto3" json:"address,omitempty"`
|
||||
Knock *ConnInfo_Knock `protobuf:"bytes,4,opt,name=knock,proto3" json:"knock,omitempty"`
|
||||
}
|
||||
|
||||
func (m *ConnInfo) Reset() { *m = ConnInfo{} }
|
||||
func (m *ConnInfo) String() string { return proto.CompactTextString(m) }
|
||||
func (*ConnInfo) ProtoMessage() {}
|
||||
func (x *ConnInfo) Reset() {
|
||||
*x = ConnInfo{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_internal_plugin_grpc_broker_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *ConnInfo) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*ConnInfo) ProtoMessage() {}
|
||||
|
||||
func (x *ConnInfo) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_internal_plugin_grpc_broker_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use ConnInfo.ProtoReflect.Descriptor instead.
|
||||
func (*ConnInfo) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_grpc_broker_3322b07398605250, []int{0}
|
||||
}
|
||||
func (m *ConnInfo) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_ConnInfo.Unmarshal(m, b)
|
||||
}
|
||||
func (m *ConnInfo) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_ConnInfo.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *ConnInfo) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_ConnInfo.Merge(dst, src)
|
||||
}
|
||||
func (m *ConnInfo) XXX_Size() int {
|
||||
return xxx_messageInfo_ConnInfo.Size(m)
|
||||
}
|
||||
func (m *ConnInfo) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_ConnInfo.DiscardUnknown(m)
|
||||
return file_internal_plugin_grpc_broker_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
var xxx_messageInfo_ConnInfo proto.InternalMessageInfo
|
||||
|
||||
func (m *ConnInfo) GetServiceId() uint32 {
|
||||
if m != nil {
|
||||
return m.ServiceId
|
||||
func (x *ConnInfo) GetServiceId() uint32 {
|
||||
if x != nil {
|
||||
return x.ServiceId
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (m *ConnInfo) GetNetwork() string {
|
||||
if m != nil {
|
||||
return m.Network
|
||||
func (x *ConnInfo) GetNetwork() string {
|
||||
if x != nil {
|
||||
return x.Network
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func (m *ConnInfo) GetAddress() string {
|
||||
if m != nil {
|
||||
return m.Address
|
||||
func (x *ConnInfo) GetAddress() string {
|
||||
if x != nil {
|
||||
return x.Address
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*ConnInfo)(nil), "plugin.ConnInfo")
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// GRPCBrokerClient is the client API for GRPCBroker service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type GRPCBrokerClient interface {
|
||||
StartStream(ctx context.Context, opts ...grpc.CallOption) (GRPCBroker_StartStreamClient, error)
|
||||
}
|
||||
|
||||
type gRPCBrokerClient struct {
|
||||
cc *grpc.ClientConn
|
||||
}
|
||||
|
||||
func NewGRPCBrokerClient(cc *grpc.ClientConn) GRPCBrokerClient {
|
||||
return &gRPCBrokerClient{cc}
|
||||
}
|
||||
|
||||
func (c *gRPCBrokerClient) StartStream(ctx context.Context, opts ...grpc.CallOption) (GRPCBroker_StartStreamClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &_GRPCBroker_serviceDesc.Streams[0], "/plugin.GRPCBroker/StartStream", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func (x *ConnInfo) GetKnock() *ConnInfo_Knock {
|
||||
if x != nil {
|
||||
return x.Knock
|
||||
}
|
||||
x := &gRPCBrokerStartStreamClient{stream}
|
||||
return x, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
type GRPCBroker_StartStreamClient interface {
|
||||
Send(*ConnInfo) error
|
||||
Recv() (*ConnInfo, error)
|
||||
grpc.ClientStream
|
||||
type ConnInfo_Knock struct {
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Knock bool `protobuf:"varint,1,opt,name=knock,proto3" json:"knock,omitempty"`
|
||||
Ack bool `protobuf:"varint,2,opt,name=ack,proto3" json:"ack,omitempty"`
|
||||
Error string `protobuf:"bytes,3,opt,name=error,proto3" json:"error,omitempty"`
|
||||
}
|
||||
|
||||
type gRPCBrokerStartStreamClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *gRPCBrokerStartStreamClient) Send(m *ConnInfo) error {
|
||||
return x.ClientStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func (x *gRPCBrokerStartStreamClient) Recv() (*ConnInfo, error) {
|
||||
m := new(ConnInfo)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
func (x *ConnInfo_Knock) Reset() {
|
||||
*x = ConnInfo_Knock{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_internal_plugin_grpc_broker_proto_msgTypes[1]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// GRPCBrokerServer is the server API for GRPCBroker service.
|
||||
type GRPCBrokerServer interface {
|
||||
StartStream(GRPCBroker_StartStreamServer) error
|
||||
func (x *ConnInfo_Knock) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func RegisterGRPCBrokerServer(s *grpc.Server, srv GRPCBrokerServer) {
|
||||
s.RegisterService(&_GRPCBroker_serviceDesc, srv)
|
||||
}
|
||||
func (*ConnInfo_Knock) ProtoMessage() {}
|
||||
|
||||
func _GRPCBroker_StartStream_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
return srv.(GRPCBrokerServer).StartStream(&gRPCBrokerStartStreamServer{stream})
|
||||
}
|
||||
|
||||
type GRPCBroker_StartStreamServer interface {
|
||||
Send(*ConnInfo) error
|
||||
Recv() (*ConnInfo, error)
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type gRPCBrokerStartStreamServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *gRPCBrokerStartStreamServer) Send(m *ConnInfo) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func (x *gRPCBrokerStartStreamServer) Recv() (*ConnInfo, error) {
|
||||
m := new(ConnInfo)
|
||||
if err := x.ServerStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
func (x *ConnInfo_Knock) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_internal_plugin_grpc_broker_proto_msgTypes[1]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return m, nil
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
var _GRPCBroker_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "plugin.GRPCBroker",
|
||||
HandlerType: (*GRPCBrokerServer)(nil),
|
||||
Methods: []grpc.MethodDesc{},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "StartStream",
|
||||
Handler: _GRPCBroker_StartStream_Handler,
|
||||
ServerStreams: true,
|
||||
ClientStreams: true,
|
||||
// Deprecated: Use ConnInfo_Knock.ProtoReflect.Descriptor instead.
|
||||
func (*ConnInfo_Knock) Descriptor() ([]byte, []int) {
|
||||
return file_internal_plugin_grpc_broker_proto_rawDescGZIP(), []int{0, 0}
|
||||
}
|
||||
|
||||
func (x *ConnInfo_Knock) GetKnock() bool {
|
||||
if x != nil {
|
||||
return x.Knock
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *ConnInfo_Knock) GetAck() bool {
|
||||
if x != nil {
|
||||
return x.Ack
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (x *ConnInfo_Knock) GetError() string {
|
||||
if x != nil {
|
||||
return x.Error
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var File_internal_plugin_grpc_broker_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_internal_plugin_grpc_broker_proto_rawDesc = []byte{
|
||||
0x0a, 0x21, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69,
|
||||
0x6e, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x62, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x2e, 0x70, 0x72,
|
||||
0x6f, 0x74, 0x6f, 0x12, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x22, 0xd2, 0x01, 0x0a, 0x08,
|
||||
0x43, 0x6f, 0x6e, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x1d, 0x0a, 0x0a, 0x73, 0x65, 0x72, 0x76,
|
||||
0x69, 0x63, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x09, 0x73, 0x65,
|
||||
0x72, 0x76, 0x69, 0x63, 0x65, 0x49, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f,
|
||||
0x72, 0x6b, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x74, 0x77, 0x6f, 0x72,
|
||||
0x6b, 0x12, 0x18, 0x0a, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x18, 0x03, 0x20, 0x01,
|
||||
0x28, 0x09, 0x52, 0x07, 0x61, 0x64, 0x64, 0x72, 0x65, 0x73, 0x73, 0x12, 0x2c, 0x0a, 0x05, 0x6b,
|
||||
0x6e, 0x6f, 0x63, 0x6b, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x70, 0x6c, 0x75,
|
||||
0x67, 0x69, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x2e, 0x4b, 0x6e, 0x6f,
|
||||
0x63, 0x6b, 0x52, 0x05, 0x6b, 0x6e, 0x6f, 0x63, 0x6b, 0x1a, 0x45, 0x0a, 0x05, 0x4b, 0x6e, 0x6f,
|
||||
0x63, 0x6b, 0x12, 0x14, 0x0a, 0x05, 0x6b, 0x6e, 0x6f, 0x63, 0x6b, 0x18, 0x01, 0x20, 0x01, 0x28,
|
||||
0x08, 0x52, 0x05, 0x6b, 0x6e, 0x6f, 0x63, 0x6b, 0x12, 0x10, 0x0a, 0x03, 0x61, 0x63, 0x6b, 0x18,
|
||||
0x02, 0x20, 0x01, 0x28, 0x08, 0x52, 0x03, 0x61, 0x63, 0x6b, 0x12, 0x14, 0x0a, 0x05, 0x65, 0x72,
|
||||
0x72, 0x6f, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x65, 0x72, 0x72, 0x6f, 0x72,
|
||||
0x32, 0x43, 0x0a, 0x0a, 0x47, 0x52, 0x50, 0x43, 0x42, 0x72, 0x6f, 0x6b, 0x65, 0x72, 0x12, 0x35,
|
||||
0x0a, 0x0b, 0x53, 0x74, 0x61, 0x72, 0x74, 0x53, 0x74, 0x72, 0x65, 0x61, 0x6d, 0x12, 0x10, 0x2e,
|
||||
0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x49, 0x6e, 0x66, 0x6f, 0x1a,
|
||||
0x10, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x43, 0x6f, 0x6e, 0x6e, 0x49, 0x6e, 0x66,
|
||||
0x6f, 0x28, 0x01, 0x30, 0x01, 0x42, 0x0a, 0x5a, 0x08, 0x2e, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69,
|
||||
0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
var (
|
||||
file_internal_plugin_grpc_broker_proto_rawDescOnce sync.Once
|
||||
file_internal_plugin_grpc_broker_proto_rawDescData = file_internal_plugin_grpc_broker_proto_rawDesc
|
||||
)
|
||||
|
||||
func file_internal_plugin_grpc_broker_proto_rawDescGZIP() []byte {
|
||||
file_internal_plugin_grpc_broker_proto_rawDescOnce.Do(func() {
|
||||
file_internal_plugin_grpc_broker_proto_rawDescData = protoimpl.X.CompressGZIP(file_internal_plugin_grpc_broker_proto_rawDescData)
|
||||
})
|
||||
return file_internal_plugin_grpc_broker_proto_rawDescData
|
||||
}
|
||||
|
||||
var file_internal_plugin_grpc_broker_proto_msgTypes = make([]protoimpl.MessageInfo, 2)
|
||||
var file_internal_plugin_grpc_broker_proto_goTypes = []interface{}{
|
||||
(*ConnInfo)(nil), // 0: plugin.ConnInfo
|
||||
(*ConnInfo_Knock)(nil), // 1: plugin.ConnInfo.Knock
|
||||
}
|
||||
var file_internal_plugin_grpc_broker_proto_depIdxs = []int32{
|
||||
1, // 0: plugin.ConnInfo.knock:type_name -> plugin.ConnInfo.Knock
|
||||
0, // 1: plugin.GRPCBroker.StartStream:input_type -> plugin.ConnInfo
|
||||
0, // 2: plugin.GRPCBroker.StartStream:output_type -> plugin.ConnInfo
|
||||
2, // [2:3] is the sub-list for method output_type
|
||||
1, // [1:2] is the sub-list for method input_type
|
||||
1, // [1:1] is the sub-list for extension type_name
|
||||
1, // [1:1] is the sub-list for extension extendee
|
||||
0, // [0:1] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func init() { file_internal_plugin_grpc_broker_proto_init() }
|
||||
func file_internal_plugin_grpc_broker_proto_init() {
|
||||
if File_internal_plugin_grpc_broker_proto != nil {
|
||||
return
|
||||
}
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_internal_plugin_grpc_broker_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ConnInfo); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
file_internal_plugin_grpc_broker_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*ConnInfo_Knock); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_internal_plugin_grpc_broker_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 2,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
},
|
||||
Metadata: "grpc_broker.proto",
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("grpc_broker.proto", fileDescriptor_grpc_broker_3322b07398605250) }
|
||||
|
||||
var fileDescriptor_grpc_broker_3322b07398605250 = []byte{
|
||||
// 175 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4c, 0x2f, 0x2a, 0x48,
|
||||
0x8e, 0x4f, 0x2a, 0xca, 0xcf, 0x4e, 0x2d, 0xd2, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x2b,
|
||||
0xc8, 0x29, 0x4d, 0xcf, 0xcc, 0x53, 0x8a, 0xe5, 0xe2, 0x70, 0xce, 0xcf, 0xcb, 0xf3, 0xcc, 0x4b,
|
||||
0xcb, 0x17, 0x92, 0xe5, 0xe2, 0x2a, 0x4e, 0x2d, 0x2a, 0xcb, 0x4c, 0x4e, 0x8d, 0xcf, 0x4c, 0x91,
|
||||
0x60, 0x54, 0x60, 0xd4, 0xe0, 0x0d, 0xe2, 0x84, 0x8a, 0x78, 0xa6, 0x08, 0x49, 0x70, 0xb1, 0xe7,
|
||||
0xa5, 0x96, 0x94, 0xe7, 0x17, 0x65, 0x4b, 0x30, 0x29, 0x30, 0x6a, 0x70, 0x06, 0xc1, 0xb8, 0x20,
|
||||
0x99, 0xc4, 0x94, 0x94, 0xa2, 0xd4, 0xe2, 0x62, 0x09, 0x66, 0x88, 0x0c, 0x94, 0x6b, 0xe4, 0xcc,
|
||||
0xc5, 0xe5, 0x1e, 0x14, 0xe0, 0xec, 0x04, 0xb6, 0x5a, 0xc8, 0x94, 0x8b, 0x3b, 0xb8, 0x24, 0xb1,
|
||||
0xa8, 0x24, 0xb8, 0xa4, 0x28, 0x35, 0x31, 0x57, 0x48, 0x40, 0x0f, 0xe2, 0x08, 0x3d, 0x98, 0x0b,
|
||||
0xa4, 0x30, 0x44, 0x34, 0x18, 0x0d, 0x18, 0x9d, 0x38, 0xa2, 0xa0, 0xae, 0x4d, 0x62, 0x03, 0x3b,
|
||||
0xde, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x10, 0x15, 0x39, 0x47, 0xd1, 0x00, 0x00, 0x00,
|
||||
GoTypes: file_internal_plugin_grpc_broker_proto_goTypes,
|
||||
DependencyIndexes: file_internal_plugin_grpc_broker_proto_depIdxs,
|
||||
MessageInfos: file_internal_plugin_grpc_broker_proto_msgTypes,
|
||||
}.Build()
|
||||
File_internal_plugin_grpc_broker_proto = out.File
|
||||
file_internal_plugin_grpc_broker_proto_rawDesc = nil
|
||||
file_internal_plugin_grpc_broker_proto_goTypes = nil
|
||||
file_internal_plugin_grpc_broker_proto_depIdxs = nil
|
||||
}
|
||||
|
||||
+10
-1
@@ -1,11 +1,20 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
syntax = "proto3";
|
||||
package plugin;
|
||||
option go_package = "plugin";
|
||||
option go_package = "./plugin";
|
||||
|
||||
message ConnInfo {
|
||||
uint32 service_id = 1;
|
||||
string network = 2;
|
||||
string address = 3;
|
||||
message Knock {
|
||||
bool knock = 1;
|
||||
bool ack = 2;
|
||||
string error = 3;
|
||||
}
|
||||
Knock knock = 4;
|
||||
}
|
||||
|
||||
service GRPCBroker {
|
||||
|
||||
+142
@@ -0,0 +1,142 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.3.0
|
||||
// - protoc (unknown)
|
||||
// source: internal/plugin/grpc_broker.proto
|
||||
|
||||
package plugin
|
||||
|
||||
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.32.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
const (
|
||||
GRPCBroker_StartStream_FullMethodName = "/plugin.GRPCBroker/StartStream"
|
||||
)
|
||||
|
||||
// GRPCBrokerClient is the client API for GRPCBroker 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.
|
||||
type GRPCBrokerClient interface {
|
||||
StartStream(ctx context.Context, opts ...grpc.CallOption) (GRPCBroker_StartStreamClient, error)
|
||||
}
|
||||
|
||||
type gRPCBrokerClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewGRPCBrokerClient(cc grpc.ClientConnInterface) GRPCBrokerClient {
|
||||
return &gRPCBrokerClient{cc}
|
||||
}
|
||||
|
||||
func (c *gRPCBrokerClient) StartStream(ctx context.Context, opts ...grpc.CallOption) (GRPCBroker_StartStreamClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &GRPCBroker_ServiceDesc.Streams[0], GRPCBroker_StartStream_FullMethodName, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &gRPCBrokerStartStreamClient{stream}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type GRPCBroker_StartStreamClient interface {
|
||||
Send(*ConnInfo) error
|
||||
Recv() (*ConnInfo, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type gRPCBrokerStartStreamClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *gRPCBrokerStartStreamClient) Send(m *ConnInfo) error {
|
||||
return x.ClientStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func (x *gRPCBrokerStartStreamClient) Recv() (*ConnInfo, error) {
|
||||
m := new(ConnInfo)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// GRPCBrokerServer is the server API for GRPCBroker service.
|
||||
// All implementations should embed UnimplementedGRPCBrokerServer
|
||||
// for forward compatibility
|
||||
type GRPCBrokerServer interface {
|
||||
StartStream(GRPCBroker_StartStreamServer) error
|
||||
}
|
||||
|
||||
// UnimplementedGRPCBrokerServer should be embedded to have forward compatible implementations.
|
||||
type UnimplementedGRPCBrokerServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedGRPCBrokerServer) StartStream(GRPCBroker_StartStreamServer) error {
|
||||
return status.Errorf(codes.Unimplemented, "method StartStream not implemented")
|
||||
}
|
||||
|
||||
// UnsafeGRPCBrokerServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to GRPCBrokerServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeGRPCBrokerServer interface {
|
||||
mustEmbedUnimplementedGRPCBrokerServer()
|
||||
}
|
||||
|
||||
func RegisterGRPCBrokerServer(s grpc.ServiceRegistrar, srv GRPCBrokerServer) {
|
||||
s.RegisterService(&GRPCBroker_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _GRPCBroker_StartStream_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
return srv.(GRPCBrokerServer).StartStream(&gRPCBrokerStartStreamServer{stream})
|
||||
}
|
||||
|
||||
type GRPCBroker_StartStreamServer interface {
|
||||
Send(*ConnInfo) error
|
||||
Recv() (*ConnInfo, error)
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type gRPCBrokerStartStreamServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *gRPCBrokerStartStreamServer) Send(m *ConnInfo) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
func (x *gRPCBrokerStartStreamServer) Recv() (*ConnInfo, error) {
|
||||
m := new(ConnInfo)
|
||||
if err := x.ServerStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// GRPCBroker_ServiceDesc is the grpc.ServiceDesc for GRPCBroker service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var GRPCBroker_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "plugin.GRPCBroker",
|
||||
HandlerType: (*GRPCBrokerServer)(nil),
|
||||
Methods: []grpc.MethodDesc{},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "StartStream",
|
||||
Handler: _GRPCBroker_StartStream_Handler,
|
||||
ServerStreams: true,
|
||||
ClientStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "internal/plugin/grpc_broker.proto",
|
||||
}
|
||||
+115
-119
@@ -1,145 +1,141 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: grpc_controller.proto
|
||||
// versions:
|
||||
// protoc-gen-go v1.31.0
|
||||
// protoc (unknown)
|
||||
// source: internal/plugin/grpc_controller.proto
|
||||
|
||||
package plugin
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
|
||||
import (
|
||||
context "golang.org/x/net/context"
|
||||
grpc "google.golang.org/grpc"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type Empty struct {
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
}
|
||||
|
||||
func (m *Empty) Reset() { *m = Empty{} }
|
||||
func (m *Empty) String() string { return proto.CompactTextString(m) }
|
||||
func (*Empty) ProtoMessage() {}
|
||||
func (x *Empty) Reset() {
|
||||
*x = Empty{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_internal_plugin_grpc_controller_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Empty) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*Empty) ProtoMessage() {}
|
||||
|
||||
func (x *Empty) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_internal_plugin_grpc_controller_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use Empty.ProtoReflect.Descriptor instead.
|
||||
func (*Empty) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_grpc_controller_08f8296ef6d80436, []int{0}
|
||||
}
|
||||
func (m *Empty) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_Empty.Unmarshal(m, b)
|
||||
}
|
||||
func (m *Empty) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_Empty.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *Empty) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_Empty.Merge(dst, src)
|
||||
}
|
||||
func (m *Empty) XXX_Size() int {
|
||||
return xxx_messageInfo_Empty.Size(m)
|
||||
}
|
||||
func (m *Empty) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_Empty.DiscardUnknown(m)
|
||||
return file_internal_plugin_grpc_controller_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
var xxx_messageInfo_Empty proto.InternalMessageInfo
|
||||
var File_internal_plugin_grpc_controller_proto protoreflect.FileDescriptor
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*Empty)(nil), "plugin.Empty")
|
||||
var file_internal_plugin_grpc_controller_proto_rawDesc = []byte{
|
||||
0x0a, 0x25, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69,
|
||||
0x6e, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65,
|
||||
0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x22,
|
||||
0x07, 0x0a, 0x05, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x32, 0x3a, 0x0a, 0x0e, 0x47, 0x52, 0x50, 0x43,
|
||||
0x43, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x12, 0x28, 0x0a, 0x08, 0x53, 0x68,
|
||||
0x75, 0x74, 0x64, 0x6f, 0x77, 0x6e, 0x12, 0x0d, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e,
|
||||
0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x0d, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x45,
|
||||
0x6d, 0x70, 0x74, 0x79, 0x42, 0x0a, 0x5a, 0x08, 0x2e, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e,
|
||||
0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
var (
|
||||
file_internal_plugin_grpc_controller_proto_rawDescOnce sync.Once
|
||||
file_internal_plugin_grpc_controller_proto_rawDescData = file_internal_plugin_grpc_controller_proto_rawDesc
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// GRPCControllerClient is the client API for GRPCController service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type GRPCControllerClient interface {
|
||||
Shutdown(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error)
|
||||
func file_internal_plugin_grpc_controller_proto_rawDescGZIP() []byte {
|
||||
file_internal_plugin_grpc_controller_proto_rawDescOnce.Do(func() {
|
||||
file_internal_plugin_grpc_controller_proto_rawDescData = protoimpl.X.CompressGZIP(file_internal_plugin_grpc_controller_proto_rawDescData)
|
||||
})
|
||||
return file_internal_plugin_grpc_controller_proto_rawDescData
|
||||
}
|
||||
|
||||
type gRPCControllerClient struct {
|
||||
cc *grpc.ClientConn
|
||||
var file_internal_plugin_grpc_controller_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||
var file_internal_plugin_grpc_controller_proto_goTypes = []interface{}{
|
||||
(*Empty)(nil), // 0: plugin.Empty
|
||||
}
|
||||
var file_internal_plugin_grpc_controller_proto_depIdxs = []int32{
|
||||
0, // 0: plugin.GRPCController.Shutdown:input_type -> plugin.Empty
|
||||
0, // 1: plugin.GRPCController.Shutdown:output_type -> plugin.Empty
|
||||
1, // [1:2] is the sub-list for method output_type
|
||||
0, // [0:1] is the sub-list for method input_type
|
||||
0, // [0:0] is the sub-list for extension type_name
|
||||
0, // [0:0] is the sub-list for extension extendee
|
||||
0, // [0:0] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func NewGRPCControllerClient(cc *grpc.ClientConn) GRPCControllerClient {
|
||||
return &gRPCControllerClient{cc}
|
||||
}
|
||||
|
||||
func (c *gRPCControllerClient) Shutdown(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) {
|
||||
out := new(Empty)
|
||||
err := c.cc.Invoke(ctx, "/plugin.GRPCController/Shutdown", in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func init() { file_internal_plugin_grpc_controller_proto_init() }
|
||||
func file_internal_plugin_grpc_controller_proto_init() {
|
||||
if File_internal_plugin_grpc_controller_proto != nil {
|
||||
return
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// GRPCControllerServer is the server API for GRPCController service.
|
||||
type GRPCControllerServer interface {
|
||||
Shutdown(context.Context, *Empty) (*Empty, error)
|
||||
}
|
||||
|
||||
func RegisterGRPCControllerServer(s *grpc.Server, srv GRPCControllerServer) {
|
||||
s.RegisterService(&_GRPCController_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _GRPCController_Shutdown_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Empty)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_internal_plugin_grpc_controller_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*Empty); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(GRPCControllerServer).Shutdown(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: "/plugin.GRPCController/Shutdown",
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(GRPCControllerServer).Shutdown(ctx, req.(*Empty))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
var _GRPCController_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "plugin.GRPCController",
|
||||
HandlerType: (*GRPCControllerServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Shutdown",
|
||||
Handler: _GRPCController_Shutdown_Handler,
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_internal_plugin_grpc_controller_proto_rawDesc,
|
||||
NumEnums: 0,
|
||||
NumMessages: 1,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "grpc_controller.proto",
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterFile("grpc_controller.proto", fileDescriptor_grpc_controller_08f8296ef6d80436)
|
||||
}
|
||||
|
||||
var fileDescriptor_grpc_controller_08f8296ef6d80436 = []byte{
|
||||
// 108 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x4d, 0x2f, 0x2a, 0x48,
|
||||
0x8e, 0x4f, 0xce, 0xcf, 0x2b, 0x29, 0xca, 0xcf, 0xc9, 0x49, 0x2d, 0xd2, 0x2b, 0x28, 0xca, 0x2f,
|
||||
0xc9, 0x17, 0x62, 0x2b, 0xc8, 0x29, 0x4d, 0xcf, 0xcc, 0x53, 0x62, 0xe7, 0x62, 0x75, 0xcd, 0x2d,
|
||||
0x28, 0xa9, 0x34, 0xb2, 0xe2, 0xe2, 0x73, 0x0f, 0x0a, 0x70, 0x76, 0x86, 0x2b, 0x14, 0xd2, 0xe0,
|
||||
0xe2, 0x08, 0xce, 0x28, 0x2d, 0x49, 0xc9, 0x2f, 0xcf, 0x13, 0xe2, 0xd5, 0x83, 0xa8, 0xd7, 0x03,
|
||||
0x2b, 0x96, 0x42, 0xe5, 0x3a, 0x71, 0x44, 0x41, 0x8d, 0x4b, 0x62, 0x03, 0x9b, 0x6e, 0x0c, 0x08,
|
||||
0x00, 0x00, 0xff, 0xff, 0xab, 0x7c, 0x27, 0xe5, 0x76, 0x00, 0x00, 0x00,
|
||||
GoTypes: file_internal_plugin_grpc_controller_proto_goTypes,
|
||||
DependencyIndexes: file_internal_plugin_grpc_controller_proto_depIdxs,
|
||||
MessageInfos: file_internal_plugin_grpc_controller_proto_msgTypes,
|
||||
}.Build()
|
||||
File_internal_plugin_grpc_controller_proto = out.File
|
||||
file_internal_plugin_grpc_controller_proto_rawDesc = nil
|
||||
file_internal_plugin_grpc_controller_proto_goTypes = nil
|
||||
file_internal_plugin_grpc_controller_proto_depIdxs = nil
|
||||
}
|
||||
|
||||
+4
-1
@@ -1,6 +1,9 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
syntax = "proto3";
|
||||
package plugin;
|
||||
option go_package = "plugin";
|
||||
option go_package = "./plugin";
|
||||
|
||||
message Empty {
|
||||
}
|
||||
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.3.0
|
||||
// - protoc (unknown)
|
||||
// source: internal/plugin/grpc_controller.proto
|
||||
|
||||
package plugin
|
||||
|
||||
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.32.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
const (
|
||||
GRPCController_Shutdown_FullMethodName = "/plugin.GRPCController/Shutdown"
|
||||
)
|
||||
|
||||
// GRPCControllerClient is the client API for GRPCController 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.
|
||||
type GRPCControllerClient interface {
|
||||
Shutdown(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error)
|
||||
}
|
||||
|
||||
type gRPCControllerClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewGRPCControllerClient(cc grpc.ClientConnInterface) GRPCControllerClient {
|
||||
return &gRPCControllerClient{cc}
|
||||
}
|
||||
|
||||
func (c *gRPCControllerClient) Shutdown(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Empty, error) {
|
||||
out := new(Empty)
|
||||
err := c.cc.Invoke(ctx, GRPCController_Shutdown_FullMethodName, in, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// GRPCControllerServer is the server API for GRPCController service.
|
||||
// All implementations should embed UnimplementedGRPCControllerServer
|
||||
// for forward compatibility
|
||||
type GRPCControllerServer interface {
|
||||
Shutdown(context.Context, *Empty) (*Empty, error)
|
||||
}
|
||||
|
||||
// UnimplementedGRPCControllerServer should be embedded to have forward compatible implementations.
|
||||
type UnimplementedGRPCControllerServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedGRPCControllerServer) Shutdown(context.Context, *Empty) (*Empty, error) {
|
||||
return nil, status.Errorf(codes.Unimplemented, "method Shutdown not implemented")
|
||||
}
|
||||
|
||||
// UnsafeGRPCControllerServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to GRPCControllerServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeGRPCControllerServer interface {
|
||||
mustEmbedUnimplementedGRPCControllerServer()
|
||||
}
|
||||
|
||||
func RegisterGRPCControllerServer(s grpc.ServiceRegistrar, srv GRPCControllerServer) {
|
||||
s.RegisterService(&GRPCController_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _GRPCController_Shutdown_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
|
||||
in := new(Empty)
|
||||
if err := dec(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if interceptor == nil {
|
||||
return srv.(GRPCControllerServer).Shutdown(ctx, in)
|
||||
}
|
||||
info := &grpc.UnaryServerInfo{
|
||||
Server: srv,
|
||||
FullMethod: GRPCController_Shutdown_FullMethodName,
|
||||
}
|
||||
handler := func(ctx context.Context, req interface{}) (interface{}, error) {
|
||||
return srv.(GRPCControllerServer).Shutdown(ctx, req.(*Empty))
|
||||
}
|
||||
return interceptor(ctx, in, info, handler)
|
||||
}
|
||||
|
||||
// GRPCController_ServiceDesc is the grpc.ServiceDesc for GRPCController service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var GRPCController_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "plugin.GRPCController",
|
||||
HandlerType: (*GRPCControllerServer)(nil),
|
||||
Methods: []grpc.MethodDesc{
|
||||
{
|
||||
MethodName: "Shutdown",
|
||||
Handler: _GRPCController_Shutdown_Handler,
|
||||
},
|
||||
},
|
||||
Streams: []grpc.StreamDesc{},
|
||||
Metadata: "internal/plugin/grpc_controller.proto",
|
||||
}
|
||||
+175
-183
@@ -1,28 +1,28 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: grpc_stdio.proto
|
||||
// versions:
|
||||
// protoc-gen-go v1.31.0
|
||||
// protoc (unknown)
|
||||
// source: internal/plugin/grpc_stdio.proto
|
||||
|
||||
package plugin
|
||||
|
||||
import proto "github.com/golang/protobuf/proto"
|
||||
import fmt "fmt"
|
||||
import math "math"
|
||||
import empty "github.com/golang/protobuf/ptypes/empty"
|
||||
|
||||
import (
|
||||
context "golang.org/x/net/context"
|
||||
grpc "google.golang.org/grpc"
|
||||
protoreflect "google.golang.org/protobuf/reflect/protoreflect"
|
||||
protoimpl "google.golang.org/protobuf/runtime/protoimpl"
|
||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||
reflect "reflect"
|
||||
sync "sync"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = proto.Marshal
|
||||
var _ = fmt.Errorf
|
||||
var _ = math.Inf
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the proto package it is being compiled against.
|
||||
// A compilation error at this line likely means your copy of the
|
||||
// proto package needs to be updated.
|
||||
const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package
|
||||
const (
|
||||
// Verify that this generated code is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)
|
||||
// Verify that runtime/protoimpl is sufficiently up-to-date.
|
||||
_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)
|
||||
)
|
||||
|
||||
type StdioData_Channel int32
|
||||
|
||||
@@ -32,202 +32,194 @@ const (
|
||||
StdioData_STDERR StdioData_Channel = 2
|
||||
)
|
||||
|
||||
var StdioData_Channel_name = map[int32]string{
|
||||
0: "INVALID",
|
||||
1: "STDOUT",
|
||||
2: "STDERR",
|
||||
}
|
||||
var StdioData_Channel_value = map[string]int32{
|
||||
"INVALID": 0,
|
||||
"STDOUT": 1,
|
||||
"STDERR": 2,
|
||||
// Enum value maps for StdioData_Channel.
|
||||
var (
|
||||
StdioData_Channel_name = map[int32]string{
|
||||
0: "INVALID",
|
||||
1: "STDOUT",
|
||||
2: "STDERR",
|
||||
}
|
||||
StdioData_Channel_value = map[string]int32{
|
||||
"INVALID": 0,
|
||||
"STDOUT": 1,
|
||||
"STDERR": 2,
|
||||
}
|
||||
)
|
||||
|
||||
func (x StdioData_Channel) Enum() *StdioData_Channel {
|
||||
p := new(StdioData_Channel)
|
||||
*p = x
|
||||
return p
|
||||
}
|
||||
|
||||
func (x StdioData_Channel) String() string {
|
||||
return proto.EnumName(StdioData_Channel_name, int32(x))
|
||||
return protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))
|
||||
}
|
||||
|
||||
func (StdioData_Channel) Descriptor() protoreflect.EnumDescriptor {
|
||||
return file_internal_plugin_grpc_stdio_proto_enumTypes[0].Descriptor()
|
||||
}
|
||||
|
||||
func (StdioData_Channel) Type() protoreflect.EnumType {
|
||||
return &file_internal_plugin_grpc_stdio_proto_enumTypes[0]
|
||||
}
|
||||
|
||||
func (x StdioData_Channel) Number() protoreflect.EnumNumber {
|
||||
return protoreflect.EnumNumber(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use StdioData_Channel.Descriptor instead.
|
||||
func (StdioData_Channel) EnumDescriptor() ([]byte, []int) {
|
||||
return fileDescriptor_grpc_stdio_db2934322ca63bd5, []int{0, 0}
|
||||
return file_internal_plugin_grpc_stdio_proto_rawDescGZIP(), []int{0, 0}
|
||||
}
|
||||
|
||||
// StdioData is a single chunk of stdout or stderr data that is streamed
|
||||
// from GRPCStdio.
|
||||
type StdioData struct {
|
||||
Channel StdioData_Channel `protobuf:"varint,1,opt,name=channel,proto3,enum=plugin.StdioData_Channel" json:"channel,omitempty"`
|
||||
Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
|
||||
XXX_NoUnkeyedLiteral struct{} `json:"-"`
|
||||
XXX_unrecognized []byte `json:"-"`
|
||||
XXX_sizecache int32 `json:"-"`
|
||||
state protoimpl.MessageState
|
||||
sizeCache protoimpl.SizeCache
|
||||
unknownFields protoimpl.UnknownFields
|
||||
|
||||
Channel StdioData_Channel `protobuf:"varint,1,opt,name=channel,proto3,enum=plugin.StdioData_Channel" json:"channel,omitempty"`
|
||||
Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"`
|
||||
}
|
||||
|
||||
func (m *StdioData) Reset() { *m = StdioData{} }
|
||||
func (m *StdioData) String() string { return proto.CompactTextString(m) }
|
||||
func (*StdioData) ProtoMessage() {}
|
||||
func (x *StdioData) Reset() {
|
||||
*x = StdioData{}
|
||||
if protoimpl.UnsafeEnabled {
|
||||
mi := &file_internal_plugin_grpc_stdio_proto_msgTypes[0]
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *StdioData) String() string {
|
||||
return protoimpl.X.MessageStringOf(x)
|
||||
}
|
||||
|
||||
func (*StdioData) ProtoMessage() {}
|
||||
|
||||
func (x *StdioData) ProtoReflect() protoreflect.Message {
|
||||
mi := &file_internal_plugin_grpc_stdio_proto_msgTypes[0]
|
||||
if protoimpl.UnsafeEnabled && x != nil {
|
||||
ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
|
||||
if ms.LoadMessageInfo() == nil {
|
||||
ms.StoreMessageInfo(mi)
|
||||
}
|
||||
return ms
|
||||
}
|
||||
return mi.MessageOf(x)
|
||||
}
|
||||
|
||||
// Deprecated: Use StdioData.ProtoReflect.Descriptor instead.
|
||||
func (*StdioData) Descriptor() ([]byte, []int) {
|
||||
return fileDescriptor_grpc_stdio_db2934322ca63bd5, []int{0}
|
||||
}
|
||||
func (m *StdioData) XXX_Unmarshal(b []byte) error {
|
||||
return xxx_messageInfo_StdioData.Unmarshal(m, b)
|
||||
}
|
||||
func (m *StdioData) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
|
||||
return xxx_messageInfo_StdioData.Marshal(b, m, deterministic)
|
||||
}
|
||||
func (dst *StdioData) XXX_Merge(src proto.Message) {
|
||||
xxx_messageInfo_StdioData.Merge(dst, src)
|
||||
}
|
||||
func (m *StdioData) XXX_Size() int {
|
||||
return xxx_messageInfo_StdioData.Size(m)
|
||||
}
|
||||
func (m *StdioData) XXX_DiscardUnknown() {
|
||||
xxx_messageInfo_StdioData.DiscardUnknown(m)
|
||||
return file_internal_plugin_grpc_stdio_proto_rawDescGZIP(), []int{0}
|
||||
}
|
||||
|
||||
var xxx_messageInfo_StdioData proto.InternalMessageInfo
|
||||
|
||||
func (m *StdioData) GetChannel() StdioData_Channel {
|
||||
if m != nil {
|
||||
return m.Channel
|
||||
func (x *StdioData) GetChannel() StdioData_Channel {
|
||||
if x != nil {
|
||||
return x.Channel
|
||||
}
|
||||
return StdioData_INVALID
|
||||
}
|
||||
|
||||
func (m *StdioData) GetData() []byte {
|
||||
if m != nil {
|
||||
return m.Data
|
||||
func (x *StdioData) GetData() []byte {
|
||||
if x != nil {
|
||||
return x.Data
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
proto.RegisterType((*StdioData)(nil), "plugin.StdioData")
|
||||
proto.RegisterEnum("plugin.StdioData_Channel", StdioData_Channel_name, StdioData_Channel_value)
|
||||
var File_internal_plugin_grpc_stdio_proto protoreflect.FileDescriptor
|
||||
|
||||
var file_internal_plugin_grpc_stdio_proto_rawDesc = []byte{
|
||||
0x0a, 0x20, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x6c, 0x75, 0x67, 0x69,
|
||||
0x6e, 0x2f, 0x67, 0x72, 0x70, 0x63, 0x5f, 0x73, 0x74, 0x64, 0x69, 0x6f, 0x2e, 0x70, 0x72, 0x6f,
|
||||
0x74, 0x6f, 0x12, 0x06, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x1a, 0x1b, 0x67, 0x6f, 0x6f, 0x67,
|
||||
0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74,
|
||||
0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x84, 0x01, 0x0a, 0x09, 0x53, 0x74, 0x64, 0x69,
|
||||
0x6f, 0x44, 0x61, 0x74, 0x61, 0x12, 0x33, 0x0a, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c,
|
||||
0x18, 0x01, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x19, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e,
|
||||
0x53, 0x74, 0x64, 0x69, 0x6f, 0x44, 0x61, 0x74, 0x61, 0x2e, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65,
|
||||
0x6c, 0x52, 0x07, 0x63, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x12, 0x0a, 0x04, 0x64, 0x61,
|
||||
0x74, 0x61, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x04, 0x64, 0x61, 0x74, 0x61, 0x22, 0x2e,
|
||||
0x0a, 0x07, 0x43, 0x68, 0x61, 0x6e, 0x6e, 0x65, 0x6c, 0x12, 0x0b, 0x0a, 0x07, 0x49, 0x4e, 0x56,
|
||||
0x41, 0x4c, 0x49, 0x44, 0x10, 0x00, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x54, 0x44, 0x4f, 0x55, 0x54,
|
||||
0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x53, 0x54, 0x44, 0x45, 0x52, 0x52, 0x10, 0x02, 0x32, 0x47,
|
||||
0x0a, 0x09, 0x47, 0x52, 0x50, 0x43, 0x53, 0x74, 0x64, 0x69, 0x6f, 0x12, 0x3a, 0x0a, 0x0b, 0x53,
|
||||
0x74, 0x72, 0x65, 0x61, 0x6d, 0x53, 0x74, 0x64, 0x69, 0x6f, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f,
|
||||
0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70,
|
||||
0x74, 0x79, 0x1a, 0x11, 0x2e, 0x70, 0x6c, 0x75, 0x67, 0x69, 0x6e, 0x2e, 0x53, 0x74, 0x64, 0x69,
|
||||
0x6f, 0x44, 0x61, 0x74, 0x61, 0x30, 0x01, 0x42, 0x0a, 0x5a, 0x08, 0x2e, 0x2f, 0x70, 0x6c, 0x75,
|
||||
0x67, 0x69, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ grpc.ClientConn
|
||||
var (
|
||||
file_internal_plugin_grpc_stdio_proto_rawDescOnce sync.Once
|
||||
file_internal_plugin_grpc_stdio_proto_rawDescData = file_internal_plugin_grpc_stdio_proto_rawDesc
|
||||
)
|
||||
|
||||
// This is a compile-time assertion to ensure that this generated file
|
||||
// is compatible with the grpc package it is being compiled against.
|
||||
const _ = grpc.SupportPackageIsVersion4
|
||||
|
||||
// GRPCStdioClient is the client API for GRPCStdio service.
|
||||
//
|
||||
// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
|
||||
type GRPCStdioClient interface {
|
||||
// StreamStdio returns a stream that contains all the stdout/stderr.
|
||||
// This RPC endpoint must only be called ONCE. Once stdio data is consumed
|
||||
// it is not sent again.
|
||||
//
|
||||
// Callers should connect early to prevent blocking on the plugin process.
|
||||
StreamStdio(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (GRPCStdio_StreamStdioClient, error)
|
||||
func file_internal_plugin_grpc_stdio_proto_rawDescGZIP() []byte {
|
||||
file_internal_plugin_grpc_stdio_proto_rawDescOnce.Do(func() {
|
||||
file_internal_plugin_grpc_stdio_proto_rawDescData = protoimpl.X.CompressGZIP(file_internal_plugin_grpc_stdio_proto_rawDescData)
|
||||
})
|
||||
return file_internal_plugin_grpc_stdio_proto_rawDescData
|
||||
}
|
||||
|
||||
type gRPCStdioClient struct {
|
||||
cc *grpc.ClientConn
|
||||
var file_internal_plugin_grpc_stdio_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
|
||||
var file_internal_plugin_grpc_stdio_proto_msgTypes = make([]protoimpl.MessageInfo, 1)
|
||||
var file_internal_plugin_grpc_stdio_proto_goTypes = []interface{}{
|
||||
(StdioData_Channel)(0), // 0: plugin.StdioData.Channel
|
||||
(*StdioData)(nil), // 1: plugin.StdioData
|
||||
(*emptypb.Empty)(nil), // 2: google.protobuf.Empty
|
||||
}
|
||||
var file_internal_plugin_grpc_stdio_proto_depIdxs = []int32{
|
||||
0, // 0: plugin.StdioData.channel:type_name -> plugin.StdioData.Channel
|
||||
2, // 1: plugin.GRPCStdio.StreamStdio:input_type -> google.protobuf.Empty
|
||||
1, // 2: plugin.GRPCStdio.StreamStdio:output_type -> plugin.StdioData
|
||||
2, // [2:3] is the sub-list for method output_type
|
||||
1, // [1:2] is the sub-list for method input_type
|
||||
1, // [1:1] is the sub-list for extension type_name
|
||||
1, // [1:1] is the sub-list for extension extendee
|
||||
0, // [0:1] is the sub-list for field type_name
|
||||
}
|
||||
|
||||
func NewGRPCStdioClient(cc *grpc.ClientConn) GRPCStdioClient {
|
||||
return &gRPCStdioClient{cc}
|
||||
}
|
||||
|
||||
func (c *gRPCStdioClient) StreamStdio(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (GRPCStdio_StreamStdioClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &_GRPCStdio_serviceDesc.Streams[0], "/plugin.GRPCStdio/StreamStdio", opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
func init() { file_internal_plugin_grpc_stdio_proto_init() }
|
||||
func file_internal_plugin_grpc_stdio_proto_init() {
|
||||
if File_internal_plugin_grpc_stdio_proto != nil {
|
||||
return
|
||||
}
|
||||
x := &gRPCStdioStreamStdioClient{stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
if !protoimpl.UnsafeEnabled {
|
||||
file_internal_plugin_grpc_stdio_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {
|
||||
switch v := v.(*StdioData); i {
|
||||
case 0:
|
||||
return &v.state
|
||||
case 1:
|
||||
return &v.sizeCache
|
||||
case 2:
|
||||
return &v.unknownFields
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type GRPCStdio_StreamStdioClient interface {
|
||||
Recv() (*StdioData, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type gRPCStdioStreamStdioClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *gRPCStdioStreamStdioClient) Recv() (*StdioData, error) {
|
||||
m := new(StdioData)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// GRPCStdioServer is the server API for GRPCStdio service.
|
||||
type GRPCStdioServer interface {
|
||||
// StreamStdio returns a stream that contains all the stdout/stderr.
|
||||
// This RPC endpoint must only be called ONCE. Once stdio data is consumed
|
||||
// it is not sent again.
|
||||
//
|
||||
// Callers should connect early to prevent blocking on the plugin process.
|
||||
StreamStdio(*empty.Empty, GRPCStdio_StreamStdioServer) error
|
||||
}
|
||||
|
||||
func RegisterGRPCStdioServer(s *grpc.Server, srv GRPCStdioServer) {
|
||||
s.RegisterService(&_GRPCStdio_serviceDesc, srv)
|
||||
}
|
||||
|
||||
func _GRPCStdio_StreamStdio_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(empty.Empty)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(GRPCStdioServer).StreamStdio(m, &gRPCStdioStreamStdioServer{stream})
|
||||
}
|
||||
|
||||
type GRPCStdio_StreamStdioServer interface {
|
||||
Send(*StdioData) error
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type gRPCStdioStreamStdioServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *gRPCStdioStreamStdioServer) Send(m *StdioData) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
var _GRPCStdio_serviceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "plugin.GRPCStdio",
|
||||
HandlerType: (*GRPCStdioServer)(nil),
|
||||
Methods: []grpc.MethodDesc{},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "StreamStdio",
|
||||
Handler: _GRPCStdio_StreamStdio_Handler,
|
||||
ServerStreams: true,
|
||||
type x struct{}
|
||||
out := protoimpl.TypeBuilder{
|
||||
File: protoimpl.DescBuilder{
|
||||
GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
|
||||
RawDescriptor: file_internal_plugin_grpc_stdio_proto_rawDesc,
|
||||
NumEnums: 1,
|
||||
NumMessages: 1,
|
||||
NumExtensions: 0,
|
||||
NumServices: 1,
|
||||
},
|
||||
},
|
||||
Metadata: "grpc_stdio.proto",
|
||||
}
|
||||
|
||||
func init() { proto.RegisterFile("grpc_stdio.proto", fileDescriptor_grpc_stdio_db2934322ca63bd5) }
|
||||
|
||||
var fileDescriptor_grpc_stdio_db2934322ca63bd5 = []byte{
|
||||
// 221 bytes of a gzipped FileDescriptorProto
|
||||
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x12, 0x48, 0x2f, 0x2a, 0x48,
|
||||
0x8e, 0x2f, 0x2e, 0x49, 0xc9, 0xcc, 0xd7, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x62, 0x2b, 0xc8,
|
||||
0x29, 0x4d, 0xcf, 0xcc, 0x93, 0x92, 0x4e, 0xcf, 0xcf, 0x4f, 0xcf, 0x49, 0xd5, 0x07, 0x8b, 0x26,
|
||||
0x95, 0xa6, 0xe9, 0xa7, 0xe6, 0x16, 0x94, 0x54, 0x42, 0x14, 0x29, 0xb5, 0x30, 0x72, 0x71, 0x06,
|
||||
0x83, 0x34, 0xb9, 0x24, 0x96, 0x24, 0x0a, 0x19, 0x73, 0xb1, 0x27, 0x67, 0x24, 0xe6, 0xe5, 0xa5,
|
||||
0xe6, 0x48, 0x30, 0x2a, 0x30, 0x6a, 0xf0, 0x19, 0x49, 0xea, 0x41, 0x0c, 0xd1, 0x83, 0xab, 0xd1,
|
||||
0x73, 0x86, 0x28, 0x08, 0x82, 0xa9, 0x14, 0x12, 0xe2, 0x62, 0x49, 0x49, 0x2c, 0x49, 0x94, 0x60,
|
||||
0x52, 0x60, 0xd4, 0xe0, 0x09, 0x02, 0xb3, 0x95, 0xf4, 0xb8, 0xd8, 0xa1, 0xea, 0x84, 0xb8, 0xb9,
|
||||
0xd8, 0x3d, 0xfd, 0xc2, 0x1c, 0x7d, 0x3c, 0x5d, 0x04, 0x18, 0x84, 0xb8, 0xb8, 0xd8, 0x82, 0x43,
|
||||
0x5c, 0xfc, 0x43, 0x43, 0x04, 0x18, 0xa1, 0x6c, 0xd7, 0xa0, 0x20, 0x01, 0x26, 0x23, 0x77, 0x2e,
|
||||
0x4e, 0xf7, 0xa0, 0x00, 0x67, 0xb0, 0x2d, 0x42, 0x56, 0x5c, 0xdc, 0xc1, 0x25, 0x45, 0xa9, 0x89,
|
||||
0xb9, 0x10, 0xae, 0x98, 0x1e, 0xc4, 0x03, 0x7a, 0x30, 0x0f, 0xe8, 0xb9, 0x82, 0x3c, 0x20, 0x25,
|
||||
0x88, 0xe1, 0x36, 0x03, 0x46, 0x27, 0x8e, 0x28, 0xa8, 0xb7, 0x93, 0xd8, 0xc0, 0xca, 0x8d, 0x01,
|
||||
0x01, 0x00, 0x00, 0xff, 0xff, 0x5d, 0xbb, 0xe0, 0x69, 0x19, 0x01, 0x00, 0x00,
|
||||
GoTypes: file_internal_plugin_grpc_stdio_proto_goTypes,
|
||||
DependencyIndexes: file_internal_plugin_grpc_stdio_proto_depIdxs,
|
||||
EnumInfos: file_internal_plugin_grpc_stdio_proto_enumTypes,
|
||||
MessageInfos: file_internal_plugin_grpc_stdio_proto_msgTypes,
|
||||
}.Build()
|
||||
File_internal_plugin_grpc_stdio_proto = out.File
|
||||
file_internal_plugin_grpc_stdio_proto_rawDesc = nil
|
||||
file_internal_plugin_grpc_stdio_proto_goTypes = nil
|
||||
file_internal_plugin_grpc_stdio_proto_depIdxs = nil
|
||||
}
|
||||
|
||||
+4
-1
@@ -1,6 +1,9 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
syntax = "proto3";
|
||||
package plugin;
|
||||
option go_package = "plugin";
|
||||
option go_package = "./plugin";
|
||||
|
||||
import "google/protobuf/empty.proto";
|
||||
|
||||
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
// Code generated by protoc-gen-go-grpc. DO NOT EDIT.
|
||||
// versions:
|
||||
// - protoc-gen-go-grpc v1.3.0
|
||||
// - protoc (unknown)
|
||||
// source: internal/plugin/grpc_stdio.proto
|
||||
|
||||
package plugin
|
||||
|
||||
import (
|
||||
context "context"
|
||||
grpc "google.golang.org/grpc"
|
||||
codes "google.golang.org/grpc/codes"
|
||||
status "google.golang.org/grpc/status"
|
||||
emptypb "google.golang.org/protobuf/types/known/emptypb"
|
||||
)
|
||||
|
||||
// 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.32.0 or later.
|
||||
const _ = grpc.SupportPackageIsVersion7
|
||||
|
||||
const (
|
||||
GRPCStdio_StreamStdio_FullMethodName = "/plugin.GRPCStdio/StreamStdio"
|
||||
)
|
||||
|
||||
// GRPCStdioClient is the client API for GRPCStdio 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.
|
||||
type GRPCStdioClient interface {
|
||||
// StreamStdio returns a stream that contains all the stdout/stderr.
|
||||
// This RPC endpoint must only be called ONCE. Once stdio data is consumed
|
||||
// it is not sent again.
|
||||
//
|
||||
// Callers should connect early to prevent blocking on the plugin process.
|
||||
StreamStdio(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (GRPCStdio_StreamStdioClient, error)
|
||||
}
|
||||
|
||||
type gRPCStdioClient struct {
|
||||
cc grpc.ClientConnInterface
|
||||
}
|
||||
|
||||
func NewGRPCStdioClient(cc grpc.ClientConnInterface) GRPCStdioClient {
|
||||
return &gRPCStdioClient{cc}
|
||||
}
|
||||
|
||||
func (c *gRPCStdioClient) StreamStdio(ctx context.Context, in *emptypb.Empty, opts ...grpc.CallOption) (GRPCStdio_StreamStdioClient, error) {
|
||||
stream, err := c.cc.NewStream(ctx, &GRPCStdio_ServiceDesc.Streams[0], GRPCStdio_StreamStdio_FullMethodName, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
x := &gRPCStdioStreamStdioClient{stream}
|
||||
if err := x.ClientStream.SendMsg(in); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := x.ClientStream.CloseSend(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return x, nil
|
||||
}
|
||||
|
||||
type GRPCStdio_StreamStdioClient interface {
|
||||
Recv() (*StdioData, error)
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
type gRPCStdioStreamStdioClient struct {
|
||||
grpc.ClientStream
|
||||
}
|
||||
|
||||
func (x *gRPCStdioStreamStdioClient) Recv() (*StdioData, error) {
|
||||
m := new(StdioData)
|
||||
if err := x.ClientStream.RecvMsg(m); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
// GRPCStdioServer is the server API for GRPCStdio service.
|
||||
// All implementations should embed UnimplementedGRPCStdioServer
|
||||
// for forward compatibility
|
||||
type GRPCStdioServer interface {
|
||||
// StreamStdio returns a stream that contains all the stdout/stderr.
|
||||
// This RPC endpoint must only be called ONCE. Once stdio data is consumed
|
||||
// it is not sent again.
|
||||
//
|
||||
// Callers should connect early to prevent blocking on the plugin process.
|
||||
StreamStdio(*emptypb.Empty, GRPCStdio_StreamStdioServer) error
|
||||
}
|
||||
|
||||
// UnimplementedGRPCStdioServer should be embedded to have forward compatible implementations.
|
||||
type UnimplementedGRPCStdioServer struct {
|
||||
}
|
||||
|
||||
func (UnimplementedGRPCStdioServer) StreamStdio(*emptypb.Empty, GRPCStdio_StreamStdioServer) error {
|
||||
return status.Errorf(codes.Unimplemented, "method StreamStdio not implemented")
|
||||
}
|
||||
|
||||
// UnsafeGRPCStdioServer may be embedded to opt out of forward compatibility for this service.
|
||||
// Use of this interface is not recommended, as added methods to GRPCStdioServer will
|
||||
// result in compilation errors.
|
||||
type UnsafeGRPCStdioServer interface {
|
||||
mustEmbedUnimplementedGRPCStdioServer()
|
||||
}
|
||||
|
||||
func RegisterGRPCStdioServer(s grpc.ServiceRegistrar, srv GRPCStdioServer) {
|
||||
s.RegisterService(&GRPCStdio_ServiceDesc, srv)
|
||||
}
|
||||
|
||||
func _GRPCStdio_StreamStdio_Handler(srv interface{}, stream grpc.ServerStream) error {
|
||||
m := new(emptypb.Empty)
|
||||
if err := stream.RecvMsg(m); err != nil {
|
||||
return err
|
||||
}
|
||||
return srv.(GRPCStdioServer).StreamStdio(m, &gRPCStdioStreamStdioServer{stream})
|
||||
}
|
||||
|
||||
type GRPCStdio_StreamStdioServer interface {
|
||||
Send(*StdioData) error
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
type gRPCStdioStreamStdioServer struct {
|
||||
grpc.ServerStream
|
||||
}
|
||||
|
||||
func (x *gRPCStdioStreamStdioServer) Send(m *StdioData) error {
|
||||
return x.ServerStream.SendMsg(m)
|
||||
}
|
||||
|
||||
// GRPCStdio_ServiceDesc is the grpc.ServiceDesc for GRPCStdio service.
|
||||
// It's only intended for direct use with grpc.RegisterService,
|
||||
// and not to be introspected or modified (even as a copy)
|
||||
var GRPCStdio_ServiceDesc = grpc.ServiceDesc{
|
||||
ServiceName: "plugin.GRPCStdio",
|
||||
HandlerType: (*GRPCStdioServer)(nil),
|
||||
Methods: []grpc.MethodDesc{},
|
||||
Streams: []grpc.StreamDesc{
|
||||
{
|
||||
StreamName: "StreamStdio",
|
||||
Handler: _GRPCStdio_StreamStdio_Handler,
|
||||
ServerStreams: true,
|
||||
},
|
||||
},
|
||||
Metadata: "internal/plugin/grpc_stdio.proto",
|
||||
}
|
||||
+3
@@ -1,3 +1,6 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package plugin
|
||||
|
||||
import (
|
||||
|
||||
+3
@@ -1,3 +1,6 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package plugin
|
||||
|
||||
import (
|
||||
|
||||
+3
@@ -1,3 +1,6 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package plugin
|
||||
|
||||
import (
|
||||
|
||||
+3
@@ -1,3 +1,6 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
// The plugin package exposes functions and helpers for communicating to
|
||||
// plugins which are implemented as standalone binary applications.
|
||||
//
|
||||
|
||||
+3
-23
@@ -1,24 +1,4 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package plugin
|
||||
|
||||
import (
|
||||
"time"
|
||||
)
|
||||
|
||||
// pidAlive checks whether a pid is alive.
|
||||
func pidAlive(pid int) bool {
|
||||
return _pidAlive(pid)
|
||||
}
|
||||
|
||||
// pidWait blocks for a process to exit.
|
||||
func pidWait(pid int) error {
|
||||
ticker := time.NewTicker(1 * time.Second)
|
||||
defer ticker.Stop()
|
||||
|
||||
for range ticker.C {
|
||||
if !pidAlive(pid) {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
+3
@@ -1,3 +1,6 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package plugin
|
||||
|
||||
import (
|
||||
|
||||
+3
@@ -1,3 +1,6 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package plugin
|
||||
|
||||
import (
|
||||
|
||||
+12
-4
@@ -1,3 +1,6 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package plugin
|
||||
|
||||
import (
|
||||
@@ -42,6 +45,8 @@ func (s *RPCServer) Config() string { return "" }
|
||||
|
||||
// ServerProtocol impl.
|
||||
func (s *RPCServer) Serve(lis net.Listener) {
|
||||
defer s.done()
|
||||
|
||||
for {
|
||||
conn, err := lis.Accept()
|
||||
if err != nil {
|
||||
@@ -82,7 +87,7 @@ func (s *RPCServer) ServeConn(conn io.ReadWriteCloser) {
|
||||
|
||||
// Connect the stdstreams (in, out, err)
|
||||
stdstream := make([]net.Conn, 2)
|
||||
for i, _ := range stdstream {
|
||||
for i := range stdstream {
|
||||
stdstream[i], err = mux.Accept()
|
||||
if err != nil {
|
||||
mux.Close()
|
||||
@@ -133,13 +138,15 @@ type controlServer struct {
|
||||
// Ping can be called to verify the connection (and likely the binary)
|
||||
// is still alive to a plugin.
|
||||
func (c *controlServer) Ping(
|
||||
null bool, response *struct{}) error {
|
||||
null bool, response *struct{},
|
||||
) error {
|
||||
*response = struct{}{}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *controlServer) Quit(
|
||||
null bool, response *struct{}) error {
|
||||
null bool, response *struct{},
|
||||
) error {
|
||||
// End the server
|
||||
c.server.done()
|
||||
|
||||
@@ -156,7 +163,8 @@ type dispenseServer struct {
|
||||
}
|
||||
|
||||
func (d *dispenseServer) Dispense(
|
||||
name string, response *uint32) error {
|
||||
name string, response *uint32,
|
||||
) error {
|
||||
// Find the function to create this implementation
|
||||
p, ok := d.plugins[name]
|
||||
if !ok {
|
||||
|
||||
+72
@@ -0,0 +1,72 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package runner
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
)
|
||||
|
||||
// Runner defines the interface required by go-plugin to manage the lifecycle of
|
||||
// of a plugin and attempt to negotiate a connection with it. Note that this
|
||||
// is orthogonal to the protocol and transport used, which is negotiated over stdout.
|
||||
type Runner interface {
|
||||
// Start should start the plugin and ensure any work required for servicing
|
||||
// other interface methods is done. If the context is cancelled, it should
|
||||
// only abort any attempts to _start_ the plugin. Waiting and shutdown are
|
||||
// handled separately.
|
||||
Start(ctx context.Context) error
|
||||
|
||||
// Diagnose makes a best-effort attempt to return any debug information that
|
||||
// might help users understand why a plugin failed to start and negotiate a
|
||||
// connection.
|
||||
Diagnose(ctx context.Context) string
|
||||
|
||||
// Stdout is used to negotiate the go-plugin protocol.
|
||||
Stdout() io.ReadCloser
|
||||
|
||||
// Stderr is used for forwarding plugin logs to the host process logger.
|
||||
Stderr() io.ReadCloser
|
||||
|
||||
// Name is a human-friendly name for the plugin, such as the path to the
|
||||
// executable. It does not have to be unique.
|
||||
Name() string
|
||||
|
||||
AttachedRunner
|
||||
}
|
||||
|
||||
// AttachedRunner defines a limited subset of Runner's interface to represent the
|
||||
// reduced responsibility for plugin lifecycle when attaching to an already running
|
||||
// plugin.
|
||||
type AttachedRunner interface {
|
||||
// Wait should wait until the plugin stops running, whether in response to
|
||||
// an out of band signal or in response to calling Kill().
|
||||
Wait(ctx context.Context) error
|
||||
|
||||
// Kill should stop the plugin and perform any cleanup required.
|
||||
Kill(ctx context.Context) error
|
||||
|
||||
// ID is a unique identifier to represent the running plugin. e.g. pid or
|
||||
// container ID.
|
||||
ID() string
|
||||
|
||||
AddrTranslator
|
||||
}
|
||||
|
||||
// AddrTranslator translates addresses between the execution context of the host
|
||||
// process and the plugin. For example, if the plugin is in a container, the file
|
||||
// path for a Unix socket may be different between the host and the container.
|
||||
//
|
||||
// It is only intended to be used by the host process.
|
||||
type AddrTranslator interface {
|
||||
// Called before connecting on any addresses received back from the plugin.
|
||||
PluginToHost(pluginNet, pluginAddr string) (hostNet string, hostAddr string, err error)
|
||||
|
||||
// Called on any host process addresses before they are sent to the plugin.
|
||||
HostToPlugin(hostNet, hostAddr string) (pluginNet string, pluginAddr string, err error)
|
||||
}
|
||||
|
||||
// ReattachFunc can be passed to a client's reattach config to reattach to an
|
||||
// already running plugin instead of starting it ourselves.
|
||||
type ReattachFunc func() (AttachedRunner, error)
|
||||
+89
-15
@@ -1,3 +1,6 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package plugin
|
||||
|
||||
import (
|
||||
@@ -8,16 +11,17 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"os"
|
||||
"os/signal"
|
||||
"os/user"
|
||||
"runtime"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
hclog "github.com/hashicorp/go-hclog"
|
||||
"github.com/hashicorp/go-plugin/internal/grpcmux"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
|
||||
@@ -131,6 +135,13 @@ type ServeTestConfig struct {
|
||||
SyncStdio bool
|
||||
}
|
||||
|
||||
func unixSocketConfigFromEnv() UnixSocketConfig {
|
||||
return UnixSocketConfig{
|
||||
Group: os.Getenv(EnvUnixSocketGroup),
|
||||
socketDir: os.Getenv(EnvUnixSocketDir),
|
||||
}
|
||||
}
|
||||
|
||||
// protocolVersion determines the protocol version and plugin set to be used by
|
||||
// the server. In the event that there is no suitable version, the last version
|
||||
// in the config is returned leaving the client to report the incompatibility.
|
||||
@@ -270,7 +281,7 @@ func Serve(opts *ServeConfig) {
|
||||
}
|
||||
|
||||
// Register a listener so we can accept a connection
|
||||
listener, err := serverListener()
|
||||
listener, err := serverListener(unixSocketConfigFromEnv())
|
||||
if err != nil {
|
||||
logger.Error("plugin init error", "error", err)
|
||||
return
|
||||
@@ -377,6 +388,12 @@ func Serve(opts *ServeConfig) {
|
||||
}
|
||||
|
||||
case ProtocolGRPC:
|
||||
var muxer *grpcmux.GRPCServerMuxer
|
||||
if multiplex, _ := strconv.ParseBool(os.Getenv(envMultiplexGRPC)); multiplex {
|
||||
muxer = grpcmux.NewGRPCServerMuxer(logger, listener)
|
||||
listener = muxer
|
||||
}
|
||||
|
||||
// Create the gRPC server
|
||||
server = &GRPCServer{
|
||||
Plugins: pluginSet,
|
||||
@@ -386,6 +403,7 @@ func Serve(opts *ServeConfig) {
|
||||
Stderr: stderr_r,
|
||||
DoneCh: doneCh,
|
||||
logger: logger,
|
||||
muxer: muxer,
|
||||
}
|
||||
|
||||
default:
|
||||
@@ -404,13 +422,27 @@ func Serve(opts *ServeConfig) {
|
||||
// bring it up. In test mode, we don't do this because clients will
|
||||
// attach via a reattach config.
|
||||
if opts.Test == nil {
|
||||
fmt.Printf("%d|%d|%s|%s|%s|%s\n",
|
||||
const grpcBrokerMultiplexingSupported = true
|
||||
protocolLine := fmt.Sprintf("%d|%d|%s|%s|%s|%s",
|
||||
CoreProtocolVersion,
|
||||
protoVersion,
|
||||
listener.Addr().Network(),
|
||||
listener.Addr().String(),
|
||||
protoType,
|
||||
serverCert)
|
||||
|
||||
// Old clients will error with new plugins if we blindly append the
|
||||
// seventh segment for gRPC broker multiplexing support, because old
|
||||
// client code uses strings.SplitN(line, "|", 6), which means a seventh
|
||||
// segment will get appended to the sixth segment as "sixthpart|true".
|
||||
//
|
||||
// If the environment variable is set, we assume the client is new enough
|
||||
// to handle a seventh segment, as it should now use
|
||||
// strings.Split(line, "|") and always handle each segment individually.
|
||||
if os.Getenv(envMultiplexGRPC) != "" {
|
||||
protocolLine += fmt.Sprintf("|%v", grpcBrokerMultiplexingSupported)
|
||||
}
|
||||
fmt.Printf("%s\n", protocolLine)
|
||||
os.Stdout.Sync()
|
||||
} else if ch := opts.Test.ReattachConfigCh; ch != nil {
|
||||
// Send back the reattach config that can be used. This isn't
|
||||
@@ -493,12 +525,12 @@ func Serve(opts *ServeConfig) {
|
||||
}
|
||||
}
|
||||
|
||||
func serverListener() (net.Listener, error) {
|
||||
func serverListener(unixSocketCfg UnixSocketConfig) (net.Listener, error) {
|
||||
if runtime.GOOS == "windows" {
|
||||
return serverListener_tcp()
|
||||
}
|
||||
|
||||
return serverListener_unix()
|
||||
return serverListener_unix(unixSocketCfg)
|
||||
}
|
||||
|
||||
func serverListener_tcp() (net.Listener, error) {
|
||||
@@ -543,8 +575,8 @@ func serverListener_tcp() (net.Listener, error) {
|
||||
return nil, errors.New("Couldn't bind plugin TCP listener")
|
||||
}
|
||||
|
||||
func serverListener_unix() (net.Listener, error) {
|
||||
tf, err := ioutil.TempFile("", "plugin")
|
||||
func serverListener_unix(unixSocketCfg UnixSocketConfig) (net.Listener, error) {
|
||||
tf, err := os.CreateTemp(unixSocketCfg.socketDir, "plugin")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -564,20 +596,62 @@ func serverListener_unix() (net.Listener, error) {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// By default, unix sockets are only writable by the owner. Set up a custom
|
||||
// group owner and group write permissions if configured.
|
||||
if unixSocketCfg.Group != "" {
|
||||
err = setGroupWritable(path, unixSocketCfg.Group, 0o660)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Wrap the listener in rmListener so that the Unix domain socket file
|
||||
// is removed on close.
|
||||
return &rmListener{
|
||||
Listener: l,
|
||||
Path: path,
|
||||
}, nil
|
||||
return newDeleteFileListener(l, path), nil
|
||||
}
|
||||
|
||||
func setGroupWritable(path, groupString string, mode os.FileMode) error {
|
||||
groupID, err := strconv.Atoi(groupString)
|
||||
if err != nil {
|
||||
group, err := user.LookupGroup(groupString)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to find gid from %q: %w", groupString, err)
|
||||
}
|
||||
groupID, err = strconv.Atoi(group.Gid)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to parse %q group's gid as an integer: %w", groupString, err)
|
||||
}
|
||||
}
|
||||
|
||||
err = os.Chown(path, -1, groupID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
err = os.Chmod(path, mode)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// rmListener is an implementation of net.Listener that forwards most
|
||||
// calls to the listener but also removes a file as part of the close. We
|
||||
// use this to cleanup the unix domain socket on close.
|
||||
// calls to the listener but also calls an additional close function. We
|
||||
// use this to cleanup the unix domain socket on close, as well as clean
|
||||
// up multiplexed listeners.
|
||||
type rmListener struct {
|
||||
net.Listener
|
||||
Path string
|
||||
close func() error
|
||||
}
|
||||
|
||||
func newDeleteFileListener(ln net.Listener, path string) *rmListener {
|
||||
return &rmListener{
|
||||
Listener: ln,
|
||||
close: func() error {
|
||||
return os.Remove(path)
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (l *rmListener) Close() error {
|
||||
@@ -587,5 +661,5 @@ func (l *rmListener) Close() error {
|
||||
}
|
||||
|
||||
// Remove the file
|
||||
return os.Remove(l.Path)
|
||||
return l.close()
|
||||
}
|
||||
|
||||
+3
@@ -1,3 +1,6 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package plugin
|
||||
|
||||
import (
|
||||
|
||||
+3
@@ -1,3 +1,6 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package plugin
|
||||
|
||||
import (
|
||||
|
||||
+32
-27
@@ -1,3 +1,6 @@
|
||||
// Copyright (c) HashiCorp, Inc.
|
||||
// SPDX-License-Identifier: MPL-2.0
|
||||
|
||||
package plugin
|
||||
|
||||
import (
|
||||
@@ -8,7 +11,7 @@ import (
|
||||
"net/rpc"
|
||||
|
||||
hclog "github.com/hashicorp/go-hclog"
|
||||
"github.com/hashicorp/go-plugin/internal/plugin"
|
||||
"github.com/hashicorp/go-plugin/internal/grpcmux"
|
||||
"github.com/mitchellh/go-testing-interface"
|
||||
"google.golang.org/grpc"
|
||||
)
|
||||
@@ -132,49 +135,51 @@ func TestGRPCConn(t testing.T, register func(*grpc.Server)) (*grpc.ClientConn, *
|
||||
|
||||
// TestPluginGRPCConn returns a plugin gRPC client and server that are connected
|
||||
// together and configured. This is used to test gRPC connections.
|
||||
func TestPluginGRPCConn(t testing.T, ps map[string]Plugin) (*GRPCClient, *GRPCServer) {
|
||||
func TestPluginGRPCConn(t testing.T, multiplex bool, ps map[string]Plugin) (*GRPCClient, *GRPCServer) {
|
||||
// Create a listener
|
||||
l, err := net.Listen("tcp", "127.0.0.1:0")
|
||||
ln, err := serverListener(UnixSocketConfig{})
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
logger := hclog.New(&hclog.LoggerOptions{
|
||||
Level: hclog.Debug,
|
||||
})
|
||||
|
||||
// Start up the server
|
||||
var muxer *grpcmux.GRPCServerMuxer
|
||||
if multiplex {
|
||||
muxer = grpcmux.NewGRPCServerMuxer(logger, ln)
|
||||
ln = muxer
|
||||
}
|
||||
server := &GRPCServer{
|
||||
Plugins: ps,
|
||||
DoneCh: make(chan struct{}),
|
||||
Server: DefaultGRPCServer,
|
||||
Stdout: new(bytes.Buffer),
|
||||
Stderr: new(bytes.Buffer),
|
||||
logger: hclog.Default(),
|
||||
logger: logger,
|
||||
muxer: muxer,
|
||||
}
|
||||
if err := server.Init(); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
go server.Serve(l)
|
||||
go server.Serve(ln)
|
||||
|
||||
// Connect to the server
|
||||
conn, err := grpc.Dial(
|
||||
l.Addr().String(),
|
||||
grpc.WithBlock(),
|
||||
grpc.WithInsecure())
|
||||
client := &Client{
|
||||
address: ln.Addr(),
|
||||
protocol: ProtocolGRPC,
|
||||
config: &ClientConfig{
|
||||
Plugins: ps,
|
||||
GRPCBrokerMultiplex: multiplex,
|
||||
},
|
||||
logger: logger,
|
||||
}
|
||||
|
||||
grpcClient, err := newGRPCClient(context.Background(), client)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
brokerGRPCClient := newGRPCBrokerClient(conn)
|
||||
broker := newGRPCBroker(brokerGRPCClient, nil)
|
||||
go broker.Run()
|
||||
go brokerGRPCClient.StartStream()
|
||||
|
||||
// Create the client
|
||||
client := &GRPCClient{
|
||||
Conn: conn,
|
||||
Plugins: ps,
|
||||
broker: broker,
|
||||
doneCtx: context.Background(),
|
||||
controller: plugin.NewGRPCControllerClient(conn),
|
||||
}
|
||||
|
||||
return client, server
|
||||
return grpcClient, server
|
||||
}
|
||||
|
||||
+81
-27
@@ -2,6 +2,7 @@ package yamux
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
@@ -63,23 +64,26 @@ type Session struct {
|
||||
|
||||
// sendCh is used to mark a stream as ready to send,
|
||||
// or to send a header out directly.
|
||||
sendCh chan sendReady
|
||||
sendCh chan *sendReady
|
||||
|
||||
// recvDoneCh is closed when recv() exits to avoid a race
|
||||
// between stream registration and stream shutdown
|
||||
recvDoneCh chan struct{}
|
||||
sendDoneCh chan struct{}
|
||||
|
||||
// shutdown is used to safely close a session
|
||||
shutdown bool
|
||||
shutdownErr error
|
||||
shutdownCh chan struct{}
|
||||
shutdownLock sync.Mutex
|
||||
shutdown bool
|
||||
shutdownErr error
|
||||
shutdownCh chan struct{}
|
||||
shutdownLock sync.Mutex
|
||||
shutdownErrLock sync.Mutex
|
||||
}
|
||||
|
||||
// sendReady is used to either mark a stream as ready
|
||||
// or to directly send a header
|
||||
type sendReady struct {
|
||||
Hdr []byte
|
||||
mu sync.Mutex // Protects Body from unsafe reads.
|
||||
Body []byte
|
||||
Err chan error
|
||||
}
|
||||
@@ -101,8 +105,9 @@ func newSession(config *Config, conn io.ReadWriteCloser, client bool) *Session {
|
||||
inflight: make(map[uint32]struct{}),
|
||||
synCh: make(chan struct{}, config.AcceptBacklog),
|
||||
acceptCh: make(chan *Stream, config.AcceptBacklog),
|
||||
sendCh: make(chan sendReady, 64),
|
||||
sendCh: make(chan *sendReady, 64),
|
||||
recvDoneCh: make(chan struct{}),
|
||||
sendDoneCh: make(chan struct{}),
|
||||
shutdownCh: make(chan struct{}),
|
||||
}
|
||||
if client {
|
||||
@@ -255,10 +260,15 @@ func (s *Session) Close() error {
|
||||
return nil
|
||||
}
|
||||
s.shutdown = true
|
||||
|
||||
s.shutdownErrLock.Lock()
|
||||
if s.shutdownErr == nil {
|
||||
s.shutdownErr = ErrSessionShutdown
|
||||
}
|
||||
s.shutdownErrLock.Unlock()
|
||||
|
||||
close(s.shutdownCh)
|
||||
|
||||
s.conn.Close()
|
||||
<-s.recvDoneCh
|
||||
|
||||
@@ -267,17 +277,18 @@ func (s *Session) Close() error {
|
||||
for _, stream := range s.streams {
|
||||
stream.forceClose()
|
||||
}
|
||||
<-s.sendDoneCh
|
||||
return nil
|
||||
}
|
||||
|
||||
// exitErr is used to handle an error that is causing the
|
||||
// session to terminate.
|
||||
func (s *Session) exitErr(err error) {
|
||||
s.shutdownLock.Lock()
|
||||
s.shutdownErrLock.Lock()
|
||||
if s.shutdownErr == nil {
|
||||
s.shutdownErr = err
|
||||
}
|
||||
s.shutdownLock.Unlock()
|
||||
s.shutdownErrLock.Unlock()
|
||||
s.Close()
|
||||
}
|
||||
|
||||
@@ -373,7 +384,7 @@ func (s *Session) waitForSendErr(hdr header, body []byte, errCh chan error) erro
|
||||
timerPool.Put(t)
|
||||
}()
|
||||
|
||||
ready := sendReady{Hdr: hdr, Body: body, Err: errCh}
|
||||
ready := &sendReady{Hdr: hdr, Body: body, Err: errCh}
|
||||
select {
|
||||
case s.sendCh <- ready:
|
||||
case <-s.shutdownCh:
|
||||
@@ -382,12 +393,34 @@ func (s *Session) waitForSendErr(hdr header, body []byte, errCh chan error) erro
|
||||
return ErrConnectionWriteTimeout
|
||||
}
|
||||
|
||||
bodyCopy := func() {
|
||||
if body == nil {
|
||||
return // A nil body is ignored.
|
||||
}
|
||||
|
||||
// In the event of session shutdown or connection write timeout,
|
||||
// we need to prevent `send` from reading the body buffer after
|
||||
// returning from this function since the caller may re-use the
|
||||
// underlying array.
|
||||
ready.mu.Lock()
|
||||
defer ready.mu.Unlock()
|
||||
|
||||
if ready.Body == nil {
|
||||
return // Body was already copied in `send`.
|
||||
}
|
||||
newBody := make([]byte, len(body))
|
||||
copy(newBody, body)
|
||||
ready.Body = newBody
|
||||
}
|
||||
|
||||
select {
|
||||
case err := <-errCh:
|
||||
return err
|
||||
case <-s.shutdownCh:
|
||||
bodyCopy()
|
||||
return ErrSessionShutdown
|
||||
case <-timer.C:
|
||||
bodyCopy()
|
||||
return ErrConnectionWriteTimeout
|
||||
}
|
||||
}
|
||||
@@ -409,7 +442,7 @@ func (s *Session) sendNoWait(hdr header) error {
|
||||
}()
|
||||
|
||||
select {
|
||||
case s.sendCh <- sendReady{Hdr: hdr}:
|
||||
case s.sendCh <- &sendReady{Hdr: hdr}:
|
||||
return nil
|
||||
case <-s.shutdownCh:
|
||||
return ErrSessionShutdown
|
||||
@@ -420,39 +453,59 @@ func (s *Session) sendNoWait(hdr header) error {
|
||||
|
||||
// send is a long running goroutine that sends data
|
||||
func (s *Session) send() {
|
||||
if err := s.sendLoop(); err != nil {
|
||||
s.exitErr(err)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Session) sendLoop() error {
|
||||
defer close(s.sendDoneCh)
|
||||
var bodyBuf bytes.Buffer
|
||||
for {
|
||||
bodyBuf.Reset()
|
||||
|
||||
select {
|
||||
case ready := <-s.sendCh:
|
||||
// Send a header if ready
|
||||
if ready.Hdr != nil {
|
||||
sent := 0
|
||||
for sent < len(ready.Hdr) {
|
||||
n, err := s.conn.Write(ready.Hdr[sent:])
|
||||
if err != nil {
|
||||
s.logger.Printf("[ERR] yamux: Failed to write header: %v", err)
|
||||
asyncSendErr(ready.Err, err)
|
||||
s.exitErr(err)
|
||||
return
|
||||
}
|
||||
sent += n
|
||||
_, err := s.conn.Write(ready.Hdr)
|
||||
if err != nil {
|
||||
s.logger.Printf("[ERR] yamux: Failed to write header: %v", err)
|
||||
asyncSendErr(ready.Err, err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Send data from a body if given
|
||||
ready.mu.Lock()
|
||||
if ready.Body != nil {
|
||||
_, err := s.conn.Write(ready.Body)
|
||||
// Copy the body into the buffer to avoid
|
||||
// holding a mutex lock during the write.
|
||||
_, err := bodyBuf.Write(ready.Body)
|
||||
if err != nil {
|
||||
ready.Body = nil
|
||||
ready.mu.Unlock()
|
||||
s.logger.Printf("[ERR] yamux: Failed to copy body into buffer: %v", err)
|
||||
asyncSendErr(ready.Err, err)
|
||||
return err
|
||||
}
|
||||
ready.Body = nil
|
||||
}
|
||||
ready.mu.Unlock()
|
||||
|
||||
if bodyBuf.Len() > 0 {
|
||||
// Send data from a body if given
|
||||
_, err := s.conn.Write(bodyBuf.Bytes())
|
||||
if err != nil {
|
||||
s.logger.Printf("[ERR] yamux: Failed to write body: %v", err)
|
||||
asyncSendErr(ready.Err, err)
|
||||
s.exitErr(err)
|
||||
return
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// No error, successful send
|
||||
asyncSendErr(ready.Err, nil)
|
||||
case <-s.shutdownCh:
|
||||
return
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -639,8 +692,9 @@ func (s *Session) incomingStream(id uint32) error {
|
||||
// Backlog exceeded! RST the stream
|
||||
s.logger.Printf("[WARN] yamux: backlog exceeded, forcing connection reset")
|
||||
delete(s.streams, id)
|
||||
stream.sendHdr.encode(typeWindowUpdate, flagRST, id, 0)
|
||||
return s.sendNoWait(stream.sendHdr)
|
||||
hdr := header(make([]byte, headerSize))
|
||||
hdr.encode(typeWindowUpdate, flagRST, id, 0)
|
||||
return s.sendNoWait(hdr)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+23
-4
@@ -2,6 +2,7 @@ package yamux
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"io"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
@@ -127,6 +128,9 @@ START:
|
||||
|
||||
// Send a window update potentially
|
||||
err = s.sendWindowUpdate()
|
||||
if err == ErrSessionShutdown {
|
||||
err = nil
|
||||
}
|
||||
return n, err
|
||||
|
||||
WAIT:
|
||||
@@ -200,6 +204,10 @@ START:
|
||||
// Send the header
|
||||
s.sendHdr.encode(typeData, flags, s.id, max)
|
||||
if err = s.session.waitForSendErr(s.sendHdr, body, s.sendErr); err != nil {
|
||||
if errors.Is(err, ErrSessionShutdown) || errors.Is(err, ErrConnectionWriteTimeout) {
|
||||
// Message left in ready queue, header re-use is unsafe.
|
||||
s.sendHdr = header(make([]byte, headerSize))
|
||||
}
|
||||
return 0, err
|
||||
}
|
||||
|
||||
@@ -273,6 +281,10 @@ func (s *Stream) sendWindowUpdate() error {
|
||||
// Send the header
|
||||
s.controlHdr.encode(typeWindowUpdate, flags, s.id, delta)
|
||||
if err := s.session.waitForSendErr(s.controlHdr, nil, s.controlErr); err != nil {
|
||||
if errors.Is(err, ErrSessionShutdown) || errors.Is(err, ErrConnectionWriteTimeout) {
|
||||
// Message left in ready queue, header re-use is unsafe.
|
||||
s.controlHdr = header(make([]byte, headerSize))
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -287,6 +299,10 @@ func (s *Stream) sendClose() error {
|
||||
flags |= flagFIN
|
||||
s.controlHdr.encode(typeWindowUpdate, flags, s.id, 0)
|
||||
if err := s.session.waitForSendErr(s.controlHdr, nil, s.controlErr); err != nil {
|
||||
if errors.Is(err, ErrSessionShutdown) || errors.Is(err, ErrConnectionWriteTimeout) {
|
||||
// Message left in ready queue, header re-use is unsafe.
|
||||
s.controlHdr = header(make([]byte, headerSize))
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
@@ -362,8 +378,9 @@ func (s *Stream) closeTimeout() {
|
||||
// Send a RST so the remote side closes too.
|
||||
s.sendLock.Lock()
|
||||
defer s.sendLock.Unlock()
|
||||
s.sendHdr.encode(typeWindowUpdate, flagRST, s.id, 0)
|
||||
s.session.sendNoWait(s.sendHdr)
|
||||
hdr := header(make([]byte, headerSize))
|
||||
hdr.encode(typeWindowUpdate, flagRST, s.id, 0)
|
||||
s.session.sendNoWait(hdr)
|
||||
}
|
||||
|
||||
// forceClose is used for when the session is exiting
|
||||
@@ -465,6 +482,7 @@ func (s *Stream) readData(hdr header, flags uint16, conn io.Reader) error {
|
||||
|
||||
if length > s.recvWindow {
|
||||
s.session.logger.Printf("[ERR] yamux: receive window exceeded (stream: %d, remain: %d, recv: %d)", s.id, s.recvWindow, length)
|
||||
s.recvLock.Unlock()
|
||||
return ErrRecvWindowExceeded
|
||||
}
|
||||
|
||||
@@ -473,14 +491,15 @@ func (s *Stream) readData(hdr header, flags uint16, conn io.Reader) error {
|
||||
// This way we can read in the whole packet without further allocations.
|
||||
s.recvBuf = bytes.NewBuffer(make([]byte, 0, length))
|
||||
}
|
||||
if _, err := io.Copy(s.recvBuf, conn); err != nil {
|
||||
copiedLength, err := io.Copy(s.recvBuf, conn)
|
||||
if err != nil {
|
||||
s.session.logger.Printf("[ERR] yamux: Failed to read stream data: %v", err)
|
||||
s.recvLock.Unlock()
|
||||
return err
|
||||
}
|
||||
|
||||
// Decrement the receive window
|
||||
s.recvWindow -= length
|
||||
s.recvWindow -= uint32(copiedLength)
|
||||
s.recvLock.Unlock()
|
||||
|
||||
// Unblock any readers
|
||||
|
||||
Reference in New Issue
Block a user