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

mendersoftware / mender / 944822637

pending completion
944822637

Pull #1319

gitlab-ci

merlin-northern
fix: do a full commonInit only when needed.

Changelog: Title
Ticket: MEN-6618
Signed-off-by: Peter Grzybowski <peter@northern.tech>
Pull Request #1319: fix: do not scan bootstrap artifact if it does not exists.

34 of 53 new or added lines in 1 file covered. (64.15%)

6359 of 10927 relevant lines covered (58.2%)

30.85 hits per line

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

69.47
/cli/commands.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 cli
15

16
import (
17
        "bytes"
18
        "fmt"
19
        "io"
20
        "io/ioutil"
21
        "os"
22
        "os/signal"
23
        "path"
24
        "sort"
25
        "strings"
26
        "syscall"
27

28
        log "github.com/sirupsen/logrus"
29

30
        "github.com/pkg/errors"
31
        "github.com/urfave/cli/v2"
32

33
        "github.com/mendersoftware/mender/app"
34
        "github.com/mendersoftware/mender/client"
35
        "github.com/mendersoftware/mender/conf"
36
        "github.com/mendersoftware/mender/dbus"
37
        dev "github.com/mendersoftware/mender/device"
38
        "github.com/mendersoftware/mender/installer"
39
        "github.com/mendersoftware/mender/store"
40
        "github.com/mendersoftware/mender/system"
41
)
42

43
type logOptionsType struct {
44
        logLevel string
45
        logFile  string
46
        noSyslog bool
47
}
48

49
type runOptionsType struct {
50
        config         string
51
        fallbackConfig string
52
        dataStore      string
53
        imageFile      string
54
        keyPassphrase  string
55
        bootstrapForce bool
56
        client.Config
57
        logOptions     logOptionsType
58
        setupOptions   setupOptionsType // Options for setup subcommand
59
        rebootExitCode bool
60
}
61

62
var out io.Writer = os.Stdout
63

64
var (
65
        errArtifactNameEmpty = errors.New(
66
                "The Artifact name is empty. Please set a valid name for the Artifact!",
67
        )
68
)
69

70
func initDualRootfsDevice(config *conf.MenderConfig) installer.DualRootfsDevice {
205✔
71
        env := installer.NewEnvironment(new(system.OsCalls), config.BootUtilitiesSetActivePart,
205✔
72
                config.BootUtilitiesGetNextActivePart)
205✔
73

205✔
74
        dualRootfsDevice := installer.NewDualRootfsDevice(
205✔
75
                env, new(system.OsCalls), config.GetDeviceConfig())
205✔
76
        if dualRootfsDevice == nil {
205✔
77
                log.Info("No dual rootfs configuration present")
×
78
        } else {
205✔
79
                ap, err := dualRootfsDevice.GetActive()
205✔
80
                if err != nil {
207✔
81
                        log.Errorf("Failed to read the current active partition: %s", err.Error())
2✔
82
                } else {
205✔
83
                        log.Infof("Mender running on partition: %s", ap)
203✔
84
                }
203✔
85
        }
86

87
        return dualRootfsDevice
205✔
88
}
89

90
var SignalHandlerChan = make(chan os.Signal, 2)
91

