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

mendersoftware / deployments / 1418500193

19 Aug 2024 01:32PM UTC coverage: 79.694%. Remained the same
1418500193

Pull #1037

gitlab-ci

merlin-northern
fix: explicitly mention how name filtering works for images and releases.

Changelog: Title
Ticket: MEN-7435
Signed-off-by: Peter Grzybowski <peter@northern.tech>
Pull Request #1037: fix: explicitly mention how name filtering works for images and relea…

8124 of 10194 relevant lines covered (79.69%)

34.59 hits per line

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

91.35
/client/workflows/client.go
1
// Copyright 2023 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 workflows
16

17
import (
18
        "bytes"
19
        "context"
20
        "encoding/json"
21
        "io"
22
        "net/http"
23
        "strings"
24
        "time"
25

26
        "github.com/mendersoftware/go-lib-micro/config"
27
        "github.com/mendersoftware/go-lib-micro/identity"
28
        "github.com/mendersoftware/go-lib-micro/log"
29
        "github.com/mendersoftware/go-lib-micro/requestid"
30
        "github.com/mendersoftware/go-lib-micro/rest_utils"
31
        "github.com/pkg/errors"
32

33
        dconfig "github.com/mendersoftware/deployments/config"
34
        "github.com/mendersoftware/deployments/model"
35
)
36

37
const (
38
        healthURL                          = "/api/v1/health"
39
        generateArtifactURL                = "/api/v1/workflow/generate_artifact"
40
        reindexReportingURL                = "/api/v1/workflow/reindex_reporting"
41
        reindexReportingDeploymentURL      = "/api/v1/workflow/reindex_reporting_deployment"
42
        reindexReportingDeploymentBatchURL = "/api/v1/workflow/reindex_reporting_deployment/batch"
43
        defaultTimeout                     = 5 * time.Second
44
)
45

46
type DeviceDeploymentShortInfo struct {
47
        ID           string
48
        DeviceID     string
49
        DeploymentID string
50
}
51

52
// Client is the workflows client
53
//
54
//go:generate ../../utils/mockgen.sh
55
type Client interface {
56
        CheckHealth(ctx context.Context) error
57
        StartGenerateArtifact(
58
                ctx context.Context,
59
                multipartGenerateImageMsg *model.MultipartGenerateImageMsg,
60
        ) error
61
        StartReindexReporting(c context.Context, device string) error
62
        StartReindexReportingDeployment(c context.Context, device, deployment, id string) error
63
        StartReindexReportingDeploymentBatch(c context.Context, info []DeviceDeploymentShortInfo) error
64
}
65

66
// NewClient returns a new workflows client
67
func NewClient() Client {
13✔
68
        workflowsBaseURL := config.Config.GetString(dconfig.SettingWorkflows)
13✔
69
        return &client{
13✔
70
                baseURL:    workflowsBaseURL,
13✔
71
                httpClient: &http.Client{Timeout: defaultTimeout},
13✔
72
        }
13✔
73
}
13✔
74

75
type client struct {
76
        baseURL    string
77
        httpClient *http.Client
78
}
79

80
func (c *client) CheckHealth(ctx context.Context) error {
4✔
81
        var (
4✔
82
                apiErr rest_utils.ApiError
4✔
83
                client http.Client
4✔
84
        )
4✔
85

4✔
86
        if ctx == nil {
5✔
87
                ctx = context.Background()
1✔
88
        }
1✔
89
        if _, ok := ctx.Deadline(); !ok {
6✔
90
                var cancel context.CancelFunc
2✔
91
                ctx, cancel = context.WithTimeout(ctx, defaultTimeout)
2✔
92
                defer cancel()
2✔
93
        }
2✔
94
        req, _ := http.NewRequestWithContext(
4✔
95
                ctx, "GET", c.baseURL+healthURL, nil,
4✔
96
        )
4✔
97

4✔
98
        rsp, err := client.Do(req)
4✔
99
        if err != nil {
5✔
100
                return err
1✔
101
        }
1✔
102
        defer rsp.Body.Close()
3✔
103
        if rsp.StatusCode >= http.StatusOK && rsp.StatusCode < 300 {
4✔
104
                return nil
1✔
105
        }
1✔
106
        decoder := json.NewDecoder(rsp.Body)
2✔
107
        err = decoder.Decode(&apiErr)
2✔
108
        if err != nil {
3✔
109
                return errors.Errorf("health check HTTP error: %s", rsp.Status)
1✔
110
        }
1✔
111
        return &apiErr
1✔
112
}
113

