Skip to content

Commit 0463c2a

Browse files
committed
WIP: translate: Stub in a translation framework
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 The interface{} -> rspec.Spec conversion happens through a JSON serialization trick suggested by 梁辰晔 (Liang Chenye) [3] after the translations, because before the translations the template may contain JSON that's not represented in the OCI Spec structure (e.g. fromContainer in namespace entries). This commit still needs: * The state JSON lookup and path logic from [4]. * Adjustments to setupNamespaces to avoid clobbering the template. [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]: #54 (comment) [4]: opencontainers/runtime-spec#391 Signed-off-by: W. Trevor King <[email protected]>
1 parent 8ab86b6 commit 0463c2a

File tree

3 files changed

+99
-6
lines changed

3 files changed

+99
-6
lines changed

generate.go

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313
"github.com/codegangsta/cli"
1414
rspec "github.com/opencontainers/runtime-spec/specs-go"
1515
"github.com/syndtr/gocapability/capability"
16+
"github.com/opencontainers/ocitools/translate"
1617
)
1718

1819
var generateFlags = []cli.Flag{
@@ -55,6 +56,7 @@ var generateFlags = []cli.Flag{
5556
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 "},
5657
cli.StringSliceFlag{Name: "seccomp-allow", Usage: "specifies syscalls to be added to allowed"},
5758
cli.StringFlag{Name: "template", Usage: "base template to use for creating the configuration"},
59+
cli.StringSliceFlag{Name: "translate", Usage: "translate higher level constructs"},
5860
cli.StringSliceFlag{Name: "label", Usage: "add annotations to the configuration e.g. key=value"},
5961
}
6062

@@ -92,12 +94,35 @@ var generateCommand = cli.Command{
9294
}
9395
}
9496

95-
err := modify(spec, context)
97+
translations := context.StringSlice("translate")
98+
for _, translation := range translations {
99+
translator, ok := translate.Translators[translation]
100+
if !ok {
101+
logrus.Fatalf("unrecognized translation: %s", translation)
102+
}
103+
var err error
104+
spec, err = translator(spec)
105+
if err != nil {
106+
logrus.Fatal(err)
107+
}
108+
}
109+
110+
buf, err := json.Marshal(spec)
111+
if err != nil {
112+
logrus.Fatal(err)
113+
}
114+
var strictSpec rspec.Spec
115+
err = json.Unmarshal(buf, &strictSpec)
116+
if err != nil {
117+
logrus.Fatal(err)
118+
}
119+
120+
err = modify(&strictSpec, context)
96121
if err != nil {
97122
logrus.Fatal(err)
98123
}
99124
cName := "config.json"
100-
data, err := json.MarshalIndent(&spec, "", "\t")
125+
data, err := json.MarshalIndent(&strictSpec, "", "\t")
101126
if err != nil {
102127
logrus.Fatal(err)
103128
}
@@ -107,7 +132,7 @@ var generateCommand = cli.Command{
107132
},
108133
}
109134

110-
func loadTemplate(path string) (spec *rspec.Spec, err error) {
135+
func loadTemplate(path string) (spec interface{}, err error) {
111136
cf, err := os.Open(path)
112137
if err != nil {
113138
if os.IsNotExist(err) {
@@ -670,12 +695,12 @@ func setupNamespaces(spec *rspec.Spec, context *cli.Context) {
670695
ns := mapStrToNamespace(nsName, nsPath)
671696
linuxNs = append(linuxNs, ns)
672697
}
673-
spec.Linux.Namespaces = linuxNs
698+
spec.Linux.Namespaces = linuxNs // FIXME: don't clobber the template namespaces
674699
}
675700

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

678-
func getDefaultTemplate() *rspec.Spec {
703+
func getDefaultTemplate() interface{} {
679704
spec := rspec.Spec{
680705
Version: rspec.Version,
681706
Platform: rspec.Platform{
@@ -790,5 +815,5 @@ func getDefaultTemplate() *rspec.Spec {
790815
},
791816
}
792817

793-
return &spec
818+
return spec
794819
}

translate/from_container.go

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package translate
2+
3+
import (
4+
"fmt"
5+
)
6+
7+
func init() {
8+
Translators["fromContainer"] = FromContainer
9+
}
10+
11+
func FromContainer(data interface{}) (translated interface{}, err error) {
12+
dataMap, ok := data.(map[string]interface{})
13+
if !ok {
14+
return nil, fmt.Errorf("data is not a map[string]interface{}: %s", data)
15+
}
16+
17+
linuxInterface, ok := dataMap["linux"]
18+
if !ok {
19+
return data, nil
20+
}
21+
22+
linux, ok := linuxInterface.(map[string]interface{})
23+
if !ok {
24+
return nil, fmt.Errorf("data.linux is not a map[string]interface{}: %s", linuxInterface)
25+
}
26+
27+
namespacesInterface, ok := linux["namespaces"]
28+
if !ok {
29+
return data, nil
30+
}
31+
32+
namespaces, ok := namespacesInterface.([]interface{})
33+
if !ok {
34+
return nil, fmt.Errorf("data.linux.namespaces is not an array: %s", namespacesInterface)
35+
}
36+
37+
for i, namespaceInterface := range namespaces {
38+
namespace, ok := namespaceInterface.(map[string]interface{})
39+
if !ok {
40+
return nil, fmt.Errorf("data.linux.namespaces[%d] is not a map[string]interface{}: %s", i, namespaceInterface)
41+
}
42+
fromContainerInterface, ok := namespace["fromContainer"]
43+
if ok {
44+
fromContainer, ok := fromContainerInterface.(string)
45+
if !ok {
46+
return nil, fmt.Errorf("data.linux.namespaces[%d].fromContainer is not a string: %s", i, fromContainerInterface)
47+
}
48+
delete(namespace, "fromContainer")
49+
namespace["path"] = fromContainer // FIXME: lookup /proc path using state JSON
50+
}
51+
}
52+
53+
return data, nil
54+
}

translate/translate.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
/*
2+
Package translate handles translation between configuration
3+
specifications.
4+
5+
For example, it allows you to generate OCI-compliant config.json from
6+
a higher-level configuration language.
7+
*/
8+
package translate
9+
10+
// Translate maps JSON from one specification to another.
11+
type Translate func(data interface{}) (translated interface{}, err error)
12+
13+
// Translators is a map from translator names to Translate functions.
14+
var Translators = map[string]Translate{}

0 commit comments

Comments
 (0)