Test helper: add "unshare"/"create-storage-layer"/"storage-layers"
Add "unshare", "create-storage-layer", and "storage-layers" commands to the test helper, along with a "-U" flag to have it unshare when handling a given command. Add "-o" as an alias for the "--owner" flag to "copy". Add "-r" as an alias for the "--ro" flag to "mount". Add a "-q" flag to "layers" so that we can list just the IDs. Drop mention of a couple of not-implemented options from docs/containers-storage-create-layer.md. Signed-off-by: Nalin Dahyabhai <nalin@redhat.com>
This commit is contained in:
parent
9d49da5f09
commit
3007ac6efb
|
|
@ -104,7 +104,7 @@ func init() {
|
||||||
maxArgs: -1,
|
maxArgs: -1,
|
||||||
action: copyContent,
|
action: copyContent,
|
||||||
addFlags: func(flags *mflag.FlagSet, cmd *command) {
|
addFlags: func(flags *mflag.FlagSet, cmd *command) {
|
||||||
flags.StringVar(&chownOptions, []string{"-chown", ""}, chownOptions, "Set owner on new copies")
|
flags.StringVar(&chownOptions, []string{"-chown", "o"}, chownOptions, "Set owner on new copies")
|
||||||
flags.BoolVar(&jsonOutput, []string{"-json", "j"}, jsonOutput, "Prefer JSON output")
|
flags.BoolVar(&jsonOutput, []string{"-json", "j"}, jsonOutput, "Prefer JSON output")
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -1,14 +1,17 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"github.com/containers/storage"
|
"github.com/containers/storage"
|
||||||
|
graphdriver "github.com/containers/storage/drivers"
|
||||||
"github.com/containers/storage/internal/opts"
|
"github.com/containers/storage/internal/opts"
|
||||||
"github.com/containers/storage/pkg/idtools"
|
"github.com/containers/storage/pkg/idtools"
|
||||||
"github.com/containers/storage/pkg/mflag"
|
"github.com/containers/storage/pkg/mflag"
|
||||||
|
"github.com/containers/storage/pkg/stringid"
|
||||||
"github.com/containers/storage/types"
|
"github.com/containers/storage/types"
|
||||||
digest "github.com/opencontainers/go-digest"
|
digest "github.com/opencontainers/go-digest"
|
||||||
)
|
)
|
||||||
|
|
@ -71,6 +74,45 @@ func paramIDMapping() (*types.IDMappingOptions, error) {
|
||||||
return &options, nil
|
return &options, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func createStorageLayer(flags *mflag.FlagSet, action string, m storage.Store, args []string) (int, error) {
|
||||||
|
parent := ""
|
||||||
|
if len(args) > 0 {
|
||||||
|
parent = args[0]
|
||||||
|
}
|
||||||
|
mappings, err := paramIDMapping()
|
||||||
|
if err != nil {
|
||||||
|
return 1, err
|
||||||
|
}
|
||||||
|
driver, err := m.GraphDriver()
|
||||||
|
if err != nil {
|
||||||
|
return 1, err
|
||||||
|
}
|
||||||
|
opts := graphdriver.CreateOpts{
|
||||||
|
MountLabel: paramMountLabel,
|
||||||
|
IDMappings: idtools.NewIDMappingsFromMaps(mappings.UIDMap, mappings.GIDMap),
|
||||||
|
}
|
||||||
|
if paramID == "" {
|
||||||
|
paramID = stringid.GenerateNonCryptoID()
|
||||||
|
}
|
||||||
|
if paramCreateRO {
|
||||||
|
if err := driver.Create(paramID, parent, &opts); err != nil {
|
||||||
|
return 1, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err := driver.CreateReadWrite(paramID, parent, &opts); err != nil {
|
||||||
|
return 1, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if jsonOutput {
|
||||||
|
if err := json.NewEncoder(os.Stdout).Encode(paramID); err != nil {
|
||||||
|
return 1, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
fmt.Printf("%s\n", paramID)
|
||||||
|
}
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
func createLayer(flags *mflag.FlagSet, action string, m storage.Store, args []string) (int, error) {
|
func createLayer(flags *mflag.FlagSet, action string, m storage.Store, args []string) (int, error) {
|
||||||
parent := ""
|
parent := ""
|
||||||
if len(args) > 0 {
|
if len(args) > 0 {
|
||||||
|
|
@ -194,6 +236,26 @@ func createContainer(flags *mflag.FlagSet, action string, m storage.Store, args
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
commands = append(commands, command{
|
||||||
|
names: []string{"create-storage-layer"},
|
||||||
|
optionsHelp: "[options [...]] [parentLayerNameOrID]",
|
||||||
|
usage: "Create a new layer only in the storage driver",
|
||||||
|
minArgs: 0,
|
||||||
|
maxArgs: 1,
|
||||||
|
action: createStorageLayer,
|
||||||
|
addFlags: func(flags *mflag.FlagSet, cmd *command) {
|
||||||
|
flags.StringVar(¶mMountLabel, []string{"-label", "l"}, "", "Mount Label")
|
||||||
|
flags.StringVar(¶mID, []string{"-id", "i"}, "", "Layer ID")
|
||||||
|
flags.BoolVar(¶mCreateRO, []string{"-readonly", "r"}, false, "Mark as read-only")
|
||||||
|
flags.BoolVar(&jsonOutput, []string{"-json", "j"}, jsonOutput, "Prefer JSON output")
|
||||||
|
flags.BoolVar(¶mHostUIDMap, []string{"-hostuidmap"}, paramHostUIDMap, "Force host UID map")
|
||||||
|
flags.BoolVar(¶mHostGIDMap, []string{"-hostgidmap"}, paramHostGIDMap, "Force host GID map")
|
||||||
|
flags.StringVar(¶mUIDMap, []string{"-uidmap"}, "", "UID map")
|
||||||
|
flags.StringVar(¶mGIDMap, []string{"-gidmap"}, "", "GID map")
|
||||||
|
flags.StringVar(¶mSubUIDMap, []string{"-subuidmap"}, "", "subuid UID map for a user")
|
||||||
|
flags.StringVar(¶mSubGIDMap, []string{"-subgidmap"}, "", "subgid GID map for a group")
|
||||||
|
},
|
||||||
|
})
|
||||||
commands = append(commands, command{
|
commands = append(commands, command{
|
||||||
names: []string{"create-layer", "createlayer"},
|
names: []string{"create-layer", "createlayer"},
|
||||||
optionsHelp: "[options [...]] [parentLayerNameOrID]",
|
optionsHelp: "[options [...]] [parentLayerNameOrID]",
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,39 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
|
|
||||||
"github.com/containers/storage"
|
"github.com/containers/storage"
|
||||||
"github.com/containers/storage/pkg/mflag"
|
"github.com/containers/storage/pkg/mflag"
|
||||||
)
|
)
|
||||||
|
|
||||||
var listLayersTree = false
|
var (
|
||||||
|
listLayersTree = false
|
||||||
|
listLayersQuick = false
|
||||||
|
)
|
||||||
|
|
||||||
|
func storageLayers(flags *mflag.FlagSet, action string, m storage.Store, args []string) (int, error) {
|
||||||
|
driver, err := m.GraphDriver()
|
||||||
|
if err != nil {
|
||||||
|
return 1, err
|
||||||
|
}
|
||||||
|
layers, err := driver.ListLayers()
|
||||||
|
if err != nil {
|
||||||
|
return 1, err
|
||||||
|
}
|
||||||
|
if jsonOutput {
|
||||||
|
if err := json.NewEncoder(os.Stdout).Encode(layers); err != nil {
|
||||||
|
return 1, err
|
||||||
|
}
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
for _, layer := range layers {
|
||||||
|
fmt.Printf("%s\n", layer)
|
||||||
|
}
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
func layers(flags *mflag.FlagSet, action string, m storage.Store, args []string) (int, error) {
|
func layers(flags *mflag.FlagSet, action string, m storage.Store, args []string) (int, error) {
|
||||||
layers, err := m.Layers()
|
layers, err := m.Layers()
|
||||||
|
|
@ -71,6 +97,9 @@ func layers(flags *mflag.FlagSet, action string, m storage.Store, args []string)
|
||||||
nodes = append(nodes, node)
|
nodes = append(nodes, node)
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("%s\n", layer.ID)
|
fmt.Printf("%s\n", layer.ID)
|
||||||
|
if listLayersQuick {
|
||||||
|
continue
|
||||||
|
}
|
||||||
for _, name := range layer.Names {
|
for _, name := range layer.Names {
|
||||||
fmt.Printf("\tname: %s\n", name)
|
fmt.Printf("\tname: %s\n", name)
|
||||||
}
|
}
|
||||||
|
|
@ -106,6 +135,18 @@ func init() {
|
||||||
maxArgs: 0,
|
maxArgs: 0,
|
||||||
addFlags: func(flags *mflag.FlagSet, cmd *command) {
|
addFlags: func(flags *mflag.FlagSet, cmd *command) {
|
||||||
flags.BoolVar(&listLayersTree, []string{"-tree", "t"}, listLayersTree, "Use a tree")
|
flags.BoolVar(&listLayersTree, []string{"-tree", "t"}, listLayersTree, "Use a tree")
|
||||||
|
flags.BoolVar(&listLayersQuick, []string{"-quick", "q"}, listLayersTree, "Just the IDs")
|
||||||
|
flags.BoolVar(&jsonOutput, []string{"-json", "j"}, jsonOutput, "Prefer JSON output")
|
||||||
|
},
|
||||||
|
})
|
||||||
|
commands = append(commands, command{
|
||||||
|
names: []string{"storage-layers"},
|
||||||
|
optionsHelp: "[options [...]]",
|
||||||
|
usage: "List storage layers",
|
||||||
|
action: storageLayers,
|
||||||
|
minArgs: 0,
|
||||||
|
maxArgs: 0,
|
||||||
|
addFlags: func(flags *mflag.FlagSet, cmd *command) {
|
||||||
flags.BoolVar(&jsonOutput, []string{"-json", "j"}, jsonOutput, "Prefer JSON output")
|
flags.BoolVar(&jsonOutput, []string{"-json", "j"}, jsonOutput, "Prefer JSON output")
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import (
|
||||||
"github.com/containers/storage/internal/opts"
|
"github.com/containers/storage/internal/opts"
|
||||||
"github.com/containers/storage/pkg/mflag"
|
"github.com/containers/storage/pkg/mflag"
|
||||||
"github.com/containers/storage/pkg/reexec"
|
"github.com/containers/storage/pkg/reexec"
|
||||||
|
"github.com/containers/storage/pkg/unshare"
|
||||||
"github.com/containers/storage/types"
|
"github.com/containers/storage/types"
|
||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
@ -36,6 +37,7 @@ func main() {
|
||||||
|
|
||||||
options := types.StoreOptions{}
|
options := types.StoreOptions{}
|
||||||
debug := false
|
debug := false
|
||||||
|
doUnshare := false
|
||||||
|
|
||||||
makeFlags := func(command string, eh mflag.ErrorHandling) *mflag.FlagSet {
|
makeFlags := func(command string, eh mflag.ErrorHandling) *mflag.FlagSet {
|
||||||
flags := mflag.NewFlagSet(command, eh)
|
flags := mflag.NewFlagSet(command, eh)
|
||||||
|
|
@ -45,6 +47,7 @@ func main() {
|
||||||
flags.StringVar(&options.GraphDriverName, []string{"-storage-driver", "s"}, options.GraphDriverName, "Storage driver to use ($STORAGE_DRIVER)")
|
flags.StringVar(&options.GraphDriverName, []string{"-storage-driver", "s"}, options.GraphDriverName, "Storage driver to use ($STORAGE_DRIVER)")
|
||||||
flags.Var(opts.NewListOptsRef(&options.GraphDriverOptions, nil), []string{"-storage-opt"}, "Set storage driver options ($STORAGE_OPTS)")
|
flags.Var(opts.NewListOptsRef(&options.GraphDriverOptions, nil), []string{"-storage-opt"}, "Set storage driver options ($STORAGE_OPTS)")
|
||||||
flags.BoolVar(&debug, []string{"-debug", "D"}, debug, "Print debugging information")
|
flags.BoolVar(&debug, []string{"-debug", "D"}, debug, "Print debugging information")
|
||||||
|
flags.BoolVar(&doUnshare, []string{"-unshare", "U"}, unshare.IsRootless(), fmt.Sprintf("Run in a user namespace (default %t)", unshare.IsRootless()))
|
||||||
return flags
|
return flags
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -68,7 +71,6 @@ func main() {
|
||||||
flags.Usage()
|
flags.Usage()
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if options.GraphRoot == "" && options.RunRoot == "" && options.GraphDriverName == "" && len(options.GraphDriverOptions) == 0 {
|
if options.GraphRoot == "" && options.RunRoot == "" && options.GraphDriverName == "" && len(options.GraphDriverOptions) == 0 {
|
||||||
options, _ = types.DefaultStoreOptionsAutoDetectUID()
|
options, _ = types.DefaultStoreOptionsAutoDetectUID()
|
||||||
}
|
}
|
||||||
|
|
@ -112,6 +114,9 @@ func main() {
|
||||||
flags.Usage()
|
flags.Usage()
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
if doUnshare {
|
||||||
|
unshare.MaybeReexecUsingUserNamespace(true)
|
||||||
|
}
|
||||||
if debug {
|
if debug {
|
||||||
logrus.SetLevel(logrus.DebugLevel)
|
logrus.SetLevel(logrus.DebugLevel)
|
||||||
logrus.Debugf("Root: %s", options.GraphRoot)
|
logrus.Debugf("Root: %s", options.GraphRoot)
|
||||||
|
|
|
||||||
|
|
@ -138,7 +138,7 @@ func init() {
|
||||||
addFlags: func(flags *mflag.FlagSet, cmd *command) {
|
addFlags: func(flags *mflag.FlagSet, cmd *command) {
|
||||||
flags.StringVar(¶mMountOptions, []string{"-opt", "o"}, "", "Mount Options")
|
flags.StringVar(¶mMountOptions, []string{"-opt", "o"}, "", "Mount Options")
|
||||||
flags.StringVar(¶mMountLabel, []string{"-label", "l"}, "", "Mount Label")
|
flags.StringVar(¶mMountLabel, []string{"-label", "l"}, "", "Mount Label")
|
||||||
flags.BoolVar(¶mReadOnly, []string{"-ro", "r"}, paramReadOnly, "Mount image readonly")
|
flags.BoolVar(¶mReadOnly, []string{"-read-only", "-ro", "r"}, paramReadOnly, "Mount image readonly")
|
||||||
flags.BoolVar(&jsonOutput, []string{"-json", "j"}, jsonOutput, "Prefer JSON output")
|
flags.BoolVar(&jsonOutput, []string{"-json", "j"}, jsonOutput, "Prefer JSON output")
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
|
||||||
|
"github.com/containers/storage"
|
||||||
|
"github.com/containers/storage/pkg/mflag"
|
||||||
|
"github.com/containers/storage/pkg/unshare"
|
||||||
|
)
|
||||||
|
|
||||||
|
func unshareFn(flags *mflag.FlagSet, action string, m storage.Store, args []string) (int, error) {
|
||||||
|
unshare.MaybeReexecUsingUserNamespace(true)
|
||||||
|
if len(args) == 0 {
|
||||||
|
shell := os.Getenv("SHELL")
|
||||||
|
if shell == "" {
|
||||||
|
shell = "/bin/sh"
|
||||||
|
}
|
||||||
|
args = []string{shell}
|
||||||
|
}
|
||||||
|
cmd := exec.Command(args[0], args[1:]...)
|
||||||
|
cmd.Stdin, cmd.Stdout, cmd.Stderr = os.Stdin, os.Stdout, os.Stderr
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
return 1, err
|
||||||
|
}
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
commands = append(commands, command{
|
||||||
|
names: []string{"unshare"},
|
||||||
|
usage: "Run a command in a user namespace",
|
||||||
|
optionsHelp: "[command [...]]",
|
||||||
|
minArgs: 0,
|
||||||
|
maxArgs: -1,
|
||||||
|
action: unshareFn,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -20,14 +20,6 @@ returned.
|
||||||
|
|
||||||
Sets the ID for the layer. If none is specified, one is generated.
|
Sets the ID for the layer. If none is specified, one is generated.
|
||||||
|
|
||||||
**-m | --metadata** *metadata-value*
|
|
||||||
|
|
||||||
Sets the metadata for the layer to the specified value.
|
|
||||||
|
|
||||||
**-f | --metadata-file** *metadata-file*
|
|
||||||
|
|
||||||
Sets the metadata for the layer to the contents of the specified file.
|
|
||||||
|
|
||||||
**-l | --label** *mount-label*
|
**-l | --label** *mount-label*
|
||||||
|
|
||||||
Sets the label which should be assigned as an SELinux context when mounting the
|
Sets the label which should be assigned as an SELinux context when mounting the
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
## containers-storage-create-storage-layer 1 "September 2022"
|
||||||
|
|
||||||
|
## NAME
|
||||||
|
containers-storage create-storage-layer - Create a layer in a lower-level storage driver
|
||||||
|
|
||||||
|
## SYNOPSIS
|
||||||
|
**containers-storage** **create-storage-layer** [*options* [...]] [*parentLayerNameOrID*]
|
||||||
|
|
||||||
|
## DESCRIPTION
|
||||||
|
Creates a new layer using the lower-level storage driver which either has a
|
||||||
|
specified layer as its parent, or if no parent is specified, is empty.
|
||||||
|
|
||||||
|
## OPTIONS
|
||||||
|
**-i | --id** *ID*
|
||||||
|
|
||||||
|
Sets the ID for the layer. If none is specified, one is generated.
|
||||||
|
|
||||||
|
**-l | --label** *mount-label*
|
||||||
|
|
||||||
|
Sets the label which should be assigned as an SELinux context when mounting the
|
||||||
|
layer.
|
||||||
|
|
||||||
|
## EXAMPLE
|
||||||
|
**containers-storage create-storage-layer somelayer**
|
||||||
|
|
||||||
|
## SEE ALSO
|
||||||
|
containers-storage-create-container(1)
|
||||||
|
containers-storage-create-image(1)
|
||||||
|
containers-storage-delete-layer(1)
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
## containers-storage-unshare 1 "September 2022"
|
||||||
|
|
||||||
|
## NAME
|
||||||
|
containers-storage unshare - Run a command in a user namespace
|
||||||
|
|
||||||
|
## SYNOPSIS
|
||||||
|
**containers-storage** **unshare** [command [...]]
|
||||||
|
|
||||||
|
## DESCRIPTION
|
||||||
|
Sets up a user namespace using mappings configured for the current user and runs
|
||||||
|
either a specified command or the current user's shell.
|
||||||
|
|
||||||
|
## EXAMPLE
|
||||||
|
**containers-storage unshare id**
|
||||||
|
|
||||||
|
## SEE ALSO
|
||||||
|
containers-storage-status(1)
|
||||||
|
|
@ -68,6 +68,8 @@ The *containers-storage* command's features are broken down into several subcomm
|
||||||
|
|
||||||
**containers-storage create-layer(1)** Create a new layer
|
**containers-storage create-layer(1)** Create a new layer
|
||||||
|
|
||||||
|
**containers-storage create-storage-layer(1)** Create a new layer in the lower-level storage driver
|
||||||
|
|
||||||
**containers-storage delete(1)** Delete a layer or image or container, with no safety checks
|
**containers-storage delete(1)** Delete a layer or image or container, with no safety checks
|
||||||
|
|
||||||
**containers-storage delete-container(1)** Delete a container, with safety checks
|
**containers-storage delete-container(1)** Delete a container, with safety checks
|
||||||
|
|
@ -116,6 +118,8 @@ The *containers-storage* command's features are broken down into several subcomm
|
||||||
|
|
||||||
**containers-storage unmount(1)** Unmount a layer or container
|
**containers-storage unmount(1)** Unmount a layer or container
|
||||||
|
|
||||||
|
**containers-storage unshare(1)** Run a command in a user namespace
|
||||||
|
|
||||||
**containers-storage version(1)** Return containers-storage version information
|
**containers-storage version(1)** Return containers-storage version information
|
||||||
|
|
||||||
**containers-storage wipe(1)** Wipe all layers, images, and containers
|
**containers-storage wipe(1)** Wipe all layers, images, and containers
|
||||||
|
|
@ -155,6 +159,11 @@ Set options which will be passed to the storage driver. If not set, but
|
||||||
comma-separated list and used instead. If the storage tree has previously been
|
comma-separated list and used instead. If the storage tree has previously been
|
||||||
initialized, these need not be provided.
|
initialized, these need not be provided.
|
||||||
|
|
||||||
|
**--unshare, -U**
|
||||||
|
|
||||||
|
When started by a non-root user, run inside of a new user namespace configured
|
||||||
|
using the system's default ID mappings for the non-root user.
|
||||||
|
|
||||||
## ENVIRONMENT OVERRIDES
|
## ENVIRONMENT OVERRIDES
|
||||||
**CONTAINERS_STORAGE_CONF**
|
**CONTAINERS_STORAGE_CONF**
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue