Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

cmd: umoci: add umoci-raw-runtime-config(1) #110

Merged
merged 7 commits into from
Apr 10, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,14 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
bit weird (`cmd` and `entrypoint` aren't treated atomically) this makes the
UX more consistent while we come up with a better `cmd` and `entrypoint` UX.
openSUSE/umoci#107
- New subcommand: `umoci raw runtime-config`. It generates the runtime-spec
config.json for a particular image without also unpacking the root
filesystem, allowing for users of `umoci` that are regularly parsing
`config.json` without caring about the root filesystem to be more efficient.
However, a downside of this approach is that some image-spec fields
(`Config.User`) require a root filesystem in order to make sense, which is
why this command is hidden under the `umoci-raw(1)` subcommand (to make sure
only users that understand what they're doing use it). openSUSE/umoci#110

### Changed
- `umoci`'s `oci/cas` and `oci/config` libraries have been massively refactored
Expand Down
8 changes: 4 additions & 4 deletions cmd/umoci/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -113,14 +113,15 @@ func main() {
tagRemoveCommand,
tagListCommand,
statCommand,
rawSubcommand,
}

app.Metadata = map[string]interface{}{}

// In order to make the uxXyz wrappers not too cumbersome we automatically
// add them to images with categories set to categoryImage or
// categoryLayout. Monkey patching was never this neat.
for idx, cmd := range app.Commands {
for _, cmd := range flattenCommands(app.Commands) {
switch cmd.Category {
case categoryImage:
oldBefore := cmd.Before
Expand All @@ -136,7 +137,7 @@ func main() {
}
return nil
}
cmd = uxImage(cmd)
*cmd = uxImage(*cmd)
case categoryLayout:
oldBefore := cmd.Before
cmd.Before = func(ctx *cli.Context) error {
Expand All @@ -148,9 +149,8 @@ func main() {
}
return nil
}
cmd = uxLayout(cmd)
*cmd = uxLayout(*cmd)
}
app.Commands[idx] = cmd
}

// Actually run umoci.
Expand Down
171 changes: 171 additions & 0 deletions cmd/umoci/raw-runtime-config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
/*
* umoci: Umoci Modifies Open Containers' Images
* Copyright (C) 2016, 2017 SUSE LLC.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package main

import (
"fmt"
"os"

"github.com/apex/log"
"github.com/openSUSE/umoci/oci/cas"
"github.com/openSUSE/umoci/oci/casext"
"github.com/openSUSE/umoci/oci/layer"
"github.com/openSUSE/umoci/pkg/idtools"
ispec "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
"github.com/urfave/cli"
"golang.org/x/net/context"
)

var rawConfigCommand = cli.Command{
Name: "runtime-config",
Aliases: []string{"config"},
Usage: "generates an OCI runtime configuration for an image",
ArgsUsage: `--image <image-path>[:<tag>] [--rootfs <rootfs>] <config.json>
Where "<image-path>" is the path to the OCI image, "<tag>" is the name of the
tagged image to unpack (if not specified, defaults to "latest"), "<rootfs>" is
a rootfs to use as a supplementary "source of truth" for certain generation
operations and "<config.json>" is the destination to write the runtime
configuration to.
Note that the results of this may not agree with umoci-unpack(1) because the
--rootfs flag affects how certain properties are interpreted.`,

// unpack reads manifest information.
Category: "image",

Flags: []cli.Flag{
cli.StringSliceFlag{
Name: "uid-map",
Usage: "specifies a uid mapping to use when generating config",
},
cli.StringSliceFlag{
Name: "gid-map",
Usage: "specifies a gid mapping to use when generating config",
},
cli.BoolFlag{
Name: "rootless",
Usage: "generate rootless configuration",
},
cli.StringFlag{
Name: "rootfs",
Usage: "path to secondary source of truth (root filesystem)",
},
},

Action: rawConfig,

Before: func(ctx *cli.Context) error {
if ctx.NArg() != 1 {
return errors.Errorf("invalid number of positional arguments: expected <config.json>")
}
if ctx.Args().First() == "" {
return errors.Errorf("config.json path cannot be empty")
}
ctx.App.Metadata["config"] = ctx.Args().First()
return nil
},
}

func rawConfig(ctx *cli.Context) error {
imagePath := ctx.App.Metadata["--image-path"].(string)
fromName := ctx.App.Metadata["--image-tag"].(string)
configPath := ctx.App.Metadata["config"].(string)

var meta UmociMeta
meta.Version = ctx.App.Version

// Parse map options.
// We need to set mappings if we're in rootless mode.
meta.MapOptions.Rootless = ctx.Bool("rootless")
if meta.MapOptions.Rootless {
if !ctx.IsSet("uid-map") {
ctx.Set("uid-map", fmt.Sprintf("%d:0:1", os.Geteuid()))
}
if !ctx.IsSet("gid-map") {
ctx.Set("gid-map", fmt.Sprintf("%d:0:1", os.Getegid()))
}
}
// Parse and set up the mapping options.
for _, uidmap := range ctx.StringSlice("uid-map") {
idMap, err := idtools.ParseMapping(uidmap)
if err != nil {
return errors.Wrapf(err, "failure parsing --uid-map %s: %s", uidmap)
}
meta.MapOptions.UIDMappings = append(meta.MapOptions.UIDMappings, idMap)
}
for _, gidmap := range ctx.StringSlice("gid-map") {
idMap, err := idtools.ParseMapping(gidmap)
if err != nil {
return errors.Wrapf(err, "failure parsing --gid-map %s: %s", gidmap)
}
meta.MapOptions.GIDMappings = append(meta.MapOptions.GIDMappings, idMap)
}

log.WithFields(log.Fields{
"map.uid": meta.MapOptions.UIDMappings,
"map.gid": meta.MapOptions.GIDMappings,
}).Debugf("parsed mappings")

// Get a reference to the CAS.
engine, err := cas.Open(imagePath)
if err != nil {
return errors.Wrap(err, "open CAS")
}
engineExt := casext.Engine{engine}
defer engine.Close()

fromDescriptor, err := engineExt.GetReference(context.Background(), fromName)
if err != nil {
return errors.Wrap(err, "get descriptor")
}
meta.From = fromDescriptor

manifestBlob, err := engineExt.FromDescriptor(context.Background(), meta.From)
if err != nil {
return errors.Wrap(err, "get manifest")
}
defer manifestBlob.Close()

// FIXME: Implement support for manifest lists.
if manifestBlob.MediaType != ispec.MediaTypeImageManifest {
return errors.Wrap(fmt.Errorf("descriptor does not point to ispec.MediaTypeImageManifest: not implemented: %s", meta.From.MediaType), "invalid --image tag")
}

// Get the manifest.
manifest, ok := manifestBlob.Data.(ispec.Manifest)
if !ok {
// Should _never_ be reached.
return errors.Errorf("[internal error] unknown manifest blob type: %s", manifestBlob.MediaType)
}

// Generate the configuration.
configFile, err := os.Create(configPath)
if err != nil {
return errors.Wrap(err, "opening config path")
}
defer configFile.Close()

// Write out the generated config.
log.Info("generating config.json")
if err := layer.UnpackRuntimeJSON(context.Background(), engineExt, configFile, ctx.String("rootfs"), manifest, &meta.MapOptions); err != nil {
return errors.Wrap(err, "generate config")
}
return nil
}
38 changes: 38 additions & 0 deletions cmd/umoci/raw.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* umoci: Umoci Modifies Open Containers' Images
* Copyright (C) 2016, 2017 SUSE LLC.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package main

import (
"github.com/urfave/cli"
)

var rawSubcommand = cli.Command{
Name: "raw",
Usage: "advanced internal image tooling",
ArgsUsage: `raw <command> [<args>...]
In order to facilitate more advanced uses of umoci, the umoci-raw(1)
subcommands allow for more fine-grained information to be provided from umoci.
Please do not use these commands if you are not familiar with the intricacies
of the OCI specifications. The top-level umoci-unpack(1) and similar commands
should be sufficient for most use-cases.`,

Subcommands: []cli.Command{
rawConfigCommand,
},
}
9 changes: 9 additions & 0 deletions cmd/umoci/utils_ux.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,15 @@ import (
// refRegexp defines the regexp that a given OCI tag must obey.
var refRegexp = regexp.MustCompile(`^([A-Za-z0-9._-]+)+$`)

func flattenCommands(cmds []cli.Command) []*cli.Command {
var flatten []*cli.Command
for idx, cmd := range cmds {
flatten = append(flatten, &cmds[idx])
flatten = append(flatten, flattenCommands(cmd.Subcommands)...)
}
return flatten
}

// uxHistory adds the full set of --history.* flags to the given cli.Command as
// well as adding relevant validation logic to the .Before of the command. The
// values will be stored in ctx.Metadata with the keys "--history.author",
Expand Down
52 changes: 26 additions & 26 deletions man/umoci-config.1.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,19 @@ umoci config - Modifies the configuration of an OCI image
[**--history.author**=*author*]
[**--history-created**=*date*]
[**--clear**=*value*]
[**--config.user**=[*value*]]
[**--config.exposedports**=[*value*]]
[**--config.env**=[*value*]]
[**--config.entrypoint**=[*value*]]
[**--config.cmd**=[*value*]]
[**--config.volume**=[*value*]]
[**--config.label**=[*value*]]
[**--config.workingdir**=[*value*]]
[**--created**=[*value*]]
[**--author**=[*value*]]
[**--architecture**=[*value*]]
[**--os**=[*value*]]
[**--manifest.annotation**=[*value*]]
[**--config.user**=*value*]
[**--config.exposedports**=*value*]
[**--config.env**=*value*]
[**--config.entrypoint**=*value*]
[**--config.cmd**=*value*]
[**--config.volume**=*value*]
[**--config.label**=*value*]
[**--config.workingdir**=*value*]
[**--created**=*value*]
[**--author**=*value*]
[**--architecture**=*value*]
[**--os**=*value*]
[**--manifest.annotation**=*value*]

# DESCRIPTION
Modify the configuration and manifest data for a particular tagged OCI image.
Expand Down Expand Up @@ -85,19 +85,19 @@ The global options are defined in **umoci**(1).
The following commands all set their corresponding values in the configuration
or image manifest. For more information see [the OCI image specification][1].

* **--config.user**=[*value*]
* **--config.exposedports**=[*value*]
* **--config.env**=[*value*]
* **--config.entrypoint**=[*value*]
* **--config.cmd**=[*value*]
* **--config.volume**=[*value*]
* **--config.label**=[*value*]
* **--config.workingdir**=[*value*]
* **--created**=[*value*]
* **--author**=[*value*]
* **--architecture**=[*value*]
* **--os**=[*value*]
* **--manifest.annotation**=[*value*]
* **--config.user**=*value*
* **--config.exposedports**=*value*
* **--config.env**=*value*
* **--config.entrypoint**=*value*
* **--config.cmd**=*value*
* **--config.volume**=*value*
* **--config.label**=*value*
* **--config.workingdir**=*value*
* **--created**=*value*
* **--author**=*value*
* **--architecture**=*value*
* **--os**=*value*
* **--manifest.annotation**=*value*

# EXAMPLE

Expand Down
1 change: 0 additions & 1 deletion man/umoci-ls.1

This file was deleted.

1 change: 1 addition & 0 deletions man/umoci-ls.1.md
1 change: 1 addition & 0 deletions man/umoci-raw-config.1.md
Loading