Fixed worker in swarm using user input

This commit is contained in:
Frikky
2024-05-19 13:50:58 +02:00
parent 9745536c45
commit 7df8e08693
2 changed files with 509 additions and 508 deletions
+1 -1
View File
@@ -6,7 +6,7 @@ require (
github.com/docker/docker v26.1.0+incompatible github.com/docker/docker v26.1.0+incompatible
github.com/gorilla/mux v1.8.1 github.com/gorilla/mux v1.8.1
github.com/satori/go.uuid v1.2.0 github.com/satori/go.uuid v1.2.0
github.com/shuffle/shuffle-shared v0.6.16 github.com/shuffle/shuffle-shared v0.6.24
k8s.io/api v0.30.0 k8s.io/api v0.30.0
k8s.io/apimachinery v0.30.0 k8s.io/apimachinery v0.30.0
k8s.io/client-go v0.30.0 k8s.io/client-go v0.30.0
+214 -213
View File
@@ -2041,20 +2041,20 @@ runWorkflowExecutionTransaction(ctx, 0, workflowExecution.ExecutionId, actionRes
// Will make sure transactions are always ran for an execution. This is recursive if it fails. Allowed to fail up to 5 times // Will make sure transactions are always ran for an execution. This is recursive if it fails. Allowed to fail up to 5 times
func runWorkflowExecutionTransaction(ctx context.Context, attempts int64, workflowExecutionId string, actionResult shuffle.ActionResult, resp http.ResponseWriter) { func runWorkflowExecutionTransaction(ctx context.Context, attempts int64, workflowExecutionId string, actionResult shuffle.ActionResult, resp http.ResponseWriter) {
//log.Printf("[DEBUG][%s] IN WORKFLOWEXECUTION SUB!", actionResult.ExecutionId) //log.Printf("[DEBUG][%s] IN WORKFLOWEXECUTION SUB!", actionResult.ExecutionId)
workflowExecution, err := shuffle.GetWorkflowExecution(ctx, workflowExecutionId) workflowExecution, err := shuffle.GetWorkflowExecution(ctx, workflowExecutionId)
if err != nil { if err != nil {
log.Printf("[ERROR] Failed getting execution cache: %s", err) log.Printf("[ERROR] Failed getting execution cache: %s", err)
resp.WriteHeader(400) resp.WriteHeader(400)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed getting execution"}`))) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed getting execution"}`)))
return return
} }
resultLength := len(workflowExecution.Results) resultLength := len(workflowExecution.Results)
setExecution := true setExecution := true
workflowExecution, dbSave, err := shuffle.ParsedExecutionResult(ctx, *workflowExecution, actionResult, true, 0) workflowExecution, dbSave, err := shuffle.ParsedExecutionResult(ctx, *workflowExecution, actionResult, true, 0)
if err == nil { if err == nil {
if workflowExecution.Status != "EXECUTING" && workflowExecution.Status != "WAITING" { if workflowExecution.Status != "EXECUTING" && workflowExecution.Status != "WAITING" {
log.Printf("[WARNING][%s] Execution is not executing, but %s. Stopping Transaction update.", workflowExecution.ExecutionId, workflowExecution.Status) log.Printf("[WARNING][%s] Execution is not executing, but %s. Stopping Transaction update.", workflowExecution.ExecutionId, workflowExecution.Status)
if resp != nil { if resp != nil {
@@ -2075,7 +2075,8 @@ if err == nil {
shutdown(*workflowExecution, "", "", false) shutdown(*workflowExecution, "", "", false)
return return
} }
} else {
} else {
if strings.Contains(strings.ToLower(fmt.Sprintf("%s", err)), "already been ran") || strings.Contains(strings.ToLower(fmt.Sprintf("%s", err)), "already finished") { if strings.Contains(strings.ToLower(fmt.Sprintf("%s", err)), "already been ran") || strings.Contains(strings.ToLower(fmt.Sprintf("%s", err)), "already finished") {
log.Printf("[ERROR][%s] Skipping rerun of action result as it's already been ran: %s", workflowExecution.ExecutionId) log.Printf("[ERROR][%s] Skipping rerun of action result as it's already been ran: %s", workflowExecution.ExecutionId)
return return
@@ -2109,20 +2110,20 @@ if err == nil {
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed getting execution"}`))) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed getting execution"}`)))
return return
} }
} }
//log.Printf(`[DEBUG][%s] Got result %s from %s. Execution status: %s. Save: %#v. Parent: %#v`, actionResult.ExecutionId, actionResult.Status, actionResult.Action.ID, workflowExecution.Status, dbSave, workflowExecution.ExecutionParent) //log.Printf(`[DEBUG][%s] Got result %s from %s. Execution status: %s. Save: %#v. Parent: %#v`, actionResult.ExecutionId, actionResult.Status, actionResult.Action.ID, workflowExecution.Status, dbSave, workflowExecution.ExecutionParent)
//dbSave := false //dbSave := false
//if len(results) != len(workflowExecution.Results) { //if len(results) != len(workflowExecution.Results) {
// log.Printf("[DEBUG][%s] There may have been an issue in transaction queue. Result lengths: %d vs %d. Should check which exists the base results, but not in entire execution, then append.", workflowExecution.ExecutionId, len(results), len(workflowExecution.Results)) // log.Printf("[DEBUG][%s] There may have been an issue in transaction queue. Result lengths: %d vs %d. Should check which exists the base results, but not in entire execution, then append.", workflowExecution.ExecutionId, len(results), len(workflowExecution.Results))
//} //}
// Validating that action results hasn't changed // Validating that action results hasn't changed
// Handled using cachhing, so actually pretty fast // Handled using cachhing, so actually pretty fast
cacheKey := fmt.Sprintf("workflowexecution_%s", workflowExecution.ExecutionId) cacheKey := fmt.Sprintf("workflowexecution_%s", workflowExecution.ExecutionId)
cache, err := shuffle.GetCache(ctx, cacheKey) cache, err := shuffle.GetCache(ctx, cacheKey)
if err == nil { if err == nil {
//parsedValue := value.(*shuffle.WorkflowExecution) //parsedValue := value.(*shuffle.WorkflowExecution)
parsedValue := &shuffle.WorkflowExecution{} parsedValue := &shuffle.WorkflowExecution{}
@@ -2147,9 +2148,9 @@ if err == nil {
} }
*/ */
} }
} }
if setExecution || workflowExecution.Status == "FINISHED" || workflowExecution.Status == "ABORTED" || workflowExecution.Status == "FAILURE" { if setExecution || workflowExecution.Status == "FINISHED" || workflowExecution.Status == "ABORTED" || workflowExecution.Status == "FAILURE" {
log.Printf("[DEBUG][%s] Running setexec with status %s and %d/%d results", workflowExecution.ExecutionId, workflowExecution.Status, len(workflowExecution.Results), len(workflowExecution.Workflow.Actions)) log.Printf("[DEBUG][%s] Running setexec with status %s and %d/%d results", workflowExecution.ExecutionId, workflowExecution.Status, len(workflowExecution.Results), len(workflowExecution.Workflow.Actions))
//result(s)", workflowExecution.ExecutionId, workflowExecution.Status, len(workflowExecution.Results)) //result(s)", workflowExecution.ExecutionId, workflowExecution.Status, len(workflowExecution.Results))
err = setWorkflowExecution(ctx, *workflowExecution, dbSave) err = setWorkflowExecution(ctx, *workflowExecution, dbSave)
@@ -2159,65 +2160,65 @@ if setExecution || workflowExecution.Status == "FINISHED" || workflowExecution.S
return return
} }
} else { } else {
log.Printf("[INFO][%s] Skipping setexec with status %s", workflowExecution.ExecutionId, workflowExecution.Status) log.Printf("[INFO][%s] Skipping setexec with status %s", workflowExecution.ExecutionId, workflowExecution.Status)
// Just in case. Should MAYBE validate finishing another time as well. // Just in case. Should MAYBE validate finishing another time as well.
// This fixes issues with e.g. shuffle.Action -> shuffle.Trigger -> shuffle.Action. // This fixes issues with e.g. shuffle.Action -> shuffle.Trigger -> shuffle.Action.
handleExecutionResult(*workflowExecution) handleExecutionResult(*workflowExecution)
} }
//if newExecutions && len(nextActions) > 0 { //if newExecutions && len(nextActions) > 0 {
// log.Printf("[DEBUG][%s] New execution: %#v. NextActions: %#v", newExecutions, nextActions) // log.Printf("[DEBUG][%s] New execution: %#v. NextActions: %#v", newExecutions, nextActions)
// //handleExecutionResult(*workflowExecution) // //handleExecutionResult(*workflowExecution)
//} //}
resp.WriteHeader(200) resp.WriteHeader(200)
resp.Write([]byte(fmt.Sprintf(`{"success": true}`))) resp.Write([]byte(fmt.Sprintf(`{"success": true}`)))
} }
func sendSelfRequest(actionResult shuffle.ActionResult) { func sendSelfRequest(actionResult shuffle.ActionResult) {
data, err := json.Marshal(actionResult) data, err := json.Marshal(actionResult)
if err != nil { if err != nil {
log.Printf("[ERROR][%s] Shutting down (24): Failed to unmarshal data for backend: %s", actionResult.ExecutionId, err) log.Printf("[ERROR][%s] Shutting down (24): Failed to unmarshal data for backend: %s", actionResult.ExecutionId, err)
return return
} }
if actionResult.ExecutionId == "TBD" { if actionResult.ExecutionId == "TBD" {
return return
} }
log.Printf("[DEBUG][%s] Sending FAILURE to self to stop the workflow execution. Action: %s (%s), app %s:%s", actionResult.ExecutionId, actionResult.Action.Label, actionResult.Action.ID, actionResult.Action.AppName, actionResult.Action.AppVersion) log.Printf("[DEBUG][%s] Sending FAILURE to self to stop the workflow execution. Action: %s (%s), app %s:%s", actionResult.ExecutionId, actionResult.Action.Label, actionResult.Action.ID, actionResult.Action.AppName, actionResult.Action.AppVersion)
// Literally sending to same worker to run it as a new request // Literally sending to same worker to run it as a new request
streamUrl := fmt.Sprintf("http://localhost:33333/api/v1/streams") streamUrl := fmt.Sprintf("http://localhost:33333/api/v1/streams")
hostenv := os.Getenv("WORKER_HOSTNAME") hostenv := os.Getenv("WORKER_HOSTNAME")
if len(hostenv) > 0 { if len(hostenv) > 0 {
streamUrl = fmt.Sprintf("http://%s:33333/api/v1/streams", hostenv) streamUrl = fmt.Sprintf("http://%s:33333/api/v1/streams", hostenv)
} }
req, err := http.NewRequest( req, err := http.NewRequest(
"POST", "POST",
streamUrl, streamUrl,
bytes.NewBuffer([]byte(data)), bytes.NewBuffer([]byte(data)),
) )
if err != nil { if err != nil {
log.Printf("[ERROR][%s] Failed creating self request (1): %s", actionResult.ExecutionId, err) log.Printf("[ERROR][%s] Failed creating self request (1): %s", actionResult.ExecutionId, err)
return return
} }
client := shuffle.GetExternalClient(streamUrl) client := shuffle.GetExternalClient(streamUrl)
newresp, err := client.Do(req) newresp, err := client.Do(req)
if err != nil { if err != nil {
log.Printf("[ERROR][%s] Error running finishing request (2): %s", actionResult.ExecutionId, err) log.Printf("[ERROR][%s] Error running finishing request (2): %s", actionResult.ExecutionId, err)
return return
} }
defer newresp.Body.Close() defer newresp.Body.Close()
if newresp.Body != nil { if newresp.Body != nil {
body, err := ioutil.ReadAll(newresp.Body) body, err := ioutil.ReadAll(newresp.Body)
//log.Printf("[INFO] BACKEND STATUS: %d", newresp.StatusCode) //log.Printf("[INFO] BACKEND STATUS: %d", newresp.StatusCode)
if err != nil { if err != nil {
@@ -2225,59 +2226,59 @@ if newresp.Body != nil {
} else { } else {
log.Printf("[DEBUG][%s] NEWRESP (from backend): %s", actionResult.ExecutionId, string(body)) log.Printf("[DEBUG][%s] NEWRESP (from backend): %s", actionResult.ExecutionId, string(body))
} }
} }
} }
func sendResult(workflowExecution shuffle.WorkflowExecution, data []byte) { func sendResult(workflowExecution shuffle.WorkflowExecution, data []byte) {
if workflowExecution.ExecutionSource == "default" && os.Getenv("SHUFFLE_SWARM_CONFIG") != "run" && os.Getenv("SHUFFLE_SWARM_CONFIG") != "swarm" { if workflowExecution.ExecutionSource == "default" && os.Getenv("SHUFFLE_SWARM_CONFIG") != "run" && os.Getenv("SHUFFLE_SWARM_CONFIG") != "swarm" {
//log.Printf("[INFO][%s] Not sending backend info since source is default (not swarm)", workflowExecution.ExecutionId) //log.Printf("[INFO][%s] Not sending backend info since source is default (not swarm)", workflowExecution.ExecutionId)
//return //return
} else { } else {
} }
// Basically to reduce backend strain // Basically to reduce backend strain
/* /*
if shuffle.ArrayContains(finishedExecutions, workflowExecution.ExecutionId) { if shuffle.ArrayContains(finishedExecutions, workflowExecution.ExecutionId) {
log.Printf("[INFO][%s] NOT sending backend info since it's already been sent before.", workflowExecution.ExecutionId) log.Printf("[INFO][%s] NOT sending backend info since it's already been sent before.", workflowExecution.ExecutionId)
return return
} }
*/ */
// Take it down again // Take it down again
/* /*
if len(finishedExecutions) > 100 { if len(finishedExecutions) > 100 {
log.Printf("[DEBUG][%s] Removing old execution from finishedExecutions: %s", workflowExecution.ExecutionId, finishedExecutions[0]) log.Printf("[DEBUG][%s] Removing old execution from finishedExecutions: %s", workflowExecution.ExecutionId, finishedExecutions[0])
finishedExecutions = finishedExecutions[99:] finishedExecutions = finishedExecutions[99:]
} }
finishedExecutions = append(finishedExecutions, workflowExecution.ExecutionId) finishedExecutions = append(finishedExecutions, workflowExecution.ExecutionId)
*/ */
streamUrl := fmt.Sprintf("%s/api/v1/streams", baseUrl) streamUrl := fmt.Sprintf("%s/api/v1/streams", baseUrl)
req, err := http.NewRequest( req, err := http.NewRequest(
"POST", "POST",
streamUrl, streamUrl,
bytes.NewBuffer([]byte(data)), bytes.NewBuffer([]byte(data)),
) )
if err != nil { if err != nil {
log.Printf("[ERROR][%s] Failed creating finishing request: %s", workflowExecution.ExecutionId, err) log.Printf("[ERROR][%s] Failed creating finishing request: %s", workflowExecution.ExecutionId, err)
log.Printf("[DEBUG][%s] Shutting down (22)", workflowExecution.ExecutionId) log.Printf("[DEBUG][%s] Shutting down (22)", workflowExecution.ExecutionId)
shutdown(workflowExecution, "", "", false) shutdown(workflowExecution, "", "", false)
return return
} }
client := shuffle.GetExternalClient(streamUrl) client := shuffle.GetExternalClient(streamUrl)
newresp, err := client.Do(req) newresp, err := client.Do(req)
if err != nil { if err != nil {
log.Printf("[ERROR][%s] Error running finishing request (1): %s", workflowExecution.ExecutionId, err) log.Printf("[ERROR][%s] Error running finishing request (1): %s", workflowExecution.ExecutionId, err)
log.Printf("[DEBUG][%s] Shutting down (23)", workflowExecution.ExecutionId) log.Printf("[DEBUG][%s] Shutting down (23)", workflowExecution.ExecutionId)
shutdown(workflowExecution, "", "", false) shutdown(workflowExecution, "", "", false)
return return
} }
defer newresp.Body.Close() defer newresp.Body.Close()
if newresp.Body != nil { if newresp.Body != nil {
body, err := ioutil.ReadAll(newresp.Body) body, err := ioutil.ReadAll(newresp.Body)
//log.Printf("[INFO] BACKEND STATUS: %d", newresp.StatusCode) //log.Printf("[INFO] BACKEND STATUS: %d", newresp.StatusCode)
if err != nil { if err != nil {
@@ -2285,27 +2286,27 @@ if newresp.Body != nil {
} else { } else {
log.Printf("[DEBUG][%s] NEWRESP (from backend): %s", workflowExecution.ExecutionId, string(body)) log.Printf("[DEBUG][%s] NEWRESP (from backend): %s", workflowExecution.ExecutionId, string(body))
} }
} }
} }
func validateFinished(workflowExecution shuffle.WorkflowExecution) bool { func validateFinished(workflowExecution shuffle.WorkflowExecution) bool {
ctx := context.Background() ctx := context.Background()
newexec, err := shuffle.GetWorkflowExecution(ctx, workflowExecution.ExecutionId) newexec, err := shuffle.GetWorkflowExecution(ctx, workflowExecution.ExecutionId)
if err != nil { if err != nil {
log.Printf("[ERROR][%s] Failed getting workflow execution: %s", workflowExecution.ExecutionId, err) log.Printf("[ERROR][%s] Failed getting workflow execution: %s", workflowExecution.ExecutionId, err)
return false return false
} else { } else {
workflowExecution = *newexec workflowExecution = *newexec
} }
//startAction, extra, children, parents, visited, executed, nextActions, environments := shuffle.GetExecutionVariables(ctx, workflowExecution.ExecutionId) //startAction, extra, children, parents, visited, executed, nextActions, environments := shuffle.GetExecutionVariables(ctx, workflowExecution.ExecutionId)
workflowExecution, _ = shuffle.Fixexecution(ctx, workflowExecution) workflowExecution, _ = shuffle.Fixexecution(ctx, workflowExecution)
_, extra, _, _, _, _, _, environments := shuffle.GetExecutionVariables(ctx, workflowExecution.ExecutionId) _, extra, _, _, _, _, _, environments := shuffle.GetExecutionVariables(ctx, workflowExecution.ExecutionId)
log.Printf("[INFO][%s] VALIDATION. Status: %s, shuffle.Actions: %d, Extra: %d, Results: %d. Parent: %#v", workflowExecution.ExecutionId, workflowExecution.Status, len(workflowExecution.Workflow.Actions), extra, len(workflowExecution.Results), workflowExecution.ExecutionParent) log.Printf("[INFO][%s] VALIDATION. Status: %s, shuffle.Actions: %d, Extra: %d, Results: %d. Parent: %#v", workflowExecution.ExecutionId, workflowExecution.Status, len(workflowExecution.Workflow.Actions), extra, len(workflowExecution.Results), workflowExecution.ExecutionParent)
if workflowExecution.Status == "FINISHED" || workflowExecution.Status == "ABORTED" || (len(environments) == 1 && requestsSent == 0 && len(workflowExecution.Results) >= 1 && os.Getenv("SHUFFLE_SWARM_CONFIG") != "run" && os.Getenv("SHUFFLE_SWARM_CONFIG") != "swarm") || (len(workflowExecution.Results) >= len(workflowExecution.Workflow.Actions)+extra && len(workflowExecution.Workflow.Actions) > 0) { if workflowExecution.Status == "FINISHED" || workflowExecution.Status == "ABORTED" || (len(environments) == 1 && requestsSent == 0 && len(workflowExecution.Results) >= 1 && os.Getenv("SHUFFLE_SWARM_CONFIG") != "run" && os.Getenv("SHUFFLE_SWARM_CONFIG") != "swarm") || (len(workflowExecution.Results) >= len(workflowExecution.Workflow.Actions)+extra && len(workflowExecution.Workflow.Actions) > 0) {
if workflowExecution.Status == "FINISHED" { if workflowExecution.Status == "FINISHED" {
for _, result := range workflowExecution.Results { for _, result := range workflowExecution.Results {
@@ -2337,63 +2338,63 @@ if workflowExecution.Status == "FINISHED" || workflowExecution.Status == "ABORT
shuffle.RunCacheCleanup(ctx, workflowExecution) shuffle.RunCacheCleanup(ctx, workflowExecution)
sendResult(workflowExecution, shutdownData) sendResult(workflowExecution, shutdownData)
return true return true
} }
return false return false
} }
func handleGetStreamResults(resp http.ResponseWriter, request *http.Request) { func handleGetStreamResults(resp http.ResponseWriter, request *http.Request) {
defer request.Body.Close() defer request.Body.Close()
body, err := ioutil.ReadAll(request.Body) body, err := ioutil.ReadAll(request.Body)
if err != nil { if err != nil {
log.Printf("[WARNING] Failed reading body for stream result queue") log.Printf("[WARNING] Failed reading body for stream result queue")
resp.WriteHeader(500) resp.WriteHeader(500)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err))) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
return return
} }
var actionResult shuffle.ActionResult var actionResult shuffle.ActionResult
err = json.Unmarshal(body, &actionResult) err = json.Unmarshal(body, &actionResult)
if err != nil { if err != nil {
log.Printf("[WARNING] Failed shuffle.ActionResult unmarshaling: %s", err) log.Printf("[WARNING] Failed shuffle.ActionResult unmarshaling: %s", err)
//resp.WriteHeader(400) //resp.WriteHeader(400)
//resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err))) //resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
//return //return
} }
if len(actionResult.ExecutionId) == 0 { if len(actionResult.ExecutionId) == 0 {
log.Printf("[WARNING] No workflow execution id in action result (2). Data: %s", string(body)) log.Printf("[WARNING] No workflow execution id in action result (2). Data: %s", string(body))
resp.WriteHeader(400) resp.WriteHeader(400)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "No workflow execution id in action result"}`))) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "No workflow execution id in action result"}`)))
return return
} }
ctx := context.Background() ctx := context.Background()
workflowExecution, err := shuffle.GetWorkflowExecution(ctx, actionResult.ExecutionId) workflowExecution, err := shuffle.GetWorkflowExecution(ctx, actionResult.ExecutionId)
if err != nil { if err != nil {
log.Printf("[INFO] Failed getting execution (streamresult) %s: %s", actionResult.ExecutionId, err) log.Printf("[INFO] Failed getting execution (streamresult) %s: %s", actionResult.ExecutionId, err)
resp.WriteHeader(400) resp.WriteHeader(400)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Bad authorization key or execution_id might not exist."}`))) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Bad authorization key or execution_id might not exist."}`)))
return return
} }
// Authorization is done here // Authorization is done here
if workflowExecution.Authorization != actionResult.Authorization { if workflowExecution.Authorization != actionResult.Authorization {
log.Printf("[ERROR] Bad authorization key when getting stream results from cache %s.", actionResult.ExecutionId) log.Printf("[ERROR] Bad authorization key when getting stream results from cache %s.", actionResult.ExecutionId)
resp.WriteHeader(401) resp.WriteHeader(401)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Bad authorization key or execution_id might not exist."}`))) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Bad authorization key or execution_id might not exist."}`)))
return return
} }
newjson, err := json.Marshal(workflowExecution) newjson, err := json.Marshal(workflowExecution)
if err != nil { if err != nil {
resp.WriteHeader(500) resp.WriteHeader(500)
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed unpacking workflow execution"}`))) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed unpacking workflow execution"}`)))
return return
} }
resp.WriteHeader(200) resp.WriteHeader(200)
resp.Write(newjson) resp.Write(newjson)
} }
@@ -2401,152 +2402,152 @@ resp.Write(newjson)
func getLocalIP() string { func getLocalIP() string {
addrs, err := net.InterfaceAddrs() addrs, err := net.InterfaceAddrs()
if err != nil { if err != nil {
return "" return ""
} }
for _, address := range addrs { for _, address := range addrs {
// check the address type and if it is not a loopback the display it // check the address type and if it is not a loopback the display it
if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() { if ipnet, ok := address.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
if ipnet.IP.To4() != nil { if ipnet.IP.To4() != nil {
return ipnet.IP.String() return ipnet.IP.String()
} }
} }
} }
return "" return ""
} }
func getAvailablePort() (net.Listener, error) { func getAvailablePort() (net.Listener, error) {
listener, err := net.Listen("tcp", ":0") listener, err := net.Listen("tcp", ":0")
if err != nil { if err != nil {
log.Printf("[WARNING] Failed to assign port by default. Defaulting to 5001") log.Printf("[WARNING] Failed to assign port by default. Defaulting to 5001")
//return ":5001" //return ":5001"
return nil, err return nil, err
} }
//defer listener.Close() //defer listener.Close()
return listener, nil return listener, nil
//return fmt.Sprintf(":%d", port) //return fmt.Sprintf(":%d", port)
} }
func webserverSetup(workflowExecution shuffle.WorkflowExecution) net.Listener { func webserverSetup(workflowExecution shuffle.WorkflowExecution) net.Listener {
hostname = getLocalIP() hostname = getLocalIP()
os.Setenv("WORKER_HOSTNAME", hostname) os.Setenv("WORKER_HOSTNAME", hostname)
// FIXME: This MAY not work because of speed between first // FIXME: This MAY not work because of speed between first
// container being launched and port being assigned to webserver // container being launched and port being assigned to webserver
listener, err := getAvailablePort() listener, err := getAvailablePort()
if err != nil { if err != nil {
log.Printf("[ERROR] Failed to create init listener: %s", err) log.Printf("[ERROR] Failed to create init listener: %s", err)
return listener return listener
}
log.Printf("[DEBUG] OLD HOSTNAME: %s", appCallbackUrl)
port := listener.Addr().(*net.TCPAddr).Port
// Set the port environment variable
os.Setenv("WORKER_PORT", fmt.Sprintf("%d", port))
log.Printf("[DEBUG] Starting webserver (2) on port %d with hostname: %s", port, hostname)
appCallbackUrl = fmt.Sprintf("http://%s:%d", hostname, port)
log.Printf("[INFO] NEW WORKER HOSTNAME: %s", appCallbackUrl)
return listener
} }
log.Printf("[DEBUG] OLD HOSTNAME: %s", appCallbackUrl)
port := listener.Addr().(*net.TCPAddr).Port
// Set the port environment variable
os.Setenv("WORKER_PORT", fmt.Sprintf("%d", port))
log.Printf("[DEBUG] Starting webserver (2) on port %d with hostname: %s", port, hostname)
appCallbackUrl = fmt.Sprintf("http://%s:%d", hostname, port)
log.Printf("[INFO] NEW WORKER HOSTNAME: %s", appCallbackUrl)
return listener
}
func downloadDockerImageBackend(client *http.Client, imageName string) error { func downloadDockerImageBackend(client *http.Client, imageName string) error {
// Check environment SHUFFLE_AUTO_IMAGE_DOWNLOAD // Check environment SHUFFLE_AUTO_IMAGE_DOWNLOAD
if os.Getenv("SHUFFLE_AUTO_IMAGE_DOWNLOAD") == "false" { if os.Getenv("SHUFFLE_AUTO_IMAGE_DOWNLOAD") == "false" {
//log.Printf("[DEBUG] SHUFFLE_AUTO_IMAGE_DOWNLOAD is false. Not downloading image %s", imageName) //log.Printf("[DEBUG] SHUFFLE_AUTO_IMAGE_DOWNLOAD is false. Not downloading image %s", imageName)
return nil return nil
} }
if arrayContains(downloadedImages, imageName) { if arrayContains(downloadedImages, imageName) {
log.Printf("[DEBUG] Image %s already downloaded", imageName) log.Printf("[DEBUG] Image %s already downloaded", imageName)
return nil return nil
} }
log.Printf("[DEBUG] Trying to download image %s from backend %s as it doesn't exist. All images: %#v", imageName, baseUrl, downloadedImages) log.Printf("[DEBUG] Trying to download image %s from backend %s as it doesn't exist. All images: %#v", imageName, baseUrl, downloadedImages)
downloadedImages = append(downloadedImages, imageName) downloadedImages = append(downloadedImages, imageName)
data := fmt.Sprintf(`{"name": "%s"}`, imageName) data := fmt.Sprintf(`{"name": "%s"}`, imageName)
dockerImgUrl := fmt.Sprintf("%s/api/v1/get_docker_image", baseUrl) dockerImgUrl := fmt.Sprintf("%s/api/v1/get_docker_image", baseUrl)
req, err := http.NewRequest( req, err := http.NewRequest(
"POST", "POST",
dockerImgUrl, dockerImgUrl,
bytes.NewBuffer([]byte(data)), bytes.NewBuffer([]byte(data)),
) )
authorization := os.Getenv("AUTHORIZATION") authorization := os.Getenv("AUTHORIZATION")
if len(authorization) > 0 { if len(authorization) > 0 {
req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", authorization)) req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", authorization))
} else { } else {
log.Printf("[WARNING] No auth found - running backend download without it.") log.Printf("[WARNING] No auth found - running backend download without it.")
//return //return
} }
newresp, err := topClient.Do(req) newresp, err := topClient.Do(req)
if err != nil { if err != nil {
log.Printf("[ERROR] Failed download request for %s: %s", imageName, err) log.Printf("[ERROR] Failed download request for %s: %s", imageName, err)
return err return err
} }
defer newresp.Body.Close() defer newresp.Body.Close()
if newresp.StatusCode != 200 { if newresp.StatusCode != 200 {
log.Printf("[ERROR] Docker download for image %s (backend) StatusCode (1): %d", imageName, newresp.StatusCode) log.Printf("[ERROR] Docker download for image %s (backend) StatusCode (1): %d", imageName, newresp.StatusCode)
return errors.New(fmt.Sprintf("Failed to get image - status code %d", newresp.StatusCode)) return errors.New(fmt.Sprintf("Failed to get image - status code %d", newresp.StatusCode))
} }
newImageName := strings.Replace(imageName, "/", "_", -1) newImageName := strings.Replace(imageName, "/", "_", -1)
newFileName := newImageName + ".tar" newFileName := newImageName + ".tar"
tar, err := os.Create(newFileName) tar, err := os.Create(newFileName)
if err != nil { if err != nil {
log.Printf("[WARNING] Failed creating file: %s", err) log.Printf("[WARNING] Failed creating file: %s", err)
return err return err
} }
defer tar.Close() defer tar.Close()
_, err = io.Copy(tar, newresp.Body) _, err = io.Copy(tar, newresp.Body)
if err != nil { if err != nil {
log.Printf("[WARNING] Failed response body copying: %s", err) log.Printf("[WARNING] Failed response body copying: %s", err)
return err return err
} }
tar.Seek(0, 0) tar.Seek(0, 0)
dockercli, err := dockerclient.NewEnvClient() dockercli, err := dockerclient.NewEnvClient()
if err != nil { if err != nil {
log.Printf("[ERROR] Unable to create docker client (3): %s", err) log.Printf("[ERROR] Unable to create docker client (3): %s", err)
return err return err
} }
defer dockercli.Close() defer dockercli.Close()
imageLoadResponse, err := dockercli.ImageLoad(context.Background(), tar, true) imageLoadResponse, err := dockercli.ImageLoad(context.Background(), tar, true)
if err != nil { if err != nil {
log.Printf("[ERROR] Error loading images: %s", err) log.Printf("[ERROR] Error loading images: %s", err)
return err return err
} }
defer imageLoadResponse.Body.Close() defer imageLoadResponse.Body.Close()
body, err := ioutil.ReadAll(imageLoadResponse.Body) body, err := ioutil.ReadAll(imageLoadResponse.Body)
if err != nil { if err != nil {
log.Printf("[ERROR] Error reading: %s", err) log.Printf("[ERROR] Error reading: %s", err)
return err return err
} }
if strings.Contains(string(body), "no such file") { if strings.Contains(string(body), "no such file") {
return errors.New(string(body)) return errors.New(string(body))
} }
baseTag := strings.Split(imageName, ":") baseTag := strings.Split(imageName, ":")
if len(baseTag) > 1 { if len(baseTag) > 1 {
tag := baseTag[1] tag := baseTag[1]
log.Printf("[DEBUG] Creating tag copies of downloaded containers from tag %s", tag) log.Printf("[DEBUG] Creating tag copies of downloaded containers from tag %s", tag)
@@ -2558,33 +2559,33 @@ if len(baseTag) > 1 {
downloadedImages = append(downloadedImages, fmt.Sprintf("frikky/shuffle:%s", tag)) downloadedImages = append(downloadedImages, fmt.Sprintf("frikky/shuffle:%s", tag))
downloadedImages = append(downloadedImages, fmt.Sprintf("registry.hub.docker.com/frikky/shuffle:%s", tag)) downloadedImages = append(downloadedImages, fmt.Sprintf("registry.hub.docker.com/frikky/shuffle:%s", tag))
} }
os.Remove(newFileName) os.Remove(newFileName)
log.Printf("[INFO] Successfully loaded image %s: %s", imageName, string(body)) log.Printf("[INFO] Successfully loaded image %s: %s", imageName, string(body))
return nil return nil
} }
func findActiveSwarmNodes(dockercli *dockerclient.Client) (int64, error) { func findActiveSwarmNodes(dockercli *dockerclient.Client) (int64, error) {
ctx := context.Background() ctx := context.Background()
nodes, err := dockercli.NodeList(ctx, types.NodeListOptions{}) nodes, err := dockercli.NodeList(ctx, types.NodeListOptions{})
if err != nil { if err != nil {
return 1, err return 1, err
} }
nodeCount := int64(0) nodeCount := int64(0)
for _, node := range nodes { for _, node := range nodes {
//log.Printf("ID: %s - %#v", node.ID, node.Status.State) //log.Printf("ID: %s - %#v", node.ID, node.Status.State)
if node.Status.State == "ready" { if node.Status.State == "ready" {
nodeCount += 1 nodeCount += 1
} }
} }
// Check for SHUFFLE_MAX_NODES // Check for SHUFFLE_MAX_NODES
maxNodesString := os.Getenv("SHUFFLE_MAX_SWARM_NODES") maxNodesString := os.Getenv("SHUFFLE_MAX_SWARM_NODES")
// Make it into a number and check if it's lower than nodeCount // Make it into a number and check if it's lower than nodeCount
if len(maxNodesString) > 0 { if len(maxNodesString) > 0 {
maxNodes, err := strconv.ParseInt(maxNodesString, 10, 64) maxNodes, err := strconv.ParseInt(maxNodesString, 10, 64)
if err != nil { if err != nil {
return nodeCount, err return nodeCount, err
@@ -2593,15 +2594,15 @@ if len(maxNodesString) > 0 {
if nodeCount > maxNodes { if nodeCount > maxNodes {
nodeCount = maxNodes nodeCount = maxNodes
} }
} }
return nodeCount, nil return nodeCount, nil
/* /*
containers, err := dockercli.ContainerList(ctx, types.ContainerListOptions{ containers, err := dockercli.ContainerList(ctx, types.ContainerListOptions{
All: true, All: true,
}) })
*/ */
} }