Add ability to provide env vars to plugins (#5359)
* Add ability to provide env vars to plugins * Update docs * Update docs with examples * Refactor TestAddTestPlugin, remove TestAddTestPluginTempDir
This commit is contained in:
parent
74ec835b3b
commit
189b893b35
|
@ -101,7 +101,7 @@ func getCluster(t *testing.T) (*vault.TestCluster, logical.SystemView) {
|
|||
os.Setenv(pluginutil.PluginCACertPEMEnv, cluster.CACertPEMFile)
|
||||
|
||||
sys := vault.TestDynamicSystemView(cores[0].Core)
|
||||
vault.TestAddTestPlugin(t, cores[0].Core, "postgresql-database-plugin", "TestBackend_PluginMain")
|
||||
vault.TestAddTestPlugin(t, cores[0].Core, "postgresql-database-plugin", "TestBackend_PluginMain", []string{}, "")
|
||||
|
||||
return cluster, sys
|
||||
}
|
||||
|
|
|
@ -94,8 +94,8 @@ func getCluster(t *testing.T) (*vault.TestCluster, logical.SystemView) {
|
|||
cores := cluster.Cores
|
||||
|
||||
sys := vault.TestDynamicSystemView(cores[0].Core)
|
||||
vault.TestAddTestPlugin(t, cores[0].Core, "test-plugin", "TestPlugin_GRPC_Main")
|
||||
vault.TestAddTestPlugin(t, cores[0].Core, "test-plugin-netRPC", "TestPlugin_NetRPC_Main")
|
||||
vault.TestAddTestPlugin(t, cores[0].Core, "test-plugin", "TestPlugin_GRPC_Main", []string{}, "")
|
||||
vault.TestAddTestPlugin(t, cores[0].Core, "test-plugin-netRPC", "TestPlugin_NetRPC_Main", []string{}, "")
|
||||
|
||||
return cluster, sys
|
||||
}
|
||||
|
|
|
@ -89,7 +89,7 @@ func testConfig(t *testing.T) (*logical.BackendConfig, func()) {
|
|||
|
||||
os.Setenv(pluginutil.PluginCACertPEMEnv, cluster.CACertPEMFile)
|
||||
|
||||
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", "TestBackend_PluginMain")
|
||||
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", "TestBackend_PluginMain", []string{}, "")
|
||||
|
||||
return config, func() {
|
||||
cluster.Cleanup()
|
||||
|
|
|
@ -43,6 +43,7 @@ type PluginRunner struct {
|
|||
Name string `json:"name" structs:"name"`
|
||||
Command string `json:"command" structs:"command"`
|
||||
Args []string `json:"args" structs:"args"`
|
||||
Env []string `json:"env" structs:"env"`
|
||||
Sha256 []byte `json:"sha256" structs:"sha256"`
|
||||
Builtin bool `json:"builtin" structs:"builtin"`
|
||||
BuiltinFactory func() (interface{}, error) `json:"-" structs:"-"`
|
||||
|
@ -65,6 +66,10 @@ func (r *PluginRunner) RunMetadataMode(ctx context.Context, wrapper RunnerUtil,
|
|||
|
||||
func (r *PluginRunner) runCommon(ctx context.Context, wrapper RunnerUtil, pluginMap map[string]plugin.Plugin, hs plugin.HandshakeConfig, env []string, logger log.Logger, isMetadataMode bool) (*plugin.Client, error) {
|
||||
cmd := exec.Command(r.Command, r.Args...)
|
||||
|
||||
// `env` should always go last to avoid overwriting internal values that might
|
||||
// have been provided externally.
|
||||
cmd.Env = append(cmd.Env, r.Env...)
|
||||
cmd.Env = append(cmd.Env, env...)
|
||||
|
||||
// Add the mlock setting to the ENV of the plugin
|
||||
|
|
|
@ -50,7 +50,7 @@ func getPluginClusterAndCore(t testing.TB, logger log.Logger) (*vault.TestCluste
|
|||
os.Setenv(pluginutil.PluginCACertPEMEnv, cluster.CACertPEMFile)
|
||||
|
||||
vault.TestWaitActive(t, core.Core)
|
||||
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", "TestPlugin_PluginMain")
|
||||
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", "TestPlugin_PluginMain", []string{}, "")
|
||||
|
||||
// Mount the mock plugin
|
||||
err = core.Client.Sys().Mount("mock", &api.MountInput{
|
||||
|
|
|
@ -269,7 +269,7 @@ func (b *SystemBackend) handlePluginCatalogUpdate(ctx context.Context, req *logi
|
|||
return logical.ErrorResponse("missing command value"), nil
|
||||
}
|
||||
|
||||
// For backwards compatibility, also accept args as part of command. Don't
|
||||
// For backwards compatibility, also accept args as part of command. Don't
|
||||
// accepts args in both command and args.
|
||||
args := d.Get("args").([]string)
|
||||
parts := strings.Split(command, " ")
|
||||
|
@ -281,12 +281,14 @@ func (b *SystemBackend) handlePluginCatalogUpdate(ctx context.Context, req *logi
|
|||
args = parts[1:]
|
||||
}
|
||||
|
||||
env := d.Get("env").([]string)
|
||||
|
||||
sha256Bytes, err := hex.DecodeString(sha256)
|
||||
if err != nil {
|
||||
return logical.ErrorResponse("Could not decode SHA-256 value from Hex"), err
|
||||
}
|
||||
|
||||
err = b.Core.pluginCatalog.Set(ctx, pluginName, parts[0], args, sha256Bytes)
|
||||
err = b.Core.pluginCatalog.Set(ctx, pluginName, parts[0], args, env, sha256Bytes)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -3526,6 +3528,11 @@ plugin directory.`,
|
|||
`The args passed to plugin command.`,
|
||||
"",
|
||||
},
|
||||
"plugin-catalog_env": {
|
||||
`The environment variables passed to plugin command.
|
||||
Each entry is of the form "key=value".`,
|
||||
"",
|
||||
},
|
||||
"leases": {
|
||||
`View or list lease metadata.`,
|
||||
`
|
||||
|
|
|
@ -20,6 +20,11 @@ import (
|
|||
"github.com/hashicorp/vault/vault"
|
||||
)
|
||||
|
||||
const (
|
||||
expectedEnvKey = "FOO"
|
||||
expectedEnvValue = "BAR"
|
||||
)
|
||||
|
||||
func TestSystemBackend_Plugin_secret(t *testing.T) {
|
||||
cluster := testSystemBackendMock(t, 1, 1, logical.TypeLogical)
|
||||
defer cluster.Cleanup()
|
||||
|
@ -103,7 +108,7 @@ func TestSystemBackend_Plugin_MismatchType(t *testing.T) {
|
|||
core := cluster.Cores[0]
|
||||
|
||||
// Replace the plugin with a credential backend
|
||||
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", "TestBackend_PluginMainCredentials")
|
||||
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", "TestBackend_PluginMainCredentials", []string{}, "")
|
||||
|
||||
// Make a request to lazy load the now-credential plugin
|
||||
// and expect an error
|
||||
|
@ -178,7 +183,7 @@ func testPlugin_CatalogRemoved(t *testing.T, btype logical.BackendType, testMoun
|
|||
switch btype {
|
||||
case logical.TypeLogical:
|
||||
// Add plugin back to the catalog
|
||||
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", "TestBackend_PluginMainLogical")
|
||||
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", "TestBackend_PluginMainLogical", []string{}, "")
|
||||
_, err = core.Client.Logical().Write("sys/mounts/mock-0", map[string]interface{}{
|
||||
"type": "plugin",
|
||||
"config": map[string]interface{}{
|
||||
|
@ -187,7 +192,7 @@ func testPlugin_CatalogRemoved(t *testing.T, btype logical.BackendType, testMoun
|
|||
})
|
||||
case logical.TypeCredential:
|
||||
// Add plugin back to the catalog
|
||||
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", "TestBackend_PluginMainCredentials")
|
||||
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", "TestBackend_PluginMainCredentials", []string{}, "")
|
||||
_, err = core.Client.Logical().Write("sys/auth/mock-0", map[string]interface{}{
|
||||
"type": "plugin",
|
||||
"plugin_name": "mock-plugin",
|
||||
|
@ -283,9 +288,9 @@ func testPlugin_continueOnError(t *testing.T, btype logical.BackendType, mismatc
|
|||
// Re-add the plugin to the catalog
|
||||
switch btype {
|
||||
case logical.TypeLogical:
|
||||
vault.TestAddTestPluginTempDir(t, core.Core, "mock-plugin", "TestBackend_PluginMainLogical", cluster.TempDir)
|
||||
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", "TestBackend_PluginMainLogical", []string{}, cluster.TempDir)
|
||||
case logical.TypeCredential:
|
||||
vault.TestAddTestPluginTempDir(t, core.Core, "mock-plugin", "TestBackend_PluginMainCredentials", cluster.TempDir)
|
||||
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", "TestBackend_PluginMainCredentials", []string{}, cluster.TempDir)
|
||||
}
|
||||
|
||||
// Reload the plugin
|
||||
|
@ -480,7 +485,7 @@ func testSystemBackendMock(t *testing.T, numCores, numMounts int, backendType lo
|
|||
|
||||
switch backendType {
|
||||
case logical.TypeLogical:
|
||||
vault.TestAddTestPluginTempDir(t, core.Core, "mock-plugin", "TestBackend_PluginMainLogical", tempDir)
|
||||
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", "TestBackend_PluginMainLogical", []string{}, tempDir)
|
||||
for i := 0; i < numMounts; i++ {
|
||||
// Alternate input styles for plugin_name on every other mount
|
||||
options := map[string]interface{}{
|
||||
|
@ -502,7 +507,7 @@ func testSystemBackendMock(t *testing.T, numCores, numMounts int, backendType lo
|
|||
}
|
||||
}
|
||||
case logical.TypeCredential:
|
||||
vault.TestAddTestPluginTempDir(t, core.Core, "mock-plugin", "TestBackend_PluginMainCredentials", tempDir)
|
||||
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", "TestBackend_PluginMainCredentials", []string{}, tempDir)
|
||||
for i := 0; i < numMounts; i++ {
|
||||
// Alternate input styles for plugin_name on every other mount
|
||||
options := map[string]interface{}{
|
||||
|
@ -530,6 +535,58 @@ func testSystemBackendMock(t *testing.T, numCores, numMounts int, backendType lo
|
|||
return cluster
|
||||
}
|
||||
|
||||
func TestSystemBackend_Plugin_Env(t *testing.T) {
|
||||
kvPair := fmt.Sprintf("%s=%s", expectedEnvKey, expectedEnvValue)
|
||||
cluster := testSystemBackend_SingleCluster_Env(t, []string{kvPair})
|
||||
defer cluster.Cleanup()
|
||||
}
|
||||
|
||||
// testSystemBackend_SingleCluster_Env is a helper func that returns a single
|
||||
// cluster and a single mounted plugin logical backend.
|
||||
func testSystemBackend_SingleCluster_Env(t *testing.T, env []string) *vault.TestCluster {
|
||||
coreConfig := &vault.CoreConfig{
|
||||
LogicalBackends: map[string]logical.Factory{
|
||||
"plugin": plugin.Factory,
|
||||
},
|
||||
}
|
||||
|
||||
// Create a tempdir, cluster.Cleanup will clean up this directory
|
||||
tempDir, err := ioutil.TempDir("", "vault-test-cluster")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
cluster := vault.NewTestCluster(t, coreConfig, &vault.TestClusterOptions{
|
||||
HandlerFunc: vaulthttp.Handler,
|
||||
KeepStandbysSealed: true,
|
||||
NumCores: 1,
|
||||
TempDir: tempDir,
|
||||
})
|
||||
cluster.Start()
|
||||
|
||||
core := cluster.Cores[0]
|
||||
vault.TestWaitActive(t, core.Core)
|
||||
client := core.Client
|
||||
|
||||
os.Setenv(pluginutil.PluginCACertPEMEnv, cluster.CACertPEMFile)
|
||||
|
||||
vault.TestAddTestPlugin(t, core.Core, "mock-plugin", "TestBackend_PluginMainEnv", env, tempDir)
|
||||
options := map[string]interface{}{
|
||||
"type": "plugin",
|
||||
"plugin_name": "mock-plugin",
|
||||
}
|
||||
|
||||
resp, err := client.Logical().Write("sys/mounts/mock", options)
|
||||
if err != nil {
|
||||
t.Fatalf("err: %v", err)
|
||||
}
|
||||
if resp != nil {
|
||||
t.Fatalf("bad: %v", resp)
|
||||
}
|
||||
|
||||
return cluster
|
||||
}
|
||||
|
||||
func TestBackend_PluginMainLogical(t *testing.T) {
|
||||
args := []string{}
|
||||
if os.Getenv(pluginutil.PluginUnwrapTokenEnv) == "" && os.Getenv(pluginutil.PluginMetadataModeEnv) != "true" {
|
||||
|
@ -588,6 +645,41 @@ func TestBackend_PluginMainCredentials(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
// TestBackend_PluginMainEnv is a mock plugin that simply checks for the existence of FOO env var.
|
||||
func TestBackend_PluginMainEnv(t *testing.T) {
|
||||
actual := os.Getenv(expectedEnvKey)
|
||||
if actual != expectedEnvValue {
|
||||
t.Fatalf("expected: %q, got: %q", expectedEnvValue, actual)
|
||||
}
|
||||
|
||||
args := []string{}
|
||||
if os.Getenv(pluginutil.PluginUnwrapTokenEnv) == "" && os.Getenv(pluginutil.PluginMetadataModeEnv) != "true" {
|
||||
return
|
||||
}
|
||||
|
||||
caPEM := os.Getenv(pluginutil.PluginCACertPEMEnv)
|
||||
if caPEM == "" {
|
||||
t.Fatal("CA cert not passed in")
|
||||
}
|
||||
args = append(args, fmt.Sprintf("--ca-cert=%s", caPEM))
|
||||
|
||||
apiClientMeta := &pluginutil.APIClientMeta{}
|
||||
flags := apiClientMeta.FlagSet()
|
||||
flags.Parse(args)
|
||||
tlsConfig := apiClientMeta.GetTLSConfig()
|
||||
tlsProviderFunc := pluginutil.VaultPluginTLSProvider(tlsConfig)
|
||||
|
||||
factoryFunc := mock.FactoryType(logical.TypeLogical)
|
||||
|
||||
err := lplugin.Serve(&lplugin.ServeOpts{
|
||||
BackendFactoryFunc: factoryFunc,
|
||||
TLSProviderFunc: tlsProviderFunc,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSystemBackend_InternalUIResultantACL(t *testing.T) {
|
||||
cluster := vault.NewTestCluster(t, nil, &vault.TestClusterOptions{
|
||||
HandlerFunc: vaulthttp.Handler,
|
||||
|
|
|
@ -293,6 +293,10 @@ func (b *SystemBackend) pluginsCatalogPath() *framework.Path {
|
|||
Type: framework.TypeStringSlice,
|
||||
Description: strings.TrimSpace(sysHelp["plugin-catalog_args"][0]),
|
||||
},
|
||||
"env": &framework.FieldSchema{
|
||||
Type: framework.TypeStringSlice,
|
||||
Description: strings.TrimSpace(sysHelp["plugin-catalog_env"][0]),
|
||||
},
|
||||
},
|
||||
|
||||
Callbacks: map[logical.Operation]framework.OperationFunc{
|
||||
|
|
|
@ -87,7 +87,7 @@ func (c *PluginCatalog) Get(ctx context.Context, name string) (*pluginutil.Plugi
|
|||
|
||||
// Set registers a new external plugin with the catalog, or updates an existing
|
||||
// external plugin. It takes the name, command and SHA256 of the plugin.
|
||||
func (c *PluginCatalog) Set(ctx context.Context, name, command string, args []string, sha256 []byte) error {
|
||||
func (c *PluginCatalog) Set(ctx context.Context, name, command string, args []string, env []string, sha256 []byte) error {
|
||||
if c.directory == "" {
|
||||
return ErrDirectoryNotConfigured
|
||||
}
|
||||
|
@ -122,6 +122,7 @@ func (c *PluginCatalog) Set(ctx context.Context, name, command string, args []st
|
|||
Name: name,
|
||||
Command: command,
|
||||
Args: args,
|
||||
Env: env,
|
||||
Sha256: sha256,
|
||||
Builtin: false,
|
||||
}
|
||||
|
|
|
@ -52,7 +52,7 @@ func TestPluginCatalog_CRUD(t *testing.T) {
|
|||
defer file.Close()
|
||||
|
||||
command := fmt.Sprintf("%s", filepath.Base(file.Name()))
|
||||
err = core.pluginCatalog.Set(context.Background(), "mysql-database-plugin", command, []string{"--test"}, []byte{'1'})
|
||||
err = core.pluginCatalog.Set(context.Background(), "mysql-database-plugin", command, []string{"--test"}, []string{"FOO=BAR"}, []byte{'1'})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -67,6 +67,7 @@ func TestPluginCatalog_CRUD(t *testing.T) {
|
|||
Name: "mysql-database-plugin",
|
||||
Command: filepath.Join(sym, filepath.Base(file.Name())),
|
||||
Args: []string{"--test"},
|
||||
Env: []string{"FOO=BAR"},
|
||||
Sha256: []byte{'1'},
|
||||
Builtin: false,
|
||||
}
|
||||
|
@ -141,13 +142,13 @@ func TestPluginCatalog_List(t *testing.T) {
|
|||
defer file.Close()
|
||||
|
||||
command := filepath.Base(file.Name())
|
||||
err = core.pluginCatalog.Set(context.Background(), "mysql-database-plugin", command, []string{"--test"}, []byte{'1'})
|
||||
err = core.pluginCatalog.Set(context.Background(), "mysql-database-plugin", command, []string{"--test"}, []string{}, []byte{'1'})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Set another plugin
|
||||
err = core.pluginCatalog.Set(context.Background(), "aaaaaaa", command, []string{"--test"}, []byte{'1'})
|
||||
err = core.pluginCatalog.Set(context.Background(), "aaaaaaa", command, []string{"--test"}, []string{}, []byte{'1'})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -347,79 +347,49 @@ func TestDynamicSystemView(c *Core) *dynamicSystemView {
|
|||
}
|
||||
|
||||
// TestAddTestPlugin registers the testFunc as part of the plugin command to the
|
||||
// plugin catalog.
|
||||
func TestAddTestPlugin(t testing.T, c *Core, name, testFunc string) {
|
||||
// plugin catalog. If provided, uses tmpDir as the plugin directory.
|
||||
func TestAddTestPlugin(t testing.T, c *Core, name, testFunc string, env []string, tempDir string) {
|
||||
file, err := os.Open(os.Args[0])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
hash := sha256.New()
|
||||
dirPath := filepath.Dir(os.Args[0])
|
||||
fileName := filepath.Base(os.Args[0])
|
||||
|
||||
_, err = io.Copy(hash, file)
|
||||
if tempDir != "" {
|
||||
fi, err := file.Stat()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Copy over the file to the temp dir
|
||||
dst := filepath.Join(tempDir, fileName)
|
||||
out, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, fi.Mode())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
if _, err = io.Copy(out, file); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = out.Sync()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
dirPath = tempDir
|
||||
}
|
||||
|
||||
// Determine plugin directory full path, evaluating potential symlink path
|
||||
fullPath, err := filepath.EvalSymlinks(dirPath)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
sum := hash.Sum(nil)
|
||||
|
||||
// Determine plugin directory path
|
||||
fullPath, err := filepath.EvalSymlinks(os.Args[0])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
directoryPath := filepath.Dir(fullPath)
|
||||
|
||||
// Set core's plugin directory and plugin catalog directory
|
||||
c.pluginDirectory = directoryPath
|
||||
c.pluginCatalog.directory = directoryPath
|
||||
|
||||
command := fmt.Sprintf("%s", filepath.Base(os.Args[0]))
|
||||
args := []string{fmt.Sprintf("--test.run=%s", testFunc)}
|
||||
err = c.pluginCatalog.Set(context.Background(), name, command, args, sum)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestAddTestPluginTempDir registers the testFunc as part of the plugin command to the
|
||||
// plugin catalog. It uses tmpDir as the plugin directory.
|
||||
func TestAddTestPluginTempDir(t testing.T, c *Core, name, testFunc, tempDir string) {
|
||||
file, err := os.Open(os.Args[0])
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
fi, err := file.Stat()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Copy over the file to the temp dir
|
||||
dst := filepath.Join(tempDir, filepath.Base(os.Args[0]))
|
||||
out, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, fi.Mode())
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer out.Close()
|
||||
|
||||
if _, err = io.Copy(out, file); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
err = out.Sync()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
// Determine plugin directory full path
|
||||
fullPath, err := filepath.EvalSymlinks(tempDir)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
reader, err := os.Open(filepath.Join(fullPath, filepath.Base(os.Args[0])))
|
||||
reader, err := os.Open(filepath.Join(fullPath, fileName))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
@ -439,9 +409,8 @@ func TestAddTestPluginTempDir(t testing.T, c *Core, name, testFunc, tempDir stri
|
|||
c.pluginDirectory = fullPath
|
||||
c.pluginCatalog.directory = fullPath
|
||||
|
||||
command := fmt.Sprintf("%s", filepath.Base(os.Args[0]))
|
||||
args := []string{fmt.Sprintf("--test.run=%s", testFunc)}
|
||||
err = c.pluginCatalog.Set(context.Background(), name, command, args, sum)
|
||||
err = c.pluginCatalog.Set(context.Background(), name, fileName, args, env, sum)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
|
|
@ -67,8 +67,15 @@ supplied name.
|
|||
they do not match the plugin can not be run.
|
||||
|
||||
- `command` `(string: <required>)` – Specifies the command used to execute the
|
||||
plugin. This is relative to the plugin directory. e.g. `"myplugin
|
||||
--my_flag=1"`
|
||||
plugin. This is relative to the plugin directory. e.g. `"myplugin"`.
|
||||
|
||||
- `args` `(array: [])` – Specifies the arguments used to execute the plugin. If
|
||||
the arguments are provided here, the `command` parameter should only contain
|
||||
the named program. e.g. `"--my_flag=1"`.
|
||||
|
||||
- `env` `(array: [])` – Specifies the environment variables used during the
|
||||
execution of the plugin. Each entry is of the form "key=value". e.g
|
||||
`"FOO=BAR"`.
|
||||
|
||||
### Sample Payload
|
||||
|
||||
|
|
Loading…
Reference in New Issue