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

mendersoftware / mender-server / 1902448596

02 Jul 2025 01:14PM UTC coverage: 65.694% (+0.07%) from 65.622%
1902448596

Pull #773

gitlab-ci

bahaa-ghazal
fix(deviceauth): Fix the pagination logic in devices search endpoint

Ticket: MEN-8521
Changelog: Title
Signed-off-by: Bahaa Aldeen Ghazal <bahaa.ghazal@northern.tech>
Pull Request #773: fix(deviceauth): Fix the pagination logic in devices search endpoint

5 of 5 new or added lines in 1 file covered. (100.0%)

243 existing lines in 4 files now uncovered.

32420 of 49350 relevant lines covered (65.69%)

1.39 hits per line

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

68.5
/backend/services/deviceauth/main.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
package main
15

16
import (
17
        "context"
18
        "encoding/json"
19
        "fmt"
20
        "os"
21
        "strings"
22
        "time"
23

24
        "github.com/pkg/errors"
25
        "github.com/urfave/cli"
26

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

31
        cinv "github.com/mendersoftware/mender-server/services/deviceauth/client/inventory"
32
        "github.com/mendersoftware/mender-server/services/deviceauth/client/orchestrator"
33
        "github.com/mendersoftware/mender-server/services/deviceauth/client/tenant"
34
        "github.com/mendersoftware/mender-server/services/deviceauth/cmd"
35
        dconfig "github.com/mendersoftware/mender-server/services/deviceauth/config"
36
        "github.com/mendersoftware/mender-server/services/deviceauth/store/mongo"
37
)
38

39
const (
40
        cliDefaultRateLimit = 50
41
)
42

43
var appVersion = version.Get()
44

45
func main() {
2✔
46
        doMain(os.Args)
2✔
47
}
2✔
48

