option.go 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  1. //
  2. // Use and distribution licensed under the Apache license version 2.
  3. //
  4. // See the COPYING file in the root project directory for full text.
  5. //
  6. package option
  7. import (
  8. "io"
  9. "io/ioutil"
  10. "log"
  11. "os"
  12. )
  13. const (
  14. DefaultChroot = "/"
  15. )
  16. const (
  17. envKeyChroot = "GHW_CHROOT"
  18. envKeyDisableWarnings = "GHW_DISABLE_WARNINGS"
  19. envKeyDisableTools = "GHW_DISABLE_TOOLS"
  20. envKeySnapshotPath = "GHW_SNAPSHOT_PATH"
  21. envKeySnapshotRoot = "GHW_SNAPSHOT_ROOT"
  22. envKeySnapshotExclusive = "GHW_SNAPSHOT_EXCLUSIVE"
  23. envKeySnapshotPreserve = "GHW_SNAPSHOT_PRESERVE"
  24. )
  25. // Alerter emits warnings about undesirable but recoverable errors.
  26. // We use a subset of a logger interface only to emit warnings, and
  27. // `Warninger` sounded ugly.
  28. type Alerter interface {
  29. Printf(format string, v ...interface{})
  30. }
  31. var (
  32. NullAlerter = log.New(ioutil.Discard, "", 0)
  33. )
  34. // EnvOrDefaultAlerter returns the default instance ghw will use to emit
  35. // its warnings. ghw will emit warnings to stderr by default unless the
  36. // environs variable GHW_DISABLE_WARNINGS is specified; in the latter case
  37. // all warning will be suppressed.
  38. func EnvOrDefaultAlerter() Alerter {
  39. var dest io.Writer
  40. if _, exists := os.LookupEnv(envKeyDisableWarnings); exists {
  41. dest = ioutil.Discard
  42. } else {
  43. // default
  44. dest = os.Stderr
  45. }
  46. return log.New(dest, "", 0)
  47. }
  48. // EnvOrDefaultChroot returns the value of the GHW_CHROOT environs variable or
  49. // the default value of "/" if not set
  50. func EnvOrDefaultChroot() string {
  51. // Grab options from the environs by default
  52. if val, exists := os.LookupEnv(envKeyChroot); exists {
  53. return val
  54. }
  55. return DefaultChroot
  56. }
  57. // EnvOrDefaultSnapshotPath returns the value of the GHW_SNAPSHOT_PATH environs variable
  58. // or the default value of "" (disable snapshot consumption) if not set
  59. func EnvOrDefaultSnapshotPath() string {
  60. if val, exists := os.LookupEnv(envKeySnapshotPath); exists {
  61. return val
  62. }
  63. return "" // default is no snapshot
  64. }
  65. // EnvOrDefaultSnapshotRoot returns the value of the the GHW_SNAPSHOT_ROOT environs variable
  66. // or the default value of "" (self-manage the snapshot unpack directory, if relevant) if not set
  67. func EnvOrDefaultSnapshotRoot() string {
  68. if val, exists := os.LookupEnv(envKeySnapshotRoot); exists {
  69. return val
  70. }
  71. return "" // default is to self-manage the snapshot directory
  72. }
  73. // EnvOrDefaultSnapshotExclusive returns the value of the GHW_SNAPSHOT_EXCLUSIVE environs variable
  74. // or the default value of false if not set
  75. func EnvOrDefaultSnapshotExclusive() bool {
  76. if _, exists := os.LookupEnv(envKeySnapshotExclusive); exists {
  77. return true
  78. }
  79. return false
  80. }
  81. // EnvOrDefaultSnapshotPreserve returns the value of the GHW_SNAPSHOT_PRESERVE environs variable
  82. // or the default value of false if not set
  83. func EnvOrDefaultSnapshotPreserve() bool {
  84. if _, exists := os.LookupEnv(envKeySnapshotPreserve); exists {
  85. return true
  86. }
  87. return false
  88. }
  89. // EnvOrDefaultTools return true if ghw should use external tools to augment the data collected
  90. // from sysfs. Most users want to do this most of time, so this is enabled by default.
  91. // Users consuming snapshots may want to opt out, thus they can set the GHW_DISABLE_TOOLS
  92. // environs variable to any value to make ghw skip calling external tools even if they are available.
  93. func EnvOrDefaultTools() bool {
  94. if _, exists := os.LookupEnv(envKeyDisableTools); exists {
  95. return false
  96. }
  97. return true
  98. }
  99. // Option is used to represent optionally-configured settings. Each field is a
  100. // pointer to some concrete value so that we can tell when something has been
  101. // set or left unset.
  102. type Option struct {
  103. // To facilitate querying of sysfs filesystems that are bind-mounted to a
  104. // non-default root mountpoint, we allow users to set the GHW_CHROOT environ
  105. // variable to an alternate mountpoint. For instance, assume that the user of
  106. // ghw is a Golang binary being executed from an application container that has
  107. // certain host filesystems bind-mounted into the container at /host. The user
  108. // would ensure the GHW_CHROOT environ variable is set to "/host" and ghw will
  109. // build its paths from that location instead of /
  110. Chroot *string
  111. // Snapshot contains options for handling ghw snapshots
  112. Snapshot *SnapshotOptions
  113. // Alerter contains the target for ghw warnings
  114. Alerter Alerter
  115. // EnableTools optionally request ghw to not call any external program to learn
  116. // about the hardware. The default is to use such tools if available.
  117. EnableTools *bool
  118. // PathOverrides optionally allows to override the default paths ghw uses internally
  119. // to learn about the system resources.
  120. PathOverrides PathOverrides
  121. // Context may contain a pointer to a `Context` struct that is constructed
  122. // during a call to the `context.WithContext` function. Only used internally.
  123. // This is an interface to get around recursive package import issues.
  124. Context interface{}
  125. }
  126. // SnapshotOptions contains options for handling of ghw snapshots
  127. type SnapshotOptions struct {
  128. // Path allows users to specify a snapshot (captured using ghw-snapshot) to be
  129. // automatically consumed. Users need to supply the path of the snapshot, and
  130. // ghw will take care of unpacking it on a temporary directory.
  131. // Set the environment variable "GHW_SNAPSHOT_PRESERVE" to make ghw skip the cleanup
  132. // stage and keep the unpacked snapshot in the temporary directory.
  133. Path string
  134. // Root is the directory on which the snapshot must be unpacked. This allows
  135. // the users to manage their snapshot directory instead of ghw doing that on
  136. // their behalf. Relevant only if SnapshotPath is given.
  137. Root *string
  138. // Exclusive tells ghw if the given directory should be considered of exclusive
  139. // usage of ghw or not If the user provides a Root. If the flag is set, ghw will
  140. // unpack the snapshot in the given SnapshotRoot iff the directory is empty; otherwise
  141. // any existing content will be left untouched and the unpack stage will exit silently.
  142. // As additional side effect, give both this option and SnapshotRoot to make each
  143. // context try to unpack the snapshot only once.
  144. Exclusive bool
  145. }
  146. // WithChroot allows to override the root directory ghw uses.
  147. func WithChroot(dir string) *Option {
  148. return &Option{Chroot: &dir}
  149. }
  150. // WithSnapshot sets snapshot-processing options for a ghw run
  151. func WithSnapshot(opts SnapshotOptions) *Option {
  152. return &Option{
  153. Snapshot: &opts,
  154. }
  155. }
  156. // WithAlerter sets alerting options for ghw
  157. func WithAlerter(alerter Alerter) *Option {
  158. return &Option{
  159. Alerter: alerter,
  160. }
  161. }
  162. // WithNullAlerter sets No-op alerting options for ghw
  163. func WithNullAlerter() *Option {
  164. return &Option{
  165. Alerter: NullAlerter,
  166. }
  167. }
  168. // WithDisableTools sets enables or prohibts ghw to call external tools to discover hardware capabilities.
  169. func WithDisableTools() *Option {
  170. false_ := false
  171. return &Option{EnableTools: &false_}
  172. }
  173. // PathOverrides is a map, keyed by the string name of a mount path, of override paths
  174. type PathOverrides map[string]string
  175. // WithPathOverrides supplies path-specific overrides for the context
  176. func WithPathOverrides(overrides PathOverrides) *Option {
  177. return &Option{
  178. PathOverrides: overrides,
  179. }
  180. }
  181. // There is intentionally no Option related to GHW_SNAPSHOT_PRESERVE because we see that as
  182. // a debug/troubleshoot aid more something users wants to do regularly.
  183. // Hence we allow that only via the environment variable for the time being.
  184. // Merge accepts one or more Options and merges them together, returning the
  185. // merged Option
  186. func Merge(opts ...*Option) *Option {
  187. merged := &Option{}
  188. for _, opt := range opts {
  189. if opt.Chroot != nil {
  190. merged.Chroot = opt.Chroot
  191. }
  192. if opt.Snapshot != nil {
  193. merged.Snapshot = opt.Snapshot
  194. }
  195. if opt.Alerter != nil {
  196. merged.Alerter = opt.Alerter
  197. }
  198. if opt.EnableTools != nil {
  199. merged.EnableTools = opt.EnableTools
  200. }
  201. // intentionally only programmatically
  202. if opt.PathOverrides != nil {
  203. merged.PathOverrides = opt.PathOverrides
  204. }
  205. if opt.Context != nil {
  206. merged.Context = opt.Context
  207. }
  208. }
  209. // Set the default value if missing from mergeOpts
  210. if merged.Chroot == nil {
  211. chroot := EnvOrDefaultChroot()
  212. merged.Chroot = &chroot
  213. }
  214. if merged.Alerter == nil {
  215. merged.Alerter = EnvOrDefaultAlerter()
  216. }
  217. if merged.Snapshot == nil {
  218. snapRoot := EnvOrDefaultSnapshotRoot()
  219. merged.Snapshot = &SnapshotOptions{
  220. Path: EnvOrDefaultSnapshotPath(),
  221. Root: &snapRoot,
  222. Exclusive: EnvOrDefaultSnapshotExclusive(),
  223. }
  224. }
  225. if merged.EnableTools == nil {
  226. enabled := EnvOrDefaultTools()
  227. merged.EnableTools = &enabled
  228. }
  229. return merged
  230. }