IVPN server data update code and ISP filter (#578)

- Use IVPN's HTTP API instead of their .zip file
- Unit tests for API and GetServers
- Paves the way for Wireguard
- Update server information for IVPN
- Add `ISP` filter for IVPN
This commit is contained in:
Quentin McGaw
2021-08-22 20:11:56 -07:00
committed by GitHub
parent b69dcb62e3
commit c348343b22
17 changed files with 711 additions and 614 deletions

View File

@@ -24,6 +24,11 @@ func (settings *Provider) readIvpn(r reader) (err error) {
return fmt.Errorf("environment variable CITY: %w", err) return fmt.Errorf("environment variable CITY: %w", err)
} }
settings.ServerSelection.ISPs, err = r.env.CSVInside("ISP", constants.IvpnISPChoices())
if err != nil {
return fmt.Errorf("environment variable ISP: %w", err)
}
settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.IvpnHostnameChoices()) settings.ServerSelection.Hostnames, err = r.env.CSVInside("SERVER_HOSTNAME", constants.IvpnHostnameChoices())
if err != nil { if err != nil {
return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err) return fmt.Errorf("environment variable SERVER_HOSTNAME: %w", err)

View File

@@ -34,6 +34,7 @@ func Test_Provider_readIvpn(t *testing.T) {
targetIP singleStringCall targetIP singleStringCall
countries sliceStringCall countries sliceStringCall
cities sliceStringCall cities sliceStringCall
isps sliceStringCall
hostnames sliceStringCall hostnames sliceStringCall
settings Provider settings Provider
err error err error
@@ -62,10 +63,21 @@ func Test_Provider_readIvpn(t *testing.T) {
}, },
err: errors.New("environment variable CITY: dummy test error"), err: errors.New("environment variable CITY: dummy test error"),
}, },
"isps error": {
targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true},
isps: sliceStringCall{call: true, err: errDummy},
settings: Provider{
Name: constants.Ivpn,
},
err: errors.New("environment variable ISP: dummy test error"),
},
"hostnames error": { "hostnames error": {
targetIP: singleStringCall{call: true}, targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true}, countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true}, cities: sliceStringCall{call: true},
isps: sliceStringCall{call: true},
hostnames: sliceStringCall{call: true, err: errDummy}, hostnames: sliceStringCall{call: true, err: errDummy},
settings: Provider{ settings: Provider{
Name: constants.Ivpn, Name: constants.Ivpn,
@@ -76,6 +88,7 @@ func Test_Provider_readIvpn(t *testing.T) {
targetIP: singleStringCall{call: true}, targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true}, countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true}, cities: sliceStringCall{call: true},
isps: sliceStringCall{call: true},
hostnames: sliceStringCall{call: true}, hostnames: sliceStringCall{call: true},
protocol: singleStringCall{call: true, err: errDummy}, protocol: singleStringCall{call: true, err: errDummy},
settings: Provider{ settings: Provider{
@@ -87,6 +100,7 @@ func Test_Provider_readIvpn(t *testing.T) {
targetIP: singleStringCall{call: true}, targetIP: singleStringCall{call: true},
countries: sliceStringCall{call: true}, countries: sliceStringCall{call: true},
cities: sliceStringCall{call: true}, cities: sliceStringCall{call: true},
isps: sliceStringCall{call: true},
hostnames: sliceStringCall{call: true}, hostnames: sliceStringCall{call: true},
protocol: singleStringCall{call: true}, protocol: singleStringCall{call: true},
settings: Provider{ settings: Provider{
@@ -97,6 +111,7 @@ func Test_Provider_readIvpn(t *testing.T) {
targetIP: singleStringCall{call: true, value: "1.2.3.4"}, targetIP: singleStringCall{call: true, value: "1.2.3.4"},
countries: sliceStringCall{call: true, values: []string{"A", "B"}}, countries: sliceStringCall{call: true, values: []string{"A", "B"}},
cities: sliceStringCall{call: true, values: []string{"C", "D"}}, cities: sliceStringCall{call: true, values: []string{"C", "D"}},
isps: sliceStringCall{call: true, values: []string{"ISP 1"}},
hostnames: sliceStringCall{call: true, values: []string{"E", "F"}}, hostnames: sliceStringCall{call: true, values: []string{"E", "F"}},
protocol: singleStringCall{call: true, value: constants.TCP}, protocol: singleStringCall{call: true, value: constants.TCP},
settings: Provider{ settings: Provider{
@@ -108,6 +123,7 @@ func Test_Provider_readIvpn(t *testing.T) {
TargetIP: net.IPv4(1, 2, 3, 4), TargetIP: net.IPv4(1, 2, 3, 4),
Countries: []string{"A", "B"}, Countries: []string{"A", "B"},
Cities: []string{"C", "D"}, Cities: []string{"C", "D"},
ISPs: []string{"ISP 1"},
Hostnames: []string{"E", "F"}, Hostnames: []string{"E", "F"},
}, },
}, },
@@ -136,6 +152,10 @@ func Test_Provider_readIvpn(t *testing.T) {
env.EXPECT().CSVInside("CITY", constants.IvpnCityChoices()). env.EXPECT().CSVInside("CITY", constants.IvpnCityChoices()).
Return(testCase.cities.values, testCase.cities.err) Return(testCase.cities.values, testCase.cities.err)
} }
if testCase.isps.call {
env.EXPECT().CSVInside("ISP", constants.IvpnISPChoices()).
Return(testCase.isps.values, testCase.isps.err)
}
if testCase.hostnames.call { if testCase.hostnames.call {
env.EXPECT().CSVInside("SERVER_HOSTNAME", constants.IvpnHostnameChoices()). env.EXPECT().CSVInside("SERVER_HOSTNAME", constants.IvpnHostnameChoices()).
Return(testCase.hostnames.values, testCase.hostnames.err) Return(testCase.hostnames.values, testCase.hostnames.err)

View File

@@ -28,6 +28,15 @@ func IvpnCityChoices() (choices []string) {
return makeUnique(choices) return makeUnique(choices)
} }
func IvpnISPChoices() (choices []string) {
servers := IvpnServers()
choices = make([]string, len(servers))
for i := range servers {
choices[i] = servers[i].ISP
}
return makeUnique(choices)
}
func IvpnHostnameChoices() (choices []string) { func IvpnHostnameChoices() (choices []string) {
servers := IvpnServers() servers := IvpnServers()
choices = make([]string, len(servers)) choices = make([]string, len(servers))

View File

@@ -25970,13 +25970,14 @@
] ]
}, },
"ivpn": { "ivpn": {
"version": 1, "version": 2,
"timestamp": 1629490838, "timestamp": 1629589118,
"servers": [ "servers": [
{ {
"country": "Australia", "country": "Australia",
"city": "", "city": "Sydney",
"hostname": "au-nsw.gw.ivpn.net", "isp": "M247",
"hostname": "au-nsw1.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
@@ -25985,8 +25986,9 @@
}, },
{ {
"country": "Austria", "country": "Austria",
"city": "", "city": "Vienna",
"hostname": "at.gw.ivpn.net", "isp": "M247",
"hostname": "at1.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
@@ -25995,8 +25997,9 @@
}, },
{ {
"country": "Belgium", "country": "Belgium",
"city": "", "city": "Brussels",
"hostname": "be.gw.ivpn.net", "isp": "M247",
"hostname": "be1.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
@@ -26005,8 +26008,9 @@
}, },
{ {
"country": "Brazil", "country": "Brazil",
"city": "", "city": "Franca",
"hostname": "br.gw.ivpn.net", "isp": "Qnax",
"hostname": "br1.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
@@ -26015,8 +26019,9 @@
}, },
{ {
"country": "Bulgaria", "country": "Bulgaria",
"city": "", "city": "Sofia",
"hostname": "bg.gw.ivpn.net", "isp": "M247",
"hostname": "bg1.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
@@ -26026,7 +26031,8 @@
{ {
"country": "Canada", "country": "Canada",
"city": "Montreal", "city": "Montreal",
"hostname": "ca-qc.gw.ivpn.net", "isp": "M247",
"hostname": "ca-qc1.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
@@ -26036,17 +26042,30 @@
{ {
"country": "Canada", "country": "Canada",
"city": "Toronto", "city": "Toronto",
"hostname": "ca.gw.ivpn.net", "isp": "Amanah",
"hostname": "ca1.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
"104.254.90.178" "104.254.90.178"
] ]
}, },
{
"country": "Canada",
"city": "Toronto",
"isp": "Amanah",
"hostname": "ca2.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"172.86.186.170"
]
},
{ {
"country": "Czech Republic", "country": "Czech Republic",
"city": "", "city": "Prague",
"hostname": "cz.gw.ivpn.net", "isp": "Datapacket",
"hostname": "cz1.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
@@ -26055,8 +26074,9 @@
}, },
{ {
"country": "Denmark", "country": "Denmark",
"city": "", "city": "Copenhagen",
"hostname": "dk.gw.ivpn.net", "isp": "M247",
"hostname": "dk1.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
@@ -26065,8 +26085,9 @@
}, },
{ {
"country": "Finland", "country": "Finland",
"city": "", "city": "Helsinki",
"hostname": "fi.gw.ivpn.net", "isp": "Creanova",
"hostname": "fi1.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
@@ -26075,8 +26096,9 @@
}, },
{ {
"country": "France", "country": "France",
"city": "", "city": "Paris",
"hostname": "fr.gw.ivpn.net", "isp": "Datapacket",
"hostname": "fr1.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
@@ -26085,8 +26107,20 @@
}, },
{ {
"country": "Germany", "country": "Germany",
"city": "", "city": "Frankfurt",
"hostname": "de.gw.ivpn.net", "isp": "Leaseweb",
"hostname": "de1.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"178.162.222.40"
]
},
{
"country": "Germany",
"city": "Frankfurt",
"isp": "Leaseweb",
"hostname": "de2.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
@@ -26095,8 +26129,20 @@
}, },
{ {
"country": "Hong Kong", "country": "Hong Kong",
"city": "", "city": "Hong Kong",
"hostname": "hk.gw.ivpn.net", "isp": "Leaseweb",
"hostname": "hk1.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"209.58.189.163"
]
},
{
"country": "Hong Kong",
"city": "Hong Kong",
"isp": "Leaseweb",
"hostname": "hk2.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
@@ -26105,8 +26151,9 @@
}, },
{ {
"country": "Hungary", "country": "Hungary",
"city": "", "city": "Budapest",
"hostname": "hu.gw.ivpn.net", "isp": "M247",
"hostname": "hu1.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
@@ -26115,8 +26162,9 @@
}, },
{ {
"country": "Iceland", "country": "Iceland",
"city": "", "city": "Reykjavik",
"hostname": "is.gw.ivpn.net", "isp": "Advania",
"hostname": "is1.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
@@ -26125,8 +26173,9 @@
}, },
{ {
"country": "Israel", "country": "Israel",
"city": "", "city": "Holon, Tel Aviv",
"hostname": "il.gw.ivpn.net", "isp": "HQServ",
"hostname": "il1.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
@@ -26135,8 +26184,9 @@
}, },
{ {
"country": "Italy", "country": "Italy",
"city": "", "city": "Milan",
"hostname": "it.gw.ivpn.net", "isp": "SEFlow",
"hostname": "it1.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
@@ -26145,8 +26195,9 @@
}, },
{ {
"country": "Japan", "country": "Japan",
"city": "", "city": "Tokyo",
"hostname": "jp.gw.ivpn.net", "isp": "M247",
"hostname": "jp1.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
@@ -26155,8 +26206,9 @@
}, },
{ {
"country": "Luxembourg", "country": "Luxembourg",
"city": "", "city": "Luxembourg",
"hostname": "lu.gw.ivpn.net", "isp": "Evoluso",
"hostname": "lu1.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
@@ -26165,18 +26217,75 @@
}, },
{ {
"country": "Netherlands", "country": "Netherlands",
"city": "", "city": "Amsterdam",
"hostname": "nl.gw.ivpn.net", "isp": "Leaseweb",
"hostname": "nl3.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"95.211.172.68"
]
},
{
"country": "Netherlands",
"city": "Amsterdam",
"isp": "Leaseweb",
"hostname": "nl4.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
"95.211.172.95" "95.211.172.95"
] ]
}, },
{
"country": "Netherlands",
"city": "Amsterdam",
"isp": "Leaseweb",
"hostname": "nl5.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"95.211.187.222"
]
},
{
"country": "Netherlands",
"city": "Amsterdam",
"isp": "Leaseweb",
"hostname": "nl6.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"95.211.187.228"
]
},
{
"country": "Netherlands",
"city": "Amsterdam",
"isp": "Leaseweb",
"hostname": "nl7.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"95.211.95.22"
]
},
{
"country": "Netherlands",
"city": "Amsterdam",
"isp": "Leaseweb",
"hostname": "nl8.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"95.211.172.18"
]
},
{ {
"country": "Norway", "country": "Norway",
"city": "", "city": "Oslo",
"hostname": "no.gw.ivpn.net", "isp": "Servethewrld",
"hostname": "no1.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
@@ -26185,8 +26294,9 @@
}, },
{ {
"country": "Poland", "country": "Poland",
"city": "", "city": "Warsaw",
"hostname": "pl.gw.ivpn.net", "isp": "Datapacket",
"hostname": "pl1.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
@@ -26195,8 +26305,9 @@
}, },
{ {
"country": "Portugal", "country": "Portugal",
"city": "", "city": "Lisbon",
"hostname": "pt.gw.ivpn.net", "isp": "Hostwebis",
"hostname": "pt1.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
@@ -26205,8 +26316,9 @@
}, },
{ {
"country": "Romania", "country": "Romania",
"city": "", "city": "Bucharest",
"hostname": "ro.gw.ivpn.net", "isp": "M247",
"hostname": "ro1.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
@@ -26215,8 +26327,9 @@
}, },
{ {
"country": "Serbia", "country": "Serbia",
"city": "", "city": "Belgrade",
"hostname": "rs.gw.ivpn.net", "isp": "M247",
"hostname": "rs1.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
@@ -26225,8 +26338,9 @@
}, },
{ {
"country": "Singapore", "country": "Singapore",
"city": "", "city": "Singapore",
"hostname": "sg.gw.ivpn.net", "isp": "M247",
"hostname": "sg1.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
@@ -26235,8 +26349,9 @@
}, },
{ {
"country": "Slovakia", "country": "Slovakia",
"city": "", "city": "Bratislava",
"hostname": "sk.gw.ivpn.net", "isp": "M247",
"hostname": "sk1.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
@@ -26245,8 +26360,9 @@
}, },
{ {
"country": "Spain", "country": "Spain",
"city": "", "city": "Madrid",
"hostname": "es.gw.ivpn.net", "isp": "Datapacket",
"hostname": "es1.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
@@ -26255,8 +26371,9 @@
}, },
{ {
"country": "Sweden", "country": "Sweden",
"city": "", "city": "Stockholm",
"hostname": "se.gw.ivpn.net", "isp": "GleSyS",
"hostname": "se1.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
@@ -26265,8 +26382,9 @@
}, },
{ {
"country": "Switzerland", "country": "Switzerland",
"city": "", "city": "Zurich",
"hostname": "ch.gw.ivpn.net", "isp": "M247",
"hostname": "ch1.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
@@ -26274,129 +26392,21 @@
] ]
}, },
{ {
"country": "USA", "country": "Switzerland",
"city": "Atlanta", "city": "Zurich",
"hostname": "us-ga.gw.ivpn.net", "isp": "Privatelayer",
"hostname": "ch3.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
"104.129.24.146" "141.255.166.194"
]
},
{
"country": "USA",
"city": "Chicago",
"hostname": "us-il.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"72.11.137.146"
]
},
{
"country": "USA",
"city": "Dallas",
"hostname": "us-tx.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"96.44.189.194"
]
},
{
"country": "USA",
"city": "Las Vegas",
"hostname": "us-nv.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"185.242.5.34"
]
},
{
"country": "USA",
"city": "Los Angeles",
"hostname": "us-ca.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"69.12.80.146"
]
},
{
"country": "USA",
"city": "Miami",
"hostname": "us-fl.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"173.44.49.90"
]
},
{
"country": "USA",
"city": "New Jersey",
"hostname": "us-nj.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"23.226.128.18"
]
},
{
"country": "USA",
"city": "New York",
"hostname": "us-ny.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"64.120.44.114"
]
},
{
"country": "USA",
"city": "Phoenix",
"hostname": "us-az.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"193.37.254.130"
]
},
{
"country": "USA",
"city": "Salt Lake City",
"hostname": "us-ut.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"198.105.216.28"
]
},
{
"country": "USA",
"city": "Seattle",
"hostname": "us-wa.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"23.19.87.209"
]
},
{
"country": "USA",
"city": "Washington",
"hostname": "us-dc.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"207.244.108.207"
] ]
}, },
{ {
"country": "Ukraine", "country": "Ukraine",
"city": "", "city": "Kharkiv",
"hostname": "ua.gw.ivpn.net", "isp": "Xservers",
"hostname": "ua1.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
@@ -26406,23 +26416,255 @@
{ {
"country": "United Kingdom", "country": "United Kingdom",
"city": "London", "city": "London",
"hostname": "gb.gw.ivpn.net", "isp": "Datapacket",
"hostname": "gb1.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
"185.59.221.88",
"185.59.221.133" "185.59.221.133"
] ]
}, },
{
"country": "United Kingdom",
"city": "London",
"isp": "Datapacket",
"hostname": "gb2.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"185.59.221.88"
]
},
{ {
"country": "United Kingdom", "country": "United Kingdom",
"city": "Manchester", "city": "Manchester",
"hostname": "gb-man.gw.ivpn.net", "isp": "M247",
"hostname": "gb-man1.gw.ivpn.net",
"tcp": false, "tcp": false,
"udp": true, "udp": true,
"ips": [ "ips": [
"89.238.141.228" "89.238.141.228"
] ]
},
{
"country": "United States",
"city": "Atlanta, GA",
"isp": "Quadranet",
"hostname": "us-ga1.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"104.129.24.146"
]
},
{
"country": "United States",
"city": "Atlanta, GA",
"isp": "Quadranet",
"hostname": "us-ga2.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"107.150.22.74"
]
},
{
"country": "United States",
"city": "Chicago, IL",
"isp": "Quadranet",
"hostname": "us-il1.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"107.150.28.82"
]
},
{
"country": "United States",
"city": "Chicago, IL",
"isp": "Quadranet",
"hostname": "us-il2.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"72.11.137.146"
]
},
{
"country": "United States",
"city": "Dallas, TX",
"isp": "Quadranet",
"hostname": "us-tx1.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"96.44.189.194"
]
},
{
"country": "United States",
"city": "Dallas, TX",
"isp": "Quadranet",
"hostname": "us-tx2.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"96.44.142.74"
]
},
{
"country": "United States",
"city": "Las Vegas, NV",
"isp": "M247",
"hostname": "us-nv1.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"185.242.5.34"
]
},
{
"country": "United States",
"city": "Los Angeles, CA",
"isp": "Quadranet",
"hostname": "us-ca1.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"173.254.196.58"
]
},
{
"country": "United States",
"city": "Los Angeles, CA",
"isp": "Quadranet",
"hostname": "us-ca2.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"69.12.80.146"
]
},
{
"country": "United States",
"city": "Los Angeles, CA",
"isp": "Leaseweb",
"hostname": "us-ca3.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"209.58.130.196"
]
},
{
"country": "United States",
"city": "Los Angeles, CA",
"isp": "Quadranet",
"hostname": "us-ca4.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"173.254.204.202"
]
},
{
"country": "United States",
"city": "Miami, FL",
"isp": "Quadranet",
"hostname": "us-fl1.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"173.44.49.90"
]
},
{
"country": "United States",
"city": "New Jersey, NJ",
"isp": "Quadranet",
"hostname": "us-nj3.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"23.226.128.18"
]
},
{
"country": "United States",
"city": "New Jersey, NJ",
"isp": "M247",
"hostname": "us-nj4.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"194.36.111.50"
]
},
{
"country": "United States",
"city": "New York, NY",
"isp": "Leaseweb",
"hostname": "us-ny1.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"64.120.44.114"
]
},
{
"country": "United States",
"city": "New York, NY",
"isp": "Leaseweb",
"hostname": "us-ny2.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"173.234.153.130"
]
},
{
"country": "United States",
"city": "Phoenix, AZ",
"isp": "M247",
"hostname": "us-az1.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"193.37.254.130"
]
},
{
"country": "United States",
"city": "Salt Lake City, UT",
"isp": "100TB",
"hostname": "us-ut1.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"198.105.216.28"
]
},
{
"country": "United States",
"city": "Seattle, WA",
"isp": "Leaseweb",
"hostname": "us-wa1.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"23.19.87.209"
]
},
{
"country": "United States",
"city": "Washington, DC",
"isp": "Leaseweb",
"hostname": "us-dc1.gw.ivpn.net",
"tcp": false,
"udp": true,
"ips": [
"207.244.108.207"
]
} }
] ]
}, },