49
func doMain(args []string) {
2✔
50
        var configPath string
2✔
51
        var debug bool
2✔
52

2✔
53
        app := cli.NewApp()
2✔
54
        app.Usage = "Device Authentication Service"
2✔
55

2✔
56
        app.Flags = []cli.Flag{
2✔
57
                cli.StringFlag{
2✔
58
                        Name: "config",
2✔
59
                        Usage: "Configuration `FILE`." +
2✔
60
                                " Supports JSON, TOML, YAML and HCL formatted configs.",
2✔
61
                        Destination: &configPath,
2✔
62
                },
2✔
63
                cli.BoolFlag{
2✔
64
                        Name:        "debug",
2✔
65
                        Usage:       "Enable debug logging",
2✔
66
                        Destination: &debug,
2✔
67
                },
2✔
68
        }
2✔
69

2✔
70
        app.Commands = []cli.Command{
2✔
71
                {
2✔
72
                        Name:  "server",
2✔
73
                        Usage: "Run the service as a server",
2✔
74
                        Flags: []cli.Flag{
2✔
75
                                cli.BoolFlag{
2✔
76
                                        Name:  "automigrate",
2✔
77
                                        Usage: "Run database migrations before starting.",
2✔
78
                                },
2✔
79
                        },
2✔
80

2✔
81
                        Action: cmdServer,
2✔
82
                },
2✔
83
                {
2✔
84
                        Name:  "migrate",
2✔
85
                        Usage: "Run migrations and exit",
2✔
86
                        Flags: []cli.Flag{
2✔
87
                                cli.StringFlag{
2✔
88
                                        Name:  "tenant",
2✔
89
                                        Usage: "Tenant ID (optional).",
2✔
90
                                },
2✔
91
                                cli.BoolFlag{
2✔
92
                                        Name:  "list-tenants",
2✔
93
                                        Usage: "List Tenant IDs. Not performing migrations.",
2✔
94
                                },
2✔
95
                        },
2✔
96

2✔
97
                        Action: cmdMigrate,
2✔
98
                },
2✔
99
                {
2✔
100
                        Name:  "propagate-inventory-statuses",
2✔
101
                        Usage: "Push device statuses to inventory",
2✔
102
                        Flags: []cli.Flag{
2✔
103
                                cli.StringFlag{
2✔
104
                                        Name:  "tenant_id",
2✔
105
                                        Usage: "Tenant ID (optional) - propagate for just a single tenant.",
2✔
106
                                },
2✔
107
                                cli.StringFlag{
2✔
108
                                        Name:  "force-set-migration",
2✔
109
                                        Usage: "Migration version to be stored in migration_info collection.",
2✔
110
                                },
2✔
111
                                cli.BoolFlag{
2✔
112
                                        Name: "dry-run",
2✔
113
                                        Usage: "Do not perform any inventory modifications," +
2✔
114
                                                " just scan and print devices.",
2✔
115
                                },
2✔
116
                        },
2✔
117

2✔
118
                        Action: cmdPropagateStatusesInventory,
2✔
119
                },
2✔
120
                {
2✔
121
                        Name:  "propagate-inventory-id-data",
2✔
122
                        Usage: "Push device id_data to inventory",
2✔
123
                        Flags: []cli.Flag{
2✔
124
                                cli.StringFlag{
2✔
125
                                        Name:  "tenant_id",
2✔
126
                                        Usage: "Tenant ID (optional) - propagate for just a single tenant.",
2✔
127
                                },
2✔
128
                                cli.BoolFlag{
2✔
129
                                        Name: "dry-run",
2✔
130
                                        Usage: "Do not perform any inventory modifications," +
2✔
131
                                                " just scan and print devices.",
2✔
132
                                },
2✔
133
                        },
2✔
134

2✔
135
                        Action: cmdPropagateIdDataInventory,
2✔
136
                },
2✔
137
                {
2✔
138
                        Name:  "propagate-reporting",
2✔
139
                        Usage: "Trigger a reindex of all the devices in the reporting services ",
2✔
140
                        Flags: []cli.Flag{
2✔
141
                                cli.StringFlag{
2✔
142
                                        Name:  "tenant_id",
2✔
143
                                        Usage: "Tenant ID (optional) - propagate for just a single tenant.",
2✔
144
                                },
2✔
145
                                cli.UintFlag{
2✔
146
                                        Name:  "rate-limit",
2✔
147
                                        Usage: "`N`umber of reindexing batch requests per second.",
2✔
148
                                        Value: cliDefaultRateLimit,
2✔
149
                                },
2✔
150
                                cli.BoolFlag{
2✔
151
                                        Name: "dry-run",
2✔
152
                                        Usage: "Do not perform any inventory modifications," +
2✔
153
                                                " just scan and print devices.",
2✔
154
                                },
2✔
155
                        },
2✔
156

2✔
157
                        Action: cmdPropagateReporting,
2✔
158
                },
2✔
159
                {
2✔
160
                        Name:  "maintenance",
2✔
161
                        Usage: "Run maintenance operations and exit",
2✔
162
                        Flags: []cli.Flag{
2✔
163
                                cli.BoolFlag{
2✔
164
                                        Name:  "decommissioning-cleanup",
2✔
165
                                        Usage: "Cleanup devauth database from leftovers after failed decommissioning",
2✔
166
                                },
2✔
167
                                cli.StringFlag{
2✔
168
                                        Name:  "tenant",
2✔
169
                                        Usage: "Tenant ID (optional).",
2✔
170
                                },
2✔
171
                                cli.BoolFlag{
2✔
172
                                        Name: "dry-run",
2✔
173
                                        Usage: "Do not perform any modifications and serves" +
2✔
174
                                                " only as a way to inspect changes and detect if any are necessary",
2✔
175
                                },
2✔
176
                        },
2✔
177

2✔
178
                        Action: cmdMaintenance,
2✔
179
                }, {
2✔
180
                        Name:  "check-device-limits",
2✔
181
                        Usage: "Warn users if user is approaching device limit",
2✔
182
                        Description: "Loops through all tenant databases and " +
2✔
183
                                "checks if the number of devices is over a " +
2✔
184
                                "threshold of the allowed limit and sends an " +
2✔
185
                                "email asking the user to upgrade or decomission" +
2✔
186
                                "unused devices.",
2✔
187
                        Flags: []cli.Flag{
2✔
188
                                cli.Float64Flag{
2✔
189
                                        Name:  "threshold, t",
2✔
190
                                        Value: 90.0,
2✔
191
                                        Usage: "Threshold in percent (%) of " +
2✔
192
                                                "device limit that trigger " +
2✔
193
                                                "email event.",
2✔
194
                                },
2✔
195
                        },
2✔
196
                        Action: cmdCheckDeviceLimits,
2✔
197
                },
2✔
198
                {
2✔
199
                        Name:  "version",
2✔
200
                        Usage: "Show version information",
2✔
201
                        Flags: []cli.Flag{
2✔
202
                                cli.StringFlag{
2✔
203
                                        Name:  "output",
2✔
204
                                        Usage: "Output format <json|text>",
2✔
205
                                        Value: "text",
2✔
206
                                },
2✔
207
                        },
2✔
208
                        Action: func(args *cli.Context) error {
2✔
209
                                switch strings.ToLower(args.String("output")) {
×
210
                                case "text":
×
211
                                        fmt.Print(appVersion)
×
212
                                case "json":
×
213
                                        _ = json.NewEncoder(os.Stdout).Encode(appVersion)
×
214
                                default:
×
215
                                        return fmt.Errorf("Unknown output format %q", args.String("output"))
×
216
                                }
217
                                return nil
×
218
                        },
219
                },
220
        }
221

222
        app.Version = appVersion.Version
2✔
223
        app.Action = cmdServer
2✔
224
        app.Before = func(args *cli.Context) error {
4✔
225
                log.Setup(debug)
2✔
226

2✔
227
                err := config.FromConfigFile(configPath, dconfig.Defaults)
2✔
228
                if err != nil {
2✔
229
                        return cli.NewExitError(
×
230
                                fmt.Sprintf("error loading configuration: %s", err),
×
231
                                1)
×
232
                }
×
233

234
                // Enable setting config values by environment variables
235
                config.Config.SetEnvPrefix("DEVICEAUTH")
2✔
236
                config.Config.SetEnvKeyReplacer(strings.NewReplacer(".", "_", "-", "_"))
2✔
237
                config.Config.AutomaticEnv()
2✔
238

2✔
239
                return nil
2✔
240
        }
241

242
        _ = app.Run(args)
2✔
243
}
244

