github.com/angdraug/packer@v1.3.2/common/bootcommand/boot_command_ast.go (about)

     1  package bootcommand
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"log"
     7  	"strings"
     8  	"time"
     9  )
    10  
    11  // KeysAction represents what we want to do with a key press.
    12  // It can take 3 states. We either want to:
    13  // * press the key once
    14  // * press and hold
    15  // * press and release
    16  type KeyAction int
    17  
    18  const (
    19  	KeyOn KeyAction = 1 << iota
    20  	KeyOff
    21  	KeyPress
    22  )
    23  
    24  func (k KeyAction) String() string {
    25  	switch k {
    26  	case KeyOn:
    27  		return "On"
    28  	case KeyOff:
    29  		return "Off"
    30  	case KeyPress:
    31  		return "Press"
    32  	}
    33  	panic(fmt.Sprintf("Unknwon KeyAction %d", k))
    34  }
    35  
    36  type expression interface {
    37  	// Do executes the expression
    38  	Do(context.Context, BCDriver) error
    39  	// Validate validates the expression without executing it
    40  	Validate() error
    41  }
    42  
    43  type expressionSequence []expression
    44  
    45  // Do executes every expression in the sequence and then flushes remaining
    46  // scancodes.
    47  func (s expressionSequence) Do(ctx context.Context, b BCDriver) error {
    48  	// validate should never fail here, since it should be called before
    49  	// expressionSequence.Do. Only reason we don't panic is so we can clean up.
    50  	if errs := s.Validate(); errs != nil {
    51  		return fmt.Errorf("Found an invalid boot command. This is likely an error in Packer, so please open a ticket.")
    52  	}
    53  
    54  	for _, exp := range s {
    55  		if err := ctx.Err(); err != nil {
    56  			return err
    57  		}
    58  		if err := exp.Do(ctx, b); err != nil {
    59  			return err
    60  		}
    61  	}
    62  	return b.Flush()
    63  }
    64  
    65  // Validate tells us if every expression in the sequence is valid.
    66  func (s expressionSequence) Validate() (errs []error) {
    67  	for _, exp := range s {
    68  		if err := exp.Validate(); err != nil {
    69  			errs = append(errs, err)
    70  		}
    71  	}
    72  	return
    73  }
    74  
    75  // GenerateExpressionSequence generates a sequence of expressions from the
    76  // given command. This is the primary entry point to the boot command parser.
    77  func GenerateExpressionSequence(command string) (expressionSequence, error) {
    78  	seq := expressionSequence{}
    79  	if command == "" {
    80  		return seq, nil
    81  	}
    82  	got, err := ParseReader("", strings.NewReader(command))
    83  	if err != nil {
    84  		return nil, err
    85  	}
    86  	for _, exp := range got.([]interface{}) {
    87  		seq = append(seq, exp.(expression))
    88  	}
    89  	return seq, nil
    90  }
    91  
    92  type waitExpression struct {
    93  	d time.Duration
    94  }
    95  
    96  // Do waits the amount of time described by the expression. It is cancellable
    97  // through the context.
    98  func (w *waitExpression) Do(ctx context.Context, driver BCDriver) error {
    99  	driver.Flush()
   100  	log.Printf("[INFO] Waiting %s", w.d)
   101  	select {
   102  	case <-time.After(w.d):
   103  		return nil
   104  	case <-ctx.Done():
   105  		return ctx.Err()
   106  	}
   107  }
   108  
   109  // Validate returns an error if the time is <= 0
   110  func (w *waitExpression) Validate() error {
   111  	if w.d <= 0 {
   112  		return fmt.Errorf("Expecting a positive wait value. Got %s", w.d)
   113  	}
   114  	return nil
   115  }
   116  
   117  func (w *waitExpression) String() string {
   118  	return fmt.Sprintf("Wait<%s>", w.d)
   119  }
   120  
   121  type specialExpression struct {
   122  	s      string
   123  	action KeyAction
   124  }
   125  
   126  // Do sends the special command to the driver, along with the key action.
   127  func (s *specialExpression) Do(ctx context.Context, driver BCDriver) error {
   128  	return driver.SendSpecial(s.s, s.action)
   129  }
   130  
   131  // Validate always passes
   132  func (s *specialExpression) Validate() error {
   133  	return nil
   134  }
   135  
   136  func (s *specialExpression) String() string {
   137  	return fmt.Sprintf("Spec-%s(%s)", s.action, s.s)
   138  }
   139  
   140  type literal struct {
   141  	s      rune
   142  	action KeyAction
   143  }
   144  
   145  // Do sends the key to the driver, along with the key action.
   146  func (l *literal) Do(ctx context.Context, driver BCDriver) error {
   147  	return driver.SendKey(l.s, l.action)
   148  }
   149  
   150  // Validate always passes
   151  func (l *literal) Validate() error {
   152  	return nil
   153  }
   154  
   155  func (l *literal) String() string {
   156  	return fmt.Sprintf("LIT-%s(%s)", l.action, string(l.s))
   157  }