Skip to content

Commit

Permalink
WIP: translate: Stub in a translation framework
Browse files Browse the repository at this point in the history
Add tooling to translate higher-level configs into the basic OCI
config.  On IRC, Julz floated a linux.namespaces[].fromContainer as a
higher-level version of linux.namespaces[].path for emulating exec
[1].  That makes sense to me, and Mrunal is open to something like
[2]:

  $ ocitools generate --template=high-level-config.json --translate=fromContainer --runtime=runc

This commit still needs:

* The state JSON lookup and path logic from [3].
* A way to convert the interface{} to an rspec.Spec (the current FIXME
  raises: FATA[0000] translated template has an invalid schema).

[1]: http://ircbot.wl.linuxfoundation.org/eavesdrop/%23opencontainers/%23opencontainers.2016-04-27.log.html#t2016-04-27T20:32:09
[2]: http://ircbot.wl.linuxfoundation.org/eavesdrop/%23opencontainers/%23opencontainers.2016-04-28.log.html#t2016-04-28T16:12:48
[3]: opencontainers/runtime-spec#391

Signed-off-by: W. Trevor King <[email protected]>
  • Loading branch information
wking committed May 3, 2016
1 parent 8ab86b6 commit 215cee9
Show file tree
Hide file tree
Showing 3 changed files with 92 additions and 5 deletions.
29 changes: 24 additions & 5 deletions generate.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/codegangsta/cli"
rspec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/syndtr/gocapability/capability"
"github.com/opencontainers/ocitools/translate"
)

var generateFlags = []cli.Flag{
Expand Down Expand Up @@ -55,6 +56,7 @@ var generateFlags = []cli.Flag{
cli.StringSliceFlag{Name: "seccomp-syscalls", Usage: "specifies Additional architectures permitted to be used for system calls, e.g Name:Action:Arg1_index/Arg1_value/Arg1_valuetwo/Arg1_op, Arg2_index/Arg2_value/Arg2_valuetwo/Arg2_op "},
cli.StringSliceFlag{Name: "seccomp-allow", Usage: "specifies syscalls to be added to allowed"},
cli.StringFlag{Name: "template", Usage: "base template to use for creating the configuration"},
cli.StringSliceFlag{Name: "translate", Usage: "translate higher level constructs"},
cli.StringSliceFlag{Name: "label", Usage: "add annotations to the configuration e.g. key=value"},
}

Expand Down Expand Up @@ -92,12 +94,29 @@ var generateCommand = cli.Command{
}
}

err := modify(spec, context)
translations := context.StringSlice("translate")
for _, translation := range translations {
translator, ok := translate.Translators[translation]
if !ok {
logrus.Fatalf("unrecognized translation: %s", translation)
}
var err error
spec, err = translator(spec)
if err != nil {
logrus.Fatal(err)
}
}

strictSpec, ok := spec.(rspec.Spec) // FIXME: assert struct type?
if !ok {
logrus.Fatal("translated template has an invalid schema")
}
err := modify(&strictSpec, context)
if err != nil {
logrus.Fatal(err)
}
cName := "config.json"
data, err := json.MarshalIndent(&spec, "", "\t")
data, err := json.MarshalIndent(&strictSpec, "", "\t")
if err != nil {
logrus.Fatal(err)
}
Expand All @@ -107,7 +126,7 @@ var generateCommand = cli.Command{
},
}

func loadTemplate(path string) (spec *rspec.Spec, err error) {
func loadTemplate(path string) (spec interface{}, err error) {
cf, err := os.Open(path)
if err != nil {
if os.IsNotExist(err) {
Expand Down Expand Up @@ -675,7 +694,7 @@ func setupNamespaces(spec *rspec.Spec, context *cli.Context) {

func sPtr(s string) *string { return &s }

func getDefaultTemplate() *rspec.Spec {
func getDefaultTemplate() interface{} {
spec := rspec.Spec{
Version: rspec.Version,
Platform: rspec.Platform{
Expand Down Expand Up @@ -790,5 +809,5 @@ func getDefaultTemplate() *rspec.Spec {
},
}

return &spec
return spec
}
54 changes: 54 additions & 0 deletions translate/from_container.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package translate

import (
"fmt"
)

func init() {
Translators["fromContainer"] = FromContainer
}

func FromContainer(data interface{}) (translated interface{}, err error) {
dataMap, ok := data.(map[string]interface{})
if !ok {
return nil, fmt.Errorf("data is not a map[string]interface{}: %s", data)
}

linuxInterface, ok := dataMap["linux"]
if !ok {
return data, nil
}

linux, ok := linuxInterface.(map[string]interface{})
if !ok {
return nil, fmt.Errorf("data.linux is not a map[string]interface{}: %s", linuxInterface)
}

namespacesInterface, ok := linux["namespaces"]
if !ok {
return data, nil
}

namespaces, ok := namespacesInterface.([]interface{})
if !ok {
return nil, fmt.Errorf("data.linux.namespaces is not an array: %s", namespacesInterface)
}

for i, namespaceInterface := range namespaces {
namespace, ok := namespaceInterface.(map[string]interface{})
if !ok {
return nil, fmt.Errorf("data.linux.namespaces[%d] is not a map[string]interface{}: %s", i, namespaceInterface)
}
fromContainerInterface, ok := namespace["fromContainer"]
if ok {
fromContainer, ok := fromContainerInterface.(string)
if !ok {
return nil, fmt.Errorf("data.linux.namespaces[%d].fromContainer is not a string: %s", i, fromContainerInterface)
}
delete(namespace, "fromContainer")
namespace["path"] = fromContainer
}
}

return data, nil
}
14 changes: 14 additions & 0 deletions translate/translate.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
/*
Package translate handles translation between configuration
specifications.
For example, it allows you to generate OCI-compliant config.json from
a higher-level configuration language.
*/
package translate

// Translate maps JSON from one specification to another.
type Translate func(data interface{}) (translated interface{}, err error)

// Translators is a map from translator names to Translate functions.
var Translators = map[string]Translate{}

0 comments on commit 215cee9

Please sign in to comment.