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

mendersoftware / deployments / 1197570064

01 Mar 2024 06:24PM UTC coverage: 52.222% (-28.4%) from 80.645%
1197570064

Pull #998

gitlab-ci

web-flow
chore: bump github.com/Azure/azure-sdk-for-go/sdk/azcore

Bumps [github.com/Azure/azure-sdk-for-go/sdk/azcore](https://github.com/Azure/azure-sdk-for-go) from 1.9.1 to 1.10.0.
- [Release notes](https://github.com/Azure/azure-sdk-for-go/releases)
- [Changelog](https://github.com/Azure/azure-sdk-for-go/blob/main/documentation/release.md)
- [Commits](https://github.com/Azure/azure-sdk-for-go/compare/sdk/azcore/v1.9.1...sdk/azcore/v1.10.0)

---
updated-dependencies:
- dependency-name: github.com/Azure/azure-sdk-for-go/sdk/azcore
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Pull Request #998: chore: bump github.com/Azure/azure-sdk-for-go/sdk/azcore from 1.9.1 to 1.10.0

5218 of 9992 relevant lines covered (52.22%)

0.55 hits per line

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

42.94
/store/mongo/migration_1_2_4.go
1
// Copyright 2021 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 mongo
16

17
import (
18
        "context"
19
        "reflect"
20

21
        "github.com/mendersoftware/go-lib-micro/log"
22
        "github.com/mendersoftware/go-lib-micro/mongo/migrate"
23
        "github.com/pkg/errors"
24
        "go.mongodb.org/mongo-driver/bson"
25
        "go.mongodb.org/mongo-driver/mongo"
26
        "go.mongodb.org/mongo-driver/mongo/options"
27

28
        "github.com/mendersoftware/deployments/model"
29
)
30

31
type migration_1_2_4 struct {
32
        client *mongo.Client
33
        db     string
34
}
35

36
func (m *migration_1_2_4) Up(from migrate.Version) error {
1✔
37
        ctx := context.Background()
1✔
38
        l := log.FromContext(ctx)
1✔
39

1✔
40
        coll := m.client.Database(m.db).Collection(CollectionDeployments)
1✔
41

1✔
42
        // update all deployments with finished timestamp to status "finished"
1✔
43
        _, _ = coll.UpdateMany(ctx, bson.M{
1✔
44
                StorageKeyDeploymentFinished: bson.M{
1✔
45
                        "$ne": nil,
1✔
46
                },
1✔
47
        }, bson.M{
1✔
48
                "$set": bson.M{
1✔
49
                        StorageKeyDeploymentStatus: model.DeploymentStatusFinished,
1✔
50
                },
1✔
51
        })
1✔
52

1✔
53
        // recalculate stats for all the non-finished deployments
1✔
54
        // we'll be iterating and modifying - sort by _id to ensure every doc is handled exactly once
1✔
55
        fopts := options.FindOptions{}
1✔
56
        fopts.SetSort(bson.M{"_id": 1})
1✔
57
        fopts.SetNoCursorTimeout(true)
1✔
58

1✔
59
        cur, err := coll.Find(ctx, bson.M{
1✔
60
                StorageKeyDeploymentFinished: bson.M{
1✔
61
                        "$eq": nil,
1✔
62
                },
1✔
63
        }, &fopts)
1✔
64
        if err != nil {
1✔
65
                return errors.Wrap(err, "failed to get deployments")
×
66
        }
×
67
        defer cur.Close(ctx)
1✔
68

1✔
69
        allstats, err := m.aggregateDeviceStatuses(ctx)
1✔
70
        if err != nil {
1✔
71
                return errors.Wrap(err, "failed to get aggregated device statuses")
×
72
        }
×
73

74
        for cur.Next(ctx) {
1✔
75
                var dep model.Deployment
×
76
                err := cur.Decode(&dep)
×
77
                if err != nil {
×
78
                        return errors.Wrap(err, "failed to get deployment")
×
79
                }
×
80
                l.Infof("processing deployment %s with stats %v", dep.Id, dep.Stats)
×
81

×
82
                var stats model.Stats
×
83
                if allstats[dep.Id] != nil {
×
84
                        stats = *allstats[dep.Id]
×
85
                } else {
×
86
                        stats = model.NewDeviceDeploymentStats()
×
87
                }
×
88
                newstats := stats
×
89
                l.Infof("computed stats: %v", dep.Stats)
×
90

×
91
                // substitute stats to recalc status with deployment.GetStatus
×
92
                dep.Stats = newstats
×
93
                status := m.getStatus(&dep)
×
94
                deviceCount, err := m.deviceCountByDeployment(ctx, dep.Id)
×
95
                if err != nil {
×
96
                        return errors.Wrapf(err, "failed to count device deployments for deployment %s", dep.Id)
×
97
                }
×
98

99
                sets := bson.M{
×
100
                        StorageKeyDeploymentStatus:     status,
×
101
                        StorageKeyDeploymentMaxDevices: deviceCount,
×
102
                }
×
103

×
104
                if !reflect.DeepEqual(newstats, dep.Stats) {
×
105
                        sets[StorageKeyDeploymentStats] = newstats
×
106
                }
×
107

108
                res, err := coll.UpdateOne(ctx, bson.M{"_id": dep.Id}, bson.M{"$set": sets})
×
109
                if err != nil {
×
110
                        return errors.Wrapf(err, "failed to update deployment %s", dep.Id)
×
111
                }
×
112

113
                if res.MatchedCount == 0 {
×
114
                        return errors.Wrapf(err, "can't find deployment for update: %s", dep.Id)
×
115
                }
×
116

117
                l.Infof("processing deployment %s: success", dep.Id)
×
118
        }
119

120
        if err := cur.Err(); err != nil {
1✔
121
                l.Warnf("cursor error after processing: %v", err)
×
122
                return err
×
123
        }
×
124

125
        // have an index on just the plain Deployment.Status field
126
        // for easy querying by status
127
        storage := NewDataStoreMongoWithClient(m.client)
1✔
128
        if err := storage.EnsureIndexes(m.db, CollectionDeployments,
1✔
129
                DeploymentStatusIndex); err != nil {
1✔
130
                return err
×
131
        }
×
132

133
        return nil
1✔
134
}
135

136
func isFinished(d *model.Deployment) bool {
×
137
        if d.Stats[model.DeviceDeploymentStatusPendingStr] == 0 &&
×
138
                d.Stats[model.DeviceDeploymentStatusDownloadingStr] == 0 &&
×
139
                d.Stats[model.DeviceDeploymentStatusInstallingStr] == 0 &&
×
140
                d.Stats[model.DeviceDeploymentStatusRebootingStr] == 0 {
×
141
                return true
×
142
        }
×
143

144
        return false
×
145
}
146

147
func isPending(d *model.Deployment) bool {
×
148
        //pending > 0, evt else == 0
×
149
        if d.Stats[model.DeviceDeploymentStatusPendingStr] > 0 &&
×
150
                d.Stats[model.DeviceDeploymentStatusDownloadingStr] == 0 &&
×
151
                d.Stats[model.DeviceDeploymentStatusInstallingStr] == 0 &&
×
152
                d.Stats[model.DeviceDeploymentStatusRebootingStr] == 0 &&
×
153
                d.Stats[model.DeviceDeploymentStatusSuccessStr] == 0 &&
×
154
                d.Stats[model.DeviceDeploymentStatusAlreadyInstStr] == 0 &&
×
155
                d.Stats[model.DeviceDeploymentStatusFailureStr] == 0 &&
×
156
                d.Stats[model.DeviceDeploymentStatusNoArtifactStr] == 0 {
×
157

×
158
                return true
×
159
        }
×
160

161
        return false
×
162
}
163

164
func (m *migration_1_2_4) getStatus(deployment *model.Deployment) model.DeploymentStatus {
×
165
        if isPending(deployment) {
×
166
                return model.DeploymentStatusPending
×
167
        } else if isFinished(deployment) {
×
168
                return model.DeploymentStatusFinished
×
169
        } else {
×
170
                return model.DeploymentStatusInProgress
×
171
        }
×
172
}
173

174
// aggregateDeviceStatuses calculates:
175
// - stats
176
// - statistics
177
// for deployment 'depId', based on individual device statuses
178
// it mirrors store.AggregateDeviceDeploymentByStatus, and freezes
179
// it's implementation in case it changes/is removed
180
// note that device statuses are the best bet as a single source of
181
// truth on deployment status (used for all GETs at the time of writing this migration)
182
func (m *migration_1_2_4) aggregateDeviceStatuses(
183
        ctx context.Context,
184
) (map[string]*model.Stats, error) {
1✔
185
        deviceDeployments := m.client.Database(m.db).Collection(CollectionDevices)
1✔
186

1✔
187
        group := bson.D{
1✔
188
                {Key: "$group", Value: bson.D{
1✔
189
                        {Key: "_id",
1✔
190
                                Value: bson.D{
1✔
191
                                        {Key: "deploymentid", Value: "$" + StorageKeyDeviceDeploymentDeploymentID},
1✔
192
                                        {Key: "status", Value: "$" + StorageKeyDeviceDeploymentStatus},
1✔
193
                                },
1✔
194
                        },
1✔
195
                        {Key: "count",
1✔
196
                                Value: bson.M{"$sum": 1}}},
1✔
197
                },
1✔
198
        }
1✔
199
        pipeline := []bson.D{
1✔
200
                group,
1✔
201
        }
1✔
202

1✔
203
        var results []struct {
1✔
204
                ID struct {
1✔
205
                        DeploymentID string `bson:"deploymentid"`
1✔
206
                        Status       model.
1✔
207
                                        DeviceDeploymentStatus `bson:"status"`
1✔
208
                } `bson:"_id"`
1✔
209
                Count int
1✔
210
        }
1✔
211

1✔
212
        cursor, err := deviceDeployments.Aggregate(ctx, pipeline)
1✔
213
        if err != nil {
1✔
214
                return nil, err
×
215
        }
×
216

217
        if err := cursor.All(ctx, &results); err != nil {
1✔
218
                if err == mongo.ErrNoDocuments {
×
219
                        return nil, nil
×
220
                }
×
221
                return nil, err
×
222
        }
223

224
        stats := make(map[string]*model.Stats)
1✔
225
        for _, res := range results {
1✔
226
                if stats[res.ID.DeploymentID] == nil {
×
227
                        raw := model.NewDeviceDeploymentStats()
×
228
                        stats[res.ID.DeploymentID] = &raw
×
229
                }
×
230
                (*stats[res.ID.DeploymentID]).Set(res.ID.Status, res.Count)
×
231
        }
232
        return stats, nil
1✔
233
}
234

235
func (m *migration_1_2_4) deviceCountByDeployment(ctx context.Context, id string) (int, error) {
×
236
        collDevs := m.client.Database(m.db).Collection(CollectionDevices)
×
237

×
238
        filter := bson.M{
×
239
                "deploymentid": id,
×
240
        }
×
241

×
242
        deviceCount, err := collDevs.CountDocuments(ctx, filter)
×
243
        if err != nil {
×
244
                return 0, err
×
245
        }
×
246

247
        return int(deviceCount), nil
×
248
}
249

250
func (m *migration_1_2_4) Version() migrate.Version {
1✔
251
        return migrate.MakeVersion(1, 2, 4)
1✔
252
}
1✔
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