114
func (c *client) StartGenerateArtifact(
115
        ctx context.Context,
116
        multipartGenerateImageMsg *model.MultipartGenerateImageMsg,
117
) error {
3✔
118
        l := log.FromContext(ctx)
3✔
119
        l.Debugf("Submit generate artifact: tenantID=%s, artifactID=%s",
3✔
120
                multipartGenerateImageMsg.TenantID, multipartGenerateImageMsg.ArtifactID)
3✔
121

3✔
122
        workflowsURL := c.baseURL + generateArtifactURL
3✔
123

3✔
124
        payload, _ := json.Marshal(multipartGenerateImageMsg)
3✔
125
        req, err := http.NewRequest("POST", workflowsURL, strings.NewReader(string(payload)))
3✔
126
        if err != nil {
3✔
127
                return err
×
128
        }
×
129

130
        res, err := c.httpClient.Do(req)
3✔
131
        if err != nil {
3✔
132
                return errors.Wrapf(err, "failed to start workflow: generate_artifact")
×
133
        }
×
134
        defer res.Body.Close()
3✔
135
        if res.StatusCode != http.StatusCreated {
4✔
136
                body, err := io.ReadAll(res.Body)
1✔
137
                if err != nil {
1✔
138
                        body = []byte("<failed to read>")
×
139
                }
×
140
                l.Errorf("generate artifact failed with status %v, response text: %s",
1✔
141
                        res.StatusCode, body)
1✔
142
                return errors.New("failed to start workflow: generate_artifact")
1✔
143
        }
144
        return nil
2✔
145
}
146

147
func (c *client) StartReindexReporting(ctx context.Context, device string) error {
3✔
148
        if _, ok := ctx.Deadline(); !ok {
6✔
149
                var cancel context.CancelFunc
3✔
150
                ctx, cancel = context.WithTimeout(ctx, defaultTimeout)
3✔
151
                defer cancel()
3✔
152
        }
3✔
153
        tenantID := ""
3✔
154
        if ident := identity.FromContext(ctx); ident != nil {
6✔
155
                tenantID = ident.Tenant
3✔
156
        }
3✔
157
        wflow := ReindexWorkflow{
3✔
158
                RequestID: requestid.FromContext(ctx),
3✔
159
                TenantID:  tenantID,
3✔
160
                DeviceID:  device,
3✔
161
                Service:   ServiceDeployments,
3✔
162
        }
3✔
163
        payload, _ := json.Marshal(wflow)
3✔
164
        req, err := http.NewRequestWithContext(ctx,
3✔
165
                "POST",
3✔
166
                c.baseURL+reindexReportingURL,
3✔
167
                bytes.NewReader(payload),
3✔
168
        )
3✔
169
        if err != nil {
3✔
170
                return errors.Wrap(err, "workflows: error preparing HTTP request")
×
171
        }
×
172

173
        req.Header.Set("Content-Type", "application/json")
3✔
174

3✔
175
        rsp, err := c.httpClient.Do(req)
3✔
176
        if err != nil {
3✔
177
                return errors.Wrap(err, "workflows: failed to trigger reporting reindex")
×
178
        }
×
179
        defer rsp.Body.Close()
3✔
180

3✔
181
        if rsp.StatusCode < 300 {
4✔
182
                return nil
1✔
183
        }
1✔
184

185
        if rsp.StatusCode == http.StatusNotFound {
3✔
186
                workflowURIparts := strings.Split(reindexReportingURL, "/")
1✔
187
                workflowName := workflowURIparts[len(workflowURIparts)-1]
1✔
188
                return errors.New(`workflows: workflow "` + workflowName + `" not defined`)
1✔
189
        }
1✔
190

191
        return errors.Errorf(
1✔
192
                "workflows: unexpected HTTP status from workflows service: %s",
1✔
193
                rsp.Status,
1✔
194
        )
1✔
195
}
196

197
func (c *client) StartReindexReportingDeployment(ctx context.Context,
198
        device, deployment, id string) error {
3✔
199
        if _, ok := ctx.Deadline(); !ok {
6✔
200
                var cancel context.CancelFunc
3✔
201
                ctx, cancel = context.WithTimeout(ctx, defaultTimeout)
3✔
202
                defer cancel()
3✔
203
        }
3✔
204
        tenantID := ""
3✔
205
        if ident := identity.FromContext(ctx); ident != nil {
6✔
206
                tenantID = ident.Tenant
3✔
207
        }
3✔
208
        wflow := ReindexDeploymentWorkflow{
3✔
209
                RequestID:    requestid.FromContext(ctx),
3✔
210
                TenantID:     tenantID,
3✔
211
                DeviceID:     device,
3✔
212
                DeploymentID: deployment,
3✔
213
                ID:           id,
3✔
214
                Service:      ServiceDeployments,
3✔
215
        }
3✔
216
        payload, _ := json.Marshal(wflow)
3✔
217
        req, err := http.NewRequestWithContext(ctx,
3✔
218
                "POST",
3✔
219
                c.baseURL+reindexReportingDeploymentURL,
3✔
220
                bytes.NewReader(payload),
3✔
221
        )
3✔
222
        if err != nil {
3✔
223
                return errors.Wrap(err, "workflows: error preparing HTTP request")
×
224
        }
×
225

226
        req.Header.Set("Content-Type", "application/json")
3✔
227

3✔
228
        rsp, err := c.httpClient.Do(req)
3✔
229
        if err != nil {
3✔
230
                return errors.Wrap(err, "workflows: failed to trigger reporting reindex deployment")
×
231
        }
×
232
        defer rsp.Body.Close()
3✔
233

3✔
234
        if rsp.StatusCode < 300 {
4✔
235
                return nil
1✔
236
        }
1✔
237

238
        if rsp.StatusCode == http.StatusNotFound {
3✔
239
                workflowURIparts := strings.Split(reindexReportingDeploymentURL, "/")
1✔
240
                workflowName := workflowURIparts[len(workflowURIparts)-1]
1✔
241
                return errors.New(`workflows: workflow "` + workflowName + `" not defined`)
1✔
242
        }
1✔
243

244
        return errors.Errorf(
1✔
245
                "workflows: unexpected HTTP status from workflows service: %s",
1✔
246
                rsp.Status,
1✔
247
        )
1✔
248
}
249

250
func (c *client) StartReindexReportingDeploymentBatch(ctx context.Context,
251
        info []DeviceDeploymentShortInfo) error {
3✔
252
        if _, ok := ctx.Deadline(); !ok {
6✔
253
                var cancel context.CancelFunc
3✔
254
                ctx, cancel = context.WithTimeout(ctx, defaultTimeout)
3✔
255
                defer cancel()
3✔
256
        }
3✔
257
        tenantID := ""
3✔
258
        if ident := identity.FromContext(ctx); ident != nil {
6✔
259
                tenantID = ident.Tenant
3✔
260
        }
3✔
261
        reqID := requestid.FromContext(ctx)
3✔
262
        wflows := make([]ReindexDeploymentWorkflow, len(info))
3✔
263
        for i, d := range info {
9✔
264
                wflows[i] = ReindexDeploymentWorkflow{
6✔
265
                        RequestID:    reqID,
6✔
266
                        TenantID:     tenantID,
6✔
267
                        DeviceID:     d.DeviceID,
6✔
268
                        DeploymentID: d.DeploymentID,
6✔
269
                        ID:           d.ID,
6✔
270
                        Service:      ServiceDeployments,
6✔
271
                }
6✔
272
        }
6✔
273
        payload, _ := json.Marshal(wflows)
3✔
274
        req, err := http.NewRequestWithContext(ctx,
3✔
275
                "POST",
3✔
276
                c.baseURL+reindexReportingDeploymentBatchURL,
3✔
277
                bytes.NewReader(payload),
3✔
278
        )
3✔
279
        if err != nil {
3✔
280
                return errors.Wrap(err, "workflows: error preparing HTTP request")
×
281
        }
×
282

283
        req.Header.Set("Content-Type", "application/json")
3✔
284

3✔
285
        rsp, err := c.httpClient.Do(req)
3✔
286
        if err != nil {
3✔
287
                return errors.Wrap(err, "workflows: failed to trigger reporting reindex deployment")
×
288
        }
×
289
        defer rsp.Body.Close()
3✔
290

3✔
291
        if rsp.StatusCode < 300 {
4✔
292
                return nil
1✔
293
        }
1✔
294

295
        if rsp.StatusCode == http.StatusNotFound {
3✔
296
                workflowURIparts := strings.Split(reindexReportingDeploymentURL, "/")
1✔
297
                workflowName := workflowURIparts[len(workflowURIparts)-1]
1✔
298
                return errors.New(`workflows: workflow "` + workflowName + `" not defined`)
1✔
299
        }
1✔
300

301
        return errors.Errorf(
1✔
302
                "workflows: unexpected HTTP status from workflows service: %s",
1✔
303
                rsp.Status,
1✔
304
        )
1✔
305
}
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