chore(updater): common GetServers signature

- Log warnings when running outside of CLI mode
- Remove updater CLI bool setting
- Warnings are logged in updating functions
This commit is contained in:
Quentin McGaw
2022-05-28 20:58:50 +00:00
parent 381089ebdf
commit 90c6c8485b
50 changed files with 896 additions and 325 deletions

View File

@@ -36,10 +36,8 @@ type UpdaterLogger interface {
Error(s string) Error(s string)
} }
func boolPtr(b bool) *bool { return &b }
func (c *CLI) Update(ctx context.Context, args []string, logger UpdaterLogger) error { func (c *CLI) Update(ctx context.Context, args []string, logger UpdaterLogger) error {
options := settings.Updater{CLI: boolPtr(true)} options := settings.Updater{}
var endUserMode, maintainerMode, updateAll bool var endUserMode, maintainerMode, updateAll bool
var dnsAddress, csvProviders string var dnsAddress, csvProviders string
flagSet := flag.NewFlagSet("update", flag.ExitOnError) flagSet := flag.NewFlagSet("update", flag.ExitOnError)

View File

@@ -26,11 +26,6 @@ type Updater struct {
// Providers is the list of VPN service providers // Providers is the list of VPN service providers
// to update server information for. // to update server information for.
Providers []string Providers []string
// CLI is to precise the updater is running in CLI
// mode. This is set automatically and cannot be set
// by settings sources. It cannot be nil in the
// internal state.
CLI *bool
} }
func (u Updater) Validate() (err error) { func (u Updater) Validate() (err error) {
@@ -62,7 +57,6 @@ func (u *Updater) copy() (copied Updater) {
Period: helpers.CopyDurationPtr(u.Period), Period: helpers.CopyDurationPtr(u.Period),
DNSAddress: helpers.CopyIP(u.DNSAddress), DNSAddress: helpers.CopyIP(u.DNSAddress),
Providers: helpers.CopyStringSlice(u.Providers), Providers: helpers.CopyStringSlice(u.Providers),
CLI: u.CLI,
} }
} }
@@ -72,7 +66,6 @@ func (u *Updater) mergeWith(other Updater) {
u.Period = helpers.MergeWithDuration(u.Period, other.Period) u.Period = helpers.MergeWithDuration(u.Period, other.Period)
u.DNSAddress = helpers.MergeWithIP(u.DNSAddress, other.DNSAddress) u.DNSAddress = helpers.MergeWithIP(u.DNSAddress, other.DNSAddress)
u.Providers = helpers.MergeStringSlices(u.Providers, other.Providers) u.Providers = helpers.MergeStringSlices(u.Providers, other.Providers)
u.CLI = helpers.MergeWithBool(u.CLI, other.CLI)
} }
// overrideWith overrides fields of the receiver // overrideWith overrides fields of the receiver
@@ -82,13 +75,11 @@ func (u *Updater) overrideWith(other Updater) {
u.Period = helpers.OverrideWithDuration(u.Period, other.Period) u.Period = helpers.OverrideWithDuration(u.Period, other.Period)
u.DNSAddress = helpers.OverrideWithIP(u.DNSAddress, other.DNSAddress) u.DNSAddress = helpers.OverrideWithIP(u.DNSAddress, other.DNSAddress)
u.Providers = helpers.OverrideWithStringSlice(u.Providers, other.Providers) u.Providers = helpers.OverrideWithStringSlice(u.Providers, other.Providers)
u.CLI = helpers.MergeWithBool(u.CLI, other.CLI)
} }
func (u *Updater) SetDefaults(vpnProvider string) { func (u *Updater) SetDefaults(vpnProvider string) {
u.Period = helpers.DefaultDuration(u.Period, 0) u.Period = helpers.DefaultDuration(u.Period, 0)
u.DNSAddress = helpers.DefaultIP(u.DNSAddress, net.IPv4(1, 1, 1, 1)) u.DNSAddress = helpers.DefaultIP(u.DNSAddress, net.IPv4(1, 1, 1, 1))
u.CLI = helpers.DefaultBool(u.CLI, false)
if len(u.Providers) == 0 && vpnProvider != providers.Custom { if len(u.Providers) == 0 && vpnProvider != providers.Custom {
u.Providers = []string{vpnProvider} u.Providers = []string{vpnProvider}
} }
@@ -108,9 +99,5 @@ func (u Updater) toLinesNode() (node *gotree.Node) {
node.Appendf("DNS address: %s", u.DNSAddress) node.Appendf("DNS address: %s", u.DNSAddress)
node.Appendf("Providers to update: %s", strings.Join(u.Providers, ", ")) node.Appendf("Providers to update: %s", strings.Join(u.Providers, ", "))
if *u.CLI {
node.Appendf("CLI mode: enabled")
}
return node return node
} }

View File

@@ -6,15 +6,14 @@ import (
"context" "context"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/updater/resolver"
) )
func GetServers(ctx context.Context, presolver resolver.Parallel, func (u *Updater) GetServers(ctx context.Context, minServers int) (
minServers int) (servers []models.Server, err error) { servers []models.Server, err error) {
possibleServers := getPossibleServers() possibleServers := getPossibleServers()
possibleHosts := possibleServers.hostsSlice() possibleHosts := possibleServers.hostsSlice()
hostToIPs, err := resolveHosts(ctx, presolver, possibleHosts, minServers) hostToIPs, err := resolveHosts(ctx, u.presolver, possibleHosts, minServers)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -0,0 +1,13 @@
package cyberghost
import "github.com/qdm12/gluetun/internal/updater/resolver"
type Updater struct {
presolver resolver.Parallel
}
func New(presolver resolver.Parallel) *Updater {
return &Updater{
presolver: presolver,
}
}

View File

@@ -9,15 +9,12 @@ import (
"github.com/qdm12/gluetun/internal/constants/vpn" "github.com/qdm12/gluetun/internal/constants/vpn"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/updater/resolver"
"github.com/qdm12/gluetun/internal/updater/unzip"
) )
var ErrNotEnoughServers = errors.New("not enough servers found") var ErrNotEnoughServers = errors.New("not enough servers found")
func GetServers(ctx context.Context, unzipper unzip.Unzipper, func (u *Updater) GetServers(ctx context.Context, minServers int) (
presolver resolver.Parallel, minServers int) ( servers []models.Server, err error) {
servers []models.Server, warnings []string, err error) {
servers = hardcodedServers() servers = hardcodedServers()
hosts := make([]string, len(servers)) hosts := make([]string, len(servers))
@@ -25,10 +22,12 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper,
hosts[i] = servers[i].Hostname hosts[i] = servers[i].Hostname
} }
hostToIPs, newWarnings, err := resolveHosts(ctx, presolver, hosts, minServers) hostToIPs, warnings, err := resolveHosts(ctx, u.presolver, hosts, minServers)
warnings = append(warnings, newWarnings...) for _, warning := range warnings {
u.warner.Warn(warning)
}
if err != nil { if err != nil {
return nil, warnings, err return nil, err
} }
i := 0 i := 0
@@ -46,11 +45,11 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper,
servers = servers[:i] servers = servers[:i]
if len(servers) < minServers { if len(servers) < minServers {
return nil, warnings, fmt.Errorf("%w: %d and expected at least %d", return nil, fmt.Errorf("%w: %d and expected at least %d",
ErrNotEnoughServers, len(servers), minServers) ErrNotEnoughServers, len(servers), minServers)
} }
sortServers(servers) sortServers(servers)
return servers, warnings, nil return servers, nil
} }

View File

@@ -0,0 +1,25 @@
package expressvpn
import (
"github.com/qdm12/gluetun/internal/updater/resolver"
"github.com/qdm12/gluetun/internal/updater/unzip"
)
type Updater struct {
unzipper unzip.Unzipper
presolver resolver.Parallel
warner Warner
}
type Warner interface {
Warn(s string)
}
func New(unzipper unzip.Unzipper, presolver resolver.Parallel,
warner Warner) *Updater {
return &Updater{
unzipper: unzipper,
presolver: presolver,
warner: warner,
}
}

View File

@@ -10,21 +10,18 @@ import (
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/updater/openvpn" "github.com/qdm12/gluetun/internal/updater/openvpn"
"github.com/qdm12/gluetun/internal/updater/resolver"
"github.com/qdm12/gluetun/internal/updater/unzip"
) )
var ErrNotEnoughServers = errors.New("not enough servers found") var ErrNotEnoughServers = errors.New("not enough servers found")
func GetServers(ctx context.Context, unzipper unzip.Unzipper, func (u *Updater) GetServers(ctx context.Context, minServers int) (
presolver resolver.Parallel, minServers int) ( servers []models.Server, err error) {
servers []models.Server, warnings []string, err error) {
const url = "https://support.fastestvpn.com/download/openvpn-tcp-udp-config-files" const url = "https://support.fastestvpn.com/download/openvpn-tcp-udp-config-files"
contents, err := unzipper.FetchAndExtract(ctx, url) contents, err := u.unzipper.FetchAndExtract(ctx, url)
if err != nil { if err != nil {
return nil, nil, err return nil, err
} else if len(contents) < minServers { } else if len(contents) < minServers {
return nil, nil, fmt.Errorf("%w: %d and expected at least %d", return nil, fmt.Errorf("%w: %d and expected at least %d",
ErrNotEnoughServers, len(contents), minServers) ErrNotEnoughServers, len(contents), minServers)
} }
@@ -37,18 +34,17 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper,
country, tcp, udp, err := parseFilename(fileName) country, tcp, udp, err := parseFilename(fileName)
if err != nil { if err != nil {
warnings = append(warnings, err.Error()) u.warner.Warn(err.Error())
continue continue
} }
host, warning, err := openvpn.ExtractHost(content) host, warning, err := openvpn.ExtractHost(content)
if warning != "" { if warning != "" {
warnings = append(warnings, warning) u.warner.Warn(warning)
} }
if err != nil { if err != nil {
// treat error as warning and go to next file // treat error as warning and go to next file
warning := err.Error() + " in " + fileName u.warner.Warn(err.Error() + " in " + fileName)
warnings = append(warnings, warning)
continue continue
} }
@@ -56,15 +52,17 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper,
} }
if len(hts) < minServers { if len(hts) < minServers {
return nil, warnings, fmt.Errorf("%w: %d and expected at least %d", return nil, fmt.Errorf("%w: %d and expected at least %d",
ErrNotEnoughServers, len(hts), minServers) ErrNotEnoughServers, len(hts), minServers)
} }
hosts := hts.toHostsSlice() hosts := hts.toHostsSlice()
hostToIPs, newWarnings, err := resolveHosts(ctx, presolver, hosts, minServers) hostToIPs, warnings, err := resolveHosts(ctx, u.presolver, hosts, minServers)
warnings = append(warnings, newWarnings...) for _, warning := range warnings {
u.warner.Warn(warning)
}
if err != nil { if err != nil {
return nil, warnings, err return nil, err
} }
hts.adaptWithIPs(hostToIPs) hts.adaptWithIPs(hostToIPs)
@@ -72,11 +70,11 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper,
servers = hts.toServersSlice() servers = hts.toServersSlice()
if len(servers) < minServers { if len(servers) < minServers {
return nil, warnings, fmt.Errorf("%w: %d and expected at least %d", return nil, fmt.Errorf("%w: %d and expected at least %d",
ErrNotEnoughServers, len(servers), minServers) ErrNotEnoughServers, len(servers), minServers)
} }
sortServers(servers) sortServers(servers)
return servers, warnings, nil return servers, nil
} }

