Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
gitpod-io
GitHub Repository: gitpod-io/gitpod
Path: blob/main/components/ws-daemon/cmd/run.go
2498 views
1
// Copyright (c) 2020 Gitpod GmbH. All rights reserved.
2
// Licensed under the GNU Affero General Public License (AGPL).
3
// See License.AGPL.txt in the project root for license information.
4
5
package cmd
6
7
import (
8
"context"
9
"os"
10
"os/exec"
11
"syscall"
12
"time"
13
14
"golang.org/x/xerrors"
15
"k8s.io/klog/v2"
16
17
"github.com/bombsimon/logrusr/v2"
18
"github.com/heptiolabs/healthcheck"
19
"github.com/spf13/cobra"
20
ctrl "sigs.k8s.io/controller-runtime"
21
22
"github.com/gitpod-io/gitpod/common-go/baseserver"
23
"github.com/gitpod-io/gitpod/common-go/log"
24
"github.com/gitpod-io/gitpod/common-go/watch"
25
"github.com/gitpod-io/gitpod/ws-daemon/pkg/config"
26
"github.com/gitpod-io/gitpod/ws-daemon/pkg/daemon"
27
)
28
29
const grpcServerName = "wsdaemon"
30
31
// serveCmd represents the serve command
32
var runCmd = &cobra.Command{
33
Use: "run",
34
Short: "Connects to the messagebus and starts the workspace monitor",
35
36
Run: func(cmd *cobra.Command, args []string) {
37
cfg, err := config.Read(configFile)
38
if err != nil {
39
log.WithError(err).Fatal("Cannot read configuration. Maybe missing --config?")
40
}
41
42
createLVMDevices()
43
44
baseLogger := logrusr.New(log.Log)
45
ctrl.SetLogger(baseLogger)
46
// Set the logger used by k8s (e.g. client-go).
47
klog.SetLogger(baseLogger)
48
49
dmn, err := daemon.NewDaemon(cfg.Daemon)
50
if err != nil {
51
log.WithError(err).Fatal("Cannot create daemon.")
52
}
53
54
health := healthcheck.NewHandler()
55
srv, err := baseserver.New(grpcServerName,
56
baseserver.WithGRPC(&cfg.Service),
57
baseserver.WithHealthHandler(health),
58
baseserver.WithMetricsRegistry(dmn.MetricsRegistry()),
59
baseserver.WithVersion(Version),
60
)
61
if err != nil {
62
log.WithError(err).Fatal("Cannot set up server.")
63
}
64
65
health.AddReadinessCheck("ws-daemon", dmn.ReadinessProbe())
66
health.AddReadinessCheck("disk-space", freeDiskSpace(cfg.Daemon))
67
68
err = dmn.Start()
69
if err != nil {
70
log.WithError(err).Fatal("Cannot start daemon.")
71
}
72
73
ctx, cancel := context.WithCancel(context.Background())
74
defer cancel()
75
76
err = watch.File(ctx, configFile, func() {
77
ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
78
defer cancel()
79
80
cfg, err := config.Read(configFile)
81
if err != nil {
82
log.WithError(err).Warn("Cannot reload configuration.")
83
return
84
}
85
86
err = dmn.ReloadConfig(ctx, &cfg.Daemon)
87
if err != nil {
88
log.WithError(err).Warn("Cannot reload configuration.")
89
}
90
})
91
if err != nil {
92
log.WithError(err).Fatal("Cannot start watch of configuration file.")
93
}
94
95
err = syscall.Setpriority(syscall.PRIO_PROCESS, os.Getpid(), -19)
96
if err != nil {
97
log.WithError(err).Error("cannot change ws-daemon priority")
98
}
99
100
err = srv.ListenAndServe()
101
if err != nil {
102
log.WithError(err).Fatal("Failed to listen and serve.")
103
}
104
},
105
}
106
107
func init() {
108
rootCmd.AddCommand(runCmd)
109
}
110
111
// createLVMDevices creates LVM logical volume special files missing when we run inside a container.
112
// Without this devices we cannot enforce disk quotas. In installations without LVM this is a NOOP.
113
func createLVMDevices() {
114
cmd := exec.Command("/usr/sbin/vgmknodes")
115
out, err := cmd.CombinedOutput()
116
if err != nil {
117
log.WithError(err).WithField("out", string(out)).Error("cannot recreate LVM files in /dev/mapper")
118
}
119
}
120
121
func freeDiskSpace(cfg daemon.Config) func() error {
122
return func() error {
123
var diskDiskAvailable uint64 = 1
124
for _, loc := range cfg.DiskSpaceGuard.Locations {
125
if loc.Path == cfg.Content.WorkingArea {
126
diskDiskAvailable = loc.MinBytesAvail
127
}
128
}
129
130
var stat syscall.Statfs_t
131
err := syscall.Statfs(cfg.Content.WorkingArea, &stat)
132
if err != nil {
133
return xerrors.Errorf("cannot get disk space details from path %s: %w", cfg.Content.WorkingArea, err)
134
}
135
136
diskAvailable := stat.Bavail * uint64(stat.Bsize) * (1024 * 1024 * 1024) // In GB
137
if diskAvailable < diskDiskAvailable {
138
return xerrors.Errorf("not enough disk available (%v)", diskAvailable)
139
}
140
141
return nil
142
}
143
}
144
145