Files
cloudpods/vendor/github.com/jaypipes/ghw/pkg/context/context.go
2022-03-27 11:00:59 +08:00

179 lines
4.9 KiB
Go

//
// 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...)
}