Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Remote FileCopy #21

Merged
merged 9 commits into from
Dec 16, 2021
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
16 changes: 14 additions & 2 deletions examples/ec2_remote/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,17 +46,29 @@ const hostname = new remote.Command("hostname", {
create: "hostname",
});

const remotePrivateIP = new remote.Command("remotePrivateIP", {
new remote.Command("remotePrivateIP", {
connection,
create: interpolate`echo ${server.privateIp} > private_ip.txt`,
delete: `rm private_ip.txt`,
}, { deleteBeforeReplace: true });

const localPrivateIP = new local.Command("localPrivateIP", {
new local.Command("localPrivateIP", {
create: interpolate`echo ${server.privateIp} > private_ip.txt`,
delete: `rm private_ip.txt`,
}, { deleteBeforeReplace: true });

const sizeFile = new remote.CopyFile("size", {
connection,
localPath: "./size.ts",
remotePath: "size.ts",
})

const catSize = new remote.Command("checkSize", {
connection,
create: "cat size.ts",
}, { dependsOn: sizeFile })

export const confirmSize = catSize.stdout;
export const publicIp = server.publicIp;
export const publicHostName = server.publicDns;
export const hostnameStdout = hostname.stdout;
2 changes: 1 addition & 1 deletion examples/ec2_remote/size.ts
Original file line number Diff line number Diff line change
@@ -1 +1 @@
export const size = "t2.nano";
export const size = "t2.nano";
49 changes: 46 additions & 3 deletions provider/cmd/pulumi-resource-command/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
"properties": {
"user": {
"type": "string",
"descripton": "The user that we should use for the connection. Defaults to `root`."
"descripton": "The user that we should use for the connection.",
"default": "root"
iwahbe marked this conversation as resolved.
Show resolved Hide resolved
},
"password": {
"type": "string",
Expand All @@ -20,7 +21,8 @@
},
"port": {
"type": "number",
"description": "The port to connect to. Defaults to 22."
"description": "The port to connect to.",
"default": 22
},
"privateKey": {
"type": "string",
Expand Down Expand Up @@ -154,7 +156,7 @@
],
"inputProperties": {
"connection": {
"description": "The parameters with which to connect to the remote host",
"description": "The parameters with which to connect to the remote host.",
"$ref": "#/types/command:remote:Connection"
},
"environment": {
Expand All @@ -180,6 +182,47 @@
"requiredInputs": [
"connection"
]
},
"command:remote:CopyFile": {
"description": "Copy a local file to a remote host.",
"inputProperties": {
"connection": {
"description": "The parameters with which to connect to the remote host.",
"$ref": "#/types/command:remote:Connection"
},
"localPath": {
"description": "The path of the file to be copied.",
iwahbe marked this conversation as resolved.
Show resolved Hide resolved
"type": "string"
},
"remotePath": {
"description": "The destination path in the remote host.",
"type": "string"
}
},
"requiredInputs": [
"connection",
"localPath",
"remotePath"
],
"properties": {
"connection": {
"description": "The parameters with which to connect to the remote host.",
"$ref": "#/types/command:remote:Connection"
},
"localPath": {
"description": "The path of the file to be copied.",
"type": "string"
},
"remotePath": {
"description": "The destination path in the remote host.",
"type": "string"
}
},
"required": [
"connection",
"localPath",
"remotePath"
]
}
},
"language": {
Expand Down
5 changes: 3 additions & 2 deletions provider/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ go 1.16

require (
github.com/golang/protobuf v1.5.2
github.com/pulumi/pulumi/pkg/v3 v3.9.1
github.com/pulumi/pulumi/sdk/v3 v3.9.1
github.com/pkg/sftp v1.13.4 // indirect
github.com/pulumi/pulumi/pkg/v3 v3.19.0
github.com/pulumi/pulumi/sdk/v3 v3.19.0
golang.org/x/crypto v0.0.0-20211202192323-5770296d904e
google.golang.org/grpc v1.37.0
)
100 changes: 93 additions & 7 deletions provider/go.sum

Large diffs are not rendered by default.

94 changes: 65 additions & 29 deletions provider/pkg/provider/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,17 @@ func (k *commandProvider) StreamInvoke(req *pulumirpc.InvokeRequest, server pulu
return fmt.Errorf("unknown StreamInvoke token %q", tok)
}

func check(urn resource.URN) error {
ty := urn.Type()
valid := []string{"local:Command", "remote:Command", "remote:CopyFile"}
for _, v := range valid {
if "command:"+v == string(ty) {
return nil
}
}
return fmt.Errorf("unknown resource type %q", ty)
}

// Check validates that the given property bag is valid for a resource of the given type and returns
// the inputs that should be passed to successive calls to Diff, Create, or Update for this
// resource. As a rule, the provider inputs returned by a call to Check should preserve the original
Expand All @@ -95,19 +106,17 @@ func (k *commandProvider) StreamInvoke(req *pulumirpc.InvokeRequest, server pulu
// the provider inputs are using for detecting and rendering diffs.
func (k *commandProvider) Check(ctx context.Context, req *pulumirpc.CheckRequest) (*pulumirpc.CheckResponse, error) {
urn := resource.URN(req.GetUrn())
ty := urn.Type()
if ty != "command:local:Command" && ty != "command:remote:Command" {
return nil, fmt.Errorf("unknown resource type %q", ty)
if err := check(urn); err != nil {
return nil, err
iwahbe marked this conversation as resolved.
Show resolved Hide resolved
}
return &pulumirpc.CheckResponse{Inputs: req.News, Failures: nil}, nil
}

// Diff checks what impacts a hypothetical update will have on the resource's properties.
func (k *commandProvider) Diff(ctx context.Context, req *pulumirpc.DiffRequest) (*pulumirpc.DiffResponse, error) {
iwahbe marked this conversation as resolved.
Show resolved Hide resolved
urn := resource.URN(req.GetUrn())
ty := urn.Type()
if ty != "command:local:Command" && ty != "command:remote:Command" {
return nil, fmt.Errorf("unknown resource type %q", ty)
if err := check(urn); err != nil {
return nil, err
}

olds, err := plugin.UnmarshalProperties(req.GetOlds(), plugin.MarshalOptions{KeepUnknowns: true, SkipNulls: true})
Expand All @@ -120,21 +129,21 @@ func (k *commandProvider) Diff(ctx context.Context, req *pulumirpc.DiffRequest)
return nil, err
}

d := olds.Diff(news)
changes := pulumirpc.DiffResponse_DIFF_NONE
var replaces []string
// TODO: Non-replace changes
for _, replaceKey := range []string{"environment", "dir", "interpreter", "create", "connection"} {
i := sort.SearchStrings(req.IgnoreChanges, replaceKey)
if i < len(req.IgnoreChanges) && req.IgnoreChanges[i] == replaceKey {
continue
}
if d.Changed(resource.PropertyKey(replaceKey)) {
changes = pulumirpc.DiffResponse_DIFF_SOME
replaces = append(replaces, replaceKey)
replaces := []string{}
if d := olds.Diff(news); d != nil {
// TODO: Non-replace changes
for _, replaceKey := range []string{"environment", "dir", "interpreter", "create", "connection", "localPath", "remotePath"} {
i := sort.SearchStrings(req.IgnoreChanges, replaceKey)
if i < len(req.IgnoreChanges) && req.IgnoreChanges[i] == replaceKey {
continue
}
if d.Changed(resource.PropertyKey(replaceKey)) {
changes = pulumirpc.DiffResponse_DIFF_SOME
replaces = append(replaces, replaceKey)
}
}
}

// TODO: Detailed diffs

return &pulumirpc.DiffResponse{
Expand All @@ -149,8 +158,8 @@ func (k *commandProvider) Create(ctx context.Context, req *pulumirpc.CreateReque
defer k.removeContext(ctx)
urn := resource.URN(req.GetUrn())
ty := urn.Type()
if ty != "command:local:Command" && ty != "command:remote:Command" {
return nil, fmt.Errorf("unknown resource type %q", ty)
if err := check(urn); err != nil {
return nil, err
}

inputProps, err := plugin.UnmarshalProperties(req.GetProperties(), plugin.MarshalOptions{KeepUnknowns: true, SkipNulls: true})
Expand Down Expand Up @@ -195,6 +204,22 @@ func (k *commandProvider) Create(ctx context.Context, req *pulumirpc.CreateReque
if err != nil {
return nil, err
}
case "command:remote:CopyFile":
var cpf remotefilecopy
err = mapper.MapI(inputs, &cpf)
if err != nil {
return nil, err
}

id, err = cpf.RunCreate(ctx, k.host, urn)
if err != nil {
return nil, err
}

outputs, err = mapper.New(&mapper.Opts{IgnoreMissing: true, IgnoreUnrecognized: true}).Encode(cpf)
if err != nil {
return nil, err
}
}

outputProperties, err := plugin.MarshalProperties(
Expand All @@ -215,9 +240,8 @@ func (k *commandProvider) Read(ctx context.Context, req *pulumirpc.ReadRequest)
ctx = k.addContext(ctx)
defer k.removeContext(ctx)
urn := resource.URN(req.GetUrn())
ty := urn.Type()
if ty != "command:local:Command" && ty != "command:remote:Command" {
return nil, fmt.Errorf("unknown resource type '%q'", ty)
if err := check(urn); err != nil {
return nil, err
}

return &pulumirpc.ReadResponse{
Expand All @@ -233,8 +257,8 @@ func (k *commandProvider) Update(ctx context.Context, req *pulumirpc.UpdateReque
defer k.removeContext(ctx)
urn := resource.URN(req.GetUrn())
ty := urn.Type()
if ty != "command:local:Command" && ty != "command:remote:Command" {
return nil, fmt.Errorf("unknown resource type %q", ty)
if err := check(urn); err != nil {
return nil, err
}

// Our Random resource will never be updated - if there is a diff, it will be a replacement.
Expand All @@ -248,8 +272,8 @@ func (k *commandProvider) Delete(ctx context.Context, req *pulumirpc.DeleteReque
defer k.removeContext(ctx)
urn := resource.URN(req.GetUrn())
ty := urn.Type()
if ty != "command:local:Command" && ty != "command:remote:Command" {
return nil, fmt.Errorf("unknown resource type %q", ty)
if err := check(urn); err != nil {
return nil, err
}

inputProps, err := plugin.UnmarshalProperties(req.GetProperties(), plugin.MarshalOptions{KeepUnknowns: true, SkipNulls: true})
Expand All @@ -258,10 +282,12 @@ func (k *commandProvider) Delete(ctx context.Context, req *pulumirpc.DeleteReque
}
inputs := inputProps.Mappable()

decoder := mapper.New(&mapper.Opts{IgnoreMissing: true, IgnoreUnrecognized: true})

switch ty {
case "command:local:Command":
var cmd command
err = mapper.New(&mapper.Opts{IgnoreMissing: true, IgnoreUnrecognized: true}).Decode(inputs, &cmd)
err = decoder.Decode(inputs, &cmd)
if err != nil {
return nil, err
}
Expand All @@ -272,7 +298,7 @@ func (k *commandProvider) Delete(ctx context.Context, req *pulumirpc.DeleteReque
}
case "command:remote:Command":
var cmd remotecommand
err = mapper.New(&mapper.Opts{IgnoreMissing: true, IgnoreUnrecognized: true}).Decode(inputs, &cmd)
err = decoder.Decode(inputs, &cmd)
if err != nil {
return nil, err
}
Expand All @@ -281,6 +307,16 @@ func (k *commandProvider) Delete(ctx context.Context, req *pulumirpc.DeleteReque
if err != nil {
return nil, err
}
case "command:remote:CopyFile":
var cpf remotefilecopy
err = decoder.Decode(inputs, &cpf)
if err != nil {
return nil, err
}
err = cpf.RunDelete(ctx, k.host, urn)
if err != nil {
return nil, err
}
}

return &pbempty.Empty{}, nil
Expand Down
Loading