mirror of
https://github.com/onedr0p/exportarr.git
synced 2026-02-06 10:57:32 +00:00
[Refactor] (1/2) Refactor client to leverage a custom Transport & url.URL (#106)
* Refactor Client & Add Retries Signed-off-by: Russell Troxel <russelltroxel@gmail.com> * Add Tests Signed-off-by: Russell Troxel <russelltroxel@gmail.com> * Catch a few missed DoRequest callsites Signed-off-by: Russell Troxel <russelltroxel@gmail.com> --------- Signed-off-by: Russell Troxel <russelltroxel@gmail.com>
This commit is contained in:
parent
90ce8b9dc0
commit
3d59a7a462
4
go.mod
4
go.mod
@ -5,6 +5,7 @@ go 1.19
|
||||
require (
|
||||
github.com/prometheus/client_golang v1.14.0
|
||||
github.com/sirupsen/logrus v1.9.0
|
||||
github.com/stretchr/testify v1.7.0
|
||||
github.com/urfave/cli/v2 v2.25.0
|
||||
)
|
||||
|
||||
@ -12,8 +13,10 @@ require (
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_model v0.3.0 // indirect
|
||||
github.com/prometheus/common v0.37.0 // indirect
|
||||
github.com/prometheus/procfs v0.8.0 // indirect
|
||||
@ -21,4 +24,5 @@ require (
|
||||
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 // indirect
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
31
go.sum
31
go.sum
@ -51,8 +51,6 @@ github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5P
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
@ -113,8 +111,8 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
@ -144,8 +142,10 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
@ -165,25 +165,18 @@ github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXP
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||
github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk=
|
||||
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
||||
github.com/prometheus/client_golang v1.12.2 h1:51L9cDoUHVrXx4zWYlcLQIZ+d+VXHgqnYKkIuq4g/34=
|
||||
github.com/prometheus/client_golang v1.12.2/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
||||
github.com/prometheus/client_golang v1.13.0 h1:b71QUfeo5M8gq2+evJdTPfZhYMAU0uKPkyPJ7TPsloU=
|
||||
github.com/prometheus/client_golang v1.13.0/go.mod h1:vTeo+zgvILHsnnj/39Ou/1fPN5nJFOEMgftOUOmlvYQ=
|
||||
github.com/prometheus/client_golang v1.14.0 h1:nJdhIvne2eSX/XRAFV9PcvFFRbrjbcTUj0VP62TMhnw=
|
||||
github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.3.0 h1:UBgGFHqYdG/TPFD1B1ogZywDqEkwp3fBMvqdiQ7Xew4=
|
||||
github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||
github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4=
|
||||
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
||||
github.com/prometheus/common v0.37.0 h1:ccBbHCgIiT9uSoFY0vX8H3zsNR5eLt17/RQLUvn8pXE=
|
||||
github.com/prometheus/common v0.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA=
|
||||
@ -191,7 +184,6 @@ github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
|
||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.8.0 h1:ODq8ZFEaYeCaZOJlZZdJA2AbQR98dSHSM1KW/You5mo=
|
||||
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
|
||||
@ -201,21 +193,15 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
|
||||
github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
|
||||
github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/urfave/cli/v2 v2.4.0 h1:m2pxjjDFgDxSPtO8WSdbndj17Wu2y8vOT86wE/tjr+I=
|
||||
github.com/urfave/cli/v2 v2.4.0/go.mod h1:NX9W0zmTvedE5oDoOMs2RTC8RvdK98NTYZE5LbaEYPg=
|
||||
github.com/urfave/cli/v2 v2.10.3 h1:oi571Fxz5aHugfBAJd5nkwSk3fzATXtMlpxdLylSCMo=
|
||||
github.com/urfave/cli/v2 v2.10.3/go.mod h1:f8iq5LtQ/bLxafbdBSLPPNsgaW0l/2fYYEHhAyPlwvo=
|
||||
github.com/urfave/cli/v2 v2.11.1 h1:UKK6SP7fV3eKOefbS87iT9YHefv7iB/53ih6e+GNAsE=
|
||||
github.com/urfave/cli/v2 v2.11.1/go.mod h1:f8iq5LtQ/bLxafbdBSLPPNsgaW0l/2fYYEHhAyPlwvo=
|
||||
github.com/urfave/cli/v2 v2.25.0 h1:ykdZKuQey2zq0yin/l7JOm9Mh+pg72ngYMeB0ABn6q8=
|
||||
@ -326,7 +312,6 @@ golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@ -351,7 +336,6 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 h1:XfKQ4OlFl8okEOr5UvAqFRVj8pY/4yfcXrddB8qAbU0=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8 h1:0A+M6Uqn+Eje4kHMK80dtF3JCXC4ykBgQG4Fe06QRhQ=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@ -410,7 +394,6 @@ golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
@ -486,24 +469,24 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175w=
|
||||
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
||||
@ -1,121 +1,95 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"compress/gzip"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
|
||||
"github.com/onedr0p/exportarr/internal/model"
|
||||
"github.com/onedr0p/exportarr/internal/utils"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
// Client struct is a Radarr client to request an instance of a Radarr
|
||||
type Client struct {
|
||||
config *cli.Context
|
||||
configFile *model.Config
|
||||
httpClient http.Client
|
||||
URL url.URL
|
||||
}
|
||||
|
||||
// NewClient method initializes a new Radarr client.
|
||||
func NewClient(c *cli.Context, cf *model.Config) *Client {
|
||||
func NewClient(c *cli.Context, cf *model.Config) (*Client, error) {
|
||||
var baseURL *url.URL
|
||||
auth := AuthConfig{
|
||||
Username: c.String("basic-auth-username"),
|
||||
Password: c.String("basic-auth-password"),
|
||||
}
|
||||
|
||||
apiVersion := cf.ApiVersion
|
||||
|
||||
if c.String("config") != "" {
|
||||
var err error
|
||||
baseURL, err = baseURL.Parse(c.String("url") + ":" + cf.Port)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Couldn't parse URL: %w", err)
|
||||
}
|
||||
baseURL = baseURL.JoinPath(cf.UrlBase, "api", apiVersion)
|
||||
auth.ApiKey = cf.ApiKey
|
||||
|
||||
} else {
|
||||
// Otherwise use the value provided in the api-key flag
|
||||
var err error
|
||||
baseURL, err = baseURL.Parse(c.String("url"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Couldn't parse URL: %w", err)
|
||||
}
|
||||
baseURL = baseURL.JoinPath("api", apiVersion)
|
||||
|
||||
if c.String("api-key") != "" {
|
||||
auth.ApiKey = c.String("api-key")
|
||||
} else if c.String("api-key-file") != "" {
|
||||
data, err := os.ReadFile(c.String("api-key-file"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Couldn't Read API Key file %w", err)
|
||||
}
|
||||
auth.ApiKey = string(data)
|
||||
}
|
||||
}
|
||||
baseTransport := http.DefaultTransport
|
||||
if c.Bool("disable-ssl-verify") {
|
||||
baseTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
}
|
||||
return &Client{
|
||||
config: c,
|
||||
configFile: cf,
|
||||
httpClient: http.Client{
|
||||
CheckRedirect: func(req *http.Request, via []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
},
|
||||
Transport: NewArrTransport(auth, baseTransport),
|
||||
},
|
||||
}
|
||||
URL: *baseURL,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DoRequest - Take a HTTP Request and return Unmarshaled data
|
||||
func (c *Client) DoRequest(endpoint string, target interface{}) error {
|
||||
apiVersion := c.configFile.ApiVersion
|
||||
|
||||
var url string
|
||||
var apiKey string
|
||||
|
||||
// Use the values from config.xml if using the config flag
|
||||
if c.config.String("config") != "" {
|
||||
url = fmt.Sprintf("%s:%s%s/api/%s/%s",
|
||||
c.config.String("url"),
|
||||
c.configFile.Port,
|
||||
utils.FormatURLBase(c.configFile.UrlBase),
|
||||
apiVersion,
|
||||
endpoint,
|
||||
)
|
||||
apiKey = c.configFile.ApiKey
|
||||
} else {
|
||||
// Otherwise use the value provided in the api-key flag
|
||||
url = fmt.Sprintf("%s/api/%s/%s",
|
||||
c.config.String("url"),
|
||||
apiVersion,
|
||||
endpoint,
|
||||
)
|
||||
if c.config.String("api-key") != "" {
|
||||
apiKey = c.config.String("api-key")
|
||||
} else if c.config.String("api-key-file") != "" {
|
||||
data, err := os.ReadFile(c.config.String("api-key-file"))
|
||||
if err != nil {
|
||||
log.Fatalf("An error has occurred during reading of API Key file %v", err)
|
||||
return err
|
||||
}
|
||||
apiKey = string(data)
|
||||
func (c *Client) DoRequest(endpoint string, target interface{}, queryParams ...map[string]string) error {
|
||||
for _, m := range queryParams {
|
||||
for k, v := range m {
|
||||
c.URL.Query().Add(k, v)
|
||||
}
|
||||
}
|
||||
|
||||
url := c.URL.JoinPath(endpoint).String()
|
||||
log.Infof("Sending HTTP request to %s", url)
|
||||
|
||||
http.DefaultTransport.(*http.Transport).TLSClientConfig = &tls.Config{InsecureSkipVerify: c.config.Bool("disable-ssl-verify")}
|
||||
req, err := http.NewRequest("GET", url, nil)
|
||||
if err != nil {
|
||||
log.Fatalf("An error has occurred when creating HTTP request %v", err)
|
||||
return err
|
||||
return fmt.Errorf("Failed to create HTTP Request(%s): %w", url, err)
|
||||
}
|
||||
|
||||
if c.config.String("basic-auth-username") != "" && c.config.String("basic-auth-password") != "" {
|
||||
req.Header.Add("Authorization", fmt.Sprintf("Basic %s",
|
||||
base64.StdEncoding.EncodeToString([]byte(c.config.String("basic-auth-username")+":"+c.config.String("basic-auth-password"))),
|
||||
))
|
||||
}
|
||||
req.Header.Add("X-Api-Key", apiKey)
|
||||
req.Header.Add("Accept-Encoding", "gzip")
|
||||
|
||||
resp, err := c.httpClient.Do(req)
|
||||
if err != nil {
|
||||
log.Fatalf("An error has occurred during retrieving statistics %v", err)
|
||||
return err
|
||||
}
|
||||
if !(resp.StatusCode >= 200 && resp.StatusCode < 300) {
|
||||
errMsg := fmt.Sprintf("An error has occurred during retrieving statistics from %s HTTP status %s", url, resp.Status)
|
||||
if location := resp.Header.Get("Location"); location != "" {
|
||||
errMsg += " (Location: " + location + ")"
|
||||
}
|
||||
log.Fatal(errMsg)
|
||||
return errors.New(errMsg)
|
||||
return fmt.Errorf("Failed to execute HTTP Request(%s): %w", url, err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
var bodyReader io.ReadCloser
|
||||
switch resp.Header.Get("Content-Encoding") {
|
||||
case "gzip":
|
||||
bodyReader, err = gzip.NewReader(resp.Body)
|
||||
if err != nil {
|
||||
log.Fatalf("An error has occurred reading gzipped statistics %v", err)
|
||||
return err
|
||||
}
|
||||
defer bodyReader.Close()
|
||||
default:
|
||||
bodyReader = resp.Body
|
||||
}
|
||||
return json.NewDecoder(bodyReader).Decode(target)
|
||||
return json.NewDecoder(resp.Body).Decode(target)
|
||||
}
|
||||
|
||||
123
internal/client/client_test.go
Normal file
123
internal/client/client_test.go
Normal file
@ -0,0 +1,123 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/urfave/cli/v2"
|
||||
|
||||
"github.com/onedr0p/exportarr/internal/model"
|
||||
)
|
||||
|
||||
func testContext(args map[string]string) *cli.Context {
|
||||
var ret cli.Context
|
||||
|
||||
osArgs := os.Args[0:1]
|
||||
for k, v := range args {
|
||||
osArgs = append(osArgs, fmt.Sprintf("--%s=%s", k, v))
|
||||
}
|
||||
app := cli.NewApp()
|
||||
app.Flags = []cli.Flag{
|
||||
&cli.StringFlag{Name: "url"},
|
||||
&cli.StringFlag{Name: "api-key"},
|
||||
&cli.StringFlag{Name: "api-key-file"},
|
||||
&cli.StringFlag{Name: "basic-auth-username"},
|
||||
&cli.StringFlag{Name: "basic-auth-password"},
|
||||
&cli.StringFlag{Name: "config"},
|
||||
&cli.StringFlag{Name: "disable-ssl-verify"},
|
||||
}
|
||||
|
||||
app.Action = func(c *cli.Context) error {
|
||||
// copy out context
|
||||
ret = *c
|
||||
return nil
|
||||
}
|
||||
err := app.Run(osArgs)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return &ret
|
||||
}
|
||||
func TestNewClient_Flags(t *testing.T) {
|
||||
require := require.New(t)
|
||||
c := testContext(map[string]string{
|
||||
"url": "http://localhost:7878",
|
||||
"api-key": "abcdef0123456789abcdef0123456789",
|
||||
})
|
||||
cf := model.NewConfig()
|
||||
client, err := NewClient(c, cf)
|
||||
require.Nil(err, "NewClient should not return an error")
|
||||
require.NotNil(client, "NewClient should return a client")
|
||||
require.Equal(client.URL.String(), "http://localhost:7878/api/v3", "NewClient should return a client with the correct URL")
|
||||
}
|
||||
|
||||
func TestNewClient_File(t *testing.T) {
|
||||
require := require.New(t)
|
||||
c := testContext(map[string]string{
|
||||
"config": "testdata/config.json",
|
||||
"url": "http://localhost",
|
||||
"api-key": "abcdef0123456789abcdef0123456789",
|
||||
})
|
||||
cf := model.NewConfig()
|
||||
cf.Port = "7878"
|
||||
cf.UrlBase = "/radarr"
|
||||
|
||||
client, err := NewClient(c, cf)
|
||||
require.Nil(err, "NewClient should not return an error")
|
||||
require.NotNil(client, "NewClient should return a client")
|
||||
require.Equal(client.URL.String(), "http://localhost:7878/radarr/api/v3", "NewClient should return a client with the correct URL")
|
||||
}
|
||||
|
||||
func TestDoRequest(t *testing.T) {
|
||||
parameters := []struct {
|
||||
name string
|
||||
endpoint string
|
||||
queryParams map[string]string
|
||||
expectedURL string
|
||||
}{
|
||||
{
|
||||
name: "noParams",
|
||||
endpoint: "queue",
|
||||
expectedURL: "http://localhost:7878/api/v3/queue",
|
||||
},
|
||||
{
|
||||
name: "params",
|
||||
endpoint: "test",
|
||||
queryParams: map[string]string{
|
||||
"page": "1",
|
||||
"testParam": "asdf",
|
||||
},
|
||||
expectedURL: "http://localhost:7878/api/v3/test?page=1&testParam=asdf",
|
||||
},
|
||||
}
|
||||
for _, param := range parameters {
|
||||
t.Run(param.name, func(t *testing.T) {
|
||||
require := require.New(t)
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Fprintln(w, "{\"test\": \"asdf2\"}")
|
||||
}))
|
||||
defer ts.Close()
|
||||
|
||||
c := testContext(map[string]string{
|
||||
"url": ts.URL,
|
||||
})
|
||||
cf := model.NewConfig()
|
||||
|
||||
target := struct {
|
||||
Test string `json:"test"`
|
||||
}{}
|
||||
expected := target
|
||||
expected.Test = "asdf2"
|
||||
client, err := NewClient(c, cf)
|
||||
require.Nil(err, "NewClient should not return an error")
|
||||
require.NotNil(client, "NewClient should return a client")
|
||||
err = client.DoRequest(param.endpoint, &target, param.queryParams)
|
||||
require.Nil(err, "DoRequest should not return an error: %s", err)
|
||||
require.Equal(expected, target, "DoRequest should return the correct data")
|
||||
})
|
||||
}
|
||||
}
|
||||
59
internal/client/transport.go
Normal file
59
internal/client/transport.go
Normal file
@ -0,0 +1,59 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
// ArrTransport is a http.RoundTripper that adds authentication to requests
|
||||
type ArrTransport struct {
|
||||
inner http.RoundTripper
|
||||
auth AuthConfig
|
||||
}
|
||||
|
||||
type AuthConfig struct {
|
||||
Username string
|
||||
Password string
|
||||
ApiKey string
|
||||
}
|
||||
|
||||
func NewArrTransport(auth AuthConfig, inner http.RoundTripper) *ArrTransport {
|
||||
return &ArrTransport{
|
||||
inner: inner,
|
||||
auth: auth,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *ArrTransport) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
if t.auth.Username != "" && t.auth.Password != "" {
|
||||
req.SetBasicAuth(t.auth.Username, t.auth.Password)
|
||||
}
|
||||
req.Header.Add("X-Api-Key", t.auth.ApiKey)
|
||||
|
||||
resp, err := t.inner.RoundTrip(req)
|
||||
if err != nil || resp.StatusCode >= 500 {
|
||||
retries := 2
|
||||
for i := 0; i < retries; i++ {
|
||||
resp, err = t.inner.RoundTrip(req)
|
||||
if err == nil && resp.StatusCode < 500 {
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Error sending HTTP Request: %w", err)
|
||||
} else {
|
||||
return nil, fmt.Errorf("Received Server Error Status Code: %d", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
if resp.StatusCode >= 400 && resp.StatusCode <= 499 {
|
||||
return nil, fmt.Errorf("Received Client Error Status Code: %d", resp.StatusCode)
|
||||
}
|
||||
if resp.StatusCode >= 300 && resp.StatusCode <= 399 {
|
||||
if location, err := resp.Location(); err == nil {
|
||||
return nil, fmt.Errorf("Received Redirect Status Code: %d, Location: %s", resp.StatusCode, location.String())
|
||||
} else {
|
||||
return nil, fmt.Errorf("Received Redirect Status Code: %d, ", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
158
internal/client/transport_test.go
Normal file
158
internal/client/transport_test.go
Normal file
@ -0,0 +1,158 @@
|
||||
package client
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
TEST_USER = "testuser1"
|
||||
TEST_PASS = "hunter2"
|
||||
TEST_KEY = "abcdef1234567890abcdef1234567890"
|
||||
)
|
||||
|
||||
type testRoundTripFunc func(req *http.Request) (*http.Response, error)
|
||||
|
||||
func (t testRoundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
return t(req)
|
||||
}
|
||||
|
||||
func TestRoundTrip_Auth(t *testing.T) {
|
||||
parameters := []struct {
|
||||
name string
|
||||
auth AuthConfig
|
||||
testFunc func(req *http.Request) (*http.Response, error)
|
||||
}{
|
||||
{
|
||||
name: "BasicAuth",
|
||||
auth: AuthConfig{
|
||||
Username: TEST_USER,
|
||||
Password: TEST_PASS,
|
||||
ApiKey: TEST_KEY,
|
||||
},
|
||||
testFunc: func(req *http.Request) (*http.Response, error) {
|
||||
require.NotNil(t, req, "Request should not be nil")
|
||||
require.NotNil(t, req.Header, "Request header should not be nil")
|
||||
require.NotEmpty(t, req.Header.Get("Authorization"), "Authorization header should be set")
|
||||
require.Equal(
|
||||
t,
|
||||
"Basic "+base64.StdEncoding.EncodeToString([]byte(TEST_USER+":"+TEST_PASS)),
|
||||
req.Header.Get("Authorization"),
|
||||
"Authorization Header set to wrong value",
|
||||
)
|
||||
require.NotEmpty(t, req.Header.Get("X-Api-Key"), "X-Api-Key header should be set")
|
||||
require.Equal(t, TEST_KEY, req.Header.Get("X-Api-Key"), "X-Api-Key Header set to wrong value")
|
||||
return &http.Response{
|
||||
StatusCode: 200,
|
||||
Body: nil,
|
||||
Header: make(http.Header),
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ApiKey",
|
||||
auth: AuthConfig{
|
||||
Username: "",
|
||||
Password: "",
|
||||
ApiKey: TEST_KEY,
|
||||
},
|
||||
testFunc: func(req *http.Request) (*http.Response, error) {
|
||||
require.NotNil(t, req, "Request should not be nil")
|
||||
require.NotNil(t, req.Header, "Request header should not be nil")
|
||||
require.Empty(t, req.Header.Get("Authorization"), "Authorization header should be empty")
|
||||
require.NotEmpty(t, req.Header.Get("X-Api-Key"), "X-Api-Key header should be set")
|
||||
require.Equal(t, TEST_KEY, req.Header.Get("X-Api-Key"), "X-Api-Key Header set to wrong value")
|
||||
return &http.Response{
|
||||
StatusCode: 200,
|
||||
Body: nil,
|
||||
Header: make(http.Header),
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, param := range parameters {
|
||||
t.Run(param.name, func(t *testing.T) {
|
||||
transport := NewArrTransport(param.auth, testRoundTripFunc(param.testFunc))
|
||||
client := &http.Client{Transport: transport}
|
||||
req, err := http.NewRequest("GET", "http://example.com", nil)
|
||||
require.Nil(t, err, "Error creating request: %s", err)
|
||||
_, err = client.Do(req)
|
||||
require.Nil(t, err, "Error sending request: %s", err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundTrip_Retries(t *testing.T) {
|
||||
parameters := []struct {
|
||||
name string
|
||||
testFunc func(req *http.Request) (*http.Response, error)
|
||||
}{
|
||||
{
|
||||
name: "500",
|
||||
testFunc: func(req *http.Request) (*http.Response, error) {
|
||||
return &http.Response{
|
||||
StatusCode: 500,
|
||||
Body: nil,
|
||||
Header: make(http.Header),
|
||||
}, nil
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Err",
|
||||
testFunc: func(req *http.Request) (*http.Response, error) {
|
||||
return nil, &http.ProtocolError{}
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, param := range parameters {
|
||||
t.Run(param.name, func(t *testing.T) {
|
||||
require := require.New(t)
|
||||
auth := AuthConfig{
|
||||
ApiKey: TEST_KEY,
|
||||
}
|
||||
attempts := 0
|
||||
transport := NewArrTransport(auth, testRoundTripFunc(func(req *http.Request) (*http.Response, error) {
|
||||
attempts++
|
||||
return param.testFunc(req)
|
||||
}))
|
||||
client := &http.Client{Transport: transport}
|
||||
req, err := http.NewRequest("GET", "http://example.com", nil)
|
||||
require.Nil(err, "Error creating request: %s", err)
|
||||
_, err = client.Do(req)
|
||||
require.NotNil(err, "Error should be returned from Do()")
|
||||
require.Equal(3, attempts, "Should retry 3 times")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestRoundTrip_StatusCodes(t *testing.T) {
|
||||
parameters := []int{200, 201, 202, 204, 301, 302, 400, 401, 403, 404, 500, 503}
|
||||
for _, param := range parameters {
|
||||
t.Run(fmt.Sprintf("%d", param), func(t *testing.T) {
|
||||
require := require.New(t)
|
||||
auth := AuthConfig{
|
||||
ApiKey: TEST_KEY,
|
||||
}
|
||||
transport := NewArrTransport(auth, testRoundTripFunc(func(req *http.Request) (*http.Response, error) {
|
||||
return &http.Response{
|
||||
StatusCode: param,
|
||||
Body: nil,
|
||||
Header: make(http.Header),
|
||||
}, nil
|
||||
}))
|
||||
client := &http.Client{Transport: transport}
|
||||
req, err := http.NewRequest("GET", "http://example.com", nil)
|
||||
require.Nil(err, "Error creating request: %s", err)
|
||||
_, err = client.Do(req)
|
||||
if param >= 200 && param < 300 {
|
||||
require.Nil(err, "Should Not error on 2XX: %s", err)
|
||||
} else {
|
||||
require.NotNil(err, "Should error on non-2XX")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -122,8 +122,18 @@ func (collector *lidarrCollector) Describe(ch chan<- *prometheus.Desc) {
|
||||
}
|
||||
|
||||
func (collector *lidarrCollector) Collect(ch chan<- prometheus.Metric) {
|
||||
c := client.NewClient(collector.config, collector.configFile)
|
||||
|
||||
c, err := client.NewClient(collector.config, collector.configFile)
|
||||
if err != nil {
|
||||
log.Errorf("Error creating client: %w", err)
|
||||
ch <- prometheus.NewInvalidMetric(
|
||||
prometheus.NewDesc(
|
||||
"lidarr_collector_error",
|
||||
"Error Collecting from Lidarr",
|
||||
nil,
|
||||
prometheus.Labels{"url": collector.config.String("url")}),
|
||||
err)
|
||||
return
|
||||
}
|
||||
var artistsFileSize int64
|
||||
var (
|
||||
artistsMonitored = 0
|
||||
@ -156,7 +166,8 @@ func (collector *lidarrCollector) Collect(ch chan<- prometheus.Metric) {
|
||||
|
||||
if collector.config.Bool("enable-additional-metrics") {
|
||||
songFile := model.SongFile{}
|
||||
if err := c.DoRequest(fmt.Sprintf("%s?artistid=%d", "trackfile", s.Id), &songFile); err != nil {
|
||||
params := map[string]string{"artistid": fmt.Sprintf("%d", s.Id)}
|
||||
if err := c.DoRequest("trackfile", &songFile, params); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for _, e := range songFile {
|
||||
@ -166,7 +177,8 @@ func (collector *lidarrCollector) Collect(ch chan<- prometheus.Metric) {
|
||||
}
|
||||
|
||||
album := model.Album{}
|
||||
if err := c.DoRequest(fmt.Sprintf("%s?artistid=%d", "album", s.Id), &album); err != nil {
|
||||
params = map[string]string{"artistid": fmt.Sprintf("%d", s.Id)}
|
||||
if err := c.DoRequest("album", &album, params); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for _, a := range album {
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package collector
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
@ -240,7 +239,18 @@ func (collector *prowlarrCollector) Describe(ch chan<- *prometheus.Desc) {
|
||||
|
||||
func (collector *prowlarrCollector) Collect(ch chan<- prometheus.Metric) {
|
||||
total := time.Now()
|
||||
c := client.NewClient(collector.config, collector.configFile)
|
||||
c, err := client.NewClient(collector.config, collector.configFile)
|
||||
if err != nil {
|
||||
log.Errorf("Error creating client: %w", err)
|
||||
ch <- prometheus.NewInvalidMetric(
|
||||
prometheus.NewDesc(
|
||||
"prowlarr_collector_error",
|
||||
"Error Collecting from Prowlarr",
|
||||
nil,
|
||||
prometheus.Labels{"url": collector.config.String("url")}),
|
||||
err)
|
||||
return
|
||||
}
|
||||
|
||||
var enabledIndexers = 0
|
||||
|
||||
@ -268,8 +278,11 @@ func (collector *prowlarrCollector) Collect(ch chan<- prometheus.Metric) {
|
||||
stats := model.IndexerStatResponse{}
|
||||
startDate := collector.lastStatUpdate.In(time.UTC)
|
||||
endDate := time.Now().In(time.UTC)
|
||||
req := fmt.Sprintf("indexerstats?startDate=%s&endDate=%s", startDate.Format(time.RFC3339), endDate.Format(time.RFC3339))
|
||||
if err := c.DoRequest(req, &stats); err != nil {
|
||||
params := map[string]string{
|
||||
"startDate": startDate.Format(time.RFC3339),
|
||||
"endDate": endDate.Format(time.RFC3339),
|
||||
}
|
||||
if err := c.DoRequest("indexerstats", &stats, params); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
collector.lastStatUpdate = endDate
|
||||
|
||||
@ -88,7 +88,18 @@ func (collector *radarrCollector) Describe(ch chan<- *prometheus.Desc) {
|
||||
}
|
||||
|
||||
func (collector *radarrCollector) Collect(ch chan<- prometheus.Metric) {
|
||||
c := client.NewClient(collector.config, collector.configFile)
|
||||
c, err := client.NewClient(collector.config, collector.configFile)
|
||||
if err != nil {
|
||||
log.Errorf("Error creating client: %w", err)
|
||||
ch <- prometheus.NewInvalidMetric(
|
||||
prometheus.NewDesc(
|
||||
"radarr_collector_error",
|
||||
"Error Collecting from Radarr",
|
||||
nil,
|
||||
prometheus.Labels{"url": collector.config.String("url")}),
|
||||
err)
|
||||
return
|
||||
}
|
||||
var fileSize int64
|
||||
var (
|
||||
downloaded = 0
|
||||
|
||||
@ -114,7 +114,18 @@ func (c *readarrCollector) Describe(ch chan<- *prometheus.Desc) {
|
||||
|
||||
func (collector *readarrCollector) Collect(ch chan<- prometheus.Metric) {
|
||||
total := time.Now()
|
||||
c := client.NewClient(collector.config, collector.configFile)
|
||||
c, err := client.NewClient(collector.config, collector.configFile)
|
||||
if err != nil {
|
||||
log.Errorf("Error creating client: %w", err)
|
||||
ch <- prometheus.NewInvalidMetric(
|
||||
prometheus.NewDesc(
|
||||
"readarr_collector_error",
|
||||
"Error Collecting from Readarr",
|
||||
nil,
|
||||
prometheus.Labels{"url": collector.config.String("url")}),
|
||||
err)
|
||||
return
|
||||
}
|
||||
tauthors := []time.Duration{}
|
||||
var authorsFileSize int64
|
||||
var (
|
||||
|
||||
@ -34,7 +34,18 @@ func (collector *systemHealthCollector) Describe(ch chan<- *prometheus.Desc) {
|
||||
}
|
||||
|
||||
func (collector *systemHealthCollector) Collect(ch chan<- prometheus.Metric) {
|
||||
c := client.NewClient(collector.config, collector.configFile)
|
||||
c, err := client.NewClient(collector.config, collector.configFile)
|
||||
if err != nil {
|
||||
log.Errorf("Error creating client: %w", err)
|
||||
ch <- prometheus.NewInvalidMetric(
|
||||
prometheus.NewDesc(
|
||||
fmt.Sprintf("%s_collector_error", collector.config.Command.Name),
|
||||
"Error Collecting metrics",
|
||||
nil,
|
||||
prometheus.Labels{"url": collector.config.String("url")}),
|
||||
err)
|
||||
return
|
||||
}
|
||||
systemHealth := model.SystemHealth{}
|
||||
if err := c.DoRequest("health", &systemHealth); err != nil {
|
||||
log.Fatal(err)
|
||||
|
||||
@ -34,7 +34,18 @@ func (collector *historyCollector) Describe(ch chan<- *prometheus.Desc) {
|
||||
}
|
||||
|
||||
func (collector *historyCollector) Collect(ch chan<- prometheus.Metric) {
|
||||
c := client.NewClient(collector.config, collector.configFile)
|
||||
c, err := client.NewClient(collector.config, collector.configFile)
|
||||
if err != nil {
|
||||
log.Errorf("Error creating client: %w", err)
|
||||
ch <- prometheus.NewInvalidMetric(
|
||||
prometheus.NewDesc(
|
||||
fmt.Sprintf("%s_collector_error", collector.config.Command.Name),
|
||||
"Error Collecting metrics",
|
||||
nil,
|
||||
prometheus.Labels{"url": collector.config.String("url")}),
|
||||
err)
|
||||
return
|
||||
}
|
||||
history := model.History{}
|
||||
if err := c.DoRequest("history", &history); err != nil {
|
||||
log.Fatal(err)
|
||||
|
||||
@ -34,19 +34,30 @@ func (collector *queueCollector) Describe(ch chan<- *prometheus.Desc) {
|
||||
}
|
||||
|
||||
func (collector *queueCollector) Collect(ch chan<- prometheus.Metric) {
|
||||
c := client.NewClient(collector.config, collector.configFile)
|
||||
c, err := client.NewClient(collector.config, collector.configFile)
|
||||
if err != nil {
|
||||
log.Errorf("Error creating client: %w", err)
|
||||
ch <- prometheus.NewInvalidMetric(
|
||||
prometheus.NewDesc(
|
||||
fmt.Sprintf("%s_collector_error", collector.config.Command.Name),
|
||||
"Error Collecting metrics",
|
||||
nil,
|
||||
prometheus.Labels{"url": collector.config.String("url")}),
|
||||
err)
|
||||
return
|
||||
}
|
||||
|
||||
unknownItemsQuery := ""
|
||||
params := map[string]string{"page": "1"}
|
||||
if collector.config.Bool("enable-unknown-queue-items") {
|
||||
if collector.config.Command.Name == "sonarr" {
|
||||
unknownItemsQuery = "&includeUnknownSeriesItems=true"
|
||||
params["includeUnknownSeriesItems"] = "true"
|
||||
} else if collector.config.Command.Name == "radarr" {
|
||||
unknownItemsQuery = "&includeUnknownMovieItems=true"
|
||||
params["includeUnknownMovieItems"] = "true"
|
||||
}
|
||||
}
|
||||
|
||||
queue := model.Queue{}
|
||||
if err := c.DoRequest(fmt.Sprintf("queue?page=1%s", unknownItemsQuery), &queue); err != nil {
|
||||
if err := c.DoRequest("queue", &queue, params); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
// Calculate total pages
|
||||
@ -56,7 +67,8 @@ func (collector *queueCollector) Collect(ch chan<- prometheus.Metric) {
|
||||
queueStatusAll = append(queueStatusAll, queue.Records...)
|
||||
if totalPages > 1 {
|
||||
for page := 2; page <= totalPages; page++ {
|
||||
if err := c.DoRequest(fmt.Sprintf("queue?page=%d%s", page, unknownItemsQuery), &queue); err != nil {
|
||||
params["page"] = fmt.Sprintf("%d", page)
|
||||
if err := c.DoRequest("queue", &queue, params); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
queueStatusAll = append(queueStatusAll, queue.Records...)
|
||||
|
||||
@ -34,7 +34,18 @@ func (collector *rootFolderCollector) Describe(ch chan<- *prometheus.Desc) {
|
||||
}
|
||||
|
||||
func (collector *rootFolderCollector) Collect(ch chan<- prometheus.Metric) {
|
||||
c := client.NewClient(collector.config, collector.configFile)
|
||||
c, err := client.NewClient(collector.config, collector.configFile)
|
||||
if err != nil {
|
||||
log.Errorf("Error creating client: %w", err)
|
||||
ch <- prometheus.NewInvalidMetric(
|
||||
prometheus.NewDesc(
|
||||
fmt.Sprintf("%s_collector_error", collector.config.Command.Name),
|
||||
"Error Collecting metrics",
|
||||
nil,
|
||||
prometheus.Labels{"url": collector.config.String("url")}),
|
||||
err)
|
||||
return
|
||||
}
|
||||
rootFolders := model.RootFolder{}
|
||||
if err := c.DoRequest("rootfolder", &rootFolders); err != nil {
|
||||
log.Fatal(err)
|
||||
|
||||
@ -6,6 +6,7 @@ import (
|
||||
"github.com/onedr0p/exportarr/internal/client"
|
||||
"github.com/onedr0p/exportarr/internal/model"
|
||||
"github.com/prometheus/client_golang/prometheus"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"github.com/urfave/cli/v2"
|
||||
)
|
||||
|
||||
@ -33,7 +34,18 @@ func (collector *systemStatusCollector) Describe(ch chan<- *prometheus.Desc) {
|
||||
}
|
||||
|
||||
func (collector *systemStatusCollector) Collect(ch chan<- prometheus.Metric) {
|
||||
c := client.NewClient(collector.config, collector.configFile)
|
||||
c, err := client.NewClient(collector.config, collector.configFile)
|
||||
if err != nil {
|
||||
log.Errorf("Error creating client: %w", err)
|
||||
ch <- prometheus.NewInvalidMetric(
|
||||
prometheus.NewDesc(
|
||||
fmt.Sprintf("%s_collector_error", collector.config.Command.Name),
|
||||
"Error Collecting metrics",
|
||||
nil,
|
||||
prometheus.Labels{"url": collector.config.String("url")}),
|
||||
err)
|
||||
return
|
||||
}
|
||||
systemStatus := model.SystemStatus{}
|
||||
if err := c.DoRequest("system/status", &systemStatus); err != nil {
|
||||
ch <- prometheus.MustNewConstMetric(collector.systemStatus, prometheus.GaugeValue, float64(0.0))
|
||||
|
||||
@ -148,7 +148,18 @@ func (collector *sonarrCollector) Describe(ch chan<- *prometheus.Desc) {
|
||||
|
||||
func (collector *sonarrCollector) Collect(ch chan<- prometheus.Metric) {
|
||||
total := time.Now()
|
||||
c := client.NewClient(collector.config, collector.configFile)
|
||||
c, err := client.NewClient(collector.config, collector.configFile)
|
||||
if err != nil {
|
||||
log.Errorf("Error creating client: %w", err)
|
||||
ch <- prometheus.NewInvalidMetric(
|
||||
prometheus.NewDesc(
|
||||
"sonarr_collector_error",
|
||||
"Error Collecting from Lidarr",
|
||||
nil,
|
||||
prometheus.Labels{"url": collector.config.String("url")}),
|
||||
err)
|
||||
return
|
||||
}
|
||||
var seriesFileSize int64
|
||||
var (
|
||||
seriesDownloaded = 0
|
||||
@ -204,7 +215,8 @@ func (collector *sonarrCollector) Collect(ch chan<- prometheus.Metric) {
|
||||
if collector.config.Bool("enable-additional-metrics") {
|
||||
textra := time.Now()
|
||||
episodeFile := model.EpisodeFile{}
|
||||
if err := c.DoRequest(fmt.Sprintf("%s?seriesId=%d", "episodefile", s.Id), &episodeFile); err != nil {
|
||||
params := map[string]string{"seriesId": fmt.Sprintf("%d", s.Id)}
|
||||
if err := c.DoRequest("episodefile", &episodeFile, params); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for _, e := range episodeFile {
|
||||
@ -214,7 +226,7 @@ func (collector *sonarrCollector) Collect(ch chan<- prometheus.Metric) {
|
||||
}
|
||||
|
||||
episode := model.Episode{}
|
||||
if err := c.DoRequest(fmt.Sprintf("%s?seriesId=%d", "episode", s.Id), &episode); err != nil {
|
||||
if err := c.DoRequest("episode", &episode, params); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for _, e := range episode {
|
||||
@ -232,7 +244,8 @@ func (collector *sonarrCollector) Collect(ch chan<- prometheus.Metric) {
|
||||
}
|
||||
|
||||
episodesMissing := model.Missing{}
|
||||
if err := c.DoRequest("wanted/missing?sortKey=airDateUtc", &episodesMissing); err != nil {
|
||||
params := map[string]string{"sortKey": "airDateUtc"}
|
||||
if err := c.DoRequest("wanted/missing", &episodesMissing, params); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
|
||||
@ -7,7 +7,6 @@ import (
|
||||
"net/url"
|
||||
"os"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/onedr0p/exportarr/internal/model"
|
||||
log "github.com/sirupsen/logrus"
|
||||
@ -37,15 +36,6 @@ func IsFileThere(filename string) bool {
|
||||
return !info.IsDir()
|
||||
}
|
||||
|
||||
// FormatURLBase - Formats a base URL
|
||||
func FormatURLBase(urlBase string) string {
|
||||
u := strings.Trim(urlBase, "/")
|
||||
if urlBase == "" {
|
||||
return u
|
||||
}
|
||||
return fmt.Sprintf("/%s", strings.Trim(u, "/"))
|
||||
}
|
||||
|
||||
// GetArrConfigFromFile - Get the config from config.xml
|
||||
func GetArrConfigFromFile(file string) (*model.Config, error) {
|
||||
xmlFile, err := os.Open(file)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user