Skip to content

Commit

Permalink
feat: add validator and retries to qa engine
Browse files Browse the repository at this point in the history
Signed-off-by: Mehant Kammakomati <kmehant@gmail.com>
  • Loading branch information
kmehant committed Oct 13, 2022
1 parent 2583e36 commit 4a982a8
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 36 deletions.
43 changes: 36 additions & 7 deletions qaengine/cliengine.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,14 @@ func (*CliEngine) fetchSelectAnswer(prob qatypes.Problem) (qatypes.Problem, erro
Options: prob.Options,
Default: def,
}
if err := survey.AskOne(prompt, &ans); err != nil {
question := &survey.Question{
Prompt: prompt,
Validate: prob.Validator,
}
if err := survey.Ask([]*survey.Question{question}, &ans); err != nil {
logrus.Fatalf("Error while asking a question : %s", err)
}

prob.Answer = ans
return prob, nil
}
Expand All @@ -95,8 +100,12 @@ func (*CliEngine) fetchMultiSelectAnswer(prob qatypes.Problem) (qatypes.Problem,
Options: prob.Options,
Default: prob.Default,
}
question := &survey.Question{
Prompt: prompt,
Validate: prob.Validator,
}
tickIcon := func(icons *survey.IconSet) { icons.MarkedOption.Text = "[\u2713]" }
if err := survey.AskOne(prompt, &ans, survey.WithIcons(tickIcon)); err != nil {
if err := survey.Ask([]*survey.Question{question}, &ans, survey.WithIcons(tickIcon)); err != nil {
logrus.Fatalf("Error while asking a question : %s", err)
}
otherAnsPresent := false
Expand All @@ -114,7 +123,11 @@ func (*CliEngine) fetchMultiSelectAnswer(prob qatypes.Problem) (qatypes.Problem,
Message: getQAMessage(prob),
Default: "",
}
if err := survey.AskOne(prompt, &multilineAns); err != nil {
question := &survey.Question{
Prompt: prompt,
Validate: prob.Validator,
}
if err := survey.Ask([]*survey.Question{question}, &multilineAns); err != nil {
logrus.Fatalf("Error while asking a question : %s", err)
}
for _, lineAns := range strings.Split(multilineAns, "\n") {
Expand All @@ -137,7 +150,11 @@ func (*CliEngine) fetchConfirmAnswer(prob qatypes.Problem) (qatypes.Problem, err
Message: getQAMessage(prob),
Default: def,
}
if err := survey.AskOne(prompt, &ans); err != nil {
question := &survey.Question{
Prompt: prompt,
Validate: prob.Validator,
}
if err := survey.Ask([]*survey.Question{question}, &ans); err != nil {
logrus.Fatalf("Error while asking a question : %s", err)
}
prob.Answer = ans
Expand All @@ -153,7 +170,11 @@ func (*CliEngine) fetchInputAnswer(prob qatypes.Problem) (qatypes.Problem, error
Message: getQAMessage(prob),
Default: def,
}
if err := survey.AskOne(prompt, &ans); err != nil {
question := &survey.Question{
Prompt: prompt,
Validate: prob.Validator,
}
if err := survey.Ask([]*survey.Question{question}, &ans); err != nil {
logrus.Fatalf("Error while asking a question : %s", err)
}
prob.Answer = ans
Expand All @@ -169,7 +190,11 @@ func (*CliEngine) fetchMultilineInputAnswer(prob qatypes.Problem) (qatypes.Probl
Message: getQAMessage(prob),
Default: def,
}
if err := survey.AskOne(prompt, &ans); err != nil {
question := &survey.Question{
Prompt: prompt,
Validate: prob.Validator,
}
if err := survey.Ask([]*survey.Question{question}, &ans); err != nil {
logrus.Fatalf("Error while asking a question : %s", err)
}
prob.Answer = ans
Expand All @@ -181,7 +206,11 @@ func (*CliEngine) fetchPasswordAnswer(prob qatypes.Problem) (qatypes.Problem, er
prompt := &survey.Password{
Message: getQAMessage(prob),
}
if err := survey.AskOne(prompt, &ans); err != nil {
question := &survey.Question{
Prompt: prompt,
Validate: prob.Validator,
}
if err := survey.Ask([]*survey.Question{question}, &ans); err != nil {
logrus.Fatalf("Error while asking a question : %s", err)
}
prob.Answer = ans
Expand Down
13 changes: 12 additions & 1 deletion qaengine/defaultengine.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package qaengine

import (
"fmt"

qatypes "github.com/konveyor/move2kube/types/qaengine"
)

Expand All @@ -42,5 +44,14 @@ func (*DefaultEngine) IsInteractiveEngine() bool {
// FetchAnswer fetches the default answers
func (*DefaultEngine) FetchAnswer(prob qatypes.Problem) (qatypes.Problem, error) {
err := prob.SetAnswer(prob.Default)
return prob, err
if err != nil {
return prob, err
}
if prob.Validator != nil {
err := prob.Validator(prob.Answer)
if err != nil {
return prob, fmt.Errorf("default value is invalid. Error : %s", err)
}
}
return prob, nil
}
8 changes: 0 additions & 8 deletions qaengine/engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -151,14 +151,6 @@ func FetchAnswer(prob qatypes.Problem) (qatypes.Problem, error) {
}
}
}
if prob.Validator != nil {
err := prob.Validator(prob.Answer)
if err != nil {
logrus.Errorf("incorrect input. Error : %s", err)
prob.Answer = nil
return FetchAnswer(prob)
}
}
for _, writeStore := range writeStores {
writeStore.AddSolution(prob)
}
Expand Down
15 changes: 13 additions & 2 deletions qaengine/httprestengine.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,13 +99,14 @@ func (h *HTTPRESTEngine) FetchAnswer(prob qatypes.Problem) (qatypes.Problem, err
logrus.Errorf("the QA problem object is invalid. Error: %q", err)
return prob, err
}
if prob.Answer == nil {
for prob.Answer == nil {
logrus.Debugf("Passing problem to HTTP REST QA Engine ID: %s, desc: %s", prob.ID, prob.Desc)
h.problemChan <- prob
prob = <-h.answerChan
if prob.Answer == nil {
return prob, fmt.Errorf("failed to resolve the QA problem: %+v", prob)
} else if prob.Type == qatypes.MultiSelectSolutionFormType {
}
if prob.Type == qatypes.MultiSelectSolutionFormType {
otherAnsPresent := false
ans, err := common.ConvertInterfaceToSliceOfStrings(prob.Answer)
if err != nil {
Expand Down Expand Up @@ -137,7 +138,17 @@ func (h *HTTPRESTEngine) FetchAnswer(prob qatypes.Problem) (qatypes.Problem, err
}
prob.Answer = newAns
}
if prob.Validator == nil {
break
}
err := prob.Validator(prob.Answer)
if err == nil {
break
}
logrus.Errorf("incorrect input. Error : %s", err)
prob.Answer = nil
}

return prob, nil
}

Expand Down
14 changes: 13 additions & 1 deletion qaengine/storeengine.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
package qaengine

import (
"fmt"

qatypes "github.com/konveyor/move2kube/types/qaengine"
)

Expand All @@ -32,7 +34,17 @@ func (se *StoreEngine) StartEngine() error {

// FetchAnswer fetches the answer from the store
func (se *StoreEngine) FetchAnswer(prob qatypes.Problem) (qatypes.Problem, error) {
return se.store.GetSolution(prob)
problem, err := se.store.GetSolution(prob)
if err != nil {
return problem, err
}
if problem.Validator != nil {
err := problem.Validator(problem.Answer)
if err != nil {
return problem, fmt.Errorf("incorrect input. Error : %s", err)
}
}
return problem, nil
}

// IsInteractiveEngine returns true if the engine interacts with the user
Expand Down
33 changes: 17 additions & 16 deletions transformer/external/starlarktransformer.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
"github.com/qri-io/starlib"
starutil "github.com/qri-io/starlib/util"
"github.com/sirupsen/logrus"
"github.com/spf13/cast"
"go.starlark.net/starlark"
"go.starlark.net/starlarkstruct"
)
Expand Down Expand Up @@ -275,28 +276,28 @@ func (t *Starlark) getStarlarkQuery() *starlark.Builtin {
prob.Type = qatypes.InputSolutionFormType
}
if validation != "" {
validationFn, ok := t.StarGlobals[validation]
if !ok {
return starlark.None, fmt.Errorf("provided validation function not found : %s", validation)
}
fn, ok := validationFn.(*starlark.Function)
if !ok {
return starlark.None, fmt.Errorf("%s is not a function", validationFn)
}
prob.Validator = func(ans interface{}) error {
validationFn := t.StarGlobals[validation]
fn, ok := validationFn.(*starlark.Function)
if !ok {
err = fmt.Errorf("%s is not a function", validationFn)
logrus.Errorf("%s", err)
}
answer, err := starutil.Marshal(ans)
if err != nil {
logrus.Errorf("unable to convert %s to starlark value : %s", ans, err)
return err
return fmt.Errorf("unable to convert %s to starlark value : %s", ans, err)
}
val, err := starlark.Call(t.StarThread, fn, starlark.Tuple{answer}, nil)
if err != nil {
logrus.Errorf("Unable to execute the starlark function: Error : %s Value : %s", err, val)
return err
return fmt.Errorf("unable to execute the starlark function: Error : %s", err)
}
value, err := starutil.Unmarshal(val)
if err != nil {
logrus.Errorf("Unable to unmarshal starlark function result : %s", err)
return err
return fmt.Errorf("unable to unmarshal starlark function result : %s", err)
}
// if empty string is returned then we assume validation is successful
if value.(string) != "" {
return fmt.Errorf("validation failed : %s", value.(string))
}
Expand Down Expand Up @@ -521,13 +522,13 @@ func (t *Starlark) getStarlarkFindXmlPath() *starlark.Builtin {
}
data := expr.Evaluate(xmlquery.CreateXPathNavigator(doc))
var result []interface{}
switch data.(type) {
switch d := data.(type) {
case bool:
result = append(result, strconv.FormatBool(data.(bool)))
result = append(result, cast.ToString(d))
case float64:
result = append(result, strconv.FormatFloat(data.(float64), 'E', -1, 64))
result = append(result, strconv.FormatFloat(d, 'E', -1, 64))
case string:
result = append(result, data.(string))
result = append(result, d)
case *xpath.NodeIterator:
iterator := data.(*xpath.NodeIterator)
for iterator.MoveNext() {
Expand Down
11 changes: 10 additions & 1 deletion types/qaengine/commonqa/commonqa.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,16 @@ func IngressHost(defaulthost string, clusterQaLabel string) string {

// MinimumReplicaCount returns minimum replica count
func MinimumReplicaCount(defaultminreplicas string) string {
return qaengine.FetchStringAnswer(common.ConfigMinReplicasKey, "Provide the minimum number of replicas each service should have", []string{"If the value is 0 pods won't be started by default"}, defaultminreplicas, nil)
return qaengine.FetchStringAnswer(common.ConfigMinReplicasKey, "Provide the minimum number of replicas each service should have", []string{"If the value is 0 pods won't be started by default"}, defaultminreplicas, func(replicaCount interface{}) error {
replicaCountI, err := cast.ToIntE(replicaCount)
if err != nil {
return err
}
if replicaCountI < 0 {
return fmt.Errorf("replica count should be a positive number")
}
return nil
})
}

// GetPortsForService returns ports used by a service
Expand Down

0 comments on commit 4a982a8

Please sign in to comment.