245
func cmdServer(args *cli.Context) error {
2✔
246
        l := log.New(log.Ctx{})
2✔
247

2✔
248
        db, err := mongo.NewDataStoreMongo(makeDataStoreConfig())
2✔
249
        if err != nil {
2✔
250
                return cli.NewExitError(
×
251
                        fmt.Sprintf("failed to connect to db: %v", err),
×
252
                        2)
×
UNCOV
253
        }
×
254

255
        if args.Bool("automigrate") {
4✔
256
                db = db.WithAutomigrate().(*mongo.DataStoreMongo)
2✔
257
        }
2✔
258

259
        if config.Config.Get(dconfig.SettingTenantAdmAddr) != "" {
2✔
260
                db = db.WithMultitenant()
×
UNCOV
261
        }
×
262

263
        ctx := context.Background()
2✔
264
        err = db.Migrate(ctx, mongo.DbVersion)
2✔
265
        if err != nil {
2✔
266
                return cli.NewExitError(
×
267
                        fmt.Sprintf("failed to run migrations: %v", err),
×
268
                        3)
×
UNCOV
269
        }
×
270

271
        l.Printf("Device Authentication Service %s starting up", args.App.Version)
2✔
272

2✔
273
        err = RunServer(config.Config)
2✔
274
        if err != nil {
2✔
275
                return cli.NewExitError(err.Error(), 4)
×
UNCOV
276
        }
×
277

278
        return nil
2✔
279
}
280

281
func cmdMigrate(args *cli.Context) error {
2✔
282
        err := cmd.Migrate(config.Config, args.String("tenant"), args.Bool("list-tenants"))
2✔
283
        if err != nil {
3✔
284
                return cli.NewExitError(err, 5)
1✔
285
        }
1✔
286
        return nil
2✔
287
}
288

289
func cmdMaintenance(args *cli.Context) error {
×
290
        err := cmd.Maintenance(
×
291
                args.Bool("decommissioning-cleanup"),
×
292
                args.String("tenant"),
×
293
                args.Bool("dry-run"),
×
294
        )
×
295
        if err != nil {
×
296
                return cli.NewExitError(err, 6)
×
297
        }
×
UNCOV
298
        return nil
×
299
}
300

