Automatically setup containers under test

This commit is contained in:
ConfusedPolarBear 2022-09-01 21:46:50 -05:00
parent 1d2c522f61
commit fa9eba300a
6 changed files with 246 additions and 21 deletions

View File

@ -1,6 +1,6 @@
{
"common": {
"library": "/full/path/to/test/library/on/host",
"library": "/full/path/to/test/library/on/host/TV",
"episode": "Episode title to search for"
},
"servers": [
@ -9,7 +9,6 @@
"image": "ghcr.io/confusedpolarbear/jellyfin-intro-skipper:latest",
"username": "admin",
"password": "hunter2",
"base": "config/official",
"browsers": [
"chrome",
"firefox"

View File

@ -99,6 +99,8 @@ func generateReport(hostAddress, apiKey, reportDestination string, keepTimestamp
}
func runAnalysisAndWait(hostAddress, apiKey string, pollInterval time.Duration) {
var taskId string = ""
type taskInfo struct {
State string
CurrentProgressPercentage int
@ -108,9 +110,24 @@ func runAnalysisAndWait(hostAddress, apiKey string, pollInterval time.Duration)
SendRequest("POST", hostAddress+"/Intros/EraseTimestamps", apiKey)
fmt.Println()
// The task ID changed with v0.1.7.
// Old task ID: 8863329048cc357f7dfebf080f2fe204
// New task ID: 6adda26c5261c40e8fa4a7e7df568be2
fmt.Println("[+] Starting analysis task")
SendRequest("POST", hostAddress+"/ScheduledTasks/Running/6adda26c5261c40e8fa4a7e7df568be2", apiKey)
fmt.Println()
for _, id := range []string{"8863329048cc357f7dfebf080f2fe204", "6adda26c5261c40e8fa4a7e7df568be2"} {
body := SendRequest("POST", hostAddress+"/ScheduledTasks/Running/"+id, apiKey)
fmt.Println()
// If the scheduled task was found, store the task ID for later
if !strings.Contains(string(body), "Not Found") {
taskId = id
break
}
}
if taskId == "" {
panic("unable to find scheduled task")
}
fmt.Println("[+] Waiting for analysis task to complete")
fmt.Print("[+] Episodes analyzed: 0%")
@ -141,7 +158,7 @@ func runAnalysisAndWait(hostAddress, apiKey string, pollInterval time.Duration)
lastQuery = time.Now()
raw := SendRequest("GET", hostAddress+"/ScheduledTasks/6adda26c5261c40e8fa4a7e7df568be2?hideUrl=1", apiKey)
raw := SendRequest("GET", hostAddress+"/ScheduledTasks/"+taskId+"?hideUrl=1", apiKey)
if err := json.Unmarshal(raw, &info); err != nil {
fmt.Printf("[!] Unable to unmarshal response into taskInfo struct: %s\n", err)

View File

@ -0,0 +1,93 @@
{
"LibraryOptions": {
"EnableArchiveMediaFiles": false,
"EnablePhotos": false,
"EnableRealtimeMonitor": false,
"ExtractChapterImagesDuringLibraryScan": false,
"EnableChapterImageExtraction": false,
"EnableInternetProviders": false,
"SaveLocalMetadata": false,
"EnableAutomaticSeriesGrouping": false,
"PreferredMetadataLanguage": "",
"MetadataCountryCode": "",
"SeasonZeroDisplayName": "Specials",
"AutomaticRefreshIntervalDays": 0,
"EnableEmbeddedTitles": false,
"EnableEmbeddedEpisodeInfos": false,
"AllowEmbeddedSubtitles": "AllowAll",
"SkipSubtitlesIfEmbeddedSubtitlesPresent": false,
"SkipSubtitlesIfAudioTrackMatches": false,
"SaveSubtitlesWithMedia": true,
"RequirePerfectSubtitleMatch": true,
"AutomaticallyAddToCollection": false,
"MetadataSavers": [],
"TypeOptions": [
{
"Type": "Series",
"MetadataFetchers": [
"TheMovieDb",
"The Open Movie Database"
],
"MetadataFetcherOrder": [
"TheMovieDb",
"The Open Movie Database"
],
"ImageFetchers": [
"TheMovieDb"
],
"ImageFetcherOrder": [
"TheMovieDb"
]
},
{
"Type": "Season",
"MetadataFetchers": [
"TheMovieDb"
],
"MetadataFetcherOrder": [
"TheMovieDb"
],
"ImageFetchers": [
"TheMovieDb"
],
"ImageFetcherOrder": [
"TheMovieDb"
]
},
{
"Type": "Episode",
"MetadataFetchers": [
"TheMovieDb",
"The Open Movie Database"
],
"MetadataFetcherOrder": [
"TheMovieDb",
"The Open Movie Database"
],
"ImageFetchers": [
"TheMovieDb",
"The Open Movie Database",
"Embedded Image Extractor",
"Screen Grabber"
],
"ImageFetcherOrder": [
"TheMovieDb",
"The Open Movie Database",
"Embedded Image Extractor",
"Screen Grabber"
]
}
],
"LocalMetadataReaderOrder": [
"Nfo"
],
"SubtitleDownloadLanguages": [],
"DisabledSubtitleFetchers": [],
"SubtitleFetcherOrder": [],
"PathInfos": [
{
"Path": "/media/TV"
}
]
}
}

View File

@ -2,6 +2,8 @@ package main
import (
"bytes"
"crypto/rand"
"encoding/hex"
"encoding/json"
"flag"
"fmt"
@ -19,10 +21,21 @@ var containerAddress string
// Path to compiled plugin DLL to install in local containers.
var pluginPath string
// Randomly generated password used to setup container with.
var containerPassword string
func flags() {
flag.StringVar(&pluginPath, "dll", "", "Path to plugin DLL to install in container images.")
flag.StringVar(&containerAddress, "caddr", "", "IP address to use when connecting to local containers.")
flag.Parse()
// Randomize the container's password
rawPassword := make([]byte, 32)
if _, err := rand.Read(rawPassword); err != nil {
panic(err)
}
containerPassword = hex.EncodeToString(rawPassword)
}
func main() {
@ -72,11 +85,6 @@ func main() {
panic(err)
}
// Copy the contents of the base configuration directory to a temp folder for the container
src, dst := server.Base+"/.", configurationDirectory
fmt.Printf(" [+] Copying %s to %s\n", src, dst)
RunProgram("cp", []string{"-ar", src, dst}, 10*time.Second)
// Create a folder to install the plugin into
pluginDirectory := path.Join(configurationDirectory, "plugins", "intro-skipper")
if lsioImage {
@ -91,15 +99,19 @@ func main() {
// If this is an LSIO container, adjust the permissions on the plugin directory
if lsioImage {
if err := os.Chown(pluginDirectory, 911, 911); err != nil {
fmt.Printf(" [!] Failed to change plugin directory UID/GID: %s\n", err)
goto cleanup
}
RunProgram(
"chown",
[]string{
"911:911",
"-R",
path.Join(configurationDirectory, "data", "plugins")},
2*time.Second)
}
// Install the plugin
fmt.Printf(" [+] Copying plugin %s to %s\n", pluginPath, pluginDirectory)
RunProgram("cp", []string{pluginPath, pluginDirectory}, 2*time.Second)
fmt.Println()
/* Start the container with the following settings:
* Name: jf-e2e
@ -116,6 +128,18 @@ func main() {
// Wait for the container to fully start
waitForServerStartup(server.Address)
fmt.Println()
fmt.Println(" [+] Setting up container")
// Set up the container
SetupServer(server.Address, containerPassword)
// Restart the container and wait for it to come back up
RunProgram("docker", []string{"restart", "jf-e2e"}, 10*time.Second)
time.Sleep(time.Second)
waitForServerStartup(server.Address)
fmt.Println()
} else {
fmt.Println("[+] Remote instance, assuming plugin is already installed")
}
@ -123,6 +147,21 @@ func main() {
// Get an API key
apiKey = login(server)
// Rescan the library if this is a server that we just setup
if server.Docker {
fmt.Println(" [+] Rescanning library")
sendRequest(
server.Address+"/ScheduledTasks/Running/7738148ffcd07979c7ceb148e06b3aed?api_key="+apiKey,
"POST",
"")
// TODO: poll for task completion
time.Sleep(10 * time.Second)
fmt.Println()
}
// Analyze episodes and save report
fmt.Println(" [+] Analyzing episodes")
fmt.Print("\033[37;1m") // change the color of the verifier's text
@ -268,8 +307,9 @@ func loadConfiguration() Configuration {
}
// Print debugging info
fmt.Printf("Library: %s\n", config.Common.Library)
fmt.Printf("Episode: \"%s\"\n", config.Common.Episode)
fmt.Printf("Library: %s\n", config.Common.Library)
fmt.Printf("Episode: \"%s\"\n", config.Common.Episode)
fmt.Printf("Password: %s\n", containerPassword)
fmt.Println()
// Check the validity of all entries
@ -282,14 +322,12 @@ func loadConfiguration() Configuration {
panic("The -caddr argument is required.")
}
if server.Base == "" {
panic("Original configuration directory is required")
}
if pluginPath == "" {
panic("The -dll argument is required.")
}
server.Username = "admin"
server.Password = containerPassword
server.Address = fmt.Sprintf("http://%s:8097", containerAddress)
server.Docker = true
}

View File

@ -0,0 +1,79 @@
package main
import (
"bytes"
_ "embed"
"fmt"
"net/http"
)
//go:embed library.json
var librarySetupPayload string
func SetupServer(server, password string) {
makeUrl := func(u string) string {
return fmt.Sprintf("%s/%s", server, u)
}
// Set the server language to English
sendRequest(
makeUrl("Startup/Configuration"),
"POST",
`{"UICulture":"en-US","MetadataCountryCode":"US","PreferredMetadataLanguage":"en"}`)
// Get the first user
sendRequest(makeUrl("Startup/User"), "GET", "")
// Create the first user
sendRequest(
makeUrl("Startup/User"),
"POST",
fmt.Sprintf(`{"Name":"admin","Password":"%s"}`, password))
// Create a TV library from the media at /media/TV.
sendRequest(
makeUrl("Library/VirtualFolders?collectionType=tvshows&refreshLibrary=false&name=Shows"),
"POST",
librarySetupPayload)
// Setup remote access
sendRequest(
makeUrl("Startup/RemoteAccess"),
"POST",
`{"EnableRemoteAccess":true,"EnableAutomaticPortMapping":false}`)
// Mark the wizard as complete
sendRequest(
makeUrl("Startup/Complete"),
"POST",
``)
}
func sendRequest(url string, method string, body string) {
// Create the request
req, err := http.NewRequest(method, url, bytes.NewBuffer([]byte(body)))
if err != nil {
panic(err)
}
// Set required headers
req.Header.Set("Content-Type", "application/json")
req.Header.Set(
"X-Emby-Authorization",
`MediaBrowser Client="JF E2E Tests", Version="0.0.1", DeviceId="E2E", Device="E2E"`)
// Send it
fmt.Printf(" [+] %s %s", method, url)
res, err := http.DefaultClient.Do(req)
if err != nil {
fmt.Println()
panic(err)
}
fmt.Printf(" %d\n", res.StatusCode)
if res.StatusCode != http.StatusNoContent && res.StatusCode != http.StatusOK {
panic("invalid status code received during setup")
}
}

View File

@ -17,7 +17,6 @@ type Server struct {
Image string `json:"image"`
Username string `json:"username"`
Password string `json:"password"`
Base string `json:"base"`
Browsers []string `json:"browsers"`
Tests []string `json:"tests"`