github.com/HashDataInc/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 }