• 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

77.14
/backend/services/deployments/model/release.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 model
16

17
import (
18
        "encoding/json"
19
        "errors"
20
        "fmt"
21
        "strconv"
22
        "strings"
23
        "time"
24
        "unicode"
25
)
26

27
const (
28
        TagsMaxPerRelease = 20  // Maximum number of tags per release.
29
        TagsMaxUnique     = 100 // Maximum number of unique tags.
30
)
31

32
var (
33
        ErrTooManyTags = errors.New(
34
                "the total number of tags per exceeded maximum of " +
35
                        strconv.Itoa(TagsMaxPerRelease),
36
        )
37
        ErrTooManyUniqueTags = errors.New(
38
                "the total number of unique tags (" +
39
                        strconv.Itoa(TagsMaxUnique) +
40
                        ") has been exceeded",
41
        )
42
)
43

44
type Tags []Tag
45

46
func (tags Tags) Validate() (err error) {
1✔
47
        if len(tags) > TagsMaxPerRelease {
2✔
48
                return ErrTooManyTags
1✔
49
        }
1✔
50
        for _, tag := range tags {
2✔
51
                if err = tag.Validate(); err != nil {
2✔
52
                        return err
1✔
53
                }
1✔
54
        }
55
        return nil
×
56
}
57

58
func (tags Tags) MarshalJSON() ([]byte, error) {
1✔
59
        if len(tags) == 0 {
2✔
60
                return []byte{'[', ']'}, nil
1✔
61
        }
1✔
62
        return json.Marshal([]Tag(tags))
×
63
}
64

65
func (tags *Tags) Dedup() {
1✔
66
        // Deduplicate tags:
1✔
67
        set := make(map[Tag]struct{})
1✔
68
        result := make(Tags, 0, len(*tags))
1✔
69
        for _, item := range *tags {
2✔
70
                if _, exists := set[item]; !exists {
2✔
71
                        set[item] = struct{}{}
1✔
72
                        result = append(result, item)
1✔
73
                }
1✔
74
        }
75
        *tags = result
1✔
76
}
77

78
func (tags *Tags) UnmarshalJSON(b []byte) error {
1✔
79
        err := json.Unmarshal(b, (*[]Tag)(tags))
1✔
80
        if err != nil {
2✔
81
                return err
1✔
82
        }
1✔
83
        tags.Dedup()
1✔
84
        return nil
1✔
85
}
86

87
type Tag string
88

89
const (
90
        TagMaxLength = 1024
91
)
92

93
var (
94
        ErrTagEmpty   = errors.New("tag cannot be empty")
95
        ErrTagTooLong = errors.New("tag must be less than " +
96
                strconv.Itoa(TagMaxLength) +
97
                " characters")
98
)
99

100
type InvalidCharacterError struct {
101
        Source string
102
        Char   rune
103
}
104

105
func (err *InvalidCharacterError) Error() string {
×
106
        return fmt.Sprintf(`invalid character '%c' in string "%s"`, err.Char, err.Source)
×
107
}
×
108

109
func (tag Tag) Validate() error {
1✔
110
        if len(tag) < 1 {
2✔
111
                return ErrTagEmpty
1✔
112
        } else if len(tag) > TagMaxLength {
3✔
113
                return ErrTagTooLong
1✔
114
        }
1✔
115
        for _, c := range tag { // [A-Za-z0-9-_.]
2✔
116
                if c >= 'A' && c <= 'Z' {
2✔
117
                        continue
1✔
118
                } else if c >= 'a' && c <= 'z' {
2✔
119
                        continue
1✔
120
                } else if c >= '0' && c <= '9' {
2✔
121
                        continue
1✔
122
                } else if c == '-' || c == '_' || c == '.' {
2✔
123
                        continue
1✔
124
                } else {
1✔
125
                        return &InvalidCharacterError{
1✔
126
                                Source: string(tag),
1✔
127
                                Char:   c,
1✔
128
                        }
1✔
129
                }
1✔
130
        }
131
        return nil
1✔
132
}
133

134
func (tag *Tag) UnmarshalJSON(b []byte) error {
1✔
135
        // Convert tag to lower case
1✔
136
        var s string
1✔
137
        err := json.Unmarshal(b, &s)
1✔
138
        if err != nil {
1✔
139
                return err
×
140
        }
×
141
        *tag = Tag(strings.ToLower(s))
1✔
142
        return nil
1✔
143
}
144

