| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178 |
- //
- // Use and distribution licensed under the Apache license version 2.
- //
- // See the COPYING file in the root project directory for full text.
- //
- package context
- import (
- "fmt"
- "github.com/jaypipes/ghw/pkg/option"
- "github.com/jaypipes/ghw/pkg/snapshot"
- )
- // Context contains the merged set of configuration switches that act as an
- // execution context when calling internal discovery methods
- type Context struct {
- Chroot string
- EnableTools bool
- SnapshotPath string
- SnapshotRoot string
- SnapshotExclusive bool
- PathOverrides option.PathOverrides
- snapshotUnpackedPath string
- alert option.Alerter
- err error
- }
- // WithContext returns an option.Option that contains a pre-existing Context
- // struct. This is useful for some internal code that sets up snapshots.
- func WithContext(ctx *Context) *option.Option {
- return &option.Option{
- Context: ctx,
- }
- }
- // Exists returns true if the supplied (merged) Option already contains
- // a context.
- //
- // TODO(jaypipes): We can get rid of this when we combine the option and
- // context packages, which will make it easier to detect the presence of a
- // pre-setup Context.
- func Exists(opt *option.Option) bool {
- return opt != nil && opt.Context != nil
- }
- // New returns a Context struct pointer that has had various options set on it
- func New(opts ...*option.Option) *Context {
- merged := option.Merge(opts...)
- var ctx *Context
- if merged.Context != nil {
- var castOK bool
- ctx, castOK = merged.Context.(*Context)
- if !castOK {
- panic("passed in a non-Context for the WithContext() function!")
- }
- return ctx
- }
- ctx = &Context{
- alert: option.EnvOrDefaultAlerter(),
- Chroot: *merged.Chroot,
- }
- if merged.Snapshot != nil {
- ctx.SnapshotPath = merged.Snapshot.Path
- // root is optional, so a extra check is warranted
- if merged.Snapshot.Root != nil {
- ctx.SnapshotRoot = *merged.Snapshot.Root
- }
- ctx.SnapshotExclusive = merged.Snapshot.Exclusive
- }
- if merged.Alerter != nil {
- ctx.alert = merged.Alerter
- }
- if merged.EnableTools != nil {
- ctx.EnableTools = *merged.EnableTools
- }
- if merged.PathOverrides != nil {
- ctx.PathOverrides = merged.PathOverrides
- }
- // New is not allowed to return error - it would break the established API.
- // so the only way out is to actually do the checks here and record the error,
- // and return it later, at the earliest possible occasion, in Setup()
- if ctx.SnapshotPath != "" && ctx.Chroot != option.DefaultChroot {
- // The env/client code supplied a value, but we are will overwrite it when unpacking shapshots!
- ctx.err = fmt.Errorf("Conflicting options: chroot %q and snapshot path %q", ctx.Chroot, ctx.SnapshotPath)
- }
- return ctx
- }
- // FromEnv returns a Context that has been populated from the environs or
- // default options values
- func FromEnv() *Context {
- chrootVal := option.EnvOrDefaultChroot()
- enableTools := option.EnvOrDefaultTools()
- snapPathVal := option.EnvOrDefaultSnapshotPath()
- snapRootVal := option.EnvOrDefaultSnapshotRoot()
- snapExclusiveVal := option.EnvOrDefaultSnapshotExclusive()
- return &Context{
- Chroot: chrootVal,
- EnableTools: enableTools,
- SnapshotPath: snapPathVal,
- SnapshotRoot: snapRootVal,
- SnapshotExclusive: snapExclusiveVal,
- }
- }
- // Do wraps a Setup/Teardown pair around the given function
- func (ctx *Context) Do(fn func() error) error {
- err := ctx.Setup()
- if err != nil {
- return err
- }
- defer func() {
- err := ctx.Teardown()
- if err != nil {
- ctx.Warn("teardown error: %v", err)
- }
- }()
- return fn()
- }
- // Setup prepares the extra optional data a Context may use.
- // `Context`s are ready to use once returned by `New`. Optional features,
- // like snapshot unpacking, may require extra steps. Run `Setup` to perform them.
- // You should call `Setup` just once. It is safe to call `Setup` if you don't make
- // use of optional extra features - `Setup` will do nothing.
- func (ctx *Context) Setup() error {
- if ctx.err != nil {
- return ctx.err
- }
- if ctx.SnapshotPath == "" {
- // nothing to do!
- return nil
- }
- var err error
- root := ctx.SnapshotRoot
- if root == "" {
- root, err = snapshot.Unpack(ctx.SnapshotPath)
- if err == nil {
- ctx.snapshotUnpackedPath = root
- }
- } else {
- var flags uint
- if ctx.SnapshotExclusive {
- flags |= snapshot.OwnTargetDirectory
- }
- _, err = snapshot.UnpackInto(ctx.SnapshotPath, root, flags)
- }
- if err != nil {
- return err
- }
- ctx.Chroot = root
- return nil
- }
- // Teardown releases any resource acquired by Setup.
- // You should always call `Teardown` if you called `Setup` to free any resources
- // acquired by `Setup`. Check `Do` for more automated management.
- func (ctx *Context) Teardown() error {
- if ctx.snapshotUnpackedPath == "" {
- // if the client code provided the unpack directory,
- // then it is also in charge of the cleanup.
- return nil
- }
- return snapshot.Cleanup(ctx.snapshotUnpackedPath)
- }
- func (ctx *Context) Warn(msg string, args ...interface{}) {
- ctx.alert.Printf("WARNING: "+msg, args...)
- }
|