mirror of
https://github.com/containers/podman.git
synced 2025-05-20 08:36:23 +08:00
System tests: add podman system check
tests
Testing `podman system check` requires that we have a way to intentionally introduce storage corruptions. Add a hidden `podman testing` command that provides the necessary internal logic in subcommands. Stub out the tunnel implementation for now. Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
This commit is contained in:
17
Makefile
17
Makefile
@ -238,7 +238,7 @@ binaries: podman podman-remote ## Build podman and podman-remote binaries
|
|||||||
else ifneq (, $(findstring $(GOOS),darwin windows))
|
else ifneq (, $(findstring $(GOOS),darwin windows))
|
||||||
binaries: podman-remote ## Build podman-remote (client) only binaries
|
binaries: podman-remote ## Build podman-remote (client) only binaries
|
||||||
else
|
else
|
||||||
binaries: podman podman-remote podmansh rootlessport quadlet ## Build podman, podman-remote and rootlessport binaries quadlet
|
binaries: podman podman-remote podman-testing podmansh rootlessport quadlet ## Build podman, podman-remote and rootlessport binaries quadlet
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Extract text following double-# for targets, as their description for
|
# Extract text following double-# for targets, as their description for
|
||||||
@ -457,6 +457,16 @@ rootlessport: bin/rootlessport
|
|||||||
podmansh: bin/podman
|
podmansh: bin/podman
|
||||||
if [ ! -f bin/podmansh ]; then ln -s podman bin/podmansh; fi
|
if [ ! -f bin/podmansh ]; then ln -s podman bin/podmansh; fi
|
||||||
|
|
||||||
|
$(SRCBINDIR)/podman-testing: $(SOURCES) go.mod go.sum
|
||||||
|
$(GOCMD) build \
|
||||||
|
$(BUILDFLAGS) \
|
||||||
|
$(GO_LDFLAGS) '$(LDFLAGS_PODMAN)' \
|
||||||
|
-tags "${BUILDTAGS}" \
|
||||||
|
-o $@ ./cmd/podman-testing
|
||||||
|
|
||||||
|
.PHONY: podman-testing
|
||||||
|
podman-testing: bin/podman-testing
|
||||||
|
|
||||||
###
|
###
|
||||||
### Secondary binary-build targets
|
### Secondary binary-build targets
|
||||||
###
|
###
|
||||||
@ -877,6 +887,11 @@ ifneq ($(shell uname -s),FreeBSD)
|
|||||||
install ${SELINUXOPT} -m 644 contrib/tmpfile/podman.conf $(DESTDIR)${TMPFILESDIR}/podman.conf
|
install ${SELINUXOPT} -m 644 contrib/tmpfile/podman.conf $(DESTDIR)${TMPFILESDIR}/podman.conf
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
.PHONY: install.testing
|
||||||
|
install.testing:
|
||||||
|
install ${SELINUXOPT} -d -m 755 $(DESTDIR)$(BINDIR)
|
||||||
|
install ${SELINUXOPT} -m 755 bin/podman-testing $(DESTDIR)$(BINDIR)/podman-testing
|
||||||
|
|
||||||
.PHONY: install.modules-load
|
.PHONY: install.modules-load
|
||||||
install.modules-load: # This should only be used by distros which might use iptables-legacy, this is not needed on RHEL
|
install.modules-load: # This should only be used by distros which might use iptables-legacy, this is not needed on RHEL
|
||||||
install ${SELINUXOPT} -m 755 -d $(DESTDIR)${MODULESLOADDIR}
|
install ${SELINUXOPT} -m 755 -d $(DESTDIR)${MODULESLOADDIR}
|
||||||
|
127
cmd/podman-testing/create.go
Normal file
127
cmd/podman-testing/create.go
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/containers/common/pkg/completion"
|
||||||
|
"github.com/containers/podman/v5/cmd/podman/validate"
|
||||||
|
"github.com/containers/podman/v5/internal/domain/entities"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
createStorageLayerDescription = `Create an unmanaged layer in local storage.`
|
||||||
|
createStorageLayerCmd = &cobra.Command{
|
||||||
|
Use: "create-storage-layer [options]",
|
||||||
|
Args: validate.NoArgs,
|
||||||
|
Short: "Create an unmanaged layer",
|
||||||
|
Long: createStorageLayerDescription,
|
||||||
|
RunE: createStorageLayer,
|
||||||
|
ValidArgsFunction: completion.AutocompleteNone,
|
||||||
|
Example: `podman testing create-storage-layer`,
|
||||||
|
}
|
||||||
|
|
||||||
|
createStorageLayerOpts entities.CreateStorageLayerOptions
|
||||||
|
|
||||||
|
createLayerDescription = `Create an unused layer in local storage.`
|
||||||
|
createLayerCmd = &cobra.Command{
|
||||||
|
Use: "create-layer [options]",
|
||||||
|
Args: validate.NoArgs,
|
||||||
|
Short: "Create an unused layer",
|
||||||
|
Long: createLayerDescription,
|
||||||
|
RunE: createLayer,
|
||||||
|
ValidArgsFunction: completion.AutocompleteNone,
|
||||||
|
Example: `podman testing create-layer`,
|
||||||
|
}
|
||||||
|
|
||||||
|
createLayerOpts entities.CreateLayerOptions
|
||||||
|
|
||||||
|
createImageDescription = `Create an image in local storage.`
|
||||||
|
createImageCmd = &cobra.Command{
|
||||||
|
Use: "create-image [options]",
|
||||||
|
Args: validate.NoArgs,
|
||||||
|
Short: "Create an image",
|
||||||
|
Long: createImageDescription,
|
||||||
|
RunE: createImage,
|
||||||
|
ValidArgsFunction: completion.AutocompleteNone,
|
||||||
|
Example: `podman testing create-image`,
|
||||||
|
}
|
||||||
|
|
||||||
|
createImageOpts entities.CreateImageOptions
|
||||||
|
|
||||||
|
createContainerDescription = `Create a container in local storage.`
|
||||||
|
createContainerCmd = &cobra.Command{
|
||||||
|
Use: "create-container [options]",
|
||||||
|
Args: validate.NoArgs,
|
||||||
|
Short: "Create a container",
|
||||||
|
Long: createContainerDescription,
|
||||||
|
RunE: createContainer,
|
||||||
|
ValidArgsFunction: completion.AutocompleteNone,
|
||||||
|
Example: `podman testing create-container`,
|
||||||
|
}
|
||||||
|
|
||||||
|
createContainerOpts entities.CreateContainerOptions
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
mainCmd.AddCommand(createStorageLayerCmd)
|
||||||
|
flags := createStorageLayerCmd.Flags()
|
||||||
|
flags.StringVarP(&createStorageLayerOpts.ID, "id", "i", "", "ID to assign the new layer (default random)")
|
||||||
|
flags.StringVarP(&createStorageLayerOpts.Parent, "parent", "p", "", "ID of parent of new layer (default none)")
|
||||||
|
|
||||||
|
mainCmd.AddCommand(createLayerCmd)
|
||||||
|
flags = createLayerCmd.Flags()
|
||||||
|
flags.StringVarP(&createLayerOpts.ID, "id", "i", "", "ID to assign the new layer (default random)")
|
||||||
|
flags.StringVarP(&createLayerOpts.Parent, "parent", "p", "", "ID of parent of new layer (default none)")
|
||||||
|
|
||||||
|
mainCmd.AddCommand(createImageCmd)
|
||||||
|
flags = createImageCmd.Flags()
|
||||||
|
flags.StringVarP(&createImageOpts.ID, "id", "i", "", "ID to assign the new image (default random)")
|
||||||
|
flags.StringVarP(&createImageOpts.Layer, "layer", "l", "", "ID of image's main layer (default none)")
|
||||||
|
|
||||||
|
mainCmd.AddCommand(createContainerCmd)
|
||||||
|
flags = createContainerCmd.Flags()
|
||||||
|
flags.StringVarP(&createContainerOpts.ID, "id", "i", "", "ID to assign the new container (default random)")
|
||||||
|
flags.StringVarP(&createContainerOpts.Image, "image", "b", "", "ID of containers's base image (default none)")
|
||||||
|
flags.StringVarP(&createContainerOpts.Layer, "layer", "l", "", "ID of containers's read-write layer (default none)")
|
||||||
|
}
|
||||||
|
|
||||||
|
func createStorageLayer(cmd *cobra.Command, args []string) error {
|
||||||
|
results, err := testingEngine.CreateStorageLayer(mainContext, createStorageLayerOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(results.ID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createLayer(cmd *cobra.Command, args []string) error {
|
||||||
|
results, err := testingEngine.CreateLayer(mainContext, createLayerOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(results.ID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createImage(cmd *cobra.Command, args []string) error {
|
||||||
|
results, err := testingEngine.CreateImage(mainContext, createImageOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(results.ID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createContainer(cmd *cobra.Command, args []string) error {
|
||||||
|
results, err := testingEngine.CreateContainer(mainContext, createContainerOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(results.ID)
|
||||||
|
return nil
|
||||||
|
}
|
405
cmd/podman-testing/data.go
Normal file
405
cmd/podman-testing/data.go
Normal file
@ -0,0 +1,405 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/containers/common/pkg/completion"
|
||||||
|
"github.com/containers/podman/v5/cmd/podman/validate"
|
||||||
|
"github.com/containers/podman/v5/internal/domain/entities"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
createLayerDataDescription = `Create data for a layer in local storage.`
|
||||||
|
createLayerDataCmd = &cobra.Command{
|
||||||
|
Use: "create-layer-data [options]",
|
||||||
|
Args: validate.NoArgs,
|
||||||
|
Short: "Create data for a layer",
|
||||||
|
Long: createLayerDataDescription,
|
||||||
|
RunE: createLayerData,
|
||||||
|
ValidArgsFunction: completion.AutocompleteNone,
|
||||||
|
Example: `podman testing create-layer-data`,
|
||||||
|
}
|
||||||
|
|
||||||
|
createLayerDataOpts entities.CreateLayerDataOptions
|
||||||
|
createLayerDataKey string
|
||||||
|
createLayerDataValue string
|
||||||
|
createLayerDataFile string
|
||||||
|
|
||||||
|
createImageDataDescription = `Create data for an image in local storage.`
|
||||||
|
createImageDataCmd = &cobra.Command{
|
||||||
|
Use: "create-image-data [options]",
|
||||||
|
Args: validate.NoArgs,
|
||||||
|
Short: "Create data for an image",
|
||||||
|
Long: createImageDataDescription,
|
||||||
|
RunE: createImageData,
|
||||||
|
ValidArgsFunction: completion.AutocompleteNone,
|
||||||
|
Example: `podman testing create-image-data`,
|
||||||
|
}
|
||||||
|
|
||||||
|
createImageDataOpts entities.CreateImageDataOptions
|
||||||
|
createImageDataKey string
|
||||||
|
createImageDataValue string
|
||||||
|
createImageDataFile string
|
||||||
|
|
||||||
|
createContainerDataDescription = `Create data for a container in local storage.`
|
||||||
|
createContainerDataCmd = &cobra.Command{
|
||||||
|
Use: "create-container-data [options]",
|
||||||
|
Args: validate.NoArgs,
|
||||||
|
Short: "Create data for a container",
|
||||||
|
Long: createContainerDataDescription,
|
||||||
|
RunE: createContainerData,
|
||||||
|
ValidArgsFunction: completion.AutocompleteNone,
|
||||||
|
Example: `podman testing create-container-data`,
|
||||||
|
}
|
||||||
|
|
||||||
|
createContainerDataOpts entities.CreateContainerDataOptions
|
||||||
|
createContainerDataKey string
|
||||||
|
createContainerDataValue string
|
||||||
|
createContainerDataFile string
|
||||||
|
|
||||||
|
modifyLayerDataDescription = `Modify data for a layer in local storage, corrupting it.`
|
||||||
|
modifyLayerDataCmd = &cobra.Command{
|
||||||
|
Use: "modify-layer-data [options]",
|
||||||
|
Args: validate.NoArgs,
|
||||||
|
Short: "Modify data for a layer",
|
||||||
|
Long: modifyLayerDataDescription,
|
||||||
|
RunE: modifyLayerData,
|
||||||
|
ValidArgsFunction: completion.AutocompleteNone,
|
||||||
|
Example: `podman testing modify-layer-data`,
|
||||||
|
}
|
||||||
|
|
||||||
|
modifyLayerDataOpts entities.ModifyLayerDataOptions
|
||||||
|
modifyLayerDataValue string
|
||||||
|
modifyLayerDataFile string
|
||||||
|
|
||||||
|
modifyImageDataDescription = `Modify data for an image in local storage, corrupting it.`
|
||||||
|
modifyImageDataCmd = &cobra.Command{
|
||||||
|
Use: "modify-image-data [options]",
|
||||||
|
Args: validate.NoArgs,
|
||||||
|
Short: "Modify data for an image",
|
||||||
|
Long: modifyImageDataDescription,
|
||||||
|
RunE: modifyImageData,
|
||||||
|
ValidArgsFunction: completion.AutocompleteNone,
|
||||||
|
Example: `podman testing modify-image-data`,
|
||||||
|
}
|
||||||
|
|
||||||
|
modifyImageDataOpts entities.ModifyImageDataOptions
|
||||||
|
modifyImageDataValue string
|
||||||
|
modifyImageDataFile string
|
||||||
|
|
||||||
|
modifyContainerDataDescription = `Modify data for a container in local storage, corrupting it.`
|
||||||
|
modifyContainerDataCmd = &cobra.Command{
|
||||||
|
Use: "modify-container-data [options]",
|
||||||
|
Args: validate.NoArgs,
|
||||||
|
Short: "Modify data for a container",
|
||||||
|
Long: modifyContainerDataDescription,
|
||||||
|
RunE: modifyContainerData,
|
||||||
|
ValidArgsFunction: completion.AutocompleteNone,
|
||||||
|
Example: `podman testing modify-container-data`,
|
||||||
|
}
|
||||||
|
|
||||||
|
modifyContainerDataOpts entities.ModifyContainerDataOptions
|
||||||
|
modifyContainerDataValue string
|
||||||
|
modifyContainerDataFile string
|
||||||
|
|
||||||
|
removeLayerDataDescription = `Remove data from a layer in local storage, corrupting it.`
|
||||||
|
removeLayerDataCmd = &cobra.Command{
|
||||||
|
Use: "remove-layer-data [options]",
|
||||||
|
Args: validate.NoArgs,
|
||||||
|
Short: "Remove data for a layer",
|
||||||
|
Long: removeLayerDataDescription,
|
||||||
|
RunE: removeLayerData,
|
||||||
|
ValidArgsFunction: completion.AutocompleteNone,
|
||||||
|
Example: `podman testing remove-layer-data`,
|
||||||
|
}
|
||||||
|
|
||||||
|
removeLayerDataOpts entities.RemoveLayerDataOptions
|
||||||
|
|
||||||
|
removeImageDataDescription = `Remove data from an image in local storage, corrupting it.`
|
||||||
|
removeImageDataCmd = &cobra.Command{
|
||||||
|
Use: "remove-image-data [options]",
|
||||||
|
Args: validate.NoArgs,
|
||||||
|
Short: "Remove data from an image",
|
||||||
|
Long: removeImageDataDescription,
|
||||||
|
RunE: removeImageData,
|
||||||
|
ValidArgsFunction: completion.AutocompleteNone,
|
||||||
|
Example: `podman testing remove-image-data`,
|
||||||
|
}
|
||||||
|
|
||||||
|
removeImageDataOpts entities.RemoveImageDataOptions
|
||||||
|
|
||||||
|
removeContainerDataDescription = `Remove data from a container in local storage, corrupting it.`
|
||||||
|
removeContainerDataCmd = &cobra.Command{
|
||||||
|
Use: "remove-container-data [options]",
|
||||||
|
Args: validate.NoArgs,
|
||||||
|
Short: "Remove data from a container",
|
||||||
|
Long: removeContainerDataDescription,
|
||||||
|
RunE: removeContainerData,
|
||||||
|
ValidArgsFunction: completion.AutocompleteNone,
|
||||||
|
Example: `podman testing remove-container-data`,
|
||||||
|
}
|
||||||
|
|
||||||
|
removeContainerDataOpts entities.RemoveContainerDataOptions
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
mainCmd.AddCommand(createLayerDataCmd)
|
||||||
|
flags := createLayerDataCmd.Flags()
|
||||||
|
flags.StringVarP(&createLayerDataOpts.ID, "layer", "i", "", "ID of the layer")
|
||||||
|
flags.StringVarP(&createLayerDataKey, "key", "k", "", "Name of the data item")
|
||||||
|
flags.StringVarP(&createLayerDataValue, "value", "v", "", "Value of the data item")
|
||||||
|
flags.StringVarP(&createLayerDataFile, "file", "f", "", "File containing the data item")
|
||||||
|
|
||||||
|
mainCmd.AddCommand(createImageDataCmd)
|
||||||
|
flags = createImageDataCmd.Flags()
|
||||||
|
flags.StringVarP(&createImageDataOpts.ID, "image", "i", "", "ID of the image")
|
||||||
|
flags.StringVarP(&createImageDataKey, "key", "k", "", "Name of the data item")
|
||||||
|
flags.StringVarP(&createImageDataValue, "value", "v", "", "Value of the data item")
|
||||||
|
flags.StringVarP(&createImageDataFile, "file", "f", "", "File containing the data item")
|
||||||
|
|
||||||
|
mainCmd.AddCommand(createContainerDataCmd)
|
||||||
|
flags = createContainerDataCmd.Flags()
|
||||||
|
flags.StringVarP(&createContainerDataOpts.ID, "container", "i", "", "ID of the container")
|
||||||
|
flags.StringVarP(&createContainerDataKey, "key", "k", "", "Name of the data item")
|
||||||
|
flags.StringVarP(&createContainerDataValue, "value", "v", "", "Value of the data item")
|
||||||
|
flags.StringVarP(&createContainerDataFile, "file", "f", "", "File containing the data item")
|
||||||
|
|
||||||
|
mainCmd.AddCommand(modifyLayerDataCmd)
|
||||||
|
flags = modifyLayerDataCmd.Flags()
|
||||||
|
flags.StringVarP(&modifyLayerDataOpts.ID, "layer", "i", "", "ID of the layer")
|
||||||
|
flags.StringVarP(&modifyLayerDataOpts.Key, "key", "k", "", "Name of the data item")
|
||||||
|
flags.StringVarP(&modifyLayerDataValue, "value", "v", "", "Value of the data item")
|
||||||
|
flags.StringVarP(&modifyLayerDataFile, "file", "f", "", "File containing the data item")
|
||||||
|
|
||||||
|
mainCmd.AddCommand(modifyImageDataCmd)
|
||||||
|
flags = modifyImageDataCmd.Flags()
|
||||||
|
flags.StringVarP(&modifyImageDataOpts.ID, "image", "i", "", "ID of the image")
|
||||||
|
flags.StringVarP(&modifyImageDataOpts.Key, "key", "k", "", "Name of the data item")
|
||||||
|
flags.StringVarP(&modifyImageDataValue, "value", "v", "", "Value of the data item")
|
||||||
|
flags.StringVarP(&modifyImageDataFile, "file", "f", "", "File containing the data item")
|
||||||
|
|
||||||
|
mainCmd.AddCommand(modifyContainerDataCmd)
|
||||||
|
flags = modifyContainerDataCmd.Flags()
|
||||||
|
flags.StringVarP(&modifyContainerDataOpts.ID, "container", "i", "", "ID of the container")
|
||||||
|
flags.StringVarP(&modifyContainerDataOpts.Key, "key", "k", "", "Name of the data item")
|
||||||
|
flags.StringVarP(&modifyContainerDataValue, "value", "v", "", "Value of the data item")
|
||||||
|
flags.StringVarP(&modifyContainerDataFile, "file", "f", "", "File containing the data item")
|
||||||
|
|
||||||
|
mainCmd.AddCommand(removeLayerDataCmd)
|
||||||
|
flags = removeLayerDataCmd.Flags()
|
||||||
|
flags.StringVarP(&removeLayerDataOpts.ID, "layer", "i", "", "ID of the layer")
|
||||||
|
flags.StringVarP(&removeLayerDataOpts.Key, "key", "k", "", "Name of the data item")
|
||||||
|
|
||||||
|
mainCmd.AddCommand(removeImageDataCmd)
|
||||||
|
flags = removeImageDataCmd.Flags()
|
||||||
|
flags.StringVarP(&removeImageDataOpts.ID, "image", "i", "", "ID of the image")
|
||||||
|
flags.StringVarP(&removeImageDataOpts.Key, "key", "k", "", "Name of the data item")
|
||||||
|
|
||||||
|
mainCmd.AddCommand(removeContainerDataCmd)
|
||||||
|
flags = removeContainerDataCmd.Flags()
|
||||||
|
flags.StringVarP(&removeContainerDataOpts.ID, "container", "i", "", "ID of the container")
|
||||||
|
flags.StringVarP(&removeContainerDataOpts.Key, "key", "k", "", "Name of the data item")
|
||||||
|
}
|
||||||
|
|
||||||
|
func createLayerData(cmd *cobra.Command, args []string) error {
|
||||||
|
if createLayerDataOpts.ID == "" {
|
||||||
|
return errors.New("layer ID not specified")
|
||||||
|
}
|
||||||
|
if createLayerDataKey == "" {
|
||||||
|
return errors.New("layer data name not specified")
|
||||||
|
}
|
||||||
|
if createLayerDataValue == "" && createLayerDataFile == "" {
|
||||||
|
return errors.New("neither layer data value nor file specified")
|
||||||
|
}
|
||||||
|
createLayerDataOpts.Data = make(map[string][]byte)
|
||||||
|
if createLayerDataValue != "" {
|
||||||
|
createLayerDataOpts.Data[createLayerDataKey] = []byte(createLayerDataValue)
|
||||||
|
}
|
||||||
|
if createLayerDataFile != "" {
|
||||||
|
buf, err := os.ReadFile(createLayerDataFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
createLayerDataOpts.Data[createLayerDataKey] = buf
|
||||||
|
}
|
||||||
|
_, err := testingEngine.CreateLayerData(mainContext, createLayerDataOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createImageData(cmd *cobra.Command, args []string) error {
|
||||||
|
if createImageDataOpts.ID == "" {
|
||||||
|
return errors.New("image ID not specified")
|
||||||
|
}
|
||||||
|
if createImageDataKey == "" {
|
||||||
|
return errors.New("image data name not specified")
|
||||||
|
}
|
||||||
|
if createImageDataValue == "" && createImageDataFile == "" {
|
||||||
|
return errors.New("neither image data value nor file specified")
|
||||||
|
}
|
||||||
|
createImageDataOpts.Data = make(map[string][]byte)
|
||||||
|
if createImageDataValue != "" {
|
||||||
|
createImageDataOpts.Data[createImageDataKey] = []byte(createImageDataValue)
|
||||||
|
}
|
||||||
|
if createImageDataFile != "" {
|
||||||
|
d, err := os.ReadFile(createImageDataFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
createImageDataOpts.Data[createImageDataKey] = d
|
||||||
|
}
|
||||||
|
_, err := testingEngine.CreateImageData(mainContext, createImageDataOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func createContainerData(cmd *cobra.Command, args []string) error {
|
||||||
|
if createContainerDataOpts.ID == "" {
|
||||||
|
return errors.New("container ID not specified")
|
||||||
|
}
|
||||||
|
if createContainerDataKey == "" {
|
||||||
|
return errors.New("container data name not specified")
|
||||||
|
}
|
||||||
|
if createContainerDataValue == "" && createContainerDataFile == "" {
|
||||||
|
return errors.New("neither container data value nor file specified")
|
||||||
|
}
|
||||||
|
createContainerDataOpts.Data = make(map[string][]byte)
|
||||||
|
if createContainerDataValue != "" {
|
||||||
|
createContainerDataOpts.Data[createContainerDataKey] = []byte(createContainerDataValue)
|
||||||
|
}
|
||||||
|
if createContainerDataFile != "" {
|
||||||
|
d, err := os.ReadFile(createContainerDataFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
createContainerDataOpts.Data[createContainerDataKey] = d
|
||||||
|
}
|
||||||
|
_, err := testingEngine.CreateContainerData(mainContext, createContainerDataOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func modifyLayerData(cmd *cobra.Command, args []string) error {
|
||||||
|
if modifyLayerDataOpts.ID == "" {
|
||||||
|
return errors.New("layer ID not specified")
|
||||||
|
}
|
||||||
|
if modifyLayerDataOpts.Key == "" {
|
||||||
|
return errors.New("layer data name not specified")
|
||||||
|
}
|
||||||
|
if modifyLayerDataValue == "" && modifyLayerDataFile == "" {
|
||||||
|
return errors.New("neither layer data value nor file specified")
|
||||||
|
}
|
||||||
|
modifyLayerDataOpts.Data = []byte(modifyLayerDataValue)
|
||||||
|
if modifyLayerDataFile != "" {
|
||||||
|
d, err := os.ReadFile(modifyLayerDataFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
modifyLayerDataOpts.Data = d
|
||||||
|
}
|
||||||
|
_, err := testingEngine.ModifyLayerData(mainContext, modifyLayerDataOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func modifyImageData(cmd *cobra.Command, args []string) error {
|
||||||
|
if modifyImageDataOpts.ID == "" {
|
||||||
|
return errors.New("image ID not specified")
|
||||||
|
}
|
||||||
|
if modifyImageDataOpts.Key == "" {
|
||||||
|
return errors.New("image data name not specified")
|
||||||
|
}
|
||||||
|
if modifyImageDataValue == "" && modifyImageDataFile == "" {
|
||||||
|
return errors.New("neither image data value nor file specified")
|
||||||
|
}
|
||||||
|
modifyImageDataOpts.Data = []byte(modifyImageDataValue)
|
||||||
|
if modifyImageDataFile != "" {
|
||||||
|
d, err := os.ReadFile(modifyImageDataFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
modifyImageDataOpts.Data = d
|
||||||
|
}
|
||||||
|
_, err := testingEngine.ModifyImageData(mainContext, modifyImageDataOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func modifyContainerData(cmd *cobra.Command, args []string) error {
|
||||||
|
if modifyContainerDataOpts.ID == "" {
|
||||||
|
return errors.New("container ID not specified")
|
||||||
|
}
|
||||||
|
if modifyContainerDataOpts.Key == "" {
|
||||||
|
return errors.New("container data name not specified")
|
||||||
|
}
|
||||||
|
if modifyContainerDataValue == "" && modifyContainerDataFile == "" {
|
||||||
|
return errors.New("neither container data value nor file specified")
|
||||||
|
}
|
||||||
|
modifyContainerDataOpts.Data = []byte(modifyContainerDataValue)
|
||||||
|
if modifyContainerDataFile != "" {
|
||||||
|
d, err := os.ReadFile(modifyContainerDataFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
modifyContainerDataOpts.Data = d
|
||||||
|
}
|
||||||
|
_, err := testingEngine.ModifyContainerData(mainContext, modifyContainerDataOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeLayerData(cmd *cobra.Command, args []string) error {
|
||||||
|
if removeLayerDataOpts.ID == "" {
|
||||||
|
return errors.New("layer ID not specified")
|
||||||
|
}
|
||||||
|
if removeLayerDataOpts.Key == "" {
|
||||||
|
return errors.New("layer data name not specified")
|
||||||
|
}
|
||||||
|
_, err := testingEngine.RemoveLayerData(mainContext, removeLayerDataOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeImageData(cmd *cobra.Command, args []string) error {
|
||||||
|
if removeImageDataOpts.ID == "" {
|
||||||
|
return errors.New("image ID not specified")
|
||||||
|
}
|
||||||
|
if removeImageDataOpts.Key == "" {
|
||||||
|
return errors.New("image data name not specified")
|
||||||
|
}
|
||||||
|
_, err := testingEngine.RemoveImageData(mainContext, removeImageDataOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeContainerData(cmd *cobra.Command, args []string) error {
|
||||||
|
if removeContainerDataOpts.ID == "" {
|
||||||
|
return errors.New("container ID not specified")
|
||||||
|
}
|
||||||
|
if removeContainerDataOpts.Key == "" {
|
||||||
|
return errors.New("container data name not specified")
|
||||||
|
}
|
||||||
|
_, err := testingEngine.RemoveContainerData(mainContext, removeContainerDataOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
91
cmd/podman-testing/layer.go
Normal file
91
cmd/podman-testing/layer.go
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/containers/common/pkg/completion"
|
||||||
|
"github.com/containers/podman/v5/cmd/podman/validate"
|
||||||
|
"github.com/containers/podman/v5/internal/domain/entities"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
populateLayerDescription = `Populate a layer in local storage.`
|
||||||
|
populateLayerCmd = &cobra.Command{
|
||||||
|
Use: "populate-layer [options]",
|
||||||
|
Args: validate.NoArgs,
|
||||||
|
Short: "Populate a layer",
|
||||||
|
Long: populateLayerDescription,
|
||||||
|
RunE: populateLayer,
|
||||||
|
ValidArgsFunction: completion.AutocompleteNone,
|
||||||
|
Example: `podman testing populate-layer`,
|
||||||
|
}
|
||||||
|
|
||||||
|
populateLayerOpts entities.PopulateLayerOptions
|
||||||
|
populateLayerFile string
|
||||||
|
|
||||||
|
modifyLayerDescription = `Modify a layer in local storage, corrupting it.`
|
||||||
|
modifyLayerCmd = &cobra.Command{
|
||||||
|
Use: "modify-layer [options]",
|
||||||
|
Args: validate.NoArgs,
|
||||||
|
Short: "Modify the contents of a layer",
|
||||||
|
Long: modifyLayerDescription,
|
||||||
|
RunE: modifyLayer,
|
||||||
|
ValidArgsFunction: completion.AutocompleteNone,
|
||||||
|
Example: `podman testing modify-layer`,
|
||||||
|
}
|
||||||
|
|
||||||
|
modifyLayerOpts entities.ModifyLayerOptions
|
||||||
|
modifyLayerFile string
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
mainCmd.AddCommand(populateLayerCmd)
|
||||||
|
flags := populateLayerCmd.Flags()
|
||||||
|
flags.StringVarP(&populateLayerOpts.ID, "layer", "l", "", "ID of layer to be populated")
|
||||||
|
flags.StringVarP(&populateLayerFile, "file", "f", "", "archive of contents to extract in layer")
|
||||||
|
|
||||||
|
mainCmd.AddCommand(modifyLayerCmd)
|
||||||
|
flags = modifyLayerCmd.Flags()
|
||||||
|
flags.StringVarP(&modifyLayerOpts.ID, "layer", "l", "", "ID of layer to be modified")
|
||||||
|
flags.StringVarP(&modifyLayerFile, "file", "f", "", "archive of contents to extract over layer")
|
||||||
|
}
|
||||||
|
|
||||||
|
func populateLayer(cmd *cobra.Command, args []string) error {
|
||||||
|
if populateLayerOpts.ID == "" {
|
||||||
|
return errors.New("layer ID not specified")
|
||||||
|
}
|
||||||
|
if populateLayerFile == "" {
|
||||||
|
return errors.New("layer contents file not specified")
|
||||||
|
}
|
||||||
|
buf, err := os.ReadFile(populateLayerFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
populateLayerOpts.ContentsArchive = buf
|
||||||
|
_, err = testingEngine.PopulateLayer(mainContext, populateLayerOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func modifyLayer(cmd *cobra.Command, args []string) error {
|
||||||
|
if modifyLayerOpts.ID == "" {
|
||||||
|
return errors.New("layer ID not specified")
|
||||||
|
}
|
||||||
|
if modifyLayerFile == "" {
|
||||||
|
return errors.New("layer contents file not specified")
|
||||||
|
}
|
||||||
|
buf, err := os.ReadFile(modifyLayerFile)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
modifyLayerOpts.ContentsArchive = buf
|
||||||
|
_, err = testingEngine.ModifyLayer(mainContext, modifyLayerOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
128
cmd/podman-testing/main.go
Normal file
128
cmd/podman-testing/main.go
Normal file
@ -0,0 +1,128 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/containers/common/pkg/config"
|
||||||
|
_ "github.com/containers/podman/v5/cmd/podman/completion"
|
||||||
|
ientities "github.com/containers/podman/v5/internal/domain/entities"
|
||||||
|
"github.com/containers/podman/v5/internal/domain/infra"
|
||||||
|
"github.com/containers/podman/v5/pkg/domain/entities"
|
||||||
|
"github.com/containers/storage"
|
||||||
|
"github.com/containers/storage/pkg/reexec"
|
||||||
|
"github.com/containers/storage/pkg/unshare"
|
||||||
|
"github.com/sirupsen/logrus"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
mainCmd = &cobra.Command{
|
||||||
|
Use: "podman-testing",
|
||||||
|
Long: "Assorted tools for use in testing podman",
|
||||||
|
RunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
return cmd.Help()
|
||||||
|
},
|
||||||
|
PersistentPreRunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
return before()
|
||||||
|
},
|
||||||
|
PersistentPostRunE: func(cmd *cobra.Command, args []string) error {
|
||||||
|
return after()
|
||||||
|
},
|
||||||
|
SilenceUsage: true,
|
||||||
|
SilenceErrors: true,
|
||||||
|
}
|
||||||
|
mainContext = context.Background()
|
||||||
|
podmanConfig entities.PodmanConfig
|
||||||
|
globalStorageOptions storage.StoreOptions
|
||||||
|
globalLogLevel string
|
||||||
|
testingEngine ientities.TestingEngine
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
podmanConfig.FlagSet = mainCmd.PersistentFlags()
|
||||||
|
fl := mainCmd.PersistentFlags()
|
||||||
|
fl.StringVar(&podmanConfig.DockerConfig, "docker-config", os.Getenv("DOCKER_CONFIG"), "path to .docker/config")
|
||||||
|
fl.StringVar(&globalLogLevel, "log-level", "warn", "logging level")
|
||||||
|
fl.StringVar(&podmanConfig.URI, "url", "", "URL to access Podman service")
|
||||||
|
fl.StringVar(&podmanConfig.RegistriesConf, "registries-conf", os.Getenv("REGISTRIES_CONF"), "path to registries.conf (REGISTRIES_CONF)")
|
||||||
|
}
|
||||||
|
|
||||||
|
func before() error {
|
||||||
|
if globalLogLevel != "" {
|
||||||
|
parsedLogLevel, err := logrus.ParseLevel(globalLogLevel)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("parsing log level %q: %w", globalLogLevel, err)
|
||||||
|
}
|
||||||
|
logrus.SetLevel(parsedLogLevel)
|
||||||
|
}
|
||||||
|
if err := storeBefore(); err != nil {
|
||||||
|
return fmt.Errorf("setting up storage: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
podmanConfig.EngineMode = engineMode
|
||||||
|
podmanConfig.Remote = podmanConfig.URI != ""
|
||||||
|
|
||||||
|
containersConf, err := config.Default()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("loading default configuration (may reference $CONTAINERS_CONF): %w", err)
|
||||||
|
}
|
||||||
|
podmanConfig.ContainersConfDefaultsRO = containersConf
|
||||||
|
containersConf, err = config.New(nil)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("loading default configuration (may reference $CONTAINERS_CONF): %w", err)
|
||||||
|
}
|
||||||
|
podmanConfig.ContainersConf = containersConf
|
||||||
|
|
||||||
|
podmanConfig.StorageDriver = globalStorageOptions.GraphDriverName
|
||||||
|
podmanConfig.GraphRoot = globalStorageOptions.GraphRoot
|
||||||
|
podmanConfig.Runroot = globalStorageOptions.RunRoot
|
||||||
|
podmanConfig.ImageStore = globalStorageOptions.ImageStore
|
||||||
|
podmanConfig.StorageOpts = globalStorageOptions.GraphDriverOptions
|
||||||
|
podmanConfig.TransientStore = globalStorageOptions.TransientStore
|
||||||
|
|
||||||
|
te, err := infra.NewTestingEngine(&podmanConfig)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("initializing libpod: %w", err)
|
||||||
|
}
|
||||||
|
testingEngine = te
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func after() error {
|
||||||
|
if err := storeAfter(); err != nil {
|
||||||
|
return fmt.Errorf("shutting down storage: %w", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if reexec.Init() {
|
||||||
|
// We were invoked with a different argv[0] indicating that we
|
||||||
|
// had a specific job to do as a subprocess, and it's done.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
unshare.MaybeReexecUsingUserNamespace(false)
|
||||||
|
|
||||||
|
exitCode := 1
|
||||||
|
if err := mainCmd.Execute(); err != nil {
|
||||||
|
if logrus.IsLevelEnabled(logrus.TraceLevel) {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error: %+v\n", err)
|
||||||
|
} else {
|
||||||
|
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||||
|
}
|
||||||
|
var ee *exec.ExitError
|
||||||
|
if errors.As(err, &ee) {
|
||||||
|
if w, ok := ee.Sys().(syscall.WaitStatus); ok {
|
||||||
|
exitCode = w.ExitStatus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
exitCode = 0
|
||||||
|
}
|
||||||
|
os.Exit(exitCode)
|
||||||
|
}
|
118
cmd/podman-testing/remove.go
Normal file
118
cmd/podman-testing/remove.go
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/containers/common/pkg/completion"
|
||||||
|
"github.com/containers/podman/v5/cmd/podman/validate"
|
||||||
|
"github.com/containers/podman/v5/internal/domain/entities"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
removeStorageLayerDescription = `Remove an unmanaged layer in local storage, potentially corrupting it.`
|
||||||
|
removeStorageLayerCmd = &cobra.Command{
|
||||||
|
Use: "remove-storage-layer [options]",
|
||||||
|
Args: validate.NoArgs,
|
||||||
|
Short: "Remove an unmanaged layer",
|
||||||
|
Long: removeStorageLayerDescription,
|
||||||
|
RunE: removeStorageLayer,
|
||||||
|
ValidArgsFunction: completion.AutocompleteNone,
|
||||||
|
Example: `podman testing remove-storage-layer`,
|
||||||
|
}
|
||||||
|
|
||||||
|
removeStorageLayerOpts entities.RemoveStorageLayerOptions
|
||||||
|
|
||||||
|
removeLayerDescription = `Remove a layer in local storage, potentially corrupting it.`
|
||||||
|
removeLayerCmd = &cobra.Command{
|
||||||
|
Use: "remove-layer [options]",
|
||||||
|
Args: validate.NoArgs,
|
||||||
|
Short: "Remove a layer",
|
||||||
|
Long: removeLayerDescription,
|
||||||
|
RunE: removeLayer,
|
||||||
|
ValidArgsFunction: completion.AutocompleteNone,
|
||||||
|
Example: `podman testing remove-layer`,
|
||||||
|
}
|
||||||
|
|
||||||
|
removeLayerOpts entities.RemoveLayerOptions
|
||||||
|
|
||||||
|
removeImageDescription = `Remove an image in local storage, potentially corrupting it.`
|
||||||
|
removeImageCmd = &cobra.Command{
|
||||||
|
Use: "remove-image [options]",
|
||||||
|
Args: validate.NoArgs,
|
||||||
|
Short: "Remove an image",
|
||||||
|
Long: removeImageDescription,
|
||||||
|
RunE: removeImage,
|
||||||
|
ValidArgsFunction: completion.AutocompleteNone,
|
||||||
|
Example: `podman testing remove-image`,
|
||||||
|
}
|
||||||
|
|
||||||
|
removeImageOpts entities.RemoveImageOptions
|
||||||
|
|
||||||
|
removeContainerDescription = `Remove a container in local storage, potentially corrupting it.`
|
||||||
|
removeContainerCmd = &cobra.Command{
|
||||||
|
Use: "remove-container [options]",
|
||||||
|
Args: validate.NoArgs,
|
||||||
|
Short: "Remove an container",
|
||||||
|
Long: removeContainerDescription,
|
||||||
|
RunE: removeContainer,
|
||||||
|
ValidArgsFunction: completion.AutocompleteNone,
|
||||||
|
Example: `podman testing remove-container`,
|
||||||
|
}
|
||||||
|
|
||||||
|
removeContainerOpts entities.RemoveContainerOptions
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
mainCmd.AddCommand(removeStorageLayerCmd)
|
||||||
|
flags := removeStorageLayerCmd.Flags()
|
||||||
|
flags.StringVarP(&removeStorageLayerOpts.ID, "layer", "i", "", "ID of the layer to remove")
|
||||||
|
|
||||||
|
mainCmd.AddCommand(removeLayerCmd)
|
||||||
|
flags = removeLayerCmd.Flags()
|
||||||
|
flags.StringVarP(&removeLayerOpts.ID, "layer", "i", "", "ID of the layer to remove")
|
||||||
|
|
||||||
|
mainCmd.AddCommand(removeImageCmd)
|
||||||
|
flags = removeImageCmd.Flags()
|
||||||
|
flags.StringVarP(&removeImageOpts.ID, "image", "i", "", "ID of the image to remove")
|
||||||
|
|
||||||
|
mainCmd.AddCommand(removeContainerCmd)
|
||||||
|
flags = removeContainerCmd.Flags()
|
||||||
|
flags.StringVarP(&removeContainerOpts.ID, "container", "i", "", "ID of the container to remove")
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeStorageLayer(cmd *cobra.Command, args []string) error {
|
||||||
|
results, err := testingEngine.RemoveStorageLayer(mainContext, removeStorageLayerOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println(results.ID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeLayer(cmd *cobra.Command, args []string) error {
|
||||||
|
results, err := testingEngine.RemoveLayer(mainContext, removeLayerOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println(results.ID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeImage(cmd *cobra.Command, args []string) error {
|
||||||
|
results, err := testingEngine.RemoveImage(mainContext, removeImageOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println(results.ID)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func removeContainer(cmd *cobra.Command, args []string) error {
|
||||||
|
results, err := testingEngine.RemoveContainer(mainContext, removeContainerOpts)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Println(results.ID)
|
||||||
|
return nil
|
||||||
|
}
|
65
cmd/podman-testing/store_supported.go
Normal file
65
cmd/podman-testing/store_supported.go
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
//go:build linux && !remote
|
||||||
|
// +build linux,!remote
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
|
"github.com/containers/podman/v5/pkg/domain/entities"
|
||||||
|
"github.com/containers/storage"
|
||||||
|
"github.com/containers/storage/types"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
globalStore storage.Store
|
||||||
|
engineMode = entities.ABIMode
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
if defaultStoreOptions, err := storage.DefaultStoreOptions(); err == nil {
|
||||||
|
globalStorageOptions = defaultStoreOptions
|
||||||
|
}
|
||||||
|
if storageConf, ok := os.LookupEnv("CONTAINERS_STORAGE_CONF"); ok {
|
||||||
|
options := globalStorageOptions
|
||||||
|
if types.ReloadConfigurationFileIfNeeded(storageConf, &options) == nil {
|
||||||
|
globalStorageOptions = options
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fl := mainCmd.PersistentFlags()
|
||||||
|
fl.StringVar(&globalStorageOptions.GraphDriverName, "storage-driver", "", "storage driver used to manage images and containers")
|
||||||
|
fl.StringVar(&globalStorageOptions.GraphRoot, "root", "", "where images and containers will be stored")
|
||||||
|
fl.StringVar(&globalStorageOptions.RunRoot, "runroot", "", "where volatile state information will be stored")
|
||||||
|
fl.StringArrayVar(&globalStorageOptions.GraphDriverOptions, "storage-opt", nil, "storage driver options")
|
||||||
|
fl.StringVar(&globalStorageOptions.ImageStore, "imagestore", "", "where to store just some parts of images")
|
||||||
|
fl.BoolVar(&globalStorageOptions.TransientStore, "transient-store", false, "enable transient container storage")
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeBefore() error {
|
||||||
|
defaultStoreOptions, err := storage.DefaultStoreOptions()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintf(os.Stderr, "selecting storage options: %v", err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
globalStorageOptions = defaultStoreOptions
|
||||||
|
store, err := storage.GetStore(globalStorageOptions)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
globalStore = store
|
||||||
|
if podmanConfig.URI != "" {
|
||||||
|
engineMode = entities.TunnelMode
|
||||||
|
} else {
|
||||||
|
engineMode = entities.ABIMode
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeAfter() error {
|
||||||
|
if globalStore != nil {
|
||||||
|
_, err := globalStore.Shutdown(false)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
16
cmd/podman-testing/store_unsupported.go
Normal file
16
cmd/podman-testing/store_unsupported.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
//go:build !linux || remote
|
||||||
|
// +build !linux remote
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "github.com/containers/podman/v5/pkg/domain/entities"
|
||||||
|
|
||||||
|
const engineMode = entities.TunnelMode
|
||||||
|
|
||||||
|
func storeBefore() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func storeAfter() error {
|
||||||
|
return nil
|
||||||
|
}
|
@ -18,8 +18,8 @@ BUILD_TAGS_TUNNEL="$BUILD_TAGS_DEFAULT,remote"
|
|||||||
BUILD_TAGS_REMOTE="remote,containers_image_openpgp"
|
BUILD_TAGS_REMOTE="remote,containers_image_openpgp"
|
||||||
|
|
||||||
SKIP_DIRS_ABI=""
|
SKIP_DIRS_ABI=""
|
||||||
SKIP_DIRS_TUNNEL="pkg/api,pkg/domain/infra/abi"
|
SKIP_DIRS_TUNNEL="pkg/api,pkg/domain/infra/abi,internal/domain/infra/abi"
|
||||||
SKIP_DIRS_REMOTE="libpod/events,pkg/api,pkg/domain/infra/abi,pkg/machine/qemu,pkg/trust,test"
|
SKIP_DIRS_REMOTE="libpod/events,pkg/api,pkg/domain/infra/abi,internal/domain/infra/abi,pkg/machine/qemu,pkg/trust,test"
|
||||||
|
|
||||||
declare -a to_lint
|
declare -a to_lint
|
||||||
to_lint=(ABI TUNNEL REMOTE)
|
to_lint=(ABI TUNNEL REMOTE)
|
||||||
|
27
internal/domain/entities/engine_testing.go
Normal file
27
internal/domain/entities/engine_testing.go
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
package entities
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TestingEngine interface { //nolint:interfacebloat
|
||||||
|
CreateStorageLayer(ctx context.Context, opts CreateStorageLayerOptions) (*CreateStorageLayerReport, error)
|
||||||
|
CreateLayer(ctx context.Context, opts CreateLayerOptions) (*CreateLayerReport, error)
|
||||||
|
CreateLayerData(ctx context.Context, opts CreateLayerDataOptions) (*CreateLayerDataReport, error)
|
||||||
|
CreateImage(ctx context.Context, opts CreateImageOptions) (*CreateImageReport, error)
|
||||||
|
CreateImageData(ctx context.Context, opts CreateImageDataOptions) (*CreateImageDataReport, error)
|
||||||
|
CreateContainer(ctx context.Context, opts CreateContainerOptions) (*CreateContainerReport, error)
|
||||||
|
CreateContainerData(ctx context.Context, opts CreateContainerDataOptions) (*CreateContainerDataReport, error)
|
||||||
|
ModifyLayer(ctx context.Context, opts ModifyLayerOptions) (*ModifyLayerReport, error)
|
||||||
|
PopulateLayer(ctx context.Context, opts PopulateLayerOptions) (*PopulateLayerReport, error)
|
||||||
|
RemoveStorageLayer(ctx context.Context, opts RemoveStorageLayerOptions) (*RemoveStorageLayerReport, error)
|
||||||
|
RemoveLayer(ctx context.Context, opts RemoveLayerOptions) (*RemoveLayerReport, error)
|
||||||
|
RemoveImage(ctx context.Context, opts RemoveImageOptions) (*RemoveImageReport, error)
|
||||||
|
RemoveContainer(ctx context.Context, opts RemoveContainerOptions) (*RemoveContainerReport, error)
|
||||||
|
RemoveLayerData(ctx context.Context, opts RemoveLayerDataOptions) (*RemoveLayerDataReport, error)
|
||||||
|
RemoveImageData(ctx context.Context, opts RemoveImageDataOptions) (*RemoveImageDataReport, error)
|
||||||
|
RemoveContainerData(ctx context.Context, opts RemoveContainerDataOptions) (*RemoveContainerDataReport, error)
|
||||||
|
ModifyLayerData(ctx context.Context, opts ModifyLayerDataOptions) (*ModifyLayerDataReport, error)
|
||||||
|
ModifyImageData(ctx context.Context, opts ModifyImageDataOptions) (*ModifyImageDataReport, error)
|
||||||
|
ModifyContainerData(ctx context.Context, opts ModifyContainerDataOptions) (*ModifyContainerDataReport, error)
|
||||||
|
}
|
153
internal/domain/entities/testing.go
Normal file
153
internal/domain/entities/testing.go
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
package entities
|
||||||
|
|
||||||
|
type CreateStorageLayerOptions struct {
|
||||||
|
Parent string
|
||||||
|
ID string
|
||||||
|
ContentsArchive []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateStorageLayerReport struct {
|
||||||
|
ID string
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateLayerOptions struct {
|
||||||
|
Parent string
|
||||||
|
ID string
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateLayerReport struct {
|
||||||
|
ID string
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateLayerDataOptions struct {
|
||||||
|
ID string
|
||||||
|
Data map[string][]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateLayerDataReport struct{}
|
||||||
|
|
||||||
|
type CreateImageOptions struct {
|
||||||
|
Layer string
|
||||||
|
Names []string
|
||||||
|
ID string
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateImageReport struct {
|
||||||
|
ID string
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateImageDataOptions struct {
|
||||||
|
ID string
|
||||||
|
Data map[string][]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateImageDataReport struct{}
|
||||||
|
|
||||||
|
type CreateContainerOptions struct {
|
||||||
|
Layer string
|
||||||
|
Image string
|
||||||
|
Names []string
|
||||||
|
ID string
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateContainerReport struct {
|
||||||
|
ID string
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateContainerDataOptions struct {
|
||||||
|
ID string
|
||||||
|
Data map[string][]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type CreateContainerDataReport struct{}
|
||||||
|
|
||||||
|
type ModifyLayerOptions struct {
|
||||||
|
ID string
|
||||||
|
ContentsArchive []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type ModifyLayerReport struct{}
|
||||||
|
|
||||||
|
type PopulateLayerOptions struct {
|
||||||
|
ID string
|
||||||
|
ContentsArchive []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type PopulateLayerReport struct{}
|
||||||
|
|
||||||
|
type RemoveStorageLayerOptions struct {
|
||||||
|
ID string
|
||||||
|
}
|
||||||
|
|
||||||
|
type RemoveStorageLayerReport struct {
|
||||||
|
ID string
|
||||||
|
}
|
||||||
|
|
||||||
|
type RemoveLayerOptions struct {
|
||||||
|
ID string
|
||||||
|
}
|
||||||
|
|
||||||
|
type RemoveLayerReport struct {
|
||||||
|
ID string
|
||||||
|
}
|
||||||
|
|
||||||
|
type RemoveImageOptions struct {
|
||||||
|
ID string
|
||||||
|
}
|
||||||
|
|
||||||
|
type RemoveImageReport struct {
|
||||||
|
ID string
|
||||||
|
}
|
||||||
|
|
||||||
|
type RemoveContainerOptions struct {
|
||||||
|
ID string
|
||||||
|
}
|
||||||
|
|
||||||
|
type RemoveContainerReport struct {
|
||||||
|
ID string
|
||||||
|
}
|
||||||
|
|
||||||
|
type RemoveLayerDataOptions struct {
|
||||||
|
ID string
|
||||||
|
Key string
|
||||||
|
}
|
||||||
|
|
||||||
|
type RemoveLayerDataReport struct{}
|
||||||
|
|
||||||
|
type RemoveImageDataOptions struct {
|
||||||
|
ID string
|
||||||
|
Key string
|
||||||
|
}
|
||||||
|
|
||||||
|
type RemoveImageDataReport struct{}
|
||||||
|
|
||||||
|
type RemoveContainerDataOptions struct {
|
||||||
|
ID string
|
||||||
|
Key string
|
||||||
|
}
|
||||||
|
|
||||||
|
type RemoveContainerDataReport struct{}
|
||||||
|
|
||||||
|
type ModifyLayerDataOptions struct {
|
||||||
|
ID string
|
||||||
|
Key string
|
||||||
|
Data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type ModifyLayerDataReport struct{}
|
||||||
|
|
||||||
|
type ModifyImageDataOptions struct {
|
||||||
|
ID string
|
||||||
|
Key string
|
||||||
|
Data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type ModifyImageDataReport struct{}
|
||||||
|
|
||||||
|
type ModifyContainerDataOptions struct {
|
||||||
|
ID string
|
||||||
|
Key string
|
||||||
|
Data []byte
|
||||||
|
}
|
||||||
|
|
||||||
|
type ModifyContainerDataReport struct{}
|
220
internal/domain/infra/abi/testing.go
Normal file
220
internal/domain/infra/abi/testing.go
Normal file
@ -0,0 +1,220 @@
|
|||||||
|
package abi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
|
"github.com/containers/image/v5/manifest"
|
||||||
|
"github.com/containers/podman/v5/internal/domain/entities"
|
||||||
|
"github.com/containers/podman/v5/libpod"
|
||||||
|
"github.com/containers/storage"
|
||||||
|
graphdriver "github.com/containers/storage/drivers"
|
||||||
|
"github.com/containers/storage/pkg/chrootarchive"
|
||||||
|
"github.com/containers/storage/pkg/stringid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TestingEngine struct {
|
||||||
|
Libpod *libpod.Runtime
|
||||||
|
Store storage.Store
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) CreateStorageLayer(ctx context.Context, opts entities.CreateStorageLayerOptions) (*entities.CreateStorageLayerReport, error) {
|
||||||
|
driver, err := te.Store.GraphDriver()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
id := opts.ID
|
||||||
|
if id == "" {
|
||||||
|
id = stringid.GenerateNonCryptoID()
|
||||||
|
}
|
||||||
|
if err := driver.CreateReadWrite(id, opts.Parent, &graphdriver.CreateOpts{}); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &entities.CreateStorageLayerReport{ID: id}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) CreateLayer(ctx context.Context, opts entities.CreateLayerOptions) (*entities.CreateLayerReport, error) {
|
||||||
|
layer, err := te.Store.CreateLayer(opts.ID, opts.Parent, nil, "", true, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &entities.CreateLayerReport{ID: layer.ID}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) CreateLayerData(ctx context.Context, opts entities.CreateLayerDataOptions) (*entities.CreateLayerDataReport, error) {
|
||||||
|
for key, data := range opts.Data {
|
||||||
|
if err := te.Store.SetLayerBigData(opts.ID, key, bytes.NewReader(data)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &entities.CreateLayerDataReport{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) ModifyLayer(ctx context.Context, opts entities.ModifyLayerOptions) (*entities.ModifyLayerReport, error) {
|
||||||
|
mnt, err := te.Store.Mount(opts.ID, "")
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
modifyError := chrootarchive.UntarWithRoot(bytes.NewReader(opts.ContentsArchive), mnt, nil, mnt)
|
||||||
|
if _, err := te.Store.Unmount(opts.ID, false); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if modifyError != nil {
|
||||||
|
return nil, modifyError
|
||||||
|
}
|
||||||
|
return &entities.ModifyLayerReport{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) PopulateLayer(ctx context.Context, opts entities.PopulateLayerOptions) (*entities.PopulateLayerReport, error) {
|
||||||
|
if _, err := te.Store.ApplyDiff(opts.ID, bytes.NewReader(opts.ContentsArchive)); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &entities.PopulateLayerReport{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) CreateImage(ctx context.Context, opts entities.CreateImageOptions) (*entities.CreateImageReport, error) {
|
||||||
|
image, err := te.Store.CreateImage(opts.ID, opts.Names, opts.Layer, "", nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &entities.CreateImageReport{ID: image.ID}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) CreateImageData(ctx context.Context, opts entities.CreateImageDataOptions) (*entities.CreateImageDataReport, error) {
|
||||||
|
for key, data := range opts.Data {
|
||||||
|
if err := te.Store.SetImageBigData(opts.ID, key, data, manifest.Digest); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &entities.CreateImageDataReport{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) CreateContainer(ctx context.Context, opts entities.CreateContainerOptions) (*entities.CreateContainerReport, error) {
|
||||||
|
image, err := te.Store.CreateContainer(opts.ID, opts.Names, opts.Image, opts.Layer, "", nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &entities.CreateContainerReport{ID: image.ID}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) CreateContainerData(ctx context.Context, opts entities.CreateContainerDataOptions) (*entities.CreateContainerDataReport, error) {
|
||||||
|
for key, data := range opts.Data {
|
||||||
|
if err := te.Store.SetContainerBigData(opts.ID, key, data); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return &entities.CreateContainerDataReport{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) RemoveStorageLayer(ctx context.Context, opts entities.RemoveStorageLayerOptions) (*entities.RemoveStorageLayerReport, error) {
|
||||||
|
driver, err := te.Store.GraphDriver()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := driver.Remove(opts.ID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &entities.RemoveStorageLayerReport{ID: opts.ID}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) RemoveLayer(ctx context.Context, opts entities.RemoveLayerOptions) (*entities.RemoveLayerReport, error) {
|
||||||
|
if err := te.Store.Delete(opts.ID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &entities.RemoveLayerReport{ID: opts.ID}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) RemoveImage(ctx context.Context, opts entities.RemoveImageOptions) (*entities.RemoveImageReport, error) {
|
||||||
|
if err := te.Store.Delete(opts.ID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &entities.RemoveImageReport{ID: opts.ID}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) RemoveContainer(ctx context.Context, opts entities.RemoveContainerOptions) (*entities.RemoveContainerReport, error) {
|
||||||
|
if err := te.Store.Delete(opts.ID); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &entities.RemoveContainerReport{ID: opts.ID}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) datapath(itemType, id, key string) (string, error) {
|
||||||
|
switch itemType {
|
||||||
|
default:
|
||||||
|
return "", fmt.Errorf("unknown item type %q", itemType)
|
||||||
|
case "layer", "image", "container":
|
||||||
|
}
|
||||||
|
driverName := te.Store.GraphDriverName()
|
||||||
|
graphRoot := te.Store.GraphRoot()
|
||||||
|
datapath := filepath.Join(graphRoot, driverName+"-"+itemType+"s", id, key) // more or less accurate for keys whose names are [.a-z0-9]+
|
||||||
|
return datapath, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) RemoveLayerData(ctx context.Context, opts entities.RemoveLayerDataOptions) (*entities.RemoveLayerDataReport, error) {
|
||||||
|
datapath, err := te.datapath("layer", opts.ID, opts.Key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = os.Remove(datapath); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &entities.RemoveLayerDataReport{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) RemoveImageData(ctx context.Context, opts entities.RemoveImageDataOptions) (*entities.RemoveImageDataReport, error) {
|
||||||
|
datapath, err := te.datapath("image", opts.ID, opts.Key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = os.Remove(datapath); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &entities.RemoveImageDataReport{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) RemoveContainerData(ctx context.Context, opts entities.RemoveContainerDataOptions) (*entities.RemoveContainerDataReport, error) {
|
||||||
|
datapath, err := te.datapath("container", opts.ID, opts.Key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = os.Remove(datapath); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &entities.RemoveContainerDataReport{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) ModifyLayerData(ctx context.Context, opts entities.ModifyLayerDataOptions) (*entities.ModifyLayerDataReport, error) {
|
||||||
|
datapath, err := te.datapath("layer", opts.ID, opts.Key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = os.WriteFile(datapath, opts.Data, 0o0600); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &entities.ModifyLayerDataReport{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) ModifyImageData(ctx context.Context, opts entities.ModifyImageDataOptions) (*entities.ModifyImageDataReport, error) {
|
||||||
|
datapath, err := te.datapath("image", opts.ID, opts.Key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = os.WriteFile(datapath, opts.Data, 0o0600); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &entities.ModifyImageDataReport{}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) ModifyContainerData(ctx context.Context, opts entities.ModifyContainerDataOptions) (*entities.ModifyContainerDataReport, error) {
|
||||||
|
datapath, err := te.datapath("container", opts.ID, opts.Key)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err = os.WriteFile(datapath, opts.Data, 0o0600); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &entities.ModifyContainerDataReport{}, nil
|
||||||
|
}
|
5
internal/domain/infra/abi/testing_test.go
Normal file
5
internal/domain/infra/abi/testing_test.go
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package abi
|
||||||
|
|
||||||
|
import "github.com/containers/podman/v5/internal/domain/entities"
|
||||||
|
|
||||||
|
var _ entities.TestingEngine = &TestingEngine{}
|
26
internal/domain/infra/runtime_abi.go
Normal file
26
internal/domain/infra/runtime_abi.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
//go:build !remote
|
||||||
|
|
||||||
|
package infra
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
ientities "github.com/containers/podman/v5/internal/domain/entities"
|
||||||
|
"github.com/containers/podman/v5/internal/domain/infra/tunnel"
|
||||||
|
"github.com/containers/podman/v5/pkg/bindings"
|
||||||
|
"github.com/containers/podman/v5/pkg/domain/entities"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewTestingEngine factory provides a libpod runtime for testing-specific operations
|
||||||
|
func NewTestingEngine(facts *entities.PodmanConfig) (ientities.TestingEngine, error) {
|
||||||
|
switch facts.EngineMode {
|
||||||
|
case entities.ABIMode:
|
||||||
|
r, err := NewLibpodTestingRuntime(facts.FlagSet, facts)
|
||||||
|
return r, err
|
||||||
|
case entities.TunnelMode:
|
||||||
|
ctx, err := bindings.NewConnectionWithIdentity(context.Background(), facts.URI, facts.Identity, facts.MachineMode)
|
||||||
|
return &tunnel.TestingEngine{ClientCtx: ctx}, err
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("runtime mode '%v' is not supported", facts.EngineMode)
|
||||||
|
}
|
26
internal/domain/infra/runtime_proxy.go
Normal file
26
internal/domain/infra/runtime_proxy.go
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
//go:build !remote
|
||||||
|
|
||||||
|
package infra
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
ientities "github.com/containers/podman/v5/internal/domain/entities"
|
||||||
|
"github.com/containers/podman/v5/internal/domain/infra/abi"
|
||||||
|
"github.com/containers/podman/v5/pkg/domain/entities"
|
||||||
|
"github.com/containers/podman/v5/pkg/domain/infra"
|
||||||
|
"github.com/containers/storage"
|
||||||
|
flag "github.com/spf13/pflag"
|
||||||
|
)
|
||||||
|
|
||||||
|
func NewLibpodTestingRuntime(flags *flag.FlagSet, opts *entities.PodmanConfig) (ientities.TestingEngine, error) {
|
||||||
|
r, err := infra.GetRuntime(context.Background(), flags, opts)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
store, err := storage.GetStore(r.StorageConfig())
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return &abi.TestingEngine{Libpod: r, Store: store}, nil
|
||||||
|
}
|
25
internal/domain/infra/runtime_tunnel.go
Normal file
25
internal/domain/infra/runtime_tunnel.go
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
//go:build remote
|
||||||
|
|
||||||
|
package infra
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
ientities "github.com/containers/podman/v5/internal/domain/entities"
|
||||||
|
"github.com/containers/podman/v5/internal/domain/infra/tunnel"
|
||||||
|
"github.com/containers/podman/v5/pkg/bindings"
|
||||||
|
"github.com/containers/podman/v5/pkg/domain/entities"
|
||||||
|
)
|
||||||
|
|
||||||
|
// NewTestingEngine factory provides a libpod runtime for testing-specific operations
|
||||||
|
func NewTestingEngine(facts *entities.PodmanConfig) (ientities.TestingEngine, error) {
|
||||||
|
switch facts.EngineMode {
|
||||||
|
case entities.ABIMode:
|
||||||
|
return nil, fmt.Errorf("direct image runtime not supported")
|
||||||
|
case entities.TunnelMode:
|
||||||
|
ctx, err := bindings.NewConnectionWithIdentity(context.Background(), facts.URI, facts.Identity, facts.MachineMode)
|
||||||
|
return &tunnel.TestingEngine{ClientCtx: ctx}, err
|
||||||
|
}
|
||||||
|
return nil, fmt.Errorf("runtime mode '%v' is not supported", facts.EngineMode)
|
||||||
|
}
|
88
internal/domain/infra/tunnel/testing.go
Normal file
88
internal/domain/infra/tunnel/testing.go
Normal file
@ -0,0 +1,88 @@
|
|||||||
|
package tunnel
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/containers/podman/v5/internal/domain/entities"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TestingEngine struct {
|
||||||
|
ClientCtx context.Context
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) CreateStorageLayer(ctx context.Context, opts entities.CreateStorageLayerOptions) (*entities.CreateStorageLayerReport, error) {
|
||||||
|
return nil, syscall.ENOSYS
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) CreateLayer(ctx context.Context, opts entities.CreateLayerOptions) (*entities.CreateLayerReport, error) {
|
||||||
|
return nil, syscall.ENOSYS
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) CreateLayerData(ctx context.Context, opts entities.CreateLayerDataOptions) (*entities.CreateLayerDataReport, error) {
|
||||||
|
return nil, syscall.ENOSYS
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) ModifyLayer(ctx context.Context, opts entities.ModifyLayerOptions) (*entities.ModifyLayerReport, error) {
|
||||||
|
return nil, syscall.ENOSYS
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) PopulateLayer(ctx context.Context, opts entities.PopulateLayerOptions) (*entities.PopulateLayerReport, error) {
|
||||||
|
return nil, syscall.ENOSYS
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) RemoveStorageLayer(ctx context.Context, opts entities.RemoveStorageLayerOptions) (*entities.RemoveStorageLayerReport, error) {
|
||||||
|
return nil, syscall.ENOSYS
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) CreateImage(ctx context.Context, opts entities.CreateImageOptions) (*entities.CreateImageReport, error) {
|
||||||
|
return nil, syscall.ENOSYS
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) CreateImageData(ctx context.Context, opts entities.CreateImageDataOptions) (*entities.CreateImageDataReport, error) {
|
||||||
|
return nil, syscall.ENOSYS
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) RemoveLayer(ctx context.Context, opts entities.RemoveLayerOptions) (*entities.RemoveLayerReport, error) {
|
||||||
|
return nil, syscall.ENOSYS
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) RemoveImage(ctx context.Context, opts entities.RemoveImageOptions) (*entities.RemoveImageReport, error) {
|
||||||
|
return nil, syscall.ENOSYS
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) RemoveContainer(ctx context.Context, opts entities.RemoveContainerOptions) (*entities.RemoveContainerReport, error) {
|
||||||
|
return nil, syscall.ENOSYS
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) CreateContainer(ctx context.Context, opts entities.CreateContainerOptions) (*entities.CreateContainerReport, error) {
|
||||||
|
return nil, syscall.ENOSYS
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) CreateContainerData(ctx context.Context, opts entities.CreateContainerDataOptions) (*entities.CreateContainerDataReport, error) {
|
||||||
|
return nil, syscall.ENOSYS
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) RemoveLayerData(ctx context.Context, opts entities.RemoveLayerDataOptions) (*entities.RemoveLayerDataReport, error) {
|
||||||
|
return nil, syscall.ENOSYS
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) RemoveImageData(ctx context.Context, opts entities.RemoveImageDataOptions) (*entities.RemoveImageDataReport, error) {
|
||||||
|
return nil, syscall.ENOSYS
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) RemoveContainerData(ctx context.Context, opts entities.RemoveContainerDataOptions) (*entities.RemoveContainerDataReport, error) {
|
||||||
|
return nil, syscall.ENOSYS
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) ModifyLayerData(ctx context.Context, opts entities.ModifyLayerDataOptions) (*entities.ModifyLayerDataReport, error) {
|
||||||
|
return nil, syscall.ENOSYS
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) ModifyImageData(ctx context.Context, opts entities.ModifyImageDataOptions) (*entities.ModifyImageDataReport, error) {
|
||||||
|
return nil, syscall.ENOSYS
|
||||||
|
}
|
||||||
|
|
||||||
|
func (te *TestingEngine) ModifyContainerData(ctx context.Context, opts entities.ModifyContainerDataOptions) (*entities.ModifyContainerDataReport, error) {
|
||||||
|
return nil, syscall.ENOSYS
|
||||||
|
}
|
5
internal/domain/infra/tunnel/testing_test.go
Normal file
5
internal/domain/infra/tunnel/testing_test.go
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
package tunnel
|
||||||
|
|
||||||
|
import "github.com/containers/podman/v5/internal/domain/entities"
|
||||||
|
|
||||||
|
var _ entities.TestingEngine = &TestingEngine{}
|
@ -240,6 +240,10 @@ export BUILDTAGS="$BASEBUILDTAGS exclude_graphdriver_btrfs btrfs_noversion remot
|
|||||||
export BUILDTAGS="$BASEBUILDTAGS $(hack/btrfs_installed_tag.sh) $(hack/btrfs_tag.sh)"
|
export BUILDTAGS="$BASEBUILDTAGS $(hack/btrfs_installed_tag.sh) $(hack/btrfs_tag.sh)"
|
||||||
%gobuild -o bin/quadlet ./cmd/quadlet
|
%gobuild -o bin/quadlet ./cmd/quadlet
|
||||||
|
|
||||||
|
# build %%{name}-testing
|
||||||
|
export BUILDTAGS="$BASEBUILDTAGS $(hack/btrfs_installed_tag.sh) $(hack/btrfs_tag.sh)"
|
||||||
|
%gobuild -o bin/podman-testing ./cmd/podman-testing
|
||||||
|
|
||||||
# reset LDFLAGS for plugins binaries
|
# reset LDFLAGS for plugins binaries
|
||||||
LDFLAGS=''
|
LDFLAGS=''
|
||||||
|
|
||||||
@ -255,8 +259,9 @@ PODMAN_VERSION=%{version} %{__make} DESTDIR=%{buildroot} PREFIX=%{_prefix} ETCDI
|
|||||||
install.docker \
|
install.docker \
|
||||||
install.docker-docs \
|
install.docker-docs \
|
||||||
install.remote \
|
install.remote \
|
||||||
|
install.testing \
|
||||||
%if %{defined _modulesloaddir}
|
%if %{defined _modulesloaddir}
|
||||||
install.modules-load
|
install.modules-load
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
sed -i 's;%{buildroot};;g' %{buildroot}%{_bindir}/docker
|
sed -i 's;%{buildroot};;g' %{buildroot}%{_bindir}/docker
|
||||||
@ -314,6 +319,7 @@ cp -pav test/system %{buildroot}/%{_datadir}/%{name}/test/
|
|||||||
%{_datadir}/zsh/site-functions/_%{name}-remote
|
%{_datadir}/zsh/site-functions/_%{name}-remote
|
||||||
|
|
||||||
%files tests
|
%files tests
|
||||||
|
%{_bindir}/%{name}-testing
|
||||||
%{_datadir}/%{name}/test
|
%{_datadir}/%{name}/test
|
||||||
|
|
||||||
%files -n %{name}sh
|
%files -n %{name}sh
|
||||||
|
248
test/system/331-system-check.bats
Normal file
248
test/system/331-system-check.bats
Normal file
@ -0,0 +1,248 @@
|
|||||||
|
#!/usr/bin/env bats -*- bats -*-
|
||||||
|
#
|
||||||
|
# Creates errors that should be caught by `system check`, and verifies
|
||||||
|
# that they are caught and remedied, even if it requires discarding some
|
||||||
|
# data in read-write layers.
|
||||||
|
#
|
||||||
|
|
||||||
|
load helpers
|
||||||
|
|
||||||
|
@test "podman system check - unmanaged layers" {
|
||||||
|
run_podman_testing create-storage-layer
|
||||||
|
layerID="$output"
|
||||||
|
run_podman_testing create-storage-layer --parent=$layerID
|
||||||
|
run_podman 125 system check
|
||||||
|
assert "$output" =~ "layer in lower level storage driver not accounted for" "output from 'podman system check' with unmanaged layers"
|
||||||
|
run_podman system check -r
|
||||||
|
run_podman system check
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "podman system check - unused layers" {
|
||||||
|
run_podman_testing create-layer
|
||||||
|
layerID="$output"
|
||||||
|
run_podman_testing create-layer --parent=$layerID
|
||||||
|
run_podman system check
|
||||||
|
run_podman 125 system check -m 0
|
||||||
|
assert "$output" =~ "layer not referenced" "output from 'podman system check' with unused layers"
|
||||||
|
run_podman system check -m 0 -r
|
||||||
|
run_podman system check -m 0
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "podman system check - layer content digest changed" {
|
||||||
|
run_podman_testing create-layer
|
||||||
|
layerID="$output"
|
||||||
|
make_layer_blob 8 ${PODMAN_TMPDIR}/archive.tar
|
||||||
|
run_podman_testing populate-layer --layer=$layerID --file=${PODMAN_TMPDIR}/archive.tar
|
||||||
|
run_podman_testing create-image --layer=$layerID
|
||||||
|
imageID="$output"
|
||||||
|
testing_make_image_metadata_for_layer_blobs $imageID ${PODMAN_TMPDIR}/archive.tar
|
||||||
|
run_podman create $imageID
|
||||||
|
make_layer_blob 1 ${PODMAN_TMPDIR}/archive.tar
|
||||||
|
run_podman_testing modify-layer --layer=$layerID --file=${PODMAN_TMPDIR}/archive.tar
|
||||||
|
run_podman 125 system check
|
||||||
|
assert "$output" =~ "checksum failed" "output from 'podman system check' with modified layer contents"
|
||||||
|
run_podman 125 system check -r
|
||||||
|
run_podman 0+w system check -r -f
|
||||||
|
run_podman system check
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "podman system check - layer content added" {
|
||||||
|
run_podman_testing create-layer
|
||||||
|
layerID="$output"
|
||||||
|
make_layer_blob 8 ${PODMAN_TMPDIR}/archive.tar
|
||||||
|
run_podman_testing populate-layer --layer=$layerID --file=${PODMAN_TMPDIR}/archive.tar
|
||||||
|
run_podman_testing create-image --layer=$layerID
|
||||||
|
imageID="$output"
|
||||||
|
testing_make_image_metadata_for_layer_blobs $imageID ${PODMAN_TMPDIR}/archive.tar
|
||||||
|
run_podman create $imageID
|
||||||
|
make_layer_blob 100 101 ${PODMAN_TMPDIR}/archive.tar
|
||||||
|
run_podman_testing modify-layer --layer=$layerID --file=${PODMAN_TMPDIR}/archive.tar
|
||||||
|
run_podman 125 system check
|
||||||
|
assert "$output" =~ "content modified" "output from 'podman system check' with unexpected content added to layer"
|
||||||
|
run_podman 125 system check -r
|
||||||
|
run_podman 0+w system check -r -f
|
||||||
|
run_podman system check
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "podman system check - storage image layer missing" {
|
||||||
|
run_podman_testing create-layer
|
||||||
|
layerID="$output"
|
||||||
|
make_layer_blob 8 ${PODMAN_TMPDIR}/archive.tar
|
||||||
|
run_podman_testing populate-layer --layer=$layerID --file=${PODMAN_TMPDIR}/archive.tar
|
||||||
|
run_podman_testing create-image --layer=$layerID
|
||||||
|
imageID="$output"
|
||||||
|
testing_make_image_metadata_for_layer_blobs $imageID ${PODMAN_TMPDIR}/archive.tar
|
||||||
|
run_podman create $imageID
|
||||||
|
run_podman_testing remove-layer --layer=$layerID
|
||||||
|
run_podman 125 system check
|
||||||
|
assert "$output" =~ "image layer is missing" "output from 'podman system check' with missing layer"
|
||||||
|
run_podman 125 system check -r
|
||||||
|
run_podman 0+w system check -r -f
|
||||||
|
run_podman system check
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "podman system check - storage container image missing" {
|
||||||
|
run_podman_testing create-layer
|
||||||
|
layerID="$output"
|
||||||
|
make_layer_blob 8 ${PODMAN_TMPDIR}/archive.tar
|
||||||
|
run_podman_testing populate-layer --layer=$layerID --file=${PODMAN_TMPDIR}/archive.tar
|
||||||
|
run_podman_testing create-image --layer=$layerID
|
||||||
|
imageID="$output"
|
||||||
|
testing_make_image_metadata_for_layer_blobs $imageID ${PODMAN_TMPDIR}/archive.tar
|
||||||
|
run_podman create $imageID
|
||||||
|
run_podman_testing remove-image --image=$imageID
|
||||||
|
run_podman 125 system check -m 0
|
||||||
|
assert "$output" =~ "image missing" "output from 'podman system check' with missing image"
|
||||||
|
run_podman 125 system check -r -m 0
|
||||||
|
run_podman 0+w system check -r -f -m 0
|
||||||
|
run_podman system check -m 0
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "podman system check - storage layer data missing" {
|
||||||
|
run_podman_testing create-layer
|
||||||
|
layerID="$output"
|
||||||
|
make_layer_blob ${PODMAN_TMPDIR}/archive.tar
|
||||||
|
run_podman_testing populate-layer --layer=$layerID --file=${PODMAN_TMPDIR}/archive.tar
|
||||||
|
make_random_file ${PODMAN_TMPDIR}/random-data.bin
|
||||||
|
run_podman_testing create-layer-data --key=foo --file=${PODMAN_TMPDIR}/random-data.bin --layer=$layerID
|
||||||
|
run_podman_testing create-image --layer=$layerID
|
||||||
|
imageID="$output"
|
||||||
|
testing_make_image_metadata_for_layer_blobs $imageID ${PODMAN_TMPDIR}/archive.tar
|
||||||
|
run_podman create $imageID
|
||||||
|
run_podman_testing remove-layer-data --key=foo --layer=$layerID
|
||||||
|
run_podman 125 system check
|
||||||
|
assert "$output" =~ "layer data item is missing" "output from 'podman system check' with missing layer data"
|
||||||
|
run_podman 125 system check -r
|
||||||
|
run_podman 0+w system check -r -f
|
||||||
|
run_podman system check
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "podman system check - storage image data missing" {
|
||||||
|
run_podman_testing create-layer
|
||||||
|
layerID="$output"
|
||||||
|
make_layer_blob 8 ${PODMAN_TMPDIR}/archive.tar
|
||||||
|
run_podman_testing populate-layer --layer=$layerID --file=${PODMAN_TMPDIR}/archive.tar
|
||||||
|
run_podman_testing create-image --layer=$layerID
|
||||||
|
imageID="$output"
|
||||||
|
testing_make_image_metadata_for_layer_blobs $imageID ${PODMAN_TMPDIR}/archive.tar
|
||||||
|
make_random_file ${PODMAN_TMPDIR}/random-data.bin
|
||||||
|
run_podman_testing create-image-data --key=foo --file=${PODMAN_TMPDIR}/random-data.bin --image=$imageID
|
||||||
|
run_podman create $imageID
|
||||||
|
run_podman_testing remove-image-data --key=foo --image=$imageID
|
||||||
|
run_podman 125 system check
|
||||||
|
assert "$output" =~ "image data item is missing" "output from 'podman system check' with missing image data"
|
||||||
|
run_podman 125 system check -r
|
||||||
|
run_podman 0+w system check -r -f
|
||||||
|
run_podman system check
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "podman system check - storage image data modified" {
|
||||||
|
run_podman_testing create-layer
|
||||||
|
layerID="$output"
|
||||||
|
make_layer_blob 8 ${PODMAN_TMPDIR}/archive.tar
|
||||||
|
run_podman_testing populate-layer --layer=$layerID --file=${PODMAN_TMPDIR}/archive.tar
|
||||||
|
run_podman_testing create-image --layer=$layerID
|
||||||
|
imageID="$output"
|
||||||
|
testing_make_image_metadata_for_layer_blobs $imageID ${PODMAN_TMPDIR}/archive.tar
|
||||||
|
run_podman create $imageID
|
||||||
|
make_random_file ${PODMAN_TMPDIR}/random-data.bin
|
||||||
|
run_podman_testing create-image-data --key=foo --file=${PODMAN_TMPDIR}/random-data.bin --image=$imageID
|
||||||
|
make_random_file ${PODMAN_TMPDIR}/random-data.bin
|
||||||
|
run_podman_testing modify-image-data --key=foo --file=${PODMAN_TMPDIR}/random-data.bin --image=$imageID
|
||||||
|
run_podman 125 system check
|
||||||
|
assert "$output" =~ "image data item has incorrect" "output from 'podman system check' with modified image data"
|
||||||
|
run_podman 125 system check -r
|
||||||
|
run_podman 0+w system check -r -f
|
||||||
|
run_podman system check
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "podman system check - container data missing" {
|
||||||
|
run_podman_testing create-layer
|
||||||
|
layerID="$output"
|
||||||
|
make_layer_blob 8 ${PODMAN_TMPDIR}/archive.tar
|
||||||
|
run_podman_testing populate-layer --layer=$layerID --file=${PODMAN_TMPDIR}/archive.tar
|
||||||
|
run_podman_testing create-image --layer=$layerID
|
||||||
|
imageID="$output"
|
||||||
|
testing_make_image_metadata_for_layer_blobs $imageID ${PODMAN_TMPDIR}/archive.tar
|
||||||
|
run_podman create $imageID
|
||||||
|
containerID="$output"
|
||||||
|
make_random_file ${PODMAN_TMPDIR}/random-data.bin
|
||||||
|
run_podman_testing create-container-data --key=foo --file=${PODMAN_TMPDIR}/random-data.bin --container=$containerID
|
||||||
|
run_podman_testing remove-container-data --key=foo --container=$containerID
|
||||||
|
run_podman 125 system check
|
||||||
|
assert "$output" =~ "container data item is missing" "output from 'podman system check' with missing container data"
|
||||||
|
run_podman 125 system check -r
|
||||||
|
run_podman 0+w system check -r -f
|
||||||
|
run_podman system check
|
||||||
|
run_podman rmi $imageID
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "podman system check - container data modified" {
|
||||||
|
run_podman_testing create-layer
|
||||||
|
layerID="$output"
|
||||||
|
make_layer_blob 8 ${PODMAN_TMPDIR}/archive.tar
|
||||||
|
run_podman_testing populate-layer --layer=$layerID --file=${PODMAN_TMPDIR}/archive.tar
|
||||||
|
run_podman_testing create-image --layer=$layerID
|
||||||
|
imageID="$output"
|
||||||
|
testing_make_image_metadata_for_layer_blobs $imageID ${PODMAN_TMPDIR}/archive.tar
|
||||||
|
run_podman create $imageID
|
||||||
|
containerID="$output"
|
||||||
|
make_random_file ${PODMAN_TMPDIR}/random-data.bin
|
||||||
|
run_podman_testing create-container-data --key=foo --file=${PODMAN_TMPDIR}/random-data.bin --container=$containerID
|
||||||
|
make_random_file ${PODMAN_TMPDIR}/random-data.bin
|
||||||
|
run_podman_testing modify-container-data --key=foo --file=${PODMAN_TMPDIR}/random-data.bin --container=$containerID
|
||||||
|
run_podman 125 system check
|
||||||
|
assert "$output" =~ "container data item has incorrect" "output from 'podman system check' with modified container data"
|
||||||
|
run_podman 125 system check -r
|
||||||
|
run_podman 0+w system check -r -f
|
||||||
|
run_podman system check
|
||||||
|
run_podman rmi $imageID
|
||||||
|
}
|
||||||
|
|
||||||
|
function make_layer_blob() {
|
||||||
|
local tmpdir=$(mktemp -d --tmpdir=${PODMAN_TMPDIR} make_layer_blob.XXXXXX)
|
||||||
|
local blobfile
|
||||||
|
local seqargs
|
||||||
|
for arg in "${@}" ; do
|
||||||
|
seqargs="${blobfile:+$seqargs $blobfile}"
|
||||||
|
blobfile="$arg"
|
||||||
|
done
|
||||||
|
seqargs="${seqargs:-8}"
|
||||||
|
local filelist=
|
||||||
|
for file in $(seq ${seqargs}); do
|
||||||
|
dd if=/dev/urandom of="$tmpdir/file$file" bs=1 count=$((1024 + $file)) status=none
|
||||||
|
filelist="$filelist file$file"
|
||||||
|
done
|
||||||
|
tar -c --owner=root:0 --group=root:0 -f "$blobfile" -C "$tmpdir" $filelist
|
||||||
|
}
|
||||||
|
|
||||||
|
function testing_make_image_metadata_for_layer_blobs() {
|
||||||
|
local tmpdir=$(mktemp -d --tmpdir=${PODMAN_TMPDIR} make_image_metadata.XXXXXX)
|
||||||
|
local imageID=$1
|
||||||
|
shift
|
||||||
|
echo '{"config":{},"rootfs":{"type":"layers","diff_ids":[' > $tmpdir/config.json
|
||||||
|
echo '{"schemaVersion":2,"mediaType":"application/vnd.oci.image.manifest.v1+json","layers":[' > $tmpdir/manifest
|
||||||
|
local comma=
|
||||||
|
for blob in "$@" ; do
|
||||||
|
local sum=$(sha256sum $blob)
|
||||||
|
sum=${sum%% *}
|
||||||
|
local size=$(wc -c $blob)
|
||||||
|
size=${size%% *}
|
||||||
|
echo $comma '"sha256:'$sum'"' >> $tmpdir/config.json
|
||||||
|
echo $comma '{"digest":"sha256:'$sum'","size":'$size',"mediaType":"application/vnd.oci.image.layer.v1.tar"}' >> $tmpdir/manifest
|
||||||
|
comma=,
|
||||||
|
done
|
||||||
|
echo ']}}' >> $tmpdir/config.json
|
||||||
|
sum=$(sha256sum $tmpdir/config.json)
|
||||||
|
sum=${sum%% *}
|
||||||
|
size=$(wc -c $tmpdir/config.json)
|
||||||
|
size=${size%% *}
|
||||||
|
echo '],"config":{"digest":"sha256:'$sum'","size":'$size',"mediaType":"application/vnd.oci.image.config.v1+json"}}' >> $tmpdir/manifest
|
||||||
|
run_podman_testing create-image-data -i $imageID -k sha256:$sum -f $tmpdir/config.json
|
||||||
|
sum=$(sha256sum $tmpdir/manifest)
|
||||||
|
sum=${sum%% *}
|
||||||
|
run_podman_testing create-image-data -i $imageID -k manifest-sha256:$sum -f $tmpdir/manifest
|
||||||
|
run_podman_testing create-image-data -i $imageID -k manifest -f $tmpdir/manifest
|
||||||
|
}
|
||||||
|
|
||||||
|
# vim: filetype=sh
|
@ -4,6 +4,9 @@
|
|||||||
PODMAN=${PODMAN:-podman}
|
PODMAN=${PODMAN:-podman}
|
||||||
QUADLET=${QUADLET:-/usr/libexec/podman/quadlet}
|
QUADLET=${QUADLET:-/usr/libexec/podman/quadlet}
|
||||||
|
|
||||||
|
# Podman testing helper used in 331-system-check tests
|
||||||
|
PODMAN_TESTING=${PODMAN_TESTING:-$(dirname ${BASH_SOURCE})/../../bin/podman-testing}
|
||||||
|
|
||||||
# crun or runc, unlikely to change. Cache, because it's expensive to determine.
|
# crun or runc, unlikely to change. Cache, because it's expensive to determine.
|
||||||
PODMAN_RUNTIME=
|
PODMAN_RUNTIME=
|
||||||
|
|
||||||
@ -449,6 +452,14 @@ function run_podman() {
|
|||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function run_podman_testing() {
|
||||||
|
printf "\n%s %s %s %s\n" "$(timestamp)" "$_LOG_PROMPT" "$PODMAN_TESTING" "$*"
|
||||||
|
run $PODMAN_TESTING "$@"
|
||||||
|
if [[ $status -ne 0 ]]; then
|
||||||
|
echo "$output"
|
||||||
|
die "Unexpected error from testing helper, which should always always succeed"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# Wait for certain output from a container, indicating that it's ready.
|
# Wait for certain output from a container, indicating that it's ready.
|
||||||
function wait_for_output {
|
function wait_for_output {
|
||||||
@ -1178,5 +1189,9 @@ function wait_for_command_output() {
|
|||||||
die "Timed out waiting for '$cmd' to return '$want'"
|
die "Timed out waiting for '$cmd' to return '$want'"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function make_random_file() {
|
||||||
|
dd if=/dev/urandom of="$1" bs=1 count=${2:-$((${RANDOM} % 8192 + 1024))} status=none
|
||||||
|
}
|
||||||
|
|
||||||
# END miscellaneous tools
|
# END miscellaneous tools
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
Reference in New Issue
Block a user