diff --git a/go.mod b/go.mod index 798769c2..29fdb244 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ toolchain go1.22.5 require ( github.com/BurntSushi/toml v1.4.0 github.com/dgraph-io/badger/v3 v3.2103.5 - github.com/docker/docker v27.2.1+incompatible + github.com/docker/docker v27.3.0+incompatible github.com/docker/go-connections v0.5.0 github.com/hashicorp/memberlist v0.5.1 github.com/hashicorp/serf v0.10.1 @@ -47,6 +47,7 @@ require ( github.com/cespare/xxhash v1.1.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/containerd/log v0.1.0 // indirect + github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.3.0 // indirect github.com/dgraph-io/ristretto v0.1.1 // indirect github.com/distribution/reference v0.6.0 // indirect diff --git a/go.sum b/go.sum index 9bc0283c..1d23125c 100644 --- a/go.sum +++ b/go.sum @@ -63,6 +63,8 @@ github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3 github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU= +github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= @@ -89,8 +91,8 @@ github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2 h1:tdlZCpZ/P9DhczC github.com/dgryski/go-farm v0.0.0-20190423205320-6a90982ecee2/go.mod h1:SqUrOPUnsFjfmXRMNPybcSiG0BgUW2AuFH8PAnS2iTw= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/docker v27.2.1+incompatible h1:fQdiLfW7VLscyoeYEBz7/J8soYFDZV1u6VW6gJEjNMI= -github.com/docker/docker v27.2.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v27.3.0+incompatible h1:BNb1QY6o4JdKpqwi9IB+HUYcRRrVN4aGFUTvDmWYK1A= +github.com/docker/docker v27.3.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= diff --git a/internal/daemon/daemon.go b/internal/daemon/daemon.go index 68723314..b2a981e2 100644 --- a/internal/daemon/daemon.go +++ b/internal/daemon/daemon.go @@ -3,6 +3,7 @@ package daemon import ( "context" "fmt" + systemd "github.com/coreos/go-systemd/daemon" "log/slog" "uncloud/internal/machine" ) @@ -27,5 +28,18 @@ func New(dataDir string) (*Daemon, error) { func (d *Daemon) Run(ctx context.Context) error { slog.Info("Starting machine.") + + // Notify systemd that the daemon is ready when the machine is started. + go func() { + select { + case <-d.machine.Started(): + _, err := systemd.SdNotify(false, systemd.SdNotifyReady) + if err != nil { + slog.Error("Failed to notify systemd that the daemon is ready.", "error", err) + } + case <-ctx.Done(): + } + }() + return d.machine.Run(ctx) } diff --git a/internal/machine/machine.go b/internal/machine/machine.go index 1e179401..77149cb3 100644 --- a/internal/machine/machine.go +++ b/internal/machine/machine.go @@ -37,6 +37,8 @@ type Machine struct { config Config state *State + // started is closed when the machine is ready to serve requests on the local API server. + started chan struct{} // initialised is signalled when the machine is configured as a member of a cluster. initialised chan struct{} @@ -90,6 +92,7 @@ func NewMachine(config *Config) (*Machine, error) { m := &Machine{ config: *config, state: state, + started: make(chan struct{}), initialised: make(chan struct{}, 1), cluster: c, newMachinesCh: c.WatchNewMachines(), @@ -110,6 +113,11 @@ func newGRPCServer(m pb.MachineServer, c pb.ClusterServer) *grpc.Server { return s } +// Started returns a channel that is closed when the machine is ready to serve requests on the local API server. +func (m *Machine) Started() <-chan struct{} { + return m.started +} + // IsInitialised returns true if the machine has been configured as a member of a cluster, // either by initialising a new cluster on it or joining an existing one. func (m *Machine) IsInitialised() bool { @@ -141,6 +149,7 @@ func (m *Machine) Run(ctx context.Context) error { return nil }, ) + close(m.started) // Control loop for managing the network controller. errGroup.Go( diff --git a/scripts/install.sh b/scripts/install.sh index 326d01ae..fc2e3d89 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -7,7 +7,7 @@ INSTALL_SYSTEMD_DIR=${INSTALL_SYSTEMD_DIR:-/etc/systemd/system} UNCLOUD_GITHUB_URL="https://github.com/psviderski/uncloud" UNCLOUD_VERSION=${UNCLOUD_VERSION:-latest} UNCLOUD_GROUP="uncloud" -# Add the specified Linux user to group $UNCLOUD_GROUP. +# Add the specified Linux user to group $UNCLOUD_GROUP to allow the user to run uncloud commands without sudo. UNCLOUD_GROUP_ADD_USER=${UNCLOUD_GROUP_ADD_USER:-} log() { @@ -137,8 +137,9 @@ After=network-online.target Wants=network-online.target [Service] -Type=simple +Type=notify ExecStart=${INSTALL_BIN_DIR}/uncloudd +TimeoutStartSec=15 Restart=always RestartSec=2 @@ -160,7 +161,9 @@ EOF # Reload systemd to recognize the new or updated unit file. systemctl daemon-reload systemctl enable uncloud.service - systemctl start uncloud.service + + log "⏳ Starting Uncloud machine daemon (uncloud.service)..." + systemctl restart uncloud.service log "✓ Uncloud machine daemon started." }