Skip to content

Commit

Permalink
feat(templates): handle task as option and spec in template query (in…
Browse files Browse the repository at this point in the history
  • Loading branch information
glinton authored Aug 26, 2020
1 parent 6151c38 commit be35109
Show file tree
Hide file tree
Showing 7 changed files with 344 additions and 40 deletions.
155 changes: 132 additions & 23 deletions pkger/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -1113,16 +1113,60 @@ func (p *Template) graphTasks() *parseErr {
}

prefix := fmt.Sprintf("tasks[%s].spec", t.MetaName())
t.query, _ = p.parseQuery(prefix, o.Spec.stringShort(fieldQuery), o.Spec.slcResource(fieldParams))
params := o.Spec.slcResource(fieldParams)
task := o.Spec.slcResource("task")

failures := p.parseNestedLabels(o.Spec, func(l *label) error {
var (
err error
failures []validationErr
)

t.query, err = p.parseQuery(prefix, o.Spec.stringShort(fieldQuery), params, task)
if err != nil {
failures = append(failures, validationErr{
Field: fieldQuery,
Msg: err.Error(),
})
}

if o.APIVersion == APIVersion2 {
for _, ref := range t.query.task {
switch ref.EnvRef {
case prefix + ".task.name", prefix + ".params.name":
t.displayName = ref
case prefix + ".task.every":
every, ok := ref.defaultVal.(time.Duration)
if ok {
t.every = every
} else {
failures = append(failures, validationErr{
Field: fieldTask,
Msg: "field every is not duration",
})
}
case prefix + ".task.offset":
offset, ok := ref.defaultVal.(time.Duration)
if ok {
t.offset = offset
} else {
failures = append(failures, validationErr{
Field: fieldTask,
Msg: "field every is not duration",
})
}
}
}
}

failures = append(failures, p.parseNestedLabels(o.Spec, func(l *label) error {
t.labels = append(t.labels, l)
p.mLabels[l.MetaName()].setMapping(t, false)
return nil
})
})...)
sort.Sort(t.labels)

p.mTasks[t.MetaName()] = t

p.setRefs(t.refs()...)
return append(failures, t.valid()...)
})
Expand Down Expand Up @@ -1217,14 +1261,14 @@ func (p *Template) eachResource(resourceKind Kind, fn func(o Object) []validatio
continue
}

