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

mendersoftware / mender-server / 1622978334

13 Jan 2025 03:51PM UTC coverage: 72.802% (-3.8%) from 76.608%
1622978334

Pull #300

gitlab-ci

alfrunes
fix: Deployment device count should not exceed max devices

Added a condition to skip deployments when the device count reaches max
devices.

Changelog: Title
Ticket: MEN-7847
Signed-off-by: Alf-Rune Siqveland <alf.rune@northern.tech>
Pull Request #300: fix: Deployment device count should not exceed max devices

4251 of 6164 branches covered (68.96%)

Branch coverage included in aggregate %.

0 of 18 new or added lines in 1 file covered. (0.0%)

2544 existing lines in 83 files now uncovered.

42741 of 58384 relevant lines covered (73.21%)

21.49 hits per line

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

2.75
/backend/pkg/accesslog/middleware.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 accesslog
15

16
import (
17
        "context"
18
        "errors"
19
        "fmt"
20
        "net"
21
        "net/http"
22
        "os"
23
        "path"
24
        "runtime"
25
        "strconv"
26
        "strings"
27
        "time"
28

29
        "github.com/ant0ine/go-json-rest/rest"
30
        "github.com/sirupsen/logrus"
31

32
        "github.com/mendersoftware/mender-server/pkg/netutils"
33
        "github.com/mendersoftware/mender-server/pkg/requestlog"
34
)
35

36
const (
37
        StatusClientClosedConnection = 499
38

39
        // nolint:lll
40
        DefaultLogFormat = "%t %S\033[0m \033[36;1m%Dμs\033[0m \"%r\" \033[1;30m%u \"%{User-Agent}i\"\033[0m"
41
        SimpleLogFormat  = "%s %Dμs %r %u %{User-Agent}i"
42

43
        envProxyDepth = "ACCESSLOG_PROXY_DEPTH"
44
)
45

46
// AccesLogMiddleware uses logger from requestlog and adds a fixed set
47
// of fields to every accesslog records.
48
type AccessLogMiddleware struct {
49
        // Format is not used but kept for historical use.
50
        // FIXME(QA-673): Remove unused attributes and properties from package.
51
        Format AccessLogFormat // nolint:unused
52

53
        ClientIPHook func(req *http.Request) net.IP
54
        DisableLog   func(statusCode int, r *rest.Request) bool
55

56
        recorder *rest.RecorderMiddleware
57
}
58

59
func getClientIPFromEnv() func(r *http.Request) net.IP {
4✔
60
        if proxyDepthEnv, ok := os.LookupEnv(envProxyDepth); ok {
4✔
61
                proxyDepth, err := strconv.ParseUint(proxyDepthEnv, 10, 8)
×
62
                if err == nil {
×
63
                        return func(r *http.Request) net.IP {
×
64
                                return netutils.GetIPFromXFFDepth(r, int(proxyDepth))
×
65
                        }
×
66
                }
67
        }
68
        return nil
4✔
69
}
70

71
const MaxTraceback = 32
72

73
func collectTrace() string {
×
74
        var (
×
75
                trace     [MaxTraceback]uintptr
×
76
                traceback strings.Builder
×
77
        )
×
78
        // Skip 4
×
79
        // = accesslog.LogFunc
×
80
        // + accesslog.collectTrace
×
81
        // + runtime.Callers
×
82
        // + runtime.gopanic
×
83
        n := runtime.Callers(4, trace[:])
×
84
        frames := runtime.CallersFrames(trace[:n])
×
85
        for frame, more := frames.Next(); frame.PC != 0 &&
×
86
                n >= 0; frame, more = frames.Next() {
×
87
                funcName := frame.Function
×
88
                if funcName == "" {
×
89
                        fmt.Fprint(&traceback, "???\n")
×
90
                } else {
×
91
                        fmt.Fprintf(&traceback, "%s@%s:%d",
×
92
                                frame.Function,
×
93
                                path.Base(frame.File),
×
94
                                frame.Line,
×
95
                        )
×
96
                }
×
97
                if more {
×
98
                        fmt.Fprintln(&traceback)
×
99
                }
×
100
                n--
×
101
        }
102
        return traceback.String()
×
103
}
104

