Perform validation of boot command.

This commit is contained in:
Matthew Hooker 2018-04-18 17:48:35 -07:00
parent 7d43324359
commit aa69bdf74e
No known key found for this signature in database
GPG Key ID: 7B5F933D9CE8C6A1
3 changed files with 81 additions and 13 deletions

View File

@ -42,13 +42,22 @@ func (k KeyAction) String() string {
} }
type expression interface { type expression interface {
// Do executes the expression
Do(context.Context, BCDriver) error Do(context.Context, BCDriver) error
// Validate validates the expression without executing it
Validate() error
} }
type expressionSequence []expression type expressionSequence []expression
// Do executes every expression in the sequence and then finalizes the driver. // Do executes every expression in the sequence and then finalizes the driver.
func (s expressionSequence) Do(ctx context.Context, b BCDriver) error { func (s expressionSequence) Do(ctx context.Context, b BCDriver) error {
// validate should never fail here, since it should be called before
// expressionSequence.Do. Only reason we don't panic is so we can clean up.
if errs := s.Validate(); errs != nil {
return fmt.Errorf("Found an invalid boot command. This is likely an error in Packer, so please open a ticket.")
}
for _, exp := range s { for _, exp := range s {
if err := ctx.Err(); err != nil { if err := ctx.Err(); err != nil {
return err return err
@ -60,6 +69,16 @@ func (s expressionSequence) Do(ctx context.Context, b BCDriver) error {
return b.Finalize() return b.Finalize()
} }
// Do executes every expression in the sequence and then finalizes the driver.
func (s expressionSequence) Validate() (errs []error) {
for _, exp := range s {
if err := exp.Validate(); err != nil {
errs = append(errs, err)
}
}
return
}
// GenerateExpressionSequence generates a sequence of expressions from the // GenerateExpressionSequence generates a sequence of expressions from the
// given command. This is the primary entry point to the boot command parser. // given command. This is the primary entry point to the boot command parser.
func GenerateExpressionSequence(command string) (expressionSequence, error) { func GenerateExpressionSequence(command string) (expressionSequence, error) {
@ -67,9 +86,6 @@ func GenerateExpressionSequence(command string) (expressionSequence, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
if got == nil {
return nil, fmt.Errorf("No expressions found.")
}
seq := expressionSequence{} seq := expressionSequence{}
for _, exp := range got.([]interface{}) { for _, exp := range got.([]interface{}) {
seq = append(seq, exp.(expression)) seq = append(seq, exp.(expression))
@ -93,6 +109,14 @@ func (w *waitExpression) Do(ctx context.Context, _ BCDriver) error {
} }
} }
// Validate returns an error if the time is <= 0
func (w *waitExpression) Validate() error {
if w.d <= 0 {
return fmt.Errorf("Expecting a positive wait value. Got %s", w.d)
}
return nil
}
func (w *waitExpression) String() string { func (w *waitExpression) String() string {
return fmt.Sprintf("Wait<%s>", w.d) return fmt.Sprintf("Wait<%s>", w.d)
} }
@ -107,6 +131,11 @@ func (s *specialExpression) Do(ctx context.Context, driver BCDriver) error {
return driver.SendSpecial(s.s, s.action) return driver.SendSpecial(s.s, s.action)
} }
// Validate always passes
func (s *specialExpression) Validate() error {
return nil
}
func (s *specialExpression) String() string { func (s *specialExpression) String() string {
return fmt.Sprintf("Spec-%s(%s)", s.action, s.s) return fmt.Sprintf("Spec-%s(%s)", s.action, s.s)
} }
@ -121,6 +150,11 @@ func (l *literal) Do(ctx context.Context, driver BCDriver) error {
return driver.SendKey(l.s, l.action) return driver.SendKey(l.s, l.action)
} }
// Validate always passes
func (l *literal) Validate() error {
return nil
}
func (l *literal) String() string { func (l *literal) String() string {
return fmt.Sprintf("LIT-%s(%s)", l.action, string(l.s)) return fmt.Sprintf("LIT-%s(%s)", l.action, string(l.s))
} }

View File

@ -100,17 +100,41 @@ func Test_special(t *testing.T) {
} }
} }
func Test_negativeWait(t *testing.T) { func Test_validation(t *testing.T) {
in := "<wait-1m>" var expressions = []struct {
_, err := ParseReader("", strings.NewReader(in)) in string
if err != nil { valid bool
log.Fatal(err) }{
{
"<wait1m>",
true,
},
{
"<wait-1m>",
false,
},
{
"<f1>",
true,
},
{
"<",
true,
},
} }
for _, tt := range expressions {
got, err := ParseReader("", strings.NewReader(tt.in))
if err != nil {
log.Fatal(err)
}
/*
gL := toIfaceSlice(got) gL := toIfaceSlice(got)
for _, g := range gL { assert.Len(t, gL, 1)
assert.Equal(t, tt.out, g.(*specialExpression).String()) err = gL[0].(expression).Validate()
} if tt.valid {
*/ assert.NoError(t, err)
} else {
assert.Error(t, err)
}
}
} }

View File

@ -33,6 +33,16 @@ func (c *BootConfig) Prepare(ctx *interpolate.Context) (errs []error) {
c.BootWait = bw c.BootWait = bw
} }
} }
if c.BootCommand != nil {
expSeq, err := GenerateExpressionSequence(c.FlatBootCommand())
if err != nil {
errs = append(errs, err)
} else if vErrs := expSeq.Validate(); vErrs != nil {
errs = append(errs, vErrs...)
}
}
return return
} }