Different implementation of multi-check for done

Co-Authored-By: Matthew Penner <me@matthewp.io>
This commit is contained in:
Dane Everitt 2020-08-04 21:29:43 -07:00
parent 1a4c6726c5
commit 642e6e6a96
No known key found for this signature in database
GPG Key ID: EEA66103B3D71F53
3 changed files with 73 additions and 58 deletions

View File

@ -0,0 +1,66 @@
package api
import (
"encoding/json"
"github.com/apex/log"
"github.com/pterodactyl/wings/parser"
"regexp"
"strings"
)
type OutputLineMatcher struct {
// The raw string to match against. This may or may not be prefixed with
// regex: which indicates we want to match against the regex expression.
raw string
reg *regexp.Regexp
}
// Determine if a given string "s" matches the given line.
func (olm *OutputLineMatcher) Matches(s string) bool {
if olm.reg == nil {
return strings.Contains(s, olm.raw)
}
return olm.reg.MatchString(s)
}
// Return the matcher's raw comparison string.
func (olm *OutputLineMatcher) String() string {
return olm.raw
}
// Unmarshal the startup lines into individual structs for easier matching abilities.
func (olm *OutputLineMatcher) UnmarshalJSON(data []byte) error {
if err := json.Unmarshal(data, &olm.raw); err != nil {
return err
}
if strings.HasPrefix(olm.raw, "regex:") && len(olm.raw) > 6 {
r, err := regexp.Compile(strings.TrimPrefix(olm.raw, "regex:"))
if err != nil {
log.WithField("error", err).WithField("raw", olm.raw).Warn("failed to compile output line marked as being regex")
}
olm.reg = r
}
return nil
}
// Defines the process configuration for a given server instance. This sets what the
// daemon is looking for to mark a server as done starting, what to do when stopping,
// and what changes to make to the configuration file for a server.
type ProcessConfiguration struct {
Startup struct {
Done []*OutputLineMatcher `json:"done"`
UserInteraction []string `json:"user_interaction"`
StripAnsi bool `json:"strip_ansi"`
} `json:"startup"`
Stop struct {
Type string `json:"type"`
Value string `json:"value"`
} `json:"stop"`
ConfigurationFiles []parser.ConfigurationFile `json:"configs"`
}

View File

@ -4,7 +4,6 @@ import (
"encoding/json" "encoding/json"
"fmt" "fmt"
"github.com/pkg/errors" "github.com/pkg/errors"
"github.com/pterodactyl/wings/parser"
) )
const ( const (
@ -26,24 +25,6 @@ type ServerConfigurationResponse struct {
ProcessConfiguration *ProcessConfiguration `json:"process_configuration"` ProcessConfiguration *ProcessConfiguration `json:"process_configuration"`
} }
// Defines the process configuration for a given server instance. This sets what the
// daemon is looking for to mark a server as done starting, what to do when stopping,
// and what changes to make to the configuration file for a server.
type ProcessConfiguration struct {
Startup struct {
Done []string `json:"done"`
UserInteraction []string `json:"user_interaction"`
StripAnsi bool `json:"strip_ansi"`
} `json:"startup"`
Stop struct {
Type string `json:"type"`
Value string `json:"value"`
} `json:"stop"`
ConfigurationFiles []parser.ConfigurationFile `json:"configs"`
}
// Defines installation script information for a server process. This is used when // Defines installation script information for a server process. This is used when
// a server is installed for the first time, and when a server is marked for re-installation. // a server is installed for the first time, and when a server is marked for re-installation.
type InstallationScript struct { type InstallationScript struct {

View File

@ -4,8 +4,6 @@ import (
"github.com/apex/log" "github.com/apex/log"
"github.com/pterodactyl/wings/api" "github.com/pterodactyl/wings/api"
"regexp" "regexp"
"strings"
"sync"
) )
// Adds all of the internal event listeners we want to use for a server. // Adds all of the internal event listeners we want to use for a server.
@ -23,12 +21,7 @@ func (s *Server) AddEventListeners() {
}() }()
} }
var ( var stripAnsiRegex = regexp.MustCompile("[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))")
stripAnsiRegex = regexp.MustCompile("[\u001B\u009B][[\\]()#;?]*(?:(?:(?:[a-zA-Z\\d]*(?:;[a-zA-Z\\d]*)*)?\u0007)|(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PRZcf-ntqry=><~]))")
regexpCacheMx sync.RWMutex
regexpCache map[string]*regexp.Regexp
)
// Custom listener for console output events that will check if the given line // Custom listener for console output events that will check if the given line
// of output matches one that should mark the server as started or not. // of output matches one that should mark the server as started or not.
@ -38,10 +31,6 @@ func (s *Server) onConsoleOutput(data string) {
// Check if the server is currently starting. // Check if the server is currently starting.
if s.GetState() == ProcessStartingState { if s.GetState() == ProcessStartingState {
// If the specific line of output is one that would mark the server as started,
// set the server to that state. Only do this if the server is not currently stopped
// or stopping.
// Check if we should strip ansi color codes. // Check if we should strip ansi color codes.
if processConfiguration.Startup.StripAnsi { if processConfiguration.Startup.StripAnsi {
// Strip ansi color codes from the data string. // Strip ansi color codes from the data string.
@ -49,40 +38,19 @@ func (s *Server) onConsoleOutput(data string) {
} }
// Iterate over all the done lines. // Iterate over all the done lines.
for _, match := range processConfiguration.Startup.Done { for _, l := range processConfiguration.Startup.Done {
if strings.HasPrefix(match, "regex:") && len(match) > 6 { if !l.Matches(data) {
match = match[6:]
regexpCacheMx.RLock()
rxp, ok := regexpCache[match]
regexpCacheMx.RUnlock()
if !ok {
var err error
rxp, err = regexp.Compile(match)
if err != nil {
log.WithError(err).Warn("failed to compile regexp")
break
}
regexpCacheMx.Lock()
regexpCache[match] = rxp
regexpCacheMx.Unlock()
}
if !rxp.MatchString(data) {
continue
}
} else if !strings.Contains(data, match) {
continue continue
} }
s.Log().WithFields(log.Fields{ s.Log().WithFields(log.Fields{
"match": match, "match": l.String(),
"against": data, "against": data,
}).Debug("detected server in running state based on console line output") }).Debug("detected server in running state based on console line output")
// If the specific line of output is one that would mark the server as started,
// set the server to that state. Only do this if the server is not currently stopped
// or stopping.
_ = s.SetState(ProcessRunningState) _ = s.SetState(ProcessRunningState)
break break
} }