mirror of
https://github.com/containers/podman.git
synced 2025-12-02 11:08:36 +08:00
This commit implements the --replace functionality for the artifact add command, allowing users to replace existing artifacts without having to manually remove them first. Changes made: - Add Replace field to ArtifactAddOptions entity types - Add --replace CLI flag with validation to prevent conflicts with --append - Implement replace logic in ABI backend to remove existing artifacts before adding - Update API handlers and tunnel implementation for podman-remote support - Add comprehensive documentation and examples to man page - Add e2e and system BATS tests for --replace functionality - Fix code formatting in pkg/bindings/artifacts/types_pull_options.go: * Reorder imports with proper spacing * Fix function declaration spacing * Convert spaces to proper tab indentation * Remove extraneous blank lines The --replace option follows the same pattern as other podman replace options like 'podman container create --replace' and 'podman pod create --replace'. It gracefully handles cases where no existing artifact exists (no error thrown). Usage examples: podman artifact add --replace quay.io/myimage/artifact:latest /path/to/file podman artifact add --replace localhost/test/artifact /tmp/newfile.txt Fixes: Implements requested --replace functionality for artifact add command Signed-off-by: Daniel J Walsh <dwalsh@redhat.com>
127 lines
3.7 KiB
Go
127 lines
3.7 KiB
Go
package tunnel
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
|
|
"github.com/containers/podman/v5/pkg/bindings/artifacts"
|
|
"github.com/containers/podman/v5/pkg/domain/entities"
|
|
"go.podman.io/image/v5/types"
|
|
)
|
|
|
|
func (ir *ImageEngine) ArtifactExtract(_ context.Context, name string, target string, opts entities.ArtifactExtractOptions) error {
|
|
options := artifacts.ExtractOptions{
|
|
Digest: &opts.Digest,
|
|
Title: &opts.Title,
|
|
ExcludeTitle: &opts.ExcludeTitle,
|
|
}
|
|
|
|
return artifacts.Extract(ir.ClientCtx, name, target, &options)
|
|
}
|
|
|
|
func (ir *ImageEngine) ArtifactExtractTarStream(_ context.Context, _ io.Writer, _ string, _ entities.ArtifactExtractOptions) error {
|
|
return fmt.Errorf("not implemented")
|
|
}
|
|
|
|
func (ir *ImageEngine) ArtifactInspect(_ context.Context, name string, _ entities.ArtifactInspectOptions) (*entities.ArtifactInspectReport, error) {
|
|
return artifacts.Inspect(ir.ClientCtx, name, &artifacts.InspectOptions{})
|
|
}
|
|
|
|
func (ir *ImageEngine) ArtifactList(_ context.Context, _ entities.ArtifactListOptions) ([]*entities.ArtifactListReport, error) {
|
|
return artifacts.List(ir.ClientCtx, &artifacts.ListOptions{})
|
|
}
|
|
|
|
func (ir *ImageEngine) ArtifactPull(_ context.Context, name string, opts entities.ArtifactPullOptions) (*entities.ArtifactPullReport, error) {
|
|
options := artifacts.PullOptions{
|
|
Username: &opts.Username,
|
|
Password: &opts.Password,
|
|
Quiet: &opts.Quiet,
|
|
RetryDelay: &opts.RetryDelay,
|
|
Retry: opts.MaxRetries,
|
|
}
|
|
|
|
switch opts.InsecureSkipTLSVerify {
|
|
case types.OptionalBoolTrue:
|
|
options.WithTlsVerify(false)
|
|
case types.OptionalBoolFalse:
|
|
options.WithTlsVerify(true)
|
|
}
|
|
|
|
return artifacts.Pull(ir.ClientCtx, name, &options)
|
|
}
|
|
|
|
func (ir *ImageEngine) ArtifactRm(_ context.Context, opts entities.ArtifactRemoveOptions) (*entities.ArtifactRemoveReport, error) {
|
|
removeOptions := artifacts.RemoveOptions{
|
|
All: &opts.All,
|
|
Artifacts: opts.Artifacts,
|
|
Ignore: &opts.Ignore,
|
|
}
|
|
|
|
return artifacts.Remove(ir.ClientCtx, "", &removeOptions)
|
|
}
|
|
|
|
func (ir *ImageEngine) ArtifactPush(_ context.Context, name string, opts entities.ArtifactPushOptions) (*entities.ArtifactPushReport, error) {
|
|
options := artifacts.PushOptions{
|
|
Username: &opts.Username,
|
|
Password: &opts.Password,
|
|
Quiet: &opts.Quiet,
|
|
RetryDelay: &opts.RetryDelay,
|
|
Retry: opts.Retry,
|
|
}
|
|
|
|
switch opts.SkipTLSVerify {
|
|
case types.OptionalBoolTrue:
|
|
options.WithTlsVerify(false)
|
|
case types.OptionalBoolFalse:
|
|
options.WithTlsVerify(true)
|
|
}
|
|
|
|
return artifacts.Push(ir.ClientCtx, name, &options)
|
|
}
|
|
|
|
func (ir *ImageEngine) ArtifactAdd(_ context.Context, name string, artifactBlob []entities.ArtifactBlob, opts entities.ArtifactAddOptions) (*entities.ArtifactAddReport, error) {
|
|
var artifactAddReport *entities.ArtifactAddReport
|
|
|
|
options := artifacts.AddOptions{
|
|
Append: &opts.Append,
|
|
ArtifactMIMEType: &opts.ArtifactMIMEType,
|
|
FileMIMEType: &opts.FileMIMEType,
|
|
Replace: &opts.Replace,
|
|
}
|
|
|
|
for k, v := range opts.Annotations {
|
|
options.Annotations = append(options.Annotations, k+"="+v)
|
|
}
|
|
|
|
for i, blob := range artifactBlob {
|
|
if i > 0 {
|
|
// When adding more than 1 blob, set append true after the first
|
|
options.WithAppend(true)
|
|
}
|
|
f, err := os.Open(blob.BlobFilePath)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer f.Close()
|
|
|
|
artifactAddReport, err = artifacts.Add(ir.ClientCtx, name, blob.FileName, f, &options)
|
|
if err != nil && i > 0 {
|
|
removeOptions := artifacts.RemoveOptions{
|
|
Artifacts: []string{name},
|
|
}
|
|
_, recoverErr := artifacts.Remove(ir.ClientCtx, "", &removeOptions)
|
|
if recoverErr != nil {
|
|
return nil, fmt.Errorf("failed to cleanup unfinished artifact add: %w", errors.Join(err, recoverErr))
|
|
}
|
|
return nil, err
|
|
}
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return artifactAddReport, nil
|
|
}
|