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  }