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,
|
||||
action: copyContent,
|
||||
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")
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/containers/storage"
|
||||
graphdriver "github.com/containers/storage/drivers"
|
||||
"github.com/containers/storage/internal/opts"
|
||||
"github.com/containers/storage/pkg/idtools"
|
||||
"github.com/containers/storage/pkg/mflag"
|
||||
"github.com/containers/storage/pkg/stringid"
|
||||
"github.com/containers/storage/types"
|
||||
digest "github.com/opencontainers/go-digest"
|
||||
)
|
||||
|
|
@ -71,6 +74,45 @@ func paramIDMapping() (*types.IDMappingOptions, error) {
|
|||
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) {
|
||||
parent := ""
|
||||
if len(args) > 0 {
|
||||
|
|
@ -194,6 +236,26 @@ func createContainer(flags *mflag.FlagSet, action string, m storage.Store, args
|
|||
}
|
||||
|
||||
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{
|
||||
names: []string{"create-layer", "createlayer"},
|
||||
optionsHelp: "[options [...]] [parentLayerNameOrID]",
|
||||
|
|
|
|||
|
|
@ -1,13 +1,39 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/containers/storage"
|
||||
"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) {
|
||||
layers, err := m.Layers()
|
||||
|
|
@ -71,6 +97,9 @@ func layers(flags *mflag.FlagSet, action string, m storage.Store, args []string)
|
|||
nodes = append(nodes, node)
|
||||
} else {
|
||||
fmt.Printf("%s\n", layer.ID)
|
||||
if listLayersQuick {
|
||||
continue
|
||||
}
|
||||
for _, name := range layer.Names {
|
||||
fmt.Printf("\tname: %s\n", name)
|
||||
}
|
||||
|
|
@ -106,6 +135,18 @@ func init() {
|
|||
maxArgs: 0,
|
||||
addFlags: func(flags *mflag.FlagSet, cmd *command) {
|
||||
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")
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import (
|
|||
"github.com/containers/storage/internal/opts"
|
||||
"github.com/containers/storage/pkg/mflag"
|
||||
"github.com/containers/storage/pkg/reexec"
|
||||
"github.com/containers/storage/pkg/unshare"
|
||||
"github.com/containers/storage/types"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
|
@ -36,6 +37,7 @@ func main() {
|
|||
|
||||
options := types.StoreOptions{}
|
||||
debug := false
|
||||
doUnshare := false
|
||||
|
||||
makeFlags := func(command string, eh mflag.ErrorHandling) *mflag.FlagSet {
|
||||
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.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(&doUnshare, []string{"-unshare", "U"}, unshare.IsRootless(), fmt.Sprintf("Run in a user namespace (default %t)", unshare.IsRootless()))
|
||||
return flags
|
||||
}
|
||||
|
||||
|
|
@ -68,7 +71,6 @@ func main() {
|
|||
flags.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if options.GraphRoot == "" && options.RunRoot == "" && options.GraphDriverName == "" && len(options.GraphDriverOptions) == 0 {
|
||||
options, _ = types.DefaultStoreOptionsAutoDetectUID()
|
||||
}
|
||||
|
|
@ -112,6 +114,9 @@ func main() {
|
|||
flags.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
if doUnshare {
|
||||
unshare.MaybeReexecUsingUserNamespace(true)
|
||||
}
|
||||
if debug {
|
||||
logrus.SetLevel(logrus.DebugLevel)
|
||||
logrus.Debugf("Root: %s", options.GraphRoot)
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ func init() {
|
|||
addFlags: func(flags *mflag.FlagSet, cmd *command) {
|
||||
flags.StringVar(¶mMountOptions, []string{"-opt", "o"}, "", "Mount Options")
|
||||
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")
|
||||
},
|
||||
})
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
**-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*
|
||||
|
||||
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-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-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 unshare(1)** Run a command in a user namespace
|
||||
|
||||
**containers-storage version(1)** Return containers-storage version information
|
||||
|
||||
**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
|
||||
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
|
||||
**CONTAINERS_STORAGE_CONF**
|
||||
|
||||
|
|
|
|||
Loading…
Reference in New Issue