View File

@@ -0,0 +1,25 @@
package fastestvpn
import (
"github.com/qdm12/gluetun/internal/updater/resolver"
"github.com/qdm12/gluetun/internal/updater/unzip"
)
type Updater struct {
unzipper unzip.Unzipper
presolver resolver.Parallel
warner Warner
}
type Warner interface {
Warn(s string)
}
func New(unzipper unzip.Unzipper, presolver resolver.Parallel,
warner Warner) *Updater {
return &Updater{
unzipper: unzipper,
presolver: presolver,
warner: warner,
}
}

View File

@@ -6,33 +6,33 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"net/http"
"github.com/qdm12/gluetun/internal/constants/vpn" "github.com/qdm12/gluetun/internal/constants/vpn"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/updater/resolver"
) )
var ErrNotEnoughServers = errors.New("not enough servers found") var ErrNotEnoughServers = errors.New("not enough servers found")
func GetServers(ctx context.Context, client *http.Client, func (u *Updater) GetServers(ctx context.Context, minServers int) (
presolver resolver.Parallel, minServers int) ( servers []models.Server, err error) {
servers []models.Server, warnings []string, err error) { tcpHostToURL, udpHostToURL, err := getAllHostToURL(ctx, u.client)
tcpHostToURL, udpHostToURL, err := getAllHostToURL(ctx, client)
if err != nil { if err != nil {
return nil, nil, err return nil, err
} }
hosts := getUniqueHosts(tcpHostToURL, udpHostToURL) hosts := getUniqueHosts(tcpHostToURL, udpHostToURL)
if len(hosts) < minServers { if len(hosts) < minServers {
return nil, nil, fmt.Errorf("%w: %d and expected at least %d", return nil, fmt.Errorf("%w: %d and expected at least %d",
ErrNotEnoughServers, len(hosts), minServers) ErrNotEnoughServers, len(hosts), minServers)
} }
hostToIPs, warnings, err := resolveHosts(ctx, presolver, hosts, minServers) hostToIPs, warnings, err := resolveHosts(ctx, u.presolver, hosts, minServers)
for _, warning := range warnings {
u.warner.Warn(warning)
}
if err != nil { if err != nil {
return nil, warnings, err return nil, err
} }
servers = make([]models.Server, 0, len(hostToIPs)) servers = make([]models.Server, 0, len(hostToIPs))
@@ -66,5 +66,5 @@ func GetServers(ctx context.Context, client *http.Client,
sortServers(servers) sortServers(servers)
return servers, warnings, nil return servers, nil
} }

View File

@@ -0,0 +1,26 @@
package hidemyass
import (
"net/http"
"github.com/qdm12/gluetun/internal/updater/resolver"
)
type Updater struct {
client *http.Client
presolver resolver.Parallel
warner Warner
}
type Warner interface {
Warn(s string)
}
func New(client *http.Client, presolver resolver.Parallel,
warner Warner) *Updater {
return &Updater{
client: client,
presolver: presolver,
warner: warner,
}
}

View File

@@ -0,0 +1,3 @@
package ipvanish
//go:generate mockgen -destination=mocks_test.go -package $GOPACKAGE . Warner

View File

@@ -0,0 +1,46 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/qdm12/gluetun/internal/provider/ipvanish/updater (interfaces: Warner)
// Package ipvanish is a generated GoMock package.
package ipvanish
import (
reflect "reflect"
gomock "github.com/golang/mock/gomock"
)
// MockWarner is a mock of Warner interface.
type MockWarner struct {
ctrl *gomock.Controller
recorder *MockWarnerMockRecorder
}
// MockWarnerMockRecorder is the mock recorder for MockWarner.
type MockWarnerMockRecorder struct {
mock *MockWarner
}
// NewMockWarner creates a new mock instance.
func NewMockWarner(ctrl *gomock.Controller) *MockWarner {
mock := &MockWarner{ctrl: ctrl}
mock.recorder = &MockWarnerMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockWarner) EXPECT() *MockWarnerMockRecorder {
return m.recorder
}
// Warn mocks base method.
func (m *MockWarner) Warn(arg0 string) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "Warn", arg0)
}
// Warn indicates an expected call of Warn.
func (mr *MockWarnerMockRecorder) Warn(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Warn", reflect.TypeOf((*MockWarner)(nil).Warn), arg0)
}

View File

@@ -10,23 +10,20 @@ import (
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/updater/openvpn" "github.com/qdm12/gluetun/internal/updater/openvpn"
"github.com/qdm12/gluetun/internal/updater/resolver"
"github.com/qdm12/gluetun/internal/updater/unzip"
"golang.org/x/text/cases" "golang.org/x/text/cases"
"golang.org/x/text/language" "golang.org/x/text/language"
) )
var ErrNotEnoughServers = errors.New("not enough servers found") var ErrNotEnoughServers = errors.New("not enough servers found")
func GetServers(ctx context.Context, unzipper unzip.Unzipper, func (u *Updater) GetServers(ctx context.Context, minServers int) (
presolver resolver.Parallel, minServers int) ( servers []models.Server, err error) {
servers []models.Server, warnings []string, err error) {
const url = "https://www.ipvanish.com/software/configs/configs.zip" const url = "https://www.ipvanish.com/software/configs/configs.zip"
contents, err := unzipper.FetchAndExtract(ctx, url) contents, err := u.unzipper.FetchAndExtract(ctx, url)
if err != nil { if err != nil {
return nil, nil, err return nil, err
} else if len(contents) < minServers { } else if len(contents) < minServers {
return nil, nil, fmt.Errorf("%w: %d and expected at least %d", return nil, fmt.Errorf("%w: %d and expected at least %d",
ErrNotEnoughServers, len(contents), minServers) ErrNotEnoughServers, len(contents), minServers)
} }
@@ -41,27 +38,24 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper,
tcp, udp, err := openvpn.ExtractProto(content) tcp, udp, err := openvpn.ExtractProto(content)
if err != nil { if err != nil {
// treat error as warning and go to next file // treat error as warning and go to next file
warning := err.Error() + ": in " + fileName u.warner.Warn(err.Error() + " in " + fileName)
warnings = append(warnings, warning)
continue continue
} }
hostname, warning, err := openvpn.ExtractHost(content) hostname, warning, err := openvpn.ExtractHost(content)
if warning != "" { if warning != "" {
warnings = append(warnings, warning) u.warner.Warn(warning)
} }
if err != nil { if err != nil {
// treat error as warning and go to next file // treat error as warning and go to next file
warning := err.Error() + " in " + fileName u.warner.Warn(err.Error() + " in " + fileName)
warnings = append(warnings, warning)
continue continue
} }
country, city, err := parseFilename(fileName, hostname, titleCaser) country, city, err := parseFilename(fileName, hostname, titleCaser)
if err != nil { if err != nil {
// treat error as warning and go to next file // treat error as warning and go to next file
warning := err.Error() + " in " + fileName u.warner.Warn(err.Error() + " in " + fileName)
warnings = append(warnings, warning)
continue continue
} }
@@ -69,15 +63,17 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper,
} }
if len(hts) < minServers { if len(hts) < minServers {
return nil, warnings, fmt.Errorf("%w: %d and expected at least %d", return nil, fmt.Errorf("%w: %d and expected at least %d",
ErrNotEnoughServers, len(hts), minServers) ErrNotEnoughServers, len(hts), minServers)
} }
hosts := hts.toHostsSlice() hosts := hts.toHostsSlice()
hostToIPs, newWarnings, err := resolveHosts(ctx, presolver, hosts, minServers) hostToIPs, warnings, err := resolveHosts(ctx, u.presolver, hosts, minServers)
warnings = append(warnings, newWarnings...) for _, warning := range warnings {
u.warner.Warn(warning)
}
if err != nil { if err != nil {
return nil, warnings, err return nil, err
} }
hts.adaptWithIPs(hostToIPs) hts.adaptWithIPs(hostToIPs)
@@ -85,11 +81,11 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper,
servers = hts.toServersSlice() servers = hts.toServersSlice()
if len(servers) < minServers { if len(servers) < minServers {
return nil, warnings, fmt.Errorf("%w: %d and expected at least %d", return nil, fmt.Errorf("%w: %d and expected at least %d",
ErrNotEnoughServers, len(servers), minServers) ErrNotEnoughServers, len(servers), minServers)
} }
sortServers(servers) sortServers(servers)
return servers, warnings, nil return servers, nil
} }

View File

