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