github.com/jaypipes/ghw@v0.21.1/pkg/context/context.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 context
     8  
     9  import (
    10  	"fmt"
    11  
    12  	"github.com/jaypipes/ghw/pkg/option"
    13  	"github.com/jaypipes/ghw/pkg/snapshot"
    14  )
    15  
    16  // Context contains the merged set of configuration switches that act as an
    17  // execution context when calling internal discovery methods
    18  type Context struct {
    19  	Chroot               string
    20  	EnableTools          bool
    21  	SnapshotPath         string
    22  	SnapshotRoot         string
    23  	SnapshotExclusive    bool
    24  	PathOverrides        option.PathOverrides
    25  	snapshotUnpackedPath string
    26  	alert                option.Alerter
    27  	err                  error
    28  }
    29  
    30  // WithContext returns an option.Option that contains a pre-existing Context
    31  // struct. This is useful for some internal code that sets up snapshots.
    32  func WithContext(ctx *Context) *option.Option {
    33  	return &option.Option{
    34  		Context: ctx,
    35  	}
    36  }
    37  
    38  // Exists returns true if the supplied (merged) Option already contains
    39  // a context.
    40  //
    41  // TODO(jaypipes): We can get rid of this when we combine the option and
    42  // context packages, which will make it easier to detect the presence of a
    43  // pre-setup Context.
    44  func Exists(opt *option.Option) bool {
    45  	return opt != nil && opt.Context != nil
    46  }
    47  
    48  // New returns a Context struct pointer that has had various options set on it
    49  func New(opts ...*option.Option) *Context {
    50  	merged := option.Merge(opts...)
    51  	var ctx *Context
    52  	if merged.Context != nil {
    53  		var castOK bool
    54  		ctx, castOK = merged.Context.(*Context)
    55  		if !castOK {
    56  			panic("passed in a non-Context for the WithContext() function!")
    57  		}
    58  		return ctx
    59  	}
    60  	ctx = &Context{
    61  		alert:  option.EnvOrDefaultAlerter(),
    62  		Chroot: *merged.Chroot,
    63  	}
    64  
    65  	if merged.Snapshot != nil {
    66  		ctx.SnapshotPath = merged.Snapshot.Path
    67  		// root is optional, so a extra check is warranted
    68  		if merged.Snapshot.Root != nil {
    69  			ctx.SnapshotRoot = *merged.Snapshot.Root
    70  		}
    71  		ctx.SnapshotExclusive = merged.Snapshot.Exclusive
    72  	}
    73  
    74  	if merged.Alerter != nil {
    75  		ctx.alert = merged.Alerter
    76  	}
    77  
    78  	if merged.EnableTools != nil {
    79  		ctx.EnableTools = *merged.EnableTools
    80  	}
    81  
    82  	if merged.PathOverrides != nil {
    83  		ctx.PathOverrides = merged.PathOverrides
    84  	}
    85  
    86  	// New is not allowed to return error - it would break the established API.
    87  	// so the only way out is to actually do the checks here and record the error,
    88  	// and return it later, at the earliest possible occasion, in Setup()
    89  	if ctx.SnapshotPath != "" && ctx.Chroot != option.DefaultChroot {
    90  		// The env/client code supplied a value, but we are will overwrite it when unpacking shapshots!
    91  		ctx.err = fmt.Errorf("Conflicting options: chroot %q and snapshot path %q", ctx.Chroot, ctx.SnapshotPath)
    92  	}
    93  	return ctx
    94  }
    95  
    96  // FromEnv returns a Context that has been populated from the environs or
    97  // default options values
    98  func FromEnv() *Context {
    99  	chrootVal := option.EnvOrDefaultChroot()
   100  	enableTools := option.EnvOrDefaultTools()
   101  	snapPathVal := option.EnvOrDefaultSnapshotPath()
   102  	snapRootVal := option.EnvOrDefaultSnapshotRoot()
   103  	snapExclusiveVal := option.EnvOrDefaultSnapshotExclusive()
   104  	return &Context{
   105  		Chroot:            chrootVal,
   106  		EnableTools:       enableTools,
   107  		SnapshotPath:      snapPathVal,
   108  		SnapshotRoot:      snapRootVal,
   109  		SnapshotExclusive: snapExclusiveVal,
   110  	}
   111  }
   112  
   113  // Do wraps a Setup/Teardown pair around the given function
   114  func (ctx *Context) Do(fn func() error) error {
   115  	err := ctx.Setup()
   116  	if err != nil {
   117  		return err
   118  	}
   119  	defer func() {
   120  		err := ctx.Teardown()
   121  		if err != nil {
   122  			ctx.Warn("teardown error: %v", err)
   123  		}
   124  	}()
   125  	return fn()
   126  }
   127  
   128  // Setup prepares the extra optional data a Context may use.
   129  // `Context`s are ready to use once returned by `New`. Optional features,
   130  // like snapshot unpacking, may require extra steps. Run `Setup` to perform them.
   131  // You should call `Setup` just once. It is safe to call `Setup` if you don't make
   132  // use of optional extra features - `Setup` will do nothing.
   133  func (ctx *Context) Setup() error {
   134  	if ctx.err != nil {
   135  		return ctx.err
   136  	}
   137  	if ctx.SnapshotPath == "" {
   138  		// nothing to do!
   139  		return nil
   140  	}
   141  
   142  	var err error
   143  	root := ctx.SnapshotRoot
   144  	if root == "" {
   145  		root, err = snapshot.Unpack(ctx.SnapshotPath)
   146  		if err == nil {
   147  			ctx.snapshotUnpackedPath = root
   148  		}
   149  	} else {
   150  		var flags uint
   151  		if ctx.SnapshotExclusive {
   152  			flags |= snapshot.OwnTargetDirectory
   153  		}
   154  		_, err = snapshot.UnpackInto(ctx.SnapshotPath, root, flags)
   155  	}
   156  	if err != nil {
   157  		return err
   158  	}
   159  
   160  	ctx.Chroot = root
   161  	return nil
   162  }
   163  
   164  // Teardown releases any resource acquired by Setup.
   165  // You should always call `Teardown` if you called `Setup` to free any resources
   166  // acquired by `Setup`. Check `Do` for more automated management.
   167  func (ctx *Context) Teardown() error {
   168  	if ctx.snapshotUnpackedPath == "" {
   169  		// if the client code provided the unpack directory,
   170  		// then it is also in charge of the cleanup.
   171  		return nil
   172  	}
   173  	return snapshot.Cleanup(ctx.snapshotUnpackedPath)
   174  }
   175  
   176  func (ctx *Context) Warn(msg string, args ...interface{}) {
   177  	ctx.alert.Printf("WARNING: "+msg, args...)
   178  }