Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 45 additions & 2 deletions pkg/specgen/generate/container_create.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ import (
"go.podman.io/common/libimage"
"go.podman.io/common/libnetwork/pasta"
"go.podman.io/common/libnetwork/slirp4netns"
"go.podman.io/common/pkg/libartifact/store"
libartTypes "go.podman.io/common/pkg/libartifact/types"
"tags.cncf.io/container-device-interface/pkg/parser"
)

Expand Down Expand Up @@ -233,7 +235,7 @@ func MakeContainer(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGener
command := makeCommand(s, imageData)

infraVol := len(compatibleOptions.Mounts) > 0 || len(compatibleOptions.Volumes) > 0 || len(compatibleOptions.ImageVolumes) > 0 || len(compatibleOptions.OverlayVolumes) > 0
opts, err := createContainerOptions(rt, s, pod, finalVolumes, finalOverlays, imageData, command, infraVol, *compatibleOptions)
opts, err := createContainerOptions(ctx, rt, s, pod, finalVolumes, finalOverlays, imageData, command, infraVol, *compatibleOptions)
if err != nil {
return nil, nil, nil, err
}
Expand Down Expand Up @@ -351,7 +353,7 @@ func isCDIDevice(device string) bool {
return parser.IsQualifiedName(device)
}

func createContainerOptions(rt *libpod.Runtime, s *specgen.SpecGenerator, pod *libpod.Pod, volumes []*specgen.NamedVolume, overlays []*specgen.OverlayVolume, imageData *libimage.ImageData, command []string, infraVolumes bool, compatibleOptions libpod.InfraInherit) ([]libpod.CtrCreateOption, error) {
func createContainerOptions(ctx context.Context, rt *libpod.Runtime, s *specgen.SpecGenerator, pod *libpod.Pod, volumes []*specgen.NamedVolume, overlays []*specgen.OverlayVolume, imageData *libimage.ImageData, command []string, infraVolumes bool, compatibleOptions libpod.InfraInherit) ([]libpod.CtrCreateOption, error) {
var options []libpod.CtrCreateOption
var err error

Expand Down Expand Up @@ -509,6 +511,11 @@ func createContainerOptions(rt *libpod.Runtime, s *specgen.SpecGenerator, pod *l
}

if len(s.ArtifactVolumes) != 0 {
// Validate artifacts exist before creating the container
if err := validateArtifactVolumes(ctx, rt, s.ArtifactVolumes); err != nil {
return nil, err
}

vols := make([]*libpod.ContainerArtifactVolume, 0, len(s.ArtifactVolumes))
for _, v := range s.ArtifactVolumes {
vols = append(vols, &libpod.ContainerArtifactVolume{
Expand Down Expand Up @@ -755,3 +762,39 @@ func Inherit(infra *libpod.Container, s *specgen.SpecGenerator, rt *libpod.Runti
}
return options, infraSpec, compatibleOptions, nil
}

// validateArtifactVolumes checks that all artifacts exist and are accessible
// at container creation time, preventing creation of containers that can never start.
func validateArtifactVolumes(ctx context.Context, rt *libpod.Runtime, artifactVolumes []*specgen.ArtifactVolume) error {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this function can be repurposed for start containers.

Copy link
Contributor Author

@MayorFaj MayorFaj Dec 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good suggestion. would it be ideal to implement that in this PR, or create a followup PR ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the latest changes has this update now

if len(artifactVolumes) == 0 {
return nil
}

artStore, err := rt.ArtifactStore()
if err != nil {
return fmt.Errorf("accessing artifact store: %w", err)
}

for _, artifactMount := range artifactVolumes {
// Use the same artifact store resolution logic as at start time
// to ensure consistent validation behavior
asr, err := store.NewArtifactStorageReference(artifactMount.Source)
if err != nil {
return fmt.Errorf("invalid artifact reference %q: %w", artifactMount.Source, err)
}

// Validate artifact exists using the same logic as container start.
// This ensures consistent behavior between creation and start time.
_ /*paths*/, err = artStore.BlobMountPaths(ctx, asr, &libartTypes.BlobMountPathOptions{
FilterBlobOptions: libartTypes.FilterBlobOptions{
Title: artifactMount.Title,
Digest: artifactMount.Digest,
},
})
if err != nil {
return fmt.Errorf("validating artifact %q: %w", artifactMount.Source, err)
}
}

return nil
}
41 changes: 41 additions & 0 deletions test/e2e/artifact_mount_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,4 +245,45 @@ var _ = Describe("Podman artifact mount", func() {
Expect(session).To(ExitWithError(125, "/test: duplicate mount destination"))
}
})

It("podman create should fail with non-existent artifact", func() {
// Verify that creating a container with a non-existent artifact fails at creation time
cmd := []string{
"create",
"--name", "test-nonexistent",
"--mount", "type=artifact,source=nonexistent-artifact,destination=/data",
ALPINE,
"echo", "hello",
}
session := podmanTest.Podman(cmd)
session.WaitWithDefaultTimeout()
Expect(session).To(ExitWithError(125, "validating artifact"))
Expect(session.ErrorToString()).To(ContainSubstring("nonexistent-artifact"))

// Ensure container was not created
rmSession := podmanTest.Podman([]string{"rm", "test-nonexistent"})
rmSession.WaitWithDefaultTimeout()
Expect(rmSession).To(ExitWithError(1, "no such container"))
})

It("podman run should fail with non-existent artifact", func() {
// Verify that running a container with a non-existent artifact fails at creation time
cmd := []string{
"run",
"--name", "test-run-nonexistent",
"--mount", "type=artifact,source=invalid-artifact-ref,destination=/test",
ALPINE,
"echo", "hello",
}
session := podmanTest.Podman(cmd)
session.WaitWithDefaultTimeout()
Expect(session).To(ExitWithError(125, "validating artifact"))
Expect(session.ErrorToString()).To(ContainSubstring("invalid-artifact-ref"))

// Ensure container was not created
psSession := podmanTest.Podman([]string{"ps", "-a", "--filter", "name=test-run-nonexistent"})
psSession.WaitWithDefaultTimeout()
Expect(psSession).Should(ExitCleanly())
Expect(psSession.OutputToString()).ToNot(ContainSubstring("test-run-nonexistent"))
})
})