145
type Notes string
146

147
var (
148
        NotesLengthMaximumCharacters = 1024
149

150
        ErrReleaseNotesTooLong  = errors.New("release notes too long")
151
        ErrCharactersNotAllowed = errors.New("release notes contain characters which are not allowed")
152
)
153

154
type InvalidCharError struct {
155
        Offset int
156
        Char   byte
157
}
158

159
func (err *InvalidCharError) Error() string {
1✔
160
        return fmt.Sprintf(`invalid character '%c' at offset %d`, err.Char, err.Offset)
1✔
161
}
1✔
162

163
func IsNotGraphic(r rune) bool {
1✔
164
        return !unicode.IsGraphic(r)
1✔
165
}
1✔
166

167
func (n Notes) Validate() error {
1✔
168
        length := len(n)
1✔
169
        if length > NotesLengthMaximumCharacters {
2✔
170
                return ErrReleaseNotesTooLong
1✔
171
        }
1✔
172
        if i := strings.IndexFunc(string(n), IsNotGraphic); i > 0 {
2✔
173
                return &InvalidCharError{
1✔
174
                        Char:   n[i],
1✔
175
                        Offset: i,
1✔
176
                }
1✔
177
        }
1✔
178

UNCOV
179
        return nil
×
180
}
181

182
type Release struct {
183
        Name           string     `json:"name" bson:"_id"`
184
        Modified       *time.Time `json:"modified,omitempty" bson:"modified,omitempty"`
185
        Artifacts      []Image    `json:"artifacts" bson:"artifacts"`
186
        ArtifactsCount int        `json:"artifacts_count" bson:"artifacts_count"`
187
        Tags           Tags       `json:"tags" bson:"tags,omitempty"`
188
        Notes          Notes      `json:"notes" bson:"notes,omitempty"`
189
}
190

191
type ReleaseV1 struct {
192
        Name           string     `json:"Name"`
193
        Modified       *time.Time `json:"Modified,omitempty"`
194
        Artifacts      []Image    `json:"Artifacts"`
195
        ArtifactsCount int        `json:"ArtifactsCount"`
196
        Tags           Tags       `json:"tags"`
197
        Notes          Notes      `json:"notes"`
198
}
199

200
func ConvertReleasesToV1(releases []Release) []ReleaseV1 {
1✔
201
        realesesV1 := make([]ReleaseV1, len(releases))
1✔
202
        for i, release := range releases {
2✔
203
                realesesV1[i] = ReleaseV1(release)
1✔
204
        }
1✔
205
        return realesesV1
1✔
206
}
207

208
type ReleasePatch struct {
209
        Notes Notes `json:"notes" bson:"notes,omitempty"`
210
}
211

UNCOV
212
func (r ReleasePatch) Validate() error {
×
UNCOV
213
        return r.Notes.Validate()
×
UNCOV
214
}
×
215

216
type ReleaseOrImageFilter struct {
217
        Name        string   `json:"name"`
218
        Description string   `json:"description"`
219
        DeviceType  string   `json:"device_type"`
220
        Tags        []string `json:"tags"`
221
        UpdateType  string   `json:"update_type"`
222
        Page        int      `json:"page"`
223
        PerPage     int      `json:"per_page"`
224
        Sort        string   `json:"sort"`
225
}
226

227
type DirectUploadMetadata struct {
228
        Size    int64    `json:"size,omitempty" valid:"-"`
229
        Updates []Update `json:"updates" valid:"-"`
230
}
231

232
const maxDirectUploadUpdatesMetadata = 1024
233

UNCOV
234
func (m DirectUploadMetadata) Validate() error {
×
UNCOV
235
        if len(m.Updates) < 1 {
×
236
                return errors.New("empty updates update")
×
237
        }
×
UNCOV
238
        if len(m.Updates) > maxDirectUploadUpdatesMetadata {
×
239
                return errors.New("updates array too large")
×
240
        }
×
UNCOV
241
        for _, f := range m.Updates {
×
UNCOV
242
                err := f.Validate()
×
UNCOV
243
                if err != nil {
×
244
                        return err
×
245
                }
×
246
        }
UNCOV
247
        return nil
×
248
}
249

250
type ReleasesDeleteError struct {
251
        Error             string   `json:"error"`
252
        RequestID         string   `json:"request_id"`
253
        ActiveDeployments []string `json:"active_deployments"`
254
}
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