92
func commonInit(
93
        config *conf.MenderConfig,
94
        opts *runOptionsType,
95
        initDbOnly bool,
96
) (*app.Mender, *app.MenderPieces, error) {
192✔
97

192✔
98
        tentok := config.GetTenantToken()
192✔
99

192✔
100
        stat, err := os.Stat(opts.dataStore)
192✔
101
        if os.IsNotExist(err) {
192✔
102
                // Create data directory if it does not exist.
×
103
                err = os.MkdirAll(opts.dataStore, 0700)
×
104
                if err != nil {
×
105
                        return nil, nil, err
×
106
                }
×
107
        } else if err != nil {
192✔
108
                return nil, nil, errors.Wrapf(err, "Could not stat data directory: %s", opts.dataStore)
×
109
        } else if !stat.IsDir() {
192✔
110
                return nil, nil, errors.Errorf("%s is not a directory", opts.dataStore)
×
111
        }
×
112

113
        var (
192✔
114
                ks       *store.Keystore
192✔
115
                dirstore *store.DirStore
192✔
116
                authmgr  *app.MenderAuthManager
192✔
117
        )
192✔
118
        dirstore = store.NewDirStore(opts.dataStore)
192✔
119
        dbstore := store.NewDBStore(opts.dataStore)
192✔
120
        if dbstore == nil {
192✔
121
                return nil, nil, errors.New("failed to initialize DB store")
×
122
        }
×
123

124
        var privateKey string
192✔
125
        var sslEngine string
192✔
126
        var static bool
192✔
127

192✔
128
        if !initDbOnly {
231✔
129
                if config.HttpsClient.Key != "" {
39✔
NEW
130
                        privateKey = config.HttpsClient.Key
×
NEW
131
                        sslEngine = config.HttpsClient.SSLEngine
×
NEW
132
                        static = true
×
NEW
133
                }
×
134
                if config.Security.AuthPrivateKey != "" {
39✔
NEW
135
                        privateKey = config.Security.AuthPrivateKey
×
NEW
136
                        sslEngine = config.Security.SSLEngine
×
NEW
137
                        static = true
×
NEW
138
                }
×
139
                if config.HttpsClient.Key == "" && config.Security.AuthPrivateKey == "" {
78✔
140
                        privateKey = conf.DefaultKeyFile
39✔
141
                        sslEngine = config.HttpsClient.SSLEngine
39✔
142
                        static = false
39✔
143
                }
39✔
144

145
                ks = store.NewKeystore(dirstore, privateKey, sslEngine, static, opts.keyPassphrase)
39✔
146
                if ks == nil {
39✔
NEW
147
                        return nil, nil, errors.New("failed to setup key storage")
×
NEW
148
                }
×
149

150
                authmgr = app.NewAuthManager(app.AuthManagerConfig{
39✔
151
                        AuthDataStore:  dbstore,
39✔
152
                        KeyStore:       ks,
39✔
153
                        IdentitySource: dev.NewIdentityDataGetter(),
39✔
154
                        TenantToken:    tentok,
39✔
155
                        Config:         config,
39✔
156
                })
39✔
157
                if authmgr == nil {
39✔
158
                        // close DB store explicitly
×
159
                        dbstore.Close()
×
NEW
160
                        return nil, nil, errors.New("error initializing authentication manager")
×
NEW
161
                }
×
162

163
                if config.DBus.Enabled {
78✔
164
                        api, err := dbus.GetDBusAPI()
39✔
165
                        if err != nil {
39✔
NEW
166
                                // close DB store explicitly
×
NEW
167
                                dbstore.Close()
×
NEW
168
                                return nil, nil, errors.Wrap(
×
NEW
169
                                        err,
×
NEW
170
                                        "DBus API support not available, but DBus is enabled",
×
NEW
171
                                )
×
NEW
172
                        }
×
173
                        authmgr.EnableDBus(api)
39✔
174
                }
175
        }
176

177
        mp := app.MenderPieces{
192✔
178
                Store:       dbstore,
192✔
179
                AuthManager: authmgr,
192✔
180
        }
192✔
181

192✔
182
        mp.DualRootfsDevice = initDualRootfsDevice(config)
192✔
183

192✔
184
        m, err := app.NewMender(config, mp)
192✔
185
        if err != nil {
192✔
186
                // close DB store explicitly
×
187
                dbstore.Close()
×
188
                return nil, nil, errors.Wrap(err, "error initializing mender controller")
×
189
        }
×
190

191
        return m, &mp, nil
188✔
192
}
193

194
func doHandleBootstrapArtifact(config *conf.MenderConfig, opts *runOptionsType) error {
164✔
195
        _, err := os.Stat(config.BootstrapArtifactFile)
164✔
196
        initDbOnly := false
164✔
197
        if err != nil {
317✔
198
                initDbOnly = true
153✔
199
        }
153✔
200
        controller, mp, err := commonInit(config, opts, initDbOnly)
164✔
201
        if err != nil {
164✔
202
                return err
×
203
        }
×
204

205
        // need to close DB store manually, since we're not running under a
206
        // daemonized version
207
        defer mp.Store.Close()
160✔
208

160✔
209
        return controller.HandleBootstrapArtifact(mp.Store)
160✔
210
}
211

