diff --git a/.golangci.yml b/.golangci.yml index 273057e5e09..5caf4533117 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -1,10 +1,10 @@ linters: enable: + - gci + - godot - gofmt - misspell - - godot - whitespace - - gci linters-settings: gci: sections: diff --git a/Makefile b/Makefile index 00ea74118db..4504c05608e 100644 --- a/Makefile +++ b/Makefile @@ -294,5 +294,17 @@ endif shellcheck test/extras/*.sh run-parts --exit-on-error --regex '.sh' test/lint +.PHONY: staticcheck +staticcheck: +ifeq ($(shell command -v staticcheck),) + (cd / ; go install -v -x honnef.co/go/tools/cmd/staticcheck@latest) +endif + # To get advance notice of deprecated function usage, consider running: + # sed -i 's/^go 1\.[0-9]\+$/go 1.18/' go.mod + # before 'make staticcheck'. + + # Run staticcheck against all the dirs containing Go files. + staticcheck $$(git ls-files *.go | sed 's|^|./|; s|/[^/]\+\.go$$||' | sort -u) + tags: */*.go find . -type f -name '*.go' | gotags -L - -f tags diff --git a/cmd/incusd/api.go b/cmd/incusd/api.go index 5c14d10413d..70396aac782 100644 --- a/cmd/incusd/api.go +++ b/cmd/incusd/api.go @@ -74,7 +74,7 @@ func restServer(d *Daemon) *http.Server { uiHttpDir := uiHttpDir{http.Dir(uiPath)} mux.PathPrefix("/ui/").Handler(http.StripPrefix("/ui/", http.FileServer(uiHttpDir))) mux.HandleFunc("/ui", func(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, "/ui/", 301) + http.Redirect(w, r, "/ui/", http.StatusMovedPermanently) }) } @@ -85,7 +85,7 @@ func restServer(d *Daemon) *http.Server { documentationHttpDir := documentationHttpDir{http.Dir(documentationPath)} mux.PathPrefix("/documentation/").Handler(http.StripPrefix("/documentation/", http.FileServer(documentationHttpDir))) mux.HandleFunc("/documentation", func(w http.ResponseWriter, r *http.Request) { - http.Redirect(w, r, "/documentation/", 301) + http.Redirect(w, r, "/documentation/", http.StatusMovedPermanently) }) } @@ -123,7 +123,7 @@ func restServer(d *Daemon) *http.Server { ua := r.Header.Get("User-Agent") if uiEnabled && strings.Contains(ua, "Gecko") { // Web browser handling. - http.Redirect(w, r, "/ui/", 301) + http.Redirect(w, r, "/ui/", http.StatusMovedPermanently) } else { // Normal client handling. _ = response.SyncResponse(true, []string{"/1.0"}).Render(w) diff --git a/cmd/incusd/api_cluster.go b/cmd/incusd/api_cluster.go index 6b5306fbf7e..1b7504ca7d4 100644 --- a/cmd/incusd/api_cluster.go +++ b/cmd/incusd/api_cluster.go @@ -711,7 +711,8 @@ func clusterPutJoin(d *Daemon, r *http.Request, req api.ClusterPut) response.Res } } - // Update cached trusted certificates. + // Update cached trusted certificates (this adds the server certificates we collected above) so that we are able to join. + // Client and metric type certificates from the cluster we are joining will not be added until later. s.UpdateCertificateCache() // Update local setup and possibly join the raft dqlite cluster. @@ -813,6 +814,9 @@ func clusterPutJoin(d *Daemon, r *http.Request, req api.ClusterPut) response.Res logger.Warn("Failed to sync images") } + // Update the cert cache again to add client and metric certs to the cache. + s.UpdateCertificateCache() + s.Events.SendLifecycle(projectParam(r), lifecycle.ClusterMemberAdded.Event(req.ServerName, op.Requestor(), nil)) revert.Success() diff --git a/cmd/incusd/dev_incus.go b/cmd/incusd/dev_incus.go index cf405ad7a2c..aaa7fcdf153 100644 --- a/cmd/incusd/dev_incus.go +++ b/cmd/incusd/dev_incus.go @@ -305,7 +305,7 @@ func hoistReq(f func(*Daemon, instance.Instance, http.ResponseWriter, *http.Requ } if rootUID != cred.Uid { - http.Error(w, "Access denied for non-root user", 401) + http.Error(w, "Access denied for non-root user", http.StatusUnauthorized) return } diff --git a/cmd/incusd/patches.go b/cmd/incusd/patches.go index 47b46148292..3a5929f158b 100644 --- a/cmd/incusd/patches.go +++ b/cmd/incusd/patches.go @@ -784,6 +784,11 @@ func patchStorageRenameCustomISOBlockVolumes(name string, d *Daemon) error { } for _, vol := range volumes { + // In a non-clusted environment ServerName will be empty. + if s.ServerName != "" && vol.Location != s.ServerName { + continue + } + // Exclude non-ISO custom volumes. if vol.ContentType != db.StoragePoolVolumeContentTypeNameISO { continue @@ -859,6 +864,11 @@ func patchZfsSetContentTypeUserProperty(name string, d *Daemon) error { } for _, vol := range volumes { + // In a non-clusted environment ServerName will be empty. + if s.ServerName != "" && vol.Location != s.ServerName { + continue + } + zfsPoolName := p.Driver().Config()["zfs.pool_name"] if zfsPoolName != "" { poolName = zfsPoolName @@ -946,7 +956,8 @@ func patchStorageZfsUnsetInvalidBlockSettings(_ string, d *Daemon) error { for pool, volumes := range poolVolumes { for _, vol := range volumes { - if vol.Location != s.ServerName { + // In a non-clusted environment ServerName will be empty. + if s.ServerName != "" && vol.Location != s.ServerName { continue } diff --git a/cmd/incusd/storage_pools.go b/cmd/incusd/storage_pools.go index 2da6d9af02f..8ca7a2a4a0c 100644 --- a/cmd/incusd/storage_pools.go +++ b/cmd/incusd/storage_pools.go @@ -624,7 +624,7 @@ func storagePoolGet(d *Daemon, r *http.Request) response.Response { poolAPI.Status = pool.LocalStatus() } - etag := []any{pool.Name, pool.Driver, poolAPI.Config} + etag := []any{pool.Name(), pool.Driver().Info().Name, pool.Description(), poolAPI.Config} return response.SyncResponseETag(true, &poolAPI, etag) } @@ -711,7 +711,7 @@ func storagePoolPut(d *Daemon, r *http.Request) response.Response { } // Validate the ETag. - etag := []any{pool.Name(), pool.Driver().Info().Name, etagConfig} + etag := []any{pool.Name(), pool.Driver().Info().Name, pool.Description(), etagConfig} err = localUtil.EtagCheck(r, etag) if err != nil { diff --git a/doc/howto/instances_create.md b/doc/howto/instances_create.md index 6cdf1a23086..4f7233e7e0b 100644 --- a/doc/howto/instances_create.md +++ b/doc/howto/instances_create.md @@ -26,7 +26,7 @@ Flags The most common flags are: - `--config` to specify a configuration option for the new instance - - `--device` to override {ref}`device options ` for a device provided through a profile + - `--device` to override {ref}`device options ` for a device provided through a profile, or to specify an {ref}`initial configuration for the root disk device ` - `--profile` to specify a {ref}`profile ` to use for the new instance - `--network` or `--storage` to make the new instance use a specific network or storage pool - `--target` to create the instance on a specific cluster member diff --git a/doc/installing.md b/doc/installing.md index 314ecafe6fc..9ad6ca184e8 100644 --- a/doc/installing.md +++ b/doc/installing.md @@ -111,6 +111,13 @@ sudo apt update sudo apt install acl attr autoconf automake dnsmasq-base git golang libacl1-dev libcap-dev liblxc1 liblxc-dev libsqlite3-dev libtool libudev-dev liblz4-dev libuv1-dev make pkg-config rsync squashfs-tools tar tcl xz-utils ebtables ``` +```{note} +If you use the `liblxc-dev` package and get compile time errors when building the `go-lxc` module, +ensure that the value for `INC_DEVEL` is `0` for your `liblxc` build. To check that, look at `/usr/include/lxc/version.h`. +If the `INC_DEVEL` value is `1`, replace it with `0` to work around the problem. It's a packaging bug, and +we are aware of it for Ubuntu 22.04/22.10. Ubuntu 23.04/23.10 does not have this problem. +``` + There are a few storage drivers for Incus besides the default `dir` driver. Installing these tools adds a bit to initramfs and may slow down your host boot, but are needed if you'd like to use a particular driver: diff --git a/internal/server/auth/oidc/oidc.go b/internal/server/auth/oidc/oidc.go index b1125a0594b..cd275f5afce 100644 --- a/internal/server/auth/oidc/oidc.go +++ b/internal/server/auth/oidc/oidc.go @@ -211,7 +211,7 @@ func (o *Verifier) Callback(w http.ResponseWriter, r *http.Request) { // Send to the UI. // NOTE: Once the UI does the redirection on its own, we may be able to use the referer here instead. - http.Redirect(w, r, "/ui/", 301) + http.Redirect(w, r, "/ui/", http.StatusMovedPermanently) }, provider) handler(w, r) diff --git a/internal/server/firewall/drivers/drivers_nftables_templates.go b/internal/server/firewall/drivers/drivers_nftables_templates.go index a2bf3883197..ecc1d76c87b 100644 --- a/internal/server/firewall/drivers/drivers_nftables_templates.go +++ b/internal/server/firewall/drivers/drivers_nftables_templates.go @@ -136,7 +136,7 @@ table {{.family}} {{.namespace}} { # Allow core ICMPv6 to Incus host. iifname "{{$.networkName}}" icmpv6 type {1, 2, 3, 4, 133, 135, 136, 143} accept - iifname {{.networkName}} jump acl{{.chainSeparator}}{{.networkName}} + iifname "{{.networkName}}" jump acl{{.chainSeparator}}{{.networkName}} } chain aclout{{.chainSeparator}}{{.networkName}} { @@ -150,12 +150,12 @@ table {{.family}} {{.namespace}} { # Allow ICMPv6 ping from host into network as dnsmasq uses this to probe IP allocations. oifname "{{$.networkName}}" icmpv6 type {1, 2, 3, 4, 128, 134, 135, 136, 143} accept - oifname {{.networkName}} jump acl{{.chainSeparator}}{{.networkName}} + oifname "{{.networkName}}" jump acl{{.chainSeparator}}{{.networkName}} } chain aclfwd{{.chainSeparator}}{{.networkName}} { - iifname {{.networkName}} jump acl{{.chainSeparator}}{{.networkName}} - oifname {{.networkName}} jump acl{{.chainSeparator}}{{.networkName}} + iifname "{{.networkName}}" jump acl{{.chainSeparator}}{{.networkName}} + oifname "{{.networkName}}" jump acl{{.chainSeparator}}{{.networkName}} } } `)) diff --git a/internal/server/instance/drivers/driver_lxc.go b/internal/server/instance/drivers/driver_lxc.go index 8117ab3c712..7a4d8e36360 100644 --- a/internal/server/instance/drivers/driver_lxc.go +++ b/internal/server/instance/drivers/driver_lxc.go @@ -1529,7 +1529,7 @@ func (d *lxc) deviceAddCgroupRules(cgroups []deviceConfig.RunConfigItem) error { // Add the new device cgroup rule. err := d.CGroupSet(rule.Key, rule.Value) if err != nil { - return fmt.Errorf("Failed to add cgroup rule for device") + return fmt.Errorf("Failed to add cgroup rule for device: %w", err) } } @@ -1752,7 +1752,10 @@ func (d *lxc) deviceHandleMounts(mounts []deviceConfig.MountEntryItem) error { // DeviceEventHandler actions the results of a RunConfig after an event has occurred on a device. func (d *lxc) DeviceEventHandler(runConf *deviceConfig.RunConfig) error { // Device events can only be processed when the container is running. - if !d.IsRunning() { + // We use InitPID here rather than IsRunning because this task can be triggered during the + // container startup process, which is during the time that the start lock is held, which causes + // IsRunning to return false (because the container hasn't fully started yet). + if d.InitPID() <= 0 { return nil } @@ -3967,8 +3970,12 @@ func (d *lxc) CGroupSet(key string, value string) error { return err } - // Make sure the container is running - if !d.IsRunning() { + // Make sure the container is running. + // We use InitPID here rather than IsRunning because this task can be triggered during the container's + // startup process, which is during the time that the start lock is held, which causes IsRunning to + // return false (because the container hasn't fully started yet) but it is sufficiently started to + // have its cgroup disk limits set. + if d.InitPID() <= 0 { return fmt.Errorf("Can't set cgroups on a stopped container") } diff --git a/staticcheck.conf b/staticcheck.conf new file mode 100644 index 00000000000..36f62fda668 --- /dev/null +++ b/staticcheck.conf @@ -0,0 +1,3 @@ +# Checks being ignored: +# ST1005: error strings should not be capitalized (5585 occurences as of 2023-10-20) +checks = ["inherit", "-ST1005"]