skel.go 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162
  1. // Copyright 2014 CNI authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. // Package skel provides skeleton code for a CNI plugin.
  15. // In particular, it implements argument parsing and validation.
  16. package skel
  17. import (
  18. "fmt"
  19. "io/ioutil"
  20. "log"
  21. "os"
  22. "github.com/containernetworking/cni/pkg/types"
  23. )
  24. // CmdArgs captures all the arguments passed in to the plugin
  25. // via both env vars and stdin
  26. type CmdArgs struct {
  27. ContainerID string
  28. Netns string
  29. IfName string
  30. Args string
  31. Path string
  32. StdinData []byte
  33. }
  34. type reqForCmdEntry map[string]bool
  35. // PluginMain is the "main" for a plugin. It accepts
  36. // two callback functions for add and del commands.
  37. func PluginMain(cmdAdd, cmdDel func(_ *CmdArgs) error) {
  38. var cmd, contID, netns, ifName, args, path string
  39. vars := []struct {
  40. name string
  41. val *string
  42. reqForCmd reqForCmdEntry
  43. }{
  44. {
  45. "CNI_COMMAND",
  46. &cmd,
  47. reqForCmdEntry{
  48. "ADD": true,
  49. "DEL": true,
  50. },
  51. },
  52. {
  53. "CNI_CONTAINERID",
  54. &contID,
  55. reqForCmdEntry{
  56. "ADD": false,
  57. "DEL": false,
  58. },
  59. },
  60. {
  61. "CNI_NETNS",
  62. &netns,
  63. reqForCmdEntry{
  64. "ADD": true,
  65. "DEL": false,
  66. },
  67. },
  68. {
  69. "CNI_IFNAME",
  70. &ifName,
  71. reqForCmdEntry{
  72. "ADD": true,
  73. "DEL": true,
  74. },
  75. },
  76. {
  77. "CNI_ARGS",
  78. &args,
  79. reqForCmdEntry{
  80. "ADD": false,
  81. "DEL": false,
  82. },
  83. },
  84. {
  85. "CNI_PATH",
  86. &path,
  87. reqForCmdEntry{
  88. "ADD": true,
  89. "DEL": true,
  90. },
  91. },
  92. }
  93. argsMissing := false
  94. for _, v := range vars {
  95. *v.val = os.Getenv(v.name)
  96. if v.reqForCmd[cmd] && *v.val == "" {
  97. log.Printf("%v env variable missing", v.name)
  98. argsMissing = true
  99. }
  100. }
  101. if argsMissing {
  102. dieMsg("required env variables missing")
  103. }
  104. stdinData, err := ioutil.ReadAll(os.Stdin)
  105. if err != nil {
  106. dieMsg("error reading from stdin: %v", err)
  107. }
  108. cmdArgs := &CmdArgs{
  109. ContainerID: contID,
  110. Netns: netns,
  111. IfName: ifName,
  112. Args: args,
  113. Path: path,
  114. StdinData: stdinData,
  115. }
  116. switch cmd {
  117. case "ADD":
  118. err = cmdAdd(cmdArgs)
  119. case "DEL":
  120. err = cmdDel(cmdArgs)
  121. default:
  122. dieMsg("unknown CNI_COMMAND: %v", cmd)
  123. }
  124. if err != nil {
  125. if e, ok := err.(*types.Error); ok {
  126. // don't wrap Error in Error
  127. dieErr(e)
  128. }
  129. dieMsg(err.Error())
  130. }
  131. }
  132. func dieMsg(f string, args ...interface{}) {
  133. e := &types.Error{
  134. Code: 100,
  135. Msg: fmt.Sprintf(f, args...),
  136. }
  137. dieErr(e)
  138. }
  139. func dieErr(e *types.Error) {
  140. if err := e.Print(); err != nil {
  141. log.Print("Error writing error JSON to stdout: ", err)
  142. }
  143. os.Exit(1)
  144. }