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

mendersoftware / mender-server / 1978029483

11 Aug 2025 02:15PM UTC coverage: 65.755% (+0.3%) from 65.495%
1978029483

Pull #860

gitlab-ci

kjaskiewiczz
docs(useradm): move API specification to single file

Changelog: Title
Ticket: QA-1094
Signed-off-by: Krzysztof Jaskiewicz <krzysztof.jaskiewicz@northern.tech>
Pull Request #860: docs(useradm): move API specification to single file

29261 of 44500 relevant lines covered (65.76%)

1.44 hits per line

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

56.76
/backend/pkg/mongo/migrate/migrator_simple.go
1
// Copyright 2024 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 migrate
15

16
import (
17
        "context"
18
        "fmt"
19
        "sort"
20
        "strings"
21

22
        "github.com/pkg/errors"
23
        "go.mongodb.org/mongo-driver/mongo"
24

25
        "github.com/mendersoftware/mender-server/pkg/log"
26
)
27

28
var (
29
        ErrNeedsMigration = "db needs migration"
30
)
31

32
func IsErrNeedsMigration(e error) bool {
×
33
        return strings.HasPrefix(e.Error(), ErrNeedsMigration)
×
34
}
×
35

36
// SimpleMigratior applies migrations by comparing `Version` of migrations
37
// passed to Apply() and already applied migrations. Only migrations that are of
38
// version higher than the last applied migration will be run. For example:
39
//
40
//        already applied migrations: 1.0.0, 1.0.1, 1.0.2
41
//        migrations in Apply(): 1.0.1, 1.0.3, 1.1.0
42
//        migrations that will be applied: 1.0.3, 1.1.0
43
type SimpleMigrator struct {
44
        Client      *mongo.Client
45
        Db          string
46
        Automigrate bool
47
}
48

49
// Apply will apply migrations, provided that Automigrate is on.
50
// After each successful migration a new migration
51
// record will be added to DB with the version of migration that was just
52
// applied. If a migration fails, Apply() returns an error and does not add a
53
// migration record (so last migration that is recorded is N-1).
54
//
55
// Apply() will log some messages when running. Logger will be extracted from
56
// context using go-lib-micro/log.LoggerContextKey as key.
57
// If Automigrate is off, the migrator will just check if the DB is up-to-date,
58
// and return with ErrNeedsMigration otherwise.
59
// Check for it with IsErrNeedsMigration.
60
func (m *SimpleMigrator) Apply(ctx context.Context, target Version, migrations []Migration) error {
9✔
61
        l := log.FromContext(ctx).F(log.Ctx{"db": m.Db})
9✔
62

9✔
63
        sort.Slice(migrations, func(i int, j int) bool {
16✔
64
                return VersionIsLess(migrations[i].Version(), migrations[j].Version())
7✔
65
        })
7✔
66

67
        applied, err := GetMigrationInfo(ctx, m.Client, m.Db)
9✔
68
        if err != nil {
9✔
69
                return errors.Wrap(err, "failed to list applied migrations")
×
70
        }
×
71

72
        // starts at 0.0.0
73
        last := Version{}
9✔
74

9✔
75
        if len(applied) != 0 {
16✔
76
                // sort applied migrations wrt. version
7✔
77
                sort.Slice(applied, func(i int, j int) bool {
11✔
78
                        return VersionIsLess(applied[i].Version, applied[j].Version)
4✔
79
                })
4✔
80
                // last version from already applied migrations
81
                last = applied[len(applied)-1].Version
7✔
82
        }
83

84
        // if Automigrate is disabled - just check
85
        // if the last applied migration is lower than the target one
86
        if !m.Automigrate {
12✔
87
                if VersionIsLess(last, target) {
3✔
88
                        return fmt.Errorf(
×
89
                                "%s: %s has version %s, needs version %s",
×
90
                                ErrNeedsMigration, m.Db, last.String(), target.String())
×
91
                } else {
3✔
92
                        return nil
3✔
93
                }
3✔
94
        }
95

96
        // try to apply migrations
97
        for _, migration := range migrations {
18✔
98
                mv := migration.Version()
9✔
99
                if VersionIsLess(target, mv) {
9✔
100
                        l.Warnf("migration to version %s skipped, target version %s is lower",
×
101
                                mv, target)
×
102
                } else if VersionIsLess(last, mv) {
18✔
103
                        // log, migration applied
9✔
104
                        l.Infof("applying migration from version %s to %s",
9✔
105
                                last, mv)
9✔
106

9✔
107
                        // apply migration
9✔
108
                        if err := migration.Up(last); err != nil {
9✔
109
                                l.Errorf("migration from %s to %s failed: %s",
×
110
                                        last, mv, err)
×
111

×
112
                                // migration from last to migration.Version() failed: err
×
113
                                return errors.Wrapf(err,
×
114
                                        "failed to apply migration from %s to %s",
×
115
                                        last, mv)
×
116
                        }
×
117

118
                        if err := UpdateMigrationInfo(ctx, mv, m.Client, m.Db); err != nil {
9✔
119

×
120
                                return errors.Wrapf(err,
×
121
                                        "failed to record migration from %s to %s",
×
122
                                        last, mv)
×
123

×
124
                        }
×
125
                        last = mv
9✔
126
                } else {
4✔
127
                        // log migration already applied
4✔
128
                        l.Infof("migration to version %s skipped", mv)
4✔
129
                }
4✔
130
        }
131

132
        // ideally, when all migrations have completed, DB should be in `target` version
133
        if VersionIsLess(last, target) {
9✔
134
                l.Warnf("last migration to version %s did not produce target version %s",
×
135
                        last, target)
×
136
                // record DB version anyways
×
137
                if err := UpdateMigrationInfo(ctx, target, m.Client, m.Db); err != nil {
×
138
                        return errors.Wrapf(err,
×
139
                                "failed to record migration from %s to %s",
×
140
                                last, target)
×
141
                }
×
142
        } else {
9✔
143
                l.Infof("DB migrated to version %s", target)
9✔
144
        }
9✔
145

146
        return nil
9✔
147
}
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