mirror of
https://github.com/yunionio/cloudpods.git
synced 2026-07-04 03:34:28 +08:00
179 lines
4.9 KiB
Go
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...)
|
|
}
|