• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

heathcliff26 / go-wol / 17074788465

19 Aug 2025 03:46PM UTC coverage: 91.182% (-0.5%) from 91.71%
17074788465

Pull #86

github

web-flow
Merge 0d9986fa1 into 9946d24e0
Pull Request #86: Add address to host attributes

84 of 95 new or added lines in 8 files covered. (88.42%)

6 existing lines in 1 file now uncovered.

910 of 998 relevant lines covered (91.18%)

54.26 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.13
/pkg/server/storage/valkey/valkey.go
1
package valkey
2

3
import (
4
        "context"
5
        "crypto/tls"
6
        "fmt"
7
        "strings"
8
        "time"
9

10
        "github.com/heathcliff26/go-wol/pkg/server/storage/types"
11
        "github.com/valkey-io/valkey-go"
12
)
13

14
const hostsListKey = "hosts"
15

16
const defaultTimeout = 5 * time.Second
17

18
type ValkeyBackend struct {
19
        client valkey.Client
20
}
21

22
type ValkeyConfig struct {
23
        Addrs    []string `json:"addresses,omitempty"`
24
        Username string   `json:"username,omitempty"`
25
        Password string   `json:"password,omitempty"`
26
        DB       int      `json:"db,omitempty"`
27
        TLS      bool     `json:"tls,omitempty"`
28

29
        // Options for sentinel
30
        Sentinel  bool   `json:"sentinel,omitempty"`
31
        MasterSet string `json:"master,omitempty"`
32
}
33

34
func NewValkeyBackend(cfg ValkeyConfig) (*ValkeyBackend, error) {
20✔
35
        var client valkey.Client
20✔
36
        var tlsConfig *tls.Config
20✔
37

20✔
38
        if cfg.TLS {
20✔
39
                tlsConfig = &tls.Config{
×
40
                        MinVersion: tls.VersionTLS12,
×
41
                }
×
42
        }
×
43

44
        opt := valkey.ClientOption{
20✔
45
                InitAddress: cfg.Addrs,
20✔
46
                Username:    cfg.Username,
20✔
47
                Password:    cfg.Password,
20✔
48
                SelectDB:    cfg.DB,
20✔
49
                TLSConfig:   tlsConfig,
20✔
50

20✔
51
                DisableCache: true,
20✔
52
        }
20✔
53

20✔
54
        if cfg.Sentinel {
20✔
55
                opt.Sentinel = valkey.SentinelOption{
×
56
                        MasterSet: cfg.MasterSet,
×
57
                        Username:  cfg.Username,
×
58
                        Password:  cfg.Password,
×
59
                }
×
60
        }
×
61

62
        client, err := valkey.NewClient(opt)
20✔
63
        if err != nil {
23✔
64
                return nil, fmt.Errorf("failed to connect to valkey server: %w", err)
3✔
65
        }
3✔
66

67
        return &ValkeyBackend{
17✔
68
                client: client,
17✔
69
        }, nil
17✔
70
}
71

72
// Add a new host, overwrite existing host name if it already exists.
73
// Ensures that the MAC address is unique and uppercase.
74
func (v *ValkeyBackend) AddHost(host types.Host) error {
150✔
75
        host.MAC = strings.ToUpper(host.MAC)
150✔
76
        value := serializeHost(host)
150✔
77

150✔
78
        cmdAdd := v.client.B().Set().Key(host.MAC).Value(value).Build()
150✔
79
        cmdZadd := v.client.B().Zadd().Key(hostsListKey).Nx().ScoreMember().ScoreMember(float64(time.Now().UnixNano()), host.MAC).Build()
150✔
80

150✔
81
        ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
150✔
82
        defer cancel()
150✔
83

150✔
84
        err := v.client.Do(ctx, cmdAdd).Error()
150✔
85
        if err != nil {
150✔
86
                return fmt.Errorf("failed to set host: %w", err)
×
87
        }
×
88

89
        err = v.client.Do(ctx, cmdZadd).Error()
150✔
90
        if err != nil {
150✔
91
                return fmt.Errorf("failed to add host to list: %w", err)
×
92
        }
×
93

94
        return nil
150✔
95
}
96

