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

mendersoftware / deployments / 731956009

pending completion
731956009

Pull #809

gitlab-ci

Fabio Tranchitella
feat: add support to filter device deployments by status `finished`
Pull Request #809: MEN-5911: feat: internal end-point to retrieve the device deployments history

95 of 131 new or added lines in 7 files covered. (72.52%)

1 existing line in 1 file now uncovered.

6112 of 7840 relevant lines covered (77.96%)

77.37 hits per line

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

90.91
/client/workflows/client.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 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
        defaultTimeout      = 5 * time.Second
42
)
43

44
// Client is the workflows client
45
//
46
//go:generate ../../utils/mockgen.sh
47
type Client interface {
48
        CheckHealth(ctx context.Context) error
49
        StartGenerateArtifact(
50
                ctx context.Context,
51
                multipartGenerateImageMsg *model.MultipartGenerateImageMsg,
52
        ) error
53
        StartReindexReporting(c context.Context, device string) error
54
}
55

56
// NewClient returns a new workflows client
57
func NewClient() Client {
15✔
58
        workflowsBaseURL := config.Config.GetString(dconfig.SettingWorkflows)
15✔
59
        return &client{
15✔
60
                baseURL:    workflowsBaseURL,
15✔
61
                httpClient: &http.Client{Timeout: defaultTimeout},
15✔
62
        }
15✔
63
}
15✔
64

65
type client struct {
66
        baseURL    string
67
        httpClient *http.Client
68
}
69

70
func (c *client) CheckHealth(ctx context.Context) error {
8✔
71
        var (
8✔
72
                apiErr rest_utils.ApiError
8✔
73
                client http.Client
8✔
74
        )
8✔
75

8✔
76
        if ctx == nil {
10✔
77
                ctx = context.Background()
2✔
78
        }
2✔
79
        if _, ok := ctx.Deadline(); !ok {
12✔
80
                var cancel context.CancelFunc
4✔
81
                ctx, cancel = context.WithTimeout(ctx, defaultTimeout)
4✔
82
                defer cancel()
4✔
83
        }
4✔
84
        req, _ := http.NewRequestWithContext(
8✔
85
                ctx, "GET", c.baseURL+healthURL, nil,
8✔
86
        )
8✔
87

8✔
88
        rsp, err := client.Do(req)
8✔
89
        if err != nil {
10✔
90
                return err
2✔
91
        }
2✔
92
        defer rsp.Body.Close()
6✔
93
        if rsp.StatusCode >= http.StatusOK && rsp.StatusCode < 300 {
8✔
94
                return nil
2✔
95
        }
2✔
96
        decoder := json.NewDecoder(rsp.Body)
4✔
97
        err = decoder.Decode(&apiErr)
4✔
98
        if err != nil {
6✔
99
                return errors.Errorf("health check HTTP error: %s", rsp.Status)
2✔
100
        }
2✔
101
        return &apiErr
2✔
102
}
103

104
func (c *client) StartGenerateArtifact(
105
        ctx context.Context,
106
        multipartGenerateImageMsg *model.MultipartGenerateImageMsg,
107
) error {
5✔
108
        l := log.FromContext(ctx)
5✔
109
        l.Debugf("Submit generate artifact: tenantID=%s, artifactID=%s",
5✔
110
                multipartGenerateImageMsg.TenantID, multipartGenerateImageMsg.ArtifactID)
5✔
111

5✔
112
        workflowsURL := c.baseURL + generateArtifactURL
5✔
113

5✔
114
        payload, _ := json.Marshal(multipartGenerateImageMsg)
5✔
115
        req, err := http.NewRequest("POST", workflowsURL, strings.NewReader(string(payload)))
5✔
116
        if err != nil {
5✔
117
                return err
×
118
        }
×
119

120
        res, err := c.httpClient.Do(req)
5✔
121
        if err != nil {
5✔
122
                return errors.Wrapf(err, "failed to start workflow: generate_artifact")
×
123
        }
×
124
        defer res.Body.Close()
5✔
125
        if res.StatusCode != http.StatusCreated {
7✔
126
                body, err := io.ReadAll(res.Body)
2✔
127
                if err != nil {
2✔
128
                        body = []byte("<failed to read>")
×
129
                }
×
130
                l.Errorf("generate artifact failed with status %v, response text: %s",
2✔
131
                        res.StatusCode, body)
2✔
132
                return errors.New("failed to start workflow: generate_artifact")
2✔
133
        }
134
        return nil
3✔
135
}
136

137
func (c *client) StartReindexReporting(ctx context.Context, device string) error {
8✔
138
        if _, ok := ctx.Deadline(); !ok {
16✔
139
                var cancel context.CancelFunc
8✔
140
                ctx, cancel = context.WithTimeout(ctx, defaultTimeout)
8✔
141
                defer cancel()
8✔
142
        }
8✔
143
        id := identity.FromContext(ctx)
8✔
144
        if id == nil {
10✔
145
                return errors.New("workflows: context lacking tenant identity")
2✔
146
        }
2✔
147
        wflow := ReindexWorkflow{
6✔
148
                RequestID: requestid.FromContext(ctx),
6✔
149
                TenantID:  id.Tenant,
6✔
150
                DeviceID:  device,
6✔
151
                Service:   ServiceDeployments,
6✔
152
        }
6✔
153
        payload, _ := json.Marshal(wflow)
6✔
154
        req, err := http.NewRequestWithContext(ctx,
6✔
155
                "POST",
6✔
156
                c.baseURL+reindexReportingURL,
6✔
157
                bytes.NewReader(payload),
6✔
158
        )
6✔
159
        if err != nil {
6✔
NEW
160
                return errors.Wrap(err, "workflows: error preparing HTTP request")
×
NEW
161
        }
×
162

163
        req.Header.Set("Content-Type", "application/json")
6✔
164

6✔
165
        rsp, err := c.httpClient.Do(req)
6✔
166
        if err != nil {
6✔
NEW
167
                return errors.Wrap(err, "workflows: failed to trigger reporting reindex")
×
NEW
168
        }
×
169
        defer rsp.Body.Close()
6✔
170

6✔
171
        if rsp.StatusCode < 300 {
8✔
172
                return nil
2✔
173
        }
2✔
174

175
        if rsp.StatusCode == http.StatusNotFound {
6✔
176
                workflowURIparts := strings.Split(reindexReportingURL, "/")
2✔
177
                workflowName := workflowURIparts[len(workflowURIparts)-1]
2✔
178
                return errors.New(`workflows: workflow "` + workflowName + `" not defined`)
2✔
179
        }
2✔
180

181
        return errors.Errorf(
2✔
182
                "workflows: unexpected HTTP status from workflows service: %s",
2✔
183
                rsp.Status,
2✔
184
        )
2✔
185
}
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