105
func (mw *AccessLogMiddleware) LogFunc(
106
        ctx context.Context, startTime time.Time,
UNCOV
107
        w rest.ResponseWriter, r *rest.Request) {
×
UNCOV
108
        fields := logrus.Fields{
×
UNCOV
109
                "type": r.Proto,
×
UNCOV
110
                "ts": startTime.
×
UNCOV
111
                        Truncate(time.Millisecond).
×
UNCOV
112
                        Format(time.RFC3339Nano),
×
UNCOV
113
                "method":    r.Method,
×
UNCOV
114
                "path":      r.URL.Path,
×
UNCOV
115
                "useragent": r.UserAgent(),
×
UNCOV
116
                "qs":        r.URL.RawQuery,
×
UNCOV
117
        }
×
UNCOV
118
        if mw.ClientIPHook != nil {
×
119
                fields["clientip"] = mw.ClientIPHook(r.Request)
×
120
        }
×
UNCOV
121
        lc := fromContext(ctx)
×
UNCOV
122
        if lc != nil {
×
UNCOV
123
                lc.addFields(fields)
×
UNCOV
124
        }
×
UNCOV
125
        statusCode, _ := r.Env["STATUS_CODE"].(int)
×
UNCOV
126
        select {
×
127
        case <-ctx.Done():
×
128
                if errors.Is(ctx.Err(), context.Canceled) {
×
129
                        statusCode = StatusClientClosedConnection
×
130
                }
×
UNCOV
131
        default:
×
132
        }
133

UNCOV
134
        if panic := recover(); panic != nil {
×
135
                trace := collectTrace()
×
136
                fields["panic"] = panic
×
137
                fields["trace"] = trace
×
138
                // Wrap in recorder middleware to make sure the response is recorded
×
139
                mw.recorder.MiddlewareFunc(func(w rest.ResponseWriter, r *rest.Request) {
×
140
                        rest.Error(w, "Internal Server Error", http.StatusInternalServerError)
×
141
                })(w, r)
×
142
                statusCode = http.StatusInternalServerError
×
UNCOV
143
        } else if mw.DisableLog != nil && mw.DisableLog(statusCode, r) {
×
UNCOV
144
                return
×
UNCOV
145
        }
×
UNCOV
146
        rspTime := time.Since(startTime)
×
UNCOV
147
        // We do not need more than 3 digit fraction
×
UNCOV
148
        if rspTime > time.Second {
×
149
                rspTime = rspTime.Round(time.Millisecond)
×
UNCOV
150
        } else if rspTime > time.Millisecond {
×
UNCOV
151
                rspTime = rspTime.Round(time.Microsecond)
×
UNCOV
152
        }
×
UNCOV
153
        fields["responsetime"] = rspTime.String()
×
UNCOV
154
        fields["byteswritten"], _ = r.Env["BYTES_WRITTEN"].(int64)
×
UNCOV
155
        fields["status"] = statusCode
×
UNCOV
156

×
UNCOV
157
        logger := requestlog.GetRequestLogger(r)
×
UNCOV
158
        var level logrus.Level = logrus.InfoLevel
×
UNCOV
159
        if statusCode >= 500 {
×
UNCOV
160
                level = logrus.ErrorLevel
×
UNCOV
161
        } else if statusCode >= 300 {
×
UNCOV
162
                level = logrus.WarnLevel
×
UNCOV
163
        }
×
UNCOV
164
        logger.WithFields(fields).
×
UNCOV
165
                Log(level)
×
166
}
167

168
// MiddlewareFunc makes AccessLogMiddleware implement the Middleware interface.
UNCOV
169
func (mw *AccessLogMiddleware) MiddlewareFunc(h rest.HandlerFunc) rest.HandlerFunc {
×
UNCOV
170
        if mw.ClientIPHook == nil {
×
UNCOV
171
                // If not set, try get it from env
×
UNCOV
172
                mw.ClientIPHook = getClientIPFromEnv()
×
UNCOV
173
        }
×
174

175
        // This middleware depends on RecorderMiddleware to work
UNCOV
176
        mw.recorder = new(rest.RecorderMiddleware)
×
UNCOV
177
        return func(w rest.ResponseWriter, r *rest.Request) {
×
UNCOV
178
                ctx := r.Request.Context()
×
UNCOV
179
                startTime := time.Now()
×
UNCOV
180
                ctx = withContext(ctx, &logContext{maxErrors: DefaultMaxErrors})
×
UNCOV
181
                r.Request = r.Request.WithContext(ctx)
×
UNCOV
182
                defer mw.LogFunc(ctx, startTime, w, r)
×
UNCOV
183
                // call the handler inside recorder context
×
UNCOV
184
                mw.recorder.MiddlewareFunc(h)(w, r)
×
UNCOV
185
        }
×
186
}
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