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:
parent
20aacc2547
commit
7c9505d11f
16
.github/ISSUE_TEMPLATE/bug_report.md
vendored
16
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -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`):
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -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
|
||||
}
|
||||
|
||||
|
@ -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,
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user