diff --git a/README.md b/README.md index 2384026..a368985 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,47 @@ To remove the agent and all viam configuration completely, run the following scr sudo /bin/sh -c "$(curl -fsSL https://storage.googleapis.com/packages.viam.com/apps/viam-agent/uninstall.sh)" ``` +## Configuration +Configuration is maintained via the "agent" section of the device's config in the viam App. Below is an example config section: +``` +{ + "agent": { + "viam-agent": { + "release_channel": "stable", + "pin_version": "1.2.3", + "pin_url": "http://example/test.binary", + "disable_subsystem": false + }, + "viam-server": { + "attributes": { + "fast_start": true + } + }, + "agent-provisioning": { + "release_channel": "stable" + }, + "agent-syscfg": { + "release_channel": "stable" + } + } +} +``` +Above there are (currently) four subsystems, `viam-agent` (the main agent program itself), `viam-server` (the core of the robot/device), `agent-provisioning` (provides early setup and network management. [Provisioning Details](https://github.com/viamrobotics/agent-provisioning) ), and `agent-syscfg` (provides various OS/system configuration tweaks [Syscfg Details](https://github.com/viamrobotics/agent-syscfg)) + +Each section primarily controls updates for that subsystem, using one of three settings, `pin_url` (highest priority), `pin_version` (checked/used only if pin_url is unset or empty), and `release_channel` (used by default, and defaults to stable, but only if `pin_url` and `pin_version` are both unset.) The example above gives all three for visual clarity, but is not actually needed. In this case, only `pin_url` would be used. + +For release channel, "stable" generally means semantically versioned releases that are tested before release, and are relatively infrequent, but will automatically upgrade when a new version is released. Using `pin_version` allows one to "lock" the subsystem to an explcit version (as provided by the release channel) no automatic upgrades will be performed until the setting is updated to a new version (or removed to revert to the release channel.) Lastly, `pin_url` can be used to point to a specific binary. Typically this is only used for testing/troubleshooting. + +The `disable_subsystem` setting can be set to true if you don't wish to use/start a particular subsystem. + +Note that only sections/settings you explicitly want to modify need to be included in the config. By default, all subsystems will use the `stable` release channel, so no configuration at all is needed to get that behavior. E.g. in the example above, viam-server will still get stable releases, as none of the update-related values are being modified, but it will ALSO use the fast_start behavior detailed below. For another example, the `agent-provisioning` or `agent-syscfg` sections could be left out entirely, and the device will use a default config for those subsystems anyway. To actually disable one, the section can be added, and `disable_subsystem` to to `true` + + +## FastStart Mode +This bypasses the normal network/online wait and update checks during inital startup, and executes viam-server as quickly as possible. Useful if you have a device that often starts when offline or on a slow connection, and having the latest version immediately after start isn't required. Note that normal, periodic update checks will continue to run afterwards. This only affects initial startup sequencing. + +To use it, set `"fast_start": true` in the attributes for the viam-server subsystem. Alternately, set `VIAM_AGENT_FASTSTART=1` in your environment. + ## Development ### Makefile Targets diff --git a/cmd/viam-agent/main.go b/cmd/viam-agent/main.go index 95c8c71..fbbc226 100644 --- a/cmd/viam-agent/main.go +++ b/cmd/viam-agent/main.go @@ -6,6 +6,7 @@ import ( "fmt" "io/fs" "os" + "os/exec" "os/signal" "os/user" "path/filepath" @@ -39,7 +40,8 @@ func main() { var opts struct { Config string `default:"/etc/viam.json" description:"Path to config file" long:"config" short:"c"` - Debug bool `description:"Enable debug logging (for agent only)" long:"debug" short:"d"` + Debug bool `description:"Enable debug logging (for agent only)" env:"VIAM_AGENT_DEBUG" long:"debug" short:"d"` + Fast bool `description:"Enable fast start mode" env:"VIAM_AGENT_FAST_START" long:"fast" short:"f"` Help bool `description:"Show this help message" long:"help" short:"h"` Version bool `description:"Show version" long:"version" short:"v"` Install bool `description:"Install systemd service" long:"install"` @@ -81,10 +83,7 @@ func main() { } if opts.Install { - err := viamagent.Install(globalLogger) - if err != nil { - globalLogger.Error(err) - } + exitIfError(viamagent.Install(globalLogger)) return } @@ -104,23 +103,11 @@ func main() { exitIfError(agent.InitPaths()) // use a lockfile to prevent running two agents on the same machine - pidFile, err := lockfile.New(filepath.Join(agent.ViamDirs["run"], "viam-agent.pid")) - exitIfError(errors.Wrap(err, "cannot init lock file")) - if err = pidFile.TryLock(); err != nil { - globalLogger.Error(errors.Wrapf(err, "cannot lock %s", pidFile)) - if errors.Is(err, lockfile.ErrBusy) { - globalLogger.Debug("Retrying to lock file") - - time.Sleep(2 * time.Second) - - if err = pidFile.TryLock(); err != nil { - globalLogger.Fatal("Please terminate any other copies of viam-agent and try again.") - } - } - } + pidFile, err := getLock() + exitIfError(err) defer func() { if err := pidFile.Unlock(); err != nil { - exitIfError(errors.Wrapf(err, "cannot unlock %s", pidFile)) + globalLogger.Error(errors.Wrapf(err, "unlocking %s", pidFile)) } }() @@ -144,7 +131,7 @@ func main() { if !errors.Is(err, fs.ErrNotExist) { if err := os.Rename(absConfigPath, absConfigPath+".old"); err != nil { // if we can't rename the file, we're up a creek, and it's fatal - globalLogger.Error(errors.Wrapf(err, "cannot remove invalid config file %s", absConfigPath)) + globalLogger.Error(errors.Wrapf(err, "removing invalid config file %s", absConfigPath)) globalLogger.Error("unable to continue with provisioning, exiting") manager.CloseAll() return @@ -159,8 +146,8 @@ func main() { if errors.Is(err, agent.ErrSubsystemDisabled) { globalLogger.Warn("provisioning subsystem disabled, please manually update /etc/viam.json and connect to internet") } else { - globalLogger.Error(errors.Wrapf(err, "could not start provisioning subsystem")) - globalLogger.Error("please manually update /etc/viam.json and connect to internet") + globalLogger.Error(errors.Wrapf(err, + "could not start provisioning subsystem, please manually update /etc/viam.json and connect to internet")) } } @@ -177,20 +164,51 @@ func main() { } } - // Start viam server as soon as possible. Then, start other subsystems and check for updates - if err := manager.StartSubsystem(ctx, viamserver.SubsysName); err != nil { - if errors.Is(err, agent.ErrSubsystemDisabled) { - globalLogger.Warn("viam-server subsystem disabled, please manually update /etc/viam.json and connect to internet") + // if FastStart is set, skip updates and start viam-server immediately, then proceed as normal + var fastSuccess bool + if opts.Fast || viamserver.FastStart.Load() { + if err := manager.StartSubsystem(ctx, viamserver.SubsysName); err != nil { + globalLogger.Error(err) } else { - globalLogger.Errorf("could not start viam-server subsystem: %s", err) + fastSuccess = true } } - globalLogger.Debug("==== Starting background checks =====") - manager.StartBackgroundChecks(ctx) + if !fastSuccess { + // wait to be online + timeoutCtx, cancel := context.WithTimeout(ctx, time.Minute) + defer cancel() + for { + cmd := exec.CommandContext(timeoutCtx, "systemctl", "is-active", "network-online.target") + _, err := cmd.CombinedOutput() - <-ctx.Done() + if err == nil { + break + } + + if errors.As(err, &exec.ExitError{}) { + // if it's not an ExitError, that means it didn't even start, so bail out + globalLogger.Error(errors.Wrap(err, "running 'systemctl is-active network-online.target'")) + break + } + if !utils.SelectContextOrWait(timeoutCtx, time.Second) { + break + } + } + // Check for self-update and restart if needed. + needRestart, err := manager.SelfUpdate(ctx) + if err != nil { + globalLogger.Error(err) + } + if needRestart { + globalLogger.Info("updated self, exiting to await restart with new version") + return + } + } + + manager.StartBackgroundChecks(ctx) + <-ctx.Done() manager.CloseAll() activeBackgroundWorkers.Wait() @@ -249,3 +267,55 @@ func exitIfError(err error) { globalLogger.Fatal(err) } } + +func getLock() (lockfile.Lockfile, error) { + pidFile, err := lockfile.New(filepath.Join(agent.ViamDirs["tmp"], "viam-agent.pid")) + if err != nil { + return "", errors.Wrap(err, "init lockfile") + } + err = pidFile.TryLock() + if err == nil { + return pidFile, nil + } + + globalLogger.Warn(errors.Wrapf(err, "locking %s", pidFile)) + + // if it's a potentially temporary error, retry + if errors.Is(err, lockfile.ErrBusy) || errors.Is(err, lockfile.ErrNotExist) { + time.Sleep(2 * time.Second) + globalLogger.Warn("retrying lock") + err = pidFile.TryLock() + if err == nil { + return pidFile, nil + } + + // if (still) busy, validate that the PID in question is actually viam-agent + // some systems use sequential, low numbered PIDs that can easily repeat after a reboot or crash + // this could result some other valid/running process that matches a leftover lockfile PID + if errors.Is(err, lockfile.ErrBusy) { + var staleFile bool + proc, err := pidFile.GetOwner() + if err != nil { + globalLogger.Error(errors.Wrap(err, "getting lockfile owner")) + staleFile = true + } + runPath, err := filepath.EvalSymlinks(fmt.Sprintf("/proc/%d/exe", proc.Pid)) + if err != nil { + globalLogger.Error(errors.Wrap(err, "cannot get info on lockfile owner")) + staleFile = true + } else if !strings.Contains(runPath, agent.SubsystemName) { + globalLogger.Warnf("lockfile owner isn't %s", agent.SubsystemName) + staleFile = true + } + if staleFile { + globalLogger.Warnf("deleting lockfile %s", pidFile) + if err := os.RemoveAll(string(pidFile)); err != nil { + return "", errors.Wrap(err, "removing lockfile") + } + return pidFile, pidFile.TryLock() + } + return "", errors.Errorf("other instance of viam-agent is already running with PID: %d", proc.Pid) + } + } + return "", err +} diff --git a/go.mod b/go.mod index 2bf0ec3..608237a 100644 --- a/go.mod +++ b/go.mod @@ -6,9 +6,12 @@ require ( github.com/edaniels/golog v0.0.0-20230215213219-28954395e8d0 github.com/jessevdk/go-flags v1.5.0 github.com/nightlyone/lockfile v1.0.0 - go.uber.org/zap v1.26.0 - go.viam.com/api v0.1.260 - go.viam.com/utils v0.1.59 + github.com/pkg/errors v0.9.1 + github.com/ulikunitz/xz v0.5.12 + go.uber.org/zap v1.27.0 + go.viam.com/api v0.1.295 + go.viam.com/utils v0.1.75 + golang.org/x/sys v0.20.0 ) require ( @@ -39,23 +42,23 @@ require ( github.com/montanaflynn/stats v0.7.1 // indirect github.com/pion/datachannel v1.5.5 // indirect github.com/pion/dtls/v2 v2.2.7 // indirect - github.com/pion/ice/v2 v2.3.11 // indirect - github.com/pion/interceptor v0.1.24 // indirect + github.com/pion/ice/v2 v2.3.13 // indirect + github.com/pion/interceptor v0.1.25 // indirect github.com/pion/logging v0.2.2 // indirect - github.com/pion/mdns v0.0.9 // indirect + github.com/pion/mdns v0.0.12 // indirect github.com/pion/randutil v0.1.0 // indirect - github.com/pion/rtcp v1.2.10 // indirect - github.com/pion/rtp v1.8.2 // indirect - github.com/pion/sctp v1.8.9 // indirect - github.com/pion/sdp/v3 v3.0.6 // indirect - github.com/pion/srtp/v2 v2.0.17 // indirect + github.com/pion/rtcp v1.2.12 // indirect + github.com/pion/rtp v1.8.5 // indirect + github.com/pion/sctp v1.8.14 // indirect + github.com/pion/sdp/v3 v3.0.9 // indirect + github.com/pion/srtp/v2 v2.0.18 // indirect github.com/pion/stun v0.6.1 // indirect github.com/pion/transport/v2 v2.2.4 // indirect github.com/pion/turn/v2 v2.1.4 // indirect - github.com/pion/webrtc/v3 v3.2.21 // indirect + github.com/pion/webrtc/v3 v3.2.36 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/rs/cors v1.10.1 // indirect - github.com/srikrsna/protoc-gen-gotag v0.6.2 // indirect + github.com/stretchr/testify v1.9.0 // indirect github.com/xdg-go/pbkdf2 v1.0.0 // indirect github.com/xdg-go/scram v1.1.2 // indirect github.com/xdg-go/stringprep v1.0.4 // indirect @@ -63,12 +66,15 @@ require ( github.com/zitadel/oidc v1.13.5 // indirect go.mongodb.org/mongo-driver v1.12.1 // indirect go.opencensus.io v0.24.0 // indirect - golang.org/x/crypto v0.21.0 // indirect + go.uber.org/atomic v1.11.0 // indirect + go.uber.org/goleak v1.3.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + golang.org/x/crypto v0.23.0 // indirect golang.org/x/mod v0.13.0 // indirect - golang.org/x/net v0.21.0 // indirect + golang.org/x/net v0.25.0 // indirect golang.org/x/oauth2 v0.13.0 // indirect golang.org/x/sync v0.4.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/text v0.15.0 // indirect golang.org/x/tools v0.14.0 // indirect google.golang.org/appengine v1.6.8 // indirect google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b // indirect @@ -80,13 +86,3 @@ require ( gopkg.in/yaml.v3 v3.0.1 // indirect nhooyr.io/websocket v1.8.9 // indirect ) - -require ( - github.com/pkg/errors v0.9.1 - github.com/stretchr/testify v1.9.0 // indirect - github.com/ulikunitz/xz v0.5.11 - go.uber.org/atomic v1.11.0 // indirect - go.uber.org/goleak v1.2.1 // indirect - go.uber.org/multierr v1.11.0 // indirect - golang.org/x/sys v0.18.0 // indirect -) diff --git a/go.sum b/go.sum index 3f364fb..f849646 100644 --- a/go.sum +++ b/go.sum @@ -126,6 +126,8 @@ github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVB github.com/franela/goreq v0.0.0-20171204163338-bcd34c9993f8/go.mod h1:ZhphrRTfi2rbfLwlschooIH4+wKKDR4Pdxhh+TRoA20= github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= github.com/fzipp/gocyclo v0.3.1/go.mod h1:DJHO6AUmbdqj2ET4Z9iArSuwWgYDRryYt2wASxc7x3E= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= @@ -147,7 +149,6 @@ github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+ github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI= github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-toolsmith/astcast v1.0.0/go.mod h1:mt2OdQTeAQcY4DQgPSArJjHCcOwlX+Wl/kwN+LbLGQ4= github.com/go-toolsmith/astcopy v1.0.0/go.mod h1:vrgyG+5Bxrnz4MZWPF+pI4R8h3qKRjjyvV/DSez4WVQ= github.com/go-toolsmith/astequal v1.0.0/go.mod h1:H+xSiq0+LtiDC11+h1G32h7Of5O3CYFJ99GVbS5lDKY= @@ -336,7 +337,6 @@ github.com/klauspost/compress v1.17.2 h1:RlWWUY/Dr4fL8qk9YG7DTZ7PDgME2V4csBXA8L/ github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= @@ -367,7 +367,6 @@ github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM= github.com/lightstep/lightstep-tracer-go v0.18.1/go.mod h1:jlF1pusYV4pidLvZ+XD0UBX0ZE6WURAspgAczcDHrL4= github.com/logrusorgru/aurora v0.0.0-20181002194514-a7b3b318ed4e/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4= -github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= github.com/lyft/protoc-gen-validate v0.0.13/go.mod h1:XbGvPuh87YZc5TdIa2/I4pLk0QoUACkjt2znoq26NVQ= github.com/magefile/mage v1.10.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= @@ -430,7 +429,6 @@ github.com/nightlyone/lockfile v1.0.0/go.mod h1:rywoIealpdNse2r832aiD9jRk8ErCatR github.com/nishanths/exhaustive v0.1.0/go.mod h1:S1j9110vxV1ECdCudXRkeMnFQ/DQk9ajLT0Uf2MYZQQ= github.com/nishanths/predeclared v0.2.1/go.mod h1:HvkGJcA3naj4lOwnFXFDkFxVtSqQMB9sbB1usJ+xjQE= github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= -github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtbWGs= github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= @@ -440,13 +438,10 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= github.com/onsi/ginkgo v1.14.2/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= -github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU= github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= github.com/onsi/gomega v1.10.4/go.mod h1:g/HbgYopi++010VEqkFgJHKC09uJiW9UkXvMUuKHUCQ= -github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk= github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= @@ -468,31 +463,30 @@ github.com/pion/datachannel v1.5.5 h1:10ef4kwdjije+M9d7Xm9im2Y3O6A6ccQb0zcqZcJew github.com/pion/datachannel v1.5.5/go.mod h1:iMz+lECmfdCMqFRhXhcA/219B0SQlbpoR2V118yimL0= github.com/pion/dtls/v2 v2.2.7 h1:cSUBsETxepsCSFSxC3mc/aDo14qQLMSL+O6IjG28yV8= github.com/pion/dtls/v2 v2.2.7/go.mod h1:8WiMkebSHFD0T+dIU+UeBaoV7kDhOW5oDCzZ7WZ/F9s= -github.com/pion/ice/v2 v2.3.11 h1:rZjVmUwyT55cmN8ySMpL7rsS8KYsJERsrxJLLxpKhdw= -github.com/pion/ice/v2 v2.3.11/go.mod h1:hPcLC3kxMa+JGRzMHqQzjoSj3xtE9F+eoncmXLlCL4E= -github.com/pion/interceptor v0.1.18/go.mod h1:tpvvF4cPM6NGxFA1DUMbhabzQBxdWMATDGEUYOR9x6I= -github.com/pion/interceptor v0.1.24 h1:lN4ua3yUAJCgNKQKcZIM52wFjBgjN0r7shLj91PkJ0c= -github.com/pion/interceptor v0.1.24/go.mod h1:wkbPYAak5zKsfpVDYMtEfWEy8D4zL+rpxCxPImLOg3Y= +github.com/pion/ice/v2 v2.3.13 h1:xOxP+4V9nSDlUaGFRf/LvAuGHDXRcjIdsbbXPK/w7c8= +github.com/pion/ice/v2 v2.3.13/go.mod h1:KXJJcZK7E8WzrBEYnV4UtqEZsGeWfHxsNqhVcVvgjxw= +github.com/pion/interceptor v0.1.25 h1:pwY9r7P6ToQ3+IF0bajN0xmk/fNw/suTgaTdlwTDmhc= +github.com/pion/interceptor v0.1.25/go.mod h1:wkbPYAak5zKsfpVDYMtEfWEy8D4zL+rpxCxPImLOg3Y= github.com/pion/logging v0.2.2 h1:M9+AIj/+pxNsDfAT64+MAVgJO0rsyLnoJKCqf//DoeY= github.com/pion/logging v0.2.2/go.mod h1:k0/tDVsRCX2Mb2ZEmTqNa7CWsQPc+YYCB7Q+5pahoms= -github.com/pion/mdns v0.0.8/go.mod h1:hYE72WX8WDveIhg7fmXgMKivD3Puklk0Ymzog0lSyaI= -github.com/pion/mdns v0.0.9 h1:7Ue5KZsqq8EuqStnpPWV33vYYEH0+skdDN5L7EiEsI4= -github.com/pion/mdns v0.0.9/go.mod h1:2JA5exfxwzXiCihmxpTKgFUpiQws2MnipoPK09vecIc= +github.com/pion/mdns v0.0.12 h1:CiMYlY+O0azojWDmxdNr7ADGrnZ+V6Ilfner+6mSVK8= +github.com/pion/mdns v0.0.12/go.mod h1:VExJjv8to/6Wqm1FXK+Ii/Z9tsVk/F5sD/N70cnYFbk= github.com/pion/randutil v0.1.0 h1:CFG1UdESneORglEsnimhUjf33Rwjubwj6xfiOXBa3mA= github.com/pion/randutil v0.1.0/go.mod h1:XcJrSMMbbMRhASFVOlj/5hQial/Y8oH/HVo7TBZq+j8= -github.com/pion/rtcp v1.2.10 h1:nkr3uj+8Sp97zyItdN60tE/S6vk4al5CPRR6Gejsdjc= github.com/pion/rtcp v1.2.10/go.mod h1:ztfEwXZNLGyF1oQDttz/ZKIBaeeg/oWbRYqzBM9TL1I= -github.com/pion/rtp v1.8.1/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= -github.com/pion/rtp v1.8.2 h1:oKMM0K1/QYQ5b5qH+ikqDSZRipP5mIxPJcgcvw5sH0w= +github.com/pion/rtcp v1.2.12 h1:bKWiX93XKgDZENEXCijvHRU/wRifm6JV5DGcH6twtSM= +github.com/pion/rtcp v1.2.12/go.mod h1:sn6qjxvnwyAkkPzPULIbVqSKI5Dv54Rv7VG0kNxh9L4= github.com/pion/rtp v1.8.2/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= +github.com/pion/rtp v1.8.3/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= +github.com/pion/rtp v1.8.5 h1:uYzINfaK+9yWs7r537z/Rc1SvT8ILjBcmDOpJcTB+OU= +github.com/pion/rtp v1.8.5/go.mod h1:pBGHaFt/yW7bf1jjWAoUjpSNoDnw98KTMg+jWWvziqU= github.com/pion/sctp v1.8.5/go.mod h1:SUFFfDpViyKejTAdwD1d/HQsCu+V/40cCs2nZIvC3s0= -github.com/pion/sctp v1.8.8/go.mod h1:igF9nZBrjh5AtmKc7U30jXltsFHicFCXSmWA2GWRaWs= -github.com/pion/sctp v1.8.9 h1:TP5ZVxV5J7rz7uZmbyvnUvsn7EJ2x/5q9uhsTtXbI3g= -github.com/pion/sctp v1.8.9/go.mod h1:cMLT45jqw3+jiJCrtHVwfQLnfR0MGZ4rgOJwUOIqLkI= -github.com/pion/sdp/v3 v3.0.6 h1:WuDLhtuFUUVpTfus9ILC4HRyHsW6TdugjEX/QY9OiUw= -github.com/pion/sdp/v3 v3.0.6/go.mod h1:iiFWFpQO8Fy3S5ldclBkpXqmWy02ns78NOKoLLL0YQw= -github.com/pion/srtp/v2 v2.0.17 h1:ECuOk+7uIpY6HUlTb0nXhfvu4REG2hjtC4ronYFCZE4= -github.com/pion/srtp/v2 v2.0.17/go.mod h1:y5WSHcJY4YfNB/5r7ca5YjHeIr1H3LM1rKArGGs8jMc= +github.com/pion/sctp v1.8.14 h1:NzwwDrtpvbdqMMWV9Q6NYGbHE/FQmjI+GEQLyeJahu4= +github.com/pion/sctp v1.8.14/go.mod h1:P6PbDVA++OJMrVNg2AL3XtYHV4uD6dvfyOovCgMs0PE= +github.com/pion/sdp/v3 v3.0.9 h1:pX++dCHoHUwq43kuwf3PyJfHlwIj4hXA7Vrifiq0IJY= +github.com/pion/sdp/v3 v3.0.9/go.mod h1:B5xmvENq5IXJimIO4zfp6LAe1fD9N+kFv+V/1lOdz8M= +github.com/pion/srtp/v2 v2.0.18 h1:vKpAXfawO9RtTRKZJbG4y0v1b11NZxQnxRl85kGuUlo= +github.com/pion/srtp/v2 v2.0.18/go.mod h1:0KJQjA99A6/a0DOVTu1PhDSw0CXF2jTkqOoMg3ODqdA= github.com/pion/stun v0.6.1 h1:8lp6YejULeHBF8NmV8e2787BogQhduZugh5PdhDyyN4= github.com/pion/stun v0.6.1/go.mod h1:/hO7APkX4hZKu/D0f2lHzNyvdkTGtIy3NDmLR7kSz/8= github.com/pion/transport v0.14.1 h1:XSM6olwW+o8J4SCmOBb/BpwZypkHeyM0PGFCxNQBr40= @@ -502,19 +496,19 @@ github.com/pion/transport/v2 v2.2.2/go.mod h1:OJg3ojoBJopjEeECq2yJdXH9YVrUJ1uQ++ github.com/pion/transport/v2 v2.2.3/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0= github.com/pion/transport/v2 v2.2.4 h1:41JJK6DZQYSeVLxILA2+F4ZkKb4Xd/tFJZRFZQ9QAlo= github.com/pion/transport/v2 v2.2.4/go.mod h1:q2U/tf9FEfnSBGSW6w5Qp5PFWRLRj3NjLhCCgpRK4p0= -github.com/pion/transport/v3 v3.0.1 h1:gDTlPJwROfSfz6QfSi0ZmeCSkFcnWWiiR9ES0ouANiM= github.com/pion/transport/v3 v3.0.1/go.mod h1:UY7kiITrlMv7/IKgd5eTUcaahZx5oUN3l9SzK5f5xE0= +github.com/pion/transport/v3 v3.0.2 h1:r+40RJR25S9w3jbA6/5uEPTzcdn7ncyU44RWCbHkLg4= +github.com/pion/transport/v3 v3.0.2/go.mod h1:nIToODoOlb5If2jF9y2Igfx3PFYWfuXi37m0IlWa/D0= github.com/pion/turn/v2 v2.1.3/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY= github.com/pion/turn/v2 v2.1.4 h1:2xn8rduI5W6sCZQkEnIUDAkrBQNl2eYIBCHMZ3QMmP8= github.com/pion/turn/v2 v2.1.4/go.mod h1:huEpByKKHix2/b9kmTAM3YoX6MKP+/D//0ClgUYR2fY= -github.com/pion/webrtc/v3 v3.2.21 h1:c8fy5JcqJkAQBwwy3Sk9huQLTBUSqaggyRlv9Lnh2zY= -github.com/pion/webrtc/v3 v3.2.21/go.mod h1:vVURQTBOG5BpWKOJz3nlr23NfTDeyKVmubRNqzQp+Tg= +github.com/pion/webrtc/v3 v3.2.36 h1:RM/miAv0M4TrhhS7h2mcZXt44K68WmpVDkUOgz2l2l8= +github.com/pion/webrtc/v3 v3.2.36/go.mod h1:wWQz1PuKNSNK4VrJJNpPN3vZmKEi4zA6i2ynaQOlxIU= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/profile v1.2.1/go.mod h1:hJw3o1OdXxsrSjjVksARp5W95eeEaEfptyVZyv6JUPA= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/polyfloyd/go-errorlint v0.0.0-20201127212506-19bd8db6546f/go.mod h1:wi9BfjxjF/bwiZ701TzmfKu6UKC357IOAtNr0Td0Lvw= @@ -566,7 +560,6 @@ github.com/ryanrolds/sqlclosecheck v0.3.0/go.mod h1:1gREqxyTGR3lVtpngyFo3hZAgk0K github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= github.com/sanposhiho/wastedassign v0.1.3/go.mod h1:LGpq5Hsv74QaqM47WtIsRSF/ik9kqk07kchgv66tLVE= -github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/securego/gosec/v2 v2.6.1/go.mod h1:I76p3NTHBXsGhybUW+cEQ692q2Vp+A0Z6ZLzDIZy+Ao= github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs= @@ -589,8 +582,6 @@ github.com/sony/gobreaker v0.4.1/go.mod h1:ZKptC7FHNvhBz7dN2LGjPVBz2sZJmc0/PkyDJ github.com/sourcegraph/go-diff v0.6.1/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= -github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= -github.com/spf13/afero v1.5.1/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= @@ -600,8 +591,6 @@ github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnIn github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= -github.com/srikrsna/protoc-gen-gotag v0.6.2 h1:ULdarjI7FNUA6CNlLPIzSNvjdV2P4C2LSygPLvCVtfA= -github.com/srikrsna/protoc-gen-gotag v0.6.2/go.mod h1:cplWV0ZNBhuF54gnj6rU9pLNrqjXf5vh65Xqa1Kjy+4= github.com/ssgreg/nlreturn/v2 v2.1.0/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= github.com/streadway/amqp v0.0.0-20190827072141-edfb9018d271/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= @@ -635,8 +624,8 @@ github.com/tomarrell/wrapcheck v0.0.0-20201130113247-1683564d9756/go.mod h1:yiFB github.com/tommy-muehle/go-mnd/v2 v2.3.1/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw= github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw= github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY= -github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8= -github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= +github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc= +github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= github.com/ultraware/funlen v0.0.3/go.mod h1:Dp4UiAus7Wdb9KUZsYWZEWiRzGuM2kXM1lPbfaF6xhA= github.com/ultraware/whitespace v0.0.4/go.mod h1:aVMh/gQve5Maj9hQ/hg+F75lr/X5A89uZnzAmWSineA= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= @@ -684,8 +673,8 @@ go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A= go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ= -go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= -go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= @@ -696,34 +685,33 @@ go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= go.uber.org/zap v1.18.1/go.mod h1:xg/QME4nWcxGxrpdeYfq7UvYrLh66cuVKdrbD1XF/NI= go.uber.org/zap v1.23.0/go.mod h1:D+nX8jyLsMHMYrln8A0rJjFt/T/9/bGgIhAqxv5URuY= -go.uber.org/zap v1.26.0 h1:sI7k6L95XOKS281NhVKOFCUNIvv9e0w4BF8N3u+tCRo= -go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= -go.viam.com/api v0.1.260 h1:lrEHIz6gmOHDWAr0765FqGOJKGvo9syjsehH/qoGIvg= -go.viam.com/api v0.1.260/go.mod h1:msa4TPrMVeRDcG4YzKA/S6wLEUC7GyHQE973JklrQ10= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.viam.com/api v0.1.295 h1:yfkJOyQEq9BQoD7Hw4UNEmyA58qZKlthSVk8Ml6gnBM= +go.viam.com/api v0.1.295/go.mod h1:msa4TPrMVeRDcG4YzKA/S6wLEUC7GyHQE973JklrQ10= go.viam.com/test v1.1.0 h1:KXf8crEwt0bHn5iMsjmXg0vPio/yzKGrfd20sWhe/t4= go.viam.com/test v1.1.0/go.mod h1:iULewngKZCHSB2QJa1xGSR6+0ICZIHbEfTUGqw0blLs= -go.viam.com/utils v0.1.59 h1:h6x9aFlX00DIS3x1DWwonG5XICIgIE5m4R3RDGObXuw= -go.viam.com/utils v0.1.59/go.mod h1:O6CsAqjd7xMfx7Ip6GWTLGsg3WTJ/wr3JySho4D55qY= +go.viam.com/utils v0.1.75 h1:eud/hSMwPkzCYVjtWWlqhADKukhIPTLuNITFHqSmT1w= +go.viam.com/utils v0.1.75/go.mod h1:3xcBBlLsRX2eWpWuQrFJ7UVxsgXwJg8Fx30Rt/kp52g= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.8.0/go.mod h1:mRqEX+O9/h5TFCrQhkgjo2yKi0yYA+9ecGkdQoHrywE= -golang.org/x/crypto v0.10.0/go.mod h1:o4eNf7Ede1fv+hwOwZsTHl9EsPFO6q6ZvYR8vYfY45I= golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio= golang.org/x/crypto v0.12.0/go.mod h1:NF0Gs7EO5K4qLn+Ylc+fih8BSTeIjAP05siRnAh98yw= -golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc= +golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= -golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= golang.org/x/crypto v0.21.0/go.mod h1:0BP7YvVV9gBbVKyeTG0Gyn+gZm94bibOW5BjDEYAOMs= +golang.org/x/crypto v0.23.0 h1:dIJU/v2J8Mdglj/8rJ6UUOM3Zc9zLZxVZwwxMooUSAI= +golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -783,7 +771,6 @@ golang.org/x/net v0.0.0-20201202161906-c7110b5ffcbb/go.mod h1:sp8m0HH+o8qH0wwXwY golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210423184538-5f58ad60dda6/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= -golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk= golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= @@ -791,12 +778,12 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.11.0/go.mod h1:2L/ixqYpgIVXmeoSA/4Lu7BzTG4KIyPIryS4IsOd1oQ= golang.org/x/net v0.13.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA= golang.org/x/net v0.14.0/go.mod h1:PpSgVXXLK0OxS0F31C1/tv6XNguvCrnXIDrFMspZIUI= -golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk= -golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= +golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY= golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -852,7 +839,6 @@ golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201024232916-9f70ab9862d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210303074136-134d130e1a04/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -872,39 +858,37 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.9.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y= +golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= -golang.org/x/term v0.9.0/go.mod h1:M6DEAAIenWoTxdKrOltXcmDY3rSplQUkrvaDU5FcQyo= golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o= golang.org/x/term v0.11.0/go.mod h1:zC9APTIj3jG3FdV/Ons+XE1riIZXG4aZ4GTHiPZJPIU= -golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU= +golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY= golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.3.8/go.mod h1:E6s5w1FMmriuDzIBO73fBruAKo1PCIq6d2Q6DHfQ8WQ= golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= -golang.org/x/text v0.10.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= golang.org/x/text v0.12.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.15.0 h1:h1V/4gjBv8v9cjcR6+AR5+/cIYK5N/WAgiv4xlsEtAk= +golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= @@ -964,7 +948,6 @@ golang.org/x/tools v0.0.0-20201023174141-c8cfbd0f21e6/go.mod h1:emZCQorbCU4vsT4f golang.org/x/tools v0.0.0-20201028025901-8cd080b735b3/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201114224030-61ea331ec02b/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201118003311-bd56c0adb394/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201230224404-63754364767c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210101214203-2dba1e4ea05c/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210102185154-773b96fafca2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= diff --git a/install.sh b/install.sh index e40ba33..4ed92ba 100755 --- a/install.sh +++ b/install.sh @@ -243,7 +243,7 @@ main() { uninstall_old_service - if [ -f /etc/systemd/system/viam-agent.service ]; then + if [ -f /etc/systemd/system/viam-agent.service ] || [ -f /usr/local/lib/systemd/system/viam-agent.service ]; then echo echo "It appears viam-agent is already installed. You can restart it with 'systemctl restart viam-agent' if it's not running." diff --git a/manager.go b/manager.go index c39c9eb..9c0531a 100644 --- a/manager.go +++ b/manager.go @@ -67,13 +67,13 @@ func (m *Manager) LoadConfig(cfgPath string) error { //nolint:gosec b, err := os.ReadFile(cfgPath) if err != nil { - return errw.Wrap(err, "error reading config file") + return errw.Wrap(err, "reading config file") } cfg := make(map[string]map[string]string) err = json.Unmarshal(b, &cfg) if err != nil { - return errw.Wrap(err, "error parsing config file") + return errw.Wrap(err, "parsing config file") } cloud, ok := cfg["cloud"] @@ -140,6 +140,9 @@ func (m *Manager) SubsystemUpdates(ctx context.Context, cfg map[string]*pb.Devic // check updates and (re)start for name, sub := range m.loadedSubsystems { + if ctx.Err() != nil { + return + } cancelCtx, cancel := context.WithTimeout(ctx, time.Minute*5) defer cancel() restart, err := sub.Update(cancelCtx, cfg[name]) @@ -244,6 +247,7 @@ func (m *Manager) StartBackgroundChecks(ctx context.Context) { if ctx.Err() != nil { return } + m.logger.Debug("starting background checks") m.activeBackgroundWorkers.Add(1) go func() { checkInterval := m.CheckUpdates(ctx) @@ -276,7 +280,7 @@ func (m *Manager) LoadSubsystems(ctx context.Context) error { cachedConfig, err := m.getCachedConfig() if err != nil { - m.logger.Error(errw.Wrap(err, "error getting cached config")) + m.logger.Error(errw.Wrap(err, "getting cached config")) } for _, name := range registry.List() { @@ -319,12 +323,12 @@ func (m *Manager) getCachedConfig() (map[string]*pb.DeviceSubsystemConfig, error if errors.Is(err, fs.ErrNotExist) { return cachedConfig, nil } - return nil, errw.Wrap(err, "error reading cached config") + return nil, errw.Wrap(err, "reading cached config") } err = json.Unmarshal(cacheBytes, &cachedConfig) if err != nil { - return nil, errw.Wrapf(err, "error parsing cached config") + return nil, errw.Wrapf(err, "parsing cached config") } return cachedConfig, nil } @@ -338,7 +342,7 @@ func (m *Manager) saveCachedConfig(cfg map[string]*pb.DeviceSubsystemConfig) err return err } //nolint:gosec - return os.WriteFile(cacheFilePath, cacheData, 0o644) + return errors.Join(os.WriteFile(cacheFilePath, cacheData, 0o644), SyncFS(cacheFilePath)) } // dial establishes a connection to the cloud for grpc communication. @@ -387,7 +391,7 @@ func (m *Manager) GetConfig(ctx context.Context) (map[string]*pb.DeviceSubsystem defer cancelFunc() if err := m.dial(timeoutCtx); err != nil { - m.logger.Error(errw.Wrapf(err, "error fetching %s config", SubsystemName)) + m.logger.Error(errw.Wrapf(err, "fetching %s config", SubsystemName)) conf, err := m.getCachedConfig() return conf, minimalCheckInterval, err } @@ -399,7 +403,7 @@ func (m *Manager) GetConfig(ctx context.Context) (map[string]*pb.DeviceSubsystem } resp, err := m.client.DeviceAgentConfig(timeoutCtx, req) if err != nil { - m.logger.Error(errw.Wrapf(err, "error fetching %s config", SubsystemName)) + m.logger.Error(errw.Wrapf(err, "fetching %s config", SubsystemName)) conf, err := m.getCachedConfig() return conf, minimalCheckInterval, err } @@ -408,7 +412,7 @@ func (m *Manager) GetConfig(ctx context.Context) (map[string]*pb.DeviceSubsystem err = m.saveCachedConfig(resp.GetSubsystemConfigs()) if err != nil { - m.logger.Error(errw.Wrap(err, "error saving agent config to cache")) + m.logger.Error(errw.Wrap(err, "saving agent config to cache")) } interval := resp.GetCheckInterval().AsDuration() diff --git a/preinstall.sh b/preinstall.sh index 15729fa..3d13cbd 100755 --- a/preinstall.sh +++ b/preinstall.sh @@ -12,8 +12,7 @@ TARBALL_ONLY=0 SERVICE_FILE=$(cat < "$TEMPDIR/etc/systemd/system/viam-agent.service" - ln -s /etc/systemd/system/viam-agent.service "$TEMPDIR/etc/systemd/system/multi-user.target.wants/viam-agent.service" + mkdir -p "$TEMPDIR/usr/local/lib/systemd/system/multi-user.target.wants/" + echo "$SERVICE_FILE" > "$TEMPDIR/usr/local/lib/systemd/system/viam-agent.service" + ln -s ../viam-agent.service "$TEMPDIR/usr/local/lib/systemd/system/multi-user.target.wants/viam-agent.service" mkdir -p "$TEMPDIR/opt/viam/cache" curl -fsSL "$URL" -o "$TEMPDIR/opt/viam/cache/viam-agent-factory-$ARCH" || return 1 @@ -107,13 +107,14 @@ create_tarball() { ln -s "/opt/viam/cache/viam-agent-factory-$ARCH" "$TEMPDIR/opt/viam/bin/viam-agent" ln -s "/opt/viam/cache/viam-agent-provisioning-factory-$ARCH" "$TEMPDIR/opt/viam/bin/agent-provisioning" + mkdir -p "$TEMPDIR/etc" if [ -f "$PROVISIONING_PATH" ]; then echo "Installing $PROVISIONING_PATH as /etc/viam-provisioning.json" cat "$PROVISIONING_PATH" > "$TEMPDIR/etc/viam-provisioning.json" fi TARBALL="$TEMPDIR/viam-preinstall-$ARCH.tar.xz" - tar -cJvpf "$TARBALL" -C "$TEMPDIR" opt/ etc/ || return 1 + tar -cJvpf "$TARBALL" -C "$TEMPDIR" opt/ etc/ usr/ || return 1 } if [ "$(id -u)" -ne 0 ]; then diff --git a/subsystem.go b/subsystem.go index 1254a80..d599376 100644 --- a/subsystem.go +++ b/subsystem.go @@ -194,7 +194,7 @@ func (s *AgentSubsystem) LoadCache() error { } else { err = json.Unmarshal(cacheBytes, cache) if err != nil { - s.logger.Error(errw.Wrap(err, "cannot parse subsystem cache, using new defaults")) + s.logger.Error(errw.Wrap(err, "parsing subsystem cache, using new defaults")) s.CacheData = &CacheData{ Versions: make(map[string]*VersionInfo), } @@ -215,7 +215,7 @@ func (s *AgentSubsystem) saveCache() error { return err } //nolint:gosec - return os.WriteFile(cacheFilePath, cacheData, 0o644) + return errors.Join(os.WriteFile(cacheFilePath, cacheData, 0o644), SyncFS(cacheFilePath)) } // SaveCache saves the cached data to disk. @@ -451,7 +451,7 @@ func (is *InternalSubsystem) Start(ctx context.Context) error { err = is.cmd.Start() if err != nil { is.mu.Unlock() - return errw.Wrapf(err, "error starting %s", is.name) + return errw.Wrapf(err, "starting %s", is.name) } is.running = true is.exitChan = make(chan struct{}) @@ -610,5 +610,5 @@ func (is *InternalSubsystem) Update(ctx context.Context, cfg *pb.DeviceSubsystem // If attribute changes, restart after writing the new config file. //nolint:gosec - return true, os.WriteFile(is.cfgPath, jsonBytes, 0o644) + return true, errors.Join(os.WriteFile(is.cfgPath, jsonBytes, 0o644), SyncFS(is.cfgPath)) } diff --git a/subsystems/viamagent/viam-agent.service b/subsystems/viamagent/viam-agent.service index e79afb1..a0d5204 100644 --- a/subsystems/viamagent/viam-agent.service +++ b/subsystems/viamagent/viam-agent.service @@ -1,6 +1,6 @@ [Unit] Description=Viam Services Agent -Wants=NetworkManager.service network-online.target +Wants=NetworkManager.service StartLimitIntervalSec=0 [Service] @@ -9,8 +9,6 @@ Restart=always RestartSec=5 User=root TimeoutSec=240 -RuntimeDirectory=viam -RuntimeDirectoryMode=0755 ExecStart=/opt/viam/bin/viam-agent --config /etc/viam.json FinalKillSignal=SIGQUIT diff --git a/subsystems/viamagent/viamagent.go b/subsystems/viamagent/viamagent.go index 84793c4..17d87a3 100644 --- a/subsystems/viamagent/viamagent.go +++ b/subsystems/viamagent/viamagent.go @@ -4,13 +4,15 @@ package viamagent import ( "context" _ "embed" + "errors" "fmt" "io/fs" "os" "os/exec" "path/filepath" + "strings" - "github.com/pkg/errors" + errw "github.com/pkg/errors" "github.com/viamrobotics/agent" "github.com/viamrobotics/agent/subsystems" "github.com/viamrobotics/agent/subsystems/registry" @@ -24,8 +26,9 @@ func init() { const ( subsysName = "viam-agent" - serviceFileDir = "/etc/systemd/system" - serviceFilePath = "/etc/systemd/system/viam-agent.service" + serviceFileDir = "/usr/local/lib/systemd/system" + fallbackFileDir = "/etc/systemd/system" + serviceFileName = "viam-agent.service" ) var ( @@ -73,8 +76,10 @@ func (a *agentSubsystem) Update(ctx context.Context, cfg *pb.DeviceSubsystemConf cmd := exec.Command(expectedPath, "--install") output, err := cmd.CombinedOutput() if err != nil { - return false, errors.Wrapf(err, "error running post install step %s", output) + return false, errw.Wrapf(err, "running post install step %s", output) } + //nolint:forbidigo + fmt.Print(string(output)) return true, nil } @@ -96,72 +101,137 @@ func GetRevision() string { } func Install(logger *zap.SugaredLogger) error { + // Check for systemd + cmd := exec.Command("systemctl", "--version") + output, err := cmd.CombinedOutput() + if err != nil { + return errw.Wrapf(err, "can only install on systems using systemd, but 'systemctl --version' returned errors %s", output) + } + // Create/check required folder structure exists. if err := agent.InitPaths(); err != nil { return err } - // Check for systemd - _, err := os.Stat(serviceFileDir) - if errors.Is(err, fs.ErrNotExist) { - return errors.Wrapf(err, "can only install on systems using systemd, but %s is missing", serviceFileDir) - } - if err != nil { - return errors.Wrapf(err, "error getting info for %s", serviceFileDir) - } - // If this is a brand new install, we want to symlink ourselves into place temporarily. expectedPath := filepath.Join(agent.ViamDirs["bin"], subsysName) curPath, err := os.Executable() if err != nil { - return errors.Wrap(err, "cannot get path to self") + return errw.Wrap(err, "getting path to self") } isSelf, err := agent.CheckIfSame(curPath, expectedPath) if err != nil { - return errors.Wrap(err, "error checking if installed viam-agent is myself") + return errw.Wrap(err, "checking if installed viam-agent is myself") } if !isSelf { logger.Infof("adding a symlink to %s at %s", curPath, expectedPath) - if err := os.Remove(expectedPath); err != nil && !errors.Is(err, fs.ErrNotExist) { - return errors.Wrapf(err, "cannot remove symlink/file at %s", expectedPath) + if err := os.Remove(expectedPath); err != nil && !errw.Is(err, fs.ErrNotExist) { + return errw.Wrapf(err, "removing symlink/file at %s", expectedPath) } if err := os.Symlink(curPath, expectedPath); err != nil { - return errors.Wrapf(err, "cannot install symlink at %s", expectedPath) + return errw.Wrapf(err, "installing symlink at %s", expectedPath) } } + serviceFilePath, removeOldFile, err := getServiceFilePath(logger) + if err != nil { + return errw.Wrap(err, "getting service file path") + } + + //nolint:gosec + if err := os.MkdirAll(filepath.Dir(serviceFilePath), 0o755); err != nil { + return errw.Wrapf(err, "creating directory %s", filepath.Dir(serviceFilePath)) + } + logger.Infof("writing systemd service file to %s", serviceFilePath) //nolint:gosec if err := os.WriteFile(serviceFilePath, serviceFileContents, 0o644); err != nil { - return errors.Wrapf(err, "unable to write systemd service file %s", serviceFilePath) + return errw.Wrapf(err, "writing systemd service file %s", serviceFilePath) + } + + if removeOldFile { + oldPath := filepath.Join(fallbackFileDir, serviceFileName) + logger.Warn("Removing system service file %s in favor of vendor file at %s", oldPath, serviceFilePath) + logger.Warn("If you customized this file, please run 'systemctl edit viam-agent' and create overrides there") + if err := os.RemoveAll(oldPath); err != nil { + logger.Error(errw.Wrapf(err, "removing old service file %s, please delete manually", oldPath)) + } } logger.Infof("enabling systemd viam-agent service") - cmd := exec.Command("systemctl", "daemon-reload") - output, err := cmd.CombinedOutput() + cmd = exec.Command("systemctl", "daemon-reload") + output, err = cmd.CombinedOutput() if err != nil { - return errors.Wrapf(err, "problem running 'systemctl daemon-reload' output: %s", output) + return errw.Wrapf(err, "running 'systemctl daemon-reload' output: %s", output) } cmd = exec.Command("systemctl", "enable", "viam-agent") output, err = cmd.CombinedOutput() if err != nil { - return errors.Wrapf(err, "problem running 'systemctl enable viam-agent' output: %s", output) + return errw.Wrapf(err, "running 'systemctl enable viam-agent' output: %s", output) } _, err = os.Stat("/etc/viam.json") if err != nil { - if errors.Is(err, fs.ErrNotExist) { - //nolint:forbidigo - fmt.Println("No config file found at /etc/viam.json, please install one before running viam-agent service.") + if errw.Is(err, fs.ErrNotExist) { + logger.Warn("No config file found at /etc/viam.json, please install one before running viam-agent service.") } else { - return errors.Wrap(err, "error reading /etc/viam.json") + return errw.Wrap(err, "reading /etc/viam.json") } } - //nolint:forbidigo - fmt.Println("Install complete. Please (re)start the service with 'systemctl restart viam-agent' when ready.") - return nil + logger.Info("Install complete. Please (re)start the service with 'systemctl restart viam-agent' when ready.") + + return errors.Join(agent.SyncFS("/etc"), agent.SyncFS(serviceFilePath), agent.SyncFS(agent.ViamDirs["viam"])) +} + +func inSystemdPath(path string, logger *zap.SugaredLogger) bool { + cmd := exec.Command("systemd-path", "systemd-search-system-unit") + output, err := cmd.CombinedOutput() + if err != nil { + logger.Error(errw.Wrapf(err, "running 'systemd-path systemd-search-system-unit' output: %s", output)) + return false + } + searchPaths := strings.Split(strings.TrimSpace(string(output)), ":") + for _, searchPath := range searchPaths { + if searchPath == path { + return true + } + } + return false +} + +func getServiceFilePath(logger *zap.SugaredLogger) (string, bool, error) { + serviceFilePath := filepath.Join(serviceFileDir, serviceFileName) + _, err := os.Stat(serviceFilePath) + if err == nil { + // file is already in place, we should be good + return serviceFilePath, false, nil + } + if !errw.Is(err, fs.ErrNotExist) { + // unknown error + return "", false, err + } + oldFilePath := filepath.Join(fallbackFileDir, serviceFileName) + + // see if we can migrate to the local path + if !inSystemdPath(serviceFileDir, logger) { + logger.Warnf("Systemd does not have %s in its unit search path, installing directly to %s", serviceFileDir, fallbackFileDir) + return oldFilePath, false, nil + } + + // migrate old file if it exists + _, err = os.Stat(oldFilePath) + if err == nil { + return serviceFilePath, true, nil + } + if errw.Is(err, fs.ErrNotExist) { + // unknown error + return "", false, err + } + + // new install, so there was nothing to migrate + return serviceFilePath, false, nil } diff --git a/subsystems/viamserver/viamserver.go b/subsystems/viamserver/viamserver.go index ab6d3f7..64df628 100644 --- a/subsystems/viamserver/viamserver.go +++ b/subsystems/viamserver/viamserver.go @@ -11,6 +11,7 @@ import ( "regexp" "strings" "sync" + "sync/atomic" "syscall" "time" @@ -31,12 +32,16 @@ const ( // stopTermTimeout must be higher than viam-server shutdown timeout of 90 secs. stopTermTimeout = time.Minute * 2 stopKillTimeout = time.Second * 10 + fastStartName = "fast_start" SubsysName = "viam-server" ) var ( ConfigFilePath = "/etc/viam.json" DefaultConfig = &pb.DeviceSubsystemConfig{} + + // Set if (cached or cloud) config has the "fast_start" attribute set on the viam-server subsystem. + FastStart atomic.Bool ) type viamServer struct { @@ -96,7 +101,7 @@ func (s *viamServer) Start(ctx context.Context) error { err = s.cmd.Start() if err != nil { s.mu.Unlock() - return errw.Wrapf(err, "error starting %s", SubsysName) + return errw.Wrapf(err, "starting %s", SubsysName) } s.running = true s.exitChan = make(chan struct{}) @@ -253,6 +258,7 @@ func (s *viamServer) HealthCheck(ctx context.Context) (errRet error) { func (s *viamServer) Update(ctx context.Context, cfg *pb.DeviceSubsystemConfig, newVersion bool) (bool, error) { s.mu.Lock() defer s.mu.Unlock() + setFastStart(cfg) if newVersion { s.logger.Info("awaiting user restart to run new viam-server version") s.shouldRun = false @@ -262,5 +268,20 @@ func (s *viamServer) Update(ctx context.Context, cfg *pb.DeviceSubsystemConfig, } func NewSubsystem(ctx context.Context, logger *zap.SugaredLogger, updateConf *pb.DeviceSubsystemConfig) (subsystems.Subsystem, error) { + setFastStart(updateConf) return agent.NewAgentSubsystem(ctx, SubsysName, logger, &viamServer{logger: logger.Named(SubsysName)}) } + +func setFastStart(cfg *pb.DeviceSubsystemConfig) { + if cfg != nil { + cfgVal, ok := cfg.GetAttributes().AsMap()[fastStartName] + if ok { + cfgBool, ok := cfgVal.(bool) + if ok { + FastStart.Store(cfgBool) + return + } + } + } + FastStart.Store(false) +} diff --git a/uninstall.sh b/uninstall.sh index 8b28940..3d3854d 100755 --- a/uninstall.sh +++ b/uninstall.sh @@ -26,7 +26,8 @@ fi # system services systemctl disable --now viam-agent systemctl disable --now viam-server -rm -v /etc/systemd/system/viam-agent.service /etc/systemd/system/viam-server.service +rm -v /etc/systemd/system/viam-agent.service /usr/local/lib/systemd/system/viam-agent.service /etc/systemd/system/viam-server.service +systemctl daemon-reload # previous appimage installs rm -v /usr/local/bin/viam-server diff --git a/utils.go b/utils.go index 1af18e7..c71f6c8 100644 --- a/utils.go +++ b/utils.go @@ -20,6 +20,7 @@ import ( errw "github.com/pkg/errors" "github.com/ulikunitz/xz" + "golang.org/x/sys/unix" ) var ViamDirs = map[string]string{"viam": "/opt/viam"} @@ -29,7 +30,6 @@ func init() { ViamDirs["cache"] = filepath.Join(ViamDirs["viam"], "cache") ViamDirs["tmp"] = filepath.Join(ViamDirs["viam"], "tmp") ViamDirs["etc"] = filepath.Join(ViamDirs["viam"], "etc") - ViamDirs["run"] = "/run/viam" } func InitPaths() error { @@ -40,11 +40,11 @@ func InitPaths() error { if errors.Is(err, fs.ErrNotExist) { //nolint:gosec if err := os.MkdirAll(p, 0o755); err != nil { - return errw.Wrapf(err, "Error creating directory %s", p) + return errw.Wrapf(err, "creating directory %s", p) } continue } - return errw.Wrapf(err, "Error checking directory %s", p) + return errw.Wrapf(err, "checking directory %s", p) } stat, ok := info.Sys().(*syscall.Stat_t) if !ok { @@ -93,7 +93,7 @@ func DownloadFile(ctx context.Context, rawURL string) (outPath string, errRet er return "", err } defer func() { - errRet = errors.Join(errRet, outfd.Close()) + errRet = errors.Join(errRet, outfd.Close(), SyncFS(outPath)) if err := os.Remove(outfd.Name()); err != nil && !os.IsNotExist(err) { errRet = errors.Join(errRet, err) } @@ -134,7 +134,7 @@ func DownloadFile(ctx context.Context, rawURL string) (outPath string, errRet er return "", err } defer func() { - errRet = errors.Join(errRet, out.Close()) + errRet = errors.Join(errRet, out.Close(), SyncFS(out.Name())) if err := os.Remove(out.Name()); err != nil && !os.IsNotExist(err) { errRet = errors.Join(errRet, err) } @@ -145,7 +145,7 @@ func DownloadFile(ctx context.Context, rawURL string) (outPath string, errRet er errRet = errors.Join(errRet, err) } - errRet = errors.Join(errRet, os.Rename(out.Name(), outPath)) + errRet = errors.Join(errRet, os.Rename(out.Name(), outPath), SyncFS(outPath)) return outPath, errRet } @@ -171,7 +171,7 @@ func DecompressFile(inPath string) (outPath string, errRet error) { } defer func() { - errRet = errors.Join(errRet, out.Close()) + errRet = errors.Join(errRet, out.Close(), SyncFS(ViamDirs["tmp"])) if err := os.Remove(out.Name()); err != nil && !os.IsNotExist(err) { errRet = errors.Join(errRet, err) } @@ -183,7 +183,7 @@ func DecompressFile(inPath string) (outPath string, errRet error) { } outPath = filepath.Join(ViamDirs["cache"], strings.Replace(filepath.Base(inPath), ".xz", "", 1)) - errRet = errors.Join(errRet, os.Rename(out.Name(), outPath)) + errRet = errors.Join(errRet, os.Rename(out.Name(), outPath), SyncFS(outPath)) return outPath, errRet } @@ -217,12 +217,12 @@ func CheckIfSame(path1, path2 string) (bool, error) { return false, nil } if err != nil { - return false, errw.Wrapf(err, "cannot evaluate symlinks pointing to %s", path1) + return false, errw.Wrapf(err, "evaluating symlinks pointing to %s", path1) } stat1, err := os.Stat(curPath) if err != nil { - return false, errw.Wrapf(err, "cannot stat %s", curPath) + return false, errw.Wrapf(err, "statting %s", curPath) } realPath, err := filepath.EvalSymlinks(path2) @@ -230,7 +230,7 @@ func CheckIfSame(path1, path2 string) (bool, error) { return false, nil } if err != nil { - return false, errw.Wrapf(err, "cannot evaluate symlinks pointing to %s", path2) + return false, errw.Wrapf(err, "evaluating symlinks pointing to %s", path2) } stat2, err := os.Stat(realPath) @@ -238,7 +238,7 @@ func CheckIfSame(path1, path2 string) (bool, error) { return false, nil } if err != nil { - return false, errw.Wrapf(err, "cannot stat %s", realPath) + return false, errw.Wrapf(err, "statting %s", realPath) } return os.SameFile(stat1, stat2), nil @@ -254,5 +254,18 @@ func ForceSymlink(orig, symlink string) error { if err != nil { return errw.Wrap(err, "symlinking file") } - return nil + + return SyncFS(symlink) +} + +func SyncFS(syncPath string) (errRet error) { + file, errRet := os.Open(filepath.Dir(syncPath)) + if errRet != nil { + return errw.Wrapf(errRet, "syncing fs %s", syncPath) + } + _, _, err := unix.Syscall(unix.SYS_SYNCFS, file.Fd(), 0, 0) + if err != 0 { + errRet = errw.Wrapf(err, "syncing fs %s", syncPath) + } + return errors.Join(errRet, file.Close()) }