Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions provider/pkg/provider/common/inputs.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,21 +19,26 @@ type ResourceInputs struct {
func (c *ResourceInputs) Annotate(a infer.Annotator) {
a.Describe(&c.Triggers, "The resource will be updated (or replaced) if any of these values change.\n\n"+
"The trigger values can be of any type.\n\n"+
"If the `update` command was provided the resource will be updated, otherwise it will be replaced using the `create` command.\n\n"+
"If the `update` command was provided the resource will be updated, otherwise it will be replaced "+
"using the `create` command.\n\n"+
"Please see the resource documentation for examples.",
)
a.Describe(&c.Create, "The command to run once on resource creation.\n\n"+
"If an `update` command isn't provided, then `create` will also be run when the resource's inputs are modified.\n\n"+
"Note that this command will not be executed if the resource has already been created and its inputs are unchanged.\n\n"+
"If an `update` command isn't provided, then `create` will also be run when the resource's "+
"inputs are modified.\n\n"+
"Note that this command will not be executed if the resource has already been created and "+
"its inputs are unchanged.\n\n"+
"Use `local.runOutput` if you need to run a command on every execution of your program.",
)
a.Describe(&c.Delete, "The command to run on resource delettion.\n\n"+
"The environment variables `PULUMI_COMMAND_STDOUT` and `PULUMI_COMMAND_STDERR` are set to the stdout and stderr properties of the Command resource from previous create or update steps.",
"The environment variables `PULUMI_COMMAND_STDOUT` and `PULUMI_COMMAND_STDERR` are set to "+
"the stdout and stderr properties of the Command resource from previous create or update steps.",
)
a.Describe(&c.Update, "The command to run when the resource is updated.\n\n"+
"If empty, the create command will be executed instead.\n\n"+
"Note that this command will not run if the resource's inputs are unchanged.\n\n"+
"Use `local.runOutput` if you need to run a command on every execution of your program.\n\n"+
"The environment variables `PULUMI_COMMAND_STDOUT` and `PULUMI_COMMAND_STDERR` are set to the `stdout` and `stderr` properties of the Command resource from previous create or update steps.",
"The environment variables `PULUMI_COMMAND_STDOUT` and `PULUMI_COMMAND_STDERR` are set to "+
"the `stdout` and `stderr` properties of the Command resource from previous create or update steps.",
)
}
6 changes: 4 additions & 2 deletions provider/pkg/provider/local/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ When specifying glob patterns the following rules apply:
- Rules are evaluated in order, so exclude rules should be after inclusion rules.
- `+"`*`"+` matches anything except `+"`/`"+`
- `+"`**`"+` matches anything, _including_ `+"`/`"+`
- All returned paths are relative to the working directory (without leading `+"`./`) e.g. `file.text` or `subfolder/file.txt`."+`
- All returned paths are relative to the working directory (without leading `+
"`./`) e.g. `file.text` or `subfolder/file.txt`."+`
- For full details of the globbing syntax, see [github.com/gobwas/glob](https://github.com/gobwas/glob)

#### Example
Expand Down Expand Up @@ -91,7 +92,8 @@ When specifying glob patterns the following rules apply:
- Rules are evaluated in order, so exclude rules should be after inclusion rules.
- `+"`*`"+` matches anything except `+"`/`"+`
- `+"`**`"+` matches anything, _including_ `+"`/`"+`
- All returned paths are relative to the working directory (without leading `+"`./`"+`) e.g. `+"`file.text` or `subfolder/file.txt`"+`.
- All returned paths are relative to the working directory (without leading `+
"`./`"+`) e.g. `+"`file.text` or `subfolder/file.txt`"+`.
- For full details of the globbing syntax, see [github.com/gobwas/glob](https://github.com/gobwas/glob)

#### Example
Expand Down
3 changes: 2 additions & 1 deletion provider/pkg/provider/local/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ package local
import (
_ "embed"

"github.com/pulumi/pulumi-command/provider/pkg/provider/common"
"github.com/pulumi/pulumi-go-provider/infer"

"github.com/pulumi/pulumi-command/provider/pkg/provider/common"
)

//go:embed command.md
Expand Down
15 changes: 11 additions & 4 deletions provider/pkg/provider/local/commandController.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,20 @@ import (
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
)

// The following statements are not required. They are type assertions to indicate to Go that Command implements the following interfaces.
// If the function signature doesn't match or isn't implemented, we get nice compile time errors at this location.
// The following statements are not required. They are type assertions to indicate to Go that Command
// implements the following interfaces. If the function signature doesn't match or isn't implemented,
// we get nice compile time errors at this location.
var (
_ = (infer.CustomResource[CommandInputs, CommandOutputs])((*Command)(nil))
_ = (infer.CustomUpdate[CommandInputs, CommandOutputs])((*Command)(nil))
_ = (infer.CustomDelete[CommandOutputs])((*Command)(nil))
)

// This is the Create method. This will be run on every Command resource creation.
func (c *Command) Create(ctx context.Context, req infer.CreateRequest[CommandInputs]) (infer.CreateResponse[CommandOutputs], error) {
func (c *Command) Create(
ctx context.Context,
req infer.CreateRequest[CommandInputs],
) (infer.CreateResponse[CommandOutputs], error) {
name := req.Name
input := req.Inputs
preview := req.DryRun
Expand Down Expand Up @@ -60,7 +64,10 @@ func (c *Command) Create(ctx context.Context, req infer.CreateRequest[CommandInp
// Because we want every output to depend on every input, we can leave the default behavior.

// The Update method will be run on every update.
func (c *Command) Update(ctx context.Context, req infer.UpdateRequest[CommandInputs, CommandOutputs]) (infer.UpdateResponse[CommandOutputs], error) {
func (c *Command) Update(
ctx context.Context,
req infer.UpdateRequest[CommandInputs, CommandOutputs],
) (infer.UpdateResponse[CommandOutputs], error) {
olds := req.State
news := req.Inputs
preview := req.DryRun
Expand Down
8 changes: 5 additions & 3 deletions provider/pkg/provider/local/commandController_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@ import (
"strings"
"testing"

"github.com/pulumi/pulumi-command/provider/pkg/provider/common"
"github.com/pulumi/pulumi-command/provider/pkg/provider/util/testutil"
"github.com/stretchr/testify/require"

"github.com/pulumi/pulumi-go-provider/infer"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
"github.com/stretchr/testify/require"

"github.com/pulumi/pulumi-command/provider/pkg/provider/common"
"github.com/pulumi/pulumi-command/provider/pkg/provider/util/testutil"
)

func TestOptionalLogging(t *testing.T) {
Expand Down
6 changes: 4 additions & 2 deletions provider/pkg/provider/local/commandOutputs.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import (
"strings"

"github.com/gobwas/glob"

"github.com/pulumi/pulumi-go-provider/infer/types"
"github.com/pulumi/pulumi/sdk/v3/go/common/diag"
"github.com/pulumi/pulumi/sdk/v3/go/common/resource"
Expand Down Expand Up @@ -54,6 +55,7 @@ func run(ctx context.Context, command string, in BaseInputs, out *BaseOutputs, l
stdouterrwriter := util.ConcurrentWriter{Writer: &stdouterrbuf}
loggingReader, loggingWriter := io.Pipe()

//nolint:gosec // G204: This is a command execution provider, running user-specified commands is the intended behavior
cmd := exec.CommandContext(ctx, args[0], args[1:]...)

stdoutWriters := []io.Writer{&stdoutbuf, &stdouterrwriter}
Expand Down Expand Up @@ -85,10 +87,10 @@ func run(ctx context.Context, command string, in BaseInputs, out *BaseOutputs, l

if in.AddPreviousOutputInEnv == nil || *in.AddPreviousOutputInEnv {
if out.Stdout != "" {
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", util.PULUMI_COMMAND_STDOUT, out.Stdout))
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", util.PulumiCommandStdout, out.Stdout))
}
if out.Stderr != "" {
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", util.PULUMI_COMMAND_STDERR, out.Stderr))
cmd.Env = append(cmd.Env, fmt.Sprintf("%s=%s", util.PulumiCommandStderr, out.Stderr))
}
}

Expand Down
5 changes: 3 additions & 2 deletions provider/pkg/provider/local/logging.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
package local

// TODO This file (along with logging_test.go) should be in the `common` package since its contents are used by `local`` and `remote`.
// It's duplicated in `local` and `remote` for the time being due to pulumi/pulumi#16221, and changes need to be made in both copies.
// TODO This file (along with logging_test.go) should be in the `common` package since its contents
// are used by `local`` and `remote`. It's duplicated in `local` and `remote` for the time being due
// to pulumi/pulumi#16221, and changes need to be made in both copies.

import "github.com/pulumi/pulumi-go-provider/infer"

Expand Down
3 changes: 2 additions & 1 deletion provider/pkg/provider/local/logging_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ package local_test
import (
"testing"

"github.com/pulumi/pulumi-command/provider/pkg/provider/local"
"github.com/stretchr/testify/assert"

"github.com/pulumi/pulumi-command/provider/pkg/provider/local"
)

func TestShouldLog(t *testing.T) {
Expand Down
5 changes: 4 additions & 1 deletion provider/pkg/provider/local/runController.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,10 @@ import (

// Invoke takes a RunInputs parameter and runs the command specified in
// it.
func (*Run) Invoke(ctx context.Context, req infer.FunctionRequest[RunInputs]) (infer.FunctionResponse[RunOutputs], error) {
func (*Run) Invoke(
ctx context.Context,
req infer.FunctionRequest[RunInputs],
) (infer.FunctionResponse[RunOutputs], error) {
input := req.Input
r := RunOutputs{RunInputs: input}
err := run(ctx, input.Command, r.RunInputs.BaseInputs, &r.BaseOutputs, input.Logging)
Expand Down
3 changes: 2 additions & 1 deletion provider/pkg/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ func NewProvider() p.Provider {
// This is the metadata for the provider
Metadata: schema.Metadata{
DisplayName: "Command",
Description: "The Pulumi Command Provider enables you to execute commands and scripts either locally or remotely as part of the Pulumi resource model.",
Description: "The Pulumi Command Provider enables you to execute commands and scripts either " +
"locally or remotely as part of the Pulumi resource model.",
Keywords: []string{
"pulumi",
"command",
Expand Down
2 changes: 1 addition & 1 deletion provider/pkg/provider/remote/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ type CommandInputs struct {
// provider:"secret" specifies that a field should be marked secret.
Stdin *string `pulumi:"stdin,optional"`
Logging *Logging `pulumi:"logging,optional"`
Connection *Connection `pulumi:"connection" provider:"secret"`
Connection *Connection `pulumi:"connection" provider:"secret"`
Environment map[string]string `pulumi:"environment,optional"`
AddPreviousOutputInEnv *bool `pulumi:"addPreviousOutputInEnv,optional"`
}
Expand Down
16 changes: 11 additions & 5 deletions provider/pkg/provider/remote/commandController.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ var _ = (infer.CustomUpdate[CommandInputs, CommandOutputs])((*Command)(nil))
var _ = (infer.CustomDelete[CommandOutputs])((*Command)(nil))

// This is the Create method. This will be run on every Command resource creation.
func (*Command) Create(ctx context.Context, req infer.CreateRequest[CommandInputs]) (infer.CreateResponse[CommandOutputs], error) {
func (*Command) Create(
ctx context.Context,
req infer.CreateRequest[CommandInputs],
) (infer.CreateResponse[CommandOutputs], error) {
name := req.Name
input := req.Inputs
preview := req.DryRun
Expand All @@ -42,12 +45,12 @@ func (*Command) Create(ctx context.Context, req infer.CreateRequest[CommandInput
return infer.CreateResponse[CommandOutputs]{ID: id, Output: state}, nil
}

if state.Create == nil {
if input.Create == nil {
return infer.CreateResponse[CommandOutputs]{ID: id, Output: state}, nil
}
cmd := ""
if state.Create != nil {
cmd = *state.Create
if input.Create != nil {
cmd = *input.Create
}

if !preview {
Expand All @@ -57,7 +60,10 @@ func (*Command) Create(ctx context.Context, req infer.CreateRequest[CommandInput
}

// The Update method will be run on every update.
func (*Command) Update(ctx context.Context, req infer.UpdateRequest[CommandInputs, CommandOutputs]) (infer.UpdateResponse[CommandOutputs], error) {
func (*Command) Update(
ctx context.Context,
req infer.UpdateRequest[CommandInputs, CommandOutputs],
) (infer.UpdateResponse[CommandOutputs], error) {
olds := req.State
news := req.Inputs
preview := req.DryRun
Expand Down
10 changes: 6 additions & 4 deletions provider/pkg/provider/remote/commandController_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ import (
"testing"

"github.com/gliderlabs/ssh"
"github.com/pulumi/pulumi-command/provider/pkg/provider/common"
"github.com/pulumi/pulumi-command/provider/pkg/provider/util/testutil"
"github.com/stretchr/testify/require"

"github.com/pulumi/pulumi-go-provider/infer"
"github.com/pulumi/pulumi/sdk/v3/go/pulumi"
"github.com/stretchr/testify/require"

"github.com/pulumi/pulumi-command/provider/pkg/provider/common"
"github.com/pulumi/pulumi-command/provider/pkg/provider/util/testutil"
)

func TestOptionalLogging(t *testing.T) {
Expand All @@ -39,7 +41,7 @@ func TestOptionalLogging(t *testing.T) {
t.Parallel()

// This SSH server always writes "foo" to stdout and "bar" to stderr, no matter the command.
server := testutil.NewTestSshServer(t, func(s ssh.Session) {
server := testutil.NewTestSSHServer(t, func(s ssh.Session) {
_, err := io.WriteString(s, "foo")
require.NoError(t, err)
_, err = io.WriteString(s.Stderr(), "bar")
Expand Down
12 changes: 6 additions & 6 deletions provider/pkg/provider/remote/commandOutputs.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,29 +43,29 @@ func (c *CommandOutputs) run(ctx context.Context, cmd string, logging *Logging)
for k, v := range c.Environment {
err := session.Setenv(k, v)
if err != nil {
return logAndWrapSetenvErr(diag.Error, k, ctx, err)
return logAndWrapSetenvErr(ctx, diag.Error, k, err)
}
}
}

if c.AddPreviousOutputInEnv == nil || *c.AddPreviousOutputInEnv {
// Set remote Stdout and Stderr environment variables optimistically, but log and continue if they fail.
if c.Stdout != "" {
err := session.Setenv(util.PULUMI_COMMAND_STDOUT, c.Stdout)
err := session.Setenv(util.PulumiCommandStdout, c.Stdout)
if err != nil {
// Set remote Stdout var optimistically, but warn and continue on failure.
//
//nolint:errcheck
logAndWrapSetenvErr(diag.Warning, util.PULUMI_COMMAND_STDOUT, ctx, err)
logAndWrapSetenvErr(ctx, diag.Warning, util.PulumiCommandStdout, err)
}
}
if c.Stderr != "" {
err := session.Setenv(util.PULUMI_COMMAND_STDERR, c.Stderr)
err := session.Setenv(util.PulumiCommandStderr, c.Stderr)
if err != nil {
// Set remote STDERR var optimistically, but warn and continue on failure.
//
//nolint:errcheck
logAndWrapSetenvErr(diag.Warning, util.PULUMI_COMMAND_STDERR, ctx, err)
logAndWrapSetenvErr(ctx, diag.Warning, util.PulumiCommandStderr, err)
}
}
}
Expand Down Expand Up @@ -107,7 +107,7 @@ func (c *CommandOutputs) run(ctx context.Context, cmd string, logging *Logging)
return nil
}

func logAndWrapSetenvErr(severity diag.Severity, key string, ctx context.Context, err error) error {
func logAndWrapSetenvErr(ctx context.Context, severity diag.Severity, key string, err error) error {
l := p.GetLogger(ctx)
msg := fmt.Sprintf(`Unable to set '%s'. This only works if your SSH server is configured to accept
these variables via AcceptEnv. Alternatively, if a Bash-like shell runs the command on the remote host, you could
Expand Down
Loading