diff --git a/.env b/.env
index 5266d136..45389f1f 100644
--- a/.env
+++ b/.env
@@ -40,3 +40,6 @@ SHUFFLE_PASS_WORKER_PROXY=TRUE
SHUFFLE_BASE_IMAGE_REGISTRY=ghcr.io
SHUFFLE_BASE_IMAGE_NAME=frikky
SHUFFLE_BASE_IMAGE_TAG_SUFFIX="-0.8.3"
+
+# Used for auto-cleanup of containers. REALLY important at scale.
+SHUFFLE_CONTAINER_AUTO_CLEANUP=false
diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py
index d88b7626..14936845 100644
--- a/backend/app_sdk/app_base.py
+++ b/backend/app_sdk/app_base.py
@@ -21,7 +21,7 @@ class AppBase:
# apikey is for the user / org
# authorization is for the specific workflow
self.url = os.getenv("CALLBACK_URL", "https://shuffler.io")
- self.base_url = os.getenv("BASE_URL", "https://shuffler.io")
+ self.base_url = os.getenv("BASE_URL", "")
self.action = os.getenv("ACTION", "")
self.authorization = os.getenv("AUTHORIZATION", "")
self.current_execution_id = os.getenv("EXECUTIONID", "")
diff --git a/backend/app_sdk/build.sh b/backend/app_sdk/build.sh
index c6911f6e..94552a85 100644
--- a/backend/app_sdk/build.sh
+++ b/backend/app_sdk/build.sh
@@ -1,6 +1,6 @@
#!/bin/bash
NAME=shuffle-app_sdk
-VERSION=0.8.52
+VERSION=0.8.53
docker rmi docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION --force
docker build . -t frikky/shuffle:app_sdk -t frikky/$NAME:$VERSION -t docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION -t ghcr.io/frikky/$NAME:$VERSION
diff --git a/backend/go-app/codegen.go b/backend/go-app/codegen.go
index fe2b6967..ba029b15 100644
--- a/backend/go-app/codegen.go
+++ b/backend/go-app/codegen.go
@@ -397,12 +397,11 @@ func makePythoncode(swagger *openapi3.Swagger, name, url, method string, paramet
verifyAddin,
)
- /*
- if strings.Contains(functionname, "search") {
- log.Println(data)
- log.Printf("Queries: %s", queryString)
- }
- */
+ //log.Printf("FUNCTION: %s", data)
+ //if strings.Contains(functionname, "search") {
+ // log.Println(data)
+ // log.Printf("Queries: %s", queryString)
+ //}
//log.Printf(data)
return functionname, data
diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go
index 0b06a1bb..0ef011a8 100644
--- a/backend/go-app/walkoff.go
+++ b/backend/go-app/walkoff.go
@@ -917,12 +917,13 @@ func validateNewWorkerExecution(body []byte) error {
return errors.New("Bad authorization when validating execution")
}
+ // used to validate if it's actually the right marshal
if len(baseExecution.Workflow.Actions) != len(execution.Workflow.Actions) {
- return errors.New(fmt.Sprintf("Bad length of actions: %d", len(execution.Workflow.Actions)))
+ return errors.New(fmt.Sprintf("Bad length of actions (probably normal app): %d", len(execution.Workflow.Actions)))
}
if len(baseExecution.Workflow.Triggers) != len(execution.Workflow.Triggers) {
- return errors.New(fmt.Sprintf("Bad length of trigger: %d", len(execution.Workflow.Triggers)))
+ return errors.New(fmt.Sprintf("Bad length of trigger: %d (probably normal app)", len(execution.Workflow.Triggers)))
}
// FIXME: Add extra here
@@ -933,8 +934,11 @@ func validateNewWorkerExecution(body []byte) error {
//log.Printf("\n\nSHOULD SET BACKEND DATA FOR EXEC \n\n")
err = setWorkflowExecution(ctx, execution, true)
- if err != nil {
- log.Printf("Successfully set the execution to wait.")
+ if err == nil {
+ log.Printf("[INFO] Set workflowexecution based on new worker (>0.8.53) for execution %s", baseExecution.ExecutionId)
+ //log.Printf("[INFO] Successfully set the execution to wait.")
+ } else {
+ log.Printf("[WARNING] Failed to set the execution to wait.")
}
return nil
@@ -957,12 +961,11 @@ func handleWorkflowQueue(resp http.ResponseWriter, request *http.Request) {
//log.Printf("Actionresult unmarshal: %s", string(body))
err = validateNewWorkerExecution(body)
if err == nil {
- log.Printf("[INFO] Set workflowexecution based on new worker")
resp.WriteHeader(200)
resp.Write([]byte(fmt.Sprintf(`{"success": true, "reason": "Success"}`)))
return
} else {
- log.Printf("[WARNING] Failed to handle new execution variant: %s", err)
+ //log.Printf("[WARNING] Failed to handle new execution variant: %s", err)
}
var actionResult ActionResult
diff --git a/docker-compose.yml b/docker-compose.yml
index 8c3b5ba7..1846975b 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -1,8 +1,8 @@
version: '3'
services:
frontend:
- #build: ./frontend
- image: ghcr.io/frikky/shuffle-frontend:0.8.51
+ build: ./frontend
+ image: ghcr.io/frikky/shuffle-frontend:0.8.52
container_name: shuffle-frontend
hostname: shuffle-frontend
ports:
@@ -17,7 +17,7 @@ services:
- backend
backend:
#build: ./backend
- image: ghcr.io/frikky/shuffle-backend:0.8.51
+ image: ghcr.io/frikky/shuffle-backend:0.8.52
container_name: shuffle-backend
hostname: ${BACKEND_HOSTNAME}
# Here for debugging:
@@ -66,6 +66,7 @@ services:
- SHUFFLE_BASE_IMAGE_NAME=${SHUFFLE_BASE_IMAGE_NAME}
- SHUFFLE_BASE_IMAGE_REGISTRY=${SHUFFLE_BASE_IMAGE_REGISTRY}
- SHUFFLE_BASE_IMAGE_TAG_SUFFIX=${SHUFFLE_BASE_IMAGE_TAG_SUFFIX}
+ - CLEANUP=${SHUFFLE_CONTAINER_AUTO_CLEANUP}
restart: unless-stopped
database:
#build: ./backend/database
diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx
index ae6e8302..2f0b127a 100644
--- a/frontend/src/views/AngularWorkflow.jsx
+++ b/frontend/src/views/AngularWorkflow.jsx
@@ -4276,11 +4276,18 @@ const AngularWorkflow = (props) => {
color="primary"
defaultValue={data.value}
placeholder={placeholder}
+ helperText={data.value !== undefined && data.value !== null && data.value.includes(".#") ?
+
+ Use "Shuffle Tools" app with "Filter List" action to handle loops
+
+ : null
+ }
onClick={() => {
console.log("CHANGE FIELD")
}}
onBlur={(e) => {
changeActionVariable(data.action_field, e.target.value)
+ setUpdate(Math.random())
}}
/>
@@ -4473,8 +4480,10 @@ const AngularWorkflow = (props) => {
}}
>
- Condition
-
+ Condition
+
+
+
+
+ Conditions can't be used for loops [ .# ]. Learn more
+
-
+ setSelectedEdge(selectedEdge)
+ workflow.branches[selectedEdgeIndex] = selectedEdge
+ setWorkflow(workflow)
+ }} color="primary">
+ Submit
+
+
@@ -6422,15 +6436,20 @@ const AngularWorkflow = (props) => {
:
- {
- setExecutionRunning(false)
- stop()
- getWorkflowExecution(props.match.params.key)
- setExecutionModalView(0)
+ {
+ setExecutionRunning(false)
+ stop()
+ getWorkflowExecution(props.match.params.key)
+ setExecutionModalView(0)
}}>
-
- See other Executions
-
+ {}}>
+
+
+ {
+ }}>
+ See other Executions
+
+
Executing Workflow
diff --git a/frontend/src/views/Apps.jsx b/frontend/src/views/Apps.jsx
index c9312c6d..97689cac 100644
--- a/frontend/src/views/Apps.jsx
+++ b/frontend/src/views/Apps.jsx
@@ -341,7 +341,7 @@ const Apps = (props) => {
}
var description = data.description
- const maxDescLen = 60
+ const maxDescLen = 58
if (description.length > maxDescLen) {
description = data.description.slice(0, maxDescLen)+"..."
}
diff --git a/functions/onprem/orborus/build.sh b/functions/onprem/orborus/build.sh
index 7a29fcfa..5fdb61ea 100644
--- a/functions/onprem/orborus/build.sh
+++ b/functions/onprem/orborus/build.sh
@@ -1,5 +1,5 @@
NAME=shuffle-orborus
-VERSION=0.8.52
+VERSION=0.8.53
echo "Running docker build with $NAME:$VERSION"
#docker rmi frikky/shuffle:$NAME --force
diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go
index 42e9d097..5a7c0285 100644
--- a/functions/onprem/orborus/orborus.go
+++ b/functions/onprem/orborus/orborus.go
@@ -49,6 +49,8 @@ var baseUrl = os.Getenv("BASE_URL")
var environment = os.Getenv("ENVIRONMENT_NAME")
var dockerApiVersion = os.Getenv("DOCKER_API_VERSION")
var runningMode = strings.ToLower(os.Getenv("RUNNING_MODE"))
+var cleanupEnv = strings.ToLower(os.Getenv("CLEANUP"))
+var workerIds = []string{}
type ExecutionRequestWrapper struct {
Data []ExecutionRequest `json:"data"`
@@ -144,6 +146,10 @@ func deployWorker(image string, identifier string, env []string) {
//log.Printf("[INFO] Empty self container id, continue without NetworkMode")
}
+ if cleanupEnv == "true" {
+ hostConfig.AutoRemove = true
+ }
+
config := &container.Config{
Image: image,
Env: env,
@@ -212,6 +218,7 @@ func deployWorker(image string, identifier string, env []string) {
//}
} else {
log.Printf("[INFO] Container %s was created under environment %s", cont.ID, environment)
+ //workerIds = append(workerIds, cont.ID)
}
return
@@ -248,7 +255,7 @@ func initializeImages() {
log.Printf("[WARNING] SHUFFLE_APP_SDK_VERSION not defined. Defaulting to %s", appSdkVersion)
}
if workerVersion == "" {
- workerVersion = "0.8.52"
+ workerVersion = "0.8.53"
log.Printf("[WARNING] SHUFFLE_WORKER_VERSION not defined. Defaulting to %s", workerVersion)
}
@@ -499,6 +506,7 @@ func main() {
fmt.Sprintf("EXECUTIONID=%s", execution.ExecutionId),
fmt.Sprintf("ENVIRONMENT_NAME=%s", environment),
fmt.Sprintf("BASE_URL=%s", baseUrl),
+ fmt.Sprintf("CLEANUP=%s", cleanupEnv),
}
if strings.ToLower(os.Getenv("SHUFFLE_PASS_WORKER_PROXY")) != "false" {
@@ -512,7 +520,7 @@ func main() {
go deployWorker(workerImage, containerName, env)
- log.Printf("[INFO] %s was deployed and to be removed from queue.", execution.ExecutionId)
+ log.Printf("[INFO] ExecutionID %s was deployed and to be removed from queue.", execution.ExecutionId)
zombiecounter += 1
toBeRemoved.Data = append(toBeRemoved.Data, execution)
}
diff --git a/functions/onprem/worker/build.sh b/functions/onprem/worker/build.sh
index 29828da4..fac6304e 100644
--- a/functions/onprem/worker/build.sh
+++ b/functions/onprem/worker/build.sh
@@ -1,5 +1,5 @@
NAME=shuffle-worker
-VERSION=0.8.52
+VERSION=0.8.53
echo "Running docker build with $NAME:$VERSION"
#CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o worker.bin .
@@ -10,5 +10,5 @@ docker build . -t frikky/shuffle:$NAME -t frikky/shuffle:$NAME_$VERSION -t docke
#docker push frikky/shuffle:$NAME_$VERSION
#docker push docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION
#docker tag frikky/shuffle:0.8.51 ghcr.io/frikky/shuffle-worker:0.8.5
-#docker push ghcr.io/frikky/$NAME:$VERSION
docker tag frikky/shuffle:$NAME ghcr.io/frikky/shuffle-worker:0.8.52
+docker push ghcr.io/frikky/$NAME:$VERSION
diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go
index 49c4b583..016ec96f 100644
--- a/functions/onprem/worker/worker.go
+++ b/functions/onprem/worker/worker.go
@@ -28,6 +28,7 @@ import (
var environment = os.Getenv("ENVIRONMENT_NAME")
var baseUrl = os.Getenv("BASE_URL")
var appCallbackUrl = os.Getenv("BASE_URL")
+var cleanupEnv = strings.ToLower(os.Getenv("CLEANUP"))
var baseimagename = "frikky/shuffle"
var registryName = "registry.hub.docker.com"
var fallbackName = "shuffle-orborus"
@@ -43,6 +44,7 @@ var children map[string][]string
var visited []string
var executed []string
var nextActions []string
+var containerIds []string
var extra int
var startAction string
@@ -68,9 +70,9 @@ func getThisContainerId() string {
func init() {
containerId = getThisContainerId()
if len(containerId) == 0 {
- log.Printf("[ERROR] No container ID found. Not running containerized?")
+ log.Printf("[WARNING] No container ID found. Not running containerized? This should only show during testing")
} else {
- log.Printf("[INFO] Found container ID: %s", containerId)
+ log.Printf("[INFO] Found container ID for this worker: %s", containerId)
}
}
@@ -770,35 +772,28 @@ type AppExecutionExample struct {
// removes every container except itself (worker)
func shutdown(executionId, workflowId string) {
- dockercli, err := dockerclient.NewEnvClient()
- if err != nil {
- log.Printf("[ERROR] Unable to create docker client: %s", err)
- os.Exit(3)
- }
+ log.Printf("[INFO] Shutdown started")
- containerOptions := types.ContainerListOptions{
- All: true,
- }
+ // Might not be necessary because of cleanupEnv hostconfig autoremoval
+ if cleanupEnv == "true" && len(containerIds) > 0 {
+ ctx := context.Background()
+ dockercli, err := dockerclient.NewEnvClient()
+ if err == nil {
+ log.Printf("[INFO] Cleaning up %d containers", len(containerIds))
+ removeOptions := types.ContainerRemoveOptions{
+ RemoveVolumes: true,
+ Force: true,
+ }
- containers, err := dockercli.ContainerList(context.Background(), containerOptions)
- if err != nil {
- panic(err)
- }
- _ = containers
-
- for _, container := range containers {
- for _, name := range container.Names {
- if strings.Contains(name, executionId) {
- // FIXME - reinstate - not here for debugging
- //err = removeContainer(container.ID)
- //if err != nil {
- // log.Printf("Failed removing %s before shutdown.", name)
- //}
-
- break
+ for _, containername := range containerIds {
+ log.Printf("[INFO] Stopping and removing container %s", containername)
+ dockercli.ContainerStop(ctx, containername, nil)
+ dockercli.ContainerRemove(ctx, containername, removeOptions)
+ //removeContainers = append(removeContainers, containername)
}
}
-
+ } else {
+ log.Printf("[INFO] NOT cleaning up containers. IDS: %d, CLEANUP env: %s", len(containerIds), cleanupEnv)
}
fullUrl := fmt.Sprintf("%s/api/v1/workflows/%s/executions/%s/abort", baseUrl, workflowId, executionId)
@@ -844,7 +839,9 @@ func shutdown(executionId, workflowId string) {
log.Printf("[INFO] Failed abort request: %s", err)
}
- log.Printf("[INFO] Finished shutdown.")
+ log.Printf("[INFO] Finished shutdown (after 15 seconds).")
+ // Allows everything to finish in subprocesses
+ time.Sleep(time.Duration(15) * time.Second)
os.Exit(3)
}
@@ -865,6 +862,10 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env []
log.Printf("[WARNING] Empty self container id, continue without NetworkMode")
}
+ if cleanupEnv == "true" {
+ hostConfig.AutoRemove = true
+ }
+
config := &container.Config{
Image: image,
Env: env,
@@ -892,6 +893,7 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env []
}
log.Printf("[INFO] Container %s is created for %s", cont.ID, identifier)
+ containerIds = append(containerIds, cont.ID)
return nil
}
@@ -1537,71 +1539,16 @@ func handleExecutionResult(workflowExecution WorkflowExecution) {
// FIXME - new request here
// FIXME - clean up stopped (remove) containers with this execution id
- dockercli, err := dockerclient.NewEnvClient()
- if err != nil {
- log.Printf("Unable to create docker client: %s", err)
- shutdown(workflowExecution.ExecutionId, workflowExecution.Workflow.ID)
- }
if len(workflowExecution.Results) == len(workflowExecution.Workflow.Actions)+extra {
shutdownCheck := true
- ctx := context.Background()
for _, result := range workflowExecution.Results {
if result.Status == "EXECUTING" {
// Cleaning up executing stuff
shutdownCheck = false
- // Check status
- containers, err := dockercli.ContainerList(ctx, types.ContainerListOptions{
- All: true,
- })
- if err != nil {
- log.Printf("Failed listing containers: %s", err)
- continue
- }
-
- stopContainers := []string{}
- removeContainers := []string{}
- for _, container := range containers {
- for _, name := range container.Names {
- if !strings.Contains(name, result.Action.ID) {
- continue
- }
-
- if container.State != "running" {
- removeContainers = append(removeContainers, container.ID)
- stopContainers = append(stopContainers, container.ID)
- }
- }
- }
-
- // FIXME - add killing of apps with same execution ID too
- // FIXME - stahp
- //for _, containername := range stopContainers {
- // if err := dockercli.ContainerStop(ctx, containername, nil); err != nil {
- // log.Printf("Unable to stop container: %s", err)
- // } else {
- // log.Printf("Stopped container %s", containername)
- // }
- //}
-
- removeOptions := types.ContainerRemoveOptions{
- RemoveVolumes: true,
- Force: true,
- }
-
- _ = removeOptions
-
- // FIXME - this
- //for _, containername := range removeContainers {
- // if err := dockercli.ContainerRemove(ctx, containername, removeOptions); err != nil {
- // log.Printf("Unable to remove container: %s", err)
- // } else {
- // log.Printf("Removed container %s", containername)
- // }
- //}
-
+ // USED TO BE CONTAINER REMOVAL
// FIXME - send POST request to kill the container
- log.Printf("Should remove (POST request) stopped containers")
+ //log.Printf("Should remove (POST request) stopped containers")
//ret = requests.post("%s%s" % (self.url, stream_path), headers=headers, json=action_result)
}
}
@@ -2511,7 +2458,7 @@ func validateFinished(workflowExecution WorkflowExecution) {
if err != nil {
log.Printf("[ERROR] Failed reading body: %s", err)
} else {
- log.Printf("NEWRESP: %s", string(body))
+ log.Printf("[INFO] NEWRESP (from backend): %s", string(body))
}
}
}
@@ -2583,7 +2530,7 @@ func setWorkflowExecution(ctx context.Context, workflowExecution WorkflowExecuti
}
// GetLocalIP returns the non loopback local IP of the host
-func GetLocalIP() string {
+func getLocalIP() string {
addrs, err := net.InterfaceAddrs()
if err != nil {
return ""
@@ -2599,22 +2546,46 @@ func GetLocalIP() string {
return ""
}
-func webserverSetup() {
- hostname := GetLocalIP()
+func getAvailablePort() (net.Listener, error) {
+ listener, err := net.Listen("tcp", ":0")
+ if err != nil {
+ log.Printf("[WARNING] Failed to assign port by default. Defaulting to 5001")
+ //return ":5001"
+ return nil, err
+ }
- log.Printf("\nStarting webserver on port 5001 with hostname: %s\n", hostname)
- log.Printf("OLD HOSTNAME: %s", appCallbackUrl)
- appCallbackUrl = fmt.Sprintf("http://%s:5001", hostname)
- log.Printf("NEW HOSTNAME: %s", appCallbackUrl)
+ return listener, nil
+ //return fmt.Sprintf(":%d", port)
}
-func runWebserver() {
+func webserverSetup(workflowExecution WorkflowExecution) net.Listener {
+ hostname := getLocalIP()
+
+ // FIXME: This MAY not work because of speed between first
+ // container being launched and port being assigned to webserver
+ listener, err := getAvailablePort()
+ if err != nil {
+ log.Printf("Failed to created listener: %s", err)
+ shutdown(workflowExecution.ExecutionId, workflowExecution.Workflow.ID)
+ }
+ port := listener.Addr().(*net.TCPAddr).Port
+
+ log.Printf("\n\nStarting webserver on port %d with hostname: %s\n\n", port, hostname)
+ log.Printf("OLD HOSTNAME: %s", appCallbackUrl)
+ appCallbackUrl = fmt.Sprintf("http://%s:%d", hostname, port)
+ log.Printf("NEW HOSTNAME: %s", appCallbackUrl)
+
+ return listener
+}
+
+func runWebserver(listener net.Listener) {
r := mux.NewRouter()
r.HandleFunc("/api/v1/streams", handleWorkflowQueue).Methods("POST")
r.HandleFunc("/api/v1/streams/results", handleGetStreamResults).Methods("POST", "OPTIONS")
http.Handle("/", r)
- log.Fatal(http.ListenAndServe(":5001", nil))
+ //log.Fatal(http.ListenAndServe(port, nil))
+ log.Fatal(http.Serve(listener, nil))
}
// Initial loop etc
@@ -2735,8 +2706,8 @@ func main() {
}
log.Printf("Environments: %s. 1 = webserver, 0 or >1 = default", environments)
- if len(environments) == 1 {
- webserverSetup()
+ if len(environments) == 1 { //&& len(workflowExecution.Actions)+len(workflowExecution.Triggers) > 1 {
+ listener := webserverSetup(workflowExecution)
err := executionInit(workflowExecution)
if err != nil {
log.Printf("[INFO] Workflow setup failed: %s", workflowExecution.ExecutionId, err)
@@ -2748,7 +2719,7 @@ func main() {
handleExecutionResult(workflowExecution)
}()
- runWebserver()
+ runWebserver(listener)
//log.Printf("Before wait")
//wg := sync.WaitGroup{}
//wg.Add(1)