View File

@@ -68,7 +68,7 @@ func Test_versions(t *testing.T) {
"Ivpn": { "Ivpn": {
model: models.IvpnServer{}, model: models.IvpnServer{},
version: allServers.Ivpn.Version, version: allServers.Ivpn.Version,
digest: "2eb80d28", digest: "abdc2848",
}, },
"Mullvad": { "Mullvad": {
model: models.MullvadServer{}, model: models.MullvadServer{},

View File

@@ -41,6 +41,7 @@ type IpvanishServer struct {
type IvpnServer struct { type IvpnServer struct {
Country string `json:"country"` Country string `json:"country"`
City string `json:"city"` City string `json:"city"`
ISP string `json:"isp"`
Hostname string `json:"hostname"` Hostname string `json:"hostname"`
TCP bool `json:"tcp"` TCP bool `json:"tcp"`
UDP bool `json:"udp"` UDP bool `json:"udp"`

View File

@@ -11,6 +11,7 @@ func (i *Ivpn) filterServers(selection configuration.ServerSelection) (
for _, server := range i.servers { for _, server := range i.servers {
switch { switch {
case case
utils.FilterByPossibilities(server.ISP, selection.ISPs),
utils.FilterByPossibilities(server.Country, selection.Countries), utils.FilterByPossibilities(server.Country, selection.Countries),
utils.FilterByPossibilities(server.City, selection.Cities), utils.FilterByPossibilities(server.City, selection.Cities),
utils.FilterByPossibilities(server.Hostname, selection.Hostnames), utils.FilterByPossibilities(server.Hostname, selection.Hostnames),

View File

@@ -110,7 +110,7 @@ func (u *updater) updateIpvanish(ctx context.Context) (err error) {
func (u *updater) updateIvpn(ctx context.Context) (err error) { func (u *updater) updateIvpn(ctx context.Context) (err error) {
minServers := getMinServers(len(u.servers.Ivpn.Servers)) minServers := getMinServers(len(u.servers.Ivpn.Servers))
servers, warnings, err := ivpn.GetServers( servers, warnings, err := ivpn.GetServers(
ctx, u.unzipper, u.presolver, minServers) ctx, u.client, u.presolver, minServers)
if u.options.CLI { if u.options.CLI {
for _, warning := range warnings { for _, warning := range warnings {
u.logger.Warn("Ivpn: " + warning) u.logger.Warn("Ivpn: " + warning)

View File

@@ -0,0 +1,66 @@
package ivpn
import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
)
var (
errBuildRequest = errors.New("cannot build HTTP request")
errDoRequest = errors.New("failed doing HTTP request")
errHTTPStatusCodeNotOK = errors.New("HTTP status code not OK")
errUnmarshalResponseBody = errors.New("failed unmarshaling response body")
errCloseBody = errors.New("failed closing HTTP body")
)
type apiData struct {
Servers []apiServer `json:"servers"`
}
type apiServer struct {
Hostnames apiHostnames `json:"hostnames"`
IsActive bool `json:"is_active"`
Country string `json:"country"`
City string `json:"city"`
ISP string `json:"isp"`
}
type apiHostnames struct {
OpenVPN string `json:"openvpn"`
}
func fetchAPI(ctx context.Context, client *http.Client) (
data apiData, err error) {
const url = "https://api.ivpn.net/v4/servers/stats"
request, err := http.NewRequestWithContext(ctx, http.MethodGet, url, nil)
if err != nil {
return data, fmt.Errorf("%w: %s", errBuildRequest, err)
}
response, err := client.Do(request)
if err != nil {
return data, fmt.Errorf("%w: %s", errDoRequest, err)
}
if response.StatusCode != http.StatusOK {
_ = response.Body.Close()
return data, fmt.Errorf("%w: %d %s",
errHTTPStatusCodeNotOK, response.StatusCode, response.Status)
}
decoder := json.NewDecoder(response.Body)
if err := decoder.Decode(&data); err != nil {
_ = response.Body.Close()
return data, fmt.Errorf("%w: %s", errUnmarshalResponseBody, err)
}
if err := response.Body.Close(); err != nil {
return data, fmt.Errorf("%w: %s", errCloseBody, err)
}
return data, nil
}

View File

@@ -0,0 +1,96 @@
package ivpn
import (
"context"
"errors"
"io"
"io/ioutil"
"net/http"
"strings"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func Test_fetchAPI(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
responseStatus int
responseBody io.ReadCloser
data apiData
err error
}{
"http response status not ok": {
responseStatus: http.StatusNoContent,
err: errors.New("HTTP status code not OK: 204 No Content"),
},
"nil body": {
responseStatus: http.StatusOK,
err: errors.New("failed unmarshaling response body: EOF"),
},
"no server": {
responseStatus: http.StatusOK,
responseBody: ioutil.NopCloser(strings.NewReader(`{}`)),
},
"success": {
responseStatus: http.StatusOK,
responseBody: ioutil.NopCloser(strings.NewReader(`{"servers":[
{"country":"Country1","city":"City A","isp":"xyz","is_active":true,"hostnames":{"openvpn":"hosta"}},
{"country":"Country2","city":"City B","isp":"abc","is_active":false,"hostnames":{"openvpn":"hostb"}}
]}`)),
data: apiData{
Servers: []apiServer{
{
Country: "Country1",
City: "City A",
IsActive: true,
ISP: "xyz",
Hostnames: apiHostnames{
OpenVPN: "hosta",
},
},
{
Country: "Country2",
City: "City B",
ISP: "abc",
Hostnames: apiHostnames{
OpenVPN: "hostb",
},
},
},
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
ctx := context.Background()
client := &http.Client{
Transport: roundTripFunc(func(r *http.Request) (*http.Response, error) {
assert.Equal(t, http.MethodGet, r.Method)
assert.Equal(t, r.URL.String(), "https://api.ivpn.net/v4/servers/stats")
return &http.Response{
StatusCode: testCase.responseStatus,
Status: http.StatusText(testCase.responseStatus),
Body: testCase.responseBody,
}, nil
}),
}
data, err := fetchAPI(ctx, client)
assert.Equal(t, testCase.data, data)
if testCase.err != nil {
require.Error(t, err)
assert.Equal(t, testCase.err.Error(), err.Error())
} else {
assert.NoError(t, err)
}
})
}
}

View File

@@ -1,16 +0,0 @@
package ivpn
import (
"strings"
)
func parseFilename(fileName string) (country, city string) {
const suffix = ".ovpn"
fileName = strings.TrimSuffix(fileName, suffix)
parts := strings.Split(fileName, "-")
country = strings.ReplaceAll(parts[0], "_", " ")
if len(parts) > 1 {
city = strings.ReplaceAll(parts[1], "_", " ")
}
return country, city
}

View File

@@ -1,41 +0,0 @@
package ivpn
import (
"testing"
"github.com/stretchr/testify/assert"
)
func Test_parseFilename(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
fileName string
country string
city string
}{
"empty filename": {},
"country only": {
fileName: "Country.ovpn",
country: "Country",
},
"country and city": {
fileName: "Country-City.ovpn",
country: "Country",
city: "City",
},
"composite country and city": {
fileName: "Coun_try-Ci_ty.ovpn",
country: "Coun try",
city: "Ci ty",
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
country, city := parseFilename(testCase.fileName)
assert.Equal(t, testCase.country, country)
assert.Equal(t, testCase.city, city)
})
}
}

View File

@@ -1,58 +0,0 @@
package ivpn
import (
"net"
"sort"
"github.com/qdm12/gluetun/internal/models"
)
type hostToServer map[string]models.IvpnServer
func (hts hostToServer) add(host, country, city string, tcp, udp bool) {
server, ok := hts[host]
if !ok {
server.Hostname = host
server.Country = country
server.City = city
}
if tcp {
server.TCP = tcp
}
if udp {
server.UDP = udp
}
hts[host] = server
}
func (hts hostToServer) toHostsSlice() (hosts []string) {
hosts = make([]string, 0, len(hts))
for host := range hts {
hosts = append(hosts, host)
}
sort.Slice(hosts, func(i, j int) bool {
return hosts[i] < hosts[j]
})
return hosts
}
func (hts hostToServer) adaptWithIPs(hostToIPs map[string][]net.IP) {
for host, IPs := range hostToIPs {
server := hts[host]
server.IPs = IPs
hts[host] = server
}
for host, server := range hts {
if len(server.IPs) == 0 {
delete(hts, host)
}
}
}
func (hts hostToServer) toServersSlice() (servers []models.IvpnServer) {
servers = make([]models.IvpnServer, 0, len(hts))
for _, server := range hts {
servers = append(servers, server)
}
return servers
}

View File

@@ -1,211 +0,0 @@
package ivpn
import (
"net"
"testing"
"github.com/qdm12/gluetun/internal/models"
"github.com/stretchr/testify/assert"
)
func Test_hostToServer_add(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
initialHTS hostToServer
host string
country string
city string
tcp bool
udp bool
expectedHTS hostToServer
}{
"empty host to server": {
initialHTS: hostToServer{},
host: "host",
country: "country",
city: "city",
tcp: true,
udp: true,
expectedHTS: hostToServer{
"host": {
Hostname: "host",
Country: "country",
City: "city",
TCP: true,
UDP: true,
},
},
},
"add server": {
initialHTS: hostToServer{
"existing host": {},
},
host: "host",
country: "country",
city: "city",
tcp: true,
udp: true,
expectedHTS: hostToServer{
"existing host": {},
"host": models.IvpnServer{
Hostname: "host",
Country: "country",
City: "city",
TCP: true,
UDP: true,
},
},
},
"extend existing server": {
initialHTS: hostToServer{
"host": models.IvpnServer{
Hostname: "host",
Country: "country",
City: "city",
TCP: true,
},
},
host: "host",
country: "country",
city: "city",
tcp: false,
udp: true,
expectedHTS: hostToServer{
"host": models.IvpnServer{
Hostname: "host",
Country: "country",
City: "city",
TCP: true,
UDP: true,
},
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
testCase.initialHTS.add(testCase.host, testCase.country, testCase.city, testCase.tcp, testCase.udp)
assert.Equal(t, testCase.expectedHTS, testCase.initialHTS)
})
}
}
func Test_hostToServer_toHostsSlice(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
hts hostToServer
hosts []string
}{
"empty host to server": {
hts: hostToServer{},
hosts: []string{},
},
"single host": {
hts: hostToServer{
"A": {},
},
hosts: []string{"A"},
},
"multiple hosts": {
hts: hostToServer{
"A": {},
"B": {},
},
hosts: []string{"A", "B"},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
hosts := testCase.hts.toHostsSlice()
assert.ElementsMatch(t, testCase.hosts, hosts)
})
}
}
func Test_hostToServer_adaptWithIPs(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
initialHTS hostToServer
hostToIPs map[string][]net.IP
expectedHTS hostToServer
}{
"create server": {
initialHTS: hostToServer{},
hostToIPs: map[string][]net.IP{
"A": {{1, 2, 3, 4}},
},
expectedHTS: hostToServer{
"A": models.IvpnServer{
IPs: []net.IP{{1, 2, 3, 4}},
},
},
},
"add IPs to existing server": {
initialHTS: hostToServer{
"A": models.IvpnServer{
Country: "country",
},
},
hostToIPs: map[string][]net.IP{
"A": {{1, 2, 3, 4}},
},
expectedHTS: hostToServer{
"A": models.IvpnServer{
Country: "country",
IPs: []net.IP{{1, 2, 3, 4}},
},
},
},
"remove server without IP": {
initialHTS: hostToServer{
"A": models.IvpnServer{
Country: "country",
},
},
hostToIPs: map[string][]net.IP{},
expectedHTS: hostToServer{},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
testCase.initialHTS.adaptWithIPs(testCase.hostToIPs)
assert.Equal(t, testCase.expectedHTS, testCase.initialHTS)
})
}
}
func Test_hostToServer_toServersSlice(t *testing.T) {
t.Parallel()
testCases := map[string]struct {
hts hostToServer
servers []models.IvpnServer
}{
"empty host to server": {
hts: hostToServer{},
servers: []models.IvpnServer{},
},
"multiple servers": {
hts: hostToServer{
"A": {Country: "A"},
"B": {Country: "B"},
},
servers: []models.IvpnServer{
{Country: "A"},
{Country: "B"},
},
},
}
for name, testCase := range testCases {
testCase := testCase
t.Run(name, func(t *testing.T) {
t.Parallel()
servers := testCase.hts.toServersSlice()
assert.ElementsMatch(t, testCase.servers, servers)
})
}
}

View File

@@ -0,0 +1,9 @@
package ivpn
import "net/http"
type roundTripFunc func(r *http.Request) (*http.Response, error)
func (f roundTripFunc) RoundTrip(r *http.Request) (*http.Response, error) {
return f(r)
}

View File

@@ -6,78 +6,64 @@ import (
"context" "context"
"errors" "errors"
"fmt" "fmt"
"strings" "net/http"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/updater/openvpn"
"github.com/qdm12/gluetun/internal/updater/resolver" "github.com/qdm12/gluetun/internal/updater/resolver"
"github.com/qdm12/gluetun/internal/updater/unzip"
) )
var ErrNotEnoughServers = errors.New("not enough servers found") var (
ErrFetchAPI = errors.New("failed fetching API")
ErrNotEnoughServers = errors.New("not enough servers found")
)
func GetServers(ctx context.Context, unzipper unzip.Unzipper, func GetServers(ctx context.Context, client *http.Client,
presolver resolver.Parallel, minServers int) ( presolver resolver.Parallel, minServers int) (
servers []models.IvpnServer, warnings []string, err error) { servers []models.IvpnServer, warnings []string, err error) {
const url = "https://www.ivpn.net/releases/config/ivpn-openvpn-config.zip" data, err := fetchAPI(ctx, client)
contents, err := unzipper.FetchAndExtract(ctx, url)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, fmt.Errorf("%w: %s", ErrFetchAPI, err)
} else if len(contents) < minServers { }
hosts := make([]string, 0, len(data.Servers))
for _, serverData := range data.Servers {
host := serverData.Hostnames.OpenVPN
if host == "" {
continue // Wireguard
}
hosts = append(hosts, host)
}
if len(hosts) < minServers {
return nil, nil, fmt.Errorf("%w: %d and expected at least %d", return nil, nil, fmt.Errorf("%w: %d and expected at least %d",
ErrNotEnoughServers, len(contents), minServers) ErrNotEnoughServers, len(hosts), minServers)
} }
hts := make(hostToServer) hostToIPs, warnings, err := resolveHosts(ctx, presolver, hosts, minServers)
for fileName, content := range contents {
if !strings.HasSuffix(fileName, ".ovpn") {
continue // not an OpenVPN file
}
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)
continue
}
host, warning, err := openvpn.ExtractHost(content)
if warning != "" {
warnings = append(warnings, warning)
}
if err != nil {
// treat error as warning and go to next file
warning := err.Error() + " in " + fileName
warnings = append(warnings, warning)
continue
}
country, city := parseFilename(fileName)
hts.add(host, country, city, tcp, udp)
}
if len(hts) < minServers {
return nil, warnings, 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...)
if err != nil { if err != nil {
return nil, warnings, err return nil, warnings, err
} }
hts.adaptWithIPs(hostToIPs) servers = make([]models.IvpnServer, 0, len(hosts))
for _, serverData := range data.Servers {
host := serverData.Hostnames.OpenVPN
if serverData.Hostnames.OpenVPN == "" {
continue // Wireguard
}
servers = hts.toServersSlice() server := models.IvpnServer{
Country: serverData.Country,
if len(servers) < minServers { City: serverData.City,
return nil, warnings, fmt.Errorf("%w: %d and expected at least %d", ISP: serverData.ISP,
ErrNotEnoughServers, len(servers), minServers) Hostname: serverData.Hostnames.OpenVPN,
// TCP is not supported
UDP: true,
IPs: hostToIPs[host],
}
servers = append(servers, server)
} }
sortServers(servers) sortServers(servers)

View File

@@ -3,27 +3,30 @@ package ivpn
import ( import (
"context" "context"
"errors" "errors"
"io/ioutil"
"net" "net"
"net/http"
"strings"
"testing" "testing"
"github.com/golang/mock/gomock" "github.com/golang/mock/gomock"
"github.com/qdm12/gluetun/internal/models" "github.com/qdm12/gluetun/internal/models"
"github.com/qdm12/gluetun/internal/updater/resolver" "github.com/qdm12/gluetun/internal/updater/resolver"
"github.com/qdm12/gluetun/internal/updater/resolver/mock_resolver" "github.com/qdm12/gluetun/internal/updater/resolver/mock_resolver"
"github.com/qdm12/gluetun/internal/updater/unzip/mock_unzip"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func Test_GetServers(t *testing.T) { func Test_GetServers(t *testing.T) {
t.Parallel() t.Parallel()
testCases := map[string]struct { testCases := map[string]struct {
// Inputs // Inputs
minServers int minServers int
// Unzip // From API
unzipContents map[string][]byte responseBody string
unzipErr error responseStatus int
// Resolution // Resolution
expectResolve bool expectResolve bool
@@ -38,47 +41,15 @@ func Test_GetServers(t *testing.T) {
warnings []string warnings []string
err error err error
}{ }{
"unzipper error": { "http response error": {
unzipErr: errors.New("dummy"), responseStatus: http.StatusNoContent,
err: errors.New("dummy"), err: errors.New("failed fetching API: HTTP status code not OK: 204 No Content"),
},
"not enough unzip contents": {
minServers: 1,
unzipContents: map[string][]byte{},
err: errors.New("not enough servers found: 0 and expected at least 1"),
},
"no openvpn file": {
minServers: 1,
unzipContents: map[string][]byte{"somefile.txt": {}},
err: errors.New("not enough servers found: 0 and expected at least 1"),
},
"invalid proto": {
minServers: 1,
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,
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,
unzipContents: map[string][]byte{
"MultiHosts.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": { "resolve error": {
unzipContents: map[string][]byte{ responseBody: `{"servers":[
"config.ovpn": []byte("remote hosta"), {"hostnames":{"openvpn":"hosta"}}
}, ]}`,
responseStatus: http.StatusOK,
expectResolve: true, expectResolve: true,
hostsToResolve: []string{"hosta"}, hostsToResolve: []string{"hosta"},
resolveSettings: getResolveSettings(0), resolveSettings: getResolveSettings(0),
@@ -87,12 +58,22 @@ func Test_GetServers(t *testing.T) {
warnings: []string{"resolve warning"}, warnings: []string{"resolve warning"},
err: errors.New("dummy"), err: errors.New("dummy"),
}, },
"not enough servers": {
minServers: 2,
responseBody: `{"servers":[
{"hostnames":{"openvpn":"hosta"}}
]}`,
responseStatus: http.StatusOK,
err: errors.New("not enough servers found: 1 and expected at least 2"),
},
"success": { "success": {
minServers: 1, minServers: 1,
unzipContents: map[string][]byte{ responseBody: `{"servers":[
"Country1-City_A.ovpn": []byte("remote hosta"), {"country":"Country1","city":"City A","hostnames":{"openvpn":"hosta"}},
"Country2-City_B.ovpn": []byte("remote hostb"), {"country":"Country2","city":"City B","hostnames":{"openvpn":"hostb"}},
}, {"country":"Country3","city":"City C","hostnames":{"wireguard":"hostc"}}
]}`,
responseStatus: http.StatusOK,
expectResolve: true, expectResolve: true,
hostsToResolve: []string{"hosta", "hostb"}, hostsToResolve: []string{"hosta", "hostb"},
resolveSettings: getResolveSettings(1), resolveSettings: getResolveSettings(1),
@@ -116,10 +97,17 @@ func Test_GetServers(t *testing.T) {
ctx := context.Background() ctx := context.Background()
unzipper := mock_unzip.NewMockUnzipper(ctrl) client := &http.Client{
const zipURL = "https://www.ivpn.net/releases/config/ivpn-openvpn-config.zip" Transport: roundTripFunc(func(r *http.Request) (*http.Response, error) {
unzipper.EXPECT().FetchAndExtract(ctx, zipURL). assert.Equal(t, http.MethodGet, r.Method)
Return(testCase.unzipContents, testCase.unzipErr) assert.Equal(t, r.URL.String(), "https://api.ivpn.net/v4/servers/stats")
return &http.Response{
StatusCode: testCase.responseStatus,
Status: http.StatusText(testCase.responseStatus),
Body: ioutil.NopCloser(strings.NewReader(testCase.responseBody)),
}, nil
}),
}
presolver := mock_resolver.NewMockParallel(ctrl) presolver := mock_resolver.NewMockParallel(ctrl)
if testCase.expectResolve { if testCase.expectResolve {
@@ -127,7 +115,7 @@ func Test_GetServers(t *testing.T) {
Return(testCase.hostToIPs, testCase.resolveWarnings, testCase.resolveErr) Return(testCase.hostToIPs, testCase.resolveWarnings, testCase.resolveErr)
} }
servers, warnings, err := GetServers(ctx, unzipper, presolver, testCase.minServers) servers, warnings, err := GetServers(ctx, client, presolver, testCase.minServers)
assert.Equal(t, testCase.servers, servers) assert.Equal(t, testCase.servers, servers)
assert.Equal(t, testCase.warnings, warnings) assert.Equal(t, testCase.warnings, warnings)