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

mendersoftware / deployments / 1072216342

14 Nov 2023 12:49PM UTC coverage: 79.663% (+1.8%) from 77.876%
1072216342

Pull #961

gitlab-ci

kjaskiewiczz
test(mongo): move release related tests to separate file

Changelog: Title
Ticket: None

Signed-off-by: Krzysztof Jaskiewicz <krzysztof.jaskiewicz@northern.tech>
Pull Request #961: add support for bulk removal of releases by names

86 of 98 new or added lines in 6 files covered. (87.76%)

11 existing lines in 1 file now uncovered.

8046 of 10100 relevant lines covered (79.66%)

33.85 hits per line

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

87.57
/api/http/api_deployments_releases.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 http
16

17
import (
18
        "encoding/json"
19
        "net/http"
20
        "strconv"
21

22
        "github.com/ant0ine/go-json-rest/rest"
23
        "github.com/pkg/errors"
24

25
        "github.com/mendersoftware/go-lib-micro/log"
26
        "github.com/mendersoftware/go-lib-micro/requestid"
27
        "github.com/mendersoftware/go-lib-micro/requestlog"
28
        "github.com/mendersoftware/go-lib-micro/rest_utils"
29

30
        "github.com/mendersoftware/deployments/app"
31
        "github.com/mendersoftware/deployments/model"
32
)
33

34
type listReleasesVersion int64
35

36
const (
37
        listReleasesV1 listReleasesVersion = iota
38
        listReleasesV2
39
)
40

41
// Errors
42
var (
43
        ErrReleaseNameNotProvided        = errors.New("at least one release name has to be provided")
44
        ErrReleaseUsedInActiveDeployment = errors.New("release(s) used in active deployment")
45
)
46

47
func redactReleaseName(r *rest.Request) {
21✔
48
        q := r.URL.Query()
21✔
49
        if q.Get(ParamName) != "" {
27✔
50
                q.Set(ParamName, Redacted)
6✔
51
                r.URL.RawQuery = q.Encode()
6✔
52
        }
6✔
53
}
54

55
func (d *DeploymentsApiHandlers) GetReleases(w rest.ResponseWriter, r *rest.Request) {
5✔
56
        l := requestlog.GetRequestLogger(r)
5✔
57

5✔
58
        defer redactReleaseName(r)
5✔
59
        filter := getReleaseOrImageFilter(r, listReleasesV1, false)
5✔
60
        releases, _, err := d.store.GetReleases(r.Context(), filter)
5✔
61
        if err != nil {
6✔
62
                d.view.RenderInternalError(w, r, err, l)
1✔
63
                return
1✔
64
        }
1✔
65

66
        d.view.RenderSuccessGet(w, model.ConvertReleasesToV1(releases))
4✔
67
}
68

69
func (d *DeploymentsApiHandlers) listReleases(w rest.ResponseWriter, r *rest.Request,
70
        version listReleasesVersion) {
9✔
71
        l := requestlog.GetRequestLogger(r)
9✔
72

9✔
73
        defer redactReleaseName(r)
9✔
74
        filter := getReleaseOrImageFilter(r, version, true)
9✔
75
        releases, totalCount, err := d.store.GetReleases(r.Context(), filter)
9✔
76
        if err != nil {
11✔
77
                d.view.RenderInternalError(w, r, err, l)
2✔
78
                return
2✔
79
        }
2✔
80

81
        hasNext := totalCount > int(filter.Page*filter.PerPage)
7✔
82
        links := rest_utils.MakePageLinkHdrs(r, uint64(filter.Page), uint64(filter.PerPage), hasNext)
7✔
83
        for _, l := range links {
14✔
84
                w.Header().Add("Link", l)
7✔
85
        }
7✔
86
        w.Header().Add(hdrTotalCount, strconv.Itoa(totalCount))
7✔
87

7✔
88
        if version == listReleasesV1 {
11✔
89
                d.view.RenderSuccessGet(w, model.ConvertReleasesToV1(releases))
4✔
90
        } else {
8✔
91
                d.view.RenderSuccessGet(w, releases)
4✔
92
        }
4✔
93
}
94

