context.go 4.9 KB

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