Fix & feat: Cyberghost server groups
- Allow multiple comma separated values for CYBERGHOST_GROUP - Defaults to all UDP groups - If TCP is enabled, defaults to all TCP groups - Check groups specified match the protocol - Default Cyberghost group to empty - Adjust formatting and messages
This commit is contained in:
@@ -4,11 +4,10 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/golibs/params"
|
||||
)
|
||||
|
||||
func (settings *Provider) cyberghostLines() (lines []string) {
|
||||
lines = append(lines, lastIndent+"Server group: "+settings.ServerSelection.Group)
|
||||
lines = append(lines, lastIndent+"Server groups: "+commaJoin(settings.ServerSelection.Groups))
|
||||
|
||||
if len(settings.ServerSelection.Regions) > 0 {
|
||||
lines = append(lines, lastIndent+"Regions: "+commaJoin(settings.ServerSelection.Regions))
|
||||
@@ -52,8 +51,8 @@ func (settings *Provider) readCyberghost(r reader) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
settings.ServerSelection.Group, err = r.env.Inside("CYBERGHOST_GROUP",
|
||||
constants.CyberghostGroupChoices(), params.Default("Premium UDP Europe"))
|
||||
settings.ServerSelection.Groups, err = r.env.CSVInside("CYBERGHOST_GROUP",
|
||||
constants.CyberghostGroupChoices())
|
||||
if err != nil {
|
||||
return fmt.Errorf("environment variable CYBERGHOST_GROUP: %w", err)
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ func Test_OpenVPN_JSON(t *testing.T) {
|
||||
"server_selection": {
|
||||
"tcp": false,
|
||||
"regions": null,
|
||||
"group": "",
|
||||
"groups": null,
|
||||
"countries": null,
|
||||
"cities": null,
|
||||
"hostnames": null,
|
||||
|
||||
@@ -24,7 +24,7 @@ func Test_Provider_lines(t *testing.T) {
|
||||
settings: Provider{
|
||||
Name: constants.Cyberghost,
|
||||
ServerSelection: ServerSelection{
|
||||
Group: "group",
|
||||
Groups: []string{"group"},
|
||||
Regions: []string{"a", "El country"},
|
||||
},
|
||||
ExtraConfigOptions: ExtraConfigOptions{
|
||||
@@ -35,7 +35,7 @@ func Test_Provider_lines(t *testing.T) {
|
||||
lines: []string{
|
||||
"|--Cyberghost settings:",
|
||||
" |--Network protocol: udp",
|
||||
" |--Server group: group",
|
||||
" |--Server groups: group",
|
||||
" |--Regions: a, El country",
|
||||
" |--Client key is set",
|
||||
" |--Client certificate is set",
|
||||
|
||||
@@ -13,7 +13,7 @@ type ServerSelection struct { //nolint:maligned
|
||||
Regions []string `json:"regions"`
|
||||
|
||||
// Cyberghost
|
||||
Group string `json:"group"`
|
||||
Groups []string `json:"groups"`
|
||||
|
||||
// Fastestvpn, HideMyAss, IPVanish, IVPN, Mullvad, PrivateVPN, Protonvpn, PureVPN, VPNUnlimited
|
||||
Countries []string `json:"countries"`
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package constants
|
||||
|
||||
import (
|
||||
"sort"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
)
|
||||
|
||||
@@ -20,11 +22,20 @@ func CyberghostRegionChoices() (choices []string) {
|
||||
|
||||
func CyberghostGroupChoices() (choices []string) {
|
||||
servers := CyberghostServers()
|
||||
choices = make([]string, len(servers))
|
||||
for i := range servers {
|
||||
choices[i] = servers[i].Group
|
||||
uniqueChoices := map[string]struct{}{}
|
||||
for _, server := range servers {
|
||||
uniqueChoices[server.Group] = struct{}{}
|
||||
}
|
||||
return makeUnique(choices)
|
||||
|
||||
choices = make([]string, 0, len(uniqueChoices))
|
||||
for choice := range uniqueChoices {
|
||||
choices = append(choices, choice)
|
||||
}
|
||||
|
||||
sortable := sort.StringSlice(choices)
|
||||
sortable.Sort()
|
||||
|
||||
return sortable
|
||||
}
|
||||
|
||||
func CyberghostHostnameChoices() (choices []string) {
|
||||
|
||||
18
internal/constants/cyberghost_test.go
Normal file
18
internal/constants/cyberghost_test.go
Normal file
@@ -0,0 +1,18 @@
|
||||
package constants
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func Test_CyberghostGroupChoices(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
expected := []string{"Premium TCP Asia", "Premium TCP Europe",
|
||||
"Premium TCP USA", "Premium UDP Asia", "Premium UDP Europe",
|
||||
"Premium UDP USA"}
|
||||
choices := CyberghostGroupChoices()
|
||||
|
||||
assert.Equal(t, expected, choices)
|
||||
}
|
||||
@@ -1,18 +1,41 @@
|
||||
package cyberghost
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/qdm12/gluetun/internal/configuration"
|
||||
"github.com/qdm12/gluetun/internal/constants"
|
||||
"github.com/qdm12/gluetun/internal/models"
|
||||
"github.com/qdm12/gluetun/internal/provider/utils"
|
||||
)
|
||||
|
||||
var ErrGroupMismatchesProtocol = errors.New("server group does not match protocol")
|
||||
|
||||
func (c *Cyberghost) filterServers(selection configuration.ServerSelection) (
|
||||
servers []models.CyberghostServer, err error) {
|
||||
if len(selection.Groups) == 0 {
|
||||
if selection.TCP {
|
||||
selection.Groups = tcpGroupChoices()
|
||||
} else {
|
||||
selection.Groups = udpGroupChoices()
|
||||
}
|
||||
}
|
||||
|
||||
// Check each group match the protocol
|
||||
groupsCheckFn := groupsAreAllUDP
|
||||
if selection.TCP {
|
||||
groupsCheckFn = groupsAreAllTCP
|
||||
}
|
||||
if err := groupsCheckFn(selection.Groups); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, server := range c.servers {
|
||||
switch {
|
||||
case selection.Group != "" && !strings.EqualFold(selection.Group, server.Group), // TODO make CSV
|
||||
case
|
||||
utils.FilterByPossibilities(server.Group, selection.Groups),
|
||||
utils.FilterByPossibilities(server.Region, selection.Regions),
|
||||
utils.FilterByPossibilities(server.Hostname, selection.Hostnames):
|
||||
default:
|
||||
@@ -26,3 +49,51 @@ func (c *Cyberghost) filterServers(selection configuration.ServerSelection) (
|
||||
|
||||
return servers, nil
|
||||
}
|
||||
|
||||
func tcpGroupChoices() (choices []string) {
|
||||
const tcp = true
|
||||
return groupsForTCP(tcp)
|
||||
}
|
||||
|
||||
func udpGroupChoices() (choices []string) {
|
||||
const tcp = false
|
||||
return groupsForTCP(tcp)
|
||||
}
|
||||
|
||||
func groupsForTCP(tcp bool) (choices []string) {
|
||||
allGroups := constants.CyberghostGroupChoices()
|
||||
choices = make([]string, 0, len(allGroups))
|
||||
for _, group := range allGroups {
|
||||
switch {
|
||||
case tcp && groupIsTCP(group):
|
||||
choices = append(choices, group)
|
||||
case !tcp && !groupIsTCP(group):
|
||||
choices = append(choices, group)
|
||||
}
|
||||
}
|
||||
return choices
|
||||
}
|
||||
|
||||
func groupIsTCP(group string) bool {
|
||||
return strings.Contains(strings.ToLower(group), "tcp")
|
||||
}
|
||||
|
||||
func groupsAreAllTCP(groups []string) error {
|
||||
for _, group := range groups {
|
||||
if !groupIsTCP(group) {
|
||||
return fmt.Errorf("%w: group %s for protocol TCP",
|
||||
ErrGroupMismatchesProtocol, group)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func groupsAreAllUDP(groups []string) error {
|
||||
for _, group := range groups {
|
||||
if groupIsTCP(group) {
|
||||
return fmt.Errorf("%w: group %s for protocol UDP",
|
||||
ErrGroupMismatchesProtocol, group)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -21,77 +21,102 @@ func Test_Cyberghost_filterServers(t *testing.T) {
|
||||
"no servers": {
|
||||
err: errors.New("no server found: for protocol udp"),
|
||||
},
|
||||
"servers without filter": {
|
||||
"servers without filter defaults to UDP": {
|
||||
servers: []models.CyberghostServer{
|
||||
{Region: "a", Group: "1"},
|
||||
{Region: "b", Group: "1"},
|
||||
{Region: "c", Group: "2"},
|
||||
{Region: "d", Group: "2"},
|
||||
{Region: "a", Group: "Premium TCP Asia"},
|
||||
{Region: "b", Group: "Premium TCP Europe"},
|
||||
{Region: "c", Group: "Premium UDP Asia"},
|
||||
{Region: "d", Group: "Premium UDP Europe"},
|
||||
},
|
||||
filteredServers: []models.CyberghostServer{
|
||||
{Region: "a", Group: "1"},
|
||||
{Region: "b", Group: "1"},
|
||||
{Region: "c", Group: "2"},
|
||||
{Region: "d", Group: "2"},
|
||||
{Region: "c", Group: "Premium UDP Asia"},
|
||||
{Region: "d", Group: "Premium UDP Europe"},
|
||||
},
|
||||
},
|
||||
"servers with TCP selection": {
|
||||
servers: []models.CyberghostServer{
|
||||
{Region: "a", Group: "Premium TCP Asia"},
|
||||
{Region: "b", Group: "Premium TCP Europe"},
|
||||
{Region: "c", Group: "Premium UDP Asia"},
|
||||
{Region: "d", Group: "Premium UDP Europe"},
|
||||
},
|
||||
selection: configuration.ServerSelection{
|
||||
TCP: true,
|
||||
},
|
||||
filteredServers: []models.CyberghostServer{
|
||||
{Region: "a", Group: "Premium TCP Asia"},
|
||||
{Region: "b", Group: "Premium TCP Europe"},
|
||||
},
|
||||
},
|
||||
"servers with regions filter": {
|
||||
servers: []models.CyberghostServer{
|
||||
{Region: "a", Group: "1"},
|
||||
{Region: "b", Group: "1"},
|
||||
{Region: "c", Group: "2"},
|
||||
{Region: "d", Group: "2"},
|
||||
{Region: "a", Group: "Premium UDP Asia"},
|
||||
{Region: "b", Group: "Premium UDP Asia"},
|
||||
{Region: "c", Group: "Premium UDP Asia"},
|
||||
{Region: "d", Group: "Premium UDP Asia"},
|
||||
},
|
||||
selection: configuration.ServerSelection{
|
||||
Regions: []string{"a", "c"},
|
||||
},
|
||||
filteredServers: []models.CyberghostServer{
|
||||
{Region: "a", Group: "1"},
|
||||
{Region: "c", Group: "2"},
|
||||
{Region: "a", Group: "Premium UDP Asia"},
|
||||
{Region: "c", Group: "Premium UDP Asia"},
|
||||
},
|
||||
},
|
||||
"servers with group filter": {
|
||||
servers: []models.CyberghostServer{
|
||||
{Region: "a", Group: "1"},
|
||||
{Region: "b", Group: "1"},
|
||||
{Region: "c", Group: "2"},
|
||||
{Region: "d", Group: "2"},
|
||||
{Region: "a", Group: "Premium UDP Europe"},
|
||||
{Region: "b", Group: "Premium UDP Europe"},
|
||||
{Region: "c", Group: "Premium TCP Europe"},
|
||||
{Region: "d", Group: "Premium TCP Europe"},
|
||||
},
|
||||
selection: configuration.ServerSelection{
|
||||
Group: "1",
|
||||
Groups: []string{"Premium UDP Europe"},
|
||||
},
|
||||
filteredServers: []models.CyberghostServer{
|
||||
{Region: "a", Group: "1"},
|
||||
{Region: "b", Group: "1"},
|
||||
{Region: "a", Group: "Premium UDP Europe"},
|
||||
{Region: "b", Group: "Premium UDP Europe"},
|
||||
},
|
||||
},
|
||||
"servers with bad group filter": {
|
||||
servers: []models.CyberghostServer{
|
||||
{Region: "a", Group: "Premium TCP Europe"},
|
||||
{Region: "b", Group: "Premium TCP Europe"},
|
||||
{Region: "c", Group: "Premium UDP Europe"},
|
||||
{Region: "d", Group: "Premium UDP Europe"},
|
||||
},
|
||||
selection: configuration.ServerSelection{
|
||||
Groups: []string{"Premium TCP Europe"},
|
||||
},
|
||||
err: errors.New("server group does not match protocol: group Premium TCP Europe for protocol UDP"),
|
||||
},
|
||||
"servers with regions and group filter": {
|
||||
servers: []models.CyberghostServer{
|
||||
{Region: "a", Group: "1"},
|
||||
{Region: "b", Group: "1"},
|
||||
{Region: "c", Group: "2"},
|
||||
{Region: "d", Group: "2"},
|
||||
{Region: "a", Group: "Premium UDP Europe"},
|
||||
{Region: "b", Group: "Premium TCP Europe"},
|
||||
{Region: "c", Group: "Premium UDP Asia"},
|
||||
{Region: "d", Group: "Premium TCP Asia"},
|
||||
},
|
||||
selection: configuration.ServerSelection{
|
||||
Regions: []string{"a", "c"},
|
||||
Group: "1",
|
||||
Groups: []string{"Premium UDP Europe"},
|
||||
},
|
||||
filteredServers: []models.CyberghostServer{
|
||||
{Region: "a", Group: "1"},
|
||||
{Region: "a", Group: "Premium UDP Europe"},
|
||||
},
|
||||
},
|
||||
"servers with hostnames filter": {
|
||||
servers: []models.CyberghostServer{
|
||||
{Hostname: "a"},
|
||||
{Hostname: "b"},
|
||||
{Hostname: "c"},
|
||||
{Hostname: "a", Group: "Premium UDP Asia"},
|
||||
{Hostname: "b", Group: "Premium UDP Asia"},
|
||||
{Hostname: "c", Group: "Premium UDP Asia"},
|
||||
},
|
||||
selection: configuration.ServerSelection{
|
||||
Hostnames: []string{"a", "c"},
|
||||
},
|
||||
filteredServers: []models.CyberghostServer{
|
||||
{Hostname: "a"},
|
||||
{Hostname: "c"},
|
||||
{Hostname: "a", Group: "Premium UDP Asia"},
|
||||
{Hostname: "c", Group: "Premium UDP Asia"},
|
||||
},
|
||||
},
|
||||
}
|
||||
@@ -113,3 +138,25 @@ func Test_Cyberghost_filterServers(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_tcpGroupChoices(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
expected := []string{
|
||||
"Premium TCP Asia", "Premium TCP Europe", "Premium TCP USA",
|
||||
}
|
||||
choices := tcpGroupChoices()
|
||||
|
||||
assert.Equal(t, expected, choices)
|
||||
}
|
||||
|
||||
func Test_udpGroupChoices(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
expected := []string{
|
||||
"Premium UDP Asia", "Premium UDP Europe", "Premium UDP USA",
|
||||
}
|
||||
choices := udpGroupChoices()
|
||||
|
||||
assert.Equal(t, expected, choices)
|
||||
}
|
||||
|
||||
@@ -25,8 +25,13 @@ func NoServerFoundError(selection configuration.ServerSelection) (err error) {
|
||||
}
|
||||
messageParts = append(messageParts, "protocol "+protocol)
|
||||
|
||||
if selection.Group != "" {
|
||||
part := "group " + selection.Group
|
||||
switch len(selection.Countries) {
|
||||
case 0:
|
||||
case 1:
|
||||
part := "group " + selection.Groups[0]
|
||||
messageParts = append(messageParts, part)
|
||||
default:
|
||||
part := "groups " + commaJoin(selection.Groups)
|
||||
messageParts = append(messageParts, part)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user