95
func (d *DeploymentsApiHandlers) ListReleases(w rest.ResponseWriter, r *rest.Request) {
5✔
96
        d.listReleases(w, r, listReleasesV1)
5✔
97
}
5✔
98

99
func (d *DeploymentsApiHandlers) ListReleasesV2(w rest.ResponseWriter, r *rest.Request) {
5✔
100
        d.listReleases(w, r, listReleasesV2)
5✔
101
}
5✔
102

103
func (d *DeploymentsApiHandlers) PatchRelease(w rest.ResponseWriter, r *rest.Request) {
4✔
104
        ctx := r.Context()
4✔
105
        l := log.FromContext(ctx)
4✔
106

4✔
107
        releaseName := r.PathParam(ParamName)
4✔
108
        if releaseName == "" {
4✔
109
                err := errors.New("path parameter 'release_name' cannot be empty")
×
110
                rest_utils.RestErrWithLog(w, r, l, err, http.StatusNotFound)
×
111
                return
×
112
        }
×
113

114
        var release model.ReleasePatch
4✔
115
        dec := json.NewDecoder(r.Body)
4✔
116
        if err := dec.Decode(&release); err != nil {
4✔
117
                rest_utils.RestErrWithLog(w, r, l,
×
118
                        errors.WithMessage(err,
×
119
                                "malformed JSON in request body"),
×
120
                        http.StatusBadRequest)
×
121
                return
×
122
        }
×
123
        if err := release.Validate(); err != nil {
5✔
124
                rest_utils.RestErrWithLog(w, r, l,
1✔
125
                        errors.WithMessage(err,
1✔
126
                                "invalid request body"),
1✔
127
                        http.StatusBadRequest)
1✔
128
                return
1✔
129
        }
1✔
130

131
        err := d.app.UpdateRelease(ctx, releaseName, release)
3✔
132
        if err != nil {
4✔
133
                status := http.StatusInternalServerError
1✔
134
                if errors.Is(err, app.ErrReleaseNotFound) {
1✔
135
                        status = http.StatusNotFound
×
136
                } else if errors.Is(err, model.ErrTooManyUniqueTags) {
1✔
137
                        status = http.StatusConflict
×
138
                }
×
139
                rest_utils.RestErrWithLog(w, r, l, err, status)
1✔
140
                return
1✔
141
        }
142

143
        w.WriteHeader(http.StatusNoContent)
2✔
144
}
145

146
func (d *DeploymentsApiHandlers) PutReleaseTags(
147
        w rest.ResponseWriter,
148
        r *rest.Request,
149
) {
8✔
150
        ctx := r.Context()
8✔
151
        l := log.FromContext(ctx)
8✔
152

8✔
153
        releaseName := r.PathParam(ParamName)
8✔
154
        if releaseName == "" {
9✔
155
                err := errors.New("path parameter 'release_name' cannot be empty")
1✔
156
                rest_utils.RestErrWithLog(w, r, l, err, http.StatusNotFound)
1✔
157
                return
1✔
158
        }
1✔
159

160
        var tags model.Tags
7✔
161
        dec := json.NewDecoder(r.Body)
7✔
162
        if err := dec.Decode(&tags); err != nil {
8✔
163
                rest_utils.RestErrWithLog(w, r, l,
1✔
164
                        errors.WithMessage(err,
1✔
165
                                "malformed JSON in request body"),
1✔
166
                        http.StatusBadRequest)
1✔
167
                return
1✔
168
        }
1✔
169
        if err := tags.Validate(); err != nil {
7✔
170
                rest_utils.RestErrWithLog(w, r, l,
1✔
171
                        errors.WithMessage(err,
1✔
172
                                "invalid request body"),
1✔
173
                        http.StatusBadRequest)
1✔
174
                return
1✔
175
        }
1✔
176

177
        err := d.app.ReplaceReleaseTags(ctx, releaseName, tags)
5✔
178
        if err != nil {
8✔
179
                status := http.StatusInternalServerError
3✔
180
                if errors.Is(err, app.ErrReleaseNotFound) {
4✔
181
                        status = http.StatusNotFound
1✔
182
                } else if errors.Is(err, model.ErrTooManyUniqueTags) {
4✔
183
                        status = http.StatusConflict
1✔
184
                }
1✔
185
                rest_utils.RestErrWithLog(w, r, l, err, status)
3✔
186
                return
3✔
187
        }
188

189
        w.WriteHeader(http.StatusNoContent)
2✔
190
}
191

