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 }