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

mendersoftware / mender-server / 1622978334

13 Jan 2025 03:51PM UTC coverage: 72.802% (-3.8%) from 76.608%
1622978334

Pull #300

gitlab-ci

alfrunes
fix: Deployment device count should not exceed max devices

Added a condition to skip deployments when the device count reaches max
devices.

Changelog: Title
Ticket: MEN-7847
Signed-off-by: Alf-Rune Siqveland <alf.rune@northern.tech>
Pull Request #300: fix: Deployment device count should not exceed max devices

4251 of 6164 branches covered (68.96%)

Branch coverage included in aggregate %.

0 of 18 new or added lines in 1 file covered. (0.0%)

2544 existing lines in 83 files now uncovered.

42741 of 58384 relevant lines covered (73.21%)

21.49 hits per line

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

61.98
/backend/services/deployments/client/inventory/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 inventory
16

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

25
        "github.com/pkg/errors"
26

27
        "github.com/mendersoftware/mender-server/pkg/config"
28
        "github.com/mendersoftware/mender-server/pkg/log"
29
        "github.com/mendersoftware/mender-server/pkg/rest_utils"
30

31
        dconfig "github.com/mendersoftware/mender-server/services/deployments/config"
32
        "github.com/mendersoftware/mender-server/services/deployments/model"
33
)
34

35
const (
36
        healthURL          = "/api/internal/v1/inventory/health"
37
        searchURL          = "/api/internal/v2/inventory/tenants/:tenantId/filters/search"
38
        getDeviceGroupsURL = "/api/internal/v1/inventory/tenants/:tenantId/devices/:deviceId/groups"
39

40
        defaultTimeout = 5 * time.Second
41

42
        hdrTotalCount = "X-Total-Count"
43
)
44

45
// Errors
46
var (
47
        ErrFilterNotFound = errors.New("Filter with given ID not found in the inventory.")
48
        ErrDevNotFound    = errors.New("Device with given ID not found in the inventory.")
49
)
50

51
// Client is the inventory client
52
//
53
//go:generate ../../../../utils/mockgen.sh
54
type Client interface {
55
        CheckHealth(ctx context.Context) error
56
        Search(
57
                ctx context.Context,
58
                tenantId string,
59
                searchParams model.SearchParams,
60
        ) ([]model.InvDevice, int, error)
61
        GetDeviceGroups(ctx context.Context, tenantId, deviceId string) ([]string, error)
62
}
63

64
// NewClient returns a new inventory client
65
func NewClient() Client {
1✔
66
        var timeout time.Duration
1✔
67
        baseURL := config.Config.GetString(dconfig.SettingInventoryAddr)
1✔
68
        timeoutStr := config.Config.GetString(dconfig.SettingInventoryTimeout)
1✔
69

1✔
70
        t, err := strconv.Atoi(timeoutStr)
1✔
71
        if err != nil {
2✔
72
                timeout = defaultTimeout
1✔
73
        } else {
1✔
UNCOV
74
                timeout = time.Duration(t) * time.Second
×
UNCOV
75
        }
×
76

77
        return &client{
1✔
78
                baseURL:    baseURL,
1✔
79
                httpClient: &http.Client{Timeout: timeout},
1✔
80
        }
1✔
81
}
82

83
type client struct {
84
        baseURL    string
85
        httpClient *http.Client
86
}
87

88
func (c *client) CheckHealth(ctx context.Context) error {
1✔
89
        var (
1✔
90
                apiErr rest_utils.ApiError
1✔
91
                client http.Client
1✔
92
        )
1✔
93

1✔
94
        if ctx == nil {
2✔
95
                ctx = context.Background()
1✔
96
        }
1✔
97
        if _, ok := ctx.Deadline(); !ok {
2✔
98
                var cancel context.CancelFunc
1✔
99
                ctx, cancel = context.WithTimeout(ctx, defaultTimeout)
1✔
100
                defer cancel()
1✔
101
        }
1✔
102
        req, _ := http.NewRequestWithContext(
1✔
103
                ctx, "GET", c.baseURL+healthURL, nil,
1✔
104
        )
1✔
105

1✔
106
        rsp, err := client.Do(req)
1✔
107
        if err != nil {
2✔
108
                return err
1✔
109
        }
1✔
110
        defer rsp.Body.Close()
1✔
111

1✔
112
        if rsp.StatusCode >= http.StatusOK && rsp.StatusCode < 300 {
2✔
113
                return nil
1✔
114
        }
1✔
115
        decoder := json.NewDecoder(rsp.Body)
1✔
116
        err = decoder.Decode(&apiErr)
1✔
117
        if err != nil {
2✔
118
                return errors.Errorf("health check HTTP error: %s", rsp.Status)
1✔
119
        }
1✔
120
        return &apiErr
1✔
121
}
122