212
func doBootstrapAuthorize(config *conf.MenderConfig, opts *runOptionsType) error {
3✔
213
        controller, mp, err := commonInit(config, opts, false)
3✔
214
        if err != nil {
3✔
215
                return err
×
216
        }
×
217

218
        // need to close DB store manually, since we're not running under a
219
        // daemonized version
220
        defer mp.Store.Close()
3✔
221

3✔
222
        authManager := mp.AuthManager
3✔
223
        if opts.bootstrapForce {
5✔
224
                authManager.ForceBootstrap()
2✔
225
        }
2✔
226

227
        if merr := authManager.Bootstrap(); merr != nil {
3✔
228
                return merr.Cause()
×
229
        }
×
230

231
        authManager.Start()
3✔
232
        defer authManager.Stop()
3✔
233

3✔
234
        _, _, err = controller.Authorize()
3✔
235

3✔
236
        return err
3✔
237
}
238

239
func getMenderDaemonPID(cmd *system.Cmd) (string, error) {
24✔
240
        buf := bytes.NewBuffer(nil)
24✔
241
        cmd.Stdout = buf
24✔
242
        err := cmd.Run()
24✔
243
        if err != nil {
24✔
244
                return "", errors.New("getMenderDaemonPID: Failed to run systemctl")
×
245
        }
×
246
        pid := strings.Trim(buf.String(), "MainPID=\n")
24✔
247
        if pid == "" || pid == "0" {
24✔
248
                return "", errors.New("could not find the PID of the mender daemon")
×
249
        }
×
250
        return pid, nil
24✔
251
}
252

253
func handleArtifactOperations(ctx *cli.Context, runOptions runOptionsType,
254
        config *conf.MenderConfig) error {
170✔
255

170✔
256
        dbstore := store.NewDBStore(runOptions.dataStore)
170✔
257
        if dbstore == nil {
170✔
258
                return errors.New("failed to initialize DB store")
×
259
        }
×
260

261
        dualRootfsDevice := initDualRootfsDevice(config)
170✔
262

170✔
263
        stateExec := dev.NewStateScriptExecutor(config)
170✔
264
        deviceManager := dev.NewDeviceManager(dualRootfsDevice, config, dbstore)
170✔
265

170✔
266
        switch ctx.Command.Name {
170✔
267
        case "show-artifact":
12✔
268
                return PrintArtifactName(deviceManager)
12✔
269

270
        case "show-provides":
90✔
271
                return PrintProvides(deviceManager)
90✔
272

273
        case "install":
53✔
274
                return app.DoStandaloneInstall(deviceManager, runOptions.imageFile,
53✔
275
                        runOptions.Config, stateExec, runOptions.rebootExitCode)
53✔
276

277
        case "commit":
11✔
278
                return app.DoStandaloneCommit(deviceManager, stateExec)
11✔
279

280
        case "rollback":
2✔
281
                return app.DoStandaloneRollback(deviceManager, stateExec)
2✔
282

283
        default:
×
284
                return errors.New("handleArtifactOperations: Should never get here")
×
285
        }
286
}
287

288
func initDaemon(config *conf.MenderConfig,
289
        opts *runOptionsType) (*app.MenderDaemon, error) {
28✔
290

28✔
291
        controller, mp, err := commonInit(config, opts, false)
28✔
292
        if err != nil {
28✔
293
                return nil, err
×
294
        }
×
295

296
        checkDemoCert()
28✔
297

28✔
298
        if opts.bootstrapForce {
28✔
299
                authManager := mp.AuthManager
×
300
                authManager.ForceBootstrap()
×
301
        }
×
302

303
        daemon, err := app.NewDaemon(config, controller, mp.Store, mp.AuthManager)
28✔
304
        if err != nil {
28✔
305
                return nil, err
×
306
        }
×
307

308
        // add logging hook; only daemon needs this
309
        log.AddHook(app.NewDeploymentLogHook(app.DeploymentLogger))
28✔
310

28✔
311
        // At the moment we don't do anything with this, just force linking to it.
28✔
312
        _, _ = dbus.GetDBusAPI()
28✔
313

28✔
314
        return daemon, nil
28✔
315
}
316

