diff --git a/data/AgentVM.ovf b/data/MigrationAssessment.ovf similarity index 97% rename from data/AgentVM.ovf rename to data/MigrationAssessment.ovf index 30e4cf2..878b3cc 100644 --- a/data/AgentVM.ovf +++ b/data/MigrationAssessment.ovf @@ -1,7 +1,7 @@ - + The list of logical networks @@ -9,7 +9,7 @@ Routable network - + A Virtual system MigrationAssessment diff --git a/internal/image/ova.go b/internal/image/ova.go index 930ed8d..34a4bf4 100644 --- a/internal/image/ova.go +++ b/internal/image/ova.go @@ -47,6 +47,31 @@ func (o *Ova) Validate() error { return nil } +func calculateTarSize(contentSize int) int { + const blockSize = 512 + + // Size of the tar header block + size := blockSize + + // Size of the file content, rounded up to nearest 512 bytes + size += (contentSize + blockSize - 1) / blockSize * blockSize + + return size +} + +func (o *Ova) OvaSize() (int, error) { + isoSize, err := o.isoSize() + if err != nil { + return -1, err + } + ovfSize, err := o.ovfSize() + if err != nil { + return -1, err + } + + return ovfSize + isoSize, nil +} + func (o *Ova) Generate() error { tw := tar.NewWriter(o.Writer) @@ -60,6 +85,8 @@ func (o *Ova) Generate() error { return err } + tw.Flush() + return nil } @@ -84,13 +111,13 @@ func (o *Ova) writeIso(tw *tar.Writer) error { if err != nil { return err } - // Create a header for AgentVM-1.iso + // Create a header for MigrationAssessment.iso length, err := reader.Seek(0, io.SeekEnd) if err != nil { return err } header := &tar.Header{ - Name: "AgentVM-1.iso", + Name: "MigrationAssessment.iso", Size: length, Mode: 0600, ModTime: time.Now(), @@ -114,14 +141,48 @@ func (o *Ova) writeIso(tw *tar.Writer) error { return nil } +func (o *Ova) isoSize() (int, error) { + // Get ISO reader + reader, err := o.isoReader() + if err != nil { + return -1, err + } + length, err := reader.Seek(0, io.SeekEnd) + if err != nil { + return -1, err + } + + // Reset the reader to start + _, err = reader.Seek(0, io.SeekStart) + if err != nil { + return -1, err + } + + return calculateTarSize(int(length)), nil +} + +func (o *Ova) ovfSize() (int, error) { + file, err := os.Open("data/MigrationAssessment.ovf") + if err != nil { + return -1, err + } + defer file.Close() + fileInfo, err := file.Stat() + if err != nil { + return -1, err + } + + return calculateTarSize(int(fileInfo.Size())), nil +} + func writeOvf(tw *tar.Writer) error { - ovfContent, err := os.ReadFile("data/AgentVM.ovf") + ovfContent, err := os.ReadFile("data/MigrationAssessment.ovf") if err != nil { return err } // Create a header for AgentVM.ovf header := &tar.Header{ - Name: "AgentVM.ovf", + Name: "MigrationAssessment.ovf", Size: int64(len(ovfContent)), Mode: 0600, ModTime: time.Now(), diff --git a/internal/service/image.go b/internal/service/image.go index ec8fed2..8ae1f21 100644 --- a/internal/service/image.go +++ b/internal/service/image.go @@ -5,6 +5,7 @@ import ( "context" "fmt" "net/http" + "strconv" "github.com/kubev2v/migration-planner/internal/api/server" "github.com/kubev2v/migration-planner/internal/image" @@ -15,7 +16,21 @@ func (h *ServiceHandler) GetImage(ctx context.Context, request server.GetImageRe if !ok { return server.GetImage500JSONResponse{Message: "error creating the HTTP stream"}, nil } + + // Ova struct ova := &image.Ova{SshKey: request.Params.SshKey, Writer: writer} + + // Calculate the size of the OVA, so the download show estimated time: + size, err := ova.OvaSize() + if err != nil { + return server.GetImage500JSONResponse{Message: "error creating the HTTP stream"}, nil + } + + // Set proper headers of the OVA file: + writer.Header().Set("Content-Type", "application/ovf") + writer.Header().Set("Content-Length", strconv.Itoa(size)) + + // Generate the OVA image if err := ova.Generate(); err != nil { return server.GetImage500JSONResponse{Message: fmt.Sprintf("error generating image %s", err)}, nil }