• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / workflows / 1565759653

29 Nov 2024 07:56AM UTC coverage: 67.786% (-14.5%) from 82.255%
1565759653

push

gitlab-ci

web-flow
Merge pull request #336 from alfrunes/2.6.x

chore(deps): Update golang builder images to latest

1050 of 1549 relevant lines covered (67.79%)

5.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

16.3
/app/processor/string.go
1
// Copyright 2022 Northern.tech AS
2
//
3
//    Licensed under the Apache License, Version 2.0 (the "License");
4
//    you may not use this file except in compliance with the License.
5
//    You may obtain a copy of the License at
6
//
7
//        http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//    Unless required by applicable law or agreed to in writing, software
10
//    distributed under the License is distributed on an "AS IS" BASIS,
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//    See the License for the specific language governing permissions and
13
//    limitations under the License.
14

15
package processor
16

17
import (
18
        "bytes"
19
        "net/url"
20
        "os"
21
        "regexp"
22
        "strings"
23
        "text/template"
24

25
        "github.com/thedevsaddam/gojsonq"
26

27
        "github.com/mendersoftware/workflows/model"
28
        "github.com/mendersoftware/workflows/utils"
29
)
30

31
const (
32
        workflowEnvVariable   = "env."
33
        workflowInputVariable = "workflow.input."
34
        regexVariable         = `\$\{(?P<options>(?:(?:[a-zA-Z]+)=(?:[a-zA-Z0-9]+);)*)` +
35
                `(?P<name>[^;\}\|]+)(?:\|(?P<default>[^\}]+))?}`
36
        regexOutputVariable = `(.*)\.json\.(.*)`
37
        encodingFlag        = "encoding"
38
        urlEncodingFlag     = "url"
39
)
40

41
var (
42
        reExpression       = regexp.MustCompile(regexVariable)
43
        reExpressionOutput = regexp.MustCompile(regexOutputVariable)
44

45
        reMatchIndexOptions = reExpression.SubexpIndex("options")
46
        reMatchIndexName    = reExpression.SubexpIndex("name")
47
        reMatchIndexDefault = reExpression.SubexpIndex("default")
48
)
49

50
type Encoding int64
51

52
const (
53
        EncodingPlain Encoding = iota
54
        EncodingURL
55
)
56

57
type JobStringProcessor struct {
58
        workflow *model.Workflow
59
        job      *model.Job
60
}
61

62
type Options struct {
63
        Encoding Encoding
64
}
65

66
func NewJobStringProcessor(
67
        workflow *model.Workflow,
68
        job *model.Job,
69
) *JobStringProcessor {
×
70
        return &JobStringProcessor{
×
71
                workflow: workflow,
×
72
                job:      job,
×
73
        }
×
74
}
×
75

76
func processOptionString(expression string) (opts Options) {
5✔
77
        const (
5✔
78
                flagTokenCount = 2
5✔
79
                lValueIndex    = 0
5✔
80
                rValueIndex    = 1
5✔
81
        )
5✔
82
        for _, flagToken := range strings.Split(expression, ";") {
19✔
83
                flagValueTokens := strings.Split(flagToken, "=")
14✔
84
                if len(flagValueTokens) < flagTokenCount {
19✔
85
                        continue
5✔
86
                }
87
                if flagValueTokens[lValueIndex] == encodingFlag {
14✔
88
                        switch flagValueTokens[rValueIndex] {
5✔
89
                        case urlEncodingFlag:
3✔
90
                                opts.Encoding = EncodingURL
3✔
91
                        }
92
                }
93
        }
94
        return
5✔
95
}
96

97
func (j *JobStringProcessor) ProcessJobString(data string) string {
×
98
        matches := reExpression.FindAllStringSubmatch(data, -1)
×
99

×
100
        // search for ${...} expressions in the data string
×
101
SubMatchLoop:
×
102
        for _, submatch := range matches {
×
103
                // content of the ${...} expression, without the brackets
×
104
                varName := submatch[reMatchIndexName]
×
105
                value := submatch[reMatchIndexDefault]
×
106
                options := processOptionString(submatch[reMatchIndexOptions])
×
107
                // now it is possible to override the encoding with flags: ${encoding=plain;identifier}
×
108
                // if encoding is supplied via flags, it takes precedence, we return the match
×
109
                // without the flags, otherwise fail back to original match and encoding
×
110
                if strings.HasPrefix(varName, workflowInputVariable) &&
×
111
                        len(varName) > len(workflowInputVariable) {
×
112
                        // Replace ${workflow.input.KEY} with the KEY input variable
×
113
                        paramName := varName[len(workflowInputVariable):]
×
114
                        for _, param := range j.job.InputParameters {
×
115
                                if param.Name == paramName {
×
116
                                        value = param.Value
×
117
                                        break
×
118
                                }
119
                        }
120
                } else if strings.HasPrefix(varName, workflowEnvVariable) &&
×
121
                        len(varName) > len(workflowEnvVariable) {
×
122
                        // Replace ${env.KEY} with the KEY environment variable
×
123
                        envName := varName[len(workflowEnvVariable):]
×
124
                        if envValue := os.Getenv(envName); envValue != "" {
×
125
                                value = envValue
×
126
                        }
×
127
                } else if output := reExpressionOutput.FindStringSubmatch(varName); len(output) > 0 {
×
128
                        // Replace ${TASK_NAME.json.JSONPATH} with the value of the JSONPATH expression from the
×
129
                        // JSON output of the previous task with name TASK_NAME. If the output is not a valid
×
130
                        // JSON or the JSONPATH does not resolve to a value, replace with empty string
×
131
                        for _, result := range j.job.Results {
×
132
                                if result.Name == output[1] {
×
133
                                        varKey := output[2]
×
134
                                        var output string
×
135
                                        if result.Type == model.TaskTypeHTTP {
×
136
                                                output = result.HTTPResponse.Body
×
137
                                        } else if result.Type == model.TaskTypeCLI {
×
138
                                                output = result.CLI.Output
×
139
                                        } else {
×
140
                                                continue
×
141
                                        }
142
                                        varValue := gojsonq.New().FromString(output).Find(varKey)
×
143
                                        if varValue == nil {
×
144
                                                varValue = ""
×
145
                                        }
×
146
                                        varValueString, err := utils.ConvertAnythingToString(varValue)
×
147
                                        if err == nil {
×
148
                                                if varValueString != "" {
×
149
                                                        value = varValueString
×
150
                                                }
×
151
                                        } else {
×
152
                                                continue SubMatchLoop
×
153
                                        }
154
                                        break
×
155
                                }
156
                        }
157
                }
158
                if options.Encoding == EncodingURL {
×
159
                        value = url.QueryEscape(value)
×
160
                }
×
161
                data = strings.ReplaceAll(data, submatch[0], value)
×
162
        }
163

164
        return data
×
165
}
166

167
// MaybeExecuteGoTemplate tries to parse and execute data as a go template
168
// if it fails to do so, data is returned.
169
func (j *JobStringProcessor) MaybeExecuteGoTemplate(data string) string {
×
170
        input := j.job.InputParameters.Map()
×
171
        tmpl, err := template.New("go-template").Parse(data)
×
172
        if err != nil {
×
173
                return data
×
174
        }
×
175
        buf := &bytes.Buffer{}
×
176
        err = tmpl.Execute(buf, input)
×
177
        if err != nil {
×
178
                return data
×
179
        }
×
180
        return buf.String()
×
181
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc