github.com/demonoid81/containerd@v1.3.4/cmd/containerd/command/oci-hook.go (about) 1 /* 2 Copyright The containerd Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package command 18 19 import ( 20 "bytes" 21 "encoding/json" 22 "io" 23 "os" 24 "path/filepath" 25 "syscall" 26 "text/template" 27 28 specs "github.com/opencontainers/runtime-spec/specs-go" 29 "github.com/urfave/cli" 30 ) 31 32 var ociHook = cli.Command{ 33 Name: "oci-hook", 34 Usage: "provides a base for OCI runtime hooks to allow arguments to be injected.", 35 Action: func(context *cli.Context) error { 36 state, err := loadHookState(os.Stdin) 37 if err != nil { 38 return err 39 } 40 spec, err := loadSpec(state.Bundle) 41 if err != nil { 42 return err 43 } 44 var ( 45 ctx = newTemplateContext(state, spec) 46 args = []string(context.Args()) 47 env = os.Environ() 48 ) 49 if err := newList(&args).render(ctx); err != nil { 50 return err 51 } 52 if err := newList(&env).render(ctx); err != nil { 53 return err 54 } 55 return syscall.Exec(args[0], args, env) 56 }, 57 } 58 59 type hookSpec struct { 60 Root struct { 61 Path string `json:"path"` 62 } `json:"root"` 63 } 64 65 func loadSpec(bundle string) (*hookSpec, error) { 66 f, err := os.Open(filepath.Join(bundle, "config.json")) 67 if err != nil { 68 return nil, err 69 } 70 defer f.Close() 71 var s hookSpec 72 if err := json.NewDecoder(f).Decode(&s); err != nil { 73 return nil, err 74 } 75 return &s, nil 76 } 77 78 func loadHookState(r io.Reader) (*specs.State, error) { 79 var s specs.State 80 if err := json.NewDecoder(r).Decode(&s); err != nil { 81 return nil, err 82 } 83 return &s, nil 84 } 85 86 func newTemplateContext(state *specs.State, spec *hookSpec) *templateContext { 87 t := &templateContext{ 88 state: state, 89 root: spec.Root.Path, 90 } 91 t.funcs = template.FuncMap{ 92 "id": t.id, 93 "bundle": t.bundle, 94 "rootfs": t.rootfs, 95 "pid": t.pid, 96 "annotation": t.annotation, 97 "status": t.status, 98 } 99 return t 100 } 101 102 type templateContext struct { 103 state *specs.State 104 root string 105 funcs template.FuncMap 106 } 107 108 func (t *templateContext) id() string { 109 return t.state.ID 110 } 111 112 func (t *templateContext) bundle() string { 113 return t.state.Bundle 114 } 115 116 func (t *templateContext) rootfs() string { 117 if filepath.IsAbs(t.root) { 118 return t.root 119 } 120 return filepath.Join(t.state.Bundle, t.root) 121 } 122 123 func (t *templateContext) pid() int { 124 return t.state.Pid 125 } 126 127 func (t *templateContext) annotation(k string) string { 128 return t.state.Annotations[k] 129 } 130 131 func (t *templateContext) status() string { 132 return t.state.Status 133 } 134 135 func render(ctx *templateContext, source string, out io.Writer) error { 136 t, err := template.New("oci-hook").Funcs(ctx.funcs).Parse(source) 137 if err != nil { 138 return err 139 } 140 return t.Execute(out, ctx) 141 } 142 143 func newList(l *[]string) *templateList { 144 return &templateList{ 145 l: l, 146 } 147 } 148 149 type templateList struct { 150 l *[]string 151 } 152 153 func (l *templateList) render(ctx *templateContext) error { 154 buf := bytes.NewBuffer(nil) 155 for i, s := range *l.l { 156 buf.Reset() 157 if err := render(ctx, s, buf); err != nil { 158 return err 159 } 160 (*l.l)[i] = buf.String() 161 } 162 buf.Reset() 163 return nil 164 }