@@ -16,12 +16,15 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func Test_GetServers(t *testing.T) { func Test_Updater_GetServers(t *testing.T) {
t.Parallel() t.Parallel()
testCases := map[string]struct { testCases := map[string]struct {
// Inputs // Inputs
minServers int minServers int
// Mocks
warnerBuilder func(ctrl *gomock.Controller) Warner
// Unzip // Unzip
unzipContents map[string][]byte unzipContents map[string][]byte
unzipErr error unzipErr error
@@ -35,48 +38,67 @@ func Test_GetServers(t *testing.T) {
resolveErr error resolveErr error
// Output // Output
servers []models.Server servers []models.Server
warnings []string err error
err error
}{ }{
"unzipper error": { "unzipper error": {
unzipErr: errors.New("dummy"), warnerBuilder: func(ctrl *gomock.Controller) Warner { return nil },
err: errors.New("dummy"), unzipErr: errors.New("dummy"),
err: errors.New("dummy"),
}, },
"not enough unzip contents": { "not enough unzip contents": {
minServers: 1, minServers: 1,
warnerBuilder: func(ctrl *gomock.Controller) Warner { return nil },
unzipContents: map[string][]byte{}, unzipContents: map[string][]byte{},
err: errors.New("not enough servers found: 0 and expected at least 1"), err: errors.New("not enough servers found: 0 and expected at least 1"),
}, },
"no openvpn file": { "no openvpn file": {
minServers: 1, minServers: 1,
warnerBuilder: func(ctrl *gomock.Controller) Warner { return nil },
unzipContents: map[string][]byte{"somefile.txt": {}}, unzipContents: map[string][]byte{"somefile.txt": {}},
err: errors.New("not enough servers found: 0 and expected at least 1"), err: errors.New("not enough servers found: 0 and expected at least 1"),
}, },
"invalid proto": { "invalid proto": {
minServers: 1, minServers: 1,
warnerBuilder: func(ctrl *gomock.Controller) Warner {
warner := NewMockWarner(ctrl)
warner.EXPECT().Warn("unknown protocol: invalid in badproto.ovpn")
return warner
},
unzipContents: map[string][]byte{"badproto.ovpn": []byte(`proto invalid`)}, unzipContents: map[string][]byte{"badproto.ovpn": []byte(`proto invalid`)},
warnings: []string{"unknown protocol: invalid: in badproto.ovpn"},
err: errors.New("not enough servers found: 0 and expected at least 1"), err: errors.New("not enough servers found: 0 and expected at least 1"),
}, },
"no host": { "no host": {
minServers: 1, minServers: 1,
warnerBuilder: func(ctrl *gomock.Controller) Warner {
warner := NewMockWarner(ctrl)
warner.EXPECT().Warn("remote host not found in nohost.ovpn")
return warner
},
unzipContents: map[string][]byte{"nohost.ovpn": []byte(``)}, unzipContents: map[string][]byte{"nohost.ovpn": []byte(``)},
warnings: []string{"remote host not found in nohost.ovpn"},
err: errors.New("not enough servers found: 0 and expected at least 1"), err: errors.New("not enough servers found: 0 and expected at least 1"),
}, },
"multiple hosts": { "multiple hosts": {
minServers: 1, minServers: 1,
warnerBuilder: func(ctrl *gomock.Controller) Warner {
warner := NewMockWarner(ctrl)
warner.EXPECT().Warn("only using the first host \"hosta\" and discarding 1 other hosts")
return warner
},
unzipContents: map[string][]byte{ unzipContents: map[string][]byte{
"ipvanish-CA-City-A-hosta.ovpn": []byte("remote hosta\nremote hostb"), "ipvanish-CA-City-A-hosta.ovpn": []byte("remote hosta\nremote hostb"),
}, },
expectResolve: true, expectResolve: true,
hostsToResolve: []string{"hosta"}, hostsToResolve: []string{"hosta"},
resolveSettings: getResolveSettings(1), resolveSettings: getResolveSettings(1),
warnings: []string{"only using the first host \"hosta\" and discarding 1 other hosts"},
err: errors.New("not enough servers found: 0 and expected at least 1"), err: errors.New("not enough servers found: 0 and expected at least 1"),
}, },
"resolve error": { "resolve error": {
warnerBuilder: func(ctrl *gomock.Controller) Warner {
warner := NewMockWarner(ctrl)
warner.EXPECT().Warn("resolve warning")
return warner
},
unzipContents: map[string][]byte{ unzipContents: map[string][]byte{
"ipvanish-CA-City-A-hosta.ovpn": []byte("remote hosta"), "ipvanish-CA-City-A-hosta.ovpn": []byte("remote hosta"),
}, },
@@ -85,19 +107,27 @@ func Test_GetServers(t *testing.T) {
resolveSettings: getResolveSettings(0), resolveSettings: getResolveSettings(0),
resolveWarnings: []string{"resolve warning"}, resolveWarnings: []string{"resolve warning"},
resolveErr: errors.New("dummy"), resolveErr: errors.New("dummy"),
warnings: []string{"resolve warning"},
err: errors.New("dummy"), err: errors.New("dummy"),
}, },
"filename parsing error": { "filename parsing error": {
minServers: 1, minServers: 1,
warnerBuilder: func(ctrl *gomock.Controller) Warner {
warner := NewMockWarner(ctrl)
warner.EXPECT().Warn("country code is unknown: unknown in ipvanish-unknown-City-A-hosta.ovpn")
return warner
},
unzipContents: map[string][]byte{ unzipContents: map[string][]byte{
"ipvanish-unknown-City-A-hosta.ovpn": []byte("remote hosta"), "ipvanish-unknown-City-A-hosta.ovpn": []byte("remote hosta"),
}, },
warnings: []string{"country code is unknown: unknown in ipvanish-unknown-City-A-hosta.ovpn"}, err: errors.New("not enough servers found: 0 and expected at least 1"),
err: errors.New("not enough servers found: 0 and expected at least 1"),
}, },
"success": { "success": {
minServers: 1, minServers: 1,
warnerBuilder: func(ctrl *gomock.Controller) Warner {
warner := NewMockWarner(ctrl)
warner.EXPECT().Warn("resolve warning")
return warner
},
unzipContents: map[string][]byte{ unzipContents: map[string][]byte{
"ipvanish-CA-City-A-hosta.ovpn": []byte("remote hosta"), "ipvanish-CA-City-A-hosta.ovpn": []byte("remote hosta"),
"ipvanish-LU-City-B-hostb.ovpn": []byte("remote hostb"), "ipvanish-LU-City-B-hostb.ovpn": []byte("remote hostb"),
@@ -128,7 +158,6 @@ func Test_GetServers(t *testing.T) {
IPs: []net.IP{{3, 3, 3, 3}, {4, 4, 4, 4}}, IPs: []net.IP{{3, 3, 3, 3}, {4, 4, 4, 4}},
}, },
}, },
warnings: []string{"resolve warning"},
}, },
} }
for name, testCase := range testCases { for name, testCase := range testCases {
@@ -150,10 +179,13 @@ func Test_GetServers(t *testing.T) {
Return(testCase.hostToIPs, testCase.resolveWarnings, testCase.resolveErr) Return(testCase.hostToIPs, testCase.resolveWarnings, testCase.resolveErr)
} }
servers, warnings, err := GetServers(ctx, unzipper, presolver, testCase.minServers) warner := testCase.warnerBuilder(ctrl)
updater := New(unzipper, presolver, warner)
servers, err := updater.GetServers(ctx, testCase.minServers)
assert.Equal(t, testCase.servers, servers) assert.Equal(t, testCase.servers, servers)
assert.Equal(t, testCase.warnings, warnings)
if testCase.err != nil { if testCase.err != nil {
require.Error(t, err) require.Error(t, err)
assert.Equal(t, testCase.err.Error(), err.Error()) assert.Equal(t, testCase.err.Error(), err.Error())

View File

@@ -0,0 +1,25 @@
package ipvanish
import (
"github.com/qdm12/gluetun/internal/updater/resolver"
"github.com/qdm12/gluetun/internal/updater/unzip"
)
type Updater struct {
unzipper unzip.Unzipper
presolver resolver.Parallel
warner Warner
}
type Warner interface {
Warn(s string)
}
func New(unzipper unzip.Unzipper, presolver resolver.Parallel,
warner Warner) *Updater {
return &Updater{
unzipper: unzipper,
presolver: presolver,
warner: warner,
}
}

View File

@@ -0,0 +1,3 @@
package ivpn
//go:generate mockgen -destination=mocks_test.go -package $GOPACKAGE . Warner

View File

@@ -0,0 +1,46 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: github.com/qdm12/gluetun/internal/provider/ivpn/updater (interfaces: Warner)
// Package ivpn is a generated GoMock package.
package ivpn
import (
reflect "reflect"
gomock "github.com/golang/mock/gomock"
)
// MockWarner is a mock of Warner interface.
type MockWarner struct {
ctrl *gomock.Controller
recorder *MockWarnerMockRecorder
}
// MockWarnerMockRecorder is the mock recorder for MockWarner.
type MockWarnerMockRecorder struct {
mock *MockWarner
}
// NewMockWarner creates a new mock instance.
func NewMockWarner(ctrl *gomock.Controller) *MockWarner {
mock := &MockWarner{ctrl: ctrl}
mock.recorder = &MockWarnerMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockWarner) EXPECT() *MockWarnerMockRecorder {
return m.recorder
}
// Warn mocks base method.
func (m *MockWarner) Warn(arg0 string) {
m.ctrl.T.Helper()
m.ctrl.Call(m, "Warn", arg0)
}
// Warn indicates an expected call of Warn.
func (mr *MockWarnerMockRecorder) Warn(arg0 interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Warn", reflect.TypeOf((*MockWarner)(nil).Warn), arg0)
}

View File

@@ -6,23 +6,20 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"net/http"
"github.com/qdm12/gluetun/internal/constants/vpn" "github.com/qdm12/gluetun/internal/constants/vpn"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/updater/resolver"
) )
var ( var (
ErrNotEnoughServers = errors.New("not enough servers found") ErrNotEnoughServers = errors.New("not enough servers found")
) )
func GetServers(ctx context.Context, client *http.Client, func (u *Updater) GetServers(ctx context.Context, minServers int) (
presolver resolver.Parallel, minServers int) ( servers []models.Server, err error) {
servers []models.Server, warnings []string, err error) { data, err := fetchAPI(ctx, u.client)
data, err := fetchAPI(ctx, client)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("failed fetching API: %w", err) return nil, fmt.Errorf("failed fetching API: %w", err)
} }
hosts := make([]string, 0, len(data.Servers)) hosts := make([]string, 0, len(data.Servers))
@@ -40,13 +37,16 @@ func GetServers(ctx context.Context, client *http.Client,
} }
if len(hosts) < minServers { if len(hosts) < minServers {
return nil, nil, fmt.Errorf("%w: %d and expected at least %d", return nil, fmt.Errorf("%w: %d and expected at least %d",
ErrNotEnoughServers, len(hosts), minServers) ErrNotEnoughServers, len(hosts), minServers)
} }
hostToIPs, warnings, err := resolveHosts(ctx, presolver, hosts, minServers) hostToIPs, warnings, err := resolveHosts(ctx, u.presolver, hosts, minServers)
for _, warning := range warnings {
u.warner.Warn(warning)
}
if err != nil { if err != nil {
return nil, warnings, err return nil, err
} }
servers = make([]models.Server, 0, len(hosts)) servers = make([]models.Server, 0, len(hosts))
@@ -78,5 +78,5 @@ func GetServers(ctx context.Context, client *http.Client,
sortServers(servers) sortServers(servers)
return servers, warnings, nil return servers, nil
} }

View File

@@ -18,13 +18,16 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func Test_GetServers(t *testing.T) { func Test_Updater_GetServers(t *testing.T) {
t.Parallel() t.Parallel()
testCases := map[string]struct { testCases := map[string]struct {
// Inputs // Inputs
minServers int minServers int
// Mocks
warnerBuilder func(ctrl *gomock.Controller) Warner
// From API // From API
responseBody string responseBody string
responseStatus int responseStatus int
@@ -38,15 +41,20 @@ func Test_GetServers(t *testing.T) {
resolveErr error resolveErr error
// Output // Output
servers []models.Server servers []models.Server
warnings []string err error
err error
}{ }{
"http response error": { "http response error": {
warnerBuilder: func(ctrl *gomock.Controller) Warner { return nil },
responseStatus: http.StatusNoContent, responseStatus: http.StatusNoContent,
err: errors.New("failed fetching API: HTTP status code not OK: 204 No Content"), err: errors.New("failed fetching API: HTTP status code not OK: 204 No Content"),
}, },
"resolve error": { "resolve error": {
warnerBuilder: func(ctrl *gomock.Controller) Warner {
warner := NewMockWarner(ctrl)
warner.EXPECT().Warn("resolve warning")
return warner
},
responseBody: `{"servers":[ responseBody: `{"servers":[
{"hostnames":{"openvpn":"hosta"}} {"hostnames":{"openvpn":"hosta"}}
]}`, ]}`,
@@ -56,11 +64,11 @@ func Test_GetServers(t *testing.T) {
resolveSettings: getResolveSettings(0), resolveSettings: getResolveSettings(0),
resolveWarnings: []string{"resolve warning"}, resolveWarnings: []string{"resolve warning"},
resolveErr: errors.New("dummy"), resolveErr: errors.New("dummy"),
warnings: []string{"resolve warning"},
err: errors.New("dummy"), err: errors.New("dummy"),
}, },
"not enough servers": { "not enough servers": {
minServers: 2, minServers: 2,
warnerBuilder: func(ctrl *gomock.Controller) Warner { return nil },
responseBody: `{"servers":[ responseBody: `{"servers":[
{"hostnames":{"openvpn":"hosta"}} {"hostnames":{"openvpn":"hosta"}}
]}`, ]}`,
@@ -69,6 +77,11 @@ func Test_GetServers(t *testing.T) {
}, },
"success": { "success": {
minServers: 1, minServers: 1,
warnerBuilder: func(ctrl *gomock.Controller) Warner {
warner := NewMockWarner(ctrl)
warner.EXPECT().Warn("resolve warning")
return warner
},
responseBody: `{"servers":[ responseBody: `{"servers":[
{"country":"Country1","city":"City A","hostnames":{"openvpn":"hosta"}}, {"country":"Country1","city":"City A","hostnames":{"openvpn":"hosta"}},
{"country":"Country2","city":"City B","hostnames":{"openvpn":"hostb"},"wg_public_key":"xyz"}, {"country":"Country2","city":"City B","hostnames":{"openvpn":"hostb"},"wg_public_key":"xyz"},
@@ -97,7 +110,6 @@ func Test_GetServers(t *testing.T) {
WgPubKey: "xyz", WgPubKey: "xyz",
IPs: []net.IP{{5, 5, 5, 5}, {6, 6, 6, 6}}}, IPs: []net.IP{{5, 5, 5, 5}, {6, 6, 6, 6}}},
}, },
warnings: []string{"resolve warning"},
}, },
} }
for name, testCase := range testCases { for name, testCase := range testCases {
@@ -126,10 +138,13 @@ func Test_GetServers(t *testing.T) {
Return(testCase.hostToIPs, testCase.resolveWarnings, testCase.resolveErr) Return(testCase.hostToIPs, testCase.resolveWarnings, testCase.resolveErr)
} }
servers, warnings, err := GetServers(ctx, client, presolver, testCase.minServers) warner := testCase.warnerBuilder(ctrl)
updater := New(client, presolver, warner)
servers, err := updater.GetServers(ctx, testCase.minServers)
assert.Equal(t, testCase.servers, servers) assert.Equal(t, testCase.servers, servers)
assert.Equal(t, testCase.warnings, warnings)
if testCase.err != nil { if testCase.err != nil {
require.Error(t, err) require.Error(t, err)
assert.Equal(t, testCase.err.Error(), err.Error()) assert.Equal(t, testCase.err.Error(), err.Error())

View File

@@ -0,0 +1,26 @@
package ivpn
import (
"net/http"
"github.com/qdm12/gluetun/internal/updater/resolver"
)
type Updater struct {
client *http.Client
presolver resolver.Parallel
warner Warner
}
type Warner interface {
Warn(s string)
}
func New(client *http.Client, presolver resolver.Parallel,
warner Warner) *Updater {
return &Updater{
client: client,
presolver: presolver,
warner: warner,
}
}

View File

@@ -6,16 +6,15 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"net/http"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
) )
var ErrNotEnoughServers = errors.New("not enough servers found") var ErrNotEnoughServers = errors.New("not enough servers found")
func GetServers(ctx context.Context, client *http.Client, minServers int) ( func (u *Updater) GetServers(ctx context.Context, minServers int) (
servers []models.Server, err error) { servers []models.Server, err error) {
data, err := fetchAPI(ctx, client) data, err := fetchAPI(ctx, u.client)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -0,0 +1,15 @@
package mullvad
import (
"net/http"
)
type Updater struct {
client *http.Client
}
func New(client *http.Client) *Updater {
return &Updater{
client: client,
}
}

View File

@@ -7,7 +7,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"net" "net"
"net/http"
"github.com/qdm12/gluetun/internal/constants/vpn" "github.com/qdm12/gluetun/internal/constants/vpn"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
@@ -19,30 +18,29 @@ var (
ErrNotEnoughServers = errors.New("not enough servers found") ErrNotEnoughServers = errors.New("not enough servers found")
) )
func GetServers(ctx context.Context, client *http.Client, minServers int) ( func (u *Updater) GetServers(ctx context.Context, minServers int) (
servers []models.Server, warnings []string, err error) { servers []models.Server, err error) {
data, err := fetchAPI(ctx, client) data, err := fetchAPI(ctx, u.client)
if err != nil { if err != nil {
return nil, nil, err return nil, err
} }
servers = make([]models.Server, 0, len(data)) servers = make([]models.Server, 0, len(data))
for _, jsonServer := range data { for _, jsonServer := range data {
if !jsonServer.Features.TCP && !jsonServer.Features.UDP { if !jsonServer.Features.TCP && !jsonServer.Features.UDP {
warning := "server does not support TCP and UDP for openvpn: " + jsonServer.Name u.warner.Warn("server does not support TCP and UDP for openvpn: " + jsonServer.Name)
warnings = append(warnings, warning)
continue continue
} }
ip, err := parseIPv4(jsonServer.IPAddress) ip, err := parseIPv4(jsonServer.IPAddress)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("%w for server %s", err, jsonServer.Name) return nil, fmt.Errorf("%w for server %s", err, jsonServer.Name)
} }
number, err := parseServerName(jsonServer.Name) number, err := parseServerName(jsonServer.Name)
if err != nil { if err != nil {
return nil, nil, err return nil, err
} }
server := models.Server{ server := models.Server{
@@ -58,11 +56,11 @@ func GetServers(ctx context.Context, client *http.Client, minServers int) (
} }
if len(servers) < minServers { if len(servers) < minServers {
return nil, warnings, fmt.Errorf("%w: %d and expected at least %d", return nil, fmt.Errorf("%w: %d and expected at least %d",
ErrNotEnoughServers, len(servers), minServers) ErrNotEnoughServers, len(servers), minServers)
} }
sortServers(servers) sortServers(servers)
return servers, warnings, nil return servers, nil
} }

View File

@@ -0,0 +1,21 @@
package nordvpn
import (
"net/http"
)
type Updater struct {
client *http.Client
warner Warner
}
type Warner interface {
Warn(message string)
}
func New(client *http.Client, warner Warner) *Updater {
return &Updater{
client: client,
warner: warner,
}
}

View File

@@ -9,13 +9,12 @@ import (
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/updater/openvpn" "github.com/qdm12/gluetun/internal/updater/openvpn"
"github.com/qdm12/gluetun/internal/updater/unzip"
) )
var ErrNotEnoughServers = errors.New("not enough servers found") var ErrNotEnoughServers = errors.New("not enough servers found")
func GetServers(ctx context.Context, unzipper unzip.Unzipper, minServers int) ( func (u *Updater) GetServers(ctx context.Context, minServers int) (
servers []models.Server, warnings []string, err error) { servers []models.Server, err error) {
zipURL := url.URL{ zipURL := url.URL{
Scheme: "https", Scheme: "https",
Host: "www.perfect-privacy.com", Host: "www.perfect-privacy.com",
@@ -28,9 +27,9 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper, minServers int) (
values.Set("protocol", "udp") // all support both TCP and UDP values.Set("protocol", "udp") // all support both TCP and UDP
zipURL.RawQuery = values.Encode() zipURL.RawQuery = values.Encode()
contents, err := unzipper.FetchAndExtract(ctx, zipURL.String()) contents, err := u.unzipper.FetchAndExtract(ctx, zipURL.String())
if err != nil { if err != nil {
return nil, nil, err return nil, err
} }
cts := make(cityToServer) cts := make(cityToServer)
@@ -38,12 +37,12 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper, minServers int) (
for fileName, content := range contents { for fileName, content := range contents {
err := addServerFromOvpn(cts, fileName, content) err := addServerFromOvpn(cts, fileName, content)
if err != nil { if err != nil {
warnings = append(warnings, fileName+": "+err.Error()) u.warner.Warn(err.Error() + " in " + fileName)
} }
} }
if len(cts) < minServers { if len(cts) < minServers {
return nil, warnings, fmt.Errorf("%w: %d and expected at least %d", return nil, fmt.Errorf("%w: %d and expected at least %d",
ErrNotEnoughServers, len(cts), minServers) ErrNotEnoughServers, len(cts), minServers)
} }
@@ -51,7 +50,7 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper, minServers int) (
sortServers(servers) sortServers(servers)
return servers, warnings, nil return servers, nil
} }
func addServerFromOvpn(cts cityToServer, func addServerFromOvpn(cts cityToServer,

View File

@@ -0,0 +1,19 @@
package perfectprivacy
import "github.com/qdm12/gluetun/internal/updater/unzip"
type Updater struct {
unzipper unzip.Unzipper
warner Warner
}
type Warner interface {
Warn(s string)
}
func New(unzipper unzip.Unzipper, warner Warner) *Updater {
return &Updater{
unzipper: unzipper,
warner: warner,
}
}

View File

@@ -6,26 +6,22 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"net/http"
"strings" "strings"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/updater/openvpn" "github.com/qdm12/gluetun/internal/updater/openvpn"
"github.com/qdm12/gluetun/internal/updater/resolver"
"github.com/qdm12/gluetun/internal/updater/unzip"
) )
var ErrNotEnoughServers = errors.New("not enough servers found") var ErrNotEnoughServers = errors.New("not enough servers found")
func GetServers(ctx context.Context, unzipper unzip.Unzipper, func (u *Updater) GetServers(ctx context.Context, minServers int) (
client *http.Client, presolver resolver.Parallel, minServers int) ( servers []models.Server, err error) {
servers []models.Server, warnings []string, err error) {
const url = "https://privado.io/apps/ovpn_configs.zip" const url = "https://privado.io/apps/ovpn_configs.zip"
contents, err := unzipper.FetchAndExtract(ctx, url) contents, err := u.unzipper.FetchAndExtract(ctx, url)
if err != nil { if err != nil {
return nil, nil, err return nil, err
} else if len(contents) < minServers { } else if len(contents) < minServers {
return nil, nil, fmt.Errorf("%w: %d and expected at least %d", return nil, fmt.Errorf("%w: %d and expected at least %d",
ErrNotEnoughServers, len(contents), minServers) ErrNotEnoughServers, len(contents), minServers)
} }
@@ -38,12 +34,11 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper,
host, warning, err := openvpn.ExtractHost(content) host, warning, err := openvpn.ExtractHost(content)
if warning != "" { if warning != "" {
warnings = append(warnings, warning) u.warner.Warn(warning)
} }
if err != nil { if err != nil {
// treat error as warning and go to next file // treat error as warning and go to next file
warning := err.Error() + " in " + fileName u.warner.Warn(err.Error() + " in " + fileName)
warnings = append(warnings, warning)
continue continue
} }
@@ -51,26 +46,28 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper,
} }
if len(hts) < minServers { if len(hts) < minServers {
return nil, warnings, fmt.Errorf("%w: %d and expected at least %d", return nil, fmt.Errorf("%w: %d and expected at least %d",
ErrNotEnoughServers, len(hts), minServers) ErrNotEnoughServers, len(hts), minServers)
} }
hosts := hts.toHostsSlice() hosts := hts.toHostsSlice()
hostToIPs, newWarnings, err := resolveHosts(ctx, presolver, hosts, minServers) hostToIPs, warnings, err := resolveHosts(ctx, u.presolver, hosts, minServers)
warnings = append(warnings, newWarnings...) for _, warning := range warnings {
u.warner.Warn(warning)
}
if err != nil { if err != nil {
return nil, warnings, err return nil, err
} }
hts.adaptWithIPs(hostToIPs) hts.adaptWithIPs(hostToIPs)
servers = hts.toServersSlice() servers = hts.toServersSlice()
if err := setLocationInfo(ctx, client, servers); err != nil { if err := setLocationInfo(ctx, u.client, servers); err != nil {
return nil, warnings, err return nil, err
} }
sortServers(servers) sortServers(servers)
return servers, warnings, nil return servers, nil
} }

View File

@@ -0,0 +1,29 @@
package privado
import (
"net/http"
"github.com/qdm12/gluetun/internal/updater/resolver"
"github.com/qdm12/gluetun/internal/updater/unzip"
)
type Updater struct {
client *http.Client
unzipper unzip.Unzipper
presolver resolver.Parallel
warner Warner
}
type Warner interface {
Warn(s string)
}
func New(client *http.Client, unzipper unzip.Unzipper,
presolver resolver.Parallel, warner Warner) *Updater {
return &Updater{
client: client,
unzipper: unzipper,
presolver: presolver,
warner: warner,
}
}

View File

@@ -6,7 +6,6 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"net/http"
"time" "time"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
@@ -14,7 +13,7 @@ import (
var ErrNotEnoughServers = errors.New("not enough servers found") var ErrNotEnoughServers = errors.New("not enough servers found")
func GetServers(ctx context.Context, client *http.Client, minServers int) ( func (u *Updater) GetServers(ctx context.Context, minServers int) (
servers []models.Server, err error) { servers []models.Server, err error) {
nts := make(nameToServer) nts := make(nameToServer)
@@ -26,7 +25,7 @@ func GetServers(ctx context.Context, client *http.Client, minServers int) (
maxTimer := time.NewTimer(maxDuration) maxTimer := time.NewTimer(maxDuration)
for { for {
data, err := fetchAPI(ctx, client) data, err := fetchAPI(ctx, u.client)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -0,0 +1,19 @@
package privateinternetaccess
import (
"net/http"
)
type Updater struct {
client *http.Client
}
type Warner interface {
Warn(s string)
}
func New(client *http.Client) *Updater {
return &Updater{
client: client,
}
}

View File

@@ -11,21 +11,18 @@ import (
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/updater/openvpn" "github.com/qdm12/gluetun/internal/updater/openvpn"
"github.com/qdm12/gluetun/internal/updater/resolver"
"github.com/qdm12/gluetun/internal/updater/unzip"
) )
var ErrNotEnoughServers = errors.New("not enough servers found") var ErrNotEnoughServers = errors.New("not enough servers found")
func GetServers(ctx context.Context, unzipper unzip.Unzipper, func (u *Updater) GetServers(ctx context.Context, minServers int) (
presolver resolver.Parallel, minServers int) ( servers []models.Server, err error) {
servers []models.Server, warnings []string, err error) {
const url = "https://privatevpn.com/client/PrivateVPN-TUN.zip" const url = "https://privatevpn.com/client/PrivateVPN-TUN.zip"
contents, err := unzipper.FetchAndExtract(ctx, url) contents, err := u.unzipper.FetchAndExtract(ctx, url)
if err != nil { if err != nil {
return nil, nil, err return nil, err
} else if len(contents) < minServers { } else if len(contents) < minServers {
return nil, nil, fmt.Errorf("%w: %d and expected at least %d", return nil, fmt.Errorf("%w: %d and expected at least %d",
ErrNotEnoughServers, len(contents), minServers) ErrNotEnoughServers, len(contents), minServers)
} }
@@ -41,18 +38,19 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper,
countryCode, city, err := parseFilename(fileName) countryCode, city, err := parseFilename(fileName)
if err != nil { if err != nil {
warnings = append(warnings, err.Error()) // treat error as warning and go to next file
u.warner.Warn(err.Error() + " in " + fileName)
continue continue
} }
country, warning := codeToCountry(countryCode, countryCodes) country, warning := codeToCountry(countryCode, countryCodes)
if warning != "" { if warning != "" {
warnings = append(warnings, warning) u.warner.Warn(warning)
} }
host, warning, err := openvpn.ExtractHost(content) host, warning, err := openvpn.ExtractHost(content)
if warning != "" { if warning != "" {
warnings = append(warnings, warning) u.warner.Warn(warning)
} }
if err == nil { // found host if err == nil { // found host
hts.add(host, country, city) hts.add(host, country, city)
@@ -61,12 +59,11 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper,
ips, extractIPErr := openvpn.ExtractIPs(content) ips, extractIPErr := openvpn.ExtractIPs(content)
if warning != "" { if warning != "" {
warnings = append(warnings, warning) u.warner.Warn(warning)
} }
if extractIPErr != nil { if extractIPErr != nil {
// treat extract host error as warning and go to next file // treat extract host error as warning and go to next file
warning := err.Error() + " in " + fileName u.warner.Warn(extractIPErr.Error() + " in " + fileName)
warnings = append(warnings, warning)
continue continue
} }
server := models.Server{ server := models.Server{
@@ -80,16 +77,18 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper,
} }
if len(noHostnameServers)+len(hts) < minServers { if len(noHostnameServers)+len(hts) < minServers {
return nil, warnings, fmt.Errorf("%w: %d and expected at least %d", return nil, fmt.Errorf("%w: %d and expected at least %d",
ErrNotEnoughServers, len(servers)+len(hts), minServers) ErrNotEnoughServers, len(servers)+len(hts), minServers)
} }
hosts := hts.toHostsSlice() hosts := hts.toHostsSlice()
hostToIPs, newWarnings, err := resolveHosts(ctx, presolver, hosts, minServers) hostToIPs, warnings, err := resolveHosts(ctx, u.presolver, hosts, minServers)
warnings = append(warnings, newWarnings...) for _, warning := range warnings {
u.warner.Warn(warning)
}
if err != nil { if err != nil {
return nil, warnings, err return nil, err
} }
hts.adaptWithIPs(hostToIPs) hts.adaptWithIPs(hostToIPs)
@@ -99,5 +98,5 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper,
sortServers(servers) sortServers(servers)
return servers, warnings, nil return servers, nil
} }

View File

@@ -0,0 +1,25 @@
package privatevpn
import (
"github.com/qdm12/gluetun/internal/updater/resolver"
"github.com/qdm12/gluetun/internal/updater/unzip"
)
type Updater struct {
unzipper unzip.Unzipper
presolver resolver.Parallel
warner Warner
}
type Warner interface {
Warn(s string)
}
func New(unzipper unzip.Unzipper, presolver resolver.Parallel,
warner Warner) *Updater {
return &Updater{
unzipper: unzipper,
presolver: presolver,
warner: warner,
}
}

View File

@@ -6,7 +6,6 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"net/http"
"github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/constants"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
@@ -14,11 +13,11 @@ import (
var ErrNotEnoughServers = errors.New("not enough servers found") var ErrNotEnoughServers = errors.New("not enough servers found")
func GetServers(ctx context.Context, client *http.Client, minServers int) ( func (u *Updater) GetServers(ctx context.Context, minServers int) (
servers []models.Server, warnings []string, err error) { servers []models.Server, err error) {
data, err := fetchAPI(ctx, client) data, err := fetchAPI(ctx, u.client)
if err != nil { if err != nil {
return nil, nil, err return nil, err
} }
countryCodes := constants.CountryCodes() countryCodes := constants.CountryCodes()
@@ -29,7 +28,7 @@ func GetServers(ctx context.Context, client *http.Client, minServers int) (
} }
if count < minServers { if count < minServers {
return nil, warnings, fmt.Errorf("%w: %d and expected at least %d", return nil, fmt.Errorf("%w: %d and expected at least %d",
ErrNotEnoughServers, count, minServers) ErrNotEnoughServers, count, minServers)
} }
@@ -40,8 +39,7 @@ func GetServers(ctx context.Context, client *http.Client, minServers int) (
name := logicalServer.Name name := logicalServer.Name
for _, physicalServer := range logicalServer.Servers { for _, physicalServer := range logicalServer.Servers {
if physicalServer.Status == 0 { // disabled so skip server if physicalServer.Status == 0 { // disabled so skip server
warnings = append(warnings, u.warner.Warn("ignoring server " + physicalServer.Domain + " with status 0")
"ignoring server "+physicalServer.Domain+" with status 0")
continue continue
} }
@@ -53,7 +51,7 @@ func GetServers(ctx context.Context, client *http.Client, minServers int) (
countryCode := logicalServer.ExitCountry countryCode := logicalServer.ExitCountry
country, warning := codeToCountry(countryCode, countryCodes) country, warning := codeToCountry(countryCode, countryCodes)
if warning != "" { if warning != "" {
warnings = append(warnings, warning) u.warner.Warn(warning)
} }
ipToServer.add(country, region, city, name, hostname, entryIP) ipToServer.add(country, region, city, name, hostname, entryIP)
@@ -61,7 +59,7 @@ func GetServers(ctx context.Context, client *http.Client, minServers int) (
} }
if len(ipToServer) < minServers { if len(ipToServer) < minServers {
return nil, warnings, fmt.Errorf("%w: %d and expected at least %d", return nil, fmt.Errorf("%w: %d and expected at least %d",
ErrNotEnoughServers, len(ipToServer), minServers) ErrNotEnoughServers, len(ipToServer), minServers)
} }
@@ -69,7 +67,7 @@ func GetServers(ctx context.Context, client *http.Client, minServers int) (
sortServers(servers) sortServers(servers)
return servers, warnings, nil return servers, nil
} }
func getStringValue(ptr *string) string { func getStringValue(ptr *string) string {

View File

@@ -0,0 +1,21 @@
package protonvpn
import (
"net/http"
)
type Updater struct {
client *http.Client
warner Warner
}
type Warner interface {
Warn(s string)
}
func New(client *http.Client, warner Warner) *Updater {
return &Updater{
client: client,
warner: warner,
}
}

View File

@@ -7,27 +7,23 @@ import (
"errors" "errors"
"fmt" "fmt"
"net" "net"
"net/http"
"strings" "strings"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/publicip" "github.com/qdm12/gluetun/internal/publicip"
"github.com/qdm12/gluetun/internal/updater/openvpn" "github.com/qdm12/gluetun/internal/updater/openvpn"
"github.com/qdm12/gluetun/internal/updater/resolver"
"github.com/qdm12/gluetun/internal/updater/unzip"
) )
var ErrNotEnoughServers = errors.New("not enough servers found") var ErrNotEnoughServers = errors.New("not enough servers found")
func GetServers(ctx context.Context, client *http.Client, func (u *Updater) GetServers(ctx context.Context, minServers int) (
unzipper unzip.Unzipper, presolver resolver.Parallel, minServers int) ( servers []models.Server, err error) {
servers []models.Server, warnings []string, err error) {
const url = "https://d32d3g1fvkpl8y.cloudfront.net/heartbleed/windows/New+OVPN+Files.zip" const url = "https://d32d3g1fvkpl8y.cloudfront.net/heartbleed/windows/New+OVPN+Files.zip"
contents, err := unzipper.FetchAndExtract(ctx, url) contents, err := u.unzipper.FetchAndExtract(ctx, url)
if err != nil { if err != nil {
return nil, nil, err return nil, err
} else if len(contents) < minServers { } else if len(contents) < minServers {
return nil, nil, fmt.Errorf("%w: %d and expected at least %d", return nil, fmt.Errorf("%w: %d and expected at least %d",
ErrNotEnoughServers, len(contents), minServers) ErrNotEnoughServers, len(contents), minServers)
} }
@@ -41,20 +37,18 @@ func GetServers(ctx context.Context, client *http.Client,
tcp, udp, err := openvpn.ExtractProto(content) tcp, udp, err := openvpn.ExtractProto(content)
if err != nil { if err != nil {
// treat error as warning and go to next file // treat error as warning and go to next file
warning := err.Error() + " in " + fileName u.warner.Warn(err.Error() + " in " + fileName)
warnings = append(warnings, warning)
continue continue
} }
host, warning, err := openvpn.ExtractHost(content) host, warning, err := openvpn.ExtractHost(content)
if warning != "" { if warning != "" {
warnings = append(warnings, warning) u.warner.Warn(warning)
} }
if err != nil { if err != nil {
// treat error as warning and go to next file // treat error as warning and go to next file
warning := err.Error() + " in " + fileName u.warner.Warn(err.Error() + " in " + fileName)
warnings = append(warnings, warning)
continue continue
} }
@@ -62,15 +56,17 @@ func GetServers(ctx context.Context, client *http.Client,
} }
if len(hts) < minServers { if len(hts) < minServers {
return nil, warnings, fmt.Errorf("%w: %d and expected at least %d", return nil, fmt.Errorf("%w: %d and expected at least %d",
ErrNotEnoughServers, len(hts), minServers) ErrNotEnoughServers, len(hts), minServers)
} }
hosts := hts.toHostsSlice() hosts := hts.toHostsSlice()
hostToIPs, newWarnings, err := resolveHosts(ctx, presolver, hosts, minServers) hostToIPs, warnings, err := resolveHosts(ctx, u.presolver, hosts, minServers)
warnings = append(warnings, newWarnings...) for _, warning := range warnings {
u.warner.Warn(warning)
}
if err != nil { if err != nil {
return nil, warnings, err return nil, err
} }
hts.adaptWithIPs(hostToIPs) hts.adaptWithIPs(hostToIPs)
@@ -78,7 +74,7 @@ func GetServers(ctx context.Context, client *http.Client,
servers = hts.toServersSlice() servers = hts.toServersSlice()
if len(servers) < minServers { if len(servers) < minServers {
return nil, warnings, fmt.Errorf("%w: %d and expected at least %d", return nil, fmt.Errorf("%w: %d and expected at least %d",
ErrNotEnoughServers, len(servers), minServers) ErrNotEnoughServers, len(servers), minServers)
} }
@@ -87,9 +83,9 @@ func GetServers(ctx context.Context, client *http.Client,
for i := range servers { for i := range servers {
ipsToGetInfo[i] = servers[i].IPs[0] ipsToGetInfo[i] = servers[i].IPs[0]
} }
ipsInfo, err := publicip.MultiInfo(ctx, client, ipsToGetInfo) ipsInfo, err := publicip.MultiInfo(ctx, u.client, ipsToGetInfo)
if err != nil { if err != nil {
return nil, warnings, err return nil, err
} }
for i := range servers { for i := range servers {
servers[i].Country = ipsInfo[i].Country servers[i].Country = ipsInfo[i].Country
@@ -99,5 +95,5 @@ func GetServers(ctx context.Context, client *http.Client,
sortServers(servers) sortServers(servers)
return servers, warnings, nil return servers, nil
} }

View File

@@ -0,0 +1,29 @@
package purevpn
import (
"net/http"
"github.com/qdm12/gluetun/internal/updater/resolver"
"github.com/qdm12/gluetun/internal/updater/unzip"
)
type Updater struct {
client *http.Client
unzipper unzip.Unzipper
presolver resolver.Parallel
warner Warner
}
type Warner interface {
Warn(s string)
}
func New(client *http.Client, unzipper unzip.Unzipper,
presolver resolver.Parallel, warner Warner) *Updater {
return &Updater{
client: client,
unzipper: unzipper,
presolver: presolver,
warner: warner,
}
}

View File

@@ -6,39 +6,40 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"net/http"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/updater/resolver"
"github.com/qdm12/gluetun/internal/updater/unzip"
) )
var ( var (
ErrNotEnoughServers = errors.New("not enough servers found") ErrNotEnoughServers = errors.New("not enough servers found")
) )
func GetServers(ctx context.Context, unzipper unzip.Unzipper, func (u *Updater) GetServers(ctx context.Context, minServers int) (
client *http.Client, presolver resolver.Parallel, minServers int) ( servers []models.Server, err error) {
servers []models.Server, warnings []string, err error) {
hts := make(hostToServer) hts := make(hostToServer)
err = addServersFromAPI(ctx, client, hts) err = addServersFromAPI(ctx, u.client, hts)
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("cannot fetch server information from API: %w", err) return nil, fmt.Errorf("cannot fetch server information from API: %w", err)
} }
warnings, err = addOpenVPNServersFromZip(ctx, unzipper, hts) warnings, err := addOpenVPNServersFromZip(ctx, u.unzipper, hts)
for _, warning := range warnings {
u.warner.Warn(warning)
}
if err != nil { if err != nil {
return nil, nil, fmt.Errorf("cannot get OpenVPN ZIP file: %w", err) return nil, fmt.Errorf("cannot get OpenVPN ZIP file: %w", err)
} }
getRemainingServers(hts) getRemainingServers(hts)
hosts := hts.toHostsSlice() hosts := hts.toHostsSlice()
hostToIPs, newWarnings, err := resolveHosts(ctx, presolver, hosts, minServers) hostToIPs, warnings, err := resolveHosts(ctx, u.presolver, hosts, minServers)
warnings = append(warnings, newWarnings...) for _, warning := range warnings {
u.warner.Warn(warning)
}
if err != nil { if err != nil {
return nil, warnings, err return nil, err
} }
hts.adaptWithIPs(hostToIPs) hts.adaptWithIPs(hostToIPs)
@@ -46,11 +47,11 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper,
servers = hts.toServersSlice() servers = hts.toServersSlice()
if len(servers) < minServers { if len(servers) < minServers {
return nil, warnings, fmt.Errorf("%w: %d and expected at least %d", return nil, fmt.Errorf("%w: %d and expected at least %d",
ErrNotEnoughServers, len(servers), minServers) ErrNotEnoughServers, len(servers), minServers)
} }
sortServers(servers) sortServers(servers)
return servers, warnings, nil return servers, nil
} }

View File

@@ -0,0 +1,29 @@
package surfshark
import (
"net/http"
"github.com/qdm12/gluetun/internal/updater/resolver"
"github.com/qdm12/gluetun/internal/updater/unzip"
)
type Updater struct {
client *http.Client
unzipper unzip.Unzipper
presolver resolver.Parallel
warner Warner
}
type Warner interface {
Warn(s string)
}
func New(client *http.Client, unzipper unzip.Unzipper,
presolver resolver.Parallel, warner Warner) *Updater {
return &Updater{
client: client,
unzipper: unzipper,
presolver: presolver,
warner: warner,
}
}

View File

@@ -10,27 +10,24 @@ import (
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/updater/openvpn" "github.com/qdm12/gluetun/internal/updater/openvpn"
"github.com/qdm12/gluetun/internal/updater/resolver"
"github.com/qdm12/gluetun/internal/updater/unzip"
"golang.org/x/text/cases" "golang.org/x/text/cases"
"golang.org/x/text/language" "golang.org/x/text/language"
) )
var ErrNotEnoughServers = errors.New("not enough servers found") var ErrNotEnoughServers = errors.New("not enough servers found")
func GetServers(ctx context.Context, unzipper unzip.Unzipper, func (u *Updater) GetServers(ctx context.Context, minServers int) (
presolver resolver.Parallel, minServers int) ( servers []models.Server, err error) {
servers []models.Server, warnings []string, err error) {
const tcpURL = "https://torguard.net/downloads/OpenVPN-TCP-Linux.zip" const tcpURL = "https://torguard.net/downloads/OpenVPN-TCP-Linux.zip"
tcpContents, err := unzipper.FetchAndExtract(ctx, tcpURL) tcpContents, err := u.unzipper.FetchAndExtract(ctx, tcpURL)
if err != nil { if err != nil {
return nil, nil, err return nil, err
} }
const udpURL = "https://torguard.net/downloads/OpenVPN-UDP-Linux.zip" const udpURL = "https://torguard.net/downloads/OpenVPN-UDP-Linux.zip"
udpContents, err := unzipper.FetchAndExtract(ctx, udpURL) udpContents, err := u.unzipper.FetchAndExtract(ctx, udpURL)
if err != nil { if err != nil {
return nil, nil, err return nil, err
} }
hts := make(hostToServer) hts := make(hostToServer)
@@ -38,26 +35,26 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper,
for fileName, content := range tcpContents { for fileName, content := range tcpContents {
const tcp, udp = true, false const tcp, udp = true, false
newWarnings := addServerFromOvpn(fileName, content, hts, tcp, udp, titleCaser) warnings := addServerFromOvpn(fileName, content, hts, tcp, udp, titleCaser)
warnings = append(warnings, newWarnings...) u.warnWarnings(warnings)
} }
for fileName, content := range udpContents { for fileName, content := range udpContents {
const tcp, udp = false, true const tcp, udp = false, true
newWarnings := addServerFromOvpn(fileName, content, hts, tcp, udp, titleCaser) warnings := addServerFromOvpn(fileName, content, hts, tcp, udp, titleCaser)
warnings = append(warnings, newWarnings...) u.warnWarnings(warnings)
} }
if len(hts) < minServers { if len(hts) < minServers {
return nil, warnings, fmt.Errorf("%w: %d and expected at least %d", return nil, fmt.Errorf("%w: %d and expected at least %d",
ErrNotEnoughServers, len(hts), minServers) ErrNotEnoughServers, len(hts), minServers)
} }
hosts := hts.toHostsSlice() hosts := hts.toHostsSlice()
hostToIPs, newWarnings, err := resolveHosts(ctx, presolver, hosts, minServers) hostToIPs, warnings, err := resolveHosts(ctx, u.presolver, hosts, minServers)
warnings = append(warnings, newWarnings...) u.warnWarnings(warnings)
if err != nil { if err != nil {
return nil, warnings, err return nil, err
} }
hts.adaptWithIPs(hostToIPs) hts.adaptWithIPs(hostToIPs)
@@ -65,13 +62,13 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper,
servers = hts.toServersSlice() servers = hts.toServersSlice()
if len(servers) < minServers { if len(servers) < minServers {
return nil, warnings, fmt.Errorf("%w: %d and expected at least %d", return nil, fmt.Errorf("%w: %d and expected at least %d",
ErrNotEnoughServers, len(servers), minServers) ErrNotEnoughServers, len(servers), minServers)
} }
sortServers(servers) sortServers(servers)
return servers, warnings, nil return servers, nil
} }
func addServerFromOvpn(fileName string, content []byte, func addServerFromOvpn(fileName string, content []byte,
@@ -104,3 +101,9 @@ func addServerFromOvpn(fileName string, content []byte,
hts.add(host, country, city, tcp, udp, ips) hts.add(host, country, city, tcp, udp, ips)
return warnings return warnings
} }
func (u *Updater) warnWarnings(warnings []string) {
for _, warning := range warnings {
u.warner.Warn(warning)
}
}

View File

@@ -0,0 +1,25 @@
package torguard
import (
"github.com/qdm12/gluetun/internal/updater/resolver"
"github.com/qdm12/gluetun/internal/updater/unzip"
)
type Updater struct {
unzipper unzip.Unzipper
presolver resolver.Parallel
warner Warner
}
type Warner interface {
Warn(s string)
}
func New(unzipper unzip.Unzipper, presolver resolver.Parallel,
warner Warner) *Updater {
return &Updater{
unzipper: unzipper,
presolver: presolver,
warner: warner,
}
}

View File

@@ -8,23 +8,25 @@ import (
"fmt" "fmt"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/updater/resolver"
"github.com/qdm12/gluetun/internal/updater/unzip"
) )
var ErrNotEnoughServers = errors.New("not enough servers found") var ErrNotEnoughServers = errors.New("not enough servers found")
func GetServers(ctx context.Context, unzipper unzip.Unzipper, func (u *Updater) GetServers(ctx context.Context, minServers int) (
presolver resolver.Parallel, minServers int) ( servers []models.Server, err error) {
servers []models.Server, warnings []string, err error) {
// Hardcoded data from a user provided ZIP file since it's behind a login wall // Hardcoded data from a user provided ZIP file since it's behind a login wall
hts, warnings := getHostToServer() hts, warnings := getHostToServer()
for _, warning := range warnings {
u.warner.Warn(warning)
}
hosts := hts.toHostsSlice() hosts := hts.toHostsSlice()
hostToIPs, newWarnings, err := resolveHosts(ctx, presolver, hosts, minServers) hostToIPs, warnings, err := resolveHosts(ctx, u.presolver, hosts, minServers)
warnings = append(warnings, newWarnings...) for _, warning := range warnings {
u.warner.Warn(warning)
}
if err != nil { if err != nil {
return nil, warnings, err return nil, err
} }
hts.adaptWithIPs(hostToIPs) hts.adaptWithIPs(hostToIPs)
@@ -32,11 +34,11 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper,
servers = hts.toServersSlice() servers = hts.toServersSlice()
if len(servers) < minServers { if len(servers) < minServers {
return nil, warnings, fmt.Errorf("%w: %d and expected at least %d", return nil, fmt.Errorf("%w: %d and expected at least %d",
ErrNotEnoughServers, len(servers), minServers) ErrNotEnoughServers, len(servers), minServers)
} }
sortServers(servers) sortServers(servers)
return servers, warnings, nil return servers, nil
} }

View File

@@ -0,0 +1,25 @@
package vpnunlimited
import (
"github.com/qdm12/gluetun/internal/updater/resolver"
"github.com/qdm12/gluetun/internal/updater/unzip"
)
type Updater struct {
unzipper unzip.Unzipper
presolver resolver.Parallel
warner Warner
}
type Warner interface {
Warn(s string)
}
func New(unzipper unzip.Unzipper, presolver resolver.Parallel,
warner Warner) *Updater {
return &Updater{
unzipper: unzipper,
presolver: presolver,
warner: warner,
}
}

View File

@@ -10,21 +10,18 @@ import (
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/updater/openvpn" "github.com/qdm12/gluetun/internal/updater/openvpn"
"github.com/qdm12/gluetun/internal/updater/resolver"
"github.com/qdm12/gluetun/internal/updater/unzip"
) )
var ErrNotEnoughServers = errors.New("not enough servers found") var ErrNotEnoughServers = errors.New("not enough servers found")
func GetServers(ctx context.Context, unzipper unzip.Unzipper, func (u *Updater) GetServers(ctx context.Context, minServers int) (
presolver resolver.Parallel, minServers int) ( servers []models.Server, err error) {
servers []models.Server, warnings []string, err error) {
const url = "https://support.vyprvpn.com/hc/article_attachments/360052617332/Vypr_OpenVPN_20200320.zip" const url = "https://support.vyprvpn.com/hc/article_attachments/360052617332/Vypr_OpenVPN_20200320.zip"
contents, err := unzipper.FetchAndExtract(ctx, url) contents, err := u.unzipper.FetchAndExtract(ctx, url)
if err != nil { if err != nil {
return nil, nil, err return nil, err
} else if len(contents) < minServers { } else if len(contents) < minServers {
return nil, nil, fmt.Errorf("%w: %d and expected at least %d", return nil, fmt.Errorf("%w: %d and expected at least %d",
ErrNotEnoughServers, len(contents), minServers) ErrNotEnoughServers, len(contents), minServers)
} }
@@ -37,26 +34,25 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper,
host, warning, err := openvpn.ExtractHost(content) host, warning, err := openvpn.ExtractHost(content)
if warning != "" { if warning != "" {
warnings = append(warnings, warning) u.warner.Warn(warning)
} }
if err != nil { if err != nil {
// treat error as warning and go to next file // treat error as warning and go to next file
warning := err.Error() + " in " + fileName u.warner.Warn(err.Error() + " in " + fileName)
warnings = append(warnings, warning)
continue continue
} }
tcp, udp, err := openvpn.ExtractProto(content) tcp, udp, err := openvpn.ExtractProto(content)
if err != nil { if err != nil {
// treat error as warning and go to next file // treat error as warning and go to next file
warning := err.Error() + " in " + fileName u.warner.Warn(err.Error() + " in " + fileName)
warnings = append(warnings, warning)
continue continue
} }
region, err := parseFilename(fileName) region, err := parseFilename(fileName)
if err != nil { if err != nil {
warnings = append(warnings, err.Error()) // treat error as warning and go to next file
u.warner.Warn(err.Error())
continue continue
} }
@@ -64,15 +60,17 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper,
} }
if len(hts) < minServers { if len(hts) < minServers {
return nil, warnings, fmt.Errorf("%w: %d and expected at least %d", return nil, fmt.Errorf("%w: %d and expected at least %d",
ErrNotEnoughServers, len(hts), minServers) ErrNotEnoughServers, len(hts), minServers)
} }
hosts := hts.toHostsSlice() hosts := hts.toHostsSlice()
hostToIPs, newWarnings, err := resolveHosts(ctx, presolver, hosts, minServers) hostToIPs, warnings, err := resolveHosts(ctx, u.presolver, hosts, minServers)
warnings = append(warnings, newWarnings...) for _, warning := range warnings {
u.warner.Warn(warning)
}
if err != nil { if err != nil {
return nil, warnings, err return nil, err
} }
hts.adaptWithIPs(hostToIPs) hts.adaptWithIPs(hostToIPs)
@@ -80,11 +78,11 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper,
servers = hts.toServersSlice() servers = hts.toServersSlice()
if len(servers) < minServers { if len(servers) < minServers {
return nil, warnings, fmt.Errorf("%w: %d and expected at least %d", return nil, fmt.Errorf("%w: %d and expected at least %d",
ErrNotEnoughServers, len(servers), minServers) ErrNotEnoughServers, len(servers), minServers)
} }
sortServers(servers) sortServers(servers)
return servers, warnings, nil return servers, nil
} }

View File

@@ -0,0 +1,25 @@
package vyprvpn
import (
"github.com/qdm12/gluetun/internal/updater/resolver"
"github.com/qdm12/gluetun/internal/updater/unzip"
)
type Updater struct {
unzipper unzip.Unzipper
presolver resolver.Parallel
warner Warner
}
type Warner interface {
Warn(s string)
}
func New(unzipper unzip.Unzipper, presolver resolver.Parallel,
warner Warner) *Updater {
return &Updater{
unzipper: unzipper,
presolver: presolver,
warner: warner,
}
}

View File

@@ -9,7 +9,6 @@ import (
"github.com/qdm12/gluetun/internal/constants/vpn" "github.com/qdm12/gluetun/internal/constants/vpn"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/updater/resolver"
) )
var ( var (
@@ -18,8 +17,8 @@ var (
ErrNotEnoughServers = errors.New("not enough servers found") ErrNotEnoughServers = errors.New("not enough servers found")
) )
func GetServers(ctx context.Context, presolver resolver.Parallel, minServers int) ( func (u *Updater) GetServers(ctx context.Context, minServers int) (
servers []models.Server, warnings []string, err error) { servers []models.Server, err error) {
cities := getAvailableCities() cities := getAvailableCities()
servers = make([]models.Server, 0, len(cities)) servers = make([]models.Server, 0, len(cities))
hostnames := make([]string, len(cities)) hostnames := make([]string, len(cities))
@@ -31,14 +30,16 @@ func GetServers(ctx context.Context, presolver resolver.Parallel, minServers int
hostnameToCity[hostname] = city hostnameToCity[hostname] = city
} }
hostnameToIPs, newWarnings, err := resolveHosts(ctx, presolver, hostnames, minServers) hostnameToIPs, warnings, err := resolveHosts(ctx, u.presolver, hostnames, minServers)
warnings = append(warnings, newWarnings...) for _, warning := range warnings {
u.warner.Warn(warning)
}
if err != nil { if err != nil {
return nil, warnings, err return nil, err
} }
if len(hostnameToIPs) < minServers { if len(hostnameToIPs) < minServers {
return nil, warnings, fmt.Errorf("%w: %d and expected at least %d", return nil, fmt.Errorf("%w: %d and expected at least %d",
ErrNotEnoughServers, len(servers), minServers) ErrNotEnoughServers, len(servers), minServers)
} }
@@ -56,5 +57,5 @@ func GetServers(ctx context.Context, presolver resolver.Parallel, minServers int
sortServers(servers) sortServers(servers)
return servers, warnings, nil return servers, nil
} }

View File

@@ -0,0 +1,21 @@
package wevpn
import (
"github.com/qdm12/gluetun/internal/updater/resolver"
)
type Updater struct {
presolver resolver.Parallel
warner Warner
}
type Warner interface {
Warn(s string)
}
func New(presolver resolver.Parallel, warner Warner) *Updater {
return &Updater{
presolver: presolver,
warner: warner,
}
}

View File

@@ -7,7 +7,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"net" "net"
"net/http"
"github.com/qdm12/gluetun/internal/constants/vpn" "github.com/qdm12/gluetun/internal/constants/vpn"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
@@ -18,9 +17,9 @@ var (
ErrNoWireguardKey = errors.New("no wireguard public key found") ErrNoWireguardKey = errors.New("no wireguard public key found")
) )
func GetServers(ctx context.Context, client *http.Client, minServers int) ( func (u *Updater) GetServers(ctx context.Context, minServers int) (
servers []models.Server, err error) { servers []models.Server, err error) {
data, err := fetchAPI(ctx, client) data, err := fetchAPI(ctx, u.client)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@@ -0,0 +1,21 @@
package windscribe
import (
"net/http"
)
type Updater struct {
client *http.Client
warner Warner
}
type Warner interface {
Warn(s string)
}
func New(client *http.Client, warner Warner) *Updater {
return &Updater{
client: client,
warner: warner,
}
}

View File

@@ -30,75 +30,76 @@ import (
windscribe "github.com/qdm12/gluetun/internal/provider/windscribe/updater" windscribe "github.com/qdm12/gluetun/internal/provider/windscribe/updater"
) )
func (u *Updater) updateProvider(ctx context.Context, provider string) ( func (u *Updater) updateProvider(ctx context.Context, provider string) (err error) {
warnings []string, err error) {
existingServers := u.getProviderServers(provider) existingServers := u.getProviderServers(provider)
minServers := getMinServers(existingServers) minServers := getMinServers(existingServers)
servers, warnings, err := u.getServers(ctx, provider, minServers) servers, err := u.getServers(ctx, provider, minServers)
if err != nil { if err != nil {
return warnings, err return err
} }
if reflect.DeepEqual(existingServers, servers) { if reflect.DeepEqual(existingServers, servers) {
return warnings, nil return nil
} }
u.patchProvider(provider, servers) u.patchProvider(provider, servers)
return warnings, nil return nil
} }
func (u *Updater) getServers(ctx context.Context, provider string, func (u *Updater) getServers(ctx context.Context, provider string,
minServers int) (servers []models.Server, warnings []string, err error) { minServers int) (servers []models.Server, err error) {
var providerUpdater interface {
GetServers(ctx context.Context, minServers int) (servers []models.Server, err error)
}
switch provider { switch provider {
case providers.Custom: case providers.Custom:
panic("cannot update custom provider") panic("cannot update custom provider")
case providers.Cyberghost: case providers.Cyberghost:
servers, err = cyberghost.GetServers(ctx, u.presolver, minServers) providerUpdater = cyberghost.New(u.presolver)
return servers, nil, err
case providers.Expressvpn: case providers.Expressvpn:
return expressvpn.GetServers(ctx, u.unzipper, u.presolver, minServers) providerUpdater = expressvpn.New(u.unzipper, u.presolver, u.logger)
case providers.Fastestvpn: case providers.Fastestvpn:
return fastestvpn.GetServers(ctx, u.unzipper, u.presolver, minServers) providerUpdater = fastestvpn.New(u.unzipper, u.presolver, u.logger)
case providers.HideMyAss: case providers.HideMyAss:
return hidemyass.GetServers(ctx, u.client, u.presolver, minServers) providerUpdater = hidemyass.New(u.client, u.presolver, u.logger)
case providers.Ipvanish: case providers.Ipvanish:
return ipvanish.GetServers(ctx, u.unzipper, u.presolver, minServers) providerUpdater = ipvanish.New(u.unzipper, u.presolver, u.logger)
case providers.Ivpn: case providers.Ivpn:
return ivpn.GetServers(ctx, u.client, u.presolver, minServers) providerUpdater = ivpn.New(u.client, u.presolver, u.logger)
case providers.Mullvad: case providers.Mullvad:
servers, err = mullvad.GetServers(ctx, u.client, minServers) providerUpdater = mullvad.New(u.client)
return servers, nil, err
case providers.Nordvpn: case providers.Nordvpn:
return nordvpn.GetServers(ctx, u.client, minServers) providerUpdater = nordvpn.New(u.client, u.logger)
case providers.Perfectprivacy: case providers.Perfectprivacy:
return perfectprivacy.GetServers(ctx, u.unzipper, minServers) providerUpdater = perfectprivacy.New(u.unzipper, u.logger)
case providers.Privado: case providers.Privado:
return privado.GetServers(ctx, u.unzipper, u.client, u.presolver, minServers) providerUpdater = privado.New(u.client, u.unzipper, u.presolver, u.logger)
case providers.PrivateInternetAccess: case providers.PrivateInternetAccess:
servers, err = privateinternetaccess.GetServers(ctx, u.client, minServers) providerUpdater = privateinternetaccess.New(u.client)
return servers, nil, err
case providers.Privatevpn: case providers.Privatevpn:
return privatevpn.GetServers(ctx, u.unzipper, u.presolver, minServers) providerUpdater = privatevpn.New(u.unzipper, u.presolver, u.logger)
case providers.Protonvpn: case providers.Protonvpn:
return protonvpn.GetServers(ctx, u.client, minServers) providerUpdater = protonvpn.New(u.client, u.logger)
case providers.Purevpn: case providers.Purevpn:
return purevpn.GetServers(ctx, u.client, u.unzipper, u.presolver, minServers) providerUpdater = purevpn.New(u.client, u.unzipper, u.presolver, u.logger)
case providers.Surfshark: case providers.Surfshark:
return surfshark.GetServers(ctx, u.unzipper, u.client, u.presolver, minServers) providerUpdater = surfshark.New(u.client, u.unzipper, u.presolver, u.logger)
case providers.Torguard: case providers.Torguard:
return torguard.GetServers(ctx, u.unzipper, u.presolver, minServers) providerUpdater = torguard.New(u.unzipper, u.presolver, u.logger)
case providers.VPNUnlimited: case providers.VPNUnlimited:
return vpnunlimited.GetServers(ctx, u.unzipper, u.presolver, minServers) providerUpdater = vpnunlimited.New(u.unzipper, u.presolver, u.logger)
case providers.Vyprvpn: case providers.Vyprvpn:
return vyprvpn.GetServers(ctx, u.unzipper, u.presolver, minServers) providerUpdater = vyprvpn.New(u.unzipper, u.presolver, u.logger)
case providers.Wevpn: case providers.Wevpn:
return wevpn.GetServers(ctx, u.presolver, minServers) providerUpdater = wevpn.New(u.presolver, u.logger)
case providers.Windscribe: case providers.Windscribe:
servers, err = windscribe.GetServers(ctx, u.client, minServers) providerUpdater = windscribe.New(u.client, u.logger)
return servers, nil, err
default: default:
panic("provider " + provider + " is unknown") panic("provider " + provider + " is unknown")
} }
servers, err = providerUpdater.GetServers(ctx, minServers)
return servers, err
} }
func (u *Updater) getProviderServers(provider string) (servers []models.Server) { func (u *Updater) getProviderServers(provider string) (servers []models.Server) {

View File

@@ -56,12 +56,7 @@ func (u *Updater) UpdateServers(ctx context.Context) (allServers models.AllServe
u.logger.Info("updating " + caser.String(provider) + " servers...") u.logger.Info("updating " + caser.String(provider) + " servers...")
// TODO support servers offering only TCP or only UDP // TODO support servers offering only TCP or only UDP
// for NordVPN and PureVPN // for NordVPN and PureVPN
warnings, err := u.updateProvider(ctx, provider) err := u.updateProvider(ctx, provider)
if *u.options.CLI {
for _, warning := range warnings {
u.logger.Warn(provider + ": " + warning)
}
}
if err != nil { if err != nil {
if ctxErr := ctx.Err(); ctxErr != nil { if ctxErr := ctx.Err(); ctxErr != nil {
return allServers, ctxErr return allServers, ctxErr