97
// Remove a host, ignore if the host does not exist
98
func (v *ValkeyBackend) RemoveHost(mac string) error {
104✔
99
        mac = strings.ToUpper(mac)
104✔
100

104✔
101
        cmdDel := v.client.B().Del().Key(mac).Build()
104✔
102
        cmdZrem := v.client.B().Zrem().Key(hostsListKey).Member(mac).Build()
104✔
103

104✔
104
        ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
104✔
105
        defer cancel()
104✔
106

104✔
107
        err := v.client.Do(ctx, cmdZrem).Error()
104✔
108
        if err != nil {
104✔
109
                return fmt.Errorf("failed to remove host from list: %w", err)
×
110
        }
×
111

112
        err = v.client.Do(ctx, cmdDel).Error()
104✔
113
        if err != nil {
104✔
114
                return fmt.Errorf("failed to delete host, but already removed host from list: %w", err)
×
115
        }
×
116

117
        return nil
104✔
118
}
119

120
// Return the host name for a given MAC address, return empty if not found
121
func (v *ValkeyBackend) GetHost(mac string) (types.Host, error) {
105✔
122
        mac = strings.ToUpper(mac)
105✔
123

105✔
124
        cmdGet := v.client.B().Get().Key(mac).Build()
105✔
125

105✔
126
        ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
105✔
127
        defer cancel()
105✔
128

105✔
129
        val, err := v.client.Do(ctx, cmdGet).ToString()
105✔
130
        if err != nil {
108✔
131
                return types.Host{}, fmt.Errorf("failed to get host: %w", err)
3✔
132
        }
3✔
133
        return deserializeHost(mac, val), nil
102✔
134
}
135

136
// Return all hosts
137
func (v *ValkeyBackend) GetHosts() ([]types.Host, error) {
105✔
138
        cmdZrange := v.client.B().Zrange().Key(hostsListKey).Min("0").Max("-1").Build()
105✔
139

105✔
140
        ctx, cancel := context.WithTimeout(context.Background(), defaultTimeout)
105✔
141
        defer cancel()
105✔
142

105✔
143
        macs, err := v.client.Do(ctx, cmdZrange).AsStrSlice()
105✔
144
        if err != nil {
106✔
145
                return nil, fmt.Errorf("failed to get known hosts list: %w", err)
1✔
146
        }
1✔
147

148
        res, err := valkey.MGet(v.client, ctx, macs)
104✔
149
        if err != nil {
104✔
150
                return nil, fmt.Errorf("failed to get host names: %w", err)
×
151
        }
×
152

153
        hosts := make([]types.Host, 0, len(macs))
104✔
154
        for _, mac := range macs {
616✔
155
                val, ok := res[mac]
512✔
156
                if !ok {
512✔
NEW
157
                        return nil, fmt.Errorf("MAC address '%s' is in list but no value is found", mac)
×
158
                }
×
159

160
                hostStr, err := val.ToString()
512✔
161
                if err != nil {
512✔
NEW
162
                        return nil, fmt.Errorf("failed to convert response value value to string: %w", err)
×
163
                }
×
164

165
                hosts = append(hosts, deserializeHost(mac, hostStr))
512✔
166
        }
167
        return hosts, nil
104✔
168
}
169

170
// Check if the storage backend is readonly
171
func (v *ValkeyBackend) Readonly() (bool, error) {
2✔
172
        // You can configure valkey to be readonly via ACL, or connect against a replica.
2✔
173
        // However i do not know how to reliably check if the connection is readonly.
2✔
174
        // Instead of hoping that no network error occurs on startup, we just default to assume we can write.
2✔
175
        return false, nil
2✔
176
}
2✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc