mirror of
https://github.com/containers/podman.git
synced 2025-05-17 06:59:07 +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))
|
||||
binaries: podman-remote ## Build podman-remote (client) only binaries
|
||||
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
|
||||
|
||||
# Extract text following double-# for targets, as their description for
|
||||
@ -457,6 +457,16 @@ rootlessport: bin/rootlessport
|
||||
podmansh: bin/podman
|
||||
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
|
||||
###
|
||||
@ -877,6 +887,11 @@ ifneq ($(shell uname -s),FreeBSD)
|
||||
install ${SELINUXOPT} -m 644 contrib/tmpfile/podman.conf $(DESTDIR)${TMPFILESDIR}/podman.conf
|
||||
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
|
||||
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}
|
||||
|
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"
|
||||
|
||||
SKIP_DIRS_ABI=""
|
||||
SKIP_DIRS_TUNNEL="pkg/api,pkg/domain/infra/abi"
|
||||
SKIP_DIRS_REMOTE="libpod/events,pkg/api,pkg/domain/infra/abi,pkg/machine/qemu,pkg/trust,test"
|
||||
SKIP_DIRS_TUNNEL="pkg/api,pkg/domain/infra/abi,internal/domain/infra/abi"
|
||||
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
|
||||
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)"
|
||||
%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
|
||||
LDFLAGS=''
|
||||
|
||||
@ -255,8 +259,9 @@ PODMAN_VERSION=%{version} %{__make} DESTDIR=%{buildroot} PREFIX=%{_prefix} ETCDI
|
||||
install.docker \
|
||||
install.docker-docs \
|
||||
install.remote \
|
||||
install.testing \
|
||||
%if %{defined _modulesloaddir}
|
||||
install.modules-load
|
||||
install.modules-load
|
||||
%endif
|
||||
|
||||
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
|
||||
|
||||
%files tests
|
||||
%{_bindir}/%{name}-testing
|
||||
%{_datadir}/%{name}/test
|
||||
|
||||
%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}
|
||||
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.
|
||||
PODMAN_RUNTIME=
|
||||
|
||||
@ -449,6 +452,14 @@ function run_podman() {
|
||||
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.
|
||||
function wait_for_output {
|
||||
@ -1178,5 +1189,9 @@ function wait_for_command_output() {
|
||||
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
|
||||
###############################################################################
|
||||
|
Reference in New Issue
Block a user