feat: publically releasing scale worker + rollback to the original
This commit is contained in:
@@ -27,9 +27,15 @@ import (
|
|||||||
dockerclient "github.com/docker/docker/client"
|
dockerclient "github.com/docker/docker/client"
|
||||||
|
|
||||||
// This is for automatic removal of certain code :)
|
// This is for automatic removal of certain code :)
|
||||||
|
/*** STARTREMOVE ***/
|
||||||
|
"math/rand"
|
||||||
|
|
||||||
|
"github.com/docker/docker/api/types/swarm"
|
||||||
|
uuid "github.com/satori/go.uuid"
|
||||||
|
|
||||||
|
/*** ENDREMOVE ***/
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
uuid "github.com/satori/go.uuid"
|
|
||||||
|
|
||||||
//k8s deps
|
//k8s deps
|
||||||
corev1 "k8s.io/api/core/v1"
|
corev1 "k8s.io/api/core/v1"
|
||||||
@@ -138,6 +144,11 @@ func setWorkflowExecution(ctx context.Context, workflowExecution shuffle.Workflo
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*** STARTREMOVE ***/
|
||||||
|
if os.Getenv("SHUFFLE_SWARM_CONFIG") == "run" || os.Getenv("SHUFFLE_SWARM_CONFIG") == "swarm" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
/*** ENDREMOVE ***/
|
||||||
|
|
||||||
handleExecutionResult(workflowExecution)
|
handleExecutionResult(workflowExecution)
|
||||||
validated := shuffle.ValidateFinished(ctx, -1, workflowExecution)
|
validated := shuffle.ValidateFinished(ctx, -1, workflowExecution)
|
||||||
@@ -311,6 +322,11 @@ func shutdown(workflowExecution shuffle.WorkflowExecution, nodeId string, reason
|
|||||||
*/
|
*/
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
|
/*** STARTREMOVE ***/
|
||||||
|
if os.Getenv("SHUFFLE_SWARM_CONFIG") != "run" && os.Getenv("SHUFFLE_SWARM_CONFIG") != "swarm" {
|
||||||
|
log.Printf("[DEBUG][%s] NOT cleaning up containers. IDS: %d, CLEANUP env: %s", workflowExecution.ExecutionId, 0, cleanupEnv)
|
||||||
|
}
|
||||||
|
/*** ENDREMOVE ***/
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(reason) > 0 && len(nodeId) > 0 {
|
if len(reason) > 0 && len(nodeId) > 0 {
|
||||||
@@ -738,6 +754,88 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env []
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*** STARTREMOVE ***/
|
||||||
|
if os.Getenv("SHUFFLE_SWARM_CONFIG") == "run" || os.Getenv("SHUFFLE_SWARM_CONFIG") == "swarm" {
|
||||||
|
|
||||||
|
appName := strings.Replace(identifier, fmt.Sprintf("_%s", action.ID), "", -1)
|
||||||
|
appName = strings.Replace(appName, fmt.Sprintf("_%s", workflowExecution.ExecutionId), "", -1)
|
||||||
|
appName = strings.ToLower(appName)
|
||||||
|
//log.Printf("[INFO][%s] New appname: %s, image: %s", workflowExecution.ExecutionId, appName, image)
|
||||||
|
|
||||||
|
if !shuffle.ArrayContains(downloadedImages, image) && isKubernetes != "true" {
|
||||||
|
log.Printf("[DEBUG] Downloading image %s from backend as it's first iteration for this image on the worker. Timeout: 60", image)
|
||||||
|
// FIXME: Not caring if it's ok or not. Just continuing
|
||||||
|
// This is working as intended, just designed to download an updated
|
||||||
|
// image on every Orborus/new worker restart.
|
||||||
|
|
||||||
|
// Running as coroutine for eventual completeness
|
||||||
|
//go downloadDockerImageBackend(&http.Client{}, image)
|
||||||
|
// FIXME: With goroutines it got too much trouble of deploying with an older version
|
||||||
|
// Allowing slow startups, as long as it's eventually fast, and uses the same registry as on host.
|
||||||
|
downloadDockerImageBackend(&http.Client{Timeout: imagedownloadTimeout}, image)
|
||||||
|
}
|
||||||
|
|
||||||
|
var exposedPort int
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if isKubernetes != "true" {
|
||||||
|
exposedPort, err = findAppInfo(image, appName)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[ERROR] Failed finding and creating port for %s: %s", appName, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// ** STARTREMOVE ***/
|
||||||
|
exposedPort = 80
|
||||||
|
err = findAppInfoKubernetes(image, appName, env)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[ERROR] Failed finding and creating port for %s: %s", appName, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// ** ENDREMOVE ***/
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Makes it not run at all.
|
||||||
|
cacheData := []byte("1")
|
||||||
|
newExecId := fmt.Sprintf("%s_%s", workflowExecution.ExecutionId, action.ID)
|
||||||
|
err = shuffle.SetCache(ctx, newExecId, cacheData, 30)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[WARNING] (1) Failed setting cache for action %s: %s", newExecId, err)
|
||||||
|
} else {
|
||||||
|
log.Printf("[DEBUG][%s] (1) Adding %s to cache (%#v)", workflowExecution.ExecutionId, newExecId, action.Name)
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
|
log.Printf("[DEBUG][%s] Should run towards port %d for app %s. DELAY: %d", workflowExecution.ExecutionId, exposedPort, appName, action.ExecutionDelay)
|
||||||
|
ctx := context.Background()
|
||||||
|
if action.ExecutionDelay > 0 {
|
||||||
|
//log.Printf("[DEBUG] Running app %s with delay of %d", action.Name, action.ExecutionDelay)
|
||||||
|
waitTime := time.Duration(action.ExecutionDelay) * time.Second
|
||||||
|
|
||||||
|
time.AfterFunc(waitTime, func() {
|
||||||
|
err = sendAppRequest(ctx, baseUrl, appName, exposedPort, &action, &workflowExecution)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[ERROR] Failed sending SCHEDULED request to app %s on port %d: %s", appName, exposedPort, err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
} else {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
waitTime := time.Duration(rand.Intn(500)) * time.Millisecond
|
||||||
|
|
||||||
|
// Added a random delay + context timeout to ensure that the function returns, and only once
|
||||||
|
time.AfterFunc(waitTime, func() {
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, 30*time.Second)
|
||||||
|
defer cancel() // Cancel the context to release resources even if not used
|
||||||
|
|
||||||
|
go sendAppRequest(ctx, baseUrl, appName, exposedPort, &action, &workflowExecution)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
/*** ENDREMOVE ***/
|
||||||
|
|
||||||
// Max 10% CPU every second
|
// Max 10% CPU every second
|
||||||
//CPUShares: 128,
|
//CPUShares: 128,
|
||||||
@@ -2288,6 +2386,19 @@ func runWorkflowExecutionTransaction(ctx context.Context, attempts int64, workfl
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*** STARTREMOVE ***/
|
||||||
|
if workflowExecution.Status == "WAITING" && (os.Getenv("SHUFFLE_SWARM_CONFIG") == "run" || os.Getenv("SHUFFLE_SWARM_CONFIG") == "swarm") {
|
||||||
|
log.Printf("[INFO][%s] Workflow execution is waiting while in swarm. Sending info to backend to ensure execution stops.", workflowExecution.ExecutionId)
|
||||||
|
|
||||||
|
shutdownData, err := json.Marshal(workflowExecution)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[ERROR][%s] Failed marshalling execution (36) - not sending backend WAITING: %s", workflowExecution.ExecutionId, err)
|
||||||
|
} else {
|
||||||
|
sendResult(*workflowExecution, shutdownData)
|
||||||
|
shutdown(*workflowExecution, "", "", false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*** ENDREMOVE ***/
|
||||||
} 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)
|
||||||
@@ -2372,6 +2483,22 @@ func runWorkflowExecutionTransaction(ctx context.Context, attempts int64, workfl
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*** STARTREMOVE ***/
|
||||||
|
if os.Getenv("SHUFFLE_SWARM_CONFIG") == "run" || os.Getenv("SHUFFLE_SWARM_CONFIG") == "swarm" {
|
||||||
|
finished := shuffle.ValidateFinished(ctx, -1, *workflowExecution)
|
||||||
|
if !finished {
|
||||||
|
log.Printf("[DEBUG][%s] Handling next node since it's not finished!", workflowExecution.ExecutionId)
|
||||||
|
handleExecutionResult(*workflowExecution)
|
||||||
|
} else {
|
||||||
|
shutdownData, err := json.Marshal(workflowExecution)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[ERROR] Failed marshalling shutdowndata during set: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
sendResult(*workflowExecution, shutdownData)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*** ENDREMOVE ***/
|
||||||
} 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)
|
||||||
|
|
||||||
@@ -2391,6 +2518,12 @@ func runWorkflowExecutionTransaction(ctx context.Context, attempts int64, workfl
|
|||||||
|
|
||||||
func sendSelfRequest(actionResult shuffle.ActionResult) {
|
func sendSelfRequest(actionResult shuffle.ActionResult) {
|
||||||
|
|
||||||
|
/*** STARTREMOVE ***/
|
||||||
|
if os.Getenv("SHUFFLE_SWARM_CONFIG") != "run" && os.Getenv("SHUFFLE_SWARM_CONFIG") != "swarm" {
|
||||||
|
log.Printf("[INFO][%s] Not sending self request info since source is default (not swarm)", actionResult.ExecutionId)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
/*** ENDREMOVE ***/
|
||||||
|
|
||||||
data, err := json.Marshal(actionResult)
|
data, err := json.Marshal(actionResult)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -2529,6 +2662,11 @@ func validateFinished(workflowExecution shuffle.WorkflowExecution) bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*** STARTREMOVE ***/
|
||||||
|
if os.Getenv("SHUFFLE_SWARM_CONFIG") != "run" && os.Getenv("SHUFFLE_SWARM_CONFIG") != "swarm" {
|
||||||
|
requestsSent += 1
|
||||||
|
}
|
||||||
|
/*** ENDREMOVE ***/
|
||||||
|
|
||||||
log.Printf("[DEBUG][%s] Should send full result to %s", workflowExecution.ExecutionId, baseUrl)
|
log.Printf("[DEBUG][%s] Should send full result to %s", workflowExecution.ExecutionId, baseUrl)
|
||||||
|
|
||||||
@@ -2613,6 +2751,74 @@ func handleGetStreamResults(resp http.ResponseWriter, request *http.Request) {
|
|||||||
// GetLocalIP returns the non loopback local IP of the host
|
// GetLocalIP returns the non loopback local IP of the host
|
||||||
func getLocalIP() string {
|
func getLocalIP() string {
|
||||||
|
|
||||||
|
/*** STARTREMOVE ***/
|
||||||
|
if os.Getenv("IS_KUBERNETES") == "true" {
|
||||||
|
return "shuffle-workers"
|
||||||
|
}
|
||||||
|
|
||||||
|
if os.Getenv("SHUFFLE_SWARM_CONFIG") == "run" || os.Getenv("SHUFFLE_SWARM_CONFIG") == "swarm" {
|
||||||
|
name, err := os.Hostname()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[ERROR] Couldn't find hostname of worker: %s", err)
|
||||||
|
os.Exit(3)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Found hostname %s since worker is running with \"run\" command", name)
|
||||||
|
return name
|
||||||
|
|
||||||
|
/**
|
||||||
|
Everything below was a test to see if we needed to match directly to a network interface. May require docker network API.
|
||||||
|
**/
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Looking for IP for the external docker-network %s", swarmNetworkName)
|
||||||
|
// Different process to ensure we find the right IP.
|
||||||
|
// Necessary due to Ingress being added to docker ser
|
||||||
|
ifaces, err := net.Interfaces()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[ERROR] FATAL: networks the container is listening in %s: %s", swarmNetworkName, err)
|
||||||
|
os.Exit(3)
|
||||||
|
}
|
||||||
|
|
||||||
|
foundIP := ""
|
||||||
|
for _, i := range ifaces {
|
||||||
|
log.Printf("NETWORK: %s", i.Name)
|
||||||
|
//If i.Name != swarmNetworkName {
|
||||||
|
// continue
|
||||||
|
//}
|
||||||
|
|
||||||
|
addrs, err := i.Addrs()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[ERROR] FATAL: Failed getting address for listener in network %s: %s", swarmNetworkName, err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, addr := range addrs {
|
||||||
|
var ip net.IP
|
||||||
|
switch v := addr.(type) {
|
||||||
|
case *net.IPNet:
|
||||||
|
ip = v.IP
|
||||||
|
case *net.IPAddr:
|
||||||
|
ip = v.IP
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("%s: IP: %#v", i.Name, ip)
|
||||||
|
|
||||||
|
// FIXME: Allow for IPv6 too!
|
||||||
|
//if strings.Count(ip.String(), ".") == 3 {
|
||||||
|
// foundIP = ip.String()
|
||||||
|
// break
|
||||||
|
//}
|
||||||
|
// process IP address
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(foundIP) == 0 {
|
||||||
|
log.Printf("[ERROR] FATAL: No valid IP found for network %s. Defaulting to base IP", swarmNetworkName)
|
||||||
|
} else {
|
||||||
|
return foundIP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/*** ENDREMOVE ***/
|
||||||
|
|
||||||
addrs, err := net.InterfaceAddrs()
|
addrs, err := net.InterfaceAddrs()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -2663,6 +2869,27 @@ func webserverSetup(workflowExecution shuffle.WorkflowExecution) net.Listener {
|
|||||||
|
|
||||||
log.Printf("[DEBUG] OLD HOSTNAME: %s", appCallbackUrl)
|
log.Printf("[DEBUG] OLD HOSTNAME: %s", appCallbackUrl)
|
||||||
|
|
||||||
|
/*** STARTREMOVE ***/
|
||||||
|
if os.Getenv("SHUFFLE_SWARM_CONFIG") == "run" || os.Getenv("SHUFFLE_SWARM_CONFIG") == "swarm" {
|
||||||
|
log.Printf("[DEBUG] Starting webserver (1) on port %d with hostname: %s", baseport, hostname)
|
||||||
|
|
||||||
|
os.Setenv("WORKER_PORT", fmt.Sprintf("%d", baseport))
|
||||||
|
appCallbackUrl = fmt.Sprintf("http://%s:%d", hostname, baseport)
|
||||||
|
if os.Getenv("IS_KUBERNETES") == "true" {
|
||||||
|
appCallbackUrl = fmt.Sprintf("http://%s:%d", "shuffle-workers", baseport)
|
||||||
|
log.Printf("[DEBUG] NEW WORKER APP: %s", appCallbackUrl)
|
||||||
|
hostname = "shuffle-workers"
|
||||||
|
}
|
||||||
|
|
||||||
|
listener, err = net.Listen("tcp", fmt.Sprintf(":%d", baseport))
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[ERROR] Failed to assign port to %d: %s", baseport, err)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return listener
|
||||||
|
}
|
||||||
|
/*** ENDREMOVE ***/
|
||||||
|
|
||||||
port := listener.Addr().(*net.TCPAddr).Port
|
port := listener.Addr().(*net.TCPAddr).Port
|
||||||
// Set the port environment variable
|
// Set the port environment variable
|
||||||
@@ -2821,8 +3048,341 @@ func findActiveSwarmNodes(dockercli *dockerclient.Client) (int64, error) {
|
|||||||
*/
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*** STARTREMOVE ***/
|
||||||
|
func deploySwarmService(dockercli *dockerclient.Client, name, image string, deployport int) error {
|
||||||
|
log.Printf("[DEBUG] Deploying service for %s to swarm on port %d", name, deployport)
|
||||||
|
//containerName := fmt.Sprintf("shuffle-worker-%s", parsedUuid)
|
||||||
|
|
||||||
|
if len(baseimagename) == 0 || baseimagename == "/" {
|
||||||
|
baseimagename = "frikky/shuffle"
|
||||||
|
//var baseimagename = "frikky/shuffle"
|
||||||
|
//var registryName = "registry.hub.docker.com"
|
||||||
|
}
|
||||||
|
|
||||||
|
//image := fmt.Sprintf("%s:%s", baseimagename, name)
|
||||||
|
networkName := "shuffle-executions"
|
||||||
|
if len(swarmNetworkName) > 0 {
|
||||||
|
networkName = swarmNetworkName
|
||||||
|
}
|
||||||
|
|
||||||
|
replicas := uint64(1)
|
||||||
|
|
||||||
|
// Sent from Orborus
|
||||||
|
// Should be equal to
|
||||||
|
scaleReplicas := os.Getenv("SHUFFLE_APP_REPLICAS")
|
||||||
|
if len(scaleReplicas) > 0 {
|
||||||
|
tmpInt, err := strconv.Atoi(scaleReplicas)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[ERROR] %s is not a valid number for replication", scaleReplicas)
|
||||||
|
} else {
|
||||||
|
replicas = uint64(tmpInt)
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] SHUFFLE_APP_REPLICAS set to value %#v. Trying to overwrite default (%d/node)", scaleReplicas, replicas)
|
||||||
|
}
|
||||||
|
|
||||||
|
cnt, err := findActiveSwarmNodes(dockercli)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[ERROR] Unable to find active swarm nodes: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
nodeCount := uint64(1)
|
||||||
|
if cnt > 0 {
|
||||||
|
nodeCount = uint64(cnt)
|
||||||
|
}
|
||||||
|
|
||||||
|
replicatedJobs := uint64(replicas * nodeCount)
|
||||||
|
log.Printf("[DEBUG] Deploying app with name %s with image %s", name, image)
|
||||||
|
|
||||||
|
containerName := fmt.Sprintf(strings.Replace(name, ".", "-", -1))
|
||||||
|
serviceSpec := swarm.ServiceSpec{
|
||||||
|
Annotations: swarm.Annotations{
|
||||||
|
Name: containerName,
|
||||||
|
Labels: map[string]string{},
|
||||||
|
},
|
||||||
|
Mode: swarm.ServiceMode{
|
||||||
|
Replicated: &swarm.ReplicatedService{
|
||||||
|
// Max replicas total (?)
|
||||||
|
Replicas: &replicatedJobs,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Networks: []swarm.NetworkAttachmentConfig{
|
||||||
|
swarm.NetworkAttachmentConfig{
|
||||||
|
Target: networkName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
EndpointSpec: &swarm.EndpointSpec{
|
||||||
|
Ports: []swarm.PortConfig{
|
||||||
|
swarm.PortConfig{
|
||||||
|
Protocol: swarm.PortConfigProtocolTCP,
|
||||||
|
PublishMode: swarm.PortConfigPublishModeIngress,
|
||||||
|
Name: "app-port",
|
||||||
|
PublishedPort: uint32(deployport),
|
||||||
|
TargetPort: uint32(deployport),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
TaskTemplate: swarm.TaskSpec{
|
||||||
|
Resources: &swarm.ResourceRequirements{
|
||||||
|
Reservations: &swarm.Resources{},
|
||||||
|
},
|
||||||
|
LogDriver: &swarm.Driver{
|
||||||
|
Name: "json-file",
|
||||||
|
Options: map[string]string{
|
||||||
|
"max-size": "10m",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
ContainerSpec: &swarm.ContainerSpec{
|
||||||
|
Image: image,
|
||||||
|
Env: []string{
|
||||||
|
fmt.Sprintf("SHUFFLE_APP_EXPOSED_PORT=%d", deployport),
|
||||||
|
fmt.Sprintf("SHUFFLE_SWARM_CONFIG=%s", os.Getenv("SHUFFLE_SWARM_CONFIG")),
|
||||||
|
fmt.Sprintf("SHUFFLE_LOGS_DISABLED=%s", logsDisabled),
|
||||||
|
},
|
||||||
|
Hosts: []string{
|
||||||
|
containerName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
RestartPolicy: &swarm.RestartPolicy{
|
||||||
|
Condition: swarm.RestartPolicyConditionAny,
|
||||||
|
},
|
||||||
|
Placement: &swarm.Placement{
|
||||||
|
// Max per node
|
||||||
|
MaxReplicas: replicatedJobs,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(os.Getenv("SHUFFLE_SWARM_OTHER_NETWORK")) > 0 {
|
||||||
|
serviceSpec.Networks = append(serviceSpec.Networks, swarm.NetworkAttachmentConfig{
|
||||||
|
Target: "shuffle_shuffle",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if strings.ToLower(os.Getenv("SHUFFLE_PASS_APP_PROXY")) == "true" {
|
||||||
|
serviceSpec.TaskTemplate.ContainerSpec.Env = append(serviceSpec.TaskTemplate.ContainerSpec.Env, fmt.Sprintf("HTTP_PROXY=%s", os.Getenv("HTTP_PROXY")))
|
||||||
|
serviceSpec.TaskTemplate.ContainerSpec.Env = append(serviceSpec.TaskTemplate.ContainerSpec.Env, fmt.Sprintf("HTTPS_PROXY=%s", os.Getenv("HTTPS_PROXY")))
|
||||||
|
serviceSpec.TaskTemplate.ContainerSpec.Env = append(serviceSpec.TaskTemplate.ContainerSpec.Env, fmt.Sprintf("NO_PROXY=%s", os.Getenv("NO_PROXY")))
|
||||||
|
}
|
||||||
|
|
||||||
|
overrideHttpProxy := os.Getenv("SHUFFLE_INTERNAL_HTTP_PROXY")
|
||||||
|
overrideHttpsProxy := os.Getenv("SHUFFLE_INTERNAL_HTTPS_PROXY")
|
||||||
|
if overrideHttpProxy != "" {
|
||||||
|
serviceSpec.TaskTemplate.ContainerSpec.Env = append(serviceSpec.TaskTemplate.ContainerSpec.Env, fmt.Sprintf("SHUFFLE_INTERNAL_HTTP_PROXY=%s", overrideHttpProxy))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if overrideHttpsProxy != "" {
|
||||||
|
serviceSpec.TaskTemplate.ContainerSpec.Env = append(serviceSpec.TaskTemplate.ContainerSpec.Env, fmt.Sprintf("SHUFFLE_INTERNAL_HTTPS_PROXY=%s", overrideHttpsProxy))
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Mounts: []mount.Mount{
|
||||||
|
mount.Mount{
|
||||||
|
Source: "/var/run/docker.sock",
|
||||||
|
Target: "/var/run/docker.sock",
|
||||||
|
Type: mount.TypeBind,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
*/
|
||||||
|
|
||||||
|
if dockerApiVersion != "" {
|
||||||
|
serviceSpec.TaskTemplate.ContainerSpec.Env = append(serviceSpec.TaskTemplate.ContainerSpec.Env, fmt.Sprintf("DOCKER_API_VERSION=%s", dockerApiVersion))
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(os.Getenv("SHUFFLE_APP_SDK_TIMEOUT")) > 0 {
|
||||||
|
serviceSpec.TaskTemplate.ContainerSpec.Env = append(serviceSpec.TaskTemplate.ContainerSpec.Env, fmt.Sprintf("SHUFFLE_APP_SDK_TIMEOUT=%s", os.Getenv("SHUFFLE_APP_SDK_TIMEOUT")))
|
||||||
|
}
|
||||||
|
|
||||||
|
// Required for certain apps
|
||||||
|
if timezone == "" {
|
||||||
|
timezone = "Europe/Amsterdam"
|
||||||
|
}
|
||||||
|
|
||||||
|
serviceSpec.TaskTemplate.ContainerSpec.Env = append(serviceSpec.TaskTemplate.ContainerSpec.Env, fmt.Sprintf("TZ=%s", timezone))
|
||||||
|
|
||||||
|
serviceOptions := types.ServiceCreateOptions{}
|
||||||
|
service, err := dockercli.ServiceCreate(
|
||||||
|
context.Background(),
|
||||||
|
serviceSpec,
|
||||||
|
serviceOptions,
|
||||||
|
)
|
||||||
|
_ = service
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[DEBUG] Failed deploying %s with image %s: %s", name, image, err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] Successfully deployed service %s with image %s on port %d", name, image, deployport)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** ENDREMOVE ***/
|
||||||
|
|
||||||
// Runs data discovery
|
// Runs data discovery
|
||||||
|
/*** STARTREMOVE ***/
|
||||||
|
|
||||||
|
func findAppInfoKubernetes(image, name string, env []string) error {
|
||||||
|
clientset, _, err := shuffle.GetKubernetesClient()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[ERROR] Failed getting kubernetes: %s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if it exists as a pod
|
||||||
|
namespace := "default"
|
||||||
|
if len(kubernetesNamespace) > 0 {
|
||||||
|
namespace = kubernetesNamespace
|
||||||
|
}
|
||||||
|
|
||||||
|
// check deployments
|
||||||
|
deployments, err := clientset.AppsV1().Deployments(namespace).List(context.Background(), metav1.ListOptions{})
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[ERROR] Failed listing deployments: %s", err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
name = strings.Replace(name, "_", "-", -1)
|
||||||
|
|
||||||
|
// check if it exists as a pod
|
||||||
|
// for _, pod := range pods.Items {
|
||||||
|
// if pod.Name == name {
|
||||||
|
// log.Printf("[INFO] Found pod %s - no need to deploy another", name)
|
||||||
|
// return nil
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
for _, deployment := range deployments.Items {
|
||||||
|
if deployment.Name == name {
|
||||||
|
log.Printf("[INFO] Found deployment %s - no need to deploy another", name)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = deployk8sApp(image, name, env)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func findAppInfo(image, name string) (int, error) {
|
||||||
|
dockercli, err := dockerclient.NewEnvClient()
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[ERROR] Unable to create docker client (2): %s", err)
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
highest := baseport
|
||||||
|
exposedPort := -1
|
||||||
|
|
||||||
|
// Exists as a "cache" layer
|
||||||
|
if portMappings != nil {
|
||||||
|
for key, value := range portMappings {
|
||||||
|
if value > highest {
|
||||||
|
highest = value
|
||||||
|
}
|
||||||
|
|
||||||
|
if key == name {
|
||||||
|
exposedPort = value
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
portMappings = make(map[string]int)
|
||||||
|
}
|
||||||
|
|
||||||
|
//Filters:
|
||||||
|
if exposedPort == -1 {
|
||||||
|
serviceListOptions := types.ServiceListOptions{}
|
||||||
|
services, err := dockercli.ServiceList(
|
||||||
|
context.Background(),
|
||||||
|
serviceListOptions,
|
||||||
|
)
|
||||||
|
|
||||||
|
// Basic self-correction
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[ERROR] Unable to list services: %s (may continue anyway?)", err)
|
||||||
|
if strings.Contains(fmt.Sprintf("%s", err), "is too new") {
|
||||||
|
// Static for some reason
|
||||||
|
defaultVersion := "1.40"
|
||||||
|
dockerApiVersion = defaultVersion
|
||||||
|
os.Setenv("DOCKER_API_VERSION", defaultVersion)
|
||||||
|
log.Printf("[DEBUG] Setting Docker API to %s default and retrying listing requests", defaultVersion)
|
||||||
|
} else {
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
|
||||||
|
services, err = dockercli.ServiceList(
|
||||||
|
context.Background(),
|
||||||
|
serviceListOptions,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[ERROR] Unable to list services (2): %s", err)
|
||||||
|
return -1, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, service := range services {
|
||||||
|
//log.Printf("[INFO] Service: %#v", service.Spec.Annotations.Name)
|
||||||
|
|
||||||
|
for _, endpoint := range service.Spec.EndpointSpec.Ports {
|
||||||
|
if strings.Contains(endpoint.Name, "port") {
|
||||||
|
portMappings[service.Spec.Annotations.Name] = int(endpoint.PublishedPort)
|
||||||
|
if int(endpoint.PublishedPort) > highest {
|
||||||
|
highest = int(endpoint.PublishedPort)
|
||||||
|
}
|
||||||
|
|
||||||
|
if service.Spec.Annotations.Name == name || service.Spec.Annotations.Name == strings.Replace(name, ".", "-", -1) {
|
||||||
|
exposedPort = int(endpoint.PublishedPort)
|
||||||
|
//break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//log.Printf("%s - %s", service.Spec.Annotations.Name, strings.Replace(name, ".", "-", -1))
|
||||||
|
if service.Spec.Annotations.Name != name && service.Spec.Annotations.Name != strings.Replace(name, ".", "-", -1) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
// Break if it's the correct port, as it's the right service
|
||||||
|
if exposedPort >= 0 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//log.Printf("[DEBUG] Portmappings: %#v", portMappings)
|
||||||
|
|
||||||
|
if exposedPort >= 0 {
|
||||||
|
//log.Printf("[INFO] Found service %s on port %d - no need to deploy another", name, exposedPort)
|
||||||
|
} else {
|
||||||
|
// Increment by 1 for highest port
|
||||||
|
if highest <= baseport {
|
||||||
|
highest = baseport
|
||||||
|
}
|
||||||
|
|
||||||
|
highest += 1
|
||||||
|
err = deploySwarmService(dockercli, name, image, highest)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[WARNING] NOT Found service: %s. error: %s", name, err)
|
||||||
|
return highest, err
|
||||||
|
} else {
|
||||||
|
log.Printf("[DEBUG] Deployed app with name %s", name)
|
||||||
|
}
|
||||||
|
|
||||||
|
exposedPort = highest
|
||||||
|
|
||||||
|
if appsInitialized {
|
||||||
|
log.Printf("[DEBUG] Waiting 30 seconds before moving on to let app start")
|
||||||
|
time.Sleep(time.Duration(30) * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return exposedPort, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** ENDREMOVE ***/
|
||||||
|
|
||||||
func sendAppRequest(ctx context.Context, incomingUrl, appName string, port int, action *shuffle.Action, workflowExecution *shuffle.WorkflowExecution) error {
|
func sendAppRequest(ctx context.Context, incomingUrl, appName string, port int, action *shuffle.Action, workflowExecution *shuffle.WorkflowExecution) error {
|
||||||
parsedRequest := shuffle.OrborusExecutionRequest{
|
parsedRequest := shuffle.OrborusExecutionRequest{
|
||||||
@@ -3218,6 +3778,11 @@ func getStreamResultsWrapper(client *http.Client, req *http.Request, workflowExe
|
|||||||
|
|
||||||
// Initial loop etc
|
// Initial loop etc
|
||||||
func main() {
|
func main() {
|
||||||
|
/*** STARTREMOVE ***/
|
||||||
|
if os.Getenv("SHUFFLE_SWARM_CONFIG") == "run" || os.Getenv("SHUFFLE_SWARM_CONFIG") == "swarm" {
|
||||||
|
logsDisabled = "true"
|
||||||
|
}
|
||||||
|
/*** ENDREMOVE ***/
|
||||||
// Elasticsearch necessary to ensure we'ren ot running with Datastore configurations for minimal/maximal data sizes
|
// Elasticsearch necessary to ensure we'ren ot running with Datastore configurations for minimal/maximal data sizes
|
||||||
// Recursive import kind of :)
|
// Recursive import kind of :)
|
||||||
_, err := shuffle.RunInit(*shuffle.GetDatastore(), *shuffle.GetStorage(), "", "worker", true, "elasticsearch", false, 0)
|
_, err := shuffle.RunInit(*shuffle.GetDatastore(), *shuffle.GetStorage(), "", "worker", true, "elasticsearch", false, 0)
|
||||||
@@ -3251,6 +3816,22 @@ func main() {
|
|||||||
swarmConfig := os.Getenv("SHUFFLE_SWARM_CONFIG")
|
swarmConfig := os.Getenv("SHUFFLE_SWARM_CONFIG")
|
||||||
log.Printf("[INFO] Running with timezone %s and swarm config %#v", timezone, swarmConfig)
|
log.Printf("[INFO] Running with timezone %s and swarm config %#v", timezone, swarmConfig)
|
||||||
|
|
||||||
|
/*** STARTREMOVE ***/
|
||||||
|
if swarmConfig == "run" || swarmConfig == "swarm" {
|
||||||
|
// Forcing download just in case on the first iteration.
|
||||||
|
log.Printf("[INFO] Running in swarm mode - forcing download of apps")
|
||||||
|
workflowExecution := shuffle.WorkflowExecution{}
|
||||||
|
|
||||||
|
go baseDeploy()
|
||||||
|
|
||||||
|
listener := webserverSetup(workflowExecution)
|
||||||
|
runWebserver(listener)
|
||||||
|
|
||||||
|
// Should never get down here
|
||||||
|
log.Printf("[ERROR] Stopped listener %#v - exiting.", listener)
|
||||||
|
os.Exit(3)
|
||||||
|
}
|
||||||
|
/*** ENDREMOVE ***/
|
||||||
|
|
||||||
authorization := ""
|
authorization := ""
|
||||||
executionId := ""
|
executionId := ""
|
||||||
@@ -3593,6 +4174,11 @@ func runWebserver(listener net.Listener) {
|
|||||||
r.HandleFunc("/api/v1/run", handleRunExecution).Methods("POST", "OPTIONS")
|
r.HandleFunc("/api/v1/run", handleRunExecution).Methods("POST", "OPTIONS")
|
||||||
r.HandleFunc("/api/v1/download", handleDownloadImage).Methods("POST", "OPTIONS")
|
r.HandleFunc("/api/v1/download", handleDownloadImage).Methods("POST", "OPTIONS")
|
||||||
|
|
||||||
|
/*** STARTREMOVE ***/
|
||||||
|
if os.Getenv("SHUFFLE_SWARM_CONFIG") == "run" || os.Getenv("SHUFFLE_SWARM_CONFIG") == "swarm" {
|
||||||
|
log.Printf("[DEBUG] Running webserver config for SWARM and K8s")
|
||||||
|
}
|
||||||
|
/*** ENDREMOVE ***/
|
||||||
|
|
||||||
if strings.ToLower(os.Getenv("SHUFFLE_DEBUG_MEMORY")) == "true" {
|
if strings.ToLower(os.Getenv("SHUFFLE_DEBUG_MEMORY")) == "true" {
|
||||||
r.HandleFunc("/debug/pprof/", pprof.Index)
|
r.HandleFunc("/debug/pprof/", pprof.Index)
|
||||||
|
|||||||
Reference in New Issue
Block a user