192
func (d *DeploymentsApiHandlers) GetReleaseTagKeys(
193
        w rest.ResponseWriter,
194
        r *rest.Request,
195
) {
3✔
196
        ctx := r.Context()
3✔
197
        l := log.FromContext(ctx)
3✔
198

3✔
199
        tags, err := d.app.ListReleaseTags(ctx)
3✔
200
        if err != nil {
5✔
201
                rest_utils.RestErrWithLog(w, r, l, err, http.StatusInternalServerError)
2✔
202
                return
2✔
203
        }
2✔
204

205
        w.WriteHeader(http.StatusOK)
1✔
206
        err = w.WriteJson(tags)
1✔
207
        if err != nil {
1✔
208
                l.Errorf("failed to serialize JSON response: %s", err.Error())
×
209
        }
×
210
}
211

212
func (d *DeploymentsApiHandlers) GetReleasesUpdateTypes(
213
        w rest.ResponseWriter,
214
        r *rest.Request,
215
) {
3✔
216
        ctx := r.Context()
3✔
217
        l := log.FromContext(ctx)
3✔
218

3✔
219
        updateTypes, err := d.app.GetReleasesUpdateTypes(ctx)
3✔
220
        if err != nil {
4✔
221
                rest_utils.RestErrWithLog(w, r, l, err, http.StatusInternalServerError)
1✔
222
                return
1✔
223
        }
1✔
224

225
        w.WriteHeader(http.StatusOK)
2✔
226
        err = w.WriteJson(updateTypes)
2✔
227
        if err != nil {
2✔
228
                l.Errorf("failed to serialize JSON response: %s", err.Error())
×
229
        }
×
230
}
231

232
func (d *DeploymentsApiHandlers) DeleteReleases(
233
        w rest.ResponseWriter,
234
        r *rest.Request,
235
) {
3✔
236
        ctx := r.Context()
3✔
237
        l := log.FromContext(ctx)
3✔
238

3✔
239
        names := r.URL.Query()[ParamName]
3✔
240

3✔
241
        if len(names) == 0 {
4✔
242
                rest_utils.RestErrWithLog(w, r, l,
1✔
243
                        ErrReleaseNameNotProvided,
1✔
244
                        http.StatusBadRequest)
1✔
245
                return
1✔
246
        }
1✔
247

248
        ids, err := d.app.DeleteReleases(ctx, names)
2✔
249
        if err != nil {
2✔
NEW
250
                rest_utils.RestErrWithLog(w, r, l, err, http.StatusInternalServerError)
×
NEW
251
                return
×
NEW
252
        }
×
253

254
        if len(ids) > 0 {
3✔
255
                w.WriteHeader(http.StatusConflict)
1✔
256
                deleteErr := model.ReleasesDeleteError{
1✔
257
                        Error:             ErrReleaseUsedInActiveDeployment.Error(),
1✔
258
                        RequestID:         requestid.GetReqId(r),
1✔
259
                        ActiveDeployments: ids,
1✔
260
                }
1✔
261
                err = w.WriteJson(deleteErr)
1✔
262
                if err != nil {
1✔
NEW
263
                        l.Errorf("failed to serialize JSON response: %s", err.Error())
×
NEW
264
                }
×
265
                return
1✔
266
        }
267

268
        w.WriteHeader(http.StatusNoContent)
1✔
269
}
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