Fixed no-path issue with requirement of leading /
This commit is contained in:
@@ -7,15 +7,12 @@ WORKDIR /app
|
|||||||
ADD ./go-app/main.go /app
|
ADD ./go-app/main.go /app
|
||||||
ADD ./go-app/walkoff.go /app
|
ADD ./go-app/walkoff.go /app
|
||||||
ADD ./go-app/docker.go /app
|
ADD ./go-app/docker.go /app
|
||||||
ADD ./go-app/codegen.go /app
|
|
||||||
ADD ./go-app/files.go /app
|
|
||||||
ADD ./go-app/oauth2.go /app
|
ADD ./go-app/oauth2.go /app
|
||||||
|
|
||||||
ADD ./go-app/go.mod /app
|
ADD ./go-app/go.mod /app
|
||||||
|
|
||||||
# Required files for code generation
|
# Required files for code generation
|
||||||
ADD ./app_sdk/app_base.py /app_sdk
|
ADD ./app_sdk/app_base.py /app_sdk
|
||||||
ADD ./app_sdk/static_baseline.py /app_sdk
|
|
||||||
ADD ./app_sdk_kali/app_base.py /app_sdk_kali
|
ADD ./app_sdk_kali/app_base.py /app_sdk_kali
|
||||||
ADD ./app_sdk_kali/static_baseline.py /app_sdk_kali
|
ADD ./app_sdk_kali/static_baseline.py /app_sdk_kali
|
||||||
ADD ./app_sdk_blackarch/app_base.py /app_sdk_blackarch
|
ADD ./app_sdk_blackarch/app_base.py /app_sdk_blackarch
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ module shuffle
|
|||||||
|
|
||||||
go 1.13
|
go 1.13
|
||||||
|
|
||||||
replace github.com/frikky/shuffle-shared => ../../../../git/shuffle-shared
|
//replace github.com/frikky/shuffle-shared => ../../../../git/shuffle-shared
|
||||||
//replace github.com/frikky/kin-openapi => ../../../../git/kin-openapi
|
//replace github.com/frikky/kin-openapi => ../../../../git/kin-openapi
|
||||||
|
|
||||||
require (
|
require (
|
||||||
@@ -18,7 +18,7 @@ require (
|
|||||||
github.com/docker/docker v1.13.1
|
github.com/docker/docker v1.13.1
|
||||||
github.com/docker/go-connections v0.4.0
|
github.com/docker/go-connections v0.4.0
|
||||||
github.com/docker/go-units v0.4.0 // indirect
|
github.com/docker/go-units v0.4.0 // indirect
|
||||||
github.com/frikky/shuffle-shared v0.0.15
|
github.com/frikky/shuffle-shared v0.0.20
|
||||||
github.com/ghodss/yaml v1.0.0
|
github.com/ghodss/yaml v1.0.0
|
||||||
github.com/go-git/go-billy/v5 v5.0.0
|
github.com/go-git/go-billy/v5 v5.0.0
|
||||||
github.com/go-git/go-git/v5 v5.0.0
|
github.com/go-git/go-git/v5 v5.0.0
|
||||||
|
|||||||
+137
-2
@@ -77,6 +77,8 @@ var registryName = "registry.hub.docker.com"
|
|||||||
var runningEnvironment = "onprem"
|
var runningEnvironment = "onprem"
|
||||||
|
|
||||||
var syncUrl = "https://shuffler.io"
|
var syncUrl = "https://shuffler.io"
|
||||||
|
|
||||||
|
//var syncUrl = "http://localhost:5002"
|
||||||
var syncSubUrl = "https://shuffler.io"
|
var syncSubUrl = "https://shuffler.io"
|
||||||
|
|
||||||
//var syncUrl = "http://localhost:5002"
|
//var syncUrl = "http://localhost:5002"
|
||||||
@@ -5463,12 +5465,12 @@ func runInit(ctx context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
iterateOpenApiGithub(fs, dir, "", "")
|
iterateOpenApiGithub(fs, dir, "", "")
|
||||||
log.Printf("Finished downloading extra API samples")
|
log.Printf("[INFO] Finished downloading extra API samples")
|
||||||
}
|
}
|
||||||
|
|
||||||
workflowLocation := os.Getenv("SHUFFLE_DOWNLOAD_WORKFLOW_LOCATION")
|
workflowLocation := os.Getenv("SHUFFLE_DOWNLOAD_WORKFLOW_LOCATION")
|
||||||
if len(workflowLocation) > 0 {
|
if len(workflowLocation) > 0 {
|
||||||
log.Printf("Downloading WORKFLOWS from %s if no workflows - EXTRA workflows", workflowLocation)
|
log.Printf("[INFO] Downloading WORKFLOWS from %s if no workflows - EXTRA workflows", workflowLocation)
|
||||||
q := datastore.NewQuery("workflow").Limit(35)
|
q := datastore.NewQuery("workflow").Limit(35)
|
||||||
var workflows []shuffle.Workflow
|
var workflows []shuffle.Workflow
|
||||||
_, err = dbclient.GetAll(ctx, q, &workflows)
|
_, err = dbclient.GetAll(ctx, q, &workflows)
|
||||||
@@ -5918,6 +5920,138 @@ func handleCloudSetup(resp http.ResponseWriter, request *http.Request) {
|
|||||||
resp.Write(respBody)
|
resp.Write(respBody)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeWorkflowPublic(resp http.ResponseWriter, request *http.Request) {
|
||||||
|
cors := shuffle.HandleCors(resp, request)
|
||||||
|
if cors {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
user, userErr := shuffle.HandleApiAuthentication(resp, request)
|
||||||
|
if userErr != nil {
|
||||||
|
log.Printf("[WARNING] Api authentication failed in make workflow public: %s", userErr)
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
location := strings.Split(request.URL.String(), "/")
|
||||||
|
var fileId string
|
||||||
|
if location[1] == "api" {
|
||||||
|
if len(location) <= 4 {
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fileId = location[4]
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx := context.Background()
|
||||||
|
if strings.Contains(fileId, "?") {
|
||||||
|
fileId = strings.Split(fileId, "?")[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(fileId) != 36 {
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false, "reason": "Workflow ID when getting workflow is not valid"}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
workflow, err := shuffle.GetWorkflow(ctx, fileId)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[WARNING] Workflow %s doesn't exist in app publish. User: %s", fileId, user.Id)
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// CHECK orgs of user, or if user is owner
|
||||||
|
// FIXME - add org check too, and not just owner
|
||||||
|
// Check workflow.Sharing == private / public / org too
|
||||||
|
if user.Id != workflow.Owner || len(user.Id) == 0 {
|
||||||
|
if workflow.OrgId == user.ActiveOrg.Id && user.Role == "admin" {
|
||||||
|
log.Printf("[INFO] User %s is accessing workflow %s as admin", user.Username, workflow.ID)
|
||||||
|
} else {
|
||||||
|
log.Printf("[WARNING] Wrong user (%s) for workflow %s (get workflow)", user.Username, workflow.ID)
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !workflow.IsValid || !workflow.PreviouslySaved {
|
||||||
|
log.Printf("[INFO] Failed uploading workflow because it's invalid or not saved")
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false, "reason": "Invalid workflows are not sharable"}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Starting validation of the POST workflow
|
||||||
|
body, err := ioutil.ReadAll(request.Body)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[WARNING] Body data error on mail: %s", err)
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedWorkflow := shuffle.Workflow{}
|
||||||
|
err = json.Unmarshal(body, &parsedWorkflow)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[WARNING] Unmarshal error on mail: %s", err)
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Super basic validation. Doesn't really matter.
|
||||||
|
if parsedWorkflow.ID != workflow.ID || len(parsedWorkflow.Actions) != len(workflow.Actions) {
|
||||||
|
log.Printf("[WARNING] Bad ID during publish: %s vs %s", workflow.ID, parsedWorkflow.ID)
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if !workflow.IsValid || !workflow.PreviouslySaved {
|
||||||
|
log.Printf("[INFO] Failed uploading new workflow because it's invalid or not saved")
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false, "reason": "Invalid workflows are not sharable"}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
workflowData, err := json.Marshal(parsedWorkflow)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[WARNING] Failed marshalling workflow: %s", err)
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(`{"success": false}`))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sanitization is done in the frontend as well
|
||||||
|
parsedWorkflow = shuffle.SanitizeWorkflow(parsedWorkflow)
|
||||||
|
parsedWorkflow.ID = uuid.NewV4().String()
|
||||||
|
action := shuffle.CloudSyncJob{
|
||||||
|
Type: "workflow",
|
||||||
|
Action: "publish",
|
||||||
|
OrgId: user.ActiveOrg.Id,
|
||||||
|
PrimaryItemId: workflow.ID,
|
||||||
|
SecondaryItem: string(workflowData),
|
||||||
|
FifthItem: user.Id,
|
||||||
|
}
|
||||||
|
|
||||||
|
err = executeCloudAction(action, user.ActiveOrg.SyncConfig.Apikey)
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("[WARNING] Failed cloud PUBLISH: %s", err)
|
||||||
|
resp.WriteHeader(401)
|
||||||
|
resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[INFO] Successfully published workflow %s (%s) TO CLOUD", workflow.Name, workflow.ID)
|
||||||
|
resp.WriteHeader(200)
|
||||||
|
resp.Write([]byte(fmt.Sprintf(`{"success": true}`)))
|
||||||
|
}
|
||||||
|
|
||||||
func initHandlers() {
|
func initHandlers() {
|
||||||
var err error
|
var err error
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
@@ -6041,6 +6175,7 @@ func initHandlers() {
|
|||||||
r.HandleFunc("/api/v1/get_openapi/{key}", getOpenapi).Methods("GET", "OPTIONS")
|
r.HandleFunc("/api/v1/get_openapi/{key}", getOpenapi).Methods("GET", "OPTIONS")
|
||||||
|
|
||||||
// NEW for 0.8.0
|
// NEW for 0.8.0
|
||||||
|
r.HandleFunc("/api/v1/workflows/{key}/publish", makeWorkflowPublic).Methods("POST", "OPTIONS")
|
||||||
r.HandleFunc("/api/v1/cloud/setup", handleCloudSetup).Methods("POST", "OPTIONS")
|
r.HandleFunc("/api/v1/cloud/setup", handleCloudSetup).Methods("POST", "OPTIONS")
|
||||||
r.HandleFunc("/api/v1/orgs", shuffle.HandleGetOrgs).Methods("GET", "OPTIONS")
|
r.HandleFunc("/api/v1/orgs", shuffle.HandleGetOrgs).Methods("GET", "OPTIONS")
|
||||||
r.HandleFunc("/api/v1/orgs/", shuffle.HandleGetOrgs).Methods("GET", "OPTIONS")
|
r.HandleFunc("/api/v1/orgs/", shuffle.HandleGetOrgs).Methods("GET", "OPTIONS")
|
||||||
|
|||||||
@@ -981,7 +981,7 @@ func validateNewWorkerExecution(body []byte) error {
|
|||||||
execution.Status = "FINISHED"
|
execution.Status = "FINISHED"
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("BASEEXECUTION LENGTH: %d", len(baseExecution.Workflow.Actions)+extra)
|
log.Printf("[INFO] BASEEXECUTION LENGTH: %d", len(baseExecution.Workflow.Actions)+extra)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: Add extra here
|
// FIXME: Add extra here
|
||||||
@@ -4782,6 +4782,7 @@ func iterateAppGithubFolders(fs billy.Filesystem, dir []os.FileInfo, extra strin
|
|||||||
workflowapp.Sharing = true
|
workflowapp.Sharing = true
|
||||||
workflowapp.Downloaded = true
|
workflowapp.Downloaded = true
|
||||||
workflowapp.Hash = md5
|
workflowapp.Hash = md5
|
||||||
|
workflowapp.Public = true
|
||||||
|
|
||||||
err = shuffle.SetWorkflowAppDatastore(ctx, workflowapp, workflowapp.ID)
|
err = shuffle.SetWorkflowAppDatastore(ctx, workflowapp, workflowapp.ID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
+6
-6
@@ -1,8 +1,8 @@
|
|||||||
version: '3'
|
version: '3'
|
||||||
services:
|
services:
|
||||||
frontend:
|
frontend:
|
||||||
#build: ./frontend
|
build: ./frontend
|
||||||
image: ghcr.io/frikky/shuffle-frontend:0.8.64
|
image: ghcr.io/frikky/shuffle-frontend:0.8.70
|
||||||
container_name: shuffle-frontend
|
container_name: shuffle-frontend
|
||||||
hostname: shuffle-frontend
|
hostname: shuffle-frontend
|
||||||
ports:
|
ports:
|
||||||
@@ -16,8 +16,8 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
- backend
|
- backend
|
||||||
backend:
|
backend:
|
||||||
#build: ./backend
|
build: ./backend
|
||||||
image: ghcr.io/frikky/shuffle-backend:0.8.64
|
image: ghcr.io/frikky/shuffle-backend:0.8.70
|
||||||
container_name: shuffle-backend
|
container_name: shuffle-backend
|
||||||
hostname: ${BACKEND_HOSTNAME}
|
hostname: ${BACKEND_HOSTNAME}
|
||||||
# Here for debugging:
|
# Here for debugging:
|
||||||
@@ -47,7 +47,7 @@ services:
|
|||||||
- database
|
- database
|
||||||
orborus:
|
orborus:
|
||||||
#build: ./functions/onprem/orborus
|
#build: ./functions/onprem/orborus
|
||||||
image: ghcr.io/frikky/shuffle-orborus:0.8.63
|
image: ghcr.io/frikky/shuffle-orborus:0.8.70
|
||||||
container_name: shuffle-orborus
|
container_name: shuffle-orborus
|
||||||
hostname: shuffle-orborus
|
hostname: shuffle-orborus
|
||||||
networks:
|
networks:
|
||||||
@@ -56,7 +56,7 @@ services:
|
|||||||
- /var/run/docker.sock:/var/run/docker.sock
|
- /var/run/docker.sock:/var/run/docker.sock
|
||||||
environment:
|
environment:
|
||||||
- SHUFFLE_APP_SDK_VERSION=0.8.60
|
- SHUFFLE_APP_SDK_VERSION=0.8.60
|
||||||
- SHUFFLE_WORKER_VERSION=0.8.63
|
- SHUFFLE_WORKER_VERSION=0.8.70
|
||||||
- ORG_ID=${ORG_ID}
|
- ORG_ID=${ORG_ID}
|
||||||
- ENVIRONMENT_NAME=${ENVIRONMENT_NAME}
|
- ENVIRONMENT_NAME=${ENVIRONMENT_NAME}
|
||||||
- BASE_URL=http://${OUTER_HOSTNAME}:${BACKEND_PORT}
|
- BASE_URL=http://${OUTER_HOSTNAME}:${BACKEND_PORT}
|
||||||
|
|||||||
+1
-1
@@ -21,7 +21,7 @@ COPY ./*.json /usr/src/app/
|
|||||||
RUN yarn build
|
RUN yarn build
|
||||||
|
|
||||||
# Production environment
|
# Production environment
|
||||||
FROM nginx:latest
|
FROM nginx:1.19
|
||||||
|
|
||||||
RUN mkdir -p /usr/share/nginx/html/build
|
RUN mkdir -p /usr/share/nginx/html/build
|
||||||
RUN mkdir -p /usr/share/nginx/html/css
|
RUN mkdir -p /usr/share/nginx/html/css
|
||||||
|
|||||||
@@ -39,6 +39,23 @@ const data = [{
|
|||||||
'border-color': '#81c784',
|
'border-color': '#81c784',
|
||||||
'background-width': '100%',
|
'background-width': '100%',
|
||||||
'background-height': '100%',
|
'background-height': '100%',
|
||||||
|
'border-radius': '5px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: `node[app_name="Shuffle Tools"]`,
|
||||||
|
css: {
|
||||||
|
'width': '30px',
|
||||||
|
'height': '30px',
|
||||||
|
'font-size': '0px',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
selector: `node[app_name="Testing"]`,
|
||||||
|
css: {
|
||||||
|
'width': '30px',
|
||||||
|
'height': '30px',
|
||||||
|
'font-size': '0px',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -112,7 +129,7 @@ const data = [{
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
selector: 'node:selected',
|
selector: ':selected',
|
||||||
css: {
|
css: {
|
||||||
'background-color': '#77b0d0',
|
'background-color': '#77b0d0',
|
||||||
'border-color': '#77b0d0',
|
'border-color': '#77b0d0',
|
||||||
@@ -187,6 +204,8 @@ const data = [{
|
|||||||
'border-width': '12px',
|
'border-width': '12px',
|
||||||
'transition-property': 'border-width',
|
'transition-property': 'border-width',
|
||||||
'transition-duration': '0.25s',
|
'transition-duration': '0.25s',
|
||||||
|
'font-size': '30px',
|
||||||
|
'label': 'data(label)',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -92,7 +92,6 @@ const AngularWorkflow = (props) => {
|
|||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
const [bodyWidth, bodyHeight] = useWindowSize();
|
const [bodyWidth, bodyHeight] = useWindowSize();
|
||||||
const appBarSize = 75
|
|
||||||
|
|
||||||
var to_be_copied = ""
|
var to_be_copied = ""
|
||||||
const [cystyle, ] = useState(cytoscapestyle)
|
const [cystyle, ] = useState(cytoscapestyle)
|
||||||
@@ -190,18 +189,22 @@ const AngularWorkflow = (props) => {
|
|||||||
const [workflowExecutions, setWorkflowExecutions] = React.useState([]);
|
const [workflowExecutions, setWorkflowExecutions] = React.useState([]);
|
||||||
const [defaultEnvironmentIndex, setDefaultEnvironmentIndex] = React.useState(0)
|
const [defaultEnvironmentIndex, setDefaultEnvironmentIndex] = React.useState(0)
|
||||||
|
|
||||||
|
// This should all be set once, not on every iteration
|
||||||
|
// Use states and don't update lol
|
||||||
const cloudSyncEnabled = props.userdata !== undefined && props.userdata.active_org !== null && props.userdata.active_org !== undefined ? props.userdata.active_org.cloud_sync === true : false
|
const cloudSyncEnabled = props.userdata !== undefined && props.userdata.active_org !== null && props.userdata.active_org !== undefined ? props.userdata.active_org.cloud_sync === true : false
|
||||||
//const triggerEnvironments = cloudSyncEnabled ? ["cloud", "onprem"] : environments
|
//const triggerEnvironments = cloudSyncEnabled ? ["cloud", "onprem"] : environments
|
||||||
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"
|
const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io"
|
||||||
|
const appBarSize = isCloud ? 75 : 60
|
||||||
const triggerEnvironments = isCloud ? ["cloud"] : ["onprem", "cloud"]
|
const triggerEnvironments = isCloud ? ["cloud"] : ["onprem", "cloud"]
|
||||||
|
|
||||||
const unloadText = 'Are you sure you want to leave without saving (CTRL+S)?'
|
const unloadText = 'Are you sure you want to leave without saving (CTRL+S)?'
|
||||||
|
|
||||||
useBeforeunload(() => {
|
useBeforeunload(() => {
|
||||||
if (!lastSaved) {
|
if (!lastSaved) {
|
||||||
return unloadText
|
return unloadText
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
const [elements, setElements] = useState([])
|
const [elements, setElements] = useState([])
|
||||||
// No point going as fast, as the nodes aren't realtime anymore, but bulk updated.
|
// No point going as fast, as the nodes aren't realtime anymore, but bulk updated.
|
||||||
// Set it from 2500 to 6000 to reduce overall load
|
// Set it from 2500 to 6000 to reduce overall load
|
||||||
@@ -1121,7 +1124,7 @@ const AngularWorkflow = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (responseJson.public) {
|
if (responseJson.public) {
|
||||||
alert.info("This workflow is public. You'll have to save it to make it your own!")
|
alert.info("This workflow is public. You will have to save it to make it your own!")
|
||||||
setLastSaved(false)
|
setLastSaved(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1215,9 +1218,36 @@ const AngularWorkflow = (props) => {
|
|||||||
setSelectedTrigger({})
|
setSelectedTrigger({})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Comparing locations between nodes and setting views
|
||||||
|
const onNodeDrag = (event, newAppAuth) => {
|
||||||
|
//console.log("DRAGGING: ", event.target)
|
||||||
|
//console.log("LEN2: ", event.target.edges.length)
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
event.target.animate({
|
||||||
|
style: {
|
||||||
|
"border-width": "12px",
|
||||||
|
"border-opacity": ".7",
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
duration: animationDuration,
|
||||||
|
})
|
||||||
|
event.target.animate({
|
||||||
|
style: {
|
||||||
|
"border-width": "12px",
|
||||||
|
"border-opacity": ".7",
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
duration: animationDuration,
|
||||||
|
})
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
// Nodeselectbatching:
|
// Nodeselectbatching:
|
||||||
// https://stackoverflow.com/questions/16677856/cy-onselect-callback-only-once
|
// https://stackoverflow.com/questions/16677856/cy-onselect-callback-only-once
|
||||||
const onNodeSelect = (event, newAppAuth) => {
|
const onNodeSelect = (event, newAppAuth) => {
|
||||||
|
|
||||||
const data = event.target.data()
|
const data = event.target.data()
|
||||||
setLastSaved(false)
|
setLastSaved(false)
|
||||||
|
|
||||||
@@ -1619,6 +1649,17 @@ const AngularWorkflow = (props) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!firstrequest && graphSetup && established && props.match.params.key !== workflow.id && workflow.id !== undefined && workflow.id !== null && workflow.id.length > 0) {
|
||||||
|
//console.log(props.match.params.key, workflow.id)
|
||||||
|
//getWorkflow()
|
||||||
|
//setCy()
|
||||||
|
//getWorkflowExecution(props.match.params.key, "")
|
||||||
|
//setEstablished(false)
|
||||||
|
//setGraphSetup(false)
|
||||||
|
window.location.pathname = "/workflows/"+props.match.params.key
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (firstrequest) {
|
if (firstrequest) {
|
||||||
setFirstrequest(false)
|
setFirstrequest(false)
|
||||||
@@ -1642,14 +1683,14 @@ const AngularWorkflow = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// App length necessary cus of cy initialization
|
// App length necessary cus of cy initialization
|
||||||
if (elements.length === 0 && !graphSetup && Object.getOwnPropertyNames(workflow).length > 0) {
|
if (elements.length === 0 && workflow.actions !== undefined && !graphSetup && Object.getOwnPropertyNames(workflow).length > 0) {
|
||||||
setGraphSetup(true)
|
setGraphSetup(true)
|
||||||
setupGraph()
|
setupGraph()
|
||||||
} else if (!established && cy !== undefined && apps !== null && apps !== undefined && apps.length > 0 && Object.getOwnPropertyNames(workflow).length > 0 && authLoaded){
|
} else if (!established && cy !== undefined && apps !== null && apps !== undefined && apps.length > 0 && Object.getOwnPropertyNames(workflow).length > 0 && authLoaded){
|
||||||
//This part has to load LAST, as it's kind of not async.
|
//This part has to load LAST, as it's kind of not async.
|
||||||
//This means we need everything else to happen first.
|
//This means we need everything else to happen first.
|
||||||
console.log("AUTH IN HERE: ", appAuthentication)
|
//
|
||||||
|
//console.log("IN THIS PART AGAIN")
|
||||||
|
|
||||||
setEstablished(true)
|
setEstablished(true)
|
||||||
cy.edgehandles({
|
cy.edgehandles({
|
||||||
@@ -1677,6 +1718,9 @@ const AngularWorkflow = (props) => {
|
|||||||
cy.on('mouseover', 'node', (e) => onNodeHover(e))
|
cy.on('mouseover', 'node', (e) => onNodeHover(e))
|
||||||
cy.on('mouseout', 'node', (e) => onNodeHoverOut(e))
|
cy.on('mouseout', 'node', (e) => onNodeHoverOut(e))
|
||||||
|
|
||||||
|
// Handles dragging
|
||||||
|
//cy.on('drag', 'node', (e) => onNodeDrag(e))
|
||||||
|
|
||||||
//cy.on('mouseover', 'node', () => $(targetElement).addClass('mouseover'));
|
//cy.on('mouseover', 'node', () => $(targetElement).addClass('mouseover'));
|
||||||
|
|
||||||
//cy.on('cxttapstart', 'node', (e) => edgeHandler.start(e.target))
|
//cy.on('cxttapstart', 'node', (e) => edgeHandler.start(e.target))
|
||||||
@@ -1711,7 +1755,7 @@ const AngularWorkflow = (props) => {
|
|||||||
const onNodeHover = (event) => {
|
const onNodeHover = (event) => {
|
||||||
event.target.animate({
|
event.target.animate({
|
||||||
style: {
|
style: {
|
||||||
"border-width": "5px",
|
"border-width": "7px",
|
||||||
"border-opacity": ".7",
|
"border-opacity": ".7",
|
||||||
}
|
}
|
||||||
}, {
|
}, {
|
||||||
@@ -1727,17 +1771,23 @@ const AngularWorkflow = (props) => {
|
|||||||
|
|
||||||
// This is here to have a proper transition for lines
|
// This is here to have a proper transition for lines
|
||||||
const onEdgeHover = (event) => {
|
const onEdgeHover = (event) => {
|
||||||
|
if (event === null || event === undefined) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
const sourcecolor = cy.getElementById(event.target.data("source")).style("border-color")
|
const sourcecolor = cy.getElementById(event.target.data("source")).style("border-color")
|
||||||
const targetcolor = cy.getElementById(event.target.data("target")).style("border-color")
|
const targetcolor = cy.getElementById(event.target.data("target")).style("border-color")
|
||||||
event.target.animate({
|
if (sourcecolor !== null && sourcecolor !== undefined && targetcolor !== null && targetcolor !== undefined) {
|
||||||
style: {
|
event.target.animate({
|
||||||
"line-fill": "linear-gradient",
|
style: {
|
||||||
'target-arrow-color': targetcolor,
|
"line-fill": "linear-gradient",
|
||||||
"line-gradient-stop-colors": [sourcecolor, targetcolor],
|
'target-arrow-color': targetcolor,
|
||||||
"line-gradient-stop-positions": [0, 1],
|
"line-gradient-stop-colors": [sourcecolor, targetcolor],
|
||||||
},
|
"line-gradient-stop-positions": [0, 1],
|
||||||
duration: 0,
|
},
|
||||||
})
|
duration: 0,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -1799,6 +1849,31 @@ const AngularWorkflow = (props) => {
|
|||||||
conditions: conditions,
|
conditions: conditions,
|
||||||
hasErrors: branch.has_errors
|
hasErrors: branch.has_errors
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// This is an attempt at prettier edges. The numbers are weird to work with.
|
||||||
|
/*
|
||||||
|
//http://manual.graphspace.org/projects/graphspace-python/en/latest/demos/edge-types.html
|
||||||
|
const sourcenode = actions.find(node => node.data._id === branch.source_id)
|
||||||
|
const destinationnode = actions.find(node => node.data._id === branch.destination_id)
|
||||||
|
if (sourcenode !== undefined && destinationnode !== undefined && branch.source_id !== branch.destination_id) {
|
||||||
|
//node.data._id = action["id"]
|
||||||
|
console.log("SOURCE: ", sourcenode.position)
|
||||||
|
console.log("DESTINATIONNODE: ", destinationnode.position)
|
||||||
|
|
||||||
|
var opposite = true
|
||||||
|
if (sourcenode.position.x > destinationnode.position.x) {
|
||||||
|
opposite = false
|
||||||
|
} else {
|
||||||
|
opposite = true
|
||||||
|
}
|
||||||
|
|
||||||
|
edge.style = {
|
||||||
|
'control-point-distance': opposite ? ["25%", "-75%"] : ["-10%", "90%"],
|
||||||
|
'control-point-weight': ['0.3', '0.7'],
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
return edge;
|
return edge;
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -4506,7 +4581,7 @@ const AngularWorkflow = (props) => {
|
|||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
console.log("CHANGE FIELD")
|
//console.log("CHANGE FIELD")
|
||||||
}}
|
}}
|
||||||
onBlur={(e) => {
|
onBlur={(e) => {
|
||||||
changeActionVariable(data.action_field, e.target.value)
|
changeActionVariable(data.action_field, e.target.value)
|
||||||
@@ -4803,6 +4878,7 @@ const AngularWorkflow = (props) => {
|
|||||||
onClick={() => {
|
onClick={() => {
|
||||||
setSelectedEdge({})
|
setSelectedEdge({})
|
||||||
|
|
||||||
|
|
||||||
var data = {
|
var data = {
|
||||||
condition: conditionValue,
|
condition: conditionValue,
|
||||||
source: sourceValue,
|
source: sourceValue,
|
||||||
@@ -4821,6 +4897,18 @@ const AngularWorkflow = (props) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var label = ""
|
||||||
|
if (selectedEdge.conditions.length === 1) {
|
||||||
|
label = selectedEdge.conditions.length+" condition"
|
||||||
|
} else if (selectedEdge.conditions.length > 1) {
|
||||||
|
label = selectedEdge.conditions.length+" conditions"
|
||||||
|
}
|
||||||
|
|
||||||
|
var currentedge = cy.getElementById(selectedEdge.id)
|
||||||
|
if (currentedge !== undefined && currentedge !== null) {
|
||||||
|
currentedge.data().label = label
|
||||||
|
}
|
||||||
|
|
||||||
setSelectedEdge(selectedEdge)
|
setSelectedEdge(selectedEdge)
|
||||||
workflow.branches[selectedEdgeIndex] = selectedEdge
|
workflow.branches[selectedEdgeIndex] = selectedEdge
|
||||||
setWorkflow(workflow)
|
setWorkflow(workflow)
|
||||||
@@ -6494,7 +6582,7 @@ const AngularWorkflow = (props) => {
|
|||||||
</span>
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
{/* <FileMenu /> */}
|
{/* <FileMenu /> */}
|
||||||
<WorkflowMenu />
|
{workflow.configuration !== null && workflow.configuration !== undefined && workflow.configuration.exit_on_error !== undefined ? <WorkflowMenu /> : null}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
@@ -7211,6 +7299,7 @@ const AngularWorkflow = (props) => {
|
|||||||
stylesheet={cystyle}
|
stylesheet={cystyle}
|
||||||
boxSelectionEnabled={true}
|
boxSelectionEnabled={true}
|
||||||
autounselectify={false}
|
autounselectify={false}
|
||||||
|
showGrid={true}
|
||||||
cy={(incy) => {
|
cy={(incy) => {
|
||||||
// FIXME: There's something specific loading when
|
// FIXME: There's something specific loading when
|
||||||
// you do the first hover of a node. Why is this different?
|
// you do the first hover of a node. Why is this different?
|
||||||
|
|||||||
@@ -1565,9 +1565,9 @@ const AppCreator = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Url verification
|
// Url verification
|
||||||
if (currentAction.url.length === 0) {
|
//if (currentAction.url.length === 0) {
|
||||||
errormessage.push("URL path can't be empty.")
|
// errormessage.push("URL path can't be empty.")
|
||||||
} else if (!currentAction.url.startsWith("/") && baseUrl.length > 0) {
|
if (!currentAction.url.startsWith("/") && baseUrl.length > 0 && currentAction.url.length > 0) {
|
||||||
errormessage.push("URL must start with /")
|
errormessage.push("URL must start with /")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+108
-95
@@ -68,11 +68,9 @@ const flexContainerStyle = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const flexBoxStyle = {
|
const flexBoxStyle = {
|
||||||
width: "333px",
|
width: 333,
|
||||||
height: "125px",
|
height: 125,
|
||||||
margin: "10px",
|
borderRadius: 4,
|
||||||
borderRadius: "4px",
|
|
||||||
boxShadow: "0px 1px 2px rgba(0, 0, 0, 0.16), 0px 2px 4px rgba(0, 0, 0, 0.12), 0px 1px 8px rgba(0, 0, 0, 0.1)",
|
|
||||||
boxSizing: "border-box",
|
boxSizing: "border-box",
|
||||||
letterSpacing: "0.4px",
|
letterSpacing: "0.4px",
|
||||||
color: "#D6791E",
|
color: "#D6791E",
|
||||||
@@ -299,37 +297,37 @@ const MyView = (props) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const paperAppContainer = {
|
const paperAppContainer = {
|
||||||
cursor: "pointer",
|
|
||||||
display: "flex",
|
display: "flex",
|
||||||
flexWrap: 'wrap',
|
flexWrap: 'wrap',
|
||||||
alignContent: "space-between",
|
alignContent: "space-between",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const paperAppStyle = {
|
const paperAppStyle = {
|
||||||
minHeight: "148px",
|
minHeight: 130,
|
||||||
width: "333px",
|
width: "100%",
|
||||||
margin: "10px",
|
|
||||||
color: "white",
|
color: "white",
|
||||||
backgroundColor: surfaceColor,
|
backgroundColor: surfaceColor,
|
||||||
|
padding: "12px 12px 0px 15px",
|
||||||
borderRadius: 5,
|
borderRadius: 5,
|
||||||
padding: "10px",
|
|
||||||
cursor: "pointer",
|
|
||||||
display: "flex",
|
display: "flex",
|
||||||
boxSizing: "border-box",
|
boxSizing: "border-box",
|
||||||
|
position: "relative",
|
||||||
}
|
}
|
||||||
|
|
||||||
const gridContainer = {
|
const gridContainer = {
|
||||||
cursor: "pointer",
|
|
||||||
height: "auto",
|
height: "auto",
|
||||||
color: "white",
|
color: "white",
|
||||||
margin: "10px",
|
margin: "10px",
|
||||||
backgroundColor: surfaceColor,
|
backgroundColor: surfaceColor,
|
||||||
}
|
}
|
||||||
|
|
||||||
const workFlowActionStyle = {
|
const workflowActionStyle = {
|
||||||
flex: "1",
|
flex: "1",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
width: "150px",
|
width: 150,
|
||||||
|
height: 44,
|
||||||
justifyContent: "space-between",
|
justifyContent: "space-between",
|
||||||
overflow: "hidden"
|
overflow: "hidden"
|
||||||
}
|
}
|
||||||
@@ -535,23 +533,28 @@ const MyView = (props) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
const getWorkFlowMeta = (data) => {
|
const getWorkflowMeta = (data) => {
|
||||||
|
let triggers = 0
|
||||||
let schedules = 0
|
let schedules = 0
|
||||||
let webhooks = 0
|
let webhooks = 0
|
||||||
let webhookImg = ""
|
let subflows = 0
|
||||||
let scheduleImg = ""
|
|
||||||
if (data.triggers !== undefined && data.triggers !== null && data.triggers.length > 0) {
|
if (data.triggers !== undefined && data.triggers !== null && data.triggers.length > 0) {
|
||||||
|
triggers = data.triggers.length
|
||||||
for (let key in data.triggers) {
|
for (let key in data.triggers) {
|
||||||
|
|
||||||
if (data.triggers[key].app_name === "Webhook") {
|
if (data.triggers[key].app_name === "Webhook") {
|
||||||
webhooks += 1
|
webhooks += 1
|
||||||
webhookImg = data.triggers[key].large_image
|
//webhookImg = data.triggers[key].large_image
|
||||||
} else if (data.triggers[key].app_name === "Schedule") {
|
} else if (data.triggers[key].app_name === "Schedule") {
|
||||||
schedules += 1
|
schedules += 1
|
||||||
scheduleImg = data.triggers[key].large_image
|
//scheduleImg = data.triggers[key].large_image
|
||||||
|
} else if (data.triggers[key].app_name === "Subflow") {
|
||||||
|
subflows += 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return [schedules, webhooks, webhookImg, scheduleImg];
|
|
||||||
|
return [triggers, schedules, webhooks, subflows]
|
||||||
}
|
}
|
||||||
|
|
||||||
// dropdown with copy etc I guess
|
// dropdown with copy etc I guess
|
||||||
@@ -565,9 +568,9 @@ const MyView = (props) => {
|
|||||||
boxWidth = "4px"
|
boxWidth = "4px"
|
||||||
}
|
}
|
||||||
|
|
||||||
var boxColor = "orange"
|
var boxColor = "#FECC00"
|
||||||
if (data.is_valid) {
|
if (data.is_valid) {
|
||||||
boxColor = "green"
|
boxColor = "#86c142"
|
||||||
}
|
}
|
||||||
|
|
||||||
const menuClick = (event) => {
|
const menuClick = (event) => {
|
||||||
@@ -575,45 +578,57 @@ const MyView = (props) => {
|
|||||||
setAnchorEl(event.currentTarget);
|
setAnchorEl(event.currentTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var parsedName = data.name
|
||||||
|
console.log("LEN: ", parsedName.length)
|
||||||
|
if (parsedName !== undefined && parsedName !== null && parsedName.length > 25) {
|
||||||
|
parsedName = parsedName.slice(0,25)+".."
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const actions = data.actions !== null ? data.actions.length : 0
|
const actions = data.actions !== null ? data.actions.length : 0
|
||||||
|
const [triggers, schedules, webhooks, subflows] = getWorkflowMeta(data)
|
||||||
|
|
||||||
const [schedules, webhooks] = getWorkFlowMeta(data);
|
|
||||||
|
|
||||||
const imgSize = 25
|
|
||||||
return (
|
return (
|
||||||
<Paper square style={paperAppStyle} onClick={(e) => {
|
<Grid item xs={4} spacing={4} style={{padding: 10,}}>
|
||||||
}}>
|
<Paper square style={paperAppStyle} >
|
||||||
<div style={{width: boxWidth, backgroundColor: boxColor,}} onClick={() => {
|
<div style={{position: "absolute", bottom: 1, left: 1, height: 12, width: 12, backgroundColor: boxColor, borderRadius: "0 100px 0 0",}} />
|
||||||
if (selectedWorkflow.id !== data.id) {
|
|
||||||
setSelectedWorkflow(data)
|
|
||||||
//getWorkflowExecution(data.id)
|
|
||||||
}
|
|
||||||
}}/>
|
|
||||||
<Grid container style={{margin: "0px 10px 0px 10px", flex: 1}}>
|
|
||||||
<Grid item style={{display: "flex", flexDirection: "column", width: "100%"}}>
|
<Grid item style={{display: "flex", flexDirection: "column", width: "100%"}}>
|
||||||
<Grid item style={{flex: 1, display: "flex"}}>
|
<Grid item style={{flex: 1, display: "flex", maxHeight: 34,}}>
|
||||||
<div onClick={() => {
|
<Typography variant="h6" style={{marginBottom: 0, paddingBottom: 0, maxHeight: 30,}}>
|
||||||
if (selectedWorkflow.id !== data.id) {
|
{parsedName}
|
||||||
setSelectedWorkflow(data)
|
</Typography>
|
||||||
//getWorkflowExecution(data.id)
|
|
||||||
}
|
|
||||||
}}>
|
|
||||||
<Typography variant="h6">
|
|
||||||
{data.name}
|
|
||||||
</Typography>
|
|
||||||
</div>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item style={workFlowActionStyle}>
|
<Grid item style={workflowActionStyle}>
|
||||||
<Tooltip color="primary" title="Edit workflow" placement="bottom">
|
<Tooltip color="primary" title="Action amount" placement="bottom">
|
||||||
<BubbleChartIcon/>
|
<span style={{color: "#979797", display: "flex"}}>
|
||||||
|
<BubbleChartIcon style={{marginTop: "auto", marginBottom: "auto",}} />
|
||||||
|
<Typography style={{marginLeft: 5, marginTop: "auto", marginBottom: "auto",}}>
|
||||||
|
{actions}
|
||||||
|
</Typography>
|
||||||
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
<Tooltip color="primary" title="Execute workflow" placement="bottom">
|
<Tooltip color="primary" title="Trigger amount" placement="bottom">
|
||||||
<PlayArrowIcon color="secondary" disabled={!data.is_valid} onClick={() => executeWorkflow(data.id)} />
|
<span style={{marginLeft: 10, color: "#979797", display: "flex"}}>
|
||||||
|
<RestoreIcon style={{color: "#979797", marginTop: "auto", marginBottom: "auto",}}/>
|
||||||
|
<Typography style={{marginLeft: 5, marginTop: "auto", marginBottom: "auto",}}>
|
||||||
|
{triggers}
|
||||||
|
</Typography>
|
||||||
|
</span>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
<Tooltip color="primary" title="Subflows used" placement="bottom">
|
||||||
|
<span style={{marginLeft: 10, display: "flex", color: "#979797", }}>
|
||||||
|
<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg" style={{color: "#979797", marginTop: "auto", marginBottom: "auto",}}>
|
||||||
|
<path d="M0 0H15V15H0V0ZM16 16H18V18H16V16ZM16 13H18V15H16V13ZM16 10H18V12H16V10ZM16 7H18V9H16V7ZM16 4H18V6H16V4ZM13 16H15V18H13V16ZM10 16H12V18H10V16ZM7 16H9V18H7V16ZM4 16H6V18H4V16Z" fill="#979797"/>
|
||||||
|
</svg>
|
||||||
|
<Typography style={{marginLeft: 5, marginTop: "auto", marginBottom: "auto",}}>
|
||||||
|
{subflows}
|
||||||
|
</Typography>
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
{/*
|
||||||
<Tooltip color="primary" title={`Actions: ${data.actions.length}`} placement="bottom">
|
<Tooltip color="primary" title={`Actions: ${data.actions.length}`} placement="bottom">
|
||||||
<AppsIcon />
|
<AppsIcon />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
{webhooks > 0 ?
|
|
||||||
<Tooltip color="primary" title={`Webhooks: ${webhooks}`} placement="bottom">
|
<Tooltip color="primary" title={`Webhooks: ${webhooks}`} placement="bottom">
|
||||||
<RestoreIcon />
|
<RestoreIcon />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
@@ -623,8 +638,9 @@ const MyView = (props) => {
|
|||||||
<RestoreIcon />
|
<RestoreIcon />
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
: null}
|
: null}
|
||||||
|
*/}
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item style={{flex: "1", justifyContent: "left", overflow: "hidden"}}>
|
<Grid item style={{flex: "1", justifyContent: "left", overflow: "hidden", marginTop: 5,}}>
|
||||||
{data.tags !== undefined ?
|
{data.tags !== undefined ?
|
||||||
data.tags.map((tag, index) => {
|
data.tags.map((tag, index) => {
|
||||||
if (index >= 3) {
|
if (index >= 3) {
|
||||||
@@ -634,7 +650,7 @@ const MyView = (props) => {
|
|||||||
return (
|
return (
|
||||||
<Chip
|
<Chip
|
||||||
key={index}
|
key={index}
|
||||||
style={{height: 30, marginRight: 5, marginTop: 2, cursor: "pointer",}}
|
style={{backgroundColor: "#3d3f43", height: 30, marginRight: 5, paddingLeft: 5, paddingRight: 5, height: 28, cursor: "pointer", borderColor: "#3d3f43", color: "white",}}
|
||||||
label={tag}
|
label={tag}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
color="primary"
|
color="primary"
|
||||||
@@ -644,30 +660,29 @@ const MyView = (props) => {
|
|||||||
: null}
|
: null}
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
|
||||||
{data.actions !== undefined && data.actions !== null ?
|
{data.actions !== undefined && data.actions !== null ?
|
||||||
<Grid item style={{display:"flex",flexDirection:"column",justifyContent:"space-between"}}>
|
<Grid item style={{display:"flex",flexDirection:"column", justifyContent:"space-between"}}>
|
||||||
<Grid>
|
<Grid>
|
||||||
<IconButton
|
<IconButton
|
||||||
aria-label="more"
|
aria-label="more"
|
||||||
aria-controls="long-menu"
|
aria-controls="long-menu"
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
style={{color: "white"}}
|
style={{color: "white"}}
|
||||||
onClick={menuClick}
|
onClick={menuClick}
|
||||||
style={{padding:"0px",color:"white"}}
|
style={{padding:"0px",color:"white", color:"#979797"}}
|
||||||
>
|
>
|
||||||
<MoreVertIcon />
|
<MoreVertIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
<Menu
|
<Menu
|
||||||
id="long-menu"
|
id="long-menu"
|
||||||
anchorEl={anchorEl}
|
anchorEl={anchorEl}
|
||||||
keepMounted
|
keepMounted
|
||||||
open={open}
|
open={open}
|
||||||
onClose={() => {
|
onClose={() => {
|
||||||
setOpen(false)
|
setOpen(false)
|
||||||
setAnchorEl(null)
|
setAnchorEl(null)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<MenuItem style={{backgroundColor: inputColor, color: "white"}} onClick={() => {
|
<MenuItem style={{backgroundColor: inputColor, color: "white"}} onClick={() => {
|
||||||
setModalOpen(true)
|
setModalOpen(true)
|
||||||
setEditingWorkflow(data)
|
setEditingWorkflow(data)
|
||||||
@@ -685,26 +700,25 @@ const MyView = (props) => {
|
|||||||
exportWorkflow(data)
|
exportWorkflow(data)
|
||||||
setOpen(false)
|
setOpen(false)
|
||||||
}} key={"export"}>{"Export"}</MenuItem>
|
}} key={"export"}>{"Export"}</MenuItem>
|
||||||
<MenuItem style={{backgroundColor: inputColor, color: "white"}} onClick={() => {
|
<MenuItem style={{backgroundColor: inputColor, color: "white"}} onClick={() => {
|
||||||
setDeleteModalOpen(true)
|
setDeleteModalOpen(true)
|
||||||
setSelectedWorkflowId(data.id)
|
setSelectedWorkflowId(data.id)
|
||||||
setOpen(false)
|
setOpen(false)
|
||||||
}} key={"delete"}>{"Delete"}</MenuItem>
|
}} key={"delete"}>{"Delete"}</MenuItem>
|
||||||
|
|
||||||
</Menu>
|
</Menu>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid>
|
<Grid>
|
||||||
<Link to={"/workflows/"+data.id}>
|
<Link to={"/workflows/"+data.id}>
|
||||||
<Tooltip title="Edit workflow" placement="bottom">
|
<Tooltip title="Edit workflow" placement="bottom">
|
||||||
<EditIcon style={{background: "#F85A3E",
|
<EditIcon style={{background: "#F85A3E", borderRadius: "4px", color: "black", height: 20, width: 20, padding: 7, fontSize: "small"}} />
|
||||||
boxShadow: "0px 1px 2px rgba(0, 0, 0, 0.16), 0px 2px 4px rgba(0, 0, 0, 0.12), 0px 1px 8px rgba(0, 0, 0, 0.1)",
|
</Tooltip>
|
||||||
borderRadius: "4px", color: "black", height: "30px", "width": "30px", fontSize: "small"}} />
|
</Link>
|
||||||
</Tooltip>
|
</Grid>
|
||||||
</Link>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
: null}
|
: null}
|
||||||
</Paper>
|
</Paper>
|
||||||
|
</Grid>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -772,7 +786,6 @@ borderRadius: "4px", color: "black", height: "30px", "width": "30px", fontSize:
|
|||||||
marginTop: "5px",
|
marginTop: "5px",
|
||||||
color: "white",
|
color: "white",
|
||||||
backgroundColor: surfaceColor,
|
backgroundColor: surfaceColor,
|
||||||
cursor: "pointer",
|
|
||||||
display: "flex",
|
display: "flex",
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1294,13 +1307,12 @@ borderRadius: "4px", color: "black", height: "30px", "width": "30px", fontSize:
|
|||||||
let workflowData = "";
|
let workflowData = "";
|
||||||
if (workflows.length > 0) {
|
if (workflows.length > 0) {
|
||||||
const columns = [
|
const columns = [
|
||||||
{ field: 'id', headerName: 'ID', width: 70, sortable: false, },
|
|
||||||
{ field: 'title', headerName: 'Title', width: 330, },
|
{ field: 'title', headerName: 'Title', width: 330, },
|
||||||
{ field: 'actions', headerName: 'Actions', width: 200, sortable: false,
|
{ field: 'actions', headerName: 'Actions', width: 200, sortable: false,
|
||||||
disableClickEventBubbling: true,
|
disableClickEventBubbling: true,
|
||||||
renderCell: (params) => {
|
renderCell: (params) => {
|
||||||
const data = params.row.record;
|
const data = params.row.record;
|
||||||
let [schedules, webhooks] = getWorkFlowMeta(data);
|
let [triggers, schedules, webhooks, subflows] = getWorkflowMeta(data);
|
||||||
|
|
||||||
return <Grid item>
|
return <Grid item>
|
||||||
<Link to={"/workflows/"+data.id}>
|
<Link to={"/workflows/"+data.id}>
|
||||||
@@ -1340,10 +1352,11 @@ borderRadius: "4px", color: "black", height: "30px", "width": "30px", fontSize:
|
|||||||
if (index >= 3) {
|
if (index >= 3) {
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Chip
|
<Chip
|
||||||
key={index}
|
key={index}
|
||||||
style={{height: 30, marginRight: 5, marginTop: 2, cursor: "pointer",}}
|
style={{backgroundColor: "#3d3f43", height: 30, marginRight: 5, paddingLeft: 5, paddingRight: 5, height: 28, cursor: "pointer", borderColor: "#3d3f43", color: "white",}}
|
||||||
label={tag}
|
label={tag}
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
color="primary"
|
color="primary"
|
||||||
@@ -1445,13 +1458,13 @@ borderRadius: "4px", color: "black", height: "30px", "width": "30px", fontSize:
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{view === "grid" && (
|
{view === "grid" && (
|
||||||
<div style={paperAppContainer}>
|
<Grid container spacing={4} style={paperAppContainer}>
|
||||||
{workflows.map((data, index) => {
|
{workflows.map((data, index) => {
|
||||||
return (
|
return (
|
||||||
<WorkflowPaper key={index} data={data} />
|
<WorkflowPaper key={index} data={data} />
|
||||||
)
|
)
|
||||||
})}
|
})}
|
||||||
</div>
|
</Grid>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{view === "list" && (
|
{view === "list" && (
|
||||||
|
|||||||
@@ -447,12 +447,12 @@ const Workflows = (props) => {
|
|||||||
for (var branchkey in data.branches) {
|
for (var branchkey in data.branches) {
|
||||||
const branch = data.branches[branchkey]
|
const branch = data.branches[branchkey]
|
||||||
if (branch.source_id === data.actions[key].id) {
|
if (branch.source_id === data.actions[key].id) {
|
||||||
console.log("CHANGING SOURCE ID IN ACTION")
|
//console.log("CHANGING SOURCE ID IN ACTION")
|
||||||
branch.source_id = newId
|
branch.source_id = newId
|
||||||
}
|
}
|
||||||
|
|
||||||
if (branch.destination_id === data.actions[key].id) {
|
if (branch.destination_id === data.actions[key].id) {
|
||||||
console.log("CHANGING DESTINATION ID IN ACTION")
|
//console.log("CHANGING DESTINATION ID IN ACTION")
|
||||||
branch.destination_id = newId
|
branch.destination_id = newId
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -505,10 +505,8 @@ const Workflows = (props) => {
|
|||||||
data = sanitizeWorkflow(data)
|
data = sanitizeWorkflow(data)
|
||||||
alert.info("Sanitizing and publishing "+data.name)
|
alert.info("Sanitizing and publishing "+data.name)
|
||||||
|
|
||||||
const url = isCloud ? globalUrl : "https://shuffler.io"
|
|
||||||
|
|
||||||
// This ALWAYS talks to Shuffle cloud
|
// This ALWAYS talks to Shuffle cloud
|
||||||
fetch(url+"/api/v1/workflows/"+data.id+"/publish", {
|
fetch(globalUrl+"/api/v1/workflows/"+data.id+"/publish", {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'application/json',
|
'Content-Type': 'application/json',
|
||||||
@@ -521,7 +519,11 @@ const Workflows = (props) => {
|
|||||||
if (response.status !== 200) {
|
if (response.status !== 200) {
|
||||||
console.log("Status not 200 for workflow publish :O!")
|
console.log("Status not 200 for workflow publish :O!")
|
||||||
} else {
|
} else {
|
||||||
alert.success("Successfully published workflow")
|
if (isCloud) {
|
||||||
|
alert.success("Successfully published workflow")
|
||||||
|
} else {
|
||||||
|
alert.success("Successfully published workflow to https://shuffler.io")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return response.json()
|
return response.json()
|
||||||
@@ -690,12 +692,10 @@ const Workflows = (props) => {
|
|||||||
setNewWorkflowTags(JSON.parse(JSON.stringify(data.tags)))
|
setNewWorkflowTags(JSON.parse(JSON.stringify(data.tags)))
|
||||||
}
|
}
|
||||||
}} key={"change"}>{"Change details"}</MenuItem>
|
}} key={"change"}>{"Change details"}</MenuItem>
|
||||||
{isCloud ?
|
<MenuItem style={{backgroundColor: inputColor, color: "white"}} onClick={() => {
|
||||||
<MenuItem style={{backgroundColor: inputColor, color: "white"}} onClick={() => {
|
console.log("Should publish", data)
|
||||||
console.log("Should publish", data)
|
publishWorkflow(data)
|
||||||
publishWorkflow(data)
|
}} key={"publish"}>{"Publish Workflow"}</MenuItem>
|
||||||
}} key={"publish"}>{"Publish Workflow"}</MenuItem>
|
|
||||||
: null}
|
|
||||||
<MenuItem style={{backgroundColor: inputColor, color: "white"}} onClick={() => {
|
<MenuItem style={{backgroundColor: inputColor, color: "white"}} onClick={() => {
|
||||||
copyWorkflow(data)
|
copyWorkflow(data)
|
||||||
setOpen(false)
|
setOpen(false)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
NAME=shuffle-orborus
|
NAME=shuffle-orborus
|
||||||
VERSION=0.8.64
|
VERSION=0.8.70
|
||||||
|
|
||||||
echo "Running docker build with $NAME:$VERSION"
|
echo "Running docker build with $NAME:$VERSION"
|
||||||
#docker rmi frikky/shuffle:$NAME --force
|
#docker rmi frikky/shuffle:$NAME --force
|
||||||
|
|||||||
@@ -248,7 +248,7 @@ func initializeImages() {
|
|||||||
log.Printf("[WARNING] SHUFFLE_APP_SDK_VERSION not defined. Defaulting to %s", appSdkVersion)
|
log.Printf("[WARNING] SHUFFLE_APP_SDK_VERSION not defined. Defaulting to %s", appSdkVersion)
|
||||||
}
|
}
|
||||||
if workerVersion == "" {
|
if workerVersion == "" {
|
||||||
workerVersion = "0.8.64"
|
workerVersion = "0.8.70"
|
||||||
log.Printf("[WARNING] SHUFFLE_WORKER_VERSION not defined. Defaulting to %s", workerVersion)
|
log.Printf("[WARNING] SHUFFLE_WORKER_VERSION not defined. Defaulting to %s", workerVersion)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ RUN go get github.com/docker/docker/api/types && \
|
|||||||
go get github.com/docker/docker/client && \
|
go get github.com/docker/docker/client && \
|
||||||
go get github.com/gorilla/mux && \
|
go get github.com/gorilla/mux && \
|
||||||
go get github.com/patrickmn/go-cache && \
|
go get github.com/patrickmn/go-cache && \
|
||||||
go get github.com/frikky/shuffle-shared
|
go get github.com/frikky/shuffle-shared && \
|
||||||
|
go get github.com/satori/go.uuid
|
||||||
|
|
||||||
RUN go build
|
RUN go build
|
||||||
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o worker .
|
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o worker .
|
||||||
@@ -21,7 +22,7 @@ FROM alpine:3.12
|
|||||||
|
|
||||||
ENV SHUFFLE_BASE_IMAGE_REGISTRY=docker.io
|
ENV SHUFFLE_BASE_IMAGE_REGISTRY=docker.io
|
||||||
ENV SHUFFLE_BASE_IMAGE_NAME=frikky/shuffle
|
ENV SHUFFLE_BASE_IMAGE_NAME=frikky/shuffle
|
||||||
ENV SHUFFLE_BASE_IMAGE_TAG_SUFFIX=0.8.5
|
ENV SHUFFLE_BASE_IMAGE_TAG_SUFFIX=0.8.70
|
||||||
|
|
||||||
RUN apk add --no-cache bash
|
RUN apk add --no-cache bash
|
||||||
COPY --from=builder /app/ /
|
COPY --from=builder /app/ /
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
NAME=shuffle-worker
|
NAME=shuffle-worker
|
||||||
VERSION=0.8.64
|
VERSION=0.8.70
|
||||||
|
|
||||||
echo "Running docker build with $NAME:$VERSION"
|
echo "Running docker build with $NAME:$VERSION"
|
||||||
#CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o worker.bin .
|
#CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o worker.bin .
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import (
|
|||||||
//"github.com/docker/docker/api/types/filters"
|
//"github.com/docker/docker/api/types/filters"
|
||||||
"github.com/docker/docker/api/types/mount"
|
"github.com/docker/docker/api/types/mount"
|
||||||
dockerclient "github.com/docker/docker/client"
|
dockerclient "github.com/docker/docker/client"
|
||||||
|
"github.com/satori/go.uuid"
|
||||||
|
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"github.com/patrickmn/go-cache"
|
"github.com/patrickmn/go-cache"
|
||||||
@@ -278,7 +279,7 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env []
|
|||||||
|
|
||||||
// Waiting to see if it exits.. Stupid, but stable(r)
|
// Waiting to see if it exits.. Stupid, but stable(r)
|
||||||
if workflowExecution.ExecutionSource != "default" {
|
if workflowExecution.ExecutionSource != "default" {
|
||||||
log.Printf("Handling NON-default execution source %s - NOT waiting and validating!", workflowExecution.ExecutionSource)
|
log.Printf("[INFO] Handling NON-default execution source %s - NOT waiting and validating!", workflowExecution.ExecutionSource)
|
||||||
} else if workflowExecution.ExecutionSource == "default" {
|
} else if workflowExecution.ExecutionSource == "default" {
|
||||||
time.Sleep(2 * time.Second)
|
time.Sleep(2 * time.Second)
|
||||||
|
|
||||||
@@ -850,12 +851,13 @@ func handleExecutionResult(workflowExecution shuffle.WorkflowExecution) {
|
|||||||
appname = strings.Replace(appname, ".", "-", -1)
|
appname = strings.Replace(appname, ".", "-", -1)
|
||||||
appversion = strings.Replace(appversion, ".", "-", -1)
|
appversion = strings.Replace(appversion, ".", "-", -1)
|
||||||
|
|
||||||
image := fmt.Sprintf("%s:%s_%s", baseimagename, action.AppName, action.AppVersion)
|
image := fmt.Sprintf("%s:%s_%s", baseimagename, strings.ToLower(action.AppName), action.AppVersion)
|
||||||
if strings.Contains(image, " ") {
|
if strings.Contains(image, " ") {
|
||||||
image = strings.ReplaceAll(image, " ", "-")
|
image = strings.ReplaceAll(image, " ", "-")
|
||||||
}
|
}
|
||||||
|
|
||||||
identifier := fmt.Sprintf("%s_%s_%s_%s", appname, appversion, action.ID, workflowExecution.ExecutionId)
|
// Added UUID to identifier just in case
|
||||||
|
identifier := fmt.Sprintf("%s_%s_%s_%s_%s", appname, appversion, action.ID, workflowExecution.ExecutionId, uuid.NewV4())
|
||||||
if strings.Contains(identifier, " ") {
|
if strings.Contains(identifier, " ") {
|
||||||
identifier = strings.ReplaceAll(identifier, " ", "-")
|
identifier = strings.ReplaceAll(identifier, " ", "-")
|
||||||
}
|
}
|
||||||
@@ -938,15 +940,15 @@ func handleExecutionResult(workflowExecution shuffle.WorkflowExecution) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Uses a few ways of getting / checking if an app is available
|
// Uses a few ways of getting / checking if an app is available
|
||||||
// 1. Try original
|
// 1. Try original with lowercase
|
||||||
// 2. Go to lowercase
|
// 2. Go to original
|
||||||
// 3. Add remote repo location
|
// 3. Add remote repo location
|
||||||
// 4. Actually download last repo
|
// 4. Actually download last repo
|
||||||
|
|
||||||
images := []string{
|
images := []string{
|
||||||
fmt.Sprintf("%s/%s:%s_%s", registryName, baseimagename, strings.ToLower(action.AppName), action.AppVersion),
|
|
||||||
image,
|
image,
|
||||||
fmt.Sprintf("%s:%s_%s", baseimagename, strings.ToLower(action.AppName), action.AppVersion),
|
fmt.Sprintf("%s:%s_%s", baseimagename, action.AppName, action.AppVersion),
|
||||||
|
fmt.Sprintf("%s/%s:%s_%s", registryName, baseimagename, strings.ToLower(action.AppName), action.AppVersion),
|
||||||
}
|
}
|
||||||
|
|
||||||
// If cleanup is set, it should run for efficiency
|
// If cleanup is set, it should run for efficiency
|
||||||
@@ -959,6 +961,7 @@ func handleExecutionResult(workflowExecution shuffle.WorkflowExecution) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[WARNING] Failed CLEANUP execution. Downloading image remotely.")
|
log.Printf("[WARNING] Failed CLEANUP execution. Downloading image remotely.")
|
||||||
|
image = images[2]
|
||||||
reader, err := dockercli.ImagePull(context.Background(), image, pullOptions)
|
reader, err := dockercli.ImagePull(context.Background(), image, pullOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[ERROR] Failed getting %s. The couldn't be find locally, AND is missing.", image)
|
log.Printf("[ERROR] Failed getting %s. The couldn't be find locally, AND is missing.", image)
|
||||||
@@ -996,7 +999,7 @@ func handleExecutionResult(workflowExecution shuffle.WorkflowExecution) {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
err = deployApp(dockercli, image, identifier, env, workflowExecution)
|
err = deployApp(dockercli, images[0], identifier, env, workflowExecution)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if strings.Contains(err.Error(), "exited prematurely") {
|
if strings.Contains(err.Error(), "exited prematurely") {
|
||||||
shutdown(workflowExecution, action.ID, err.Error(), true)
|
shutdown(workflowExecution, action.ID, err.Error(), true)
|
||||||
@@ -1004,7 +1007,7 @@ func handleExecutionResult(workflowExecution shuffle.WorkflowExecution) {
|
|||||||
|
|
||||||
// Trying to replace with lowercase to deploy again. This seems to work with Dockerhub well.
|
// Trying to replace with lowercase to deploy again. This seems to work with Dockerhub well.
|
||||||
// FIXME: Should try to remotely download directly if this persists.
|
// FIXME: Should try to remotely download directly if this persists.
|
||||||
image = fmt.Sprintf("%s:%s_%s", baseimagename, strings.ToLower(action.AppName), action.AppVersion)
|
image = images[1]
|
||||||
if strings.Contains(image, " ") {
|
if strings.Contains(image, " ") {
|
||||||
image = strings.ReplaceAll(image, " ", "-")
|
image = strings.ReplaceAll(image, " ", "-")
|
||||||
}
|
}
|
||||||
@@ -1015,7 +1018,7 @@ func handleExecutionResult(workflowExecution shuffle.WorkflowExecution) {
|
|||||||
shutdown(workflowExecution, action.ID, err.Error(), true)
|
shutdown(workflowExecution, action.ID, err.Error(), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
image = fmt.Sprintf("%s/%s:%s_%s", registryName, baseimagename, strings.ToLower(action.AppName), action.AppVersion)
|
image = images[2]
|
||||||
if strings.Contains(image, " ") {
|
if strings.Contains(image, " ") {
|
||||||
image = strings.ReplaceAll(image, " ", "-")
|
image = strings.ReplaceAll(image, " ", "-")
|
||||||
}
|
}
|
||||||
@@ -1026,7 +1029,7 @@ func handleExecutionResult(workflowExecution shuffle.WorkflowExecution) {
|
|||||||
shutdown(workflowExecution, action.ID, err.Error(), true)
|
shutdown(workflowExecution, action.ID, err.Error(), true)
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Printf("[WARNING] Failed deploying image THRICE. Attempting to download the latter as last resort.")
|
log.Printf("[WARNING] Failed deploying image THREE TIMES. Attempting to download the latter as last resort.")
|
||||||
reader, err := dockercli.ImagePull(context.Background(), image, pullOptions)
|
reader, err := dockercli.ImagePull(context.Background(), image, pullOptions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Printf("[ERROR] Failed getting %s. The couldn't be find locally, AND is missing.", image)
|
log.Printf("[ERROR] Failed getting %s. The couldn't be find locally, AND is missing.", image)
|
||||||
|
|||||||
Reference in New Issue
Block a user