-
-
Notifications
You must be signed in to change notification settings - Fork 31
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'dn2_instance_test' into unstable
- Loading branch information
Showing
16 changed files
with
489 additions
and
438 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
// Package checkapp provides a suite of small procedures to check integration URLs and commands. | ||
// This is used by all the double-green check marks on the client UI. | ||
package checkapp | ||
|
||
import ( | ||
"context" | ||
"errors" | ||
"html" | ||
"net/http" | ||
"strconv" | ||
"strings" | ||
|
||
"github.com/Notifiarr/notifiarr/pkg/configfile" | ||
"github.com/Notifiarr/notifiarr/pkg/mnd" | ||
"github.com/Notifiarr/notifiarr/pkg/snapshot" | ||
"github.com/gorilla/mux" | ||
) | ||
|
||
const ( | ||
success = "Connection Successful! Version: " | ||
connecting = "Connecting: " | ||
validation = "Validation: " | ||
) | ||
|
||
type Input struct { | ||
Real *configfile.Config | ||
Post *configfile.Config | ||
Type string | ||
Index int | ||
} | ||
|
||
var ErrBadIndex = errors.New("index provided has no configuration data") | ||
|
||
func Test(orig *configfile.Config, writer http.ResponseWriter, req *http.Request) { | ||
posted := configfile.Config{} | ||
|
||
if err := mnd.ConfigPostDecoder.Decode(&posted, req.PostForm); err != nil { | ||
http.Error(writer, "Decoding POST data into Go data structure failed: "+err.Error(), http.StatusBadRequest) | ||
return | ||
} | ||
|
||
index, _ := strconv.Atoi(mux.Vars(req)["index"]) | ||
reply, code := testInstance(req.Context(), &Input{ | ||
Real: orig, | ||
Post: &posted, | ||
Type: mux.Vars(req)["type"], | ||
Index: index, | ||
}) | ||
http.Error(writer, html.EscapeString(reply), code) | ||
} | ||
|
||
//nolint:funlen,cyclop // It's really not that bad. | ||
func testInstance(ctx context.Context, input *Input) (string, int) { | ||
switch strings.ToLower(input.Type) { | ||
// commands.go | ||
case "commands": | ||
return testCommand(ctx, input) | ||
// downloaders.go | ||
case "nzbget": | ||
return checkAndRun(ctx, testNZBGet, input, input.Post.Apps, input.Post.Apps.NZBGet) | ||
case "deluge": | ||
return checkAndRun(ctx, testDeluge, input, input.Post.Apps, input.Post.Apps.Deluge) | ||
case "qbit": | ||
return checkAndRun(ctx, testQbit, input, input.Post.Apps, input.Post.Apps.Qbit) | ||
case "rtorrent": | ||
return checkAndRun(ctx, testRtorrent, input, input.Post.Apps, input.Post.Apps.Rtorrent) | ||
case "transmission": | ||
return checkAndRun(ctx, testTransmission, input, input.Post.Apps, input.Post.Apps.Transmission) | ||
case "sabnzb": | ||
return checkAndRun(ctx, testSabNZB, input, input.Post.Apps, input.Post.Apps.SabNZB) | ||
// starr.go | ||
case "lidarr": | ||
return checkAndRun(ctx, testLidarr, input, input.Post.Apps, input.Post.Apps.Lidarr) | ||
case "prowlarr": | ||
return checkAndRun(ctx, testProwlarr, input, input.Post.Apps, input.Post.Apps.Prowlarr) | ||
case "radarr": | ||
return checkAndRun(ctx, testRadarr, input, input.Post.Apps, input.Post.Apps.Radarr) | ||
case "readarr": | ||
return checkAndRun(ctx, testReadarr, input, input.Post.Apps, input.Post.Apps.Readarr) | ||
case "sonarr": | ||
return checkAndRun(ctx, testSonarr, input, input.Post.Apps, input.Post.Apps.Sonarr) | ||
// snapshots.go | ||
case "mysql": | ||
return checkAndRun(ctx, testMySQL, input, input.Post.Snapshot, input.Post.Snapshot.Plugins.MySQL) | ||
case "nvidia": | ||
return checkAndRun(ctx, testNvidia, input, input.Post.Snapshot, | ||
[]*snapshot.NvidiaConfig{input.Post.Snapshot.Plugins.Nvidia}) // ad-hoc slice, index is already 0. | ||
// services.go | ||
case "tcp": | ||
return checkAndRun(ctx, testTCP, input, input.Post.Service, input.Post.Service) | ||
case "http": | ||
return checkAndRun(ctx, testHTTP, input, input.Post.Service, input.Post.Service) | ||
case "process": | ||
return checkAndRun(ctx, testProcess, input, input.Post.Service, input.Post.Service) | ||
case "ping", "icmp": | ||
return checkAndRun(ctx, testPing, input, input.Post.Service, input.Post.Service) | ||
// media.go | ||
case "plex": | ||
return testPlex(ctx, input.Post.Plex) | ||
case "tautulli": | ||
return testTautulli(ctx, input.Post.Apps.Tautulli) | ||
default: | ||
return "Unknown Check Type Requested! (" + input.Type + ")", http.StatusNotImplemented | ||
} | ||
} | ||
|
||
// checkAndRun makes sure the slice length is at least as long as the index, and checks parents for nil. | ||
func checkAndRun[D any]( | ||
ctx context.Context, | ||
checker func(ctx context.Context, input D) (string, int), | ||
input *Input, | ||
parent any, | ||
slice []D, | ||
) (string, int) { | ||
if parent == nil || len(slice) <= input.Index { | ||
return ErrBadIndex.Error(), http.StatusBadRequest | ||
} | ||
|
||
return checker(ctx, slice[input.Index]) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,40 @@ | ||
package checkapp | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"net/http" | ||
|
||
"github.com/Notifiarr/notifiarr/pkg/triggers/commands" | ||
"github.com/Notifiarr/notifiarr/pkg/triggers/common" | ||
"github.com/Notifiarr/notifiarr/pkg/website" | ||
) | ||
|
||
func testCommand(ctx context.Context, input *Input) (string, int) { | ||
if len(input.Real.Commands) > input.Index { | ||
input.Real.Commands[input.Index].Run(&common.ActionInput{Type: website.EventGUI}) | ||
return fmt.Sprintf("Command Triggered: %s", input.Real.Commands[input.Index].Name), http.StatusOK | ||
} else if len(input.Post.Commands) > input.Index { // check POST input for "new" command. | ||
input.Post.Commands[input.Index].Setup(input.Real.Logger, input.Real.Services.Website) | ||
|
||
if err := input.Post.Commands[input.Index].SetupRegexpArgs(); err != nil { | ||
return err.Error(), http.StatusInternalServerError | ||
} | ||
|
||
return testCustomCommand(ctx, input.Post.Commands[input.Index]) | ||
} | ||
|
||
return ErrBadIndex.Error(), http.StatusBadRequest | ||
} | ||
|
||
func testCustomCommand(ctx context.Context, cmd *commands.Command) (string, int) { | ||
ctx, cancel := context.WithTimeout(ctx, cmd.Timeout.Duration) | ||
defer cancel() | ||
|
||
output, err := cmd.RunNow(ctx, &common.ActionInput{Type: website.EventGUI}) | ||
if err != nil { | ||
return fmt.Sprintf("Command Failed! Error: %v", err), http.StatusInternalServerError | ||
} | ||
|
||
return fmt.Sprintf("Command Successful! Output: %s", output), http.StatusOK | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,98 @@ | ||
package checkapp | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"net/http" | ||
"net/url" | ||
|
||
"github.com/Notifiarr/notifiarr/pkg/apps" | ||
"github.com/Notifiarr/notifiarr/pkg/mnd" | ||
"github.com/hekmon/transmissionrpc/v3" | ||
"golift.io/deluge" | ||
"golift.io/nzbget" | ||
"golift.io/qbit" | ||
"golift.io/version" | ||
) | ||
|
||
func testQbit(ctx context.Context, config *apps.QbitConfig) (string, int) { | ||
qbit, err := qbit.New(ctx, config.Config) | ||
if err != nil { | ||
return connecting + err.Error(), http.StatusBadGateway | ||
} | ||
|
||
xfers, err := qbit.GetXfersContext(ctx) | ||
if err != nil { | ||
return "Getting Transfers: " + err.Error(), http.StatusBadGateway | ||
} | ||
|
||
return fmt.Sprintf("Connection Successful! %d Transfers", len(xfers)), http.StatusOK | ||
} | ||
|
||
func testRtorrent(_ context.Context, config *apps.RtorrentConfig) (string, int) { | ||
config.Setup(0, nil) | ||
|
||
result, err := config.Client.Call("system.hostname") | ||
if err != nil { | ||
return "Getting Server Name: " + err.Error(), http.StatusBadGateway | ||
} | ||
|
||
if names, ok := result.([]any); ok { | ||
result = names[0] | ||
} | ||
|
||
if name, ok := result.(string); ok { | ||
return fmt.Sprintf("Connection Successful! Server name: %s", name), http.StatusOK | ||
} | ||
|
||
return "Getting Server Name: result was not a string?", http.StatusBadGateway | ||
} | ||
|
||
func testSabNZB(ctx context.Context, app *apps.SabNZBConfig) (string, int) { | ||
app.Setup(0, nil) | ||
|
||
sab, err := app.GetQueue(ctx) | ||
if err != nil { | ||
return "Getting Queue: " + err.Error(), http.StatusBadGateway | ||
} | ||
|
||
return success + sab.Version, http.StatusOK | ||
} | ||
|
||
func testNZBGet(ctx context.Context, config *apps.NZBGetConfig) (string, int) { | ||
ver, err := nzbget.New(config.Config).VersionContext(ctx) | ||
if err != nil { | ||
return "Getting Version: " + err.Error(), http.StatusBadGateway | ||
} | ||
|
||
return fmt.Sprintf("%s%s", success, ver), http.StatusOK | ||
} | ||
|
||
func testDeluge(ctx context.Context, config *apps.DelugeConfig) (string, int) { | ||
deluge, err := deluge.New(ctx, config.Config) | ||
if err != nil { | ||
return connecting + err.Error(), http.StatusBadGateway | ||
} | ||
|
||
return fmt.Sprintf("%s%s", success, deluge.Version), http.StatusOK | ||
} | ||
|
||
func testTransmission(ctx context.Context, config *apps.XmissionConfig) (string, int) { | ||
endpoint, err := url.Parse(config.URL) | ||
if err != nil { | ||
return "parsing url: " + err.Error(), http.StatusBadGateway | ||
} else if config.User != "" { | ||
endpoint.User = url.UserPassword(config.User, config.Pass) | ||
} | ||
|
||
client, _ := transmissionrpc.New(endpoint, &transmissionrpc.Config{ | ||
UserAgent: fmt.Sprintf("%s v%s-%s %s", mnd.Title, version.Version, version.Revision, version.Branch), | ||
}) | ||
|
||
args, err := client.SessionArgumentsGetAll(ctx) | ||
if err != nil { | ||
return "Getting Server Version: " + err.Error(), http.StatusBadGateway | ||
} | ||
|
||
return fmt.Sprintln("Transmission Server version:", *args.Version), http.StatusOK | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
package checkapp | ||
|
||
import ( | ||
"context" | ||
"fmt" | ||
"net/http" | ||
|
||
"github.com/Notifiarr/notifiarr/pkg/apps" | ||
) | ||
|
||
func testPlex(ctx context.Context, app *apps.PlexConfig) (string, int) { | ||
app.Setup(0, nil) | ||
|
||
info, err := app.GetInfo(ctx) | ||
if err != nil { | ||
return "Getting Info: " + err.Error(), http.StatusBadGateway | ||
} | ||
|
||
return "Plex OK! Version: " + info.Version, http.StatusOK | ||
} | ||
|
||
func testTautulli(ctx context.Context, app *apps.TautulliConfig) (string, int) { | ||
app.Setup(0, nil) | ||
|
||
users, err := app.GetUsers(ctx) | ||
if err != nil { | ||
return "Getting Users: " + err.Error(), http.StatusBadGateway | ||
} | ||
|
||
return fmt.Sprintf("Tautulli OK! Users: %d", len(users.Response.Data)), http.StatusOK | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
package checkapp | ||
|
||
import ( | ||
"context" | ||
"net/http" | ||
|
||
"github.com/Notifiarr/notifiarr/pkg/services" | ||
) | ||
|
||
func testTCP(ctx context.Context, svc *services.Service) (string, int) { | ||
if err := svc.Validate(); err != nil { | ||
return validation + err.Error(), http.StatusBadRequest | ||
} | ||
|
||
res := svc.CheckOnly(ctx) | ||
if res.State != services.StateOK { | ||
return res.State.String() + " " + res.Output.String(), http.StatusBadGateway | ||
} | ||
|
||
return "TCP Port is OPEN and reachable: " + res.Output.String(), http.StatusOK | ||
} | ||
|
||
func testHTTP(ctx context.Context, svc *services.Service) (string, int) { | ||
if err := svc.Validate(); err != nil { | ||
return validation + err.Error(), http.StatusBadRequest | ||
} | ||
|
||
res := svc.CheckOnly(ctx) | ||
if res.State != services.StateOK { | ||
return res.State.String() + " " + res.Output.String(), http.StatusBadGateway | ||
} | ||
|
||
// add test | ||
return "HTTP Response Code Acceptable! " + res.Output.String(), http.StatusOK | ||
} | ||
|
||
func testProcess(ctx context.Context, svc *services.Service) (string, int) { | ||
if err := svc.Validate(); err != nil { | ||
return validation + err.Error(), http.StatusBadRequest | ||
} | ||
|
||
res := svc.CheckOnly(ctx) | ||
if res.State != services.StateOK { | ||
return res.State.String() + " " + res.Output.String(), http.StatusBadGateway | ||
} | ||
|
||
return "Process Tested OK: " + res.Output.String(), http.StatusOK | ||
} | ||
|
||
func testPing(ctx context.Context, svc *services.Service) (string, int) { | ||
if err := svc.Validate(); err != nil { | ||
return validation + err.Error(), http.StatusBadRequest | ||
} | ||
|
||
res := svc.CheckOnly(ctx) | ||
if res.State != services.StateOK { | ||
return res.State.String() + " " + res.Output.String(), http.StatusBadGateway | ||
} | ||
|
||
return "Ping Tested OK: " + res.Output.String(), http.StatusOK | ||
} |
Oops, something went wrong.