-
Notifications
You must be signed in to change notification settings - Fork 2.4k
/
containers_top.go
131 lines (115 loc) · 3.28 KB
/
containers_top.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
//go:build !remote
package compat
import (
"encoding/json"
"fmt"
"net/http"
"strings"
"time"
"github.com/containers/podman/v5/libpod"
"github.com/containers/podman/v5/pkg/api/handlers"
"github.com/containers/podman/v5/pkg/api/handlers/utils"
api "github.com/containers/podman/v5/pkg/api/types"
"github.com/sirupsen/logrus"
)
func TopContainer(w http.ResponseWriter, r *http.Request) {
runtime := r.Context().Value(api.RuntimeKey).(*libpod.Runtime)
decoder := utils.GetDecoder(r)
var psArgs []string
if !utils.IsLibpodRequest(r) {
psArgs = []string{"-ef"}
}
query := struct {
Delay int `schema:"delay"`
PsArgs []string `schema:"ps_args"`
Stream bool `schema:"stream"`
}{
Delay: 5,
PsArgs: psArgs,
}
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
utils.Error(w, http.StatusBadRequest, fmt.Errorf("failed to parse parameters for %s: %w", r.URL.String(), err))
return
}
if query.Delay < 1 {
utils.Error(w, http.StatusBadRequest, fmt.Errorf("\"delay\" parameter of value %d < 1", query.Delay))
return
}
name := utils.GetName(r)
c, err := runtime.LookupContainer(name)
if err != nil {
utils.ContainerNotFound(w, name, err)
return
}
statusWritten := false
w.Header().Set("Content-Type", "application/json")
encoder := json.NewEncoder(w)
args := query.PsArgs
if len(args) == 1 &&
utils.IsLibpodRequest(r) {
if _, err := utils.SupportedVersion(r, "< 4.8.0"); err == nil {
// Ugly workaround for older clients which used to send arguments comma separated.
args = strings.Split(args[0], ",")
}
}
loop: // break out of for/select infinite` loop
for {
select {
case <-r.Context().Done():
break loop
default:
output, err := c.Top(args)
if err != nil {
if !statusWritten {
utils.InternalServerError(w, err)
} else {
logrus.Errorf("From %s %q : %v", r.Method, r.URL, err)
}
break loop
}
if len(output) > 0 {
body := handlers.ContainerTopOKBody{}
body.Titles = utils.PSTitles(output[0])
for i := range body.Titles {
body.Titles[i] = strings.TrimSpace(body.Titles[i])
}
for _, line := range output[1:] {
process := strings.FieldsFunc(line, func(r rune) bool {
return r == ' ' || r == '\t'
})
if len(process) > len(body.Titles) {
// Docker assumes the last entry is *always* command
// Which can include spaces.
// All other descriptors are assumed to NOT include extra spaces.
// So combine any extras.
cmd := strings.Join(process[len(body.Titles)-1:], " ")
var finalProc []string
finalProc = append(finalProc, process[:len(body.Titles)-1]...)
finalProc = append(finalProc, cmd)
body.Processes = append(body.Processes, finalProc)
} else {
body.Processes = append(body.Processes, process)
}
}
if err := encoder.Encode(body); err != nil {
if !statusWritten {
utils.InternalServerError(w, err)
} else {
logrus.Errorf("From %s %q : %v", r.Method, r.URL, err)
}
break loop
}
// after the first write we can no longer send a different status code
statusWritten = true
if f, ok := w.(http.Flusher); ok {
f.Flush()
}
}
if query.Stream {
time.Sleep(time.Duration(query.Delay) * time.Second)
} else {
break loop
}
}
}
}