From 90c6c8485b0cf6dee497052f43426a52667779bc Mon Sep 17 00:00:00 2001 From: Quentin McGaw Date: Sat, 28 May 2022 20:58:50 +0000 Subject: [PATCH] chore(updater): common `GetServers` signature - Log warnings when running outside of CLI mode - Remove updater CLI bool setting - Warnings are logged in updating functions --- internal/cli/update.go | 4 +- internal/configuration/settings/updater.go | 13 ---- .../provider/cyberghost/updater/servers.go | 7 +- .../provider/cyberghost/updater/updater.go | 13 ++++ .../provider/expressvpn/updater/servers.go | 19 +++--- .../provider/expressvpn/updater/updater.go | 25 +++++++ .../provider/fastestvpn/updater/servers.go | 34 +++++----- .../provider/fastestvpn/updater/updater.go | 25 +++++++ .../provider/hidemyass/updater/servers.go | 22 +++---- .../provider/hidemyass/updater/updater.go | 26 ++++++++ .../ipvanish/updater/mocks_generate_test.go | 3 + .../provider/ipvanish/updater/mocks_test.go | 46 +++++++++++++ internal/provider/ipvanish/updater/servers.go | 38 +++++------ .../provider/ipvanish/updater/servers_test.go | 66 ++++++++++++++----- internal/provider/ipvanish/updater/updater.go | 25 +++++++ .../ivpn/updater/mocks_generate_test.go | 3 + internal/provider/ivpn/updater/mocks_test.go | 46 +++++++++++++ internal/provider/ivpn/updater/servers.go | 22 +++---- .../provider/ivpn/updater/servers_test.go | 33 +++++++--- internal/provider/ivpn/updater/updater.go | 26 ++++++++ internal/provider/mullvad/updater/servers.go | 5 +- internal/provider/mullvad/updater/updater.go | 15 +++++ internal/provider/nordvpn/updater/servers.go | 20 +++--- internal/provider/nordvpn/updater/updater.go | 21 ++++++ .../perfectprivacy/updater/servers.go | 15 ++--- .../perfectprivacy/updater/updater.go | 19 ++++++ internal/provider/privado/updater/servers.go | 35 +++++----- internal/provider/privado/updater/updater.go | 29 ++++++++ .../privateinternetaccess/updater/servers.go | 5 +- .../privateinternetaccess/updater/updater.go | 19 ++++++ .../provider/privatevpn/updater/servers.go | 37 +++++------ .../provider/privatevpn/updater/updater.go | 25 +++++++ .../provider/protonvpn/updater/servers.go | 20 +++--- .../provider/protonvpn/updater/updater.go | 21 ++++++ internal/provider/purevpn/updater/servers.go | 40 +++++------ internal/provider/purevpn/updater/updater.go | 29 ++++++++ .../provider/surfshark/updater/servers.go | 31 ++++----- .../provider/surfshark/updater/updater.go | 29 ++++++++ internal/provider/torguard/updater/servers.go | 41 ++++++------ internal/provider/torguard/updater/updater.go | 25 +++++++ .../provider/vpnunlimited/updater/servers.go | 22 ++++--- .../provider/vpnunlimited/updater/updater.go | 25 +++++++ internal/provider/vyprvpn/updater/servers.go | 38 +++++------ internal/provider/vyprvpn/updater/updater.go | 25 +++++++ internal/provider/wevpn/updater/servers.go | 17 ++--- internal/provider/wevpn/updater/updater.go | 21 ++++++ .../provider/windscribe/updater/servers.go | 5 +- .../provider/windscribe/updater/updater.go | 21 ++++++ internal/updater/providers.go | 63 +++++++++--------- internal/updater/updater.go | 7 +- 50 files changed, 896 insertions(+), 325 deletions(-) create mode 100644 internal/provider/cyberghost/updater/updater.go create mode 100644 internal/provider/expressvpn/updater/updater.go create mode 100644 internal/provider/fastestvpn/updater/updater.go create mode 100644 internal/provider/hidemyass/updater/updater.go create mode 100644 internal/provider/ipvanish/updater/mocks_generate_test.go create mode 100644 internal/provider/ipvanish/updater/mocks_test.go create mode 100644 internal/provider/ipvanish/updater/updater.go create mode 100644 internal/provider/ivpn/updater/mocks_generate_test.go create mode 100644 internal/provider/ivpn/updater/mocks_test.go create mode 100644 internal/provider/ivpn/updater/updater.go create mode 100644 internal/provider/mullvad/updater/updater.go create mode 100644 internal/provider/nordvpn/updater/updater.go create mode 100644 internal/provider/perfectprivacy/updater/updater.go create mode 100644 internal/provider/privado/updater/updater.go create mode 100644 internal/provider/privateinternetaccess/updater/updater.go create mode 100644 internal/provider/privatevpn/updater/updater.go create mode 100644 internal/provider/protonvpn/updater/updater.go create mode 100644 internal/provider/purevpn/updater/updater.go create mode 100644 internal/provider/surfshark/updater/updater.go create mode 100644 internal/provider/torguard/updater/updater.go create mode 100644 internal/provider/vpnunlimited/updater/updater.go create mode 100644 internal/provider/vyprvpn/updater/updater.go create mode 100644 internal/provider/wevpn/updater/updater.go create mode 100644 internal/provider/windscribe/updater/updater.go diff --git a/internal/cli/update.go b/internal/cli/update.go index 3eb548ef..acc04b38 100644 --- a/internal/cli/update.go +++ b/internal/cli/update.go @@ -36,10 +36,8 @@ type UpdaterLogger interface { Error(s string) } -func boolPtr(b bool) *bool { return &b } - 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 dnsAddress, csvProviders string flagSet := flag.NewFlagSet("update", flag.ExitOnError) diff --git a/internal/configuration/settings/updater.go b/internal/configuration/settings/updater.go index 92cd55c3..82db7848 100644 --- a/internal/configuration/settings/updater.go +++ b/internal/configuration/settings/updater.go @@ -26,11 +26,6 @@ type Updater struct { // Providers is the list of VPN service providers // to update server information for. 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) { @@ -62,7 +57,6 @@ func (u *Updater) copy() (copied Updater) { Period: helpers.CopyDurationPtr(u.Period), DNSAddress: helpers.CopyIP(u.DNSAddress), 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.DNSAddress = helpers.MergeWithIP(u.DNSAddress, other.DNSAddress) u.Providers = helpers.MergeStringSlices(u.Providers, other.Providers) - u.CLI = helpers.MergeWithBool(u.CLI, other.CLI) } // 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.DNSAddress = helpers.OverrideWithIP(u.DNSAddress, other.DNSAddress) u.Providers = helpers.OverrideWithStringSlice(u.Providers, other.Providers) - u.CLI = helpers.MergeWithBool(u.CLI, other.CLI) } func (u *Updater) SetDefaults(vpnProvider string) { u.Period = helpers.DefaultDuration(u.Period, 0) 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 { u.Providers = []string{vpnProvider} } @@ -108,9 +99,5 @@ func (u Updater) toLinesNode() (node *gotree.Node) { node.Appendf("DNS address: %s", u.DNSAddress) node.Appendf("Providers to update: %s", strings.Join(u.Providers, ", ")) - if *u.CLI { - node.Appendf("CLI mode: enabled") - } - return node } diff --git a/internal/provider/cyberghost/updater/servers.go b/internal/provider/cyberghost/updater/servers.go index f16519d7..ade563b6 100644 --- a/internal/provider/cyberghost/updater/servers.go +++ b/internal/provider/cyberghost/updater/servers.go @@ -6,15 +6,14 @@ import ( "context" "github.com/qdm12/gluetun/internal/models" - "github.com/qdm12/gluetun/internal/updater/resolver" ) -func GetServers(ctx context.Context, presolver resolver.Parallel, - minServers int) (servers []models.Server, err error) { +func (u *Updater) GetServers(ctx context.Context, minServers int) ( + servers []models.Server, err error) { possibleServers := getPossibleServers() possibleHosts := possibleServers.hostsSlice() - hostToIPs, err := resolveHosts(ctx, presolver, possibleHosts, minServers) + hostToIPs, err := resolveHosts(ctx, u.presolver, possibleHosts, minServers) if err != nil { return nil, err } diff --git a/internal/provider/cyberghost/updater/updater.go b/internal/provider/cyberghost/updater/updater.go new file mode 100644 index 00000000..588f5727 --- /dev/null +++ b/internal/provider/cyberghost/updater/updater.go @@ -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, + } +} diff --git a/internal/provider/expressvpn/updater/servers.go b/internal/provider/expressvpn/updater/servers.go index 73626028..0e4cc352 100644 --- a/internal/provider/expressvpn/updater/servers.go +++ b/internal/provider/expressvpn/updater/servers.go @@ -9,15 +9,12 @@ import ( "github.com/qdm12/gluetun/internal/constants/vpn" "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") -func GetServers(ctx context.Context, unzipper unzip.Unzipper, - presolver resolver.Parallel, minServers int) ( - servers []models.Server, warnings []string, err error) { +func (u *Updater) GetServers(ctx context.Context, minServers int) ( + servers []models.Server, err error) { servers = hardcodedServers() hosts := make([]string, len(servers)) @@ -25,10 +22,12 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper, hosts[i] = servers[i].Hostname } - hostToIPs, newWarnings, err := resolveHosts(ctx, presolver, hosts, minServers) - warnings = append(warnings, newWarnings...) + hostToIPs, warnings, err := resolveHosts(ctx, u.presolver, hosts, minServers) + for _, warning := range warnings { + u.warner.Warn(warning) + } if err != nil { - return nil, warnings, err + return nil, err } i := 0 @@ -46,11 +45,11 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper, servers = servers[:i] 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) } sortServers(servers) - return servers, warnings, nil + return servers, nil } diff --git a/internal/provider/expressvpn/updater/updater.go b/internal/provider/expressvpn/updater/updater.go new file mode 100644 index 00000000..49d7907b --- /dev/null +++ b/internal/provider/expressvpn/updater/updater.go @@ -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, + } +} diff --git a/internal/provider/fastestvpn/updater/servers.go b/internal/provider/fastestvpn/updater/servers.go index ded7c6dc..77288c3c 100644 --- a/internal/provider/fastestvpn/updater/servers.go +++ b/internal/provider/fastestvpn/updater/servers.go @@ -10,21 +10,18 @@ import ( "github.com/qdm12/gluetun/internal/models" "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") -func GetServers(ctx context.Context, unzipper unzip.Unzipper, - presolver resolver.Parallel, minServers int) ( - servers []models.Server, warnings []string, err error) { +func (u *Updater) GetServers(ctx context.Context, minServers int) ( + servers []models.Server, err error) { 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 { - return nil, nil, err + return nil, err } 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) } @@ -37,18 +34,17 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper, country, tcp, udp, err := parseFilename(fileName) if err != nil { - warnings = append(warnings, err.Error()) + u.warner.Warn(err.Error()) continue } host, warning, err := openvpn.ExtractHost(content) if warning != "" { - warnings = append(warnings, warning) + u.warner.Warn(warning) } if err != nil { // treat error as warning and go to next file - warning := err.Error() + " in " + fileName - warnings = append(warnings, warning) + u.warner.Warn(err.Error() + " in " + fileName) continue } @@ -56,15 +52,17 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper, } 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) } hosts := hts.toHostsSlice() - hostToIPs, newWarnings, err := resolveHosts(ctx, presolver, hosts, minServers) - warnings = append(warnings, newWarnings...) + hostToIPs, warnings, err := resolveHosts(ctx, u.presolver, hosts, minServers) + for _, warning := range warnings { + u.warner.Warn(warning) + } if err != nil { - return nil, warnings, err + return nil, err } hts.adaptWithIPs(hostToIPs) @@ -72,11 +70,11 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper, servers = hts.toServersSlice() 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) } sortServers(servers) - return servers, warnings, nil + return servers, nil } diff --git a/internal/provider/fastestvpn/updater/updater.go b/internal/provider/fastestvpn/updater/updater.go new file mode 100644 index 00000000..3a0b5c3a --- /dev/null +++ b/internal/provider/fastestvpn/updater/updater.go @@ -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, + } +} diff --git a/internal/provider/hidemyass/updater/servers.go b/internal/provider/hidemyass/updater/servers.go index e428e396..c8b43a22 100644 --- a/internal/provider/hidemyass/updater/servers.go +++ b/internal/provider/hidemyass/updater/servers.go @@ -6,33 +6,33 @@ import ( "context" "errors" "fmt" - "net/http" "github.com/qdm12/gluetun/internal/constants/vpn" "github.com/qdm12/gluetun/internal/models" - "github.com/qdm12/gluetun/internal/updater/resolver" ) var ErrNotEnoughServers = errors.New("not enough servers found") -func GetServers(ctx context.Context, client *http.Client, - presolver resolver.Parallel, minServers int) ( - servers []models.Server, warnings []string, err error) { - tcpHostToURL, udpHostToURL, err := getAllHostToURL(ctx, client) +func (u *Updater) GetServers(ctx context.Context, minServers int) ( + servers []models.Server, err error) { + tcpHostToURL, udpHostToURL, err := getAllHostToURL(ctx, u.client) if err != nil { - return nil, nil, err + return nil, err } hosts := getUniqueHosts(tcpHostToURL, udpHostToURL) 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) } - 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 { - return nil, warnings, err + return nil, err } servers = make([]models.Server, 0, len(hostToIPs)) @@ -66,5 +66,5 @@ func GetServers(ctx context.Context, client *http.Client, sortServers(servers) - return servers, warnings, nil + return servers, nil } diff --git a/internal/provider/hidemyass/updater/updater.go b/internal/provider/hidemyass/updater/updater.go new file mode 100644 index 00000000..e56bdc5e --- /dev/null +++ b/internal/provider/hidemyass/updater/updater.go @@ -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, + } +} diff --git a/internal/provider/ipvanish/updater/mocks_generate_test.go b/internal/provider/ipvanish/updater/mocks_generate_test.go new file mode 100644 index 00000000..1c03396d --- /dev/null +++ b/internal/provider/ipvanish/updater/mocks_generate_test.go @@ -0,0 +1,3 @@ +package ipvanish + +//go:generate mockgen -destination=mocks_test.go -package $GOPACKAGE . Warner diff --git a/internal/provider/ipvanish/updater/mocks_test.go b/internal/provider/ipvanish/updater/mocks_test.go new file mode 100644 index 00000000..8ae9d058 --- /dev/null +++ b/internal/provider/ipvanish/updater/mocks_test.go @@ -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) +} diff --git a/internal/provider/ipvanish/updater/servers.go b/internal/provider/ipvanish/updater/servers.go index e34a14af..a93c8738 100644 --- a/internal/provider/ipvanish/updater/servers.go +++ b/internal/provider/ipvanish/updater/servers.go @@ -10,23 +10,20 @@ import ( "github.com/qdm12/gluetun/internal/models" "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/language" ) var ErrNotEnoughServers = errors.New("not enough servers found") -func GetServers(ctx context.Context, unzipper unzip.Unzipper, - presolver resolver.Parallel, minServers int) ( - servers []models.Server, warnings []string, err error) { +func (u *Updater) GetServers(ctx context.Context, minServers int) ( + servers []models.Server, err error) { 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 { - return nil, nil, err + return nil, err } 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) } @@ -41,27 +38,24 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper, tcp, udp, err := openvpn.ExtractProto(content) if err != nil { // treat error as warning and go to next file - warning := err.Error() + ": in " + fileName - warnings = append(warnings, warning) + u.warner.Warn(err.Error() + " in " + fileName) continue } hostname, warning, err := openvpn.ExtractHost(content) if warning != "" { - warnings = append(warnings, warning) + u.warner.Warn(warning) } if err != nil { // treat error as warning and go to next file - warning := err.Error() + " in " + fileName - warnings = append(warnings, warning) + u.warner.Warn(err.Error() + " in " + fileName) continue } country, city, err := parseFilename(fileName, hostname, titleCaser) if err != nil { // treat error as warning and go to next file - warning := err.Error() + " in " + fileName - warnings = append(warnings, warning) + u.warner.Warn(err.Error() + " in " + fileName) continue } @@ -69,15 +63,17 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper, } 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) } hosts := hts.toHostsSlice() - hostToIPs, newWarnings, err := resolveHosts(ctx, presolver, hosts, minServers) - warnings = append(warnings, newWarnings...) + hostToIPs, warnings, err := resolveHosts(ctx, u.presolver, hosts, minServers) + for _, warning := range warnings { + u.warner.Warn(warning) + } if err != nil { - return nil, warnings, err + return nil, err } hts.adaptWithIPs(hostToIPs) @@ -85,11 +81,11 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper, servers = hts.toServersSlice() 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) } sortServers(servers) - return servers, warnings, nil + return servers, nil } diff --git a/internal/provider/ipvanish/updater/servers_test.go b/internal/provider/ipvanish/updater/servers_test.go index 2709bc9b..12642804 100644 --- a/internal/provider/ipvanish/updater/servers_test.go +++ b/internal/provider/ipvanish/updater/servers_test.go @@ -16,12 +16,15 @@ import ( "github.com/stretchr/testify/require" ) -func Test_GetServers(t *testing.T) { +func Test_Updater_GetServers(t *testing.T) { t.Parallel() testCases := map[string]struct { // Inputs minServers int + // Mocks + warnerBuilder func(ctrl *gomock.Controller) Warner + // Unzip unzipContents map[string][]byte unzipErr error @@ -35,48 +38,67 @@ func Test_GetServers(t *testing.T) { resolveErr error // Output - servers []models.Server - warnings []string - err error + servers []models.Server + err error }{ "unzipper error": { - unzipErr: errors.New("dummy"), - err: errors.New("dummy"), + warnerBuilder: func(ctrl *gomock.Controller) Warner { return nil }, + unzipErr: errors.New("dummy"), + err: errors.New("dummy"), }, "not enough unzip contents": { minServers: 1, + warnerBuilder: func(ctrl *gomock.Controller) Warner { return nil }, unzipContents: map[string][]byte{}, err: errors.New("not enough servers found: 0 and expected at least 1"), }, "no openvpn file": { minServers: 1, + warnerBuilder: func(ctrl *gomock.Controller) Warner { return nil }, unzipContents: map[string][]byte{"somefile.txt": {}}, err: errors.New("not enough servers found: 0 and expected at least 1"), }, "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`)}, - warnings: []string{"unknown protocol: invalid: in badproto.ovpn"}, err: errors.New("not enough servers found: 0 and expected at least 1"), }, "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(``)}, - warnings: []string{"remote host not found in nohost.ovpn"}, err: errors.New("not enough servers found: 0 and expected at least 1"), }, "multiple hosts": { 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{ "ipvanish-CA-City-A-hosta.ovpn": []byte("remote hosta\nremote hostb"), }, expectResolve: true, hostsToResolve: []string{"hosta"}, 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"), }, "resolve error": { + warnerBuilder: func(ctrl *gomock.Controller) Warner { + warner := NewMockWarner(ctrl) + warner.EXPECT().Warn("resolve warning") + return warner + }, unzipContents: map[string][]byte{ "ipvanish-CA-City-A-hosta.ovpn": []byte("remote hosta"), }, @@ -85,19 +107,27 @@ func Test_GetServers(t *testing.T) { resolveSettings: getResolveSettings(0), resolveWarnings: []string{"resolve warning"}, resolveErr: errors.New("dummy"), - warnings: []string{"resolve warning"}, err: errors.New("dummy"), }, "filename parsing error": { 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{ "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": { minServers: 1, + warnerBuilder: func(ctrl *gomock.Controller) Warner { + warner := NewMockWarner(ctrl) + warner.EXPECT().Warn("resolve warning") + return warner + }, unzipContents: map[string][]byte{ "ipvanish-CA-City-A-hosta.ovpn": []byte("remote hosta"), "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}}, }, }, - warnings: []string{"resolve warning"}, }, } for name, testCase := range testCases { @@ -150,10 +179,13 @@ func Test_GetServers(t *testing.T) { 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.warnings, warnings) if testCase.err != nil { require.Error(t, err) assert.Equal(t, testCase.err.Error(), err.Error()) diff --git a/internal/provider/ipvanish/updater/updater.go b/internal/provider/ipvanish/updater/updater.go new file mode 100644 index 00000000..edd73374 --- /dev/null +++ b/internal/provider/ipvanish/updater/updater.go @@ -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, + } +} diff --git a/internal/provider/ivpn/updater/mocks_generate_test.go b/internal/provider/ivpn/updater/mocks_generate_test.go new file mode 100644 index 00000000..c6e92d88 --- /dev/null +++ b/internal/provider/ivpn/updater/mocks_generate_test.go @@ -0,0 +1,3 @@ +package ivpn + +//go:generate mockgen -destination=mocks_test.go -package $GOPACKAGE . Warner diff --git a/internal/provider/ivpn/updater/mocks_test.go b/internal/provider/ivpn/updater/mocks_test.go new file mode 100644 index 00000000..350f76d6 --- /dev/null +++ b/internal/provider/ivpn/updater/mocks_test.go @@ -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) +} diff --git a/internal/provider/ivpn/updater/servers.go b/internal/provider/ivpn/updater/servers.go index 60cdb167..783991f8 100644 --- a/internal/provider/ivpn/updater/servers.go +++ b/internal/provider/ivpn/updater/servers.go @@ -6,23 +6,20 @@ import ( "context" "errors" "fmt" - "net/http" "github.com/qdm12/gluetun/internal/constants/vpn" "github.com/qdm12/gluetun/internal/models" - "github.com/qdm12/gluetun/internal/updater/resolver" ) var ( ErrNotEnoughServers = errors.New("not enough servers found") ) -func GetServers(ctx context.Context, client *http.Client, - presolver resolver.Parallel, minServers int) ( - servers []models.Server, warnings []string, err error) { - data, err := fetchAPI(ctx, client) +func (u *Updater) GetServers(ctx context.Context, minServers int) ( + servers []models.Server, err error) { + data, err := fetchAPI(ctx, u.client) 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)) @@ -40,13 +37,16 @@ func GetServers(ctx context.Context, client *http.Client, } 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) } - 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 { - return nil, warnings, err + return nil, err } servers = make([]models.Server, 0, len(hosts)) @@ -78,5 +78,5 @@ func GetServers(ctx context.Context, client *http.Client, sortServers(servers) - return servers, warnings, nil + return servers, nil } diff --git a/internal/provider/ivpn/updater/servers_test.go b/internal/provider/ivpn/updater/servers_test.go index 28e8547d..2f3d9aae 100644 --- a/internal/provider/ivpn/updater/servers_test.go +++ b/internal/provider/ivpn/updater/servers_test.go @@ -18,13 +18,16 @@ import ( "github.com/stretchr/testify/require" ) -func Test_GetServers(t *testing.T) { +func Test_Updater_GetServers(t *testing.T) { t.Parallel() testCases := map[string]struct { // Inputs minServers int + // Mocks + warnerBuilder func(ctrl *gomock.Controller) Warner + // From API responseBody string responseStatus int @@ -38,15 +41,20 @@ func Test_GetServers(t *testing.T) { resolveErr error // Output - servers []models.Server - warnings []string - err error + servers []models.Server + err error }{ "http response error": { + warnerBuilder: func(ctrl *gomock.Controller) Warner { return nil }, responseStatus: http.StatusNoContent, err: errors.New("failed fetching API: HTTP status code not OK: 204 No Content"), }, "resolve error": { + warnerBuilder: func(ctrl *gomock.Controller) Warner { + warner := NewMockWarner(ctrl) + warner.EXPECT().Warn("resolve warning") + return warner + }, responseBody: `{"servers":[ {"hostnames":{"openvpn":"hosta"}} ]}`, @@ -56,11 +64,11 @@ func Test_GetServers(t *testing.T) { resolveSettings: getResolveSettings(0), resolveWarnings: []string{"resolve warning"}, resolveErr: errors.New("dummy"), - warnings: []string{"resolve warning"}, err: errors.New("dummy"), }, "not enough servers": { - minServers: 2, + minServers: 2, + warnerBuilder: func(ctrl *gomock.Controller) Warner { return nil }, responseBody: `{"servers":[ {"hostnames":{"openvpn":"hosta"}} ]}`, @@ -69,6 +77,11 @@ func Test_GetServers(t *testing.T) { }, "success": { minServers: 1, + warnerBuilder: func(ctrl *gomock.Controller) Warner { + warner := NewMockWarner(ctrl) + warner.EXPECT().Warn("resolve warning") + return warner + }, responseBody: `{"servers":[ {"country":"Country1","city":"City A","hostnames":{"openvpn":"hosta"}}, {"country":"Country2","city":"City B","hostnames":{"openvpn":"hostb"},"wg_public_key":"xyz"}, @@ -97,7 +110,6 @@ func Test_GetServers(t *testing.T) { WgPubKey: "xyz", IPs: []net.IP{{5, 5, 5, 5}, {6, 6, 6, 6}}}, }, - warnings: []string{"resolve warning"}, }, } for name, testCase := range testCases { @@ -126,10 +138,13 @@ func Test_GetServers(t *testing.T) { 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.warnings, warnings) if testCase.err != nil { require.Error(t, err) assert.Equal(t, testCase.err.Error(), err.Error()) diff --git a/internal/provider/ivpn/updater/updater.go b/internal/provider/ivpn/updater/updater.go new file mode 100644 index 00000000..294594e8 --- /dev/null +++ b/internal/provider/ivpn/updater/updater.go @@ -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, + } +} diff --git a/internal/provider/mullvad/updater/servers.go b/internal/provider/mullvad/updater/servers.go index 2919e19f..9386a8f5 100644 --- a/internal/provider/mullvad/updater/servers.go +++ b/internal/provider/mullvad/updater/servers.go @@ -6,16 +6,15 @@ import ( "context" "errors" "fmt" - "net/http" "github.com/qdm12/gluetun/internal/models" ) 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) { - data, err := fetchAPI(ctx, client) + data, err := fetchAPI(ctx, u.client) if err != nil { return nil, err } diff --git a/internal/provider/mullvad/updater/updater.go b/internal/provider/mullvad/updater/updater.go new file mode 100644 index 00000000..7b4ef630 --- /dev/null +++ b/internal/provider/mullvad/updater/updater.go @@ -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, + } +} diff --git a/internal/provider/nordvpn/updater/servers.go b/internal/provider/nordvpn/updater/servers.go index 3b8894c4..09e90dae 100644 --- a/internal/provider/nordvpn/updater/servers.go +++ b/internal/provider/nordvpn/updater/servers.go @@ -7,7 +7,6 @@ import ( "errors" "fmt" "net" - "net/http" "github.com/qdm12/gluetun/internal/constants/vpn" "github.com/qdm12/gluetun/internal/models" @@ -19,30 +18,29 @@ var ( ErrNotEnoughServers = errors.New("not enough servers found") ) -func GetServers(ctx context.Context, client *http.Client, minServers int) ( - servers []models.Server, warnings []string, err error) { - data, err := fetchAPI(ctx, client) +func (u *Updater) GetServers(ctx context.Context, minServers int) ( + servers []models.Server, err error) { + data, err := fetchAPI(ctx, u.client) if err != nil { - return nil, nil, err + return nil, err } servers = make([]models.Server, 0, len(data)) for _, jsonServer := range data { if !jsonServer.Features.TCP && !jsonServer.Features.UDP { - warning := "server does not support TCP and UDP for openvpn: " + jsonServer.Name - warnings = append(warnings, warning) + u.warner.Warn("server does not support TCP and UDP for openvpn: " + jsonServer.Name) continue } ip, err := parseIPv4(jsonServer.IPAddress) 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) if err != nil { - return nil, nil, err + return nil, err } server := models.Server{ @@ -58,11 +56,11 @@ func GetServers(ctx context.Context, client *http.Client, minServers int) ( } 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) } sortServers(servers) - return servers, warnings, nil + return servers, nil } diff --git a/internal/provider/nordvpn/updater/updater.go b/internal/provider/nordvpn/updater/updater.go new file mode 100644 index 00000000..d0e928ae --- /dev/null +++ b/internal/provider/nordvpn/updater/updater.go @@ -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, + } +} diff --git a/internal/provider/perfectprivacy/updater/servers.go b/internal/provider/perfectprivacy/updater/servers.go index 13b26061..83d91c5d 100644 --- a/internal/provider/perfectprivacy/updater/servers.go +++ b/internal/provider/perfectprivacy/updater/servers.go @@ -9,13 +9,12 @@ import ( "github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/updater/openvpn" - "github.com/qdm12/gluetun/internal/updater/unzip" ) var ErrNotEnoughServers = errors.New("not enough servers found") -func GetServers(ctx context.Context, unzipper unzip.Unzipper, minServers int) ( - servers []models.Server, warnings []string, err error) { +func (u *Updater) GetServers(ctx context.Context, minServers int) ( + servers []models.Server, err error) { zipURL := url.URL{ Scheme: "https", 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 zipURL.RawQuery = values.Encode() - contents, err := unzipper.FetchAndExtract(ctx, zipURL.String()) + contents, err := u.unzipper.FetchAndExtract(ctx, zipURL.String()) if err != nil { - return nil, nil, err + return nil, err } cts := make(cityToServer) @@ -38,12 +37,12 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper, minServers int) ( for fileName, content := range contents { err := addServerFromOvpn(cts, fileName, content) if err != nil { - warnings = append(warnings, fileName+": "+err.Error()) + u.warner.Warn(err.Error() + " in " + fileName) } } 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) } @@ -51,7 +50,7 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper, minServers int) ( sortServers(servers) - return servers, warnings, nil + return servers, nil } func addServerFromOvpn(cts cityToServer, diff --git a/internal/provider/perfectprivacy/updater/updater.go b/internal/provider/perfectprivacy/updater/updater.go new file mode 100644 index 00000000..78546fcf --- /dev/null +++ b/internal/provider/perfectprivacy/updater/updater.go @@ -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, + } +} diff --git a/internal/provider/privado/updater/servers.go b/internal/provider/privado/updater/servers.go index e2403146..ff13499e 100644 --- a/internal/provider/privado/updater/servers.go +++ b/internal/provider/privado/updater/servers.go @@ -6,26 +6,22 @@ import ( "context" "errors" "fmt" - "net/http" "strings" "github.com/qdm12/gluetun/internal/models" "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") -func GetServers(ctx context.Context, unzipper unzip.Unzipper, - client *http.Client, presolver resolver.Parallel, minServers int) ( - servers []models.Server, warnings []string, err error) { +func (u *Updater) GetServers(ctx context.Context, minServers int) ( + servers []models.Server, err error) { 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 { - return nil, nil, err + return nil, err } 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) } @@ -38,12 +34,11 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper, host, warning, err := openvpn.ExtractHost(content) if warning != "" { - warnings = append(warnings, warning) + u.warner.Warn(warning) } if err != nil { // treat error as warning and go to next file - warning := err.Error() + " in " + fileName - warnings = append(warnings, warning) + u.warner.Warn(err.Error() + " in " + fileName) continue } @@ -51,26 +46,28 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper, } 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) } hosts := hts.toHostsSlice() - hostToIPs, newWarnings, err := resolveHosts(ctx, presolver, hosts, minServers) - warnings = append(warnings, newWarnings...) + hostToIPs, warnings, err := resolveHosts(ctx, u.presolver, hosts, minServers) + for _, warning := range warnings { + u.warner.Warn(warning) + } if err != nil { - return nil, warnings, err + return nil, err } hts.adaptWithIPs(hostToIPs) servers = hts.toServersSlice() - if err := setLocationInfo(ctx, client, servers); err != nil { - return nil, warnings, err + if err := setLocationInfo(ctx, u.client, servers); err != nil { + return nil, err } sortServers(servers) - return servers, warnings, nil + return servers, nil } diff --git a/internal/provider/privado/updater/updater.go b/internal/provider/privado/updater/updater.go new file mode 100644 index 00000000..ac37afe6 --- /dev/null +++ b/internal/provider/privado/updater/updater.go @@ -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, + } +} diff --git a/internal/provider/privateinternetaccess/updater/servers.go b/internal/provider/privateinternetaccess/updater/servers.go index 9fc49c6a..d3e54d3d 100644 --- a/internal/provider/privateinternetaccess/updater/servers.go +++ b/internal/provider/privateinternetaccess/updater/servers.go @@ -6,7 +6,6 @@ import ( "context" "errors" "fmt" - "net/http" "time" "github.com/qdm12/gluetun/internal/models" @@ -14,7 +13,7 @@ import ( 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) { nts := make(nameToServer) @@ -26,7 +25,7 @@ func GetServers(ctx context.Context, client *http.Client, minServers int) ( maxTimer := time.NewTimer(maxDuration) for { - data, err := fetchAPI(ctx, client) + data, err := fetchAPI(ctx, u.client) if err != nil { return nil, err } diff --git a/internal/provider/privateinternetaccess/updater/updater.go b/internal/provider/privateinternetaccess/updater/updater.go new file mode 100644 index 00000000..53bb2ca2 --- /dev/null +++ b/internal/provider/privateinternetaccess/updater/updater.go @@ -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, + } +} diff --git a/internal/provider/privatevpn/updater/servers.go b/internal/provider/privatevpn/updater/servers.go index da7f64fd..912170bd 100644 --- a/internal/provider/privatevpn/updater/servers.go +++ b/internal/provider/privatevpn/updater/servers.go @@ -11,21 +11,18 @@ import ( "github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/models" "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") -func GetServers(ctx context.Context, unzipper unzip.Unzipper, - presolver resolver.Parallel, minServers int) ( - servers []models.Server, warnings []string, err error) { +func (u *Updater) GetServers(ctx context.Context, minServers int) ( + servers []models.Server, err error) { 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 { - return nil, nil, err + return nil, err } 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) } @@ -41,18 +38,19 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper, countryCode, city, err := parseFilename(fileName) 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 } country, warning := codeToCountry(countryCode, countryCodes) if warning != "" { - warnings = append(warnings, warning) + u.warner.Warn(warning) } host, warning, err := openvpn.ExtractHost(content) if warning != "" { - warnings = append(warnings, warning) + u.warner.Warn(warning) } if err == nil { // found host hts.add(host, country, city) @@ -61,12 +59,11 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper, ips, extractIPErr := openvpn.ExtractIPs(content) if warning != "" { - warnings = append(warnings, warning) + u.warner.Warn(warning) } if extractIPErr != nil { // treat extract host error as warning and go to next file - warning := err.Error() + " in " + fileName - warnings = append(warnings, warning) + u.warner.Warn(extractIPErr.Error() + " in " + fileName) continue } server := models.Server{ @@ -80,16 +77,18 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper, } 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) } hosts := hts.toHostsSlice() - hostToIPs, newWarnings, err := resolveHosts(ctx, presolver, hosts, minServers) - warnings = append(warnings, newWarnings...) + hostToIPs, warnings, err := resolveHosts(ctx, u.presolver, hosts, minServers) + for _, warning := range warnings { + u.warner.Warn(warning) + } if err != nil { - return nil, warnings, err + return nil, err } hts.adaptWithIPs(hostToIPs) @@ -99,5 +98,5 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper, sortServers(servers) - return servers, warnings, nil + return servers, nil } diff --git a/internal/provider/privatevpn/updater/updater.go b/internal/provider/privatevpn/updater/updater.go new file mode 100644 index 00000000..46d2bdd5 --- /dev/null +++ b/internal/provider/privatevpn/updater/updater.go @@ -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, + } +} diff --git a/internal/provider/protonvpn/updater/servers.go b/internal/provider/protonvpn/updater/servers.go index bf80059e..8d4bd44f 100644 --- a/internal/provider/protonvpn/updater/servers.go +++ b/internal/provider/protonvpn/updater/servers.go @@ -6,7 +6,6 @@ import ( "context" "errors" "fmt" - "net/http" "github.com/qdm12/gluetun/internal/constants" "github.com/qdm12/gluetun/internal/models" @@ -14,11 +13,11 @@ import ( var ErrNotEnoughServers = errors.New("not enough servers found") -func GetServers(ctx context.Context, client *http.Client, minServers int) ( - servers []models.Server, warnings []string, err error) { - data, err := fetchAPI(ctx, client) +func (u *Updater) GetServers(ctx context.Context, minServers int) ( + servers []models.Server, err error) { + data, err := fetchAPI(ctx, u.client) if err != nil { - return nil, nil, err + return nil, err } countryCodes := constants.CountryCodes() @@ -29,7 +28,7 @@ func GetServers(ctx context.Context, client *http.Client, minServers int) ( } 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) } @@ -40,8 +39,7 @@ func GetServers(ctx context.Context, client *http.Client, minServers int) ( name := logicalServer.Name for _, physicalServer := range logicalServer.Servers { if physicalServer.Status == 0 { // disabled so skip server - warnings = append(warnings, - "ignoring server "+physicalServer.Domain+" with status 0") + u.warner.Warn("ignoring server " + physicalServer.Domain + " with status 0") continue } @@ -53,7 +51,7 @@ func GetServers(ctx context.Context, client *http.Client, minServers int) ( countryCode := logicalServer.ExitCountry country, warning := codeToCountry(countryCode, countryCodes) if warning != "" { - warnings = append(warnings, warning) + u.warner.Warn(warning) } 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 { - 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) } @@ -69,7 +67,7 @@ func GetServers(ctx context.Context, client *http.Client, minServers int) ( sortServers(servers) - return servers, warnings, nil + return servers, nil } func getStringValue(ptr *string) string { diff --git a/internal/provider/protonvpn/updater/updater.go b/internal/provider/protonvpn/updater/updater.go new file mode 100644 index 00000000..d9a1a08a --- /dev/null +++ b/internal/provider/protonvpn/updater/updater.go @@ -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, + } +} diff --git a/internal/provider/purevpn/updater/servers.go b/internal/provider/purevpn/updater/servers.go index 82c15f0d..1f3aff8a 100644 --- a/internal/provider/purevpn/updater/servers.go +++ b/internal/provider/purevpn/updater/servers.go @@ -7,27 +7,23 @@ import ( "errors" "fmt" "net" - "net/http" "strings" "github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/publicip" "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") -func GetServers(ctx context.Context, client *http.Client, - unzipper unzip.Unzipper, presolver resolver.Parallel, minServers int) ( - servers []models.Server, warnings []string, err error) { +func (u *Updater) GetServers(ctx context.Context, minServers int) ( + servers []models.Server, err error) { 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 { - return nil, nil, err + return nil, err } 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) } @@ -41,20 +37,18 @@ func GetServers(ctx context.Context, client *http.Client, tcp, udp, err := openvpn.ExtractProto(content) if err != nil { // treat error as warning and go to next file - warning := err.Error() + " in " + fileName - warnings = append(warnings, warning) + u.warner.Warn(err.Error() + " in " + fileName) continue } host, warning, err := openvpn.ExtractHost(content) if warning != "" { - warnings = append(warnings, warning) + u.warner.Warn(warning) } if err != nil { // treat error as warning and go to next file - warning := err.Error() + " in " + fileName - warnings = append(warnings, warning) + u.warner.Warn(err.Error() + " in " + fileName) continue } @@ -62,15 +56,17 @@ func GetServers(ctx context.Context, client *http.Client, } 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) } hosts := hts.toHostsSlice() - hostToIPs, newWarnings, err := resolveHosts(ctx, presolver, hosts, minServers) - warnings = append(warnings, newWarnings...) + hostToIPs, warnings, err := resolveHosts(ctx, u.presolver, hosts, minServers) + for _, warning := range warnings { + u.warner.Warn(warning) + } if err != nil { - return nil, warnings, err + return nil, err } hts.adaptWithIPs(hostToIPs) @@ -78,7 +74,7 @@ func GetServers(ctx context.Context, client *http.Client, servers = hts.toServersSlice() 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) } @@ -87,9 +83,9 @@ func GetServers(ctx context.Context, client *http.Client, for i := range servers { ipsToGetInfo[i] = servers[i].IPs[0] } - ipsInfo, err := publicip.MultiInfo(ctx, client, ipsToGetInfo) + ipsInfo, err := publicip.MultiInfo(ctx, u.client, ipsToGetInfo) if err != nil { - return nil, warnings, err + return nil, err } for i := range servers { servers[i].Country = ipsInfo[i].Country @@ -99,5 +95,5 @@ func GetServers(ctx context.Context, client *http.Client, sortServers(servers) - return servers, warnings, nil + return servers, nil } diff --git a/internal/provider/purevpn/updater/updater.go b/internal/provider/purevpn/updater/updater.go new file mode 100644 index 00000000..0faead4e --- /dev/null +++ b/internal/provider/purevpn/updater/updater.go @@ -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, + } +} diff --git a/internal/provider/surfshark/updater/servers.go b/internal/provider/surfshark/updater/servers.go index a1dab7a8..41303192 100644 --- a/internal/provider/surfshark/updater/servers.go +++ b/internal/provider/surfshark/updater/servers.go @@ -6,39 +6,40 @@ import ( "context" "errors" "fmt" - "net/http" "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") ) -func GetServers(ctx context.Context, unzipper unzip.Unzipper, - client *http.Client, presolver resolver.Parallel, minServers int) ( - servers []models.Server, warnings []string, err error) { +func (u *Updater) GetServers(ctx context.Context, minServers int) ( + servers []models.Server, err error) { hts := make(hostToServer) - err = addServersFromAPI(ctx, client, hts) + err = addServersFromAPI(ctx, u.client, hts) 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 { - 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) hosts := hts.toHostsSlice() - hostToIPs, newWarnings, err := resolveHosts(ctx, presolver, hosts, minServers) - warnings = append(warnings, newWarnings...) + hostToIPs, warnings, err := resolveHosts(ctx, u.presolver, hosts, minServers) + for _, warning := range warnings { + u.warner.Warn(warning) + } if err != nil { - return nil, warnings, err + return nil, err } hts.adaptWithIPs(hostToIPs) @@ -46,11 +47,11 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper, servers = hts.toServersSlice() 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) } sortServers(servers) - return servers, warnings, nil + return servers, nil } diff --git a/internal/provider/surfshark/updater/updater.go b/internal/provider/surfshark/updater/updater.go new file mode 100644 index 00000000..e8e4d6f3 --- /dev/null +++ b/internal/provider/surfshark/updater/updater.go @@ -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, + } +} diff --git a/internal/provider/torguard/updater/servers.go b/internal/provider/torguard/updater/servers.go index b60423f1..8688886a 100644 --- a/internal/provider/torguard/updater/servers.go +++ b/internal/provider/torguard/updater/servers.go @@ -10,27 +10,24 @@ import ( "github.com/qdm12/gluetun/internal/models" "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/language" ) var ErrNotEnoughServers = errors.New("not enough servers found") -func GetServers(ctx context.Context, unzipper unzip.Unzipper, - presolver resolver.Parallel, minServers int) ( - servers []models.Server, warnings []string, err error) { +func (u *Updater) GetServers(ctx context.Context, minServers int) ( + servers []models.Server, err error) { 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 { - return nil, nil, err + return nil, err } 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 { - return nil, nil, err + return nil, err } hts := make(hostToServer) @@ -38,26 +35,26 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper, for fileName, content := range tcpContents { const tcp, udp = true, false - newWarnings := addServerFromOvpn(fileName, content, hts, tcp, udp, titleCaser) - warnings = append(warnings, newWarnings...) + warnings := addServerFromOvpn(fileName, content, hts, tcp, udp, titleCaser) + u.warnWarnings(warnings) } for fileName, content := range udpContents { const tcp, udp = false, true - newWarnings := addServerFromOvpn(fileName, content, hts, tcp, udp, titleCaser) - warnings = append(warnings, newWarnings...) + warnings := addServerFromOvpn(fileName, content, hts, tcp, udp, titleCaser) + u.warnWarnings(warnings) } 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) } hosts := hts.toHostsSlice() - hostToIPs, newWarnings, err := resolveHosts(ctx, presolver, hosts, minServers) - warnings = append(warnings, newWarnings...) + hostToIPs, warnings, err := resolveHosts(ctx, u.presolver, hosts, minServers) + u.warnWarnings(warnings) if err != nil { - return nil, warnings, err + return nil, err } hts.adaptWithIPs(hostToIPs) @@ -65,13 +62,13 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper, servers = hts.toServersSlice() 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) } sortServers(servers) - return servers, warnings, nil + return servers, nil } func addServerFromOvpn(fileName string, content []byte, @@ -104,3 +101,9 @@ func addServerFromOvpn(fileName string, content []byte, hts.add(host, country, city, tcp, udp, ips) return warnings } + +func (u *Updater) warnWarnings(warnings []string) { + for _, warning := range warnings { + u.warner.Warn(warning) + } +} diff --git a/internal/provider/torguard/updater/updater.go b/internal/provider/torguard/updater/updater.go new file mode 100644 index 00000000..b9a86431 --- /dev/null +++ b/internal/provider/torguard/updater/updater.go @@ -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, + } +} diff --git a/internal/provider/vpnunlimited/updater/servers.go b/internal/provider/vpnunlimited/updater/servers.go index 3514bbde..84b868ce 100644 --- a/internal/provider/vpnunlimited/updater/servers.go +++ b/internal/provider/vpnunlimited/updater/servers.go @@ -8,23 +8,25 @@ import ( "fmt" "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") -func GetServers(ctx context.Context, unzipper unzip.Unzipper, - presolver resolver.Parallel, minServers int) ( - servers []models.Server, warnings []string, err error) { +func (u *Updater) GetServers(ctx context.Context, minServers int) ( + servers []models.Server, err error) { // Hardcoded data from a user provided ZIP file since it's behind a login wall hts, warnings := getHostToServer() + for _, warning := range warnings { + u.warner.Warn(warning) + } hosts := hts.toHostsSlice() - hostToIPs, newWarnings, err := resolveHosts(ctx, presolver, hosts, minServers) - warnings = append(warnings, newWarnings...) + hostToIPs, warnings, err := resolveHosts(ctx, u.presolver, hosts, minServers) + for _, warning := range warnings { + u.warner.Warn(warning) + } if err != nil { - return nil, warnings, err + return nil, err } hts.adaptWithIPs(hostToIPs) @@ -32,11 +34,11 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper, servers = hts.toServersSlice() 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) } sortServers(servers) - return servers, warnings, nil + return servers, nil } diff --git a/internal/provider/vpnunlimited/updater/updater.go b/internal/provider/vpnunlimited/updater/updater.go new file mode 100644 index 00000000..e2ccd824 --- /dev/null +++ b/internal/provider/vpnunlimited/updater/updater.go @@ -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, + } +} diff --git a/internal/provider/vyprvpn/updater/servers.go b/internal/provider/vyprvpn/updater/servers.go index 342ba547..ad96b3ff 100644 --- a/internal/provider/vyprvpn/updater/servers.go +++ b/internal/provider/vyprvpn/updater/servers.go @@ -10,21 +10,18 @@ import ( "github.com/qdm12/gluetun/internal/models" "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") -func GetServers(ctx context.Context, unzipper unzip.Unzipper, - presolver resolver.Parallel, minServers int) ( - servers []models.Server, warnings []string, err error) { +func (u *Updater) GetServers(ctx context.Context, minServers int) ( + servers []models.Server, err error) { 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 { - return nil, nil, err + return nil, err } 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) } @@ -37,26 +34,25 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper, host, warning, err := openvpn.ExtractHost(content) if warning != "" { - warnings = append(warnings, warning) + u.warner.Warn(warning) } if err != nil { // treat error as warning and go to next file - warning := err.Error() + " in " + fileName - warnings = append(warnings, warning) + u.warner.Warn(err.Error() + " in " + fileName) continue } tcp, udp, err := openvpn.ExtractProto(content) if err != nil { // treat error as warning and go to next file - warning := err.Error() + " in " + fileName - warnings = append(warnings, warning) + u.warner.Warn(err.Error() + " in " + fileName) continue } region, err := parseFilename(fileName) if err != nil { - warnings = append(warnings, err.Error()) + // treat error as warning and go to next file + u.warner.Warn(err.Error()) continue } @@ -64,15 +60,17 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper, } 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) } hosts := hts.toHostsSlice() - hostToIPs, newWarnings, err := resolveHosts(ctx, presolver, hosts, minServers) - warnings = append(warnings, newWarnings...) + hostToIPs, warnings, err := resolveHosts(ctx, u.presolver, hosts, minServers) + for _, warning := range warnings { + u.warner.Warn(warning) + } if err != nil { - return nil, warnings, err + return nil, err } hts.adaptWithIPs(hostToIPs) @@ -80,11 +78,11 @@ func GetServers(ctx context.Context, unzipper unzip.Unzipper, servers = hts.toServersSlice() 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) } sortServers(servers) - return servers, warnings, nil + return servers, nil } diff --git a/internal/provider/vyprvpn/updater/updater.go b/internal/provider/vyprvpn/updater/updater.go new file mode 100644 index 00000000..b4bcfe9a --- /dev/null +++ b/internal/provider/vyprvpn/updater/updater.go @@ -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, + } +} diff --git a/internal/provider/wevpn/updater/servers.go b/internal/provider/wevpn/updater/servers.go index b46eeeca..1c524826 100644 --- a/internal/provider/wevpn/updater/servers.go +++ b/internal/provider/wevpn/updater/servers.go @@ -9,7 +9,6 @@ import ( "github.com/qdm12/gluetun/internal/constants/vpn" "github.com/qdm12/gluetun/internal/models" - "github.com/qdm12/gluetun/internal/updater/resolver" ) var ( @@ -18,8 +17,8 @@ var ( ErrNotEnoughServers = errors.New("not enough servers found") ) -func GetServers(ctx context.Context, presolver resolver.Parallel, minServers int) ( - servers []models.Server, warnings []string, err error) { +func (u *Updater) GetServers(ctx context.Context, minServers int) ( + servers []models.Server, err error) { cities := getAvailableCities() servers = make([]models.Server, 0, len(cities)) hostnames := make([]string, len(cities)) @@ -31,14 +30,16 @@ func GetServers(ctx context.Context, presolver resolver.Parallel, minServers int hostnameToCity[hostname] = city } - hostnameToIPs, newWarnings, err := resolveHosts(ctx, presolver, hostnames, minServers) - warnings = append(warnings, newWarnings...) + hostnameToIPs, warnings, err := resolveHosts(ctx, u.presolver, hostnames, minServers) + for _, warning := range warnings { + u.warner.Warn(warning) + } if err != nil { - return nil, warnings, err + return nil, err } 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) } @@ -56,5 +57,5 @@ func GetServers(ctx context.Context, presolver resolver.Parallel, minServers int sortServers(servers) - return servers, warnings, nil + return servers, nil } diff --git a/internal/provider/wevpn/updater/updater.go b/internal/provider/wevpn/updater/updater.go new file mode 100644 index 00000000..8d118e6c --- /dev/null +++ b/internal/provider/wevpn/updater/updater.go @@ -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, + } +} diff --git a/internal/provider/windscribe/updater/servers.go b/internal/provider/windscribe/updater/servers.go index ad9b2fed..6bcdef7e 100644 --- a/internal/provider/windscribe/updater/servers.go +++ b/internal/provider/windscribe/updater/servers.go @@ -7,7 +7,6 @@ import ( "errors" "fmt" "net" - "net/http" "github.com/qdm12/gluetun/internal/constants/vpn" "github.com/qdm12/gluetun/internal/models" @@ -18,9 +17,9 @@ var ( 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) { - data, err := fetchAPI(ctx, client) + data, err := fetchAPI(ctx, u.client) if err != nil { return nil, err } diff --git a/internal/provider/windscribe/updater/updater.go b/internal/provider/windscribe/updater/updater.go new file mode 100644 index 00000000..eed8aa3f --- /dev/null +++ b/internal/provider/windscribe/updater/updater.go @@ -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, + } +} diff --git a/internal/updater/providers.go b/internal/updater/providers.go index 568a1b87..c27e8ecd 100644 --- a/internal/updater/providers.go +++ b/internal/updater/providers.go @@ -30,75 +30,76 @@ import ( windscribe "github.com/qdm12/gluetun/internal/provider/windscribe/updater" ) -func (u *Updater) updateProvider(ctx context.Context, provider string) ( - warnings []string, err error) { +func (u *Updater) updateProvider(ctx context.Context, provider string) (err error) { existingServers := u.getProviderServers(provider) minServers := getMinServers(existingServers) - servers, warnings, err := u.getServers(ctx, provider, minServers) + servers, err := u.getServers(ctx, provider, minServers) if err != nil { - return warnings, err + return err } if reflect.DeepEqual(existingServers, servers) { - return warnings, nil + return nil } u.patchProvider(provider, servers) - return warnings, nil + return nil } 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 { case providers.Custom: panic("cannot update custom provider") case providers.Cyberghost: - servers, err = cyberghost.GetServers(ctx, u.presolver, minServers) - return servers, nil, err + providerUpdater = cyberghost.New(u.presolver) case providers.Expressvpn: - return expressvpn.GetServers(ctx, u.unzipper, u.presolver, minServers) + providerUpdater = expressvpn.New(u.unzipper, u.presolver, u.logger) case providers.Fastestvpn: - return fastestvpn.GetServers(ctx, u.unzipper, u.presolver, minServers) + providerUpdater = fastestvpn.New(u.unzipper, u.presolver, u.logger) case providers.HideMyAss: - return hidemyass.GetServers(ctx, u.client, u.presolver, minServers) + providerUpdater = hidemyass.New(u.client, u.presolver, u.logger) case providers.Ipvanish: - return ipvanish.GetServers(ctx, u.unzipper, u.presolver, minServers) + providerUpdater = ipvanish.New(u.unzipper, u.presolver, u.logger) case providers.Ivpn: - return ivpn.GetServers(ctx, u.client, u.presolver, minServers) + providerUpdater = ivpn.New(u.client, u.presolver, u.logger) case providers.Mullvad: - servers, err = mullvad.GetServers(ctx, u.client, minServers) - return servers, nil, err + providerUpdater = mullvad.New(u.client) case providers.Nordvpn: - return nordvpn.GetServers(ctx, u.client, minServers) + providerUpdater = nordvpn.New(u.client, u.logger) case providers.Perfectprivacy: - return perfectprivacy.GetServers(ctx, u.unzipper, minServers) + providerUpdater = perfectprivacy.New(u.unzipper, u.logger) 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: - servers, err = privateinternetaccess.GetServers(ctx, u.client, minServers) - return servers, nil, err + providerUpdater = privateinternetaccess.New(u.client) case providers.Privatevpn: - return privatevpn.GetServers(ctx, u.unzipper, u.presolver, minServers) + providerUpdater = privatevpn.New(u.unzipper, u.presolver, u.logger) case providers.Protonvpn: - return protonvpn.GetServers(ctx, u.client, minServers) + providerUpdater = protonvpn.New(u.client, u.logger) 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: - 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: - return torguard.GetServers(ctx, u.unzipper, u.presolver, minServers) + providerUpdater = torguard.New(u.unzipper, u.presolver, u.logger) case providers.VPNUnlimited: - return vpnunlimited.GetServers(ctx, u.unzipper, u.presolver, minServers) + providerUpdater = vpnunlimited.New(u.unzipper, u.presolver, u.logger) case providers.Vyprvpn: - return vyprvpn.GetServers(ctx, u.unzipper, u.presolver, minServers) + providerUpdater = vyprvpn.New(u.unzipper, u.presolver, u.logger) case providers.Wevpn: - return wevpn.GetServers(ctx, u.presolver, minServers) + providerUpdater = wevpn.New(u.presolver, u.logger) case providers.Windscribe: - servers, err = windscribe.GetServers(ctx, u.client, minServers) - return servers, nil, err + providerUpdater = windscribe.New(u.client, u.logger) default: panic("provider " + provider + " is unknown") } + + servers, err = providerUpdater.GetServers(ctx, minServers) + return servers, err } func (u *Updater) getProviderServers(provider string) (servers []models.Server) { diff --git a/internal/updater/updater.go b/internal/updater/updater.go index 93a7f86a..b3ae4429 100644 --- a/internal/updater/updater.go +++ b/internal/updater/updater.go @@ -56,12 +56,7 @@ func (u *Updater) UpdateServers(ctx context.Context) (allServers models.AllServe u.logger.Info("updating " + caser.String(provider) + " servers...") // TODO support servers offering only TCP or only UDP // for NordVPN and PureVPN - warnings, err := u.updateProvider(ctx, provider) - if *u.options.CLI { - for _, warning := range warnings { - u.logger.Warn(provider + ": " + warning) - } - } + err := u.updateProvider(ctx, provider) if err != nil { if ctxErr := ctx.Err(); ctxErr != nil { return allServers, ctxErr