mirror of
https://github.com/stashapp/stash.git
synced 2025-12-17 20:34:37 +03:00
Add plugin tasks (#651)
This commit is contained in:
24
vendor/github.com/natefinch/pie/.gitignore
generated
vendored
Normal file
24
vendor/github.com/natefinch/pie/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
# Compiled Object files, Static and Dynamic libs (Shared Objects)
|
||||
*.o
|
||||
*.a
|
||||
*.so
|
||||
|
||||
# Folders
|
||||
_obj
|
||||
_test
|
||||
|
||||
# Architecture specific extensions/prefixes
|
||||
*.[568vq]
|
||||
[568vq].out
|
||||
|
||||
*.cgo1.go
|
||||
*.cgo2.c
|
||||
_cgo_defun.c
|
||||
_cgo_gotypes.go
|
||||
_cgo_export.*
|
||||
|
||||
_testmain.go
|
||||
|
||||
*.exe
|
||||
*.test
|
||||
*.prof
|
||||
22
vendor/github.com/natefinch/pie/LICENSE
generated
vendored
Normal file
22
vendor/github.com/natefinch/pie/LICENSE
generated
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2015 Nate Finch
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
183
vendor/github.com/natefinch/pie/README.md
generated
vendored
Normal file
183
vendor/github.com/natefinch/pie/README.md
generated
vendored
Normal file
@@ -0,0 +1,183 @@
|
||||
# pie [](https://godoc.org/github.com/natefinch/pie) [ ](https://app.codeship.com/projects/232834)
|
||||
|
||||
import "github.com/natefinch/pie"
|
||||
|
||||
package pie provides a toolkit for creating plugins for Go applications.
|
||||
|
||||

|
||||
|
||||
**Why is it called pie?**
|
||||
|
||||
Because if you pronounce API like "a pie", then all this consuming and serving
|
||||
of APIs becomes a lot more palatable. Also, pies are the ultimate pluggable
|
||||
interface - depending on what's inside, you can get dinner, dessert, a snack, or
|
||||
even breakfast. Plus, then I get to say that plugins in Go are as easy as...
|
||||
well, you know.
|
||||
|
||||
If you have to explain it to your boss, just say it's an acronym for Plug In
|
||||
Executables. <sub>(but it's not, really)</sub>
|
||||
|
||||
## About Pie
|
||||
|
||||
Plugins using this toolkit and the applications managing those plugins
|
||||
communicate via RPC over the plugin application's Stdin and Stdout.
|
||||
|
||||
Functions in this package with the prefix `New` are intended to be used by the
|
||||
plugin to set up its end of the communication. Functions in this package
|
||||
with the prefix `Start` are intended to be used by the main application to set
|
||||
up its end of the communication and start a plugin executable.
|
||||
|
||||
<img src="https://cloud.githubusercontent.com/assets/3185864/7915136/8487d69e-0849-11e5-9dfa-13fc868f258f.png" />
|
||||
|
||||
This package provides two conceptually different types of plugins, based on
|
||||
which side of the communication is the server and which is the client.
|
||||
Plugins which provide an API server for the main application to call are
|
||||
called Providers. Plugins which consume an API provided by the main
|
||||
application are called Consumers.
|
||||
|
||||
The default codec for RPC for this package is Go's gob encoding, however you
|
||||
may provide your own codec, such as JSON-RPC provided by net/rpc/jsonrpc.
|
||||
|
||||
There is no requirement that plugins for applications using this toolkit be
|
||||
written in Go. As long as the plugin application can consume or provide an
|
||||
RPC API of the correct codec, it can interoperate with main applications
|
||||
using this process. For example, if your main application uses JSON-RPC,
|
||||
many languages are capable of producing an executable that can provide a
|
||||
JSON-RPC API for your application to use.
|
||||
|
||||
Included in this repo are some simple examples of a master process and a
|
||||
plugin process, to see how the library can be used. An example of the
|
||||
standard plugin that provides an API the master process consumes is in the
|
||||
examples/provider directory. master\_provider expects plugin\_provider to be
|
||||
in the same directory or in your $PATH. You can just go install both of
|
||||
them, and it'll work correctly.
|
||||
|
||||
In addition to a regular plugin that provides an API, this package can be
|
||||
used for plugins that consume an API provided by the main process. To see an
|
||||
example of this, look in the examples/consumer folder.
|
||||
|
||||
|
||||
## func NewConsumer
|
||||
``` go
|
||||
func NewConsumer() *rpc.Client
|
||||
```
|
||||
NewConsumer returns an rpc.Client that will consume an API from the host
|
||||
process over this application's Stdin and Stdout using gob encoding.
|
||||
|
||||
|
||||
## func NewConsumerCodec
|
||||
``` go
|
||||
func NewConsumerCodec(f func(io.ReadWriteCloser) rpc.ClientCodec) *rpc.Client
|
||||
```
|
||||
NewConsumerCodec returns an rpc.Client that will consume an API from the host
|
||||
process over this application's Stdin and Stdout using the ClientCodec
|
||||
returned by f.
|
||||
|
||||
|
||||
## func StartProvider
|
||||
``` go
|
||||
func StartProvider(output io.Writer, path string, args ...string) (*rpc.Client, error)
|
||||
```
|
||||
StartProvider start a provider-style plugin application at the given path and
|
||||
args, and returns an RPC client that communicates with the plugin using gob
|
||||
encoding over the plugin's Stdin and Stdout. The writer passed to output
|
||||
will receive output from the plugin's stderr. Closing the RPC client
|
||||
returned from this function will shut down the plugin application.
|
||||
|
||||
|
||||
## func StartProviderCodec
|
||||
``` go
|
||||
func StartProviderCodec(
|
||||
f func(io.ReadWriteCloser) rpc.ClientCodec,
|
||||
output io.Writer,
|
||||
path string,
|
||||
args ...string,
|
||||
) (*rpc.Client, error)
|
||||
```
|
||||
StartProviderCodec starts a provider-style plugin application at the given
|
||||
path and args, and returns an RPC client that communicates with the plugin
|
||||
using the ClientCodec returned by f over the plugin's Stdin and Stdout. The
|
||||
writer passed to output will receive output from the plugin's stderr.
|
||||
Closing the RPC client returned from this function will shut down the plugin
|
||||
application.
|
||||
|
||||
|
||||
## type Server
|
||||
``` go
|
||||
type Server struct {
|
||||
// contains filtered or unexported fields
|
||||
}
|
||||
```
|
||||
Server is a type that represents an RPC server that serves an API over
|
||||
stdin/stdout.
|
||||
|
||||
|
||||
### func NewProvider
|
||||
``` go
|
||||
func NewProvider() Server
|
||||
```
|
||||
NewProvider returns a Server that will serve RPC over this
|
||||
application's Stdin and Stdout. This method is intended to be run by the
|
||||
plugin application.
|
||||
|
||||
|
||||
### func StartConsumer
|
||||
``` go
|
||||
func StartConsumer(output io.Writer, path string, args ...string) (Server, error)
|
||||
```
|
||||
StartConsumer starts a consumer-style plugin application with the given path
|
||||
and args, writing its stderr to output. The plugin consumes an API this
|
||||
application provides. The function returns the Server for this host
|
||||
application, which should be used to register APIs for the plugin to consume.
|
||||
|
||||
|
||||
### func (Server) Close
|
||||
``` go
|
||||
func (s Server) Close() error
|
||||
```
|
||||
Close closes the connection with the client. If the client is a plugin
|
||||
process, the process will be stopped. Further communication using this
|
||||
Server will fail.
|
||||
|
||||
|
||||
### func (Server) Register
|
||||
``` go
|
||||
func (s Server) Register(rcvr interface{}) error
|
||||
```
|
||||
Register publishes in the provider the set of methods of the receiver value
|
||||
that satisfy the following conditions:
|
||||
|
||||
|
||||
- exported method
|
||||
- two arguments, both of exported type
|
||||
- the second argument is a pointer
|
||||
- one return value, of type error
|
||||
|
||||
It returns an error if the receiver is not an exported type or has no
|
||||
suitable methods. It also logs the error using package log. The client
|
||||
accesses each method using a string of the form "Type.Method", where Type is
|
||||
the receiver's concrete type.
|
||||
|
||||
|
||||
### func (Server) RegisterName
|
||||
``` go
|
||||
func (s Server) RegisterName(name string, rcvr interface{}) error
|
||||
```
|
||||
RegisterName is like Register but uses the provided name for the type
|
||||
instead of the receiver's concrete type.
|
||||
|
||||
|
||||
### func (Server) Serve
|
||||
``` go
|
||||
func (s Server) Serve()
|
||||
```
|
||||
Serve starts the Server's RPC server, serving via gob encoding. This call
|
||||
will block until the client hangs up.
|
||||
|
||||
|
||||
### func (Server) ServeCodec
|
||||
``` go
|
||||
func (s Server) ServeCodec(f func(io.ReadWriteCloser) rpc.ServerCodec)
|
||||
```
|
||||
ServeCodec starts the Server's RPC server, serving via the encoding returned
|
||||
by f. This call will block until the client hangs up.
|
||||
37
vendor/github.com/natefinch/pie/doc.go
generated
vendored
Normal file
37
vendor/github.com/natefinch/pie/doc.go
generated
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
// Package pie provides a toolkit for creating plugins for Go applications.
|
||||
//
|
||||
// Plugins using this toolkit and the applications managing those plugins
|
||||
// communicate via RPC over the plugin application's Stdin and Stdout.
|
||||
//
|
||||
// Functions in this package with the prefix New are intended to be used by the
|
||||
// plugin to set up its end of the communication. Functions in this package
|
||||
// with the prefix Start are intended to be used by the main application to set
|
||||
// up its end of the communication and run a plugin executable.
|
||||
//
|
||||
// This package provides two conceptually different types of plugins, based on
|
||||
// which side of the communication is the server and which is the client.
|
||||
// Plugins which provide an API server for the main application to call are
|
||||
// called Providers. Plugins which consume an API provided by the main
|
||||
// application are called Consumers.
|
||||
//
|
||||
// The default codec for RPC for this package is Go's gob encoding, however you
|
||||
// may provide your own codec, such as JSON-RPC provided by net/rpc/jsonrpc.
|
||||
//
|
||||
// There is no requirement that plugins for applications using this toolkit be
|
||||
// written in Go. As long as the plugin application can consume or provide an
|
||||
// RPC API of the correct codec, it can interoperate with main applications
|
||||
// using this process. For example, if your main application uses JSON-RPC,
|
||||
// many languages are capable of producing an executable that can provide a
|
||||
// JSON-RPC API for your application to use.
|
||||
//
|
||||
// Included in this repo are some simple examples of a master process and a
|
||||
// plugin process, to see how the library can be used. An example of the
|
||||
// standard plugin that provides an API the master process consumes is in the
|
||||
// exmaples/provider directory. master_provider expects plugin_provider to be
|
||||
// in the same directory or in your $PATH. You can just go install both of
|
||||
// them, and it'll work correctly.
|
||||
|
||||
// In addition to a regular plugin that provides an API, this package can be
|
||||
// used for plugins that consume an API provided by the main process. To see an
|
||||
// example of this, look in the examples/consumer folder.
|
||||
package pie
|
||||
260
vendor/github.com/natefinch/pie/pie.go
generated
vendored
Normal file
260
vendor/github.com/natefinch/pie/pie.go
generated
vendored
Normal file
@@ -0,0 +1,260 @@
|
||||
package pie
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/rpc"
|
||||
"os"
|
||||
"os/exec"
|
||||
"time"
|
||||
)
|
||||
|
||||
var errProcStopTimeout = errors.New("process killed after timeout waiting for process to stop")
|
||||
|
||||
// NewProvider returns a Server that will serve RPC over this
|
||||
// application's Stdin and Stdout. This method is intended to be run by the
|
||||
// plugin application.
|
||||
func NewProvider() Server {
|
||||
return Server{
|
||||
server: rpc.NewServer(),
|
||||
rwc: rwCloser{os.Stdin, os.Stdout},
|
||||
}
|
||||
}
|
||||
|
||||
// Server is a type that represents an RPC server that serves an API over
|
||||
// stdin/stdout.
|
||||
type Server struct {
|
||||
server *rpc.Server
|
||||
rwc io.ReadWriteCloser
|
||||
codec rpc.ServerCodec
|
||||
}
|
||||
|
||||
// Close closes the connection with the client. If the client is a plugin
|
||||
// process, the process will be stopped. Further communication using this
|
||||
// Server will fail.
|
||||
func (s Server) Close() error {
|
||||
if s.codec != nil {
|
||||
return s.codec.Close()
|
||||
}
|
||||
return s.rwc.Close()
|
||||
}
|
||||
|
||||
// Serve starts the Server's RPC server, serving via gob encoding. This call
|
||||
// will block until the client hangs up.
|
||||
func (s Server) Serve() {
|
||||
s.server.ServeConn(s.rwc)
|
||||
}
|
||||
|
||||
// ServeCodec starts the Server's RPC server, serving via the encoding returned
|
||||
// by f. This call will block until the client hangs up.
|
||||
func (s Server) ServeCodec(f func(io.ReadWriteCloser) rpc.ServerCodec) {
|
||||
s.server.ServeCodec(f(s.rwc))
|
||||
}
|
||||
|
||||
// Register publishes in the provider the set of methods of the receiver value
|
||||
// that satisfy the following conditions:
|
||||
//
|
||||
// - exported method
|
||||
// - two arguments, both of exported type
|
||||
// - the second argument is a pointer
|
||||
// - one return value, of type error
|
||||
//
|
||||
// It returns an error if the receiver is not an exported type or has no
|
||||
// suitable methods. It also logs the error using package log. The client
|
||||
// accesses each method using a string of the form "Type.Method", where Type is
|
||||
// the receiver's concrete type.
|
||||
func (s Server) Register(rcvr interface{}) error {
|
||||
return s.server.Register(rcvr)
|
||||
}
|
||||
|
||||
// RegisterName is like Register but uses the provided name for the type
|
||||
// instead of the receiver's concrete type.
|
||||
func (s Server) RegisterName(name string, rcvr interface{}) error {
|
||||
return s.server.RegisterName(name, rcvr)
|
||||
}
|
||||
|
||||
// StartProvider start a provider-style plugin application at the given path and
|
||||
// args, and returns an RPC client that communicates with the plugin using gob
|
||||
// encoding over the plugin's Stdin and Stdout. The writer passed to output
|
||||
// will receive output from the plugin's stderr. Closing the RPC client
|
||||
// returned from this function will shut down the plugin application.
|
||||
func StartProvider(output io.Writer, path string, args ...string) (*rpc.Client, error) {
|
||||
pipe, err := start(makeCommand(output, path, args))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rpc.NewClient(pipe), nil
|
||||
}
|
||||
|
||||
// StartProviderCodec starts a provider-style plugin application at the given
|
||||
// path and args, and returns an RPC client that communicates with the plugin
|
||||
// using the ClientCodec returned by f over the plugin's Stdin and Stdout. The
|
||||
// writer passed to output will receive output from the plugin's stderr.
|
||||
// Closing the RPC client returned from this function will shut down the plugin
|
||||
// application.
|
||||
func StartProviderCodec(
|
||||
f func(io.ReadWriteCloser) rpc.ClientCodec,
|
||||
output io.Writer,
|
||||
path string,
|
||||
args ...string,
|
||||
) (*rpc.Client, error) {
|
||||
pipe, err := start(makeCommand(output, path, args))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return rpc.NewClientWithCodec(f(pipe)), nil
|
||||
}
|
||||
|
||||
// StartConsumer starts a consumer-style plugin application with the given path
|
||||
// and args, writing its stderr to output. The plugin consumes an API this
|
||||
// application provides. The function returns the Server for this host
|
||||
// application, which should be used to register APIs for the plugin to consume.
|
||||
func StartConsumer(output io.Writer, path string, args ...string) (Server, error) {
|
||||
pipe, err := start(makeCommand(output, path, args))
|
||||
if err != nil {
|
||||
return Server{}, err
|
||||
}
|
||||
return Server{
|
||||
server: rpc.NewServer(),
|
||||
rwc: pipe,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// NewConsumer returns an rpc.Client that will consume an API from the host
|
||||
// process over this application's Stdin and Stdout using gob encoding.
|
||||
func NewConsumer() *rpc.Client {
|
||||
return rpc.NewClient(rwCloser{os.Stdin, os.Stdout})
|
||||
}
|
||||
|
||||
// NewConsumerCodec returns an rpc.Client that will consume an API from the host
|
||||
// process over this application's Stdin and Stdout using the ClientCodec
|
||||
// returned by f.
|
||||
func NewConsumerCodec(f func(io.ReadWriteCloser) rpc.ClientCodec) *rpc.Client {
|
||||
return rpc.NewClientWithCodec(f(rwCloser{os.Stdin, os.Stdout}))
|
||||
}
|
||||
|
||||
// start runs the plugin and returns an ioPipe that can be used to control the
|
||||
// plugin.
|
||||
func start(cmd commander) (_ ioPipe, err error) {
|
||||
in, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
return ioPipe{}, err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
in.Close()
|
||||
}
|
||||
}()
|
||||
out, err := cmd.StdoutPipe()
|
||||
if err != nil {
|
||||
return ioPipe{}, err
|
||||
}
|
||||
defer func() {
|
||||
if err != nil {
|
||||
out.Close()
|
||||
}
|
||||
}()
|
||||
|
||||
proc, err := cmd.Start()
|
||||
if err != nil {
|
||||
return ioPipe{}, err
|
||||
}
|
||||
return ioPipe{out, in, proc}, nil
|
||||
}
|
||||
|
||||
// makeCommand is a function that just creates an exec.Cmd and the process in
|
||||
// it. It exists to facilitate testing.
|
||||
var makeCommand = func(w io.Writer, path string, args []string) commander {
|
||||
cmd := exec.Command(path, args...)
|
||||
cmd.Stderr = w
|
||||
return execCmd{cmd}
|
||||
}
|
||||
|
||||
type execCmd struct {
|
||||
*exec.Cmd
|
||||
}
|
||||
|
||||
func (e execCmd) Start() (osProcess, error) {
|
||||
if err := e.Cmd.Start(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return e.Cmd.Process, nil
|
||||
}
|
||||
|
||||
// commander is an interface that is fulfilled by exec.Cmd and makes our testing
|
||||
// a little easier.
|
||||
type commander interface {
|
||||
StdinPipe() (io.WriteCloser, error)
|
||||
StdoutPipe() (io.ReadCloser, error)
|
||||
// Start is like exec.Cmd's start, except it also returns the os.Process if
|
||||
// start succeeds.
|
||||
Start() (osProcess, error)
|
||||
}
|
||||
|
||||
// osProcess is an interface that is fullfilled by *os.Process and makes our
|
||||
// testing a little easier.
|
||||
type osProcess interface {
|
||||
Wait() (*os.ProcessState, error)
|
||||
Kill() error
|
||||
Signal(os.Signal) error
|
||||
}
|
||||
|
||||
// ioPipe simply wraps a ReadCloser, WriteCloser, and a Process, and coordinates
|
||||
// them so they all close together.
|
||||
type ioPipe struct {
|
||||
io.ReadCloser
|
||||
io.WriteCloser
|
||||
proc osProcess
|
||||
}
|
||||
|
||||
// Close closes the pipe's WriteCloser, ReadClosers, and process.
|
||||
func (iop ioPipe) Close() error {
|
||||
err := iop.ReadCloser.Close()
|
||||
if writeErr := iop.WriteCloser.Close(); writeErr != nil {
|
||||
err = writeErr
|
||||
}
|
||||
if procErr := iop.closeProc(); procErr != nil {
|
||||
err = procErr
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// procTimeout is the timeout to wait for a process to stop after being
|
||||
// signalled. It is adjustable to keep tests fast.
|
||||
var procTimeout = time.Second
|
||||
|
||||
// closeProc sends an interrupt signal to the pipe's process, and if it doesn't
|
||||
// respond in one second, kills the process.
|
||||
func (iop ioPipe) closeProc() error {
|
||||
result := make(chan error, 1)
|
||||
go func() { _, err := iop.proc.Wait(); result <- err }()
|
||||
if err := iop.proc.Signal(os.Interrupt); err != nil {
|
||||
return err
|
||||
}
|
||||
select {
|
||||
case err := <-result:
|
||||
return err
|
||||
case <-time.After(procTimeout):
|
||||
if err := iop.proc.Kill(); err != nil {
|
||||
return fmt.Errorf("error killing process after timeout: %s", err)
|
||||
}
|
||||
return errProcStopTimeout
|
||||
}
|
||||
}
|
||||
|
||||
// rwCloser just merges a ReadCloser and a WriteCloser into a ReadWriteCloser.
|
||||
type rwCloser struct {
|
||||
io.ReadCloser
|
||||
io.WriteCloser
|
||||
}
|
||||
|
||||
// Close closes both the ReadCloser and the WriteCloser, returning the last
|
||||
// error from either.
|
||||
func (rw rwCloser) Close() error {
|
||||
err := rw.ReadCloser.Close()
|
||||
if err := rw.WriteCloser.Close(); err != nil {
|
||||
return err
|
||||
}
|
||||
return err
|
||||
}
|
||||
Reference in New Issue
Block a user