chore: do not use sentinel errors when unneeded

- main reason being it's a burden to always define sentinel errors at global scope, wrap them with `%w` instead of using a string directly
- only use sentinel errors when it has to be checked using `errors.Is`
- replace all usage of these sentinel errors in `fmt.Errorf` with direct strings that were in the sentinel error
- exclude the sentinel error definition requirement from .golangci.yml
- update unit tests to use ContainersError instead of ErrorIs so it stays as a "not a change detector test" without requiring a sentinel error
This commit is contained in:
Quentin McGaw
2026-05-02 00:50:16 +00:00
parent 9b6f048fe8
commit 4a78989d9d
172 changed files with 666 additions and 1433 deletions
+2 -4
View File
@@ -6,8 +6,6 @@ import (
"fmt"
"net/http"
"net/netip"
"github.com/qdm12/gluetun/internal/provider/common"
)
type apiData struct {
@@ -48,8 +46,8 @@ func fetchAPI(ctx context.Context, client *http.Client) (
if response.StatusCode != http.StatusOK {
_ = response.Body.Close()
return data, fmt.Errorf("%w: %d %s",
common.ErrHTTPStatusCodeNotOK, response.StatusCode, response.Status)
return data, fmt.Errorf("HTTP status code not OK: %d %s",
response.StatusCode, response.Status)
}
decoder := json.NewDecoder(response.Body)
-5
View File
@@ -1,5 +0,0 @@
package common
import "errors"
var ErrPortForwardNotSupported = errors.New("port forwarding not supported")
+2 -4
View File
@@ -10,10 +10,8 @@ import (
)
var (
ErrNotEnoughServers = errors.New("not enough servers found")
ErrHTTPStatusCodeNotOK = errors.New("HTTP status code not OK")
ErrIPFetcherUnsupported = errors.New("IP fetcher not supported")
ErrCredentialsMissing = errors.New("credentials missing")
ErrNotEnoughServers = errors.New("not enough servers found")
ErrCredentialsMissing = errors.New("credentials are missing")
)
type Fetcher interface {
+1 -4
View File
@@ -1,7 +1,6 @@
package custom
import (
"errors"
"fmt"
"github.com/qdm12/gluetun/internal/configuration/settings"
@@ -10,8 +9,6 @@ import (
"github.com/qdm12/gluetun/internal/models"
)
var ErrVPNTypeNotSupported = errors.New("VPN type not supported for custom provider")
// GetConnection gets the connection from the OpenVPN configuration file.
func (p *Provider) GetConnection(selection settings.ServerSelection, _ bool) (
connection models.Connection, err error,
@@ -22,7 +19,7 @@ func (p *Provider) GetConnection(selection settings.ServerSelection, _ bool) (
case vpn.Wireguard, vpn.AmneziaWg:
return getWireguardConnection(selection), nil
default:
return connection, fmt.Errorf("%w: %s", ErrVPNTypeNotSupported, selection.VPN)
return connection, fmt.Errorf("VPN type not supported for custom provider: %s", selection.VPN)
}
}
-3
View File
@@ -1,7 +1,6 @@
package custom
import (
"errors"
"fmt"
"strconv"
"strings"
@@ -12,8 +11,6 @@ import (
"github.com/qdm12/gluetun/internal/provider/utils"
)
var ErrExtractData = errors.New("failed extracting information from custom configuration file")
func (p *Provider) OpenVPNConfig(connection models.Connection,
settings settings.OpenVPN, ipv6Supported bool,
) (lines []string) {
+2 -5
View File
@@ -3,13 +3,10 @@ package updater
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
)
var errHTTPStatusCodeNotOK = errors.New("HTTP status code not OK")
type apiData struct {
Servers []apiServer `json:"servers"`
}
@@ -42,8 +39,8 @@ func fetchAPI(ctx context.Context, client *http.Client) (
if response.StatusCode != http.StatusOK {
_ = response.Body.Close()
return data, fmt.Errorf("%w: %d %s",
errHTTPStatusCodeNotOK, response.StatusCode, response.Status)
return data, fmt.Errorf("HTTP status code not OK: %d %s",
response.StatusCode, response.Status)
}
decoder := json.NewDecoder(response.Body)
@@ -21,21 +21,17 @@ func Test_Provider_GetConnection(t *testing.T) {
const provider = providers.Expressvpn
errTest := errors.New("test error")
testCases := map[string]struct {
filteredServers []models.Server
storageErr error
selection settings.ServerSelection
ipv6Supported bool
connection models.Connection
errWrapped error
errMessage string
panicMessage string
}{
"error": {
storageErr: errTest,
errWrapped: errTest,
storageErr: errors.New("test error"),
errMessage: "filtering servers: test error",
},
"default OpenVPN TCP port": {
@@ -100,9 +96,10 @@ func Test_Provider_GetConnection(t *testing.T) {
connection, err := provider.GetConnection(testCase.selection, testCase.ipv6Supported)
assert.ErrorIs(t, err, testCase.errWrapped)
if testCase.errWrapped != nil {
if testCase.errMessage != "" {
assert.EqualError(t, err, testCase.errMessage)
} else {
assert.NoError(t, err)
}
assert.Equal(t, testCase.connection, connection)
+3 -8
View File
@@ -3,14 +3,11 @@ package updater
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"strings"
"github.com/qdm12/gluetun/internal/provider/common"
)
type apiServer struct {
@@ -19,8 +16,6 @@ type apiServer struct {
hostname string
}
var ErrDataMalformed = errors.New("data is malformed")
const apiURL = "https://support.fastestvpn.com/wp-admin/admin-ajax.php"
// The API URL and requests are shamelessly taken from network operations
@@ -49,7 +44,7 @@ func fetchAPIServers(ctx context.Context, client *http.Client, protocol string)
if response.StatusCode != http.StatusOK {
_ = response.Body.Close()
return nil, fmt.Errorf("%w: %d", common.ErrHTTPStatusCodeNotOK, response.StatusCode)
return nil, fmt.Errorf("HTTP status code not OK: %d", response.StatusCode)
}
data, err := io.ReadAll(response.Body)
@@ -79,8 +74,8 @@ func fetchAPIServers(ctx context.Context, client *http.Client, protocol string)
for i := range numberOfTDBlocks {
tdBlock := getNextTDBlock(trBlock)
if tdBlock == nil {
return nil, fmt.Errorf("%w: expected 3 <td> blocks in <tr> block %q",
ErrDataMalformed, string(trBlock))
return nil, fmt.Errorf("data is malformed: expected 3 <td> blocks in <tr> block %q",
string(trBlock))
}
trBlock = trBlock[len(tdBlock):]
@@ -8,7 +8,6 @@ import (
"strings"
"testing"
"github.com/qdm12/gluetun/internal/provider/common"
"github.com/stretchr/testify/assert"
)
@@ -21,8 +20,6 @@ func (f roundTripFunc) RoundTrip(r *http.Request) (*http.Response, error) {
func Test_fechAPIServers(t *testing.T) {
t.Parallel()
errTest := errors.New("test error")
testCases := map[string]struct {
ctx context.Context
protocol string
@@ -31,7 +28,6 @@ func Test_fechAPIServers(t *testing.T) {
responseBody io.ReadCloser
transportErr error
servers []apiServer
errWrapped error
errMessage string
}{
"transport_error": {
@@ -39,8 +35,7 @@ func Test_fechAPIServers(t *testing.T) {
protocol: "tcp",
requestBody: "action=vpn_servers&protocol=tcp",
responseStatus: http.StatusOK,
transportErr: errTest,
errWrapped: errTest,
transportErr: errors.New("test error"),
errMessage: `sending request: Post ` +
`"https://support.fastestvpn.com/wp-admin/admin-ajax.php": ` +
`test error`,
@@ -50,7 +45,6 @@ func Test_fechAPIServers(t *testing.T) {
protocol: "tcp",
requestBody: "action=vpn_servers&protocol=tcp",
responseStatus: http.StatusNotFound,
errWrapped: common.ErrHTTPStatusCodeNotOK,
errMessage: "HTTP status code not OK: 404",
},
"empty_data": {
@@ -110,9 +104,10 @@ func Test_fechAPIServers(t *testing.T) {
entries, err := fetchAPIServers(testCase.ctx, client, testCase.protocol)
assert.ErrorIs(t, err, testCase.errWrapped)
if testCase.errWrapped != nil {
if testCase.errMessage != "" {
assert.EqualError(t, err, testCase.errMessage)
} else {
assert.NoError(t, err)
}
assert.Equal(t, testCase.servers, entries)
})
@@ -1,19 +1,16 @@
package updater
import (
"errors"
"fmt"
"strings"
)
var errNotOvpnExt = errors.New("filename does not have the openvpn file extension")
func parseFilename(fileName string) (
region string, err error,
) {
const suffix = ".ovpn"
if !strings.HasSuffix(fileName, suffix) {
return "", fmt.Errorf("%w: %s", errNotOvpnExt, fileName)
return "", fmt.Errorf("filename does not have the openvpn file extension: %s", fileName)
}
region = strings.TrimSuffix(fileName, suffix)
@@ -1,7 +1,6 @@
package updater
import (
"errors"
"fmt"
"strings"
@@ -9,8 +8,6 @@ import (
"golang.org/x/text/cases"
)
var errCountryCodeUnknown = errors.New("country code is unknown")
func parseFilename(fileName, hostname string, titleCaser cases.Caser) (
country, city string, err error,
) {
@@ -28,7 +25,7 @@ func parseFilename(fileName, hostname string, titleCaser cases.Caser) (
countryCode := strings.ToLower(parts[0])
country, ok := countryCodes[countryCode]
if !ok {
return "", "", fmt.Errorf("%w: %s", errCountryCodeUnknown, countryCode)
return "", "", fmt.Errorf("country code is unknown: %s", countryCode)
}
country = titleCaser.String(country)
+4 -7
View File
@@ -22,20 +22,16 @@ func Test_Provider_GetConnection(t *testing.T) {
const provider = providers.Ivpn
errTest := errors.New("test error")
testCases := map[string]struct {
filteredServers []models.Server
storageErr error
selection settings.ServerSelection
ipv6Supported bool
connection models.Connection
errWrapped error
errMessage string
}{
"error": {
storageErr: errTest,
errWrapped: errTest,
storageErr: errors.New("test error"),
errMessage: "filtering servers: test error",
},
"default OpenVPN TCP port": {
@@ -104,9 +100,10 @@ func Test_Provider_GetConnection(t *testing.T) {
connection, err := provider.GetConnection(testCase.selection, testCase.ipv6Supported)
assert.ErrorIs(t, err, testCase.errWrapped)
if testCase.errWrapped != nil {
if testCase.errMessage != "" {
assert.EqualError(t, err, testCase.errMessage)
} else {
assert.NoError(t, err)
}
assert.Equal(t, testCase.connection, connection)
+2 -5
View File
@@ -3,13 +3,10 @@ package updater
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
)
var errHTTPStatusCodeNotOK = errors.New("HTTP status code not OK")
type apiData struct {
Servers []apiServer `json:"servers"`
}
@@ -45,8 +42,8 @@ func fetchAPI(ctx context.Context, client *http.Client) (
if response.StatusCode != http.StatusOK {
_ = response.Body.Close()
return data, fmt.Errorf("%w: %d %s",
errHTTPStatusCodeNotOK, response.StatusCode, response.Status)
return data, fmt.Errorf("HTTP status code not OK: %d %s",
response.StatusCode, response.Status)
}
decoder := json.NewDecoder(response.Body)
+4 -7
View File
@@ -22,20 +22,16 @@ func Test_Provider_GetConnection(t *testing.T) {
const provider = providers.Mullvad
errTest := errors.New("test error")
testCases := map[string]struct {
filteredServers []models.Server
storageErr error
selection settings.ServerSelection
ipv6Supported bool
connection models.Connection
errWrapped error
errMessage string
}{
"error": {
storageErr: errTest,
errWrapped: errTest,
storageErr: errors.New("test error"),
errMessage: "filtering servers: test error",
},
"default Wireguard port": {
@@ -70,9 +66,10 @@ func Test_Provider_GetConnection(t *testing.T) {
connection, err := provider.GetConnection(testCase.selection, testCase.ipv6Supported)
assert.ErrorIs(t, err, testCase.errWrapped)
if testCase.errWrapped != nil {
if testCase.errMessage != "" {
assert.EqualError(t, err, testCase.errMessage)
} else {
assert.NoError(t, err)
}
assert.Equal(t, testCase.connection, connection)
+2 -9
View File
@@ -3,16 +3,10 @@ package updater
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
)
var (
ErrHTTPStatusCodeNotOK = errors.New("HTTP status code not OK")
ErrDecodeResponseBody = errors.New("failed decoding response body")
)
type serverData struct {
Hostname string `json:"hostname"`
Country string `json:"country_name"`
@@ -41,13 +35,12 @@ func fetchAPI(ctx context.Context, client *http.Client) (data []serverData, err
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
return nil, fmt.Errorf("%w: %d %s", ErrHTTPStatusCodeNotOK,
response.StatusCode, response.Status)
return nil, fmt.Errorf("HTTP status code not OK: %d %s", response.StatusCode, response.Status)
}
decoder := json.NewDecoder(response.Body)
if err := decoder.Decode(&data); err != nil {
return nil, fmt.Errorf("%w: %s", ErrDecodeResponseBody, err)
return nil, fmt.Errorf("failed decoding response body: %s", err)
}
if err := response.Body.Close(); err != nil {
@@ -12,20 +12,13 @@ import (
type hostToServer map[string]models.Server
var (
ErrNoIP = errors.New("no IP address for VPN server")
ErrIPIsNotV4 = errors.New("IP address is not IPv4")
ErrIPIsNotV6 = errors.New("IP address is not IPv6")
ErrVPNTypeNotSupported = errors.New("VPN type not supported")
)
func (hts hostToServer) add(data serverData) (err error) {
if !data.Active {
return nil
}
if data.IPv4 == "" && data.IPv6 == "" {
return fmt.Errorf("%w", ErrNoIP)
return errors.New("no IP address for VPN server")
}
server, ok := hts[data.Hostname]
@@ -40,7 +33,7 @@ func (hts hostToServer) add(data serverData) (err error) {
// ignore bridge servers
return nil
default:
return fmt.Errorf("%w: %s", ErrVPNTypeNotSupported, data.Type)
return fmt.Errorf("VPN type not supported: %s", data.Type)
}
if data.IPv4 != "" {
@@ -48,7 +41,7 @@ func (hts hostToServer) add(data serverData) (err error) {
if err != nil {
return fmt.Errorf("parsing IPv4 address: %w", err)
} else if !ipv4.Is4() {
return fmt.Errorf("%w: %s", ErrIPIsNotV4, data.IPv4)
return fmt.Errorf("IP address is not IPv4: %s", data.IPv4)
}
server.IPs = append(server.IPs, ipv4)
}
@@ -58,7 +51,7 @@ func (hts hostToServer) add(data serverData) (err error) {
if err != nil {
return fmt.Errorf("parsing IPv6 address: %w", err)
} else if !ipv6.Is6() {
return fmt.Errorf("%w: %s", ErrIPIsNotV6, data.IPv6)
return fmt.Errorf("IP address is not IPv6: %s", data.IPv6)
}
server.IPs = append(server.IPs, ipv6)
}
+1 -4
View File
@@ -3,13 +3,10 @@ package updater
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
)
var ErrHTTPStatusCodeNotOK = errors.New("HTTP status code not OK")
func fetchAPI(ctx context.Context, client *http.Client,
limit uint,
) (data serversData, err error) {
@@ -28,7 +25,7 @@ func fetchAPI(ctx context.Context, client *http.Client,
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
return serversData{}, fmt.Errorf("%w: %s", ErrHTTPStatusCodeNotOK, response.Status)
return serversData{}, fmt.Errorf("HTTP status code not OK: %s", response.Status)
}
decoder := json.NewDecoder(response.Body)
+2 -8
View File
@@ -168,11 +168,6 @@ func (s *serverData) ips() (ips []netip.Addr) {
return ips
}
var (
ErrWireguardPublicKeyMalformed = errors.New("wireguard public key is malformed")
ErrWireguardPublicKeyNotFound = errors.New("wireguard public key not found")
)
// wireguardPublicKey returns the Wireguard public key for the server.
func (s *serverData) wireguardPublicKey(technologies map[uint32]technologyData) (
wgPubKey string, err error,
@@ -189,11 +184,10 @@ func (s *serverData) wireguardPublicKey(technologies map[uint32]technologyData)
wgPubKey = metadata.Value
_, err = base64.StdEncoding.DecodeString(wgPubKey)
if err != nil {
return "", fmt.Errorf("%w: %s cannot be decoded: %s",
ErrWireguardPublicKeyMalformed, wgPubKey, err)
return "", fmt.Errorf("wireguard public key is malformed: %s cannot be decoded: %s", wgPubKey, err)
}
return metadata.Value, nil
}
}
return "", fmt.Errorf("%w", ErrWireguardPublicKeyNotFound)
return "", errors.New("wireguard public key not found")
}
+2 -5
View File
@@ -7,10 +7,7 @@ import (
"strings"
)
var (
ErrNoIDInServerName = errors.New("no ID in server name")
ErrInvalidIDInServerName = errors.New("invalid ID in server name")
)
var ErrNoIDInServerName = errors.New("no ID in server name")
func parseServerName(serverName string) (number uint16, err error) {
i := strings.IndexRune(serverName, '#')
@@ -21,7 +18,7 @@ func parseServerName(serverName string) (number uint16, err error) {
idString := serverName[i+1:]
idUint64, err := strconv.ParseUint(idString, 10, 16)
if err != nil {
return 0, fmt.Errorf("%w: %s", ErrInvalidIDInServerName, serverName)
return 0, fmt.Errorf("invalid ID in server name: %s", serverName)
}
number = uint16(idUint64)
@@ -11,8 +11,6 @@ import (
"github.com/qdm12/gluetun/internal/provider/common"
)
var ErrNotIPv4 = errors.New("IP address is not IPv4")
func (u *Updater) FetchServers(ctx context.Context, minServers int) (
servers []models.Server, err error,
) {
@@ -21,8 +21,6 @@ import (
"github.com/qdm12/gluetun/internal/provider/utils"
)
var ErrServerNameNotFound = errors.New("server name not found in servers")
// PortForward obtains a VPN server side port forwarded from PIA.
func (p *Provider) PortForward(ctx context.Context,
objects utils.PortForwardObjects,
@@ -42,7 +40,7 @@ func (p *Provider) PortForward(ctx context.Context,
logger := objects.Logger
if !objects.CanPortForward {
return nil, fmt.Errorf("%w: for server %s", ErrServerNameNotFound, serverName)
return nil, fmt.Errorf("server name %s not found in servers", serverName)
}
privateIPClient, err := newHTTPClient(serverName)
@@ -91,8 +89,6 @@ func (p *Provider) PortForward(ctx context.Context,
return map[uint16]uint16{data.Port: data.Port}, nil
}
var ErrPortForwardedExpired = errors.New("port forwarded data expired")
func (p *Provider) KeepPortForward(ctx context.Context,
objects utils.PortForwardObjects,
) (err error) {
@@ -136,14 +132,12 @@ func (p *Provider) KeepPortForward(ctx context.Context,
}
keepAliveTimer.Reset(keepAlivePeriod)
case <-expiryTimer.C:
return fmt.Errorf("%w: on %s", ErrPortForwardedExpired,
return fmt.Errorf("port forwarded data expired on %s",
data.Expiration.Format(time.RFC1123))
}
}
}
var errAPIIPNotFound = errors.New("API IP address not found")
func findAPIIP(ctx context.Context, client *http.Client, gateway netip.Addr) (
apiIP netip.Addr, err error,
) {
@@ -188,7 +182,7 @@ func findAPIIP(ctx context.Context, client *http.Client, gateway netip.Addr) (
return ip, nil
}
return netip.Addr{}, fmt.Errorf("%w: %w", errAPIIPNotFound, errors.Join(errs...))
return netip.Addr{}, fmt.Errorf("API IP address not found: %w", errors.Join(errs...))
}
func refreshPIAPortForwardData(ctx context.Context, client, privateIPClient *http.Client,
@@ -290,8 +284,6 @@ func packPayload(port uint16, token string, expiration time.Time) (payload strin
return payload, nil
}
var errEmptyToken = errors.New("token received is empty")
func fetchToken(ctx context.Context, client *http.Client,
username, password string,
) (token string, err error) {
@@ -340,7 +332,7 @@ func fetchToken(ctx context.Context, client *http.Client,
}
if result.Token == "" {
return "", errEmptyToken
return "", errors.New("token received is empty")
}
return result.Token, nil
}
@@ -391,7 +383,7 @@ func fetchPortForwardData(ctx context.Context, client *http.Client, apiIP netip.
}
if data.Status != "OK" {
return 0, "", expiration, fmt.Errorf("%w: status is: %s", ErrBadResponse, data.Status)
return 0, "", expiration, fmt.Errorf("bad response received with status %s", data.Status)
}
port, _, expiration, err = unpackPayload(data.Payload)
@@ -401,8 +393,6 @@ func fetchPortForwardData(ctx context.Context, client *http.Client, apiIP netip.
return port, data.Signature, expiration, err
}
var ErrBadResponse = errors.New("bad response received")
func bindPort(ctx context.Context, client *http.Client, apiIPAddress netip.Addr, data piaPortForwardData) (err error) {
// Define a timeout since the default client has a large timeout and we don't
// want to wait too long.
@@ -455,7 +445,7 @@ func bindPort(ctx context.Context, client *http.Client, apiIPAddress netip.Addr,
}
if responseData.Status != "OK" {
return fmt.Errorf("%w: %s: %s", ErrBadResponse, responseData.Status, responseData.Message)
return fmt.Errorf("bad response received with status %q and message %q", responseData.Status, responseData.Message)
}
return nil
@@ -464,7 +454,7 @@ func bindPort(ctx context.Context, client *http.Client, apiIPAddress netip.Addr,
// replaceInErr is used to remove sensitive information from errors.
func replaceInErr(err error, substitutions map[string]string) error {
s := replaceInString(err.Error(), substitutions)
return errors.New(s) //nolint:err113
return errors.New(s)
}
// replaceInString is used to remove sensitive information.
@@ -475,8 +465,6 @@ func replaceInString(s string, substitutions map[string]string) string {
return s
}
var ErrHTTPStatusCodeNotOK = errors.New("HTTP status code is not OK")
func makeNOKStatusError(response *http.Response, substitutions map[string]string) (err error) {
url := response.Request.URL.String()
url = replaceInString(url, substitutions)
@@ -487,7 +475,6 @@ func makeNOKStatusError(response *http.Response, substitutions map[string]string
shortenMessage = strings.ReplaceAll(shortenMessage, " ", " ")
shortenMessage = replaceInString(shortenMessage, substitutions)
return fmt.Errorf("%w: %s: %d %s: response received: %s",
ErrHTTPStatusCodeNotOK, url, response.StatusCode,
response.Status, shortenMessage)
return fmt.Errorf("HTTP status code not OK: %s: %d %s: response received: %s",
url, response.StatusCode, response.Status, shortenMessage)
}
@@ -4,15 +4,12 @@ import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/netip"
)
var ErrHTTPStatusCodeNotOK = errors.New("HTTP status code not OK")
type apiData struct {
Regions []regionData `json:"regions"`
}
@@ -50,7 +47,7 @@ func fetchAPI(ctx context.Context, client *http.Client) (
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
return data, fmt.Errorf("%w: %d %s", ErrHTTPStatusCodeNotOK,
return data, fmt.Errorf("HTTP status code not OK: %d %s",
response.StatusCode, response.Status)
}
+3 -7
View File
@@ -11,14 +11,11 @@ import (
"strconv"
"time"
"github.com/qdm12/gluetun/internal/provider/common"
"github.com/qdm12/gluetun/internal/provider/utils"
)
var regexPort = regexp.MustCompile(`[1-9][0-9]{0,4}`)
var ErrPortForwardedNotFound = errors.New("port forwarded not found")
// PortForward obtains a VPN server side port forwarded from the PrivateVPN API.
// It returns 0 if all ports are to forwarded on a dedicated server IP.
func (p *Provider) PortForward(ctx context.Context, objects utils.PortForwardObjects) (
@@ -42,8 +39,7 @@ func (p *Provider) PortForward(ctx context.Context, objects utils.PortForwardObj
}
if response.StatusCode != http.StatusOK {
return nil, fmt.Errorf("%w: %d %s", common.ErrHTTPStatusCodeNotOK,
response.StatusCode, response.Status)
return nil, fmt.Errorf("HTTP status code not OK: %d %s", response.StatusCode, response.Status)
}
defer response.Body.Close()
@@ -62,12 +58,12 @@ func (p *Provider) PortForward(ctx context.Context, objects utils.PortForwardObj
return nil, fmt.Errorf("decoding JSON response: %w; data is: %s",
err, string(bytes))
} else if !data.Supported {
return nil, fmt.Errorf("%w for this VPN server", common.ErrPortForwardNotSupported)
return nil, errors.New("port forwarding not supported for this VPN server")
}
portString := regexPort.FindString(data.Status)
if portString == "" {
return nil, fmt.Errorf("%w: in status %q", ErrPortForwardedNotFound, data.Status)
return nil, fmt.Errorf("port forwarded not found in status %q", data.Status)
}
const base, bitSize = 10, 16
@@ -22,8 +22,6 @@ func (s roundTripFunc) RoundTrip(r *http.Request) (*http.Response, error) {
func Test_Provider_PortForward(t *testing.T) {
t.Parallel()
errTest := errors.New("test error")
canceledCtx, cancel := context.WithCancel(context.Background())
cancel()
@@ -59,7 +57,7 @@ func Test_Provider_PortForward(t *testing.T) {
assert.Equal(t,
"https://connect.pvdatanet.com/v3/Api/port?ip[]=10.10.10.10",
r.URL.String())
return nil, errTest
return nil, errors.New("test error")
}),
},
},
@@ -156,7 +154,7 @@ func Test_Provider_PortForward(t *testing.T) {
}),
},
},
errMessage: "port forwarded not found: in status \"no port here\"",
errMessage: "port forwarded not found in status \"no port here\"",
},
"port_too_big": {
ctx: context.Background(),
@@ -1,7 +1,6 @@
package updater
import (
"errors"
"fmt"
"regexp"
"strings"
@@ -9,12 +8,6 @@ import (
var trailingNumber = regexp.MustCompile(` [0-9]+$`)
var (
errBadPrefix = errors.New("bad prefix in file name")
errBadSuffix = errors.New("bad suffix in file name")
errNotEnoughParts = errors.New("not enough parts in file name")
)
func parseFilename(fileName string) (
countryCode, city string, err error,
) {
@@ -22,7 +15,7 @@ func parseFilename(fileName string) (
const prefix = "PrivateVPN-"
if !strings.HasPrefix(fileName, prefix) {
return "", "", fmt.Errorf("%w: %s", errBadPrefix, fileName)
return "", "", fmt.Errorf("bad prefix in file name %s", fileName)
}
s := strings.TrimPrefix(fileName, prefix)
@@ -34,7 +27,7 @@ func parseFilename(fileName string) (
case strings.HasSuffix(fileName, udpSuffix):
s = strings.TrimSuffix(s, udpSuffix)
default:
return "", "", fmt.Errorf("%w: %s", errBadSuffix, fileName)
return "", "", fmt.Errorf("bad suffix in file name %s", fileName)
}
s = trailingNumber.ReplaceAllString(s, "")
@@ -42,7 +35,7 @@ func parseFilename(fileName string) (
parts := strings.Split(s, "-")
const minParts = 2
if len(parts) < minParts {
return "", "", fmt.Errorf("%w: %s", errNotEnoughParts, fileName)
return "", "", fmt.Errorf("not enough parts in file name %s", fileName)
}
countryCode, city = parts[0], parts[1]
+4 -12
View File
@@ -12,8 +12,6 @@ import (
"github.com/qdm12/gluetun/internal/provider/utils"
)
var ErrServerPortForwardNotSupported = errors.New("server does not support port forwarding")
const nonSymmetricPortStart uint16 = 56789
// PortForward obtains a VPN server side port forwarded from ProtonVPN gateway.
@@ -21,7 +19,7 @@ func (p *Provider) PortForward(ctx context.Context, objects utils.PortForwardObj
internalToExternalPorts map[uint16]uint16, err error,
) {
if !objects.CanPortForward {
return nil, fmt.Errorf("%w", ErrServerPortForwardNotSupported)
return nil, errors.New("server does not support port forwarding")
}
client := natpmp.New()
@@ -104,11 +102,6 @@ func checkExternalPorts(logger utils.Logger, udpPort, tcpPort uint16) {
}
}
var (
ErrInternalPortChanged = errors.New("internal port changed")
ErrExternalPortChanged = errors.New("external port changed")
)
func (p *Provider) KeepPortForward(ctx context.Context,
objects utils.PortForwardObjects,
) (err error) {
@@ -135,11 +128,10 @@ func (p *Provider) KeepPortForward(ctx context.Context,
}
checkLifetime(logger, networkProtocol, lifetime, assignedLiftetime)
if externalPort != assignedExternalPort {
return fmt.Errorf("%w: %d changed to %d",
ErrExternalPortChanged, externalPort, assignedExternalPort)
return fmt.Errorf("external port changed from %d to %d", externalPort, assignedExternalPort)
} else if internalPort != assignedInternalPort {
return fmt.Errorf("%w: %d (for external port %d) changed to %d",
ErrInternalPortChanged, internalPort, externalPort, assignedInternalPort)
return fmt.Errorf("internal port changed from %d (for external port %d) to %d",
internalPort, externalPort, assignedInternalPort)
}
}
objects.Logger.Debug(fmt.Sprintf("port forwarded %d maintained", externalPort))
+34 -53
View File
@@ -60,8 +60,6 @@ func newAPIClient(ctx context.Context, httpClient *http.Client) (client *apiClie
}, nil
}
var ErrCodeNotSuccess = errors.New("response code is not success")
// setHeaders sets the minimal necessary headers for Proton API requests
// to succeed without being blocked by their "security" measures.
// See for example [getMostRecentStableTag] on how the app version must
@@ -126,8 +124,6 @@ func (c *apiClient) authenticate(ctx context.Context, email, password string,
return authCookie, nil
}
var ErrSessionIDNotFound = errors.New("session ID not found in cookies")
func (c *apiClient) getSessionID(ctx context.Context) (sessionID string, err error) {
const url = "https://account.proton.me/vpn"
request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
@@ -150,11 +146,9 @@ func (c *apiClient) getSessionID(ctx context.Context) (sessionID string, err err
}
}
return "", fmt.Errorf("%w", ErrSessionIDNotFound)
return "", errors.New("session ID not found in cookies")
}
var ErrDataFieldMissing = errors.New("data field missing in response")
func (c *apiClient) getUnauthSession(ctx context.Context, sessionID string) (
tokenType, accessToken, refreshToken, uid string, err error,
) {
@@ -198,24 +192,22 @@ func (c *apiClient) getUnauthSession(ctx context.Context, sessionID string) (
const successCode = 1000
switch {
case data.Code != successCode:
return "", "", "", "", fmt.Errorf("%w: expected %d got %d",
ErrCodeNotSuccess, successCode, data.Code)
return "", "", "", "", fmt.Errorf("response code %d is not expected success code %d",
data.Code, successCode)
case data.AccessToken == "":
return "", "", "", "", fmt.Errorf("%w: access token is empty", ErrDataFieldMissing)
return "", "", "", "", errors.New("access token is empty in response")
case data.RefreshToken == "":
return "", "", "", "", fmt.Errorf("%w: refresh token is empty", ErrDataFieldMissing)
return "", "", "", "", errors.New("refresh token is empty in response")
case data.TokenType == "":
return "", "", "", "", fmt.Errorf("%w: token type is empty", ErrDataFieldMissing)
return "", "", "", "", errors.New("token type is empty in response")
case data.UID == "":
return "", "", "", "", fmt.Errorf("%w: UID is empty", ErrDataFieldMissing)
return "", "", "", "", errors.New("UID is empty in response")
}
// Ignore Scopes and LocalID fields, we don't use them.
return data.TokenType, data.AccessToken, data.RefreshToken, data.UID, nil
}
var ErrUIDMismatch = errors.New("UID in response does not match request UID")
func (c *apiClient) cookieToken(ctx context.Context, sessionID, tokenType, accessToken,
refreshToken, uid string,
) (cookieToken string, err error) {
@@ -282,11 +274,11 @@ func (c *apiClient) cookieToken(ctx context.Context, sessionID, tokenType, acces
const successCode = 1000
switch {
case cookies.Code != successCode:
return "", fmt.Errorf("%w: expected %d got %d",
ErrCodeNotSuccess, successCode, cookies.Code)
return "", fmt.Errorf("response code %d is not expected success code %d",
cookies.Code, successCode)
case cookies.UID != requestBody.UID:
return "", fmt.Errorf("%w: expected %s got %s",
ErrUIDMismatch, requestBody.UID, cookies.UID)
return "", fmt.Errorf("UID %s in response does not match request UID %s",
cookies.UID, requestBody.UID)
}
// Ignore LocalID and RefreshCounter fields, we don't use them.
@@ -296,11 +288,9 @@ func (c *apiClient) cookieToken(ctx context.Context, sessionID, tokenType, acces
}
}
return "", fmt.Errorf("%w", ErrAuthCookieNotFound)
return "", errors.New("auth cookie not found")
}
var ErrUsernameDoesNotExist = errors.New("username does not exist")
// authInfo fetches SRP parameters for the account.
func (c *apiClient) authInfo(ctx context.Context, email string, unauthCookie cookie) (
username, modulusPGPClearSigned, serverEphemeralBase64, saltBase64, srpSessionHex string,
@@ -358,20 +348,20 @@ func (c *apiClient) authInfo(ctx context.Context, email string, unauthCookie coo
const successCode = 1000
switch {
case info.Code != successCode:
return "", "", "", "", "", 0, fmt.Errorf("%w: expected %d got %d",
ErrCodeNotSuccess, successCode, info.Code)
return "", "", "", "", "", 0, fmt.Errorf("response code %d is not expected success code %d",
info.Code, successCode)
case info.Modulus == "":
return "", "", "", "", "", 0, fmt.Errorf("%w: modulus is empty", ErrDataFieldMissing)
return "", "", "", "", "", 0, errors.New("modulus is empty in response")
case info.ServerEphemeral == "":
return "", "", "", "", "", 0, fmt.Errorf("%w: server ephemeral is empty", ErrDataFieldMissing)
return "", "", "", "", "", 0, errors.New("server ephemeral is empty in response")
case info.Salt == "":
return "", "", "", "", "", 0, fmt.Errorf("%w (salt data field is empty)", ErrUsernameDoesNotExist)
return "", "", "", "", "", 0, errors.New("salt is empty in response")
case info.SRPSession == "":
return "", "", "", "", "", 0, fmt.Errorf("%w: SRP session is empty", ErrDataFieldMissing)
return "", "", "", "", "", 0, errors.New("SRP session is empty in response")
case info.Username == "":
return "", "", "", "", "", 0, fmt.Errorf("%w: username is empty", ErrDataFieldMissing)
return "", "", "", "", "", 0, errors.New("username is empty in response")
case info.Version == nil:
return "", "", "", "", "", 0, fmt.Errorf("%w: version is missing", ErrDataFieldMissing)
return "", "", "", "", "", 0, errors.New("version is missing in response")
}
version = int(*info.Version) //nolint:gosec
@@ -399,13 +389,7 @@ func (c *cookie) String() string {
return s
}
var (
// ErrServerProofNotValid indicates the M2 from the server didn't match the expected proof.
ErrServerProofNotValid = errors.New("server proof from server is not valid")
ErrVPNScopeNotFound = errors.New("VPN scope not found in scopes")
ErrTwoFANotSupported = errors.New("two factor authentication not supported in this client")
ErrAuthCookieNotFound = errors.New("auth cookie not found")
)
// ErrServerProofNotValid indicates the M2 from the server didn't match the expected proof.
// auth performs the SRP proof submission (and optionally TOTP) to obtain tokens.
func (c *apiClient) auth(ctx context.Context, unauthCookie cookie,
@@ -495,22 +479,22 @@ func (c *apiClient) auth(ctx context.Context, unauthCookie cookie,
return cookie{}, fmt.Errorf("decoding server proof: %w", err)
}
if !bytes.Equal(m2, proofs.ExpectedServerProof) {
return cookie{}, fmt.Errorf("%w: expected %x got %x",
ErrServerProofNotValid, proofs.ExpectedServerProof, m2)
return cookie{}, fmt.Errorf("server proof from server %x is not expected proof %x",
m2, proofs.ExpectedServerProof)
}
const successCode = 1000
switch {
case auth.Code != successCode:
return cookie{}, fmt.Errorf("%w: expected %d got %d",
ErrCodeNotSuccess, successCode, auth.Code)
return cookie{}, fmt.Errorf("response code %d is not expected success code %d",
auth.Code, successCode)
case auth.UID != unauthCookie.uid:
return cookie{}, fmt.Errorf("%w: expected %s got %s",
ErrUIDMismatch, unauthCookie.uid, auth.UID)
return cookie{}, fmt.Errorf("UID %s in response does not match request UID %s",
auth.UID, unauthCookie.uid)
case auth.TwoFactor != 0:
return cookie{}, fmt.Errorf("%w", ErrTwoFANotSupported)
return cookie{}, errors.New("two factor authentication not supported in this client")
case !slices.Contains(auth.Scopes, "vpn"):
return cookie{}, fmt.Errorf("%w: in %v", ErrVPNScopeNotFound, auth.Scopes)
return cookie{}, fmt.Errorf("VPN scope not found in scopes %v", auth.Scopes)
}
for _, setCookieHeader := range response.Header.Values("Set-Cookie") {
@@ -524,8 +508,7 @@ func (c *apiClient) auth(ctx context.Context, unauthCookie cookie,
}
}
return cookie{}, fmt.Errorf("%w: in HTTP headers %s",
ErrAuthCookieNotFound, httpHeadersToString(response.Header))
return cookie{}, fmt.Errorf("auth cookie not found in HTTP headers %s", httpHeadersToString(response.Header))
}
// generateLettersDigits mimicing Proton's own random string generator:
@@ -611,8 +594,6 @@ func (c *apiClient) fetchServers(ctx context.Context, cookie cookie) (
return data, nil
}
var ErrHTTPStatusCodeNotOK = errors.New("HTTP status code not OK")
func buildError(httpCode int, body []byte) error {
prettyCode := http.StatusText(httpCode)
var protonError struct {
@@ -624,8 +605,8 @@ func buildError(httpCode int, body []byte) error {
decoder.DisallowUnknownFields()
err := decoder.Decode(&protonError)
if err != nil || protonError.Error == nil || protonError.Code == nil {
return fmt.Errorf("%w: %s: %s",
ErrHTTPStatusCodeNotOK, prettyCode, body)
return fmt.Errorf("HTTP status code not OK: %s: %s",
prettyCode, body)
}
details := make([]string, 0, len(protonError.Details))
@@ -633,6 +614,6 @@ func buildError(httpCode int, body []byte) error {
details = append(details, fmt.Sprintf("%s: %s", key, value))
}
return fmt.Errorf("%w: %s: %s (code %d with details: %s)",
ErrHTTPStatusCodeNotOK, prettyCode, *protonError.Error, *protonError.Code, strings.Join(details, ", "))
return fmt.Errorf("HTTP status code not OK: %s: %s (code %d with details: %s)",
prettyCode, *protonError.Error, *protonError.Code, strings.Join(details, ", "))
}
@@ -45,7 +45,7 @@ func getMostRecentStableTag(ctx context.Context, client *http.Client) (version s
}
if response.StatusCode != http.StatusOK {
return "", fmt.Errorf("%w: %s: %s", ErrHTTPStatusCodeNotOK, response.Status, data)
return "", fmt.Errorf("HTTP status code not OK: %s: %s", response.Status, data)
}
var tags []struct {
+1 -1
View File
@@ -17,7 +17,7 @@ func (u *Updater) FetchServers(ctx context.Context, minServers int) (
servers []models.Server, err error,
) {
if !u.ipFetcher.CanFetchAnyIP() {
return nil, fmt.Errorf("%w: %s", common.ErrIPFetcherUnsupported, u.ipFetcher.String())
return nil, fmt.Errorf("IP fetcher %s does not support fetching any IP", u.ipFetcher.String())
}
const url = "https://d11a57lttb2ffq.cloudfront.net/heartbleed/router/Recommended-CA2.zip"
+1 -4
View File
@@ -3,7 +3,6 @@ package updater
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
@@ -38,8 +37,6 @@ func addServersFromAPI(ctx context.Context, client *http.Client,
return nil
}
var ErrHTTPStatusCodeNotOK = errors.New("HTTP status code not OK")
type serverData struct {
Host string `json:"connectionName"`
Region string `json:"region"`
@@ -66,7 +63,7 @@ func fetchAPI(ctx context.Context, client *http.Client) (
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
return nil, fmt.Errorf("%w: %d %s", ErrHTTPStatusCodeNotOK,
return nil, fmt.Errorf("HTTP status code not OK: %d %s",
response.StatusCode, response.Status)
}
@@ -1,20 +1,17 @@
package updater
import (
"errors"
"fmt"
"github.com/qdm12/gluetun/internal/provider/surfshark/servers"
)
var errHostnameNotFound = errors.New("hostname not found in hostname to location mapping")
func getHostInformation(host string, hostnameToLocation map[string]servers.ServerLocation) (
data servers.ServerLocation, err error,
) {
locationData, ok := hostnameToLocation[host]
if !ok {
return locationData, fmt.Errorf("%w: %s", errHostnameNotFound, host)
return locationData, fmt.Errorf("hostname %s not found in hostname to location mapping", host)
}
return locationData, nil
+4 -8
View File
@@ -19,8 +19,6 @@ import (
func Test_GetConnection(t *testing.T) {
t.Parallel()
errTest := errors.New("test error")
testCases := map[string]struct {
provider string
filteredServers []models.Server
@@ -30,12 +28,10 @@ func Test_GetConnection(t *testing.T) {
ipv6Supported bool
randSource rand.Source
connection models.Connection
errWrapped error
errMessage string
}{
"storage filter error": {
filterError: errTest,
errWrapped: errTest,
filterError: errors.New("test error"),
errMessage: "filtering servers: test error",
},
"server without IPs": {
@@ -50,7 +46,6 @@ func Test_GetConnection(t *testing.T) {
OpenVPNUDPPort: 1,
WireguardPort: 1,
},
errWrapped: ErrNoConnectionToPickFrom,
errMessage: "no connection to pick from",
},
"OpenVPN server with hostname": {
@@ -199,9 +194,10 @@ func Test_GetConnection(t *testing.T) {
testCase.randSource)
assert.Equal(t, testCase.connection, connection)
assert.ErrorIs(t, err, testCase.errWrapped)
if testCase.errWrapped != nil {
if testCase.errMessage != "" {
assert.EqualError(t, err, testCase.errMessage)
} else {
assert.NoError(t, err)
}
})
}
+1 -4
View File
@@ -2,7 +2,6 @@ package utils
import (
"context"
"errors"
"fmt"
"github.com/qdm12/gluetun/internal/models"
@@ -18,10 +17,8 @@ func NewNoFetcher(providerName string) *NoFetcher {
}
}
var ErrFetcherNotSupported = errors.New("fetching of servers is not supported")
func (n *NoFetcher) FetchServers(context.Context, int) (
servers []models.Server, err error,
) {
return nil, fmt.Errorf("%w: for %s", ErrFetcherNotSupported, n.providerName)
return nil, fmt.Errorf("fetching of servers is not supported for %s", n.providerName)
}
+3 -7
View File
@@ -11,8 +11,6 @@ import (
"github.com/qdm12/gluetun/internal/models"
)
var ErrNoConnectionToPickFrom = errors.New("no connection to pick from")
// pickConnection picks a connection from a pool of connections.
// If the VPN protocol is Wireguard and the target IP is set,
// it finds the connection corresponding to this target IP.
@@ -23,7 +21,7 @@ func pickConnection(connections []models.Connection,
connection models.Connection, err error,
) {
if len(connections) == 0 {
return connection, ErrNoConnectionToPickFrom
return connection, errors.New("no connection to pick from")
}
var targetIP netip.Addr
@@ -56,8 +54,6 @@ func pickRandomConnection(connections []models.Connection,
return connections[rand.New(source).Intn(len(connections))] //nolint:gosec
}
var errTargetIPNotFound = errors.New("target IP address not found")
func getTargetIPConnection(connections []models.Connection,
targetIP netip.Addr,
) (connection models.Connection, err error) {
@@ -66,6 +62,6 @@ func getTargetIPConnection(connections []models.Connection,
return connection, nil
}
}
return connection, fmt.Errorf("%w: in %d filtered connections",
errTargetIPNotFound, len(connections))
return connection, fmt.Errorf("target IP address not found: in %d filtered connections",
len(connections))
}
@@ -33,8 +33,6 @@ func fetchServers(ctx context.Context, client *http.Client,
return servers, nil
}
var ErrHTMLServersDivNotFound = errors.New("HTML servers container div not found")
const divString = "div"
func parseHTML(rootNode *html.Node) (servers []models.Server,
@@ -43,7 +41,7 @@ func parseHTML(rootNode *html.Node) (servers []models.Server,
// Find div container for all servers, searching with BFS.
serversDiv := findServersDiv(rootNode)
if serversDiv == nil {
return nil, nil, htmlutils.WrapError(ErrHTMLServersDivNotFound, rootNode)
return nil, nil, htmlutils.WrapError(errors.New("HTML servers container div not found"), rootNode)
}
for countryNode := serversDiv.FirstChild; countryNode != nil; countryNode = countryNode.NextSibling {
@@ -31,12 +31,10 @@ func Test_fetchServers(t *testing.T) {
responseStatus int
responseBody io.ReadCloser
servers []models.Server
errWrapped error
errMessage string
}{
"context canceled": {
ctx: canceledCtx,
errWrapped: context.Canceled,
errMessage: `fetching HTML code: Get "https://www.vpnsecure.me/vpn-locations/": context canceled`,
},
"success": {
@@ -105,9 +103,10 @@ func Test_fetchServers(t *testing.T) {
servers, err := fetchServers(testCase.ctx, client, warner)
assert.ErrorIs(t, err, testCase.errWrapped)
if testCase.errWrapped != nil {
if testCase.errMessage != "" {
assert.EqualError(t, err, testCase.errMessage)
} else {
assert.NoError(t, err)
}
assert.Equal(t, testCase.servers, servers)
})
@@ -121,12 +120,10 @@ func Test_parseHTML(t *testing.T) {
rootNode *html.Node
servers []models.Server
warnings []string
errWrapped error
errMessage string
}{
"empty html": {
rootNode: parseTestHTML(t, ""),
errWrapped: ErrHTMLServersDivNotFound,
errMessage: `HTML servers container div not found: in HTML code: <html><head></head><body></body></html>`,
},
"test data": {
@@ -223,9 +220,10 @@ func Test_parseHTML(t *testing.T) {
assert.Equal(t, testCase.servers, servers)
assert.Equal(t, testCase.warnings, warnings)
assert.ErrorIs(t, err, testCase.errWrapped)
if testCase.errWrapped != nil {
if testCase.errMessage != "" {
assert.EqualError(t, err, testCase.errMessage)
} else {
assert.NoError(t, err)
}
})
}
@@ -1,19 +1,16 @@
package updater
import (
"errors"
"fmt"
"strings"
)
var errNotOvpnExt = errors.New("filename does not have the openvpn file extension")
func parseFilename(fileName string) (
region string, err error,
) {
const suffix = ".ovpn"
if !strings.HasSuffix(fileName, suffix) {
return "", fmt.Errorf("%w: %s", errNotOvpnExt, fileName)
return "", fmt.Errorf("filename does not have the openvpn file extension: %s", fileName)
}
region = strings.TrimSuffix(fileName, suffix)
@@ -22,21 +22,17 @@ func Test_Provider_GetConnection(t *testing.T) {
const provider = providers.Windscribe
errTest := errors.New("test error")
testCases := map[string]struct {
filteredServers []models.Server
storageErr error
selection settings.ServerSelection
ipv6Supported bool
connection models.Connection
errWrapped error
errMessage string
panicMessage string
}{
"error": {
storageErr: errTest,
errWrapped: errTest,
storageErr: errors.New("test error"),
errMessage: "filtering servers: test error",
},
"default OpenVPN TCP port": {
@@ -111,9 +107,10 @@ func Test_Provider_GetConnection(t *testing.T) {
connection, err := provider.GetConnection(testCase.selection, testCase.ipv6Supported)
assert.ErrorIs(t, err, testCase.errWrapped)
if testCase.errWrapped != nil {
if testCase.errMessage != "" {
assert.EqualError(t, err, testCase.errMessage)
} else {
assert.NoError(t, err)
}
assert.Equal(t, testCase.connection, connection)
+1 -4
View File
@@ -3,7 +3,6 @@ package updater
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"net/netip"
@@ -11,8 +10,6 @@ import (
"time"
)
var ErrHTTPStatusCodeNotOK = errors.New("HTTP status code not OK")
type apiData struct {
Data []regionData `json:"data"`
}
@@ -55,7 +52,7 @@ func fetchAPI(ctx context.Context, client *http.Client) (
defer response.Body.Close()
if response.StatusCode != http.StatusOK {
return data, fmt.Errorf("%w: %d %s", ErrHTTPStatusCodeNotOK,
return data, fmt.Errorf("HTTP status code not OK: %d %s",
response.StatusCode, response.Status)
}
@@ -2,7 +2,6 @@ package updater
import (
"context"
"errors"
"fmt"
"net/netip"
"sort"
@@ -12,8 +11,6 @@ import (
"github.com/qdm12/gluetun/internal/provider/common"
)
var ErrNoWireguardKey = errors.New("no wireguard public key found")
func (u *Updater) FetchServers(ctx context.Context, minServers int) (
servers []models.Server, err error,
) {
@@ -51,7 +48,7 @@ func (u *Updater) FetchServers(ctx context.Context, minServers int) (
if !node.IP3.IsValid() { // Wireguard + Stealth
continue
} else if wgPubKey == "" {
return nil, fmt.Errorf("%w: for node %s", ErrNoWireguardKey, node.Hostname)
return nil, fmt.Errorf("no wireguard public key found: for node %s", node.Hostname)
}
server.VPN = vpn.Wireguard