123
func (c *client) Search(
124
        ctx context.Context,
125
        tenantId string,
126
        searchParams model.SearchParams,
UNCOV
127
) ([]model.InvDevice, int, error) {
×
UNCOV
128
        l := log.FromContext(ctx)
×
UNCOV
129
        l.Debugf("Search")
×
UNCOV
130

×
UNCOV
131
        repl := strings.NewReplacer(":tenantId", tenantId)
×
UNCOV
132
        url := c.baseURL + repl.Replace(searchURL)
×
UNCOV
133

×
UNCOV
134
        payload, _ := json.Marshal(searchParams)
×
UNCOV
135
        req, err := http.NewRequest("POST", url, strings.NewReader(string(payload)))
×
UNCOV
136
        if err != nil {
×
137
                return nil, -1, err
×
138
        }
×
UNCOV
139
        req.Header.Set("Content-Type", "application/json")
×
UNCOV
140

×
UNCOV
141
        rsp, err := c.httpClient.Do(req)
×
UNCOV
142
        if err != nil {
×
143
                return nil, -1, errors.Wrap(err, "search devices request failed")
×
144
        }
×
UNCOV
145
        defer rsp.Body.Close()
×
UNCOV
146

×
UNCOV
147
        if rsp.StatusCode != http.StatusOK {
×
148
                return nil, -1, errors.Errorf(
×
149
                        "search devices request failed with unexpected status %v",
×
150
                        rsp.StatusCode,
×
151
                )
×
152
        }
×
153

UNCOV
154
        devs := []model.InvDevice{}
×
UNCOV
155
        if err := json.NewDecoder(rsp.Body).Decode(&devs); err != nil {
×
156
                return nil, -1, errors.Wrap(err, "error parsing search devices response")
×
157
        }
×
158

UNCOV
159
        totalCountStr := rsp.Header.Get(hdrTotalCount)
×
UNCOV
160
        totalCount, err := strconv.Atoi(totalCountStr)
×
UNCOV
161
        if err != nil {
×
162
                return nil, -1, errors.Wrap(err, "error parsing "+hdrTotalCount+" header")
×
163
        }
×
164

UNCOV
165
        return devs, totalCount, nil
×
166
}
167

168
func (c *client) GetDeviceGroups(ctx context.Context, tenantId, deviceId string) ([]string, error) {
1✔
169
        repl := strings.NewReplacer(":tenantId", tenantId, ":deviceId", deviceId)
1✔
170
        url := c.baseURL + repl.Replace(getDeviceGroupsURL)
1✔
171

1✔
172
        if ctx == nil {
1✔
173
                ctx = context.Background()
×
174
        }
×
175
        if _, ok := ctx.Deadline(); !ok {
2✔
176
                var cancel context.CancelFunc
1✔
177
                ctx, cancel = context.WithTimeout(ctx, defaultTimeout)
1✔
178
                defer cancel()
1✔
179
        }
1✔
180
        req, err := http.NewRequestWithContext(
1✔
181
                ctx, "GET", url, nil,
1✔
182
        )
1✔
183
        if err != nil {
1✔
184
                return nil, err
×
185
        }
×
186

187
        rsp, err := c.httpClient.Do(req)
1✔
188
        if err != nil {
1✔
189
                return nil, errors.Wrap(err, "get device groups request failed")
×
190
        }
×
191
        defer rsp.Body.Close()
1✔
192

1✔
193
        if rsp.StatusCode != http.StatusOK {
2✔
194
                if rsp.StatusCode == http.StatusNotFound {
2✔
195
                        return []string{}, nil
1✔
196
                }
1✔
197
                return nil, errors.Errorf(
1✔
198
                        "get device groups request failed with unexpected status: %v",
1✔
199
                        rsp.StatusCode,
1✔
200
                )
1✔
201
        }
202

203
        res := model.DeviceGroups{}
1✔
204
        if err := json.NewDecoder(rsp.Body).Decode(&res); err != nil {
1✔
205
                return nil, errors.Wrap(err, "error parsing device groups response")
×
206
        }
×
207

208
        return res.Groups, nil
1✔
209
}
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