317
func checkDemoCert() {
28✔
318
        entries, err := ioutil.ReadDir(DefaultLocalTrustMenderDir)
28✔
319
        if err != nil {
28✔
320
                log.Debugf("Could not open local Mender trust store directory: %s", err.Error())
×
321
                return
×
322
        }
×
323

324
        for _, entry := range entries {
56✔
325
                if strings.HasPrefix(entry.Name(), DefaultLocalTrustMenderPrefix) {
28✔
326
                        log.Warnf("Running with demo certificate installed in trust store. This is INSECURE! "+
×
327
                                "Please remove %s if you plan to use this device in production.",
×
328
                                path.Join(DefaultLocalTrustMenderDir, entry.Name()))
×
329
                }
×
330
        }
331
}
332

333
func PrintArtifactName(device *dev.DeviceManager) error {
12✔
334
        name, err := device.GetCurrentArtifactName()
12✔
335
        if err != nil {
12✔
336
                return err
×
337
        } else if name == "" {
12✔
338
                return errArtifactNameEmpty
×
339
        }
×
340
        fmt.Fprintln(out, name)
12✔
341
        return nil
12✔
342
}
343

344
func PrintProvides(device *dev.DeviceManager) error {
90✔
345
        provides, err := device.GetProvides()
90✔
346
        if err != nil {
90✔
347
                return err
×
348
        }
×
349
        keys := make([]string, 0, len(provides))
90✔
350
        for k := range provides {
180✔
351
                keys = append(keys, k)
90✔
352
        }
90✔
353
        sort.Strings(keys)
90✔
354
        for _, key := range keys {
180✔
355
                fmt.Fprintln(out, key+"="+provides[key])
90✔
356
        }
90✔
357
        return nil
90✔
358
}
359

360
func runDaemon(d *app.MenderDaemon) error {
28✔
361
        // Handle user forcing update check.
28✔
362
        go func() {
56✔
363
                defer signal.Stop(SignalHandlerChan)
28✔
364

28✔
365
                for {
56✔
366
                        s := <-SignalHandlerChan // Block until a signal is received.
28✔
367
                        if s == syscall.SIGUSR1 {
48✔
368
                                log.Debug("SIGUSR1 signal received.")
20✔
369
                                d.ForceToState <- app.States.UpdateCheck
20✔
370
                        } else if s == syscall.SIGUSR2 {
20✔
371
                                log.Debug("SIGUSR2 signal received.")
×
372
                                d.ForceToState <- app.States.InventoryUpdate
×
373
                        }
×
374
                        d.Sctx.WakeupChan <- true
20✔
375
                        log.Debug("Sent wake up!")
20✔
376
                }
377
        }()
378
        return d.Run()
28✔
379
}
380

381
// sendSignalToProcess sends a SIGUSR{1,2} signal to the running mender daemon.
382
func sendSignalToProcess(cmdKill, cmdGetPID *system.Cmd) error {
24✔
383
        pid, err := getMenderDaemonPID(cmdGetPID)
24✔
384
        if err != nil {
24✔
385
                return errors.Wrap(err, "failed to force updateCheck")
×
386
        }
×
387
        cmdKill.Args = append(cmdKill.Args, pid)
24✔
388
        err = cmdKill.Run()
24✔
389
        if err != nil {
24✔
390
                return fmt.Errorf(
×
391
                        "updateCheck: Failed to send %s the mender process, pid: %s",
×
392
                        cmdKill.Args[len(cmdKill.Args)-1],
×
393
                        pid,
×
394
                )
×
395
        }
×
396
        return nil
24✔
397
}
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