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  }