[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:
Russell Troxel 2023-03-13 15:59:20 -07:00 committed by GitHub
parent 90ce8b9dc0
commit 3d59a7a462
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 546 additions and 138 deletions

4
go.mod
View File

@ -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
View File

@ -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=

View File

@ -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)
}

View 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")
})
}
}

View 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
}

View 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")
}
})
}
}

View File

@ -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 {

View File

@ -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

View File

@ -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

View File

@ -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 (

View File

@ -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)

View File

@ -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)

View File

@ -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...)

View File

@ -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)

View File

@ -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))

View File

@ -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)
}

View File

@ -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)