Automatically setup containers under test
This commit is contained in:
parent
1d2c522f61
commit
fa9eba300a
@ -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"
|
||||
|
@ -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)
|
||||
|
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
|
@ -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")
|
||||
}
|
||||
}
|
@ -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"`
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user