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

fix(file): fix SSH file upload on Windows #308

Merged
merged 2 commits into from
Apr 20, 2023
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: 12 additions & 4 deletions .github/ISSUE_TEMPLATE/bug_report.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@ A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
1. Create a resource '....'
2. Run '....'
3. See error
4. Modify the resource '....'
5. Run '....'
6. See error

Please also provide a minimal Terraform configuration that reproduces the issue.

**Expected behavior**
A clear and concise description of what you expected to happen.
Expand All @@ -25,3 +29,7 @@ If applicable, add screenshots to help explain your problem.

**Additional context**
Add any other context about the problem here.
- Provider version (ideally it should be the latest version):
- Terraform version:
- OS (where you run Terraform from)):
- Debug logs (`TF_LOG=DEBUG terraform apply`):
36 changes: 32 additions & 4 deletions proxmox/datastores.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
"os"
"path/filepath"
"sort"
"strings"

"github.com/hashicorp/terraform-plugin-log/tflog"
"golang.org/x/crypto/ssh"
Expand Down Expand Up @@ -183,6 +184,13 @@ func (c *VirtualEnvironmentClient) UploadFileToDatastore(
) (*DatastoreUploadResponseBody, error) {
switch d.ContentType {
case "iso", "vztmpl":
tflog.Debug(ctx, "uploading file to datastore using PVE API", map[string]interface{}{
"node_name": d.NodeName,
"datastore_id": d.DatastoreID,
"file_name": d.FileName,
"content_type": d.ContentType,
})

r, w := io.Pipe()

defer func(r *io.PipeReader) {
Expand Down Expand Up @@ -227,7 +235,7 @@ func (c *VirtualEnvironmentClient) UploadFileToDatastore(
return
}

_, err = io.Copy(part, d.FileReader)
_, err = io.Copy(part, d.File)

if err != nil {
return
Expand Down Expand Up @@ -311,6 +319,19 @@ func (c *VirtualEnvironmentClient) UploadFileToDatastore(
default:
// We need to upload all other files using SFTP due to API limitations.
// Hopefully, this will not be required in future releases of Proxmox VE.
tflog.Debug(ctx, "uploading file to datastore using SFTP", map[string]interface{}{
"node_name": d.NodeName,
"datastore_id": d.DatastoreID,
"file_name": d.FileName,
"content_type": d.ContentType,
})

fileInfo, err := d.File.Stat()
if err != nil {
return nil, fmt.Errorf("failed to get file info: %w", err)
}
fileSize := fileInfo.Size()

sshClient, err := c.OpenNodeShell(ctx, d.NodeName)
if err != nil {
return nil, err
Expand All @@ -337,8 +358,8 @@ func (c *VirtualEnvironmentClient) UploadFileToDatastore(
if d.ContentType != "" {
remoteFileDir = filepath.Join(remoteFileDir, d.ContentType)
}
remoteFilePath := strings.ReplaceAll(filepath.Join(remoteFileDir, d.FileName), `\`, `/`)

remoteFilePath := filepath.Join(remoteFileDir, d.FileName)
sftpClient, err := sftp.NewClient(sshClient)
if err != nil {
return nil, fmt.Errorf("failed to create SFTP client: %w", err)
Expand Down Expand Up @@ -372,11 +393,18 @@ func (c *VirtualEnvironmentClient) UploadFileToDatastore(
}
}(remoteFile)

_, err = remoteFile.ReadFrom(d.FileReader)
bytesUploaded, err := remoteFile.ReadFrom(d.File)
if err != nil {
return nil, fmt.Errorf("failed to upload file %s: %w", remoteFilePath, err)
}

if bytesUploaded != fileSize {
return nil, fmt.Errorf("failed to upload file %s: uploaded %d bytes, expected %d bytes",
remoteFilePath, bytesUploaded, fileSize)
}
tflog.Debug(ctx, "uploaded file to datastore", map[string]interface{}{
"remote_file_path": remoteFilePath,
"size": bytesUploaded,
})
return &DatastoreUploadResponseBody{}, nil
}
}
12 changes: 6 additions & 6 deletions proxmox/datastores_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
package proxmox

import (
"io"
"os"

"github.com/bpg/terraform-provider-proxmox/proxmox/types"
)
Expand Down Expand Up @@ -88,11 +88,11 @@ type DatastoreListResponseData struct {

// DatastoreUploadRequestBody contains the body for a datastore upload request.
type DatastoreUploadRequestBody struct {
ContentType string `json:"content,omitempty"`
DatastoreID string `json:"storage,omitempty"`
FileName string `json:"filename,omitempty"`
FileReader io.Reader `json:"-"`
NodeName string `json:"node,omitempty"`
ContentType string `json:"content,omitempty"`
DatastoreID string `json:"storage,omitempty"`
FileName string `json:"filename,omitempty"`
NodeName string `json:"node,omitempty"`
File *os.File `json:"-"`
}

// DatastoreUploadResponseBody contains the body from a datastore upload response.
Expand Down
20 changes: 19 additions & 1 deletion proxmox/virtual_environment_nodes.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"net/http"
"net/url"
"os"
"path"
"sort"
"strings"
"time"
Expand Down Expand Up @@ -199,7 +200,20 @@ func (c *VirtualEnvironmentClient) OpenNodeShell(
return nil, fmt.Errorf("failed to determine the home directory: %w", err)
}
sshHost := fmt.Sprintf("%s:22", *nodeAddress)
khPath := fmt.Sprintf("%s/.ssh/known_hosts", homeDir)
sshPath := path.Join(homeDir, ".ssh")
if _, err = os.Stat(sshPath); os.IsNotExist(err) {
e := os.Mkdir(sshPath, 0o700)
if e != nil {
return nil, fmt.Errorf("failed to create %s: %w", sshPath, e)
}
}
khPath := path.Join(sshPath, "known_hosts")
if _, err = os.Stat(khPath); os.IsNotExist(err) {
e := os.WriteFile(khPath, []byte{}, 0o600)
if e != nil {
return nil, fmt.Errorf("failed to create %s: %w", khPath, e)
}
}
kh, err := knownhosts.New(khPath)
if err != nil {
return nil, fmt.Errorf("failed to read %s: %w", khPath, err)
Expand Down Expand Up @@ -243,6 +257,10 @@ func (c *VirtualEnvironmentClient) OpenNodeShell(
return nil, fmt.Errorf("failed to dial %s: %w", sshHost, err)
}

tflog.Debug(ctx, "SSH connection established", map[string]interface{}{
"host": sshHost,
"user": ur[0],
})
return sshClient, nil
}

Expand Down
2 changes: 1 addition & 1 deletion proxmoxtf/resource/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ func fileCreate(ctx context.Context, d *schema.ResourceData, m interface{}) diag
ContentType: *contentType,
DatastoreID: datastoreID,
FileName: *fileName,
FileReader: file,
File: file,
NodeName: nodeName,
}

Expand Down