0
0
mirror of https://github.com/bpg/terraform-provider-proxmox.git synced 2025-07-02 03:22:59 +00:00

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

* fix(file): SSH file upload on Windows

* update bug report template
This commit is contained in:
Pavel Boldyrev 2023-04-19 22:14:48 -04:00 committed by GitHub
parent 20aacc2547
commit 7c9505d11f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 70 additions and 16 deletions

View File

@ -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.
@ -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`):

View File

@ -17,6 +17,7 @@ import (
"os"
"path/filepath"
"sort"
"strings"
"github.com/hashicorp/terraform-plugin-log/tflog"
"golang.org/x/crypto/ssh"
@ -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) {
@ -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
@ -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
@ -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)
@ -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
}
}

View File

@ -5,7 +5,7 @@
package proxmox
import (
"io"
"os"
"github.com/bpg/terraform-provider-proxmox/proxmox/types"
)
@ -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.

View File

@ -12,6 +12,7 @@ import (
"net/http"
"net/url"
"os"
"path"
"sort"
"strings"
"time"
@ -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)
@ -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
}

View File

@ -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,
}