mirror of
https://github.com/containers/podman.git
synced 2025-11-28 17:18:58 +08:00
Tremendous amount of changes in here, but all should amount to the same thing: changing Go import paths from v5 to v6. Also bumped go.mod to github.com/containers/podman/v6 and updated version to v6.0.0-dev. Signed-off-by: Matt Heon <mheon@redhat.com>
114 lines
2.7 KiB
Go
114 lines
2.7 KiB
Go
package artifacts
|
|
|
|
import (
|
|
"archive/tar"
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"io/fs"
|
|
"net/http"
|
|
"os"
|
|
"path/filepath"
|
|
|
|
"github.com/containers/podman/v6/pkg/bindings"
|
|
)
|
|
|
|
func Extract(ctx context.Context, artifactName string, target string, options *ExtractOptions) error {
|
|
conn, err := bindings.GetClient(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("getting client: %w", err)
|
|
}
|
|
|
|
if options == nil {
|
|
options = new(ExtractOptions)
|
|
}
|
|
|
|
// Check if target is a directory to know if we can copy more than one blob
|
|
targetIsDirectory := false
|
|
stat, err := os.Stat(target)
|
|
if err == nil {
|
|
targetIsDirectory = stat.IsDir()
|
|
} else if !errors.Is(err, fs.ErrNotExist) {
|
|
return fmt.Errorf("stat target %q failed: %w", target, err)
|
|
}
|
|
|
|
// If the target is not a directory, request API to return the blob without title.
|
|
// If a blob has a malicious title it will be returned from the API without it
|
|
// as the file will be written to the provided target
|
|
if !targetIsDirectory {
|
|
options.WithExcludeTitle(true)
|
|
}
|
|
|
|
params, err := options.ToParams()
|
|
if err != nil {
|
|
return fmt.Errorf("converting options to params: %w", err)
|
|
}
|
|
|
|
response, err := conn.DoRequest(ctx, nil, http.MethodGet, "/artifacts/%s/extract", params, nil, artifactName)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer response.Body.Close()
|
|
|
|
if !response.IsSuccess() {
|
|
return response.Process(nil)
|
|
}
|
|
|
|
multipleBlobs := false
|
|
tr := tar.NewReader(response.Body)
|
|
for {
|
|
header, err := tr.Next()
|
|
if err == io.EOF {
|
|
break // End of archive
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if !targetIsDirectory && multipleBlobs {
|
|
return fmt.Errorf("the artifact consists of several blobs and the target %q is not a directory and neither digest or title was specified to only copy a single blob", target)
|
|
}
|
|
|
|
// If destination isn't a file, extract to target/filename
|
|
fileTarget := target
|
|
if targetIsDirectory {
|
|
fileTarget = filepath.Join(target, header.Name)
|
|
}
|
|
|
|
if header.Typeflag == tar.TypeReg {
|
|
err = extractFile(tr, fileTarget)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
// Signal that the first blob has been extracted so we can return an error if more
|
|
// than one blob are being extracted when target is not a directory.
|
|
multipleBlobs = true
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func extractFile(tr *tar.Reader, fileTarget string) (retErr error) {
|
|
outFile, err := os.Create(fileTarget)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// Use an anonymous function to enable capturing the error from
|
|
// outFile.Close() upon returning.
|
|
defer func() {
|
|
cErr := outFile.Close()
|
|
if retErr == nil {
|
|
retErr = cErr
|
|
}
|
|
}()
|
|
|
|
_, err = io.Copy(outFile, tr)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to extract blob to %s: %w", fileTarget, err)
|
|
}
|
|
return nil
|
|
}
|