301
func cmdPropagateStatusesInventory(args *cli.Context) error {
1✔
302
        db, err := mongo.NewDataStoreMongo(makeDataStoreConfig())
1✔
303
        if err != nil {
1✔
304
                return err
×
UNCOV
305
        }
×
306

307
        inv := config.Config.GetString(dconfig.SettingInventoryAddr)
1✔
308
        c := cinv.NewClient(inv, false)
1✔
309

1✔
310
        err = cmd.PropagateStatusesInventory(db,
1✔
311
                c,
1✔
312
                args.String("tenant_id"),
1✔
313
                args.String("force-set-migration"),
1✔
314
                args.Bool("dry-run"))
1✔
315
        if err != nil {
1✔
316
                return cli.NewExitError(err, 7)
×
UNCOV
317
        }
×
318
        return nil
1✔
319
}
320

321
func cmdPropagateIdDataInventory(args *cli.Context) error {
×
322
        db, err := mongo.NewDataStoreMongo(makeDataStoreConfig())
×
323
        if err != nil {
×
324
                return err
×
UNCOV
325
        }
×
326

327
        inv := config.Config.GetString(dconfig.SettingInventoryAddr)
×
328
        c := cinv.NewClient(inv, false)
×
329

×
330
        err = cmd.PropagateIdDataInventory(db,
×
331
                c,
×
332
                args.String("tenant_id"),
×
333
                args.Bool("dry-run"))
×
334
        if err != nil {
×
335
                return cli.NewExitError(err, 7)
×
336
        }
×
UNCOV
337
        return nil
×
338
}
339

340
func cmdPropagateReporting(args *cli.Context) error {
×
341
        if !config.Config.GetBool(dconfig.SettingEnableReporting) {
×
342
                return cli.NewExitError(errors.New("reporting support not enabled"), 1)
×
UNCOV
343
        }
×
344

345
        db, err := mongo.NewDataStoreMongo(makeDataStoreConfig())
×
346
        if err != nil {
×
347
                return err
×
UNCOV
348
        }
×
349

350
        wflows := orchestrator.NewClient(orchestrator.Config{
×
351
                OrchestratorAddr: config.Config.GetString(
×
352
                        dconfig.SettingOrchestratorAddr,
×
353
                ),
×
354
        })
×
355

×
356
        var requestPeriod time.Duration
×
357
        if rateLimit := args.Uint("rate-limit"); rateLimit > 0 {
×
358
                requestPeriod = time.Second / time.Duration(rateLimit)
×
UNCOV
359
        }
×
360

361
        err = cmd.PropagateReporting(
×
362
                db,
×
363
                wflows,
×
364
                args.String("tenant_id"),
×
365
                requestPeriod,
×
366
                args.Bool("dry-run"),
×
367
        )
×
368
        if err != nil {
×
369
                return cli.NewExitError(err, 7)
×
370
        }
×
UNCOV
371
        return nil
×
372
}
373

374
func makeDataStoreConfig() mongo.DataStoreMongoConfig {
2✔
375
        return mongo.DataStoreMongoConfig{
2✔
376
                ConnectionString: config.Config.GetString(dconfig.SettingDb),
2✔
377

2✔
378
                SSL:           config.Config.GetBool(dconfig.SettingDbSSL),
2✔
379
                SSLSkipVerify: config.Config.GetBool(dconfig.SettingDbSSLSkipVerify),
2✔
380

2✔
381
                Username: config.Config.GetString(dconfig.SettingDbUsername),
2✔
382
                Password: config.Config.GetString(dconfig.SettingDbPassword),
2✔
383
        }
2✔
384

2✔
385
}
2✔
386

387
func cmdCheckDeviceLimits(args *cli.Context) error {
×
388
        mgoConf := makeDataStoreConfig()
×
389
        ds, err := mongo.NewDataStoreMongo(mgoConf)
×
390
        if err != nil {
×
391
                return errors.Wrap(err, "cmd: failed to initialize DataStore client")
×
UNCOV
392
        }
×
393
        // Initialize tenantadm and workflows clients.
394
        tadm := tenant.NewClient(tenant.Config{
×
395
                TenantAdmAddr: config.Config.GetString(
×
396
                        dconfig.SettingTenantAdmAddr,
×
397
                ),
×
398
        })
×
399
        wflows := orchestrator.NewClient(orchestrator.Config{
×
400
                OrchestratorAddr: config.Config.GetString(
×
401
                        dconfig.SettingOrchestratorAddr,
×
402
                ),
×
403
        })
×
404
        return cmd.CheckDeviceLimits(
×
405
                args.Float64("threshold"),
×
406
                ds, tadm, wflows,
×
UNCOV
407
        )
×
408
}
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