github.com/AbhinandanKurakure/podman/v3@v3.4.10/libpod/network/cni/cni_exec.go (about)

     1  // Copyright 2016 CNI authors
     2  // Copyright 2021 Podman authors
     3  //
     4  // This code has been originally copied from github.com/containernetworking/cni
     5  // but has been changed to better fit the Podman use case.
     6  //
     7  // Licensed under the Apache License, Version 2.0 (the "License");
     8  // you may not use this file except in compliance with the License.
     9  // You may obtain a copy of the License at
    10  //
    11  //     https://www.apache.org/licenses/LICENSE-2.0
    12  //
    13  // Unless required by applicable law or agreed to in writing, software
    14  // distributed under the License is distributed on an "AS IS" BASIS,
    15  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    16  // See the License for the specific language governing permissions and
    17  // limitations under the License.
    18  
    19  // +build linux
    20  
    21  package cni
    22  
    23  import (
    24  	"bytes"
    25  	"context"
    26  	"encoding/json"
    27  	"fmt"
    28  	"os/exec"
    29  	"path/filepath"
    30  
    31  	"github.com/containernetworking/cni/pkg/invoke"
    32  	"github.com/containernetworking/cni/pkg/version"
    33  	"github.com/containers/podman/v3/pkg/rootless"
    34  )
    35  
    36  type cniExec struct {
    37  	version.PluginDecoder
    38  }
    39  
    40  type cniPluginError struct {
    41  	plugin  string
    42  	Code    uint   `json:"code"`
    43  	Msg     string `json:"msg"`
    44  	Details string `json:"details,omitempty"`
    45  }
    46  
    47  // Error returns a nicely formatted error message for the cni plugin errors.
    48  func (e *cniPluginError) Error() string {
    49  	err := fmt.Sprintf("cni plugin %s failed", e.plugin)
    50  	if e.Msg != "" {
    51  		err = fmt.Sprintf("%s: %s", err, e.Msg)
    52  	} else if e.Code > 0 {
    53  		err = fmt.Sprintf("%s with error code %d", err, e.Code)
    54  	}
    55  	if e.Details != "" {
    56  		err = fmt.Sprintf("%s: %s", err, e.Details)
    57  	}
    58  	return err
    59  }
    60  
    61  // ExecPlugin execute the cni plugin. Returns the stdout of the plugin or an error.
    62  func (e *cniExec) ExecPlugin(ctx context.Context, pluginPath string, stdinData []byte, environ []string) ([]byte, error) {
    63  	stdout := &bytes.Buffer{}
    64  	stderr := &bytes.Buffer{}
    65  	c := exec.CommandContext(ctx, pluginPath)
    66  	c.Env = environ
    67  	c.Stdin = bytes.NewBuffer(stdinData)
    68  	c.Stdout = stdout
    69  	c.Stderr = stderr
    70  
    71  	// The dnsname plugin tries to use XDG_RUNTIME_DIR to store files.
    72  	// podman run will have XDG_RUNTIME_DIR set and thus the cni plugin can use
    73  	// it. The problem is that XDG_RUNTIME_DIR is unset for the conmon process
    74  	// for rootful users. This causes issues since the cleanup process is spawned
    75  	// by conmon and thus not have XDG_RUNTIME_DIR set to same value as podman run.
    76  	// Because of it dnsname will not find the config files and cannot correctly cleanup.
    77  	// To fix this we should also unset XDG_RUNTIME_DIR for the cni plugins as rootful.
    78  	if !rootless.IsRootless() {
    79  		c.Env = append(c.Env, "XDG_RUNTIME_DIR=")
    80  	}
    81  
    82  	err := c.Run()
    83  	if err != nil {
    84  		return nil, annotatePluginError(err, pluginPath, stdout.Bytes(), stderr.Bytes())
    85  	}
    86  	return stdout.Bytes(), nil
    87  }
    88  
    89  // annotatePluginError parses the common cni plugin error json.
    90  func annotatePluginError(err error, plugin string, stdout []byte, stderr []byte) error {
    91  	pluginName := filepath.Base(plugin)
    92  	emsg := cniPluginError{
    93  		plugin: pluginName,
    94  	}
    95  	if len(stdout) == 0 {
    96  		if len(stderr) == 0 {
    97  			emsg.Msg = err.Error()
    98  		} else {
    99  			emsg.Msg = string(stderr)
   100  		}
   101  	} else if perr := json.Unmarshal(stdout, &emsg); perr != nil {
   102  		emsg.Msg = fmt.Sprintf("failed to unmarshal error message %q: %v", string(stdout), perr)
   103  	}
   104  	return &emsg
   105  }
   106  
   107  // FindInPath finds the plugin in the given paths.
   108  func (e *cniExec) FindInPath(plugin string, paths []string) (string, error) {
   109  	return invoke.FindInPath(plugin, paths)
   110  }