if k.APIVersion != APIVersion {
if k.APIVersion != APIVersion && k.APIVersion != APIVersion2 {
pErr.append(resourceErr{
Kind: k.Kind.String(),
Idx: intPtr(i),
ValidationErrs: []validationErr{
{
Field: fieldAPIVersion,
Msg: fmt.Sprintf("invalid API version provided %q; must be 1 in [%s]", k.APIVersion, APIVersion),
Msg: fmt.Sprintf("invalid API version provided %q; must be 1 in [%s, %s]", k.APIVersion, APIVersion, APIVersion2),
},
},
})
Expand Down Expand Up @@ -1532,7 +1576,7 @@ func (p *Template) parseChartQueries(dashMetaName string, chartIdx int, resource
continue
}
prefix := fmt.Sprintf("dashboards[%s].spec.charts[%d].queries[%d]", dashMetaName, chartIdx, i)
qq, err := p.parseQuery(prefix, source, rq.slcResource(fieldParams))
qq, err := p.parseQuery(prefix, source, rq.slcResource(fieldParams), nil)
if err != nil {
vErrs = append(vErrs, validationErr{
Field: "query",
Expand All @@ -1545,7 +1589,7 @@ func (p *Template) parseChartQueries(dashMetaName string, chartIdx int, resource
return q, vErrs
}

func (p *Template) parseQuery(prefix, source string, params []Resource) (query, error) {
func (p *Template) parseQuery(prefix, source string, params, task []Resource) (query, error) {
files := parser.ParseSource(source).Files
if len(files) != 1 {
return query{}, influxErr(influxdb.EInvalid, "invalid query source")
Expand All @@ -1555,29 +1599,52 @@ func (p *Template) parseQuery(prefix, source string, params []Resource) (query,
Query: strings.TrimSpace(source),
}

opt, err := edit.GetOption(files[0], "params")
if err != nil {
return q, nil
}
obj, ok := opt.(*ast.ObjectExpression)
if !ok {
mParams := make(map[string]*references)
tParams := make(map[string]*references)

paramsOpt, paramsErr := edit.GetOption(files[0], "params")
taskOpt, taskErr := edit.GetOption(files[0], "task")
if paramsErr != nil && taskErr != nil {
return q, nil
}

mParams := make(map[string]*references)
for _, p := range obj.Properties {
sl, ok := p.Key.(*ast.Identifier)
if !ok {
continue
if paramsErr == nil {
obj, ok := paramsOpt.(*ast.ObjectExpression)
if ok {
for _, p := range obj.Properties {
sl, ok := p.Key.(*ast.Identifier)
if !ok {
continue
}

mParams[sl.Name] = &references{
EnvRef: sl.Name,
defaultVal: valFromExpr(p.Value),
valType: p.Value.Type(),
}
}
}
}

if taskErr == nil {
tobj, ok := taskOpt.(*ast.ObjectExpression)
if ok {
for _, p := range tobj.Properties {
sl, ok := p.Key.(*ast.Identifier)
if !ok {
continue
}

mParams[sl.Name] = &references{
EnvRef: sl.Name,
defaultVal: valFromExpr(p.Value),
valType: p.Value.Type(),
tParams[sl.Name] = &references{
EnvRef: sl.Name,
defaultVal: valFromExpr(p.Value),
valType: p.Value.Type(),
}
}
}
}

// override defaults here maybe?
for _, pr := range params {
field := pr.stringShort(fieldKey)
if field == "" {
Expand All @@ -1587,7 +1654,6 @@ func (p *Template) parseQuery(prefix, source string, params []Resource) (query,
if _, ok := mParams[field]; !ok {
mParams[field] = &references{EnvRef: field}
}

if def, ok := pr[fieldDefault]; ok {
mParams[field].defaultVal = def
}
Expand All @@ -1596,6 +1662,39 @@ func (p *Template) parseQuery(prefix, source string, params []Resource) (query,
}
}

var err error
for _, pr := range task {
field := pr.stringShort(fieldKey)
if field == "" {
continue
}

if _, ok := tParams[field]; !ok {
tParams[field] = &references{EnvRef: field}
}

if valtype, ok := pr.string(fieldType); ok {
tParams[field].valType = valtype
}

if def, ok := pr[fieldDefault]; ok {
switch tParams[field].valType {
case "duration":
switch defDur := def.(type) {
case string:
tParams[field].defaultVal, err = time.ParseDuration(defDur)
if err != nil {
return query{}, influxErr(influxdb.EInvalid, err.Error())
}
case time.Duration:
tParams[field].defaultVal = defDur
}
default:
tParams[field].defaultVal = def
}
}
}

for _, ref := range mParams {
envRef := fmt.Sprintf("%s.params.%s", prefix, ref.EnvRef)
q.params = append(q.params, &references{
Expand All @@ -1605,6 +1704,16 @@ func (p *Template) parseQuery(prefix, source string, params []Resource) (query,
valType: ref.valType,
})
}

for _, ref := range tParams {
envRef := fmt.Sprintf("%s.task.%s", prefix, ref.EnvRef)
q.task = append(q.task, &references{
EnvRef: envRef,
defaultVal: ref.defaultVal,
val: p.mEnvVals[envRef],
valType: ref.valType,
})
}
return q, nil
}

Expand Down
57 changes: 41 additions & 16 deletions pkger/parser_models.go
Original file line number Diff line number Diff line change
Expand Up @@ -1044,10 +1044,11 @@ func (c colors) valid() []validationErr {
type query struct {
Query string `json:"query" yaml:"query"`
params []*references
task []*references
}

func (q query) DashboardQuery() string {
if len(q.params) == 0 {
if len(q.params) == 0 && len(q.task) == 0 {
return q.Query
}

Expand All @@ -1056,25 +1057,37 @@ func (q query) DashboardQuery() string {
return q.Query
}

opt, err := edit.GetOption(files[0], "params")
if err != nil {
// no params option present in query
paramsOpt, paramsErr := edit.GetOption(files[0], "params")
taskOpt, taskErr := edit.GetOption(files[0], "task")
if taskErr != nil && paramsErr != nil {
return q.Query
}

obj, ok := opt.(*ast.ObjectExpression)
if !ok {
// params option present is invalid. Should always be an Object.
return q.Query
}
if paramsErr == nil {
obj, ok := paramsOpt.(*ast.ObjectExpression)
if ok {
for _, ref := range q.params {
parts := strings.Split(ref.EnvRef, ".")
key := parts[len(parts)-1]
edit.SetProperty(obj, key, ref.expression())
}

for _, ref := range q.params {
parts := strings.Split(ref.EnvRef, ".")
key := parts[len(parts)-1]
edit.SetProperty(obj, key, ref.expression())
edit.SetOption(files[0], "params", obj)
}
}

edit.SetOption(files[0], "params", obj)
if taskErr == nil {
tobj, ok := taskOpt.(*ast.ObjectExpression)
if ok {
for _, ref := range q.task {
parts := strings.Split(ref.EnvRef, ".")
key := parts[len(parts)-1]
edit.SetProperty(tobj, key, ref.expression())
}

edit.SetOption(files[0], "task", tobj)
}
}
return ast.Format(files[0])
}

Expand Down Expand Up @@ -1805,6 +1818,7 @@ func toSummaryTagRules(tagRules []struct{ k, v, op string }) []SummaryTagRule {

const (
fieldTaskCron = "cron"
fieldTask = "task"
)

type task struct {
Expand Down Expand Up @@ -1857,9 +1871,15 @@ func (t *task) summarize() SummaryTask {
field := fmt.Sprintf("spec.params.%s", parts[len(parts)-1])
refs = append(refs, convertRefToRefSummary(field, ref))
}
for _, ref := range t.query.task {
parts := strings.Split(ref.EnvRef, ".")
field := fmt.Sprintf("spec.task.%s", parts[len(parts)-1])
refs = append(refs, convertRefToRefSummary(field, ref))
}
sort.Slice(refs, func(i, j int) bool {
return refs[i].EnvRefKey < refs[j].EnvRefKey
})

return SummaryTask{
SummaryIdentifier: SummaryIdentifier{
Kind: KindTask,
Expand All @@ -1883,6 +1903,7 @@ func (t *task) valid() []validationErr {
if err, ok := isValidName(t.Name(), 1); !ok {
vErrs = append(vErrs, err)
}

if t.cron == "" && t.every == 0 {
vErrs = append(vErrs,
validationErr{
Expand Down Expand Up @@ -2169,7 +2190,7 @@ const (
)

type references struct {
EnvRef string
EnvRef string // key used to reference parameterized field
Secret string

val interface{}
Expand Down Expand Up @@ -2295,7 +2316,11 @@ func astBoolFromIface(v interface{}) *ast.BooleanLiteral {
func astDurationFromIface(v interface{}) *ast.DurationLiteral {
s, ok := v.(string)
if !ok {
return nil
d, ok := v.(time.Duration)
if !ok {
return nil
}
s = d.String()
}
dur, _ := parser.ParseSignedDuration(s)
return dur
Expand Down
Loading

0 comments on commit be35109

Please sign in to comment.