Automatically setup containers under test
This commit is contained in:
parent
1d2c522f61
commit
fa9eba300a
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"common": {
|
"common": {
|
||||||
"library": "/full/path/to/test/library/on/host",
|
"library": "/full/path/to/test/library/on/host/TV",
|
||||||
"episode": "Episode title to search for"
|
"episode": "Episode title to search for"
|
||||||
},
|
},
|
||||||
"servers": [
|
"servers": [
|
||||||
@ -9,7 +9,6 @@
|
|||||||
"image": "ghcr.io/confusedpolarbear/jellyfin-intro-skipper:latest",
|
"image": "ghcr.io/confusedpolarbear/jellyfin-intro-skipper:latest",
|
||||||
"username": "admin",
|
"username": "admin",
|
||||||
"password": "hunter2",
|
"password": "hunter2",
|
||||||
"base": "config/official",
|
|
||||||
"browsers": [
|
"browsers": [
|
||||||
"chrome",
|
"chrome",
|
||||||
"firefox"
|
"firefox"
|
||||||
|
@ -99,6 +99,8 @@ func generateReport(hostAddress, apiKey, reportDestination string, keepTimestamp
|
|||||||
}
|
}
|
||||||
|
|
||||||
func runAnalysisAndWait(hostAddress, apiKey string, pollInterval time.Duration) {
|
func runAnalysisAndWait(hostAddress, apiKey string, pollInterval time.Duration) {
|
||||||
|
var taskId string = ""
|
||||||
|
|
||||||
type taskInfo struct {
|
type taskInfo struct {
|
||||||
State string
|
State string
|
||||||
CurrentProgressPercentage int
|
CurrentProgressPercentage int
|
||||||
@ -108,9 +110,24 @@ func runAnalysisAndWait(hostAddress, apiKey string, pollInterval time.Duration)
|
|||||||
SendRequest("POST", hostAddress+"/Intros/EraseTimestamps", apiKey)
|
SendRequest("POST", hostAddress+"/Intros/EraseTimestamps", apiKey)
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
|
|
||||||
|
// The task ID changed with v0.1.7.
|
||||||
|
// Old task ID: 8863329048cc357f7dfebf080f2fe204
|
||||||
|
// New task ID: 6adda26c5261c40e8fa4a7e7df568be2
|
||||||
fmt.Println("[+] Starting analysis task")
|
fmt.Println("[+] Starting analysis task")
|
||||||
SendRequest("POST", hostAddress+"/ScheduledTasks/Running/6adda26c5261c40e8fa4a7e7df568be2", apiKey)
|
for _, id := range []string{"8863329048cc357f7dfebf080f2fe204", "6adda26c5261c40e8fa4a7e7df568be2"} {
|
||||||
fmt.Println()
|
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.Println("[+] Waiting for analysis task to complete")
|
||||||
fmt.Print("[+] Episodes analyzed: 0%")
|
fmt.Print("[+] Episodes analyzed: 0%")
|
||||||
@ -141,7 +158,7 @@ func runAnalysisAndWait(hostAddress, apiKey string, pollInterval time.Duration)
|
|||||||
|
|
||||||
lastQuery = time.Now()
|
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 {
|
if err := json.Unmarshal(raw, &info); err != nil {
|
||||||
fmt.Printf("[!] Unable to unmarshal response into taskInfo struct: %s\n", err)
|
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 (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/hex"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
@ -19,10 +21,21 @@ var containerAddress string
|
|||||||
// Path to compiled plugin DLL to install in local containers.
|
// Path to compiled plugin DLL to install in local containers.
|
||||||
var pluginPath string
|
var pluginPath string
|
||||||
|
|
||||||
|
// Randomly generated password used to setup container with.
|
||||||
|
var containerPassword string
|
||||||
|
|
||||||
func flags() {
|
func flags() {
|
||||||
flag.StringVar(&pluginPath, "dll", "", "Path to plugin DLL to install in container images.")
|
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.StringVar(&containerAddress, "caddr", "", "IP address to use when connecting to local containers.")
|
||||||
flag.Parse()
|
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() {
|
func main() {
|
||||||
@ -72,11 +85,6 @@ func main() {
|
|||||||
panic(err)
|
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
|
// Create a folder to install the plugin into
|
||||||
pluginDirectory := path.Join(configurationDirectory, "plugins", "intro-skipper")
|
pluginDirectory := path.Join(configurationDirectory, "plugins", "intro-skipper")
|
||||||
if lsioImage {
|
if lsioImage {
|
||||||
@ -91,15 +99,19 @@ func main() {
|
|||||||
|
|
||||||
// If this is an LSIO container, adjust the permissions on the plugin directory
|
// If this is an LSIO container, adjust the permissions on the plugin directory
|
||||||
if lsioImage {
|
if lsioImage {
|
||||||
if err := os.Chown(pluginDirectory, 911, 911); err != nil {
|
RunProgram(
|
||||||
fmt.Printf(" [!] Failed to change plugin directory UID/GID: %s\n", err)
|
"chown",
|
||||||
goto cleanup
|
[]string{
|
||||||
}
|
"911:911",
|
||||||
|
"-R",
|
||||||
|
path.Join(configurationDirectory, "data", "plugins")},
|
||||||
|
2*time.Second)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Install the plugin
|
// Install the plugin
|
||||||
fmt.Printf(" [+] Copying plugin %s to %s\n", pluginPath, pluginDirectory)
|
fmt.Printf(" [+] Copying plugin %s to %s\n", pluginPath, pluginDirectory)
|
||||||
RunProgram("cp", []string{pluginPath, pluginDirectory}, 2*time.Second)
|
RunProgram("cp", []string{pluginPath, pluginDirectory}, 2*time.Second)
|
||||||
|
fmt.Println()
|
||||||
|
|
||||||
/* Start the container with the following settings:
|
/* Start the container with the following settings:
|
||||||
* Name: jf-e2e
|
* Name: jf-e2e
|
||||||
@ -116,6 +128,18 @@ func main() {
|
|||||||
|
|
||||||
// Wait for the container to fully start
|
// Wait for the container to fully start
|
||||||
waitForServerStartup(server.Address)
|
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 {
|
} else {
|
||||||
fmt.Println("[+] Remote instance, assuming plugin is already installed")
|
fmt.Println("[+] Remote instance, assuming plugin is already installed")
|
||||||
}
|
}
|
||||||
@ -123,6 +147,21 @@ func main() {
|
|||||||
// Get an API key
|
// Get an API key
|
||||||
apiKey = login(server)
|
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
|
// Analyze episodes and save report
|
||||||
fmt.Println(" [+] Analyzing episodes")
|
fmt.Println(" [+] Analyzing episodes")
|
||||||
fmt.Print("\033[37;1m") // change the color of the verifier's text
|
fmt.Print("\033[37;1m") // change the color of the verifier's text
|
||||||
@ -268,8 +307,9 @@ func loadConfiguration() Configuration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Print debugging info
|
// Print debugging info
|
||||||
fmt.Printf("Library: %s\n", config.Common.Library)
|
fmt.Printf("Library: %s\n", config.Common.Library)
|
||||||
fmt.Printf("Episode: \"%s\"\n", config.Common.Episode)
|
fmt.Printf("Episode: \"%s\"\n", config.Common.Episode)
|
||||||
|
fmt.Printf("Password: %s\n", containerPassword)
|
||||||
fmt.Println()
|
fmt.Println()
|
||||||
|
|
||||||
// Check the validity of all entries
|
// Check the validity of all entries
|
||||||
@ -282,14 +322,12 @@ func loadConfiguration() Configuration {
|
|||||||
panic("The -caddr argument is required.")
|
panic("The -caddr argument is required.")
|
||||||
}
|
}
|
||||||
|
|
||||||
if server.Base == "" {
|
|
||||||
panic("Original configuration directory is required")
|
|
||||||
}
|
|
||||||
|
|
||||||
if pluginPath == "" {
|
if pluginPath == "" {
|
||||||
panic("The -dll argument is required.")
|
panic("The -dll argument is required.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
server.Username = "admin"
|
||||||
|
server.Password = containerPassword
|
||||||
server.Address = fmt.Sprintf("http://%s:8097", containerAddress)
|
server.Address = fmt.Sprintf("http://%s:8097", containerAddress)
|
||||||
server.Docker = true
|
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"`
|
Image string `json:"image"`
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
Base string `json:"base"`
|
|
||||||
Browsers []string `json:"browsers"`
|
Browsers []string `json:"browsers"`
|
||||||
Tests []string `json:"tests"`
|
Tests []string `json:"tests"`
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user