@@ -28,12 +28,12 @@ SHUFFLE_APP_HOTLOAD_FOLDER=./shuffle-apps
|
||||
SHUFFLE_APP_HOTLOAD_LOCATION=./shuffle-apps
|
||||
SHUFFLE_FILE_LOCATION=./shuffle-files
|
||||
|
||||
# Encryption modifier. This HAS to be set to encrypt any authentication being used in Shuffle. This is put together with other relevant values to ensure multiple parts are needed to decrypt.
|
||||
# Encryption modifier. This HAS to be set to encrypt any authentication being used in Shuffle. This is put together with other relevant values to ensure multiple parts are needed to decrypt.
|
||||
# If this key is lost or changed, you will have to reauthenticate all apps.
|
||||
SHUFFLE_ENCRYPTION_MODIFIER=
|
||||
|
||||
# Other configs
|
||||
BASE_URL=http://shuffle-backend:5001
|
||||
BASE_URL=http://shuffle-backend:5001
|
||||
SSO_REDIRECT_URL=http://localhost:3001
|
||||
BACKEND_HOSTNAME=shuffle-backend
|
||||
BACKEND_PORT=5001
|
||||
@@ -46,7 +46,7 @@ OUTER_HOSTNAME=shuffle-backend
|
||||
DB_LOCATION=./shuffle-database
|
||||
DOCKER_API_VERSION=1.40
|
||||
|
||||
# Orborus/Proxy configurations
|
||||
# Orborus/Proxy configurations
|
||||
HTTP_PROXY=
|
||||
HTTPS_PROXY=
|
||||
SHUFFLE_PASS_WORKER_PROXY=TRUE
|
||||
@@ -58,10 +58,10 @@ TZ=Europe/Amsterdam
|
||||
# Used to FIND the containername. cgroup v2: issue 501
|
||||
ORBORUS_CONTAINER_NAME=
|
||||
# Used for setting up a startup delay for Orborus
|
||||
SHUFFLE_ORBORUS_STARTUP_DELAY=
|
||||
SHUFFLE_SKIPSSL_VERIFY=true
|
||||
SHUFFLE_ORBORUS_STARTUP_DELAY=
|
||||
SHUFFLE_SKIPSSL_VERIFY=true
|
||||
# Used for controlling if the environment should run in kubernetes or not
|
||||
IS_KUBERNETES=false
|
||||
IS_KUBERNETES=false
|
||||
|
||||
#SHUFFLE_BASE_IMAGE_NAME=shuffle
|
||||
#SHUFFLE_BASE_IMAGE_REGISTRY=ghcr.io
|
||||
@@ -72,9 +72,9 @@ SHUFFLE_BASE_IMAGE_REPOSITORY=frikky
|
||||
# where they don't want to update http, subflow and shuffle tools again
|
||||
SHUFFLE_USE_GCHR_OVERRIDE_FOR_AUTODEPLOY=true
|
||||
|
||||
# The eth0 interface inside a container corresponds
|
||||
# to the virtual Ethernet interface that connects
|
||||
# the container to the docker0
|
||||
# The eth0 interface inside a container corresponds
|
||||
# to the virtual Ethernet interface that connects
|
||||
# the container to the docker0
|
||||
SHUFFLE_SWARM_BRIDGE_DEFAULT_INTERFACE=eth0
|
||||
# 1500 by default
|
||||
SHUFFLE_SWARM_BRIDGE_DEFAULT_MTU=1500
|
||||
@@ -84,7 +84,7 @@ SHUFFLE_MEMCACHED=
|
||||
SHUFFLE_CONTAINER_AUTO_CLEANUP=true
|
||||
# The amount of concurrent executions Orborus can handle. This is a soft limit, but it's recommended to keep it low.
|
||||
SHUFFLE_ORBORUS_EXECUTION_CONCURRENCY=5
|
||||
SHUFFLE_HEALTHCHECK_DISABLED=false
|
||||
SHUFFLE_HEALTHCHECK_DISABLED=false
|
||||
SHUFFLE_ELASTIC=true
|
||||
SHUFFLE_LOGS_DISABLED=true
|
||||
SHUFFLE_CHAT_DISABLED=false
|
||||
@@ -112,8 +112,9 @@ SHUFFLE_OPENSEARCH_USERNAME="admin"
|
||||
SHUFFLE_OPENSEARCH_PASSWORD="StrongShufflePassword321!" # In use for the first time setup of OpenSearch + backend of Shuffle
|
||||
OPENSEARCH_INITIAL_ADMIN_PASSWORD="StrongShufflePassword321!" # In use for the first time setup of OpenSearch
|
||||
|
||||
#Tenzir related
|
||||
#Tenzir related
|
||||
SHUFFLE_TENZIR_URL=
|
||||
|
||||
SHUFFLE_PROTECTED_CLEANUP_DISABLED=true
|
||||
|
||||
DEBUG_MODE=false
|
||||
|
||||
+124
-121
@@ -15,142 +15,145 @@ jobs:
|
||||
os: [ubuntu-latest]
|
||||
architecture: [x64, arm64]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Install Docker Compose
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y docker-compose
|
||||
docker-compose --version
|
||||
- name: Shai-Hulud 2.0 Detector
|
||||
uses: gensecaihq/Shai-Hulud-2.0-Detector@v1.0.0
|
||||
|
||||
- name: Set up opensearch directory
|
||||
run: chmod -R 777 shuffle-database
|
||||
- name: Install Docker Compose
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y docker-compose
|
||||
docker-compose --version
|
||||
|
||||
- name: Build the stack
|
||||
run: docker compose up -d
|
||||
- name: Set up opensearch directory
|
||||
run: chmod -R 777 shuffle-database
|
||||
|
||||
- name: Wait for 30 seconds
|
||||
run: sleep 30
|
||||
- name: Build the stack
|
||||
run: docker compose up -d
|
||||
|
||||
- name: Check for restarting containers in a loop and fixing perms again
|
||||
run: |
|
||||
# echo "Changing permissions on shuffle-database directory again"
|
||||
# chmod -R 777 shuffle-database
|
||||
- name: Wait for 30 seconds
|
||||
run: sleep 30
|
||||
|
||||
ATTEMPTS=30 # Total time = ATTEMPTS * 5 seconds = 30 seconds
|
||||
for i in $(seq 1 $ATTEMPTS); do
|
||||
RESTARTING_CONTAINERS=$(docker ps --filter "status=restarting" --format "{{.Names}}")
|
||||
if [ -n "$RESTARTING_CONTAINERS" ]; then
|
||||
echo "The following containers are restarting:"
|
||||
echo "$RESTARTING_CONTAINERS"
|
||||
- name: Check for restarting containers in a loop and fixing perms again
|
||||
run: |
|
||||
# echo "Changing permissions on shuffle-database directory again"
|
||||
# chmod -R 777 shuffle-database
|
||||
|
||||
ATTEMPTS=30 # Total time = ATTEMPTS * 5 seconds = 30 seconds
|
||||
for i in $(seq 1 $ATTEMPTS); do
|
||||
RESTARTING_CONTAINERS=$(docker ps --filter "status=restarting" --format "{{.Names}}")
|
||||
if [ -n "$RESTARTING_CONTAINERS" ]; then
|
||||
echo "The following containers are restarting:"
|
||||
echo "$RESTARTING_CONTAINERS"
|
||||
exit 1
|
||||
fi
|
||||
echo "No containers are restarting. Attempt $i/$ATTEMPTS."
|
||||
sleep 1
|
||||
done
|
||||
echo "No containers were found in a restarting state after $ATTEMPTS checks."
|
||||
|
||||
- name: Check if the response from the frontend contains the word "Shuffle"
|
||||
run: |
|
||||
RESPONSE=$(curl -s http://localhost:3001)
|
||||
if echo "$RESPONSE" | grep -q "Shuffle"; then
|
||||
echo "The word 'Shuffle' was found in the response."
|
||||
else
|
||||
echo "The word 'Shuffle' was not found in the response."
|
||||
exit 1
|
||||
fi
|
||||
echo "No containers are restarting. Attempt $i/$ATTEMPTS."
|
||||
sleep 1
|
||||
done
|
||||
echo "No containers were found in a restarting state after $ATTEMPTS checks."
|
||||
- name: Register a user and check the status code
|
||||
run: |
|
||||
MAX_RETRIES=30
|
||||
RETRY_INTERVAL=10
|
||||
CONTAINER_NAME="shuffle-backend"
|
||||
|
||||
- name: Check if the response from the frontend contains the word "Shuffle"
|
||||
run: |
|
||||
RESPONSE=$(curl -s http://localhost:3001)
|
||||
if echo "$RESPONSE" | grep -q "Shuffle"; then
|
||||
echo "The word 'Shuffle' was found in the response."
|
||||
else
|
||||
echo "The word 'Shuffle' was not found in the response."
|
||||
for (( i=1; i<=$MAX_RETRIES; i++ ))
|
||||
do
|
||||
STATUS_CODE=$(curl -s -o /dev/null -w "%{http_code}" 'http://localhost:3001/api/v1/register' \
|
||||
-H 'Accept: */*' \
|
||||
-H 'Accept-Language: en-US,en;q=0.9' \
|
||||
-H 'Connection: keep-alive' \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data-raw '{"username":"demo@demo.io","password":"supercoolpassword"}')
|
||||
|
||||
if [ "$STATUS_CODE" -eq 200 ]; then
|
||||
echo "User registration was successful with status code 200."
|
||||
exit 0
|
||||
elif [ "$STATUS_CODE" -ne 502 ]; then
|
||||
echo "User registration failed with status code $STATUS_CODE."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Received status code $STATUS_CODE. Retrying in $RETRY_INTERVAL seconds... ($i/$MAX_RETRIES)"
|
||||
echo "Fetching last 30 lines of logs from container $CONTAINER_NAME..."
|
||||
|
||||
logs_output=$(docker logs --tail 30 "$CONTAINER_NAME")
|
||||
echo "$logs_output"
|
||||
|
||||
echo "Fetching last 30 lines of logs from container shuffle-opensearch..."
|
||||
|
||||
opensearch_logs=$(docker logs --tail 30 shuffle-opensearch)
|
||||
echo "$opensearch_logs"
|
||||
|
||||
sleep $RETRY_INTERVAL
|
||||
done
|
||||
|
||||
echo "User registration failed after $MAX_RETRIES attempts."
|
||||
exit 1
|
||||
fi
|
||||
- name: Register a user and check the status code
|
||||
run: |
|
||||
MAX_RETRIES=30
|
||||
RETRY_INTERVAL=10
|
||||
CONTAINER_NAME="shuffle-backend"
|
||||
|
||||
for (( i=1; i<=$MAX_RETRIES; i++ ))
|
||||
do
|
||||
STATUS_CODE=$(curl -s -o /dev/null -w "%{http_code}" 'http://localhost:3001/api/v1/register' \
|
||||
-H 'Accept: */*' \
|
||||
-H 'Accept-Language: en-US,en;q=0.9' \
|
||||
-H 'Connection: keep-alive' \
|
||||
-H 'Content-Type: application/json' \
|
||||
--data-raw '{"username":"demo@demo.io","password":"supercoolpassword"}')
|
||||
- name: Run Selenium testing for frontend
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE/frontend
|
||||
# write some log to see the current directory
|
||||
chmod +x frontend-testing.sh
|
||||
./frontend-testing.sh
|
||||
|
||||
if [ "$STATUS_CODE" -eq 200 ]; then
|
||||
echo "User registration was successful with status code 200."
|
||||
- name: Get the API key and run a health check
|
||||
id: health_check
|
||||
run: |
|
||||
RESPONSE=$(curl -s -k -u admin:StrongShufflePassword321! 'https://localhost:9200/users/_search')
|
||||
echo "Raw Response: $RESPONSE"
|
||||
|
||||
API_KEY=$(echo "$RESPONSE" | jq -r '.hits.hits[0]._source.apikey')
|
||||
if [ -n "$API_KEY" ] && [ "$API_KEY" != "null" ]; then
|
||||
echo "Admin API key: $API_KEY"
|
||||
echo "API_KEY=$API_KEY" >> $GITHUB_ENV
|
||||
else
|
||||
echo "Failed to retrieve the API key for the admin user."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Waiting 1 minute before sending the health API request..."
|
||||
sleep 60
|
||||
|
||||
echo "Checking health API..."
|
||||
HEALTH_RESPONSE=$(curl -s 'http://localhost:3001/api/v1/health?force=true' \
|
||||
-H "Authorization: Bearer $API_KEY")
|
||||
|
||||
echo "Health API Response: $HEALTH_RESPONSE"
|
||||
|
||||
WORKFLOWS_CREATE=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.create')
|
||||
WORKFLOWS_RUN=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.run')
|
||||
WORKFLOWS_RUN_FINISHED=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.run_finished')
|
||||
WORKFLOWS_RUN_STATUS=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.run_status')
|
||||
WORKFLOWS_DELETE=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.delete')
|
||||
|
||||
echo "WORKFLOWS_CREATE: $WORKFLOWS_CREATE"
|
||||
echo "WORKFLOWS_RUN: $WORKFLOWS_RUN"
|
||||
echo "WORKFLOWS_RUN_FINISHED: $WORKFLOWS_RUN_FINISHED"
|
||||
echo "WORKFLOWS_RUN_STATUS: $WORKFLOWS_RUN_STATUS"
|
||||
echo "WORKFLOWS_DELETE: $WORKFLOWS_DELETE"
|
||||
|
||||
if [ "$WORKFLOWS_CREATE" = "true" ] && [ "$WORKFLOWS_RUN" = "true" ] && [ "$WORKFLOWS_RUN_FINISHED" = "true" ] && [ "$WORKFLOWS_RUN_STATUS" = "FINISHED" ] && [ "$WORKFLOWS_DELETE" = "true" ]; then
|
||||
echo "Health endpoint check was successful."
|
||||
exit 0
|
||||
elif [ "$STATUS_CODE" -ne 502 ]; then
|
||||
echo "User registration failed with status code $STATUS_CODE."
|
||||
else
|
||||
echo "Health check failed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Received status code $STATUS_CODE. Retrying in $RETRY_INTERVAL seconds... ($i/$MAX_RETRIES)"
|
||||
echo "Fetching last 30 lines of logs from container $CONTAINER_NAME..."
|
||||
|
||||
logs_output=$(docker logs --tail 30 "$CONTAINER_NAME")
|
||||
echo "$logs_output"
|
||||
|
||||
echo "Fetching last 30 lines of logs from container shuffle-opensearch..."
|
||||
|
||||
opensearch_logs=$(docker logs --tail 30 shuffle-opensearch)
|
||||
echo "$opensearch_logs"
|
||||
|
||||
sleep $RETRY_INTERVAL
|
||||
done
|
||||
|
||||
echo "User registration failed after $MAX_RETRIES attempts."
|
||||
exit 1
|
||||
|
||||
- name: Run Selenium testing for frontend
|
||||
run: |
|
||||
cd $GITHUB_WORKSPACE/frontend
|
||||
# write some log to see the current directory
|
||||
chmod +x frontend-testing.sh
|
||||
./frontend-testing.sh
|
||||
|
||||
- name: Get the API key and run a health check
|
||||
id: health_check
|
||||
run: |
|
||||
RESPONSE=$(curl -s -k -u admin:StrongShufflePassword321! 'https://localhost:9200/users/_search')
|
||||
echo "Raw Response: $RESPONSE"
|
||||
|
||||
API_KEY=$(echo "$RESPONSE" | jq -r '.hits.hits[0]._source.apikey')
|
||||
if [ -n "$API_KEY" ] && [ "$API_KEY" != "null" ]; then
|
||||
echo "Admin API key: $API_KEY"
|
||||
echo "API_KEY=$API_KEY" >> $GITHUB_ENV
|
||||
else
|
||||
echo "Failed to retrieve the API key for the admin user."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Waiting 1 minute before sending the health API request..."
|
||||
sleep 60
|
||||
|
||||
echo "Checking health API..."
|
||||
HEALTH_RESPONSE=$(curl -s 'http://localhost:3001/api/v1/health?force=true' \
|
||||
-H "Authorization: Bearer $API_KEY")
|
||||
|
||||
echo "Health API Response: $HEALTH_RESPONSE"
|
||||
|
||||
WORKFLOWS_CREATE=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.create')
|
||||
WORKFLOWS_RUN=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.run')
|
||||
WORKFLOWS_RUN_FINISHED=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.run_finished')
|
||||
WORKFLOWS_RUN_STATUS=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.run_status')
|
||||
WORKFLOWS_DELETE=$(echo "$HEALTH_RESPONSE" | jq -r '.workflows.delete')
|
||||
|
||||
echo "WORKFLOWS_CREATE: $WORKFLOWS_CREATE"
|
||||
echo "WORKFLOWS_RUN: $WORKFLOWS_RUN"
|
||||
echo "WORKFLOWS_RUN_FINISHED: $WORKFLOWS_RUN_FINISHED"
|
||||
echo "WORKFLOWS_RUN_STATUS: $WORKFLOWS_RUN_STATUS"
|
||||
echo "WORKFLOWS_DELETE: $WORKFLOWS_DELETE"
|
||||
|
||||
if [ "$WORKFLOWS_CREATE" = "true" ] && [ "$WORKFLOWS_RUN" = "true" ] && [ "$WORKFLOWS_RUN_FINISHED" = "true" ] && [ "$WORKFLOWS_RUN_STATUS" = "FINISHED" ] && [ "$WORKFLOWS_DELETE" = "true" ]; then
|
||||
echo "Health endpoint check was successful."
|
||||
exit 0
|
||||
else
|
||||
echo "Health check failed."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
notify:
|
||||
needs: build
|
||||
if: failure()
|
||||
@@ -163,7 +166,7 @@ jobs:
|
||||
Repository: ${{ github.repository }}
|
||||
Branch: ${{ github.ref_name }}
|
||||
Workflow URL: $WORKFLOW_URL"
|
||||
|
||||
|
||||
curl -s -X POST https://api.twilio.com/2010-04-01/Accounts/${{ secrets.TWILIO_ACCOUNT_SID }}/Messages.json \
|
||||
--data-urlencode "To=${{ secrets.TWILIO_TO_NUMBER }}" \
|
||||
--data-urlencode "From=${{ secrets.TWILIO_FROM_NUMBER }}" \
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o shufflebackend .
|
||||
|
||||
# Certificate build - gets required certs
|
||||
FROM alpine:latest as certs
|
||||
RUN apk add --update ca-certificates
|
||||
RUN apk add --update --no-scripts ca-certificates
|
||||
|
||||
# Sets up the final image
|
||||
FROM alpine:3.22.2
|
||||
|
||||
@@ -21,7 +21,6 @@ import (
|
||||
"github.com/docker/docker/api/types"
|
||||
//"github.com/docker/docker/api/types/container"
|
||||
"github.com/docker/docker/api/types/image"
|
||||
"github.com/docker/docker/client"
|
||||
newdockerclient "github.com/fsouza/go-dockerclient"
|
||||
"github.com/go-git/go-billy/v5"
|
||||
|
||||
@@ -35,12 +34,13 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"time"
|
||||
|
||||
batchv1 "k8s.io/api/batch/v1"
|
||||
corev1 "k8s.io/api/core/v1"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/client-go/kubernetes"
|
||||
"k8s.io/client-go/rest"
|
||||
"time"
|
||||
// "k8s.io/client-go/tools/clientcmd"
|
||||
// "k8s.io/client-go/util/homedir"
|
||||
)
|
||||
@@ -206,7 +206,8 @@ func fixTags(tags []string) []string {
|
||||
// Custom Docker image builder wrapper in memory
|
||||
func buildImageMemory(fs billy.Filesystem, tags []string, dockerfileFolder string, downloadIfFail bool) error {
|
||||
ctx := context.Background()
|
||||
client, err := client.NewEnvClient()
|
||||
// client, err := client.NewEnvClient()
|
||||
client, _, err := shuffle.GetDockerClient()
|
||||
defer client.Close()
|
||||
if err != nil {
|
||||
log.Printf("Unable to create docker client: %s", err)
|
||||
@@ -493,7 +494,8 @@ func buildImage(tags []string, dockerfileLocation string) error {
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
client, err := client.NewEnvClient()
|
||||
// client, err := client.NewEnvClient()
|
||||
client, _, err := shuffle.GetDockerClient()
|
||||
defer client.Close()
|
||||
if err != nil {
|
||||
log.Printf("Unable to create docker client: %s", err)
|
||||
@@ -581,7 +583,8 @@ func imageCheckBuilder(images []string) error {
|
||||
return nil
|
||||
|
||||
ctx := context.Background()
|
||||
client, err := client.NewEnvClient()
|
||||
// client, err := client.NewEnvClient()
|
||||
client, _, err := shuffle.GetDockerClient()
|
||||
if err != nil {
|
||||
log.Printf("Unable to create docker client: %s", err)
|
||||
return err
|
||||
@@ -670,7 +673,7 @@ func getDockerImage(resp http.ResponseWriter, request *http.Request) {
|
||||
}
|
||||
|
||||
//log.Printf("[DEBUG] Image to load: %s", version.Name)
|
||||
dockercli, err := client.NewEnvClient()
|
||||
dockercli, _, err := shuffle.GetDockerClient()
|
||||
if err != nil {
|
||||
log.Printf("[WARNING] Unable to create docker client: %s", err)
|
||||
resp.WriteHeader(422)
|
||||
|
||||
+27
-21
@@ -26,15 +26,15 @@ require (
|
||||
github.com/gorilla/mux v1.8.1
|
||||
github.com/h2non/filetype v1.1.3
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
github.com/shuffle/shuffle-shared v0.9.54
|
||||
github.com/shuffle/shuffle-shared v0.9.69
|
||||
github.com/shuffle/singul v0.0.20
|
||||
golang.org/x/crypto v0.40.0
|
||||
golang.org/x/crypto v0.45.0
|
||||
google.golang.org/api v0.236.0
|
||||
google.golang.org/grpc v1.72.2
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
k8s.io/api v0.33.1
|
||||
k8s.io/apimachinery v0.33.1
|
||||
k8s.io/client-go v0.33.1
|
||||
k8s.io/api v0.34.2
|
||||
k8s.io/apimachinery v0.34.2
|
||||
k8s.io/client-go v0.34.2
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -70,13 +70,13 @@ require (
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect
|
||||
github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/frikky/schemaless v0.0.24 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/frikky/schemaless v0.0.25 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-jose/go-jose/v4 v4.0.5 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
@@ -87,8 +87,7 @@ require (
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/gnostic-models v0.6.9 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/gnostic-models v0.7.0 // indirect
|
||||
github.com/google/go-github/v28 v28.1.1 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
@@ -110,9 +109,10 @@ require (
|
||||
github.com/moby/sys/userns v0.1.0 // indirect
|
||||
github.com/moby/term v0.5.2 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/openai/openai-go/v3 v3.8.1 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||
github.com/osteele/liquid v1.7.0 // indirect
|
||||
@@ -132,6 +132,10 @@ require (
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect
|
||||
github.com/tidwall/gjson v1.18.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/tidwall/sjson v1.2.5 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
github.com/zeebo/errs v1.4.0 // indirect
|
||||
@@ -145,13 +149,15 @@ require (
|
||||
go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.36.0 // indirect
|
||||
go.uber.org/atomic v1.9.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
|
||||
golang.org/x/net v0.41.0 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/oauth2 v0.30.0 // indirect
|
||||
golang.org/x/sync v0.16.0 // indirect
|
||||
golang.org/x/sys v0.34.0 // indirect
|
||||
golang.org/x/term v0.33.0 // indirect
|
||||
golang.org/x/text v0.27.0 // indirect
|
||||
golang.org/x/sync v0.18.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/term v0.37.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 // indirect
|
||||
@@ -163,10 +169,10 @@ require (
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
|
||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
|
||||
sigs.k8s.io/yaml v1.6.0 // indirect
|
||||
)
|
||||
|
||||
@@ -138,8 +138,8 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/frikky/kin-openapi v0.42.0 h1:d5Z6vnuQ6RnCCPIxZaDL+TH2ODLxT8abytOt+Zh+Kd0=
|
||||
github.com/frikky/kin-openapi v0.42.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4=
|
||||
github.com/frikky/schemaless v0.0.24 h1:1bNg96gm82yLbRlDS0haQH/96X3GlCAEuTCSasXIJZE=
|
||||
github.com/frikky/schemaless v0.0.24/go.mod h1:m9s+6gALXhA5ZERCrJw+jI2rRtTPNa8mkl4vav9sxnY=
|
||||
github.com/frikky/schemaless v0.0.25 h1:qXjrKT54LWl3tzDFIUmevZ86F7FiusPpQKfa40xxNl8=
|
||||
github.com/frikky/schemaless v0.0.25/go.mod h1:m9s+6gALXhA5ZERCrJw+jI2rRtTPNa8mkl4vav9sxnY=
|
||||
github.com/fsouza/go-dockerclient v1.12.1 h1:FMoLq+Zhv9Oz/rFmu6JWkImfr6CBgZOPcL+bHW4gS0o=
|
||||
github.com/fsouza/go-dockerclient v1.12.1/go.mod h1:OqsgJJcpCwqyM3JED7TdfM9QVWS5O7jSYwXxYKmOooY=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||
@@ -344,8 +344,8 @@ github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/shuffle/opensearch-go/v4 v4.0.0 h1:Mh85CD1MwOgXiFFYlzS1llnvdqL3CztRdR1ZT/SLIjU=
|
||||
github.com/shuffle/opensearch-go/v4 v4.0.0/go.mod h1:gVLZKQE5khQWMb68XBtgKrhu78oLGL2zHwAGnFMDwC0=
|
||||
github.com/shuffle/shuffle-shared v0.9.53 h1:ykziiRA82FZX0YiuErSmFLQMxvAFi6yUajgRjd3J5xE=
|
||||
github.com/shuffle/shuffle-shared v0.9.53/go.mod h1:g03P/ZM8pUwBbdxW0l9A1siMMuemEQshgDpO9hF23eE=
|
||||
github.com/shuffle/shuffle-shared v0.9.60 h1:8NiovcsSsVX8i0UHmbFBANjUQ/u+4PDDpJXg9JfMAUo=
|
||||
github.com/shuffle/shuffle-shared v0.9.60/go.mod h1:vK6t1WY5Nfg5vOAk6taT788jIGKs6/4iN1d8Argyn4o=
|
||||
github.com/shuffle/singul v0.0.20 h1:Lz+K4l2GJQ5W6o8ePr/2qlIcGuZuMC7W8Wj/JFw1XDI=
|
||||
github.com/shuffle/singul v0.0.20/go.mod h1:qY3ZmwaNwmRApLWMTqZaNEMXO6X8bjr/zY44xDU62hQ=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
|
||||
@@ -3,7 +3,7 @@ package main
|
||||
import (
|
||||
uuid "github.com/satori/go.uuid"
|
||||
"github.com/shuffle/shuffle-shared"
|
||||
"github.com/shuffle/singul/pkg"
|
||||
singul "github.com/shuffle/singul/pkg"
|
||||
|
||||
"net/http/pprof"
|
||||
|
||||
@@ -44,11 +44,12 @@ import (
|
||||
"github.com/go-git/go-git/v5/storage/memory"
|
||||
|
||||
// Random
|
||||
"sort"
|
||||
|
||||
xj "github.com/basgys/goxml2json"
|
||||
newscheduler "github.com/carlescere/scheduler"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"gopkg.in/yaml.v3"
|
||||
"sort"
|
||||
|
||||
// Web
|
||||
"github.com/gorilla/mux"
|
||||
@@ -1312,7 +1313,9 @@ func checkAdminLogin(resp http.ResponseWriter, request *http.Request) {
|
||||
// Should run calculations
|
||||
if len(org.SSOConfig.OpenIdAuthorization) > 0 {
|
||||
baseSSOUrl = shuffle.GetOpenIdUrl(request, *org)
|
||||
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed getting OpenID URL for org %s: %s", org.Name, err)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
@@ -5433,7 +5436,7 @@ func initHandlers() {
|
||||
r.HandleFunc("/api/v1/users/getsettings", shuffle.HandleSettings).Methods("GET", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/users/getusers", shuffle.HandleGetUsers).Methods("GET", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/users/updateuser", shuffle.HandleUpdateUser).Methods("PUT", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/users/{userID}/remove", shuffle.HandleDeleteUsersAccount).Methods("DELETE", "OPTIONS")
|
||||
// r.HandleFunc("/api/v1/users/{userID}/remove", shuffle.HandleDeleteUsersAccount).Methods("DELETE", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/users/{user}", shuffle.DeleteUser).Methods("DELETE", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/users/passwordchange", shuffle.HandlePasswordChange).Methods("POST", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/users/{key}/get2fa", shuffle.HandleGet2fa).Methods("GET", "OPTIONS")
|
||||
@@ -5657,7 +5660,7 @@ func initHandlers() {
|
||||
|
||||
// Docker orborus specific - downloads an image
|
||||
r.HandleFunc("/api/v1/get_docker_image", getDockerImage).Methods("POST", "GET", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/login_sso", shuffle.HandleSSO).Methods("GET", "POST", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/login_sso", shuffle.HandleSAML).Methods("GET", "POST", "OPTIONS")
|
||||
r.HandleFunc("/api/v1/login_openid", shuffle.HandleOpenId).Methods("GET", "POST", "OPTIONS")
|
||||
|
||||
// Important for email, IDS etc. Create this by:
|
||||
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
|
||||
//"math/rand"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@@ -21,7 +22,6 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/docker/docker/api/types/image"
|
||||
dockerclient "github.com/docker/docker/client"
|
||||
"github.com/h2non/filetype"
|
||||
uuid "github.com/satori/go.uuid"
|
||||
|
||||
@@ -2997,6 +2997,8 @@ func IterateAppGithubFolders(ctx context.Context, fs billy.Filesystem, dir []os.
|
||||
"email",
|
||||
"shuffle-ai",
|
||||
"shuffle-subflow",
|
||||
"yara",
|
||||
"sigma",
|
||||
}
|
||||
|
||||
// It's here to prevent getting them in every iteration
|
||||
@@ -3393,6 +3395,8 @@ func IterateAppGithubFolders(ctx context.Context, fs billy.Filesystem, dir []os.
|
||||
fmt.Sprintf("/apps"),
|
||||
orgId,
|
||||
false,
|
||||
"HIGH",
|
||||
"APP_BUID",
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3527,7 +3531,8 @@ func LoadSpecificApps(resp http.ResponseWriter, request *http.Request) {
|
||||
|
||||
// As it's not even Docker
|
||||
if tmpBody.ForceUpdate {
|
||||
dockercli, err := dockerclient.NewEnvClient()
|
||||
// dockercli, err := dockerclient.NewEnvClient()
|
||||
dockercli, _, err := shuffle.GetDockerClient()
|
||||
if err == nil {
|
||||
|
||||
appSdk := os.Getenv("SHUFFLE_APP_SDK_VERSION")
|
||||
|
||||
+4
-4
@@ -50,7 +50,7 @@ services:
|
||||
- HTTPS_PROXY=${HTTPS_PROXY}
|
||||
- SHUFFLE_PASS_WORKER_PROXY=${SHUFFLE_PASS_WORKER_PROXY}
|
||||
- SHUFFLE_PASS_APP_PROXY=${SHUFFLE_PASS_APP_PROXY}
|
||||
- SHUFFLE_APP_REPLICAS=${SHUFFLE_APP_REPLICAS}
|
||||
# - SHUFFLE_APP_REPLICAS=${SHUFFLE_APP_REPLICAS}
|
||||
- SHUFFLE_STATS_DISABLED=true
|
||||
- SHUFFLE_LOGS_DISABLED=true
|
||||
- SHUFFLE_SWARM_CONFIG=run
|
||||
@@ -80,7 +80,7 @@ services:
|
||||
soft: -1
|
||||
hard: -1
|
||||
nofile:
|
||||
soft: 65536
|
||||
soft: 65536
|
||||
hard: 65536
|
||||
volumes:
|
||||
- shuffle-database:/usr/share/opensearch/data:z
|
||||
@@ -127,7 +127,7 @@ services:
|
||||
|
||||
#docker-socket-proxy:
|
||||
# image: tecnativa/docker-socket-proxy
|
||||
# container_name: docker-socket-proxy
|
||||
# container_name: docker-socket-proxy
|
||||
# hostname: docker-socket-proxy
|
||||
# privileged: true
|
||||
# environment:
|
||||
@@ -167,4 +167,4 @@ networks:
|
||||
driver: bridge
|
||||
# driver_opts:
|
||||
# com.docker.network.driver.mtu: 1460
|
||||
# uncomment to set MTU for swarm mode. MTU should be whatever is your host's preferred MTU is: https://shuffler.io/docs/troubleshooting#TLS_timeout_error/Timeout_Errors/EOF_Errors
|
||||
# uncomment to set MTU for swarm mode. MTU should be whatever is your host's preferred MTU is: https://shuffler.io/docs/troubleshooting#TLS_timeout_error/Timeout_Errors/EOF_Errors
|
||||
|
||||
@@ -26,10 +26,13 @@
|
||||
"cpx": "^1.5.0",
|
||||
"create-react-app": "^5.0.1",
|
||||
"cytoscape": "^3.29.2",
|
||||
"cytoscape-bubblesets": "^4.1.0",
|
||||
"cytoscape-clipboard": "^2.2.1",
|
||||
"cytoscape-cose-bilkent": "^4.1.0",
|
||||
"cytoscape-cxtmenu": "^3.4.0",
|
||||
"cytoscape-edgehandles": "^3.5.1",
|
||||
"cytoscape-grid-guide": "~2.3.3",
|
||||
"cytoscape-layers": "^3.1.0",
|
||||
"cytoscape-node-html-label": "^1.2.2",
|
||||
"cytoscape-panzoom": "^2.5.3",
|
||||
"cytoscape-undo-redo": "^1.3.3",
|
||||
|
||||
@@ -127,6 +127,8 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
|
||||
setCurrentSelectedTheme(userdata?.theme);
|
||||
}
|
||||
}, [userdata]);
|
||||
|
||||
|
||||
|
||||
const CustomPopper = (props) => {
|
||||
return (
|
||||
@@ -1949,7 +1951,7 @@ const LeftSideBar = ({ userdata, serverside, globalUrl, notifications, }) => {
|
||||
</div>
|
||||
}
|
||||
|
||||
{!isCloud ? (
|
||||
{!isCloud && activeOrgData?.old_org? (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
Collapse,
|
||||
} from '@mui/material';
|
||||
import { Context } from '../context/ContextApi.jsx';
|
||||
import {getTheme} from '../theme.jsx';
|
||||
import { getTheme } from '../theme.jsx';
|
||||
import Checkbox from '@mui/material/Checkbox';
|
||||
import { isMobile } from "react-device-detect"
|
||||
import { NestedMenuItem } from "mui-nested-menu"
|
||||
@@ -27,7 +27,7 @@ import { SetJsonDotnotation } from "../views/AngularWorkflow.jsx";
|
||||
import Draggable from "react-draggable";
|
||||
|
||||
import {
|
||||
Storage as StorageIcon,
|
||||
Storage as StorageIcon,
|
||||
FullscreenExit as FullscreenExitIcon,
|
||||
Extension as ExtensionIcon,
|
||||
Apps as AppsIcon,
|
||||
@@ -46,7 +46,7 @@ import {
|
||||
DragIndicator as DragIndicatorIcon,
|
||||
RestartAlt as RestartAltIcon,
|
||||
ArrowForward as ArrowForwardIcon,
|
||||
KeyboardReturn as KeyboardReturnIcon,
|
||||
KeyboardReturn as KeyboardReturnIcon,
|
||||
FormatIndentIncrease as FormatIndentIncreaseIcon,
|
||||
Fullscreen as FullscreenIcon,
|
||||
} from '@mui/icons-material';
|
||||
@@ -153,7 +153,7 @@ const CodeEditor = (props) => {
|
||||
if (!content || typeof content !== 'string' || content.trim().length === 0) {
|
||||
return content;
|
||||
}
|
||||
|
||||
|
||||
try {
|
||||
// Check if content looks like JSON (starts with { or [)
|
||||
const trimmedContent = content.trim();
|
||||
@@ -165,7 +165,7 @@ const CodeEditor = (props) => {
|
||||
return IndentJsonLikeString(content, 2);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Return original content if it doesn't look like JSON
|
||||
return content;
|
||||
} catch (error) {
|
||||
@@ -179,7 +179,7 @@ const CodeEditor = (props) => {
|
||||
const [localcodedata, setlocalcodedata] = React.useState(codedata === undefined || codedata === null || codedata.length === 0 ? "" : codedata);
|
||||
|
||||
// const {codelang, setcodelang} = props
|
||||
const {themeMode, supportEmail} = useContext(Context)
|
||||
const { themeMode, supportEmail } = useContext(Context)
|
||||
const theme = getTheme(themeMode)
|
||||
|
||||
const [validation, setValidation] = React.useState(false);
|
||||
@@ -370,9 +370,9 @@ const CodeEditor = (props) => {
|
||||
setMainVariables(tmpVariables)
|
||||
}
|
||||
|
||||
const handleKeyDown = (event) => {
|
||||
if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {
|
||||
event.preventDefault()
|
||||
const handleKeyDown = (event) => {
|
||||
if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {
|
||||
event.preventDefault()
|
||||
const tryItButton = document.getElementById("try-it-button")
|
||||
if (tryItButton !== undefined && tryItButton !== null) {
|
||||
tryItButton.click()
|
||||
@@ -382,7 +382,7 @@ const CodeEditor = (props) => {
|
||||
|
||||
// Remove the original useEffect for actionlist since we'll update on action/trigger changes
|
||||
useEffect(() => {
|
||||
document.addEventListener("keydown", handleKeyDown)
|
||||
document.addEventListener("keydown", handleKeyDown)
|
||||
updateAvailableVariables(actionlist)
|
||||
}, [])
|
||||
|
||||
@@ -972,7 +972,11 @@ const CodeEditor = (props) => {
|
||||
// Resolve the child paths/variables first,
|
||||
// So that "$a.b.c" is handled before "$a.b" and then "$a"
|
||||
// Replace "$parent.child" before "$parent" so "$parent.child" doesn't become "parentValue.child" (which can't be matched later).
|
||||
found.sort((a, b) => b.length - a.length)
|
||||
console.log("FOUND PROD BEFORE SORT:", found)
|
||||
found.sort((a, b) => {
|
||||
return Number(b.length) - Number(a.length)
|
||||
})
|
||||
console.log("FOUND PROD AFTER SORT:", found)
|
||||
|
||||
//console.log("FOUND: ", found)
|
||||
try {
|
||||
@@ -983,19 +987,19 @@ const CodeEditor = (props) => {
|
||||
|
||||
var valuefound = false
|
||||
|
||||
// First check if it's a workflow variable
|
||||
if (actionlist !== undefined && actionlist.length > 0) {
|
||||
const workflowVar = actionlist?.find(item =>
|
||||
item.type === "workflow_variable" &&
|
||||
// First check if it's a workflow variable
|
||||
if (actionlist !== undefined && actionlist.length > 0) {
|
||||
const workflowVar = actionlist?.find(item =>
|
||||
item.type === "workflow_variable" &&
|
||||
`$${item.autocomplete.toLowerCase()}` === fixedVariable.toLowerCase()
|
||||
)
|
||||
|
||||
|
||||
if (workflowVar && workflowVar.example) {
|
||||
valuefound = true
|
||||
try {
|
||||
// Try to parse the example value if it's stored as a JSON string
|
||||
if (typeof workflowVar.example === "string" &&
|
||||
(workflowVar.example.startsWith("[") || workflowVar.example.startsWith("{"))) {
|
||||
if (typeof workflowVar.example === "string" &&
|
||||
(workflowVar.example.startsWith("[") || workflowVar.example.startsWith("{"))) {
|
||||
const parsedExample = JSON.parse(workflowVar.example)
|
||||
input = input.replace(found[i], JSON.stringify(parsedExample), -1)
|
||||
} else {
|
||||
@@ -1029,7 +1033,7 @@ const CodeEditor = (props) => {
|
||||
foundlocation = j
|
||||
break
|
||||
}
|
||||
|
||||
|
||||
// FIXME: There is something wrong here with:
|
||||
// $variable.#
|
||||
// vs
|
||||
@@ -1069,7 +1073,7 @@ const CodeEditor = (props) => {
|
||||
const replacedSlice = input.slice(foundlocation, input.length).replace(fixedVariable, newvalue, 1)
|
||||
input = input.slice(0, foundlocation) + replacedSlice
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log("Replace error: ", e)
|
||||
}
|
||||
@@ -1082,26 +1086,26 @@ const CodeEditor = (props) => {
|
||||
var shouldbreak = false
|
||||
for (var k = 0; k < actionlist.length; k++) {
|
||||
var parsedPaths = []
|
||||
|
||||
// Handle both workflow variables and regular actions
|
||||
if (actionlist[k].type === "workflow_variable") {
|
||||
// Try to parse the value if it's a string that could be JSON
|
||||
if (typeof actionlist[k].value === "string") {
|
||||
try {
|
||||
const parsedValue = JSON.parse(actionlist[k].value)
|
||||
if (typeof parsedValue === "object") {
|
||||
parsedPaths = GetParsedPaths(parsedValue, "");
|
||||
}
|
||||
} catch (e) {
|
||||
// Not valid JSON, use the value directly
|
||||
parsedPaths = GetParsedPaths(actionlist[k].value, "");
|
||||
|
||||
// Handle both workflow variables and regular actions
|
||||
if (actionlist[k].type === "workflow_variable") {
|
||||
// Try to parse the value if it's a string that could be JSON
|
||||
if (typeof actionlist[k].value === "string") {
|
||||
try {
|
||||
const parsedValue = JSON.parse(actionlist[k].value)
|
||||
if (typeof parsedValue === "object") {
|
||||
parsedPaths = GetParsedPaths(parsedValue, "");
|
||||
}
|
||||
} else if (typeof actionlist[k].value === "object") {
|
||||
} catch (e) {
|
||||
// Not valid JSON, use the value directly
|
||||
parsedPaths = GetParsedPaths(actionlist[k].value, "");
|
||||
}
|
||||
} else if (typeof actionlist[k].example === "object") {
|
||||
parsedPaths = GetParsedPaths(actionlist[k].example, "");
|
||||
}
|
||||
} else if (typeof actionlist[k].value === "object") {
|
||||
parsedPaths = GetParsedPaths(actionlist[k].value, "");
|
||||
}
|
||||
} else if (typeof actionlist[k].example === "object") {
|
||||
parsedPaths = GetParsedPaths(actionlist[k].example, "");
|
||||
}
|
||||
|
||||
for (var key in parsedPaths) {
|
||||
const fullpath = "$" + actionlist[k].autocomplete.toLowerCase() + parsedPaths[key].autocomplete.toLowerCase()
|
||||
@@ -1115,22 +1119,22 @@ const CodeEditor = (props) => {
|
||||
|
||||
var new_input = ""
|
||||
try {
|
||||
const sourceData = actionlist[k].type === "workflow_variable" ?
|
||||
(() => {
|
||||
// Try to parse the value if it's a JSON string
|
||||
if (typeof actionlist[k].value === "string") {
|
||||
try {
|
||||
return JSON.parse(actionlist[k].value);
|
||||
} catch (e) {
|
||||
// If parsing fails, return the original string value
|
||||
return actionlist[k].value;
|
||||
const sourceData = actionlist[k].type === "workflow_variable" ?
|
||||
(() => {
|
||||
// Try to parse the value if it's a JSON string
|
||||
if (typeof actionlist[k].value === "string") {
|
||||
try {
|
||||
return JSON.parse(actionlist[k].value);
|
||||
} catch (e) {
|
||||
// If parsing fails, return the original string value
|
||||
return actionlist[k].value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return actionlist[k].value;
|
||||
})() :
|
||||
actionlist[k].example;
|
||||
|
||||
new_input = FindJsonPath(fullpath, sourceData)
|
||||
return actionlist[k].value;
|
||||
})() :
|
||||
actionlist[k].example;
|
||||
|
||||
new_input = FindJsonPath(fullpath, sourceData)
|
||||
} catch (e) {
|
||||
console.log("ERR IN INPUT: ", e)
|
||||
}
|
||||
@@ -1437,85 +1441,85 @@ const CodeEditor = (props) => {
|
||||
|
||||
return (
|
||||
<Draggable
|
||||
style={{
|
||||
position: "absolute",
|
||||
zIndex: 15000,
|
||||
}}
|
||||
onDrag={(e) => {
|
||||
e.preventDefault()
|
||||
// Check if inside div.ace_content
|
||||
if (e.srcElement.className === "ace_content") {
|
||||
// Input on the correct line. Each line is:
|
||||
// Show some tooltip at mouse cursor that shows "Insert Action"
|
||||
style={{
|
||||
position: "absolute",
|
||||
zIndex: 15000,
|
||||
}}
|
||||
onDrag={(e) => {
|
||||
e.preventDefault()
|
||||
// Check if inside div.ace_content
|
||||
if (e.srcElement.className === "ace_content") {
|
||||
// Input on the correct line. Each line is:
|
||||
// Show some tooltip at mouse cursor that shows "Insert Action"
|
||||
|
||||
// Append the text to the DOM
|
||||
} else {
|
||||
//console.log("PAGEX: ", e.pageX, e.pageY)
|
||||
//console.log("OffsetX: ", e.offsetX, e.offsetY)
|
||||
//console.log("E: ", e)
|
||||
}
|
||||
// Append the text to the DOM
|
||||
} else {
|
||||
//console.log("PAGEX: ", e.pageX, e.pageY)
|
||||
//console.log("OffsetX: ", e.offsetX, e.offsetY)
|
||||
//console.log("E: ", e)
|
||||
}
|
||||
|
||||
if (!dragging) {
|
||||
setDragging(true)
|
||||
}
|
||||
}}
|
||||
onStop={(e) => {
|
||||
if (e.srcElement.className === "ace_content") {
|
||||
console.log("DRAG STOP IN CONTENT!", e.srcElement.className)
|
||||
if (!dragging) {
|
||||
setDragging(true)
|
||||
}
|
||||
}}
|
||||
onStop={(e) => {
|
||||
if (e.srcElement.className === "ace_content") {
|
||||
console.log("DRAG STOP IN CONTENT!", e.srcElement.className)
|
||||
|
||||
const usedposition = e.offsetY
|
||||
if (usedposition === undefined || usedposition === null) {
|
||||
toast.info(`Error: LayerY is undefined or null. Please contact ${supportEmail}`)
|
||||
return
|
||||
}
|
||||
const usedposition = e.offsetY
|
||||
if (usedposition === undefined || usedposition === null) {
|
||||
toast.info(`Error: LayerY is undefined or null. Please contact ${supportEmail}`)
|
||||
return
|
||||
}
|
||||
|
||||
if (usedposition === 0) {
|
||||
usedposition = 1
|
||||
}
|
||||
if (usedposition === 0) {
|
||||
usedposition = 1
|
||||
}
|
||||
|
||||
const lineheight = 15
|
||||
const codedatasplit = localcodedata.split('\n')
|
||||
if (codedatasplit === undefined || codedatasplit === null || codedatasplit.length === 0) {
|
||||
return
|
||||
}
|
||||
const lineheight = 15
|
||||
const codedatasplit = localcodedata.split('\n')
|
||||
if (codedatasplit === undefined || codedatasplit === null || codedatasplit.length === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
// Int
|
||||
const lineposition = parseInt(usedposition/lineheight)
|
||||
// Int
|
||||
const lineposition = parseInt(usedposition / lineheight)
|
||||
|
||||
// Find the correct line
|
||||
if (lineposition > codedatasplit.length) {
|
||||
codedatasplit[codedatasplit.length-1] += value
|
||||
} else {
|
||||
codedatasplit[lineposition] += value
|
||||
}
|
||||
// Find the correct line
|
||||
if (lineposition > codedatasplit.length) {
|
||||
codedatasplit[codedatasplit.length - 1] += value
|
||||
} else {
|
||||
codedatasplit[lineposition] += value
|
||||
}
|
||||
|
||||
|
||||
//e.srcElement.layerY
|
||||
setlocalcodedata(codedatasplit.join('\n'))
|
||||
}
|
||||
//e.srcElement.layerY
|
||||
setlocalcodedata(codedatasplit.join('\n'))
|
||||
}
|
||||
|
||||
setDragging(false)
|
||||
}}
|
||||
dragging={dragging}
|
||||
position={{
|
||||
x: 0,
|
||||
y: 0,
|
||||
}}
|
||||
onMouseHover={() => {
|
||||
setHovering(true)
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
setHovering(false)
|
||||
}}
|
||||
setDragging(false)
|
||||
}}
|
||||
dragging={dragging}
|
||||
position={{
|
||||
x: 0,
|
||||
y: 0,
|
||||
}}
|
||||
onMouseHover={() => {
|
||||
setHovering(true)
|
||||
}}
|
||||
onMouseLeave={() => {
|
||||
setHovering(false)
|
||||
}}
|
||||
>
|
||||
<div
|
||||
<div
|
||||
style={{
|
||||
cursor: dragging ? "grabbing" : "grab",
|
||||
minWidth: 100,
|
||||
border: "1px solid rgba(255,255,255,0.5)",
|
||||
borderRadius: theme.palette.borderRadius/2,
|
||||
marginRight: 10,
|
||||
padding: 5,
|
||||
cursor: dragging ? "grabbing" : "grab",
|
||||
minWidth: 100,
|
||||
border: "1px solid rgba(255,255,255,0.5)",
|
||||
borderRadius: theme.palette.borderRadius / 2,
|
||||
marginRight: 10,
|
||||
padding: 5,
|
||||
}}
|
||||
>
|
||||
{value}
|
||||
@@ -1525,56 +1529,56 @@ const CodeEditor = (props) => {
|
||||
}
|
||||
|
||||
const IndentJsonLikeString = (input, indentSize = 2) => {
|
||||
const indent = ' '.repeat(indentSize);
|
||||
let level = 0;
|
||||
let inString = false;
|
||||
let escapeNext = false;
|
||||
let result = '';
|
||||
const indent = ' '.repeat(indentSize);
|
||||
let level = 0;
|
||||
let inString = false;
|
||||
let escapeNext = false;
|
||||
let result = '';
|
||||
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
let char = input[i];
|
||||
for (let i = 0; i < input.length; i++) {
|
||||
let char = input[i];
|
||||
|
||||
if (escapeNext) {
|
||||
result += char;
|
||||
escapeNext = false;
|
||||
continue;
|
||||
if (escapeNext) {
|
||||
result += char;
|
||||
escapeNext = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (char === '\\') {
|
||||
escapeNext = true;
|
||||
result += char;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (char === '"') {
|
||||
inString = !inString;
|
||||
result += char;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!inString) {
|
||||
if (char === '{' || char === '[') {
|
||||
result += char + '\n' + indent.repeat(++level);
|
||||
continue;
|
||||
} else if (char === '}' || char === ']') {
|
||||
result += '\n' + indent.repeat(--level) + char;
|
||||
continue;
|
||||
} else if (char === ',') {
|
||||
result += char + '\n' + indent.repeat(level);
|
||||
continue;
|
||||
} else if (char === ':') {
|
||||
result += ': ';
|
||||
continue;
|
||||
} else if (char === ' ' || char === '\t' || char === '\n' || char === '\r') {
|
||||
// Skip whitespace characters when not in string
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
result += char;
|
||||
}
|
||||
|
||||
if (char === '\\') {
|
||||
escapeNext = true;
|
||||
result += char;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (char === '"') {
|
||||
inString = !inString;
|
||||
result += char;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!inString) {
|
||||
if (char === '{' || char === '[') {
|
||||
result += char + '\n' + indent.repeat(++level);
|
||||
continue;
|
||||
} else if (char === '}' || char === ']') {
|
||||
result += '\n' + indent.repeat(--level) + char;
|
||||
continue;
|
||||
} else if (char === ',') {
|
||||
result += char + '\n' + indent.repeat(level);
|
||||
continue;
|
||||
} else if (char === ':') {
|
||||
result += ': ';
|
||||
continue;
|
||||
} else if (char === ' ' || char === '\t' || char === '\n' || char === '\r') {
|
||||
// Skip whitespace characters when not in string
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
result += char;
|
||||
}
|
||||
|
||||
return result;
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -1585,16 +1589,16 @@ const CodeEditor = (props) => {
|
||||
|
||||
|
||||
return (
|
||||
<div style={{minHeight: 40, marginTop: 10, }}>
|
||||
<div style={{ minHeight: 40, marginTop: 10, }}>
|
||||
<div style={{
|
||||
cursor: "pointer",
|
||||
display: "flex",
|
||||
position: "relative",
|
||||
cursor: "pointer",
|
||||
display: "flex",
|
||||
position: "relative",
|
||||
}} onClick={() => {
|
||||
setExpanded(!expanded)
|
||||
}}>
|
||||
<ValueBox name={innerdata?.name} value={innerdata?.value} />
|
||||
<Typography style={{marginTop: 5, maxWidth: 150, maxHeight: 40, overflow: "hidden", }}>
|
||||
<Typography style={{ marginTop: 5, maxWidth: 150, maxHeight: 40, overflow: "hidden", }}>
|
||||
{innerdata?.name}
|
||||
</Typography>
|
||||
</div>
|
||||
@@ -1622,8 +1626,8 @@ const CodeEditor = (props) => {
|
||||
aria-labelledby="draggable-dialog-title"
|
||||
// disableBackdropClick={true}
|
||||
disableEnforceFocus={true}
|
||||
style={{
|
||||
pointerEvents: "none",
|
||||
style={{
|
||||
pointerEvents: "none",
|
||||
zIndex: activeDialog === "codeeditor" ? 1200 : 1100,
|
||||
}}
|
||||
hideBackdrop={true}
|
||||
@@ -1673,7 +1677,7 @@ const CodeEditor = (props) => {
|
||||
</Tooltip>
|
||||
: null}
|
||||
|
||||
{fullScreenModeEnabled ? null :
|
||||
{fullScreenModeEnabled ? null :
|
||||
<Tooltip
|
||||
color="primary"
|
||||
title={`Move window`}
|
||||
@@ -1747,7 +1751,7 @@ const CodeEditor = (props) => {
|
||||
</IconButton>
|
||||
</Tooltip>
|
||||
<div style={{ display: "flex" }}>
|
||||
{sourceDataOpen ?
|
||||
{sourceDataOpen ?
|
||||
<div style={{ minWidth: 350, maxWidth: 350, marginLeft: 5, borderRight: "1px solid rgba(255,255,255,0.3)", paddingLeft: 5, overflow: "hidden", marginRight: 10, }}>
|
||||
<Typography variant="h6">
|
||||
Source Data
|
||||
@@ -1781,7 +1785,7 @@ const CodeEditor = (props) => {
|
||||
};
|
||||
|
||||
const handleActionHover = (inside, actionId) => {
|
||||
};
|
||||
};
|
||||
|
||||
const handleMouseover = () => {
|
||||
if (innerdata.type === "Execution Argument") {
|
||||
@@ -1802,20 +1806,20 @@ const CodeEditor = (props) => {
|
||||
var parsedPaths = [];
|
||||
if (innerdata.type === "workflow_variable") {
|
||||
// Try to parse the value if it's a string that could be JSON
|
||||
if (typeof innerdata.value === "string") {
|
||||
if (typeof innerdata.value === "string") {
|
||||
try {
|
||||
const parsedValue = JSON.parse(innerdata.value)
|
||||
if (typeof parsedValue === "object") {
|
||||
parsedPaths = GetParsedPaths(parsedValue, "");
|
||||
}
|
||||
const parsedValue = JSON.parse(innerdata.value)
|
||||
if (typeof parsedValue === "object") {
|
||||
parsedPaths = GetParsedPaths(parsedValue, "");
|
||||
}
|
||||
} catch (e) {
|
||||
// Not valid JSON, use the value directly
|
||||
parsedPaths = GetParsedPaths(innerdata.value, "");
|
||||
// Not valid JSON, use the value directly
|
||||
parsedPaths = GetParsedPaths(innerdata.value, "");
|
||||
}
|
||||
} else if (typeof innerdata.value === "object") {
|
||||
} else if (typeof innerdata.value === "object") {
|
||||
parsedPaths = GetParsedPaths(innerdata.value, "");
|
||||
}
|
||||
} else if (typeof innerdata.example === "object") {
|
||||
}
|
||||
} else if (typeof innerdata.example === "object") {
|
||||
parsedPaths = GetParsedPaths(innerdata.example, "");
|
||||
}
|
||||
|
||||
@@ -1830,7 +1834,7 @@ const CodeEditor = (props) => {
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
: null}
|
||||
: null}
|
||||
|
||||
<div style={{ flex: 3, }}>
|
||||
{isFileEditor ?
|
||||
@@ -1858,7 +1862,7 @@ const CodeEditor = (props) => {
|
||||
height: 50,
|
||||
width: 50,
|
||||
right: 25,
|
||||
top: 90,
|
||||
top: 90,
|
||||
zIndex: 5000,
|
||||
}}
|
||||
disabled={localcodedata === undefined || localcodedata === null || localcodedata.length === 0}
|
||||
@@ -1902,7 +1906,7 @@ const CodeEditor = (props) => {
|
||||
{isFileEditor || isWorkflowEditor ? null :
|
||||
<div style={{ display: "flex", maxHeight: 40, }}>
|
||||
<ButtonGroup style={{ borderRadius: theme.palette.borderRadius, }}>
|
||||
{userdata !== undefined && userdata !== null && userdata.support === true ?
|
||||
{userdata !== undefined && userdata !== null && userdata.support === true ?
|
||||
<Button
|
||||
id="basic-button"
|
||||
aria-haspopup="true"
|
||||
@@ -1919,9 +1923,9 @@ const CodeEditor = (props) => {
|
||||
setSourceDataOpen(!sourceDataOpen)
|
||||
}}
|
||||
>
|
||||
<AddIcon /> Show Source Data
|
||||
<AddIcon /> Show Source Data
|
||||
</Button>
|
||||
: null}
|
||||
: null}
|
||||
|
||||
<Button
|
||||
id="basic-button"
|
||||
@@ -1976,7 +1980,7 @@ const CodeEditor = (props) => {
|
||||
setAnchorEl3(event.currentTarget);
|
||||
}}
|
||||
>
|
||||
Python Examples
|
||||
Python Examples
|
||||
</Button>
|
||||
<Button
|
||||
id="basic-button"
|
||||
@@ -2012,24 +2016,24 @@ const CodeEditor = (props) => {
|
||||
>
|
||||
{pythonFilters.map((item, index) => {
|
||||
return (
|
||||
<MenuItem
|
||||
<MenuItem
|
||||
style={{
|
||||
borderTop: item.name === "Use files" || (item.name.toLowerCase().includes("run") && item.name.toLowerCase().includes("subflow")) ? "2px solid rgba(255,255,255,0.3)" : "none",
|
||||
|
||||
}}
|
||||
key={index} onClick={() => {
|
||||
if (item.disabled) {
|
||||
toast.error("This feature may not work in your environment until you update your Shuffle Tools app.", { autoClose: 10000 })
|
||||
}
|
||||
if (item.disabled) {
|
||||
toast.error("This feature may not work in your environment until you update your Shuffle Tools app.", { autoClose: 10000 })
|
||||
}
|
||||
|
||||
if (selectedAction.name !== "execute_python") {
|
||||
var newitem = JSON.parse(JSON.stringify(item))
|
||||
newitem.value = `{% python %}\n${item.value}\n{% endpython %}`
|
||||
handleClick(newitem)
|
||||
} else {
|
||||
handleClick(item)
|
||||
}
|
||||
}}>{item.name}</MenuItem>
|
||||
if (selectedAction.name !== "execute_python") {
|
||||
var newitem = JSON.parse(JSON.stringify(item))
|
||||
newitem.value = `{% python %}\n${item.value}\n{% endpython %}`
|
||||
handleClick(newitem)
|
||||
} else {
|
||||
handleClick(item)
|
||||
}
|
||||
}}>{item.name}</MenuItem>
|
||||
)
|
||||
})}
|
||||
</Menu>
|
||||
@@ -2069,11 +2073,11 @@ const CodeEditor = (props) => {
|
||||
) : innerdata.type === "workflow_variable" ||
|
||||
innerdata.type === "execution_variable" ? (
|
||||
<FavoriteBorderIcon style={{ marginRight: 10 }} />
|
||||
) :
|
||||
innerdata.type === "Shuffle DB" ?
|
||||
<StorageIcon style={{ marginRight: 10, }} />
|
||||
:
|
||||
<ScheduleIcon style={{ marginRight: 10 }} />
|
||||
) :
|
||||
innerdata.type === "Shuffle DB" ?
|
||||
<StorageIcon style={{ marginRight: 10, }} />
|
||||
:
|
||||
<ScheduleIcon style={{ marginRight: 10 }} />
|
||||
|
||||
const handleExecArgumentHover = (inside) => {
|
||||
var exec_text_field = document.getElementById(
|
||||
@@ -2110,20 +2114,20 @@ const CodeEditor = (props) => {
|
||||
var parsedPaths = [];
|
||||
if (innerdata.type === "workflow_variable") {
|
||||
// Try to parse the value if it's a string that could be JSON
|
||||
if (typeof innerdata.value === "string") {
|
||||
if (typeof innerdata.value === "string") {
|
||||
try {
|
||||
const parsedValue = JSON.parse(innerdata.value)
|
||||
if (typeof parsedValue === "object") {
|
||||
parsedPaths = GetParsedPaths(parsedValue, "");
|
||||
}
|
||||
const parsedValue = JSON.parse(innerdata.value)
|
||||
if (typeof parsedValue === "object") {
|
||||
parsedPaths = GetParsedPaths(parsedValue, "");
|
||||
}
|
||||
} catch (e) {
|
||||
// Not valid JSON, use the value directly
|
||||
parsedPaths = GetParsedPaths(innerdata.value, "");
|
||||
// Not valid JSON, use the value directly
|
||||
parsedPaths = GetParsedPaths(innerdata.value, "");
|
||||
}
|
||||
} else if (typeof innerdata.value === "object") {
|
||||
} else if (typeof innerdata.value === "object") {
|
||||
parsedPaths = GetParsedPaths(innerdata.value, "");
|
||||
}
|
||||
} else if (typeof innerdata.example === "object") {
|
||||
}
|
||||
} else if (typeof innerdata.example === "object") {
|
||||
parsedPaths = GetParsedPaths(innerdata.example, "");
|
||||
}
|
||||
|
||||
@@ -2381,7 +2385,7 @@ const CodeEditor = (props) => {
|
||||
</div>
|
||||
}
|
||||
|
||||
<div
|
||||
<div
|
||||
style={{
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
position: "relative",
|
||||
@@ -2393,7 +2397,7 @@ const CodeEditor = (props) => {
|
||||
onDrop={(e) => {
|
||||
console.log("DROP: ", e)
|
||||
}}
|
||||
>
|
||||
>
|
||||
{(availableVariables !== undefined && availableVariables !== null && availableVariables.length > 0) || isFileEditor || isWorkflowEditor ? (
|
||||
<AceEditor
|
||||
id="shuffle-codeeditor"
|
||||
@@ -2421,7 +2425,7 @@ const CodeEditor = (props) => {
|
||||
backgroundColor: "rgba(40,40,40,1)",
|
||||
zIndex: activeDialog === "codeeditor" ? 1200 : 1100,
|
||||
|
||||
|
||||
|
||||
}}
|
||||
onLoad={(editor) => {
|
||||
highlight_variables(localcodedata)
|
||||
@@ -2465,12 +2469,12 @@ const CodeEditor = (props) => {
|
||||
</div>
|
||||
|
||||
{isFileEditor || isWorkflowEditor ? null :
|
||||
<div style={{
|
||||
flex: sourceDataOpen ? 1.5 : 3,
|
||||
marginLeft: 5,
|
||||
borderLeft: "1px solid rgba(255,255,255,0.3)",
|
||||
paddingLeft: 5,
|
||||
overflow: "hidden",
|
||||
<div style={{
|
||||
flex: sourceDataOpen ? 1.5 : 3,
|
||||
marginLeft: 5,
|
||||
borderLeft: "1px solid rgba(255,255,255,0.3)",
|
||||
paddingLeft: 5,
|
||||
overflow: "hidden",
|
||||
transitions: "all 1s ease-in-out",
|
||||
}}>
|
||||
<div>
|
||||
@@ -2486,59 +2490,59 @@ const CodeEditor = (props) => {
|
||||
}}
|
||||
>
|
||||
<div>
|
||||
{actionId === null && triggerId === null ?
|
||||
<div style={{display: "flex", alignItems: "center"}}>
|
||||
{`Condition ${selectedEdge?.conditions?.findIndex(cond => cond.condition.id === conditionId) + 1 || "0"}`}
|
||||
{/* Source node image */}
|
||||
{selectedEdge?.source ?
|
||||
<img
|
||||
src={sourceImage || ""}
|
||||
alt="Source"
|
||||
style={{
|
||||
width: 30,
|
||||
height: 30,
|
||||
marginRight: 10,
|
||||
borderRadius: "50%",
|
||||
marginLeft: 10,
|
||||
border: conditionField === "source" ? `3px solid #FF8544` : null,
|
||||
}}
|
||||
/>
|
||||
: null
|
||||
}
|
||||
{actionId === null && triggerId === null ?
|
||||
<div style={{ display: "flex", alignItems: "center" }}>
|
||||
{`Condition ${selectedEdge?.conditions?.findIndex(cond => cond.condition.id === conditionId) + 1 || "0"}`}
|
||||
{/* Source node image */}
|
||||
{selectedEdge?.source ?
|
||||
<img
|
||||
src={sourceImage || ""}
|
||||
alt="Source"
|
||||
style={{
|
||||
width: 30,
|
||||
height: 30,
|
||||
marginRight: 10,
|
||||
borderRadius: "50%",
|
||||
marginLeft: 10,
|
||||
border: conditionField === "source" ? `3px solid #FF8544` : null,
|
||||
}}
|
||||
/>
|
||||
: null
|
||||
}
|
||||
|
||||
{/* Add arrow icon */}
|
||||
{
|
||||
selectedEdge && Object.keys(selectedEdge).length > 0 ?
|
||||
<ArrowForwardIcon style={{
|
||||
{/* Add arrow icon */}
|
||||
{
|
||||
selectedEdge && Object.keys(selectedEdge).length > 0 ?
|
||||
<ArrowForwardIcon style={{
|
||||
color: theme.palette.textPrimary,
|
||||
fontSize: 18,
|
||||
marginLeft: -5,
|
||||
marginRight: -5,
|
||||
}} />
|
||||
: null
|
||||
}
|
||||
}
|
||||
|
||||
{/* Destination node image */}
|
||||
{selectedEdge?.target ?
|
||||
<img
|
||||
src={targetImage || ""}
|
||||
alt="Destination"
|
||||
style={{
|
||||
width: 30,
|
||||
height: 30,
|
||||
marginLeft: 10,
|
||||
borderRadius: "50%",
|
||||
border: conditionField === "destination" ? `3px solid #FF8544` : null,
|
||||
}}
|
||||
/>
|
||||
: null
|
||||
}
|
||||
</div>
|
||||
:
|
||||
<span style={{ color: theme.palette.text.primary }}>
|
||||
{selectedAction.name === "execute_python" || selectedAction.name === "execute_bash" ?
|
||||
"Code to run" :
|
||||
triggerId ?
|
||||
{/* Destination node image */}
|
||||
{selectedEdge?.target ?
|
||||
<img
|
||||
src={targetImage || ""}
|
||||
alt="Destination"
|
||||
style={{
|
||||
width: 30,
|
||||
height: 30,
|
||||
marginLeft: 10,
|
||||
borderRadius: "50%",
|
||||
border: conditionField === "destination" ? `3px solid #FF8544` : null,
|
||||
}}
|
||||
/>
|
||||
: null
|
||||
}
|
||||
</div>
|
||||
:
|
||||
<span style={{ color: theme.palette.text.primary }}>
|
||||
{selectedAction.name === "execute_python" || selectedAction.name === "execute_bash" ?
|
||||
"Code to run" :
|
||||
triggerId ?
|
||||
`Output: ${triggerName?.replaceAll("_", " ").slice(0, 1).toUpperCase() + triggerName?.replaceAll("_", " ").slice(1)} (${triggerField})` :
|
||||
`Output: ${appName?.replaceAll("_", " ").slice(0, 1).toUpperCase() + appName?.replaceAll("_", " ").slice(1)} (${fieldName})`
|
||||
}
|
||||
@@ -2570,7 +2574,7 @@ const CodeEditor = (props) => {
|
||||
backdropFilter: "blur(8px)",
|
||||
boxShadow: "0 4px 6px rgba(0, 0, 0, 0.1), 0 1px 3px rgba(0, 0, 0, 0.08)",
|
||||
transition: "all 0.2s ease",
|
||||
paddingRight: 20,
|
||||
paddingRight: 20,
|
||||
color: "#FF8544",
|
||||
borderRadius: theme.palette?.borderRadius,
|
||||
"&:hover": {
|
||||
@@ -2592,18 +2596,18 @@ const CodeEditor = (props) => {
|
||||
<span>
|
||||
|
||||
<PlayArrowIcon style={{ height: 18, width: 18, marginBottom: -4, marginLeft: 5, }} />
|
||||
{selectedAction === undefined ? <Typography style={{color: "inherit"}}>Try it</Typography> : selectedAction.name === "execute_python" ? "Run Python Code" : selectedAction.name === "execute_bash" ? "Run Bash" : "Try it"}
|
||||
<span
|
||||
style={{
|
||||
color: "#C8C8C8",
|
||||
fontSize: "12px",
|
||||
whiteSpace: "nowrap",
|
||||
marginLeft: 5,
|
||||
marginRight: 10,
|
||||
}}
|
||||
>
|
||||
<kbd>Ctrl</kbd> + <kbd><KeyboardReturnIcon style={{width: 13, position: "absolute", marginLeft: 3, top: 5, }}/></kbd>
|
||||
</span>
|
||||
{selectedAction === undefined ? <Typography style={{ color: "inherit" }}>Try it</Typography> : selectedAction.name === "execute_python" ? "Run Python Code" : selectedAction.name === "execute_bash" ? "Run Bash" : "Try it"}
|
||||
<span
|
||||
style={{
|
||||
color: "#C8C8C8",
|
||||
fontSize: "12px",
|
||||
whiteSpace: "nowrap",
|
||||
marginLeft: 5,
|
||||
marginRight: 10,
|
||||
}}
|
||||
>
|
||||
<kbd>Ctrl</kbd> + <kbd><KeyboardReturnIcon style={{ width: 13, position: "absolute", marginLeft: 3, top: 5, }} /></kbd>
|
||||
</span>
|
||||
|
||||
</span>
|
||||
}
|
||||
@@ -2713,7 +2717,7 @@ const CodeEditor = (props) => {
|
||||
Output is based on the last VALID run of the node(s) you are referencing. Refresh the page to get updated Variable values.
|
||||
{selectedAction?.name === "execute_python" ?
|
||||
"For Python: exit() to stop a python script ANYWHERE."
|
||||
: null}
|
||||
: null}
|
||||
</Typography>
|
||||
<Typography variant="body2" style={{ maxHeight: 150, overflow: "auto", marginTop: 20, }}>
|
||||
No test output yet.
|
||||
@@ -2735,7 +2739,7 @@ const CodeEditor = (props) => {
|
||||
</div>
|
||||
|
||||
|
||||
<div style={{ display: 'flex', width: fullScreenModeEnabled ? "92%" : isWorkflowEditor ? "90%" : "100%", }}>
|
||||
<div style={{ display: 'flex', width: fullScreenModeEnabled ? "92%" : isWorkflowEditor ? "90%" : "100%", }}>
|
||||
<Button
|
||||
style={{
|
||||
height: 35,
|
||||
@@ -2758,7 +2762,7 @@ const CodeEditor = (props) => {
|
||||
<Button
|
||||
variant="contained"
|
||||
color="primary"
|
||||
disabled={isWorkflowEditor}
|
||||
disabled={isWorkflowEditor}
|
||||
style={{
|
||||
height: 35,
|
||||
flex: 1,
|
||||
|
||||
@@ -812,6 +812,7 @@ const Workflows2 = (props) => {
|
||||
const [isLoadingWorkflow, setIsLoadingWorkflow] = useState(false);
|
||||
const [isLoadingPublicWorkflow, setIsLoadingPublicWorkflow] = useState(false);
|
||||
const [view, setView] = useState(localStorage?.getItem("workflowView") || "grid");
|
||||
const [currentOrg, setCurrentOrg] = useState(null);
|
||||
|
||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io";
|
||||
const [showExecutionStats, setShowExecutionStats] = React.useState(localStorage?.getItem("showExecutionStats") === "true")
|
||||
@@ -971,6 +972,7 @@ const Workflows2 = (props) => {
|
||||
.then((org) => {
|
||||
if (!fetched && org) {
|
||||
if (!isCloud) {
|
||||
setCurrentOrg(org);
|
||||
if (org?.cloud_sync && org?.subscriptions[0]?.name?.toLowerCase().includes("enterprise") && org?.subscriptions[0]?.active) {
|
||||
setIsProdStatusOn(true);
|
||||
} else if (org?.subscriptions[0]?.name?.toLowerCase().includes("enterprise") && org?.subscriptions[0]?.active) {
|
||||
@@ -5518,7 +5520,7 @@ const Workflows2 = (props) => {
|
||||
</div>
|
||||
}
|
||||
|
||||
{!isCloud ? (
|
||||
{!isCloud && currentOrg?.old_org ? (
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
|
||||
@@ -10,3 +10,32 @@ More details in the [kubernetes/Charts/Shuffle folder.](https://github.com/Shuff
|
||||
Here is the default architecture it follows, with the "Frontend" being the exposed container you interact with.
|
||||
|
||||
<img width="1006" height="1069" alt="image" src="https://github.com/user-attachments/assets/263369a1-6944-4ef5-8f19-14bc234130d8" />
|
||||
|
||||
## GKE testing
|
||||
For testing on GKE, you can use the following command:
|
||||
|
||||
```bash
|
||||
PROJECT_NAME="project"
|
||||
CLUSTER_NAME="cluster"
|
||||
|
||||
# feel free to change the region and machine type
|
||||
gcloud container clusters create "$CLUSTER_NAME" \
|
||||
--project "$PROJECT_NAME" \
|
||||
--region "asia-south2" \
|
||||
--release-channel "regular" \
|
||||
--machine-type "e2-standard-2" \
|
||||
--num-nodes "1" \
|
||||
--node-locations "asia-south2-b,asia-south2-a,asia-south2-c"
|
||||
|
||||
# given that your gcloud cli is connected with kubectl
|
||||
kubectl config set-context $CLUSTER_NAME
|
||||
|
||||
# apply shuffle k8s helm chart
|
||||
helm install shuffle oci://ghcr.io/shuffle/charts/shuffle \
|
||||
--version 2.1.1 \
|
||||
--namespace shuffle \
|
||||
--create-namespace
|
||||
|
||||
# verify
|
||||
kubectl get pods -n shuffle
|
||||
```
|
||||
|
||||
@@ -1558,15 +1558,6 @@ app:
|
||||
##
|
||||
annotations: {}
|
||||
## @param app.serviceAccount.automountServiceAccountToken Automount service account token for the app service account
|
||||
## NOTE: You likely want to allow access to cluster-proxies, e.g:
|
||||
## extraEgress:
|
||||
## - to:
|
||||
## - namespaceSelector:
|
||||
## matchLabels:
|
||||
## kubernetes.io/metadata.name: istio-system
|
||||
## podSelector:
|
||||
## matchLabels:
|
||||
## istio: pilot
|
||||
##
|
||||
automountServiceAccountToken: true
|
||||
## @param app.serviceAccount.imagePullSecrets Add image pull secrets to the app service account
|
||||
|
||||
@@ -10,9 +10,9 @@ require (
|
||||
github.com/docker/docker v28.3.3+incompatible
|
||||
github.com/docker/go-connections v0.5.0
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
github.com/shuffle/shuffle-shared v0.9.53
|
||||
k8s.io/api v0.33.1
|
||||
k8s.io/apimachinery v0.33.1
|
||||
github.com/shuffle/shuffle-shared v0.9.69
|
||||
k8s.io/api v0.34.2
|
||||
k8s.io/apimachinery v0.34.2
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -52,14 +52,14 @@ require (
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect
|
||||
github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/frikky/kin-openapi v0.42.0 // indirect
|
||||
github.com/frikky/schemaless v0.0.24 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/frikky/schemaless v0.0.25 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/ghodss/yaml v1.0.0 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.6.2 // indirect
|
||||
@@ -73,8 +73,7 @@ require (
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/gnostic-models v0.6.9 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/gnostic-models v0.7.0 // indirect
|
||||
github.com/google/go-github/v28 v28.1.1 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
@@ -89,8 +88,9 @@ require (
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/sys/sequential v0.6.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/openai/openai-go/v3 v3.8.1 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||
github.com/osteele/liquid v1.7.0 // indirect
|
||||
@@ -106,8 +106,12 @@ require (
|
||||
github.com/shuffle/opensearch-go/v4 v4.0.0 // indirect
|
||||
github.com/skeema/knownhosts v1.3.1 // indirect
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect
|
||||
github.com/tidwall/gjson v1.18.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/tidwall/sjson v1.2.5 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
github.com/zeebo/errs v1.4.0 // indirect
|
||||
@@ -121,14 +125,16 @@ require (
|
||||
go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.36.0 // indirect
|
||||
go.opentelemetry.io/proto/otlp v1.5.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
|
||||
golang.org/x/crypto v0.40.0 // indirect
|
||||
golang.org/x/net v0.41.0 // indirect
|
||||
golang.org/x/crypto v0.45.0 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/oauth2 v0.30.0 // indirect
|
||||
golang.org/x/sync v0.16.0 // indirect
|
||||
golang.org/x/sys v0.34.0 // indirect
|
||||
golang.org/x/term v0.33.0 // indirect
|
||||
golang.org/x/text v0.27.0 // indirect
|
||||
golang.org/x/sync v0.18.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/term v0.37.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
google.golang.org/api v0.236.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
@@ -142,12 +148,12 @@ require (
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/client-go v0.33.1 // indirect
|
||||
k8s.io/client-go v0.34.2 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
|
||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
|
||||
sigs.k8s.io/yaml v1.6.0 // indirect
|
||||
)
|
||||
|
||||
@@ -128,8 +128,8 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/frikky/kin-openapi v0.42.0 h1:d5Z6vnuQ6RnCCPIxZaDL+TH2ODLxT8abytOt+Zh+Kd0=
|
||||
github.com/frikky/kin-openapi v0.42.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4=
|
||||
github.com/frikky/schemaless v0.0.24 h1:1bNg96gm82yLbRlDS0haQH/96X3GlCAEuTCSasXIJZE=
|
||||
github.com/frikky/schemaless v0.0.24/go.mod h1:m9s+6gALXhA5ZERCrJw+jI2rRtTPNa8mkl4vav9sxnY=
|
||||
github.com/frikky/schemaless v0.0.25 h1:qXjrKT54LWl3tzDFIUmevZ86F7FiusPpQKfa40xxNl8=
|
||||
github.com/frikky/schemaless v0.0.25/go.mod h1:m9s+6gALXhA5ZERCrJw+jI2rRtTPNa8mkl4vav9sxnY=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
@@ -309,8 +309,8 @@ github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/shuffle/opensearch-go/v4 v4.0.0 h1:Mh85CD1MwOgXiFFYlzS1llnvdqL3CztRdR1ZT/SLIjU=
|
||||
github.com/shuffle/opensearch-go/v4 v4.0.0/go.mod h1:gVLZKQE5khQWMb68XBtgKrhu78oLGL2zHwAGnFMDwC0=
|
||||
github.com/shuffle/shuffle-shared v0.9.53 h1:ykziiRA82FZX0YiuErSmFLQMxvAFi6yUajgRjd3J5xE=
|
||||
github.com/shuffle/shuffle-shared v0.9.53/go.mod h1:g03P/ZM8pUwBbdxW0l9A1siMMuemEQshgDpO9hF23eE=
|
||||
github.com/shuffle/shuffle-shared v0.9.61 h1:osyJqgRx68m9l+kssB1fpHA4pmkNXK4ZAVVEHL5dW3Q=
|
||||
github.com/shuffle/shuffle-shared v0.9.61/go.mod h1:vK6t1WY5Nfg5vOAk6taT788jIGKs6/4iN1d8Argyn4o=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
|
||||
@@ -136,11 +136,34 @@ var window = shuffle.NewTimeWindow(1 * time.Minute)
|
||||
func init() {
|
||||
var err error
|
||||
|
||||
dockercli, err = dockerclient.NewEnvClient()
|
||||
// dockercli, err = dockerclient.NewEnvClient()
|
||||
dockercli, dockerApiVersion, err = shuffle.GetDockerClient()
|
||||
if err != nil {
|
||||
log.Printf("Unable to create docker client: %s", err)
|
||||
}
|
||||
|
||||
if os.Getenv("SHUFFLE_EC2_INSTANCE") == "true" {
|
||||
log.Printf("[INFO] Detected AWS EC2 instance. Setting up Docker Swarm with AWS optimizations.")
|
||||
containers, err := dockercli.ContainerList(context.Background(), container.ListOptions{})
|
||||
if err == nil {
|
||||
for _, container := range containers {
|
||||
if strings.Contains(container.Image, "shuffle-orborus") {
|
||||
if len(container.Names) != 0 {
|
||||
if strings.Contains(container.Names[0], "shuffle-orborus") {
|
||||
containerName = container.Names[0]
|
||||
containerName = strings.TrimPrefix(containerName, "/")
|
||||
os.Setenv("ORBORUS_CONTAINER_NAME", containerName)
|
||||
log.Printf("[DEBUG] Found orborus container name: %s", containerName)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log.Printf("[ERROR] Failed to find orborus container: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
getThisContainerId()
|
||||
|
||||
if len(pipelineApikey) == 0 {
|
||||
@@ -1426,6 +1449,9 @@ func deployK8sWorker(image string, identifier string, env []string) error {
|
||||
}
|
||||
|
||||
replicaNumberInt32 := int32(replicaNumber)
|
||||
// worker makes authenticated requests to the k8s api to create app deployments.
|
||||
// Therefore, it needs to have access to the service account token.
|
||||
automountServiceAccountToken := true
|
||||
|
||||
deployment := &appsv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -1445,9 +1471,10 @@ func deployK8sWorker(image string, identifier string, env []string) error {
|
||||
Containers: []corev1.Container{
|
||||
containerAttachment,
|
||||
},
|
||||
DNSPolicy: corev1.DNSClusterFirst,
|
||||
ServiceAccountName: workerServiceAccountName,
|
||||
SecurityContext: podSecurityContext,
|
||||
DNSPolicy: corev1.DNSClusterFirst,
|
||||
ServiceAccountName: workerServiceAccountName,
|
||||
AutomountServiceAccountToken: &automountServiceAccountToken,
|
||||
SecurityContext: podSecurityContext,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -2411,6 +2438,7 @@ func main() {
|
||||
}
|
||||
|
||||
if swarmConfig == "run" || swarmConfig == "swarm" || isKubernetes == "true" {
|
||||
|
||||
if isKubernetes != "true" {
|
||||
checkSwarmService(ctx)
|
||||
}
|
||||
@@ -2745,6 +2773,8 @@ func main() {
|
||||
fmt.Sprintf("/detections/Sigma"),
|
||||
org,
|
||||
true,
|
||||
"LOW",
|
||||
"TENZIR_START",
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
@@ -2755,6 +2785,10 @@ func main() {
|
||||
}
|
||||
|
||||
} else {
|
||||
if debug {
|
||||
log.Printf("[DEBUG] Passing execution ID request to normal queue: %#v", incRequest.ExecutionId)
|
||||
}
|
||||
|
||||
newrequests = append(newrequests, incRequest)
|
||||
}
|
||||
}
|
||||
@@ -4289,6 +4323,10 @@ func sendWorkerRequest(workflowExecution shuffle.ExecutionRequest, image string,
|
||||
Timeout: time.Duration(120 * time.Second),
|
||||
}
|
||||
|
||||
if debug {
|
||||
log.Printf("[DEBUG][%s] Worker request to be sent to URL: %s", workflowExecution.ExecutionId, streamUrl)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(
|
||||
"POST",
|
||||
streamUrl,
|
||||
|
||||
@@ -11,11 +11,11 @@ require (
|
||||
github.com/docker/docker v28.3.3+incompatible
|
||||
github.com/gorilla/mux v1.8.1
|
||||
github.com/satori/go.uuid v1.2.0
|
||||
github.com/shuffle/shuffle-shared v0.9.53
|
||||
github.com/shuffle/shuffle-shared v0.9.69
|
||||
github.com/shuffle/singul v0.0.20
|
||||
k8s.io/api v0.33.1
|
||||
k8s.io/apimachinery v0.33.1
|
||||
k8s.io/client-go v0.33.1
|
||||
k8s.io/api v0.34.2
|
||||
k8s.io/apimachinery v0.34.2
|
||||
k8s.io/client-go v0.34.2
|
||||
)
|
||||
|
||||
require (
|
||||
@@ -54,14 +54,14 @@ require (
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/envoyproxy/go-control-plane/envoy v1.32.4 // indirect
|
||||
github.com/envoyproxy/protoc-gen-validate v1.2.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/frikky/kin-openapi v0.42.0 // indirect
|
||||
github.com/frikky/schemaless v0.0.24 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/frikky/schemaless v0.0.25 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.9.0 // indirect
|
||||
github.com/ghodss/yaml v1.0.0 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.6.2 // indirect
|
||||
@@ -75,8 +75,7 @@ require (
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/gnostic-models v0.6.9 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/gnostic-models v0.7.0 // indirect
|
||||
github.com/google/go-github/v28 v28.1.1 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/s2a-go v0.1.9 // indirect
|
||||
@@ -91,8 +90,9 @@ require (
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/openai/openai-go/v3 v3.8.1 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||
github.com/osteele/liquid v1.7.0 // indirect
|
||||
@@ -110,6 +110,10 @@ require (
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/spiffe/go-spiffe/v2 v2.5.0 // indirect
|
||||
github.com/tidwall/gjson v1.18.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/tidwall/sjson v1.2.5 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
github.com/zeebo/errs v1.4.0 // indirect
|
||||
@@ -121,14 +125,16 @@ require (
|
||||
go.opentelemetry.io/otel/sdk v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk/metric v1.36.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.36.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
|
||||
golang.org/x/crypto v0.40.0 // indirect
|
||||
golang.org/x/net v0.41.0 // indirect
|
||||
golang.org/x/crypto v0.45.0 // indirect
|
||||
golang.org/x/net v0.47.0 // indirect
|
||||
golang.org/x/oauth2 v0.30.0 // indirect
|
||||
golang.org/x/sync v0.16.0 // indirect
|
||||
golang.org/x/sys v0.34.0 // indirect
|
||||
golang.org/x/term v0.33.0 // indirect
|
||||
golang.org/x/text v0.27.0 // indirect
|
||||
golang.org/x/sync v0.18.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/term v0.37.0 // indirect
|
||||
golang.org/x/text v0.31.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
google.golang.org/api v0.236.0 // indirect
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
@@ -143,10 +149,10 @@ require (
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/klog/v2 v2.130.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
|
||||
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241010143419-9aa6b5e7a4b3 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20250710124328-f3f2b991d03b // indirect
|
||||
k8s.io/utils v0.0.0-20250604170112-4c0f3b243397 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
|
||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect
|
||||
sigs.k8s.io/yaml v1.6.0 // indirect
|
||||
)
|
||||
|
||||
@@ -128,8 +128,8 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2
|
||||
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
|
||||
github.com/frikky/kin-openapi v0.42.0 h1:d5Z6vnuQ6RnCCPIxZaDL+TH2ODLxT8abytOt+Zh+Kd0=
|
||||
github.com/frikky/kin-openapi v0.42.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4=
|
||||
github.com/frikky/schemaless v0.0.24 h1:1bNg96gm82yLbRlDS0haQH/96X3GlCAEuTCSasXIJZE=
|
||||
github.com/frikky/schemaless v0.0.24/go.mod h1:m9s+6gALXhA5ZERCrJw+jI2rRtTPNa8mkl4vav9sxnY=
|
||||
github.com/frikky/schemaless v0.0.25 h1:qXjrKT54LWl3tzDFIUmevZ86F7FiusPpQKfa40xxNl8=
|
||||
github.com/frikky/schemaless v0.0.25/go.mod h1:m9s+6gALXhA5ZERCrJw+jI2rRtTPNa8mkl4vav9sxnY=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||
@@ -311,8 +311,8 @@ github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 h1:n661drycOFuPLCN
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=
|
||||
github.com/shuffle/opensearch-go/v4 v4.0.0 h1:Mh85CD1MwOgXiFFYlzS1llnvdqL3CztRdR1ZT/SLIjU=
|
||||
github.com/shuffle/opensearch-go/v4 v4.0.0/go.mod h1:gVLZKQE5khQWMb68XBtgKrhu78oLGL2zHwAGnFMDwC0=
|
||||
github.com/shuffle/shuffle-shared v0.9.53 h1:ykziiRA82FZX0YiuErSmFLQMxvAFi6yUajgRjd3J5xE=
|
||||
github.com/shuffle/shuffle-shared v0.9.53/go.mod h1:g03P/ZM8pUwBbdxW0l9A1siMMuemEQshgDpO9hF23eE=
|
||||
github.com/shuffle/shuffle-shared v0.9.60 h1:8NiovcsSsVX8i0UHmbFBANjUQ/u+4PDDpJXg9JfMAUo=
|
||||
github.com/shuffle/shuffle-shared v0.9.60/go.mod h1:vK6t1WY5Nfg5vOAk6taT788jIGKs6/4iN1d8Argyn4o=
|
||||
github.com/shuffle/singul v0.0.20 h1:Lz+K4l2GJQ5W6o8ePr/2qlIcGuZuMC7W8Wj/JFw1XDI=
|
||||
github.com/shuffle/singul v0.0.20/go.mod h1:qY3ZmwaNwmRApLWMTqZaNEMXO6X8bjr/zY44xDU62hQ=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
|
||||
@@ -625,6 +625,8 @@ func deployk8sApp(image string, identifier string, env []string) error {
|
||||
}
|
||||
|
||||
replicaNumberInt32 := int32(replicaNumber)
|
||||
// apps do not need access the k8s api.
|
||||
automountServiceAccountToken := false
|
||||
|
||||
deployment := &appsv1.Deployment{
|
||||
ObjectMeta: metav1.ObjectMeta{
|
||||
@@ -656,9 +658,10 @@ func deployk8sApp(image string, identifier string, env []string) error {
|
||||
Resources: buildResourcesFromEnv(),
|
||||
},
|
||||
},
|
||||
DNSPolicy: corev1.DNSClusterFirst,
|
||||
ServiceAccountName: appServiceAccountName,
|
||||
SecurityContext: podSecurityContext,
|
||||
DNSPolicy: corev1.DNSClusterFirst,
|
||||
ServiceAccountName: appServiceAccountName,
|
||||
AutomountServiceAccountToken: &automountServiceAccountToken,
|
||||
SecurityContext: podSecurityContext,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -1243,7 +1246,8 @@ func DeployContainer(ctx context.Context, cli *dockerclient.Client, config *cont
|
||||
func removeContainer(containername string) error {
|
||||
ctx := context.Background()
|
||||
|
||||
cli, err := dockerclient.NewEnvClient()
|
||||
// cli, err := dockerclient.NewEnvClient()
|
||||
cli, _, err := shuffle.GetDockerClient()
|
||||
if err != nil {
|
||||
log.Printf("[DEBUG] Unable to create docker client: %s", err)
|
||||
return err
|
||||
@@ -1341,7 +1345,8 @@ func getWorkerURLs() ([]string, error) {
|
||||
}
|
||||
|
||||
// Create a new Docker client
|
||||
cli, err := dockerclient.NewEnvClient()
|
||||
// cli, err := dockerclient.NewEnvClient()
|
||||
cli, _, err := shuffle.GetDockerClient()
|
||||
if err != nil {
|
||||
log.Println("[ERROR] Failed to create Docker client:", err)
|
||||
return workerUrls, err
|
||||
@@ -1458,7 +1463,8 @@ func handleExecutionResult(workflowExecution shuffle.WorkflowExecution) {
|
||||
var err error
|
||||
|
||||
if isKubernetes != "true" {
|
||||
dockercli, err = dockerclient.NewEnvClient()
|
||||
// dockercli, err = dockerclient.NewEnvClient()
|
||||
dockercli, _, err = shuffle.GetDockerClient()
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Unable to create docker client (3): %s", err)
|
||||
return
|
||||
@@ -3578,7 +3584,8 @@ func findAppInfo(image, name string, redeploy bool) (int, error) {
|
||||
|
||||
//Filters:
|
||||
if exposedPort == -1 || redeploy {
|
||||
dockercli, err := dockerclient.NewEnvClient()
|
||||
// dockercli, err := dockerclient.NewEnvClient()
|
||||
dockercli, _, err := shuffle.GetDockerClient()
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Unable to create docker client (2): %s", err)
|
||||
return -1, err
|
||||
@@ -3690,7 +3697,8 @@ func findAppInfo(image, name string, redeploy bool) (int, error) {
|
||||
if exposedPort >= 0 {
|
||||
//log.Printf("[INFO] Found service %s on port %d - no need to deploy another", name, exposedPort)
|
||||
} else {
|
||||
dockercli, err := dockerclient.NewEnvClient()
|
||||
// dockercli, err := dockerclient.NewEnvClient()
|
||||
dockercli, _, err := shuffle.GetDockerClient()
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Unable to create docker client (2): %s", err)
|
||||
return -1, err
|
||||
@@ -3768,7 +3776,8 @@ func findAppInfoKubernetes(image, name string, env []string) error {
|
||||
// Backups in case networks are removed
|
||||
func initSwarmNetwork() error {
|
||||
ctx := context.Background()
|
||||
dockercli, err := dockerclient.NewEnvClient()
|
||||
// dockercli, err := dockerclient.NewEnvClient()
|
||||
dockercli, _, err := shuffle.GetDockerClient()
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Unable to create docker client (2): %s", err)
|
||||
return err
|
||||
@@ -4074,7 +4083,8 @@ func baseDeploy() {
|
||||
//var err error
|
||||
|
||||
if isKubernetes != "true" {
|
||||
cli, err := dockerclient.NewEnvClient()
|
||||
// cli, err := dockerclient.NewEnvClient()
|
||||
cli, _, err := shuffle.GetDockerClient()
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Unable to create docker client (3): %s", err)
|
||||
return
|
||||
@@ -4815,7 +4825,8 @@ func handleDownloadImage(resp http.ResponseWriter, request *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
client, err := dockerclient.NewEnvClient()
|
||||
// client, err := dockerclient.NewEnvClient()
|
||||
client, _, err := shuffle.GetDockerClient()
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Unable to create docker client (4): %s", err)
|
||||
resp.WriteHeader(401)
|
||||
@@ -4979,7 +4990,13 @@ func AutoScaleApps(ctx context.Context, client *dockerclient.Client, maxExecutio
|
||||
}
|
||||
|
||||
func scaleApps(ctx context.Context, client *dockerclient.Client, replicas uint64) error {
|
||||
client, err := dockerclient.NewEnvClient()
|
||||
// client, err := dockerclient.NewEnvClient()
|
||||
client, _, err := shuffle.GetDockerClient()
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Unable to create docker client (scaleApps): %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
services, err := client.ServiceList(ctx, types.ServiceListOptions{})
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Failed to find services in the swarm: %s", err)
|
||||
@@ -5057,7 +5074,8 @@ func numberOfApps(ctx context.Context, dockercli *dockerclient.Client) int {
|
||||
}
|
||||
|
||||
if dockercli == nil {
|
||||
dockercli, err = dockerclient.NewEnvClient()
|
||||
// dockercli, err = dockerclient.NewEnvClient()
|
||||
dockercli, _, err = shuffle.GetDockerClient()
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Unable to create docker client (5): %s", err)
|
||||
return 0
|
||||
@@ -5144,7 +5162,13 @@ func IsServiceRunning(ctx context.Context, cli *dockerclient.Client) bool {
|
||||
}
|
||||
|
||||
func numberOfWorkers(ctx context.Context, cli *dockerclient.Client) int {
|
||||
cli, err := dockerclient.NewEnvClient()
|
||||
// cli, err := dockerclient.NewEnvClient()
|
||||
cli, _, err := shuffle.GetDockerClient()
|
||||
if err != nil {
|
||||
log.Printf("[ERROR] Unable to create docker client (5): %s", err)
|
||||
return 0
|
||||
}
|
||||
|
||||
service, _, err := cli.ServiceInspectWithRaw(ctx, "shuffle-workers", types.ServiceInspectOptions{})
|
||||
if err != nil {
|
||||
return 0
|
||||
|
||||
Reference in New Issue
Block a user