From 2ab58d24d41539ffe1fd77e266170f62b7e05a37 Mon Sep 17 00:00:00 2001 From: Miek Gieben Date: Sat, 21 Mar 2026 12:16:09 +0100 Subject: [PATCH] chore: modernize the codebase using go 1.26 (#278) This runs: ``` go run golang.org/x/tools/gopls/internal/analysis/modernize/cmd/modernize@latest -fix ./... ``` over the codebase, as this is using go 1.26, it can also use the new new() functionallity so AsPtr and boolPtr is are needed anymore. Signed-off-by: Miek Gieben --- cmd/uncloud/ps_test.go | 72 ++++++++++----------- cmd/uncloud/service/run.go | 4 +- experiment/logger.go | 24 +++---- internal/cli/flags.go | 2 +- internal/dns/api.go | 2 +- internal/machine/api/proxy/director.go | 2 +- internal/machine/caddyconfig/caddyfile.go | 7 +- internal/machine/firewall/iptables_linux.go | 2 +- internal/proxy/proxy.go | 2 +- pkg/api/client.go | 4 +- pkg/api/config_test.go | 13 ++-- pkg/api/service.go | 10 +-- pkg/api/service_test.go | 8 +-- pkg/api/volume.go | 9 +-- pkg/client/compose/machines.go | 4 +- pkg/client/compose/service.go | 4 +- pkg/client/image.go | 10 ++- pkg/client/logmerger_test.go | 2 +- test/e2e/service_test.go | 2 +- 19 files changed, 82 insertions(+), 101 deletions(-) diff --git a/cmd/uncloud/ps_test.go b/cmd/uncloud/ps_test.go index 36337a19..567bbd6e 100644 --- a/cmd/uncloud/ps_test.go +++ b/cmd/uncloud/ps_test.go @@ -39,25 +39,25 @@ func (m *mockClusterClient) ListMachines(ctx context.Context, in *emptypb.Empty, func TestCollectContainers_NilMetadata(t *testing.T) { // Setup container data - containerData := map[string]interface{}{ + containerData := map[string]any{ "Id": "container1", "Name": "test-container", - "Config": map[string]interface{}{ + "Config": map[string]any{ "Image": "test-image", }, - "State": map[string]interface{}{ + "State": map[string]any{ "Status": "running", "StartedAt": "2023-01-01T12:00:00Z", "FinishedAt": "0001-01-01T00:00:00Z", }, - "NetworkSettings": map[string]interface{}{ - "Networks": map[string]interface{}{}, + "NetworkSettings": map[string]any{ + "Networks": map[string]any{}, }, } containerJSON, err := json.Marshal(containerData) require.NoError(t, err) - serviceSpecJSON, err := json.Marshal(map[string]interface{}{}) + serviceSpecJSON, err := json.Marshal(map[string]any{}) require.NoError(t, err) // Setup mocks @@ -120,28 +120,28 @@ func TestCollectContainers_NilMetadata_MultipleMachines_Error(t *testing.T) { // If we have multiple machines but receive nil metadata, it should return an error as it is ambiguous // Setup container data - containerData1 := map[string]interface{}{ + containerData1 := map[string]any{ "Id": "container1", } containerJSON1, _ := json.Marshal(containerData1) - containerData2 := map[string]interface{}{ + containerData2 := map[string]any{ "Id": "container2", - "Config": map[string]interface{}{ + "Config": map[string]any{ "Image": "test-image", }, - "State": map[string]interface{}{ + "State": map[string]any{ "Status": "running", "StartedAt": "2023-01-01T12:00:00Z", "FinishedAt": "0001-01-01T00:00:00Z", }, - "NetworkSettings": map[string]interface{}{ - "Networks": map[string]interface{}{}, + "NetworkSettings": map[string]any{ + "Networks": map[string]any{}, }, } containerJSON2, _ := json.Marshal(containerData2) - serviceSpecJSON, _ := json.Marshal(map[string]interface{}{}) + serviceSpecJSON, _ := json.Marshal(map[string]any{}) // Setup mocks mockDocker := &mockDockerClient{ @@ -210,41 +210,41 @@ func TestCollectContainers_MetadataPresent_MultipleMachines(t *testing.T) { // Verify correct mapping of containers to machines when metadata is present // Setup container data - containerData1 := map[string]interface{}{ + containerData1 := map[string]any{ "Id": "container1", "Name": "container-1", - "Config": map[string]interface{}{ + "Config": map[string]any{ "Image": "image-1", }, - "State": map[string]interface{}{ + "State": map[string]any{ "Status": "running", "StartedAt": "2023-01-01T12:00:00Z", "FinishedAt": "0001-01-01T00:00:00Z", }, - "NetworkSettings": map[string]interface{}{ - "Networks": map[string]interface{}{}, + "NetworkSettings": map[string]any{ + "Networks": map[string]any{}, }, } containerJSON1, _ := json.Marshal(containerData1) - containerData2 := map[string]interface{}{ + containerData2 := map[string]any{ "Id": "container2", "Name": "container-2", - "Config": map[string]interface{}{ + "Config": map[string]any{ "Image": "image-2", }, - "State": map[string]interface{}{ + "State": map[string]any{ "Status": "running", "StartedAt": "2023-01-01T12:00:00Z", "FinishedAt": "0001-01-01T00:00:00Z", }, - "NetworkSettings": map[string]interface{}{ - "Networks": map[string]interface{}{}, + "NetworkSettings": map[string]any{ + "Networks": map[string]any{}, }, } containerJSON2, _ := json.Marshal(containerData2) - serviceSpecJSON, _ := json.Marshal(map[string]interface{}{}) + serviceSpecJSON, _ := json.Marshal(map[string]any{}) // Setup mocks mockDocker := &mockDockerClient{ @@ -321,22 +321,22 @@ func TestCollectContainers_MetadataPresent_MultipleMachines(t *testing.T) { func TestCollectContainers_NilMetadata_NoMachines(t *testing.T) { // Case: 1 msc with nil metadata but no machines at all - containerData := map[string]interface{}{ + containerData := map[string]any{ "Id": "container1", - "Config": map[string]interface{}{ + "Config": map[string]any{ "Image": "test-image", }, - "State": map[string]interface{}{ + "State": map[string]any{ "Status": "running", "StartedAt": "2023-01-01T12:00:00Z", "FinishedAt": "0001-01-01T00:00:00Z", }, - "NetworkSettings": map[string]interface{}{ - "Networks": map[string]interface{}{}, + "NetworkSettings": map[string]any{ + "Networks": map[string]any{}, }, } containerJSON, _ := json.Marshal(containerData) - serviceSpecJSON, _ := json.Marshal(map[string]interface{}{}) + serviceSpecJSON, _ := json.Marshal(map[string]any{}) mockDocker := &mockDockerClient{ listResp: &pb.ListServiceContainersResponse{ @@ -378,22 +378,22 @@ func TestCollectContainers_NilMetadata_NoMachines(t *testing.T) { func TestCollectContainers_MetadataPresent_NotInMapping(t *testing.T) { // Case: msc with metadata that is not in the IP-to-name mapping - containerData := map[string]interface{}{ + containerData := map[string]any{ "Id": "container1", - "Config": map[string]interface{}{ + "Config": map[string]any{ "Image": "test-image", }, - "State": map[string]interface{}{ + "State": map[string]any{ "Status": "running", "StartedAt": "2023-01-01T12:00:00Z", "FinishedAt": "0001-01-01T00:00:00Z", }, - "NetworkSettings": map[string]interface{}{ - "Networks": map[string]interface{}{}, + "NetworkSettings": map[string]any{ + "Networks": map[string]any{}, }, } containerJSON, _ := json.Marshal(containerData) - serviceSpecJSON, _ := json.Marshal(map[string]interface{}{}) + serviceSpecJSON, _ := json.Marshal(map[string]any{}) mockDocker := &mockDockerClient{ listResp: &pb.ListServiceContainersResponse{ diff --git a/cmd/uncloud/service/run.go b/cmd/uncloud/service/run.go index cb355b71..6f1d733a 100644 --- a/cmd/uncloud/service/run.go +++ b/cmd/uncloud/service/run.go @@ -358,8 +358,8 @@ func parseVolumeFlagValue(volume string) (api.VolumeSpec, api.VolumeMount, error volumeNoCopy := false if len(parts) == 3 { - opts := strings.Split(parts[2], ",") - for _, opt := range opts { + opts := strings.SplitSeq(parts[2], ",") + for opt := range opts { switch opt { case "ro", "readonly": mount.ReadOnly = true diff --git a/experiment/logger.go b/experiment/logger.go index 10dc2aa0..793400d9 100644 --- a/experiment/logger.go +++ b/experiment/logger.go @@ -17,57 +17,57 @@ func newIPFSLogger(l *slog.Logger) *ipfsLogger { return &ipfsLogger{log: *l} } -func (l *ipfsLogger) Debug(args ...interface{}) { +func (l *ipfsLogger) Debug(args ...any) { l.log.Debug(fmt.Sprint(args...)) } -func (l *ipfsLogger) Debugf(format string, args ...interface{}) { +func (l *ipfsLogger) Debugf(format string, args ...any) { l.log.Debug(fmt.Sprintf(format, args...)) } -func (l *ipfsLogger) Error(args ...interface{}) { +func (l *ipfsLogger) Error(args ...any) { l.log.Error(fmt.Sprint(args...)) } -func (l *ipfsLogger) Errorf(format string, args ...interface{}) { +func (l *ipfsLogger) Errorf(format string, args ...any) { l.log.Error(fmt.Sprintf(format, args...)) } -func (l *ipfsLogger) Fatal(args ...interface{}) { +func (l *ipfsLogger) Fatal(args ...any) { l.log.Error(fmt.Sprint(args...)) os.Exit(1) } -func (l *ipfsLogger) Fatalf(format string, args ...interface{}) { +func (l *ipfsLogger) Fatalf(format string, args ...any) { l.log.Error(fmt.Sprintf(format, args...)) os.Exit(1) } -func (l *ipfsLogger) Info(args ...interface{}) { +func (l *ipfsLogger) Info(args ...any) { l.log.Info(fmt.Sprint(args...)) } -func (l *ipfsLogger) Infof(format string, args ...interface{}) { +func (l *ipfsLogger) Infof(format string, args ...any) { l.log.Info(fmt.Sprintf(format, args...)) } -func (l *ipfsLogger) Panic(args ...interface{}) { +func (l *ipfsLogger) Panic(args ...any) { msg := fmt.Sprint(args...) l.log.Error(msg) panic(msg) } -func (l *ipfsLogger) Panicf(format string, args ...interface{}) { +func (l *ipfsLogger) Panicf(format string, args ...any) { msg := fmt.Sprintf(format, args...) l.log.Error(msg) panic(msg) } -func (l *ipfsLogger) Warn(args ...interface{}) { +func (l *ipfsLogger) Warn(args ...any) { l.log.Warn(fmt.Sprint(args...)) } -func (l *ipfsLogger) Warnf(format string, args ...interface{}) { +func (l *ipfsLogger) Warnf(format string, args ...any) { l.log.Warn(fmt.Sprintf(format, args...)) } diff --git a/internal/cli/flags.go b/internal/cli/flags.go index c94eff20..64883294 100644 --- a/internal/cli/flags.go +++ b/internal/cli/flags.go @@ -20,7 +20,7 @@ func ExpandCommaSeparatedValues(values []string) []string { var expanded []string for _, value := range values { - for _, v := range strings.Split(value, ",") { + for v := range strings.SplitSeq(value, ",") { if v = strings.TrimSpace(v); v != "" { expanded = append(expanded, v) } diff --git a/internal/dns/api.go b/internal/dns/api.go index ec160cc6..1f56f594 100644 --- a/internal/dns/api.go +++ b/internal/dns/api.go @@ -26,7 +26,7 @@ type RecordResponse struct { type AuthErrorResponse struct { Status int `json:"status,omitempty"` Message string `json:"msg,omitempty"` - Data authErrorData `json:"data,omitempty"` + Data authErrorData `json:"data"` } type authErrorData struct { diff --git a/internal/machine/api/proxy/director.go b/internal/machine/api/proxy/director.go index 65f27274..5d6085d0 100644 --- a/internal/machine/api/proxy/director.go +++ b/internal/machine/api/proxy/director.go @@ -107,7 +107,7 @@ func (d *Director) remoteBackend(addr string) (*RemoteBackend, error) { // FlushRemoteBackends closes all remote backend connections and removes them from the cache. func (d *Director) FlushRemoteBackends() { - d.remoteBackends.Range(func(key, value interface{}) bool { + d.remoteBackends.Range(func(key, value any) bool { backend, ok := value.(*RemoteBackend) if !ok { return true diff --git a/internal/machine/caddyconfig/caddyfile.go b/internal/machine/caddyconfig/caddyfile.go index e8b77942..979a2307 100644 --- a/internal/machine/caddyconfig/caddyfile.go +++ b/internal/machine/caddyconfig/caddyfile.go @@ -226,12 +226,13 @@ func (g *CaddyfileGenerator) Generate( // Append error summary as comment if there were any invalid configs. if len(configErrors) > 0 { - errorsComment := "# Skipped invalid user-defined configs:\n" + var errorsComment strings.Builder + errorsComment.WriteString("# Skipped invalid user-defined configs:\n") for _, e := range configErrors { - errorsComment += fmt.Sprintf("# - %s\n", e) + errorsComment.WriteString(fmt.Sprintf("# - %s\n", e)) } - caddyfile += "\n" + errorsComment + caddyfile += "\n" + errorsComment.String() } return caddyfileHeader + "\n" + caddyfile, nil diff --git a/internal/machine/firewall/iptables_linux.go b/internal/machine/firewall/iptables_linux.go index ee637786..d1da0c9d 100644 --- a/internal/machine/firewall/iptables_linux.go +++ b/internal/machine/firewall/iptables_linux.go @@ -103,7 +103,7 @@ func createIptablesChains() error { } firstRejectRuleNum := 0 - for _, line := range strings.Split(string(out), "\n") { + for line := range strings.SplitSeq(string(out), "\n") { fields := strings.Fields(line) if len(fields) < 2 { continue diff --git a/internal/proxy/proxy.go b/internal/proxy/proxy.go index 272928d1..8d959213 100644 --- a/internal/proxy/proxy.go +++ b/internal/proxy/proxy.go @@ -116,7 +116,7 @@ func (p *Proxy) handleConnection(ctx context.Context, localConn net.Conn) { }() // Wait for both copies to complete or context cancel. - for i := 0; i < 2; i++ { + for range 2 { select { case <-ctx.Done(): // Close connections to abort ongoing copies. diff --git a/pkg/api/client.go b/pkg/api/client.go index 0d752fa2..55214d13 100644 --- a/pkg/api/client.go +++ b/pkg/api/client.go @@ -62,6 +62,8 @@ type VolumeClient interface { } // AsPtr returns a pointer to the given value. Useful for optional fields in API structs. +// +//go:fix inline func AsPtr[T any](v T) *T { - return &v + return new(v) } diff --git a/pkg/api/config_test.go b/pkg/api/config_test.go index 9f45208f..d2524ad2 100644 --- a/pkg/api/config_test.go +++ b/pkg/api/config_test.go @@ -8,11 +8,6 @@ import ( "github.com/stretchr/testify/require" ) -// uint64Ptr is a convenience function to create a pointer to a uint64 value -func uint64Ptr(v uint64) *uint64 { - return &v -} - func TestConfigMount_GetNumericUid(t *testing.T) { t.Parallel() @@ -30,12 +25,12 @@ func TestConfigMount_GetNumericUid(t *testing.T) { { name: "valid numeric uid", uid: "1000", - expected: uint64Ptr(1000), + expected: new(uint64(1000)), }, { name: "zero uid", uid: "0", - expected: uint64Ptr(0), + expected: new(uint64(0)), }, { name: "invalid non-numeric uid", @@ -96,12 +91,12 @@ func TestConfigMount_GetNumericGid(t *testing.T) { { name: "valid numeric gid", gid: "1000", - expected: uint64Ptr(1000), + expected: new(uint64(1000)), }, { name: "zero gid", gid: "0", - expected: uint64Ptr(0), + expected: new(uint64(0)), }, { name: "invalid non-numeric gid", diff --git a/pkg/api/service.go b/pkg/api/service.go index 045851a1..2f2efb64 100644 --- a/pkg/api/service.go +++ b/pkg/api/service.go @@ -73,7 +73,7 @@ type ServiceSpec struct { // Default is 10 seconds if not specified. StopGracePeriod *time.Duration `json:",omitempty"` // UpdateConfig configures how the service is updated during a deployment. - UpdateConfig UpdateConfig `json:",omitempty"` + UpdateConfig UpdateConfig // Volumes is list of data volumes that can be mounted into the container. Volumes []VolumeSpec } @@ -348,9 +348,7 @@ func (s *ContainerSpec) Clone() ContainerSpec { } if s.Env != nil { spec.Env = make(EnvVars, len(s.Env)) - for k, v := range s.Env { - spec.Env[k] = v - } + maps.Copy(spec.Env, s.Env) } if s.Healthcheck != nil { hc := *s.Healthcheck @@ -380,9 +378,7 @@ func (s *ContainerSpec) Clone() ContainerSpec { } if s.Sysctls != nil { spec.Sysctls = make(map[string]string, len(s.Sysctls)) - for k, v := range s.Sysctls { - spec.Sysctls[k] = v - } + maps.Copy(spec.Sysctls, s.Sysctls) } if s.Resources.Ulimits != nil { spec.Resources.Ulimits = maps.Clone(s.Resources.Ulimits) diff --git a/pkg/api/service_test.go b/pkg/api/service_test.go index da8fd65f..dd4f4741 100644 --- a/pkg/api/service_test.go +++ b/pkg/api/service_test.go @@ -9,12 +9,6 @@ import ( "github.com/stretchr/testify/require" ) -// boolPtr is a convenience function to create a pointer to a uint64 value -// TODO: Make this a generic function that works for any type -func boolPtr(b bool) *bool { - return &b -} - func TestServiceSpec_Validate_CaddyAndPorts(t *testing.T) { tests := []struct { name string @@ -219,7 +213,7 @@ func TestContainerSpec_Clone(t *testing.T) { "BAZ": "qux", }, Image: "nginx:latest", - Init: boolPtr(true), + Init: new(true), LogDriver: &LogDriver{ Name: "json-file", Options: map[string]string{ diff --git a/pkg/api/volume.go b/pkg/api/volume.go index 67caf458..ade5bc1c 100644 --- a/pkg/api/volume.go +++ b/pkg/api/volume.go @@ -2,6 +2,7 @@ package api import ( "fmt" + "maps" "reflect" "slices" "sort" @@ -165,18 +166,14 @@ func (v *VolumeSpec) Clone() VolumeSpec { driver := *v.VolumeOptions.Driver if driver.Options != nil { driver.Options = make(map[string]string, len(v.VolumeOptions.Driver.Options)) - for k, val := range v.VolumeOptions.Driver.Options { - driver.Options[k] = val - } + maps.Copy(driver.Options, v.VolumeOptions.Driver.Options) } opts.Driver = &driver } if opts.Labels != nil { opts.Labels = make(map[string]string, len(v.VolumeOptions.Labels)) - for k, val := range v.VolumeOptions.Labels { - opts.Labels[k] = val - } + maps.Copy(opts.Labels, v.VolumeOptions.Labels) } spec.VolumeOptions = &opts diff --git a/pkg/client/compose/machines.go b/pkg/client/compose/machines.go index d828e4c4..a1c6899e 100644 --- a/pkg/client/compose/machines.go +++ b/pkg/client/compose/machines.go @@ -11,7 +11,7 @@ const MachinesExtensionKey = "x-machines" type MachinesSource []string // DecodeMapstructure implements custom decoding for multiple input types -func (m *MachinesSource) DecodeMapstructure(value interface{}) error { +func (m *MachinesSource) DecodeMapstructure(value any) error { switch v := value.(type) { case *MachinesSource: // Handle case where compose-go passes a pointer to an already created instance @@ -38,7 +38,7 @@ func (m *MachinesSource) DecodeMapstructure(value interface{}) error { } *m = MachinesSource(machines) return nil - case []interface{}: + case []any: // Support interface array that may come from YAML parsing machineNames := make([]string, 0, len(v)) for i, machine := range v { diff --git a/pkg/client/compose/service.go b/pkg/client/compose/service.go index d6530040..6210105e 100644 --- a/pkg/client/compose/service.go +++ b/pkg/client/compose/service.go @@ -346,9 +346,7 @@ func dockerVolumeSpecFromCompose(serviceVolume types.ServiceVolumeConfig, volume func mergeLabels(labels ...types.Labels) types.Labels { merged := types.Labels{} for _, l := range labels { - for k, v := range l { - merged[k] = v - } + maps.Copy(merged, l) } return merged } diff --git a/pkg/client/image.go b/pkg/client/image.go index 3c347431..f6101f02 100644 --- a/pkg/client/image.go +++ b/pkg/client/image.go @@ -474,12 +474,10 @@ func toPushProgressEvent(jm jsonmessage.JSONMessage) *progress.Event { percent := 0 if jm.Progress.Total > 0 { - percent = int(jm.Progress.Current * 100 / jm.Progress.Total) - // Cap percent at 100 to prevent index out of bounds in progress display. - // Docker can report Current > Total in some cases (e.g., compression). - if percent > 100 { - percent = 100 - } + percent = min( + // Cap percent at 100 to prevent index out of bounds in progress display. + // Docker can report Current > Total in some cases (e.g., compression). + int(jm.Progress.Current*100/jm.Progress.Total), 100) } switch jm.Status { diff --git a/pkg/client/logmerger_test.go b/pkg/client/logmerger_test.go index fdb3e0e1..2130811c 100644 --- a/pkg/client/logmerger_test.go +++ b/pkg/client/logmerger_test.go @@ -264,7 +264,7 @@ func TestLogMerger_UnevenStreams(t *testing.T) { baseTime := time.Now() // Stream 1 sends many entries quickly (0ms - 104ms). - for i := 0; i < numFastEntries; i++ { + for i := range numFastEntries { ch1 <- testEntry(api.LogStreamStdout, baseTime.Add(time.Duration(i)*time.Millisecond), "fast") } diff --git a/test/e2e/service_test.go b/test/e2e/service_test.go index cbfdd9b6..a6bc93f0 100644 --- a/test/e2e/service_test.go +++ b/test/e2e/service_test.go @@ -1052,7 +1052,7 @@ myapp.example.com { // Machine 0: vol1, vol2 // Machine 1: vol1, vol2 // Machine 2: vol1 - for i := 0; i < 3; i++ { + for i := range 3 { _, err := cli.CreateVolume(ctx, c.Machines[i].Name, volume.CreateOptions{Name: vol1Name}) require.NoError(t, err) if i < 2 {