Skip to content

Commit ed3c92d

Browse files
committed
Add artifact validation during container creation
Validate artifact mounts exist at container creation time instead of start time. This prevents creation of containers that can never start due to missing artifacts, providing immediate feedback with clear error messages. Fixes: #27747 Signed-off-by: MayorFaj <[email protected]>
1 parent 23306d1 commit ed3c92d

File tree

2 files changed

+95
-2
lines changed

2 files changed

+95
-2
lines changed

pkg/specgen/generate/container_create.go

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ import (
2424
"go.podman.io/common/libimage"
2525
"go.podman.io/common/libnetwork/pasta"
2626
"go.podman.io/common/libnetwork/slirp4netns"
27+
"go.podman.io/common/pkg/libartifact/store"
28+
libartTypes "go.podman.io/common/pkg/libartifact/types"
2729
"tags.cncf.io/container-device-interface/pkg/parser"
2830
)
2931

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

235237
infraVol := len(compatibleOptions.Mounts) > 0 || len(compatibleOptions.Volumes) > 0 || len(compatibleOptions.ImageVolumes) > 0 || len(compatibleOptions.OverlayVolumes) > 0
236-
opts, err := createContainerOptions(rt, s, pod, finalVolumes, finalOverlays, imageData, command, infraVol, *compatibleOptions)
238+
opts, err := createContainerOptions(ctx, rt, s, pod, finalVolumes, finalOverlays, imageData, command, infraVol, *compatibleOptions)
237239
if err != nil {
238240
return nil, nil, nil, err
239241
}
@@ -351,7 +353,7 @@ func isCDIDevice(device string) bool {
351353
return parser.IsQualifiedName(device)
352354
}
353355

354-
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) {
356+
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) {
355357
var options []libpod.CtrCreateOption
356358
var err error
357359

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

511513
if len(s.ArtifactVolumes) != 0 {
514+
// Validate artifacts exist before creating the container
515+
if err := validateArtifactVolumes(ctx, rt, s.ArtifactVolumes); err != nil {
516+
return nil, err
517+
}
518+
512519
vols := make([]*libpod.ContainerArtifactVolume, 0, len(s.ArtifactVolumes))
513520
for _, v := range s.ArtifactVolumes {
514521
vols = append(vols, &libpod.ContainerArtifactVolume{
@@ -755,3 +762,39 @@ func Inherit(infra *libpod.Container, s *specgen.SpecGenerator, rt *libpod.Runti
755762
}
756763
return options, infraSpec, compatibleOptions, nil
757764
}
765+
766+
// validateArtifactVolumes checks that all artifacts exist and are accessible
767+
// at container creation time, preventing creation of containers that can never start.
768+
func validateArtifactVolumes(ctx context.Context, rt *libpod.Runtime, artifactVolumes []*specgen.ArtifactVolume) error {
769+
if len(artifactVolumes) == 0 {
770+
return nil
771+
}
772+
773+
artStore, err := rt.ArtifactStore()
774+
if err != nil {
775+
return fmt.Errorf("accessing artifact store: %w", err)
776+
}
777+
778+
for _, artifactMount := range artifactVolumes {
779+
// Use the same artifact store resolution logic as at start time
780+
// to ensure consistent validation behavior
781+
asr, err := store.NewArtifactStorageReference(artifactMount.Source)
782+
if err != nil {
783+
return fmt.Errorf("invalid artifact reference %q: %w", artifactMount.Source, err)
784+
}
785+
786+
// Validate artifact exists using the same logic as container start.
787+
// This ensures consistent behavior between creation and start time.
788+
_ /*paths*/, err = artStore.BlobMountPaths(ctx, asr, &libartTypes.BlobMountPathOptions{
789+
FilterBlobOptions: libartTypes.FilterBlobOptions{
790+
Title: artifactMount.Title,
791+
Digest: artifactMount.Digest,
792+
},
793+
})
794+
if err != nil {
795+
return fmt.Errorf("validating artifact %q: %w", artifactMount.Source, err)
796+
}
797+
}
798+
799+
return nil
800+
}

test/e2e/artifact_mount_test.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,4 +245,54 @@ var _ = Describe("Podman artifact mount", func() {
245245
Expect(session).To(ExitWithError(125, "/test: duplicate mount destination"))
246246
}
247247
})
248+
249+
It("podman create should fail with non-existent artifact", func() {
250+
// Verify that creating a container with a non-existent artifact fails at creation time
251+
cmd := []string{
252+
"create",
253+
"--name", "test-nonexistent",
254+
"--mount", "type=artifact,source=nonexistent-artifact,destination=/data",
255+
ALPINE,
256+
"echo", "hello",
257+
}
258+
session := podmanTest.Podman(cmd)
259+
session.WaitWithDefaultTimeout()
260+
Expect(session).Should(Exit(125))
261+
// Verify error comes from our validation code
262+
Expect(session.ErrorToString()).To(SatisfyAll(
263+
ContainSubstring("validating artifact"),
264+
ContainSubstring("nonexistent-artifact"),
265+
))
266+
267+
// Ensure container was not created
268+
rmSession := podmanTest.Podman([]string{"rm", "test-nonexistent"})
269+
rmSession.WaitWithDefaultTimeout()
270+
Expect(rmSession).Should(Exit(1))
271+
Expect(rmSession.ErrorToString()).To(ContainSubstring("no such container"))
272+
})
273+
274+
It("podman run should fail with non-existent artifact", func() {
275+
// Verify that running a container with a non-existent artifact fails at creation time
276+
cmd := []string{
277+
"run",
278+
"--name", "test-run-nonexistent",
279+
"--mount", "type=artifact,source=invalid-artifact-ref,destination=/test",
280+
ALPINE,
281+
"echo", "hello",
282+
}
283+
session := podmanTest.Podman(cmd)
284+
session.WaitWithDefaultTimeout()
285+
Expect(session).Should(Exit(125))
286+
// Verify error comes from our validation code
287+
Expect(session.ErrorToString()).To(SatisfyAll(
288+
ContainSubstring("validating artifact"),
289+
ContainSubstring("invalid-artifact-ref"),
290+
))
291+
292+
// Ensure container was not created
293+
psSession := podmanTest.Podman([]string{"ps", "-a", "--filter", "name=test-run-nonexistent"})
294+
psSession.WaitWithDefaultTimeout()
295+
Expect(psSession).Should(Exit(0))
296+
Expect(psSession.OutputToString()).ToNot(ContainSubstring("test-run-nonexistent"))
297+
})
248298
})

0 commit comments

Comments
 (0)