diff --git a/cmd/incusd/api_1.0.go b/cmd/incusd/api_1.0.go index 5f38ae4eae4..24f253c67aa 100644 --- a/cmd/incusd/api_1.0.go +++ b/cmd/incusd/api_1.0.go @@ -9,7 +9,7 @@ import ( "os" "strings" - "github.com/lxc/incus/v6/client" + incus "github.com/lxc/incus/v6/client" "github.com/lxc/incus/v6/internal/server/auth" "github.com/lxc/incus/v6/internal/server/auth/oidc" "github.com/lxc/incus/v6/internal/server/cluster" @@ -29,6 +29,7 @@ import ( "github.com/lxc/incus/v6/shared/osarch" "github.com/lxc/incus/v6/shared/revert" localtls "github.com/lxc/incus/v6/shared/tls" + "github.com/lxc/incus/v6/shared/util" ) var api10Cmd = APIEndpoint{ @@ -459,10 +460,7 @@ func api10Put(d *Daemon, r *http.Request) response.Response { // for reacting to the values that changed. if isClusterNotification(r) { logger.Debug("Handling config changed notification") - changed := make(map[string]string) - for key, value := range req.Config { - changed[key] = value - } + changed := util.CloneMap(req.Config) // Get the current (updated) config. var config *clusterConfig.Config @@ -587,7 +585,7 @@ func doApi10Update(d *Daemon, r *http.Request, req api.ServerPut, patch bool) re nodeChanged := map[string]string{} var newNodeConfig *node.Config - oldNodeConfig := make(map[string]string) + var oldNodeConfig map[string]string err := s.DB.Node.Transaction(r.Context(), func(ctx context.Context, tx *db.NodeTx) error { var err error @@ -597,9 +595,7 @@ func doApi10Update(d *Daemon, r *http.Request, req api.ServerPut, patch bool) re } // Keep old config around in case something goes wrong. In that case the config will be reverted. - for k, v := range newNodeConfig.Dump() { - oldNodeConfig[k] = v - } + oldNodeConfig = util.CloneMap(newNodeConfig.Dump()) // We currently don't allow changing the cluster.https_address once it's set. if s.ServerClustered { @@ -687,7 +683,7 @@ func doApi10Update(d *Daemon, r *http.Request, req api.ServerPut, patch bool) re // Then deal with cluster wide configuration var clusterChanged map[string]string var newClusterConfig *clusterConfig.Config - oldClusterConfig := make(map[string]string) + var oldClusterConfig map[string]string err = s.DB.Cluster.Transaction(r.Context(), func(ctx context.Context, tx *db.ClusterTx) error { var err error @@ -697,9 +693,7 @@ func doApi10Update(d *Daemon, r *http.Request, req api.ServerPut, patch bool) re } // Keep old config around in case something goes wrong. In that case the config will be reverted. - for k, v := range newClusterConfig.Dump() { - oldClusterConfig[k] = v - } + oldClusterConfig = util.CloneMap(newClusterConfig.Dump()) if patch { clusterChanged, err = newClusterConfig.Patch(req.Config) @@ -760,11 +754,8 @@ func doApi10Update(d *Daemon, r *http.Request, req api.ServerPut, patch bool) re } serverPut := server.Writable() - serverPut.Config = make(map[string]string) // Only propagated cluster-wide changes - for key, value := range clusterChanged { - serverPut.Config[key] = value - } + serverPut.Config = util.CloneMap(clusterChanged) return client.UpdateServer(serverPut, etag) }) diff --git a/cmd/incusd/api_cluster.go b/cmd/incusd/api_cluster.go index 811983e18b4..e1db42c3c07 100644 --- a/cmd/incusd/api_cluster.go +++ b/cmd/incusd/api_cluster.go @@ -18,7 +18,7 @@ import ( "github.com/gorilla/mux" - "github.com/lxc/incus/v6/client" + incus "github.com/lxc/incus/v6/client" internalInstance "github.com/lxc/incus/v6/internal/instance" "github.com/lxc/incus/v6/internal/server/auth" "github.com/lxc/incus/v6/internal/server/certificate" @@ -759,11 +759,7 @@ func clusterPutJoin(d *Daemon, r *http.Request, req api.ClusterPut) response.Res d.globalConfig = currentClusterConfig d.globalConfigMu.Unlock() - existingConfigDump := currentClusterConfig.Dump() - changes := make(map[string]string, len(existingConfigDump)) - for k, v := range existingConfigDump { - changes[k] = v - } + changes := util.CloneMap(currentClusterConfig.Dump()) err = doApi10UpdateTriggers(d, nil, changes, nodeConfig, currentClusterConfig) if err != nil { diff --git a/cmd/incusd/networks.go b/cmd/incusd/networks.go index cfd1e273cb0..f799bfe74b3 100644 --- a/cmd/incusd/networks.go +++ b/cmd/incusd/networks.go @@ -5,6 +5,7 @@ import ( "encoding/json" "errors" "fmt" + "maps" "net" "net/http" "net/url" @@ -716,10 +717,7 @@ func networksPostCluster(ctx context.Context, s *state.State, projectName string // Clone the network config for this node so we don't modify it and potentially end up sending // this node's config to another node. - nodeConfig := make(map[string]string, len(netConfig)) - for k, v := range netConfig { - nodeConfig[k] = v - } + nodeConfig := util.CloneMap(netConfig) // Merge node specific config items into global config. for key, value := range nodeConfigs[server.Environment.ServerName] { diff --git a/cmd/incusd/storage_pools.go b/cmd/incusd/storage_pools.go index d111ce441a7..3044b01509a 100644 --- a/cmd/incusd/storage_pools.go +++ b/cmd/incusd/storage_pools.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "maps" "net/http" "net/url" "slices" @@ -554,10 +555,7 @@ func storagePoolsPostCluster(ctx context.Context, s *state.State, pool *api.Stor // Clone fresh node config so we don't modify req.Config with this node's specific config which // could result in it being sent to other nodes later. - nodeReq.Config = make(map[string]string, len(req.Config)) - for k, v := range req.Config { - nodeReq.Config[k] = v - } + nodeReq.Config = util.CloneMap(req.Config) // Merge node specific config items into global config. for key, value := range configs[server.Environment.ServerName] { diff --git a/internal/server/device/config/devices.go b/internal/server/device/config/devices.go index 8f63254360f..7bac745313a 100644 --- a/internal/server/device/config/devices.go +++ b/internal/server/device/config/devices.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/lxc/incus/v6/shared/api" + "github.com/lxc/incus/v6/shared/util" ) // Device represents an instance device. @@ -13,13 +14,7 @@ type Device map[string]string // Clone returns a copy of the Device. func (device Device) Clone() Device { - copy := make(map[string]string, len(device)) - - for k, v := range device { - copy[k] = v - } - - return copy + return util.CloneMap(device) } // Validate accepts a map of field/validation functions to run against the device's config. diff --git a/internal/server/operations/operations.go b/internal/server/operations/operations.go index 1bcabe3f8a2..5cd8d2ff2bb 100644 --- a/internal/server/operations/operations.go +++ b/internal/server/operations/operations.go @@ -19,6 +19,7 @@ import ( "github.com/lxc/incus/v6/shared/api" "github.com/lxc/incus/v6/shared/cancel" "github.com/lxc/incus/v6/shared/logger" + "github.com/lxc/incus/v6/shared/util" ) var debug bool @@ -66,12 +67,7 @@ func Clone() map[string]*Operation { operationsLock.Lock() defer operationsLock.Unlock() - localOperations := make(map[string]*Operation, len(operations)) - for k, v := range operations { - localOperations[k] = v - } - - return localOperations + return util.CloneMap(operations) } // OperationGetInternal returns the operation with the given id. It returns an diff --git a/internal/server/storage/backend.go b/internal/server/storage/backend.go index 755db3b0700..c716a14e67d 100644 --- a/internal/server/storage/backend.go +++ b/internal/server/storage/backend.go @@ -10,6 +10,7 @@ import ( "fmt" "io" "io/fs" + "maps" "net/http" "net/url" "os" @@ -4032,10 +4033,7 @@ func (b *backend) ImportBucket(projectName string, poolVol *backupConfig.Config, defer revert.Fail() // Copy bucket config from backup file if present (so BucketDBCreate can safely modify the copy if needed). - bucketConfig := make(map[string]string, len(poolVol.Bucket.Config)) - for k, v := range poolVol.Bucket.Config { - bucketConfig[k] = v - } + bucketConfig := util.CloneMap(poolVol.Bucket.Config) bucket := &api.StorageBucketsPost{ Name: poolVol.Bucket.Name, @@ -5810,10 +5808,7 @@ func (b *backend) ImportCustomVolume(projectName string, poolVol *backupConfig.C defer revert.Fail() // Copy volume config from backup file if present (so VolumeDBCreate can safely modify the copy if needed). - volumeConfig := make(map[string]string, len(poolVol.Volume.Config)) - for k, v := range poolVol.Volume.Config { - volumeConfig[k] = v - } + volumeConfig := util.CloneMap(poolVol.Volume.Config) // Validate config and create database entry for restored storage volume. err := VolumeDBCreate(b, projectName, poolVol.Volume.Name, poolVol.Volume.Description, drivers.VolumeTypeCustom, false, volumeConfig, poolVol.Volume.CreatedAt, time.Time{}, drivers.ContentType(poolVol.Volume.ContentType), false, true) @@ -5829,10 +5824,7 @@ func (b *backend) ImportCustomVolume(projectName string, poolVol *backupConfig.C // Copy volume config from backup file if present // (so VolumeDBCreate can safely modify the copy if needed). - snapVolumeConfig := make(map[string]string, len(poolVolSnap.Config)) - for k, v := range poolVolSnap.Config { - snapVolumeConfig[k] = v - } + snapVolumeConfig := util.CloneMap(poolVolSnap.Config) // Validate config and create database entry for restored storage volume. err = VolumeDBCreate(b, projectName, fullSnapName, poolVolSnap.Description, drivers.VolumeTypeCustom, true, snapVolumeConfig, poolVolSnap.CreatedAt, time.Time{}, drivers.ContentType(poolVolSnap.ContentType), false, true) @@ -6881,10 +6873,7 @@ func (b *backend) ImportInstance(inst instance.Instance, poolVol *backupConfig.C // Copy volume config from backup file config if present, // so VolumeDBCreate can safely modify the copy if needed. if poolVol.Volume != nil { - volumeConfig = make(map[string]string, len(poolVol.Volume.Config)) - for k, v := range poolVol.Volume.Config { - volumeConfig[k] = v - } + volumeConfig = util.CloneMap(poolVol.Volume.Config) if !poolVol.Volume.CreatedAt.IsZero() { creationDate = poolVol.Volume.CreatedAt @@ -6906,10 +6895,7 @@ func (b *backend) ImportInstance(inst instance.Instance, poolVol *backupConfig.C // Copy volume config from backup file if present, // so VolumeDBCreate can safely modify the copy if needed. - snapVolumeConfig := make(map[string]string, len(poolVolSnap.Config)) - for k, v := range poolVolSnap.Config { - snapVolumeConfig[k] = v - } + snapVolumeConfig := util.CloneMap(poolVolSnap.Config) // Validate config and create database entry for recovered storage volume. err = VolumeDBCreate(b, inst.Project().Name, fullSnapName, poolVolSnap.Description, volType, true, snapVolumeConfig, poolVolSnap.CreatedAt, time.Time{}, contentType, false, true) diff --git a/internal/server/storage/drivers/driver_common.go b/internal/server/storage/drivers/driver_common.go index 801efdbcd29..f5c91367e31 100644 --- a/internal/server/storage/drivers/driver_common.go +++ b/internal/server/storage/drivers/driver_common.go @@ -3,6 +3,7 @@ package drivers import ( "fmt" "io" + "maps" "net/url" "os/exec" "regexp" @@ -240,12 +241,7 @@ func (d *common) Logger() logger.Logger { // Config returns the storage pool config (as a copy, so not modifiable). func (d *common) Config() map[string]string { - confCopy := make(map[string]string, len(d.config)) - for k, v := range d.config { - confCopy[k] = v - } - - return confCopy + return util.CloneMap(d.config) } // ApplyPatch looks for a suitable patch and runs it. diff --git a/internal/server/storage/drivers/volume.go b/internal/server/storage/drivers/volume.go index 68ad091ca6d..fb8b0e60505 100644 --- a/internal/server/storage/drivers/volume.go +++ b/internal/server/storage/drivers/volume.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "maps" "os" "slices" "strings" @@ -577,16 +578,10 @@ func (v *Volume) SetHasSource(hasSource bool) { // Clone returns a copy of the volume. func (v Volume) Clone() Volume { // Copy the config map to avoid internal modifications affecting external state. - newConfig := make(map[string]string, len(v.config)) - for k, v := range v.config { - newConfig[k] = v - } + newConfig := util.CloneMap(v.config) // Copy the pool config map to avoid internal modifications affecting external state. - newPoolConfig := make(map[string]string, len(v.poolConfig)) - for k, v := range v.poolConfig { - newPoolConfig[k] = v - } + newPoolConfig := util.CloneMap(v.poolConfig) return NewVolume(v.driver, v.pool, v.volType, v.contentType, v.name, newConfig, newPoolConfig) } diff --git a/internal/server/util/config.go b/internal/server/util/config.go index 60f23137a1b..a9e825ce8ca 100644 --- a/internal/server/util/config.go +++ b/internal/server/util/config.go @@ -2,6 +2,7 @@ package util import ( "fmt" + "maps" "slices" "sort" "strings" @@ -52,10 +53,5 @@ func CompareConfigs(config1, config2 map[string]string, exclude []string) error // CopyConfig creates a new map with a copy of the given config. func CopyConfig(config map[string]string) map[string]string { - copy := make(map[string]string, len(config)) - for key, value := range config { - copy[key] = value - } - - return copy + return util.CloneMap(config) } diff --git a/shared/cliconfig/default.go b/shared/cliconfig/default.go index a060741900a..46375001549 100644 --- a/shared/cliconfig/default.go +++ b/shared/cliconfig/default.go @@ -1,5 +1,7 @@ package cliconfig +import "github.com/lxc/incus/v6/shared/util" + // LocalRemote is the default local remote (over the unix socket). var LocalRemote = Remote{ Addr: "unix://", @@ -28,14 +30,8 @@ var DefaultRemotes = map[string]Remote{ // DefaultConfig returns the default configuration. func DefaultConfig() *Config { - // Duplicate remotes from DefaultRemotes. - defaultRoutes := make(map[string]Remote, len(DefaultRemotes)) - for k, v := range DefaultRemotes { - defaultRoutes[k] = v - } - return &Config{ - Remotes: defaultRoutes, + Remotes: util.CloneMap(DefaultRemotes), Aliases: make(map[string]string), DefaultRemote: "local", }