Skip to content

Commit

Permalink
Document Command in its own Markdown file, with triggers examples (#…
Browse files Browse the repository at this point in the history
…472)

Resolves #40
Part of #196 

Issue #40 showed that the `triggers` property could be confusing to
users. This PR adds more docs, including sample code.

The actual changes are all in inputs.go and command.md.
  • Loading branch information
thomas11 authored Jun 20, 2024
1 parent 40d4ab3 commit bdee886
Show file tree
Hide file tree
Showing 18 changed files with 1,117 additions and 90 deletions.
12 changes: 6 additions & 6 deletions provider/cmd/pulumi-resource-command/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,7 @@
},
"resources": {
"command:local:Command": {
"description": "A local command to be executed.\nThis command can be inserted into the life cycles of other resources using the\n`dependsOn` or `parent` resource options. A command is considered to have\nfailed when it finished with a non-zero exit code. This will fail the CRUD step\nof the `Command` resource.",
"description": "A local command to be executed.\n\nThis command can be inserted into the life cycles of other resources using the `dependsOn` or `parent` resource options. A command is considered to have failed when it finished with a non-zero exit code. This will fail the CRUD step of the `Command` resource.\n\n{{% examples %}}\n## Example Usage\n\n{{% example %}}\n### Triggers\n\nThis example defines several trigger values of various kinds. Changes to any of them will cause `cmd` to be re-run.\n\n```typescript\nimport * as pulumi from \"@pulumi/pulumi\";\nimport * as command from \"@pulumi/command\";\nimport * as random from \"@pulumi/random\";\n\nconst str = \"foo\";\nconst fileAsset = new pulumi.asset.FileAsset(\"Pulumi.yaml\");\nconst rand = new random.RandomString(\"rand\", {length: 5});\nconst localFile = new command.local.Command(\"localFile\", {\n create: \"touch foo.txt\",\n archivePaths: [\"*.txt\"],\n});\n\nconst cmd = new command.local.Command(\"cmd\", {\n create: \"echo create > op.txt\",\n delete: \"echo delete >> op.txt\",\n triggers: [\n str,\n rand.result,\n fileAsset,\n localFile.archive,\n ],\n});\n```\n\n```python\nimport pulumi\nimport pulumi_command as command\nimport pulumi_random as random\n\nfoo = \"foo\"\nfile_asset_var = pulumi.FileAsset(\"Pulumi.yaml\")\nrand = random.RandomString(\"rand\", length=5)\nlocal_file = command.local.Command(\"localFile\",\n create=\"touch foo.txt\",\n archive_paths=[\"*.txt\"])\n\ncmd = command.local.Command(\"cmd\",\n create=\"echo create > op.txt\",\n delete=\"echo delete >> op.txt\",\n triggers=[\n foo,\n rand.result,\n file_asset_var,\n local_file.archive,\n ])\n```\n\n```go\npackage main\n\nimport (\n\t\"github.com/pulumi/pulumi-command/sdk/go/command/local\"\n\t\"github.com/pulumi/pulumi-random/sdk/v4/go/random\"\n\t\"github.com/pulumi/pulumi/sdk/v3/go/pulumi\"\n)\n\nfunc main() {\n\tpulumi.Run(func(ctx *pulumi.Context) error {\n\t\tstr := pulumi.String(\"foo\")\n\n\t\tfileAsset := pulumi.NewFileAsset(\"Pulumi.yaml\")\n\n\t\trand, err := random.NewRandomString(ctx, \"rand\", &random.RandomStringArgs{\n\t\t\tLength: pulumi.Int(5),\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tlocalFile, err := local.NewCommand(ctx, \"localFile\", &local.CommandArgs{\n\t\t\tCreate: pulumi.String(\"touch foo.txt\"),\n\t\t\tArchivePaths: pulumi.StringArray{\n\t\t\t\tpulumi.String(\"*.txt\"),\n\t\t\t},\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t_, err = local.NewCommand(ctx, \"cmd\", &local.CommandArgs{\n\t\t\tCreate: pulumi.String(\"echo create > op.txt\"),\n\t\t\tDelete: pulumi.String(\"echo delete >> op.txt\"),\n\t\t\tTriggers: pulumi.Array{\n\t\t\t\tstr,\n\t\t\t\trand.Result,\n\t\t\t\tfileAsset,\n\t\t\t\tlocalFile.Archive,\n\t\t\t},\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t})\n}\n```\n\n```csharp\nusing Pulumi;\nusing Command = Pulumi.Command;\nusing Random = Pulumi.Random;\n\nreturn await Deployment.RunAsync(() =>\n{\n var str = \"foo\";\n\n var fileAssetVar = new FileAsset(\"Pulumi.yaml\");\n\n var rand = new Random.RandomString(\"rand\", new()\n {\n Length = 5,\n });\n\n var localFile = new Command.Local.Command(\"localFile\", new()\n {\n Create = \"touch foo.txt\",\n ArchivePaths = new[]\n {\n \"*.txt\",\n },\n });\n\n var cmd = new Command.Local.Command(\"cmd\", new()\n {\n Create = \"echo create > op.txt\",\n Delete = \"echo delete >> op.txt\",\n Triggers = new object[]\n {\n str,\n rand.Result,\n fileAssetVar,\n localFile.Archive,\n },\n });\n\n});\n```\n\n```java\npublic class App {\n public static void main(String[] args) {\n Pulumi.run(App::stack);\n }\n\n public static void stack(Context ctx) {\n final var fileAssetVar = new FileAsset(\"Pulumi.yaml\");\n\n var rand = new RandomString(\"rand\", RandomStringArgs.builder()\n .length(5)\n .build());\n\n var localFile = new Command(\"localFile\", CommandArgs.builder()\n .create(\"touch foo.txt\")\n .archivePaths(\"*.txt\")\n .build());\n\n var cmd = new Command(\"cmd\", CommandArgs.builder()\n .create(\"echo create > op.txt\")\n .delete(\"echo delete >> op.txt\")\n .triggers(\n rand.result(),\n fileAssetVar,\n localFile.archive())\n .build());\n\n }\n}\n```\n\n```yaml\nconfig: {}\noutputs: {}\nresources:\n rand:\n type: random:index/randomString:RandomString\n properties:\n length: 5\n\n localFile:\n type: command:local:Command\n properties:\n create: touch foo.txt\n archivePaths:\n - \"*.txt\"\n\n cmd:\n type: command:local:Command\n properties:\n create: echo create > op.txt\n delete: echo delete >> op.txt\n triggers:\n - ${rand.result}\n - ${fileAsset}\n - ${localFile.archive}\n\nvariables:\n fileAsset:\n fn::fileAsset: \"Pulumi.yaml\"\n```\n{{% /example %}}\n\n{{% /examples %}}",
"properties": {
"addPreviousOutputInEnv": {
"type": "boolean",
Expand Down Expand Up @@ -284,7 +284,7 @@
"items": {
"$ref": "pulumi.json#/Any"
},
"description": "Trigger replacements on changes to this input.",
"description": "Trigger a resource replacement on changes to any of these values. The\ntrigger values can be of any type. If a value is different in the current update compared to the\nprevious update, the resource will be replaced, i.e., the \"create\" command will be re-run.\nPlease see the resource documentation for examples.",
"replaceOnChanges": true
},
"update": {
Expand Down Expand Up @@ -356,7 +356,7 @@
"items": {
"$ref": "pulumi.json#/Any"
},
"description": "Trigger replacements on changes to this input.",
"description": "Trigger a resource replacement on changes to any of these values. The\ntrigger values can be of any type. If a value is different in the current update compared to the\nprevious update, the resource will be replaced, i.e., the \"create\" command will be re-run.\nPlease see the resource documentation for examples.",
"replaceOnChanges": true
},
"update": {
Expand All @@ -366,7 +366,7 @@
}
},
"command:remote:Command": {
"description": "A command to run on a remote host.\nThe connection is established via ssh.",
"description": "A command to run on a remote host. The connection is established via ssh.\n\n{{% examples %}}\n## Example Usage\n\n{{% example %}}\n### Triggers\n\nThis example defines several trigger values of various kinds. Changes to any of them will cause `cmd` to be re-run.\n\n```typescript\nimport * as pulumi from \"@pulumi/pulumi\";\nimport * as command from \"@pulumi/command\";\nimport * as random from \"@pulumi/random\";\n\nconst str = \"foo\";\nconst fileAsset = new pulumi.asset.FileAsset(\"Pulumi.yaml\");\nconst rand = new random.RandomString(\"rand\", {length: 5});\nconst localFile = new command.local.Command(\"localFile\", {\n create: \"touch foo.txt\",\n archivePaths: [\"*.txt\"],\n});\nconst cmd = new command.remote.Command(\"cmd\", {\n connection: {\n host: \"insert host here\",\n },\n create: \"echo create > op.txt\",\n delete: \"echo delete >> op.txt\",\n triggers: [\n str,\n rand.result,\n fileAsset,\n localFile.archive,\n ],\n});\n\n```\n\n```python\nimport pulumi\nimport pulumi_command as command\nimport pulumi_random as random\n\nfoo = \"foo\"\nfile_asset_var = pulumi.FileAsset(\"Pulumi.yaml\")\nrand = random.RandomString(\"rand\", length=5)\nlocal_file = command.local.Command(\"localFile\",\n create=\"touch foo.txt\",\n archive_paths=[\"*.txt\"])\n\ncmd = command.remote.Command(\"cmd\",\n connection=command.remote.ConnectionArgs(\n host=\"insert host here\",\n ),\n create=\"echo create > op.txt\",\n delete=\"echo delete >> op.txt\",\n triggers=[\n foo,\n rand.result,\n file_asset_var,\n local_file.archive,\n ])\n```\n\n```go\npackage main\n\nimport (\n\t\"github.com/pulumi/pulumi-command/sdk/go/command/local\"\n\t\"github.com/pulumi/pulumi-command/sdk/go/command/remote\"\n\t\"github.com/pulumi/pulumi-random/sdk/v4/go/random\"\n\t\"github.com/pulumi/pulumi/sdk/v3/go/pulumi\"\n)\n\nfunc main() {\n\tpulumi.Run(func(ctx *pulumi.Context) error {\n\t\tstr := pulumi.String(\"foo\")\n\n\t\tfileAsset := pulumi.NewFileAsset(\"Pulumi.yaml\")\n\n\t\trand, err := random.NewRandomString(ctx, \"rand\", &random.RandomStringArgs{\n\t\t\tLength: pulumi.Int(5),\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tlocalFile, err := local.NewCommand(ctx, \"localFile\", &local.CommandArgs{\n\t\t\tCreate: pulumi.String(\"touch foo.txt\"),\n\t\t\tArchivePaths: pulumi.StringArray{\n\t\t\t\tpulumi.String(\"*.txt\"),\n\t\t\t},\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t_, err = remote.NewCommand(ctx, \"cmd\", &remote.CommandArgs{\n\t\t\tConnection: &remote.ConnectionArgs{\n\t\t\t\tHost: pulumi.String(\"insert host here\"),\n\t\t\t},\n\t\t\tCreate: pulumi.String(\"echo create > op.txt\"),\n\t\t\tDelete: pulumi.String(\"echo delete >> op.txt\"),\n\t\t\tTriggers: pulumi.Array{\n\t\t\t\tstr,\n\t\t\t\trand.Result,\n\t\t\t\tfileAsset,\n\t\t\t\tlocalFile.Archive,\n\t\t\t},\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t})\n}\n```\n\n```csharp\nusing Pulumi;\nusing Command = Pulumi.Command;\nusing Random = Pulumi.Random;\n\nreturn await Deployment.RunAsync(() => \n{\n var str = \"foo\";\n\n var fileAssetVar = new FileAsset(\"Pulumi.yaml\");\n\n var rand = new Random.RandomString(\"rand\", new()\n {\n Length = 5,\n });\n\n var localFile = new Command.Local.Command(\"localFile\", new()\n {\n Create = \"touch foo.txt\",\n ArchivePaths = new[]\n {\n \"*.txt\",\n },\n });\n\n var cmd = new Command.Remote.Command(\"cmd\", new()\n {\n Connection = new Command.Remote.Inputs.ConnectionArgs\n {\n Host = \"insert host here\",\n },\n Create = \"echo create > op.txt\",\n Delete = \"echo delete >> op.txt\",\n Triggers = new object[]\n {\n str,\n rand.Result,\n fileAssetVar,\n localFile.Archive,\n },\n });\n\n});\n```\n\n```java\npublic class App {\n public static void main(String[] args) {\n Pulumi.run(App::stack);\n }\n\n public static void stack(Context ctx) {\n final var fileAssetVar = new FileAsset(\"Pulumi.yaml\");\n\n var rand = new RandomString(\"rand\", RandomStringArgs.builder()\n .length(5)\n .build());\n\n var localFile = new Command(\"localFile\", CommandArgs.builder()\n .create(\"touch foo.txt\")\n .archivePaths(\"*.txt\")\n .build());\n\n var cmd = new Command(\"cmd\", CommandArgs.builder()\n .connection(ConnectionArgs.builder()\n .host(\"insert host here\")\n .build())\n .create(\"echo create > op.txt\")\n .delete(\"echo delete >> op.txt\")\n .triggers( \n rand.result(),\n fileAssetVar,\n localFile.archive())\n .build());\n\n }\n}\n```\n\n```yaml\nconfig: {}\noutputs: {}\n\nresources:\n rand:\n type: random:index/randomString:RandomString\n properties:\n length: 5\n\n localFile:\n type: command:local:Command\n properties:\n create: touch foo.txt\n archivePaths:\n - \"*.txt\"\n\n cmd:\n type: command:remote:Command\n properties:\n connection:\n host: \"insert host here\"\n create: echo create > op.txt\n delete: echo delete >> op.txt\n triggers:\n - ${rand.result}\n - ${fileAsset}\n - ${localFile.archive}\n\nvariables:\n fileAsset:\n fn::fileAsset: \"Pulumi.yaml\"\n```\n\n{{% /example %}}\n\n{{% /examples %}}",
"properties": {
"addPreviousOutputInEnv": {
"type": "boolean",
Expand Down Expand Up @@ -414,7 +414,7 @@
"items": {
"$ref": "pulumi.json#/Any"
},
"description": "Trigger replacements on changes to this input.",
"description": "Trigger a resource replacement on changes to any of these values. The\ntrigger values can be of any type. If a value is different in the current update compared to the\nprevious update, the resource will be replaced, i.e., the \"create\" command will be re-run.\nPlease see the resource documentation for examples.",
"replaceOnChanges": true
},
"update": {
Expand Down Expand Up @@ -467,7 +467,7 @@
"items": {
"$ref": "pulumi.json#/Any"
},
"description": "Trigger replacements on changes to this input.",
"description": "Trigger a resource replacement on changes to any of these values. The\ntrigger values can be of any type. If a value is different in the current update compared to the\nprevious update, the resource will be replaced, i.e., the \"create\" command will be re-run.\nPlease see the resource documentation for examples.",
"replaceOnChanges": true
},
"update": {
Expand Down
5 changes: 4 additions & 1 deletion provider/pkg/provider/common/inputs.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@ type ResourceInputs struct {
// Annotate lets you provide descriptions and default values for fields and they will
// be visible in the provider's schema and the generated SDKs.
func (c *ResourceInputs) Annotate(a infer.Annotator) {
a.Describe(&c.Triggers, "Trigger replacements on changes to this input.")
a.Describe(&c.Triggers, `Trigger a resource replacement on changes to any of these values. The
trigger values can be of any type. If a value is different in the current update compared to the
previous update, the resource will be replaced, i.e., the "create" command will be re-run.
Please see the resource documentation for examples.`)
a.Describe(&c.Create, "The command to run on create.")
a.Describe(&c.Delete, `The command to run on delete. The environment variables PULUMI_COMMAND_STDOUT
and PULUMI_COMMAND_STDERR are set to the stdout and stderr properties of the
Expand Down
13 changes: 6 additions & 7 deletions provider/pkg/provider/local/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,18 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// This file contains metadata around the types for

package local

import (
_ "embed"

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

//go:embed command.md
var resourceDoc string

// This is the type that implements the Command resource methods.
// The methods are declared in the commandController.go file.
type Command struct{}
Expand All @@ -34,11 +37,7 @@ var _ = (infer.Annotated)((*Command)(nil))
// Implementing Annotate lets you provide descriptions and default values for resources and they will
// be visible in the provider's schema and the generated SDKs.
func (c *Command) Annotate(a infer.Annotator) {
a.Describe(&c, "A local command to be executed.\n"+
"This command can be inserted into the life cycles of other resources using the\n"+
"`dependsOn` or `parent` resource options. A command is considered to have\n"+
"failed when it finished with a non-zero exit code. This will fail the CRUD step\n"+
"of the `Command` resource.")
a.Describe(&c, resourceDoc)
}

// These are the inputs (or arguments) to a Command resource.
Expand Down
Loading

0 comments on commit bdee886

Please sign in to comment.