From 0408db39f62712607b4a2968e6d02bc410a940a7 Mon Sep 17 00:00:00 2001 From: pookie Date: Mon, 7 Dec 2020 01:45:47 -0800 Subject: [PATCH 001/117] ci init --- .github/workflows/ci.yaml | 66 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 .github/workflows/ci.yaml diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml new file mode 100644 index 00000000..6b235f6b --- /dev/null +++ b/.github/workflows/ci.yaml @@ -0,0 +1,66 @@ +name: ci + +on: + push: + branches: master +jobs: + main: + runs-on: ubuntu-latest + continue-on-error: ${{ matrix.experimental }} + strategy: + fail-fast: false + matrix: + include: + - app: frontend + path: frontend + version: 0.8.3 + experimental: true + - app: backend + path: backend + version: 0.8.3 + experimental: false + - app: orborus + path: functions/onprem/orborus + version: 0.8.0 + experimental: false + - app: database + path: backend/database + version: 0.8.0 + experimental: false + steps: + - + name: Checkout + uses: actions/checkout@v2 + - + name: Set up QEMU + uses: docker/setup-qemu-action@v1 + - + name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + - + name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} +# Use below configuration for ghcr.io +# with: +# registry: ghcr.io +# username: ${{ github.repository_owner }} +# password: ${{ secrets.CR_PAT }} + - + name: Build and push + id: docker_build + uses: docker/build-push-action@v2 + env: + BUILDX_NO_DEFAULT_LOAD: true + with: + context: ${{ matrix.path }}/ + file: ${{ matrix.path }}/Dockerfile + platforms: linux/amd64,linux/arm64 + #,linux/386 - no node image I guess? + push: true + tags: ${{ secrets.DOCKERHUB_USERNAME }}/${{ matrix.app }}:${{ matrix.version }} + - + name: Image digest + run: echo ${{ steps.docker_build.outputs.digest }} \ No newline at end of file From 31af9ec2ea0c09514fa029c7687aa5a64c50b245 Mon Sep 17 00:00:00 2001 From: pookie Date: Tue, 8 Dec 2020 17:42:41 -0800 Subject: [PATCH 002/117] stuff --- frontend/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/Dockerfile b/frontend/Dockerfile index cc791b82..187e1e4f 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -8,7 +8,7 @@ ENV PATH /usr/src/app/node_modules/.bin:$PATH COPY package.json /usr/src/app/package.json -RUN npm install --verbose +RUN npm install --verbose --legacy-peer-deps # copy only required files to not trigger rebuilding every time COPY ./certs /usr/src/app/certs/ From da2fd18d64645a3cdf383ebf84019bbfaf229104 Mon Sep 17 00:00:00 2001 From: pookie Date: Tue, 8 Dec 2020 17:55:41 -0800 Subject: [PATCH 003/117] stuff --- frontend/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/frontend/package.json b/frontend/package.json index a4c40596..4310246d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -25,6 +25,7 @@ "github-markdown-css": "^3.0.1", "import": "0.0.6", "interweave": "^11.2.0", + "is-core-module": "2.2.0", "material-icons": "^0.3.1", "material-icons-react": "^1.0.4", "material-ui-chip-input": "^2.0.0-beta.2", From 78dbdb8f3faf0c11cb06411d59e7fff7662656df Mon Sep 17 00:00:00 2001 From: frikky Date: Fri, 16 Jul 2021 14:32:43 +0200 Subject: [PATCH 004/117] Fixed minor bugs with environment parsing --- backend/Dockerfile | 6 --- backend/go-app/docker.go | 23 +++++----- backend/go-app/go.mod | 4 +- backend/go-app/main.go | 10 ++--- docker-compose.yml | 2 +- frontend/src/views/Admin.jsx | 61 +++++++++++++++++++------- frontend/src/views/AngularWorkflow.jsx | 3 +- 7 files changed, 65 insertions(+), 44 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index 5d7c484d..c3e3ee87 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -13,10 +13,6 @@ ADD ./go-app/go.mod /app # Required files for code generation ADD ./app_sdk/app_base.py /app_sdk -ADD ./app_sdk_kali/app_base.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/static_baseline.py /app_sdk_blackarch ADD ./app_gen /app_gen RUN go get -v @@ -31,8 +27,6 @@ FROM alpine:3.12 COPY --from=builder /app/ /app COPY --from=builder /app_sdk/ /app_sdk -COPY --from=builder /app_sdk_kali/ /app_sdk_kali -COPY --from=builder /app_sdk_blackarch/ /app_sdk_blackarch COPY --from=builder /app_gen/ /app_gen COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt diff --git a/backend/go-app/docker.go b/backend/go-app/docker.go index a4101b61..cf260598 100644 --- a/backend/go-app/docker.go +++ b/backend/go-app/docker.go @@ -220,19 +220,18 @@ func buildImageMemory(fs billy.Filesystem, tags []string, dockerfileFolder strin // docker build --build-arg http_proxy=http://my.proxy.url // Attempt at setting name according to #359: https://github.com/frikky/Shuffle/issues/359 labels := map[string]string{} - target := "" - if len(tags) > 0 { - if strings.Contains(tags[0], ":") { - version := strings.Split(tags[0], ":") - if len(version) == 2 { - target = fmt.Sprintf("shuffle-build-%s", version[1]) - tags = append(tags, target) - labels["name"] = target - } - } - } + //target := "" + //if len(tags) > 0 { + // if strings.Contains(tags[0], ":") { + // version := strings.Split(tags[0], ":") + // if len(version) == 2 { + // target = fmt.Sprintf("shuffle-build-%s", version[1]) + // tags = append(tags, target) + // labels["name"] = target + // } + // } + //} - _ = labels buildOptions := types.ImageBuildOptions{ Remove: true, Tags: tags, diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index fcc25a96..05370617 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -2,7 +2,7 @@ module shuffle 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 @@ -22,7 +22,7 @@ require ( github.com/docker/go-units v0.4.0 // indirect github.com/elastic/go-elasticsearch/v7 v7.13.1 // indirect github.com/frikky/kin-openapi v0.39.0 - github.com/frikky/shuffle-shared v0.0.69 + github.com/frikky/shuffle-shared v0.0.71 github.com/fsouza/go-dockerclient v1.7.2 github.com/ghodss/yaml v1.0.0 github.com/go-git/go-billy/v5 v5.0.0 diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 825a20bb..7d3e7cdb 100644 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -5304,8 +5304,8 @@ func migrateDatabase(resp http.ResponseWriter, request *http.Request) { } ctx := context.Background() - es := shuffle.GetEsConfig() - _, err := shuffle.RunInit(*dbclient, *es, storage.Client{}, gceProject, "onprem", false, "") + //es := shuffle.GetEsConfig() + _, err := shuffle.RunInit(*dbclient, storage.Client{}, gceProject, "onprem", false, "") if err != nil { log.Printf("[WARNING] Failed to start migration because of init issues: %s", err) resp.WriteHeader(401) @@ -5387,7 +5387,7 @@ func migrateDatabase(resp http.ResponseWriter, request *http.Request) { envSuccess := 0 hookSuccess := 0 scheduleSuccess := 0 - _, err = shuffle.RunInit(*dbclient, *es, storage.Client{}, gceProject, "onprem", false, "elasticsearch") + _, err = shuffle.RunInit(*dbclient, storage.Client{}, gceProject, "onprem", false, "elasticsearch") for _, item := range orgs { err = shuffle.SetOrg(ctx, item, item.Id) @@ -5668,14 +5668,14 @@ func initHandlers() { log.Fatalf("[DEBUG] Database client error during init: %s", err) } - es := shuffle.GetEsConfig() + //es := shuffle.GetEsConfig() elasticConfig := "elasticsearch" if strings.ToLower(os.Getenv("SHUFFLE_ELASTIC")) == "false" { elasticConfig = "" } for { - _, err = shuffle.RunInit(*dbclient, *es, storage.Client{}, gceProject, "onprem", true, elasticConfig) + _, err = shuffle.RunInit(*dbclient, storage.Client{}, gceProject, "onprem", true, elasticConfig) if err != nil { log.Printf("[ERROR] Error in initial database connection. Retrying in 5 seconds. %s", err) time.Sleep(5 * time.Second) diff --git a/docker-compose.yml b/docker-compose.yml index 78e6ae61..26dfad03 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,7 +16,7 @@ services: depends_on: - backend backend: - #build: ./backend + build: ./backend image: ghcr.io/frikky/shuffle-backend:nightly container_name: shuffle-backend hostname: ${BACKEND_HOSTNAME} diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index f32040fb..b1244b51 100644 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -559,19 +559,19 @@ const Admin = (props) => { } // Horrible frontend fix for environments - const setDefaultEnvironment = (name) => { - // FIXME - add some check here ROFL - alert.info("Setting default env to " + name) + const setDefaultEnvironment = (environment) => { + // FIXME - add more checks to this + alert.info("Setting default env to " + environment.name) var newEnv = [] for (var key in environments) { - if (environments[key].Name == name) { + if (environments[key].id == environment.id) { if (environments[key].archived) { alert.error("Can't set archived to default") return } environments[key].default = true - } else if (environments[key].default == true && environments[key].name !== name) { + } else if (environments[key].default == true && environments[key].id !== environment.id) { environments[key].default = false } @@ -592,11 +592,15 @@ const Admin = (props) => { response.json().then(responseJson => { if (responseJson["success"] === false) { alert.error(responseJson.reason) - getEnvironments() + setTimeout(() => { + getEnvironments() + }, 1500) } else { setLoginInfo("") setModalOpen(false) - getEnvironments() + setTimeout(() => { + getEnvironments() + }, 1500) } }), ) @@ -632,23 +636,46 @@ const Admin = (props) => { }) } - const deleteEnvironment = (name) => { + const deleteEnvironment = (environment) => { // FIXME - add some check here ROFL - alert.info("Deleting environment " + name) + //const name = environment.name + + //alert.info("Modifying environment " + name) + //var newEnv = [] + //for (var key in environments) { + // if (environments[key].Name == name) { + // if (environments[key].default) { + // alert.error("Can't modify the default environment") + // return + // } + + // if (environments[key].type === "cloud" && !environments[key].archived) { + // alert.error("Can't modify cloud environments") + // return + // } + + // environments[key].archived = !environments[key].archived + // } + + // newEnv.push(environments[key]) + //} + const id = environment.id + + alert.info("Modifying environment " + environment.name) var newEnv = [] for (var key in environments) { - if (environments[key].Name == name) { + if (environments[key].id == id) { if (environments[key].default) { - alert.error("Can't delete the default environment") + alert.error("Can't modify the default environment") return } - if (environments[key].type === "cloud") { - alert.error("Can't delete the cloud environments") + if (environments[key].type === "cloud" && !environments[key].archived) { + alert.error("Can't modify cloud environments") return } - environments[key].archived = true + environments[key].archived = !environments[key].archived } newEnv.push(environments[key]) @@ -2489,7 +2516,7 @@ const Admin = (props) => {

Environments

- Decides what Orborus environment to execute an action in a workflow in.Learn more + Decides what Orborus environment to execute an action in a workflow in. Learn more
+ } - + {/**/} { > {subworkflow.actions.map((action, index) => { //console.log(action) + const isParent = getParents(selectedTrigger).find(parent => parent.id === action.id) return ( - parent.id === action.id) ? "red" : "white"}} value={action}> + {action.label} ) From d0976bbe5068810c8380ba6ff530f286f34e829b Mon Sep 17 00:00:00 2001 From: frikky Date: Fri, 16 Jul 2021 14:33:48 +0200 Subject: [PATCH 005/117] #437: Modified openapi for 3.0.0 instead of 3.0 --- frontend/src/views/AppCreator.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx index 24006b3a..ad752412 100644 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -970,7 +970,7 @@ const AppCreator = (props) => { const basePath = "/"+(splitBase.slice(3, )).join("/") const data = { - "openapi": "3.0", + "openapi": "3.0.0", "info": { "title": name, "description": description, From 1adc31767db83daad97974f4fda3bf37250b4077 Mon Sep 17 00:00:00 2001 From: frikky Date: Sun, 18 Jul 2021 23:51:50 +0200 Subject: [PATCH 006/117] #428: Fixed scheduling access rights --- backend/go-app/go.mod | 2 +- backend/go-app/main.go | 2 +- backend/go-app/oauth2.go | 18 ++++++++------ backend/go-app/walkoff.go | 34 ++++++++++++++------------ frontend/src/views/Admin.jsx | 4 ++- frontend/src/views/AngularWorkflow.jsx | 3 +++ 6 files changed, 37 insertions(+), 26 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 05370617..410cf847 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -2,7 +2,7 @@ module shuffle 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 diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 7d3e7cdb..1dab6d7d 100644 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -3161,7 +3161,7 @@ func buildSwaggerApp(resp http.ResponseWriter, body []byte, user shuffle.User) { //log.Printf("Should generate yaml") swagger, api, pythonfunctions, err := shuffle.GenerateYaml(swagger, newmd5) if err != nil { - log.Printf("Failed building and generating yaml: %s", err) + log.Printf("[WARNING] Failed building and generating yaml (buildapp): %s", err) resp.WriteHeader(500) resp.Write([]byte(`{"success": false, "reason": "Failed building and parsing yaml"}`)) return diff --git a/backend/go-app/oauth2.go b/backend/go-app/oauth2.go index 773661f3..245c209e 100644 --- a/backend/go-app/oauth2.go +++ b/backend/go-app/oauth2.go @@ -810,18 +810,22 @@ func handleDeleteOutlookSub(resp http.ResponseWriter, request *http.Request) { user, err := shuffle.HandleApiAuthentication(resp, request) if err != nil { - log.Printf("Api authentication failed in outlook deploy: %s", err) + log.Printf("[WARNING] Api authentication failed in outlook deploy: %s", err) resp.WriteHeader(401) resp.Write([]byte(`{"success": false}`)) return } // FIXME - have a check for org etc too.. - if user.Id != workflow.Owner && user.Role != "admin" { - log.Printf("Wrong user (%s) for workflow %s when deploying outlook", user.Username, workflow.ID) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return + 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 %s as admin (delete outlook sub)", user.Username, workflow.ID) + } else { + log.Printf("[WARNING] Wrong user (%s) for workflow %s when deploying outlook", user.Username, workflow.ID) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } } // Check what kind of sub it is @@ -879,7 +883,7 @@ func createOutlookSub(resp http.ResponseWriter, request *http.Request) { // FIXME - have a check for org etc too.. if user.Id != workflow.Owner && user.Role != "admin" { - log.Printf("Wrong user (%s) for workflow %s when deploying outlook", user.Username, workflow.ID) + log.Printf("[WARNING] Wrong user (%s) for workflow %s when deploying outlook", user.Username, workflow.ID) resp.WriteHeader(401) resp.Write([]byte(`{"success": false}`)) return diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index c0f16bbf..964670f5 100644 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -506,7 +506,7 @@ func createSchedule(ctx context.Context, scheduleId, workflowId, name, startNode //log.Printf("BODY: %s", string(body)) parsedArgument := strings.Replace(string(body), "\"", "\\\"", -1) bodyWrapper := fmt.Sprintf(`{"start": "%s", "execution_source": "schedule", "execution_argument": "%s"}`, startNode, parsedArgument) - log.Printf("WRAPPER BODY: \n%s", bodyWrapper) + log.Printf("[INFO] Body for schedule %s in workflow %s: \n%s", scheduleId, workflowId, bodyWrapper) job := func() { request := &http.Request{ URL: &url.URL{}, @@ -1155,7 +1155,7 @@ func deleteWorkflow(resp http.ResponseWriter, request *http.Request) { if workflow.OrgId == user.ActiveOrg.Id && user.Role == "admin" { log.Printf("[INFO] User %s is deleting workflow %s as admin. Owner: %s", user.Username, workflow.ID, workflow.Owner) } else { - log.Printf("Wrong user (%s) for workflow %s", user.Username, workflow.ID) + log.Printf("[WARNING] Wrong user (%s) for workflow %s (delete workflow)", user.Username, workflow.ID) resp.WriteHeader(401) resp.Write([]byte(`{"success": false}`)) return @@ -2201,9 +2201,9 @@ func stopSchedule(resp http.ResponseWriter, request *http.Request) { return } - if user.Id != workflow.Owner && user.Role != "scheduler" { + if user.Id != workflow.Owner || len(user.Id) == 0 { if workflow.OrgId == user.ActiveOrg.Id && user.Role == "admin" { - log.Printf("[DEBUG] User %s is accessing workflow %s as admin", user.Username, workflow.ID) + log.Printf("[DEBUG] User %s is accessing workflow %s as admin (stop schedule)", user.Username, workflow.ID) } else { log.Printf("[WARNING] Wrong user (%s) for workflow %s (stop schedule)", user.Username, workflow.ID) resp.WriteHeader(401) @@ -2347,7 +2347,7 @@ func stopScheduleGCP(resp http.ResponseWriter, request *http.Request) { // FIXME - have a check for org etc too.. // FIXME - admin check like this? idk if user.Id != workflow.Owner && user.Role != "scheduler" { - log.Printf("Wrong user (%s) for workflow %s (stop schedule)", user.Username, workflow.ID) + log.Printf("[WARNING] Wrong user (%s) for workflow %s (stop schedule)", user.Username, workflow.ID) resp.WriteHeader(401) resp.Write([]byte(`{"success": false}`)) return @@ -2411,7 +2411,7 @@ func scheduleWorkflow(resp http.ResponseWriter, request *http.Request) { user, err := shuffle.HandleApiAuthentication(resp, request) if err != nil { - log.Printf("Api authentication failed in schedule workflow: %s", err) + log.Printf("[WARNING] Api authentication failed in schedule workflow: %s", err) resp.WriteHeader(401) resp.Write([]byte(`{"success": false}`)) return @@ -2445,13 +2445,15 @@ func scheduleWorkflow(resp http.ResponseWriter, request *http.Request) { return } - // FIXME - have a check for org etc too.. - // FIXME - admin check like this? idk - if user.Id != workflow.Owner && user.Role != "scheduler" { - log.Printf("Wrong user (%s) for workflow %s", user.Username, workflow.ID) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return + if user.Id != workflow.Owner || len(user.Id) == 0 { + if workflow.OrgId == user.ActiveOrg.Id && user.Role == "admin" { + log.Printf("[INFO] User %s is deleting workflow %s as admin. Owner: %s", user.Username, workflow.ID, workflow.Owner) + } else { + log.Printf("[WARNING] Wrong user (%s) for workflow %s (schedule start). Owner: %s", user.Username, workflow.ID, workflow.Owner) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } } if len(workflow.Actions) == 0 { @@ -2496,7 +2498,7 @@ func scheduleWorkflow(resp http.ResponseWriter, request *http.Request) { startNode = workflow.Start } - log.Printf("Startnode: %s", startNode) + //log.Printf("Startnode: %s", startNode) if len(schedule.Id) != 36 { log.Printf("ID length is not 36 for schedule: %s", err) @@ -2597,7 +2599,7 @@ func scheduleWorkflow(resp http.ResponseWriter, request *http.Request) { } } - log.Printf("Schedulearg: %s", parsedBody) + //log.Printf("Schedulearg: %s", parsedBody) err = createSchedule( ctx, @@ -3282,7 +3284,7 @@ func iterateOpenApiGithub(fs billy.Filesystem, dir []os.FileInfo, extra string, //log.Printf("Should generate yaml") swagger, api, _, err := shuffle.GenerateYaml(swagger, parsedOpenApi.ID) if err != nil { - log.Printf("Failed building and generating yaml in loop (2) (%s): %s. Continuing.", filename, err) + log.Printf("[WARNING] Failed building and generating yaml in loop (2) (%s): %s. Continuing.", filename, err) continue } diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index b1244b51..656d4c90 100644 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -2048,7 +2048,9 @@ const Admin = (props) => { } } - getFiles() + setTimeout(() => { + getFiles() + }, 2500) } const uploadFile = (e) => { diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index e12f45b4..a5c6a249 100644 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -6263,6 +6263,9 @@ const AngularWorkflow = (props) => { workflow.triggers[selectedTriggerIndex].parameters[1] = {"name": "tmp", "value": "webhook_"+selectedTrigger.id} workflow.triggers[selectedTriggerIndex].parameters[2] = {"name": "auth_headers", "value": ""} setWorkflow(workflow) + } else { + // Always update + workflow.triggers[selectedTriggerIndex].parameters[0].value = referenceUrl+"webhook_"+selectedTrigger.id } const trigger_header_auth = workflow.triggers[selectedTriggerIndex].parameters.length > 2 ? workflow.triggers[selectedTriggerIndex].parameters[2].value : "" From ad81fa041594453235e80be7120b7707960af556 Mon Sep 17 00:00:00 2001 From: frikky Date: Mon, 19 Jul 2021 00:17:06 +0200 Subject: [PATCH 007/117] #445: Fixed startnode issues for import/exports during execution --- backend/go-app/walkoff.go | 37 +++++++++++++++----------- frontend/src/views/Admin.jsx | 4 ++- frontend/src/views/AngularWorkflow.jsx | 27 ++++++++++++++++--- 3 files changed, 48 insertions(+), 20 deletions(-) diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index 964670f5..04827474 100644 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -1551,23 +1551,32 @@ func handleExecution(id string, workflow shuffle.Workflow, request *http.Request } workflowExecution.ExecutionVariables = workflow.ExecutionVariables - // Local authorization for this single workflow used in workers. - - // FIXME: Used for cloud - //mappedData, err := json.Marshal(workflowExecution) - //if err != nil { - // log.Printf("Failed workflowexecution marshalling: %s", err) - // resp.WriteHeader(http.StatusInternalServerError) - // resp.Write([]byte(`{"success": false}`)) - // return - //} - - //log.Println(string(mappedData)) if len(workflowExecution.Start) == 0 && len(workflowExecution.Workflow.Start) > 0 { workflowExecution.Start = workflowExecution.Workflow.Start } - //log.Printf("[INFO] New startnode: %s", workflowExecution.Start) + + startnodeFound := false + newStartnode := "" + for _, item := range workflowExecution.Workflow.Actions { + if item.ID == workflowExecution.Start { + startnodeFound = true + } + + if item.IsStartNode { + newStartnode = item.ID + } + } + + if !startnodeFound { + log.Printf("[INFO] Couldn't find startnode %s. Remapping to %#v", workflowExecution.Start, newStartnode) + + if len(newStartnode) > 0 { + workflowExecution.Start = newStartnode + } else { + return shuffle.WorkflowExecution{}, fmt.Sprintf("Startnode couldn't be found"), errors.New("Startnode isn't defined in this workflow..") + } + } childNodes := shuffle.FindChildNodes(workflowExecution, workflowExecution.Start) @@ -2122,8 +2131,6 @@ func executeWorkflow(resp http.ResponseWriter, request *http.Request) { return } - // FIXME - have a check for org etc too.. - // FIXME - admin check like this? idk if user.Id != workflow.Owner && user.Role != "scheduler" && user.Role != fmt.Sprintf("workflow_%s", fileId) { if workflow.OrgId == user.ActiveOrg.Id && user.Role == "admin" { log.Printf("[INFO] Letting user %s execute %s because they're admin of the same org", user.Username, workflow.ID) diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index 656d4c90..3c77d3ab 100644 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -184,7 +184,9 @@ const Admin = (props) => { if (responseJson["success"] === false) { alert.error("Failed stopping schedule") } else { - getSchedules() + setTimeout(() => { + getSchedules() + }, 1500) alert.success("Successfully stopped schedule!") } }), diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index a5c6a249..93fe0126 100644 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -930,9 +930,22 @@ const AngularWorkflow = (props) => { } const monitorUpdates = () => { - const firstnode = cy.getElementById(workflow.start) + var firstnode = cy.getElementById(workflow.start) if (firstnode.length === 0) { - return false + var found = false + for (var key in workflow.actions) { + if (workflow.actions[key].isStartNode) { + console.log("Updating startnode") + workflow.start = workflow.actions[key].id + firstnode = cy.getElementById(workflow.actions[key].id) + found = true + break + } + } + + if (!found) { + return false + } } cy.elements().removeClass('success-highlight failure-highlight executing-highlight') @@ -6265,7 +6278,13 @@ const AngularWorkflow = (props) => { setWorkflow(workflow) } else { // Always update - workflow.triggers[selectedTriggerIndex].parameters[0].value = referenceUrl+"webhook_"+selectedTrigger.id + const newUrl = referenceUrl+"webhook_"+selectedTrigger.id + console.log("Validating webhook url: ", newUrl) + if (newUrl !== workflow.triggers[selectedTriggerIndex].parameters[0].value) { + console.log("Url is wrong - updating") + workflow.triggers[selectedTriggerIndex].parameters[0].value = newUrl + setWorkflow(workflow) + } } const trigger_header_auth = workflow.triggers[selectedTriggerIndex].parameters.length > 2 ? workflow.triggers[selectedTriggerIndex].parameters[2].value : "" @@ -6650,7 +6669,7 @@ const AngularWorkflow = (props) => { return } - alert.info("Stopping webhook") + //alert.info("Stopping webhook") fetch(globalUrl+"/api/v1/hooks/"+trigger.id+"/delete", { method: 'DELETE', From ecdadf5c2c38427a1c5ed6386340011c110202ea Mon Sep 17 00:00:00 2001 From: frikky Date: Mon, 19 Jul 2021 00:37:49 +0200 Subject: [PATCH 008/117] #441: Fixed URL update during webhook selection --- frontend/src/views/Admin.jsx | 2 +- frontend/src/views/AngularWorkflow.jsx | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index 3c77d3ab..7688ab08 100644 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -663,7 +663,7 @@ const Admin = (props) => { //} const id = environment.id - alert.info("Modifying environment " + environment.name) + //alert.info("Modifying environment " + environment.Name) var newEnv = [] for (var key in environments) { if (environments[key].id == id) { diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 93fe0126..101b4ec8 100644 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -2011,14 +2011,19 @@ const AngularWorkflow = (props) => { //console.log("Should handle trigger "+data.triggertype) //console.log(data) const trigger_index = workflow.triggers.findIndex(a => a.id === data.id) - setSelectedTriggerIndex(trigger_index) - setSelectedTrigger(data) - setSelectedActionEnvironment(data.env) if (data.app_name === "Shuffle Workflow") { getAvailableWorkflows(trigger_index) getSettings() + } else if (data.app_name === "Webhook") { + if (workflow.triggers[trigger_index].parameters !== undefined) { + workflow.triggers[trigger_index].parameters[0] = {"name": "url", "value": referenceUrl+"webhook_"+selectedTrigger.id} + } } + + setSelectedTriggerIndex(trigger_index) + setSelectedTrigger(data) + setSelectedActionEnvironment(data.env) } else { alert.error("Can't handle "+data.type) } From 2499a3176e41b1157e7ca494f67e57e93e6cb5b4 Mon Sep 17 00:00:00 2001 From: frikky Date: Mon, 19 Jul 2021 00:59:15 +0200 Subject: [PATCH 009/117] #432: Fixed bools in authentication fields --- frontend/src/components/ParsedAction.jsx | 1 + frontend/src/views/AngularWorkflow.jsx | 9 +++++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index 05cf9fb1..7dbdaad6 100644 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -1168,6 +1168,7 @@ const ParsedAction = (props) => {
{}}> { if (selectedApp.authentication.parameters[key].value !== undefined && selectedApp.authentication.parameters[key].value !== null && selectedApp.authentication.parameters[key].value.length > 0) { authenticationOption.fields[selectedApp.authentication.parameters[key].name] = selectedApp.authentication.parameters[key].value } else { - alert.info("Field "+selectedApp.authentication.parameters[key].name+" can't be empty") - return + if (selectedApp.authentication.parameters[key].schema.type === "bool") { + authenticationOption.fields[selectedApp.authentication.parameters[key].name] = "false" + } else { + alert.info("Field "+selectedApp.authentication.parameters[key].name+" can't be empty") + return + } } } } @@ -8710,6 +8714,7 @@ const AngularWorkflow = (props) => { defaultValue={"false"} fullWidth onChange={(e) => { + console.log("Value: ", e.target.value) authenticationOption.fields[data.name] = e.target.value }} style={{backgroundColor: theme.palette.surfaceColor, color: "white", height: 50}} From 229d38fd73a2c633842871bac9b6a36ce15bfe73 Mon Sep 17 00:00:00 2001 From: frikky Date: Mon, 19 Jul 2021 01:22:54 +0200 Subject: [PATCH 010/117] Cleaned up for minor release --- backend/go-app/docker.go | 8 ++++---- backend/go-app/go.mod | 4 ++-- docker-compose.yml | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/backend/go-app/docker.go b/backend/go-app/docker.go index cf260598..069a88e6 100644 --- a/backend/go-app/docker.go +++ b/backend/go-app/docker.go @@ -260,15 +260,15 @@ func buildImageMemory(fs billy.Filesystem, tags []string, dockerfileFolder strin //log.Printf("RESPONSE: %#v", imageBuildResponse) //log.Printf("Response: %#v", imageBuildResponse.Body) - //log.Printf("IMAGERESPONSE: %#v", imageBuildResponse.Body) + log.Printf("[DEBUG] IMAGERESPONSE: %#v", imageBuildResponse.Body) - defer imageBuildResponse.Body.Close() + //defer imageBuildResponse.Body.Close() buildBuf := new(strings.Builder) _, newerr := io.Copy(buildBuf, imageBuildResponse.Body) if newerr != nil { - log.Printf("Failed reading Docker build STDOUT: %s", newerr) + log.Printf("[WARNING] Failed reading Docker build STDOUT: %s", newerr) } else { - log.Printf("STRING: %s", buildBuf.String()) + log.Printf("[INFO] STRING: %s", buildBuf.String()) if strings.Contains(buildBuf.String(), "errorDetail") { log.Printf("[ERROR] Docker build:\n%s\nERROR ABOVE: Trying to pull tags from: %s", buildBuf.String(), strings.Join(tags, "\n")) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 410cf847..21ea23af 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -2,7 +2,7 @@ module shuffle 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 @@ -22,7 +22,7 @@ require ( github.com/docker/go-units v0.4.0 // indirect github.com/elastic/go-elasticsearch/v7 v7.13.1 // indirect github.com/frikky/kin-openapi v0.39.0 - github.com/frikky/shuffle-shared v0.0.71 + github.com/frikky/shuffle-shared v0.0.72 github.com/fsouza/go-dockerclient v1.7.2 github.com/ghodss/yaml v1.0.0 github.com/go-git/go-billy/v5 v5.0.0 diff --git a/docker-compose.yml b/docker-compose.yml index 26dfad03..78e6ae61 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,7 +16,7 @@ services: depends_on: - backend backend: - build: ./backend + #build: ./backend image: ghcr.io/frikky/shuffle-backend:nightly container_name: shuffle-backend hostname: ${BACKEND_HOSTNAME} From a120561744da3e622c20a02c2f39e83e6dd551b4 Mon Sep 17 00:00:00 2001 From: frikky Date: Mon, 19 Jul 2021 01:58:36 +0200 Subject: [PATCH 011/117] #438: Fixed docker version crashes --- .env | 1 + backend/go-app/docker.go | 70 +++++++++++++++++++++------------------- docker-compose.yml | 2 +- 3 files changed, 38 insertions(+), 35 deletions(-) diff --git a/.env b/.env index be6f9de0..e10cbb18 100644 --- a/.env +++ b/.env @@ -33,6 +33,7 @@ FRONTEND_PORT_HTTPS=3443 # CHANGE THIS IF YOU WANT GOOD LOCAL EXECUTIONS: OUTER_HOSTNAME=shuffle-backend DB_LOCATION=./shuffle-database +DOCKER_API_VERSION=1.40 # Proxy configurations. SHUFFLE_PASS_WORKER_PROXY must be FALSE to not pass the proxy information to sub-apps. # PS: It will skip proxy for diff --git a/backend/go-app/docker.go b/backend/go-app/docker.go index 069a88e6..2e9a4314 100644 --- a/backend/go-app/docker.go +++ b/backend/go-app/docker.go @@ -262,45 +262,47 @@ func buildImageMemory(fs billy.Filesystem, tags []string, dockerfileFolder strin //log.Printf("Response: %#v", imageBuildResponse.Body) log.Printf("[DEBUG] IMAGERESPONSE: %#v", imageBuildResponse.Body) - //defer imageBuildResponse.Body.Close() - buildBuf := new(strings.Builder) - _, newerr := io.Copy(buildBuf, imageBuildResponse.Body) - if newerr != nil { - log.Printf("[WARNING] Failed reading Docker build STDOUT: %s", newerr) - } else { - log.Printf("[INFO] STRING: %s", buildBuf.String()) - if strings.Contains(buildBuf.String(), "errorDetail") { - log.Printf("[ERROR] Docker build:\n%s\nERROR ABOVE: Trying to pull tags from: %s", buildBuf.String(), strings.Join(tags, "\n")) + if imageBuildResponse.Body != nil { + defer imageBuildResponse.Body.Close() + buildBuf := new(strings.Builder) + _, newerr := io.Copy(buildBuf, imageBuildResponse.Body) + if newerr != nil { + log.Printf("[WARNING] Failed reading Docker build STDOUT: %s", newerr) + } else { + log.Printf("[INFO] STRING: %s", buildBuf.String()) + if strings.Contains(buildBuf.String(), "errorDetail") { + log.Printf("[ERROR] Docker build:\n%s\nERROR ABOVE: Trying to pull tags from: %s", buildBuf.String(), strings.Join(tags, "\n")) - // Handles pulling of the same image if applicable - // This fixes some issues with older versions of Docker which can't build - // on their own ( <17.05 ) - pullOptions := types.ImagePullOptions{} - downloaded := false - for _, image := range tags { - // Is this ok? Not sure. Tags shouldn't be controlled here prolly. - image = strings.ToLower(image) + // Handles pulling of the same image if applicable + // This fixes some issues with older versions of Docker which can't build + // on their own ( <17.05 ) + pullOptions := types.ImagePullOptions{} + downloaded := false + for _, image := range tags { + // Is this ok? Not sure. Tags shouldn't be controlled here prolly. + image = strings.ToLower(image) - newImage := fmt.Sprintf("%s/%s", registryName, image) - log.Printf("[INFO] Pulling image %s", newImage) - reader, err := client.ImagePull(ctx, newImage, pullOptions) - if err != nil { - log.Printf("[ERROR] Failed getting image %s: %s", newImage, err) - continue + newImage := fmt.Sprintf("%s/%s", registryName, image) + log.Printf("[INFO] Pulling image %s", newImage) + reader, err := client.ImagePull(ctx, newImage, pullOptions) + if err != nil { + log.Printf("[ERROR] Failed getting image %s: %s", newImage, err) + continue + } + + // Attempt to retag the image to not contain registry... + + //newBuf := buildBuf + downloaded = true + io.Copy(os.Stdout, reader) + log.Printf("[INFO] Successfully downloaded and built %s", newImage) } - // Attempt to retag the image to not contain registry... - - //newBuf := buildBuf - downloaded = true - io.Copy(os.Stdout, reader) - log.Printf("[INFO] Successfully downloaded and built %s", newImage) + if !downloaded { + return errors.New(fmt.Sprintf("Failed to build / download images %s", strings.Join(tags, ","))) + } + //baseDockerName } - - if !downloaded { - return errors.New(fmt.Sprintf("Failed to build / download images %s", strings.Join(tags, ","))) - } - //baseDockerName } } diff --git a/docker-compose.yml b/docker-compose.yml index 78e6ae61..69ca24b7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -53,7 +53,7 @@ services: - ORG_ID=${ORG_ID} - ENVIRONMENT_NAME=${ENVIRONMENT_NAME} - BASE_URL=http://${OUTER_HOSTNAME}:${BACKEND_PORT} - - DOCKER_API_VERSION=1.35 + - DOCKER_API_VERSION=1.40 - SHUFFLE_BASE_IMAGE_NAME=${SHUFFLE_BASE_IMAGE_NAME} - SHUFFLE_BASE_IMAGE_REGISTRY=${SHUFFLE_BASE_IMAGE_REGISTRY} - SHUFFLE_BASE_IMAGE_TAG_SUFFIX=${SHUFFLE_BASE_IMAGE_TAG_SUFFIX} From 67453eb9f5cfd2d4b9b0ed7ee0d3042dd6e1cbd3 Mon Sep 17 00:00:00 2001 From: frikky Date: Tue, 27 Jul 2021 17:46:20 +0200 Subject: [PATCH 012/117] Started fixing suborg swapping --- backend/go-app/go.mod | 2 +- backend/go-app/main.go | 63 ++++++++++++++++------ frontend/src/App.jsx | 2 +- frontend/src/components/Header.js | 59 ++++++++++++++++----- frontend/src/views/Admin.jsx | 86 ++++++++++++++++++++++++++++--- frontend/src/views/Apps.jsx | 6 ++- 6 files changed, 178 insertions(+), 40 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 21ea23af..21dd9dc8 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -2,7 +2,7 @@ module shuffle 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 diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 1dab6d7d..55c5d705 100644 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -976,38 +976,67 @@ func handleInfo(resp http.ResponseWriter, request *http.Request) { } err = shuffle.SetUser(ctx, &userInfo, true) if err != nil { - log.Printf("Error patching User for activeOrg: %s", err) + log.Printf("[INFO] Error patching User for activeOrg: %s", err) } } } - // FIXME: Remove this dependency by updating users' orgs when org itself is updated org, err := shuffle.GetOrg(ctx, userInfo.ActiveOrg.Id) if err == nil { userInfo.ActiveOrg = shuffle.OrgMini{ - Id: org.Id, - Name: org.Name, + Id: org.Id, + Name: org.Name, + CreatorOrg: org.CreatorOrg, + Role: userInfo.ActiveOrg.Role, + Image: org.Image, } userInfo.ActiveOrg.Users = []shuffle.UserMini{} } userInfo.ActiveOrg.Users = []shuffle.UserMini{} - currentOrg, err := json.Marshal(userInfo.ActiveOrg) - if err != nil { - currentOrg = []byte("{}") + userOrgs := []shuffle.OrgMini{} + for _, item := range userInfo.Orgs { + if item == userInfo.ActiveOrg.Id { + userOrgs = append(userOrgs, userInfo.ActiveOrg) + continue + } + + org, err := shuffle.GetOrg(ctx, item) + if err == nil { + userOrgs = append(userOrgs, shuffle.OrgMini{ + Id: org.Id, + Name: org.Name, + CreatorOrg: org.CreatorOrg, + Image: org.Image, + }) + // Role: "admin", + } } - returnData := fmt.Sprintf(`{ - "success": true, - "username": "%s", - "admin": %s, - "tutorials": [], - "id": "%s", - "orgs": [%s], - "active_org": %s, - "cookies": [{"key": "session_token", "value": "%s", "expiration": %d}] -}`, userInfo.Username, parsedAdmin, userInfo.Id, currentOrg, currentOrg, userInfo.Session, expiration.Unix()) + returnValue := shuffle.HandleInfo{ + Success: true, + Username: userInfo.Username, + Admin: parsedAdmin, + Id: userInfo.Id, + Orgs: userOrgs, + ActiveOrg: userInfo.ActiveOrg, + Cookies: []shuffle.SessionCookie{ + shuffle.SessionCookie{ + Key: "session_token", + Value: userInfo.Session, + Expiration: expiration.Unix(), + }, + }, + } + + returnData, err := json.Marshal(returnValue) + if err != nil { + log.Printf("[WARNING] Failed marshalling info: %s", err) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } resp.WriteHeader(200) resp.Write([]byte(returnData)) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 70df8462..eb04e147 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -75,7 +75,7 @@ const App = (message, props) => { .then(response => response.json()) .then(responseJson => { if (responseJson.success === true) { - //console.log(responseJson.success) + console.log(responseJson) setUserData(responseJson) setIsLoggedIn(true) //console.log("Cookies: ", cookies) diff --git a/frontend/src/components/Header.js b/frontend/src/components/Header.js index eb6d6e0c..c980cb7e 100644 --- a/frontend/src/components/Header.js +++ b/frontend/src/components/Header.js @@ -3,24 +3,17 @@ import {BrowserView, MobileView} from "react-device-detect"; import {Link} from 'react-router-dom'; -import List from '@material-ui/core/List'; -import Avatar from '@material-ui/core/Avatar'; -import Menu from '@material-ui/core/Menu'; -import ListItem from '@material-ui/core/ListItem'; -import MenuItem from '@material-ui/core/MenuItem'; -import Select from '@material-ui/core/Select'; -import Button from '@material-ui/core/Button'; -import IconButton from '@material-ui/core/IconButton'; -import HomeIcon from '@material-ui/icons/Home'; -import PolymerIcon from '@material-ui/icons/Polymer'; -import AppsIcon from '@material-ui/icons/Apps'; -import DescriptionIcon from '@material-ui/icons/Description'; -import Grid from '@material-ui/core/Grid'; import { useTheme } from '@material-ui/core/styles'; +import { Tooltip, List, Avatar, Menu, ListItem, MenuItem, Select, Button, IconButton, Grid } from '@material-ui/core'; +import { Home as HomeIcon, Polymer as PolymerIcon, Apps as AppsIcon, Description as DescriptionIcon} from '@material-ui/icons'; + const hoverColor = "#f85a3e" const hoverOutColor = "#e8eaf6" + +const defaultImage = "data:image/png;base64,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" + const Header = props => { const { globalUrl, isLoggedIn, removeCookie, homePage, isLoaded, userdata, cookies } = props; const theme = useTheme(); @@ -239,9 +232,49 @@ const Header = props => { } + {/*userdata === undefined || userdata.orgs === undefined || userdata.orgs === null || userdata.orgs.length <= 1 ? null : + + + + */}
+ //console.log("USR: ", userdata.orgs) + const loginTextMobile = !isLoggedIn ?
diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index 7688ab08..82345091 100644 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -29,6 +29,7 @@ const Admin = (props) => { const [firstRequest, setFirstRequest] = React.useState(true); const [orgRequest, setOrgRequest] = React.useState(true); const [modalUser, setModalUser] = React.useState({}); + const [orgName, setOrgName] = React.useState("") const [modalOpen, setModalOpen] = React.useState(false); const [cloudSyncModalOpen, setCloudSyncModalOpen] = React.useState(false); @@ -367,6 +368,45 @@ const Admin = (props) => { }); } + const createSubOrg = (currentOrgId, name) => { + const data = { "name": name, "org_id": currentOrgId} + console.log(data) + const url = globalUrl + `/api/v1/orgs/${currentOrgId}/create_sub_org` + + fetch(url, { + mode: 'cors', + method: 'POST', + body: JSON.stringify(data), + credentials: 'include', + crossDomain: true, + withCredentials: true, + headers: { + 'Content-Type': 'application/json; charset=utf-8', + }, + }) + .then(response => + response.json().then(responseJson => { + if (responseJson["success"] === false) { + if (responseJson.reason !== undefined) { + alert.error(responseJson.reason) + } else { + alert.error("Failed setting new password") + } + } else { + alert.success("Successfully updated password!") + setSelectedUserModalOpen(false) + } + + setOrgName("") + setModalOpen(false) + }), + ) + .catch(error => { + alert.error("Err: " + error.toString()) + }); + + } + const onPasswordChange = () => { const data = { "username": selectedUser.username, "newpassword": newPassword } const url = globalUrl + '/api/v1/users/passwordchange'; @@ -1773,14 +1813,20 @@ const Admin = (props) => { }} > - {curTab === 1 ? "Add user" : "Add environment"} + {curTab === 1 ? "Add user" : curTab === 7 ? "Add Sub-Organization" : "Add environment"} {curTab === 1 && isCloud ? We'll send an email to invite them to your organization. - : null} + : + curTab === 7 ? + + The organization created will become a child of your current organization, and be available to you. + + : + null } {curTab === 1 ?
Username @@ -1830,6 +1876,31 @@ const Admin = (props) => { }
+ : curTab === 7 ? +
+ Name + { + setOrgName(event.target.value) + }} + /> +
: curTab === 5 ?
Environment Name @@ -1867,6 +1938,8 @@ const Admin = (props) => { } else { submitUser(modalUser) } + } else if (curTab === 7) { + createSubOrg(selectedOrganization.id, orgName) } else if (curTab === 5) { submitEnvironment(modalUser) } @@ -2334,13 +2407,13 @@ const Admin = (props) => { style={{minWidth: 150, maxWidth: 150, overflow: "hidden"}} /> - {categories.map(data => { + {categories.map((data, index) => { if (data.apps.length === 0) { return null } return ( - + { style={{}} variant="contained" color="primary" - disabled onClick={() => { setModalOpen(true) }} > - Add organization + Add suborganization @@ -2772,13 +2844,13 @@ const Admin = (props) => {
{organizationView} {authenticationView} - {appCategoryView} {usersView} {environmentView} {schedulesView} {filesView} {hybridTab} {organizationsTab} + {appCategoryView}
diff --git a/frontend/src/views/Apps.jsx b/frontend/src/views/Apps.jsx index b4b98103..8ef4c199 100644 --- a/frontend/src/views/Apps.jsx +++ b/frontend/src/views/Apps.jsx @@ -932,7 +932,11 @@ const Apps = (props) => { console.log("Error in dropzone: ", e) } - reader.readAsText(files[0]); + try { + reader.readAsText(files[0]); + } catch(error) { + alert.error("Failed to read file") + } }; useEffect(() => { From aca9d9011ab59c85397c8b80ba07a2570ff70366 Mon Sep 17 00:00:00 2001 From: anna Date: Wed, 28 Jul 2021 10:36:30 +0800 Subject: [PATCH 013/117] Anonymous function point is the same value, so all the job params will be Covered. so, when restart the containers, all the schedules will be called, but used the same params --- backend/go-app/main.go | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 55c5d705..cc0cf04f 100644 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -3970,15 +3970,10 @@ func runInitEs(ctx context.Context) { log.Printf("[WARNING] Failed getting schedules during service init: %s", err) } else { log.Printf("[INFO] Setting up %d schedule(s)", len(schedules)) - url := &url.URL{} - for _, schedule := range schedules { - if schedule.Environment == "cloud" { - log.Printf("Skipping cloud schedule") - continue - } - //log.Printf("Schedule: %#v", schedule) - job := func() { + url := &url.URL{} + job := func(schedule ScheduleOld) func() { + return func() { //log.Printf("[INFO] Running schedule %s with interval %d.", schedule.Id, schedule.Seconds) //log.Printf("ARG: %s", schedule.WrappedArgument) @@ -3993,9 +3988,18 @@ func runInitEs(ctx context.Context) { log.Printf("[WARNING] Failed to execute %s: %s", schedule.WorkflowId, err) } } + } + + for _, schedule := range schedules { + if schedule.Environment == "cloud" { + log.Printf("Skipping cloud schedule") + continue + } + + //log.Printf("Schedule: %#v", schedule) //log.Printf("Schedule time: every %d seconds", schedule.Seconds) - jobret, err := newscheduler.Every(schedule.Seconds).Seconds().NotImmediately().Run(job) + jobret, err := newscheduler.Every(schedule.Seconds).Seconds().NotImmediately().Run(job(schedule)) if err != nil { log.Printf("Failed to schedule workflow: %s", err) } From eceb3a2fca3563a861e9f6db750984b9e50677e6 Mon Sep 17 00:00:00 2001 From: frikky Date: Fri, 30 Jul 2021 01:36:41 +0200 Subject: [PATCH 014/117] Added org and suborg swapping POC --- backend/go-app/go.mod | 2 +- backend/go-app/main.go | 12 +- docker-compose.yml | 4 +- frontend/src/App.jsx | 2 +- frontend/src/components/Header.js | 117 ++++++++++++------- frontend/src/theme.js | 1 + frontend/src/views/Admin.jsx | 181 +++++++++++++++++------------- frontend/src/views/Workflows.jsx | 30 ++++- 8 files changed, 223 insertions(+), 126 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 21dd9dc8..9841d41a 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -22,7 +22,7 @@ require ( github.com/docker/go-units v0.4.0 // indirect github.com/elastic/go-elasticsearch/v7 v7.13.1 // indirect github.com/frikky/kin-openapi v0.39.0 - github.com/frikky/shuffle-shared v0.0.72 + github.com/frikky/shuffle-shared v0.0.74 github.com/fsouza/go-dockerclient v1.7.2 github.com/ghodss/yaml v1.0.0 github.com/go-git/go-billy/v5 v5.0.0 diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 55c5d705..926ea1ec 100644 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -739,12 +739,15 @@ func handleRegister(resp http.ResponseWriter, request *http.Request) { } } + apikey := "" if count != 0 { if user.Role != "admin" { resp.WriteHeader(401) resp.Write([]byte(`{"success": false, "reason": "Can't register without being admin (2)"}`)) return } + } else { + apikey = uuid.NewV4().String() } // Gets a struct of Username, password @@ -819,7 +822,7 @@ func handleRegister(resp http.ResponseWriter, request *http.Request) { } } - err = createNewUser(data.Username, data.Password, role, "", currentOrg) + err = createNewUser(data.Username, data.Password, role, apikey, currentOrg) if err != nil { log.Printf("[WARNING] Failed registering user: %s", err) resp.WriteHeader(401) @@ -828,7 +831,7 @@ func handleRegister(resp http.ResponseWriter, request *http.Request) { } resp.WriteHeader(200) - resp.Write([]byte(`{"success": true}`)) + resp.Write([]byte(fmt.Sprintf(`{"success": true, "apikey": "%s"}`, apikey))) log.Printf("[INFO] %s Successfully registered.", data.Username) } @@ -4147,12 +4150,12 @@ func runInitEs(ctx context.Context) { r, err := git.Clone(storer, fs, cloneOptions) if err != nil { - log.Printf("Failed loading repo into memory (init): %s", err) + log.Printf("[WARNING] Failed loading repo into memory (init): %s", err) } dir, err := fs.ReadDir("") if err != nil { - log.Printf("Failed reading folder: %s", err) + log.Printf("[WARNING] Failed reading folder (init): %s", err) } _ = r //iterateAppGithubFolders(fs, dir, "", "testing") @@ -5837,6 +5840,7 @@ func initHandlers() { r.HandleFunc("/api/v1/orgs/{orgId}", shuffle.HandleGetOrg).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/orgs/{orgId}", shuffle.HandleEditOrg).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/orgs/{orgId}/create_sub_org", shuffle.HandleCreateSubOrg).Methods("POST", "OPTIONS") + r.HandleFunc("/api/v1/orgs/{orgId}/change", shuffle.HandleChangeUserOrg).Methods("POST", "OPTIONS") // Swaps to the org // This is a new API that validates if a key has been seen before. // Not sure what the best course of action is for it. diff --git a/docker-compose.yml b/docker-compose.yml index 69ca24b7..3bba8d9c 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '3' services: frontend: - #build: ./frontend + build: ./frontend image: ghcr.io/frikky/shuffle-frontend:nightly container_name: shuffle-frontend hostname: shuffle-frontend @@ -16,7 +16,7 @@ services: depends_on: - backend backend: - #build: ./backend + build: ./backend image: ghcr.io/frikky/shuffle-backend:nightly container_name: shuffle-backend hostname: ${BACKEND_HOSTNAME} diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index eb04e147..a74153a9 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -105,7 +105,7 @@ const App = (message, props) => {
:
-
+
} /> } /> } /> diff --git a/frontend/src/components/Header.js b/frontend/src/components/Header.js index c980cb7e..e57c4068 100644 --- a/frontend/src/components/Header.js +++ b/frontend/src/components/Header.js @@ -7,13 +7,11 @@ import { useTheme } from '@material-ui/core/styles'; import { Tooltip, List, Avatar, Menu, ListItem, MenuItem, Select, Button, IconButton, Grid } from '@material-ui/core'; import { Home as HomeIcon, Polymer as PolymerIcon, Apps as AppsIcon, Description as DescriptionIcon} from '@material-ui/icons'; +import { useAlert } from "react-alert"; const hoverColor = "#f85a3e" const hoverOutColor = "#e8eaf6" - -const defaultImage = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAK4AAACuCAYAAACvDDbuAAAgAElEQVR4Xu19e9CvV1Xe3r/vnJOcBEhBSgMEBaoUK9POCOVmAuP0HwcUCNYZSUsh9xv3hGl1qNippQRE20IFEhQoBJiaKVpEgQRnhD+0QHSmRkAsxE4doBZQTs71u/zezruv6/Ksvffvkn/qd8bBfN/3XvZe+1nPevbaa+/XuxX/Tbe6C/ece8qOd5dNk3um9+7Jk/OPds49zE3uyPy4ST5zCr/y4f+sfxO4j16vHsqfmV7gpgm8Yzm/lP9+km1B9+W2zn/zzk2sDeR55ffy3enn1Lf5J/1e3bb42kX43/Do1nt9fUdpLrSbbFt8vvls+idlm/qsaJPWuNK/TfvOLU44577pnPuSc9PvT95/9szu3p9c/IH/c2oVKDbeyB8z/Yz7noNd9zzn3U+5hX+6m9wjnXM7GqXCHpbR0+PnjudGxEtBkzRo02vFtRB83rkAXPqPGD4MnmWGGa3CDqp9+pp4Bwd2dCwPzIXur6D1xaGRXaQz8nf4Kfix1/3joDXtDm0jHVbYspirbZdkj4Npcv/XO/8F59xdfv/o7zz0A1/9yxEAd4E7/by74OCke5Fb+Bvd5J7unDvGHmyCKiMzX40ByW+XQCxvyoSafgGMBT0fgTayXmA/kykr8PI1kS0JIOMPRiSJ7ctOWfuI+mcw4Uj7lO25Y0TQoyHGbKsjJbqXR5HoKijK6nuz3bPhokEX+f4956Z7nXP/8cz+w//bxR/4H00GbgJ3ep170sHC/WxgWecvUJ5AemrLA4NF2cCAa4BDSCbjYVR2RYN2ZXlAOgxZqcXUDLh9toyvSkybopAJ+KBbOpHADOEjoO1EkVYU6LB0HbPUDtaN8N7TzrmPOnfwry9631/8mcW+JnD3Xu+eu3DubZNzT1Xhu8eyxaXw47lWBIzcAW2fGSpoM1WPgzYNWqSRwqjsnSYokgMyvW5JnzWZNrQpC9tC+ZVZW1Ek9GbBQK8ZuS99mrrW1sPh7XXsTeDmqHrvYvKve+j77/8MAi9E1v6t7vnOu7c7555gaM4SuiHGOsajcV8xmeEUMSDFf4yJQLiUmpYNTg906iVCl7b6ltu4gi5lTMs6Z4RpwLQsVMP+IZaNTtYngRoNI1uOzwdYhJxtEhqKAFsdvoI0XHy/m3ZeedH7v/ZxCV7Vir1b3HP8wr3PBC1HDsog5IkJxSdDHAyBlRqBgyHwAANuI3sg3j4G+shSHASQaRlbZtDW8AmiT7b3TLJD8qACrWrwkclYg2l70qACWow5IZvQ9lHQUmeZ7p+8v/Lh7/3z36NDw6w7a9rljvvw5NwPWykOOuEono5YT8IvXQNBK5yhGnwFVoCgZYPYZgvSBtnGMMkJnR2ZrIh3Fga3mGoR9YgpL2xAjbGlBC2YTEGWJu01J3mkrwYGuFP25EF1XMFj9y6Wiyse9p+/9hXl2il78G7n3T9radomMVphpMdGUG9wAJRLYJ4Wz2CZ73QmUpkxa9jlA2fmKvNz0414Fm+Abw6b+U8D4Km+xfsb0l5E99Z+S4mApIHhaEz2bCAPSmMkaNMzW4Av94ac3vtPHdu96TG3f32evFXhuH+ru8J5f4dzrmYPVGiKN7Bfsx/syVj+C07AA3WQmlYBa4TR+bpWnhYCgjyLtN+OBrbezPdAwNcQCjq4EAsaCEAS8BF4PNIZoBKhWdo9PMO0DX5vbmGTRMi41SgyxrQlTcmsVQB+2jt/3UXv/9qdxVLTre5RBwv/m25yz+Qspe2NQWt5ZEQ5x79kMgzaoTC4DM9SuoqDvcEWSqKsNlnJHYvvQ5oWvRutiInrijdwALGBLZ003sEcMl7DSEdHoGTHyoQ4eqTxaqymzdmYuq40wrS1D5A80i+985/1u0de/LAPf+Vb4Y7917uXO+dud84fRcClIOKdVwPGQZRuNBuj6DujMHakCUCgadUsubeMS1A/vhRLBxZEgR6g8juLbUzAE1tWe8wslv9hh9HyQPknZNpEAsUQaJWPDgpttyCjsDxuTMQMwAOiUoTknNv1frrmovfd/wE/1x4cTP4jbuF+3AItwFe81ACGXm0SAC/5UTYGoSBAdwCwmZE9GAqhInmvBr8JPMmAo0zLB7G5YmdMALkTy/fOz5+NonO0VaJl0Bmyh3jDakzL2xL6JnLF8dGjk8xKDBWPtc3euY+dWT7kJX73VveMhfe/4Zy7ONOcgDpnP+a+iC1WLpZJ7RPhArLCPD7snZSVpnmGkgMZTdIrA5BIwOoHBkDLHWs1eRBMZ/Wr2FWGTcFmGQSMZYhjAInAB1C2WQIlrBGUi1JtUaJ8DHrtVIJtwyjZ6bxKjA3Q1n590y8Wl/uDW9wt08LfFgpmCCgx81FroU40KqhUvNLPYu+0NBSYiBUckjDHxtX0eCJJeqBN7cfh2WazKa/FK4MK+wGmHV/t40wbmyoBj8erYDQ5VGYC1VzFmBZpjUzEjGIjhREO5NS2A+f9v/T7t7q7nPc/qZPbYuhZeJdGL6lIMgFQ12gsJUBxwObLxP1KHujO+8nQZT15ANiOsrRcooZVXjDEyyovAzypbDKNQRifdSZiFXTI9m2JQCNBZcAcv+x7w9iF/zEACwlLZEd6ZZiFmWI7vJvu8gevd/dNk/+hPFDa02rwtXOZdZij8axlUoldwnilAYaRDKa1c5ixizxMVqfAEcUIo122RW2OCwv8/aPSotolLn5Y+nBkRWxk4aQuqGu7GOMxE5nPtQcN0DLggvGGYySjRR3L1L77/P6t/tvOuUc0gdvSZb0qL+hxtbSw/tkGma49SB3J2sl4R0SOBqNm+AYbFW9Hz0KgqIPIgWsCgHhzvaZ9rwYsjgKyzZiJMVmNVoiNsi3oWxi3BlkUwshMW7JO35mBu+dc3LmgwNvUfZVNasfH5AHVX/kOvHNBDzbXfX3jZkxnThliwSwtSswcLQDng7haFVUEWb99umAm6+5iyzA0yDbcntDmhQSwo2U5EvPzI3pWOzzDC/OaLtMm0Pm9GbhVpkD0hheX9hZwpxdWPYQ6iiqMweCgUMizB7nBYtXOeKeo0optXC1Mg8WFagMzAtV62mpKwCiC4miBdcCNSRg2aOnQNSu4Amn5MAi2LGyzoF2aaDtLflfCS2kDtBPRtBVf+cr4Dr+XgMs7Tn9CA86uNkDBr4k/AY0jQ0XRsoIdUqV9NXjLUXrywAJ8Yj2LBYrXovsrqMwI1Mhlhn6xUWq/I9tzTKsT1hNO15dNkqV5VFHAE7bTzpGep/6Afi8jXXWMBnBB2CeGXVUeaNAaYV5kD3IYLP5mFpTwlFSkx9VraWv1m+UYNqB46AL2Q6yf2lgr0HRoDX1PBTk1EliEgkO8dPhcLs+Ba9ybTRsuXmVFLI6Cwgoihh7gBcFx4JohKoli8nAJqGhczbIZQIWsynXCSA2mHQ6Don0YuHYY5HvEWsArMKs6by2Wthml9pkzeWwVilwI8KQPrdJEU/rI+xtsa/SfQwKQ1RRlKL1OLwpph6rAtUEbH5z+Xkm3LyFoOMtXwwkLBO1g8XgeSADa7COlzWqASHhKS0RU0rAwqJwyLrOGCUrD4XFKrtqusDRMefEVsfyaVR0ya17AK7GLrWXmsoxGioOKYXB4p0SjCA7ZyqpFNh3KTRG4LbZIf4vMKXa66hYKyh2ZJfNl3Poe6hhW9iCxDGkjBF6nAJwHi5EQT1hwrrBYYatOjkDM5I3FCxrJxmt9OaDs2oP2ZCqMd2jo6osL7SiJbcww1okCfu+WzNK5mXUjXjZafmAc4Lb2ZSzF1IOlafnzqIcaOw9qc0jnKhD6LMByu8Tbx3KhYiAt0ALDZ9uRqUJiAhm9UPagioTS15ZDkrqIEu0U0WBNW5+fbwDABflXjhMiXSxihDZK76x/I4+t7U3ARR1AtbRCR5mxR4TC1myarYhx7RaWcNkoU8t3KrXsMGOsqInoEJqCnA2F7x5Lz88m9m9FOMpu5LqxsksepfBuaquuAsiXYG5QT6vG3SCf1nVCMpSgGTqKHYqN/t4tCxN+sAjcvjopDvnSRiJcLOOW9fkm6LAnjw0sdryxe9Hyqnhetmyr5hSDNqGarLqxsxWkYxhEkydtyX6mz7P2aaerTVwNtENRT/Rfw6mzcJJu8BC4Bf55JEjnGsDV9bRGI1TdQZUgVVdZUUCyeSsKGJ7bOvfALD6viwsVEONOysOvBTxZTxtTekWDC5aSsixcR5weArcxEVt1qw1lxnVAW/ycSRhgU3Xhwmng1haE7BCuV2XSO/0AKn4QyMWKWKQaOYlrhArmVEL3mQOrwR0v7YX4PBlNxwT1nm+ExiwS2gfEVdCOti0afgTYluyR0my9ZVwbtNl17D1szPk6EsSF1FmYg00KuP0TCTVo1SY3GOo7RyL1QFFop2afMmzDrTgEF6eqbGSE93CBdphcT8tlUx/wehuR9V5UBL7i3rfEtPhQPQu08ffBqQoZrFZ7wHGWbGKNAxnfdeUB1dsMuCZoFTC0hGiHQnz4XGaMuN1jLOVVo8YIeMigRT/lk6Tig3bZIHcKW3pwdxZyxnTK0doDBHoJFM6erD1W9kFJwnFNC8HHcMLbB6NIa9LO5EP+obavAheE4HIvCvkpTKk/WUaqupY4ee6cAkSKrqjzOuzTkIkA1HRIw3iFaUHRi9K4lkRoRhEN2jjuKzpkM7cenpWVChnOFKuK1h/L08J0ngU+I11GMWUeu8pAi5eY/d7rYlahT/scEklbsf1JJNSmx3lv1dLyk1usMBqZsFpesEoFhhqcov1aEgJKmrktqQi8KT9Im0UYDMAeBC1tuLFVR/SNs/mcMsQBsV/WCEoTK2GAvvdDPHI6MfGuIVMcR4XvtRY/AHDJAyDTijBYrCZZU8oDYGATOCTE03BOHawJjDpoOERZuUyUORgJ0/Wa0qyGU2ikVU1rH/ckB9bapmT1TUeq8Xpa0L9evW/zFHZDcil5YGtuv5sYV64mSX6tPyPWMxqS5AFlzAIkU37USZJkWsYsEBi54ECwUiy1EA2XbRY7F0zH6NScNlkayYPVJ2J27QGaIOWRk7JrTB5Qs9UdvzpLwPCSbIBroBtEwAa4OVGc/O7rdjiENKAKfnRDRhcXJNs2JmJQG5FoaTKZZFlgIBOMaHFhtYlYn2lTaWIa4dijUdDSvnhwdBMBJyCEUqheAIWWcG25xh85tkAQMSijd4dpw4uMsknO8EPAZTWVZWJiMd78brAiVjyydboMaZxmaTLi1pLgWgcqI00LDGy2O4GvKV309nG9WGNXaWVp0ZY9DeCx72yMgraSTZ2M2hKOTpJUerSXPSi2a0QBIeQr47ZCt4yy/cxBsnUFQJywII/LwxK3k0BJkn8PU2ZEFrTAAx2NL7HGd1ttxEzDmRbd3znzoHS4x2RygYeYKnh5I3wXpkWM1ulXfk2LqBioQI2xAJ2yMwRudhfcvgHgjoR5fGJiZs32ipHWZYpZWvKAGAUyknnvINOG53Mwjy24EJAQh1w53RWEZXw/Zz7iJIp0eIiOfwY5WgbK/AOYRCvGtLT06KZSKSEa7TP6FoELdVE1TDUYQH/v8LkVWXAcfJxpYxtRiDdYkBJWKWiR90MtHs6ibUsmDdqsaRX5dEoTY8pq6Ms2iSdQBDJAKyJclWc0PYpAZrM0cy7xvQoWTcPybe78yMJHHcfQzt3XsslZ+F02Mg+DABQDp4CbeUbx0T71ThF+CM7Sfw5MxkxtJSdj4xOxvjQIoC0YaO5h6xS4w2382C4MtDzSjTOt5i+bWa0l9vAMk6ykEwwtMUOHlMCtoMjvR40IBwNyeTAM9hz00nOLkUswTE1oMnWs07WPn7dzmSx3GZkWnOaNBmx04+Wgpm2A1pJWbZbX8sDnhRTm9YwtCyj4OCAZojUnjY7ZYtGksI65MmwZ2xHg4igqgIvSMyDU9j4SEhpmTsRYgTaUBsVrbSas7IA6hu5DedoRphWar+VQWUsSpxypQNM7MizbNRiQrO02t9soWl1B0zJNrO8L6b1YeCIC5IimTZgR8oU+iLI5AS7RRqWBRgPsExPjBKKlbZTIG89lhtCpPl7XCk3cGNUmwtM7oa1ffGQ7yur3jjK7ZjU88dMTn0wWsdWD28eFBFD4r2RFkCvsYjJt2yELHEmUSsAFxmqlnhrbbTqnqLCiCNvQmAnVqYlHLnDuyBG1LV6xeJMlyeF0s4XgZNPKsZKBN5gsGt1c0RMn8zSS7wk4/DWAWFQ7ePRjNRTLAzftnSWk1l8RG4+QCLQj0kBEa8HA+ccM3OAl5Rqkv4ayBw15AItBUOcMlk+TuYrBhTvyYz/jdn7wR51bHojQxH+EYyl1n4oEzUcO/tGwR79BHEyDb5NBWr+mtsf7hdv/8/vcubtuc9PuWTsPLB6igTtQzBNsO764kAu45nSK5Sj+3GuP6P2IaM/U2l+24WyFG2ItAWttVO73O+7Yle91O0990eCwHl4mLbD/p59zp3/5ajedPSl0KdKk4XeC4AQ7lhdIQhqti0DRATu/333tkbkxMWuzxopYjHV9pi29hsciGZMkaml5EsshcDf2xP0vf86d/vfjwGXkC8d9BXkAIxxzmFD2iIOTd4Fxqw6TtkgPEpqWd6ABWlF7AAGuDCAmibV1fIHhELgPAnAx02YJUse9tzwdyAx8/jU1OeQy29utcEqQSJ1zryHAlbqPAHZM2+QHoMUBJLo7TNsqmjkE7vaAe+ZU4hQJ3Phzn2nR2M6/G1uxY4G1FATJd/PMg4/ANSZEDLhi2bFRLRU6S9JW0bmwUbT1a9ledUpw72LWuL92qHE3gG+QCkHjEuDW2S+oBBiYiJX74TIumT8C0jKPstLj78+95iiXERGsheYz0+Zbw8+t0kSRPVCgLS6MnUWfvmIAfmbcqw6BuwFuHQcuZzRddjk+gR5jWj03wsVE5b0J9LGdArh6GZezcaMAPDNqcgOmh1i8seVBdAoiN+SoUIc5BO4mmA33RuBeo7IKxt438T40EWvIA2Ns65gT/crkCcUc07iUcSVwx8O7jiujx4RWT1dgz6ZCM9igcX/N7TztMB22LoI1cKuuZHMn40gpLoDXWFxIDcd12gmwoSGa7Py51xyb9NfHI413GTCSdnw9ERyR0/un05Ql3LywRJ9XRkM/P152JAH3heuO29/4+2bgnvrla5wLeVxRy5HHQuWjENOOTMK0NIi4MWp4W9F3GaQCB67MHsQKLBTes7vUBunMQ3vHadMxSg4GvXsRvhcbGfcQuOt6YAZumJyhCfpaoMVkFtuoQM9rm+E19Z7cnPk3ALiV4eCsnjKh6NhK1f1EBkDAmx6fvPsQuOvitdxXgBvSYRJUePLMJeGaK2I5IjP8kMhagAdIa5mW78696li5fZWUVepA4MVVmTZLi+pBoxVi5NyDxSHjborcANxfmidnBLhoPqGZMr0abbw0AE8Ij0fa/AcqCfkzGL5TzYzPwB2bSeIwMF7lxfXqaoCvVVxx08cipMOOHEqFtfEbgXttzCpkxh2aiM2vHNluwyf3OLKiOYwBXHLSZwKuqMlU2obYhnQMZgGgxybAs9NNbLHOR0IXgIduJeAeaty1cev2v/x5zrhxQPPsgmjStD8s/H297IG9YVYCV8uDQoxkQcyffdV5BKbjB3WsxpYo84Bmp8ZELK3E1SGa17oX7ryrfnWzydlcDjmBU6bXx8L6d9KZB30KgVGB1chbWD4L3LCz4/a/+N/dqf9wQ5QKa+9a6JwHkV4NU16S3Y0a8GAaMUwVuCZT8swAZNnsp3lCxewUwdj8hhjeo8THqbw4gTsw7q9uJBX2P/NBt//Hdzvnd8TIGlkUGG2MyJGf2FhlzMerBozBKDeWzSmvYvATs3H5fL9w04nvuP2v/qFzB7KemYd4u2BmbAkYnoBEGw1wU5JK8NO4ziXgdlbESKfXYloCbLyF3GbajF713i0A99ydP+v2Pv0O5xdHe6dVJjvSyJEmisBJNTMm52WHKKM+06S7dggyixaF3yR8s6+kCPBmgBR2nyOXdNogB8TGxpWLwIk9ObbY5lRAlgWwgWXtiZ4/+6rz4bkKVe3wF+vMA2ScWtKWjISFuRVmyHYaybQZKNsA7kfe4Pbv+RXnFkcLVzFi6hQSxUhisaI16UhDM3Y2rdKbdGDtzxw0xqwbCSTbjh7ZBByts/0+NgVMxKztU+Raf/aV5xtTMdn5uBrGVEFrIrYFeWB++G5+71wddtV7NpIK5wJw35mAK8v3DEZMA58nG2nRT1yMQBsHqSCxOPTIZKfBnErLcuCpw5hDA0bqaRv73xRiBPhauGD3oolYm2kLb2Hgoh2kwjtaTKM+Kd+fiMUBHWDa7DlzrcLVmwN3LwGXj//IwBrn05rhr2r9qi7QbmM0mPF3+S8l6iEQgN/1o4hk2SAXmAhCS/v2xgC52qo0s3g2IQ2oaTURAMaNg1ZDEjr+xy5xK/dZIR7+nuhF677S1awDF9sB7t3vdG5nlgrFl9WuYXnuQf5ZhypsF8p69SMo48Dg77EkSA4F8f9nabaSQ5Ybx9tGEbhZygsxLRkTFuoXTgBXG0WHGkvTJb3aK5hRo03ytOYZXgBUs8a9es4qvEB57+gvzn3oDW7v0xS4IzWnKGwnPrRCaJa1pX8UGIjtKr9C0LJBRPcLSYePouJba8qLRqSLiL6pPbimRWIK4Ccv46LzglURV2wfAS7WtKU/JhOSgbS+hlhoWDaa7Etqnm0LALU14L7LuZ35bAbLIanhJSAIyw0fgTrCZhq4kTmlHTDo9WmSqG9Ivo20zXiWkofSmQ09mw/0s3aRM8+tkTkBV4NWV/J0BrZ4HfZGrYdEOqkl2hGo5rLGq+/YjHE/8ga3d/e7yuRMM7UuYuZ6UYQy9gCuac1zvBh7WkzbYPSq6dipjkUi9E6DDG1GB093okjxWS/OcEPhHWMny5m0uFDmrVpPE8Dmpb2zrzwe7s8J8OLZTLwAMOa/q3pK5Mn8d7HB/GuN8L2g1rd0KkzONgRukAoIuDKKWBMxzD5kBFJN81geFIXakvJCMgR8kagMSwyoA3o9AVd5bT/Er6tpS1daEzHm0DJn7mepcLzkcfGBxRZoeceK90jW0TMY8DmmTigjMqX+544776o73JF/tInG/VdR44Y8bp6Pyn41Io2haQNwm3laBCokQyR4CJuRd8txi5UFDVnB0D1amogJSZlgRtN8fHD4Q4NpG4sLBULhGXihpwAXAg/qWq6rasPHmTY0rCUNwt+VHha3bB243NDJ8NDvOkxWl2/HQaHfg0Ar5Yd1mLU8twAAiICCk+34Mm6RI+UBtM0GaAcWFyo+xLluJAL7s69IUmHVCvho6UQufdCqrSGmR9qGK4M7/8diS8Cd87ghHYZDIyO2sp9JOxVTVi1Q2E6biRpuhwK7B4ydKWPAG8vTctaMmjL+DoIWEh1dc7E+jRvsGRWWsJ0ixvQLf+YVxye9ImaHbl4sI/aWQaaMQ1oOVG4yrWaZ2Bu5fy3uOTvv6tu3IBVmjXuEgSWHXrL4l3CJQZG7FM0vWUKEdzXiI0X0KMqNkIUI18X266W8eqfLNORBdMrBxQW9/y05CsGOP/OKC+qP2GPqoKHywrU0LdI/NijCWEvA+53tAPeeOR12lEoXcEJ5Z+8ca9s4KKJTgu8tIOcmYwPly0j2YF15kDSz/owtGUcdQUsE8Uke6HYjohqzHwCuwbbprYoBqb6BFpWrYsZkpxo+BwMSP9LginMVtsG4u/e8y/mdo8kxrLZhRwunby9RPW/HCQsJH6nzGHXSjwQFYe4MdvV+zszxMrmZjLRt/tMcbZpRsO7EXUEe1JVXrmmzr4L5hJ6ItTIqFbjaY9TDY/+6wCtQDl8gJ4CPf2D3hzkorBBS+5zEe7fCuD/ndu95p/M7x0S9MAkjFpNNS7d4zJPdzt9/bu3T7KOwLh2Twf6f/r47+F/3haL4pKdY/MrprCpFqGSa3M6jv98decpzNDbRGJEFgtAav3DLb3/d7f3R7zq3v8+iKm3EWPUb719AZ1oNaxaoZ2UbXggmYooI63v8mZtnqWCAkcyelUhuMi0qlgGgbzgLazNcgNiGVJiB++6kcQVmgvUbNRkH++7YpVe48696q3MLq5JKPFP8eOZDv+B2P/HuWiuBpJp1ntbBgTv67Be7C69/i3M7qKa2/e75r/tf+oI7+bYbnIM7IKwNBCgSVPwwnGgnVuWusZVGbXMjEvgzN18IAjzXHqsxLZcG+Ulzco+ZEoKWh7bmh/22xrgWcO0JaiCKg3139NIr3PFNgHvnL7jdT94RMiT1H9J98XcsVG8FuJ93J992o3Nye3oam6YmDY0BTDs3NLCttB+aTFbQVvLV/VdFTnOMJsCt+kM1qK17uW8v2qtweQigHqahEL0zh9RlYMnzrn73hlkFi3E7Kbm57csDd/TSl2wZuEqjJq0IjrNaLt2xZ7/YXXD9bRsw7ufdyV+8MTJu2eWbVYsBPBQVEgCqRGg7fdXUekVMbEXRGj3PgCDjqu81IG+RjYvyIDhiU+y3S//aEwWiBefNktfM6bCf6MdE44qzd/6c25ulwlxkk/91qvZL17YOXA3aktaksixT0/JgY+Dufenz7tTMuEQqzJo2t4RzC2VCDcxqlzHQsu/NjdieXBNwVoFbG1YAaIh8rImTrl0RtPFd8pwyoIdpYXM6V+H8DYF77s7EuAW4/bLG8g2xgz139LJZKrxlfY175791u5+4vTqOsJ0KStSpZscJGndzxp13+YZV2qGUGp4PBWnQ+E6IzbJ56UFKBPAeUhdTgFuS7ubhutb+MKJTVi1NHP4ehJvcNH/KkhLjwm0HuDNwZo3ZTmGpAvCDvenopVf441dvCbggBFcCSY5MkbwVxv2CO/WLN6ZjRkcKgYxJPNS0gnxK20HBzFSSOlrrV/FbF9YK47ZOzGvSONq5gNhyYP9aaKBhGPSpoa1JhdtxVoHurxNGDyYJk2tz7OsAAB/vSURBVLOXuO0CN/afARZOYn18/49sxrh7X4rAnaXCqlvIWfYgTci4IpMMamQOGDABdkQFXH6vP3PTQ8J/K+8ugM3/oTUte6dmjPhIAMbxw/FQnWjacBiAO0/ONtW4beDmEKc02ZaAe+4TdzifsgqqOi9EgTpnrsCYgXvgjv7I5RtJhQxcfSCI1NvAoUJjVpMHHKMD2YPUYVjuGYBL8ixjZ4jFnQsFq+ZMEzeueE1TWiQJIq4pTfWzVNgUuG+Mk7NQq1AdlEwyZ7fzcCKxJeDufuIONy129A5qsxY5te5BAy4GLZNpiWFZDUkGsrqwSJDQ8Dj2CBeSbWuUVlp/ttbpxLhwwhXuIIdDhJeinQtISAPaR3uKoDzQK0nqPIfFgwFcvn08GszQfvPkbAtSYWZc7jjJbnq0+KpjAO4sFd68QTrsCyEdNp09neBmT5BUc3A9bVwJJUTIC2YGACtIkL+3gjkBdyTdRQYxOT37Liz1tMZEJ1xmMnRhWRIfa5gqr5j/ujXgzlIhTc5KDUB+U6PgYzlr3J/eWOMOAldvbJyzChsCN0iFtybgijwuIrIwbO0KrwhbMidQErTnkOnvWjZxh/ZnbnooOLbK8gyZ8mrk7EgDV9K0XAixmWSEU92efv6179pQ477R7d1N0lEJryXlxZxRhLitAfc9fOVMTcZQfncG0NIdffbl7sIb1mfcCNybuufjlqE0vufMCEVEqQqD7jaihHFAVEBe+NM3PZT7AAvd6XjJ1Jj2woIV4kAtLayN0BOx5jljs8bdMnCzCIOaVkaRWeNeNmcVbtsgj/smFxk3Lfm2zqbN6IjyLWUVtgxc5TTVc+OhlpKokFPpjY2McMojUZSv72CA52QWoo8ALvKKGi5tabBd0DIBb0mLLQF3lzJuBgViWhHi5lqFY5sC94MUuEb0YrGWSJeQDtsicK1PmI7kaMsYEaxIgDL7YdByojIiTWLfClzzXIFYexAKpUyPLNICa9NWo8XWaAXaYhTR2ZBVeKc78vQXGDWxCn3qF2c/9PNu9573kHQU0LQgdIdfHextEbg0q5GbifOgpRNbA+7N9HxcMn5pKmIc86kKX6BE6KS8yNhq6du+NwIXApJQPllNYxNGqjkJLAZPdIT7+bEexmFq5wee5fwjH08OZ7bCTz2SiKJ3ef8fuYNvfCWFwLHK+zxgoTrs2T/ljr/830SpACcdDedZLNzZD93mzt39PlEdJvpqRYEHB7iswaY8KExGL+8WzOBa7snn3WDkYf19c/70jQ9TJs8aL8CgtQQsdJ+tS1AY5Cmv6Opy/1WjdmC+YVq6sAuBnQkb+m9W/nPy3zHOh0XSR0wapsktHn6xWzz2B5PBUR/xlp/c14Nv/E+3/NZfiAHtMG0miwLcf7d2OmyenJ18682yHjcCqdQd0H4hYjDkgULViDwgTtuM0s5h4M5LrIVarQHBQr28r/wHuF+xSAVFeS0I0ZwOeEhlK4MwgtR2FNk4pUNJupqW31suD1tn5AilvliGp3aZdz6UMuU2KGoT03VbAu6pt97sptOnnMulIDMXhNEfmYjNrZJVgQZexNYkKAnNRRcWzmOqmDJu2GqjcpmlIVX/GFVEbKwa8kOsL/OcdQvwbEeGNlBse1sbccdCwG3v5I0mJO+WABU/g3DWnp3nMYISgbRtTseFydlmjHvqLTe7aS4kX+Ro58O2myI/ZX/LH7g0iBaZK8wkCyBbrfE1SYZLAdyyKmbStAWKKHrCbU3gSS1Iwm+hQQEMaDgRtoutbNCGx4+cLsNHrOSRi0Pn9nQZNQ0kG8d+2aSbyEGA9F5JBFsE7vLM6Tj0wymvyLQQ3K0QX8IikITMYREG+OnvgXEZ05bWIMq3j/WpTGaFCg7arPO4g7ZFeQQfB20FFAItZkedp+2UNBZ/bLN5LcgxnE+xEWpfo7yQ3r8/55G3wbivcMszJ/mh2tLZGEJr7UHdrNifTEWUA8IJg9o/LkrWCvvTN/4tHuFb+tDwJh5+EXA7tQeQxfSg4vNX03UtUORPcaDySKPgw1xybEUVYTtuWIMIGNMMgnYOyftzHvlFG0uFk295RZQKQ5qWfCTRlGW5Q9LJUZTsEEF6lC5wD1IhAreGUmDgTUsTxWDzAe3sps0mhTXDuWf2RGLdc7zyE2Nbx5jW3Gbf1X2tXcIGAAJwZ8Z902ZZhRWBq+RBq28getfLB2RTmtNoTkrAfXBWxDTLRpiBjX9wCZgP2Nhp19rbY6cNYMBdqsbEwRogoemVUw4yGYvO5YeGNJuBe+nl7sIbtw1c5KTcfpHpLEnIJ2M4cqH79fNaW4n8qRuyVLBZi0646MCYX8UJHQOdRYPY1X0MSPnqmK5p3FvCCARu47yEpr4jjhFFespzG4PYqz0IjRxZrROyaX7trHEvvdw9ZKvARRmAdfO087hlIxGjhgpndu4UlinWCefpUf7UDQ8Hw6+32kTT1Y41WZrkgWvTpcbphIrQcMurka6Vnj7OtDGixfvbkz09sLgQSAMtRxvtbBS4Y5ovtHcrwL3XBY0753HpsRcFEegAv4GJWJJ1Cp+43JWhOyaR+1EPALc/O69s1piIJX1DNWLpCA4zbLUrhwndeStMcaeK9wk2E5OnAiZJAErTin6mgWX61wzvQr4U3bfaEnO5LdZGuWl/PtfhhZsx7hcTcOmBIAC0tfed3djFLiPRmzt3G1MKk9MgcEd1aUp5FbShIzQbbFRGx2LbwRAPswdtXcXDToNVBPhVuGrJAwIKrWn7bJuLnIJ5Z+BetiFwv3SvO3kbyirIxQUEWDGOsW98S1fTmTm4cQTHhBHezKTCUPZglGUB5ff27TfrIpA8qMbralriFONMa7ElYfciLwZmySZwkbYUAyvD7IMGXJny6kc4Kn/gbmHm3Qg/0XZas9p2qcCNoCJ6A7GsxZYogzBSMENYJtCI8ZGQrD5Vz7g8MPeHgfsMI8X+lz9iTZt/S+ZncpZtHB00lt1QOVWk97cB3CIV8p4zuRo2NvunCwt1REc20/Lns3qTKteSmTngE3DlZAwclQ71YZoACRT0vU47gK1pG2F7marDMjHmdhjfHGPNbNQf11qKaCwO0NGJF2CWhsbO1VjxXZbEEoXkz7ncPeTmDbbuBOC+Mi1AiOyBOd5AvxLD1v8cm8TJoaPRsHUclz91wyPA19NX1LTp7XqyMhY+zexB03gLt/P3nuUWj3qiqsfVbNrKO1K1icJY7yw0cr/Z3ngNGlTK3mzQtAhOD8mctnRHvv8fuvN+9PK1tw7tAeAW3+8dElO8mcum0uyhVOBIsQ0niowxf+r6R5Bxrg9KGx7SL+SA6lpauL0dpz/YKALQVrliHqocP15y/rXvcEef8eMEuNZo///6+zknip1tpMczcB8gjFvm1D3QBS9EgEUTTD4PiVegKjIRyTAJhM8czM8gwK1hkdODMRlT4UHOAC2D5t9bjcfF19Qxwgx0seOOX/ef3NGnP39kjA6vARYowD09H3qX9G1vAl1ChwRuf4IZNVej9LExh4mvjRGcABeB1gKenIiN3is7ZkzEWjpwbnzu3CFwN3bGCtwzjT2FGJDG5Nb8nkQsfB2v16WdK6qEOJU/df338DlL+cmQB0ysjeZpqdbtZg6KzMqarzY8d2feObBwx69/xyHjbgDfCNxX15WzFuMJQuGXdspRy8fJwcQut589kF+HahYKcMt9kPHQ/rCkVZjhOnvEkr7hE5X0gEaICsAtuZKUZzwE7gaQjbcG4N726phVYJkYnBExT3TshPiYo1XPjCnDwkpC45beYUkZgNsG7fyEWgSeZ3XQak1RbyWZBzRtQTrZanMI3O0CNwIrwUjOVxBJjeZ4O9+DMCN8axK3cP5klgoDTFsEsjRZR5fm7e9aFyUvszw2r6aEIxNFwcchcB8M4Or9dDwnzjdSDkymmh/1E7KTqoaQ4VBEWHPN/uR1WeOi8BAuzJE6PXcke0BmnKnaB8sDOzyE64s2AitOM3Cvm9Nhh1mFdRFcpEKpDuMTsUi/YvKd5V5Dk4ZLzOq+fvbBXowqkX/yJ697JCBClKcFIOukTnKJGvWkYuSBe+MbQWndfO9iBu7bD4G7Lmqpxj192ihr7JcX2vn7+cSLxlctgSwpMlQhkhBX+hsGLqmuMjVtL9/XqqfV0qJoK5pBiAlxWZqYPPYQuBtANt4aGfc1sB63eeAgmTixRoTBG015oWiL7tWgDXdqxm1sIS/UiXK86+ZpReojhRnItLRO9hC4WwDuH8asQpYKhFBUuktpPYyBsXMVNGj1uXQzYOf98hK48b0EuPZO3Mh86V+Lac2Om/fy4vFinIHdrmFydigVNkHv3hclcOVqGAFYayKW/ja+L1Bo6QCwsXWD3F8O3NIAROP9ukxW6Kws2ikCL4YZAG1oXlqAeMbzNhm7v9H3BuC+ec7jzmWNqxe8yPmKwnY4WD/pXJH2yj+GSrgBTcuW/CvjysM6UFmjLQ/Ce9MXCWHKC6bLonOklEVKIIxuZ5lv3UmMewjcdb1vBu6JN7/aOQjcNN4dps27eHNMZYeENOTFKhMxtO3fn7z2UbF2wUpvNEBX1IN5jb24QCdh8TnDoI2zVb9wFwSpcAjczYD7mnIgSJWE/ZRVwGRvi7qBKRu0AgcN6RmBW3K1eMWEG0auIxuyIkposB+j5gXjQf3W2bJop3ES5uHR3h37x1e6nSc9DZyYWFsMI4BigtbQo0hDrjdf0LlPvLI4cvl9inrm81GbV3inX7j9//1Vd/Y33++m3V3yMMS0+rm5UgtiwyLBzGOQ6FT2IJlE6u48OYvAxVvBe3WZ4ECN0JEeS5cjkcaZFgt/ckynFdIMbVXb2dDUJaQYgOjaZ5UyPvAOfUhfncwa381lDjCSsmShdhC0LaYtDcA2w6fao5SX4SxpTBLjVhasHlQYLzelLAdGXYruyUrHSJUkULMshZWnVYRinPAnAbv2hk8UbdBhHbbWz85Qm8RCrmIQpd0YGOV7UPQiX0EKl4/pUnMi1tKzaexKmGfjI9+7yvjb8oAHRm5L/8C1fycMNWvzgKfKWR4AvIBeAnprGRcydWlw9JcsQWqvOCAAw/L+dfZClft72Q3ECLmKDYCuGYmSqXofCmGDJPaIhed3nCq2obC2crBGiI9OKckKOTtxIIIALC2a516QI/bpe1IxeQFuk+IZ2o3UyUCVVxMU7WqjYSMn8Ofrc8ubmqwYuKG3ufuDY4NybYX8poE2ugwmwfTLRjpKvXtkN25+S2FDnTPXJJADcRFJNfUzuoVcO23SoqLbTdCGHsOT0ZPzBMYtT+zWD4A0Wbm5X0wcL0XhV3upmcxuhbOWntUTxTqQPZbtMVFiI9W0DIyATJzHLhFEaVZiE/Jg9n0OOjfp2qUCipFATx6UXSfCASm0KQYEPNdg2kju1KnyL4gNI3DbIZoc1oAFdy97UHWIBC3SZMjAeBCZRiRM25cGIJxB8LbbV8ZuKC3Ebcfw0vrMKGHbsQOpcd/GisDB+LZ2+7YcepoZE+3kbDOtiqyl/7xt/oFrMuMaHpVutLeQG3oujWr3fFrGJo0Dz1i4lNKlhkXNeiOatqdnRdilrNLdxo21cHhEAqxyNPb8GqWKmjOjnLBLKnipcon0A4YHfn/zIG1jPMpjIRl25YFm2hTNBJE7/8A1F4Mu0A5IQU4eMTMNt3rNPLCnji4uoHc1WI9IgzHACjYKN422TTNZ6/zWGu/sKBW/I0b/YYek8qACo00YuHjfdnjZEnQKOLtGyLISfQygWcUy7D50OqchXwFwA0OFlTyac1Ne29XDuUkjmtZyDgO0gnrW0sPBwAi0CaCNMBj+1DrzoTJibmlRS6FH6SMh0NnYSNZJWOWHAflCSkohm7dCvLkiJhwFMKpdq4JLE3larvv8aMv0XgFcoi9N3TaWdomdGGWzkYJlwnjJ8Guddg21rGA9hShp1JEMALGlpDOlaS0mlCmvEdnTOenSiJDFX/KsnaVIUfvqPsfqazjXrKKKkIfx7zrKS7MFvihZBSoVClgNBoThgQ9q1rRmPa0AhQYf8jwNWjOP3FvNCr1HTsWYLDElCPOwQF4OrL4vPBDmaVV/p/q1PLptCjGtHPD4s8nkHabNk2x+f5vh87W4ymtuSv7yH0+X1qBpSB4gYPOhiPMd/oFrHl0vWW3XQnKI2jH+Lsq2yhNDu8eq7PHgFPzVsGzWRZRLWlGAi63ctxrq6945khi326bat+I3xCog4oJBs0CbpKwUoYKQrrfbrH0kUsEAbx+ahAlbpTsZBqwon3CZ+8aBS8IvoujxlNlIyqt2ooLdCpc6TGvQosUP9LyR7MEoy7ZBy9o4mO6i0qrYBQJvhGkHGDqFaO0U6Pn8d7V9KLdv1x4Qh4yRv6TMjHrt1H9JwJVxm2wLBpPuww/3rjbRWSmcEYkC70NhhWkmJA2yyxuhqjCCpemlfBGSKXs/BG26l7e7hFXmzEN9Q9KuHeJrFBqRFlj64AnqGhOxVo2FOPC5sO4sFXDFTnsJN8X69P8MYIwUvMBGE6OX2DdUZKO1aRl4EAkUKAD4WqG2OlSRFJxpUXi0IovczTwAPMFG8Q5iJ9Y/g3zUSeDyvei+egAdj84StMiZAVEwsZoB0d506R+4+jHhNjWGAyFqlcWF+I5sWrAOTVi1yM1STs9BW7A8NBFbLRLQd0cYGA5TjKYHJ7bPp7QXHdoUDpkQBSmv3kocfzfW3C2nJJqRj33oS0yGln/1PzNLmSWmtKsEP9mrY5OovaztXDHKtaJrAO5KoE0fr4olZauBYizMa4/nHYdhVqRTwjX6OPtsuB7T9pwWOFkBfH6HtbgA5AH/1YA2Je/v34s0ewMUzKnKvRGz4X87KS9hG9i+YizM5vYnFeq7/YnEuIzyjeR69bh89Xi9KvQ6jWRwYLAd4hArVO2G5Mto+G2cd9UDbSt7oPoL5AGyidLrnAVjnwf6BhcXkHTRNsfFMgAHUB6OMm0FZjUDllYAuI0kN9NDI9mD+NLciLi1jRReIBC0PHY4hIa2ZV+p+B5e7QM6jMVUNLC19oCRgAJd/Kt50Am7WbeD6gI1uAz0oI2t/rfunQtmwuG2vGetZVyTafPOX4O02gxdHaACN4dHFiq0J8cHj6SV8ApOWEk2NFQ1DDvSPw50oy6Cecfgd3vRztFWlRt/B2A8xbTZkJIJQS2tKrkUoAPSpUaw/kdCYqTkJMJY2nDKONaWdLFTXuxxQ5q2UevLQnW1iwYu8vgm06bOKQ8ywhnybK4j9P63XmV/eaZRFyFOHIyjIcGxWlqIRBE3rZCnpWGgAKNhu+gwDWnAUaIK3MOjW5NYk2mj45knJrIP9RrSQmLJkEH1cEN6Q9t5/YmrHwu+uiPYsgkMrEEboSK1jmuXOKDohHNDwCsmGK+LoObJA6sCjXo+b0cBbm8ZV9iO2iW+UzKm1nTr1tKaac6OHi5jAYGW7Fz+xp2q2pHay1pcWD0K5LEDwK0PC40wQWtV9MuVFOQ5PARV+wCQ9lJe4eYx6aJZdmQSRtpKhXMALP+bFX5nPZt7phzaYrz0ewpaBQqTLfO4rTYWNYokDLDnr1lLC48oWC+K0MDsT1z1WOFXtdHRUKOgiIPIoz7SR5hRIOP1QmgIg6PtE4zZ+kq3ofmyt+McrdCDpEMZuNU2I1VeiI0szTkie9S9Kl1YzK1OJWqDlvWLhbNGtBRjq0jAZPT4TAHcCrT6XCN7QAaXhnkOQAlcBdry0eJ8XzwkZORbEtlCxooYawgIZ0O6DxveXFxQepO3DYfudkQaZOiMnWiU1m7cFkuPTMSgZpbRBxEWQfSwZscROHQ2Mi6XB/E06YZm7IGicYJNNGxRKoMrdmhw12daXR2FmMwA7cBELKJI1tKO1THTnQv9KKSjSO5bm0C0/MEZlb48yPKI+cNgLTeTiL3oKivEMnDDfeXMA6QbLS8aKQLnTJvDAjVu/O9+agfX0iaF0tPDvS8agvBU5QHcQp6YTh4XhTTtWN9wcXznKKs0dmZtc4NlC49AsJGJWBgg4SiWxlcgrHUvhpSs8tp4pjzbzp+46pKI2fIyYxm3AQoVzpBGJBetVYeL9SwEDpqE5ZOyNYvJAeHRh8qg/KHoGvSkM4O6gzwQLUZpRCBcoM2lT8jHkE+PMyZDY8FqU8K9ot5hlGll9snQtC1p0LBLQbOqW/BBKuy5yR+J/VsNtNqx2qmdzLRUv+dQAyoqRE7SOqwjhb2WdjM/oEKBZ0mDOWkhC2aUVmeZjbYu5WE6a1JoS0r3xWjCscKNqO2N+UUB7np1B2U+wirR1gCt6p98hlkhtu9PXPW4b7tpekRT0zKkNcIF8h7GtMjIIzNsJF0QYPXzYzgxjFqkifH3/pFIOfIV4Cq2Y4wn2iekCTNfb3m6PHcctKSxkSda8kA8H0bVAjwLtLy/MdLSVrD5jviDPMaUveM7/q+vvOSPvVs8BbKgYXTaibJo0GC8ONlDE6yG0Zk3GsU8LO6DZ/U+WWSyVXw5XhEjg0HkFQcFmg+g/sdnKUD1loDTuJgLEx0mGzpdxgSu7EefaSuMUKQyMJBsC4nATX/iT1x5ya9PbvFPOHA1C+Iw3xggAWRFxpss4w5M4rqrRg0mDm1dhuR9Z39ZjAR9aQAijZglF/t3J5irTGKN6KgGo69p4fg39ojRgvaIJp8OLGQCGy9RJ80OAR/Gxt3lT1z5fbdObnqzc24nGs8GYx98yKMQ23aYdorZ3FW2tzOSaR7rn9+NBrWeeaAjEGZaFZTW3tHRtn3tHxofdG/pXyX0FGH4OKI8eLxFRgK2JG9Fq1aUUwDSUWggihwsJ/cv/Hdf/vhnOr/8qHPu4hgf5ek0cjWsYaTc01KUk79LSJiLe2m6gwLeAm1nwNLIdpmWHLNJwVls2tuNG2yEKrySXTqSiebM6/uxw4PsCGcuTjRCI0rHRPZr11PX4VxNHkA93LJLrv6DbMGwMV/5zQO38yL/zZf+gwuPH/nrDzvnf0Ks1zIaV1oDNYTpkrZ2yTgrnzRkz1uvYGbsXIDarvzKIJUhYC0WHFlcwJqe+DYZprYmDu3s1SIb1VosxFsSofzekBalpfZWm6ipetucgGRKztfbfl9OsHHut/z+4qdDS7/78u99qXP+Pc65Yxz0sSNDoE254AACMRELv1tb044zLawuY6Gbgza2Nf8z9ogpLSCWcY1CccaWBBiQjdQ7BIBSWM6/tccDkYVle1lPq+/lZx5YoENLzOJawzGi5VEtLopA4Xf7y+Xy2ks+8bvvCz+duO5Jj5x2z/5X5/xl5WEEwbbXElAVFEiwDyx1lo6tybRVV/FwaUx0FPHMxhs6gC5OxBjghx3SIAEGWgC8BFrYZnBvNUBiP4tljfcqp8g4MBh/nJCAY8C5iARt/dk7/wd+37/w4k996i/Lb7/7su/7p5N3t3vnLmBbnLmO0uHNMF69cN08rel1LCjEMArYoLPSZ8sDFOLn5wNduwJo+8BDfYjRqzoKsom+rxBNC7Qj42bZlkYpJg9J9Oo45CDgaQXb6YMDd+3jPnnPh1iPv37dYy64cPfYrzjnXtY3MtZk6r6B1I5Re4DBSIuByASQIVmFbhQCE2u2CmaE5t460ypjaYdpzrAb4Xc8T8ulU462SkJpQJQQr1ROpwA/v6OpaUHfls5/cLFz/vWP+djH5s9gcjF64p8/8UnLxXJG9FOZZGANbzMhCzWtwWnJgx6TldoKCUo7zJSIlw6fy8u4ZmgMI1KlQR2gxrI29SCyvq4Y07Jnjm6TONpARj0hy1jfeCERkU60YCZlqFuTqdBhrJlRJVlvgSk0hETGYXKMfb/3YOFe8rjfuvvPBKdXi//VlY9/rp+m9zq3eEL4bRO03GOLPQeAZzItrgnlH97oz7Dh+n3oymD2II64XFxQg5mAgR1GsfTAwgkFhXKqBuCH5IHB0uVe5gHtmmgOPBSBgYQhwBVREnhqtqn/2r7zV37vb3/qM/Qe6FLffdkTnz/56e1uchG81OMZmEX6I4xU6TDxdjmwjVpaajz53sJIwp8kI4msBgPQQD1tbAKo9OpJnzSaYwdN48kKH1Bh+x7wFI3JzIEGUxxO2pb26TJqeMzic/7MzLgQ8AJT5B33T37nlZd8/JMfl3axYoH7q5c94TneTb/knH8qWk0bK020JhNrFIH3wowxqNUIMXOg9JsaCSYPkr2xpmfGFO9XA9Rhy1iW2JM+OQbWsynCYyGTCdACeaGYlhCDciBgf72ShhyDE5mKIsz+DOz3Huy4Wx73sbt/DzmzCdz54u++/Ik/sJzcG72bLnfOX0B2Log1/E7Kiw1ae7WmNrKChXYWAg+AgodplO4iRi73V2nQ1qVigIDTMFA0QFsYT7Fley5RSQp981fWHuhJWPwNjZhGuE+AVc0bmoQlO1kH62HQnnaT//WlX77pkt/+9FdwBMKFnOzasLK2OPmCaXKvnpz/Ye/cUcbspm6JhRWZIzbZiavDGQqhIPTmy1bMHnBjDUzGoESAQNDnma37OabmMmmZiCX/1cDlQ29p1M7ignbIzBfRhKqQSIwRd/hdN/nPueX0Tnfs+G/k7MHawM03PvDSv/uofeee57z/Se+mp03O/W3n/E63AJywmQIE8wAGxmKAsXwfN0h5ZW8ixt4PFhd65YUEsCoSaIrSS+jDs3YOvPCuXi0t6xvhD1R0bk2m4a6KyqIqOrIBbh8TmoB94Jz/lp/856Zp+V+Wx93vXPLRT3/bAiv9fVMqoAcEBvYP/NByWlzm3OJZzk1Pds5f7Nx0kXPuCF7qNHbiio6q9yWDKr0IBkXei2sPLI9HW25GmXbFLfmFiRIALBsA+VG6rYEGvrWAn6/8ySxNRCwtnmmtpqVFk8i4lZC8c3uTcw+45fQN5xdfnvzyD5bTkc8e3Xf3XfypT50aAWy+5v8BUrIHNHvQF7oAAAAASUVORK5CYII=" - const Header = props => { const { globalUrl, isLoggedIn, removeCookie, homePage, isLoaded, userdata, cookies } = props; const theme = useTheme(); @@ -24,6 +22,7 @@ const Header = props => { const [DocsHoverColor, setDocsHoverColor] = useState(hoverOutColor); const [HelpHoverColor, setHelpHoverColor] = useState(hoverOutColor); const [anchorEl, setAnchorEl] = React.useState(null); + const alert = useAlert() const hrefStyle = { color: hoverOutColor, @@ -58,6 +57,47 @@ const Header = props => { }) } + const handleClickChangeOrg = (orgId) => { + // Don't really care about the logout + //name: org.name, + //orgId = "asd" + const data = { + org_id: orgId, + } + + fetch(`${globalUrl}/api/v1/orgs/${orgId}/change`, { + mode: 'cors', + method: 'POST', + body: JSON.stringify(data), + credentials: 'include', + crossDomain: true, + withCredentials: true, + headers: { + 'Content-Type': 'application/json; charset=utf-8', + }, + }) + .then(function(response) { + if (response.status !== 200) { + console.log("Error in response") + } + + return response.json(); + }).then(function(responseJson) { + if (responseJson.success !== undefined && responseJson.success) { + alert.success("Successfully changed active organization - refreshing!") + setTimeout(() => { + window.location.reload() + }, 2000) + } else { + alert.error("Failed changing org: ", responseJson.reason) + } + }) + .catch(error => { + console.log("error changing: ", error) + //removeCookie("session_token", {path: "/"}) + }) + } + // Rofl this is weird const handleDocsHover = () => { setDocsHoverColor(hoverColor) @@ -232,44 +272,43 @@ const Header = props => { } - {/*userdata === undefined || userdata.orgs === undefined || userdata.orgs === null || userdata.orgs.length <= 1 ? null : - - { + handleClickChangeOrg(e.target.value) + }} + > + {userdata.orgs.map((data, index) => { + const imagesize = 22 + const imageStyle = {width: imagesize, height: imagesize, pointerEvents: "none", marginRight: 10, marginLeft: data.creator_org !== undefined && data.creator_org.length > 0 ? 20 : 0} + const image = data.image === "" ? + {data.name} + : + {data.name} - return ( - + return ( + - -
- {image} {data.name} -
-
-
- ) - })} - -
- */} + +
+ {image} {data.name} +
+
+ + ) + })} + + }
diff --git a/frontend/src/theme.js b/frontend/src/theme.js index f5424ea8..8a84975f 100644 --- a/frontend/src/theme.js +++ b/frontend/src/theme.js @@ -33,6 +33,7 @@ const theme = createMuiTheme({ boxShadow: 1, fontSize: 11, }, + defaultImage: "data:image/png;base64,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", }, typography: { "fontFamily": `"Roboto", "Helvetica", "Arial", sans-serif`, diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index 82345091..cdce9c36 100644 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -390,10 +390,10 @@ const Admin = (props) => { if (responseJson.reason !== undefined) { alert.error(responseJson.reason) } else { - alert.error("Failed setting new password") + alert.error("Failed creating suborg") } } else { - alert.success("Successfully updated password!") + alert.success("Successfully created suborg!") setSelectedUserModalOpen(false) } @@ -499,6 +499,10 @@ const Admin = (props) => { if (responseJson["success"] === false) { alert.error("Failed getting org: ", responseJson.readon) } else { + if (responseJson.sync_features === undefined || responseJson.sync_features === null) { + responseJson.sync_features = {} + + } setSelectedOrganization(responseJson) var lists = { "active": { @@ -1726,65 +1730,65 @@ const Admin = (props) => { } Cloud sync features - - {Object.keys(selectedOrganization.sync_features).map(function(key, index) { - if (key === "schedule") { - return null - } + + {selectedOrganization.sync_features === undefined || selectedOrganization.sync_features === null ? null : Object.keys(selectedOrganization.sync_features).map(function(key, index) { + if (key === "schedule") { + return null + } - const item = selectedOrganization.sync_features[key] - const newkey = key.replaceAll("_", " ") - const griditem = { - "primary": newkey, - "secondary": item.description === undefined || item.description === null || item.description.length === 0 ? "Not defined yet" : item.description, - "limit": item.limit, - "usage": 0, - "data_collection": "None", - "active": item.active, - "icon": , - } + const item = selectedOrganization.sync_features[key] + const newkey = key.replaceAll("_", " ") + const griditem = { + "primary": newkey, + "secondary": item.description === undefined || item.description === null || item.description.length === 0 ? "Not defined yet" : item.description, + "limit": item.limit, + "usage": 0, + "data_collection": "None", + "active": item.active, + "icon": , + } - return ( - - - - ) - })} - - - {isCloud && selectedOrganization.subscriptions !== undefined && selectedOrganization.subscriptions !== null && selectedOrganization.subscriptions.length > 0 ? -
- - Your subscription{selectedOrganization.subscriptions.length > 1 ? "s" : ""} - - - {selectedOrganization.subscriptions.reverse().map((sub, index) => { - return ( - - - Type: {sub.level}
- Recurrence: {sub.recurrence}
- {sub.active ? -
- Started: {new Date(sub.startdate*1000).toISOString()}
- -
- : -
- Cancelled: {new Date(sub.cancellationdate*1000).toISOString()}
- - Status: Deactivated - -
- } - - - ) + return ( + + + + ) })} + + + {isCloud && selectedOrganization.subscriptions !== undefined && selectedOrganization.subscriptions !== null && selectedOrganization.subscriptions.length > 0 ? +
+ + Your subscription{selectedOrganization.subscriptions.length > 1 ? "s" : ""} + + + {selectedOrganization.subscriptions.reverse().map((sub, index) => { + return ( + + + Type: {sub.level}
+ Recurrence: {sub.recurrence}
+ {sub.active ? +
+ Started: {new Date(sub.startdate*1000).toISOString()}
+ +
+ : +
+ Cancelled: {new Date(sub.cancellationdate*1000).toISOString()}
+ + Status: Deactivated + +
+ } + + + ) + })}
@@ -1813,7 +1817,7 @@ const Admin = (props) => { }} > - {curTab === 1 ? "Add user" : curTab === 7 ? "Add Sub-Organization" : "Add environment"} + {curTab === 1 ? "Add user" : curTab === 6 ? "Add Sub-Organization" : "Add environment"} {curTab === 1 && isCloud ? @@ -1821,7 +1825,7 @@ const Admin = (props) => { We'll send an email to invite them to your organization. : - curTab === 7 ? + curTab === 6 ? The organization created will become a child of your current organization, and be available to you. @@ -1876,7 +1880,7 @@ const Admin = (props) => { }
- : curTab === 7 ? + : curTab === 6 ?
Name { } else { submitUser(modalUser) } - } else if (curTab === 7) { + } else if (curTab === 6) { createSubOrg(selectedOrganization.id, orgName) } else if (curTab === 5) { submitEnvironment(modalUser) @@ -2513,6 +2517,7 @@ const Admin = (props) => { bgColor = "#1f2023" } + return ( {
: null - const organizationsTab = curTab === 7 ? + const organizationsTab = curTab === 6 ?

Organizations

@@ -2716,17 +2721,21 @@ const Admin = (props) => { + { style={{minWidth: 150, maxWidth: 150}} /> - {organizations !== undefined && organizations !== null && organizations.length > 0 ? + {userdata.orgs !== undefined && userdata.orgs !== null && userdata.orgs.length > 0 ? - {organizations.map((data, index) => { + {userdata.orgs.map((data, index) => { const isSelected = props.userdata.active_org.id === undefined ? "False" : props.userdata.active_org.id === data.id ? "True" : "False" + const imagesize = 40 + const imageStyle = {width: imagesize, height: imagesize, pointerEvents: "none", } + const image = data.image === "" ? + {data.name} + : + {data.name} + + var bgColor = "#27292d" + if (index % 2 === 0) { + bgColor = "#1f2023" + } + return ( - + + {
: null - const hybridTab = curTab === 6 ? + const hybridTab = curTab === 7 ?

Hybrid

@@ -2836,9 +2861,9 @@ const Admin = (props) => { Files /> Schedules /> {isCloud ? null : Environments/>} - {window.location.protocol == "http:" && window.location.port === "3000" ? Hybrid/> : null} - {window.location.protocol == "http:" && window.location.port === "3000" ? Organizations/> : null} - {window.location.protocol === "http:" && window.location.port === "3000" ? Categories/> : null} + {isCloud ? null : Organizations/>} + {/*window.location.protocol == "http:" && window.location.port === "3000" ? Hybrid/> : null*/} + {/*window.location.protocol === "http:" && window.location.port === "3000" ? Categories/> : null*/}
diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index 87c4830e..ac279bd7 100644 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -1,6 +1,7 @@ import React, { useEffect} from 'react'; import { useInterval } from 'react-powerhooks'; import { makeStyles } from '@material-ui/core/styles'; +import { useTheme } from '@material-ui/core/styles'; import {Avatar, Grid, Paper, Tooltip, Divider, Button, TextField, FormControl, IconButton, Menu, MenuItem, FormControlLabel, Chip, Switch, Typography, Zoom, CircularProgress, Dialog, DialogTitle, DialogActions, DialogContent} from '@material-ui/core'; import {Close as CloseIcon, Compare as CompareIcon, Maximize as MaximizeIcon, Minimize as MinimizeIcon, AddCircle as AddCircleIcon, Toc as TocIcon, Send as SendIcon, Search as SearchIcon, FileCopy as FileCopyIcon, Delete as DeleteIcon, BubbleChart as BubbleChartIcon, Restore as RestoreIcon, Cached as CachedIcon, GetApp as GetAppIcon, Apps as AppsIcon, Edit as EditIcon, MoreVert as MoreVertIcon, PlayArrow as PlayArrowIcon, Add as AddIcon, Publish as PublishIcon, CloudUpload as CloudUploadIcon, CloudDownload as CloudDownloadIcon} from '@material-ui/icons'; @@ -328,6 +329,7 @@ export const validateJson = (showResult) => { const Workflows = (props) => { const { globalUrl, isLoggedIn, isLoaded, removeCookie, cookies, userdata} = props; document.title = "Shuffle - Workflows" + const theme = useTheme(); const referenceUrl = globalUrl+"/api/v1/hooks/" const alert = useAlert() @@ -391,6 +393,10 @@ const Workflows = (props) => { return true } else if (curWorkflow.tags.includes(filter)) { return true + } else if (curWorkflow.owner === filter) { + return true + } else if (curWorkflow.org_id === filter) { + return true } else if (curWorkflow.actions !== null && curWorkflow.actions !== undefined) { const newfilter = filter.toLowerCase() for (var key in curWorkflow.actions) { @@ -1065,16 +1071,38 @@ const Workflows = (props) => { parsedName = parsedName.slice(0,26)+".." } - const actions = data.actions !== null ? data.actions.length : 0 const [triggers, schedules, webhooks, subflows] = getWorkflowMeta(data) + const imagesize = 22 + const foundOrg = userdata.orgs.find(org => org.id === data["org_id"]) + + //position: "absolute", bottom: 5, right: -5, + const imageStyle = {width: imagesize, height: imagesize, pointerEvents: "none", marginRight: 10, marginLeft: data.creator_org !== undefined && data.creator_org.length > 0 ? 20 : 0, borderRadius: 10, border: foundOrg.id === userdata.active_org.id ? `4px solid ${boxColor}` : null, cursor: "pointer", marginRight: 10, } + + // + const image = foundOrg.image === "" ? + {foundOrg.name} + : + {foundOrg.name} { + //setFilteredWorkflows(newWorkflows) + }}/> + return (
+ +
{ + addFilter(foundOrg.id) + //setFilters(["Org "+foundOrg.name]) + //setFilteredWorkflows(newWorkflows) + }}> + {image} +
+
From 4010258c373962f259186ed0f2fab3c118352597 Mon Sep 17 00:00:00 2001 From: frikky Date: Fri, 30 Jul 2021 15:38:33 +0200 Subject: [PATCH 015/117] Fixed smaller than not implemented bug by @ael on discord --- backend/app_sdk/app_base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index a4da37fb..cf6a462e 100644 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -1675,7 +1675,7 @@ class AppBase: return True return False - elif check.lower() == "larger than": + elif check.lower() == "larger than" or check.lower() == "bigger than": try: if str(sourcevalue).isdigit() and str(destinationvalue).isdigit(): if int(sourcevalue) > int(destinationvalue): @@ -1684,7 +1684,7 @@ class AppBase: except AttributeError as e: self.logger.error("[WARNING] Condition larger than failed with values %s and %s: %s" % (sourcevalue, destinationvalue, e)) return False - elif check.lower() == "smaller than": + elif check.lower() == "smaller than" or check.lower() == "less than": try: if str(sourcevalue).isdigit() and str(destinationvalue).isdigit(): if int(sourcevalue) < int(destinationvalue): From 575b0bb297a21360695ba88fc83be69ecd2eee5b Mon Sep 17 00:00:00 2001 From: frikky Date: Fri, 30 Jul 2021 19:53:13 +0200 Subject: [PATCH 016/117] Added debug info for bug with workflow deletions --- backend/app_sdk/build.sh | 2 +- backend/go-app/go.mod | 4 ++-- backend/go-app/walkoff.go | 5 +++-- docker-compose.yml | 2 +- frontend/src/views/Workflows.jsx | 2 +- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/backend/app_sdk/build.sh b/backend/app_sdk/build.sh index 1e52f74d..028328e1 100644 --- a/backend/app_sdk/build.sh +++ b/backend/app_sdk/build.sh @@ -3,7 +3,7 @@ ### DEFAULT NAME=shuffle-app_sdk -VERSION=0.8.104 +VERSION=0.9.02 docker rmi docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION --force docker build . -f Dockerfile -t frikky/shuffle:app_sdk -t frikky/$NAME:$VERSION -t docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION -t ghcr.io/frikky/$NAME:$VERSION diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 9841d41a..8fd1bd75 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -2,7 +2,7 @@ module shuffle 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 @@ -22,7 +22,7 @@ require ( github.com/docker/go-units v0.4.0 // indirect github.com/elastic/go-elasticsearch/v7 v7.13.1 // indirect github.com/frikky/kin-openapi v0.39.0 - github.com/frikky/shuffle-shared v0.0.74 + github.com/frikky/shuffle-shared v0.0.76 github.com/fsouza/go-dockerclient v1.7.2 github.com/ghodss/yaml v1.0.0 github.com/go-git/go-billy/v5 v5.0.0 diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index 04827474..34f6d5bb 100644 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -1190,12 +1190,12 @@ func deleteWorkflow(resp http.ResponseWriter, request *http.Request) { // FIXME - maybe delete workflow executions err = shuffle.DeleteKey(ctx, "workflow", fileId) if err != nil { - log.Printf("Failed deleting key %s", fileId) + log.Printf("[DEBUG]] Failed deleting key %s", fileId) resp.WriteHeader(401) resp.Write([]byte(`{"success": false, "reason": "Failed deleting key"}`)) return } - log.Printf("[INFO] Should have deleted workflow %s", fileId) + log.Printf("[INFO] Should have deleted workflow %s (%s)", workflow.Name, fileId) //memcacheName := fmt.Sprintf("%s_%s", user.Username, fileId) //memcache.Delete(ctx, memcacheName) @@ -1204,6 +1204,7 @@ func deleteWorkflow(resp http.ResponseWriter, request *http.Request) { //cacheKey := fmt.Sprintf("%s_workflows", user.Id) cacheKey := fmt.Sprintf("%s_workflows", user.Id) shuffle.DeleteCache(ctx, cacheKey) + log.Printf("[DEBUG] Cleared workflow cache for %s (%s)", user.Username, user.Id) resp.WriteHeader(200) resp.Write([]byte(`{"success": true}`)) diff --git a/docker-compose.yml b/docker-compose.yml index 3bba8d9c..471d1e38 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '3' services: frontend: - build: ./frontend + #build: ./frontend image: ghcr.io/frikky/shuffle-frontend:nightly container_name: shuffle-frontend hostname: shuffle-frontend diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index ac279bd7..c8898acf 100644 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -1979,7 +1979,7 @@ const Workflows = (props) => { //backgroundColor: inputColor, }, }} - placeholder="Filter Your Workflows" + placeholder="Add Filter" color="primary" fullWidth value={filters} From 08dc1559f5c5fe1fd7289c47903eea4474813e1c Mon Sep 17 00:00:00 2001 From: frikky Date: Fri, 30 Jul 2021 20:07:48 +0200 Subject: [PATCH 017/117] Fixed minor issue with #450 --- backend/go-app/go.mod | 2 +- backend/go-app/main.go | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 8fd1bd75..60670e20 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -2,7 +2,7 @@ module shuffle 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 diff --git a/backend/go-app/main.go b/backend/go-app/main.go index c5b9b863..9c0d1f68 100644 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -3975,10 +3975,9 @@ func runInitEs(ctx context.Context) { log.Printf("[INFO] Setting up %d schedule(s)", len(schedules)) url := &url.URL{} - job := func(schedule ScheduleOld) func() { + job := func(schedule shuffle.ScheduleOld) func() { return func() { - //log.Printf("[INFO] Running schedule %s with interval %d.", schedule.Id, schedule.Seconds) - //log.Printf("ARG: %s", schedule.WrappedArgument) + log.Printf("[INFO] Running schedule %s with interval %d.", schedule.Id, schedule.Seconds) request := &http.Request{ URL: url, @@ -4000,7 +3999,6 @@ func runInitEs(ctx context.Context) { } //log.Printf("Schedule: %#v", schedule) - //log.Printf("Schedule time: every %d seconds", schedule.Seconds) jobret, err := newscheduler.Every(schedule.Seconds).Seconds().NotImmediately().Run(job(schedule)) if err != nil { From c75975d7bd795589b893a80de494712424b02ee0 Mon Sep 17 00:00:00 2001 From: frikky Date: Mon, 2 Aug 2021 14:55:52 +0200 Subject: [PATCH 018/117] Fixed URL parsing issue in app builds --- backend/go-app/go.mod | 4 +-- backend/go-app/main.go | 4 +-- docker-compose.yml | 2 +- frontend/src/components/Header.js | 2 +- frontend/src/views/AngularWorkflow.jsx | 2 +- frontend/src/views/Workflows.jsx | 40 ++++++++++++++++---------- 6 files changed, 31 insertions(+), 23 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 60670e20..36c29f1f 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -2,7 +2,7 @@ module shuffle 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 @@ -22,7 +22,7 @@ require ( github.com/docker/go-units v0.4.0 // indirect github.com/elastic/go-elasticsearch/v7 v7.13.1 // indirect github.com/frikky/kin-openapi v0.39.0 - github.com/frikky/shuffle-shared v0.0.76 + github.com/frikky/shuffle-shared v0.0.78 github.com/fsouza/go-dockerclient v1.7.2 github.com/ghodss/yaml v1.0.0 github.com/go-git/go-billy/v5 v5.0.0 diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 9c0d1f68..0e6db708 100644 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -993,8 +993,6 @@ func handleInfo(resp http.ResponseWriter, request *http.Request) { Role: userInfo.ActiveOrg.Role, Image: org.Image, } - - userInfo.ActiveOrg.Users = []shuffle.UserMini{} } userInfo.ActiveOrg.Users = []shuffle.UserMini{} @@ -4078,7 +4076,7 @@ func runInitEs(ctx context.Context) { for _, org := range activeOrgs { if !org.CloudSync { - log.Printf("[WARNING] Skipping org %s because sync isn't set (1).", org.Id) + log.Printf("[WARNING] Skipping org syncCheck for %s because sync isn't set (1).", org.Id) continue } diff --git a/docker-compose.yml b/docker-compose.yml index 471d1e38..69ca24b7 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,7 +16,7 @@ services: depends_on: - backend backend: - build: ./backend + #build: ./backend image: ghcr.io/frikky/shuffle-backend:nightly container_name: shuffle-backend hostname: ${BACKEND_HOSTNAME} diff --git a/frontend/src/components/Header.js b/frontend/src/components/Header.js index e57c4068..994f16f3 100644 --- a/frontend/src/components/Header.js +++ b/frontend/src/components/Header.js @@ -84,10 +84,10 @@ const Header = props => { return response.json(); }).then(function(responseJson) { if (responseJson.success !== undefined && responseJson.success) { - alert.success("Successfully changed active organization - refreshing!") setTimeout(() => { window.location.reload() }, 2000) + alert.success("Successfully changed active organization - refreshing!") } else { alert.error("Failed changing org: ", responseJson.reason) } diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 41cfaae0..5bfcbd24 100644 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -3483,7 +3483,7 @@ const AngularWorkflow = (props) => { } const stopSchedule = (trigger, triggerindex) => { - alert.info("Stopping schedule") + //alert.info("Stopping schedule") fetch(globalUrl+"/api/v1/workflows/"+props.match.params.key+"/schedule/"+trigger.id, { method: 'DELETE', headers: { diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index c8898acf..ce409a2f 100644 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -1067,26 +1067,36 @@ const Workflows = (props) => { } var parsedName = data.name - if (parsedName !== undefined && parsedName !== null && parsedName.length > 25) { - parsedName = parsedName.slice(0,26)+".." + if (parsedName !== undefined && parsedName !== null && parsedName.length > 22) { + parsedName = parsedName.slice(0,23)+".." } const actions = data.actions !== null ? data.actions.length : 0 const [triggers, schedules, webhooks, subflows] = getWorkflowMeta(data) const imagesize = 22 - const foundOrg = userdata.orgs.find(org => org.id === data["org_id"]) - //position: "absolute", bottom: 5, right: -5, - const imageStyle = {width: imagesize, height: imagesize, pointerEvents: "none", marginRight: 10, marginLeft: data.creator_org !== undefined && data.creator_org.length > 0 ? 20 : 0, borderRadius: 10, border: foundOrg.id === userdata.active_org.id ? `4px solid ${boxColor}` : null, cursor: "pointer", marginRight: 10, } + var image = "" + var orgName = "" + var orgId = "" + if (userdata.orgs !== undefined) { + const foundOrg = userdata.orgs.find(org => org.id === data["org_id"]) + if (foundOrg !== undefined && foundOrg !== null) { + //position: "absolute", bottom: 5, right: -5, + const imageStyle = {width: imagesize, height: imagesize, pointerEvents: "none", marginRight: 10, marginLeft: data.creator_org !== undefined && data.creator_org.length > 0 ? 20 : 0, borderRadius: 10, border: foundOrg.id === userdata.active_org.id ? `3px solid ${boxColor}` : null, cursor: "pointer", marginRight: 10, } - // - const image = foundOrg.image === "" ? - {foundOrg.name} - : - {foundOrg.name} { - //setFilteredWorkflows(newWorkflows) - }}/> + // + image = foundOrg.image === "" ? + {foundOrg.name} + : + {foundOrg.name} { + //setFilteredWorkflows(newWorkflows) + }}/> + + orgName = foundOrg.name + orgId = foundOrg.id + } + } return ( @@ -1094,10 +1104,10 @@ const Workflows = (props) => {
- +
{ - addFilter(foundOrg.id) - //setFilters(["Org "+foundOrg.name]) + addFilter(orgId) + //setFilters(["Org "+orgName]) //setFilteredWorkflows(newWorkflows) }}> {image} From a6a18acd3c1491c223d3133828c7444f0be7e6f9 Mon Sep 17 00:00:00 2001 From: frikky Date: Mon, 2 Aug 2021 21:40:40 +0200 Subject: [PATCH 019/117] Removed app sdk debug prints --- backend/app_sdk/app_base.py | 25 +++++++-------- backend/app_sdk/build.sh | 2 +- backend/go-app/go.mod | 2 +- frontend/src/components/Header.js | 2 +- frontend/src/views/AngularWorkflow.jsx | 42 +++++++++++++++++--------- frontend/src/views/Workflows.jsx | 2 +- 6 files changed, 44 insertions(+), 31 deletions(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index cf6a462e..789c4b72 100644 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -279,7 +279,7 @@ class AppBase: #self.action = action loopnames = [] - print(f"Baseparams to check!!: {baseparams}") + #print(f"Baseparams to check!!: {baseparams}") for key, value in baseparams.items(): check_value = "" for param in self.action["parameters"]: @@ -327,11 +327,11 @@ class AppBase: should_merge = True # Specific for OpenAPI body replacement - print("\n\n\nDOING STUFF BELOW HERE") + #print("\n\n\nDOING STUFF BELOW HERE") if not should_merge: for parameter in self.action["parameters"]: if parameter["name"] == key: - print("CHECKING BODY FOR VALUE REPLACE DATA!") + #print("CHECKING BODY FOR VALUE REPLACE DATA!") try: values = parameter["value_replace"] if values != None: @@ -343,7 +343,7 @@ class AppBase: except: pass - print(f"MERGE: {should_merge}") + #print(f"MERGE: {should_merge}") if isinstance(value, list): print("Item {value} is a list.") if len(value) <= 1: @@ -368,7 +368,8 @@ class AppBase: all_list_keys.append(key) all_lists.append(baseparams[key]) else: - print(f"{value} is not a list") + #print(f"{value} is not a list") + pass print("Listlengths: %s" % listlengths) if len(listlengths) == 0: @@ -433,7 +434,7 @@ class AppBase: # Runs recursed versions with inner loops and such async def run_recursed_items(self, func, baseparams, loop_wrapper): - print(f"RECURSED ITEMS: {baseparams}") + #print(f"RECURSED ITEMS: {baseparams}") has_loop = False newparams = {} @@ -465,7 +466,7 @@ class AppBase: newparams[key] = value[0] has_loop = True else: - print(f"Key {key} is NOT a list within a list. Value: {value}") + #print(f"Key {key} is NOT a list within a list. Value: {value}") newparams[key] = value results = [] @@ -473,7 +474,7 @@ class AppBase: print("[WARNING] Should run inner loop: %s" % newparams) ret = await self.run_recursed_items(func, newparams, loop_wrapper) else: - print("[INFO] Should run multiplier check with params (inner): %s" % newparams) + #print("[INFO] Should run multiplier check with params (inner): %s" % newparams) # 1. Find the loops that are required and create new multipliers # If here: check for multipliers within this scope. ret = [] @@ -481,7 +482,7 @@ class AppBase: # FIXME: This does a deduplication of the data new_params = self.validate_unique_fields(param_multiplier) - print(f"NEW PARAMS: {new_params}") + #print(f"NEW PARAMS: {new_params}") if len(new_params) == 0: print("[WARNING] SHOULD STOP MULTI-EXECUTION BECAUSE FIELDS AREN'T UNIQUE") action_result = { @@ -499,7 +500,7 @@ class AppBase: #return else: #subparams = new_params - print(f"NEW PARAMS: {new_params}") + #print(f"NEW PARAMS: {new_params}") param_multiplier = new_params #print("Returned with newparams of length %d", len(new_params)) @@ -516,7 +517,7 @@ class AppBase: print("[INFO] Multiplier length: %d" % len(param_multiplier)) #tmp = "" for subparams in param_multiplier: - print(f"SUBPARAMS IN MULTI: {subparams}") + #print(f"SUBPARAMS IN MULTI: {subparams}") try: #tmp = await func(**subparams) @@ -549,7 +550,7 @@ class AppBase: tmp = "An error occured during execution: %s" % e - print("RET from execution: %s" % ret) + #print("RET from execution: %s" % ret) new_value = tmp if tmp == None: new_value = "" diff --git a/backend/app_sdk/build.sh b/backend/app_sdk/build.sh index 028328e1..83e9355a 100644 --- a/backend/app_sdk/build.sh +++ b/backend/app_sdk/build.sh @@ -3,7 +3,7 @@ ### DEFAULT NAME=shuffle-app_sdk -VERSION=0.9.02 +VERSION=0.9.05 docker rmi docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION --force docker build . -f Dockerfile -t frikky/shuffle:app_sdk -t frikky/$NAME:$VERSION -t docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION -t ghcr.io/frikky/$NAME:$VERSION diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 36c29f1f..d98a85b3 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -21,7 +21,7 @@ require ( github.com/docker/go-connections v0.4.0 github.com/docker/go-units v0.4.0 // indirect github.com/elastic/go-elasticsearch/v7 v7.13.1 // indirect - github.com/frikky/kin-openapi v0.39.0 + github.com/frikky/kin-openapi v0.40.0 github.com/frikky/shuffle-shared v0.0.78 github.com/fsouza/go-dockerclient v1.7.2 github.com/ghodss/yaml v1.0.0 diff --git a/frontend/src/components/Header.js b/frontend/src/components/Header.js index 994f16f3..f62ec077 100644 --- a/frontend/src/components/Header.js +++ b/frontend/src/components/Header.js @@ -283,7 +283,7 @@ const Header = props => { }} value={userdata.active_org.id} fullWidth - style={{backgroundColor: theme.palette.surfaceColor, marginRight: 15, color: "white", height: 50, width: 200}} + style={{marginTop: 5, backgroundColor: theme.palette.surfaceColor, marginRight: 15, color: "white", height: 50, width: 200}} onChange={(e) => { handleClickChangeOrg(e.target.value) }} diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 5bfcbd24..e33c581b 100644 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -1914,11 +1914,13 @@ const AngularWorkflow = (props) => { //console.log("BRANCHES: ", branch) if (data.type === "ACTION") { + //setSelectedApp({}) + //setSelectedAction({}) var curaction = workflow.actions.find(a => a.id === data.id) //console.log("INSIDE CURACTION: ", curaction) if (!curaction || curaction === undefined) { //event.target.unselect() - //alert.error("Action not found. Please remake it.") + alert.error("Action not found. Please remake it.") return } @@ -1989,6 +1991,16 @@ const AngularWorkflow = (props) => { //setSelectedAction(JSON.parse(JSON.stringify(curaction))) //console.log("CURAPP: ", curapp, selectedApp) + //console.log("ACTION: ", curaction) + if (curaction.parameters !== undefined && curaction.parameters !== null && curaction.parameters.length > 0) { + //console.log("params: ", curaction.parameters) + for (var key in curaction.parameters) { + if (curaction.parameters[key].options !== undefined && curaction.parameters[key].options !== null && curaction.parameters[key].options.length > 0 && curaction.parameters[key].value == "") { + curaction.parameters[key].value = curaction.parameters[key].options[0] + } + } + } + setSelectedApp(curapp) setSelectedAction(curaction) @@ -2759,22 +2771,22 @@ const AngularWorkflow = (props) => { case 67: if (previouskey === 17) { console.log("CTRL+C") - const filteredelements = cy.filter(function(element, i){ - return element.hasClass('selected') - }) + //const filteredelements = cy.filter(function(element, i){ + // return element.hasClass('selected') + //}) - for (var key in filteredelements) { - } + //for (var key in filteredelements) { + //} - var copyText = document.getElementById("copy_element_shuffle") - if (copyText !== undefined && copyText !== null) { - const clipboard = navigator.clipboard - if (clipboard === undefined) { - alert.error("Can only copy over HTTPS (port 3443)") - return - } - } - console.log("FILTERED: ", filteredelements) + //var copyText = document.getElementById("copy_element_shuffle") + //if (copyText !== undefined && copyText !== null) { + // const clipboard = navigator.clipboard + // if (clipboard === undefined) { + // alert.error("Can only copy over HTTPS (port 3443)") + // return + // } + //} + //console.log("FILTERED: ", filteredelements) } break; case 86: diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index ce409a2f..872f3f71 100644 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -91,7 +91,7 @@ export const GetIconInfo = (action) => { {"key": "merge", "values": ["join", "merge"]}, {"key": "search", "values": ["search", "find", "locate", "index",]}, {"key": "list", "values": ["list", "head", "options"]}, - {"key": "download", "values": ["get", "download", "return", "hello_world", "curl",]}, + {"key": "download", "values": ["capture", "get", "download", "return", "hello_world", "curl",]}, {"key": "add", "values": ["add"]}, {"key": "delete", "values": ["delete", "remove", "clear", "clean",]}, {"key": "send", "values": ["send", "dispatch", "mail", "forward", "post", "submit", "mark", "set"]}, From e8b7b2b4447d0b7ef4f7fa9bb2e5730e5b781ceb Mon Sep 17 00:00:00 2001 From: frikky Date: Tue, 3 Aug 2021 00:33:48 +0200 Subject: [PATCH 020/117] #416: Added Liquid formattinggit status --- backend/app_sdk/Dockerfile | 6 ++-- backend/app_sdk/app_base.py | 39 +++++++++++++++++++++++- backend/app_sdk/build.sh | 2 +- backend/app_sdk/requirements.txt | 1 + frontend/src/components/ParsedAction.jsx | 5 +-- 5 files changed, 46 insertions(+), 7 deletions(-) diff --git a/backend/app_sdk/Dockerfile b/backend/app_sdk/Dockerfile index 75ec12c4..d4646ff4 100644 --- a/backend/app_sdk/Dockerfile +++ b/backend/app_sdk/Dockerfile @@ -6,11 +6,11 @@ RUN apk --no-cache add --update alpine-sdk libffi libffi-dev musl-dev openssl-de RUN mkdir /install WORKDIR /install -COPY requirements.txt /requirements.txt -RUN pip3 install -r /requirements.txt - FROM base COPY --from=builder /install /usr/local +COPY requirements.txt /requirements.txt +RUN pip3 install -r /requirements.txt + COPY __init__.py /app/walkoff_app_sdk/__init__.py COPY app_base.py /app/walkoff_app_sdk/app_base.py diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index 789c4b72..2c21d584 100644 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -10,6 +10,8 @@ import urllib.parse import http.client import urllib3 import hashlib +from liquid import Liquid +import liquid class AppBase: __version__ = None @@ -1509,6 +1511,31 @@ class AppBase: print("Error in decoder: %s" % e) return returndata, is_loop + def parse_liquid(template): + #print("Inside liquid with glob: %s" % globals()) + try: + #print(globals()) + print("Running with \"%s\"" % template) + run = Liquid(template, {'mode': 'python'}) + ret = run.render(**globals()) + return ret + #try: + #run = Liquid(template) + #return ret + #except liquid.exceptions.LiquidSyntaxError as e: + # run = Liquid(template, {'mode': 'python'}) + # ret = run.render(**globals()) + # return ret + #except liquid.exceptions.LiquidRenderError as e: + # print("Render error: %s" % e) + + except liquid.exceptions.LiquidRenderError as e: + print("Render error: %s" % e) + except liquid.exceptions.LiquidSyntaxError as e: + print("Syntax error: %s" % e) + + return template + # Parses parameters sent to it and returns whether it did it successfully with the values found def parse_params(action, fullexecution, parameter): # Skip if it starts with $? @@ -1518,7 +1545,8 @@ class AppBase: # Matches with space in the first part, but not in subsequent parts. # JSON / yaml etc shouldn't have spaces in their fields anyway. #match = ".*?([$]{1}([a-zA-Z0-9 _-]+\.?){1}([a-zA-Z0-9#_-]+\.?){0,})[$/, ]?" - match = ".*?([$]{1}([a-zA-Z0-9 _-]+\.?){1}([a-zA-Z0-9#_-]+\.?){0,})" + #match = ".*?([$]{1}([a-zA-Z0-9 _-]+\.?){1}([a-zA-Z0-9#_-]+\.?){0,})" + match = ".*?([$]{1}([a-zA-Z0-9_-]+\.?){1}([a-zA-Z0-9#_-]+\.?){0,})" # Removed space - no longer ok # Regex to find all the things if parameter["variant"] == "STATIC_VALUE": @@ -1640,6 +1668,15 @@ class AppBase: except json.decoder.JSONDecodeError as e: parameter["value"] = parameter["value"].replace(to_be_replaced, value) + # Just here in case it breaks + # Implemented 02.08.2021 + print("Pre liquid: %s" % parameter["value"]) + try: + parameter["value"] = parse_liquid(parameter["value"]) + except: + pass + + print("POST liquid: %s" % parameter["value"]) return "", parameter["value"], is_loop def run_validation(sourcevalue, check, destinationvalue): diff --git a/backend/app_sdk/build.sh b/backend/app_sdk/build.sh index 83e9355a..5abb5960 100644 --- a/backend/app_sdk/build.sh +++ b/backend/app_sdk/build.sh @@ -3,7 +3,7 @@ ### DEFAULT NAME=shuffle-app_sdk -VERSION=0.9.05 +VERSION=0.9.06 docker rmi docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION --force docker build . -f Dockerfile -t frikky/shuffle:app_sdk -t frikky/$NAME:$VERSION -t docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION -t ghcr.io/frikky/$NAME:$VERSION diff --git a/backend/app_sdk/requirements.txt b/backend/app_sdk/requirements.txt index 60b59271..60502c8d 100644 --- a/backend/app_sdk/requirements.txt +++ b/backend/app_sdk/requirements.txt @@ -1,2 +1,3 @@ urllib3==1.25.9 requests==2.25.1 +liquidpy==0.6.3 diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index 7dbdaad6..f20c3fdf 100644 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -8,7 +8,7 @@ import { useTheme } from '@material-ui/core/styles'; import NestedMenuItem from "material-ui-nested-menu-item"; //import NestedMenuItem from "./NestedMenu.jsx"; -import {Popper, TextField, Drawer, Button, Paper, Grid, Tabs, InputAdornment, Tab, ButtonBase, Tooltip, Select, MenuItem, Divider, Dialog, Modal, DialogActions, DialogTitle, InputLabel, DialogContent, FormControl, IconButton, Menu, Input, FormGroup, FormControlLabel, Typography, Checkbox, Breadcrumbs, CircularProgress, Switch, Fade} from '@material-ui/core'; +import {Popper, TextField, TextareaAutosize, Drawer, Button, Paper, Grid, Tabs, InputAdornment, Tab, ButtonBase, Tooltip, Select, MenuItem, Divider, Dialog, Modal, DialogActions, DialogTitle, InputLabel, DialogContent, FormControl, IconButton, Menu, Input, FormGroup, FormControlLabel, Typography, Checkbox, Breadcrumbs, CircularProgress, Switch, Fade} from '@material-ui/core'; import {GetApp as GetAppIcon, Search as SearchIcon, ArrowUpward as ArrowUpwardIcon, Visibility as VisibilityIcon, Done as DoneIcon, Close as CloseIcon, Error as ErrorIcon, FindReplace as FindreplaceIcon, ArrowLeft as ArrowLeftIcon, Cached as CachedIcon, DirectionsRun as DirectionsRunIcon, Add as AddIcon, Polymer as PolymerIcon, FormatListNumbered as FormatListNumberedIcon, Create as CreateIcon, PlayArrow as PlayArrowIcon, AspectRatio as AspectRatioIcon, MoreVert as MoreVertIcon, Apps as AppsIcon, Schedule as ScheduleIcon, FavoriteBorder as FavoriteBorderIcon, Pause as PauseIcon, Delete as DeleteIcon, AddCircleOutline as AddCircleOutlineIcon, Save as SaveIcon, KeyboardArrowLeft as KeyboardArrowLeftIcon, KeyboardArrowRight as KeyboardArrowRightIcon, ArrowBack as ArrowBackIcon, Settings as SettingsIcon, LockOpen as LockOpenIcon, ExpandMore as ExpandMoreIcon, VpnKey as VpnKeyIcon} from '@material-ui/icons'; import Autocomplete from '@material-ui/lab/Autocomplete'; @@ -685,9 +685,10 @@ const ParsedAction = (props) => { const clickedFieldId = "rightside_field_"+count var datafield = + // Date: Tue, 10 Aug 2021 11:24:59 +0200 Subject: [PATCH 021/117] Update docker-compose.yml change made on the docker-compose.yml to use the official release of opensearch:1.0.0 . Parameter opendistro_security.disabled=true changed to plugins.security.disabled=true as this is the proper way to call it. Some testing need to be done on other stack to make sur this change is safe. --- docker-compose.yml | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 69ca24b7..e81a8d73 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -25,18 +25,18 @@ services: - "${BACKEND_PORT}:5001" networks: - shuffle - volumes: - - /var/run/docker.sock:/var/run/docker.sock - - ${SHUFFLE_APP_HOTLOAD_LOCATION}:/shuffle-apps + volumes: + - /var/run/docker.sock:/var/run/docker.sock + - ${SHUFFLE_APP_HOTLOAD_LOCATION}:/shuffle-apps - ${SHUFFLE_FILE_LOCATION}:/shuffle-files #- ${SHUFFLE_OPENSEARCH_CERTIFICATE_FILE}:/shuffle-files/es_certificate - env_file: .env + env_file: .env environment: - SHUFFLE_APP_HOTLOAD_FOLDER=/shuffle-apps - SHUFFLE_FILE_LOCATION=/shuffle-files restart: unless-stopped #depends_on: - #- opensearch + #- opensearch #- database orborus: #build: ./functions/onprem/orborus @@ -66,13 +66,13 @@ services: - CLEANUP=${SHUFFLE_CONTAINER_AUTO_CLEANUP} restart: unless-stopped opensearch: - image: opensearchproject/opensearch:1.0.0-beta1 + image: opensearchproject/opensearch:1.0.0 hostname: shuffle-opensearch container_name: shuffle-opensearch environment: - - bootstrap.memory_lock=true + - bootstrap.memory_lock=true - "OPENSEARCH_JAVA_OPTS=-Xms1024m -Xmx1024m" # minimum and maximum Java heap size, recommend setting both to 50% of system RAM - - opendistro_security.disabled=true + - plugins.security.disabled=true - cluster.routing.allocation.disk.threshold_enabled=false - cluster.name=shuffle-cluster - node.name=shuffle-opensearch From 7f65c98f9435e05cd7582c8b51c471a6c7e13743 Mon Sep 17 00:00:00 2001 From: frikky Date: Wed, 11 Aug 2021 13:42:44 +0200 Subject: [PATCH 022/117] Fixed issues in app sdk for liquid --- backend/app_sdk/app_base.py | 21 ++++++++++++++++++--- backend/go-app/go.mod | 2 +- docker-compose.yml | 2 +- frontend/src/views/Workflows.jsx | 4 ++-- 4 files changed, 22 insertions(+), 7 deletions(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index 2c21d584..e0a5290a 100644 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -1512,10 +1512,22 @@ class AppBase: return returndata, is_loop def parse_liquid(template): + #print("Inside liquid with glob: %s" % globals()) try: + if len(template) > 250000: + print("Skipping liquid - size is %d" % len(template)) + return template + + if not "{{" in template or not "}}" in template: + print("Skipping liquid - missing {{ }} ") + return template + + #if not "{{" in template or not "}}" in template: + # return template + #print(globals()) - print("Running with \"%s\"" % template) + print("Running liquid with data of length %d" % len(template)) run = Liquid(template, {'mode': 'python'}) ret = run.render(**globals()) return ret @@ -1533,6 +1545,9 @@ class AppBase: print("Render error: %s" % e) except liquid.exceptions.LiquidSyntaxError as e: print("Syntax error: %s" % e) + except: + print("General exception for liquid") + pass return template @@ -1670,13 +1685,13 @@ class AppBase: # Just here in case it breaks # Implemented 02.08.2021 - print("Pre liquid: %s" % parameter["value"]) + #print("Pre liquid: %s" % parameter["value"]) try: parameter["value"] = parse_liquid(parameter["value"]) except: pass - print("POST liquid: %s" % parameter["value"]) + #print("POST liquid: %s" % parameter["value"]) return "", parameter["value"], is_loop def run_validation(sourcevalue, check, destinationvalue): diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index d98a85b3..e5b4c2e3 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -22,7 +22,7 @@ require ( github.com/docker/go-units v0.4.0 // indirect github.com/elastic/go-elasticsearch/v7 v7.13.1 // indirect github.com/frikky/kin-openapi v0.40.0 - github.com/frikky/shuffle-shared v0.0.78 + github.com/frikky/shuffle-shared v0.0.81 github.com/fsouza/go-dockerclient v1.7.2 github.com/ghodss/yaml v1.0.0 github.com/go-git/go-billy/v5 v5.0.0 diff --git a/docker-compose.yml b/docker-compose.yml index 69ca24b7..471d1e38 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,7 +16,7 @@ services: depends_on: - backend backend: - #build: ./backend + build: ./backend image: ghcr.io/frikky/shuffle-backend:nightly container_name: shuffle-backend hostname: ${BACKEND_HOSTNAME} diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index 872f3f71..face1f82 100644 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -1067,8 +1067,8 @@ const Workflows = (props) => { } var parsedName = data.name - if (parsedName !== undefined && parsedName !== null && parsedName.length > 22) { - parsedName = parsedName.slice(0,23)+".." + if (parsedName !== undefined && parsedName !== null && parsedName.length > 20) { + parsedName = parsedName.slice(0,21)+".." } const actions = data.actions !== null ? data.actions.length : 0 From 215078f31497789a810a7b714160f75e212dd897 Mon Sep 17 00:00:00 2001 From: frikky Date: Wed, 11 Aug 2021 13:44:58 +0200 Subject: [PATCH 023/117] Minor fix to compose file with 1.0.0 --- docker-compose.yml | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 4daa7718..513fd4a9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -83,7 +83,7 @@ services: soft: -1 hard: -1 nofile: - soft: 65536 # maximum number of open files for the OpenSearch user, set to at least 65536 on modern systems + soft: 65536 hard: 65536 volumes: - ${DB_LOCATION}:/usr/share/opensearch/data:rw @@ -92,21 +92,6 @@ services: networks: - shuffle restart: unless-stopped - #database: - # #build: ./backend/database - # image: frikky/shuffle:database - # container_name: shuffle-database - # hostname: shuffle-database - # ports: - # - "8000:8000" - # networks: - # - shuffle - # environment: - # - _JAVA_OPTIONS="-Xmx2g" - # restart: unless-stopped - # volumes: - # - ${DB_LOCATION}:/etc/shuffle - networks: shuffle: driver: bridge From 50b724f47ce92e938c28215091674b13817687d0 Mon Sep 17 00:00:00 2001 From: frikky Date: Wed, 11 Aug 2021 14:16:59 +0200 Subject: [PATCH 024/117] Fixed backend connection to new db --- backend/go-app/go.mod | 2 +- docker-compose.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index e5b4c2e3..f1f8c908 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -22,7 +22,7 @@ require ( github.com/docker/go-units v0.4.0 // indirect github.com/elastic/go-elasticsearch/v7 v7.13.1 // indirect github.com/frikky/kin-openapi v0.40.0 - github.com/frikky/shuffle-shared v0.0.81 + github.com/frikky/shuffle-shared v0.0.82 github.com/fsouza/go-dockerclient v1.7.2 github.com/ghodss/yaml v1.0.0 github.com/go-git/go-billy/v5 v5.0.0 diff --git a/docker-compose.yml b/docker-compose.yml index 513fd4a9..eec29471 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -16,7 +16,7 @@ services: depends_on: - backend backend: - build: ./backend + #build: ./backend image: ghcr.io/frikky/shuffle-backend:nightly container_name: shuffle-backend hostname: ${BACKEND_HOSTNAME} From 56185e53e2bba279e7c1a68d7c0e0997178f6db6 Mon Sep 17 00:00:00 2001 From: frikky Date: Fri, 13 Aug 2021 13:14:51 +0200 Subject: [PATCH 025/117] #470: Added better error messaging for later debugs --- backend/go-app/walkoff.go | 4 ++-- frontend/src/views/AngularWorkflow.jsx | 30 ++++++++++++++++++++++++++ frontend/src/views/AppCreator.jsx | 29 +++++++++++++++++++++++++ frontend/src/views/Docs.jsx | 4 ++-- 4 files changed, 63 insertions(+), 4 deletions(-) diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index 34f6d5bb..88c4b09b 100644 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -1950,8 +1950,8 @@ func handleExecution(id string, workflow shuffle.Workflow, request *http.Request //Org []Org `json:"org,omitempty" datastore:"org"` err = shuffle.SetWorkflowExecution(ctx, workflowExecution, true) if err != nil { - log.Printf("Error saving workflow execution for updates %s: %s", topic, err) - return shuffle.WorkflowExecution{}, "Failed getting workflowexecution", err + log.Printf("[WARNING] Error saving workflow execution for updates %s: %s", topic, err) + return shuffle.WorkflowExecution{}, fmt.Sprintf("Failed setting workflowexecution: %s", err), err } // Adds queue for onprem execution diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index e33c581b..c154d7e6 100644 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -8138,6 +8138,7 @@ const AngularWorkflow = (props) => { // //cf80fa70-65cf-4963-b474-b459a6dead81 //} + console.log(selectedResult) const codePopoutModal = !codeModalOpen ? null : { @@ -8295,6 +8296,35 @@ const AngularWorkflow = (props) => { }}>{selectedResult.result}
} +
+ {selectedResult.action.parameters !== null && selectedResult.action.parameters !== undefined ? +
+ + + Variables + + {selectedResult.action.parameters.map((data, index) => { + if (data.value.length === 0) { + return null + } + + if (data.example !== undefined && data.example !== null && data.example.includes("***")) { + return null + } + + return ( +
+ + {data.name}: {data.value} + +
+ ) + })} +
+ : + null + } +
diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx index ad752412..69c787ce 100644 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -1097,9 +1097,16 @@ const AppCreator = (props) => { } if (item.queries.length > 0) { + var skipped = false for (var querykey in item.queries) { const queryitem = item.queries[querykey] + if (queryitem.name.toLowerCase() == "url") { + console.log(item.name+" uses a bad query: url") + skipped = true + break + } + var newitem = { "in": "query", "name": queryitem.name, @@ -1117,12 +1124,27 @@ const AppCreator = (props) => { data.paths[item.url][item.method.toLowerCase()].parameters.push(newitem) //console.log(queryitem) } + + // Bad code as it doesn't allow for "anything". + if (skipped) { + alert.info("Bad configuration of "+item.name+". Skipping because queries are invalid.") + continue + } } //data.paths[item.url][item.method.toLowerCase()].parameters.push(newitem) if (item.paths.length > 0) { for (querykey in item.paths) { const queryitem = item.paths[querykey] + + if (queryitem.toLowerCase() == "url") { + queryitem = "action_url" + } + + if (queryitem.toLowerCase() == "apikey") { + queryitem = "action_apikey" + } + newitem = { "in": "path", "name": queryitem, @@ -1315,6 +1337,13 @@ const AppCreator = (props) => { if (setExtraAuth.length > 0) { for (var key in extraAuth) { const curauth = extraAuth[key] + + if (curauth.name.toLowerCase() == "url") { + alert.error("Can't add extra auth with Name URL") + setAppBuilding(false) + return + } + data.components.securitySchemes[curauth.name] = { "type": "apiKey", "in": curauth.type, diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index 58c2983f..c79e8f4f 100644 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -234,7 +234,7 @@ const Docs = (props) => { // ); //} - const postDataBrowser = + const postDataBrowser = list === undefined || list === null ? null :
@@ -278,7 +278,7 @@ const Docs = (props) => { flexDirection: "column", } - const postDataMobile = + const postDataMobile = list === undefined || list === null ? null :
diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index f20c3fdf..2ee180fb 100644 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -46,7 +46,7 @@ const useStyles = makeStyles({ //) const ParsedAction = (props) => { - const {workflow, setWorkflow, setAction, setSelectedAction, setUpdate, appActionArguments, selectedApp, workflowExecutions, setSelectedResult, selectedAction, setSelectedApp, setSelectedTrigger, setSelectedEdge, setCurrentView, cy, setAuthenticationModalOpen,setVariablesModalOpen, setCodeModalOpen, selectedNameChange, rightsidebarStyle, showEnvironment, selectedActionEnvironment, environments, setNewSelectedAction, appApiViewStyle, globalUrl, setSelectedActionEnvironment, requiresAuthentication, hideExtraTypes, scrollConfig, setScrollConfig } = props + const {workflow, setWorkflow, setAction, setSelectedAction, setUpdate, appActionArguments, selectedApp, workflowExecutions, setSelectedResult, selectedAction, setSelectedApp, setSelectedTrigger, setSelectedEdge, setCurrentView, cy, setAuthenticationModalOpen,setVariablesModalOpen, setCodeModalOpen, selectedNameChange, rightsidebarStyle, showEnvironment, selectedActionEnvironment, environments, setNewSelectedAction, appApiViewStyle, globalUrl, setSelectedActionEnvironment, requiresAuthentication, hideExtraTypes, scrollConfig, setScrollConfig, authenticationType, appAuthentication, getAppAuthentication } = props const theme = useTheme(); const classes = useStyles() @@ -1389,6 +1389,42 @@ const ParsedAction = (props) => { + +
+ ) +} + +export default AuthenticationOauth2 diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index e5b9bdd8..3d0013ea 100644 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -1116,6 +1116,14 @@ const ParsedAction = (props) => { // itemColor = "#ffeb3b" //} {/*
*/} + + + + if (authenticationType.type === "oauth2" && data.configuration === true) { + return null + } + //&& authenticationType.redirect_uri !== undefined && authenticationType.redirect_uri !== null) { + return (
@@ -1389,47 +1397,7 @@ const ParsedAction = (props) => {
{isCloud ? null : - - - + + + - } + }
{ - + {isCloud ? null : + + } + + {defaultConfigSet ? + + ... or + + + : null}
) diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index e63065df..a988ab6b 100644 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -1172,7 +1172,7 @@ const AngularWorkflow = (props) => { if (updateAction === true) { //console.log("Should update authentication for selectedAction!!") - console.log(responseJson) + //console.log(responseJson) if (selectedApp.authentication.required) { //console.log("App requires auth!!") @@ -1184,7 +1184,7 @@ const AngularWorkflow = (props) => { } var tmpAuth = JSON.parse(JSON.stringify(responseJson.data)) - console.log("FOUND AUTH: ", tmpAuth) + //console.log("FOUND AUTH: ", tmpAuth) //console.log("Checking authentication: ", tmpAuth) var latest = 0 @@ -1211,8 +1211,7 @@ const AngularWorkflow = (props) => { //if (item.id === findAuthId) { // selectedAction.selectedAuthentication = item //} - - console.log("ACTION: ", selectedAction) + //console.log("ACTION: ", selectedAction) //console.log("OPTIONS: ", authenticationOptions) selectedAction.authentication = authenticationOptions @@ -2007,14 +2006,16 @@ const AngularWorkflow = (props) => { setSelectedAction(curaction) //return } else { - //console.log("AUTHENTICATION: ", curapp.authentication) - //console.log(curapp.authentication) + console.log("AUTHENTICATION: ", curapp.authentication) + console.log(curapp.authentication) setAuthenticationType(curapp.authentication.type === "oauth2" && curapp.authentication.redirect_uri !== undefined && curapp.authentication.redirect_uri !== null ? { - "type": "oauth2", - "redirect_uri": curapp.authentication.redirect_uri, - "token_uri": curapp.authentication.token_uri, - "scope": curapp.authentication.scope, + "type": "oauth2", + "redirect_uri": curapp.authentication.redirect_uri, + "token_uri": curapp.authentication.token_uri, + "scope": curapp.authentication.scope, + "client_id": curapp.authentication.client_id, + "client_secret": curapp.authentication.client_secret, } : { "type": "" } From 781efd8f55f48cb15f648aad1b288cca3a38399e Mon Sep 17 00:00:00 2001 From: frikky Date: Tue, 24 Aug 2021 12:02:25 +0200 Subject: [PATCH 034/117] Fixed regex issue in conditions for app sdk --- backend/app_sdk/app_base.py | 11 +++++++++++ backend/app_sdk/build.sh | 2 +- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index 077795d0..a6871a4f 100644 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -1748,6 +1748,17 @@ class AppBase: except AttributeError as e: self.logger.error("[WARNING] Condition smaller than failed with values %s and %s: %s" % (sourcevalue, destinationvalue, e)) return False + elif check.lower() == "re" or check.lower() == "matches regex": + try: + found = re.search(destinationvalue, sourcevalue) + except re.error as e: + print("[WARNING] Regex error in condition: %s" % e) + return False + + if found == None: + return False + + return True else: self.logger.info("Condition: can't handle %s yet. Setting to true" % check) diff --git a/backend/app_sdk/build.sh b/backend/app_sdk/build.sh index 5abb5960..6130a6a6 100644 --- a/backend/app_sdk/build.sh +++ b/backend/app_sdk/build.sh @@ -3,7 +3,7 @@ ### DEFAULT NAME=shuffle-app_sdk -VERSION=0.9.06 +VERSION=0.9.09 docker rmi docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION --force docker build . -f Dockerfile -t frikky/shuffle:app_sdk -t frikky/$NAME:$VERSION -t docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION -t ghcr.io/frikky/$NAME:$VERSION From 66c1a4b114513f9a92a2f258daacc7125aff54be Mon Sep 17 00:00:00 2001 From: frikky Date: Tue, 24 Aug 2021 15:36:11 +0200 Subject: [PATCH 035/117] Added timezone locale to App SDK by default --- backend/app_sdk/Dockerfile | 3 ++- backend/app_sdk/build.sh | 2 +- backend/go-app/go.mod | 4 ++-- docker-compose.yml | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/backend/app_sdk/Dockerfile b/backend/app_sdk/Dockerfile index d4646ff4..84f4f950 100644 --- a/backend/app_sdk/Dockerfile +++ b/backend/app_sdk/Dockerfile @@ -1,13 +1,14 @@ FROM python:3.9.1-alpine as base FROM base as builder -RUN apk --no-cache add --update alpine-sdk libffi libffi-dev musl-dev openssl-dev +RUN apk --no-cache add --update alpine-sdk libffi libffi-dev musl-dev openssl-dev tzdata RUN mkdir /install WORKDIR /install FROM base +RUN apk update && apk add --no-cache tzdata COPY --from=builder /install /usr/local COPY requirements.txt /requirements.txt RUN pip3 install -r /requirements.txt diff --git a/backend/app_sdk/build.sh b/backend/app_sdk/build.sh index 6130a6a6..4dfe2531 100644 --- a/backend/app_sdk/build.sh +++ b/backend/app_sdk/build.sh @@ -3,7 +3,7 @@ ### DEFAULT NAME=shuffle-app_sdk -VERSION=0.9.09 +VERSION=0.9.10 docker rmi docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION --force docker build . -f Dockerfile -t frikky/shuffle:app_sdk -t frikky/$NAME:$VERSION -t docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION -t ghcr.io/frikky/$NAME:$VERSION diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index a0d72ede..fe726c85 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -2,7 +2,7 @@ module shuffle 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 @@ -22,7 +22,7 @@ require ( github.com/docker/go-units v0.4.0 // indirect github.com/elastic/go-elasticsearch/v7 v7.13.1 // indirect github.com/frikky/kin-openapi v0.40.0 - github.com/frikky/shuffle-shared v0.0.83 + github.com/frikky/shuffle-shared v0.0.84 github.com/fsouza/go-dockerclient v1.7.2 github.com/ghodss/yaml v1.0.0 github.com/go-git/go-billy/v5 v5.0.0 diff --git a/docker-compose.yml b/docker-compose.yml index 466f1546..f65be525 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '3' services: frontend: - #build: ./frontend + build: ./frontend image: ghcr.io/frikky/shuffle-frontend:nightly container_name: shuffle-frontend hostname: shuffle-frontend From cbdf225dfb667cbd2932715d01bdfbe8ce83cf23 Mon Sep 17 00:00:00 2001 From: frikky Date: Wed, 25 Aug 2021 11:14:55 +0200 Subject: [PATCH 036/117] #477: Added timezone control to all sub-containers (orborus, worker & sdk) --- .env | 1 + functions/onprem/orborus/Dockerfile | 2 +- functions/onprem/orborus/build.sh | 2 +- functions/onprem/orborus/orborus.go | 6 ++++++ functions/onprem/worker/Dockerfile | 2 +- functions/onprem/worker/build.sh | 2 +- functions/onprem/worker/worker.go | 6 ++++++ 7 files changed, 17 insertions(+), 4 deletions(-) diff --git a/.env b/.env index e10cbb18..76f6f2f7 100644 --- a/.env +++ b/.env @@ -41,6 +41,7 @@ HTTP_PROXY= HTTPS_PROXY= SHUFFLE_PASS_WORKER_PROXY=TRUE SHUFFLE_PASS_APP_PROXY=FALSE +TZ=Europe/Amsterdam #Timezone-handler SHUFFLE_BASE_IMAGE_REGISTRY=ghcr.io SHUFFLE_BASE_IMAGE_NAME=frikky diff --git a/functions/onprem/orborus/Dockerfile b/functions/onprem/orborus/Dockerfile index 6479c09e..3ae68760 100644 --- a/functions/onprem/orborus/Dockerfile +++ b/functions/onprem/orborus/Dockerfile @@ -16,7 +16,7 @@ RUN go build RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o orborus . FROM alpine:3.12 -RUN apk add --no-cache bash +RUN apk add --no-cache bash tzdata COPY --from=builder /app/ / CMD ["./orborus"] diff --git a/functions/onprem/orborus/build.sh b/functions/onprem/orborus/build.sh index 8a35018c..eb1d12db 100644 --- a/functions/onprem/orborus/build.sh +++ b/functions/onprem/orborus/build.sh @@ -1,5 +1,5 @@ NAME=shuffle-orborus -VERSION=0.8.98 +VERSION=0.9.10 echo "Running docker build with $NAME:$VERSION" #docker rmi frikky/shuffle:$NAME --force diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index c01c27b1..98c8133f 100644 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -55,6 +55,7 @@ var environment = os.Getenv("ENVIRONMENT_NAME") var dockerApiVersion = os.Getenv("DOCKER_API_VERSION") var runningMode = strings.ToLower(os.Getenv("RUNNING_MODE")) var cleanupEnv = strings.ToLower(os.Getenv("CLEANUP")) +var timezone = os.Getenv("TZ") var executionIds = []string{} var dockercli *dockerclient.Client @@ -345,6 +346,10 @@ func main() { os.Exit(3) } + if timezone == "" { + timezone = "Europe/Amsterdam" + } + workerTimeout := 600 if workerTimeoutEnv != "" { tmpInt, err := strconv.Atoi(workerTimeoutEnv) @@ -559,6 +564,7 @@ func main() { fmt.Sprintf("ENVIRONMENT_NAME=%s", environment), fmt.Sprintf("BASE_URL=%s", baseUrl), fmt.Sprintf("CLEANUP=%s", cleanupEnv), + fmt.Sprintf("TZ=%s", timezone), fmt.Sprintf("SHUFFLE_PASS_APP_PROXY=%s", os.Getenv("SHUFFLE_PASS_APP_PROXY")), } diff --git a/functions/onprem/worker/Dockerfile b/functions/onprem/worker/Dockerfile index 3b8218a1..d49a1043 100644 --- a/functions/onprem/worker/Dockerfile +++ b/functions/onprem/worker/Dockerfile @@ -31,7 +31,7 @@ ENV SHUFFLE_BASE_IMAGE_REGISTRY=docker.io ENV SHUFFLE_BASE_IMAGE_NAME=frikky/shuffle ENV SHUFFLE_BASE_IMAGE_TAG_SUFFIX=0.8.70 -RUN apk add --no-cache bash +RUN apk add --no-cache bash tzdata COPY --from=builder /app/ / CMD ["./worker"] diff --git a/functions/onprem/worker/build.sh b/functions/onprem/worker/build.sh index ee09f954..19a1e43d 100644 --- a/functions/onprem/worker/build.sh +++ b/functions/onprem/worker/build.sh @@ -1,5 +1,5 @@ NAME=shuffle-worker -VERSION=0.8.101 +VERSION=0.9.10 echo "Running docker build with $NAME:$VERSION" #CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o worker.bin . diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index b1741f1f..f333b6a6 100644 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -39,6 +39,7 @@ var environment = os.Getenv("ENVIRONMENT_NAME") var baseUrl = os.Getenv("BASE_URL") var appCallbackUrl = os.Getenv("BASE_URL") var cleanupEnv = strings.ToLower(os.Getenv("CLEANUP")) +var timezone = os.Getenv("TZ") var baseimagename = "frikky/shuffle" var registryName = "registry.hub.docker.com" var fallbackName = "shuffle-orborus" @@ -957,6 +958,7 @@ func handleExecutionResult(workflowExecution shuffle.WorkflowExecution) { fmt.Sprintf("AUTHORIZATION=%s", workflowExecution.Authorization), fmt.Sprintf("CALLBACK_URL=%s", baseUrl), fmt.Sprintf("BASE_URL=%s", appCallbackUrl), + fmt.Sprintf("TZ=%s", timezone), } if strings.ToLower(os.Getenv("SHUFFLE_PASS_APP_PROXY")) == "true" { @@ -2041,6 +2043,10 @@ func main() { } } + if timezone == "" { + timezone = "Europe/Amsterdam" + } + //imageName := fmt.Sprintf("%s/%s:shuffle_openapi_1.0.0", registryName, baseimagename) //downloadDockerImageBackend(client, imageName) From c4c09074852e473a85838ed4e5a3dce5d4ea2efe Mon Sep 17 00:00:00 2001 From: frikky Date: Wed, 25 Aug 2021 12:08:14 +0200 Subject: [PATCH 037/117] More timezone fixes --- .env | 2 +- functions/onprem/orborus/orborus.go | 2 ++ functions/onprem/worker/worker.go | 2 ++ 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.env b/.env index 76f6f2f7..8610fe2b 100644 --- a/.env +++ b/.env @@ -41,7 +41,7 @@ HTTP_PROXY= HTTPS_PROXY= SHUFFLE_PASS_WORKER_PROXY=TRUE SHUFFLE_PASS_APP_PROXY=FALSE -TZ=Europe/Amsterdam #Timezone-handler +TZ=Europe/Amsterdam #Timezone-handler in Orborus, Worker and Apps SHUFFLE_BASE_IMAGE_REGISTRY=ghcr.io SHUFFLE_BASE_IMAGE_NAME=frikky diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 98c8133f..6ca42b24 100644 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -350,6 +350,8 @@ func main() { timezone = "Europe/Amsterdam" } + log.Printf("[INFO] Running with timezone %s", timezone) + workerTimeout := 600 if workerTimeoutEnv != "" { tmpInt, err := strconv.Atoi(workerTimeoutEnv) diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index f333b6a6..39b11a5c 100644 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -2047,6 +2047,8 @@ func main() { timezone = "Europe/Amsterdam" } + log.Printf("[INFO] Running with timezone %s", timezone) + //imageName := fmt.Sprintf("%s/%s:shuffle_openapi_1.0.0", registryName, baseimagename) //downloadDockerImageBackend(client, imageName) From 33ae5b587f831b2073aa7ced0e5be0d4d97e41bd Mon Sep 17 00:00:00 2001 From: frikky Date: Wed, 25 Aug 2021 12:24:24 +0200 Subject: [PATCH 038/117] Fixed install guide for windows --- .github/install-guide.md | 29 ++++++++++++++++++++--------- 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/.github/install-guide.md b/.github/install-guide.md index 697759ce..bfa29d94 100644 --- a/.github/install-guide.md +++ b/.github/install-guide.md @@ -45,14 +45,28 @@ This step is for setting up with Docker on windows from scratch. OUTER_HOSTNAME=YOUR.IP.HERE ``` -5. Configure max memory (WSL) by opening a new CMD/Powershell window. Required for Elasticsearch +5. Configure max memory (WSL) by opening a new CMD/Powershell window. Required for Opensearch (Elasticsearch). Run one command at a time, otherwise it may not work (WSL issues)! ``` wsl -d docker-desktop -sysctl -w vm.max_map_count=262144 -echo "vm.max_map_count = 262144" > /etc/sysctl.d/99-docker-desktop.conf -echo -e "\nvm.max_map_count = 262144\n" >> /etc/sysctl.d/00-alpine.conf +``` +``` +sysctl -w vm.max_map_count=262144 +``` + +``` +echo "vm.max_map_count = 262144" > /etc/sysctl.d/99-docker-desktop.conf +``` + +``` +echo -e "\nvm.max_map_count = 262144\n" >> /etc/sysctl.d/00-alpine.conf +``` + +``` # https://stackoverflow.com/questions/42111566/elasticsearch-in-windows-docker-image-vm-max-map-count + +exit +``` ``` 6. Run docker-compose @@ -106,11 +120,8 @@ go run *.go **WINDOWS USERS:** You'll have to to add the "export" part as an environment variable. -## Database - Datastore -Based on Google datastore -``` -docker run -p 8000:8000 google/cloud-sdk gcloud beta emulators datastore start --project=shuffle --host-port 0.0.0.0:8000 --no-store-on-disk -``` +## Database - Opensearch +This is a database based on Elasticsearch, hence we're using Elasticsearch API's for it. To get it running, we suggest you run the original docker-compose, as there's no good one-liner for getting opensearch working. ## Orborus Execution of Workflows: From 0b5c37d9724fca71934313f634c5f66dd4141d16 Mon Sep 17 00:00:00 2001 From: frikky Date: Wed, 25 Aug 2021 12:41:32 +0200 Subject: [PATCH 039/117] Fixed issue in compose --- .github/install-guide.md | 1 - docker-compose.yml | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/install-guide.md b/.github/install-guide.md index bfa29d94..301d180a 100644 --- a/.github/install-guide.md +++ b/.github/install-guide.md @@ -67,7 +67,6 @@ echo -e "\nvm.max_map_count = 262144\n" >> /etc/sysctl.d/00-alpine.conf exit ``` -``` 6. Run docker-compose ``` diff --git a/docker-compose.yml b/docker-compose.yml index f65be525..6485cde9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '3' services: frontend: - build: ./frontend + #build: ./frontend image: ghcr.io/frikky/shuffle-frontend:nightly container_name: shuffle-frontend hostname: shuffle-frontend @@ -16,7 +16,7 @@ services: depends_on: - backend backend: - build: ./backend + #build: ./backend image: ghcr.io/frikky/shuffle-backend:nightly container_name: shuffle-backend hostname: ${BACKEND_HOSTNAME} From faa555e26f3e622531dcf10892cabdddbc4915de Mon Sep 17 00:00:00 2001 From: frikky Date: Fri, 27 Aug 2021 03:17:15 +0200 Subject: [PATCH 040/117] #223: Fixed faster default responses from subflows --- backend/app_sdk/app_base.py | 23 +++++ backend/go-app/go.mod | 2 +- backend/go-app/main.go | 2 +- backend/go-app/walkoff.go | 123 +++++-------------------- frontend/src/views/AngularWorkflow.jsx | 1 + frontend/src/views/Workflows.jsx | 73 ++++++++++++--- functions/onprem/worker/build.sh | 2 +- functions/onprem/worker/go.mod | 4 +- functions/onprem/worker/worker.go | 119 +++++++++++++----------- 9 files changed, 178 insertions(+), 171 deletions(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index a6871a4f..101569fa 100644 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -1714,6 +1714,29 @@ class AppBase: elif check.lower() == "contains": if destinationvalue.lower() in sourcevalue.lower(): return True + + elif check.lower() == "is empty": + if len(sourcevalue) == 0: + return True + + if str(sourcevalue) == 0: + return True + + return False + #if tmp == "[]": + # tmp = [] + + #if type(tmp) == list and len(tmp) == 0 and not flip: + # new_list.append(item) + #elif type(tmp) == list and len(tmp) > 0 and flip: + # new_list.append(item) + #elif type(tmp) == str and not tmp and not flip: + # new_list.append(item) + #elif type(tmp) == str and tmp and flip: + # new_list.append(item) + #else: + # failed_list.append(item) + elif check.lower() == "contains_any_of": newvalue = [destinationvalue.lower()] if "," in destinationvalue: diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index fe726c85..f7fe10af 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -2,7 +2,7 @@ module shuffle 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 diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 24404c6c..43852d29 100644 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -5614,7 +5614,7 @@ func makeWorkflowPublic(resp http.ResponseWriter, request *http.Request) { if workflow.OrgId == user.ActiveOrg.Id && user.Role == "admin" { log.Printf("[AUDIT] User %s is accessing workflow %s as admin (public)", user.Username, workflow.ID) } else { - log.Printf("[WARNING] Wrong user (%s) for workflow %s (public)", user.Username, workflow.ID) + log.Printf("[AUDIT] Wrong user (%s) for workflow %s (public)", user.Username, workflow.ID) resp.WriteHeader(401) resp.Write([]byte(`{"success": false}`)) return diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index 205dea93..84562ff4 100644 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -628,7 +628,7 @@ func handleGetWorkflowqueueConfirm(resp http.ResponseWriter, request *http.Reque if err != nil { log.Printf("[ERROR] Failed deleting %d execution keys for org %s", len(ids), id) } else { - log.Printf("[INFO] Deleted %d keys from org %s", len(ids), parsedId) + //log.Printf("[INFO] Deleted %d keys from org %s", len(ids), parsedId) } //var newExecutionRequests ExecutionRequestWrapper @@ -687,7 +687,7 @@ func handleGetWorkflowqueue(resp http.ResponseWriter, request *http.Request) { if len(executionRequests.Data) == 0 { executionRequests.Data = []shuffle.ExecutionRequest{} } else { - log.Printf("[INFO] Executionrequests (%s): %d", id, len(executionRequests.Data)) + //log.Printf("[INFO] Executionrequests (%s): %d", id, len(executionRequests.Data)) //log.Printf("IDS: %#v", executionRequests.Data[0].ExecutionId) } @@ -755,80 +755,6 @@ func handleGetStreamResults(resp http.ResponseWriter, request *http.Request) { } -// Checks if data is sent from Worker >0.8.51, which sends a full execution -// instead of individial results -func validateNewWorkerExecution(body []byte) error { - ctx := context.Background() - var execution shuffle.WorkflowExecution - err := json.Unmarshal(body, &execution) - if err != nil { - log.Printf("[WARNING] Failed execution unmarshaling: %s", err) - return err - } - //log.Printf("\n\nGOT EXEC WITH RESULT %#v (%d)\n\n", execution.Status, len(execution.Results)) - - baseExecution, err := shuffle.GetWorkflowExecution(ctx, execution.ExecutionId) - if err != nil { - log.Printf("[ERROR] Failed getting execution (workflowqueue) %s: %s", execution.ExecutionId, err) - return err - } - - if baseExecution.Authorization != execution.Authorization { - return errors.New("Bad authorization when validating execution") - } - - // used to validate if it's actually the right marshal - if len(baseExecution.Workflow.Actions) != len(execution.Workflow.Actions) { - return errors.New(fmt.Sprintf("Bad length of actions (probably normal app): %d", len(execution.Workflow.Actions))) - } - - if len(baseExecution.Workflow.Triggers) != len(execution.Workflow.Triggers) { - return errors.New(fmt.Sprintf("Bad length of trigger: %d (probably normal app)", len(execution.Workflow.Triggers))) - } - - if len(baseExecution.Results) >= len(execution.Results) { - return errors.New(fmt.Sprintf("Can't have less actions in a full execution than what exists: %d (old) vs %d (new)", len(baseExecution.Results), len(execution.Results))) - } - - //if baseExecution.Status != "WAITING" && baseExecution.Status != "EXECUTING" { - // return errors.New(fmt.Sprintf("Workflow is already finished or failed. Can't update")) - //} - - if execution.Status == "EXECUTING" { - //log.Printf("[INFO] Inside executing.") - extra := 0 - for _, trigger := range execution.Workflow.Triggers { - //log.Printf("Appname trigger (0): %s", trigger.AppName) - if trigger.AppName == "User Input" || trigger.AppName == "Shuffle Workflow" { - extra += 1 - } - } - - if len(execution.Workflow.Actions)+extra == len(execution.Results) { - execution.Status = "FINISHED" - } - - //log.Printf("[INFO] BASEEXECUTION LENGTH: %d", len(baseExecution.Workflow.Actions)+extra) - } - - // FIXME: Add extra here - //executionLength := len(baseExecution.Workflow.Actions) - //if executionLength != len(execution.Results) { - // return errors.New(fmt.Sprintf("Bad length of actions vs results: want: %d have: %d", executionLength, len(execution.Results))) - //} - - //log.Printf("\n\nSHOULD SET BACKEND DATA FOR EXEC \n\n") - err = shuffle.SetWorkflowExecution(ctx, execution, true) - if err == nil { - log.Printf("[INFO] Set workflowexecution based on new worker (>0.8.53) for execution %s. Actions: %d, Triggers: %d, Results: %d, Status: %s", execution.ExecutionId, len(execution.Workflow.Actions), len(execution.Workflow.Triggers), len(execution.Results), execution.Status) //, execution.Result) - //log.Printf("[INFO] Successfully set the execution to wait.") - } else { - log.Printf("[WARNING] Failed to set the execution to wait.") - } - - return nil -} - func handleWorkflowQueue(resp http.ResponseWriter, request *http.Request) { cors := handleCors(resp, request) if cors { @@ -844,7 +770,7 @@ func handleWorkflowQueue(resp http.ResponseWriter, request *http.Request) { } //log.Printf("Actionresult unmarshal: %s", string(body)) - err = validateNewWorkerExecution(body) + err = shuffle.ValidateNewWorkerExecution(body) if err == nil { resp.WriteHeader(200) resp.Write([]byte(fmt.Sprintf(`{"success": true, "reason": "Success"}`))) @@ -980,31 +906,9 @@ func runWorkflowExecutionTransaction(ctx context.Context, attempts int64, workfl return } - //log.Printf("NEW LENGTH: %d", len(workflowExecution.Results)) - _ = dbSave //resultLength := len(workflowExecution.Results) setExecution := true - - //newExecution, err := shuffle.GetWorkflowExecution(ctx, workflowExecution.ExecutionId) - //if err == nil { - // //log.Printf("GOT GOOD EXECUTION CACHE FOR %s!", workflowExecution.ExecutionId) - // if len(newExecution.Results) > 0 && len(newExecution.Results) != resultLength { - // setExecution = false - // if attempts > 5 { - // //log.Printf("\n\nSkipping execution input - %d vs %d. Attempts: (%d)\n\n", len(parsedValue.Results), resultLength, attempts) - // } - - // attempts += 1 - // //if len(workflowExecution.Results) <= len(workflowExecution.Workflow.Actions) { - // // log.Printf("RUNNING AGAIN!!") - // // runWorkflowExecutionTransaction(ctx, attempts, workflowExecutionId, actionResult, resp) - // // return - // } - //} else { - // log.Printf("[WARNING] Failed getting cache for %s: %s", workflowExecution.ExecutionId, err) - //} - if setExecution || workflowExecution.Status == "FINISHED" || workflowExecution.Status == "ABORTED" || workflowExecution.Status == "FAILURE" { err = shuffle.SetWorkflowExecution(ctx, *workflowExecution, true) //err = shuffle.SetWorkflowExecution(ctx, *workflowExecution, dbSave) @@ -1347,6 +1251,22 @@ func handleExecution(id string, workflow shuffle.Workflow, request *http.Request } } + sourceAuth, sourceAuthOk := request.URL.Query()["source_auth"] + if sourceAuthOk { + //log.Printf("\n\n\nSETTING SOURCE WORKFLOW AUTH TO %s!!!\n\n\n", sourceAuth[0]) + workflowExecution.ExecutionSourceAuth = sourceAuth[0] + } else { + //log.Printf("Did NOT get source workflow") + } + + sourceNode, sourceNodeOk := request.URL.Query()["source_node"] + if sourceNodeOk { + //log.Printf("\n\n\nSETTING SOURCE WORKFLOW NODE TO %s!!!\n\n\n", sourceNode[0]) + workflowExecution.ExecutionSourceNode = sourceNode[0] + } else { + //log.Printf("Did NOT get source workflow") + } + //workflowExecution.ExecutionSource = "default" sourceWorkflow, sourceWorkflowOk := request.URL.Query()["source_workflow"] if sourceWorkflowOk { @@ -1354,7 +1274,6 @@ func handleExecution(id string, workflow shuffle.Workflow, request *http.Request workflowExecution.ExecutionSource = sourceWorkflow[0] } else { //log.Printf("Did NOT get source workflow") - } sourceExecution, sourceExecutionOk := request.URL.Query()["source_execution"] @@ -1371,7 +1290,7 @@ func handleExecution(id string, workflow shuffle.Workflow, request *http.Request //log.Println(body) //if string(body)[0] == "\"" && string(body)[string(body) - log.Printf("[INFO] Body: %s", string(body)) + log.Printf("[DEBUG] Body: %s", string(body)) } var execution shuffle.ExecutionRequest @@ -2230,7 +2149,7 @@ func stopSchedule(resp http.ResponseWriter, request *http.Request) { if user.Id != workflow.Owner || len(user.Id) == 0 { if workflow.OrgId == user.ActiveOrg.Id && user.Role == "admin" { - log.Printf("[DEBUG] User %s is accessing workflow %s as admin (stop schedule)", user.Username, workflow.ID) + log.Printf("[AUDIT] User %s is accessing workflow %s as admin (stop schedule)", user.Username, workflow.ID) } else { log.Printf("[WARNING] Wrong user (%s) for workflow %s (stop schedule)", user.Username, workflow.ID) resp.WriteHeader(401) diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index a988ab6b..a653f569 100644 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -4880,6 +4880,7 @@ const AngularWorkflow = (props) => { if (actionlist.length === 0) { // FIXME: Have previous execution values in here actionlist.push({"type": "Execution Argument", "name": "Execution Argument", "value": "$exec", "highlight": "exec", "autocomplete": "exec", "example": "hello"}) + //actionlist.push({"type": "Key:Value store", "name": "Shuffle KV store", "value": "$shuffle_cache", "highlight": "shuffle_cache", "autocomplete": "shuffle_cache", "example": ""}) if (workflow.workflow_variables !== null && workflow.workflow_variables !== undefined && workflow.workflow_variables.length > 0) { for (var key in workflow.workflow_variables) { diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index c7b2dcb0..26a0baf7 100644 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -359,6 +359,10 @@ const Workflows = (props) => { const [newWorkflowName, setNewWorkflowName] = React.useState(""); const [newWorkflowDescription, setNewWorkflowDescription] = React.useState(""); const [newWorkflowTags, setNewWorkflowTags] = React.useState([]); + + const [showExtraOptions, setShowExtraOptions] = React.useState(true); + const [defaultReturnValue, setDefaultReturnValue] = React.useState(""); + const [update, setUpdate] = React.useState("test"); const [deleteModalOpen, setDeleteModalOpen] = React.useState(false); const [publishModalOpen, setPublishModalOpen] = React.useState(false); @@ -368,6 +372,8 @@ const Workflows = (props) => { const [isDropzone, setIsDropzone] = React.useState(false); const [view, setView] = React.useState("grid") const [filters, setFilters] = React.useState([]) + const [submitLoading, setSubmitLoading] = React.useState(false) + const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" const findWorkflow = (filters) => { @@ -570,14 +576,14 @@ const Workflows = (props) => { } // Initialize the workflow itself - const ret = setNewWorkflow(data.name, data.description, data.tags, {}, false) + const ret = setNewWorkflow(data.name, data.description, data.tags, data.default_return_value, {}, false) .then((response) => { if (response !== undefined) { // SET THE FULL THING data.id = response.id // Actually create it - const ret = setNewWorkflow(data.name, data.description, data.tags, data, false) + const ret = setNewWorkflow(data.name, data.description, data.tags, data.default_return_value, data, false) .then((response) => { if (response !== undefined) { alert.success(`Successfully imported ${data.name}`) @@ -1233,10 +1239,12 @@ const Workflows = (props) => { }} > { + //console.log("DATA:" ,data) setModalOpen(true) setEditingWorkflow(data) setNewWorkflowName(data.name) setNewWorkflowDescription(data.description) + setDefaultReturnValue(data.default_return_value) if (data.tags !== undefined && data.tags !== null) { setNewWorkflowTags(JSON.parse(JSON.stringify(data.tags))) } @@ -1489,7 +1497,7 @@ const Workflows = (props) => { } // Can create and set workflows - const setNewWorkflow = (name, description, tags, editingWorkflow, redirect) => { + const setNewWorkflow = (name, description, tags, defaultReturnValue, editingWorkflow, redirect) => { var method = "POST" var extraData = "" @@ -1511,6 +1519,12 @@ const Workflows = (props) => { if (tags !== undefined) { workflowdata["tags"] = tags } + + if (defaultReturnValue !== undefined) { + workflowdata["default_return_value"] = defaultReturnValue + //console.log("WORKFLOW: ", workflowdata) + } + //console.log(workflowdata) //return @@ -1521,26 +1535,31 @@ const Workflows = (props) => { 'Accept': 'application/json', }, body: JSON.stringify(workflowdata), - credentials: "include", - }) + credentials: "include", + }) .then((response) => { if (response.status !== 200) { console.log("Status not 200 for workflows :O!") return } + setSubmitLoading(false) + return response.json() }) .then((responseJson) => { if (method === "POST" && redirect) { window.location.pathname = "/workflows/"+responseJson["id"] + setModalOpen(false) } else if (!redirect) { // Update :) setTimeout(() => { getAvailableWorkflows() }, 1000) setImportLoading(false) + setModalOpen(false) } else { alert.info("Successfully changed basic info for workflow") + setModalOpen(false) } return responseJson @@ -1548,6 +1567,8 @@ const Workflows = (props) => { .catch(error => { alert.error(error.toString()) setImportLoading(false) + setModalOpen(false) + setSubmitLoading(false) }); } @@ -1581,7 +1602,7 @@ const Workflows = (props) => { } // Initialize the workflow itself - const ret = setNewWorkflow(data.name, data.description, data.tags, {}, false) + const ret = setNewWorkflow(data.name, data.description, data.tags, data.default_return_value, {}, false) .then((response) => { if (response !== undefined) { // SET THE FULL THING @@ -1591,7 +1612,7 @@ const Workflows = (props) => { data.is_valid = false // Actually create it - const ret = setNewWorkflow(data.name, data.description, data.tags, data, false) + const ret = setNewWorkflow(data.name, data.description, data.tags, data.default_return_value, data, false) .then((response) => { if (response !== undefined) { alert.success("Successfully imported "+data.name) @@ -1776,7 +1797,7 @@ const Workflows = (props) => { color="primary" defaultValue={newWorkflowDescription} placeholder="Description" - rows="6" + rows="3" multiline margin="dense" fullWidth @@ -1802,11 +1823,29 @@ const Workflows = (props) => { setUpdate("delete "+chip) }} /> + {showExtraOptions ? + setDefaultReturnValue(event.target.value)} + InputProps={{ + style:{ + color: "white", + }, + }} + color="primary" + defaultValue={defaultReturnValue} + placeholder="Default return value (used for Subflows if the subflow fails)" + rows="3" + multiline + margin="dense" + fullWidth + /> + : null} @@ -2065,7 +2110,7 @@ const Workflows = (props) => { return response.json() }) .then((responseJson) => { - console.log("DATA: ", responseJson) + //console.log("DATA: ", responseJson) if (!responseJson.success) { if (responseJson.reason !== undefined) { alert.error("Failed loading: "+responseJson.reason) @@ -2192,6 +2237,8 @@ const Workflows = (props) => { diff --git a/functions/onprem/worker/build.sh b/functions/onprem/worker/build.sh index 19a1e43d..cd38f58a 100644 --- a/functions/onprem/worker/build.sh +++ b/functions/onprem/worker/build.sh @@ -1,5 +1,5 @@ NAME=shuffle-worker -VERSION=0.9.10 +VERSION=0.9.12 echo "Running docker build with $NAME:$VERSION" #CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o worker.bin . diff --git a/functions/onprem/worker/go.mod b/functions/onprem/worker/go.mod index eb62aca7..468e5f51 100644 --- a/functions/onprem/worker/go.mod +++ b/functions/onprem/worker/go.mod @@ -12,9 +12,10 @@ require ( github.com/docker/docker v20.10.5+incompatible github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.4.0 // indirect - github.com/frikky/shuffle-shared v0.0.63 + github.com/frikky/shuffle-shared v0.0.86 github.com/fsouza/go-dockerclient v1.7.2 github.com/go-git/go-billy/v5 v5.3.1 // indirect + github.com/go-git/go-git/v5 v5.4.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/google/go-github/v28 v28.1.1 // indirect github.com/gorilla/mux v1.8.0 @@ -23,4 +24,5 @@ require ( github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pkg/errors v0.9.1 // indirect google.golang.org/grpc v1.37.1 // indirect + gopkg.in/src-d/go-git.v4 v4.13.1 // indirect ) diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index 39b11a5c..dfbe8312 100644 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -135,62 +135,67 @@ func shutdown(workflowExecution shuffle.WorkflowExecution, nodeId string, reason log.Printf("[INFO] NOT cleaning up containers. IDS: %d, CLEANUP env: %s", len(containerIds), cleanupEnv) } - abortUrl := fmt.Sprintf("%s/api/v1/workflows/%s/executions/%s/abort", baseUrl, workflowExecution.Workflow.ID, workflowExecution.ExecutionId) + if len(reason) > 0 && len(nodeId) > 0 { + log.Printf("[INFO] Running abort of workflow because it should be finished") - path := fmt.Sprintf("?reason=%s", url.QueryEscape(reason)) - if len(nodeId) > 0 { - path += fmt.Sprintf("&node=%s", url.QueryEscape(nodeId)) - } - if len(environment) > 0 { - path += fmt.Sprintf("&env=%s", url.QueryEscape(environment)) - } - - //fmt.Println(url.QueryEscape(query)) - abortUrl += path - log.Printf("[INFO] Abort URL: %s", abortUrl) - - req, err := http.NewRequest( - "GET", - abortUrl, - nil, - ) - - if err != nil { - log.Println("[INFO] Failed building request: %s", err) - } - - // FIXME: Add an API call to the backend - authorization := os.Getenv("AUTHORIZATION") - if len(authorization) > 0 { - req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", authorization)) - } else { - log.Printf("[ERROR] No authorization specified for abort") - } - - req.Header.Add("Content-Type", "application/json") - client := &http.Client{ - Transport: &http.Transport{ - Proxy: nil, - }, - } - - httpProxy := os.Getenv("HTTP_PROXY") - httpsProxy := os.Getenv("HTTPS_PROXY") - if (len(httpProxy) > 0 || len(httpsProxy) > 0) && baseUrl != "http://shuffle-backend:5001" { - client = &http.Client{} - } else { - if len(httpProxy) > 0 { - log.Printf("[INFO] Running with HTTP proxy %s (env: HTTP_PROXY)", httpProxy) + abortUrl := fmt.Sprintf("%s/api/v1/workflows/%s/executions/%s/abort", baseUrl, workflowExecution.Workflow.ID, workflowExecution.ExecutionId) + path := fmt.Sprintf("?reason=%s", url.QueryEscape(reason)) + if len(nodeId) > 0 { + path += fmt.Sprintf("&node=%s", url.QueryEscape(nodeId)) } - if len(httpsProxy) > 0 { - log.Printf("[INFO] Running with HTTPS proxy %s (env: HTTPS_PROXY)", httpsProxy) + if len(environment) > 0 { + path += fmt.Sprintf("&env=%s", url.QueryEscape(environment)) } - } - log.Printf("[INFO] All App Logs: %#v", allLogs) - _, err = client.Do(req) - if err != nil { - log.Printf("[WARNING] Failed abort request: %s", err) + //fmt.Println(url.QueryEscape(query)) + abortUrl += path + log.Printf("[INFO] Abort URL: %s", abortUrl) + + req, err := http.NewRequest( + "GET", + abortUrl, + nil, + ) + + if err != nil { + log.Println("[INFO] Failed building request: %s", err) + } + + // FIXME: Add an API call to the backend + authorization := os.Getenv("AUTHORIZATION") + if len(authorization) > 0 { + req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", authorization)) + } else { + log.Printf("[ERROR] No authorization specified for abort") + } + + req.Header.Add("Content-Type", "application/json") + client := &http.Client{ + Transport: &http.Transport{ + Proxy: nil, + }, + } + + httpProxy := os.Getenv("HTTP_PROXY") + httpsProxy := os.Getenv("HTTPS_PROXY") + if (len(httpProxy) > 0 || len(httpsProxy) > 0) && baseUrl != "http://shuffle-backend:5001" { + client = &http.Client{} + } else { + if len(httpProxy) > 0 { + log.Printf("[INFO] Running with HTTP proxy %s (env: HTTP_PROXY)", httpProxy) + } + if len(httpsProxy) > 0 { + log.Printf("[INFO] Running with HTTPS proxy %s (env: HTTPS_PROXY)", httpsProxy) + } + } + + log.Printf("[INFO] All App Logs: %#v", allLogs) + _, err = client.Do(req) + if err != nil { + log.Printf("[WARNING] Failed abort request: %s", err) + } + } else { + log.Printf("[INFO] NOT running abort during shutdown.") } log.Printf("[INFO] Finished shutdown (after %d seconds). ", sleepDuration) @@ -761,6 +766,16 @@ func handleExecutionResult(workflowExecution shuffle.WorkflowExecution) { Value: workflowExecution.ExecutionId, }) + action.Parameters = append(action.Parameters, shuffle.WorkflowAppActionParameter{ + Name: "source_node", + Value: trigger.ID, + }) + + action.Parameters = append(action.Parameters, shuffle.WorkflowAppActionParameter{ + Name: "source_auth", + Value: workflowExecution.Authorization, + }) + //trigger.LargeImage = "" //err = handleSubworkflowExecution(client, workflowExecution, trigger, action) //if err != nil { From f213451f9b9fd783402c72f8f6e66bf7cf7a8982 Mon Sep 17 00:00:00 2001 From: frikky Date: Fri, 27 Aug 2021 16:02:59 +0200 Subject: [PATCH 041/117] Lots of fixes towards making Oauth2 smooth --- backend/go-app/go.mod | 4 +-- frontend/src/components/AlertTemplate.js | 2 +- frontend/src/components/Oauth2Auth.jsx | 35 ++++++++++++---------- frontend/src/components/ParsedAction.jsx | 2 +- frontend/src/views/Admin.jsx | 4 +-- frontend/src/views/AngularWorkflow.jsx | 37 ++++++++++++++++++------ 6 files changed, 54 insertions(+), 30 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index f7fe10af..99b85fd6 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -2,7 +2,7 @@ module shuffle 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 @@ -22,7 +22,7 @@ require ( github.com/docker/go-units v0.4.0 // indirect github.com/elastic/go-elasticsearch/v7 v7.13.1 // indirect github.com/frikky/kin-openapi v0.40.0 - github.com/frikky/shuffle-shared v0.0.84 + github.com/frikky/shuffle-shared v0.0.87 github.com/fsouza/go-dockerclient v1.7.2 github.com/ghodss/yaml v1.0.0 github.com/go-git/go-billy/v5 v5.0.0 diff --git a/frontend/src/components/AlertTemplate.js b/frontend/src/components/AlertTemplate.js index 58ff46cc..66f4b1c7 100644 --- a/frontend/src/components/AlertTemplate.js +++ b/frontend/src/components/AlertTemplate.js @@ -15,7 +15,7 @@ const alertStyle = { justifyContent: 'space-between', alignItems: 'center', boxShadow: '0px 2px 2px 2px rgba(0, 0, 0, 0.03)', - width: 400, + width: 300, boxSizing: 'border-box', zIndex: 100001, overflow: "hidden", diff --git a/frontend/src/components/Oauth2Auth.jsx b/frontend/src/components/Oauth2Auth.jsx index b2b06d75..64a82d5b 100644 --- a/frontend/src/components/Oauth2Auth.jsx +++ b/frontend/src/components/Oauth2Auth.jsx @@ -57,27 +57,32 @@ const AuthenticationOauth2 = (props) => { try { var newwin = window.open(url, "", "width=400,height=200") - console.log(newwin) - setTimeout(() => { - console.log(newwin) - console.log("CLOSED", newwin.closed) - }, 1000) + //console.log(newwin) + + var open = true + const timer = setInterval(() => { + if (newwin.closed) { + clearInterval(timer); + //alert('"Secure Payment" window closed!'); - setTimeout(() => { - console.log(newwin) - console.log("CLOSED", newwin.closed) - if (newwin.closed) { getAppAuthentication(true, true) setTimeout(() => { console.log("APPAUTH: ", appAuthentication) - - //{selectedAction.authentication.map(data => { - setAuthenticationModalOpen(false) - saveWorkflow(workflow) }, 1500) - } - }, 10000) + } + }, 1000); + //do { + // setTimeout(() => { + // console.log(newwin) + // console.log("CLOSED", newwin.closed) + // if (newwin.closed) { + + // open = false + // } + // }, 1000) + //} + //while(open === true) } catch (e) { alert.error("Failed authentication - probably bad credentials. Try again") setButtonClicked(false) diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index 3d0013ea..45f7a8eb 100644 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -1397,7 +1397,7 @@ const ParsedAction = (props) => { + + {fileNamespaces !== undefined && fileNamespaces !== null && fileNamespaces.length > 1 ? + + Namespace + + + : null} + @@ -2204,6 +2238,14 @@ const Admin = (props) => { /> {files === undefined || files === null ? null : files.map((file, index) => { + if (file.namespace === "") { + file.namespace = "default" + } + + if (file.namespace !== selectedNamespace) { + return null + } + var bgColor = "#27292d" if (index % 2 === 0) { bgColor = "#1f2023" From 17a353e975fbc9bc80d455db4a46e8e6effc0741 Mon Sep 17 00:00:00 2001 From: frikky Date: Tue, 31 Aug 2021 02:31:07 +0200 Subject: [PATCH 054/117] Bumped liquid version due to https://github.com/pwwang/liquidpy/issues/34. Cheers :D --- backend/app_sdk/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/app_sdk/requirements.txt b/backend/app_sdk/requirements.txt index 60502c8d..98bbcfcf 100644 --- a/backend/app_sdk/requirements.txt +++ b/backend/app_sdk/requirements.txt @@ -1,3 +1,3 @@ urllib3==1.25.9 requests==2.25.1 -liquidpy==0.6.3 +liquidpy==0.7.0 From 1d8e81585711ccf2e9f81e09a5dd31321a92efbc Mon Sep 17 00:00:00 2001 From: frikky Date: Tue, 31 Aug 2021 02:34:36 +0200 Subject: [PATCH 055/117] Added HTTP catch-all failures and file namespace downloads --- backend/app_sdk/app_base.py | 31 ++++++++++++ backend/app_sdk/build.sh | 2 +- frontend/src/components/ParsedAction.jsx | 62 ++++++++++++------------ frontend/src/views/AngularWorkflow.jsx | 2 +- frontend/src/views/Workflows.jsx | 2 +- 5 files changed, 65 insertions(+), 34 deletions(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index 4ed95b03..5e859c06 100644 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -12,6 +12,8 @@ import urllib3 import hashlib from liquid import Liquid import liquid +import zipfile +from io import BytesIO class AppBase: __version__ = None @@ -615,7 +617,23 @@ class AppBase: print("\nLOOP: %s\nRESULTS: %s" % (loop_wrapper, results)) return results + # Downloads all files from a namespace + # Currently only working on local version of Shuffle + def get_file_namespace(self, namespace): + org_id = self.full_execution["workflow"]["execution_org"]["id"] + get_path = "/api/v1/files/namespaces/%s?execution_id=%s" % (namespace, self.full_execution["execution_id"]) + headers = { + "Authorization": "Bearer %s" % self.authorization + } + + ret1 = requests.get("%s%s" % (self.url, get_path), headers=headers) + if ret1.status_code != 200: + return None + + filebytes = BytesIO(ret1.content) + myzipfile = zipfile.ZipFile(filebytes) + return myzipfile # Things to consider for files: # - How can you download / stream a file? @@ -2656,6 +2674,19 @@ class AppBase: action_result["status"] = "FAILURE" action_result["result"] = "Function %s is not callable." % actionname + # https://ptb.discord.com/channels/747075026288902237/882017498550112286/882043773138382890 + except (requests.exceptions.RequestException, TimeoutError) as e: + print(f"Failed to execute request: {e}") + self.logger.exception(f"Failed to execute {e}-{action['id']}") + action_result["status"] = "SUCCESS" + try: + action_result["result"] = json.dumps({ + "success": False, + "reason": f"Request error - failing silently. Details: {e}" + }) + except json.decoder.JSONDecodeError as e: + action_result["result"] = f"Request error: {e}" + except Exception as e: print(f"Failed to execute: {e}") self.logger.exception(f"Failed to execute {e}-{action['id']}") diff --git a/backend/app_sdk/build.sh b/backend/app_sdk/build.sh index 4dfe2531..623458b8 100644 --- a/backend/app_sdk/build.sh +++ b/backend/app_sdk/build.sh @@ -3,7 +3,7 @@ ### DEFAULT NAME=shuffle-app_sdk -VERSION=0.9.10 +VERSION=0.9.11 docker rmi docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION --force docker build . -f Dockerfile -t frikky/shuffle:app_sdk -t frikky/$NAME:$VERSION -t docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION -t ghcr.io/frikky/$NAME:$VERSION diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index 7bae11dd..d70ee5b6 100644 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -341,7 +341,7 @@ const ParsedAction = (props) => { }) const changeActionParameter = (event, count, data) => { - console.log(event) + //console.log(event) if (data.name.startsWith("${") && data.name.endsWith("}")) { // PARAM FIX - Gonna use the ID field, even though it's a hack const paramcheck = selectedAction.parameters.find(param => param.name === "body") @@ -833,12 +833,12 @@ const ParsedAction = (props) => { } const clickedFieldId = "rightside_field_"+count + // { maxWidth: "95%", fontSize: "1em", }, - // endAdornment: ( - // hideExtraTypes ? null : - // - // - // { - // setMenuPosition({ - // top: event.pageY+10, - // left: event.pageX+10, - // }) - // setShowDropdownNumber(count) - // setShowDropdown(true) - // setShowAutocomplete(true) - // }}/> - // - // - - // ) + endAdornment: ( + hideExtraTypes ? null : + + + { + setMenuPosition({ + top: event.pageY+10, + left: event.pageX+10, + }) + setShowDropdownNumber(count) + setShowDropdown(true) + setShowAutocomplete(true) + }}/> + + + + ) }} fullWidth multiline={multiline} @@ -879,20 +879,20 @@ const ParsedAction = (props) => { rows={rows} color="primary" defaultValue={data.value} - - value={data.value} - options={{ - theme: 'gruvbox-dark', - keyMap: 'sublime', - mode: 'python', - }} - height = {200} + //value={data.value} + //options={{ + // theme: 'gruvbox-dark', + // keyMap: 'sublime', + // mode: 'python', + //}} + //height={multiline ? 50 : 150} type={placeholder.includes("***") || (data.configuration && (data.name.toLowerCase().includes("api") || data.name.toLowerCase().includes("key") || data.name.toLowerCase().includes("pass"))) ? "password" : "text"} placeholder={placeholder} onChange={(event) => { - changeActionParameterCodemirror(event, count, data) + //changeActionParameterCodemirror(event, count, data) + changeActionParameter(event, count, data) }} helperText={selectedApp.generated && selectedApp.activated && data.name === "body" ? diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index daacc7c8..be3abe91 100644 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -1612,7 +1612,7 @@ const AngularWorkflow = (props) => { var hiddenNodes = [] const onNodeDragStop = (event, selectedAction) => { const nodedata = event.target.data() - console.log("IN NODE DRAG STOP: ", nodedata) + //console.log("IN NODE DRAG STOP: ", nodedata) if (nodedata.id === selectedAction.id) { return } diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index 26a0baf7..d30bbde0 100644 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -89,7 +89,7 @@ export const GetIconInfo = (action) => { {"key": "cache_get", "values": ["get_cache"]}, {"key": "filter", "values": ["filter", "route", "router",]}, {"key": "merge", "values": ["join", "merge"]}, - {"key": "search", "values": ["search", "find", "locate", "index",]}, + {"key": "search", "values": ["search", "find", "locate", "index", "analyze", "anal",]}, {"key": "list", "values": ["list", "head", "options"]}, {"key": "download", "values": ["capture", "get", "download", "return", "hello_world", "curl",]}, {"key": "add", "values": ["add"]}, From da75cfd5414fa8336634c7909d6e3321b4bc2dd5 Mon Sep 17 00:00:00 2001 From: frikky Date: Tue, 31 Aug 2021 03:17:55 +0200 Subject: [PATCH 056/117] Fixed frontend for Oauth2 in app creator --- frontend/src/views/AppCreator.jsx | 100 ++++++++++++++++++++++++++++-- 1 file changed, 95 insertions(+), 5 deletions(-) diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx index 0b39a84a..6619f95f 100644 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -201,7 +201,7 @@ const AppCreator = (props) => { const actionNonBodyRequest = ["GET", "HEAD", "DELETE", "CONNECT"] const actionBodyRequest = ["POST", "PUT", "PATCH",] //const authenticationOptions = ["No authentication", "API key", "Bearer auth", "Basic auth", "JWT", "Oauth2"] - const authenticationOptions = ["No authentication", "API key", "Bearer auth", "Basic auth"] + const authenticationOptions = ["No authentication", "API key", "Bearer auth", "Basic auth"]//, "Oauth2"] const apikeySelection = ["Header", "Query",] const [name, setName] = useState(""); @@ -220,6 +220,9 @@ const AppCreator = (props) => { const [newWorkflowCategories, setNewWorkflowCategories] = React.useState([]); const [parameterName, setParameterName] = useState(""); const [parameterLocation, setParameterLocation] = useState(apikeySelection.length > 0 ? apikeySelection[0] : ""); + const [refreshUrl, setRefreshUrl] = useState(""); + const [oauth2Scopes, setOauth2Scopes] = useState(["google.com"]); + const [urlPath, setUrlPath] = useState(""); //const [urlPathQueries, setUrlPathQueries] = useState([{"name": "test", "required": false}]); const [urlPathQueries, setUrlPathQueries] = useState([]); @@ -904,11 +907,24 @@ const AppCreator = (props) => { //if (Object.entries(securitySchemes) > 1 && var newauth = [] for (const [key, value] of Object.entries(securitySchemes)) { + console.log(key, value) if (value.scheme === "bearer") { setAuthenticationOption("Bearer auth") setAuthenticationRequired(true) - } else if (key === "oauth2") { - alert.info("Can't handle Oauth2 auth yet.") + } else if (key === "Oauth2") { + //alert.info("Can't handle Oauth2 auth yet.") + setAuthenticationOption("Oauth2") + setAuthenticationRequired(true) + + if (value.flow.authorizationCode.authorizationUrl !== undefined) { + setParameterName(value.flow.authorizationCode.authorizationUrl) + } + if (value.flow.authorizationCode.tokenUrl !== undefined) { + setParameterLocation(value.flow.authorizationCode.tokenUrl) + } + if (value.flow.authorizationCode.refreshUrl !== undefined) { + setRefreshUrl(value.flow.authorizationCode.refreshUrl) + } } else if (key === "ApiKeyAuth") { setAuthenticationOption("API key") @@ -1324,14 +1340,26 @@ const AppCreator = (props) => { "scheme": "basic", } } else if (authenticationOption === "Oauth2") { + //parameterName, parameterValue, revocationUrl data.components.securitySchemes["Oauth2"] = { "type": "oauth2", + "description": "Oauth2.0 authorizationCode authentication", "flow": { "authorizationCode": { - + "authorizationUrl": parameterName, + "tokenUrl": parameterLocation, + "refreshUrl": refreshUrl, + "scopes": [], }, }, } + + if (oauth2Scopes.scopes > 0) { + for (var key in oauth2Scopes) { + const scope = oauth2Scopes[key] + data.components.securitySchemes["Oauth2"]["flow"]["authorizationCode"]["scopes"].push(scope) + } + } } if (setExtraAuth.length > 0) { @@ -1629,8 +1657,70 @@ const AppCreator = (props) => {
Oauth2 authentication - Add the URL to redirect to + Base Authorization URL + setParameterName(e.target.value)} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style:{ + color: "white", + }, + }} + /> + + Token URL + + setParameterLocation(e.target.value)} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style:{ + color: "white", + }, + }} + /> + + Refresh-token URL + + setRefreshUrl(e.target.value)} + InputProps={{ + style:{ + color: "white", + }, + }} + />
: null From 9f3889c76d06f8e1c47b01168a032df809a09e52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20C?= <82955277+gc-advens@users.noreply.github.com> Date: Tue, 31 Aug 2021 08:20:14 +0200 Subject: [PATCH 057/117] Revert changes --- functions/onprem/orborus/orborus.go | 67 ++++++++++++++++++++++++++--- functions/onprem/orborus/run.sh | 1 + 2 files changed, 61 insertions(+), 7 deletions(-) diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 73faafeb..6ca42b24 100644 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -16,6 +16,7 @@ import ( "log" "net/http" "os" + "os/exec" "strconv" "strings" "time" @@ -52,12 +53,13 @@ var orgId = os.Getenv("ORG_ID") var baseUrl = os.Getenv("BASE_URL") var environment = os.Getenv("ENVIRONMENT_NAME") var dockerApiVersion = os.Getenv("DOCKER_API_VERSION") +var runningMode = strings.ToLower(os.Getenv("RUNNING_MODE")) var cleanupEnv = strings.ToLower(os.Getenv("CLEANUP")) var timezone = os.Getenv("TZ") -var containerName = os.Getenv("CONTAINER_NAME") var executionIds = []string{} var dockercli *dockerclient.Client +var containerId string func init() { var err error @@ -66,16 +68,60 @@ func init() { if err != nil { panic(fmt.Sprintf("Unable to create docker client: %s", err)) } + + getThisContainerId() +} + +// form id of current running container +func getThisContainerId() { + fCol := "" + + // some adjusting based on current running mode + switch runningMode { + case "kubernetes": + // cgroup will be like: + // 11:net_cls,net_prio:/kubepods/besteffort/podf132b44d-cfcf-43f7-9906-79f58e268333/851466f8b5ed5aa0f265b1c95c6d2bafbc51a38dd5c5a1621b6e586572150009 + fCol = "5" + log.Printf("[INFO] Running containerized in Kubernetes!") + + case "docker": + // cgroup will be like: + // 12:perf_event:/docker/0f06810364f52a2cd6e80bfba27419cb8a29758a204cd676388f4913bb366f2b + fCol = "3" + log.Printf("[INFO] Running containerized in Docker!") + + default: + fCol = "3" // for backward-compatibility with production + log.Printf("[WARNING] RUNNING_MODE not set - defaulting to Docker (NOT Kubernetes).") + } + + if fCol != "" { + cmd := fmt.Sprintf("cat /proc/self/cgroup | grep memory | tail -1 | cut -d/ -f%s | grep -o -E '[0-9A-z]{64}'", fCol) + out, err := exec.Command("bash", "-c", cmd).Output() + if err == nil { + containerId = strings.TrimSpace(string(out)) + + // cgroup error. Hardcoding this. + // https://github.com/moby/moby/issues/7015 + //log.Printf("Checking if %s is in %s", ".scope", string(out)) + if strings.Contains(string(out), ".scope") { + containerId = "shuffle-orborus" + //docker-76c537e9a4b7c7233011f5d70e6b7f2d600b6413ac58a96519b8dca7a3f7117a.scope + } + } else { + if fCol == "0" { + containerId = "shuffle-orborus" + log.Printf("[WARNING] Failed getting container ID: %s", err) + } + } + } + + log.Printf(`[INFO] Started with containerId "%s"`, containerId) } // Deploys the internal worker whenever something happens // https://docs.docker.com/engine/api/sdk/examples/ func deployWorker(image string, identifier string, env []string) { - if containerName == "" { - log.Printf("[INFO] CONTAINER_NAME is not set, defaulting to 'shuffle-orborus'") - containerName = "shuffle-orborus" - } - // Binds is the actual "-v" volume. // Max 20% CPU every second @@ -91,7 +137,14 @@ func deployWorker(image string, identifier string, env []string) { Binds: []string{ "/var/run/docker.sock:/var/run/docker.sock:rw", }, - NetworkMode: container.NetworkMode(fmt.Sprintf("container:%s", containerName)) + } + + // form container id and use it as network source if it's not empty + if containerId != "" { + //log.Printf("[INFO] Found container ID %s", containerId) + hostConfig.NetworkMode = container.NetworkMode(fmt.Sprintf("container:%s", containerId)) + } else { + //log.Printf("[INFO] Empty self container id, continue without NetworkMode") } if cleanupEnv == "true" { diff --git a/functions/onprem/orborus/run.sh b/functions/onprem/orborus/run.sh index 9c6d8db5..48e5b701 100644 --- a/functions/onprem/orborus/run.sh +++ b/functions/onprem/orborus/run.sh @@ -3,6 +3,7 @@ docker run \ --env ENVIRONMENT_NAME="Shuffle" \ --env BASE_URL=http://shuffle-backend:5001 \ --env DOCKER_API_VERSION=1.42 \ + --env RUNNING_MODE="Docker" \ --network "shuffle_shuffle" \ -v /var/run/docker.sock:/var/run/docker.sock \ frikky/shuffle:orborus From 233477407c8ec69b148ba6dc7c721ea26f8fbc8b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9goire=20C?= <82955277+gc-advens@users.noreply.github.com> Date: Tue, 31 Aug 2021 08:45:41 +0200 Subject: [PATCH 058/117] Add fallback strategies when orborus container id was not found --- functions/onprem/orborus/orborus.go | 29 +++++++++++++++-------------- 1 file changed, 15 insertions(+), 14 deletions(-) diff --git a/functions/onprem/orborus/orborus.go b/functions/onprem/orborus/orborus.go index 6ca42b24..7bc275d0 100644 --- a/functions/onprem/orborus/orborus.go +++ b/functions/onprem/orborus/orborus.go @@ -56,6 +56,7 @@ var dockerApiVersion = os.Getenv("DOCKER_API_VERSION") var runningMode = strings.ToLower(os.Getenv("RUNNING_MODE")) var cleanupEnv = strings.ToLower(os.Getenv("CLEANUP")) var timezone = os.Getenv("TZ") +var containerName = os.Getenv("CONTAINER_NAME") var executionIds = []string{} var dockercli *dockerclient.Client @@ -101,18 +102,25 @@ func getThisContainerId() { if err == nil { containerId = strings.TrimSpace(string(out)) - // cgroup error. Hardcoding this. + // cgroup error. Use fallback strategy below. // https://github.com/moby/moby/issues/7015 //log.Printf("Checking if %s is in %s", ".scope", string(out)) if strings.Contains(string(out), ".scope") { - containerId = "shuffle-orborus" + containerId = "" //docker-76c537e9a4b7c7233011f5d70e6b7f2d600b6413ac58a96519b8dca7a3f7117a.scope } } else { - if fCol == "0" { - containerId = "shuffle-orborus" - log.Printf("[WARNING] Failed getting container ID: %s", err) - } + log.Printf("[WARNING] Failed getting container ID: %s", err) + } + } + + if containerId == "" { + if containerName != "" { + containerId = containerName + log.Printf("[INFO] Falling back to CONTAINER_NAME as container ID") + } else { + containerId = "shuffle-orborus" + log.Printf(`[WARNING] CONTAINER_NAME is not set. Falling back to default name "%s" as container ID`, containerId) } } @@ -137,14 +145,7 @@ func deployWorker(image string, identifier string, env []string) { Binds: []string{ "/var/run/docker.sock:/var/run/docker.sock:rw", }, - } - - // form container id and use it as network source if it's not empty - if containerId != "" { - //log.Printf("[INFO] Found container ID %s", containerId) - hostConfig.NetworkMode = container.NetworkMode(fmt.Sprintf("container:%s", containerId)) - } else { - //log.Printf("[INFO] Empty self container id, continue without NetworkMode") + NetworkMode: container.NetworkMode(fmt.Sprintf("container:%s", containerId)), } if cleanupEnv == "true" { From 24c01eb89d5e06f86fab4c1c8ff8273245120f2c Mon Sep 17 00:00:00 2001 From: frikky Date: Wed, 1 Sep 2021 00:56:53 +0200 Subject: [PATCH 059/117] Added namespace downloads for files as ZIP --- backend/database/Dockerfile | 5 --- backend/go-app/walkoff.go | 4 +- frontend/src/components/ConfigureWorkflow.jsx | 41 ++++++++++++++++--- frontend/src/components/ParsedAction.jsx | 3 +- frontend/src/views/AngularWorkflow.jsx | 10 +++-- frontend/src/views/Workflows.jsx | 2 + functions/onprem/worker/build.sh | 2 +- functions/onprem/worker/go.mod | 2 +- 8 files changed, 50 insertions(+), 19 deletions(-) delete mode 100644 backend/database/Dockerfile diff --git a/backend/database/Dockerfile b/backend/database/Dockerfile deleted file mode 100644 index 2b1758f7..00000000 --- a/backend/database/Dockerfile +++ /dev/null @@ -1,5 +0,0 @@ -# 2. docker run -p 8000:8000 -FROM google/cloud-sdk - -EXPOSE 8000 -CMD ["gcloud", "beta", "emulators", "datastore", "start", "--project=shuffle", "--host-port", "0.0.0.0:8000", "--data-dir=/etc/shuffle"] diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index afb31c86..f1c295df 100644 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -2076,9 +2076,9 @@ func executeWorkflow(resp http.ResponseWriter, request *http.Request) { if user.Id != workflow.Owner && user.Role != "scheduler" && user.Role != fmt.Sprintf("workflow_%s", fileId) { if workflow.OrgId == user.ActiveOrg.Id && user.Role == "admin" { - log.Printf("[INFO] Letting user %s execute %s because they're admin of the same org", user.Username, workflow.ID) + log.Printf("[AUDIT] Letting user %s execute %s because they're admin of the same org", user.Username, workflow.ID) } else { - log.Printf("[WARNING] Wrong user (%s) for workflow %s (execute)", user.Username, workflow.ID) + log.Printf("[AUDIT] Wrong user (%s) for workflow %s (execute)", user.Username, workflow.ID) resp.WriteHeader(401) resp.Write([]byte(`{"success": false}`)) return diff --git a/frontend/src/components/ConfigureWorkflow.jsx b/frontend/src/components/ConfigureWorkflow.jsx index e82ebb6b..43b93d33 100644 --- a/frontend/src/components/ConfigureWorkflow.jsx +++ b/frontend/src/components/ConfigureWorkflow.jsx @@ -11,8 +11,8 @@ import { FixName } from "../views/Apps.jsx"; // Triggers // // Specifically used for UNSAVED workflows only? -const Workflow = (props) => { - const { globalUrl, theme, workflow, appAuthentication, setSelectedAction, setAuthenticationModalOpen, setSelectedApp, apps, selectedAction,setConfigureWorkflowModalOpen, saveWorkflow, newWebhook, submitSchedule, referenceUrl, isCloud, setAuthenticationType, } = props +const ConfigureWorkflow = (props) => { + const { globalUrl, theme, workflow, appAuthentication, setSelectedAction, setAuthenticationModalOpen, setSelectedApp, apps, selectedAction,setConfigureWorkflowModalOpen, saveWorkflow, newWebhook, submitSchedule, referenceUrl, isCloud, setAuthenticationType, alert, } = props const [requiredActions, setRequiredActions] = React.useState([]) const [requiredVariables, setRequiredVariables] = React.useState([]) const [requiredTriggers, setRequiredTriggers] = React.useState([]) @@ -90,7 +90,7 @@ const Workflow = (props) => { const app = apps.find(app => app.name === action.app_name && (app.app_version === action.app_version || (app.loop_versions !== null && app.loop_versions.includes(action.app_version)))) if (app === undefined || app === null) { - console.log("App not found!") + console.log("App not found: ", action.app_name) newaction.must_activate = true } else { @@ -395,8 +395,8 @@ const Workflow = (props) => { : null} {action.must_activate ? -
*/} - {(selectedActionParameters[count].options !== undefined && selectedActionParameters[count].options !== null && selectedActionParameters[count].options.length > 0 && selectedActionParameters[count].required === true && selectedActionParameters[count].unique_toggled !== undefined) || hideExtraTypes ? null : + {/*(selectedActionParameters[count].options !== undefined && selectedActionParameters[count].options !== null && selectedActionParameters[count].options.length > 0 && selectedActionParameters[count].required === true && selectedActionParameters[count].unique_toggled !== undefined) || hideExtraTypes ? null :
{}}> @@ -1349,7 +1349,7 @@ const ParsedAction = (props) => {
- } + */}
{datafield} {showDropdown && showDropdownNumber === count && data.variant === "STATIC_VALUE" && jsonList.length > 0 ? diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index 4d896186..fcdd7121 100644 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -91,10 +91,10 @@ export const GetIconInfo = (action) => { {"key": "merge", "values": ["join", "merge"]}, {"key": "search", "values": ["search", "find", "locate", "index", "analyze", "anal", "match"]}, {"key": "list", "values": ["list", "head", "options"]}, - {"key": "download", "values": ["capture", "get", "download", "return", "hello_world", "curl", "request", ]}, + {"key": "download", "values": ["capture", "get", "download", "return", "hello_world", "curl", "request", "export", "preview"]}, {"key": "add", "values": ["add"]}, {"key": "delete", "values": ["delete", "remove", "clear", "clean",]}, - {"key": "send", "values": ["send", "dispatch", "mail", "forward", "post", "submit", "mark", "set"]}, + {"key": "send", "values": ["send", "dispatch", "mail", "forward", "post", "submit", "mark", "set", "release", ]}, {"key": "repeat", "values": ["repeat", "retry", "pause", "skip", "copy", "replicat", ]}, {"key": "execute", "values": ["execute", "run", "play", "raise",]}, {"key": "extract", "values": ["extract", "unpack", "decompress", "open"]}, diff --git a/functions/extensions/aws-lambda/s3_function.py b/functions/extensions/aws-lambda/s3_function.py index c183cb89..c5e11cde 100644 --- a/functions/extensions/aws-lambda/s3_function.py +++ b/functions/extensions/aws-lambda/s3_function.py @@ -3,7 +3,7 @@ import urllib.parse import requests import os -#print('Loading function') +print('Loading function') def lambda_handler(event, context): #print("Received event: " + json.dumps(event, indent=2)) From e67d577205d7c7b326cd82041433bdd8ae5bf63f Mon Sep 17 00:00:00 2001 From: frikky Date: Mon, 6 Sep 2021 19:21:46 +0200 Subject: [PATCH 069/117] Added auto-login for shuffle frontend during setup --- backend/app_sdk/app_base.py | 13 ++++-- frontend/src/App.jsx | 2 +- frontend/src/components/ParsedAction.jsx | 39 +--------------- frontend/src/views/Admin.jsx | 8 ++-- frontend/src/views/LoginPage.jsx | 58 ++++++++++++++++++++++-- 5 files changed, 70 insertions(+), 50 deletions(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index 59127d03..2617dd9a 100644 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -521,7 +521,7 @@ class AppBase: print("[INFO] Multiplier length: %d" % len(param_multiplier)) #tmp = "" for subparams in param_multiplier: - #print(f"SUBPARAMS IN MULTI: {subparams}") + print(f"SUBPARAMS IN MULTI: {subparams}") try: #tmp = await func(**subparams) @@ -530,6 +530,7 @@ class AppBase: tmp = await func(**subparams) break except TypeError as e: + print("BASE TYPEERROR: %s" % e) errorstring = "%s" % e if "got an unexpected keyword argument" in errorstring: fieldsplit = errorstring.split("'") @@ -2087,26 +2088,30 @@ class AppBase: newvalue = json.loads(action["parameters"][counter]["value"]) deletekeys = [] for key, value in newvalue.items(): - print(key, value) + print("%s: %s" % (key, value)) if isinstance(value, str) and len(value) == 0: deletekeys.append(key) continue if value == "${%s}" % key: - print("Deleting %s because key = value") + print("Deleting %s because key = value" % key) deletekeys.append(key) continue for deletekey in deletekeys: del newvalue[deletekey] - print("Post delete: %s" % newvalue) + #print("Post delete: %s" % newvalue) for key, value in newvalue.items(): + if isinstance(value, bool): + continue + try: value = json.loads(value) newvalue[key] = value except json.decoder.JSONDecodeError as e: print("Inner overwrite issue: %s" % e) + continue action["parameters"][counter]["value"] = json.dumps(newvalue) diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 562c154e..3b48175a 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -107,7 +107,7 @@ const App = (message, props) => {
- } /> + } /> } /> } /> } /> diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index 352de397..abcd6309 100644 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -1553,44 +1553,7 @@ const ParsedAction = (props) => {
+ }
From 52ba4a9537b68bbac1388190de03a113d68dea8b Mon Sep 17 00:00:00 2001 From: frikky Date: Tue, 7 Sep 2021 03:14:36 +0200 Subject: [PATCH 070/117] Fixed oauth2 and backslash SDK issue --- backend/app_sdk/app_base.py | 44 ++++++++++++++++++------ frontend/src/components/Oauth2Auth.jsx | 4 ++- frontend/src/components/ParsedAction.jsx | 6 ++-- frontend/src/views/LoginPage.jsx | 5 ++- functions/onprem/worker/worker.go | 4 +-- 5 files changed, 45 insertions(+), 18 deletions(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index 2617dd9a..7b90b818 100644 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -521,7 +521,7 @@ class AppBase: print("[INFO] Multiplier length: %d" % len(param_multiplier)) #tmp = "" for subparams in param_multiplier: - print(f"SUBPARAMS IN MULTI: {subparams}") + #print(f"SUBPARAMS IN MULTI: {subparams}") try: #tmp = await func(**subparams) @@ -1129,6 +1129,7 @@ class AppBase: return default_error # Parses the INNER value and recurses until everything is done + # Looks for a way to use e.g. int() or number() as a value def parse_wrapper(data): try: if "(" not in data or ")" not in data: @@ -1177,7 +1178,7 @@ class AppBase: c_parentheses = parse_nested_param(parse_string, 0)[0] match_string = re.escape(c_parentheses) custom_casting = re.findall(fr"({wrapper_group})\({match_string}", parse_string) - + print("In ELSE: %s" % custom_casting) # check if a wrapper was found if len(custom_casting) != 0: inner_result = parse_type(c_parentheses, custom_casting[0]) @@ -1189,6 +1190,7 @@ class AppBase: else: parse_string = inner_result + print("PARSE STRING: %s" % parse_string) return parse_string, True # Looks for parantheses to grab special cases within a string, e.g: @@ -1203,16 +1205,28 @@ class AppBase: if "(" not in data or ")" not in data: return data + if isinstance(data, str) and len(data) > 4: + if (data[0] == "{" or data[0] == "[") and (data[len(data)-1] == "]" or data[len(data)-1] == "}"): + print("Skipping parser because use of {[ and ]}") + return data + newdata = [] newstring = "" record = True paranCnt = 0 + charcnt = 0 for char in data: if char == "(": - paranCnt += 1 - - if not record: - record = True + charskip = False + if charcnt > 0: + if data[charcnt-1] == " ": + charskip = True + + if not charskip: + paranCnt += 1 + + if not record: + record = True if record: newstring += char @@ -1227,6 +1241,8 @@ class AppBase: if paranCnt == 0: record = False + + charcnt += 1 if len(newstring) > 0: newdata.append(newstring) @@ -1378,7 +1394,7 @@ class AppBase: basejson = json.loads(basejson[value]) print("BASEJSON: %s" % basejson) except json.decoder.JSONDecodeError as e: - print("RETURNING BECAUSE '%s' IS A NORMAL STRING" % basejson[value]) + print("RETURNING BECAUSE '%s' IS A NORMAL STRING (0)" % basejson[value]) return basejson[value], False else: basejson = basejson[value] @@ -1398,7 +1414,7 @@ class AppBase: basejson = json.loads(basejson[value]) print("BASEJSON: %s" % basejson) except json.decoder.JSONDecodeError as e: - print("RETURNING BECAUSE '%s' IS A NORMAL STRING" % basejson[value]) + print("RETURNING BECAUSE '%s' IS A NORMAL STRING (1)" % basejson[value]) return basejson[value], False else: basejson = basejson[value] @@ -2206,9 +2222,9 @@ class AppBase: for i in range(len(json_replacement)): if isinstance(json_replacement[i], dict) or isinstance(json_replacement[i], list): tmp_replacer = json.dumps(json_replacement[i]) - newvalue = tmpitem.replace(actualitem[index][0], tmp_replacer, 1) + newvalue = tmpitem.replace(str(actualitem[index][0]), str(tmp_replacer), 1) else: - newvalue = tmpitem.replace(actualitem[index][0], json_replacement[i], 1) + newvalue = tmpitem.replace(str(actualitem[index][0]), str(json_replacement[i]), 1) try: newvalue = json.loads(newvalue) @@ -2281,6 +2297,7 @@ class AppBase: actualitem = replace[2] if actualitem.endswith("}$"): actualitem = actualitem[:-2] + except IndexError: continue @@ -2344,6 +2361,7 @@ class AppBase: print("(2) JSON ERROR IN FILE HANDLING: %s" % e) if not isfile: + tmpitem = tmpitem.replace("\\\\", "\\", -1) resultarray.append(tmpitem) # With this parameter ready, add it to... a greater list of parameters. Rofl @@ -2397,7 +2415,11 @@ class AppBase: else: # Parses things like int(value) print("Normal parsing (not looping)")#with data %s" % value) + + # This part has fucked over so many random JSON usages because of weird paranthesis parsing + value = parse_wrapper_start(value) + print("Post return: %s" % value) #if parameter["id"] == "body_replacement": # print("Should run body replacement in index %d with %s" % (bodyindex, parameter)) @@ -2436,7 +2458,7 @@ class AppBase: #remove_params.append(parameter["name"]) # Fix lists here # FIXME: This doesn't really do anything anymore - print("CHECKING multi execution list!") + print("CHECKING multi execution list: %d!" % len(multi_execution_lists)) if len(multi_execution_lists) > 0: print("\n Multi execution list has more data: %d" % len(multi_execution_lists)) filteredlist = [] diff --git a/frontend/src/components/Oauth2Auth.jsx b/frontend/src/components/Oauth2Auth.jsx index 64a82d5b..321cd811 100644 --- a/frontend/src/components/Oauth2Auth.jsx +++ b/frontend/src/components/Oauth2Auth.jsx @@ -45,7 +45,9 @@ const AuthenticationOauth2 = (props) => { console.log("EDIT SCOPE!") } - const redirectUri = `http://${window.location.host}/set_authentication` + console.log(window.location) + //const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" + const redirectUri = `${window.location.protocol}//${window.location.host}/set_authentication` const state = `workflow_id%3D${workflow.id}%26reference_action_id%3d${selectedAction.app_id}%26app_name%3d${selectedAction.app_name}%26app_id%3d${selectedAction.app_id}%26app_version%3d${selectedAction.app_version}%26authentication_url%3d${authentication_url}%26scope%3d${resources}%26client_id%3d${client_id}%26client_secret%3d${client_secret}` const url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&scope=${resources}&state=${state}` diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index abcd6309..7e6fd11e 100644 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -1552,9 +1552,9 @@ const ParsedAction = (props) => {
:
diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index b9ae4e59..919d661f 100644 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -186,7 +186,7 @@ func deployApp(cli *dockerclient.Client, image string, identifier string, env [] Type: "json-file", Config: map[string]string{}, }, - Resources: container.Resources{}, + Resources: container.Resources{}, NetworkMode: container.NetworkMode(fmt.Sprintf("container:worker-%s", workflowExecution.ExecutionId)), } @@ -1107,7 +1107,7 @@ func handleExecutionResult(workflowExecution shuffle.WorkflowExecution) { if strings.Contains(buildBuf.String(), "errorDetail") { log.Printf("[ERROR] Docker build:\n%s\nERROR ABOVE: Trying to pull tags from: %s", buildBuf.String(), image) log.Printf("[DEBUG] Shutting down (14)") - shutdown(workflowExecution, action.ID, fmt.Sprintf("%s", err.Error()), true) + shutdown(workflowExecution, action.ID, fmt.Sprintf("Error deploying container: %s", buildBuf.String()), true) } log.Printf("[INFO] Successfully downloaded %s", image) From e028e9af99f11669ed83d572f9898cb1ac3ce004 Mon Sep 17 00:00:00 2001 From: frikky Date: Wed, 8 Sep 2021 01:09:57 +0200 Subject: [PATCH 071/117] Made SDK have more included libraries, and made App Builder MUCH faster --- backend/app_gen/openapi/baseline/Dockerfile | 25 ++++++++++++--------- backend/app_sdk/Dockerfile | 4 +++- backend/app_sdk/app_base.py | 19 +++++++++++++--- backend/go-app/go.mod | 2 +- functions/onprem/worker/build.sh | 2 +- 5 files changed, 35 insertions(+), 17 deletions(-) diff --git a/backend/app_gen/openapi/baseline/Dockerfile b/backend/app_gen/openapi/baseline/Dockerfile index 740fee62..8512e833 100644 --- a/backend/app_gen/openapi/baseline/Dockerfile +++ b/backend/app_gen/openapi/baseline/Dockerfile @@ -2,24 +2,27 @@ FROM frikky/shuffle:app_sdk as base # We're going to stage away all of the bloat from the build tools so lets create a builder stage -FROM base as builder +#FROM base as builder # Install all alpine build tools needed for our pip installs -RUN apk --no-cache add --update alpine-sdk libffi libffi-dev musl-dev openssl-dev +#RUN apk --no-cache add --update alpine-sdk libffi libffi-dev musl-dev openssl-dev # Install all of our pip packages in a single directory that we can copy to our base image later -RUN mkdir /install -WORKDIR /install -COPY requirements.txt /requirements.txt -RUN pip install --prefix="/install" -r /requirements.txt +#RUN mkdir /install +#WORKDIR /install +#COPY requirements.txt /requirements.txt +# +## Switch back to our base image and copy in all of our built packages and source code +#FROM base +#COPY --from=builder /install /usr/local -# Switch back to our base image and copy in all of our built packages and source code -FROM base -COPY --from=builder /install /usr/local -COPY src /app # Install any binary dependencies needed in our final image - this can be a lot of different stuff -RUN apk --no-cache add --update libmagic +#RUN apk --no-cache add --update libmagic +WORKDIR / +COPY requirements.txt /requirements.txt +RUN pip install --prefix="/usr/local" -r /requirements.txt +COPY src /app # Finally, lets run our app! WORKDIR /app diff --git a/backend/app_sdk/Dockerfile b/backend/app_sdk/Dockerfile index 84f4f950..b2febec6 100644 --- a/backend/app_sdk/Dockerfile +++ b/backend/app_sdk/Dockerfile @@ -8,7 +8,9 @@ WORKDIR /install FROM base -RUN apk update && apk add --no-cache tzdata +#--no-cache +RUN apk update && apk add --update tzdata libmagic alpine-sdk libffi libffi-dev musl-dev openssl-dev + COPY --from=builder /install /usr/local COPY requirements.txt /requirements.txt RUN pip3 install -r /requirements.txt diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index 7b90b818..022e0e5c 100644 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -2113,9 +2113,12 @@ class AppBase: print("Deleting %s because key = value" % key) deletekeys.append(key) continue - + for deletekey in deletekeys: - del newvalue[deletekey] + try: + del newvalue[deletekey] + except: + pass #print("Post delete: %s" % newvalue) for key, value in newvalue.items(): @@ -2128,8 +2131,16 @@ class AppBase: except json.decoder.JSONDecodeError as e: print("Inner overwrite issue: %s" % e) continue + except: + print("General error in newvalue items loop") + continue - action["parameters"][counter]["value"] = json.dumps(newvalue) + try: + action["parameters"][counter]["value"] = json.dumps(newvalue) + except json.decoder.JSONDecodeError as e: + print("[WARNING] JsonDecodeError: %s" % e) + action["parameters"][counter]["value"] = newvalue + except json.decoder.JSONDecodeError as e: print("Failed JSON replacement for OpenAPI keys (2) {e}") @@ -2137,6 +2148,7 @@ class AppBase: break #print(action["parameters"]) + print("Pre parameters") for parameter in newparams: action["parameters"].append(parameter) @@ -2150,6 +2162,7 @@ class AppBase: # Multi_parameter has the data for each. variable minlength = 0 + print("Pre-loading parameters") multi_parameters = json.loads(json.dumps(params)) multiexecution = False multi_execution_lists = [] diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index c0296204..630b5b0c 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -2,7 +2,7 @@ module shuffle 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 diff --git a/functions/onprem/worker/build.sh b/functions/onprem/worker/build.sh index 2dbd7f04..5119977c 100644 --- a/functions/onprem/worker/build.sh +++ b/functions/onprem/worker/build.sh @@ -1,5 +1,5 @@ NAME=shuffle-worker -VERSION=0.9.14 +VERSION=0.9.15 echo "Running docker build with $NAME:$VERSION" #CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o worker.bin . From b06baaf878529048f3891fd19ba9ed620bc9a39c Mon Sep 17 00:00:00 2001 From: frikky Date: Wed, 8 Sep 2021 01:36:51 +0200 Subject: [PATCH 072/117] Fixed Liquid for conditions --- backend/app_sdk/app_base.py | 20 ++++++++++++-------- backend/app_sdk/build.sh | 2 +- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index 022e0e5c..ee1f4cd4 100644 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -1201,7 +1201,12 @@ class AppBase: # = # Authorization=Bearer authkey # ^ Double space. - def parse_wrapper_start(data): + def parse_wrapper_start(data, self): + try: + data = parse_liquid(data, self) + except: + pass + if "(" not in data or ")" not in data: return data @@ -1917,19 +1922,18 @@ class AppBase: sourcevalue = condition["source"]["value"] check, sourcevalue, is_loop = parse_params(action, fullexecution, condition["source"], self) if check: - return False, {"success": False, "reason": "Failed condition: %s %s %s because %s" % (sourcevalue, condition["condition"]["value"], destinationvalue, check)} - + return False, {"success": False, "reason": "Failed condition (1): %s %s %s because %s" % (sourcevalue, condition["condition"]["value"], destinationvalue, check)} #sourcevalue = sourcevalue.encode("utf-8") - sourcevalue = parse_wrapper_start(sourcevalue) + sourcevalue = parse_wrapper_start(sourcevalue, self) destinationvalue = condition["destination"]["value"] check, destinationvalue, is_loop = parse_params(action, fullexecution, condition["destination"], self) if check: - return False, {"success": False, "reason": "Failed condition: %s %s %s because %s" % (sourcevalue, condition["condition"]["value"], destinationvalue, check)} + return False, {"success": False, "reason": "Failed condition (2): %s %s %s because %s" % (sourcevalue, condition["condition"]["value"], destinationvalue, check)} #destinationvalue = destinationvalue.encode("utf-8") - destinationvalue = parse_wrapper_start(destinationvalue) + destinationvalue = parse_wrapper_start(destinationvalue, self) available_checks = [ "=", "equals", @@ -1966,7 +1970,7 @@ class AppBase: if not validation: self.logger.info("Failed condition check for %s %s %s." % (sourcevalue, condition["condition"]["value"], destinationvalue)) - return False, {"success": False, "reason": "Failed condition: %s %s %s" % (sourcevalue, condition["condition"]["value"], destinationvalue)} + return False, {"success": False, "reason": "Failed condition (3): %s %s %s" % (sourcevalue, condition["condition"]["value"], destinationvalue)} # Make a general parser here, at least to get param["name"] = param["value"] in maparameter[string]string @@ -2431,7 +2435,7 @@ class AppBase: # This part has fucked over so many random JSON usages because of weird paranthesis parsing - value = parse_wrapper_start(value) + value = parse_wrapper_start(value, self) print("Post return: %s" % value) #if parameter["id"] == "body_replacement": diff --git a/backend/app_sdk/build.sh b/backend/app_sdk/build.sh index 7dab91cf..a3f6d877 100644 --- a/backend/app_sdk/build.sh +++ b/backend/app_sdk/build.sh @@ -3,7 +3,7 @@ ### DEFAULT NAME=shuffle-app_sdk -VERSION=0.9.15 +VERSION=0.9.16 docker rmi docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION --force docker build . -f Dockerfile -t frikky/shuffle:app_sdk -t frikky/$NAME:$VERSION -t docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION -t ghcr.io/frikky/$NAME:$VERSION From f860545de41358f12e6e6c351cb7d5f238dd9076 Mon Sep 17 00:00:00 2001 From: frikky Date: Wed, 8 Sep 2021 20:05:26 +0200 Subject: [PATCH 073/117] Added a fix for vm.max_map_count to docker-compose, bypassing 262144 limit for test environments --- backend/app_sdk/requirements.txt | 1 + docker-compose.yml | 1 + frontend/src/views/AngularWorkflow.jsx | 2 +- functions/extensions/elasticsearch/run.sh | 19 + functions/onprem/worker/build.sh | 2 +- functions/onprem/worker/go.sum | 697 ++++++++++++++++++++++ functions/onprem/worker/worker.go | 20 +- 7 files changed, 733 insertions(+), 9 deletions(-) create mode 100644 functions/extensions/elasticsearch/run.sh create mode 100644 functions/onprem/worker/go.sum diff --git a/backend/app_sdk/requirements.txt b/backend/app_sdk/requirements.txt index 98bbcfcf..691f1ee6 100644 --- a/backend/app_sdk/requirements.txt +++ b/backend/app_sdk/requirements.txt @@ -1,3 +1,4 @@ urllib3==1.25.9 requests==2.25.1 +MarkupSafe==2.0.1 liquidpy==0.7.0 diff --git a/docker-compose.yml b/docker-compose.yml index cd70fa4a..42189543 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -78,6 +78,7 @@ services: - node.name=shuffle-opensearch - discovery.seed_hosts=shuffle-opensearch - cluster.initial_master_nodes=shuffle-opensearch + - node.store.allow_mmap=false ulimits: memlock: soft: -1 diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index c323c006..094ab423 100644 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -7167,7 +7167,7 @@ const AngularWorkflow = (props) => {
- Interval (seconds) + Interval (UTC)
Date: Tue, 14 Sep 2021 10:27:50 +0200 Subject: [PATCH 074/117] Added example modal to popup windows --- .github/install-guide.md | 24 -------------------- backend/go-app/main.go | 1 + backend/go-app/walkoff.go | 22 ++++++++++--------- docker-compose.yml | 2 +- frontend/src/components/ParsedAction.jsx | 28 +++++++++++++++++++++++- functions/onprem/worker/go.mod | 2 +- 6 files changed, 42 insertions(+), 37 deletions(-) diff --git a/.github/install-guide.md b/.github/install-guide.md index a9f6d4cb..8b41eb15 100644 --- a/.github/install-guide.md +++ b/.github/install-guide.md @@ -18,13 +18,6 @@ cd Shuffle 3. Fix prerequisites for the Opensearch database (Elasticsearch): ``` sudo chown 1000:1000 -R shuffle-database # Required for Opensearch -sudo sysctl -w vm.max_map_count=262144 # https://www.elastic.co/guide/en/elasticsearch/reference/current/vm-max-map-count.html - -# To make the changes permanent, do: -# 1. Open the file /etc/sysctl.conf -# 2. Go to the bottom of the file -# 3. Add this line: -vm.max_map_count=262144 ``` 4. Run docker-compose. @@ -49,23 +42,6 @@ This step is for setting up with Docker on windows from scratch. OUTER_HOSTNAME=YOUR.IP.HERE ``` -5. Configure max memory (WSL) by opening a new CMD/Powershell window. Run each command one-by-one, or else installation might not work. (Required for fixing consistent restart of Elasticsearch issue in Windows Operation System). -``` -wsl -d docker-desktop -``` -``` -sysctl -w vm.max_map_count=262144 -``` -``` -echo "vm.max_map_count = 262144" > /etc/sysctl.d/99-docker-desktop.conf -``` -``` -echo -e "\nvm.max_map_count = 262144\n" >> /etc/sysctl.d/00-alpine.conf -``` -``` -# https://stackoverflow.com/questions/42111566/elasticsearch-in-windows-docker-image-vm-max-map-count -``` - 6. Run docker-compose ``` docker compose up -d diff --git a/backend/go-app/main.go b/backend/go-app/main.go index a3185ac0..87e0eb9c 100644 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -4978,6 +4978,7 @@ func handleStopCloudSync(syncUrl string, org shuffle.Org) (*shuffle.Org, error) return &org, err } + // FIXME: If it says bad API-key, stop cloud sync for the Org if newresp.StatusCode != 200 { return &org, errors.New(fmt.Sprintf("Got status code %d when disabling org remotely. Expected 200. Contact support.", newresp.StatusCode)) } diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index 0a01ac6a..8ca30639 100644 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -901,7 +901,7 @@ func runWorkflowExecutionTransaction(ctx context.Context, attempts int64, workfl //log.Printf("BASE LENGTH: %d", len(workflowExecution.Results)) workflowExecution, dbSave, err := shuffle.ParsedExecutionResult(ctx, *workflowExecution, actionResult, false) if err != nil { - log.Printf("[ERROR] Failed execution of parsedexecution: %s", err) + log.Printf("[ERROR] Failed running of parsedexecution: %s", err) resp.WriteHeader(401) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "Failed getting execution"}`))) return @@ -3865,16 +3865,18 @@ func IterateAppGithubFolders(ctx context.Context, fs billy.Filesystem, dir []os. } } - log.Printf("[INFO] Starting build of %d skipped docker images", len(buildLaterList)) - for _, item := range buildLaterList { - err = buildImageMemory(fs, item.Tags, item.Extra, true) - if err != nil { - log.Printf("[INFO] Failed image build memory: %s", err) - } else { - if len(item.Tags) > 0 { - log.Printf("[INFO] Successfully built image %s", item.Tags[0]) + if len(buildLaterList) > 0 { + log.Printf("[INFO] Starting build of %d skipped docker images", len(buildLaterList)) + for _, item := range buildLaterList { + err = buildImageMemory(fs, item.Tags, item.Extra, true) + if err != nil { + log.Printf("[INFO] Failed image build memory: %s", err) } else { - log.Printf("[INFO] Successfully built Docker image") + if len(item.Tags) > 0 { + log.Printf("[INFO] Successfully built image %s", item.Tags[0]) + } else { + log.Printf("[INFO] Successfully built Docker image") + } } } } diff --git a/docker-compose.yml b/docker-compose.yml index 42189543..6e1cc561 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -62,7 +62,7 @@ services: - SHUFFLE_PASS_WORKER_PROXY=${SHUFFLE_PASS_WORKER_PROXY} - SHUFFLE_PASS_APP_PROXY=${SHUFFLE_PASS_APP_PROXY} - SHUFFLE_ORBORUS_EXECUTION_TIMEOUT=600 - - SHUFFLE_ORBORUS_EXECUTION_CONCURRENCY=50 + - SHUFFLE_ORBORUS_EXECUTION_CONCURRENCY=15 - CLEANUP=${SHUFFLE_CONTAINER_AUTO_CLEANUP} restart: unless-stopped opensearch: diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index 7e6fd11e..ec212870 100644 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -54,6 +54,9 @@ const ParsedAction = (props) => { const theme = useTheme(); const classes = useStyles() + + const [expansionModalOpen, setExpansionModalOpen] = React.useState(false); + const keywords = ["len(", "lower(", "upper(", "trim(", "split(", "length(", "number(", "parse(", "join("] const getParents = (action) => { if (cy === undefined) { @@ -869,7 +872,8 @@ const ParsedAction = (props) => { fullWidth multiline={multiline} onClick={() => { - //console.log("Clicked field: ", clickedFieldId) + console.log("Clicked field: ", clickedFieldId) + setExpansionModalOpen(false) if (setScrollConfig !== undefined && scrollConfig !== null && scrollConfig !== undefined && scrollConfig.selected !== clickedFieldId) { scrollConfig.selected = clickedFieldId setScrollConfig(scrollConfig) @@ -1420,6 +1424,27 @@ const ParsedAction = (props) => { return null } + const expansionModal = + { + setExpansionModalOpen(false) + }} + PaperProps={{ + style: { + backgroundColor: theme.palette.surfaceColor, + color: "white", + minWidth: 600, + padding: 50, + }, + }} + > + Workflow Variable + + Hello + + + //const CustomPopper = function (props) { // const classes = useStyles() // return @@ -1428,6 +1453,7 @@ const ParsedAction = (props) => { const baselabel = selectedAction.label return (
+ {expansionModal} {hideExtraTypes === true ? null :
diff --git a/functions/onprem/worker/go.mod b/functions/onprem/worker/go.mod index 39e8be21..4ca87b17 100644 --- a/functions/onprem/worker/go.mod +++ b/functions/onprem/worker/go.mod @@ -12,7 +12,7 @@ require ( github.com/docker/docker v20.10.5+incompatible github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.4.0 // indirect - github.com/frikky/shuffle-shared v0.0.88 + github.com/frikky/shuffle-shared v0.0.95 github.com/fsouza/go-dockerclient v1.7.2 github.com/go-git/go-billy/v5 v5.3.1 // indirect github.com/go-git/go-git/v5 v5.4.2 // indirect From 5927d2e1a98e027ce3712e15358be05345f1d531 Mon Sep 17 00:00:00 2001 From: frikky Date: Tue, 14 Sep 2021 12:59:31 +0200 Subject: [PATCH 075/117] #516: Better description for local development --- .github/install-guide.md | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/install-guide.md b/.github/install-guide.md index 8b41eb15..fa3d1030 100644 --- a/.github/install-guide.md +++ b/.github/install-guide.md @@ -84,6 +84,7 @@ npm start http://localhost:5001 - REST API - requires [>=go1.13](https://golang.org/dl/) ```bash export SHUFFLE_OPENSEARCH_URL="http://localhost:9200" +export SHUFFLE_ELASTIC=true cd backend/go-app go run *.go ``` From edd295cfc2568699aea9d72e5794cbdfd4fed161 Mon Sep 17 00:00:00 2001 From: frikky Date: Wed, 15 Sep 2021 00:45:24 +0200 Subject: [PATCH 076/117] #127: Added a category filter system to creator. Will extend to others as well --- frontend/src/views/AppCreator.jsx | 94 ++++++++++++++++++++++++++++--- frontend/src/views/LoginPage.jsx | 28 ++++++--- 2 files changed, 106 insertions(+), 16 deletions(-) diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx index 6619f95f..3b0a90ad 100644 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -66,6 +66,7 @@ const appIconStyle = { marginLeft: "5px", } + const useStyles = makeStyles({ notchedOutline: { borderColor: "#f85a3e !important" @@ -222,6 +223,8 @@ const AppCreator = (props) => { const [parameterLocation, setParameterLocation] = useState(apikeySelection.length > 0 ? apikeySelection[0] : ""); const [refreshUrl, setRefreshUrl] = useState(""); const [oauth2Scopes, setOauth2Scopes] = useState(["google.com"]); + const [projectCategories, setProjectCategories] = useState([]); + const [selectedCategory, setSelectedCategory] = useState(""); const [urlPath, setUrlPath] = useState(""); //const [urlPathQueries, setUrlPathQueries] = useState([{"name": "test", "required": false}]); @@ -231,6 +234,7 @@ const AppCreator = (props) => { const [firstrequest, setFirstrequest] = React.useState(true) const [basedata, setBasedata] = React.useState({}) const [actions, setActions] = useState([]) + const [filteredActions, setFilteredActions] = useState([]) const [errorCode, setErrorCode] = useState("") const [appBuilding, setAppBuilding] = useState(false) const [extraBodyFields, setExtraBodyFields] = useState([]) @@ -288,6 +292,7 @@ const AppCreator = (props) => { "method": actionNonBodyRequest[0], }); + const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" useEffect(() => { @@ -391,7 +396,7 @@ const AppCreator = (props) => { const handleGetRef = (parameter, data) => { try { if (parameter === null || parameter["$ref"] === undefined) { - console.log("$ref not found in getref: ") + //console.log("$ref not found in getref: ") return parameter } } catch (e) { @@ -527,6 +532,7 @@ const AppCreator = (props) => { var newActions = [] var wordlist = {} + var all_categories = [] if (data.paths !== null && data.paths !== undefined) { for (let [path, pathvalue] of Object.entries(data.paths)) { for (let [method, methodvalue] of Object.entries(pathvalue)) { @@ -560,6 +566,24 @@ const AppCreator = (props) => { "example_response": "", } + // Finding category + if (path.includes("/")) { + const pathsplit = path.split("/") + var categoryindex = -1 + // Stupid way of finding a category/grouping + for (var key in pathsplit) { + if (pathsplit[key].length > 0 && pathsplit[key] !== "v1" && pathsplit[key] !== "v2" && pathsplit[key] !== "api" && pathsplit[key] !== "1.0" && pathsplit[key] !== "apis") { + newaction["category"] = pathsplit[key] + if (!all_categories.includes(pathsplit[key])) { + all_categories.push(pathsplit[key]) + } + break + } + } + } + + //console.log("Category: + //console.log("Schema is application/json: ", methodvalue) //console.log("DATA", data) if (methodvalue["requestBody"] !== undefined) { @@ -964,12 +988,15 @@ const AppCreator = (props) => { setActionAmount(newActions.length) } - if (newActions.length > 1000) { + //const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" + if (newActions.length > 1000 && isCloud) { alert.error("Cut down actions from "+newActions.length+" to 999 because of limit") newActions = newActions.slice(0,999) } + setProjectCategories(all_categories) setActions(newActions) + setFilteredActions(newActions) setIsAppLoaded(true) } @@ -1515,6 +1542,7 @@ const AppCreator = (props) => { } setActions(actions) + setFilteredActions(actions) setUpdate(Math.random()) } @@ -1534,6 +1562,7 @@ const AppCreator = (props) => { }) setActions(actions) + setFilteredActions(actions) setActionAmount(actionAmount-1) setUpdate(Math.random()) } @@ -1827,7 +1856,7 @@ const AppCreator = (props) => { null :
- {actions.slice(0,actionAmount).map((data, index) => { + {filteredActions.slice(0,actionAmount).map((data, index) => { var error = data.errors.length > 0 ? @@ -2015,6 +2044,7 @@ const AppCreator = (props) => { } setActions(actions) + setFilteredActions(actions) } const getActionErrors = () => { @@ -2943,12 +2973,12 @@ const AppCreator = (props) => { const actionView =
- {actionAmount > 0 && actionAmount < actions.length ? + {actionAmount > 0 && actionAmount < filteredActions.length ? : null}
-

Actions {actionAmount > 0 ? ({actionAmount} / {actions.length}) : null}

+

Actions {actionAmount > 0 ? ({actionAmount} / {filteredActions.length}) : null}

+ + {projectCategories !== undefined && projectCategories !== null && projectCategories.length > 1 ? +
+ {projectCategories.map((tag, index) => { + const newname = tag.charAt(0).toUpperCase() + tag.slice(1) + return ( + { + if (selectedCategory === tag) { + setFilteredActions(actions) + setSelectedCategory("") + setActionAmount(50) + return + } + + const foundActions = actions.filter(data => data.category === tag) + setFilteredActions(foundActions) + setSelectedCategory(tag) + if (actionAmount > foundActions.length) { + setActionAmount(foundActions.length) + } + + //{filteredActions.slice(0,actionAmount).map((data, index) => { + + //console.log("Found: ", foundActions) + //{filteredActions.slice(0,actionAmount).map((data, index) => { + //{actions.slice(0,actionAmount).map((data, index) => { + }} + variant={selectedCategory === tag ? "contained " : "outlined"} + color={selectedCategory === tag ? "primary" : "secondary"} + /> + ) + })} +
+ : null} Actions are the tasks performed by an app - usually single URL paths for REST API's.
{loopActions} diff --git a/frontend/src/views/LoginPage.jsx b/frontend/src/views/LoginPage.jsx index 628073ba..542e331d 100644 --- a/frontend/src/views/LoginPage.jsx +++ b/frontend/src/views/LoginPage.jsx @@ -59,15 +59,19 @@ const LoginDialog = props => { }) .then(response => response.json().then(responseJson => { - if (loginViewLoading) { - setLoginViewLoading(false) - checkLogin() - stop() - } - if (responseJson["success"] === false) { setLoginInfo(responseJson["reason"]) } else { + if (loginViewLoading) { + setLoginViewLoading(false) + checkLogin() + stop() + + if (responseJson.reason !== undefined && responseJson.reason !== null) { + setLoginInfo(responseJson.reason) + } + } + if (responseJson.reason === "stay") { window.location.pathname = "/adminsetup" } @@ -199,8 +203,16 @@ const LoginDialog = props => { {loginViewLoading ?
- Waiting for Shuffle the database to become available. This may take up to a minute. + Waiting for the Shuffle database to become available. This may take up to a minute. + + {loginInfo === undefined || loginInfo === null || loginInfo.length === 0 ? + null + : +
+ Response: {loginInfo} +
+ } @@ -215,7 +227,7 @@ const LoginDialog = props => { marginTop: 15, }}> - Are you sure Shuffle is installed correctly + Are you sure Shuffle is installed correctly? 1. Make sure shuffle-database folder has correct access:

From e28b5d1e9ba9af0a4bfca1092e9f03d944aa2977 Mon Sep 17 00:00:00 2001 From: frikky Date: Wed, 15 Sep 2021 20:24:59 +0200 Subject: [PATCH 077/117] Fixed startnode button showing for startnode --- backend/go-app/walkoff.go | 19 +++++ frontend/src/components/ParsedAction.jsx | 4 +- frontend/src/views/AngularWorkflow.jsx | 4 + functions/extensions/kafka/docker-compose.yml | 30 +++----- functions/extensions/kafka/kafka_local.py | 75 +++++++++++++------ 5 files changed, 88 insertions(+), 44 deletions(-) diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index 8ca30639..0eef90ac 100644 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -3667,6 +3667,25 @@ func IterateAppGithubFolders(ctx context.Context, fs billy.Filesystem, dir []os. continue } + readmeNames := []string{"readme", "readme.md"} + for _, readmeName := range readmeNames { + readmePath := fmt.Sprintf("%s/%s", extra, readmeName) + readmeInfo, err := fs.Open(readmePath) + if err != nil { + log.Printf("[WARNING] Failed to read README path %s", readmePath) + continue + } + + dockerfileData, err := ioutil.ReadAll(dockerfile) + if err != nil { + log.Printf("[WARNING] Failed to read readme file at %s", readmePath) + continue + } else { + log.Printf("[INFO] Found file with name %s", readmeName) + break + } + } + combined := []byte{} combined = append(combined, appfileData...) combined = append(combined, appPythonData...) diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index ec212870..31f0019c 100644 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -1500,7 +1500,7 @@ const ParsedAction = (props) => {
- {selectedAction.id === workflow.start ? null : + {/*selectedAction.id === workflow.start ? null : - } + */} {selectedApp.versions !== null && selectedApp.versions !== undefined && selectedApp.versions.length > 1 ? { }} fullWidth onChange={(e) => { - //console.log("CHOSE AN AUTHENTICATION OPTION: ", e.target.value) - selectedAction.selectedAuthentication = e.target.value - selectedAction.authentication_id = e.target.value.id - setSelectedAction(selectedAction) - setUpdate(Math.random()) + if (e.target.value === "No selection") { + selectedAction.selectedAuthentication = {} + selectedAction.authentication_id = "" + + for (var key in selectedAction.parameters) { + console.log(selectedAction.parameters[key]) + if (selectedAction.parameters[key].configuration) { + selectedAction.parameters[key].value = "" + } + } + setSelectedAction(selectedAction) + setUpdate(Math.random()) + } else { + //console.log("CHOSE AN AUTHENTICATION OPTION: ", e.target.value) + selectedAction.selectedAuthentication = e.target.value + selectedAction.authentication_id = e.target.value.id + setSelectedAction(selectedAction) + setUpdate(Math.random()) + } }} style={{backgroundColor: theme.palette.inputColor, color: "white", height: 50, maxWidth: rightsidebarStyle.maxWidth-80, borderRadius: theme.palette.borderRadius,}} > + + + No selection + {selectedAction.authentication.map(data => { //console.log("AUTH DATA: ", data) return( @@ -1711,7 +1729,7 @@ const ParsedAction = (props) => { No selection - + {workflow.execution_variables.map(data => ( {data.name} From dae5612722865d885c02f43445c9235ce24da986 Mon Sep 17 00:00:00 2001 From: frikky Date: Fri, 17 Sep 2021 23:21:39 +0200 Subject: [PATCH 082/117] Re-added basic list-view for workflows with datagrid --- frontend/src/theme.js | 1 + frontend/src/views/Workflows.jsx | 155 ++++++++++++++++++++++--------- 2 files changed, 113 insertions(+), 43 deletions(-) diff --git a/frontend/src/theme.js b/frontend/src/theme.js index 8a84975f..92371aad 100644 --- a/frontend/src/theme.js +++ b/frontend/src/theme.js @@ -12,6 +12,7 @@ const theme = createMuiTheme({ text: { secondary: "rgba(255,255,255,0.7)", }, + type: 'dark', surfaceColor: "#27292d", inputColor: "#383B40", borderRadius: 5, diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index fcdd7121..63c1df24 100644 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -4,7 +4,7 @@ import { makeStyles } from '@material-ui/core/styles'; import { useTheme } from '@material-ui/core/styles'; import {Avatar, Grid, Paper, Tooltip, Divider, Button, TextField, FormControl, IconButton, Menu, MenuItem, FormControlLabel, Chip, Switch, Typography, Zoom, CircularProgress, Dialog, DialogTitle, DialogActions, DialogContent} from '@material-ui/core'; -import {Close as CloseIcon, Compare as CompareIcon, Maximize as MaximizeIcon, Minimize as MinimizeIcon, AddCircle as AddCircleIcon, Toc as TocIcon, Send as SendIcon, Search as SearchIcon, FileCopy as FileCopyIcon, Delete as DeleteIcon, BubbleChart as BubbleChartIcon, Restore as RestoreIcon, Cached as CachedIcon, GetApp as GetAppIcon, Apps as AppsIcon, Edit as EditIcon, MoreVert as MoreVertIcon, PlayArrow as PlayArrowIcon, Add as AddIcon, Publish as PublishIcon, CloudUpload as CloudUploadIcon, CloudDownload as CloudDownloadIcon} from '@material-ui/icons'; +import {GridOn as GridOnIcon, List as ListIcon, Close as CloseIcon, Compare as CompareIcon, Maximize as MaximizeIcon, Minimize as MinimizeIcon, AddCircle as AddCircleIcon, Toc as TocIcon, Send as SendIcon, Search as SearchIcon, FileCopy as FileCopyIcon, Delete as DeleteIcon, BubbleChart as BubbleChartIcon, Restore as RestoreIcon, Cached as CachedIcon, GetApp as GetAppIcon, Apps as AppsIcon, Edit as EditIcon, MoreVert as MoreVertIcon, PlayArrow as PlayArrowIcon, Add as AddIcon, Publish as PublishIcon, CloudUpload as CloudUploadIcon, CloudDownload as CloudDownloadIcon} from '@material-ui/icons'; //import {Search as SearchIcon, ArrowUpward as ArrowUpwardIcon, Visibility as VisibilityIcon, Done as DoneIcon, Close as CloseIcon, Error as ErrorIcon, FindReplace as FindreplaceIcon, ArrowLeft as ArrowLeftIcon, Cached as CachedIcon, DirectionsRun as DirectionsRunIcon, Add as AddIcon, Polymer as PolymerIcon, FormatListNumbered as FormatListNumberedIcon, Create as CreateIcon, PlayArrow as PlayArrowIcon, AspectRatio as AspectRatioIcon, MoreVert as MoreVertIcon, Apps as AppsIcon, Schedule as ScheduleIcon, FavoriteBorder as FavoriteBorderIcon, Pause as PauseIcon, Delete as DeleteIcon, AddCircleOutline as AddCircleOutlineIcon, Save as SaveIcon, KeyboardArrowLeft as KeyboardArrowLeftIcon, KeyboardArrowRight as KeyboardArrowRightIcon, ArrowBack as ArrowBackIcon, Settings as SettingsIcon, LockOpen as LockOpenIcon, ExpandMore as ExpandMoreIcon, VpnKey as VpnKeyIcon} from '@material-ui/icons'; //https://next.material-ui.com/components/material-icons/ @@ -47,13 +47,13 @@ const flexBoxStyle = { } const useStyles = makeStyles((theme) => ({ - root: { + datagrid: { border: 0, '& .MuiDataGrid-columnsContainer': { - backgroundColor: theme.palette.type === 'light' ? '#fafafa' : '#1d1d1d', + backgroundColor: theme.palette.type === 'light' ? '#fafafa' : theme.palette.inputColor, }, '& .MuiDataGrid-iconSeparator': { - display: 'none', + display: 'none', }, '& .MuiDataGrid-colCell, .MuiDataGrid-cell': { borderRight: `1px solid ${ @@ -330,10 +330,10 @@ const Workflows = (props) => { const { globalUrl, isLoggedIn, isLoaded, removeCookie, cookies, userdata} = props; document.title = "Shuffle - Workflows" const theme = useTheme(); - const referenceUrl = globalUrl+"/api/v1/hooks/" - const alert = useAlert() - const classes = useStyles(); + const classes = useStyles(theme); + + const referenceUrl = globalUrl+"/api/v1/hooks/" var upload = "" const [file, setFile] = React.useState(""); @@ -1659,48 +1659,99 @@ const Workflows = (props) => { return [triggers, schedules, webhooks, subflows] } - const WorkflowGridView = () => { + const WorkflowListView = () => { let workflowData = ""; if (workflows.length > 0) { const columns = [ - { field: 'title', headerName: 'Title', width: 330, }, - { field: 'actions', headerName: 'Actions', width: 200, sortable: false, + { field: 'title', headerName: 'Title', width: 330, renderCell: (params) => { + const data = params.row.record + + return ( + + + + {data.name} + + + + ) + }}, + {/* field: 'category', headerName: 'category', width: 140, renderCell: (params) => { + const data = params.row.record + const category = data.category === undefined ? "not defined" : data.category + return ( + + {category} + + ) + } */}, + { field: 'actions', headerName: 'Options', width: 200, sortable: false, disableClickEventBubbling: true, renderCell: (params) => { const data = params.row.record; + const actions = data.actions !== null ? data.actions.length : 0 let [triggers, schedules, webhooks, subflows] = getWorkflowMeta(data); return ( - - - - - - - - executeWorkflow(data.id)} /> - - - - - {webhooks > 0 ? - - +
+ + + + + {actions} + + - : null} - {schedules > 0 ? - - + + + + + {triggers} + + - : null} + + { + if (subflows === 0) { + alert.info("No subflows for "+data.name) + return + } + + var newWorkflows = [data] + for (var key in data.triggers) { + const trigger = data.triggers[key] + if (trigger.app_name !== "Shuffle Workflow") { + continue + } + + if (trigger.parameters !== undefined && trigger.parameters !== null && trigger.parameters.length > 0 && trigger.parameters[0].name === "workflow") { + const newWorkflow = workflows.find(item => item.id === trigger.parameters[0].value) + if (newWorkflow !== null && newWorkflow !== undefined) { + newWorkflows.push(newWorkflow) + continue + } + } + } + + setFilters(["Subflows of "+data.name]) + setFilteredWorkflows(newWorkflows) + }}> + + + + + {subflows} + + + + +
) } }, - { field: 'tags', headerName: 'Tags', maxHeight: 15, width: 390, sortable: false, + {/* field: 'tags', headerName: 'Tags', maxHeight: 15, width: 390, sortable: false, disableClickEventBubbling: true, renderCell: (params) => { const data = params.row.record; @@ -1726,7 +1777,7 @@ const Workflows = (props) => { ) } - }, + */} ]; let rows = []; rows = workflows.map((data, index) => { @@ -1739,9 +1790,19 @@ const Workflows = (props) => { return obj; }); workflowData = - + } return (
@@ -1905,6 +1966,16 @@ const Workflows = (props) => { : null*/} + {view === "list" && ( + + + + )} + {view === "grid" && ( + + + + )} {importLoading ?
- {view === "grid" && ( + {view === "grid" ? {filteredWorkflows.map((data, index) => { @@ -2062,11 +2133,9 @@ const Workflows = (props) => { ) })} - )} - - {/*view === "list" && ( - - )*/} + : + + }
From 96cf883f08dfa169872e8fc91c6b4fcc635ddede Mon Sep 17 00:00:00 2001 From: frikky Date: Sat, 18 Sep 2021 02:47:29 +0200 Subject: [PATCH 083/117] #176: Started building out SAML/SSO authentication example with Okta --- backend/go-app/go.mod | 5 ++++- backend/go-app/main.go | 1 + frontend/src/App.jsx | 2 ++ frontend/src/components/Header.js | 4 ++++ frontend/src/views/LoginPage.jsx | 13 ++++++++++++- 5 files changed, 23 insertions(+), 2 deletions(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 2fa79b57..2a067a08 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -2,7 +2,7 @@ module shuffle 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 @@ -12,6 +12,7 @@ require ( cloud.google.com/go/pubsub v1.3.1 cloud.google.com/go/storage v1.12.0 github.com/Masterminds/semver v1.5.0 // indirect + github.com/RobotsAndPencils/go-saml v0.0.0-20170520135329-fb13cb52a46b // indirect github.com/algolia/algoliasearch-client-go/v3 v3.18.1 // indirect github.com/basgys/goxml2json v1.1.0 github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 // indirect @@ -31,6 +32,8 @@ require ( github.com/gorilla/handlers v1.4.2 // indirect github.com/gorilla/mux v1.8.0 github.com/h2non/filetype v1.0.12 + github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect + github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect github.com/patrickmn/go-cache v2.1.0+incompatible github.com/satori/go.uuid v1.2.0 go4.org v0.0.0-20201209231011-d4a079459e60 // indirect diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 87e0eb9c..9423eadd 100644 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -5866,6 +5866,7 @@ func initHandlers() { // Docker orborus specific - downloads an image r.HandleFunc("/api/v1/get_docker_image", getDockerImage).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/migrate_database", migrateDatabase).Methods("POST", "OPTIONS") + r.HandleFunc("/api/v1/login_sso", shuffle.HandleSSO).Methods("GET", "POST", "OPTIONS") // Important for email, IDS etc. Create this by: // PS: For cloud, this has to use cloud storage. diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 3b48175a..488d766e 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -23,6 +23,7 @@ import Admin from "./views/Admin"; import Docs from "./views/Docs"; import Introduction from "./views/Introduction"; import SetAuthentication from "./views/SetAuthentication"; +import SetAuthenticationSSO from "./views/SetAuthenticationSSO"; import LandingPageNew from "./views/LandingpageNew"; import LoginPage from "./views/LoginPage"; @@ -127,6 +128,7 @@ const App = (message, props) => { } /> } /> } /> + } /> } />
diff --git a/frontend/src/components/Header.js b/frontend/src/components/Header.js index f62ec077..af9d272d 100644 --- a/frontend/src/components/Header.js +++ b/frontend/src/components/Header.js @@ -289,6 +289,10 @@ const Header = props => { }} > {userdata.orgs.map((data, index) => { + if (data.name === undefined || data.name === null || data.name.length === 0) { + return null + } + const imagesize = 22 const imageStyle = {width: imagesize, height: imagesize, pointerEvents: "none", marginRight: 10, marginLeft: data.creator_org !== undefined && data.creator_org.length > 0 ? 20 : 0} const image = data.image === "" ? diff --git a/frontend/src/views/LoginPage.jsx b/frontend/src/views/LoginPage.jsx index 542e331d..1b621751 100644 --- a/frontend/src/views/LoginPage.jsx +++ b/frontend/src/views/LoginPage.jsx @@ -299,13 +299,24 @@ const LoginDialog = props => { />
-
{loginInfo}
+ + Or + +
+ +
} From e0aa368b24eaeb3f8550969475a31c46d4f0155e Mon Sep 17 00:00:00 2001 From: frikky Date: Sat, 18 Sep 2021 17:44:59 +0200 Subject: [PATCH 084/117] Added frontend components to control certificates and the like for SSO --- backend/go-app/main.go | 7 +- frontend/src/components/OrgHeader.js | 100 +++++++++++++++++++++++---- frontend/src/views/LoginPage.jsx | 31 ++++++--- 3 files changed, 111 insertions(+), 27 deletions(-) diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 9423eadd..aac348fe 100644 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -857,6 +857,7 @@ func handleInfo(resp http.ResponseWriter, request *http.Request) { userInfo, err := shuffle.HandleApiAuthentication(resp, request) if err != nil { log.Printf("[WARNING] Api authentication failed in handleInfo: %s", err) + resp.WriteHeader(401) resp.Write([]byte(`{"success": false}`)) return @@ -1177,8 +1178,10 @@ func checkAdminLogin(resp http.ResponseWriter, request *http.Request) { return } + //ssoUrl = org.SSOConfig.SOSOEntrypoint + redirectUri := shuffle.SSOUrl resp.WriteHeader(200) - resp.Write([]byte(fmt.Sprintf(`{"success": true, "reason": "redirect"}`))) + resp.Write([]byte(fmt.Sprintf(`{"success": true, "reason": "redirect", "sso_url": "%s"}`, redirectUri))) } func handleLogin(resp http.ResponseWriter, request *http.Request) { @@ -5866,7 +5869,7 @@ func initHandlers() { // Docker orborus specific - downloads an image r.HandleFunc("/api/v1/get_docker_image", getDockerImage).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/migrate_database", migrateDatabase).Methods("POST", "OPTIONS") - r.HandleFunc("/api/v1/login_sso", shuffle.HandleSSO).Methods("GET", "POST", "OPTIONS") + r.HandleFunc("/api/v1/login_sso", shuffle.HandleSSO).Methods("POST", "OPTIONS") // Important for email, IDS etc. Create this by: // PS: For cloud, this has to use cloud storage. diff --git a/frontend/src/components/OrgHeader.js b/frontend/src/components/OrgHeader.js index 5d86745a..896517dc 100644 --- a/frontend/src/components/OrgHeader.js +++ b/frontend/src/components/OrgHeader.js @@ -36,6 +36,8 @@ const OrgHeader = (props) => { const [appDownloadBranch, setAppDownloadBranch] = React.useState(selectedOrganization.defaults === undefined ? defaultBranch : selectedOrganization.defaults.app_download_branch === undefined || selectedOrganization.defaults.app_download_branch.length === 0 ? defaultBranch : selectedOrganization.defaults.app_download_branch) const [workflowDownloadUrl, setWorkflowDownloadUrl] = React.useState(selectedOrganization.defaults === undefined ? "https://github.com/frikky/shuffle-apps" : selectedOrganization.defaults.workflow_download_repo === undefined || selectedOrganization.defaults.workflow_download_repo.length === 0 ? "https://github.com/frikky/shuffle-workflows" : selectedOrganization.defaults.workflow_download_repo) const [workflowDownloadBranch, setWorkflowDownloadBranch] = React.useState(selectedOrganization.defaults === undefined ? defaultBranch : selectedOrganization.defaults.workflow_download_branch === undefined || selectedOrganization.defaults.workflow_download_branch.length === 0 ? defaultBranch : selectedOrganization.defaults.workflow_download_branch) + const [ssoEntrypoint, setSsoEntrypoint] = React.useState(selectedOrganization.sso_config === undefined ? "" : selectedOrganization.sso_config.sso_entrypoint === undefined || selectedOrganization.sso_config.sso_entrypoint.length === 0 ? "" : selectedOrganization.sso_config.sso_entrypoint) + const [ssoCertificate, setSsoCertificate] = React.useState(selectedOrganization.sso_config === undefined ? "" : selectedOrganization.sso_config.sso_certificate === undefined || selectedOrganization.sso_config.sso_certificate.length === 0 ? "" : selectedOrganization.sso_config.sso_certificate) const [file, setFile] = React.useState("") const [fileBase64, setFileBase64] = React.useState(selectedOrganization.image) @@ -67,13 +69,14 @@ const OrgHeader = (props) => { } } - const handleEditOrg = (name, description, orgId, image, defaults) => { + const handleEditOrg = (name, description, orgId, image, defaults, sso_config) => { const data = { "name": name, "description": description, "org_id": orgId, "image": image, "defaults": defaults, + "sso_config": sso_config, } const url = globalUrl + `/api/v1/orgs/${selectedOrganization.id}`; @@ -111,19 +114,25 @@ const OrgHeader = (props) => { } const orgSaveButton = - + + + var imageData = file.length > 0 ? file : fileBase64 imageData = imageData === undefined || imageData.length === 0 ? defaultImage : imageData @@ -337,6 +346,69 @@ const OrgHeader = (props) => { /> + + + + SSO Entrypoint + + 0} + id="outlined-with-placeholder" + margin="normal" + variant="outlined" + placeholder="The entrypoint URL from your provider" + value={ssoEntrypoint} + onChange={e => { + setSsoEntrypoint(e.target.value) + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style:{ + color: "white", + }, + }} + /> + + + + + + SSO Certificate + + { + setSsoCertificate(e.target.value) + }} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style:{ + color: "white", + }, + }} + /> + + {/* {expanded ? diff --git a/frontend/src/views/LoginPage.jsx b/frontend/src/views/LoginPage.jsx index 1b621751..35ee4e90 100644 --- a/frontend/src/views/LoginPage.jsx +++ b/frontend/src/views/LoginPage.jsx @@ -34,6 +34,7 @@ const LoginDialog = props => { const [firstRequest, setFirstRequest] = useState(true); const [loginLoading, setLoginLoading] = useState(false); const [loginViewLoading, setLoginViewLoading] = useState(false); + const [ssoUrl, setSSOUrl] = useState("") // Used to swap from login to register. True = login, false = register @@ -62,6 +63,10 @@ const LoginDialog = props => { if (responseJson["success"] === false) { setLoginInfo(responseJson["reason"]) } else { + if (responseJson.sso_url !== undefined && responseJson.sso_url !== null) { + setSSOUrl(responseJson.sso_url) + } + if (loginViewLoading) { setLoginViewLoading(false) checkLogin() @@ -306,17 +311,21 @@ const LoginDialog = props => {
{loginInfo}
- - Or - -
- -
+ {ssoUrl !== undefined && ssoUrl !== null && ssoUrl.length > 0 ? +
+ + Or + +
+ +
+
+ : null} } From 2ea4e8d1c3d334f49a95bb5995e88e61b7c0b8ac Mon Sep 17 00:00:00 2001 From: frikky Date: Tue, 21 Sep 2021 01:06:27 +0200 Subject: [PATCH 085/117] Started K8s helm template creation --- .env | 1 + backend/go-app/go.mod | 4 +- docker-compose.yml | 4 +- frontend/src/views/Admin.jsx | 18 +- frontend/src/views/Workflows.jsx | 2 +- functions/extensions/k8s/shuffle/.helmignore | 23 ++ functions/extensions/k8s/shuffle/Chart.yaml | 24 ++ .../k8s/shuffle/templates/NOTES.txt | 22 ++ .../k8s/shuffle/templates/_helpers.tpl | 62 +++++ .../k8s/shuffle/templates/deployment.yaml | 253 ++++++++++++++++++ .../extensions/k8s/shuffle/templates/hpa.yaml | 28 ++ .../k8s/shuffle/templates/ingress.yaml | 61 +++++ .../shuffle/templates/persistent-volume.yaml | 24 ++ .../templates/persistentvolumeclaim.yaml | 17 ++ .../k8s/shuffle/templates/service.yaml | 52 ++++ .../k8s/shuffle/templates/serviceaccount.yaml | 12 + .../templates/tests/test-connection.yaml | 15 ++ functions/extensions/k8s/shuffle/values.yaml | 82 ++++++ 18 files changed, 694 insertions(+), 10 deletions(-) create mode 100644 functions/extensions/k8s/shuffle/.helmignore create mode 100644 functions/extensions/k8s/shuffle/Chart.yaml create mode 100644 functions/extensions/k8s/shuffle/templates/NOTES.txt create mode 100644 functions/extensions/k8s/shuffle/templates/_helpers.tpl create mode 100644 functions/extensions/k8s/shuffle/templates/deployment.yaml create mode 100644 functions/extensions/k8s/shuffle/templates/hpa.yaml create mode 100644 functions/extensions/k8s/shuffle/templates/ingress.yaml create mode 100644 functions/extensions/k8s/shuffle/templates/persistent-volume.yaml create mode 100644 functions/extensions/k8s/shuffle/templates/persistentvolumeclaim.yaml create mode 100644 functions/extensions/k8s/shuffle/templates/service.yaml create mode 100644 functions/extensions/k8s/shuffle/templates/serviceaccount.yaml create mode 100644 functions/extensions/k8s/shuffle/templates/tests/test-connection.yaml create mode 100644 functions/extensions/k8s/shuffle/values.yaml diff --git a/.env b/.env index 27018187..821a69cb 100644 --- a/.env +++ b/.env @@ -68,4 +68,5 @@ SHUFFLE_OPENSEARCH_CERTIFICATE_FILE= SHUFFLE_OPENSEARCH_APIKEY= SHUFFLE_OPENSEARCH_CLOUDID= SHUFFLE_OPENSEARCH_PROXY= +SHUFFLE_OPENSEARCH_INDEX_PREFIX= SHUFFLE_OPENSEARCH_SKIPSSL_VERIFY=true diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 2a067a08..c323832b 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -2,7 +2,7 @@ module shuffle 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 @@ -23,7 +23,7 @@ require ( github.com/docker/go-units v0.4.0 // indirect github.com/elastic/go-elasticsearch/v7 v7.13.1 // indirect github.com/frikky/kin-openapi v0.39.0 - github.com/frikky/shuffle-shared v0.0.97 + github.com/frikky/shuffle-shared v0.0.98 github.com/fsouza/go-dockerclient v1.7.2 github.com/ghodss/yaml v1.0.0 github.com/go-git/go-billy/v5 v5.0.0 diff --git a/docker-compose.yml b/docker-compose.yml index 6e1cc561..89a8c331 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '3' services: frontend: - #build: ./frontend + build: ./frontend image: ghcr.io/frikky/shuffle-frontend:nightly container_name: shuffle-frontend hostname: shuffle-frontend @@ -16,7 +16,7 @@ services: depends_on: - backend backend: - #build: ./backend + build: ./backend image: ghcr.io/frikky/shuffle-backend:nightly container_name: shuffle-backend hostname: ${BACKEND_HOSTNAME} diff --git a/frontend/src/views/Admin.jsx b/frontend/src/views/Admin.jsx index 7f703255..dae09bbb 100644 --- a/frontend/src/views/Admin.jsx +++ b/frontend/src/views/Admin.jsx @@ -2005,7 +2005,11 @@ const Admin = (props) => { /> + { } style ={{ minWidth: 135, maxWidth: 135, marginRight: 15,}} /> - + + { diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index 63c1df24..9e2555cc 100644 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -99,7 +99,7 @@ export const GetIconInfo = (action) => { {"key": "execute", "values": ["execute", "run", "play", "raise",]}, {"key": "extract", "values": ["extract", "unpack", "decompress", "open"]}, {"key": "inflate", "values": ["inflate", "pack", "compress",]}, - {"key": "edit", "values": ["update", "create", "edit", "put", "patch", "change", "replace", "conver", "map", "format", "escape", "describe", ]}, + {"key": "edit", "values": ["modify", "update", "create", "edit", "put", "patch", "change", "replace", "conver", "map", "format", "escape", "describe", ]}, {"key": "compare", "values": ["compare", "convert", "to", "filter", "translate", "parse"]}, {"key": "close", "values": ["close", "stop", "cancel", "block", ]}, ] diff --git a/functions/extensions/k8s/shuffle/.helmignore b/functions/extensions/k8s/shuffle/.helmignore new file mode 100644 index 00000000..0e8a0eb3 --- /dev/null +++ b/functions/extensions/k8s/shuffle/.helmignore @@ -0,0 +1,23 @@ +# Patterns to ignore when building packages. +# This supports shell glob matching, relative path matching, and +# negation (prefixed with !). Only one pattern per line. +.DS_Store +# Common VCS dirs +.git/ +.gitignore +.bzr/ +.bzrignore +.hg/ +.hgignore +.svn/ +# Common backup files +*.swp +*.bak +*.tmp +*.orig +*~ +# Various IDEs +.project +.idea/ +*.tmproj +.vscode/ diff --git a/functions/extensions/k8s/shuffle/Chart.yaml b/functions/extensions/k8s/shuffle/Chart.yaml new file mode 100644 index 00000000..bd42981f --- /dev/null +++ b/functions/extensions/k8s/shuffle/Chart.yaml @@ -0,0 +1,24 @@ +apiVersion: v2 +name: shuffle +description: A Helm chart for Kubernetes + +# A chart can be either an 'application' or a 'library' chart. +# +# Application charts are a collection of templates that can be packaged into versioned archives +# to be deployed. +# +# Library charts provide useful utilities or functions for the chart developer. They're included as +# a dependency of application charts to inject those utilities and functions into the rendering +# pipeline. Library charts do not define any templates and therefore cannot be deployed. +type: application + +# This is the chart version. This version number should be incremented each time you make changes +# to the chart and its templates, including the app version. +# Versions are expected to follow Semantic Versioning (https://semver.org/) +version: 0.1.0 + +# This is the version number of the application being deployed. This version number should be +# incremented each time you make changes to the application. Versions are not expected to +# follow Semantic Versioning. They should reflect the version the application is using. +# It is recommended to use it with quotes. +appVersion: "1.16.0" diff --git a/functions/extensions/k8s/shuffle/templates/NOTES.txt b/functions/extensions/k8s/shuffle/templates/NOTES.txt new file mode 100644 index 00000000..eaba4460 --- /dev/null +++ b/functions/extensions/k8s/shuffle/templates/NOTES.txt @@ -0,0 +1,22 @@ +1. Get the application URL by running these commands: +{{- if .Values.ingress.enabled }} +{{- range $host := .Values.ingress.hosts }} + {{- range .paths }} + http{{ if $.Values.ingress.tls }}s{{ end }}://{{ $host.host }}{{ .path }} + {{- end }} +{{- end }} +{{- else if contains "NodePort" .Values.service.type }} + export NODE_PORT=$(kubectl get --namespace {{ .Release.Namespace }} -o jsonpath="{.spec.ports[0].nodePort}" services {{ include "shuffle.fullname" . }}) + export NODE_IP=$(kubectl get nodes --namespace {{ .Release.Namespace }} -o jsonpath="{.items[0].status.addresses[0].address}") + echo http://$NODE_IP:$NODE_PORT +{{- else if contains "LoadBalancer" .Values.service.type }} + NOTE: It may take a few minutes for the LoadBalancer IP to be available. + You can watch the status of by running 'kubectl get --namespace {{ .Release.Namespace }} svc -w {{ include "shuffle.fullname" . }}' + export SERVICE_IP=$(kubectl get svc --namespace {{ .Release.Namespace }} {{ include "shuffle.fullname" . }} --template "{{"{{ range (index .status.loadBalancer.ingress 0) }}{{.}}{{ end }}"}}") + echo http://$SERVICE_IP:{{ .Values.service.port }} +{{- else if contains "ClusterIP" .Values.service.type }} + export POD_NAME=$(kubectl get pods --namespace {{ .Release.Namespace }} -l "app.kubernetes.io/name={{ include "shuffle.name" . }},app.kubernetes.io/instance={{ .Release.Name }}" -o jsonpath="{.items[0].metadata.name}") + export CONTAINER_PORT=$(kubectl get pod --namespace {{ .Release.Namespace }} $POD_NAME -o jsonpath="{.spec.containers[0].ports[0].containerPort}") + echo "Visit http://127.0.0.1:8080 to use your application" + kubectl --namespace {{ .Release.Namespace }} port-forward $POD_NAME 8080:$CONTAINER_PORT +{{- end }} diff --git a/functions/extensions/k8s/shuffle/templates/_helpers.tpl b/functions/extensions/k8s/shuffle/templates/_helpers.tpl new file mode 100644 index 00000000..66efef07 --- /dev/null +++ b/functions/extensions/k8s/shuffle/templates/_helpers.tpl @@ -0,0 +1,62 @@ +{{/* +Expand the name of the chart. +*/}} +{{- define "shuffle.name" -}} +{{- default .Chart.Name .Values.nameOverride | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Create a default fully qualified app name. +We truncate at 63 chars because some Kubernetes name fields are limited to this (by the DNS naming spec). +If release name contains chart name it will be used as a full name. +*/}} +{{- define "shuffle.fullname" -}} +{{- if .Values.fullnameOverride }} +{{- .Values.fullnameOverride | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- $name := default .Chart.Name .Values.nameOverride }} +{{- if contains $name .Release.Name }} +{{- .Release.Name | trunc 63 | trimSuffix "-" }} +{{- else }} +{{- printf "%s-%s" .Release.Name $name | trunc 63 | trimSuffix "-" }} +{{- end }} +{{- end }} +{{- end }} + +{{/* +Create chart name and version as used by the chart label. +*/}} +{{- define "shuffle.chart" -}} +{{- printf "%s-%s" .Chart.Name .Chart.Version | replace "+" "_" | trunc 63 | trimSuffix "-" }} +{{- end }} + +{{/* +Common labels +*/}} +{{- define "shuffle.labels" -}} +helm.sh/chart: {{ include "shuffle.chart" . }} +{{ include "shuffle.selectorLabels" . }} +{{- if .Chart.AppVersion }} +app.kubernetes.io/version: {{ .Chart.AppVersion | quote }} +{{- end }} +app.kubernetes.io/managed-by: {{ .Release.Service }} +{{- end }} + +{{/* +Selector labels +*/}} +{{- define "shuffle.selectorLabels" -}} +app.kubernetes.io/name: {{ include "shuffle.name" . }} +app.kubernetes.io/instance: {{ .Release.Name }} +{{- end }} + +{{/* +Create the name of the service account to use +*/}} +{{- define "shuffle.serviceAccountName" -}} +{{- if .Values.serviceAccount.create }} +{{- default (include "shuffle.fullname" .) .Values.serviceAccount.name }} +{{- else }} +{{- default "default" .Values.serviceAccount.name }} +{{- end }} +{{- end }} diff --git a/functions/extensions/k8s/shuffle/templates/deployment.yaml b/functions/extensions/k8s/shuffle/templates/deployment.yaml new file mode 100644 index 00000000..f2050d82 --- /dev/null +++ b/functions/extensions/k8s/shuffle/templates/deployment.yaml @@ -0,0 +1,253 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Values.name }}frontend + namespace: {{ .Values.namespace | quote }} +spec: + replicas: 1 + selector: + matchLabels: + service: shuffle + app: shuffle-frontend + template: + metadata: + labels: + service: shuffle + app: shuffle-frontend + spec: + containers: + - name: shuffle-frontend + image: ghcr.io/frikky/shuffle-frontend:nightly + imagePullPolicy: {{ .Values.image.pullPolicy | quote }} + env: + - name: BACKEND_HOSTNAME + value: backend-service + - name: TZ + value: Asia/Shanghai + ports: + - name: http + containerPort: 80 + hostPort: 3001 + - name: https + containerPort: 443 + hostname: shuffle-frontend + restartPolicy: Always + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Values.name }}backend + namespace: {{ .Values.namespace | quote }} +spec: + replicas: 1 + selector: + matchLabels: + service: shuffle + app: shuffle-backend + template: + metadata: + labels: + service: shuffle + app: shuffle-backend + spec: + containers: + - name: shuffle-backend + image: ghcr.io/frikky/shuffle-backend:nightly + env: + - name: BACKEND_HOSTNAME + value: "backend-service" + - name: BACKEND_PORT + value: "5001" + - name: ENVIRONMENT_NAME + value: "Shuffle" + - name: HTTPS_PROXY + - name: HTTP_PROXY + - name: ORG_ID + value: "Shuffle" + - name: OUTER_HOSTNAME + value: "backend-service" + - name: SHUFFLE_APP_FORCE_UPDATE + value: "false" + - name: SHUFFLE_APP_HOTLOAD_FOLDER + value: "/shuffle-apps" + - name: SHUFFLE_APP_HOTLOAD_LOCATION + value: "/shuffle-apps" + - name: SHUFFLE_CONTAINER_AUTO_CLEANUP + value: "false" + - name: SHUFFLE_DEFAULT_APIKEY + - name: SHUFFLE_DEFAULT_PASSWORD + - name: SHUFFLE_DEFAULT_USERNAME + - name: SHUFFLE_DOWNLOAD_AUTH_BRANCH + - name: SHUFFLE_DOWNLOAD_AUTH_PASSWORD + - name: SHUFFLE_DOWNLOAD_AUTH_USERNAME + - name: SHUFFLE_DOWNLOAD_WORKFLOW_BRANCH + - name: SHUFFLE_DOWNLOAD_WORKFLOW_LOCATION + - name: SHUFFLE_DOWNLOAD_WORKFLOW_PASSWORD + - name: SHUFFLE_DOWNLOAD_WORKFLOW_USERNAME + - name: SHUFFLE_ELASTIC + value: "true" + - name: SHUFFLE_OPENSEARCH_APIKEY + - name: SHUFFLE_OPENSEARCH_CERTIFICATE_FILE + - name: SHUFFLE_OPENSEARCH_CLOUDID + - name: SHUFFLE_OPENSEARCH_PASSWORD + - name: SHUFFLE_OPENSEARCH_PROXY + - name: SHUFFLE_OPENSEARCH_SKIPSSL_VERIFY + value: "true" + - name: SHUFFLE_OPENSEARCH_URL + value: http://opensearch-service:9200 + - name: SHUFFLE_OPENSEARCH_USERNAME + value: "" + - name: SHUFFLE_PASS_APP_PROXY + value: "FALSE" + - name: SHUFFLE_PASS_WORKER_PROXY + value: "FALSE" + volumeMounts: + - mountPath: /var/run/docker.sock + name: docker-sock + - mountPath: /shuffle-apps + name: shuffle-app-hotload-location + - mountPath: /shuffle-files + name: shuffle-file-location + hostname: shuffle-backend + volumes: + - name: docker-sock + hostPath: + path: /var/run/docker.sock + - name: shuffle-app-hotload-location + hostPath: + path: /data/kubernetes/shuffle-apps + type: DirectoryOrCreate + - name: shuffle-file-location + hostPath: + path: /data/kubernetes/shuffle-files + type: DirectoryOrCreate + +--- +apiVersion: apps/v1 +kind: Deployment +metadata: + name: {{ .Values.name }}orborus + namespace: {{ .Values.namespace | quote }} +spec: + replicas: 1 + selector: + matchLabels: + service: shuffle + app: shuffle-orborus + template: + metadata: + labels: + service: shuffle + app: shuffle-orborus + spec: + containers: + - name: shuffle-orborus + image: ghcr.io/frikky/shuffle-orborus:nightly + env: + - name: RUNNING_MODE + value: kubernetes + - name: BASE_URL + value: http://backend-service:5001 + - name: CLEANUP + value: "false" + - name: DOCKER_API_VERSION + value: "1.40" + - name: ENVIRONMENT_NAME + value: Shuffle + - name: HTTPS_PROXY + - name: HTTP_PROXY + - name: ORG_ID + value: Shuffle + - name: SHUFFLE_APP_SDK_VERSION + value: 0.8.97 + - name: SHUFFLE_BASE_IMAGE_NAME + value: frikky + - name: SHUFFLE_BASE_IMAGE_REGISTRY + value: ghcr.io + - name: SHUFFLE_BASE_IMAGE_TAG_SUFFIX + value: "-0.8.80" + - name: SHUFFLE_ORBORUS_EXECUTION_TIMEOUT + value: "600" + - name: SHUFFLE_ORBORUS_EXECUTION_CONCURRENCY + value: "50" + - name: SHUFFLE_PASS_WORKER_PROXY + value: "TRUE" + - name: SHUFFLE_WORKER_VERSION + value: nightly + - name: TZ + value: Asia/Shanghai + volumeMounts: + - mountPath: /var/run/docker.sock + name: docker-sock + hostname: shuffle-orborus + volumes: + - name: docker-sock + hostPath: + path: /var/run/docker.sock +#--- +#apiVersion: apps/v1 +#kind: Deployment +#metadata: +# name: {{ .Values.name }}opensearch +# namespace: {{ .Values.namespace | quote }} +#spec: +# replicas: 1 +# selector: +# matchLabels: +# service: shuffle +# app: shuffle-opensearch +# template: +# metadata: +# labels: +# service: shuffle +# app: shuffle-opensearch +# spec: +# nodeSelector: +# node.bdlab-venus.com/opensearch: available +# initContainers: +# - name: permissions-fix +# image: frikky/busybox +# #volumeMounts: +# # - name: opensearch-claim0 +# # mountPath: /usr/share/elasticsearch/data +# command: [ 'chown' ] +# args: [ '1000:1000', '/usr/share/elasticsearch/data' ] +# containers: +# - name: shuffle-opensearch +# image: opensearchproject/opensearch:1.0.1 +# env: +# - name: TZ +# value: Asia/Shanghai +# - name: bootstrap.memory_lock +# value: "false" +# - name: OPENSEARCH_JAVA_OPTS +# value: "-Xms1024m -Xmx1024m" +# - name: opendistro_security.disabled +# value: "true" +# - name: cluster.routing.allocation.disk.threshold_enabled +# value: "false" +# - name: cluster.name +# value: shuffle-cluster +# - name: node.name +# value: opensearch-service +# - name: discovery.seed_hosts +# value: opensearch-service +# - name: cluster.initial_master_nodes +# value: opensearch-service + # volumeMounts: + # - mountPath: /usr/share/opensearch/data + # name: opensearch-claim0 + #volumes: + # - name: opensearch-claim0 + # persistentVolumeClaim: + # claimName: opensearch-claim0 +# volumeMounts: +# - mountPath: /usr/share/opensearch/data +# readOnly: true +# name: db-location +# volumes: +# - name: db-location +# hostPath: +# path: /data/kubernetes/shuffle-opensearch +# type: DirectoryOrCreate diff --git a/functions/extensions/k8s/shuffle/templates/hpa.yaml b/functions/extensions/k8s/shuffle/templates/hpa.yaml new file mode 100644 index 00000000..49f84ca2 --- /dev/null +++ b/functions/extensions/k8s/shuffle/templates/hpa.yaml @@ -0,0 +1,28 @@ +{{- if .Values.autoscaling.enabled }} +apiVersion: autoscaling/v2beta1 +kind: HorizontalPodAutoscaler +metadata: + name: {{ include "shuffle.fullname" . }} + labels: + {{- include "shuffle.labels" . | nindent 4 }} +spec: + scaleTargetRef: + apiVersion: apps/v1 + kind: Deployment + name: {{ include "shuffle.fullname" . }} + minReplicas: {{ .Values.autoscaling.minReplicas }} + maxReplicas: {{ .Values.autoscaling.maxReplicas }} + metrics: + {{- if .Values.autoscaling.targetCPUUtilizationPercentage }} + - type: Resource + resource: + name: cpu + targetAverageUtilization: {{ .Values.autoscaling.targetCPUUtilizationPercentage }} + {{- end }} + {{- if .Values.autoscaling.targetMemoryUtilizationPercentage }} + - type: Resource + resource: + name: memory + targetAverageUtilization: {{ .Values.autoscaling.targetMemoryUtilizationPercentage }} + {{- end }} +{{- end }} diff --git a/functions/extensions/k8s/shuffle/templates/ingress.yaml b/functions/extensions/k8s/shuffle/templates/ingress.yaml new file mode 100644 index 00000000..e35d2ae0 --- /dev/null +++ b/functions/extensions/k8s/shuffle/templates/ingress.yaml @@ -0,0 +1,61 @@ +{{- if .Values.ingress.enabled -}} +{{- $fullName := include "shuffle.fullname" . -}} +{{- $svcPort := .Values.service.port -}} +{{- if and .Values.ingress.className (not (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion)) }} + {{- if not (hasKey .Values.ingress.annotations "kubernetes.io/ingress.class") }} + {{- $_ := set .Values.ingress.annotations "kubernetes.io/ingress.class" .Values.ingress.className}} + {{- end }} +{{- end }} +{{- if semverCompare ">=1.19-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1 +{{- else if semverCompare ">=1.14-0" .Capabilities.KubeVersion.GitVersion -}} +apiVersion: networking.k8s.io/v1beta1 +{{- else -}} +apiVersion: extensions/v1beta1 +{{- end }} +kind: Ingress +metadata: + name: {{ $fullName }} + labels: + {{- include "shuffle.labels" . | nindent 4 }} + {{- with .Values.ingress.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +spec: + {{- if and .Values.ingress.className (semverCompare ">=1.18-0" .Capabilities.KubeVersion.GitVersion) }} + ingressClassName: {{ .Values.ingress.className }} + {{- end }} + {{- if .Values.ingress.tls }} + tls: + {{- range .Values.ingress.tls }} + - hosts: + {{- range .hosts }} + - {{ . | quote }} + {{- end }} + secretName: {{ .secretName }} + {{- end }} + {{- end }} + rules: + {{- range .Values.ingress.hosts }} + - host: {{ .host | quote }} + http: + paths: + {{- range .paths }} + - path: {{ .path }} + {{- if and .pathType (semverCompare ">=1.18-0" $.Capabilities.KubeVersion.GitVersion) }} + pathType: {{ .pathType }} + {{- end }} + backend: + {{- if semverCompare ">=1.19-0" $.Capabilities.KubeVersion.GitVersion }} + service: + name: {{ $fullName }} + port: + number: {{ $svcPort }} + {{- else }} + serviceName: {{ $fullName }} + servicePort: {{ $svcPort }} + {{- end }} + {{- end }} + {{- end }} +{{- end }} diff --git a/functions/extensions/k8s/shuffle/templates/persistent-volume.yaml b/functions/extensions/k8s/shuffle/templates/persistent-volume.yaml new file mode 100644 index 00000000..934c68e2 --- /dev/null +++ b/functions/extensions/k8s/shuffle/templates/persistent-volume.yaml @@ -0,0 +1,24 @@ +#apiVersion: v1 +#kind: PersistentVolume +#metadata: +# name: opensearch-claim0 +# labels: +# app: opensearch-claim0 +#spec: +# capacity: +# storage: "10G" +# volumeMode: Filesystem +# persistentVolumeReclaimPolicy: Retain +# storageClassName: local-storage +# accessModes: +# - "ReadWriteOnce" +# local: +# path: "/data/kubernetes/shuffle-opensearch" +# nodeAffinity: +# required: +# nodeSelectorTerms: +# - matchExpressions: +# - key: node.dollar.com/opensearch +# operator: In +# values: +# - available diff --git a/functions/extensions/k8s/shuffle/templates/persistentvolumeclaim.yaml b/functions/extensions/k8s/shuffle/templates/persistentvolumeclaim.yaml new file mode 100644 index 00000000..7d1736a4 --- /dev/null +++ b/functions/extensions/k8s/shuffle/templates/persistentvolumeclaim.yaml @@ -0,0 +1,17 @@ +#apiVersion: v1 +#kind: PersistentVolumeClaim +#metadata: +# name: opensearch-claim0 +# namespace: {{ .Values.namespace }} +# labels: +# app: opensearch-claim0 +#spec: +# selector: +# matchLabels: +# app: opensearch-claim0 +# accessModes: +# - ReadWriteOnce +# storageClassName: local-storage +# resources: +# requests: +# storage: 5Gi diff --git a/functions/extensions/k8s/shuffle/templates/service.yaml b/functions/extensions/k8s/shuffle/templates/service.yaml new file mode 100644 index 00000000..8fc78dfa --- /dev/null +++ b/functions/extensions/k8s/shuffle/templates/service.yaml @@ -0,0 +1,52 @@ +apiVersion: v1 +kind: Service +metadata: + name: backend-service + namespace: {{ .Values.namespace | quote }} +spec: + ports: + - name: "5001" + port: 5001 + targetPort: 5001 + selector: + app: shuffle-backend + +--- +apiVersion: v1 +kind: Service +metadata: + name: frontend-service + namespace: {{ .Values.namespace | quote }} +spec: + type: NodePort + externalTrafficPolicy: Local + ports: + - name: "3001" + port: 3001 + nodePort: 3001 + targetPort: 80 + - name: "3443" + port: 3443 + nodePort: 3443 + targetPort: 443 + protocol: TCP + selector: + app: shuffle-frontend + +#--- +#apiVersion: v1 +#kind: Service +#metadata: +# name: opensearch-service +# namespace: {{ .Values.namespace | quote }} +#spec: +# type: NodePort +# externalTrafficPolicy: Local +# ports: +# - name: "9200" +# port: 9200 +# targetPort: 9200 +# nodePort: 9200 +# protocol: TCP +# selector: +# app: shuffle-opensearch diff --git a/functions/extensions/k8s/shuffle/templates/serviceaccount.yaml b/functions/extensions/k8s/shuffle/templates/serviceaccount.yaml new file mode 100644 index 00000000..11e9bdb7 --- /dev/null +++ b/functions/extensions/k8s/shuffle/templates/serviceaccount.yaml @@ -0,0 +1,12 @@ +{{- if .Values.serviceAccount.create -}} +apiVersion: v1 +kind: ServiceAccount +metadata: + name: {{ include "shuffle.serviceAccountName" . }} + labels: + {{- include "shuffle.labels" . | nindent 4 }} + {{- with .Values.serviceAccount.annotations }} + annotations: + {{- toYaml . | nindent 4 }} + {{- end }} +{{- end }} diff --git a/functions/extensions/k8s/shuffle/templates/tests/test-connection.yaml b/functions/extensions/k8s/shuffle/templates/tests/test-connection.yaml new file mode 100644 index 00000000..e68f3142 --- /dev/null +++ b/functions/extensions/k8s/shuffle/templates/tests/test-connection.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Pod +metadata: + name: "{{ include "shuffle.fullname" . }}-test-connection" + labels: + {{- include "shuffle.labels" . | nindent 4 }} + annotations: + "helm.sh/hook": test +spec: + containers: + - name: wget + image: busybox + command: ['wget'] + args: ['{{ include "shuffle.fullname" . }}:{{ .Values.service.port }}'] + restartPolicy: Never diff --git a/functions/extensions/k8s/shuffle/values.yaml b/functions/extensions/k8s/shuffle/values.yaml new file mode 100644 index 00000000..07e935c6 --- /dev/null +++ b/functions/extensions/k8s/shuffle/values.yaml @@ -0,0 +1,82 @@ +# Default values for shuffle. +# This is a YAML-formatted file. +# Declare variables to be passed into your templates. + +replicaCount: 1 + +image: + repository: nginx + pullPolicy: IfNotPresent + # Overrides the image tag whose default is the chart appVersion. + tag: "" + +imagePullSecrets: [] +nameOverride: "" +fullnameOverride: "" + +serviceAccount: + # Specifies whether a service account should be created + create: true + # Annotations to add to the service account + annotations: {} + # The name of the service account to use. + # If not set and create is true, a name is generated using the fullname template + name: "" + +podAnnotations: {} + +podSecurityContext: {} + # fsGroup: 2000 + +securityContext: {} + # capabilities: + # drop: + # - ALL + # readOnlyRootFilesystem: true + # runAsNonRoot: true + # runAsUser: 1000 + +service: + type: ClusterIP + port: 80 + +ingress: + enabled: false + className: "" + annotations: {} + # kubernetes.io/ingress.class: nginx + # kubernetes.io/tls-acme: "true" + hosts: + - host: chart-example.local + paths: + - path: / + pathType: ImplementationSpecific + tls: [] + # - secretName: chart-example-tls + # hosts: + # - chart-example.local + +resources: {} + # We usually recommend not to specify default resources and to leave this as a conscious + # choice for the user. This also increases chances charts run on environments with little + # resources, such as Minikube. If you do want to specify resources, uncomment the following + # lines, adjust them as necessary, and remove the curly braces after 'resources:'. + # limits: + # cpu: 100m + # memory: 128Mi + # requests: + # cpu: 100m + # memory: 128Mi + +autoscaling: + enabled: false + minReplicas: 1 + maxReplicas: 100 + targetCPUUtilizationPercentage: 80 + # targetMemoryUtilizationPercentage: 80 + +nodeSelector: {} + +tolerations: [] + +affinity: {} From 137df9f20afe039ca8c8e68264f16150e8ecf59b Mon Sep 17 00:00:00 2001 From: frikky Date: Tue, 21 Sep 2021 14:28:39 +0200 Subject: [PATCH 086/117] Fixed SSO bugs for PingID --- backend/go-app/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index c323832b..2c94f896 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -2,7 +2,7 @@ module shuffle 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 From 58524258c7115323e0a39ad25dd57f08733e09b6 Mon Sep 17 00:00:00 2001 From: frikky Date: Thu, 23 Sep 2021 15:31:04 +0200 Subject: [PATCH 087/117] Added authenticationSSO file --- backend/go-app/go.mod | 4 +- docker-compose.yml | 6 +- frontend/src/components/OrgHeader.js | 8 +- frontend/src/views/Admin.jsx | 49 ++++++- frontend/src/views/AngularWorkflow.jsx | 2 +- frontend/src/views/AppCreator.jsx | 110 +++++++++++++-- frontend/src/views/Docs.jsx | 4 +- frontend/src/views/SetAuthenticationSSO.jsx | 143 ++++++++++++++++++++ frontend/src/views/Workflows.jsx | 59 +++++++- 9 files changed, 354 insertions(+), 31 deletions(-) create mode 100644 frontend/src/views/SetAuthenticationSSO.jsx diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 2c94f896..5165e520 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -2,7 +2,7 @@ module shuffle 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 @@ -23,7 +23,7 @@ require ( github.com/docker/go-units v0.4.0 // indirect github.com/elastic/go-elasticsearch/v7 v7.13.1 // indirect github.com/frikky/kin-openapi v0.39.0 - github.com/frikky/shuffle-shared v0.0.98 + github.com/frikky/shuffle-shared v0.0.99 github.com/fsouza/go-dockerclient v1.7.2 github.com/ghodss/yaml v1.0.0 github.com/go-git/go-billy/v5 v5.0.0 diff --git a/docker-compose.yml b/docker-compose.yml index 89a8c331..edb20333 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '3' services: frontend: - build: ./frontend + #build: ./frontend image: ghcr.io/frikky/shuffle-frontend:nightly container_name: shuffle-frontend hostname: shuffle-frontend @@ -16,7 +16,7 @@ services: depends_on: - backend backend: - build: ./backend + #build: ./backend image: ghcr.io/frikky/shuffle-backend:nightly container_name: shuffle-backend hostname: ${BACKEND_HOSTNAME} @@ -62,7 +62,7 @@ services: - SHUFFLE_PASS_WORKER_PROXY=${SHUFFLE_PASS_WORKER_PROXY} - SHUFFLE_PASS_APP_PROXY=${SHUFFLE_PASS_APP_PROXY} - SHUFFLE_ORBORUS_EXECUTION_TIMEOUT=600 - - SHUFFLE_ORBORUS_EXECUTION_CONCURRENCY=15 + - SHUFFLE_ORBORUS_EXECUTION_CONCURRENCY=5 - CLEANUP=${SHUFFLE_CONTAINER_AUTO_CLEANUP} restart: unless-stopped opensearch: diff --git a/frontend/src/components/OrgHeader.js b/frontend/src/components/OrgHeader.js index 896517dc..65dd6e97 100644 --- a/frontend/src/components/OrgHeader.js +++ b/frontend/src/components/OrgHeader.js @@ -22,7 +22,7 @@ const useStyles = makeStyles({ const defaultImage = "data:image/png;base64,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" const OrgHeader = (props) => { - const {selectedOrganization, setSelectedOrganization, globalUrl} = props + const {userdata, selectedOrganization, setSelectedOrganization, globalUrl} = props const theme = useTheme() const alert = useAlert() @@ -113,12 +113,14 @@ const OrgHeader = (props) => { setFile(fileObject) } + console.log("USER: ", userdata) const orgSaveButton = +
+ } {isCloud ? null : @@ -1626,7 +1666,7 @@ const Admin = (props) => { {selectedOrganization.name.length > 0 ? - + :
@@ -2069,10 +2109,9 @@ const Admin = (props) => { value={data.role} fullWidth onChange={(e) => { - console.log("VALUE: ", e.target.value) - - setUser(data.id, "role", e.target.value) - }} + console.log("VALUE: ", e.target.value) + setUser(data.id, "role", e.target.value) + }} style={{ backgroundColor: theme.palette.surfaceColor, color: "white", height: "50px" }} > diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 5f325d39..85d3c6ed 100644 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -8186,7 +8186,7 @@ const AngularWorkflow = (props) => { } } - if (data.action.app_name === "Shuffle Tools") { + if (data.action.app_name === "Shuffle Tools" && data.action.id !== undefined && cy !== undefined) { //console.log("APP (TOOLS): ", data.action) const nodedata = cy.getElementById(data.action.id).data() diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx index 3b0a90ad..f44ce565 100644 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -1146,8 +1146,37 @@ const AppCreator = (props) => { if (queryitem.name.toLowerCase() == "url") { console.log(item.name+" uses a bad query: url") - skipped = true - break + continue + //skipped = true + //break + } + + if (queryitem.name.toLowerCase() == "url" + || queryitem.name.toLowerCase() == "body" + || queryitem.name.toLowerCase() == "queries" + || queryitem.name.toLowerCase() == "headers" + || queryitem.name.includes("[") + || queryitem.name.includes("]") + || queryitem.name.includes("{") + || queryitem.name.includes("}") + || queryitem.name.includes("(") + || queryitem.name.includes(")") + || queryitem.name.includes("!") + || queryitem.name.includes("@") + || queryitem.name.includes("#") + || queryitem.name.includes("$") + || queryitem.name.includes("%") + || queryitem.name.includes("^") + || queryitem.name.includes("&") + || queryitem.name.includes(":") + || queryitem.name.includes(";") + || queryitem.name.includes("<") + || queryitem.name.includes(">") + || queryitem.name.includes("\"") + || queryitem.name.includes("\'") + ) { + console.log(item.name+" error: uses a bad query - not adding: ", queryitem.name) + continue } var newitem = { @@ -1344,10 +1373,13 @@ const AppCreator = (props) => { return } + var newparamName = parameterName.replaceAll("\"", "") + newparamName = newparamName.replaceAll("\'", "") + data.components.securitySchemes["ApiKeyAuth"] = { "type": "apiKey", "in": parameterLocation.toLowerCase(), - "name": parameterName, + "name": newparamName, } } else if (authenticationOption === "Bearer auth") { data.components.securitySchemes["BearerAuth"] = { @@ -1367,13 +1399,16 @@ const AppCreator = (props) => { "scheme": "basic", } } else if (authenticationOption === "Oauth2") { + var newparamName = parameterName.replaceAll("\"", "") + newparamName = newparamName.replaceAll("\'", "") + //parameterName, parameterValue, revocationUrl data.components.securitySchemes["Oauth2"] = { "type": "oauth2", "description": "Oauth2.0 authorizationCode authentication", "flow": { "authorizationCode": { - "authorizationUrl": parameterName, + "authorizationUrl": newparamName, "tokenUrl": parameterLocation, "refreshUrl": refreshUrl, "scopes": [], @@ -1769,8 +1804,10 @@ const AppCreator = (props) => { margin="normal" variant="outlined" value={parameterName} - helperText={Can't be empty. Can't contain any of the following characters: !#$%&'^+-._~|]+$} - onChange={e => setParameterName(e.target.value)} + helperText={Can't be empty. Can't contain any of the following characters: !#$%&'^"+-._~|]+$} + onChange={e => { + setParameterName(e.target.value) + }} InputProps={{ classes: { notchedOutline: classes.notchedOutline, @@ -1954,11 +1991,60 @@ const AppCreator = (props) => { } } + const HandleIndividualChip = (props) => { + const { chipData, index } = props; + const [chipRequired, setChipRequired] = useState(false) + + return ( + + { + console.log("CLICK: ", chipData) + setChipRequired(!chipRequired) + }} + /> + + ) + } + + const SetExtraBodyField = (props) => { + const { extraBodyFields } = props; + + if (extraBodyFields === undefined || extraBodyFields === null) { + return null + } + + //const parsedlist = extraBodyFields.join(", ") + //console.log("LIST: ", parsedlist) + + return ( + + {extraBodyFields.map((data, index) => { + return ( + + ) + })} + + ) + } + const bodyInfo = actionBodyRequest.includes(currentActionMethod) ?
Request Body: {extraBodyFields.length > 0 ? - Variables: {extraBodyFields.join(", ")} + : @@ -2988,10 +3074,13 @@ const AppCreator = (props) => { : null}
-

Actions {actionAmount > 0 ? ({actionAmount} / {filteredActions.length}) : null}

+ Actions {actionAmount > 0 ? ({actionAmount} / {filteredActions.length}) : null} + + Actions are the tasks performed by an app - usually single URL paths for REST API's. + {projectCategories !== undefined && projectCategories !== null && projectCategories.length > 1 ? -
+
{projectCategories.map((tag, index) => { const newname = tag.charAt(0).toUpperCase() + tag.slice(1) return ( @@ -3000,7 +3089,7 @@ const AppCreator = (props) => { style={{ backgroundColor: tag === selectedCategory ? "#f86a3e" : "#3d3f43", height: 30, - marginRight: 5, + margin: 3, paddingLeft: 5, paddingRight: 5, height: 28, @@ -3037,7 +3126,6 @@ const AppCreator = (props) => { })}
: null} - Actions are the tasks performed by an app - usually single URL paths for REST API's.
{loopActions}
diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index c79e8f4f..597019aa 100644 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -243,9 +243,9 @@ const Docs = (props) => { const path = "/docs/"+item const newname = item.charAt(0).toUpperCase()+item.substring(1).split("_").join(" ").split("-").join(" ") return ( -
  • +
  • {fetchDocs(item)}}> - {newname} + > {newname}
  • ) diff --git a/frontend/src/views/SetAuthenticationSSO.jsx b/frontend/src/views/SetAuthenticationSSO.jsx new file mode 100644 index 00000000..5fcd0f2b --- /dev/null +++ b/frontend/src/views/SetAuthenticationSSO.jsx @@ -0,0 +1,143 @@ +import React, {useRef, useState, useEffect, useLayoutEffect} from 'react'; + +import { Typography, CircularProgress } from '@material-ui/core'; + +const SetAuthentication = (props) => { + const { globalUrl, isLoggedIn, isLoaded, userdata } = props; + + const [firstRequest, setFirstRequest] = useState(true) + const [finished, setFinished] = useState(false) + const [response, setResponse] = useState("") + const [failed, setFailed] = useState(false) + + if (firstRequest) { + setFirstRequest(false) + + //code + //session_state + const urlSearchParams = new URLSearchParams(window.location.search); + const params = Object.fromEntries(urlSearchParams.entries()); + + const authenticationStore = [] + var appAuthData = { + "label": "", + "app": { + "name": "", + "id": "", + "app_version": "", + }, + "fields": [], + "type": "oauth2", + } + + if (window !== undefined && window !== null) { + console.log(window.location) + appAuthData.fields.push({"key": "redirect_uri", "value": window.location.origin+window.location.pathname}) + } + + if (params.code !== undefined && params.code !== null) { + appAuthData.fields.push({"key": "code", "value": params.code}) + } + + if (params.session_state !== undefined && params.session_state !== null) { + appAuthData.fields.push({"key": "session_state", "value": params.session_state}) + } + + if (params.state !== undefined && params.state !== null) { + const paramsplit = params.state.split("&") + console.log(paramsplit) + for (var key in paramsplit) { + const query = paramsplit[key].split("=") + console.log(query) + + if (query.length !== 2) { + console.log("INVALID QUERY: ", query) + continue + } + + if (query[0] === "workflow_id") { + appAuthData.reference_workflow = query[1] + } + + if (query[0] === "reference_action_id") { + //appAuthData.ReferenceWorkflow = query[1] + } + + if (query[0] === "app_name") { + appAuthData.app.name = query[1] + appAuthData.label = "Oauth2 for "+query[1] + } + + if (query[0] === "app_id") { + appAuthData.app.id = query[1] + } + + if (query[0] === "app_version") { + appAuthData.app.app_version = query[1] + } + + if (query[0] === "authentication_url") { + appAuthData.fields.push({"key": "authentication_url", "value": query[1]}) + } + + if (query[0] === "scope") { + appAuthData.fields.push({"key": "scope", "value": query[1]}) + } + + if (query[0] === "client_id") { + appAuthData.fields.push({"key": "client_id", "value": query[1]}) + } + + if (query[0] === "client_secret") { + appAuthData.fields.push({"key": "client_secret", "value": query[1]}) + } + } + } + + console.log(appAuthData) + + fetch(globalUrl+"/api/v1/apps/authentication", { + method: 'PUT', + headers: { + 'Content-Type': 'application/json', + 'Accept': 'application/json', + }, + credentials: "include", + body: JSON.stringify(appAuthData), + }) + .then((response) => { + if (response.status !== 200) { + console.log("Status not 200 for oauth2 authentication") + setFailed(true) + } + + return response.json() + }) + .then((responseJson) => { + //setUserSettings(responseJson) + console.log("Resp: ", responseJson) + setFinished(true) + setResponse(responseJson.reason) + + setTimeout(() => { + window.close() + }, 1000) + }) + .catch(error => { + console.log(error) + }); + + } + + return ( +
    + + {!finished ? : "DONE WITH AUTH - this will close soon!!"} +
    + {failed ? "Failed setup. Error: " : ""} {response} + +
    + ) +} + +export default SetAuthentication; diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index 9e2555cc..58dd9625 100644 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -354,6 +354,8 @@ const Workflows = (props) => { const [downloadUrl, setDownloadUrl] = React.useState("https://github.com/frikky/shuffle-workflows") const [downloadBranch, setDownloadBranch] = React.useState("master") const [loadWorkflowsModalOpen, setLoadWorkflowsModalOpen] = React.useState(false) + const [exportModalOpen, setExportModalOpen] = React.useState(false) + const [exportData, setExportData] = React.useState(""); const [modalOpen, setModalOpen] = React.useState(false); const [newWorkflowName, setNewWorkflowName] = React.useState(""); @@ -473,6 +475,50 @@ const Workflows = (props) => { findWorkflow(newfilters) } + const exportVerifyModal = exportModalOpen ? + { + setExportModalOpen(false) + setSelectedWorkflow({}) + }} + PaperProps={{ + style: { + backgroundColor: surfaceColor, + color: "white", + minWidth: 500, + padding: 30, + }, + }} + > + +
    + Want to auto-sanitize this workflow before exporting? +
    +
    + + + This will make potentially sensitive fields such as username, password, url etc. empty + + + + + +
    + : null + const publishModal = publishModalOpen ? { const exportAllWorkflows = () => { for (var key in workflows) { - exportWorkflow(workflows[key]) + exportWorkflow(workflows[key], false) } } @@ -890,10 +936,13 @@ const Workflows = (props) => { return data } - const exportWorkflow = (data) => { + const exportWorkflow = (data, sanitize) => { data = JSON.parse(JSON.stringify(data)) let exportFileDefaultName = data.name+'.json'; - data = sanitizeWorkflow(data) + + if (sanitize === true) { + data = sanitizeWorkflow(data) + } //return // Add correct ID's for triggers @@ -1269,7 +1318,8 @@ const Workflows = (props) => { {"Duplicate Workflow"} { - exportWorkflow(data) + setExportModalOpen(true) + setExportData(data) setOpen(false) }} key={"export"}> @@ -2323,6 +2373,7 @@ const Workflows = (props) => { {modalView} {deleteModal} + {exportVerifyModal} {publishModal} {workflowDownloadModalOpen}
    From 89d5a5e8956cf8ccc0b1d01ff21758f821092478 Mon Sep 17 00:00:00 2001 From: frikky Date: Sun, 26 Sep 2021 22:04:18 +0200 Subject: [PATCH 088/117] Added more info to the contribution docs --- .github/CONTRIBUTING.md | 11 ++++++- backend/app_sdk/build.sh | 2 +- backend/go-app/go.mod | 2 +- frontend/src/views/Docs.jsx | 60 +++++++++++++++++++++++++++++++++---- 4 files changed, 67 insertions(+), 8 deletions(-) diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index d3f66e5b..d22fd6f0 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -16,7 +16,7 @@ As with everything else, app creation for Shuffle is made as accessibl as possib Workflows are where the magic of Shuffle automation happens. Our current ones [are outlined here](https://github.com/frikky/security-openapis), and will be automatically imported into Shuffle instances in the future. They are split into Prepare and Response, but don't necessarily have to be. If you'd like to talk about workflow creation or use-cases in general, either Open a [new issue](https://github.com/frikky/shuffle-workflows/issues/new) or send us an email at [frikky@shuffler.io](mailto:frikky@shuffler.io) #### Documentation (Markdown) -Documentation is essential to any product, and Shuffle is no exception. Documentation in Shuffle uses markdown and is located in the [shuffle-docs](https://github.com/frikky/shuffle-docs/tree/master/docs) repository. These are then loaded into Shuffle when someone visits [https://shuffler/docs/about](https://shuffler/docs/about), then cached for later use. If you make an edit, expect it on our website in about an hour. +Documentation is essential to any product, and Shuffle is no exception. Documentation in Shuffle uses markdown and is located in the [shuffle-docs](https://github.com/frikky/shuffle-docs/tree/master/docs) repository. These are then loaded into Shuffle when someone visits [https://shuffler/docs/about](https://shuffler/docs/about), then cached for later use. If you make an edit, expect it on our website in about an hour. #### Frontend (ReactJS) The frontend of Shuffle is what everyone sees when they log in. Our goal here is to make it easy to get started and keep going with Shuffle - removing any blockers from the point of accessibility. If you'd like to get started, find [an issue](https://github.com/frikky/Shuffle/issues) and check the [installation guide](https://github.com/frikky/Shuffle/blob/master/install-guide.md#local-development-installation) for setting it up locally without Docker. @@ -24,6 +24,15 @@ The frontend of Shuffle is what everyone sees when they log in. Our goal here is #### Backend (Golang) The backend of Shuffle is our REST API Server that runs in the background, handling all the API-calls in general, whether from users or apps. If you'd like to get started, find [an issue](https://github.com/frikky/Shuffle/issues) and check the [installation guide](https://github.com/frikky/Shuffle/blob/master/install-guide.md#local-development-installation) for setting it up locally without Docker. +#### Scaling (Golang & Python) +Shuffle runs using Docker, and is built to scale. There are many areas that may revolve around scaling, but the main issues come down to how we use Docker in our [architecture](https://shuffler.io/docs/architecture). If you want to help by submitting Helm charts (K8s), Docker swarm configurations, blogposts, or talk about code changes that would help scaling - please reach out (or just start building!), and we can discuss the possibilities. Make sure to read about the architecture first :) + +#### Testing +Whether it's security testing, code testing or CI/CD, we could always need another hand. E.g. an example of CI/CD used for apps can be found [here](https://github.com/Shuffle/Shuffle-apps/blob/master/.github/workflows/ci.yaml), but we don't at all limit the scope to Github actions. If you find a security issue, whether open source or not, please contact [security@shuffler.io](mailto:security@shuffler.io) or [contact us on our website](https://shuffler.io/contact). + +#### Community +What is a product without a community? Want to help out? Whether it be through blogposts, videos or community management, don't hesitate to [reach out](https://shuffler.io/contact) if you would like to help, and get a more keen understanding of how we work. (PS: We're hiring) + ## Working on an issue **Shuffle** uses the [GitHub flow](https://guides.github.com/introduction/flow/index.html). All project changes are made through pull requests. diff --git a/backend/app_sdk/build.sh b/backend/app_sdk/build.sh index a3f6d877..85b1b238 100644 --- a/backend/app_sdk/build.sh +++ b/backend/app_sdk/build.sh @@ -3,7 +3,7 @@ ### DEFAULT NAME=shuffle-app_sdk -VERSION=0.9.16 +VERSION=0.9.19 docker rmi docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION --force docker build . -f Dockerfile -t frikky/shuffle:app_sdk -t frikky/$NAME:$VERSION -t docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION -t ghcr.io/frikky/$NAME:$VERSION diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 5165e520..431adaf0 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -2,7 +2,7 @@ module shuffle 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 diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index 597019aa..3b9f466b 100644 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -5,7 +5,8 @@ import ReactMarkdown from 'react-markdown'; import {BrowserView, MobileView} from "react-device-detect"; import {Link} from 'react-router-dom'; -import {Divider, Button, Menu, MenuItem, Typography, Paper, List} from '@material-ui/core'; +import {Tooltip, Divider, Button, Menu, MenuItem, Typography, Paper, List} from '@material-ui/core'; +import {Edit as EditIcon} from '@material-ui/icons'; const Body = { maxWidth: '1000px', @@ -32,6 +33,8 @@ const Docs = (props) => { const [list, setList] = useState([]); const [, setListLoaded] = useState(false); const [anchorEl, setAnchorEl] = React.useState(null); + const [headingSet, setHeadingSet] = React.useState(false); + const [selectedMeta, setSelectedMeta] = React.useState({link: "hello", read_time: 2, }); const [baseUrl, setBaseUrl] = React.useState(serverside === true ? "" : window.location.href) function handleClick(event) { @@ -71,7 +74,7 @@ const Docs = (props) => { if (responseJson.success) { setList(responseJson.list) } else { - setList(["error"]) + setList(["# Error loading documentation. Please contact us if this persists."]) } setListLoaded(true) }) @@ -91,6 +94,10 @@ const Docs = (props) => { if (responseJson.success) { setData(responseJson.reason) document.title = "Shuffle "+docId+" documentation" + + if (responseJson.meta !== undefined) { + setSelectedMeta(responseJson.meta) + } } else { setData("# Error\nThis page doesn't exist.") } @@ -215,11 +222,48 @@ const Docs = (props) => { } function Heading(props) { - const element = React.createElement(`h${props.level}`, {style: {marginTop: 40}}, props.children) + const element = React.createElement(`h${props.level}`, {style: {marginTop: 50}}, props.children) + + var extraInfo = "" + if (props.level === 1) { + extraInfo = +
    +
    + + + + + +
    + + {selectedMeta.read_time} minute{selectedMeta.read_time === 1 ? "" : "s"} to read + +
    +
    + {selectedMeta.contributors === undefined || selectedMeta.contributors === null ? "" : +
    + {selectedMeta.contributors.slice(0,7).map((data, index) => { + return ( + + + {data.url} + + + ) + })} +
    + } +
    +
    + } + return ( {props.level !== 1 ? : null} {element} + {extraInfo} ) } @@ -239,7 +283,12 @@ const Docs = (props) => {
    - {list.map((item, index) => { + {list.map((data, index) => { + const item = data.name + if (item === undefined) { + return null + } + const path = "/docs/"+item const newname = item.charAt(0).toUpperCase()+item.substring(1).split("_").join(" ").split("-").join(" ") return ( @@ -294,7 +343,8 @@ const Docs = (props) => { open={Boolean(anchorEl)} onClose={handleClose} > - {list.map((item, index) => { + {list.map((data, index) => { + const item = data.name const path = "/docs/"+item const newname = item.charAt(0).toUpperCase()+item.substring(1).split("_").join(" ").split("-").join(" ") return ( From 9d1b5a41e9f352d0c81f93abb71d6a1f63c0fbbe Mon Sep 17 00:00:00 2001 From: frikky Date: Mon, 27 Sep 2021 03:01:36 +0200 Subject: [PATCH 089/117] Fixed response forwarding for OpenAPI apps with all details --- backend/app_sdk/app_base.py | 94 +++++++++-------- backend/app_sdk/build.sh | 2 +- frontend/src/views/Apps.jsx | 10 -- frontend/src/views/Docs.jsx | 64 ++++++++---- frontend/src/views/Workflows.jsx | 171 ------------------------------- 5 files changed, 99 insertions(+), 242 deletions(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index ee1f4cd4..f2a7c3f1 100644 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -36,6 +36,16 @@ class AppBase: self.start_time = int(time.time()) self.result_wrapper_count = 0 + self.action_result = { + "action": self.action, + "authorization": self.authorization, + "execution_id": self.current_execution_id, + "result": f"", + "started_at": self.start_time, + "status": "", + "completed_at": int(time.time()), + } + if isinstance(self.action, str): try: self.action = json.loads(self.action) @@ -489,7 +499,7 @@ class AppBase: #print(f"NEW PARAMS: {new_params}") if len(new_params) == 0: print("[WARNING] SHOULD STOP MULTI-EXECUTION BECAUSE FIELDS AREN'T UNIQUE") - action_result = { + self.action_result = { "action": self.action, "authorization": self.authorization, "execution_id": self.current_execution_id, @@ -499,7 +509,7 @@ class AppBase: "completed_at": int(time.time()), } - self.send_result(action_result, {"Content-Type": "application/json", "Authorization": "Bearer %s" % self.authorization}, "/api/v1/streams") + self.send_result(self.action_result, {"Content-Type": "application/json", "Authorization": "Bearer %s" % self.authorization}, "/api/v1/streams") exit() #return else: @@ -834,7 +844,7 @@ class AppBase: # !!! Let this line stay - its used for some horrible codegeneration / stitching !!! # #STARTCOPY stream_path = "/api/v1/streams" - action_result = { + self.action_result = { "action": action, "authorization": self.authorization, "execution_id": self.current_execution_id, @@ -861,20 +871,20 @@ class AppBase: if len(self.action) == 0: print("ACTION env not defined") - action_result["result"] = "Error in setup ENV: ACTION not defined" - self.send_result(action_result, headers, stream_path) + self.action_result["result"] = "Error in setup ENV: ACTION not defined" + self.send_result(self.action_result, headers, stream_path) return if len(self.authorization) == 0: print("AUTHORIZATION env not defined") - action_result["result"] = "Error in setup ENV: AUTHORIZATION not defined" - self.send_result(action_result, headers, stream_path) + self.action_result["result"] = "Error in setup ENV: AUTHORIZATION not defined" + self.send_result(self.action_result, headers, stream_path) return if len(self.current_execution_id) == 0: print("EXECUTIONID env not defined") - action_result["result"] = "Error in setup ENV: EXECUTIONID not defined" - self.send_result(action_result, headers, stream_path) + self.action_result["result"] = "Error in setup ENV: EXECUTIONID not defined" + self.send_result(self.action_result, headers, stream_path) return @@ -922,21 +932,21 @@ class AppBase: except json.decoder.JSONDecodeError: pass - action_result["result"] = "Bad result from backend: %d" % ret.status_code - self.send_result(action_result, headers, stream_path) + self.action_result["result"] = "Bad result from backend: %d" % ret.status_code + self.send_result(self.action_result, headers, stream_path) return except requests.exceptions.ConnectionError as e: self.logger.info("Connectionerror: %s" % e) - action_result["result"] = "Connection error during startup: %s" % e - self.send_result(action_result, headers, stream_path) + self.action_result["result"] = "Connection error during startup: %s" % e + self.send_result(self.action_result, headers, stream_path) return else: try: fullexecution = json.loads(self.full_execution) except json.decoder.JSONDecodeError as e: print("Json decode execution error: %s" % e) - action_result["result"] = "Json error during startup: %s" % e - self.send_result(action_result, headers, stream_path) + self.action_result["result"] = "Json error during startup: %s" % e + self.send_result(self.action_result, headers, stream_path) return print("") @@ -1993,10 +2003,10 @@ class AppBase: if not branchcheck: self.logger.info("Failed one or more branch conditions.") - action_result["result"] = tmpresult - action_result["status"] = "SKIPPED" + self.action_result["result"] = tmpresult + self.action_result["status"] = "SKIPPED" try: - ret = requests.post("%s%s" % (self.base_url, stream_path), headers=headers, json=action_result) + ret = requests.post("%s%s" % (self.base_url, stream_path), headers=headers, json=self.action_result) self.logger.info("Result: %d" % ret.status_code) if ret.status_code != 200: self.logger.info(ret.text) @@ -2021,8 +2031,8 @@ class AppBase: func = getattr(self, actionname, None) if func == None: self.logger.debug(f"Failed executing {actionname} because func is None.") - action_result["status"] = "FAILURE" - action_result["result"] = "Function %s doesn't exist." % actionname + self.action_result["status"] = "FAILURE" + self.action_result["result"] = "Function %s doesn't exist." % actionname elif callable(func): try: if len(action["parameters"]) < 1: @@ -2385,9 +2395,9 @@ class AppBase: print("LENGTH OF ARR: %d" % len(resultarray)) if len(resultarray) == 0: print("[WARNING] Returning empty array because the array length to be looped is 0 (0)") - action_result["status"] = "SUCCESS" - action_result["result"] = "[]" - self.send_result(action_result, headers, stream_path) + self.action_result["status"] = "SUCCESS" + self.action_result["result"] = "[]" + self.send_result(self.action_result, headers, stream_path) return #print("RESULTARRAY: %s" % resultarray) @@ -2531,10 +2541,10 @@ class AppBase: params = new_params[0] else: print("[WARNING] SHOULD STOP EXECUTION BECAUSE FIELDS AREN'T UNIQUE") - action_result["status"] = "SKIPPED" - action_result["result"] = f"A non-unique value was found" - action_result["completed_at"] = int(time.time()) - self.send_result(action_result, headers, stream_path) + self.action_result["status"] = "SKIPPED" + self.action_result["result"] = f"A non-unique value was found" + self.action_result["completed_at"] = int(time.time()) + self.send_result(self.action_result, headers, stream_path) return print("[INFO] Running normal execution (not loop)\n") @@ -2764,46 +2774,46 @@ class AppBase: print("Normal result - no list?") result = results - action_result["status"] = "SUCCESS" - action_result["result"] = str(result) - if action_result["result"] == "": - action_result["result"] = result + self.action_result["status"] = "SUCCESS" + self.action_result["result"] = str(result) + if self.action_result["result"] == "": + self.action_result["result"] = result self.logger.debug(f"Executed {action['label']}-{action['id']}")#with result: {result}") #self.logger.debug(f"Data: %s" % action_result) except TypeError as e: print("TypeError issue: %s" % e) - action_result["status"] = "FAILURE" - action_result["result"] = "TypeError: %s" % str(e) + self.action_result["status"] = "FAILURE" + self.action_result["result"] = "TypeError: %s" % str(e) else: print("Function %s doesn't exist?" % action["name"]) self.logger.error(f"App {self.__class__.__name__}.{action['name']} is not callable") - action_result["status"] = "FAILURE" - action_result["result"] = "Function %s is not callable." % actionname + self.action_result["status"] = "FAILURE" + self.action_result["result"] = "Function %s is not callable." % actionname # https://ptb.discord.com/channels/747075026288902237/882017498550112286/882043773138382890 except (requests.exceptions.RequestException, TimeoutError) as e: print(f"Failed to execute request: {e}") self.logger.exception(f"Failed to execute {e}-{action['id']}") - action_result["status"] = "SUCCESS" + self.action_result["status"] = "SUCCESS" try: - action_result["result"] = json.dumps({ + self.action_result["result"] = json.dumps({ "success": False, "reason": f"Request error - failing silently. Details: {e}" }) except json.decoder.JSONDecodeError as e: - action_result["result"] = f"Request error: {e}" + self.action_result["result"] = f"Request error: {e}" except Exception as e: print(f"Failed to execute: {e}") self.logger.exception(f"Failed to execute {e}-{action['id']}") - action_result["status"] = "FAILURE" - action_result["result"] = f"General exception: {e}" + self.action_result["status"] = "FAILURE" + self.action_result["result"] = f"General exception: {e}" - action_result["completed_at"] = int(time.time()) + self.action_result["completed_at"] = int(time.time()) # Send the result :) - self.send_result(action_result, headers, stream_path) + self.send_result(self.action_result, headers, stream_path) return @classmethod diff --git a/backend/app_sdk/build.sh b/backend/app_sdk/build.sh index 85b1b238..30560f50 100644 --- a/backend/app_sdk/build.sh +++ b/backend/app_sdk/build.sh @@ -3,7 +3,7 @@ ### DEFAULT NAME=shuffle-app_sdk -VERSION=0.9.19 +VERSION=0.9.20 docker rmi docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION --force docker build . -f Dockerfile -t frikky/shuffle:app_sdk -t frikky/$NAME:$VERSION -t docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION -t ghcr.io/frikky/$NAME:$VERSION diff --git a/frontend/src/views/Apps.jsx b/frontend/src/views/Apps.jsx index 8a2ff410..cf113e8e 100644 --- a/frontend/src/views/Apps.jsx +++ b/frontend/src/views/Apps.jsx @@ -9,7 +9,6 @@ import { useTheme } from '@material-ui/core/styles'; import YAML from 'yaml' import {Link} from 'react-router-dom'; -import ReactJson from 'react-json-view' import { useAlert } from "react-alert"; import Dropzone from '../components/Dropzone'; @@ -620,15 +619,6 @@ const Apps = (props) => { ) })} - {/* - - */}
    ) } diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index 3b9f466b..cec08ba1 100644 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -6,7 +6,7 @@ import {BrowserView, MobileView} from "react-device-detect"; import {Link} from 'react-router-dom'; import {Tooltip, Divider, Button, Menu, MenuItem, Typography, Paper, List} from '@material-ui/core'; -import {Edit as EditIcon} from '@material-ui/icons'; +import {Link as LinkIcon, Edit as EditIcon} from '@material-ui/icons'; const Body = { maxWidth: '1000px', @@ -28,6 +28,7 @@ const Docs = (props) => { const { globalUrl, selectedDoc, serverside, isMobile, } = props; const theme = useTheme(); + const [mobile, setMobile] = useState(isMobile === true ? true : false); const [data, setData] = useState(""); const [firstrequest, setFirstrequest] = useState(true); const [list, setList] = useState([]); @@ -107,14 +108,21 @@ const Docs = (props) => { if (firstrequest) { setFirstrequest(false) + if (!serverside) { + if (window.innerWidth < 768) { + setMobile(true) + } + } if (selectedDoc !== undefined) { setData(selectedDoc.reason) setList(selectedDoc.list) setListLoaded(true) } else { - fetchDocList() - fetchDocs(props.match.params.key) + if (!serverside) { + fetchDocList() + fetchDocs(props.match.params.key) + } } } @@ -194,10 +202,10 @@ const Docs = (props) => { const markdownStyle = { color: "rgba(255, 255, 255, 0.65)", flex: "1", - maxWidth: isMobile ? "100%" : 750, + maxWidth: mobile ? "100%" : 750, overflow: "hidden", paddingBottom: 200, - marginLeft: isMobile ? 0 : 275, + marginLeft: mobile ? 0 : 275, } function OuterLink(props) { @@ -221,34 +229,39 @@ const Docs = (props) => { ) } - function Heading(props) { + const Heading = (props) => { const element = React.createElement(`h${props.level}`, {style: {marginTop: 50}}, props.children) + const [hover, setHover] = useState(false) var extraInfo = "" if (props.level === 1) { extraInfo =
    - - - - - -
    - + {mobile ? null : + + + + + + } + {mobile ? null : +
    + } + {selectedMeta.read_time} minute{selectedMeta.read_time === 1 ? "" : "s"} to read
    - {selectedMeta.contributors === undefined || selectedMeta.contributors === null ? "" : + {mobile || selectedMeta.contributors === undefined || selectedMeta.contributors === null ? "" :
    {selectedMeta.contributors.slice(0,7).map((data, index) => { return ( - {data.url} + {data.url} ) @@ -260,9 +273,20 @@ const Docs = (props) => { } return ( - + { + setHover(true) + }} > {props.level !== 1 ? : null} {element} + {/*hover ? {setHover(true)}} style={{cursor: "pointer", display: "inline", }} onClick={() => { + window.location.href += "#hello" + console.log(window.location) + //window.history.pushState('page2', 'Title', '/page2.php'); + //window.history.replaceState('page2', 'Title', '/page2.php'); + }} /> + : "" + */} {extraInfo} ) @@ -345,6 +369,10 @@ const Docs = (props) => { > {list.map((data, index) => { const item = data.name + if (item === undefined) { + return null + } + const path = "/docs/"+item const newname = item.charAt(0).toUpperCase()+item.substring(1).split("_").join(" ").split("-").join(" ") return ( diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index 58dd9625..2d3150dc 100644 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -12,7 +12,6 @@ import {DataGrid, GridToolbarContainer, GridDensitySelector, GridToolbar} from ' //import JSONPretty from 'react-json-pretty'; //import JSONPrettyMon from 'react-json-pretty/dist/monikai' -import ReactJson from 'react-json-view' import Dropzone from '../components/Dropzone'; import {Link} from 'react-router-dom'; @@ -1372,181 +1371,11 @@ const Workflows = (props) => { return string.split(search).join(replace); } - const resultsPaper = (data) => { - var boxWidth = "2px" - var boxColor = "orange" - if (data.status === "ABORTED" || data.status === "UNFINISHED" || data.status === "FAILURE"){ - boxColor = "red" - } else if (data.status === "FINISHED" || data.status === "SUCCESS") { - boxColor = "green" - } else if (data.status === "SKIPPED" || data.status === "EXECUTING") { - boxColor = "yellow" - } else { - boxColor = "green" - } - var t = new Date(data.started_at*1000) - var showResult = data.result.trim() - const validate = validateJson(showResult) - - if (validate.valid) { - showResult = - } else { - // FIXME - have everything parsed as json, either just for frontend - // or in the backend? - /* - const newdata = {"result": data.result} - showResult = - */ - } - - return ( - {}}> -
    -
    - - - -

    Name: {data.action.label}

    -
    - - App: {data.action.app_name}, Version: {data.action.app_version} - - - Action: {data.action.name}, Environment: {data.action.environment}, Status: {data.status} - -
    - - Started: {t.toISOString()} - -
    - -
    - - {showResult} - -
    -
    -
    -
    - ) - } - - const resultsHandler = Object.getOwnPropertyNames(selectedExecution).length > 0 && selectedExecution.results !== null ? -
    - {selectedExecution.results.sort((a, b) => a.started_at - b.started_at).map((data, index) => { - return ( -
    - {resultsPaper(data)} -
    - ) - })} -
    - : -
    - No results yet -
    const resultsLength = Object.getOwnPropertyNames(selectedExecution).length > 0 && selectedExecution.results !== null ? selectedExecution.results.length : 0 - const ExecutionDetails = () => { - var starttime = new Date(selectedExecution.started_at*1000) - var endtime = new Date(selectedExecution.started_at*1000) - var parsedArgument = selectedExecution.execution_argument - if (selectedExecution.execution_argument !== undefined && selectedExecution.execution_argument.length > 0) { - parsedArgument = replaceAll(parsedArgument, " None", " \"None\""); - } - - var arg = null - if (selectedExecution.execution_argument !== undefined && selectedExecution.execution_argument.length > 0) { - var showResult = selectedExecution.execution_argument.trim() - const validate = validateJson(showResult) - - arg = validate.valid ? - - : showResult - } - - var lastresult = null - if (selectedExecution.result !== undefined && selectedExecution.result.length > 0) { - var showResult = selectedExecution.result.trim() - const validate = validateJson(showResult) - lastresult = validate.valid ? - - : showResult - } - - /* -
    - ID: {selectedExecution.execution_id} -
    -
    - Last node: {selectedExecution.workflow.actions.find(data => data.id === selectedExecution.last_node).actions[0].label} -
    - */ - if (Object.getOwnPropertyNames(selectedExecution).length > 0 && selectedExecution.workflow.actions !== null) { - return ( -
    -
    - Status: {selectedExecution.status} -
    -
    - Started: {starttime.toISOString()} -
    -
    - Finished: {endtime.toISOString()} -
    - {/* -
    - Last Result: {lastresult} -
    - */} -
    - {arg} -
    - - {resultsHandler} -
    - ) - } - - return ( - executionLoading ? -
    - -
    - : -

    - There are no executiondetails yet. Click "execute" to run your first one. -

    - - ) - } // Can create and set workflows const setNewWorkflow = (name, description, tags, defaultReturnValue, editingWorkflow, redirect) => { From d1e4f409a67129e73d93f953c63ec809465cb56d Mon Sep 17 00:00:00 2001 From: frikky Date: Wed, 29 Sep 2021 03:30:13 +0200 Subject: [PATCH 090/117] Loads of updates to extensions and their documentation --- backend/app_sdk/Dockerfile | 13 +- backend/app_sdk/app_base.py | 167 +++++++++++++----- backend/go-app/go.mod | 4 +- docker-compose.yml | 2 +- frontend/src/views/AppCreator.jsx | 8 + functions/extensions/aws-lambda/README.md | 2 - functions/extensions/aws-lambda/s3_deploy.sh | 16 -- .../extensions/aws-lambda/s3_function.py | 26 --- functions/extensions/kafka/README.md | 4 - functions/extensions/kafka/docker-compose.yml | 16 -- functions/extensions/kafka/kafka_local.py | 87 --------- functions/extensions/kafka/requirements.txt | 2 - functions/extensions/misp/Dockerfile | 23 +++ functions/extensions/misp/docker-compose.yml | 9 + functions/extensions/misp/requirements.txt | 2 + functions/extensions/misp/sub.py | 57 ++++++ 16 files changed, 232 insertions(+), 206 deletions(-) delete mode 100644 functions/extensions/aws-lambda/README.md delete mode 100644 functions/extensions/aws-lambda/s3_deploy.sh delete mode 100644 functions/extensions/aws-lambda/s3_function.py delete mode 100644 functions/extensions/kafka/README.md delete mode 100644 functions/extensions/kafka/docker-compose.yml delete mode 100644 functions/extensions/kafka/kafka_local.py delete mode 100644 functions/extensions/kafka/requirements.txt create mode 100644 functions/extensions/misp/Dockerfile create mode 100644 functions/extensions/misp/docker-compose.yml create mode 100644 functions/extensions/misp/requirements.txt create mode 100644 functions/extensions/misp/sub.py diff --git a/backend/app_sdk/Dockerfile b/backend/app_sdk/Dockerfile index b2febec6..e50fd25a 100644 --- a/backend/app_sdk/Dockerfile +++ b/backend/app_sdk/Dockerfile @@ -5,15 +5,12 @@ RUN apk --no-cache add --update alpine-sdk libffi libffi-dev musl-dev openssl-de RUN mkdir /install WORKDIR /install +COPY requirements.txt /requirements.txt +RUN pip3 install --prefix="/install" -r /requirements.txt FROM base - -#--no-cache -RUN apk update && apk add --update tzdata libmagic alpine-sdk libffi libffi-dev musl-dev openssl-dev - COPY --from=builder /install /usr/local -COPY requirements.txt /requirements.txt -RUN pip3 install -r /requirements.txt -COPY __init__.py /app/walkoff_app_sdk/__init__.py -COPY app_base.py /app/walkoff_app_sdk/app_base.py +WORKDIR /app +COPY sub.py /app/sub.py +CMD python sub.py diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index f2a7c3f1..e9622eca 100644 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -1644,6 +1644,86 @@ class AppBase: return template + # Suboptimal cleanup script for BOdy parsing of OpenAPI + # Should have a regex which looks for the value, then goes out and cleans up the key + def recurse_cleanup_script(data): + try: + if not isinstance(data, dict): + newvalue = json.loads(data) + else: + newvalue = data + + deletekeys = [] + for key, value in newvalue.items(): + #print("%s: %s" % (key, value)) + if isinstance(value, str) and len(value) == 0: + deletekeys.append(key) + continue + + if isinstance(value, list): + try: + value = json.dumps(value) + print(value) + except: + print("Json parsing issue in recursed value") + pass + + if value == "${%s}" % key: + print("Deleting %s because key = value" % key) + deletekeys.append(key) + continue + elif "${" in value and "}" in value: + print("Deleting %s because it contains ${ and }" % key) + deletekeys.append(key) + continue + + if isinstance(value, dict): + newvalue[key] = recurse_cleanup_script(value) + + except json.decoder.JSONDecodeError as e: + print("Failed JSON replacement for OpenAPI keys (3) {e}") + + for deletekey in deletekeys: + try: + del newvalue[deletekey] + except: + pass + + try: + #print("Post delete: %s" % newvalue) + for key, value in newvalue.items(): + if isinstance(value, bool): + continue + elif isinstance(value, dict) and not bool(value): + continue + + try: + value = json.loads(value) + newvalue[key] = value + except json.decoder.JSONDecodeError as e: + #print("Inner overwrite issue for \"%s\": %s" % (key, e)) + continue + except Exception as e: + #print("General error in newvalue items loop: %s" % e) + continue + + try: + data = json.dumps(newvalue) + except json.decoder.JSONDecodeError as e: + print("[WARNING] JsonDecodeError: %s" % e) + data = newvalue + + except json.decoder.JSONDecodeError as e: + print("Failed JSON replacement for OpenAPI keys (2) {e}") + + #if isinstance(data, str): + # tmpdata = json.dumps(data) + # print(tmpdata) + # foundvalue = re.findall(".*?(${\w+})", tmpdata, re.MULTILINE) + # print("FOUND: %s", foundvalue) + + return data + # Parses parameters sent to it and returns whether it did it successfully with the values found def parse_params(action, fullexecution, parameter, self): # Skip if it starts with $? @@ -2112,60 +2192,63 @@ class AppBase: print("KeyError body OpenAPI: %s" % e) pass - + print(f"""HANDLING {action["parameters"][counter]["value"]}""") - try: - newvalue = json.loads(action["parameters"][counter]["value"]) - deletekeys = [] - for key, value in newvalue.items(): - print("%s: %s" % (key, value)) - if isinstance(value, str) and len(value) == 0: - deletekeys.append(key) - continue + action["parameters"][counter]["value"] = recurse_cleanup_script(action["parameters"][counter]["value"]) + #try: + # newvalue = json.loads(action["parameters"][counter]["value"]) + # deletekeys = [] + # for key, value in newvalue.items(): + # print("%s: %s" % (key, value)) + # if isinstance(value, str) and len(value) == 0: + # deletekeys.append(key) + # continue - if value == "${%s}" % key: - print("Deleting %s because key = value" % key) - deletekeys.append(key) - continue - - for deletekey in deletekeys: - try: - del newvalue[deletekey] - except: - pass + # if value == "${%s}" % key: + # print("Deleting %s because key = value" % key) + # deletekeys.append(key) + # continue + # + # for deletekey in deletekeys: + # try: + # del newvalue[deletekey] + # except: + # pass - #print("Post delete: %s" % newvalue) - for key, value in newvalue.items(): - if isinstance(value, bool): - continue + # #print("Post delete: %s" % newvalue) + # for key, value in newvalue.items(): + # if isinstance(value, bool): + # continue - try: - value = json.loads(value) - newvalue[key] = value - except json.decoder.JSONDecodeError as e: - print("Inner overwrite issue: %s" % e) - continue - except: - print("General error in newvalue items loop") - continue + # try: + # value = json.loads(value) + # newvalue[key] = value + # except json.decoder.JSONDecodeError as e: + # print("Inner overwrite issue: %s" % e) + # continue + # except Exception as e: + # print("General error in newvalue items loop: %s" % e) + # continue - try: - action["parameters"][counter]["value"] = json.dumps(newvalue) - except json.decoder.JSONDecodeError as e: - print("[WARNING] JsonDecodeError: %s" % e) - action["parameters"][counter]["value"] = newvalue - + # try: + # action["parameters"][counter]["value"] = json.dumps(newvalue) + # except json.decoder.JSONDecodeError as e: + # print("[WARNING] JsonDecodeError: %s" % e) + # action["parameters"][counter]["value"] = newvalue + # - except json.decoder.JSONDecodeError as e: - print("Failed JSON replacement for OpenAPI keys (2) {e}") - - break + #except json.decoder.JSONDecodeError as e: + # print("Failed JSON replacement for OpenAPI keys (2) {e}") #print(action["parameters"]) + + # This seems redundant now print("Pre parameters") for parameter in newparams: action["parameters"].append(parameter) + self.action = action + # calltimes is used to handle forloops in the app itself. # 2 kinds of loop - one in gui with one app each, and one like this, # which is super fast, but has a bad overview (potentially good tho) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 431adaf0..13cc619d 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -2,7 +2,7 @@ module shuffle 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 @@ -23,7 +23,7 @@ require ( github.com/docker/go-units v0.4.0 // indirect github.com/elastic/go-elasticsearch/v7 v7.13.1 // indirect github.com/frikky/kin-openapi v0.39.0 - github.com/frikky/shuffle-shared v0.0.99 + github.com/frikky/shuffle-shared v0.1.1 github.com/fsouza/go-dockerclient v1.7.2 github.com/ghodss/yaml v1.0.0 github.com/go-git/go-billy/v5 v5.0.0 diff --git a/docker-compose.yml b/docker-compose.yml index edb20333..c033b414 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '3' services: frontend: - #build: ./frontend + build: ./frontend image: ghcr.io/frikky/shuffle-frontend:nightly container_name: shuffle-frontend hostname: shuffle-frontend diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx index f44ce565..5b3f0be2 100644 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -1331,6 +1331,10 @@ const AppCreator = (props) => { const headersplit = header.split("= ") key = headersplit[0] value = headersplit[1] + } else if (header.length > 0 && header.includes(" =")) { + const headersplit = header.split(" =") + key = headersplit[0] + value = headersplit[1] } else if (header.length > 0 && header.includes("=")) { const headersplit = header.split("=") key = headersplit[0] @@ -1339,6 +1343,10 @@ const AppCreator = (props) => { const headersplit = header.split(": ") key = headersplit[0] value = headersplit[1] + } else if (header.length > 0 && header.includes(" :")) { + const headersplit = header.split(" :") + key = headersplit[0] + value = headersplit[1] } else if (header.length > 0 && header.includes(":")) { const headersplit = header.split(":") key = headersplit[0] diff --git a/functions/extensions/aws-lambda/README.md b/functions/extensions/aws-lambda/README.md deleted file mode 100644 index f665851a..00000000 --- a/functions/extensions/aws-lambda/README.md +++ /dev/null @@ -1,2 +0,0 @@ -# AWS Lambda forwarder to Shuffle -This function is made to forward S3 notifications to Shuffle to run a workflow when an object is made or updated. diff --git a/functions/extensions/aws-lambda/s3_deploy.sh b/functions/extensions/aws-lambda/s3_deploy.sh deleted file mode 100644 index cedda90f..00000000 --- a/functions/extensions/aws-lambda/s3_deploy.sh +++ /dev/null @@ -1,16 +0,0 @@ -#GOOS=linux go build main.go -zip s3_function.zip s3_function.py - -WEBHOOK=https://shuffler.io/api/v1/hooks/webhook_eccf47b1-8f6a-49fc-b2b8-383365a22353 -ROLE=arn:aws:iam::202262580068:role/service-role/shuffle-forwarder -REGION=us-east-1 -BUCKETNAME=helo - -aws lambda create-function \ - --role $ROLE \ - --region $REGION \ - --function-name shuffler-webhook-forwarder-3 \ - --zip-file fileb://s3_function.zip \ - --runtime python3.9 \ - --environment Variables={SHUFFLE_WEBHOOK=$WEBHOOK} \ - --handler lambda_handler diff --git a/functions/extensions/aws-lambda/s3_function.py b/functions/extensions/aws-lambda/s3_function.py deleted file mode 100644 index c5e11cde..00000000 --- a/functions/extensions/aws-lambda/s3_function.py +++ /dev/null @@ -1,26 +0,0 @@ -import json -import urllib.parse -import requests -import os - -print('Loading function') - -def lambda_handler(event, context): - #print("Received event: " + json.dumps(event, indent=2)) - - # Get the object from the event and show its content type - bucket = event['Records'][0]['s3']['bucket']['name'] - - print(type(event)) - print("Getting bucket: %s" % bucket) - webhook = os.environ.get("SHUFFLE_WEBHOOK") - if not webhook: - return "No webhook environment defined: SHUFFLE_WEBHOOK" - - ret = requests.post(webhook, json=event["Records"][0]) - if ret.status_code != 200: - return "Bad status code for webhook: %d" % ret.status_code - - print("Status code: %d\nData: %s" % (ret.status_code, ret.text)) - - # response = s3.get_object(Bucket=bucket, Key=key) diff --git a/functions/extensions/kafka/README.md b/functions/extensions/kafka/README.md deleted file mode 100644 index dceb22a3..00000000 --- a/functions/extensions/kafka/README.md +++ /dev/null @@ -1,4 +0,0 @@ -``` -curl -sSL https://raw.githubusercontent.com/bitnami/bitnami-docker-kafka/master/docker-compose.yml > docker-compose.yml -docker-compose up -d -``` diff --git a/functions/extensions/kafka/docker-compose.yml b/functions/extensions/kafka/docker-compose.yml deleted file mode 100644 index 02e96798..00000000 --- a/functions/extensions/kafka/docker-compose.yml +++ /dev/null @@ -1,16 +0,0 @@ -version: '2' -services: - zookeeper: - image: wurstmeister/zookeeper - ports: - - "2181:2181" - kafka: - build: . - ports: - - "9092" - environment: - DOCKER_API_VERSION: 1.40 - KAFKA_ADVERTISED_HOST_NAME: 192.168.193.140 - KAFKA_ZOOKEEPER_CONNECT: zookeeper:2181 - volumes: - - /var/run/docker.sock:/var/run/docker.sock diff --git a/functions/extensions/kafka/kafka_local.py b/functions/extensions/kafka/kafka_local.py deleted file mode 100644 index 8ca8a6a5..00000000 --- a/functions/extensions/kafka/kafka_local.py +++ /dev/null @@ -1,87 +0,0 @@ -import json -import os -import requests -from time import sleep -from kafka import KafkaProducer, KafkaConsumer -import kafka - -shuffle_url = os.getenv("SHUFFLE_URL") -shuffle_apikey = os.getenv("SHUFFLE_APIKEY") -shuffle_workflow = os.getenv("SHUFFLE_WORKFLOW") - -headers = {"Authorization": "Bearer %s" % shuffle_apikey} -topic = "workflow_%s" % shuffle_workflow -group = "testing" -server = "localhost:9092" -def produce(): - try: - #group_id=group, - #value_serializer=lambda x: json.dumps(x).encode('utf-8') - producer = KafkaProducer( - bootstrap_servers=[server], - ) - except kafka.errors.NoBrokersAvailable as e: - print(f"Error with producer: {e}") - sleep(5) - produce() - return - except ValueError as e: - print(f"ValuError with producer: {e}") - sleep(5) - produce() - return - - - for i in range(15): - producer.send(topic, json.dumps({"some": i, "data": "luuuuul"})) - - producer.flush() - producer.close() - exit() - - #print(f"Adding data {data}") - - #try: - # ret = producer.send(topic, value=data) - # print(ret.get()) - # producer.flush() - - # #future = producer.send('foobar', b'another_message') - # #result = future.get(timeout=60) - # #print(result) - - #except kafka.errors.KafkaTimeoutError as e: - # print("Kafka error: %s" % e) - # continue - -def consume(): - print("Starting consumer") - #group_id=group, - #auto_offset_reset="earliest", - #enable_auto_commit=True, - consumer = KafkaConsumer( - topic, - bootstrap_servers=[server], - auto_offset_reset='earliest', - max_poll_records=2, - ) - #value_deserializer=lambda x: json.loads(x.decode('utf-8')) - - consumer.poll() - #consumer.seek_to_beginning() - - print(f"Getting data from topic {topic}") - for message in consumer: - #message = message.value - #message = message - print("MSG: ", message) - ret = requests.post("%s/api/v1/%s/execute" % shuffle_url, headers=headers, data=message) - print(ret.status_code) - print(ret.text) - -if __name__ == "__main__": - if topic == "workflow_None": - topic = "testing" - print("Starting producer on %s for topic %s" % (server, topic)) - produce() - #consume() diff --git a/functions/extensions/kafka/requirements.txt b/functions/extensions/kafka/requirements.txt deleted file mode 100644 index 652ae858..00000000 --- a/functions/extensions/kafka/requirements.txt +++ /dev/null @@ -1,2 +0,0 @@ -kafka -kafka-python diff --git a/functions/extensions/misp/Dockerfile b/functions/extensions/misp/Dockerfile new file mode 100644 index 00000000..f2b8ae3f --- /dev/null +++ b/functions/extensions/misp/Dockerfile @@ -0,0 +1,23 @@ +FROM python:3.9.4-alpine as base + +FROM base as builder + +RUN mkdir /install +WORKDIR /install + +FROM base +RUN apk add g++ + +COPY --from=builder /install /usr/local +COPY requirements.txt /requirements.txt +RUN pip3 install -r /requirements.txt + + +RUN mkdir /app +WORKDIR /app +COPY requirements.txt /app/requirements.txt +RUN python3 -m pip install -r /app/requirements.txt + +COPY sub.py /app/sub.py + +CMD ["python3", "sub.py"] diff --git a/functions/extensions/misp/docker-compose.yml b/functions/extensions/misp/docker-compose.yml new file mode 100644 index 00000000..5b0130c0 --- /dev/null +++ b/functions/extensions/misp/docker-compose.yml @@ -0,0 +1,9 @@ +version: '3' +services: + zmq: + image: ghcr.io/frikky/shuffle-zmq:latest + environment: + - ZMQ_HOSTNAME=localhost + - ZMQ_PORT=50000 + - ZMQ_FORWARD_URL=https://shuffler.io/api/v1/hooks/webhook_e09bea36-9976-1421-82bc-b8764ca83c1e + restart: unless-stopped diff --git a/functions/extensions/misp/requirements.txt b/functions/extensions/misp/requirements.txt new file mode 100644 index 00000000..a1cdf7dd --- /dev/null +++ b/functions/extensions/misp/requirements.txt @@ -0,0 +1,2 @@ +pyzmq +requests diff --git a/functions/extensions/misp/sub.py b/functions/extensions/misp/sub.py new file mode 100644 index 00000000..feb78c08 --- /dev/null +++ b/functions/extensions/misp/sub.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +print("Running imports") +import sys +import zmq +import json +import time +import pprint +import os +import sys +import requests + +forward_url = os.getenv("ZMQ_FORWARD_URL", "") +print("Checking forward url (ZMQ_FORWARD_URL): %s" % forward_url) +def handle_hook(data): + ret = requests.post(forward_url, json=data) + print(ret.text) + print(ret.status_code) + +def main(): + host = os.getenv("ZMQ_HOST", "localhost") + port = os.getenv("ZMQ_PORT", "50000") + + if len(forward_url) == 0: + print("Failed to start - define ZMQ_FORWARD_URL for webhook forwarder") + exit(0) + + print("Starting connection setup to %s:%s" % (host, port)) + context = zmq.Context() + socket = context.socket(zmq.SUB) + socket.connect ("tcp://%s:%s" % (host, port)) + socket.setsockopt(zmq.SUBSCRIBE, b'') + + poller = zmq.Poller() + poller.register(socket, zmq.POLLIN) + + print("Starting zmq check for %s:%s" % (host, port)) + while True: + socks = dict(poller.poll(timeout=None)) + if socket in socks and socks[socket] == zmq.POLLIN: + message = socket.recv() + #print(message) + topic, s, m = message.decode('utf-8').partition(" ") + + d = json.loads(m) + try: + # print test if you want status (heartbeat) + test = d["status"] + except KeyError: + handle_hook(d) + + time.sleep(1) + +if __name__ == "__main__": + print("In init ") + main() From 6a8d97fb0c34b41aec80a63fd4b131e08f8db310 Mon Sep 17 00:00:00 2001 From: frikky Date: Thu, 30 Sep 2021 01:26:56 +0200 Subject: [PATCH 091/117] #522: Built core part of gmail trigger --- backend/app_sdk/app_base.py | 2 +- backend/go-app/go.mod | 2 +- backend/go-app/main.go | 19 +- backend/go-app/oauth2.go | 747 +++++++++++++++++++++++-- docker-compose.yml | 2 +- frontend/src/views/AngularWorkflow.jsx | 134 ++++- frontend/src/views/Workflows.jsx | 2 +- 7 files changed, 820 insertions(+), 88 deletions(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index e9622eca..8d263caf 100644 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -1647,13 +1647,13 @@ class AppBase: # Suboptimal cleanup script for BOdy parsing of OpenAPI # Should have a regex which looks for the value, then goes out and cleans up the key def recurse_cleanup_script(data): + deletekeys = [] try: if not isinstance(data, dict): newvalue = json.loads(data) else: newvalue = data - deletekeys = [] for key, value in newvalue.items(): #print("%s: %s" % (key, value)) if isinstance(value, str) and len(value) == 0: diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 13cc619d..2de1c49a 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -2,7 +2,7 @@ module shuffle 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 diff --git a/backend/go-app/main.go b/backend/go-app/main.go index aac348fe..a7e78fed 100644 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -3605,7 +3605,7 @@ func handleCloudJob(job shuffle.CloudSyncJob) error { redirectDomain := "localhost:5001" redirectUrl := fmt.Sprintf("http://%s/api/v1/triggers/outlook/register", redirectDomain) - outlookClient, _, err := getOutlookClient(ctx, "", hook.OauthToken, redirectUrl) + outlookClient, _, err := shuffle.GetOutlookClient(ctx, "", hook.OauthToken, redirectUrl) if err != nil { log.Printf("Oauth client failure - triggerauth: %s", err) return err @@ -5824,8 +5824,6 @@ func initHandlers() { r.HandleFunc("/api/v1/workflows/download_remote", loadSpecificWorkflows).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/workflows/{key}/execute", executeWorkflow).Methods("GET", "POST", "OPTIONS") r.HandleFunc("/api/v1/workflows/{key}/schedule/{schedule}", stopSchedule).Methods("DELETE", "OPTIONS") - r.HandleFunc("/api/v1/workflows/{key}/outlook", createOutlookSub).Methods("POST", "OPTIONS") - r.HandleFunc("/api/v1/workflows/{key}/outlook/{triggerId}", handleDeleteOutlookSub).Methods("DELETE", "OPTIONS") r.HandleFunc("/api/v1/workflows/{key}", deleteWorkflow).Methods("DELETE", "OPTIONS") r.HandleFunc("/api/v1/workflows/{key}", shuffle.SaveWorkflow).Methods("PUT", "OPTIONS") r.HandleFunc("/api/v1/workflows/{key}", shuffle.GetSpecificWorkflow).Methods("GET", "OPTIONS") @@ -5843,9 +5841,24 @@ func initHandlers() { r.HandleFunc("/api/v1/get_openapi/{key}", getOpenapi).Methods("GET", "OPTIONS") // Specific triggers + r.HandleFunc("/api/v1/workflows/{key}/outlook", handleCreateOutlookSub).Methods("POST", "OPTIONS") + r.HandleFunc("/api/v1/workflows/{key}/outlook/{triggerId}", handleDeleteOutlookSub).Methods("DELETE", "OPTIONS") r.HandleFunc("/api/v1/triggers/outlook/register", handleNewOutlookRegister).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/triggers/outlook/getFolders", shuffle.HandleGetOutlookFolders).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/triggers/outlook/{key}", handleGetSpecificTrigger).Methods("GET", "OPTIONS") + r.HandleFunc("/api/v1/triggers/gmail/register", handleNewGmailRegister).Methods("GET", "OPTIONS") + r.HandleFunc("/api/v1/triggers/gmail/getFolders", shuffle.HandleGetGmailFolders).Methods("GET", "OPTIONS") + + // FIXME: This should only be cloud. Done locally with ngrok to test + r.HandleFunc("/api/v1/triggers/gmail/routing", handleGmailRouting).Methods("POST", "OPTIONS") + + r.HandleFunc("/api/v1/triggers/gmail/{key}", handleGetSpecificTrigger).Methods("GET", "OPTIONS") + r.HandleFunc("/api/v1/workflows/{key}/gmail", handleCreateGmailSub).Methods("POST", "OPTIONS") + r.HandleFunc("/api/v1/workflows/{key}/gmail/{triggerId}", handleDeleteGmailSub).Methods("DELETE", "OPTIONS") + + //r.HandleFunc("/api/v1/triggers/gmail/{key}", handleGetSpecificGmailTrigger).Methods("GET", "OPTIONS") + //r.HandleFunc("/api/v1/triggers/outlook/getFolders", shuffle.HandleGetOutlookFolders).Methods("GET", "OPTIONS") + //r.HandleFunc("/api/v1/triggers/outlook/{key}", handleGetSpecificTrigger).Methods("GET", "OPTIONS") //r.HandleFunc("/api/v1/triggers/outlook/{key}/callback", handleOutlookCallback).Methods("POST", "OPTIONS") //r.HandleFunc("/api/v1/stats/{key}", handleGetSpecificStats).Methods("GET", "OPTIONS") diff --git a/backend/go-app/oauth2.go b/backend/go-app/oauth2.go index 245c209e..d2f0b2f5 100644 --- a/backend/go-app/oauth2.go +++ b/backend/go-app/oauth2.go @@ -5,6 +5,7 @@ import ( "bytes" "context" + "encoding/base64" "encoding/json" "errors" "fmt" @@ -14,8 +15,7 @@ import ( "net/url" "strings" "time" - - "golang.org/x/oauth2" + //"golang.org/x/oauth2" ) func getOutlookAttachment(client *http.Client, emailId, attachmentId string) ([]shuffle.FullEmail, error) { @@ -131,6 +131,177 @@ func getOutlookProfile(client *http.Client) (shuffle.OutlookProfile, error) { return profile, nil } +func handleNewGmailRegister(resp http.ResponseWriter, request *http.Request) { + cors := shuffle.HandleCors(resp, request) + if cors { + return + } + + user, err := shuffle.HandleApiAuthentication(resp, request) + if err != nil { + log.Printf("[INFO] Api authentication failed in getting specific trigger: %s", err) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + code := request.URL.Query().Get("code") + if len(code) == 0 { + log.Println("No code") + resp.WriteHeader(401) + return + } + + url := fmt.Sprintf("http://%s%s", request.Host, request.URL.EscapedPath()) + log.Printf("URL: %s", url) + ctx := context.Background() + _, accessToken, err := shuffle.GetGmailClient(ctx, code, shuffle.OauthToken{}, url) + if err != nil { + log.Printf("Oauth client failure - outlook register: %s", err) + resp.WriteHeader(401) + return + } + + // This should be possible, and will also give the actual username + + /* + profile, err := getOutlookProfile(client) + if err != nil { + log.Printf("Outlook profile failure: %s", err) + resp.WriteHeader(401) + return + } + */ + + // This is a state workaround, which should really be for CSRF checks lol + state := request.URL.Query().Get("state") + if len(state) == 0 { + log.Println("No state") + resp.WriteHeader(401) + return + } + + stateitems := strings.Split(state, "%26") + if len(stateitems) == 1 { + stateitems = strings.Split(state, "&") + } + + // FIXME - trigger auth + senderUser := "" + trigger := shuffle.TriggerAuth{} + for _, item := range stateitems { + itemsplit := strings.Split(item, "%3D") + if len(itemsplit) == 1 { + itemsplit = strings.Split(item, "=") + } + + if len(itemsplit) != 2 { + continue + } + + //log.Printf("ITEM: %#v", itemsplit) + + // Do something here + if itemsplit[0] == "workflow_id" { + trigger.WorkflowId = itemsplit[1] + } else if itemsplit[0] == "trigger_id" { + trigger.Id = itemsplit[1] + } else if itemsplit[0] == "type" { + trigger.Type = itemsplit[1] + } else if itemsplit[0] == "start" { + trigger.Start = itemsplit[1] + } else if itemsplit[0] == "username" { + trigger.Username = itemsplit[1] + trigger.Owner = itemsplit[1] + senderUser = itemsplit[1] + } + } + + // THis is an override based on the user in oauth return + /* + if len(profile.Mail) > 0 { + trigger.Username = profile.Mail + } + */ + + trigger.Code = code + trigger.OauthToken = shuffle.OauthToken{ + AccessToken: accessToken.AccessToken, + TokenType: accessToken.TokenType, + RefreshToken: accessToken.RefreshToken, + Expiry: accessToken.Expiry, + } + + //log.Printf("Done with client: %#v", accessToken) + //log.Printf("Done with client2: %#v", trigger.OauthToken) + //resp.WriteHeader(401) + //return + + //log.Printf("%#v", trigger) + log.Println(trigger.WorkflowId) + log.Println(trigger.Id) + log.Println(senderUser) + log.Println(trigger.Type) + log.Printf("STARTNODE: %s", trigger.Start) + log.Printf("[INFO] Attempting to set up outlook trigger for %s", senderUser) + if trigger.WorkflowId == "" || trigger.Id == "" || senderUser == "" || trigger.Type == "" { + log.Printf("[INFO] All oauth items need to contain data to register a new state") + resp.WriteHeader(401) + return + } + + // Should also update the user + Userdata, err := shuffle.GetUser(ctx, user.Id) + if err != nil { + log.Printf("[INFO] Username %s doesn't exist (oauth2): %s", trigger.Username, err) + resp.WriteHeader(401) + return + } + + Userdata.Authentication = append(Userdata.Authentication, shuffle.UserAuth{ + Name: "Gmail", + Description: "oauth2", + Workflows: []string{trigger.WorkflowId}, + Username: trigger.Username, + Fields: []shuffle.UserAuthField{ + shuffle.UserAuthField{ + Key: "trigger_id", + Value: trigger.Id, + }, + shuffle.UserAuthField{ + Key: "username", + Value: trigger.Username, + }, + shuffle.UserAuthField{ + Key: "code", + Value: code, + }, + shuffle.UserAuthField{ + Key: "type", + Value: trigger.Type, + }, + }, + }) + + // Set apikey for the user if they don't have one + err = shuffle.SetUser(ctx, Userdata, false) + if err != nil { + log.Printf("[WARNING] Failed setting user data for %s: %s (gmail)", Userdata.Username, err) + resp.WriteHeader(401) + return + } + + err = shuffle.SetTriggerAuth(ctx, trigger) + if err != nil { + log.Printf("[WARNING] Failed to set trigger auth for %s - %s (gmail)", trigger.Username, err) + resp.WriteHeader(401) + return + } + + resp.WriteHeader(200) + resp.Write([]byte(`{"success": true}`)) +} + func handleNewOutlookRegister(resp http.ResponseWriter, request *http.Request) { cors := shuffle.HandleCors(resp, request) if cors { @@ -155,7 +326,7 @@ func handleNewOutlookRegister(resp http.ResponseWriter, request *http.Request) { url := fmt.Sprintf("http://%s%s", request.Host, request.URL.EscapedPath()) log.Println(url) ctx := context.Background() - _, accessToken, err := getOutlookClient(ctx, code, shuffle.OauthToken{}, url) + _, accessToken, err := shuffle.GetOutlookClient(ctx, code, shuffle.OauthToken{}, url) if err != nil { log.Printf("Oauth client failure - outlook register: %s", err) resp.WriteHeader(401) @@ -279,16 +450,16 @@ func handleNewOutlookRegister(resp http.ResponseWriter, request *http.Request) { }) // Set apikey for the user if they don't have one - err = shuffle.SetUser(ctx, Userdata, true) + err = shuffle.SetUser(ctx, Userdata, false) if err != nil { - log.Printf("Failed setting user data for %s: %s", Userdata.Username, err) + log.Printf("[WARNING] Failed setting user data for %s: %s (outlook)", Userdata.Username, err) resp.WriteHeader(401) return } err = shuffle.SetTriggerAuth(ctx, trigger) if err != nil { - log.Printf("Failed to set trigger auth for %s - %s", trigger.Username, err) + log.Printf("[WARNING] Failed to set trigger auth for %s - %s (outlook)", trigger.Username, err) resp.WriteHeader(401) return } @@ -297,45 +468,6 @@ func handleNewOutlookRegister(resp http.ResponseWriter, request *http.Request) { resp.Write([]byte(`{"success": true}`)) } -// THis all of a sudden became really horrible.. fml -func getOutlookClient(ctx context.Context, code string, accessToken shuffle.OauthToken, redirectUri string) (*http.Client, *oauth2.Token, error) { - - conf := &oauth2.Config{ - ClientID: "fd55c175-aa30-4fa6-b303-09a29fb3f750", - ClientSecret: "14OBKgUpov.D7fe0~hp0z-cIQdP~SlYm.8", - Scopes: []string{ - "Mail.Read", - }, - RedirectURL: redirectUri, - Endpoint: oauth2.Endpoint{ - AuthURL: "https://login.microsoftonline.com/common/oauth2/authorize", - TokenURL: "https://login.microsoftonline.com/common/oauth2/token", - }, - } - - if len(code) > 0 { - access_token, err := conf.Exchange(ctx, code) - if err != nil { - log.Printf("Access_token issue: %s", err) - return &http.Client{}, access_token, err - } - - client := conf.Client(ctx, access_token) - return client, access_token, nil - } - - // Manually recreate the oauthtoken - access_token := &oauth2.Token{ - AccessToken: accessToken.AccessToken, - TokenType: accessToken.TokenType, - RefreshToken: accessToken.RefreshToken, - Expiry: accessToken.Expiry, - } - - client := conf.Client(ctx, access_token) - return client, access_token, nil -} - func handleGetSpecificTrigger(resp http.ResponseWriter, request *http.Request) { cors := handleCors(resp, request) if cors { @@ -427,11 +559,11 @@ func getOutlookSubscriptions(outlookClient *http.Client) (SubscriptionsWrapper, } type SubscriptionsWrapper struct { - OdataContext string `json:"@odata.context"` - Value []Subscription `json:"value"` + OdataContext string `json:"@odata.context"` + Value []OutlookSubscription `json:"value"` } -type Subscription struct { +type OutlookSubscription struct { ChangeType string `json:"changeType"` NotificationURL string `json:"notificationUrl"` Resource string `json:"resource"` @@ -440,6 +572,67 @@ type Subscription struct { Id string `json:"id"` } +type GmailSubscription struct { + TopicName string `json:"topicName"` + LabelIds []string `json:"labelIds"` +} + +func makeGmailSubscription(client *http.Client, folderIds []string, notificationURL string) (shuffle.SubResponse, error) { + fullUrl := "https://www.googleapis.com/gmail/v1/users/me/watch" + + // FIXME - this expires rofl + //t := time.Now().Local().Add(time.Minute * time.Duration(4200)) + //timeFormat := fmt.Sprintf("%d-%02d-%02dT%02d:%02d:%02d.0000000Z", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second()) + + resource := "projects/shuffler/topics/gmail_testing" + log.Printf("[INFO] Subscription resource to get(s) for gmail: %s", resource) + sub := GmailSubscription{ + TopicName: resource, + LabelIds: folderIds, + } + //ClientState: "This is a test", + + data, err := json.Marshal(sub) + if err != nil { + log.Printf("Marshal: %s", err) + return shuffle.SubResponse{}, err + } + + req, err := http.NewRequest( + "POST", + fullUrl, + bytes.NewBuffer(data), + ) + req.Header.Add("Content-Type", "application/json") + res, err := client.Do(req) + if err != nil { + log.Printf("[WARNING] GMAIL Client: %s", err) + return shuffle.SubResponse{}, err + } + + log.Printf("[INFO] Subscription on GMAIL Status: %d", res.StatusCode) + body, err := ioutil.ReadAll(res.Body) + if err != nil { + log.Printf("[WARNING] Gmail subscription Body: %s", err) + return shuffle.SubResponse{}, err + } + + log.Printf("GMAIL RESP: %s", string(body)) + + if res.StatusCode != 200 && res.StatusCode != 201 { + return shuffle.SubResponse{}, errors.New(fmt.Sprintf("Subscription failed: %s", string(body))) + } + + // Use data from body here to create thingy + newSub := shuffle.SubResponse{} + err = json.Unmarshal(body, &newSub) + if err != nil { + return shuffle.SubResponse{}, err + } + + return newSub, nil +} + func makeOutlookSubscription(client *http.Client, folderIds []string, notificationURL string) (string, error) { fullUrl := "https://graph.microsoft.com/v1.0/subscriptions" @@ -449,7 +642,7 @@ func makeOutlookSubscription(client *http.Client, folderIds []string, notificati resource := fmt.Sprintf("me/mailfolders('%s')/messages", strings.Join(folderIds, "','")) log.Printf("[INFO] Subscription resource to get(s): %s", resource) - sub := Subscription{ + sub := OutlookSubscription{ ChangeType: "created", ClientState: "Shuffle subscription", NotificationURL: notificationURL, @@ -489,7 +682,7 @@ func makeOutlookSubscription(client *http.Client, folderIds []string, notificati } // Use data from body here to create thingy - newSub := Subscription{} + newSub := OutlookSubscription{} err = json.Unmarshal(body, &newSub) if err != nil { return "", err @@ -574,7 +767,7 @@ func handleOutlookCallback(resp http.ResponseWriter, request *http.Request) { redirectDomain := "localhost:5001" redirectUrl := fmt.Sprintf("http://%s/api/v1/triggers/outlook/register", redirectDomain) - outlookClient, _, err := getOutlookClient(ctx, "", hook.OauthToken, redirectUrl) + outlookClient, _, err := shuffle.GetOutlookClient(ctx, "", hook.OauthToken, redirectUrl) if err != nil { log.Printf("Oauth client failure - triggerauth: %s", err) resp.WriteHeader(401) @@ -704,6 +897,96 @@ func removeOutlookSubscription(outlookClient *http.Client, subscriptionId string return nil } +func handleGmailSubRemoval(ctx context.Context, user shuffle.User, workflowId, triggerId string) error { + // 1. Get the auth for trigger + // 2. Stop the subscription + // 3. Remove the function + // 4. Remove the database entry for auth + trigger, err := shuffle.GetTriggerAuth(ctx, triggerId) + if err != nil { + log.Printf("Trigger auth %s doesn't exist - outlook sub removal.", triggerId) + return err + } + //if runningEnvironment != "cloud" { + // log.Printf("[INFO] SHOULD STOP OUTLOOK SUB ONPREM SYNC WITH CLOUD for workflow ID %s", workflowId) + // org, err := shuffle.GetOrg(ctx, user.ActiveOrg.Id) + // if err != nil { + // log.Printf("[INFO] Failed finding org %s during outlook removal: %s", org.Id, err) + // return err + // } + + // log.Printf("[INFO] Stopping cloud configuration for trigger %s in org %s for workflow %s", trigger.Id, org.Id, trigger.WorkflowId) + // action := shuffle.CloudSyncJob{ + // Type: "outlook", + // Action: "stop", + // OrgId: org.Id, + // PrimaryItemId: trigger.Id, + // SecondaryItem: trigger.Start, + // ThirdItem: trigger.WorkflowId, + // } + + // err = executeCloudAction(action, org.SyncConfig.Apikey) + // if err != nil { + // log.Printf("[INFO] Failed cloud action STOP outlook execution: %s", err) + // return err + // } else { + // log.Printf("[INFO] Successfully set STOPPED outlook execution trigger") + // } + //} else { + // log.Printf("SHOULD STOP OUTLOOK SUB IN CLOUD") + //} + + // Actually delete the thing + redirectDomain := "localhost:5001" + url := fmt.Sprintf("http://%s/api/v1/triggers/outlook/register", redirectDomain) + gmailClient, _, err := shuffle.GetGmailClient(ctx, "", trigger.OauthToken, url) + if err != nil { + log.Printf("[WARNING] Oauth client failure - gmail delete: %s", err) + return err + } + + // bytes.NewBuffer(data) + fullUrl := "https://www.googleapis.com/gmail/v1/users/me/stop" + req, err := http.NewRequest( + "POST", + fullUrl, + nil, + ) + req.Header.Add("Content-Type", "application/json") + res, err := gmailClient.Do(req) + if err != nil { + log.Printf("[WARNING] GMAIL Client (2): %s", err) + return err + } + + log.Printf("[INFO] Stop subscription on GMAIL Status: %d", res.StatusCode) + body, err := ioutil.ReadAll(res.Body) + if err != nil { + log.Printf("[WARNING] Gmail subscription Body (2): %s", err) + return err + } + + log.Printf("GMAIL RESP (%d): %s", res.StatusCode, string(body)) + + if res.StatusCode != 200 && res.StatusCode != 201 { + return errors.New(fmt.Sprintf("Subscription failed: %s", string(body))) + } + //notificationURL := fmt.Sprintf("%s/api/v1/hooks/webhook_%s", syncSubUrl, trigger.Id) + //curSubscriptions, err := getOutlookSubscriptions(outlookClient) + //if err == nil { + // for _, sub := range curSubscriptions.Value { + // if sub.NotificationURL == notificationURL { + // log.Printf("[INFO] Removing subscription %s from gmail for %s", sub.Id, workflowId) + // removeOutlookSubscription(outlookClient, sub.Id) + // } + // } + //} else { + // log.Printf("Failed to get subscriptions - need to overwrite") + //} + + return nil +} + // Remove AUTH // Remove function // Remove subscription @@ -750,7 +1033,7 @@ func handleOutlookSubRemoval(ctx context.Context, user shuffle.User, workflowId, // Actually delete the thing redirectDomain := "localhost:5001" url := fmt.Sprintf("http://%s/api/v1/triggers/outlook/register", redirectDomain) - outlookClient, _, err := getOutlookClient(ctx, "", trigger.OauthToken, url) + outlookClient, _, err := shuffle.GetOutlookClient(ctx, "", trigger.OauthToken, url) if err != nil { log.Printf("[WARNING] Oauth client failure - outlook folders: %s", err) return err @@ -771,6 +1054,83 @@ func handleOutlookSubRemoval(ctx context.Context, user shuffle.User, workflowId, return nil } +func handleDeleteGmailSub(resp http.ResponseWriter, request *http.Request) { + cors := handleCors(resp, request) + if cors { + return + } + + location := strings.Split(request.URL.String(), "/") + + var workflowId string + var triggerId string + if location[1] == "api" { + if len(location) <= 6 { + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + workflowId = location[4] + triggerId = location[6] + } + + if len(workflowId) == 0 || len(triggerId) == 0 { + log.Printf("Ids can't be zero when deleting %s", workflowId) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + ctx := context.Background() + workflow, err := shuffle.GetWorkflow(ctx, workflowId) + if err != nil { + log.Printf("[WARNING] Failed getting the workflow locally (delete outlook): %s", err) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + user, err := shuffle.HandleApiAuthentication(resp, request) + if err != nil { + log.Printf("[WARNING] Api authentication failed in outlook deploy: %s", err) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + 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 %s as admin (delete gmail sub)", user.Username, workflow.ID) + } else { + log.Printf("[WARNING] Wrong user (%s) for workflow %s when deleting gmail ", user.Username, workflow.ID) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + } + + trigger, err := shuffle.GetTriggerAuth(ctx, triggerId) + if err != nil { + log.Printf("[WARNING] Wrong user (%s) for workflow %s when deleting gmail (2)", user.Username, workflow.ID) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + err = handleGmailSubRemoval(ctx, user, workflowId, triggerId) + if err == nil { + // FIXME: Actually delete the hook, not just the data + shuffle.DeleteKey(ctx, "gmail_subscription", trigger.SubscriptionId) + shuffle.DeleteKey(ctx, "trigger_auth", triggerId) + } else { + log.Printf("[WARNING] Failed deleting gmail sub: %s", err) + } + + resp.WriteHeader(200) + resp.Write([]byte(`{"success": true}`)) +} + func handleDeleteOutlookSub(resp http.ResponseWriter, request *http.Request) { cors := handleCors(resp, request) if cors { @@ -845,7 +1205,7 @@ func handleDeleteOutlookSub(resp http.ResponseWriter, request *http.Request) { // Parses data from the workflow to see whether access is right to subscribe it // Creates the cloud function for outlook return // Wait for it to be available, then schedule a workflow to it -func createOutlookSub(resp http.ResponseWriter, request *http.Request) { +func handleCreateOutlookSub(resp http.ResponseWriter, request *http.Request) { cors := handleCors(resp, request) if cors { return @@ -934,7 +1294,7 @@ func createOutlookSub(resp http.ResponseWriter, request *http.Request) { // First - lets regenerate an oauth token for outlook.office.com from the original items trigger, err := shuffle.GetTriggerAuth(ctx, curTrigger.ID) if err != nil { - log.Printf("[INFO] Trigger %s doesn't exist - outlook sub.", curTrigger.ID) + log.Printf("[INFO] Trigger %s doesn't exist - gmail sub.", curTrigger.ID) resp.WriteHeader(401) resp.Write([]byte(`{"success": false, "reason": ""}`)) return @@ -944,9 +1304,9 @@ func createOutlookSub(resp http.ResponseWriter, request *http.Request) { //url := fmt.Sprintf("https://shuffler.io") redirectDomain := "localhost:5001" url := fmt.Sprintf("http://%s/api/v1/triggers/outlook/register", redirectDomain) - outlookClient, _, err := getOutlookClient(ctx, "", trigger.OauthToken, url) + outlookClient, _, err := shuffle.GetOutlookClient(ctx, "", trigger.OauthToken, url) if err != nil { - log.Printf("Oauth client failure - triggerauth: %s", err) + log.Printf("[WARNING] Oauth client failure for gmail - triggerauth: %s", err) resp.Write([]byte(`{"success": false, "reason": ""}`)) resp.WriteHeader(401) return @@ -1003,12 +1363,14 @@ func createOutlookSub(resp http.ResponseWriter, request *http.Request) { maxFails := 5 failCnt := 0 - log.Println(curTrigger.Folders) + log.Println("[INFO] Folders: %#v", curTrigger.Folders) for { subId, err := makeOutlookSubscription(outlookClient, curTrigger.Folders, notificationURL) if err != nil { failCnt += 1 - log.Printf("Failed making oauth subscription, retrying in 5 seconds: %s", err) + + log.Printf("[WARNING] Failed making oauth subscription for outlook, retrying in 5 seconds: %s", err) + time.Sleep(5 * time.Second) if failCnt == maxFails { log.Printf("Failed to set up subscription %d times.", maxFails) @@ -1023,7 +1385,7 @@ func createOutlookSub(resp http.ResponseWriter, request *http.Request) { trigger.SubscriptionId = subId err = shuffle.SetTriggerAuth(ctx, *trigger) if err != nil { - log.Printf("Failed setting triggerauth: %s", err) + log.Printf("[WARNING] Failed setting triggerauth (gmail): %s", err) } break @@ -1035,3 +1397,270 @@ func createOutlookSub(resp http.ResponseWriter, request *http.Request) { resp.WriteHeader(200) resp.Write([]byte(`{"success": true}`)) } + +// This sets up the sub with outlook itself +// Parses data from the workflow to see whether access is right to subscribe it +// Creates the cloud function for outlook return +// Wait for it to be available, then schedule a workflow to it +func handleCreateGmailSub(resp http.ResponseWriter, request *http.Request) { + cors := handleCors(resp, request) + if cors { + return + } + + location := strings.Split(request.URL.String(), "/") + + var workflowId string + if location[1] == "api" { + if len(location) <= 4 { + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + workflowId = location[4] + } + + ctx := context.Background() + workflow, err := shuffle.GetWorkflow(ctx, workflowId) + if err != nil { + log.Printf("[WARNING] Failed getting the workflow locally (outlook sub): %s", err) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + user, err := shuffle.HandleApiAuthentication(resp, request) + if err != nil { + log.Printf("Api authentication failed in outlook deploy: %s", err) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + // FIXME - have a check for org etc too.. + if user.Id != workflow.Owner && user.Role != "admin" { + log.Printf("[WARNING] Wrong user (%s) for workflow %s when deploying outlook", user.Username, workflow.ID) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + log.Println("[INFO] Handle gmail subscription for trigger!") + + // Should already be authorized at this point, as the workflow is shared + body, err := ioutil.ReadAll(request.Body) + if err != nil { + log.Printf("Failed body read for workflow %s", workflow.ID) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + // Based on the input data from frontend + type CurTrigger struct { + Name string `json:"name"` + Folders []string `json:"folders"` + ID string `json:"id"` + } + + //log.Println(string(body)) + var curTrigger CurTrigger + err = json.Unmarshal(body, &curTrigger) + if err != nil { + log.Printf("Failed body read unmarshal for trigger %s", workflow.ID) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + if len(curTrigger.Folders) == 0 { + log.Printf("[WARNING] Error for %s. Choosing folders is required, currently 0", workflow.ID) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + // Now that it's deployed - wait a few seconds before generating: + // 1. Oauth2 token thingies for outlook.office.com + // 2. Set the url to have the right mailboxes (probably ID?) ("https://outlook.office.com/api/v2.0/me/mailfolders('inbox')/messages") + // 3. Set the callback URL to be the new trigger + // 4. Run subscription test + // 5. Set the subscriptionId to the trigger object + + // First - lets regenerate an oauth token for outlook.office.com from the original items + trigger, err := shuffle.GetTriggerAuth(ctx, curTrigger.ID) + if err != nil { + log.Printf("[INFO] Trigger %s doesn't exist - gmail sub.", curTrigger.ID) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false, "reason": ""}`)) + return + } + + // url doesn't really matter here + //url := fmt.Sprintf("https://shuffler.io") + redirectDomain := "localhost:5001" + url := fmt.Sprintf("http://%s/api/v1/triggers/gmail/register", redirectDomain) + gmailClient, _, err := shuffle.GetGmailClient(ctx, "", trigger.OauthToken, url) + if err != nil { + log.Printf("[WARNING] Oauth client failure in gmail - triggerauth: %s", err) + resp.Write([]byte(`{"success": false, "reason": ""}`)) + resp.WriteHeader(401) + return + } + + // Location + + + // This is here simply to let the function start + // Usually takes 10 attempts minimum :O + // 10 * 5 = 50 seconds. That's waaay too much :( + + /* + if runningEnvironment != "cloud" { + org, err := shuffle.GetOrg(ctx, user.ActiveOrg.Id) + if err != nil { + log.Printf("Failed finding org %s: %s", org.Id, err) + return + } + log.Printf("[INFO] Starting cloud configuration TO START trigger %s in org %s for workflow %s", trigger.Id, org.Id, trigger.WorkflowId) + + action := shuffle.CloudSyncJob{ + Type: "gmail", + Action: "start", + OrgId: org.Id, + PrimaryItemId: trigger.Id, + SecondaryItem: trigger.Start, + ThirdItem: workflowId, + } + + err = executeCloudAction(action, org.SyncConfig.Apikey) + if err != nil { + log.Printf("[INFO] Failed cloud action START gmail execution: %s", err) + resp.WriteHeader(401) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err))) + return + } else { + log.Printf("[INFO] Successfully set up cloud action trigger") + } + } else { + log.Printf("Should configure a running environment for CLOUD") + } + */ + + notificationURL := fmt.Sprintf("%s/api/v1/hooks/webhook_%s", syncSubUrl, trigger.Id) + //curSubscriptions, err := getOutlookSubscriptions(outlookClient) + //if err == nil { + // for _, sub := range curSubscriptions.Value { + // if sub.NotificationURL == notificationURL { + // log.Printf("[INFO] Removing existing subscription %s", sub.Id) + // removeOutlookSubscription(outlookClient, sub.Id) + // } + // } + //} else { + // log.Printf("[INFO] Failed to get subscriptions - need to overwrite") + //} + + //maxFails := 5 + //failCnt := 0 + log.Printf("Starting with notificationURL %s", notificationURL) + log.Println("[INFO] Folders: %#v", curTrigger.Folders) + for { + sub, err := makeGmailSubscription(gmailClient, curTrigger.Folders, notificationURL) + if err != nil { + //failCnt += 1 + + log.Printf("[WARNING] Failed making oauth subscription for gmail, retrying in 5 seconds: %s", err) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + + //time.Sleep(5 * time.Second) + //if failCnt == maxFails { + // log.Printf("Failed to set up subscription %d times.", maxFails) + // resp.WriteHeader(401) + // return + //} + + //continue + } + + // May not need a new one; could just do this in triggerId with search tbh + newSub := shuffle.SubscriptionRecipient{ + HistoryId: sub.HistoryId, + TriggerId: trigger.Id, + Edited: 0, + Expiration: sub.Expiration, + LastSync: int(time.Now().Unix()), + } + err = shuffle.SetSubscriptionRecipient(ctx, newSub, newSub.HistoryId) + if err != nil { + log.Printf("[WARNING] Failed setting sub ID (gmail): %s", sub.HistoryId) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return + } + + // Set the ID somewhere here + trigger.SubscriptionId = sub.HistoryId + err = shuffle.SetTriggerAuth(ctx, *trigger) + if err != nil { + log.Printf("[WARNING] Failed setting triggerauth (gmail): %s", err) + } + + break + } + + log.Printf("[INFO] Successfully handled outlook subscription for trigger %s in workflow %s", curTrigger.ID, workflow.ID) + + //log.Printf("%#v", user) + resp.WriteHeader(200) + resp.Write([]byte(`{"success": true}`)) +} + +func handleGmailRouting(resp http.ResponseWriter, request *http.Request) { + body, err := ioutil.ReadAll(request.Body) + if err != nil { + log.Printf("[WARNING] Body read error for gmail message: %s", err) + resp.WriteHeader(200) + return + } + + parsedMessage := shuffle.Inputdata{} + err = json.Unmarshal(body, &parsedMessage) + if err != nil { + log.Printf("[WARNING] Unmarshal error for gmail message: %s", err) + resp.WriteHeader(200) + return + } + + log.Printf("GMAIL BODY:\n%s", string(body)) + parsedData, err := base64.StdEncoding.DecodeString(parsedMessage.Message.Data) + if err != nil { + log.Printf("[WARNING] Failed base64 decode in gmail routing: %s", err) + resp.WriteHeader(200) + return + } + + log.Printf("Parsed data: %s", string(parsedData)) + findHistory := shuffle.ParsedMessage{} + err = json.Unmarshal(parsedData, &findHistory) + if err != nil { + log.Printf("[WARNING] Unmarshal error for gmail message (2): %s", err) + resp.WriteHeader(200) + return + } + + // This History ID will match the ID that is received when the subscription is configured. + log.Printf("HistoryId: %d. Email: %s", findHistory.HistoryId, findHistory.EmailAddress) + ctx := context.Background() + subscription, err := shuffle.GetSubscriptionRecipient(ctx, fmt.Sprintf("%d", findHistory.HistoryId)) + if err != nil { + log.Printf("[WARNING] Failed finding gmail history for ID %d: %s. Should the subscription be cancelled?", findHistory.HistoryId, err) + resp.WriteHeader(200) + return + } + + log.Printf("SUB: %#v", subscription) + + resp.WriteHeader(200) +} diff --git a/docker-compose.yml b/docker-compose.yml index c033b414..637b9fba 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -66,7 +66,7 @@ services: - CLEANUP=${SHUFFLE_CONTAINER_AUTO_CLEANUP} restart: unless-stopped opensearch: - image: opensearchproject/opensearch:1.0.0 + image: opensearchproject/opensearch:1.0.1 hostname: shuffle-opensearch container_name: shuffle-opensearch environment: diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 85d3c6ed..02dc154d 100644 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -4201,10 +4201,10 @@ const AngularWorkflow = (props) => { return } - if (data.is_valid === false) { - alert.error(data.name+" requires hybrid version of Shuffle") - return - } + //if (data.is_valid === false) { + // alert.error(data.name+" requires hybrid version of Shuffle") + // return + //} const triggerLabel = getNextActionName(data.name) @@ -5570,7 +5570,38 @@ const AngularWorkflow = (props) => { setSelectedTrigger(selectedTrigger) } - const setFolders = () => { + const setGmailFolders = () => { + console.log("In set gmail folders") + fetch(globalUrl+"/api/v1/triggers/gmail/getFolders?trigger_id="+selectedTrigger.id, { + method: "GET", + headers: {"content-type": "application/json"}, + credentials: "include", + }) + .then((response) => { + if (response.status !== 200) { + throw new Error("No folders :o!") + } + + return response.json() + }) + .then((responseJson) => { + if (responseJson !== undefined && responseJson !== null && responseJson.success !== false && responseJson.length > 0) { + setTriggerFolders(responseJson) + } + + if (workflow.triggers[selectedTriggerIndex].parameters.length === 0 && responseJson.length > 0) { + workflow.triggers[selectedTriggerIndex].parameters = [{"value": responseJson[0].displayName, "name": "outlookfolder", "id": responseJson[0].id}] + selectedTrigger.parameters = [{"value": responseJson[0].displayName, "name": "outlookfolder", "id": responseJson[0].id}] + setWorkflow(workflow) + setSelectedTrigger(selectedTrigger) + } + }) + .catch(error => { + console.log(error.toString()) + }) + } + + const setOutlookFolders = () => { fetch(globalUrl+"/api/v1/triggers/outlook/getFolders?trigger_id="+selectedTrigger.id, { method: "GET", headers: {"content-type": "application/json"}, @@ -5584,7 +5615,7 @@ const AngularWorkflow = (props) => { return response.json() }) .then((responseJson) => { - if (responseJson !== null && responseJson.success !== false) { + if (responseJson !== null && responseJson.success !== false && responseJson.length > 0) { setTriggerFolders(responseJson) } @@ -5625,7 +5656,8 @@ const AngularWorkflow = (props) => { // This is horrible hahah if (localFirstrequest) { getTriggerAuth() - setFolders() + setOutlookFolders() + setGmailFolders() setLocalFirstrequest(false) } @@ -5633,6 +5665,65 @@ const AngularWorkflow = (props) => { } + const gmailButton = + + const outlookButton =
    {outlookButton} - {/* - - */} + {gmailButton}
    return( @@ -6715,9 +6802,10 @@ const AngularWorkflow = (props) => { if (trigger.id === undefined) { return } - alert.info("Deleting mail trigger") - fetch(globalUrl+"/api/v1/workflows/"+props.match.params.key+"/outlook/"+trigger.id, { + alert.info("Stopping mail trigger") + const requesttype = triggerAuthentication.type + fetch(`${globalUrl}/api/v1/workflows/${props.match.params.key}/${requesttype}/${trigger.id}`, { method: 'DELETE', headers: { 'Content-Type': 'application/json', @@ -6769,14 +6857,16 @@ const AngularWorkflow = (props) => { folders.push(curfolder.id) } - alert.info("Creating outlook subscription with name " + trigger.name) const data = { "name": trigger.name, "folders": folders, "id": trigger.id, } - fetch(globalUrl+"/api/v1/workflows/"+props.match.params.key+"/outlook", { + const requesttype = triggerAuthentication.type + alert.info("Creating "+requesttype+" subscription with name " + trigger.name) + + fetch(globalUrl+"/api/v1/workflows/"+props.match.params.key+"/"+requesttype, { method: 'POST', headers: { 'Content-Type': 'application/json', diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index 2d3150dc..46bcafd4 100644 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -1794,7 +1794,7 @@ const Workflows = (props) => { }} color="primary"> Cancel - + : null} + + ) + } const notificationMenu = @@ -172,27 +242,14 @@ const Header = props => { anchorEl={anchorEl} keepMounted open={Boolean(anchorEl)} - style={{zIndex: 10002}} + style={{zIndex: 10002, maxHeight: "100vh", overflowX: "hidden", overflowY: "auto",}} onClose={() => { handleClose() }} > {notifications.map((data, index) => { return ( - {}}> - - - - - {new Date(data.date).toISOString()} - - - {data.title} - - - - - + ) })} @@ -202,15 +259,15 @@ const Header = props => { const avatarMenu = { - setAnchorEl(event.currentTarget); + setAnchorElAvatar(event.currentTarget); }}> { handleClose() @@ -315,7 +372,7 @@ const Header = props => {
    {avatarMenu} - {notificationMenu} + {/*notificationMenu*/} {userdata === undefined || userdata.admin === undefined || userdata.admin === null || !userdata.admin ? null :
    :
    -
    -
    +
    +
    } /> } /> } /> diff --git a/frontend/src/components/Header.js b/frontend/src/components/Header.js index ce5dab2f..d15b37b9 100644 --- a/frontend/src/components/Header.js +++ b/frontend/src/components/Header.js @@ -13,7 +13,7 @@ const hoverColor = "#f85a3e" const hoverOutColor = "#e8eaf6" const Header = props => { - const { globalUrl, notifications, isLoggedIn, removeCookie, homePage, isLoaded, userdata, cookies } = props; + const { globalUrl, setNotifications, notifications, isLoggedIn, removeCookie, homePage, isLoaded, userdata, cookies } = props; const theme = useTheme(); const [HomeHoverColor, setHomeHoverColor] = useState(hoverOutColor); @@ -34,7 +34,7 @@ const Header = props => { // Don't really care about the logout fetch(`${globalUrl}/api/v1/notifications/${alert_id}/markasread`, { credentials: "include", - method: 'POST', + method: 'GET', headers: { 'Content-Type': 'application/json', }, @@ -46,16 +46,16 @@ const Header = props => { return response.json(); }).then(function(responseJson) { - if (responseJson.success !== undefined && responseJson.success) { - setTimeout(() => { - window.location.reload() - }, 2000) + if (responseJson.success === true) { + const newNotifications = notifications.filter(data => data.id !== alert_id) + console.log("NEW NOTIFICATIONS: ", newNotifications) + setNotifications(newNotifications) } else { - alert.error("Failed changing org: ", responseJson.reason) + alert.error("Failed dismissing notification. Please try again later.") } }) .catch(error => { - console.log("error changing: ", error) + console.log("error in notification dismissal: ", error) //removeCookie("session_token", {path: "/"}) }) } @@ -187,13 +187,22 @@ const Header = props => { const {data} = props return ( - - + + {/* {new Date(data.updated_at).toISOString()} - - - {data.title} - + */} + {data.reference_url !== undefined && data.reference_url !== null && data.reference_url.length > 0 ? + + + {data.title} + + + : + + {data.title} + + } + {data.image !== undefined && data.image !== null && data.image.length > 0 ? {data.title} : @@ -372,7 +381,7 @@ const Header = props => {
    {avatarMenu} - {/*notificationMenu*/} + {notificationMenu} {userdata === undefined || userdata.admin === undefined || userdata.admin === null || !userdata.admin ? null : - - -
    - ) - } - const WorkflowMenu = () => { const [newAnchor, setNewAnchor] = React.useState(null) const [showShuffleMenu, setShowShuffleMenu] = React.useState(false) @@ -7529,6 +7471,19 @@ const AngularWorkflow = (props) => {

    This menu is used to control the workflow itself.

    + + Skip Notifications
    } + control={ + { + workflow.configuration.skip_notifications = !workflow.configuration.skip_notifications + setWorkflow(workflow) + setUpdate("skip_notifications"+workflow.configuration.skip_notification? "true" : "false") + //setShowShuffleMenu(false) + }} /> + } + /> Exit on Error
    } @@ -7537,7 +7492,7 @@ const AngularWorkflow = (props) => { workflow.configuration.exit_on_error = !workflow.configuration.exit_on_error setWorkflow(workflow) setUpdate("exit_on_error_"+workflow.configuration.exit_on_error ? "true" : "false") - setShowShuffleMenu(false) + //setShowShuffleMenu(false) }} /> } /> @@ -7549,7 +7504,7 @@ const AngularWorkflow = (props) => { workflow.configuration.start_from_top = !workflow.configuration.start_from_top setWorkflow(workflow) setUpdate("start_from_top_"+workflow.configuration.start_from_top ? "true" : "false") - setShowShuffleMenu(false) + //setShowShuffleMenu(false) }} /> } /> @@ -7714,7 +7669,6 @@ const AngularWorkflow = (props) => { - {/* */} {workflow.configuration !== null && workflow.configuration !== undefined && workflow.configuration.exit_on_error !== undefined ? : null}
    From a509dcd300ed7d15c633d91c11f5d5903dd38939 Mon Sep 17 00:00:00 2001 From: frikky Date: Mon, 4 Oct 2021 22:22:29 +0200 Subject: [PATCH 100/117] Fixed the shared memoryspace between same node issue --- docker-compose.yml | 4 ++-- frontend/src/components/ParsedAction.jsx | 1 + frontend/src/views/AngularWorkflow.jsx | 25 ++++++++++++++++++++---- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 2445fe52..5f18df8b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '3' services: frontend: - #build: ./frontend + build: ./frontend image: ghcr.io/frikky/shuffle-frontend:nightly container_name: shuffle-frontend hostname: shuffle-frontend @@ -16,7 +16,7 @@ services: depends_on: - backend backend: - #build: ./backend + build: ./backend image: ghcr.io/frikky/shuffle-backend:nightly container_name: shuffle-backend hostname: ${BACKEND_HOSTNAME} diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index 3e77046e..a582af95 100644 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -481,6 +481,7 @@ const ParsedAction = (props) => { } } + //console.log("CHANGING ACTION COUNT !") selectedActionParameters[count].value = event.target.value selectedAction.parameters[count].value = event.target.value setSelectedAction(selectedAction) diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 3627275d..c6d15a56 100644 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -1892,6 +1892,7 @@ const AngularWorkflow = (props) => { // https://stackoverflow.com/questions/16677856/cy-onselect-callback-only-once const onNodeSelect = (event, newAppAuth) => { const data = event.target.data() + //console.log("NEW NODE ID: ", data.id) if (data.isButton) { //console.log("BUTTON CLICKED: ", data) if (data.buttonType === "delete") { @@ -2043,12 +2044,13 @@ const AngularWorkflow = (props) => { //const branch = workflow.branches.filter(branch => branch.source_id === data.id || branch.destination_id === data.id) //console.log("APPAUTH: ", newAppAuth) //console.log("BRANCHES: ", branch) - console.log("CLICK: ", data) + //console.log("CLICK: ", data) if (data.type === "ACTION") { //setSelectedApp({}) //setSelectedAction({}) var curaction = workflow.actions.find(a => a.id === data.id) + //console.log("PARAMS: ", curaction.parameters[0]) //console.log("INSIDE CURACTION: ", curaction) if (!curaction || curaction === undefined) { //event.target.unselect() @@ -2056,6 +2058,7 @@ const AngularWorkflow = (props) => { return } + const curapp = apps.find(a => a.name === curaction.app_name && ((a.app_version === curaction.app_version || (a.loop_versions !== null && a.loop_versions.includes(curaction.app_version))))) //console.log("APP: ", curapp) if (!curapp || curapp === undefined) { @@ -2069,8 +2072,8 @@ const AngularWorkflow = (props) => { actions: [curaction], } - console.log(tmpapp) - console.log(curaction) + //console.log(tmpapp) + //console.log(curaction) setSelectedApp(tmpapp) //setSelectedAction(JSON.parse(JSON.stringify(curaction))) setSelectedAction(curaction) @@ -2434,6 +2437,7 @@ const AngularWorkflow = (props) => { if (dstdata.parameters[paramkey].value.length === 0) { dstdata.parameters[paramkey].value = `$${parentlabel}${foundresult}` } else { + dstdata.parameters[paramkey].value = `$${parentlabel}${foundresult}` //console.log("Skipping ", dstdata.parameters[paramkey], " because it already has a value") } } @@ -2654,7 +2658,7 @@ const AngularWorkflow = (props) => { console.log("SHOULD STITCH WITH STARTNODE") cy.add(edgeToBeAdded) } - + if (nodedata.app_name === "Shuffle Tools") { const iconInfo = GetIconInfo(nodedata) const svg_pin = `` @@ -2669,6 +2673,19 @@ const AngularWorkflow = (props) => { } } + if (nodedata.parameters !== undefined && nodedata.parameters !== null && !nodedata.label.endsWith("_copy")) { + var newparameters = [] + + for (var subkey in nodedata.parameters) { + var newparam = JSON.parse(JSON.stringify(nodedata.parameters[subkey])) + newparam.id = uuidv4() + newparam.value = "" + newparameters.push(newparam) + } + + nodedata.parameters = newparameters + } + if (workflow.actions === undefined || workflow.actions === null) { workflow.actions = [nodedata] } else { From b49aa6f25c26cf9413de0b660cd45f73b55d0b64 Mon Sep 17 00:00:00 2001 From: frikky Date: Mon, 4 Oct 2021 22:25:33 +0200 Subject: [PATCH 101/117] Fixed compose issues for latest --- backend/go-app/go.mod | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 42a809d8..c4085944 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -23,7 +23,7 @@ require ( github.com/docker/go-units v0.4.0 // indirect github.com/elastic/go-elasticsearch/v7 v7.13.1 // indirect github.com/frikky/kin-openapi v0.39.0 - github.com/frikky/shuffle-shared v0.1.2 + github.com/frikky/shuffle-shared v0.1.3 github.com/fsouza/go-dockerclient v1.7.2 github.com/ghodss/yaml v1.0.0 github.com/go-git/go-billy/v5 v5.0.0 From 9ad5401d51e3b672795d3f8366f8b89ae3e723bf Mon Sep 17 00:00:00 2001 From: frikky Date: Mon, 4 Oct 2021 22:26:31 +0200 Subject: [PATCH 102/117] Minor docker-compose cleanup --- docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 5f18df8b..2445fe52 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '3' services: frontend: - build: ./frontend + #build: ./frontend image: ghcr.io/frikky/shuffle-frontend:nightly container_name: shuffle-frontend hostname: shuffle-frontend @@ -16,7 +16,7 @@ services: depends_on: - backend backend: - build: ./backend + #build: ./backend image: ghcr.io/frikky/shuffle-backend:nightly container_name: shuffle-backend hostname: ${BACKEND_HOSTNAME} From b3817faaf4e6317c0ee96a83114b8dd89139b99a Mon Sep 17 00:00:00 2001 From: frikky Date: Mon, 4 Oct 2021 22:39:59 +0200 Subject: [PATCH 103/117] #527: Added disclaimer for localhost on webhook to UI --- frontend/src/views/AngularWorkflow.jsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index c6d15a56..fb538fcb 100644 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -6736,6 +6736,12 @@ const AngularWorkflow = (props) => { console.log("Couldn't find webhook URI field: ", copyText) } }} + helperText={workflow.triggers[selectedTriggerIndex].parameters[0].value !== undefined && workflow.triggers[selectedTriggerIndex].parameters[0].value !== null && (workflow.triggers[selectedTriggerIndex].parameters[0].value.includes("localhost") || workflow.triggers[selectedTriggerIndex].parameters[0].value.includes("127.0.0.1"))? + + PS: This does NOT work with localhost. Use your local IP instead. + + : null + } InputProps={{ style:{ color: "white", From 96aafe49ef33f0b748bffb8d460b3e9892302fc0 Mon Sep 17 00:00:00 2001 From: frikky Date: Tue, 5 Oct 2021 11:51:52 +0200 Subject: [PATCH 104/117] Re-added Kali & BlackArch --- backend/app_sdk/Dockerfile_blackarch | 19 +++++++++++++++++++ backend/app_sdk/Dockerfile_kali | 19 +++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 backend/app_sdk/Dockerfile_blackarch create mode 100644 backend/app_sdk/Dockerfile_kali diff --git a/backend/app_sdk/Dockerfile_blackarch b/backend/app_sdk/Dockerfile_blackarch new file mode 100644 index 00000000..02480aad --- /dev/null +++ b/backend/app_sdk/Dockerfile_blackarch @@ -0,0 +1,19 @@ +FROM peterclemenko/blackarch as base + +FROM base as builder + +RUN /bin/pacman -Syu --noconfirm + +RUN /bin/pacman -Sy --noconfirm base-devel libffi musl openssl python python-pip -y + +RUN mkdir /install +WORKDIR /install + +COPY requirements.txt /requirements.txt +RUN pip install --prefix="/install" -r /requirements.txt + +FROM base + +COPY --from=builder /install /usr/local +COPY __init__.py /app/walkoff_app_sdk/__init__.py +COPY app_base.py /app/walkoff_app_sdk/app_base.py diff --git a/backend/app_sdk/Dockerfile_kali b/backend/app_sdk/Dockerfile_kali new file mode 100644 index 00000000..af7fd24f --- /dev/null +++ b/backend/app_sdk/Dockerfile_kali @@ -0,0 +1,19 @@ +FROM kalilinux/kali-rolling as base + +FROM base as builder + +RUN apt-get update +RUN apt-get dist-upgrade -y +RUN apt install build-essential libffi-dev musl-dev openssl python3 python3-pip -y + +RUN mkdir /install +WORKDIR /install + +COPY requirements.txt /requirements.txt +RUN pip install --prefix="/install" -r /requirements.txt + +FROM base + +COPY --from=builder /install /usr/local +COPY __init__.py /app/walkoff_app_sdk/__init__.py +COPY app_base.py /app/walkoff_app_sdk/app_base.py From 2842b04a69935407b94a5df69ded38d1ed13c4de Mon Sep 17 00:00:00 2001 From: frikky Date: Tue, 5 Oct 2021 20:27:08 +0200 Subject: [PATCH 105/117] https://github.com/pwwang/liquidpy/issues/38: New Liquidpy release <3 --- backend/app_sdk/requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/app_sdk/requirements.txt b/backend/app_sdk/requirements.txt index 691f1ee6..c887c4f5 100644 --- a/backend/app_sdk/requirements.txt +++ b/backend/app_sdk/requirements.txt @@ -1,4 +1,4 @@ urllib3==1.25.9 requests==2.25.1 MarkupSafe==2.0.1 -liquidpy==0.7.0 +liquidpy==0.7.1 From 030adab32736e3419a17614094204ea981d02214 Mon Sep 17 00:00:00 2001 From: frikky Date: Tue, 5 Oct 2021 22:01:54 +0200 Subject: [PATCH 106/117] #124: Frontend changes for latest issues with notifications --- backend/app_sdk/Dockerfile | 16 +- backend/app_sdk/app_base.py | 226 ++++++++---------------- backend/app_sdk/build.sh | 2 +- backend/go-app/go.mod | 2 +- backend/go-app/main.go | 19 +- frontend/src/App.jsx | 2 +- frontend/src/components/Header.js | 66 ++++++- frontend/src/components/ScrollToTop.jsx | 11 +- frontend/src/views/Docs.jsx | 4 +- 9 files changed, 165 insertions(+), 183 deletions(-) diff --git a/backend/app_sdk/Dockerfile b/backend/app_sdk/Dockerfile index e50fd25a..c323b73e 100644 --- a/backend/app_sdk/Dockerfile +++ b/backend/app_sdk/Dockerfile @@ -1,16 +1,18 @@ FROM python:3.9.1-alpine as base - FROM base as builder RUN apk --no-cache add --update alpine-sdk libffi libffi-dev musl-dev openssl-dev tzdata RUN mkdir /install WORKDIR /install -COPY requirements.txt /requirements.txt -RUN pip3 install --prefix="/install" -r /requirements.txt FROM base -COPY --from=builder /install /usr/local -WORKDIR /app -COPY sub.py /app/sub.py -CMD python sub.py +#--no-cache +RUN apk update && apk add --update tzdata libmagic alpine-sdk libffi libffi-dev musl-dev openssl-dev + +COPY --from=builder /install /usr/local +COPY requirements.txt /requirements.txt +RUN pip3 install -r /requirements.txt + +COPY __init__.py /app/walkoff_app_sdk/__init__.py +COPY app_base.py /app/walkoff_app_sdk/app_base.py diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index 8d263caf..a483b6fd 100644 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -30,6 +30,7 @@ class AppBase: self.url = os.getenv("CALLBACK_URL", "https://shuffler.io") self.base_url = os.getenv("BASE_URL", "https://shuffler.io") self.action = os.getenv("ACTION", "") + self.original_action = os.getenv("ACTION", "") self.authorization = os.getenv("AUTHORIZATION", "") self.current_execution_id = os.getenv("EXECUTIONID", "") self.full_execution = os.getenv("FULL_EXECUTION", "") @@ -49,6 +50,7 @@ class AppBase: if isinstance(self.action, str): try: self.action = json.loads(self.action) + self.original_action = json.loads(self.action) except: print("[WARNING] Failed parsing action as JSON") @@ -162,7 +164,7 @@ class AppBase: except (KeyError, NameError) as e: print(f"""Key/NameError in param handler for {param["name"]}: {e}""") - print(f"OUTER VALUE: {param_value}") + print(f"[DEBUG] OUTER VALUE: {param_value}") if len(param_value) > 0: md5 = hashlib.md5(param_value.encode('utf-8')).hexdigest() values.append(md5) @@ -296,7 +298,7 @@ class AppBase: #print(f"Baseparams to check!!: {baseparams}") for key, value in baseparams.items(): check_value = "" - for param in self.action["parameters"]: + for param in self.original_action["parameters"]: if param["name"] == key: #print("PARAM: %s" % param) check_value = param["value"] @@ -343,7 +345,7 @@ class AppBase: # Specific for OpenAPI body replacement #print("\n\n\nDOING STUFF BELOW HERE") if not should_merge: - for parameter in self.action["parameters"]: + for parameter in self.original_action["parameters"]: if parameter["name"] == key: #print("CHECKING BODY FOR VALUE REPLACE DATA!") try: @@ -359,7 +361,7 @@ class AppBase: #print(f"MERGE: {should_merge}") if isinstance(value, list): - print("Item {value} is a list.") + print(f"[DEBUG] Item {value} is a list.") if len(value) <= 1: if len(value) == 1: baseparams[key] = value[0] @@ -368,7 +370,7 @@ class AppBase: # should_merge = True else: if not should_merge: - print("Adding WITHOUT looping list") + print("[DEBUG] Adding WITHOUT looping list") else: if len(value) not in listlengths: listlengths.append(len(value)) @@ -385,38 +387,38 @@ class AppBase: #print(f"{value} is not a list") pass - print("Listlengths: %s" % listlengths) + print("[DEBUG] Listlengths: %s" % listlengths) if len(listlengths) == 0: - print("NO multiplier. Running a single iteration.") + print("[DEBUG] NO multiplier. Running a single iteration.") paramlist.append(baseparams) elif len(listlengths) == 1: - print("NO MULTIPLIER NECESSARY. Length is %d" % len(listitems)) + print("[DEBUG] NO MULTIPLIER NECESSARY. Length is %d" % len(listitems)) for item in listitems: # This loops should always be length 1 for key, value in item.items(): if isinstance(value, int): - print("\nShould run key %s %d times from %s" % (key, value, baseparams[key])) + print("\n[DEBUG] Should run key %s %d times from %s" % (key, value, baseparams[key])) if len(paramlist) == value: - print("List ALREADY exists - just changing values") + print("[DEBUG] List ALREADY exists - just changing values") for subloop in range(value): baseitem = copy.deepcopy(baseparams) paramlist[subloop][key] = baseparams[key][subloop] else: - print("List DOESNT exist - ADDING values") + print("[DEBUG] List DOESNT exist - ADDING values") for subloop in range(value): baseitem = copy.deepcopy(baseparams) baseitem[key] = baseparams[key][subloop] paramlist.append(baseitem) else: - print("Multipliers to handle: %s" % listitems) + print("[DEBUG] Multipliers to handle: %s" % listitems) newlength = 1 for item in listitems: for key, value in item.items(): newlength = newlength * value - print("Newlength of array: %d. Lists: %s" % (newlength, all_lists)) + print("[DEBUG] Newlength of array: %d. Lists: %s" % (newlength, all_lists)) # Get the cartesian product of the arrays cartesian = await self.cartesian_product(all_lists) newlist = [] @@ -454,7 +456,7 @@ class AppBase: newparams = {} for key, value in baseparams.items(): if isinstance(value, list) and len(value) > 0: - print(f"In list check for {key}") + print(f"[DEBUG] In list check for {key}") try: # Added skip for body (OpenAPI) which uses data= in requests @@ -462,33 +464,36 @@ class AppBase: if key != "body": value[0] = json.loads(value[0]) except json.decoder.JSONDecodeError as e: - print("JSON casting error: %s" % e) + print("[WARNING] JSON casting error (1): %s" % e) except TypeError as e: - print("TypeError: %s" % e) + print("[WARNING] TypeError: %s" % e) - print("POST initial list check") + print("[DEBUG] POST initial list check") - if isinstance(value, list) and len(value) == 1 and isinstance(value[0], list): - try: - loop_wrapper[key] += 1 - except IndexError: - loop_wrapper[key] = 1 - except KeyError: - loop_wrapper[key] = 1 + try: + if isinstance(value, list) and len(value) == 1 and isinstance(value[0], list): + try: + loop_wrapper[key] += 1 + except Exception as e: + print("[WARNING] Exception in loop wrapper: {e}") + loop_wrapper[key] = 1 - print(f"Key {key} is a list: {value}") - newparams[key] = value[0] - has_loop = True - else: - #print(f"Key {key} is NOT a list within a list. Value: {value}") + print(f"[DEBUG] Key {key} is a list: {value}") + newparams[key] = value[0] + has_loop = True + else: + #print(f"Key {key} is NOT a list within a list. Value: {value}") + newparams[key] = value + except Exception as e: + print(f"[WARNING] Error in baseparams list: {e}") newparams[key] = value results = [] if has_loop: - print("[WARNING] Should run inner loop: %s" % newparams) + print(f"[DEBUG] Should run inner loop: {newparams}") ret = await self.run_recursed_items(func, newparams, loop_wrapper) else: - #print("[INFO] Should run multiplier check with params (inner): %s" % newparams) + print(f"[DEBUG] Should run multiplier check with params (inner): {newparams}") # 1. Find the loops that are required and create new multipliers # If here: check for multipliers within this scope. ret = [] @@ -592,10 +597,10 @@ class AppBase: #else: ret.append(new_value) - print("Ret length: %d" % len(ret)) + print("[INFO] Ret length: %d" % len(ret)) if len(ret) == 1: #ret = ret[0] - print("DONT make list of 1 into 0!!") + print("[DEBUG] DONT make list of 1 into 0!!") print("Return from execution: %s" % ret) if ret == None: @@ -909,7 +914,7 @@ class AppBase: # If found, we get the full results list from backend fullexecution = {} if len(self.full_execution) == 0: - print("NO EXECUTION - LOADING!") + print("[DEBUG] NO EXECUTION - LOADING!") try: tmpdata = { "authorization": self.authorization, @@ -944,7 +949,7 @@ class AppBase: try: fullexecution = json.loads(self.full_execution) except json.decoder.JSONDecodeError as e: - print("Json decode execution error: %s" % e) + print("[WARNING] Json decode execution error: %s" % e) self.action_result["result"] = "Json error during startup: %s" % e self.send_result(self.action_result, headers, stream_path) return @@ -1011,8 +1016,9 @@ class AppBase: try: return int(data) except ValueError: - print("ValueError while casting %s to int" % data) + print("[DEBUG] ValueError while casting %s to int" % data) return data + if "lower" in thistype: return data.lower() if "upper" in thistype: @@ -1156,8 +1162,8 @@ class AppBase: inner_value = parse_nested_param(data, maxDepth(data) - 0) outer_value = parse_nested_param(data, maxDepth(data) - 1) - print("INNER: ", inner_value) - print("OUTER: ", outer_value) + #print("[DEBUG] INNER: ", inner_value) + #print("[DEBUG] OUTER: ", outer_value) wrapper_group = "|".join(wrappers) parse_string = data @@ -1336,7 +1342,7 @@ class AppBase: newvalue = [] firstitem = actualitem[0][0] seconditem = actualitem[0][1] - print("ACTUAL PARSED: %s" % actualitem) + print("[DEBUG] ACTUAL PARSED: %s" % actualitem) # Means it's a single item -> continue if seconditem == "": @@ -1348,7 +1354,7 @@ class AppBase: else: firstitem = int(firstitem) - #print("Post lower checks") + print(f"[DEBUG] Post lower checks with item {firstitem}") tmpitem = basejson[int(firstitem)] try: newvalue, is_loop = recurse_json(tmpitem, parsersplit[outercnt+1:]) @@ -1370,7 +1376,7 @@ class AppBase: else: seconditem = int(seconditem) - print("Post lower checks") + print(f"[DEBUG] Post lower checks 2: {firstitem} AND {seconditem}") newvalue = [] if int(seconditem) > len(basejson): seconditem = len(basejson) @@ -1383,7 +1389,7 @@ class AppBase: try: ret, tmp_loop = recurse_json(basejson[i], parsersplit[outercnt+1:]) except IndexError: - print("INDEXERROR: ", parsersplit[outercnt]) + print("[DEBUG] INDEXERROR: ", parsersplit[outercnt]) #ret = innervalue ret, tmp_loop = recurse_json(innervalue, parsersplit[outercnt:]) @@ -1404,12 +1410,12 @@ class AppBase: print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (list): %s" % value) return basejson, False elif isinstance(basejson[value], str): - print(f"[INFO] LOADING STRING '%s' AS JSON" % basejson[value]) + #print(f"[INFO] LOADING STRING '%s' AS JSON" % basejson[value]) try: basejson = json.loads(basejson[value]) - print("BASEJSON: %s" % basejson) + #print("[DEBUG] BASEJSON: %s" % basejson) except json.decoder.JSONDecodeError as e: - print("RETURNING BECAUSE '%s' IS A NORMAL STRING (0)" % basejson[value]) + #print("[DEBUG] RETURNING BECAUSE '%s' IS A NORMAL STRING (0)" % basejson[value]) return basejson[value], False else: basejson = basejson[value] @@ -1427,9 +1433,9 @@ class AppBase: print(f"[INFO] LOADING STRING '%s' AS JSON" % basejson[value]) try: basejson = json.loads(basejson[value]) - print("BASEJSON: %s" % basejson) + print("[DEBUG] BASEJSON: %s" % basejson) except json.decoder.JSONDecodeError as e: - print("RETURNING BECAUSE '%s' IS A NORMAL STRING (1)" % basejson[value]) + print("[DEBUG] RETURNING BECAUSE '%s' IS A NORMAL STRING (1)" % basejson[value]) return basejson[value], False else: basejson = basejson[value] @@ -1539,7 +1545,7 @@ class AppBase: pass except KeyError as error: - print(f"KeyError in JSON: {error}") + print(f"[DEBUG] KeyError in JSON: {error}") print(f"[INFO] After first trycatch. Baseresult")#, baseresult) @@ -1603,12 +1609,13 @@ class AppBase: print("Error in decoder: %s" % e) return returndata, is_loop + # Sending self as it's not a normal function def parse_liquid(template, self): #print("Inside liquid with glob: %s" % globals()) try: if len(template) > 5000000: - print("Skipping liquid - size is %d" % len(template)) + print("[DEBUG] Skipping liquid - size too big (%d)" % len(template)) return template #if not "{{" in template or not "}}" in template: @@ -1620,7 +1627,7 @@ class AppBase: # return template #print(globals()) - print("Running liquid with data of length %d" % len(template)) + print("[DEBUG] Running liquid with data of length %d" % len(template)) run = Liquid(template, mode="wild", from_file=False) # Can't handle self yet (?) @@ -2074,7 +2081,7 @@ class AppBase: # Checks whether conditions are met, otherwise set branchcheck, tmpresult = check_branch_conditions(action, fullexecution, self) if isinstance(tmpresult, object) or isinstance(tmpresult, list): - print("Fixing branch return as object -> string") + print("[DEBUG] Fixing branch return as object -> string") try: #tmpresult = tmpresult.replace("'", "\"") tmpresult = json.dumps(tmpresult) @@ -2093,7 +2100,7 @@ class AppBase: except requests.exceptions.ConnectionError as e: self.logger.exception(e) - print("\n\nRETURNING BECAUSE A BRANCH FAILED: %s\n\n" % tmpresult) + print("\n\n[DEBUG] RETURNING BECAUSE A BRANCH FAILED: %s\n\n" % tmpresult) return # Replace name cus there might be issues @@ -2130,7 +2137,7 @@ class AppBase: #print("AUTH: ", key, value) params[item["key"]] = item["value"] except KeyError: - print("No authentication specified!") + print("[DEBUG] No authentication specified!") pass #action["authentication"] @@ -2195,50 +2202,6 @@ class AppBase: print(f"""HANDLING {action["parameters"][counter]["value"]}""") action["parameters"][counter]["value"] = recurse_cleanup_script(action["parameters"][counter]["value"]) - #try: - # newvalue = json.loads(action["parameters"][counter]["value"]) - # deletekeys = [] - # for key, value in newvalue.items(): - # print("%s: %s" % (key, value)) - # if isinstance(value, str) and len(value) == 0: - # deletekeys.append(key) - # continue - - # if value == "${%s}" % key: - # print("Deleting %s because key = value" % key) - # deletekeys.append(key) - # continue - # - # for deletekey in deletekeys: - # try: - # del newvalue[deletekey] - # except: - # pass - - # #print("Post delete: %s" % newvalue) - # for key, value in newvalue.items(): - # if isinstance(value, bool): - # continue - - # try: - # value = json.loads(value) - # newvalue[key] = value - # except json.decoder.JSONDecodeError as e: - # print("Inner overwrite issue: %s" % e) - # continue - # except Exception as e: - # print("General error in newvalue items loop: %s" % e) - # continue - - # try: - # action["parameters"][counter]["value"] = json.dumps(newvalue) - # except json.decoder.JSONDecodeError as e: - # print("[WARNING] JsonDecodeError: %s" % e) - # action["parameters"][counter]["value"] = newvalue - # - - #except json.decoder.JSONDecodeError as e: - # print("Failed JSON replacement for OpenAPI keys (2) {e}") #print(action["parameters"]) @@ -2249,6 +2212,12 @@ class AppBase: self.action = action + # Setting due to them being overwritten, but still later useful + try: + self.original_action = json.loads(json.dumps(action)) + except Exception as e: + print(f"[ERROR] Failed parsing action as JSON to original action. This COULD have bad effects on LOOPED executions: {e}") + # calltimes is used to handle forloops in the app itself. # 2 kinds of loop - one in gui with one app each, and one like this, # which is super fast, but has a bad overview (potentially good tho) @@ -2409,6 +2378,7 @@ class AppBase: actualitem = actualitem[:-2] except IndexError: + print("[WARNING] Indexerror") continue #print(f"\n\nTMPITEM: {actualitem}\n\n") @@ -2488,65 +2458,13 @@ class AppBase: multi_execution_lists.append(resultarray) multi_parameters[parameter["name"]] = resultarray - - #if parameter["id"] == "body_replacement": - # print("Should run body MULTI replacement in index %d with %s" % (bodyindex, parameter)) - # try: - # print("PREBODY: %s" % params["body"]) - - # parsedarray = str(resultarray) - # try: - # parsedarray = json.dumps(resultarray) - # except: - # pass - - # if f'\"{parameter["name"]}\"' in params["body"]: - # params["body"] = params["body"].replace(f'\"{parameter["name"]}\"' , parsedarray, -1) - # multi_parameters["body"] = multi_parameters["body"].replace(f'\"{parameter["name"]}\"' , parsedarray, -1) - # else: - # params["body"] = params["body"].replace(parameter["name"], parsedarray, -1) - # multi_parameters["body"] = multi_parameters["body"].replace(parameter["name"], parsedarray, -1) - - # #print("POSTBODY: %s" % params["body"]) - # #if isinstance(multi_parameters, list): - # # print("MULTIPARAM AS LIST (NOT REPLACING!!)!") - # # for multiparam in multi_parameters: - # # print(f"MULTIPARAM: {multiparam}") - # # #multi_parameters["body"] = multi_parameters["body"].replace(parameter["name"], str(parameter["value"]), -1) - # #else: - - # except KeyError as e: - # print("KEYERROR: %s" % e) - - # remove_params.append(parameter["name"]) - # #bodyindex = counter - # continue - else: # Parses things like int(value) - print("Normal parsing (not looping)")#with data %s" % value) - + print("[DEBUG] Normal parsing (not looping)")#with data %s" % value) # This part has fucked over so many random JSON usages because of weird paranthesis parsing value = parse_wrapper_start(value, self) - print("Post return: %s" % value) - - #if parameter["id"] == "body_replacement": - # print("Should run body replacement in index %d with %s" % (bodyindex, parameter)) - # try: - # print("PREBODY: %s" % params["body"]) - # params["body"] = params["body"].replace(parameter["name"], parameter["value"], -1) - # print("POSTBODY: %s" % params["body"]) - # except KeyError as e: - # print("KEYERROR: %s" % e) - - # #bodyindex = counter - # continue - - #for parameter in action["parameters"]: - #if parameter["name"] == "body": - # print("PARAM: %s" % parameter) - #if param.id == "body_replacement": + print("[DEBUG] Post return: %s" % value) #print("POST data value: %s" % value) params[parameter["name"]] = value @@ -2568,9 +2486,9 @@ class AppBase: #remove_params.append(parameter["name"]) # Fix lists here # FIXME: This doesn't really do anything anymore - print("CHECKING multi execution list: %d!" % len(multi_execution_lists)) + print("[DEBUG] CHECKING multi execution list: %d!" % len(multi_execution_lists)) if len(multi_execution_lists) > 0: - print("\n Multi execution list has more data: %d" % len(multi_execution_lists)) + print("\n [DEBUG] Multi execution list has more data: %d" % len(multi_execution_lists)) filteredlist = [] for listitem in multi_execution_lists: if listitem in filteredlist: @@ -2709,7 +2627,7 @@ class AppBase: # 1. Use number of executions based on the arrays being similar # 2. Find the right value from the parsed multi_params - print("[INFO] Running WITHOUT outer loop") + print("[INFO] Running WITHOUT outer loop (looping)") json_object = False results = await self.run_recursed_items(func, multi_parameters, {}) if isinstance(results, dict) or isinstance(results, list): diff --git a/backend/app_sdk/build.sh b/backend/app_sdk/build.sh index 30560f50..966bd8cc 100644 --- a/backend/app_sdk/build.sh +++ b/backend/app_sdk/build.sh @@ -3,7 +3,7 @@ ### DEFAULT NAME=shuffle-app_sdk -VERSION=0.9.20 +VERSION=0.9.21 docker rmi docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION --force docker build . -f Dockerfile -t frikky/shuffle:app_sdk -t frikky/$NAME:$VERSION -t docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION -t ghcr.io/frikky/$NAME:$VERSION diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index c4085944..c152f70e 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -23,7 +23,7 @@ require ( github.com/docker/go-units v0.4.0 // indirect github.com/elastic/go-elasticsearch/v7 v7.13.1 // indirect github.com/frikky/kin-openapi v0.39.0 - github.com/frikky/shuffle-shared v0.1.3 + github.com/frikky/shuffle-shared v0.1.5 github.com/fsouza/go-dockerclient v1.7.2 github.com/ghodss/yaml v1.0.0 github.com/go-git/go-billy/v5 v5.0.0 diff --git a/backend/go-app/main.go b/backend/go-app/main.go index d7a6f4f9..2a8c1f6f 100644 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -7,6 +7,7 @@ import ( "bytes" "context" "crypto/md5" + "crypto/tls" //"crypto/x509" "encoding/hex" "encoding/json" @@ -48,6 +49,7 @@ import ( "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/storage/memory" + githttp "gopkg.in/src-d/go-git.v4/plumbing/transport/http" // Random xj "github.com/basgys/goxml2json" @@ -57,7 +59,7 @@ import ( "gopkg.in/yaml.v3" // PROXY overrides - // "gopkg.in/src-d/go-git.v4/plumbing/transport/client" + "gopkg.in/src-d/go-git.v4/plumbing/transport/client" // githttp "gopkg.in/src-d/go-git.v4/plumbing/transport/http" // Web @@ -4131,6 +4133,17 @@ func runInitEs(ctx context.Context) { // FIXME: Isn't this a little backwards? workflowapps, err := shuffle.GetAllWorkflowApps(ctx, 1000) log.Printf("[INFO] Getting and validating workflowapps. Got %d with err %#v", len(workflowapps), err) + + // accept any certificate (might be useful for testing) + customGitClient := &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + }, + Timeout: 15 * time.Second, + } + client.InstallProtocol("http", githttp.NewClient(customGitClient)) + client.InstallProtocol("https", githttp.NewClient(customGitClient)) + if err != nil && len(workflowapps) == 0 { log.Printf("[WARNING] Failed getting apps (runInit): %s", err) } else if err == nil { @@ -4156,6 +4169,7 @@ func runInitEs(ctx context.Context) { Password: password, } } + branch := os.Getenv("SHUFFLE_DOWNLOAD_AUTH_BRANCH") if len(branch) > 0 && branch != "master" && branch != "main" { cloneOptions.ReferenceName = plumbing.ReferenceName(branch) @@ -4202,7 +4216,7 @@ func runInitEs(ctx context.Context) { } _, err = git.Clone(storer, fs, cloneOptions) if err != nil { - log.Printf("Failed loading repo %s into memory: %s", apis, err) + log.Printf("[WARNING] Failed loading repo %s into memory: %s", apis, err) } else { log.Printf("[INFO] Finished git clone. Looking for updates to the repo.") dir, err := fs.ReadDir("") @@ -5898,6 +5912,7 @@ func initHandlers() { // Introduced in 0.9.21 to handle notifications for e.g. failed Workflow r.HandleFunc("/api/v1/notifications", shuffle.HandleGetNotifications).Methods("GET", "OPTIONS") + r.HandleFunc("/api/v1/notifications/clear", shuffle.HandleClearNotifications).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/notifications/{notificationId}/markasread", shuffle.HandleMarkAsRead).Methods("GET", "OPTIONS") //r.HandleFunc("/api/v1/notifications/{notificationId}/markasread", shuffle.HandleMarkAsRead).Methods("GET", "OPTIONS") diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 01ab169c..72f4464a 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -127,7 +127,7 @@ const App = (message, props) => { } />
    :
    - +
    } /> diff --git a/frontend/src/components/Header.js b/frontend/src/components/Header.js index d15b37b9..8fe90568 100644 --- a/frontend/src/components/Header.js +++ b/frontend/src/components/Header.js @@ -30,6 +30,40 @@ const Header = props => { textDecoration: "none", } + const handleClose = () => { + setAnchorEl(null); + setAnchorElAvatar(null); + }; + + const clearNotifications = () => { + // Don't really care about the logout + fetch(`${globalUrl}/api/v1/notifications/clear`, { + credentials: "include", + method: 'GET', + headers: { + 'Content-Type': 'application/json', + }, + }) + .then(function(response) { + if (response.status !== 200) { + console.log("Error in response") + } + + return response.json(); + }).then(function(responseJson) { + if (responseJson.success === true) { + setNotifications([]) + handleClose() + } else { + alert.error("Failed dismissing notifications. Please try again later.") + } + }) + .catch(error => { + console.log("error in notification dismissal: ", error) + //removeCookie("session_token", {path: "/"}) + }) + } + const dismissNotification = (alert_id) => { // Don't really care about the logout fetch(`${globalUrl}/api/v1/notifications/${alert_id}/markasread`, { @@ -175,19 +209,17 @@ const Header = props => { setAnchorEl(event.currentTarget); }; - const handleClose = () => { - setAnchorEl(null); - setAnchorElAvatar(null); - }; const chipStyle = { backgroundColor: "#3d3f43", height: 30, marginRight: 5, paddingLeft: 5, paddingRight: 5, height: 28, cursor: "pointer", borderColor: "#3d3f43", color: "white", } + + const notificationWidth = 350 const NotificationItem = (props) => { const {data} = props return ( - + {/* {new Date(data.updated_at).toISOString()} */} @@ -251,11 +283,33 @@ const Header = props => { anchorEl={anchorEl} keepMounted open={Boolean(anchorEl)} - style={{zIndex: 10002, maxHeight: "100vh", overflowX: "hidden", overflowY: "auto",}} + style={{zIndex: 10002, maxHeight: "90vh", overflowX: "hidden", overflowY: "auto",}} + PaperProps={{ + style: { + backgroundColor: theme.palette.surfaceColor, + } + }} onClose={() => { handleClose() }} > + +
    + + Your Notifications ({notifications.length}) + + {notifications.length > 1 ? + + : null} +
    + + Notifications are made by Shuffle to help you discover issues or improvements. + +
    {notifications.map((data, index) => { return ( diff --git a/frontend/src/components/ScrollToTop.jsx b/frontend/src/components/ScrollToTop.jsx index 6b3c9911..88d2d158 100644 --- a/frontend/src/components/ScrollToTop.jsx +++ b/frontend/src/components/ScrollToTop.jsx @@ -1,8 +1,7 @@ import { useEffect } from 'react'; import { withRouter } from 'react-router-dom'; -import ReactGA from 'react-ga'; -function ScrollToTop({setCurpath, history }) { +function ScrollToTop({getUserNotifications, setCurpath, history }) { useEffect(() => { const unlisten = history.listen(() => { window.scroll({ @@ -11,14 +10,8 @@ function ScrollToTop({setCurpath, history }) { behavior: "smooth", }); - //ReactGA.event({ - // category: "referral", - // action: "new_user_referral", - // label: "", - //}) - - ReactGA.pageview(window.location.pathname) setCurpath(window.location.pathname) + getUserNotifications() }); return () => { unlisten(); diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index c9f86028..f9ec6024 100644 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -380,14 +380,14 @@ const Docs = (props) => { > {newname} {itemMatching && tocLines !== null && tocLines !== undefined && tocLines.length > 0 ? -
    +
    {tocLines.map((data, index) => { console.log(data) return ( {}}> - {data.text} + - {data.text} ) From 088728914941cf6ab405c2d19ae5138a5dbf41c7 Mon Sep 17 00:00:00 2001 From: frikky Date: Thu, 7 Oct 2021 01:51:13 +0200 Subject: [PATCH 107/117] #522: Fixed hybrid gmail and outlook app to properly work --- backend/go-app/go.mod | 2 +- backend/go-app/main.go | 48 +- backend/go-app/oauth2.go | 1666 ------------------------ backend/go-app/walkoff.go | 9 +- frontend/src/views/AngularWorkflow.jsx | 102 +- frontend/src/views/AppCreator.jsx | 2 + frontend/src/views/Apps.jsx | 7 +- frontend/src/views/Docs.jsx | 14 +- 8 files changed, 119 insertions(+), 1731 deletions(-) delete mode 100644 backend/go-app/oauth2.go diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index c152f70e..749f1507 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -2,7 +2,7 @@ module shuffle 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 diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 2a8c1f6f..ea6c9d16 100644 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -7,7 +7,7 @@ import ( "bytes" "context" "crypto/md5" - "crypto/tls" + //"crypto/tls" //"crypto/x509" "encoding/hex" "encoding/json" @@ -49,7 +49,7 @@ import ( "github.com/go-git/go-git/v5" "github.com/go-git/go-git/v5/plumbing" "github.com/go-git/go-git/v5/storage/memory" - githttp "gopkg.in/src-d/go-git.v4/plumbing/transport/http" + //githttp "gopkg.in/src-d/go-git.v4/plumbing/transport/http" // Random xj "github.com/basgys/goxml2json" @@ -59,7 +59,7 @@ import ( "gopkg.in/yaml.v3" // PROXY overrides - "gopkg.in/src-d/go-git.v4/plumbing/transport/client" + //"gopkg.in/src-d/go-git.v4/plumbing/transport/client" // githttp "gopkg.in/src-d/go-git.v4/plumbing/transport/http" // Web @@ -3591,7 +3591,7 @@ func handleCloudJob(job shuffle.CloudSyncJob) error { if job.Action == "execute" { // FIXME: Get the email ctx := context.Background() - maildata := shuffle.MailData{} + maildata := shuffle.MailDataOutlook{} err := json.Unmarshal([]byte(job.ThirdItem), &maildata) if err != nil { log.Printf("Maildata unmarshal error: %s", err) @@ -3613,7 +3613,7 @@ func handleCloudJob(job shuffle.CloudSyncJob) error { return err } - emails, err := getOutlookEmail(outlookClient, maildata) + emails, err := shuffle.GetOutlookEmail(outlookClient, maildata) //log.Printf("EMAILS: %d", len(emails)) //log.Printf("INSIDE GET OUTLOOK EMAIL!: %#v, %s", emails, err) @@ -4135,14 +4135,14 @@ func runInitEs(ctx context.Context) { log.Printf("[INFO] Getting and validating workflowapps. Got %d with err %#v", len(workflowapps), err) // accept any certificate (might be useful for testing) - customGitClient := &http.Client{ - Transport: &http.Transport{ - TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, - }, - Timeout: 15 * time.Second, - } - client.InstallProtocol("http", githttp.NewClient(customGitClient)) - client.InstallProtocol("https", githttp.NewClient(customGitClient)) + //customGitClient := &http.Client{ + // Transport: &http.Transport{ + // TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, + // }, + // Timeout: 15 * time.Second, + //} + //client.InstallProtocol("http", githttp.NewClient(customGitClient)) + //client.InstallProtocol("https", githttp.NewClient(customGitClient)) if err != nil && len(workflowapps) == 0 { log.Printf("[WARNING] Failed getting apps (runInit): %s", err) @@ -5855,20 +5855,20 @@ func initHandlers() { r.HandleFunc("/api/v1/get_openapi/{key}", getOpenapi).Methods("GET", "OPTIONS") // Specific triggers - r.HandleFunc("/api/v1/workflows/{key}/outlook", handleCreateOutlookSub).Methods("POST", "OPTIONS") - r.HandleFunc("/api/v1/workflows/{key}/outlook/{triggerId}", handleDeleteOutlookSub).Methods("DELETE", "OPTIONS") - r.HandleFunc("/api/v1/triggers/outlook/register", handleNewOutlookRegister).Methods("GET", "OPTIONS") + r.HandleFunc("/api/v1/workflows/{key}/outlook", shuffle.HandleCreateOutlookSub).Methods("POST", "OPTIONS") + r.HandleFunc("/api/v1/workflows/{key}/outlook/{triggerId}", shuffle.HandleDeleteOutlookSub).Methods("DELETE", "OPTIONS") + r.HandleFunc("/api/v1/triggers/outlook/register", shuffle.HandleNewOutlookRegister).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/triggers/outlook/getFolders", shuffle.HandleGetOutlookFolders).Methods("GET", "OPTIONS") - r.HandleFunc("/api/v1/triggers/outlook/{key}", handleGetSpecificTrigger).Methods("GET", "OPTIONS") - r.HandleFunc("/api/v1/triggers/gmail/register", handleNewGmailRegister).Methods("GET", "OPTIONS") + r.HandleFunc("/api/v1/triggers/outlook/{key}", shuffle.HandleGetSpecificTrigger).Methods("GET", "OPTIONS") + r.HandleFunc("/api/v1/triggers/gmail/register", shuffle.HandleNewGmailRegister).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/triggers/gmail/getFolders", shuffle.HandleGetGmailFolders).Methods("GET", "OPTIONS") // FIXME: This should only be cloud. Done locally with ngrok to test - r.HandleFunc("/api/v1/triggers/gmail/routing", handleGmailRouting).Methods("POST", "OPTIONS") + //r.HandleFunc("/api/v1/triggers/gmail/routing", handleGmailRouting).Methods("POST", "OPTIONS") - r.HandleFunc("/api/v1/triggers/gmail/{key}", handleGetSpecificTrigger).Methods("GET", "OPTIONS") - r.HandleFunc("/api/v1/workflows/{key}/gmail", handleCreateGmailSub).Methods("POST", "OPTIONS") - r.HandleFunc("/api/v1/workflows/{key}/gmail/{triggerId}", handleDeleteGmailSub).Methods("DELETE", "OPTIONS") + r.HandleFunc("/api/v1/triggers/gmail/{key}", shuffle.HandleGetSpecificTrigger).Methods("GET", "OPTIONS") + r.HandleFunc("/api/v1/workflows/{key}/gmail", shuffle.HandleCreateGmailSub).Methods("POST", "OPTIONS") + r.HandleFunc("/api/v1/workflows/{key}/gmail/{triggerId}", shuffle.HandleDeleteGmailSub).Methods("DELETE", "OPTIONS") //r.HandleFunc("/api/v1/triggers/gmail/{key}", handleGetSpecificGmailTrigger).Methods("GET", "OPTIONS") //r.HandleFunc("/api/v1/triggers/outlook/getFolders", shuffle.HandleGetOutlookFolders).Methods("GET", "OPTIONS") @@ -5879,8 +5879,8 @@ func initHandlers() { // EVERYTHING below here is NEW for 0.8.0 (written 25.05.2021) 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/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") r.HandleFunc("/api/v1/orgs/{orgId}", shuffle.HandleGetOrg).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/orgs/{orgId}", shuffle.HandleEditOrg).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/orgs/{orgId}/create_sub_org", shuffle.HandleCreateSubOrg).Methods("POST", "OPTIONS") diff --git a/backend/go-app/oauth2.go b/backend/go-app/oauth2.go deleted file mode 100644 index d2f0b2f5..00000000 --- a/backend/go-app/oauth2.go +++ /dev/null @@ -1,1666 +0,0 @@ -package main - -import ( - "github.com/frikky/shuffle-shared" - - "bytes" - "context" - "encoding/base64" - "encoding/json" - "errors" - "fmt" - "io/ioutil" - "log" - "net/http" - "net/url" - "strings" - "time" - //"golang.org/x/oauth2" -) - -func getOutlookAttachment(client *http.Client, emailId, attachmentId string) ([]shuffle.FullEmail, error) { - //requestUrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/users/ec03b4f2-fccf-4c35-b0eb-be85a0f5dd43/mailFolders") - - requestUrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/%s/attachments/%s", emailId, attachmentId) - //log.Printf("Outlook email URL: %#v", requestUrl) - - ret, err := client.Get(requestUrl) - if err != nil { - log.Printf("[INFO] OutlookErr: %s", err) - return []shuffle.FullEmail{}, err - } - - body, err := ioutil.ReadAll(ret.Body) - if err != nil { - log.Printf("[WARNING] Failed body decoding from outlook email") - return []shuffle.FullEmail{}, err - } - - //type FullEmail struct { - log.Printf("[INFO] Attachment Body: %s", string(body)) - log.Printf("[INFO] Status email: %d", ret.StatusCode) - if ret.StatusCode != 200 { - return []shuffle.FullEmail{}, err - } - - //log.Printf("Body: %s", string(body)) - - /* - parsedmail := FullEmail{} - err = json.Unmarshal(body, &parsedmail) - if err != nil { - log.Printf("[INFO] Email unmarshal error: %s", err) - return []FullEmail{}, err - } - - emails = append(emails, parsedmail) - */ - - return []shuffle.FullEmail{}, nil -} - -func getOutlookEmail(client *http.Client, maildata shuffle.MailData) ([]shuffle.FullEmail, error) { - //requestUrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/users/ec03b4f2-fccf-4c35-b0eb-be85a0f5dd43/mailFolders") - - emails := []shuffle.FullEmail{} - for _, email := range maildata.Value { - //messageId := email.Resourcedata.ID - //requestUrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/me/%s", messageId) - requestUrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/%s", email.Resource) - //log.Printf("Outlook email URL: %#v", requestUrl) - - ret, err := client.Get(requestUrl) - if err != nil { - log.Printf("[INFO] OutlookErr: %s", err) - return []shuffle.FullEmail{}, err - } - - body, err := ioutil.ReadAll(ret.Body) - if err != nil { - log.Printf("[WARNING] Failed body decoding from outlook email") - return []shuffle.FullEmail{}, err - } - - //type shuffle.FullEmail struct { - //log.Printf("[INFO] EMAIL Body: %s", string(body)) - //log.Printf("[INFO] Status email: %d", ret.StatusCode) - if ret.StatusCode != 200 { - return []shuffle.FullEmail{}, err - } - - //log.Printf("Body: %s", string(body)) - - parsedmail := shuffle.FullEmail{} - err = json.Unmarshal(body, &parsedmail) - if err != nil { - log.Printf("[INFO] Email unmarshal error: %s", err) - return []shuffle.FullEmail{}, err - } - - emails = append(emails, parsedmail) - } - - return emails, nil -} - -func getOutlookProfile(client *http.Client) (shuffle.OutlookProfile, error) { - requestUrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/me?$select=mail") - - ret, err := client.Get(requestUrl) - if err != nil { - log.Printf("[INFO] Folder error: %s", err) - return shuffle.OutlookProfile{}, err - } - - log.Printf("[INFO] Status profile: %d", ret.StatusCode) - body, err := ioutil.ReadAll(ret.Body) - if err != nil { - log.Printf("[INFO] Body: %s", err) - return shuffle.OutlookProfile{}, err - } - - log.Printf("[INFO] BODY: %s", string(body)) - - profile := shuffle.OutlookProfile{} - err = json.Unmarshal(body, &profile) - if err != nil { - log.Printf("Unmarshal: %s", err) - return shuffle.OutlookProfile{}, err - } - - return profile, nil -} - -func handleNewGmailRegister(resp http.ResponseWriter, request *http.Request) { - cors := shuffle.HandleCors(resp, request) - if cors { - return - } - - user, err := shuffle.HandleApiAuthentication(resp, request) - if err != nil { - log.Printf("[INFO] Api authentication failed in getting specific trigger: %s", err) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - code := request.URL.Query().Get("code") - if len(code) == 0 { - log.Println("No code") - resp.WriteHeader(401) - return - } - - url := fmt.Sprintf("http://%s%s", request.Host, request.URL.EscapedPath()) - log.Printf("URL: %s", url) - ctx := context.Background() - _, accessToken, err := shuffle.GetGmailClient(ctx, code, shuffle.OauthToken{}, url) - if err != nil { - log.Printf("Oauth client failure - outlook register: %s", err) - resp.WriteHeader(401) - return - } - - // This should be possible, and will also give the actual username - - /* - profile, err := getOutlookProfile(client) - if err != nil { - log.Printf("Outlook profile failure: %s", err) - resp.WriteHeader(401) - return - } - */ - - // This is a state workaround, which should really be for CSRF checks lol - state := request.URL.Query().Get("state") - if len(state) == 0 { - log.Println("No state") - resp.WriteHeader(401) - return - } - - stateitems := strings.Split(state, "%26") - if len(stateitems) == 1 { - stateitems = strings.Split(state, "&") - } - - // FIXME - trigger auth - senderUser := "" - trigger := shuffle.TriggerAuth{} - for _, item := range stateitems { - itemsplit := strings.Split(item, "%3D") - if len(itemsplit) == 1 { - itemsplit = strings.Split(item, "=") - } - - if len(itemsplit) != 2 { - continue - } - - //log.Printf("ITEM: %#v", itemsplit) - - // Do something here - if itemsplit[0] == "workflow_id" { - trigger.WorkflowId = itemsplit[1] - } else if itemsplit[0] == "trigger_id" { - trigger.Id = itemsplit[1] - } else if itemsplit[0] == "type" { - trigger.Type = itemsplit[1] - } else if itemsplit[0] == "start" { - trigger.Start = itemsplit[1] - } else if itemsplit[0] == "username" { - trigger.Username = itemsplit[1] - trigger.Owner = itemsplit[1] - senderUser = itemsplit[1] - } - } - - // THis is an override based on the user in oauth return - /* - if len(profile.Mail) > 0 { - trigger.Username = profile.Mail - } - */ - - trigger.Code = code - trigger.OauthToken = shuffle.OauthToken{ - AccessToken: accessToken.AccessToken, - TokenType: accessToken.TokenType, - RefreshToken: accessToken.RefreshToken, - Expiry: accessToken.Expiry, - } - - //log.Printf("Done with client: %#v", accessToken) - //log.Printf("Done with client2: %#v", trigger.OauthToken) - //resp.WriteHeader(401) - //return - - //log.Printf("%#v", trigger) - log.Println(trigger.WorkflowId) - log.Println(trigger.Id) - log.Println(senderUser) - log.Println(trigger.Type) - log.Printf("STARTNODE: %s", trigger.Start) - log.Printf("[INFO] Attempting to set up outlook trigger for %s", senderUser) - if trigger.WorkflowId == "" || trigger.Id == "" || senderUser == "" || trigger.Type == "" { - log.Printf("[INFO] All oauth items need to contain data to register a new state") - resp.WriteHeader(401) - return - } - - // Should also update the user - Userdata, err := shuffle.GetUser(ctx, user.Id) - if err != nil { - log.Printf("[INFO] Username %s doesn't exist (oauth2): %s", trigger.Username, err) - resp.WriteHeader(401) - return - } - - Userdata.Authentication = append(Userdata.Authentication, shuffle.UserAuth{ - Name: "Gmail", - Description: "oauth2", - Workflows: []string{trigger.WorkflowId}, - Username: trigger.Username, - Fields: []shuffle.UserAuthField{ - shuffle.UserAuthField{ - Key: "trigger_id", - Value: trigger.Id, - }, - shuffle.UserAuthField{ - Key: "username", - Value: trigger.Username, - }, - shuffle.UserAuthField{ - Key: "code", - Value: code, - }, - shuffle.UserAuthField{ - Key: "type", - Value: trigger.Type, - }, - }, - }) - - // Set apikey for the user if they don't have one - err = shuffle.SetUser(ctx, Userdata, false) - if err != nil { - log.Printf("[WARNING] Failed setting user data for %s: %s (gmail)", Userdata.Username, err) - resp.WriteHeader(401) - return - } - - err = shuffle.SetTriggerAuth(ctx, trigger) - if err != nil { - log.Printf("[WARNING] Failed to set trigger auth for %s - %s (gmail)", trigger.Username, err) - resp.WriteHeader(401) - return - } - - resp.WriteHeader(200) - resp.Write([]byte(`{"success": true}`)) -} - -func handleNewOutlookRegister(resp http.ResponseWriter, request *http.Request) { - cors := shuffle.HandleCors(resp, request) - if cors { - return - } - - user, err := shuffle.HandleApiAuthentication(resp, request) - if err != nil { - log.Printf("[INFO] Api authentication failed in getting specific trigger: %s", err) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - code := request.URL.Query().Get("code") - if len(code) == 0 { - log.Println("No code") - resp.WriteHeader(401) - return - } - - url := fmt.Sprintf("http://%s%s", request.Host, request.URL.EscapedPath()) - log.Println(url) - ctx := context.Background() - _, accessToken, err := shuffle.GetOutlookClient(ctx, code, shuffle.OauthToken{}, url) - if err != nil { - log.Printf("Oauth client failure - outlook register: %s", err) - resp.WriteHeader(401) - return - } - - // This should be possible, and will also give the actual username - - /* - profile, err := getOutlookProfile(client) - if err != nil { - log.Printf("Outlook profile failure: %s", err) - resp.WriteHeader(401) - return - } - */ - - // This is a state workaround, which should really be for CSRF checks lol - state := request.URL.Query().Get("state") - if len(state) == 0 { - log.Println("No state") - resp.WriteHeader(401) - return - } - - stateitems := strings.Split(state, "%26") - if len(stateitems) == 1 { - stateitems = strings.Split(state, "&") - } - - // FIXME - trigger auth - senderUser := "" - trigger := shuffle.TriggerAuth{} - for _, item := range stateitems { - itemsplit := strings.Split(item, "%3D") - if len(itemsplit) == 1 { - itemsplit = strings.Split(item, "=") - } - - if len(itemsplit) != 2 { - continue - } - - //log.Printf("ITEM: %#v", itemsplit) - - // Do something here - if itemsplit[0] == "workflow_id" { - trigger.WorkflowId = itemsplit[1] - } else if itemsplit[0] == "trigger_id" { - trigger.Id = itemsplit[1] - } else if itemsplit[0] == "type" { - trigger.Type = itemsplit[1] - } else if itemsplit[0] == "start" { - trigger.Start = itemsplit[1] - } else if itemsplit[0] == "username" { - trigger.Username = itemsplit[1] - trigger.Owner = itemsplit[1] - senderUser = itemsplit[1] - } - } - - // THis is an override based on the user in oauth return - /* - if len(profile.Mail) > 0 { - trigger.Username = profile.Mail - } - */ - - trigger.Code = code - trigger.OauthToken = shuffle.OauthToken{ - AccessToken: accessToken.AccessToken, - TokenType: accessToken.TokenType, - RefreshToken: accessToken.RefreshToken, - Expiry: accessToken.Expiry, - } - - //log.Printf("%#v", trigger) - log.Println(trigger.WorkflowId) - log.Println(trigger.Id) - log.Println(senderUser) - log.Println(trigger.Type) - log.Printf("STARTNODE: %s", trigger.Start) - log.Printf("[INFO] Attempting to set up outlook trigger for %s", senderUser) - if trigger.WorkflowId == "" || trigger.Id == "" || senderUser == "" || trigger.Type == "" { - log.Printf("[INFO] All oauth items need to contain data to register a new state") - resp.WriteHeader(401) - return - } - - // Should also update the user - Userdata, err := shuffle.GetUser(ctx, user.Id) - if err != nil { - log.Printf("[INFO] Username %s doesn't exist (oauth2): %s", trigger.Username, err) - resp.WriteHeader(401) - return - } - - Userdata.Authentication = append(Userdata.Authentication, shuffle.UserAuth{ - Name: "Outlook", - Description: "oauth2", - Workflows: []string{trigger.WorkflowId}, - Username: trigger.Username, - Fields: []shuffle.UserAuthField{ - shuffle.UserAuthField{ - Key: "trigger_id", - Value: trigger.Id, - }, - shuffle.UserAuthField{ - Key: "username", - Value: trigger.Username, - }, - shuffle.UserAuthField{ - Key: "code", - Value: code, - }, - shuffle.UserAuthField{ - Key: "type", - Value: trigger.Type, - }, - }, - }) - - // Set apikey for the user if they don't have one - err = shuffle.SetUser(ctx, Userdata, false) - if err != nil { - log.Printf("[WARNING] Failed setting user data for %s: %s (outlook)", Userdata.Username, err) - resp.WriteHeader(401) - return - } - - err = shuffle.SetTriggerAuth(ctx, trigger) - if err != nil { - log.Printf("[WARNING] Failed to set trigger auth for %s - %s (outlook)", trigger.Username, err) - resp.WriteHeader(401) - return - } - - resp.WriteHeader(200) - resp.Write([]byte(`{"success": true}`)) -} - -func handleGetSpecificTrigger(resp http.ResponseWriter, request *http.Request) { - cors := handleCors(resp, request) - if cors { - return - } - - user, err := shuffle.HandleApiAuthentication(resp, request) - if err != nil { - log.Printf("Api authentication failed in getting specific workflow: %s", err) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - location := strings.Split(request.URL.String(), "/") - - var workflowId string - if location[1] == "api" { - if len(location) <= 4 { - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - workflowId = location[5] - } - - if strings.Contains(workflowId, "?") { - workflowId = strings.Split(workflowId, "?")[0] - } - - ctx := context.Background() - trigger, err := shuffle.GetTriggerAuth(ctx, workflowId) - if err != nil { - log.Printf("[INFO] Trigger %s doesn't exist - specific trigger.", workflowId) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false, "reason": ""}`)) - return - } - - if user.Username != trigger.Owner && user.Role != "admin" { - log.Printf("Wrong user (%s) for trigger %s", user.Username, trigger.Id) - resp.WriteHeader(401) - return - } - - trigger.OauthToken = shuffle.OauthToken{} - trigger.Code = "" - - b, err := json.Marshal(trigger) - if err != nil { - log.Println("Failed to marshal data") - resp.WriteHeader(401) - return - } - - resp.WriteHeader(200) - resp.Write(b) -} - -// Lists the users current subscriptions -func getOutlookSubscriptions(outlookClient *http.Client) (SubscriptionsWrapper, error) { - fullUrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/subscriptions") - req, err := http.NewRequest( - "GET", - fullUrl, - nil, - ) - req.Header.Add("Content-Type", "application/json") - res, err := outlookClient.Do(req) - if err != nil { - log.Printf("suberror Client: %s", err) - return SubscriptionsWrapper{}, err - } - - body, err := ioutil.ReadAll(res.Body) - if err != nil { - log.Printf("Suberror Body: %s", err) - return SubscriptionsWrapper{}, err - } - - newSubs := SubscriptionsWrapper{} - err = json.Unmarshal(body, &newSubs) - if err != nil { - return SubscriptionsWrapper{}, err - } - - return newSubs, nil -} - -type SubscriptionsWrapper struct { - OdataContext string `json:"@odata.context"` - Value []OutlookSubscription `json:"value"` -} - -type OutlookSubscription struct { - ChangeType string `json:"changeType"` - NotificationURL string `json:"notificationUrl"` - Resource string `json:"resource"` - ExpirationDateTime string `json:"expirationDateTime"` - ClientState string `json:"clientState"` - Id string `json:"id"` -} - -type GmailSubscription struct { - TopicName string `json:"topicName"` - LabelIds []string `json:"labelIds"` -} - -func makeGmailSubscription(client *http.Client, folderIds []string, notificationURL string) (shuffle.SubResponse, error) { - fullUrl := "https://www.googleapis.com/gmail/v1/users/me/watch" - - // FIXME - this expires rofl - //t := time.Now().Local().Add(time.Minute * time.Duration(4200)) - //timeFormat := fmt.Sprintf("%d-%02d-%02dT%02d:%02d:%02d.0000000Z", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second()) - - resource := "projects/shuffler/topics/gmail_testing" - log.Printf("[INFO] Subscription resource to get(s) for gmail: %s", resource) - sub := GmailSubscription{ - TopicName: resource, - LabelIds: folderIds, - } - //ClientState: "This is a test", - - data, err := json.Marshal(sub) - if err != nil { - log.Printf("Marshal: %s", err) - return shuffle.SubResponse{}, err - } - - req, err := http.NewRequest( - "POST", - fullUrl, - bytes.NewBuffer(data), - ) - req.Header.Add("Content-Type", "application/json") - res, err := client.Do(req) - if err != nil { - log.Printf("[WARNING] GMAIL Client: %s", err) - return shuffle.SubResponse{}, err - } - - log.Printf("[INFO] Subscription on GMAIL Status: %d", res.StatusCode) - body, err := ioutil.ReadAll(res.Body) - if err != nil { - log.Printf("[WARNING] Gmail subscription Body: %s", err) - return shuffle.SubResponse{}, err - } - - log.Printf("GMAIL RESP: %s", string(body)) - - if res.StatusCode != 200 && res.StatusCode != 201 { - return shuffle.SubResponse{}, errors.New(fmt.Sprintf("Subscription failed: %s", string(body))) - } - - // Use data from body here to create thingy - newSub := shuffle.SubResponse{} - err = json.Unmarshal(body, &newSub) - if err != nil { - return shuffle.SubResponse{}, err - } - - return newSub, nil -} - -func makeOutlookSubscription(client *http.Client, folderIds []string, notificationURL string) (string, error) { - fullUrl := "https://graph.microsoft.com/v1.0/subscriptions" - - // FIXME - this expires rofl - t := time.Now().Local().Add(time.Minute * time.Duration(4200)) - timeFormat := fmt.Sprintf("%d-%02d-%02dT%02d:%02d:%02d.0000000Z", t.Year(), t.Month(), t.Day(), t.Hour(), t.Minute(), t.Second()) - - resource := fmt.Sprintf("me/mailfolders('%s')/messages", strings.Join(folderIds, "','")) - log.Printf("[INFO] Subscription resource to get(s): %s", resource) - sub := OutlookSubscription{ - ChangeType: "created", - ClientState: "Shuffle subscription", - NotificationURL: notificationURL, - ExpirationDateTime: timeFormat, - Resource: resource, - } - //ClientState: "This is a test", - - data, err := json.Marshal(sub) - if err != nil { - log.Printf("Marshal: %s", err) - return "", err - } - - req, err := http.NewRequest( - "POST", - fullUrl, - bytes.NewBuffer(data), - ) - req.Header.Add("Content-Type", "application/json") - - res, err := client.Do(req) - if err != nil { - log.Printf("Client: %s", err) - return "", err - } - - log.Printf("[INFO] Subscription Status: %d", res.StatusCode) - body, err := ioutil.ReadAll(res.Body) - if err != nil { - log.Printf("Body: %s", err) - return "", err - } - - if res.StatusCode != 200 && res.StatusCode != 201 { - return "", errors.New(fmt.Sprintf("Subscription failed: %s", string(body))) - } - - // Use data from body here to create thingy - newSub := OutlookSubscription{} - err = json.Unmarshal(body, &newSub) - if err != nil { - return "", err - } - - return newSub.Id, nil -} - -// Basically the same as a webhook -func handleOutlookCallback(resp http.ResponseWriter, request *http.Request) { - path := strings.Split(request.URL.String(), "/") - if len(path) < 4 { - log.Printf("[INFO] Bad outlook callback URL: %s", path) - resp.WriteHeader(403) - resp.Write([]byte(`{"success": false}`)) - return - } - - // 1. Get config with hookId - //fmt.Sprintf("%s/api/v1/hooks/%s", callbackUrl, hookId) - ctx := context.Background() - location := strings.Split(request.URL.String(), "/") - - var hookId string - if location[1] == "api" { - if len(location) <= 4 { - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - hookId = location[5] - } - - // ID: webhook_ - if len(hookId) != 36 { - log.Printf("[WARNING] Bad hook ID: %s (%d)", hookId, len(hookId)) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false, "message": "ID not valid"}`)) - return - } - - //func getTriggerAuth(ctx context.Context, id string) (*TriggerAuth, error) { - hook, err := shuffle.GetTriggerAuth(ctx, hookId) - if err != nil { - log.Printf("[INFO] Failed getting trigger %s (callback): %s", hookId, err) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - body, err := ioutil.ReadAll(request.Body) - if err != nil { - log.Printf("[INFO] Body data error: %s", err) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - //log.Printf("[INFO] BODY: %s. Len: %d", string(body), len(body)) - //key, ok := request.URL.Query()["validationToken"] - //if ok { - //} - token := request.URL.Query().Get("validationToken") - if len(body) == 0 && len(token) > 0 { - log.Printf("[INFO] Should handle trigger token %s", token) - resp.WriteHeader(200) - resp.Write([]byte(string(token))) - return - } - - // 1. Take the body and parse data -> Get the email itself - - maildata := shuffle.MailData{} - err = json.Unmarshal(body, &maildata) - if err != nil { - log.Printf("Maildata unmarshal error: %s", err) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - redirectDomain := "localhost:5001" - redirectUrl := fmt.Sprintf("http://%s/api/v1/triggers/outlook/register", redirectDomain) - outlookClient, _, err := shuffle.GetOutlookClient(ctx, "", hook.OauthToken, redirectUrl) - if err != nil { - log.Printf("Oauth client failure - triggerauth: %s", err) - resp.WriteHeader(401) - return - } - - emails, err := getOutlookEmail(outlookClient, maildata) - log.Printf("[INFO] EMAILS: %d. If this is more than 1, please contact frikky@shuffler.io", len(emails)) - //log.Printf("INSIDE GET OUTLOOK EMAIL!: %#v, %s", emails, err) - - //type shuffle.FullEmail struct { - email := shuffle.FullEmail{} - if len(emails) == 1 { - email = emails[0] - } - - // Parse indicators (domains, emails, ips, domains etc)! - newEmail := shuffle.ParsedShuffleMail{} - newEmail.Body.ContentType = email.Body.Contenttype - newEmail.Body.Content = email.Body.Content - newEmail.Body.RawBody = email.Body.Content - - newEmail.Header.Subject = email.Subject - newEmail.Header.From = email.From.Emailaddress.Address - for _, to := range email.Torecipients { - newEmail.Header.To = append(newEmail.Header.To, to.Emailaddress.Address) - } - newEmail.Header.Date = email.Receiveddatetime.String() - - newEmail.MessageID = email.ID - - if email.Hasattachments { - log.Printf("SHOULD HANDLE ATTACHMENTS FOR EMAIL!") - - for _, attachment := range email.Attachments { - parsedAttachment, err := getOutlookAttachment(outlookClient, email.ID, attachment.ID) - if err != nil { - log.Printf("Failed attachment %s: %s", attachment.ID, err) - continue - } - - log.Printf("ATTACHMENT: %#v", parsedAttachment) - } - //log.Printf("%#v", attachments) - //log.Printf("%s", err) - //GET /users/{id | userPrincipalName}/events/{id}/attachments/{id} - } - - emailBytes, err := json.Marshal(email) - if err != nil { - log.Printf("[INFO] Failed email marshaling: %s", err) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - type ExecutionStruct struct { - Start string `json:"start"` - ExecutionSource string `json:"execution_source"` - ExecutionArgument string `json:"execution_argument"` - } - - newBody := ExecutionStruct{ - Start: hook.Start, - ExecutionSource: "outlook", - ExecutionArgument: string(emailBytes), - } - - b, err := json.Marshal(newBody) - if err != nil { - log.Printf("[INFO] Failed newBody marshaling: %s", err) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - baseUrl := &url.URL{} - newRequest := &http.Request{ - URL: baseUrl, - Method: "POST", - Body: ioutil.NopCloser(bytes.NewReader(b)), - } - - workflow := shuffle.Workflow{ - ID: "", - } - - // OrgId: activeOrgs[0].Id, - workflowExecution, executionResp, err := handleExecution(hook.WorkflowId, workflow, newRequest) - if err == nil { - resp.WriteHeader(200) - resp.Write([]byte(fmt.Sprintf(`{"success": true, "execution_id": "%s", "authorization": "%s"}`, workflowExecution.ExecutionId, workflowExecution.Authorization))) - return - } - - resp.WriteHeader(500) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, executionResp))) -} - -func removeOutlookSubscription(outlookClient *http.Client, subscriptionId string) error { - // DELETE https://graph.microsoft.com/v1.0/subscriptions/{id} - fullUrl := fmt.Sprintf("https://graph.microsoft.com/v1.0/subscriptions/%s", subscriptionId) - req, err := http.NewRequest( - "DELETE", - fullUrl, - nil, - ) - req.Header.Add("Content-Type", "application/json") - res, err := outlookClient.Do(req) - if err != nil { - log.Printf("Client: %s", err) - return err - } - - if res.StatusCode != 200 && res.StatusCode != 201 && res.StatusCode != 204 { - return errors.New(fmt.Sprintf("Bad status code when deleting subscription: %d", res.StatusCode)) - } - - body, err := ioutil.ReadAll(res.Body) - if err != nil { - log.Printf("Body: %s", err) - return err - } - - _ = body - - return nil -} - -func handleGmailSubRemoval(ctx context.Context, user shuffle.User, workflowId, triggerId string) error { - // 1. Get the auth for trigger - // 2. Stop the subscription - // 3. Remove the function - // 4. Remove the database entry for auth - trigger, err := shuffle.GetTriggerAuth(ctx, triggerId) - if err != nil { - log.Printf("Trigger auth %s doesn't exist - outlook sub removal.", triggerId) - return err - } - //if runningEnvironment != "cloud" { - // log.Printf("[INFO] SHOULD STOP OUTLOOK SUB ONPREM SYNC WITH CLOUD for workflow ID %s", workflowId) - // org, err := shuffle.GetOrg(ctx, user.ActiveOrg.Id) - // if err != nil { - // log.Printf("[INFO] Failed finding org %s during outlook removal: %s", org.Id, err) - // return err - // } - - // log.Printf("[INFO] Stopping cloud configuration for trigger %s in org %s for workflow %s", trigger.Id, org.Id, trigger.WorkflowId) - // action := shuffle.CloudSyncJob{ - // Type: "outlook", - // Action: "stop", - // OrgId: org.Id, - // PrimaryItemId: trigger.Id, - // SecondaryItem: trigger.Start, - // ThirdItem: trigger.WorkflowId, - // } - - // err = executeCloudAction(action, org.SyncConfig.Apikey) - // if err != nil { - // log.Printf("[INFO] Failed cloud action STOP outlook execution: %s", err) - // return err - // } else { - // log.Printf("[INFO] Successfully set STOPPED outlook execution trigger") - // } - //} else { - // log.Printf("SHOULD STOP OUTLOOK SUB IN CLOUD") - //} - - // Actually delete the thing - redirectDomain := "localhost:5001" - url := fmt.Sprintf("http://%s/api/v1/triggers/outlook/register", redirectDomain) - gmailClient, _, err := shuffle.GetGmailClient(ctx, "", trigger.OauthToken, url) - if err != nil { - log.Printf("[WARNING] Oauth client failure - gmail delete: %s", err) - return err - } - - // bytes.NewBuffer(data) - fullUrl := "https://www.googleapis.com/gmail/v1/users/me/stop" - req, err := http.NewRequest( - "POST", - fullUrl, - nil, - ) - req.Header.Add("Content-Type", "application/json") - res, err := gmailClient.Do(req) - if err != nil { - log.Printf("[WARNING] GMAIL Client (2): %s", err) - return err - } - - log.Printf("[INFO] Stop subscription on GMAIL Status: %d", res.StatusCode) - body, err := ioutil.ReadAll(res.Body) - if err != nil { - log.Printf("[WARNING] Gmail subscription Body (2): %s", err) - return err - } - - log.Printf("GMAIL RESP (%d): %s", res.StatusCode, string(body)) - - if res.StatusCode != 200 && res.StatusCode != 201 { - return errors.New(fmt.Sprintf("Subscription failed: %s", string(body))) - } - //notificationURL := fmt.Sprintf("%s/api/v1/hooks/webhook_%s", syncSubUrl, trigger.Id) - //curSubscriptions, err := getOutlookSubscriptions(outlookClient) - //if err == nil { - // for _, sub := range curSubscriptions.Value { - // if sub.NotificationURL == notificationURL { - // log.Printf("[INFO] Removing subscription %s from gmail for %s", sub.Id, workflowId) - // removeOutlookSubscription(outlookClient, sub.Id) - // } - // } - //} else { - // log.Printf("Failed to get subscriptions - need to overwrite") - //} - - return nil -} - -// Remove AUTH -// Remove function -// Remove subscription -func handleOutlookSubRemoval(ctx context.Context, user shuffle.User, workflowId, triggerId string) error { - // 1. Get the auth for trigger - // 2. Stop the subscription - // 3. Remove the function - // 4. Remove the database entry for auth - trigger, err := shuffle.GetTriggerAuth(ctx, triggerId) - if err != nil { - log.Printf("Trigger auth %s doesn't exist - outlook sub removal.", triggerId) - return err - } - - if runningEnvironment != "cloud" { - log.Printf("[INFO] SHOULD STOP OUTLOOK SUB ONPREM SYNC WITH CLOUD for workflow ID %s", workflowId) - org, err := shuffle.GetOrg(ctx, user.ActiveOrg.Id) - if err != nil { - log.Printf("[INFO] Failed finding org %s during outlook removal: %s", org.Id, err) - return err - } - - log.Printf("[INFO] Stopping cloud configuration for trigger %s in org %s for workflow %s", trigger.Id, org.Id, trigger.WorkflowId) - action := shuffle.CloudSyncJob{ - Type: "outlook", - Action: "stop", - OrgId: org.Id, - PrimaryItemId: trigger.Id, - SecondaryItem: trigger.Start, - ThirdItem: trigger.WorkflowId, - } - - err = executeCloudAction(action, org.SyncConfig.Apikey) - if err != nil { - log.Printf("[INFO] Failed cloud action STOP outlook execution: %s", err) - return err - } else { - log.Printf("[INFO] Successfully set STOPPED outlook execution trigger") - } - } else { - log.Printf("SHOULD STOP OUTLOOK SUB IN CLOUD") - } - - // Actually delete the thing - redirectDomain := "localhost:5001" - url := fmt.Sprintf("http://%s/api/v1/triggers/outlook/register", redirectDomain) - outlookClient, _, err := shuffle.GetOutlookClient(ctx, "", trigger.OauthToken, url) - if err != nil { - log.Printf("[WARNING] Oauth client failure - outlook folders: %s", err) - return err - } - notificationURL := fmt.Sprintf("%s/api/v1/hooks/webhook_%s", syncSubUrl, trigger.Id) - curSubscriptions, err := getOutlookSubscriptions(outlookClient) - if err == nil { - for _, sub := range curSubscriptions.Value { - if sub.NotificationURL == notificationURL { - log.Printf("[INFO] Removing subscription %s from o365 for workflow %s", sub.Id, workflowId) - removeOutlookSubscription(outlookClient, sub.Id) - } - } - } else { - log.Printf("Failed to get subscriptions - need to overwrite") - } - - return nil -} - -func handleDeleteGmailSub(resp http.ResponseWriter, request *http.Request) { - cors := handleCors(resp, request) - if cors { - return - } - - location := strings.Split(request.URL.String(), "/") - - var workflowId string - var triggerId string - if location[1] == "api" { - if len(location) <= 6 { - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - workflowId = location[4] - triggerId = location[6] - } - - if len(workflowId) == 0 || len(triggerId) == 0 { - log.Printf("Ids can't be zero when deleting %s", workflowId) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - ctx := context.Background() - workflow, err := shuffle.GetWorkflow(ctx, workflowId) - if err != nil { - log.Printf("[WARNING] Failed getting the workflow locally (delete outlook): %s", err) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - user, err := shuffle.HandleApiAuthentication(resp, request) - if err != nil { - log.Printf("[WARNING] Api authentication failed in outlook deploy: %s", err) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - 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 %s as admin (delete gmail sub)", user.Username, workflow.ID) - } else { - log.Printf("[WARNING] Wrong user (%s) for workflow %s when deleting gmail ", user.Username, workflow.ID) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - } - - trigger, err := shuffle.GetTriggerAuth(ctx, triggerId) - if err != nil { - log.Printf("[WARNING] Wrong user (%s) for workflow %s when deleting gmail (2)", user.Username, workflow.ID) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - err = handleGmailSubRemoval(ctx, user, workflowId, triggerId) - if err == nil { - // FIXME: Actually delete the hook, not just the data - shuffle.DeleteKey(ctx, "gmail_subscription", trigger.SubscriptionId) - shuffle.DeleteKey(ctx, "trigger_auth", triggerId) - } else { - log.Printf("[WARNING] Failed deleting gmail sub: %s", err) - } - - resp.WriteHeader(200) - resp.Write([]byte(`{"success": true}`)) -} - -func handleDeleteOutlookSub(resp http.ResponseWriter, request *http.Request) { - cors := handleCors(resp, request) - if cors { - return - } - - location := strings.Split(request.URL.String(), "/") - - var workflowId string - var triggerId string - if location[1] == "api" { - if len(location) <= 6 { - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - workflowId = location[4] - triggerId = location[6] - } - - if len(workflowId) == 0 || len(triggerId) == 0 { - log.Printf("Ids can't be zero when deleting %s", workflowId) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - ctx := context.Background() - workflow, err := shuffle.GetWorkflow(ctx, workflowId) - if err != nil { - log.Printf("[WARNING] Failed getting the workflow locally (delete outlook): %s", err) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - user, err := shuffle.HandleApiAuthentication(resp, request) - if err != nil { - log.Printf("[WARNING] Api authentication failed in outlook deploy: %s", err) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - // FIXME - have a check for org etc 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 %s as admin (delete outlook sub)", user.Username, workflow.ID) - } else { - log.Printf("[WARNING] Wrong user (%s) for workflow %s when deploying outlook", user.Username, workflow.ID) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - } - - // Check what kind of sub it is - err = handleOutlookSubRemoval(ctx, user, workflowId, triggerId) - if err != nil { - log.Printf("Failed sub removal: %s", err) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - resp.WriteHeader(200) - resp.Write([]byte(`{"success": true}`)) -} - -// This sets up the sub with outlook itself -// Parses data from the workflow to see whether access is right to subscribe it -// Creates the cloud function for outlook return -// Wait for it to be available, then schedule a workflow to it -func handleCreateOutlookSub(resp http.ResponseWriter, request *http.Request) { - cors := handleCors(resp, request) - if cors { - return - } - - location := strings.Split(request.URL.String(), "/") - - var workflowId string - if location[1] == "api" { - if len(location) <= 4 { - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - workflowId = location[4] - } - - ctx := context.Background() - workflow, err := shuffle.GetWorkflow(ctx, workflowId) - if err != nil { - log.Printf("[WARNING] Failed getting the workflow locally (outlook sub): %s", err) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - user, err := shuffle.HandleApiAuthentication(resp, request) - if err != nil { - log.Printf("Api authentication failed in outlook deploy: %s", err) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - // FIXME - have a check for org etc too.. - if user.Id != workflow.Owner && user.Role != "admin" { - log.Printf("[WARNING] Wrong user (%s) for workflow %s when deploying outlook", user.Username, workflow.ID) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - log.Println("[INFO] Handle outlook subscription for trigger") - - // Should already be authorized at this point, as the workflow is shared - body, err := ioutil.ReadAll(request.Body) - if err != nil { - log.Printf("Failed body read for workflow %s", workflow.ID) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - // Based on the input data from frontend - type CurTrigger struct { - Name string `json:"name"` - Folders []string `json:"folders"` - ID string `json:"id"` - } - - //log.Println(string(body)) - var curTrigger CurTrigger - err = json.Unmarshal(body, &curTrigger) - if err != nil { - log.Printf("Failed body read unmarshal for trigger %s", workflow.ID) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - if len(curTrigger.Folders) == 0 { - log.Printf("Error for %s. Choosing folders is required, currently 0", workflow.ID) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - // Now that it's deployed - wait a few seconds before generating: - // 1. Oauth2 token thingies for outlook.office.com - // 2. Set the url to have the right mailboxes (probably ID?) ("https://outlook.office.com/api/v2.0/me/mailfolders('inbox')/messages") - // 3. Set the callback URL to be the new trigger - // 4. Run subscription test - // 5. Set the subscriptionId to the trigger object - - // First - lets regenerate an oauth token for outlook.office.com from the original items - trigger, err := shuffle.GetTriggerAuth(ctx, curTrigger.ID) - if err != nil { - log.Printf("[INFO] Trigger %s doesn't exist - gmail sub.", curTrigger.ID) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false, "reason": ""}`)) - return - } - - // url doesn't really matter here - //url := fmt.Sprintf("https://shuffler.io") - redirectDomain := "localhost:5001" - url := fmt.Sprintf("http://%s/api/v1/triggers/outlook/register", redirectDomain) - outlookClient, _, err := shuffle.GetOutlookClient(ctx, "", trigger.OauthToken, url) - if err != nil { - log.Printf("[WARNING] Oauth client failure for gmail - triggerauth: %s", err) - resp.Write([]byte(`{"success": false, "reason": ""}`)) - resp.WriteHeader(401) - return - } - - // Location + - - // This is here simply to let the function start - // Usually takes 10 attempts minimum :O - // 10 * 5 = 50 seconds. That's waaay too much :( - - if runningEnvironment != "cloud" { - org, err := shuffle.GetOrg(ctx, user.ActiveOrg.Id) - if err != nil { - log.Printf("Failed finding org %s: %s", org.Id, err) - return - } - log.Printf("[INFO] Starting cloud configuration TO START trigger %s in org %s for workflow %s", trigger.Id, org.Id, trigger.WorkflowId) - - action := shuffle.CloudSyncJob{ - Type: "outlook", - Action: "start", - OrgId: org.Id, - PrimaryItemId: trigger.Id, - SecondaryItem: trigger.Start, - ThirdItem: workflowId, - } - - err = executeCloudAction(action, org.SyncConfig.Apikey) - if err != nil { - log.Printf("[INFO] Failed cloud action START outlook execution: %s", err) - resp.WriteHeader(401) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err))) - return - } else { - log.Printf("[INFO] Successfully set up cloud action trigger") - } - } else { - log.Printf("Should configure a running environment for CLOUD") - } - - notificationURL := fmt.Sprintf("%s/api/v1/hooks/webhook_%s", syncSubUrl, trigger.Id) - curSubscriptions, err := getOutlookSubscriptions(outlookClient) - if err == nil { - for _, sub := range curSubscriptions.Value { - if sub.NotificationURL == notificationURL { - log.Printf("[INFO] Removing existing subscription %s", sub.Id) - removeOutlookSubscription(outlookClient, sub.Id) - } - } - } else { - log.Printf("[INFO] Failed to get subscriptions - need to overwrite") - } - - maxFails := 5 - failCnt := 0 - log.Println("[INFO] Folders: %#v", curTrigger.Folders) - for { - subId, err := makeOutlookSubscription(outlookClient, curTrigger.Folders, notificationURL) - if err != nil { - failCnt += 1 - - log.Printf("[WARNING] Failed making oauth subscription for outlook, retrying in 5 seconds: %s", err) - - time.Sleep(5 * time.Second) - if failCnt == maxFails { - log.Printf("Failed to set up subscription %d times.", maxFails) - resp.WriteHeader(401) - return - } - - continue - } - - // Set the ID somewhere here - trigger.SubscriptionId = subId - err = shuffle.SetTriggerAuth(ctx, *trigger) - if err != nil { - log.Printf("[WARNING] Failed setting triggerauth (gmail): %s", err) - } - - break - } - - log.Printf("[INFO] Successfully handled outlook subscription for trigger %s in workflow %s", curTrigger.ID, workflow.ID) - - //log.Printf("%#v", user) - resp.WriteHeader(200) - resp.Write([]byte(`{"success": true}`)) -} - -// This sets up the sub with outlook itself -// Parses data from the workflow to see whether access is right to subscribe it -// Creates the cloud function for outlook return -// Wait for it to be available, then schedule a workflow to it -func handleCreateGmailSub(resp http.ResponseWriter, request *http.Request) { - cors := handleCors(resp, request) - if cors { - return - } - - location := strings.Split(request.URL.String(), "/") - - var workflowId string - if location[1] == "api" { - if len(location) <= 4 { - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - workflowId = location[4] - } - - ctx := context.Background() - workflow, err := shuffle.GetWorkflow(ctx, workflowId) - if err != nil { - log.Printf("[WARNING] Failed getting the workflow locally (outlook sub): %s", err) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - user, err := shuffle.HandleApiAuthentication(resp, request) - if err != nil { - log.Printf("Api authentication failed in outlook deploy: %s", err) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - // FIXME - have a check for org etc too.. - if user.Id != workflow.Owner && user.Role != "admin" { - log.Printf("[WARNING] Wrong user (%s) for workflow %s when deploying outlook", user.Username, workflow.ID) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - log.Println("[INFO] Handle gmail subscription for trigger!") - - // Should already be authorized at this point, as the workflow is shared - body, err := ioutil.ReadAll(request.Body) - if err != nil { - log.Printf("Failed body read for workflow %s", workflow.ID) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - // Based on the input data from frontend - type CurTrigger struct { - Name string `json:"name"` - Folders []string `json:"folders"` - ID string `json:"id"` - } - - //log.Println(string(body)) - var curTrigger CurTrigger - err = json.Unmarshal(body, &curTrigger) - if err != nil { - log.Printf("Failed body read unmarshal for trigger %s", workflow.ID) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - if len(curTrigger.Folders) == 0 { - log.Printf("[WARNING] Error for %s. Choosing folders is required, currently 0", workflow.ID) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - // Now that it's deployed - wait a few seconds before generating: - // 1. Oauth2 token thingies for outlook.office.com - // 2. Set the url to have the right mailboxes (probably ID?) ("https://outlook.office.com/api/v2.0/me/mailfolders('inbox')/messages") - // 3. Set the callback URL to be the new trigger - // 4. Run subscription test - // 5. Set the subscriptionId to the trigger object - - // First - lets regenerate an oauth token for outlook.office.com from the original items - trigger, err := shuffle.GetTriggerAuth(ctx, curTrigger.ID) - if err != nil { - log.Printf("[INFO] Trigger %s doesn't exist - gmail sub.", curTrigger.ID) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false, "reason": ""}`)) - return - } - - // url doesn't really matter here - //url := fmt.Sprintf("https://shuffler.io") - redirectDomain := "localhost:5001" - url := fmt.Sprintf("http://%s/api/v1/triggers/gmail/register", redirectDomain) - gmailClient, _, err := shuffle.GetGmailClient(ctx, "", trigger.OauthToken, url) - if err != nil { - log.Printf("[WARNING] Oauth client failure in gmail - triggerauth: %s", err) - resp.Write([]byte(`{"success": false, "reason": ""}`)) - resp.WriteHeader(401) - return - } - - // Location + - - // This is here simply to let the function start - // Usually takes 10 attempts minimum :O - // 10 * 5 = 50 seconds. That's waaay too much :( - - /* - if runningEnvironment != "cloud" { - org, err := shuffle.GetOrg(ctx, user.ActiveOrg.Id) - if err != nil { - log.Printf("Failed finding org %s: %s", org.Id, err) - return - } - log.Printf("[INFO] Starting cloud configuration TO START trigger %s in org %s for workflow %s", trigger.Id, org.Id, trigger.WorkflowId) - - action := shuffle.CloudSyncJob{ - Type: "gmail", - Action: "start", - OrgId: org.Id, - PrimaryItemId: trigger.Id, - SecondaryItem: trigger.Start, - ThirdItem: workflowId, - } - - err = executeCloudAction(action, org.SyncConfig.Apikey) - if err != nil { - log.Printf("[INFO] Failed cloud action START gmail execution: %s", err) - resp.WriteHeader(401) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err))) - return - } else { - log.Printf("[INFO] Successfully set up cloud action trigger") - } - } else { - log.Printf("Should configure a running environment for CLOUD") - } - */ - - notificationURL := fmt.Sprintf("%s/api/v1/hooks/webhook_%s", syncSubUrl, trigger.Id) - //curSubscriptions, err := getOutlookSubscriptions(outlookClient) - //if err == nil { - // for _, sub := range curSubscriptions.Value { - // if sub.NotificationURL == notificationURL { - // log.Printf("[INFO] Removing existing subscription %s", sub.Id) - // removeOutlookSubscription(outlookClient, sub.Id) - // } - // } - //} else { - // log.Printf("[INFO] Failed to get subscriptions - need to overwrite") - //} - - //maxFails := 5 - //failCnt := 0 - log.Printf("Starting with notificationURL %s", notificationURL) - log.Println("[INFO] Folders: %#v", curTrigger.Folders) - for { - sub, err := makeGmailSubscription(gmailClient, curTrigger.Folders, notificationURL) - if err != nil { - //failCnt += 1 - - log.Printf("[WARNING] Failed making oauth subscription for gmail, retrying in 5 seconds: %s", err) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - - //time.Sleep(5 * time.Second) - //if failCnt == maxFails { - // log.Printf("Failed to set up subscription %d times.", maxFails) - // resp.WriteHeader(401) - // return - //} - - //continue - } - - // May not need a new one; could just do this in triggerId with search tbh - newSub := shuffle.SubscriptionRecipient{ - HistoryId: sub.HistoryId, - TriggerId: trigger.Id, - Edited: 0, - Expiration: sub.Expiration, - LastSync: int(time.Now().Unix()), - } - err = shuffle.SetSubscriptionRecipient(ctx, newSub, newSub.HistoryId) - if err != nil { - log.Printf("[WARNING] Failed setting sub ID (gmail): %s", sub.HistoryId) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - // Set the ID somewhere here - trigger.SubscriptionId = sub.HistoryId - err = shuffle.SetTriggerAuth(ctx, *trigger) - if err != nil { - log.Printf("[WARNING] Failed setting triggerauth (gmail): %s", err) - } - - break - } - - log.Printf("[INFO] Successfully handled outlook subscription for trigger %s in workflow %s", curTrigger.ID, workflow.ID) - - //log.Printf("%#v", user) - resp.WriteHeader(200) - resp.Write([]byte(`{"success": true}`)) -} - -func handleGmailRouting(resp http.ResponseWriter, request *http.Request) { - body, err := ioutil.ReadAll(request.Body) - if err != nil { - log.Printf("[WARNING] Body read error for gmail message: %s", err) - resp.WriteHeader(200) - return - } - - parsedMessage := shuffle.Inputdata{} - err = json.Unmarshal(body, &parsedMessage) - if err != nil { - log.Printf("[WARNING] Unmarshal error for gmail message: %s", err) - resp.WriteHeader(200) - return - } - - log.Printf("GMAIL BODY:\n%s", string(body)) - parsedData, err := base64.StdEncoding.DecodeString(parsedMessage.Message.Data) - if err != nil { - log.Printf("[WARNING] Failed base64 decode in gmail routing: %s", err) - resp.WriteHeader(200) - return - } - - log.Printf("Parsed data: %s", string(parsedData)) - findHistory := shuffle.ParsedMessage{} - err = json.Unmarshal(parsedData, &findHistory) - if err != nil { - log.Printf("[WARNING] Unmarshal error for gmail message (2): %s", err) - resp.WriteHeader(200) - return - } - - // This History ID will match the ID that is received when the subscription is configured. - log.Printf("HistoryId: %d. Email: %s", findHistory.HistoryId, findHistory.EmailAddress) - ctx := context.Background() - subscription, err := shuffle.GetSubscriptionRecipient(ctx, fmt.Sprintf("%d", findHistory.HistoryId)) - if err != nil { - log.Printf("[WARNING] Failed finding gmail history for ID %d: %s. Should the subscription be cancelled?", findHistory.HistoryId, err) - resp.WriteHeader(200) - return - } - - log.Printf("SUB: %#v", subscription) - - resp.WriteHeader(200) -} diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index 4b9756cb..aaf6e2a6 100644 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -1080,9 +1080,14 @@ func deleteWorkflow(resp http.ResponseWriter, request *http.Request) { // log.Printf("Failed to delete webhook: %s", err) //} } else if item.TriggerType == "EMAIL" { - err = handleOutlookSubRemoval(ctx, user, workflow.ID, item.ID) + err = shuffle.HandleOutlookSubRemoval(ctx, user, workflow.ID, item.ID) if err != nil { - log.Printf("Failed to delete email sub: %s", err) + log.Printf("Failed to delete OUTLOOK email sub (checking gmail after): %s", err) + } + + err = shuffle.HandleGmailSubRemoval(ctx, user, workflow.ID, item.ID) + if err != nil { + log.Printf("Failed to delete gmail email sub: %s", err) } } diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index fb538fcb..a410d447 100644 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -4125,18 +4125,32 @@ const AngularWorkflow = (props) => { "long_description": "Create a schedule based on cron", }, { - "name": "Email", + "name": "Office365", "type": "TRIGGER", "status": "uninitialized", - "description": "Add your email provider", + "description": "Starts upon O365 email", "trigger_type": "EMAIL", "errors": null, "is_valid": cloudSyncEnabled || isCloud ? true : false, "label": "Email", "environment": "cloud", - "large_image": 'data:image/png;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/hAytodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMy1jMDExIDY2LjE0NTY2MSwgMjAxMi8wMi8wNi0xNDo1NjoyNyAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNiAoV2luZG93cykiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6Nzg4QTJBMjVEMDI1MTFFN0EwQUVDODc5QjYyQkFCMUQiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6Nzg4QTJBMjZEMDI1MTFFN0EwQUVDODc5QjYyQkFCMUQiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDo3ODhBMkEyM0QwMjUxMUU3QTBBRUM4NzlCNjJCQUIxRCIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDo3ODhBMkEyNEQwMjUxMUU3QTBBRUM4NzlCNjJCQUIxRCIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pv/bAEMAAwICAgICAwICAgMDAwMEBgQEBAQECAYGBQYJCAoKCQgJCQoM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', + "large_image": 'data:image/jpg;base64,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', "long_description": "Execute a workflow when you get an email", - }] + }, + { + "name": "Gmail", + "type": "TRIGGER", + "status": "uninitialized", + "description": "Trigger based on Gmail", + "trigger_type": "EMAIL", + "errors": null, + "is_valid": cloudSyncEnabled || isCloud ? true : false, + "label": "Email", + "environment": "cloud", + "large_image": 'data:image/jpg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/4QAYRXhpZgAASUkqAAgAAAAAAAAAAAAAAP/hAzFodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQ1IDc5LjE2MzQ5OSwgMjAxOC8wOC8xMy0xNjo0MDoyMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKE1hY2ludG9zaCkiIHhtcE1NOkluc3RhbmNlSUQ9InhtcC5paWQ6QTIyMjgyMEYwMDJDMTFFQkJBOEE5OUJBM0MzMTA2RDIiIHhtcE1NOkRvY3VtZW50SUQ9InhtcC5kaWQ6QTIyMjgyMTAwMDJDMTFFQkJBOEE5OUJBM0MzMTA2RDIiPiA8eG1wTU06RGVyaXZlZEZyb20gc3RSZWY6aW5zdGFuY2VJRD0ieG1wLmlpZDozQTMwMDQxRTAwMEUxMUVCQkE4QTk5QkEzQzMxMDZEMiIgc3RSZWY6ZG9jdW1lbnRJRD0ieG1wLmRpZDpBMjIyODIwRTAwMkMxMUVCQkE4QTk5QkEzQzMxMDZEMiIvPiA8L3JkZjpEZXNjcmlwdGlvbj4gPC9yZGY6UkRGPiA8L3g6eG1wbWV0YT4gPD94cGFja2V0IGVuZD0iciI/Pv/bAEMAAwICAwICAwMCAwMDAwMEBwUEBAQECQYHBQcKCQsL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', + "long_description": "Execute a workflow when you get an email", + } + ] const TriggersView = () => { const triggersViewStyle = { @@ -5700,17 +5714,24 @@ const AngularWorkflow = (props) => { } - const gmailButton = + const gmailButton = selectedTrigger.name !== "Gmail" ? null : - const outlookButton = + const outlookButton = selectedTrigger.name !== "Office365" ? null : @@ -5844,7 +5877,7 @@ const AngularWorkflow = (props) => {
    - Select a folder + Select folders
    { cy.add(cybranch) } + console.log("Value to be set: ", e.target.value) try { workflow.triggers[selectedTriggerIndex].parameters[3].value = e.target.value.id } catch { @@ -6927,7 +6973,7 @@ const AngularWorkflow = (props) => { const item = splitItem[key] const curfolder = triggerFolders.find(a => a.displayName === item) if (curfolder === undefined) { - alert.error("Something went wrong with outlook folder: "+item) + alert.error("Something went wrong with folder selection: "+item) return } @@ -7002,7 +7048,6 @@ const AngularWorkflow = (props) => { } const param = trigger.parameters.find(param => param.name === "auth_headers") - console.log("PARAM: ", param) var auth = "" if (param !== undefined && param !== null) { auth = param.value @@ -7258,7 +7303,7 @@ const AngularWorkflow = (props) => { }} fullWidth color="primary" - placeholder={"+474823212,+460203042"} + placeholder={"+474823212132,+46020304242"} defaultValue={workflow.triggers[selectedTriggerIndex].parameters[4].value} onBlur={(event) => { workflow.triggers[selectedTriggerIndex].parameters[4].value = event.target.value @@ -7633,7 +7678,7 @@ const AngularWorkflow = (props) => { @@ -8156,12 +8201,12 @@ const AngularWorkflow = (props) => {
    -

    Workflow Results

    +

    Execution info

    - + + Submit + diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index 46bcafd4..8c5ef7a0 100644 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -707,6 +707,11 @@ const Workflows = (props) => { useEffect(() => { if (workflows.length <= 0 && firstrequest) { + const tmpView = localStorage.getItem('view'); + if (tmpView !== undefined && tmpView !== null) { + setView(tmpView) + } + setFirstrequest(false) getAvailableWorkflows() } @@ -1847,12 +1852,18 @@ const Workflows = (props) => { : null*/} {view === "list" && ( - + )} {view === "grid" && ( - + )} From 09d02c7f18770ec924564b6eeebfc58aeddf9543 Mon Sep 17 00:00:00 2001 From: frikky Date: Tue, 12 Oct 2021 00:50:59 +0200 Subject: [PATCH 110/117] 0.9.22 release with loads of bugfixes --- docker-compose.yml | 4 ++-- frontend/package.json | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 09f810e1..069fedad 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '3' services: frontend: - build: ./frontend + #build: ./frontend image: ghcr.io/frikky/shuffle-frontend:nightly container_name: shuffle-frontend hostname: shuffle-frontend @@ -16,7 +16,7 @@ services: depends_on: - backend backend: - build: ./backend + #build: ./backend image: ghcr.io/frikky/shuffle-backend:nightly container_name: shuffle-backend hostname: ${BACKEND_HOSTNAME} diff --git a/frontend/package.json b/frontend/package.json index dde376fd..43072ce4 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -10,7 +10,6 @@ "@material-ui/lab": "^4.0.0-alpha.58", "@material-ui/styles": "^4.5.2", "@material-ui/utils": "^4.11.2", - "@mui/icons-material": "^5.0.3", "@uiw/react-codemirror": "^3.2.1", "@use-it/interval": "^1.0.0", "babel-eslint": "^10.1.0", From 7159bf207c694c3279c8a6b03607c7144b05b644 Mon Sep 17 00:00:00 2001 From: frikky Date: Tue, 12 Oct 2021 18:41:47 +0200 Subject: [PATCH 111/117] Moved prints to logging in python --- backend/app_sdk/app_base.py | 786 ++++++++++++++++++------------------ 1 file changed, 399 insertions(+), 387 deletions(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index 252885b2..ce954c25 100644 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -52,7 +52,7 @@ class AppBase: self.action = json.loads(self.action) self.original_action = json.loads(self.action) except: - print("[WARNING] Failed parsing action as JSON") + self.logger.info("[WARNING] Failed parsing action as JSON") if len(self.base_url) == 0: self.base_url = self.url @@ -63,45 +63,48 @@ class AppBase: if action_result["status"] == "EXECUTING": action_result["status"] = "FAILURE" + self.logger.info(f"""[DEBUG] Inside Send result with status {action_result["status"]}""") + # FIXME: Add cleanup of parameters to not send to frontend here params = {} #action = action_result["action"] #try: # for item in action["authentication"]: # for action["parameters"] - # print("AUTH: ", key, value) + # self.logger.info("AUTH: ", key, value) # params[item["key"]] = item["value"] #except KeyError: - # print("No authentication specified!") + # self.logger.info("No authentication specified!") # pass # I wonder if this actually works - self.logger.info("Before last stream result") + self.logger.info(f"[DEBUG] Before last stream result") url = "%s%s" % (self.base_url, stream_path) - #print("[INFO] URL (URL): %s" % url) + #self.logger.info("[INFO] URL (URL): %s" % url) try: ret = requests.post(url, headers=headers, json=action_result) - self.logger.info("Result: %d" % ret.status_code) + self.logger.info(f"[DEBUG] Result: {res.status_code}") if ret.status_code != 200: - self.logger.info(ret.text) + self.logger.info(f"[DEBUG] Shuffle Response: {ret.text}") + + self.logger.info("[DEBUG] Stream result was sent!") except requests.exceptions.ConnectionError as e: - #self.logger.exception("ConnectionError: %s" % e) - self.logger.info("Expected ConnectionError happened") + self.logger.info(f"[DEBUG] Unexpected ConnectionError happened: {e}") return except TypeError as e: #self.logger.exception(e) action_result["status"] = "FAILURE" - action_result["result"] = "POST error: %s" % e - self.logger.info("Before typeerror stream result") + action_result["result"] = f"POST error: {e}" + self.logger.info(f"[DEBUG] Before typeerror stream result: {e}") ret = requests.post("%s%s" % (self.base_url, stream_path), headers=headers, json=action_result) - self.logger.info("Result: %d" % ret.status_code) + self.logger.info(f"[DEBUG] Result: {ret.status_code}") if ret.status_code != 200: self.logger.info(ret.text) except http.client.RemoteDisconnected as e: - self.logger.info("Expected Remotedisconnect happened") + self.logger.info(f"[DEBUG] Expected Remotedisconnect happened: {e}") return except urllib3.exceptions.ProtocolError as e: - self.logger.info("Expected ProtocolError happened") + self.logger.info(f"[DEBUG] Expected ProtocolError happened: {e}") return async def cartesian_product(self, L): @@ -112,19 +115,19 @@ class AppBase: # Handles unique fields by negoiating with the backend def validate_unique_fields(self, params): - #print("IN THE UNIQUE FIELDS PLACE!") + #self.logger.info("IN THE UNIQUE FIELDS PLACE!") newlist = [params] if isinstance(params, list): - #print("ITS A LIST!") + #self.logger.info("ITS A LIST!") newlist = params #self.full_execution = os.getenv("FULL_EXECUTION", "") - #print(len(params)) - #print(params.items()) - #print(list(params.items())) - #print(f"PARAM: {params}") - #print(f"NEWLIST: {newlist}") + #self.logger.info(len(params)) + #self.logger.info(params.items()) + #self.logger.info(list(params.items())) + #self.logger.info(f"PARAM: {params}") + #self.logger.info(f"NEWLIST: {newlist}") # FIXME: Also handle MULTI PARAM values = [] @@ -133,21 +136,21 @@ class AppBase: index = 0 for outerparam in newlist: - #print(f"INNERTYPE: {type(outerparam)}") - #print(f"HANDLING PARAM {key}") + #self.logger.info(f"INNERTYPE: {type(outerparam)}") + #self.logger.info(f"HANDLING PARAM {key}") param_value = "" for key, value in outerparam.items(): - #print("KEY: %s" % key) + #self.logger.info("KEY: %s" % key) #value = params[key] for param in self.action["parameters"]: try: if param["name"] == key and param["unique_toggled"]: - print(f"FOUND: {key} with param {param}!") + self.logger.info(f"[DEBUG] FOUND: {key} with param {param}!") if isinstance(value, dict) or isinstance(value, list): try: value = json.dumps(value) except json.decoder.JSONDecodeError as e: - print(f"Error in json decode for param {value}: {e}") + self.logger.info(f"[WARNING] Error in json decode for param {value}: {e}") continue elif isinstance(value, int) or isinstance(value, float): value = str(value) @@ -156,15 +159,15 @@ class AppBase: elif value == True: value = "True" - print(f"VALUE APPEND: {value}") + self.logger.info(f"[DEBUG] VALUE APPEND: {value}") param_value += value if param["name"] not in param_names: param_names.append(param["name"]) except (KeyError, NameError) as e: - print(f"""Key/NameError in param handler for {param["name"]}: {e}""") + self.logger.info(f"""Key/NameError in param handler for {param["name"]}: {e}""") - print(f"[DEBUG] OUTER VALUE: {param_value}") + self.logger.info(f"[DEBUG] OUTER VALUE: {param_value}") if len(param_value) > 0: md5 = hashlib.md5(param_value.encode('utf-8')).hexdigest() values.append(md5) @@ -201,12 +204,12 @@ class AppBase: }] } - #print(f"DATA: {data}") + #self.logger.info(f"DATA: {data}") # 1594869a676630b397bc34f7dc0951a3 - #print(f"VALUE URL: {url}") - #print(f"RET: {ret.text}") - #print(f"ID: {ret.status_code}") + #self.logger.info(f"VALUE URL: {url}") + #self.logger.info(f"RET: {ret.text}") + #self.logger.info(f"ID: {ret.status_code}") url = f"{self.url}/api/v1/orgs/{org_id}/validate_app_values" ret = requests.post(url, json=data) if ret.status_code == 200: @@ -214,27 +217,27 @@ class AppBase: if len(json_value["found"]) > 0: modifier = 0 for item in json_value["found"]: - print(f"Should remove {item}") + self.logger.info(f"Should remove {item}") try: - print(f"FOUND: {all_values[item]}") - print(f"SHOULD REMOVE INDEX: {all_values[item]['index']}") + self.logger.info(f"FOUND: {all_values[item]}") + self.logger.info(f"SHOULD REMOVE INDEX: {all_values[item]['index']}") try: newlist.pop(all_values[item]["index"]-modifier) modifier += 1 except IndexError as e: - print(f"Error popping value from array: {e}") + self.logger.info(f"Error popping value from array: {e}") except (NameError, KeyError) as e: - print(f"Failed removal: {e}") + self.logger.info(f"Failed removal: {e}") #return False else: - print("None of the items were found!") + self.logger.info("None of the items were found!") return newlist else: - print(f"[WARNING] Failed checking values with status code {ret.status_code}!") + self.logger.info(f"[WARNING] Failed checking values with status code {ret.status_code}!") #return True return newlist @@ -295,19 +298,19 @@ class AppBase: #self.action = action loopnames = [] - #print(f"Baseparams to check!!: {baseparams}") + #self.logger.info(f"Baseparams to check!!: {baseparams}") for key, value in baseparams.items(): check_value = "" for param in self.original_action["parameters"]: if param["name"] == key: - #print("PARAM: %s" % param) + #self.logger.info("PARAM: %s" % param) check_value = param["value"] # self.result_wrapper_count = 0 octothorpe_count = param["value"].count(".#") if octothorpe_count > self.result_wrapper_count: self.result_wrapper_count = octothorpe_count - print("[INFO] NEW OCTOTHORPE WRAPPER: %d" % octothorpe_count) + self.logger.info("[INFO] NEW OCTOTHORPE WRAPPER: %d" % octothorpe_count) # This whole thing is hard. @@ -328,13 +331,13 @@ class AppBase: # FIXME: Check the above, and fix so that nested looped items can be # Skipped if wanted - #print("\nCHECK: %s" % check_value) + #self.logger.info("\nCHECK: %s" % check_value) #try: # values = parameter["value_replace"] # if values != None: - # print(values) + # self.logger.info(values) # for val in values: - # print(val) + # self.logger.info(val) #except: # pass @@ -343,15 +346,15 @@ class AppBase: should_merge = True # Specific for OpenAPI body replacement - #print("\n\n\nDOING STUFF BELOW HERE") + #self.logger.info("\n\n\nDOING STUFF BELOW HERE") if not should_merge: for parameter in self.original_action["parameters"]: if parameter["name"] == key: - #print("CHECKING BODY FOR VALUE REPLACE DATA!") + #self.logger.info("CHECKING BODY FOR VALUE REPLACE DATA!") try: values = parameter["value_replace"] if values != None: - print(values) + self.logger.info(values) for val in values: if "#" in val["value"]: should_merge = True @@ -359,9 +362,9 @@ class AppBase: except: pass - #print(f"MERGE: {should_merge}") + #self.logger.info(f"MERGE: {should_merge}") if isinstance(value, list): - print(f"[DEBUG] Item {value} is a list.") + self.logger.info(f"[DEBUG] Item {value} is a list.") if len(value) <= 1: if len(value) == 1: baseparams[key] = value[0] @@ -370,7 +373,7 @@ class AppBase: # should_merge = True else: if not should_merge: - print("[DEBUG] Adding WITHOUT looping list") + self.logger.info("[DEBUG] Adding WITHOUT looping list") else: if len(value) not in listlengths: listlengths.append(len(value)) @@ -384,41 +387,41 @@ class AppBase: all_list_keys.append(key) all_lists.append(baseparams[key]) else: - #print(f"{value} is not a list") + #self.logger.info(f"{value} is not a list") pass - print("[DEBUG] Listlengths: %s" % listlengths) + self.logger.info("[DEBUG] Listlengths: %s" % listlengths) if len(listlengths) == 0: - print("[DEBUG] NO multiplier. Running a single iteration.") + self.logger.info("[DEBUG] NO multiplier. Running a single iteration.") paramlist.append(baseparams) elif len(listlengths) == 1: - print("[DEBUG] NO MULTIPLIER NECESSARY. Length is %d" % len(listitems)) + self.logger.info("[DEBUG] NO MULTIPLIER NECESSARY. Length is %d" % len(listitems)) for item in listitems: # This loops should always be length 1 for key, value in item.items(): if isinstance(value, int): - print("\n[DEBUG] Should run key %s %d times from %s" % (key, value, baseparams[key])) + self.logger.info("\n[DEBUG] Should run key %s %d times from %s" % (key, value, baseparams[key])) if len(paramlist) == value: - print("[DEBUG] List ALREADY exists - just changing values") + self.logger.info("[DEBUG] List ALREADY exists - just changing values") for subloop in range(value): baseitem = copy.deepcopy(baseparams) paramlist[subloop][key] = baseparams[key][subloop] else: - print("[DEBUG] List DOESNT exist - ADDING values") + self.logger.info("[DEBUG] List DOESNT exist - ADDING values") for subloop in range(value): baseitem = copy.deepcopy(baseparams) baseitem[key] = baseparams[key][subloop] paramlist.append(baseitem) else: - print("[DEBUG] Multipliers to handle: %s" % listitems) + self.logger.info("[DEBUG] Multipliers to handle: %s" % listitems) newlength = 1 for item in listitems: for key, value in item.items(): newlength = newlength * value - print("[DEBUG] Newlength of array: %d. Lists: %s" % (newlength, all_lists)) + self.logger.info("[DEBUG] Newlength of array: %d. Lists: %s" % (newlength, all_lists)) # Get the cartesian product of the arrays cartesian = await self.cartesian_product(all_lists) newlist = [] @@ -433,7 +436,7 @@ class AppBase: paramlist.append(baseitem) - #print("PARAMLIST: %s" % paramlist) + #self.logger.info("PARAMLIST: %s" % paramlist) #newlist[subitem[0]] #if len(newlist) > 0: @@ -441,22 +444,22 @@ class AppBase: # How do we get it back, ordered? #for item in cartesian: - #print("Listlengths: %s" % listlengths) + #self.logger.info("Listlengths: %s" % listlengths) #paramlist = [baseparams] - #print("[INFO] Return paramlist: %s" % paramlist) + #self.logger.info("[INFO] Return paramlist: %s" % paramlist) return paramlist # Runs recursed versions with inner loops and such async def run_recursed_items(self, func, baseparams, loop_wrapper): - #print(f"RECURSED ITEMS: {baseparams}") + #self.logger.info(f"RECURSED ITEMS: {baseparams}") has_loop = False newparams = {} for key, value in baseparams.items(): if isinstance(value, list) and len(value) > 0: - print(f"[DEBUG] In list check for {key}") + self.logger.info(f"[DEBUG] In list check for {key}") try: # Added skip for body (OpenAPI) which uses data= in requests @@ -464,36 +467,36 @@ class AppBase: if key != "body": value[0] = json.loads(value[0]) except json.decoder.JSONDecodeError as e: - print("[WARNING] JSON casting error (1): %s" % e) + self.logger.info("[WARNING] JSON casting error (1): %s" % e) except TypeError as e: - print("[WARNING] TypeError: %s" % e) + self.logger.info("[WARNING] TypeError: %s" % e) - print("[DEBUG] POST initial list check") + self.logger.info("[DEBUG] POST initial list check") try: if isinstance(value, list) and len(value) == 1 and isinstance(value[0], list): try: loop_wrapper[key] += 1 except Exception as e: - print("[WARNING] Exception in loop wrapper: {e}") + self.logger.info("[WARNING] Exception in loop wrapper: {e}") loop_wrapper[key] = 1 - print(f"[DEBUG] Key {key} is a list: {value}") + self.logger.info(f"[DEBUG] Key {key} is a list: {value}") newparams[key] = value[0] has_loop = True else: - #print(f"Key {key} is NOT a list within a list. Value: {value}") + #self.logger.info(f"Key {key} is NOT a list within a list. Value: {value}") newparams[key] = value except Exception as e: - print(f"[WARNING] Error in baseparams list: {e}") + self.logger.info(f"[WARNING] Error in baseparams list: {e}") newparams[key] = value results = [] if has_loop: - print(f"[DEBUG] Should run inner loop: {newparams}") + self.logger.info(f"[DEBUG] Should run inner loop: {newparams}") ret = await self.run_recursed_items(func, newparams, loop_wrapper) else: - print(f"[DEBUG] Should run multiplier check with params (inner): {newparams}") + self.logger.info(f"[DEBUG] Should run multiplier check with params (inner): {newparams}") # 1. Find the loops that are required and create new multipliers # If here: check for multipliers within this scope. ret = [] @@ -501,9 +504,9 @@ class AppBase: # FIXME: This does a deduplication of the data new_params = self.validate_unique_fields(param_multiplier) - #print(f"NEW PARAMS: {new_params}") + #self.logger.info(f"NEW PARAMS: {new_params}") if len(new_params) == 0: - print("[WARNING] SHOULD STOP MULTI-EXECUTION BECAUSE FIELDS AREN'T UNIQUE") + self.logger.info("[WARNING] SHOULD STOP MULTI-EXECUTION BECAUSE FIELDS AREN'T UNIQUE") self.action_result = { "action": self.action, "authorization": self.authorization, @@ -519,24 +522,24 @@ class AppBase: #return else: #subparams = new_params - #print(f"NEW PARAMS: {new_params}") + #self.logger.info(f"NEW PARAMS: {new_params}") param_multiplier = new_params - #print("Returned with newparams of length %d", len(new_params)) + #self.logger.info("Returned with newparams of length %d", len(new_params)) #if isinstance(new_params, list) and len(new_params) == 1: # params = new_params[0] #else: - # print("[WARNING] SHOULD STOP EXECUTION BECAUSE FIELDS AREN'T UNIQUE") + # self.logger.info("[WARNING] SHOULD STOP EXECUTION BECAUSE FIELDS AREN'T UNIQUE") # action_result["status"] = "SKIPPED" # action_result["result"] = f"A non-unique value was found" # action_result["completed_at"] = int(time.time()) # self.send_result(action_result, headers, stream_path) # return - print("[INFO] Multiplier length: %d" % len(param_multiplier)) + self.logger.info("[INFO] Multiplier length: %d" % len(param_multiplier)) #tmp = "" for subparams in param_multiplier: - #print(f"SUBPARAMS IN MULTI: {subparams}") + #self.logger.info(f"SUBPARAMS IN MULTI: {subparams}") try: #tmp = await func(**subparams) @@ -545,7 +548,7 @@ class AppBase: tmp = await func(**subparams) break except TypeError as e: - print("BASE TYPEERROR: %s" % e) + self.logger.info("BASE TYPEERROR: %s" % e) errorstring = "%s" % e if "got an unexpected keyword argument" in errorstring: fieldsplit = errorstring.split("'") @@ -554,7 +557,7 @@ class AppBase: try: del subparams[field] - print("Removed field invalid field %s" % field) + self.logger.info("Removed field invalid field %s" % field) except KeyError: break else: @@ -565,12 +568,12 @@ class AppBase: try: e = sys.exc_info()[1] except: - print("Exc check fail: %s" % e) + self.logger.info("Exc check fail: %s" % e) pass tmp = "An error occured during execution: %s" % e - #print("RET from execution: %s" % ret) + #self.logger.info("RET from execution: %s" % ret) new_value = tmp if tmp == None: new_value = "" @@ -589,7 +592,7 @@ class AppBase: pass except: pass - #print("Json: %s" % e) + #self.logger.info("Json: %s" % e) #ret.append(tmp) #if self.result_wrapper_count > 0: @@ -597,12 +600,12 @@ class AppBase: #else: ret.append(new_value) - print("[INFO] Ret length: %d" % len(ret)) + self.logger.info("[INFO] Ret length: %d" % len(ret)) if len(ret) == 1: #ret = ret[0] - print("[DEBUG] DONT make list of 1 into 0!!") + self.logger.info("[DEBUG] DONT make list of 1 into 0!!") - print("Return from execution: %s" % ret) + self.logger.info("Return from execution: %s" % ret) if ret == None: results.append("") json_object = False @@ -619,7 +622,7 @@ class AppBase: results.append(json.loads(ret)) json_object = True except json.decoder.JSONDecodeError as e: - #print("Json: %s" % e) + #self.logger.info("Json: %s" % e) results.append(ret) except TypeError as e: results.append(ret) @@ -628,9 +631,9 @@ class AppBase: if len(results) == 1: #results = results[0] - print("DONT MAKE LIST FROM 1 TO 0!!") + self.logger.info("DONT MAKE LIST FROM 1 TO 0!!") - print("\nLOOP: %s\nRESULTS: %s" % (loop_wrapper, results)) + self.logger.info("\nLOOP: %s\nRESULTS: %s" % (loop_wrapper, results)) return results # Downloads all files from a namespace @@ -658,10 +661,10 @@ class AppBase: full_execution = self.full_execution org_id = full_execution["workflow"]["execution_org"]["id"] - print("SHOULD GET FILES BASED ON ORG %s, workflow %s and value(s) %s" % (org_id, full_execution["workflow"]["id"], value)) + self.logger.info("SHOULD GET FILES BASED ON ORG %s, workflow %s and value(s) %s" % (org_id, full_execution["workflow"]["id"], value)) if isinstance(value, list): - print("IS LIST!") + self.logger.info("IS LIST!") #if len(value) == 1: # value = value[0] else: @@ -669,9 +672,9 @@ class AppBase: returns = [] for item in value: - print("VALUE: %s" % item) + self.logger.info("VALUE: %s" % item) if len(item) != 36: - print("Bad length for file value %s" % item) + self.logger.info("Bad length for file value %s" % item) continue #return { # "filename": "", @@ -686,7 +689,7 @@ class AppBase: } ret1 = requests.get("%s%s" % (self.url, get_path), headers=headers) - print("RET1 (file get): %s" % ret1.text) + self.logger.info("RET1 (file get): %s" % ret1.text) if ret1.status_code != 200: returns.append({ "filename": "", @@ -697,7 +700,7 @@ class AppBase: content_path = "/api/v1/files/%s/content?execution_id=%s" % (item, full_execution["execution_id"]) ret2 = requests.get("%s%s" % (self.url, content_path), headers=headers) - print("RET2 (file get) done") + self.logger.info("RET2 (file get) done") if ret2.status_code == 200: tmpdata = ret1.json() returndata = { @@ -707,7 +710,7 @@ class AppBase: } returns.append(returndata) - print("RET3 (file get done)") + self.logger.info("RET3 (file get done)") if len(returns) == 0: return { @@ -739,7 +742,7 @@ class AppBase: allvalues["value"] = str(value) return allvalues except: - print("Value couldn't be parsed") + self.logger.info("Value couldn't be parsed") #return response.json() return {"success": False} @@ -757,19 +760,19 @@ class AppBase: value = requests.post(url, json=data) try: allvalues = value.json() - print("VAL1: ", allvalues) + self.logger.info("VAL1: ", allvalues) allvalues["key"] = key - print("VAL2: ", allvalues) + self.logger.info("VAL2: ", allvalues) try: parsedvalue = json.loads(allvalues["value"]) allvalues["value"] = parsedvalue except: - print("Parsing of value as JSON failed. Continue anyway!") + self.logger.info("Parsing of value as JSON failed. Continue anyway!") return allvalues except: - print("Value couldn't be parsed, or json dump of value failed") + self.logger.info("Value couldn't be parsed, or json dump of value failed") #return value.json() return {"success": False} @@ -803,28 +806,28 @@ class AppBase: data["filename"] = curfile["filename"] filename = curfile["filename"] except KeyError as e: - print(f"KeyError in file setup: {e}") + self.logger.info(f"KeyError in file setup: {e}") pass ret = requests.post("%s%s" % (self.url, create_path), headers=headers, json=data) - print(f"Ret CREATE: {ret.text}") + self.logger.info(f"Ret CREATE: {ret.text}") cur_id = "" if ret.status_code == 200: - print("RET: %s" % ret.text) + self.logger.info("RET: %s" % ret.text) ret_json = ret.json() if not ret_json["success"]: - print("Not success in file upload creation.") + self.logger.info("Not success in file upload creation.") continue - print("Should handle ID %s" % ret_json["id"]) + self.logger.info("Should handle ID %s" % ret_json["id"]) file_ids.append(ret_json["id"]) cur_id = ret_json["id"] else: - print("Bad status code: %d" % ret.status_code) + self.logger.info("Bad status code: %d" % ret.status_code) continue if len(cur_id) == 0: - print("No file ID specified from backend") + self.logger.info("No file ID specified from backend") continue new_headers = { @@ -832,16 +835,16 @@ class AppBase: } upload_path = "/api/v1/files/%s/upload?execution_id=%s" % (cur_id, full_execution["execution_id"]) - print("Create path: %s" % create_path) + self.logger.info("Create path: %s" % create_path) files={"shuffle_file": (filename, curfile["data"])} #open(filename,'rb')} ret = requests.post("%s%s" % (self.url, upload_path), files=files, headers=new_headers) - print("Ret UPLOAD: %s" % ret.text) - print("Ret2 UPLOAD: %d" % ret.status_code) + self.logger.info("Ret UPLOAD: %s" % ret.text) + self.logger.info("Ret2 UPLOAD: %d" % ret.status_code) - print("IDS TO RETURN: %s" % file_ids) + self.logger.info("IDS TO RETURN: %s" % file_ids) return file_ids async def execute_action(self, action): @@ -875,19 +878,19 @@ class AppBase: } if len(self.action) == 0: - print("ACTION env not defined") + self.logger.info("ACTION env not defined") self.action_result["result"] = "Error in setup ENV: ACTION not defined" self.send_result(self.action_result, headers, stream_path) return if len(self.authorization) == 0: - print("AUTHORIZATION env not defined") + self.logger.info("AUTHORIZATION env not defined") self.action_result["result"] = "Error in setup ENV: AUTHORIZATION not defined" self.send_result(self.action_result, headers, stream_path) return if len(self.current_execution_id) == 0: - print("EXECUTIONID env not defined") + self.logger.info("EXECUTIONID env not defined") self.action_result["result"] = "Error in setup ENV: EXECUTIONID not defined" self.send_result(self.action_result, headers, stream_path) return @@ -904,7 +907,7 @@ class AppBase: # if ret.status_code != 200: # self.logger.info(ret.text) #except requests.exceptions.ConnectionError as e: - # print("Connectionerror: %s" % e) + # self.logger.info("Connectionerror: %s" % e) # action_result["result"] = "Bad setup during startup: %s" % e # self.send_result(action_result, headers, stream_path) @@ -914,7 +917,7 @@ class AppBase: # If found, we get the full results list from backend fullexecution = {} if len(self.full_execution) == 0: - print("[DEBUG] NO EXECUTION - LOADING!") + self.logger.info("[DEBUG] NO EXECUTION - LOADING!") try: tmpdata = { "authorization": self.authorization, @@ -949,12 +952,12 @@ class AppBase: try: fullexecution = json.loads(self.full_execution) except json.decoder.JSONDecodeError as e: - print("[WARNING] Json decode execution error: %s" % e) + self.logger.info("[WARNING] Json decode execution error: %s" % e) self.action_result["result"] = "Json error during startup: %s" % e self.send_result(self.action_result, headers, stream_path) return - print("") + self.logger.info("") self.full_execution = fullexecution @@ -1016,7 +1019,7 @@ class AppBase: try: return int(data) except ValueError: - print("[DEBUG] ValueError while casting %s to int" % data) + self.logger.info("[DEBUG] ValueError while casting %s to int" % data) return data if "lower" in thistype: @@ -1030,7 +1033,7 @@ class AppBase: if "split" in thistype: return data.split() if "replace" in thistype: - print("Running replace!") + self.logger.info("Running replace!") splitvalues = data.split(",") if len(splitvalues) > 2: @@ -1058,47 +1061,47 @@ class AppBase: else: return f"replace({data})" if "join" in thistype: - print(f"SHOULD JOIN: {data}") + self.logger.info(f"SHOULD JOIN: {data}") try: splitvalues = data.split(",") if "," not in data: return f"join({data})" if len(splitvalues) >= 2: - print(f"SPLITVALUE: {splitvalues[-1]}") + self.logger.info(f"SPLITVALUE: {splitvalues[-1]}") # 1. Take the list and parse it from string # 2. Take all the items and join them # 3. Parse them back as string and return values = ",".join(splitvalues[0:-1]) - print(f"VALUES: {values}") + self.logger.info(f"VALUES: {values}") tmp = json.loads(values) - print(f"TMP: {tmp}") + self.logger.info(f"TMP: {tmp}") #tmp = tmp[1:-1] - #print(f"TMP2: {tmp}") + #self.logger.info(f"TMP2: {tmp}") try: newvalues = splitvalues[-1].join(str(item).strip() for item in tmp) except TypeError: newvalues = splitvalues[-1].join(json.dumps(item).strip() for item in tmp) - print(f"new: {newvalues}") + self.logger.info(f"new: {newvalues}") return newvalues else: - print("Returning default") + self.logger.info("Returning default") return f"join({data})" except (KeyError, IndexError) as e: - print(f"ERROR in join(): {e}") + self.logger.info(f"ERROR in join(): {e}") except json.decoder.JSONDecodeError as e: - print(f"JSON ERROR in join(): {e}") + self.logger.info(f"JSON ERROR in join(): {e}") if "len" in thistype or "length" in thistype or "lenght" in thistype: - print(f"Trying to length-parse: {data}") + self.logger.info(f"Trying to length-parse: {data}") try: tmp_len = json.loads(data, parse_float=str, parse_int=str, parse_constant=str) except (NameError, KeyError, TypeError, json.decoder.JSONDecodeError) as e: try: - print(f"[WARNING] INITIAL Parsing bug for length in app sdk: {e}") + self.logger.info(f"[WARNING] INITIAL Parsing bug for length in app sdk: {e}") # data = data.replace("\'", "\"") data = data.replace("True", "true") data = data.replace("False", "false") @@ -1139,7 +1142,7 @@ class AppBase: else: tmp = json.loads(parsedlist)[lastsplit[0]] - #print(tmp) + #self.logger.info(tmp) return tmp except IndexError as e: return default_error @@ -1162,8 +1165,8 @@ class AppBase: inner_value = parse_nested_param(data, maxDepth(data) - 0) outer_value = parse_nested_param(data, maxDepth(data) - 1) - #print("[DEBUG] INNER: ", inner_value) - #print("[DEBUG] OUTER: ", outer_value) + #self.logger.info("[DEBUG] INNER: ", inner_value) + #self.logger.info("[DEBUG] OUTER: ", outer_value) wrapper_group = "|".join(wrappers) parse_string = data @@ -1194,7 +1197,7 @@ class AppBase: c_parentheses = parse_nested_param(parse_string, 0)[0] match_string = re.escape(c_parentheses) custom_casting = re.findall(fr"({wrapper_group})\({match_string}", parse_string) - print("In ELSE: %s" % custom_casting) + self.logger.info("In ELSE: %s" % custom_casting) # check if a wrapper was found if len(custom_casting) != 0: inner_result = parse_type(c_parentheses, custom_casting[0]) @@ -1206,7 +1209,7 @@ class AppBase: else: parse_string = inner_result - print("PARSE STRING: %s" % parse_string) + self.logger.info("PARSE STRING: %s" % parse_string) return parse_string, True # Looks for parantheses to grab special cases within a string, e.g: @@ -1228,7 +1231,7 @@ class AppBase: if isinstance(data, str) and len(data) > 4: if (data[0] == "{" or data[0] == "[") and (data[len(data)-1] == "]" or data[len(data)-1] == "}"): - print("Skipping parser because use of {[ and ]}") + self.logger.info("Skipping parser because use of {[ and ]}") return data newdata = [] @@ -1284,12 +1287,12 @@ class AppBase: return data if len(parsedlist) > 0 and not non_string: - #print("Returning parsed list: ", parsedlist) + #self.logger.info("Returning parsed list: ", parsedlist) return " ".join(parsedlist) elif len(parsedlist) == 1 and non_string: return parsedlist[0] else: - #print("Casting back to string because multi: ", parsedlist) + #self.logger.info("Casting back to string because multi: ", parsedlist) newlist = [] for item in parsedlist: try: @@ -1305,7 +1308,7 @@ class AppBase: # $nodename.data.#min-max.info.id def recurse_json(basejson, parsersplit): match = "#([0-9a-z]+):?-?([0-9a-z]+)?#?" - #print("Split: %s\n%s" % (parsersplit, basejson)) + #self.logger.info("Split: %s\n%s" % (parsersplit, basejson)) try: outercnt = 0 @@ -1314,9 +1317,9 @@ class AppBase: #if " " in value: # value = value.replace(" ", "_", -1) - #print("VALUE: %s\n" % value) + #self.logger.info("VALUE: %s\n" % value) actualitem = re.findall(match, value, re.MULTILINE) - #print("ACTUAL RECURSE: (%s) %s" % (value, actualitem)) + #self.logger.info("ACTUAL RECURSE: (%s) %s" % (value, actualitem)) if value == "#": newvalue = [] for innervalue in basejson: @@ -1336,17 +1339,17 @@ class AppBase: return newvalue, True elif len(actualitem) > 0: - #print("[INFO] In recursion v2: ", actualitem) + #self.logger.info("[INFO] In recursion v2: ", actualitem) is_loop = True newvalue = [] firstitem = actualitem[0][0] seconditem = actualitem[0][1] - print("[DEBUG] ACTUAL PARSED: %s" % actualitem) + self.logger.info("[DEBUG] ACTUAL PARSED: %s" % actualitem) # Means it's a single item -> continue if seconditem == "": - print("[INFO] In first - handling %s. Len: %d" % (firstitem, len(basejson))) + self.logger.info("[INFO] In first - handling %s. Len: %d" % (firstitem, len(basejson))) if firstitem.lower() == "max" or firstitem.lower() == "last": firstitem = len(basejson)-1 elif firstitem.lower() == "min" or firstitem.lower() == "first": @@ -1354,14 +1357,14 @@ class AppBase: else: firstitem = int(firstitem) - print(f"[DEBUG] Post lower checks with item {firstitem}") + self.logger.info(f"[DEBUG] Post lower checks with item {firstitem}") tmpitem = basejson[int(firstitem)] try: newvalue, is_loop = recurse_json(tmpitem, parsersplit[outercnt+1:]) except IndexError: newvalue, is_loop = (tmpitem, parsersplit[outercnt+1:]) else: - print("[INFO] In ELSE - handling %s and %s" % (firstitem, seconditem)) + self.logger.info("[INFO] In ELSE - handling %s and %s" % (firstitem, seconditem)) if firstitem.lower() == "max" or firstitem.lower() == "last": firstitem = len(basejson)-1 elif firstitem.lower() == "min" or firstitem.lower() == "first": @@ -1376,7 +1379,7 @@ class AppBase: else: seconditem = int(seconditem) - print(f"[DEBUG] Post lower checks 2: {firstitem} AND {seconditem}") + self.logger.info(f"[DEBUG] Post lower checks 2: {firstitem} AND {seconditem}") newvalue = [] if int(seconditem) > len(basejson): seconditem = len(basejson) @@ -1384,68 +1387,68 @@ class AppBase: for i in range(int(firstitem), int(seconditem)+1): # 1. Check the next item (message) # 2. Call this function again - #print("Base: %s" % basejson[i]) + #self.logger.info("Base: %s" % basejson[i]) try: ret, tmp_loop = recurse_json(basejson[i], parsersplit[outercnt+1:]) except IndexError: - print("[DEBUG] INDEXERROR: ", parsersplit[outercnt]) + self.logger.info("[DEBUG] INDEXERROR: ", parsersplit[outercnt]) #ret = innervalue ret, tmp_loop = recurse_json(innervalue, parsersplit[outercnt:]) - #print("IN LIST: %s" % ret) + #self.logger.info("IN LIST: %s" % ret) #exit() newvalue.append(ret) - #print("Returning %s" % newvalue) + #self.logger.info("Returning %s" % newvalue) return newvalue, is_loop else: - #print("BEFORE NORMAL VALUE: ", basejson, value) + #self.logger.info("BEFORE NORMAL VALUE: ", basejson, value) if len(value) == 0: return basejson, False try: if isinstance(basejson, list): - print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (list): %s" % value) + self.logger.info("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (list): %s" % value) return basejson, False elif isinstance(basejson[value], str): - #print(f"[INFO] LOADING STRING '%s' AS JSON" % basejson[value]) + #self.logger.info(f"[INFO] LOADING STRING '%s' AS JSON" % basejson[value]) try: basejson = json.loads(basejson[value]) - #print("[DEBUG] BASEJSON: %s" % basejson) + #self.logger.info("[DEBUG] BASEJSON: %s" % basejson) except json.decoder.JSONDecodeError as e: - #print("[DEBUG] RETURNING BECAUSE '%s' IS A NORMAL STRING (0)" % basejson[value]) + #self.logger.info("[DEBUG] RETURNING BECAUSE '%s' IS A NORMAL STRING (0)" % basejson[value]) return basejson[value], False else: basejson = basejson[value] except KeyError as e: - print("[WARNING] Running secondary value check with replacement of underscore in %s: %s" % (value, e)) + self.logger.info("[WARNING] Running secondary value check with replacement of underscore in %s: %s" % (value, e)) if "_" in value: value = value.replace("_", " ", -1) elif " " in value: value = value.replace(" ", "_", -1) if isinstance(basejson, list): - print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (list): %s" % value) + self.logger.info("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (list): %s" % value) return basejson, False elif isinstance(basejson[value], str): - print(f"[INFO] LOADING STRING '%s' AS JSON" % basejson[value]) + self.logger.info(f"[INFO] LOADING STRING '%s' AS JSON" % basejson[value]) try: basejson = json.loads(basejson[value]) - print("[DEBUG] BASEJSON: %s" % basejson) + self.logger.info("[DEBUG] BASEJSON: %s" % basejson) except json.decoder.JSONDecodeError as e: - print("[DEBUG] RETURNING BECAUSE '%s' IS A NORMAL STRING (1)" % basejson[value]) + self.logger.info("[DEBUG] RETURNING BECAUSE '%s' IS A NORMAL STRING (1)" % basejson[value]) return basejson[value], False else: basejson = basejson[value] - #print("Parsed BASEJSON: %s" % basejson) + #self.logger.info("Parsed BASEJSON: %s" % basejson) outercnt += 1 except KeyError as e: - print("[INFO] Lower keyerror: %s" % e) + self.logger.info("[INFO] Lower keyerror: %s" % e) return "", False #return basejson @@ -1460,13 +1463,13 @@ class AppBase: actionname_lower = parsersplit[0][1:].lower() #Actionname: Start_node - print(f"\n[INFO] Actionname: {actionname_lower}") + self.logger.info(f"\n[INFO] Actionname: {actionname_lower}") # 1. Find the action baseresult = "" appendresult = "" - print("[INFO] Parsersplit length: %d" % len(parsersplit)) + self.logger.info("[INFO] Parsersplit length: %d" % len(parsersplit)) if (actionname_lower.startswith("exec ") or actionname_lower.startswith("webhook ") or actionname_lower.startswith("schedule ") or actionname_lower.startswith("userinput ") or actionname_lower.startswith("email_trigger ") or actionname_lower.startswith("trigger ")) and len(parsersplit) == 1: record = False for char in actionname_lower: @@ -1486,21 +1489,21 @@ class AppBase: if actionname_lower == "exec" or actionname_lower == "webhook" or actionname_lower == "schedule" or actionname_lower == "userinput" or actionname_lower == "email_trigger" or actionname_lower == "trigger": baseresult = execution_data["execution_argument"] elif actionname_lower == "shuffle_cache": - print("SHOULD GET CACHE KEY: %s" % parsersplit) + self.logger.info("SHOULD GET CACHE KEY: %s" % parsersplit) if len(parsersplit) > 1: actual_key = parsersplit[1] - print("KEY: %s" % actual_key) + self.logger.info("KEY: %s" % actual_key) cachedata = self.get_cache(actual_key) - print("CACHE: %s" % cachedata) + self.logger.info("CACHE: %s" % cachedata) parsersplit.pop(1) try: baseresult = json.dumps(cachedata) except json.decoder.JSONDecodeError as e: - print("Failed json dumping: %s" % e) + self.logger.info("Failed json dumping: %s" % e) #returndata = str(baseresult)+str(appendresult) else: - #print("Within execution data check. Execution data: %s", execution_data["results"]) + #self.logger.info("Within execution data check. Execution data: %s", execution_data["results"]) if execution_data["results"] != None: for result in execution_data["results"]: resultlabel = result["action"]["label"].replace(" ", "_", -1).lower() @@ -1508,13 +1511,13 @@ class AppBase: baseresult = result["result"] break else: - print("No results to get values from.") + self.logger.info("No results to get values from.") baseresult = "$" + parsersplit[0][1:] - print("BEFORE VARIABLES!") + self.logger.info("BEFORE VARIABLES!") if len(baseresult) == 0: try: - #print("WF Variables: %s" % execution_data["workflow"]["workflow_variables"]) + #self.logger.info("WF Variables: %s" % execution_data["workflow"]["workflow_variables"]) for variable in execution_data["workflow"]["workflow_variables"]: variablename = variable["name"].replace(" ", "_", -1).lower() @@ -1523,47 +1526,47 @@ class AppBase: break except KeyError as e: - print("[INFO] KeyError wf variables: %s" % e) + self.logger.info("[INFO] KeyError wf variables: %s" % e) pass except TypeError as e: - print("[INFO] TypeError wf variables: %s" % e) + self.logger.info("[INFO] TypeError wf variables: %s" % e) pass - print("BEFORE EXECUTION VAR") + self.logger.info("BEFORE EXECUTION VAR") if len(baseresult) == 0: try: - #print("Execution Variables: %s" % execution_data["execution_variables"]) + #self.logger.info("Execution Variables: %s" % execution_data["execution_variables"]) for variable in execution_data["execution_variables"]: variablename = variable["name"].replace(" ", "_", -1).lower() if variablename.lower() == actionname_lower: baseresult = variable["value"] break except KeyError as e: - print("[INFO] KeyError exec variables: %s" % e) + self.logger.info("[INFO] KeyError exec variables: %s" % e) pass except TypeError as e: - print("[INFO] TypeError exec variables: %s" % e) + self.logger.info("[INFO] TypeError exec variables: %s" % e) pass except KeyError as error: - print(f"[DEBUG] KeyError in JSON: {error}") + self.logger.info(f"[DEBUG] KeyError in JSON: {error}") - print(f"[INFO] After first trycatch. Baseresult")#, baseresult) + self.logger.info(f"[INFO] After first trycatch. Baseresult")#, baseresult) # 2. Find the JSON data if len(baseresult) == 0: return ""+appendresult, False - print("[INFO] After second return") + self.logger.info("[INFO] After second return") if len(parsersplit) == 1: returndata = str(baseresult)+str(appendresult) - print("RETURNING!")#: %s" % returndata) + self.logger.info("RETURNING!")#: %s" % returndata) return returndata, False baseresult = baseresult.replace(" True,", " true,") baseresult = baseresult.replace(" False", " false,") - print("[INFO] After third parser return - Formatted")#, baseresult) + self.logger.info("[INFO] After third parser return - Formatted")#, baseresult) basejson = {} try: basejson = json.loads(baseresult) @@ -1572,10 +1575,10 @@ class AppBase: baseresult = baseresult.replace("\'", "\"") basejson = json.loads(baseresult) except json.decoder.JSONDecodeError as e: - print("Parser issue with JSON: %s" % e) + self.logger.info("Parser issue with JSON: %s" % e) return str(baseresult)+str(appendresult), False - print("[INFO] After fourth parser return as JSON") + self.logger.info("[INFO] After fourth parser return as JSON") data, is_loop = recurse_json(basejson, parsersplit[1:]) parseditem = data @@ -1583,52 +1586,52 @@ class AppBase: try: parseditem = json.dumps(parseditem) except json.decoder.JSONDecodeError as e: - print("Parseditem issue: %s" % e) + self.logger.info("Parseditem issue: %s" % e) pass - print("DATA: (%s) %s" % (type(data), data)) + self.logger.info("DATA: (%s) %s" % (type(data), data)) if is_loop: - print("DATA IS A LOOP - SHOULD WRAP") + self.logger.info("DATA IS A LOOP - SHOULD WRAP") if parsersplit[-1] == "#": - print("SET DATA WRAPPER TO NORMAL!") + self.logger.info("SET DATA WRAPPER TO NORMAL!") parseditem = "${SHUFFLE_NO_SPLITTER%s}$" % json.dumps(data) else: # Return value: ${id[12345, 45678]}$ - print("SET DATA WRAPPER TO %s!" % parsersplit[-1]) + self.logger.info("SET DATA WRAPPER TO %s!" % parsersplit[-1]) parseditem = "${%s%s}$" % (parsersplit[-1], json.dumps(data)) - print("Before last return with %s" % appendresult) + self.logger.info("Before last return with %s" % appendresult) returndata = str(parseditem)+str(appendresult) # New in 0.8.97: Don't return items without lists - print("RETURNDATA: %s" % returndata) + self.logger.info("RETURNDATA: %s" % returndata) #return returndata, is_loop try: return json.dumps(json.loads(returndata)), is_loop except json.decoder.JSONDecodeError as e: - print("Error in decoder: %s" % e) + self.logger.info("Error in decoder: %s" % e) return returndata, is_loop # Sending self as it's not a normal function def parse_liquid(template, self): - #print("Inside liquid with glob: %s" % globals()) + #self.logger.info("Inside liquid with glob: %s" % globals()) try: if len(template) > 5000000: - print("[DEBUG] Skipping liquid - size too big (%d)" % len(template)) + self.logger.info("[DEBUG] Skipping liquid - size too big (%d)" % len(template)) return template #if not "{{" in template or not "}}" in template: # if not "{%" in template or not "%}" in template: - # print("Skipping liquid - missing {{ }} and {% %}") + # self.logger.info("Skipping liquid - missing {{ }} and {% %}") # return template #if not "{{" in template or not "}}" in template: # return template - #print(globals()) - print("[DEBUG] Running liquid with data of length %d" % len(template)) + #self.logger.info(globals()) + self.logger.info("[DEBUG] Running liquid with data of length %d" % len(template)) run = Liquid(template, mode="wild", from_file=False) # Can't handle self yet (?) @@ -1642,13 +1645,13 @@ class AppBase: # ret = run.render(**globals()) # return ret #except liquid.exceptions.LiquidRenderError as e: - # print("Render error: %s" % e) + # self.logger.info("Render error: %s" % e) except jinja2.exceptions.TemplateNotFound as e: - print("[ERROR] Template error: %s" % e) + self.logger.info("[ERROR] Template error: %s" % e) except jinja2.exceptions.TemplateSyntaxError as e: - print("[ERROR] Syntax error: %s" % e) + self.logger.info("[ERROR] Syntax error: %s" % e) except: - print("[ERROR] General exception for liquid") + self.logger.info("[ERROR] General exception for liquid") return template @@ -1656,6 +1659,7 @@ class AppBase: # Should have a regex which looks for the value, then goes out and cleans up the key def recurse_cleanup_script(data): deletekeys = [] + newvalue = data try: if not isinstance(data, dict): newvalue = json.loads(data) @@ -1663,7 +1667,7 @@ class AppBase: newvalue = data for key, value in newvalue.items(): - #print("%s: %s" % (key, value)) + #self.logger.info("%s: %s" % (key, value)) if isinstance(value, str) and len(value) == 0: deletekeys.append(key) continue @@ -1671,17 +1675,17 @@ class AppBase: if isinstance(value, list): try: value = json.dumps(value) - print(value) + self.logger.info(value) except: - print("Json parsing issue in recursed value") + self.logger.info("[WARNING] Json parsing issue in recursed value") pass if value == "${%s}" % key: - print("Deleting %s because key = value" % key) + self.logger.info("[WARNING] Deleting %s because key = value" % key) deletekeys.append(key) continue elif "${" in value and "}" in value: - print("Deleting %s because it contains ${ and }" % key) + self.logger.info("[WARNING] Deleting %s because it contains ${ and }" % key) deletekeys.append(key) continue @@ -1689,16 +1693,22 @@ class AppBase: newvalue[key] = recurse_cleanup_script(value) except json.decoder.JSONDecodeError as e: - print("Failed JSON replacement for OpenAPI keys (3) {e}") + self.logger.info(f"[WARNING] Failed JSON replacement for OpenAPI keys (3) {e}") + except Exception as e: + self.logger.info(f"[WARNING] Failed as an exception (1): {e}") - for deletekey in deletekeys: - try: - del newvalue[deletekey] - except: - pass + try: + for deletekey in deletekeys: + try: + del newvalue[deletekey] + except: + pass + except Exception as e: + self.logger.info(f"[WARNING] Failed in deletekeys: {e}") + return data try: - #print("Post delete: %s" % newvalue) + #self.logger.info("Post delete: %s" % newvalue) for key, value in newvalue.items(): if isinstance(value, bool): continue @@ -1709,26 +1719,28 @@ class AppBase: value = json.loads(value) newvalue[key] = value except json.decoder.JSONDecodeError as e: - #print("Inner overwrite issue for \"%s\": %s" % (key, e)) + #self.logger.info("Inner overwrite issue for \"%s\": %s" % (key, e)) continue except Exception as e: - #print("General error in newvalue items loop: %s" % e) + #self.logger.info("General error in newvalue items loop: %s" % e) continue try: data = json.dumps(newvalue) except json.decoder.JSONDecodeError as e: - print("[WARNING] JsonDecodeError: %s" % e) + self.logger.info("[WARNING] JsonDecodeError: %s" % e) data = newvalue except json.decoder.JSONDecodeError as e: - print("Failed JSON replacement for OpenAPI keys (2) {e}") + self.logger.info("[WARNING] Failed JSON replacement for OpenAPI keys (2) {e}") + except Exception as e: + self.logger.info(f"[WARNING] Failed as an exception (2): {e}") #if isinstance(data, str): # tmpdata = json.dumps(data) - # print(tmpdata) + # self.logger.info(tmpdata) # foundvalue = re.findall(".*?(${\w+})", tmpdata, re.MULTILINE) - # print("FOUND: %s", foundvalue) + # self.logger.info("FOUND: %s", foundvalue) return data @@ -1750,13 +1762,13 @@ class AppBase: escape_replacement = "\\%\\%\\%\\%\\%" end_variable = "^_^" - #print("Input value: %s" % parameter["value"]) + #self.logger.info("Input value: %s" % parameter["value"]) try: parameter["value"] = parameter["value"].replace(escaped_dollar, escape_replacement, -1) except: - print("Error in initial replacement of escaped dollar!") + self.logger.info("Error in initial replacement of escaped dollar!") - #print("POST input value: %s" % parameter["value"]) + #self.logger.info("POST input value: %s" % parameter["value"]) # Regex to find all the things if parameter["variant"] == "STATIC_VALUE": @@ -1765,7 +1777,7 @@ class AppBase: #self.logger.debug(f"\n\nHandle static data with JSON: {data}\n\n") #self.logger.info("STATIC PARSED: %s" % actualitem) if len(actualitem) > 0: - print("ACTUAL: ", actualitem) + self.logger.info("ACTUAL: ", actualitem) for replace in actualitem: try: to_be_replaced = replace[0] @@ -1777,8 +1789,8 @@ class AppBase: # Trying without string dumping. value, is_loop = get_json_value(fullexecution, to_be_replaced) - #print("\n\nType of value: %s. Value: %s" % (type(value), value)) - print("\n\nType of value: %s" % type(value)) + #self.logger.info("\n\nType of value: %s. Value: %s" % (type(value), value)) + self.logger.info("\n\nType of value: %s" % type(value)) if isinstance(value, str): parameter["value"] = parameter["value"].replace(to_be_replaced, value) elif isinstance(value, dict) or isinstance(value, list): @@ -1790,18 +1802,18 @@ class AppBase: # parameter["value"] = parameter["value"].replace(to_be_replaced, str(value)) #except: # parameter["value"] = parameter["value"].replace(to_be_replaced, json.dumps(value)) - # print("Failed parsing value as string?") + # self.logger.info("Failed parsing value as string?") else: - print("Unknown type %s" % type(value)) + self.logger.info("Unknown type %s" % type(value)) try: parameter["value"] = parameter["value"].replace(to_be_replaced, json.dumps(value)) except json.decoder.JSONDecodeError as e: parameter["value"] = parameter["value"].replace(to_be_replaced, value) - #print("VALUE: %s" % parameter["value"]) + #self.logger.info("VALUE: %s" % parameter["value"]) if parameter["variant"] == "WORKFLOW_VARIABLE": - print("Handling workflow variable") + self.logger.info("Handling workflow variable") found = False try: for item in fullexecution["workflow"]["workflow_variables"]: @@ -1810,10 +1822,10 @@ class AppBase: parameter["value"] = item["value"] break except KeyError as e: - print("KeyError WF variable 1: %s" % e) + self.logger.info("KeyError WF variable 1: %s" % e) pass except TypeError as e: - print("TypeError WF variables 1: %s" % e) + self.logger.info("TypeError WF variables 1: %s" % e) pass if not found: @@ -1823,10 +1835,10 @@ class AppBase: parameter["value"] = item["value"] break except KeyError as e: - print("KeyError WF variable 2: %s" % e) + self.logger.info("KeyError WF variable 2: %s" % e) pass except TypeError as e: - print("TypeError WF variables 2: %s" % e) + self.logger.info("TypeError WF variables 2: %s" % e) pass elif parameter["variant"] == "ACTION_RESULT": @@ -1859,44 +1871,44 @@ class AppBase: try: to_be_replaced = replace[0] except IndexError: - print("Nothing to replace?: " % e) + self.logger.info("Nothing to replace?: " % e) continue # This will never be a loop aka multi argument parameter["value"] = to_be_replaced value, is_loop = get_json_value(fullexecution, to_be_replaced) - print("Loop: %s" % is_loop) + self.logger.info("Loop: %s" % is_loop) if isinstance(value, str): parameter["value"] = parameter["value"].replace(to_be_replaced, value) elif isinstance(value, dict): parameter["value"] = parameter["value"].replace(to_be_replaced, json.dumps(value)) else: - print("Unknown type %s" % type(value)) + self.logger.info("Unknown type %s" % type(value)) try: parameter["value"] = parameter["value"].replace(to_be_replaced, json.dumps(value)) except json.decoder.JSONDecodeError as e: parameter["value"] = parameter["value"].replace(to_be_replaced, value) - #print("PRE Replaced data: %s" % parameter["value"]) + #self.logger.info("PRE Replaced data: %s" % parameter["value"]) try: parameter["value"] = parameter["value"].replace(end_variable, "", -1) parameter["value"] = parameter["value"].replace(escape_replacement, "$", -1) except: - print("Error in datareplacement") + self.logger.info("Error in datareplacement") # Just here in case it breaks # Implemented 02.08.2021 - #print("Pre liquid: %s" % parameter["value"]) + #self.logger.info("Pre liquid: %s" % parameter["value"]) try: parameter["value"] = parse_liquid(parameter["value"], self) except: pass - #print("Replaced data: %s" % parameter["value"]) + #self.logger.info("Replaced data: %s" % parameter["value"]) - #print("POST liquid: %s" % parameter["value"]) + #self.logger.info("POST liquid: %s" % parameter["value"]) return "", parameter["value"], is_loop def run_validation(sourcevalue, check, destinationvalue): @@ -1952,7 +1964,7 @@ class AppBase: continue if item.strip() in sourcevalue: - print("[INFO] Found %s in %s" % (item, sourcevalue)) + self.logger.info("[INFO] Found %s in %s" % (item, sourcevalue)) return True return False @@ -1978,7 +1990,7 @@ class AppBase: try: found = re.search(destinationvalue, sourcevalue) except re.error as e: - print("[WARNING] Regex error in condition: %s" % e) + self.logger.info("[WARNING] Regex error in condition: %s" % e) return False if found == None: @@ -2055,7 +2067,7 @@ class AppBase: self.logger.warning("Skipping %s %s %s because %s is invalid." % (sourcevalue, condition["condition"]["value"], destinationvalue, condition["condition"]["value"])) continue - #print(destinationvalue) + #self.logger.info(destinationvalue) # NEGATE # Configuration = negated because of WorkflowAppActionParam.. @@ -2082,12 +2094,12 @@ class AppBase: # Checks whether conditions are met, otherwise set branchcheck, tmpresult = check_branch_conditions(action, fullexecution, self) if isinstance(tmpresult, object) or isinstance(tmpresult, list): - print("[DEBUG] Fixing branch return as object -> string") + self.logger.info("[DEBUG] Fixing branch return as object -> string") try: #tmpresult = tmpresult.replace("'", "\"") tmpresult = json.dumps(tmpresult) except json.decoder.JSONDecodeError as e: - print(f"[WARNING] Failed condition parsing {tmpresult} to string") + self.logger.info(f"[WARNING] Failed condition parsing {tmpresult} to string") if not branchcheck: self.logger.info("Failed one or more branch conditions.") @@ -2101,7 +2113,7 @@ class AppBase: except requests.exceptions.ConnectionError as e: self.logger.exception(e) - print("\n\n[DEBUG] RETURNING BECAUSE A BRANCH FAILED: %s\n\n" % tmpresult) + self.logger.info("\n\n[DEBUG] RETURNING BECAUSE A BRANCH FAILED: %s\n\n" % tmpresult) return # Replace name cus there might be issues @@ -2129,16 +2141,16 @@ class AppBase: # Potentially parse JSON here # FIXME - add potential authentication as first parameter(s) here # params[parameter["name"]] = parameter["value"] - #print(fullexecution["authentication"] + #self.logger.info(fullexecution["authentication"] # What variables are necessary here tho hmm params = {} try: for item in action["authentication"]: - #print("AUTH: ", key, value) + #self.logger.info("AUTH: ", key, value) params[item["key"]] = item["value"] except KeyError: - print("[DEBUG] No authentication specified!") + self.logger.info("[DEBUG] No authentication specified!") pass #action["authentication"] @@ -2152,16 +2164,16 @@ class AppBase: # Hack for key:value in options using || try: if parameter["options"] != None and len(parameter["options"]) > 0: - #print(f'OPTIONS: {parameter["options"]}') - #print(f'OPTIONS VAL: {parameter}') + #self.logger.info(f'OPTIONS: {parameter["options"]}') + #self.logger.info(f'OPTIONS VAL: {parameter}') if "||" in parameter["value"]: splitvalue = parameter["value"].split("||") if len(splitvalue) > 1: - #print(f'[INFO] Parsed split || options of actions["parameters"]["name"]') + #self.logger.info(f'[INFO] Parsed split || options of actions["parameters"]["name"]') action["parameters"][counter]["value"] = splitvalue[1] except (IndexError, KeyError, TypeError) as e: - print("[WARNING] Options err: {e}") + self.logger.info("[WARNING] Options err: {e}") # This part is purely for OpenAPI accessibility. # It replaces the data back into the main item @@ -2169,7 +2181,7 @@ class AppBase: # but this has changed to do lists within items and such if parameter["name"] == "body": bodyindex = counter - #print("PARAM: %s" % parameter) + #self.logger.info("PARAM: %s" % parameter) try: values = parameter["value_replace"] if values != None: @@ -2178,36 +2190,36 @@ class AppBase: replace_value = val["value"] replace_key = val["key"] if (val["value"].startswith("{") and val["value"].endswith("}")) or (val["value"].startswith("[") and val["value"].endswith("]")): - print(f"""Trying to parse as JSON: {val["value"]}""") + self.logger.info(f"""Trying to parse as JSON: {val["value"]}""") try: value_replace = json.loads(val["value"]) # If it gets here, remove the "" infront and behind the key as well since this is preventing the JSON from being loaded replace_key = f"\"{replace_key}\"" except json.decoder.JSONDecodeError as e: - print("Failed JSON replacement for OpenAPI %s", val["key"]) + self.logger.info("Failed JSON replacement for OpenAPI %s", val["key"]) elif val["value"].lower() == "true" or val["value"].lower() == "false": replace_key = f"\"{replace_key}\"" action["parameters"][counter]["value"] = action["parameters"][counter]["value"].replace(replace_key, replace_value, 1) - print(f'[INFO] Added param {val["key"]} for body (using OpenAPI)') + self.logger.info(f'[INFO] Added param {val["key"]} for body (using OpenAPI)') added += 1 #action["parameters"]["body"] - print("ADDED %d parameters for body" % added) + self.logger.info("ADDED %d parameters for body" % added) except KeyError as e: - print("KeyError body OpenAPI: %s" % e) + self.logger.info("KeyError body OpenAPI: %s" % e) pass - print(f"""HANDLING {action["parameters"][counter]["value"]}""") + self.logger.info(f"""HANDLING {action["parameters"][counter]["value"]}""") action["parameters"][counter]["value"] = recurse_cleanup_script(action["parameters"][counter]["value"]) - #print(action["parameters"]) + #self.logger.info(action["parameters"]) # This seems redundant now - print("Pre parameters") + self.logger.info("Pre parameters") for parameter in newparams: action["parameters"].append(parameter) @@ -2217,7 +2229,7 @@ class AppBase: try: self.original_action = json.loads(json.dumps(action)) except Exception as e: - print(f"[ERROR] Failed parsing action as JSON to original action. This COULD have bad effects on LOOPED executions: {e}") + self.logger.info(f"[ERROR] Failed parsing action as JSON to original action. This COULD have bad effects on LOOPED executions: {e}") # calltimes is used to handle forloops in the app itself. # 2 kinds of loop - one in gui with one app each, and one like this, @@ -2229,7 +2241,7 @@ class AppBase: # Multi_parameter has the data for each. variable minlength = 0 - print("Pre-loading parameters") + self.logger.info("Pre-loading parameters") multi_parameters = json.loads(json.dumps(params)) multiexecution = False multi_execution_lists = [] @@ -2241,34 +2253,34 @@ class AppBase: # Custom format for ${name[0,1,2,...]}$ #submatch = "([${]{2}([0-9a-zA-Z_-]+)(\[.*\])[}$]{2})" - #print(f"Returnedvalue: {value}") + #self.logger.info(f"Returnedvalue: {value}") # OLD: Used until 13.03.2021: submatch = "([${]{2}#?([0-9a-zA-Z_-]+)#?(\[.*\])[}$]{2})" # \${[0-9a-zA-Z_-]+#?(\[.*?]}\$) submatch = "([${]{2}#?([0-9a-zA-Z_-]+)#?(\[.*?]}\$))" actualitem = re.findall(submatch, value, re.MULTILINE) try: if action["skip_multicheck"]: - print("Skipping multicheck") + self.logger.info("Skipping multicheck") actualitem = [] except KeyError: pass - #print("Return value: %s" % value) + #self.logger.info("Return value: %s" % value) actionname = action["name"] - #print("Multicheck ", actualitem) - #print("ITEM LENGTH: %d, Actual item: %s" % (len(actualitem), actualitem)) + #self.logger.info("Multicheck ", actualitem) + #self.logger.info("ITEM LENGTH: %d, Actual item: %s" % (len(actualitem), actualitem)) if len(actualitem) > 0: multiexecution = True # Loop WITHOUT JSON variables go here. # Loop WITH variables go in else. - print("Before first part in multiexec!") + self.logger.info("Before first part in multiexec!") handled = False # Has a loop without a variable used inside if len(actualitem[0]) > 2 and actualitem[0][1] == "SHUFFLE_NO_SPLITTER": - print("(1) Pre replacement: %s" % actualitem[0][2]) + self.logger.info("(1) Pre replacement: %s" % actualitem[0][2]) tmpitem = value index = 0 @@ -2279,10 +2291,10 @@ class AppBase: if replacement.startswith("\"") and replacement.endswith("\""): replacement = replacement[1:len(replacement)-1] - print("POST replacement: %s" % replacement) + self.logger.info("POST replacement: %s" % replacement) #json_replacement = tmpitem.replace(actualitem[index][0], replacement, 1) - #print("AFTER POST replacement: %s" % json_replacement) + #self.logger.info("AFTER POST replacement: %s" % json_replacement) json_replacement = replacement try: json_replacement = json.loads(replacement) @@ -2291,12 +2303,12 @@ class AppBase: replacement = replacement.replace("\'", "\"", -1) json_replacement = json.loads(replacement) except: - print("JSON error singular: %s" % e) + self.logger.info("JSON error singular: %s" % e) if len(json_replacement) > minlength: minlength = len(json_replacement) - print("PRE new_replacement") + self.logger.info("PRE new_replacement") new_replacement = [] for i in range(len(json_replacement)): @@ -2309,12 +2321,12 @@ class AppBase: try: newvalue = json.loads(newvalue) except json.decoder.JSONDecodeError as e: - print("DECODER ERROR: %s" % e) + self.logger.info("DECODER ERROR: %s" % e) pass new_replacement.append(newvalue) - print("New replacement: %s" % new_replacement) + self.logger.info("New replacement: %s" % new_replacement) # New tmpitem = tmpitem.replace(actualitem[index][0], replacement, 1) @@ -2323,47 +2335,47 @@ class AppBase: resultarray = [] isfile = False try: - print("(1) ------------ PARAM: %s" % parameter["schema"]["type"]) + self.logger.info("(1) ------------ PARAM: %s" % parameter["schema"]["type"]) if parameter["schema"]["type"] == "file" and len(value) > 0: - print("(1) SHOULD HANDLE FILE IN MULTI. Get based on value %s" % tmpitem) + self.logger.info("(1) SHOULD HANDLE FILE IN MULTI. Get based on value %s" % tmpitem) # This is silly :) # Q: Is there something wrong with the download system? # It seems to return "FILE CONTENT: %s" with the ID as %s for tmp_file_split in json.loads(tmpitem): - print("(1) PRE GET FILE %s" % tmp_file_split) + self.logger.info("(1) PRE GET FILE %s" % tmp_file_split) file_value = self.get_file(tmp_file_split) - print("(1) POST AWAIT %s" % file_value) + self.logger.info("(1) POST AWAIT %s" % file_value) resultarray.append(file_value) - print("(1) FILE VALUE FOR VAL %s: %s" % (tmp_file_split, file_value)) + self.logger.info("(1) FILE VALUE FOR VAL %s: %s" % (tmp_file_split, file_value)) isfile = True except NameError as e: - print("(1) SCHEMA NAMEERROR IN FILE HANDLING: %s" % e) + self.logger.info("(1) SCHEMA NAMEERROR IN FILE HANDLING: %s" % e) except KeyError as e: - print("(1) SCHEMA KEYERROR IN FILE HANDLING: %s" % e) + self.logger.info("(1) SCHEMA KEYERROR IN FILE HANDLING: %s" % e) except json.decoder.JSONDecodeError as e: - print("(1) JSON ERROR IN FILE HANDLING: %s" % e) + self.logger.info("(1) JSON ERROR IN FILE HANDLING: %s" % e) if not isfile: - print("Resultarray (NOT FILE): %s" % resultarray) + self.logger.info("Resultarray (NOT FILE): %s" % resultarray) params[parameter["name"]] = tmpitem multi_parameters[parameter["name"]] = new_replacement else: - print("Resultarray (FILE): %s" % resultarray) + self.logger.info("Resultarray (FILE): %s" % resultarray) params[parameter["name"]] = resultarray multi_parameters[parameter["name"]] = resultarray #if len(resultarray) == 0: - # print("[WARNING] Returning empty array because the array length to be looped is 0 (1)") + # self.logger.info("[WARNING] Returning empty array because the array length to be looped is 0 (1)") # action_result["status"] = "SUCCESS" # action_result["result"] = "[]" # self.send_result(action_result, headers, stream_path) # return multi_execution_lists.append(new_replacement) - #print("MULTI finished: %s" % json_replacement) + #self.logger.info("MULTI finished: %s" % json_replacement) else: - print(f"(2) Pre replacement (loop with variables). Variables: {actualitem}") #% actualitem) + self.logger.info(f"(2) Pre replacement (loop with variables). Variables: {actualitem}") #% actualitem) # This is here to handle for loops within variables.. kindof # 1. Find the length of the longest array # 2. Build an array with the base values based on parameter["value"] @@ -2379,12 +2391,12 @@ class AppBase: actualitem = actualitem[:-2] except IndexError: - print("[WARNING] Indexerror") + self.logger.info("[WARNING] Indexerror") continue - #print(f"\n\nTMPITEM: {actualitem}\n\n") + #self.logger.info(f"\n\nTMPITEM: {actualitem}\n\n") #actualitem = parse_wrapper_start(actualitem) - #print(f"\n\nTMPITEM2: {actualitem}\n\n") + #self.logger.info(f"\n\nTMPITEM2: {actualitem}\n\n") try: itemlist = json.loads(actualitem) @@ -2395,7 +2407,7 @@ class AppBase: curminlength = len(itemlist) except json.decoder.JSONDecodeError as e: - print("JSON Error (replace): %s in %s" % (e, actualitem)) + self.logger.info("JSON Error (replace): %s in %s" % (e, actualitem)) replacements[to_be_replaced] = actualitem @@ -2403,7 +2415,7 @@ class AppBase: # Parses the data as string with length, split etc. before moving on. - #print("In second part of else: %s" % (len(itemlist))) + #self.logger.info("In second part of else: %s" % (len(itemlist))) # This is a result array for JUST this value.. # What if there are more? resultarray = [] @@ -2415,81 +2427,81 @@ class AppBase: replacement = replacement[1:len(replacement)-1] #except json.decoder.JSONDecodeError as e: - #print("REPLACING %s with %s" % (key, replacement)) + #self.logger.info("REPLACING %s with %s" % (key, replacement)) #replacement = parse_wrapper_start(replacement) tmpitem = tmpitem.replace(key, replacement, -1) # This code handles files. - print("(2) ------------ PARAM: %s" % parameter["schema"]["type"]) + self.logger.info("(2) ------------ PARAM: %s" % parameter["schema"]["type"]) isfile = False try: if parameter["schema"]["type"] == "file" and len(value) > 0: - print("(2) SHOULD HANDLE FILE IN MULTI. Get based on value %s" % parameter["value"]) + self.logger.info("(2) SHOULD HANDLE FILE IN MULTI. Get based on value %s" % parameter["value"]) for tmp_file_split in json.loads(parameter["value"]): - print("(2) PRE GET FILE %s" % tmp_file_split) + self.logger.info("(2) PRE GET FILE %s" % tmp_file_split) file_value = self.get_file(tmp_file_split) - print("(2) POST AWAIT %s" % file_value) + self.logger.info("(2) POST AWAIT %s" % file_value) resultarray.append(file_value) - print("(2) FILE VALUE FOR VAL %s: %s" % (tmp_file_split, file_value)) + self.logger.info("(2) FILE VALUE FOR VAL %s: %s" % (tmp_file_split, file_value)) isfile = True except KeyError as e: - print("(2) SCHEMA ERROR IN FILE HANDLING: %s" % e) + self.logger.info("(2) SCHEMA ERROR IN FILE HANDLING: %s" % e) except json.decoder.JSONDecodeError as e: - print("(2) JSON ERROR IN FILE HANDLING: %s" % e) + self.logger.info("(2) JSON ERROR IN FILE HANDLING: %s" % e) if not isfile: tmpitem = tmpitem.replace("\\\\", "\\", -1) resultarray.append(tmpitem) # With this parameter ready, add it to... a greater list of parameters. Rofl - print("LENGTH OF ARR: %d" % len(resultarray)) + self.logger.info("LENGTH OF ARR: %d" % len(resultarray)) if len(resultarray) == 0: - print("[WARNING] Returning empty array because the array length to be looped is 0 (0)") + self.logger.info("[WARNING] Returning empty array because the array length to be looped is 0 (0)") self.action_result["status"] = "SUCCESS" self.action_result["result"] = "[]" self.send_result(self.action_result, headers, stream_path) return - #print("RESULTARRAY: %s" % resultarray) + #self.logger.info("RESULTARRAY: %s" % resultarray) if resultarray not in multi_execution_lists: multi_execution_lists.append(resultarray) multi_parameters[parameter["name"]] = resultarray else: # Parses things like int(value) - print("[DEBUG] Normal parsing (not looping)")#with data %s" % value) + self.logger.info("[DEBUG] Normal parsing (not looping)")#with data %s" % value) # This part has fucked over so many random JSON usages because of weird paranthesis parsing value = parse_wrapper_start(value, self) - print("[DEBUG] Post return: %s" % value) + self.logger.info("[DEBUG] Post return: %s" % value) - #print("POST data value: %s" % value) + #self.logger.info("POST data value: %s" % value) params[parameter["name"]] = value multi_parameters[parameter["name"]] = value # This code handles files. try: if parameter["schema"]["type"] == "file" and len(value) > 0: - print("\n SHOULD HANDLE FILE. Get based on value %s. <--- is this a valid ID?" % parameter["value"]) + self.logger.info("\n SHOULD HANDLE FILE. Get based on value %s. <--- is this a valid ID?" % parameter["value"]) file_value = self.get_file(value) - print("FILE VALUE: %s \n" % file_value) + self.logger.info("FILE VALUE: %s \n" % file_value) params[parameter["name"]] = file_value multi_parameters[parameter["name"]] = file_value except KeyError as e: - print("SCHEMA ERROR IN FILE HANDLING: %s" % e) + self.logger.info("SCHEMA ERROR IN FILE HANDLING: %s" % e) #remove_params.append(parameter["name"]) # Fix lists here # FIXME: This doesn't really do anything anymore - print("[DEBUG] CHECKING multi execution list: %d!" % len(multi_execution_lists)) + self.logger.info("[DEBUG] CHECKING multi execution list: %d!" % len(multi_execution_lists)) if len(multi_execution_lists) > 0: - print("\n [DEBUG] Multi execution list has more data: %d" % len(multi_execution_lists)) + self.logger.info("\n [DEBUG] Multi execution list has more data: %d" % len(multi_execution_lists)) filteredlist = [] for listitem in multi_execution_lists: if listitem in filteredlist: @@ -2504,55 +2516,55 @@ class AppBase: #else: # filteredlist.append(listitem) - #print("New list length: %d" % len(filteredlist)) + #self.logger.info("New list length: %d" % len(filteredlist)) if len(filteredlist) > 1: - print(f"Calculating new multi-loop length with {len(filteredlist)} lists") + self.logger.info(f"Calculating new multi-loop length with {len(filteredlist)} lists") tmplength = 1 for innerlist in filteredlist: tmplength = len(innerlist)*tmplength - print("List length: %d. %d*%d" % (tmplength, len(innerlist), tmplength)) + self.logger.info("List length: %d. %d*%d" % (tmplength, len(innerlist), tmplength)) minlength = tmplength - print("New multi execution length: %d\n" % tmplength) + self.logger.info("New multi execution length: %d\n" % tmplength) # Cleaning up extra list params for subparam in remove_params: - #print(f"DELETING {subparam}") + #self.logger.info(f"DELETING {subparam}") try: del params[subparam] except: pass - #print(f"Error with subparam deletion of {subparam} in {params}") + #self.logger.info(f"Error with subparam deletion of {subparam} in {params}") try: del multi_parameters[subparam] except: - #print(f"Error with subparam deletion of {subparam} in {multi_parameters} (2)") + #self.logger.info(f"Error with subparam deletion of {subparam} in {multi_parameters} (2)") pass - #print() - #print(f"Param: {params}") - #print(f"Multiparams: {multi_parameters}") - #print() + #self.logger.info() + #self.logger.info(f"Param: {params}") + #self.logger.info(f"Multiparams: {multi_parameters}") + #self.logger.info() if not multiexecution: # Runs a single iteration here new_params = self.validate_unique_fields(params) - print(f"Returned with newparams of length {len(new_params)}") + self.logger.info(f"Returned with newparams of length {len(new_params)}") if isinstance(new_params, list) and len(new_params) == 1: params = new_params[0] else: - print("[WARNING] SHOULD STOP EXECUTION BECAUSE FIELDS AREN'T UNIQUE") + self.logger.info("[WARNING] SHOULD STOP EXECUTION BECAUSE FIELDS AREN'T UNIQUE") self.action_result["status"] = "SKIPPED" self.action_result["result"] = f"A non-unique value was found" self.action_result["completed_at"] = int(time.time()) self.send_result(self.action_result, headers, stream_path) return - print("[INFO] Running normal execution (not loop)\n") + self.logger.info("[INFO] Running normal execution (not loop)\n") #newres = await func(**params) - #print("PARAMS: %s" % params) + #self.logger.info("PARAMS: %s" % params) #newres = "" while True: try: @@ -2568,32 +2580,32 @@ class AppBase: try: del params[field] - print("Removed field invalid field %s" % field) + self.logger.info("Removed field invalid field %s" % field) except KeyError: break else: raise e #break - print("\n[INFO] Returned from execution with types %s" % type(newres)) - #print("\n[INFO] Returned from execution with %s of types %s" % (newres, type(newres)))#, newres) + self.logger.info("\n[INFO] Returned from execution with types %s" % type(newres)) + #self.logger.info("\n[INFO] Returned from execution with %s of types %s" % (newres, type(newres)))#, newres) if isinstance(newres, tuple): - print("[INFO] Handling return as tuple") + self.logger.info("[INFO] Handling return as tuple") # Handles files. filedata = "" file_ids = [] - print("TUPLE: %s" % newres[1]) + self.logger.info("TUPLE: %s" % newres[1]) if isinstance(newres[1], list): - print("[INFO] HANDLING LIST FROM RET") + self.logger.info("[INFO] HANDLING LIST FROM RET") file_ids = self.set_files(newres[1]) elif isinstance(newres[1], object): - print("[INFO] Handling JSON from ret") + self.logger.info("[INFO] Handling JSON from ret") file_ids = self.set_files([newres[1]]) elif isinstance(newres[1], str): - print("[INFO] Handling STRING from ret") + self.logger.info("[INFO] Handling STRING from ret") file_ids = self.set_files([newres[1]]) else: - print("[INFO] NO FILES TO HANDLE") + self.logger.info("[INFO] NO FILES TO HANDLE") tmp_result = { "result": newres[0], @@ -2602,33 +2614,33 @@ class AppBase: result = json.dumps(tmp_result) elif isinstance(newres, str): - print("[INFO] Handling return as string of length %d" % len(newres)) + self.logger.info("[INFO] Handling return as string of length %d" % len(newres)) result += newres elif isinstance(newres, dict) or isinstance(newres, list): try: result += json.dumps(newres, indent=4) except json.JSONDecodeError as e: - print("Failed decoding result: %s" % e) + self.logger.info("Failed decoding result: %s" % e) try: result += str(newres) except ValueError: result += "Failed autocasting. Can't handle %s type from function. Must be string" % type(newres) - print("Can't handle type %s value from function" % (type(newres))) + self.logger.info("Can't handle type %s value from function" % (type(newres))) else: try: result += str(newres) except ValueError: result += "Failed autocasting. Can't handle %s type from function. Must be string" % type(newres) - print("Can't handle type %s value from function" % (type(newres))) + self.logger.info("Can't handle type %s value from function" % (type(newres))) - print("[INFO] POST NEWRES RESULT!")#, result) + self.logger.info("[INFO] POST NEWRES RESULT!")#, result) else: - #print("[INFO] APP_SDK DONE: Starting MULTI execution (length: %d) with values %s" % (minlength, multi_parameters)) + #self.logger.info("[INFO] APP_SDK DONE: Starting MULTI execution (length: %d) with values %s" % (minlength, multi_parameters)) # 1. Use number of executions based on the arrays being similar # 2. Find the right value from the parsed multi_params - print("[INFO] Running WITHOUT outer loop (looping)") + self.logger.info("[INFO] Running WITHOUT outer loop (looping)") json_object = False results = await self.run_recursed_items(func, multi_parameters, {}) if isinstance(results, dict) or isinstance(results, list): @@ -2636,14 +2648,14 @@ class AppBase: #for i in range(0, minlength): # # To be able to use the results as a list: - # print("1: %s" % multi_parameters) + # self.logger.info("1: %s" % multi_parameters) # #baseparams = json.loads(json.dumps(multi_parameters)) # baseparams = copy.deepcopy(multi_parameters) - # print("2: %s: %s" % (type(baseparams), baseparams)) + # self.logger.info("2: %s: %s" % (type(baseparams), baseparams)) - # print("4") - # print("Running with params (1): %s" % baseparams) + # self.logger.info("4") + # self.logger.info("Running with params (1): %s" % baseparams) # results = await self.run_recursed_items(func, baseparams, {}) # if isinstance(results, dict) or isinstance(results, list): @@ -2659,7 +2671,7 @@ class AppBase: #try: # firstlist = True # for key, value in baseparams.items(): - # print("Itemtype: %s" % type(value)) + # self.logger.info("Itemtype: %s" % type(value)) # if isinstance(value, list): # try: # newvalue = value[i] @@ -2668,16 +2680,16 @@ class AppBase: # if len(value) != minlength and len(value) > 0: # newarray = [] - # print("VALUE: ", value) + # self.logger.info("VALUE: ", value) # additiontime = minlength/len(value) - # print("Bad length for value: %d - should be %d. Additiontime: %d" % (len(value), minlength, additiontime)) + # self.logger.info("Bad length for value: %d - should be %d. Additiontime: %d" % (len(value), minlength, additiontime)) # if firstlist: - # print("Running normal list (FIRST)") + # self.logger.info("Running normal list (FIRST)") # for subvalue in value: # for number in range(int(additiontime)): # newarray.append(subvalue) # else: - # #print("Running secondary lists") + # #self.logger.info("Running secondary lists") # ## 1. Set up length of array # ## 2. Put values spread out # # FIXME: This works well, except if lists are same length @@ -2687,31 +2699,31 @@ class AppBase: # for number in range(int(additiontime)): # for subvaluerange in range(len(value)): # # newlocation = number+(additiontime*subvaluerange) - # # print("%d+(%d*%d) = %d. VAL: %s" % (number, additiontime, subvaluerange, newlocation, value[subvaluerange])) + # # self.logger.info("%d+(%d*%d) = %d. VAL: %s" % (number, additiontime, subvaluerange, newlocation, value[subvaluerange])) # # Reverse if same length? # if int(minlength/len(value)) == len(value): # tmp = int(len(value)-subvaluerange-1) - # print("NEW: %d" % tmp) + # self.logger.info("NEW: %d" % tmp) # newarray[cnt] = value[tmp] # else: # newarray[cnt] = value[subvaluerange] # cnt += 1 - # #print("Newarray =", newarray) + # #self.logger.info("Newarray =", newarray) # newvalue = newarray[i] # firstlist = False # baseparams[key] = newvalue - # print("3") + # self.logger.info("3") #except IndexError as e: - # print("IndexError: %s" % e) + # self.logger.info("IndexError: %s" % e) # baseparams[key] = "IndexError: %s" % e #except KeyError as e: - # print("KeyError: %s" % e) + # self.logger.info("KeyError: %s" % e) # baseparams[key] = "KeyError: %s" % e - #print("4") - #print("Running with params (1): %s" % baseparams) + #self.logger.info("4") + #self.logger.info("Running with params (1): %s" % baseparams) #results = await self.run_recursed_items(func, baseparams, {}) #if isinstance(results, dict) or isinstance(results, list): @@ -2720,7 +2732,7 @@ class AppBase: # Check the structure here. If "isloop", try to recurse? # ret, is_loop = recurse_json(innervalue, parsersplit[outercnt+1:]) #ret = await func(**baseparams) - #print("Return from execution: %s" % ret) + #self.logger.info("Return from execution: %s" % ret) #if ret == None: # results.append("") # json_object = False @@ -2734,22 +2746,22 @@ class AppBase: # results.append(json.loads(ret)) # json_object = True # except json.decoder.JSONDecodeError as e: - # #print("Json: %s" % e) + # #self.logger.info("Json: %s" % e) # results.append(ret) - #print("Inner ret parsed: %s" % ret) + #self.logger.info("Inner ret parsed: %s" % ret) # Dump the result as a string of a list - #print("RESULTS: %s" % results) + #self.logger.info("RESULTS: %s" % results) if isinstance(results, list) or isinstance(results, dict): - print("JSON OBJECT? ", json_object) + self.logger.info("JSON OBJECT? ", json_object) # This part is weird lol if json_object: try: result = json.dumps(results) except json.JSONDecodeError as e: - print(f"Failed to decode: {e}") + self.logger.info(f"Failed to decode: {e}") result = results else: result = "[" @@ -2759,7 +2771,7 @@ class AppBase: result += item except json.decoder.JSONDecodeError as e: # Common nested issue which puts " around everything - print("Decodingerror: %s" % e) + self.logger.info("Decodingerror: %s" % e) try: tmpitem = item.replace("\\\"", "\"", -1) json.loads(tmpitem) @@ -2773,7 +2785,7 @@ class AppBase: result = result[:-2] result += "]" else: - print("Normal result - no list?") + self.logger.info("Normal result - no list?") result = results self.action_result["status"] = "SUCCESS" @@ -2784,18 +2796,18 @@ class AppBase: self.logger.debug(f"Executed {action['label']}-{action['id']}")#with result: {result}") #self.logger.debug(f"Data: %s" % action_result) except TypeError as e: - print("TypeError issue: %s" % e) + self.logger.info("TypeError issue: %s" % e) self.action_result["status"] = "FAILURE" self.action_result["result"] = "TypeError: %s" % str(e) else: - print("Function %s doesn't exist?" % action["name"]) + self.logger.info("Function %s doesn't exist?" % action["name"]) self.logger.error(f"App {self.__class__.__name__}.{action['name']} is not callable") self.action_result["status"] = "FAILURE" self.action_result["result"] = "Function %s is not callable." % actionname # https://ptb.discord.com/channels/747075026288902237/882017498550112286/882043773138382890 except (requests.exceptions.RequestException, TimeoutError) as e: - print(f"Failed to execute request: {e}") + self.logger.info(f"Failed to execute request: {e}") self.logger.exception(f"Failed to execute {e}-{action['id']}") self.action_result["status"] = "SUCCESS" try: @@ -2807,7 +2819,7 @@ class AppBase: self.action_result["result"] = f"Request error: {e}" except Exception as e: - print(f"Failed to execute: {e}") + self.logger.info(f"Failed to execute: {e}") self.logger.exception(f"Failed to execute {e}-{action['id']}") self.action_result["status"] = "FAILURE" self.action_result["result"] = f"General exception: {e}" @@ -2824,14 +2836,14 @@ class AppBase: logger = logging.getLogger(f"{cls.__name__}") logger.setLevel(logging.DEBUG) - #print("Started execution: %s!!" % cls) - #print("Action: %s" % action) + #self.logger.info("Started execution: %s!!" % cls) + #self.logger.info("Action: %s" % action) #if isinstance(cls, object): # self.action = cls app = cls(redis=None, logger=logger, console_logger=logger) if isinstance(action, str): - print("Normal execution. Action is a string.") + self.logger.info("Normal execution. Action is a string.") elif isinstance(action, object): app.action = action @@ -2851,6 +2863,6 @@ class AppBase: except: pass else: - print("ACTION TYPE (unhandled): %s" % type(action)) + self.logger.info("ACTION TYPE (unhandled): %s" % type(action)) await app.execute_action(app.action) From 1632a03753f570c2a11d342f11014df3e123822d Mon Sep 17 00:00:00 2001 From: frikky Date: Wed, 13 Oct 2021 02:28:46 +0200 Subject: [PATCH 112/117] Fixed suborg zIndex, execution debugging, Oauth2 view and SDK updates --- backend/app_sdk/Dockerfile | 4 +- backend/app_sdk/app_base.py | 202 +++++++++-------------- backend/app_sdk/build.sh | 2 +- backend/go-app/go.mod | 2 +- backend/go-app/main.go | 5 +- backend/go-app/walkoff.go | 7 +- docker-compose.yml | 4 +- frontend/src/components/Header.js | 9 +- frontend/src/components/Oauth2Auth.jsx | 110 ++++++++++-- frontend/src/views/AngularWorkflow.jsx | 21 ++- frontend/src/views/AppCreator.jsx | 89 +++++++--- frontend/src/views/Apps.jsx | 14 +- frontend/src/views/Docs.jsx | 28 ++-- frontend/src/views/SetAuthentication.jsx | 4 + functions/onprem/orborus/build.sh | 2 +- 15 files changed, 307 insertions(+), 196 deletions(-) diff --git a/backend/app_sdk/Dockerfile b/backend/app_sdk/Dockerfile index c323b73e..68bd625d 100644 --- a/backend/app_sdk/Dockerfile +++ b/backend/app_sdk/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.9.1-alpine as base FROM base as builder -RUN apk --no-cache add --update alpine-sdk libffi libffi-dev musl-dev openssl-dev tzdata +RUN apk --no-cache add --update alpine-sdk libffi libffi-dev musl-dev openssl-dev tzdata coreutils RUN mkdir /install WORKDIR /install @@ -8,7 +8,7 @@ WORKDIR /install FROM base #--no-cache -RUN apk update && apk add --update tzdata libmagic alpine-sdk libffi libffi-dev musl-dev openssl-dev +RUN apk update && apk add --update tzdata libmagic alpine-sdk libffi libffi-dev musl-dev openssl-dev coreutils COPY --from=builder /install /usr/local COPY requirements.txt /requirements.txt diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index ce954c25..36a795dc 100644 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -83,11 +83,11 @@ class AppBase: #self.logger.info("[INFO] URL (URL): %s" % url) try: ret = requests.post(url, headers=headers, json=action_result) - self.logger.info(f"[DEBUG] Result: {res.status_code}") - if ret.status_code != 200: - self.logger.info(f"[DEBUG] Shuffle Response: {ret.text}") + #self.logger.info(f"[DEBUG] Result: {ret.status_code}") + #if ret.status_code != 200: + # self.logger.info(f"[DEBUG] Shuffle Response: {ret.text}") - self.logger.info("[DEBUG] Stream result was sent!") + self.logger.info(f"[DEBUG] Successful request: Status= {ret.status_code} & Response= {ret.text}") except requests.exceptions.ConnectionError as e: self.logger.info(f"[DEBUG] Unexpected ConnectionError happened: {e}") return @@ -97,9 +97,11 @@ class AppBase: action_result["result"] = f"POST error: {e}" self.logger.info(f"[DEBUG] Before typeerror stream result: {e}") ret = requests.post("%s%s" % (self.base_url, stream_path), headers=headers, json=action_result) - self.logger.info(f"[DEBUG] Result: {ret.status_code}") - if ret.status_code != 200: - self.logger.info(ret.text) + #self.logger.info(f"[DEBUG] Result: {ret.status_code}") + #if ret.status_code != 200: + # pr + + self.logger.info(f"[DEBUG] TypeError request: Status= {ret.status_code} & Response= {ret.text}") except http.client.RemoteDisconnected as e: self.logger.info(f"[DEBUG] Expected Remotedisconnect happened: {e}") return @@ -870,7 +872,7 @@ class AppBase: pass self.action = copy.deepcopy(action) - self.logger.info("Sending starting action result (EXECUTING)") + self.logger.info("[DEBUG] Sending starting action result (EXECUTING)") headers = { "Content-Type": "application/json", @@ -924,7 +926,7 @@ class AppBase: "execution_id": self.current_execution_id } - self.logger.info("Before FULLEXEC stream result") + self.logger.info("[DEBUG] Before FULLEXEC stream result") ret = requests.post( "%s/api/v1/streams/results" % (self.base_url), headers=headers, @@ -961,7 +963,7 @@ class AppBase: self.full_execution = fullexecution - self.logger.info("AFTER FULLEXEC stream result (init)") + self.logger.info("[DEBUG] AFTER FULLEXEC stream result (init)") # Gets the value at the parenthesis level you want def parse_nested_param(string, level): @@ -1019,7 +1021,6 @@ class AppBase: try: return int(data) except ValueError: - self.logger.info("[DEBUG] ValueError while casting %s to int" % data) return data if "lower" in thistype: @@ -1033,7 +1034,6 @@ class AppBase: if "split" in thistype: return data.split() if "replace" in thistype: - self.logger.info("Running replace!") splitvalues = data.split(",") if len(splitvalues) > 2: @@ -1061,47 +1061,39 @@ class AppBase: else: return f"replace({data})" if "join" in thistype: - self.logger.info(f"SHOULD JOIN: {data}") try: splitvalues = data.split(",") if "," not in data: return f"join({data})" if len(splitvalues) >= 2: - self.logger.info(f"SPLITVALUE: {splitvalues[-1]}") # 1. Take the list and parse it from string # 2. Take all the items and join them # 3. Parse them back as string and return values = ",".join(splitvalues[0:-1]) - self.logger.info(f"VALUES: {values}") tmp = json.loads(values) - self.logger.info(f"TMP: {tmp}") - #tmp = tmp[1:-1] - #self.logger.info(f"TMP2: {tmp}") try: newvalues = splitvalues[-1].join(str(item).strip() for item in tmp) except TypeError: newvalues = splitvalues[-1].join(json.dumps(item).strip() for item in tmp) - self.logger.info(f"new: {newvalues}") return newvalues else: - self.logger.info("Returning default") return f"join({data})" except (KeyError, IndexError) as e: - self.logger.info(f"ERROR in join(): {e}") + print(f"ERROR in join(): {e}") except json.decoder.JSONDecodeError as e: - self.logger.info(f"JSON ERROR in join(): {e}") + print(f"JSON ERROR in join(): {e}") if "len" in thistype or "length" in thistype or "lenght" in thistype: - self.logger.info(f"Trying to length-parse: {data}") + #self.logger.info(f"Trying to length-parse: {data}") try: tmp_len = json.loads(data, parse_float=str, parse_int=str, parse_constant=str) except (NameError, KeyError, TypeError, json.decoder.JSONDecodeError) as e: try: - self.logger.info(f"[WARNING] INITIAL Parsing bug for length in app sdk: {e}") + #self.logger.info(f"[WARNING] INITIAL Parsing bug for length in app sdk: {e}") # data = data.replace("\'", "\"") data = data.replace("True", "true") data = data.replace("False", "false") @@ -1142,7 +1134,6 @@ class AppBase: else: tmp = json.loads(parsedlist)[lastsplit[0]] - #self.logger.info(tmp) return tmp except IndexError as e: return default_error @@ -1165,9 +1156,6 @@ class AppBase: inner_value = parse_nested_param(data, maxDepth(data) - 0) outer_value = parse_nested_param(data, maxDepth(data) - 1) - #self.logger.info("[DEBUG] INNER: ", inner_value) - #self.logger.info("[DEBUG] OUTER: ", outer_value) - wrapper_group = "|".join(wrappers) parse_string = data max_depth = maxDepth(parse_string) @@ -1197,7 +1185,7 @@ class AppBase: c_parentheses = parse_nested_param(parse_string, 0)[0] match_string = re.escape(c_parentheses) custom_casting = re.findall(fr"({wrapper_group})\({match_string}", parse_string) - self.logger.info("In ELSE: %s" % custom_casting) + print("[DEBUG] In ELSE: %s" % custom_casting) # check if a wrapper was found if len(custom_casting) != 0: inner_result = parse_type(c_parentheses, custom_casting[0]) @@ -1209,7 +1197,7 @@ class AppBase: else: parse_string = inner_result - self.logger.info("PARSE STRING: %s" % parse_string) + print("PARSE STRING: %s" % parse_string) return parse_string, True # Looks for parantheses to grab special cases within a string, e.g: @@ -1308,7 +1296,6 @@ class AppBase: # $nodename.data.#min-max.info.id def recurse_json(basejson, parsersplit): match = "#([0-9a-z]+):?-?([0-9a-z]+)?#?" - #self.logger.info("Split: %s\n%s" % (parsersplit, basejson)) try: outercnt = 0 @@ -1317,9 +1304,7 @@ class AppBase: #if " " in value: # value = value.replace(" ", "_", -1) - #self.logger.info("VALUE: %s\n" % value) actualitem = re.findall(match, value, re.MULTILINE) - #self.logger.info("ACTUAL RECURSE: (%s) %s" % (value, actualitem)) if value == "#": newvalue = [] for innervalue in basejson: @@ -1339,17 +1324,16 @@ class AppBase: return newvalue, True elif len(actualitem) > 0: - #self.logger.info("[INFO] In recursion v2: ", actualitem) is_loop = True newvalue = [] firstitem = actualitem[0][0] seconditem = actualitem[0][1] - self.logger.info("[DEBUG] ACTUAL PARSED: %s" % actualitem) + print("[DEBUG] ACTUAL PARSED: %s" % actualitem) # Means it's a single item -> continue if seconditem == "": - self.logger.info("[INFO] In first - handling %s. Len: %d" % (firstitem, len(basejson))) + print("[INFO] In first - handling %s. Len: %d" % (firstitem, len(basejson))) if firstitem.lower() == "max" or firstitem.lower() == "last": firstitem = len(basejson)-1 elif firstitem.lower() == "min" or firstitem.lower() == "first": @@ -1357,14 +1341,14 @@ class AppBase: else: firstitem = int(firstitem) - self.logger.info(f"[DEBUG] Post lower checks with item {firstitem}") + print(f"[DEBUG] Post lower checks with item {firstitem}") tmpitem = basejson[int(firstitem)] try: newvalue, is_loop = recurse_json(tmpitem, parsersplit[outercnt+1:]) except IndexError: newvalue, is_loop = (tmpitem, parsersplit[outercnt+1:]) else: - self.logger.info("[INFO] In ELSE - handling %s and %s" % (firstitem, seconditem)) + print("[INFO] In ELSE - handling %s and %s" % (firstitem, seconditem)) if firstitem.lower() == "max" or firstitem.lower() == "last": firstitem = len(basejson)-1 elif firstitem.lower() == "min" or firstitem.lower() == "first": @@ -1379,7 +1363,7 @@ class AppBase: else: seconditem = int(seconditem) - self.logger.info(f"[DEBUG] Post lower checks 2: {firstitem} AND {seconditem}") + print(f"[DEBUG] Post lower checks 2: {firstitem} AND {seconditem}") newvalue = [] if int(seconditem) > len(basejson): seconditem = len(basejson) @@ -1392,63 +1376,55 @@ class AppBase: try: ret, tmp_loop = recurse_json(basejson[i], parsersplit[outercnt+1:]) except IndexError: - self.logger.info("[DEBUG] INDEXERROR: ", parsersplit[outercnt]) + print("[DEBUG] INDEXERROR: ", parsersplit[outercnt]) #ret = innervalue ret, tmp_loop = recurse_json(innervalue, parsersplit[outercnt:]) - #self.logger.info("IN LIST: %s" % ret) - #exit() newvalue.append(ret) - #self.logger.info("Returning %s" % newvalue) return newvalue, is_loop else: - #self.logger.info("BEFORE NORMAL VALUE: ", basejson, value) if len(value) == 0: return basejson, False try: if isinstance(basejson, list): - self.logger.info("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (list): %s" % value) + print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (list): %s" % value) return basejson, False elif isinstance(basejson[value], str): - #self.logger.info(f"[INFO] LOADING STRING '%s' AS JSON" % basejson[value]) try: basejson = json.loads(basejson[value]) - #self.logger.info("[DEBUG] BASEJSON: %s" % basejson) except json.decoder.JSONDecodeError as e: - #self.logger.info("[DEBUG] RETURNING BECAUSE '%s' IS A NORMAL STRING (0)" % basejson[value]) return basejson[value], False else: basejson = basejson[value] except KeyError as e: - self.logger.info("[WARNING] Running secondary value check with replacement of underscore in %s: %s" % (value, e)) + print("[WARNING] Running secondary value check with replacement of underscore in %s: %s" % (value, e)) if "_" in value: value = value.replace("_", " ", -1) elif " " in value: value = value.replace(" ", "_", -1) if isinstance(basejson, list): - self.logger.info("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (list): %s" % value) + print("[WARNING] VALUE IN ISINSTANCE IS NOT TO BE USED (list): %s" % value) return basejson, False elif isinstance(basejson[value], str): - self.logger.info(f"[INFO] LOADING STRING '%s' AS JSON" % basejson[value]) + print(f"[INFO] LOADING STRING '%s' AS JSON" % basejson[value]) try: basejson = json.loads(basejson[value]) - self.logger.info("[DEBUG] BASEJSON: %s" % basejson) + print("[DEBUG] BASEJSON: %s" % basejson) except json.decoder.JSONDecodeError as e: - self.logger.info("[DEBUG] RETURNING BECAUSE '%s' IS A NORMAL STRING (1)" % basejson[value]) + print("[DEBUG] RETURNING BECAUSE '%s' IS A NORMAL STRING (1)" % basejson[value]) return basejson[value], False else: basejson = basejson[value] - #self.logger.info("Parsed BASEJSON: %s" % basejson) outercnt += 1 except KeyError as e: - self.logger.info("[INFO] Lower keyerror: %s" % e) + print("[INFO] Lower keyerror: %s" % e) return "", False #return basejson @@ -1463,13 +1439,13 @@ class AppBase: actionname_lower = parsersplit[0][1:].lower() #Actionname: Start_node - self.logger.info(f"\n[INFO] Actionname: {actionname_lower}") + print(f"\n[INFO] Actionname: {actionname_lower}") # 1. Find the action baseresult = "" appendresult = "" - self.logger.info("[INFO] Parsersplit length: %d" % len(parsersplit)) + print("[INFO] Parsersplit length: %d" % len(parsersplit)) if (actionname_lower.startswith("exec ") or actionname_lower.startswith("webhook ") or actionname_lower.startswith("schedule ") or actionname_lower.startswith("userinput ") or actionname_lower.startswith("email_trigger ") or actionname_lower.startswith("trigger ")) and len(parsersplit) == 1: record = False for char in actionname_lower: @@ -1489,21 +1465,19 @@ class AppBase: if actionname_lower == "exec" or actionname_lower == "webhook" or actionname_lower == "schedule" or actionname_lower == "userinput" or actionname_lower == "email_trigger" or actionname_lower == "trigger": baseresult = execution_data["execution_argument"] elif actionname_lower == "shuffle_cache": - self.logger.info("SHOULD GET CACHE KEY: %s" % parsersplit) + print("[DEBUG] SHOULD GET CACHE KEY: %s" % parsersplit) if len(parsersplit) > 1: actual_key = parsersplit[1] - self.logger.info("KEY: %s" % actual_key) + print("[DEBUG] KEY: %s" % actual_key) cachedata = self.get_cache(actual_key) - self.logger.info("CACHE: %s" % cachedata) + print("CACHE: %s" % cachedata) parsersplit.pop(1) try: baseresult = json.dumps(cachedata) except json.decoder.JSONDecodeError as e: - self.logger.info("Failed json dumping: %s" % e) + print("[WARNING] Failed json dumping: %s" % e) - #returndata = str(baseresult)+str(appendresult) else: - #self.logger.info("Within execution data check. Execution data: %s", execution_data["results"]) if execution_data["results"] != None: for result in execution_data["results"]: resultlabel = result["action"]["label"].replace(" ", "_", -1).lower() @@ -1511,13 +1485,12 @@ class AppBase: baseresult = result["result"] break else: - self.logger.info("No results to get values from.") + print("[DEBUG] No results to get values from.") baseresult = "$" + parsersplit[0][1:] - self.logger.info("BEFORE VARIABLES!") + print("[DEBUG] BEFORE VARIABLES!") if len(baseresult) == 0: try: - #self.logger.info("WF Variables: %s" % execution_data["workflow"]["workflow_variables"]) for variable in execution_data["workflow"]["workflow_variables"]: variablename = variable["name"].replace(" ", "_", -1).lower() @@ -1526,47 +1499,46 @@ class AppBase: break except KeyError as e: - self.logger.info("[INFO] KeyError wf variables: %s" % e) + print("[INFO] KeyError wf variables: %s" % e) pass except TypeError as e: - self.logger.info("[INFO] TypeError wf variables: %s" % e) + print("[INFO] TypeError wf variables: %s" % e) pass - self.logger.info("BEFORE EXECUTION VAR") + print("[DEBUG] BEFORE EXECUTION VAR") if len(baseresult) == 0: try: - #self.logger.info("Execution Variables: %s" % execution_data["execution_variables"]) for variable in execution_data["execution_variables"]: variablename = variable["name"].replace(" ", "_", -1).lower() if variablename.lower() == actionname_lower: baseresult = variable["value"] break except KeyError as e: - self.logger.info("[INFO] KeyError exec variables: %s" % e) + print("[INFO] KeyError exec variables: %s" % e) pass except TypeError as e: - self.logger.info("[INFO] TypeError exec variables: %s" % e) + print("[INFO] TypeError exec variables: %s" % e) pass except KeyError as error: - self.logger.info(f"[DEBUG] KeyError in JSON: {error}") + print(f"[DEBUG] KeyError in JSON: {error}") - self.logger.info(f"[INFO] After first trycatch. Baseresult")#, baseresult) + print(f"[INFO] After first trycatch. Baseresult")#, baseresult) # 2. Find the JSON data if len(baseresult) == 0: return ""+appendresult, False - self.logger.info("[INFO] After second return") + print("[INFO] After second return") if len(parsersplit) == 1: returndata = str(baseresult)+str(appendresult) - self.logger.info("RETURNING!")#: %s" % returndata) + print("[DEBUG] RETURNING!")#: %s" % returndata) return returndata, False baseresult = baseresult.replace(" True,", " true,") baseresult = baseresult.replace(" False", " false,") - self.logger.info("[INFO] After third parser return - Formatted")#, baseresult) + print("[INFO] After third parser return - Formatted")#, baseresult) basejson = {} try: basejson = json.loads(baseresult) @@ -1575,10 +1547,10 @@ class AppBase: baseresult = baseresult.replace("\'", "\"") basejson = json.loads(baseresult) except json.decoder.JSONDecodeError as e: - self.logger.info("Parser issue with JSON: %s" % e) + print("Parser issue with JSON: %s" % e) return str(baseresult)+str(appendresult), False - self.logger.info("[INFO] After fourth parser return as JSON") + print("[INFO] After fourth parser return as JSON") data, is_loop = recurse_json(basejson, parsersplit[1:]) parseditem = data @@ -1586,31 +1558,31 @@ class AppBase: try: parseditem = json.dumps(parseditem) except json.decoder.JSONDecodeError as e: - self.logger.info("Parseditem issue: %s" % e) + print("[WARNING] Parseditem issue: %s" % e) pass - self.logger.info("DATA: (%s) %s" % (type(data), data)) + print("[DEBUG] DATA: (%s) %s" % (type(data), data)) if is_loop: - self.logger.info("DATA IS A LOOP - SHOULD WRAP") + print("[DEBUG] DATA IS A LOOP - SHOULD WRAP") if parsersplit[-1] == "#": - self.logger.info("SET DATA WRAPPER TO NORMAL!") + print("[WARNING] SET DATA WRAPPER TO NORMAL!") parseditem = "${SHUFFLE_NO_SPLITTER%s}$" % json.dumps(data) else: # Return value: ${id[12345, 45678]}$ - self.logger.info("SET DATA WRAPPER TO %s!" % parsersplit[-1]) + print("[WARNING] SET DATA WRAPPER TO %s!" % parsersplit[-1]) parseditem = "${%s%s}$" % (parsersplit[-1], json.dumps(data)) - self.logger.info("Before last return with %s" % appendresult) + print("[DEBUG] Before last return with %s" % appendresult) returndata = str(parseditem)+str(appendresult) # New in 0.8.97: Don't return items without lists - self.logger.info("RETURNDATA: %s" % returndata) + #self.logger.info("RETURNDATA: %s" % returndata) #return returndata, is_loop try: return json.dumps(json.loads(returndata)), is_loop except json.decoder.JSONDecodeError as e: - self.logger.info("Error in decoder: %s" % e) + print("Error in decoder: %s" % e) return returndata, is_loop # Sending self as it's not a normal function @@ -1667,7 +1639,6 @@ class AppBase: newvalue = data for key, value in newvalue.items(): - #self.logger.info("%s: %s" % (key, value)) if isinstance(value, str) and len(value) == 0: deletekeys.append(key) continue @@ -1675,17 +1646,16 @@ class AppBase: if isinstance(value, list): try: value = json.dumps(value) - self.logger.info(value) except: - self.logger.info("[WARNING] Json parsing issue in recursed value") + print("[WARNING] Json parsing issue in recursed value") pass if value == "${%s}" % key: - self.logger.info("[WARNING] Deleting %s because key = value" % key) + print("[WARNING] Deleting %s because key = value" % key) deletekeys.append(key) continue elif "${" in value and "}" in value: - self.logger.info("[WARNING] Deleting %s because it contains ${ and }" % key) + print("[WARNING] Deleting %s because it contains ${ and }" % key) deletekeys.append(key) continue @@ -1693,9 +1663,9 @@ class AppBase: newvalue[key] = recurse_cleanup_script(value) except json.decoder.JSONDecodeError as e: - self.logger.info(f"[WARNING] Failed JSON replacement for OpenAPI keys (3) {e}") + print(f"[WARNING] Failed JSON replacement for OpenAPI keys (3) {e}") except Exception as e: - self.logger.info(f"[WARNING] Failed as an exception (1): {e}") + print(f"[WARNING] Failed as an exception (1): {e}") try: for deletekey in deletekeys: @@ -1704,11 +1674,10 @@ class AppBase: except: pass except Exception as e: - self.logger.info(f"[WARNING] Failed in deletekeys: {e}") + print(f"[WARNING] Failed in deletekeys: {e}") return data try: - #self.logger.info("Post delete: %s" % newvalue) for key, value in newvalue.items(): if isinstance(value, bool): continue @@ -1719,28 +1688,20 @@ class AppBase: value = json.loads(value) newvalue[key] = value except json.decoder.JSONDecodeError as e: - #self.logger.info("Inner overwrite issue for \"%s\": %s" % (key, e)) continue except Exception as e: - #self.logger.info("General error in newvalue items loop: %s" % e) continue try: data = json.dumps(newvalue) except json.decoder.JSONDecodeError as e: - self.logger.info("[WARNING] JsonDecodeError: %s" % e) + print("[WARNING] JsonDecodeError: %s" % e) data = newvalue except json.decoder.JSONDecodeError as e: - self.logger.info("[WARNING] Failed JSON replacement for OpenAPI keys (2) {e}") + print("[WARNING] Failed JSON replacement for OpenAPI keys (2) {e}") except Exception as e: - self.logger.info(f"[WARNING] Failed as an exception (2): {e}") - - #if isinstance(data, str): - # tmpdata = json.dumps(data) - # self.logger.info(tmpdata) - # foundvalue = re.findall(".*?(${\w+})", tmpdata, re.MULTILINE) - # self.logger.info("FOUND: %s", foundvalue) + print(f"[WARNING] Failed as an exception (2): {e}") return data @@ -1912,7 +1873,7 @@ class AppBase: return "", parameter["value"], is_loop def run_validation(sourcevalue, check, destinationvalue): - self.logger.info("Checking %s %s %s" % (sourcevalue, check, destinationvalue)) + print("[DEBUG] Checking %s %s %s" % (sourcevalue, check, destinationvalue)) if check == "=" or check.lower() == "equals": if str(sourcevalue).lower() == str(destinationvalue).lower(): @@ -1964,7 +1925,7 @@ class AppBase: continue if item.strip() in sourcevalue: - self.logger.info("[INFO] Found %s in %s" % (item, sourcevalue)) + print("[INFO] Found %s in %s" % (item, sourcevalue)) return True return False @@ -1975,7 +1936,7 @@ class AppBase: return True except AttributeError as e: - self.logger.error("[WARNING] Condition larger than failed with values %s and %s: %s" % (sourcevalue, destinationvalue, e)) + print("[WARNING] Condition larger than failed with values %s and %s: %s" % (sourcevalue, destinationvalue, e)) return False elif check.lower() == "smaller than" or check.lower() == "less than": try: @@ -1984,13 +1945,13 @@ class AppBase: return True except AttributeError as e: - self.logger.error("[WARNING] Condition smaller than failed with values %s and %s: %s" % (sourcevalue, destinationvalue, e)) + print("[WARNING] Condition smaller than failed with values %s and %s: %s" % (sourcevalue, destinationvalue, e)) return False elif check.lower() == "re" or check.lower() == "matches regex": try: found = re.search(destinationvalue, sourcevalue) except re.error as e: - self.logger.info("[WARNING] Regex error in condition: %s" % e) + print("[WARNING] Regex error in condition: %s" % e) return False if found == None: @@ -1998,7 +1959,7 @@ class AppBase: return True else: - self.logger.info("Condition: can't handle %s yet. Setting to true" % check) + print("[DEBUG] Condition: can't handle %s yet. Setting to true" % check) return False @@ -2219,7 +2180,7 @@ class AppBase: #self.logger.info(action["parameters"]) # This seems redundant now - self.logger.info("Pre parameters") + self.logger.info("[DEBUG] Pre parameters") for parameter in newparams: action["parameters"].append(parameter) @@ -2241,7 +2202,7 @@ class AppBase: # Multi_parameter has the data for each. variable minlength = 0 - self.logger.info("Pre-loading parameters") + self.logger.info("[DEBUG] Pre-loading parameters") multi_parameters = json.loads(json.dumps(params)) multiexecution = False multi_execution_lists = [] @@ -2550,7 +2511,7 @@ class AppBase: if not multiexecution: # Runs a single iteration here new_params = self.validate_unique_fields(params) - self.logger.info(f"Returned with newparams of length {len(new_params)}") + self.logger.info(f"[DEBUG] Returned with newparams of length {len(new_params)}") if isinstance(new_params, list) and len(new_params) == 1: params = new_params[0] else: @@ -2793,15 +2754,15 @@ class AppBase: if self.action_result["result"] == "": self.action_result["result"] = result - self.logger.debug(f"Executed {action['label']}-{action['id']}")#with result: {result}") + self.logger.debug(f"[DEBUG] Executed {action['label']}-{action['id']}")#with result: {result}") #self.logger.debug(f"Data: %s" % action_result) except TypeError as e: self.logger.info("TypeError issue: %s" % e) self.action_result["status"] = "FAILURE" self.action_result["result"] = "TypeError: %s" % str(e) else: - self.logger.info("Function %s doesn't exist?" % action["name"]) - self.logger.error(f"App {self.__class__.__name__}.{action['name']} is not callable") + self.logger.info("[DEBUG] Function %s doesn't exist?" % action["name"]) + self.logger.error(f"[ERROR] App {self.__class__.__name__}.{action['name']} is not callable") self.action_result["status"] = "FAILURE" self.action_result["result"] = "Function %s is not callable." % actionname @@ -2843,8 +2804,9 @@ class AppBase: app = cls(redis=None, logger=logger, console_logger=logger) if isinstance(action, str): - self.logger.info("Normal execution. Action is a string.") + print("[DEBUG] Normal execution. Action is a string.") elif isinstance(action, object): + print("[DEBUG] OBJECT execution. Action is NOT a string.") app.action = action try: diff --git a/backend/app_sdk/build.sh b/backend/app_sdk/build.sh index 966bd8cc..5a0fbcb9 100644 --- a/backend/app_sdk/build.sh +++ b/backend/app_sdk/build.sh @@ -3,7 +3,7 @@ ### DEFAULT NAME=shuffle-app_sdk -VERSION=0.9.21 +VERSION=0.9.23 docker rmi docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION --force docker build . -f Dockerfile -t frikky/shuffle:app_sdk -t frikky/$NAME:$VERSION -t docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION -t ghcr.io/frikky/$NAME:$VERSION diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 960ac02a..836ab746 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -23,7 +23,7 @@ require ( github.com/docker/go-units v0.4.0 // indirect github.com/elastic/go-elasticsearch/v7 v7.13.1 // indirect github.com/frikky/kin-openapi v0.39.0 - github.com/frikky/shuffle-shared v0.1.12 + github.com/frikky/shuffle-shared v0.1.13 github.com/fsouza/go-dockerclient v1.7.2 github.com/ghodss/yaml v1.0.0 github.com/go-git/go-billy/v5 v5.0.0 diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 53b58fb4..7dab4a4d 100644 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -1014,7 +1014,8 @@ func handleInfo(resp http.ResponseWriter, request *http.Request) { CreatorOrg: org.CreatorOrg, Image: org.Image, }) - // Role: "admin", + } else { + log.Printf("[WARNING] Failed to get org %s for user %s", item, userInfo.Username) } } @@ -1036,7 +1037,7 @@ func handleInfo(resp http.ResponseWriter, request *http.Request) { returnData, err := json.Marshal(returnValue) if err != nil { - log.Printf("[WARNING] Failed marshalling info: %s", err) + log.Printf("[WARNING] Failed marshalling info in handleinfo: %s", err) resp.WriteHeader(401) resp.Write([]byte(`{"success": false}`)) return diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index 8539dbd3..a4eb0bca 100644 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -771,10 +771,11 @@ func handleWorkflowQueue(resp http.ResponseWriter, request *http.Request) { } //log.Printf("Actionresult unmarshal: %s", string(body)) + log.Printf("[DEBUG] Got workflow result from %s of length %d", request.RemoteAddr, len(body)) err = shuffle.ValidateNewWorkerExecution(body) if err == nil { resp.WriteHeader(200) - resp.Write([]byte(fmt.Sprintf(`{"success": true, "reason": "Success"}`))) + resp.Write([]byte(fmt.Sprintf(`{"success": true, "reason": "success"}`))) return } else { //log.Printf("[WARNING] Handling other execution variant: %s", err) @@ -1574,7 +1575,7 @@ func handleExecution(id string, workflow shuffle.Workflow, request *http.Request newParams := []shuffle.WorkflowAppActionParameter{} if strings.ToLower(curAuth.Type) == "oauth2" { - log.Printf("\n\nShould replace auth parameters!!!\n\n") + log.Printf("[DEBUG] Should replace auth parameters (Oauth2)") for _, param := range curAuth.Fields { if param.Key == "expiration" { @@ -1588,7 +1589,7 @@ func handleExecution(id string, workflow shuffle.Workflow, request *http.Request } for _, param := range action.Parameters { - log.Printf("Param: %#v", param) + //log.Printf("Param: %#v", param) if param.Configuration { continue } diff --git a/docker-compose.yml b/docker-compose.yml index 069fedad..09f810e1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '3' services: frontend: - #build: ./frontend + build: ./frontend image: ghcr.io/frikky/shuffle-frontend:nightly container_name: shuffle-frontend hostname: shuffle-frontend @@ -16,7 +16,7 @@ services: depends_on: - backend backend: - #build: ./backend + build: ./backend image: ghcr.io/frikky/shuffle-backend:nightly container_name: shuffle-backend hostname: ${BACKEND_HOSTNAME} diff --git a/frontend/src/components/Header.js b/frontend/src/components/Header.js index a25b8679..449c057b 100644 --- a/frontend/src/components/Header.js +++ b/frontend/src/components/Header.js @@ -350,7 +350,7 @@ const Header = props => { handleClose() handleClickLogout() }}> - Logout +  Logout @@ -455,7 +455,10 @@ const Header = props => { }} value={userdata.active_org.id} fullWidth - style={{marginTop: 5, backgroundColor: theme.palette.surfaceColor, marginRight: 15, color: "white", height: 50, width: 200}} + style={{zIndex: 10012, marginTop: 5, backgroundColor: theme.palette.surfaceColor, marginRight: 15, color: "white", height: 50, width: 200}} + MenuProps={{ + style: {zIndex: 10012} + }} onChange={(e) => { handleClickChangeOrg(e.target.value) }} @@ -473,7 +476,7 @@ const Header = props => { {data.name} return ( - +
    diff --git a/frontend/src/components/Oauth2Auth.jsx b/frontend/src/components/Oauth2Auth.jsx index 0c680713..34ab2bfc 100644 --- a/frontend/src/components/Oauth2Auth.jsx +++ b/frontend/src/components/Oauth2Auth.jsx @@ -13,9 +13,10 @@ const AuthenticationOauth2 = (props) => { const [defaultConfigSet, setDefaultConfigSet] = React.useState(authenticationType.client_id !== undefined && authenticationType.client_id !== null && authenticationType.client_id.length > 0 && authenticationType.client_secret !== undefined && authenticationType.client_secret !== null && authenticationType.client_secret.length > 0) const [clientId, setClientId] = React.useState(defaultConfigSet ? authenticationType.client_id : "") const [clientSecret, setClientSecret] = React.useState(defaultConfigSet ? authenticationType.client_secret : "") + const [oauthUrl, setOauthUrl] = React.useState("") const [buttonClicked, setButtonClicked] = React.useState(false) - const [manuallyConfigure, setManuallyConfigure] = React.useState(false) + const [manuallyConfigure, setManuallyConfigure] = React.useState(defaultConfigSet ? false : true) const [authenticationOption, setAuthenticationOptions] = React.useState({ app: JSON.parse(JSON.stringify(selectedApp)), fields: {}, @@ -31,7 +32,7 @@ const AuthenticationOauth2 = (props) => { return null } - const handleOauth2Request = (client_id, client_secret) => { + const handleOauth2Request = (client_id, client_secret, oauth_url) => { setButtonClicked(true) //if (authenticationType.type === "oauth2" && authenticationType.redirect_uri !== undefined && authenticationType.redirect_uri !== null) { // These are test credentials @@ -40,17 +41,26 @@ const AuthenticationOauth2 = (props) => { const authentication_url = authenticationType.token_uri - const resources = "UserAuthenticationMethod.ReadWrite.All" + var resources = "" + console.log("SCOPES: ", resources) if (authenticationType.scope !== undefined && authenticationType.scope !== null) { - console.log("EDIT SCOPE!") + resources = authenticationType.scope.join(",") } - console.log(window.location) - //const isCloud = window.location.host === "localhost:3002" || window.location.host === "shuffler.io" - const redirectUri = `${window.location.protocol}//${window.location.host}/set_authentication` - const state = `workflow_id%3D${workflow.id}%26reference_action_id%3d${selectedAction.app_id}%26app_name%3d${selectedAction.app_name}%26app_id%3d${selectedAction.app_id}%26app_version%3d${selectedAction.app_version}%26authentication_url%3d${authentication_url}%26scope%3d${resources}%26client_id%3d${client_id}%26client_secret%3d${client_secret}` + resources = ["AaaServer.profile.READ"] - const url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&scope=${resources}&state=${state}` + console.log("SCOPES2: ", resources) + const redirectUri = `${window.location.protocol}//${window.location.host}/set_authentication` + var state = `workflow_id%3D${workflow.id}%26reference_action_id%3d${selectedAction.app_id}%26app_name%3d${selectedAction.app_name}%26app_id%3d${selectedAction.app_id}%26app_version%3d${selectedAction.app_version}%26authentication_url%3d${authentication_url}%26scope%3d${resources}%26client_id%3d${client_id}%26client_secret%3d${client_secret}` + if (oauth_url !== undefined && oauth_url !== null && oauth_url.length > 0) { + state += `%26oauth_url%3d${oauth_url}` + console.log("ADDING OAUTH2 URL: ", state) + } + const url = `${authenticationType.redirect_uri}?client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&scope=${resources}&prompt=consent&state=${state}` + + //const url = `https://accounts.zoho.com/oauth/v2/auth?response_type=code&client_id=${client_id}&scope=AaaServer.profile.Read&redirect_uri=${redirectUri}&prompt=consent` + console.log("Full URI: ", url) + console.log("Redirect Uri: ", redirectUri) // &resource=https%3A%2F%2Fgraph.microsoft.com& // FIXME: Awful, but works for prototyping @@ -64,14 +74,11 @@ const AuthenticationOauth2 = (props) => { var open = true const timer = setInterval(() => { if (newwin.closed) { + setButtonClicked(false) clearInterval(timer); //alert('"Secure Payment" window closed!'); getAppAuthentication(true, true) - setTimeout(() => { - console.log("APPAUTH: ", appAuthentication) - setAuthenticationModalOpen(false) - }, 1500) } }, 1000); //do { @@ -206,6 +213,75 @@ const AuthenticationOauth2 = (props) => { {!manuallyConfigure ? null : + {selectedApp.authentication.parameters.map((data, index) => { + //console.log(data, index) + if (data.name === "client_id" || data.name === "client_secret") { + return null + } + + if (data.name !== "url") { + return null + } + + if (oauthUrl.length === 0) { + setOauthUrl(data.value) + } + + return ( +
    + + {data.name} + + {data.schema !== undefined && data.schema !== null && data.schema.type === "bool" ? + + : + { + authenticationOption.fields[data.name] = event.target.value + console.log("Setting oauth url") + setOauthUrl(event.target.value) + //const [oauthUrl, setOauthUrl] = React.useState("") + }} + /> + } +
    + ) + })} { }
    - : null} + : + null + }
    ) diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 8c4558db..b6029cd5 100644 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -1227,6 +1227,7 @@ const AngularWorkflow = (props) => { if (selectedApp.authentication.required) { //console.log("App requires auth!!") // Setup auth here :) + var appUpdates = false const authenticationOptions = [] var findAuthId = "" if (selectedAction.authentication_id !== null && selectedAction.authentication_id !== undefined && selectedAction.authentication_id.length > 0) { @@ -1261,6 +1262,7 @@ const AngularWorkflow = (props) => { console.log("Setting auth at: ", workflow.actions[key], item.id) workflow.actions[key].selectedAuthentication = item workflow.actions[key].authentication_id = item.id + appUpdates = true //if (workflow.actions[key].selectedAuthentication === undefined || workflow.actions[key].selectedAuthentication === null || workflow.actions[key].selectedAuthentication.length === 0) { // console.log("Setting inner auth: ", workflow.actions[key]) //} @@ -1282,12 +1284,16 @@ const AngularWorkflow = (props) => { selectedAction.selectedAuthentication = {} } - setSelectedAction(selectedAction) - setWorkflow(workflow) - saveWorkflow(workflow) - //for (var key in - alert.info("Added and updated authentication!") + if (appUpdates === true) { + setAuthenticationModalOpen(false) + setSelectedAction(selectedAction) + setWorkflow(workflow) + saveWorkflow(workflow) + alert.info("Added and updated authentication!") + } else { + alert.error("Failed to find new authentication - did it work?") + } } else { alert.info("No authentication to update") } @@ -2098,6 +2104,7 @@ const AngularWorkflow = (props) => { { "type": "oauth2", "redirect_uri": curapp.authentication.redirect_uri, + "refresh_uri": curapp.authentication.refresh_uri, "token_uri": curapp.authentication.token_uri, "scope": curapp.authentication.scope, "client_id": curapp.authentication.client_id, @@ -2946,7 +2953,7 @@ const AngularWorkflow = (props) => { //console.log(event.keyCode) switch( event.keyCode ) { case 27: - console.log("ESCAPE") + //console.log("ESCAPE") if (configureWorkflowModalOpen === true) { setConfigureWorkflowModalOpen(false) } @@ -9271,7 +9278,7 @@ const AngularWorkflow = (props) => { // This whole part is redundant. Made it part of Arguments instead. - console.log(selectedApp) + //console.log(selectedApp) const authenticationModal = authenticationModalOpen ? { const actionNonBodyRequest = ["GET", "HEAD", "DELETE", "CONNECT"] const actionBodyRequest = ["POST", "PUT", "PATCH",] //const authenticationOptions = ["No authentication", "API key", "Bearer auth", "Basic auth", "JWT", "Oauth2"] - const authenticationOptions = ["No authentication", "API key", "Bearer auth", "Basic auth"]//, "Oauth2"] + const authenticationOptions = ["No authentication", "API key", "Bearer auth", "Basic auth", "Oauth2"] const apikeySelection = ["Header", "Query",] const [name, setName] = useState(""); @@ -222,7 +222,7 @@ const AppCreator = (props) => { const [parameterName, setParameterName] = useState(""); const [parameterLocation, setParameterLocation] = useState(apikeySelection.length > 0 ? apikeySelection[0] : ""); const [refreshUrl, setRefreshUrl] = useState(""); - const [oauth2Scopes, setOauth2Scopes] = useState(["google.com"]); + const [oauth2Scopes, setOauth2Scopes] = useState(["OAUTH2.SCOPE.HERE"]); const [projectCategories, setProjectCategories] = useState([]); const [selectedCategory, setSelectedCategory] = useState(""); @@ -949,6 +949,10 @@ const AppCreator = (props) => { if (value.flow.authorizationCode.refreshUrl !== undefined) { setRefreshUrl(value.flow.authorizationCode.refreshUrl) } + if (value.flow.authorizationCode.scopes !== undefined && value.flow.authorizationCode.scopes !== null && value.flow.authorizationCode.scopes.length > 0) { + setOauth2Scopes(value.flow.authorizationCode.scopes) + } + } else if (key === "ApiKeyAuth") { setAuthenticationOption("API key") @@ -1316,7 +1320,7 @@ const AppCreator = (props) => { data.paths[item.url][item.method.toLowerCase()].parameters.push(newitem) } else { - console.log("Nothing to append?") + //console.log("Nothing to append?") } // https://swagger.io/docs/specification/describing-request-body/file-upload/ @@ -1438,17 +1442,19 @@ const AppCreator = (props) => { "authorizationUrl": newparamName, "tokenUrl": parameterLocation, "refreshUrl": refreshUrl, - "scopes": [], + "scopes": oauth2Scopes === undefined || oauth2Scopes === null ? [] : oauth2Scopes, }, }, } - if (oauth2Scopes.scopes > 0) { - for (var key in oauth2Scopes) { - const scope = oauth2Scopes[key] - data.components.securitySchemes["Oauth2"]["flow"]["authorizationCode"]["scopes"].push(scope) - } - } + console.log("SCOPES: ", oauth2Scopes) + + //if (oauth2Scopes.scopes > 0) { + // for (var key in oauth2Scopes) { + // const scope = oauth2Scopes[key] + // data.components.securitySchemes["Oauth2"]["flow"]["authorizationCode"]["scopes"].push(scope) + // } + //} } if (setExtraAuth.length > 0) { @@ -1747,12 +1753,12 @@ const AppCreator = (props) => { const oauth2Auth = authenticationOption === "Oauth2" ?
    Oauth2 authentication - + Base Authorization URL { }, }} /> - + Token URL { }, }} /> - + Refresh-token URL { }, }} /> + + Scopes + + { + oauth2Scopes.push(chip) + console.log(oauth2Scopes) + setOauth2Scopes(oauth2Scopes) + }} + onDelete={(chip, index) => { + oauth2Scopes.splice(index, 1) + console.log(oauth2Scopes) + setOauth2Scopes(oauth2Scopes) + }} + />
    : null @@ -2429,26 +2460,46 @@ const AppCreator = (props) => { }} onBlur={event => { var parsedurl = event.target.value - console.log("URL: ", parsedurl) + //console.log("URL: ", parsedurl) + if (parsedurl.includes(" ")) { + parsedurl = parsedurl.replaceAll(" ", " ") + } + + if (parsedurl.includes(" ")) { + parsedurl = parsedurl.replaceAll(" ", " ") + } + + if (parsedurl.includes("[") && parsedurl.includes("]")) { + //console.log("REPLACE1") + parsedurl = parsedurl.replaceAll("[", "{") + parsedurl = parsedurl.replaceAll("]", "}") + } + if (parsedurl.includes("<") && parsedurl.includes(">")) { - console.log("REPLACE") + //console.log("REPLACE2") parsedurl = parsedurl.replaceAll("<", "{") parsedurl = parsedurl.replaceAll(">", "}") } + //console.log("URL2: ", parsedurl) if (parsedurl.startsWith("PUT ") || parsedurl.startsWith("GET ") ||parsedurl.startsWith("POST ") || parsedurl.startsWith("DELETE ") ||parsedurl.startsWith("PATCH ") || parsedurl.startsWith("CONNECT ")) { + const tmp = parsedurl.split(" ") if (tmp.length > 1) { - parsedurl = tmp[1] + parsedurl = tmp[1].trim() setActionField("url", parsedurl) setCurrentActionMethod(tmp[0].toUpperCase()) setActionField("method", tmp[0].toUpperCase()) } + + console.log("URL3: ", parsedurl) setUpdate(Math.random()) } else if (parsedurl.startsWith("curl")) { + console.log("URL4: ", parsedurl) + const request = parseCurl(event.target.value) if (request !== event.target.value && request.method !== undefined && request.method !== null) { if (request.method.toUpperCase() !== currentAction.Method) { diff --git a/frontend/src/views/Apps.jsx b/frontend/src/views/Apps.jsx index 6f3d1801..61f8ebdb 100644 --- a/frontend/src/views/Apps.jsx +++ b/frontend/src/views/Apps.jsx @@ -480,7 +480,7 @@ const Apps = (props) => { color: "white", borderRadius: 5, backgroundColor: surfaceColor, - display: "flex", + //display: "flex", marginBottom: 10, overflow: "hidden", } @@ -522,7 +522,7 @@ const Apps = (props) => {
    {isCloud ? null : + {isLoading ? null : + } diff --git a/frontend/src/views/Docs.jsx b/frontend/src/views/Docs.jsx index 7b2a7ed9..e7ac92d8 100644 --- a/frontend/src/views/Docs.jsx +++ b/frontend/src/views/Docs.jsx @@ -139,7 +139,7 @@ const Docs = (props) => { parsedline[1] = parsedline[1].replaceAll(")", "") parsedline[1] = parsedline[1].replaceAll("(", "") parsedline[1] = parsedline[1].trim() - console.log(parsedline[0], parsedline[1]) + //console.log(parsedline[0], parsedline[1]) innerTocLines.push({ "text": parsedline[0], @@ -205,12 +205,12 @@ const Docs = (props) => { // Fix location.. if (element.innerHTML.toLowerCase() === name) { - console.log(element.offsetTop) - //element.scrollIntoView({behavior: "smooth"}) - element.scrollTo({ - top: element.offsetTop+offset, - behavior: "smooth" - }) + //console.log(element.offsetTop) + element.scrollIntoView({behavior: "smooth"}) + //element.scrollTo({ + // top: element.offsetTop+offset, + // behavior: "smooth" + //}) found = true //element.scrollTo({ // top: element.offsetTop-100, @@ -222,7 +222,7 @@ const Docs = (props) => { // H# if (!found) { elements = parent.getElementsByTagName('h3') - console.log(name) + //console.log("NAMe: ", name) found = false for (key in elements) { const element = elements[key] @@ -232,11 +232,11 @@ const Docs = (props) => { // Fix location.. if (element.innerHTML.toLowerCase() === name) { - //element.scrollIntoView({behavior: "smooth"}) - element.scrollTo({ - top: element.offsetTop-offset, - behavior: "smooth" - }) + element.scrollIntoView({behavior: "smooth"}) + //element.scrollTo({ + // top: element.offsetTop-offset, + // behavior: "smooth" + //}) found = true //element.scrollTo({ // top: element.offsetTop-100, @@ -392,7 +392,7 @@ const Docs = (props) => { {itemMatching && tocLines !== null && tocLines !== undefined && tocLines.length > 0 ?
    {tocLines.map((data, index) => { - console.log(data) + //console.log(data) return ( {}}> diff --git a/frontend/src/views/SetAuthentication.jsx b/frontend/src/views/SetAuthentication.jsx index 5fcd0f2b..17849a28 100644 --- a/frontend/src/views/SetAuthentication.jsx +++ b/frontend/src/views/SetAuthentication.jsx @@ -91,6 +91,10 @@ const SetAuthentication = (props) => { if (query[0] === "client_secret") { appAuthData.fields.push({"key": "client_secret", "value": query[1]}) } + + if (query[0] === "oauth_url") { + appAuthData.fields.push({"key": "oauth_url", "value": query[1]}) + } } } diff --git a/functions/onprem/orborus/build.sh b/functions/onprem/orborus/build.sh index 3beae1f3..98bb5f27 100644 --- a/functions/onprem/orborus/build.sh +++ b/functions/onprem/orborus/build.sh @@ -1,5 +1,5 @@ NAME=shuffle-orborus -VERSION=0.9.14 +VERSION=0.9.23 echo "Running docker build with $NAME:$VERSION" #docker rmi frikky/shuffle:$NAME --force From b15ac9f4d574ac013255a43d72909ff0ee60b1d2 Mon Sep 17 00:00:00 2001 From: frikky Date: Wed, 13 Oct 2021 02:30:05 +0200 Subject: [PATCH 113/117] Rolled back compose changes --- docker-compose.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docker-compose.yml b/docker-compose.yml index 09f810e1..069fedad 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '3' services: frontend: - build: ./frontend + #build: ./frontend image: ghcr.io/frikky/shuffle-frontend:nightly container_name: shuffle-frontend hostname: shuffle-frontend @@ -16,7 +16,7 @@ services: depends_on: - backend backend: - build: ./backend + #build: ./backend image: ghcr.io/frikky/shuffle-backend:nightly container_name: shuffle-backend hostname: ${BACKEND_HOSTNAME} From 66ed72b241fd922030cf14a8ec95ef2075c52a61 Mon Sep 17 00:00:00 2001 From: frikky Date: Thu, 14 Oct 2021 02:06:24 +0200 Subject: [PATCH 114/117] #168: Got successful Oauth2 implementation with Gmail OpenAPIls --- backend/app_sdk/app_base.py | 14 +- backend/go-app/go.mod | 2 +- backend/go-app/main.go | 27 ++- docker-compose.yml | 4 +- frontend/src/App.jsx | 2 +- frontend/src/components/Oauth2Auth.jsx | 73 +++++-- frontend/src/components/ParsedAction.jsx | 2 +- frontend/src/views/AngularWorkflow.jsx | 31 +-- frontend/src/views/AppCreator.jsx | 243 ++++++++++++++++------- frontend/src/views/Apps.jsx | 6 +- frontend/src/views/Workflows.jsx | 180 +++++++++++------ 11 files changed, 406 insertions(+), 178 deletions(-) diff --git a/backend/app_sdk/app_base.py b/backend/app_sdk/app_base.py index 36a795dc..b725ae9f 100644 --- a/backend/app_sdk/app_base.py +++ b/backend/app_sdk/app_base.py @@ -1378,7 +1378,7 @@ class AppBase: except IndexError: print("[DEBUG] INDEXERROR: ", parsersplit[outercnt]) #ret = innervalue - ret, tmp_loop = recurse_json(innervalue, parsersplit[outercnt:]) + ret, tmp_loop = recurse_json(basejson[i], parsersplit[outercnt:]) newvalue.append(ret) @@ -1738,7 +1738,7 @@ class AppBase: #self.logger.debug(f"\n\nHandle static data with JSON: {data}\n\n") #self.logger.info("STATIC PARSED: %s" % actualitem) if len(actualitem) > 0: - self.logger.info("ACTUAL: ", actualitem) + #self.logger.info("[DEACTUAL: ", actualitem) for replace in actualitem: try: to_be_replaced = replace[0] @@ -1774,7 +1774,7 @@ class AppBase: #self.logger.info("VALUE: %s" % parameter["value"]) if parameter["variant"] == "WORKFLOW_VARIABLE": - self.logger.info("Handling workflow variable") + self.logger.info("[DEBUG] Handling workflow variable") found = False try: for item in fullexecution["workflow"]["workflow_variables"]: @@ -2533,7 +2533,8 @@ class AppBase: break except TypeError as e: newres = "" - errorstring = "%s" % e + self.logger.info(f"[DEBUG] Got exec error: {errorstring}") + errorstring = f"{e}" if "got an unexpected keyword argument" in errorstring: fieldsplit = errorstring.split("'") if len(fieldsplit) > 1: @@ -2541,7 +2542,7 @@ class AppBase: try: del params[field] - self.logger.info("Removed field invalid field %s" % field) + self.logger.info("[WARNING] Removed field invalid field %s" % field) except KeyError: break else: @@ -2774,7 +2775,8 @@ class AppBase: try: self.action_result["result"] = json.dumps({ "success": False, - "reason": f"Request error - failing silently. Details: {e}" + "reason": f"Request error - failing silently. Details in detail section", + "details": f"{e}", }) except json.decoder.JSONDecodeError as e: self.action_result["result"] = f"Request error: {e}" diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 836ab746..120e93c5 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -23,7 +23,7 @@ require ( github.com/docker/go-units v0.4.0 // indirect github.com/elastic/go-elasticsearch/v7 v7.13.1 // indirect github.com/frikky/kin-openapi v0.39.0 - github.com/frikky/shuffle-shared v0.1.13 + github.com/frikky/shuffle-shared v0.1.14 github.com/fsouza/go-dockerclient v1.7.2 github.com/ghodss/yaml v1.0.0 github.com/go-git/go-billy/v5 v5.0.0 diff --git a/backend/go-app/main.go b/backend/go-app/main.go index 7dab4a4d..ae80d6d5 100644 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -1985,8 +1985,10 @@ func handleWebhookCallback(resp http.ResponseWriter, request *http.Request) { location := strings.Split(request.URL.String(), "/") var hookId string + var queries string if location[1] == "api" { if len(location) <= 4 { + log.Printf("[INFO] Couldn't handle location. Too short in webhook: %d", len(location)) resp.WriteHeader(401) resp.Write([]byte(`{"success": false}`)) return @@ -1995,10 +1997,20 @@ func handleWebhookCallback(resp http.ResponseWriter, request *http.Request) { hookId = location[4] } + if strings.Contains(hookId, "?") { + splitter := strings.Split(hookId, "?") + hookId = splitter[0] + + if len(splitter) > 1 { + queries = splitter[1] + } + } + // ID: webhook_ if len(hookId) != 44 { + log.Printf("[INFO] Couldn't handle hookId. Too short in webhook: %d", len(hookId)) resp.WriteHeader(401) - resp.Write([]byte(`{"success": false, "message": "ID not valid"}`)) + resp.Write([]byte(`{"success": false, "reason": "Hook ID not valid"}`)) return } @@ -2022,19 +2034,19 @@ func handleWebhookCallback(resp http.ResponseWriter, request *http.Request) { if hook.Status == "stopped" { log.Printf("[WARNING] Not running %s because hook status is stopped", hook.Id) resp.WriteHeader(401) - resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "The webhook isn't running. Click start to start it"}`))) + resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "The webhook isn't running. Is it running?"}`))) return } if len(hook.Workflows) == 0 { - log.Printf("Not running because hook isn't connected to any workflows") + log.Printf("[DEBUG] Not running because hook isn't connected to any workflows") resp.WriteHeader(401) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "No workflows are defined"}`))) return } if hook.Environment == "cloud" { - log.Printf("This should trigger in the cloud. Duplicate action allowed onprem.") + log.Printf("[DEBUG] This should trigger in the cloud. Duplicate action allowed onprem.") } // Check auth @@ -2050,12 +2062,16 @@ func handleWebhookCallback(resp http.ResponseWriter, request *http.Request) { body, err := ioutil.ReadAll(request.Body) if err != nil { - log.Printf("Body data error: %s", err) + log.Printf("[DEBUG] Body data error: %s", err) resp.WriteHeader(401) resp.Write([]byte(`{"success": false}`)) return } + if len(queries) > 0 && len(body) == 0 { + body = []byte(queries) + } + //log.Printf("BODY: %s", parsedBody) // This is a specific fix for MSteams and may fix other things as well @@ -5862,7 +5878,6 @@ func initHandlers() { r.HandleFunc("/api/v1/triggers/gmail/register", shuffle.HandleNewGmailRegister).Methods("GET", "OPTIONS") r.HandleFunc("/api/v1/triggers/gmail/getFolders", shuffle.HandleGetGmailFolders).Methods("GET", "OPTIONS") - // FIXME: This should only be cloud. Done locally with ngrok to test //r.HandleFunc("/api/v1/triggers/gmail/routing", handleGmailRouting).Methods("POST", "OPTIONS") r.HandleFunc("/api/v1/triggers/gmail/{key}", shuffle.HandleGetSpecificTrigger).Methods("GET", "OPTIONS") diff --git a/docker-compose.yml b/docker-compose.yml index 069fedad..09f810e1 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '3' services: frontend: - #build: ./frontend + build: ./frontend image: ghcr.io/frikky/shuffle-frontend:nightly container_name: shuffle-frontend hostname: shuffle-frontend @@ -16,7 +16,7 @@ services: depends_on: - backend backend: - #build: ./backend + build: ./backend image: ghcr.io/frikky/shuffle-backend:nightly container_name: shuffle-backend hostname: ${BACKEND_HOSTNAME} diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 72f4464a..c4ef9a57 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -78,7 +78,7 @@ const App = (message, props) => { .then(response => response.json()) .then(responseJson => { if (responseJson.success === true && responseJson.notifications !== null && responseJson.notifications !== undefined && responseJson.notifications.length > 0) { - console.log("RESP: ", responseJson) + //console.log("RESP: ", responseJson) setNotifications(responseJson.notifications) } }) diff --git a/frontend/src/components/Oauth2Auth.jsx b/frontend/src/components/Oauth2Auth.jsx index 34ab2bfc..4fd89ac7 100644 --- a/frontend/src/components/Oauth2Auth.jsx +++ b/frontend/src/components/Oauth2Auth.jsx @@ -2,9 +2,22 @@ import React, {useRef, useState, useEffect, useLayoutEffect} from 'react'; import { useTheme } from '@material-ui/core/styles'; import { v4 as uuidv4 } from 'uuid'; -import { TextField, Drawer, Button, Paper, Grid, Tabs, InputAdornment, Tab, ButtonBase, Tooltip, Select, MenuItem, Divider, Dialog, Modal, DialogActions, DialogTitle, InputLabel, DialogContent, FormControl, IconButton, Menu, Input, FormGroup, FormControlLabel, Typography, Checkbox, Breadcrumbs, CircularProgress, Switch, Fade } from '@material-ui/core'; +import { ListItemText, TextField, Drawer, Button, Paper, Grid, Tabs, InputAdornment, Tab, ButtonBase, Tooltip, Select, MenuItem, Divider, Dialog, Modal, DialogActions, DialogTitle, InputLabel, DialogContent, FormControl, IconButton, Menu, Input, FormGroup, FormControlLabel, Typography, Checkbox, Breadcrumbs, CircularProgress, Switch, Fade } from '@material-ui/core'; import { LockOpen as LockOpenIcon } from '@material-ui/icons'; +const ITEM_HEIGHT = 55 +const ITEM_PADDING_TOP = 8 +const MenuProps = { + PaperProps: { + style: { + maxHeight: ITEM_HEIGHT * 4.5 + ITEM_PADDING_TOP, + minWidth: 500, + maxWidth: 500, + scrollX: "auto", + }, + }, +} + const AuthenticationOauth2 = (props) => { const { saveWorkflow, selectedApp, workflow, selectedAction, authenticationType, getAppAuthentication, appAuthentication, setSelectedAction, setNewAppAuth, setAuthenticationModalOpen} = props; const theme = useTheme(); @@ -15,6 +28,8 @@ const AuthenticationOauth2 = (props) => { const [clientSecret, setClientSecret] = React.useState(defaultConfigSet ? authenticationType.client_secret : "") const [oauthUrl, setOauthUrl] = React.useState("") const [buttonClicked, setButtonClicked] = React.useState(false) + const [selectedScopes, setSelectedScopes] = React.useState([]) + const allscopes = authenticationType.scope !== undefined ? authenticationType.scope: [] const [manuallyConfigure, setManuallyConfigure] = React.useState(defaultConfigSet ? false : true) const [authenticationOption, setAuthenticationOptions] = React.useState({ @@ -32,22 +47,16 @@ const AuthenticationOauth2 = (props) => { return null } - const handleOauth2Request = (client_id, client_secret, oauth_url) => { + const handleOauth2Request = (client_id, client_secret, oauth_url, scopes) => { setButtonClicked(true) - //if (authenticationType.type === "oauth2" && authenticationType.redirect_uri !== undefined && authenticationType.redirect_uri !== null) { - // These are test credentials - //const client_id = "dae24316-4bec-4832-b660-4cba6dc2477b" - //const client_secret = "._Qu3EvYY-OW_D57uy79qwEo.32qD6.l0z" - - const authentication_url = authenticationType.token_uri + console.log("SCOPES: ", scopes) var resources = "" - console.log("SCOPES: ", resources) - if (authenticationType.scope !== undefined && authenticationType.scope !== null) { - resources = authenticationType.scope.join(",") + if (scopes !== undefined && scopes !== null & scopes.length > 0) { + resources = scopes.join(",") } - resources = ["AaaServer.profile.READ"] + const authentication_url = authenticationType.token_uri console.log("SCOPES2: ", resources) const redirectUri = `${window.location.protocol}//${window.location.host}/set_authentication` @@ -68,7 +77,7 @@ const AuthenticationOauth2 = (props) => { // How can we properly try-catch without breaks on error? try { - var newwin = window.open(url, "", "width=400,height=200") + var newwin = window.open(url, "", "width=800,height=600") //console.log(newwin) var open = true @@ -176,6 +185,18 @@ const AuthenticationOauth2 = (props) => { } + const handleScopeChange = (event) => { + const { + target: { value }, + } = event; + + console.log("VALUE: ", value) + + // On autofill we get a the stringified value. + setSelectedScopes(typeof value === 'string' ? value.split(',') : value) + } + + if (authenticationOption.label === null || authenticationOption.label === undefined) { authenticationOption.label = selectedApp.name+" authentication" } @@ -282,6 +303,29 @@ const AuthenticationOauth2 = (props) => {
    ) })} + {allscopes.length === 0 ? null : + } + renderValue={(selected) => selected.join(', ')} + MenuProps={MenuProps} + > + {allscopes.map((data, index) => { + return ( + + -1} /> + + + ) + })} + + } { variant="contained" fullWidth onClick={() => { - //setAuthenticationModalOpen(false) - handleOauth2Request(clientId, clientSecret, oauthUrl) + handleOauth2Request(clientId, clientSecret, oauthUrl, selectedScopes) }} color="primary" > diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index e57feade..937f592e 100644 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -1616,7 +1616,7 @@ const ParsedAction = (props) => { selectedAction.authentication_id = "" for (var key in selectedAction.parameters) { - console.log(selectedAction.parameters[key]) + //console.log(selectedAction.parameters[key]) if (selectedAction.parameters[key].configuration) { selectedAction.parameters[key].value = "" } diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index b6029cd5..274c21bf 100644 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -31,8 +31,8 @@ import { GetParsedPaths } from "./Apps.jsx"; import ConfigureWorkflow from '../components/ConfigureWorkflow.jsx'; import AuthenticationOauth2 from "../components/Oauth2Auth.jsx"; import ParsedAction from '../components/ParsedAction.jsx'; -import Scroll from 'react-scroll' -import { Element as ScrollElement, animateScroll as scroll, scrollSpy, scroller } from 'react-scroll' +//import Scroll from 'react-scroll' +//import { Element as ScrollElement, animateScroll as scroll, scrollSpy, scroller } from 'react-scroll' const surfaceColor = "#27292D" const inputColor = "#383B40" @@ -1008,6 +1008,7 @@ const AngularWorkflow = (props) => { if (hasSaved === false) { //alert.error("You might have forgotten to save before executing.") //const saveWorkflow = (curworkflow) => { + //setExecutionRunning(true) setExecutionRequestStarted(true) saveWorkflow(workflow, executionArgument, startNode) console.log("FIXME: Might have forgotten to save before executing.") @@ -3013,10 +3014,10 @@ const AngularWorkflow = (props) => { } break; case 83: - if (previouskey === 17) { - event.preventDefault() - saveWorkflow() - } + //if (previouskey === 17) { + // event.preventDefault() + // saveWorkflow() + //} break; case 70: //if (previouskey === 17) { @@ -3158,7 +3159,7 @@ const AngularWorkflow = (props) => { setEstablished(true) // Validate if the node is just a node lol - console.log("CY: ", cy) + //console.log("CY: ", cy) //console.log("CY: ", cy.edgehandles()) //try { cy.edgehandles({ @@ -5796,7 +5797,7 @@ const AngularWorkflow = (props) => { const url = `https://accounts.google.com/o/oauth2/v2/auth?access_type=offline&prompt=consent&client_id=${client_id}&redirect_uri=${redirectUri}&response_type=code&scope=${scopes}&state=workflow_id%3D${props.match.params.key}%26trigger_id%3D${selectedTrigger.id}%26username%3D${username}%26type%3Dgmail%26start%3d${startnode}` console.log("URL: ", url) - var newwin = window.open(url, "", "width=200,height=100") + var newwin = window.open(url, "", "width=800,height=600") // Check whether we got a callback somewhere var id = setInterval(function () { @@ -5871,7 +5872,7 @@ const AngularWorkflow = (props) => { console.log("URL: ", url) - var newwin = window.open(url, "", "width=200,height=100") + var newwin = window.open(url, "", "width=800,height=600") // Check whether we got a callback somewhere var id = setInterval(function () { @@ -9406,10 +9407,10 @@ const AngularWorkflow = (props) => { //console.log("SCROLL IS NOT 0: ", scrollConfig.top) //rightSideActionView.scrollTop = scrollConfig.top setTimeout(() => { - scroller.scrollTo('elements_wrapper', { - containerId: 'rightside_actions', - offset: scrollConfig.top, - }) + //scroller.scrollTo('elements_wrapper', { + // containerId: 'rightside_actions', + // offset: scrollConfig.top, + //}) if (scrollConfig.selected !== undefined && scrollConfig.selected !== null) { const selectedField = document.getElementById(scrollConfig.selected) @@ -9437,15 +9438,15 @@ const AngularWorkflow = (props) => { return (
    - {loadedCheck} -
    ) + // + // } export default AngularWorkflow diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx index b0a906fd..bf0431fb 100644 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -222,7 +222,7 @@ const AppCreator = (props) => { const [parameterName, setParameterName] = useState(""); const [parameterLocation, setParameterLocation] = useState(apikeySelection.length > 0 ? apikeySelection[0] : ""); const [refreshUrl, setRefreshUrl] = useState(""); - const [oauth2Scopes, setOauth2Scopes] = useState(["OAUTH2.SCOPE.HERE"]); + const [oauth2Scopes, setOauth2Scopes] = useState([]); const [projectCategories, setProjectCategories] = useState([]); const [selectedCategory, setSelectedCategory] = useState(""); @@ -396,7 +396,7 @@ const AppCreator = (props) => { const handleGetRef = (parameter, data) => { try { if (parameter === null || parameter["$ref"] === undefined) { - //console.log("$ref not found in getref: ") + //console.log("$ref not found in getref for: ", parameter) return parameter } } catch (e) { @@ -425,12 +425,6 @@ const AppCreator = (props) => { } return newitem - - //console.log("Should get ", parameter["$ref"]) - //const subkeys = parameter["$ref"].split("/") - // setBasedata(data) - - // handleGetReference(parameter["$ref"]) } // Sets the data up as it should be at later points @@ -476,7 +470,18 @@ const AppCreator = (props) => { document.title = "Apps - "+data.info.title if (data.info["x-logo"] !== undefined) { - setFileBase64(data.info["x-logo"]) + + if (data.info["x-logo"].url !== undefined) { + console.log("PARSED LOGO: ", data.info["x-logo"].url) + setFileBase64(data.info["x-logo"].url) + } else { + setFileBase64(data.info["x-logo"]) + } + console.log("") + console.log("") + console.log("LOGO: ", data.info["x-logo"]) + console.log("") + console.log("") } if (data.info.contact !== undefined) { @@ -542,8 +547,11 @@ const AppCreator = (props) => { } if (!allowedfunctions.includes(method.toUpperCase())) { - console.log("Invalid method: ", method, "data: ", methodvalue) - alert.info("Skipped method (not allowed): "+method) + // Typical YAML issue + if (method !== "parameters") { + console.log("Invalid method: ", method, "data: ", methodvalue) + alert.info("Skipped method (not allowed): "+method) + } continue } @@ -552,6 +560,8 @@ const AppCreator = (props) => { tmpname = methodvalue.operationId } + tmpname = tmpname.replaceAll(".", " ") + var newaction = { "name": tmpname, "description": methodvalue.description, @@ -582,10 +592,8 @@ const AppCreator = (props) => { } } - //console.log("Category: - //console.log("Schema is application/json: ", methodvalue) - //console.log("DATA", data) + // Typescript? I think not ;) if (methodvalue["requestBody"] !== undefined) { //console.log("Handle requestbody: ", methodvalue["requestBody"]) if (methodvalue["requestBody"]["content"] !== undefined) { @@ -637,15 +645,13 @@ const AppCreator = (props) => { } } } else { + //console.log("REQUESTBODY: ", methodvalue["requestBody"]["content"]) if (methodvalue["requestBody"]["content"]["example"] !== undefined) { if (methodvalue["requestBody"]["content"]["example"]["example"] !== undefined) { newaction["body"] = methodvalue["requestBody"]["content"]["example"]["example"] //JSON.stringify(tmpobject, null, 2) } - } - - //console.log(methodvalue["requestBody"]["content"]) - if (methodvalue["requestBody"]["content"]["multipart/form-data"] !== undefined) { + } else if (methodvalue["requestBody"]["content"]["multipart/form-data"] !== undefined) { if (methodvalue["requestBody"]["content"]["multipart/form-data"]["schema"] !== undefined && methodvalue["requestBody"]["content"]["multipart/form-data"]["schema"] !== null) { if (methodvalue["requestBody"]["content"]["multipart/form-data"]["schema"]["type"] === "object") { const fieldname = methodvalue["requestBody"]["content"]["multipart/form-data"]["schema"]["properties"]["fieldname"] @@ -655,6 +661,63 @@ const AppCreator = (props) => { } } } + } else { + + var schemas = [] + const content = methodvalue["requestBody"]["content"] + if (content !== undefined && content !== null) { + //console.log("CONTENT: ", content) + for (const [subkey, subvalue] of Object.entries(content)) { + if (subvalue["schema"] !== undefined) { + //console.log("SCHEMA: ", subvalue["schema"]) + if (subvalue["schema"]["$ref"] !== undefined) { + //console.log("SCHEMA FOUND REF!") + + if (!schemas.includes(subvalue["schema"]["$ref"])) { + schemas.push(subvalue["schema"]["$ref"]) + } + } + } else { + console.log("ERROR: couldn't find schema for ", subvalue, method) + } + } + } + + if (schemas.length === 1) { + const parameter = handleGetRef({"$ref": schemas[0]}, data) + + if (parameter.properties !== undefined && parameter["type"] === "object") { + var newbody = {} + for (var propkey in parameter.properties) { + const parsedkey = propkey.replaceAll(" ", "_").toLowerCase() + if (parameter.properties[propkey].type === undefined) { + console.log("Skipping (4): ", parameter.properties[propkey]) + continue + } + + if (parameter.properties[propkey].type === "string") { + if (parameter.properties[propkey].description !== undefined) { + newbody[parsedkey] = parameter.properties[propkey].description + } else { + newbody[parsedkey] = "" + } + } else if (parameter.properties[propkey].type.includes("int") || parameter.properties[propkey].type.includes("uint64")) { + newbody[parsedkey] = 0 + } else if (parameter.properties[propkey].type.includes("boolean")) { + newbody[parsedkey] = false + } else if (parameter.properties[propkey].type.includes("array")) { + newbody[parsedkey] = [] + } else { + console.log("CANT HANDLE JSON TYPE (4)", parameter.properties[propkey].type, parameter.properties[propkey]) + newbody[parsedkey] = [] + } + } + + newaction["body"] = JSON.stringify(newbody, null, 2) + } else { + console.log("CANT HANDLE PARAM: (4) ", parameter.properties) + } + } } } } @@ -696,7 +759,7 @@ const AppCreator = (props) => { for (var propkey in parameter.properties) { const parsedkey = propkey.replaceAll(" ", "_").toLowerCase() if (parameter.properties[propkey].type === undefined) { - console.log("Skipping: ", parameter.properties[propkey]) + console.log("Skipping (1): ", parameter.properties[propkey]) continue } @@ -708,6 +771,13 @@ const AppCreator = (props) => { } } else if (parameter.properties[propkey].type.includes("int")) { newbody[parsedkey] = 0 + } else if (parameter.properties[propkey].type.includes("boolean")) { + newbody[parsedkey] = false + } else if (parameter.properties[propkey].type.includes("array")) { + //console.log("Added empty array. Base is: ", parameter.properties[propkey].type) + + //const parameter = handleGetRef(selectedExample["content"]["application/json"]["schema"], data) + newbody[parsedkey] = [] } else { console.log("CANT HANDLE JSON TYPE ", parameter.properties[propkey].type, parameter.properties[propkey]) newbody[parsedkey] = [] @@ -732,7 +802,7 @@ const AppCreator = (props) => { for (var propkey in parameter.properties) { const parsedkey = propkey.replaceAll(" ", "_").toLowerCase() if (parameter.properties[propkey].type === undefined) { - console.log("Skipping: ", parameter.properties[propkey]) + console.log("Skipping (2): ", parameter.properties[propkey]) continue } @@ -744,6 +814,8 @@ const AppCreator = (props) => { } } else if (parameter.properties[propkey].type.includes("int")) { newbody[parsedkey] = 0 + } else if (parameter.properties[propkey].type.includes("boolean")) { + newbody[parsedkey] = false } else { console.log("CANT HANDLE JSON TYPE (2) ", parameter.properties[propkey].type) newbody[parsedkey] = [] @@ -767,7 +839,7 @@ const AppCreator = (props) => { for (var propkey in parameter.properties) { const parsedkey = propkey.replaceAll(" ", "_").toLowerCase() if (parameter.properties[propkey].type === undefined) { - console.log("Skipping: ", parameter.properties[propkey]) + console.log("Skipping (3): ", parameter.properties[propkey]) continue } @@ -790,7 +862,7 @@ const AppCreator = (props) => { //newaction.example_response = JSON.stringify(parameter.properties, null, 2) } else { //newaction.example_response = parameter.properties - console.log("CANT HANDLE PARAM: (2) ", parameter.properties) + console.log("CANT HANDLE PARAM: (3) ", parameter.properties) } } } @@ -894,6 +966,7 @@ const AppCreator = (props) => { newaction.errors.push("Missing name") } } + newActions.push(newaction) } } @@ -924,33 +997,71 @@ const AppCreator = (props) => { } - console.log("SECURITYSCHEMES: ", securitySchemes) + //console.log("SECURITYSCHEMES: ", securitySchemes) if (securitySchemes !== undefined) { // FIXME: Should add Oauth2 (Microsoft) and JWT (Wazuh) //console.log("SECURITY: ", securitySchemes) //if (Object.entries(securitySchemes) > 1 && var newauth = [] for (const [key, value] of Object.entries(securitySchemes)) { - console.log(key, value) + //console.log(key, value) if (value.scheme === "bearer") { setAuthenticationOption("Bearer auth") setAuthenticationRequired(true) - } else if (key === "Oauth2") { + } else if (key === "Oauth2" || key === "Oauth2c") { //alert.info("Can't handle Oauth2 auth yet.") setAuthenticationOption("Oauth2") setAuthenticationRequired(true) - if (value.flow.authorizationCode.authorizationUrl !== undefined) { - setParameterName(value.flow.authorizationCode.authorizationUrl) - } - if (value.flow.authorizationCode.tokenUrl !== undefined) { - setParameterLocation(value.flow.authorizationCode.tokenUrl) - } - if (value.flow.authorizationCode.refreshUrl !== undefined) { - setRefreshUrl(value.flow.authorizationCode.refreshUrl) - } - if (value.flow.authorizationCode.scopes !== undefined && value.flow.authorizationCode.scopes !== null && value.flow.authorizationCode.scopes.length > 0) { - setOauth2Scopes(value.flow.authorizationCode.scopes) + //console.log("FLOW-1: ", value) + const flowkey = value.flow === undefined ? "flows" : "flow" + //console.log("FLOW: ", value[flowkey]) + const basekey = value[flowkey].authorizationCode !== undefined ? "authorizationCode" : "implicit" + //console.log("FLOW2: ", value[flowkey][basekey]) + if (value[flowkey] !== undefined && value[flowkey][basekey] !== undefined) { + if (value[flowkey][basekey].authorizationUrl !== undefined && parameterName.length === 0) { + setParameterName(value[flowkey][basekey].authorizationUrl) + } + + var tokenUrl = "" + if (value[flowkey][basekey].tokenUrl !== undefined) { + setParameterLocation(value[flowkey][basekey].tokenUrl) + tokenUrl = value[flowkey][basekey].tokenUrl + } else { + setParameterLocation("") + } + + if (value[flowkey][basekey].refreshUrl !== undefined) { + setRefreshUrl(value[flowkey][basekey].refreshUrl) + } else if (tokenUrl.length > 0) { + setRefreshUrl(tokenUrl) + } + + if (value[flowkey][basekey].scopes !== undefined && value[flowkey][basekey].scopes !== null) { + if (value[flowkey][basekey].scopes.length > 0) { + setOauth2Scopes(value[flowkey][basekey].scopes) + } else { + var newscopes = [] + for (let [scopekey, scopevalue] of Object.entries(value[flowkey][basekey].scopes)) { + if (scopekey.startsWith("http")) { + const scopekeysplit = scopekey.split("/") + if (scopekeysplit.length < 5) { + console.log("Skipping scope: ", scopekey) + alert.info("Skipping scope: "+scopekey) + continue + } + + //console.log("Checking scope for: ", scopekey, scopekeysplit.length) + } + + newscopes.push(scopekey) + } + + setOauth2Scopes(newscopes) + } + } + } else { + console.log("Bad flowkey and basekey for oauth2: ", flowkey, basekey) } } else if (key === "ApiKeyAuth") { @@ -973,11 +1084,12 @@ const AppCreator = (props) => { setAuthenticationOption("Oauth2") setAuthenticationRequired(true) } else { - newauth.push({ - "name": key, - "type": value.in, - "example": "", - }) + alert.error("Couldn't handle AUTH type: ", key) + //newauth.push({ + // "name": key, + // "type": value.in, + // "example": "", + //}) } } @@ -1446,15 +1558,6 @@ const AppCreator = (props) => { }, }, } - - console.log("SCOPES: ", oauth2Scopes) - - //if (oauth2Scopes.scopes > 0) { - // for (var key in oauth2Scopes) { - // const scope = oauth2Scopes[key] - // data.components.securitySchemes["Oauth2"]["flow"]["authorizationCode"]["scopes"].push(scope) - // } - //} } if (setExtraAuth.length > 0) { @@ -1826,21 +1929,25 @@ const AppCreator = (props) => { InputProps={{ style:{ color: "white", + maxHeight: 50, }, }} + style={{maxHeight: 80, overflowX: "hidden", overflowY: "auto",}} placeholder="Scopes" color="primary" fullWidth - defaultValue={oauth2Scopes} + value={oauth2Scopes} onAdd={(chip) => { oauth2Scopes.push(chip) console.log(oauth2Scopes) setOauth2Scopes(oauth2Scopes) + setUpdate(Math.random()) }} onDelete={(chip, index) => { oauth2Scopes.splice(index, 1) console.log(oauth2Scopes) setOauth2Scopes(oauth2Scopes) + setUpdate(Math.random()) }} />
    @@ -3265,10 +3372,14 @@ const AppCreator = (props) => { 0, 0, canvas.width, canvas.height ) - const canvasUrl = canvas.toDataURL() - if (canvasUrl !== fileBase64) { - //console.log("SET URL TO: ", canvasUrl) - setFileBase64(canvasUrl) + try { + const canvasUrl = canvas.toDataURL() + if (canvasUrl !== fileBase64) { + //console.log("SET URL TO: ", canvasUrl) + setFileBase64(canvasUrl) + } + } catch (e) { + alert.error("Failed to parse canvasurl!") } } @@ -3278,12 +3389,6 @@ const AppCreator = (props) => { //canvas.height = img.height } - //const imageInfo = file.length === 0 ? - //
    - // Upload logo - //
    : - // - const [imageUploadError, setImageUploadError] = useState(""); const [openImageModal, setOpenImageModal] = useState(""); const [scale, setScale] = useState(1); @@ -3293,7 +3398,7 @@ const AppCreator = (props) => { let imageData = fileBase64; let croppedData = file.length > 0 ? file : fileBase64 - const imageInfo = + const imageInfo = const alternateImg = { upload.click() @@ -3326,11 +3431,15 @@ const AppCreator = (props) => { const onSaveAppIcon = () => { if(editor){ - setFile(""); - const canvas = editor.getImageScaledToCanvas(); - setFileBase64(canvas.toDataURL()); - setOpenImageModal(false) - setDisableImageUpload(true); + try { + setFile(""); + const canvas = editor.getImageScaledToCanvas(); + setFileBase64(canvas.toDataURL()) + setOpenImageModal(false) + setDisableImageUpload(true); + } catch (e) { + alert.error("Failed to set image. Replace it if this persists.") + } } } diff --git a/frontend/src/views/Apps.jsx b/frontend/src/views/Apps.jsx index 61f8ebdb..03142daa 100644 --- a/frontend/src/views/Apps.jsx +++ b/frontend/src/views/Apps.jsx @@ -845,7 +845,7 @@ const Apps = (props) => {

    App Creator

    How it works  - Security API's -  - OpenAPI directory +  - OpenAPI directory  - OpenAPI Validator
    @@ -1073,10 +1073,10 @@ const Apps = (props) => { : - + No apps have been created, uploaded or downloaded yet. Click "Load existing apps" above to get the baseline. This may take a while as its building docker images. - + If you're still not able to see any apps, please follow our troubleshooting guide for loading apps! diff --git a/frontend/src/views/Workflows.jsx b/frontend/src/views/Workflows.jsx index 8c5ef7a0..5805dd78 100644 --- a/frontend/src/views/Workflows.jsx +++ b/frontend/src/views/Workflows.jsx @@ -5,6 +5,7 @@ import { useTheme } from '@material-ui/core/styles'; import {Avatar, Grid, Paper, Tooltip, Divider, Button, TextField, FormControl, IconButton, Menu, MenuItem, FormControlLabel, Chip, Switch, Typography, Zoom, CircularProgress, Dialog, DialogTitle, DialogActions, DialogContent} from '@material-ui/core'; import {GridOn as GridOnIcon, List as ListIcon, Close as CloseIcon, Compare as CompareIcon, Maximize as MaximizeIcon, Minimize as MinimizeIcon, AddCircle as AddCircleIcon, Toc as TocIcon, Send as SendIcon, Search as SearchIcon, FileCopy as FileCopyIcon, Delete as DeleteIcon, BubbleChart as BubbleChartIcon, Restore as RestoreIcon, Cached as CachedIcon, GetApp as GetAppIcon, Apps as AppsIcon, Edit as EditIcon, MoreVert as MoreVertIcon, PlayArrow as PlayArrowIcon, Add as AddIcon, Publish as PublishIcon, CloudUpload as CloudUploadIcon, CloudDownload as CloudDownloadIcon} from '@material-ui/icons'; +import NestedMenuItem from "material-ui-nested-menu-item"; //import {Search as SearchIcon, ArrowUpward as ArrowUpwardIcon, Visibility as VisibilityIcon, Done as DoneIcon, Close as CloseIcon, Error as ErrorIcon, FindReplace as FindreplaceIcon, ArrowLeft as ArrowLeftIcon, Cached as CachedIcon, DirectionsRun as DirectionsRunIcon, Add as AddIcon, Polymer as PolymerIcon, FormatListNumbered as FormatListNumberedIcon, Create as CreateIcon, PlayArrow as PlayArrowIcon, AspectRatio as AspectRatioIcon, MoreVert as MoreVertIcon, Apps as AppsIcon, Schedule as ScheduleIcon, FavoriteBorder as FavoriteBorderIcon, Pause as PauseIcon, Delete as DeleteIcon, AddCircleOutline as AddCircleOutlineIcon, Save as SaveIcon, KeyboardArrowLeft as KeyboardArrowLeftIcon, KeyboardArrowRight as KeyboardArrowRightIcon, ArrowBack as ArrowBackIcon, Settings as SettingsIcon, LockOpen as LockOpenIcon, ExpandMore as ExpandMoreIcon, VpnKey as VpnKeyIcon} from '@material-ui/icons'; //https://next.material-ui.com/components/material-icons/ @@ -27,6 +28,7 @@ import CytoscapeWrapper from '../components/RenderCytoscape' const inputColor = "#383B40" const surfaceColor = "#27292D" const svgSize = 24 +const imagesize = 22 const flexContainerStyle = { display: "flex", @@ -1102,7 +1104,6 @@ const Workflows = (props) => { ) } - const WorkflowPaper = (props) => { const { data } = props; const [open, setOpen] = React.useState(false); @@ -1135,7 +1136,69 @@ const Workflows = (props) => { const actions = data.actions !== null ? data.actions.length : 0 const [triggers, schedules, webhooks, subflows] = getWorkflowMeta(data) - const imagesize = 22 + + const workflowMenuButtons = { + setOpen(false) + setAnchorEl(null) + }} + > + { + //console.log("DATA:" ,data) + setModalOpen(true) + setEditingWorkflow(data) + setNewWorkflowName(data.name) + setNewWorkflowDescription(data.description) + setDefaultReturnValue(data.default_return_value) + if (data.tags !== undefined && data.tags !== null) { + setNewWorkflowTags(JSON.parse(JSON.stringify(data.tags))) + } + }} key={"change"}> + + {"Change details"} + + { + setSelectedWorkflow(data) + setPublishModalOpen(true) + }} key={"publish"}> + + {"Publish Workflow"} + + { + copyWorkflow(data) + setOpen(false) + }} key={"duplicate"}> + + {"Duplicate Workflow"} + + = 0} style={{backgroundColor: inputColor, color: "white"}} onClick={() => { + //copyWorkflow(data) + //setOpen(false) + }} key={"duplicate"}> + + {"Copy to Child Org"} + + { + setExportModalOpen(true) + setExportData(data) + setOpen(false) + }} key={"export"}> + + {"Export Workflow"} + + { + setDeleteModalOpen(true) + setSelectedWorkflowId(data.id) + setOpen(false) + }} key={"delete"}> + + {"Delete Workflow"} + + var image = "" var orgName = "" @@ -1283,62 +1346,7 @@ const Workflows = (props) => { > - { - setOpen(false) - setAnchorEl(null) - }} - > - { - //console.log("DATA:" ,data) - setModalOpen(true) - setEditingWorkflow(data) - setNewWorkflowName(data.name) - setNewWorkflowDescription(data.description) - setDefaultReturnValue(data.default_return_value) - if (data.tags !== undefined && data.tags !== null) { - setNewWorkflowTags(JSON.parse(JSON.stringify(data.tags))) - } - }} key={"change"}> - - {"Change details"} - - { - setSelectedWorkflow(data) - setPublishModalOpen(true) - }} key={"publish"}> - - {"Publish Workflow"} - - { - copyWorkflow(data) - setOpen(false) - }} key={"duplicate"}> - - {"Duplicate Workflow"} - - { - setExportModalOpen(true) - setExportData(data) - setOpen(false) - }} key={"export"}> - - {"Export Workflow"} - - { - setDeleteModalOpen(true) - setSelectedWorkflowId(data.id) - setOpen(false) - }} key={"delete"}> - - {"Delete Workflow"} - - - + {workflowMenuButtons} {/* @@ -1547,6 +1555,50 @@ const Workflows = (props) => { let workflowData = ""; if (workflows.length > 0) { const columns = [ + { field: 'image', headerName: 'Logo', width: 42, renderCell: (params) => { + const data = params.row.record + + var boxColor = "#FECC00" + if (data.is_valid) { + boxColor = "#86c142" + } + + if (!data.previously_saved) { + boxColor = "#f85a3e" + } + + var image = "" + var orgName = "" + var orgId = "" + if (userdata.orgs !== undefined) { + const foundOrg = userdata.orgs.find(org => org.id === data["org_id"]) + if (foundOrg !== undefined && foundOrg !== null) { + //position: "absolute", bottom: 5, right: -5, + const imageStyle = {width: imagesize+7, height: imagesize+7, pointerEvents: "none", marginLeft: data.creator_org !== undefined && data.creator_org.length > 0 ? 20 : 0, borderRadius: 10, border: foundOrg.id === userdata.active_org.id ? `3px solid ${boxColor}` : null, cursor: "pointer", marginTop: 5, } + + // + image = foundOrg.image === "" ? + {foundOrg.name} + : + {foundOrg.name} { + //setFilteredWorkflows(newWorkflows) + }}/> + + orgName = foundOrg.name + orgId = foundOrg.id + } + } + + return ( +
    { + //addFilter(orgId) + //setFilters(["Org "+orgName]) + //setFilteredWorkflows(newWorkflows) + }}> + {image} +
    + ) + }}, { field: 'title', headerName: 'Title', width: 330, renderCell: (params) => { const data = params.row.record @@ -1569,7 +1621,7 @@ const Workflows = (props) => {
    ) } */}, - { field: 'actions', headerName: 'Options', width: 200, sortable: false, + { field: 'options', headerName: 'Options', width: 200, sortable: false, disableClickEventBubbling: true, renderCell: (params) => { const data = params.row.record; @@ -1635,7 +1687,7 @@ const Workflows = (props) => { ) } }, - {/* field: 'tags', headerName: 'Tags', maxHeight: 15, width: 390, sortable: false, + { field: 'tags', headerName: 'Tags', maxHeight: 15, width: 300, sortable: false, disableClickEventBubbling: true, renderCell: (params) => { const data = params.row.record; @@ -1661,7 +1713,12 @@ const Workflows = (props) => { ) } - */} + }, + { field: '', headerName: '', maxHeight: 15, width: 100, sortable: false, + disableClickEventBubbling: true, + renderCell: (params) => { + } + } ]; let rows = []; rows = workflows.map((data, index) => { @@ -1732,6 +1789,7 @@ const Workflows = (props) => { placeholder="Name" margin="dense" defaultValue={newWorkflowName} + autoFocus fullWidth /> Date: Thu, 14 Oct 2021 23:00:07 +0200 Subject: [PATCH 115/117] LOTS of security fixes of containers and other versions --- backend/Dockerfile | 4 +- backend/app_sdk/Dockerfile | 3 +- backend/app_sdk/build.sh | 2 +- backend/app_sdk/requirements.txt | 2 +- backend/go-app/go.mod | 5 +- backend/go-app/main.go | 115 ---- docker-compose.yml | 4 +- frontend/Dockerfile | 2 +- frontend/src/components/ParsedAction.jsx | 11 + frontend/src/views/AngularWorkflow.jsx | 14 +- frontend/src/views/AppCreator.jsx | 51 +- functions/onprem/orborus/Dockerfile | 4 +- functions/onprem/orborus/go.mod | 39 +- functions/onprem/orborus/go.sum | 824 +++++++++++++++++++++++ functions/onprem/worker/Dockerfile | 4 +- functions/onprem/worker/build.sh | 2 +- functions/onprem/worker/go.mod | 33 +- functions/onprem/worker/go.sum | 732 ++++++++++++++++++++ functions/onprem/worker/worker.go | 2 +- 19 files changed, 1696 insertions(+), 157 deletions(-) diff --git a/backend/Dockerfile b/backend/Dockerfile index f0a48525..f09fe3b3 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.16.0-buster as builder +FROM golang:1.17.2-buster as builder # Add files RUN mkdir /app @@ -22,7 +22,7 @@ RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o webapp . FROM alpine:latest as certs RUN apk --update add ca-certificates -FROM alpine:3.12 +FROM alpine:3.14.2 COPY --from=builder /app/ /app COPY --from=builder /app_sdk/ /app_sdk diff --git a/backend/app_sdk/Dockerfile b/backend/app_sdk/Dockerfile index 68bd625d..2bb4e4d3 100644 --- a/backend/app_sdk/Dockerfile +++ b/backend/app_sdk/Dockerfile @@ -1,4 +1,5 @@ -FROM python:3.9.1-alpine as base +#FROM python:3.9.1-alpine as base +FROM python:3.10.0-alpine as base FROM base as builder RUN apk --no-cache add --update alpine-sdk libffi libffi-dev musl-dev openssl-dev tzdata coreutils diff --git a/backend/app_sdk/build.sh b/backend/app_sdk/build.sh index 5a0fbcb9..a7518354 100644 --- a/backend/app_sdk/build.sh +++ b/backend/app_sdk/build.sh @@ -6,7 +6,7 @@ NAME=shuffle-app_sdk VERSION=0.9.23 docker rmi docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION --force -docker build . -f Dockerfile -t frikky/shuffle:app_sdk -t frikky/$NAME:$VERSION -t docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION -t ghcr.io/frikky/$NAME:$VERSION +docker build . -f Dockerfile -t frikky/shuffle:app_sdk -t frikky/$NAME:$VERSION -t docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION -t ghcr.io/frikky/$NAME:$VERSION -t ghcr.io/frikky/$NAME:nightly #docker push frikky/$NAME:$VERSION #docker push docker.pkg.github.com/frikky/shuffle/$NAME:$VERSION diff --git a/backend/app_sdk/requirements.txt b/backend/app_sdk/requirements.txt index c887c4f5..79cf5ea2 100644 --- a/backend/app_sdk/requirements.txt +++ b/backend/app_sdk/requirements.txt @@ -1,4 +1,4 @@ -urllib3==1.25.9 +urllib3==1.26.5 requests==2.25.1 MarkupSafe==2.0.1 liquidpy==0.7.1 diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 120e93c5..7dc5e24a 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -2,7 +2,7 @@ module shuffle go 1.13 -//replace github.com/frikky/shuffle-shared => ../../../../git/shuffle-shared +replace github.com/shuffle/shuffle-shared => ../../../../git/shuffle-shared //replace github.com/frikky/kin-openapi => ../../../../git/kin-openapi @@ -23,7 +23,7 @@ require ( github.com/docker/go-units v0.4.0 // indirect github.com/elastic/go-elasticsearch/v7 v7.13.1 // indirect github.com/frikky/kin-openapi v0.39.0 - github.com/frikky/shuffle-shared v0.1.14 + github.com/frikky/shuffle-shared v0.1.15 // indirect github.com/fsouza/go-dockerclient v1.7.2 github.com/ghodss/yaml v1.0.0 github.com/go-git/go-billy/v5 v5.0.0 @@ -36,6 +36,7 @@ require ( github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect github.com/patrickmn/go-cache v2.1.0+incompatible github.com/satori/go.uuid v1.2.0 + github.com/shuffle/shuffle-shared v0.1.14 go4.org v0.0.0-20201209231011-d4a079459e60 // indirect golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 golang.org/x/oauth2 v0.0.0-20210113160501-8b1d76fa0423 diff --git a/backend/go-app/main.go b/backend/go-app/main.go index ae80d6d5..e330ca0f 100644 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -2742,121 +2742,6 @@ func validateAppYaml(fileLocation string) error { return nil } -func handleSendalert(resp http.ResponseWriter, request *http.Request) { - user, err := shuffle.HandleApiAuthentication(resp, request) - if err != nil { - log.Printf("[WARNING] Api authentication failed in sendalert: %s", err) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - if user.Role != "mail" && user.Role != "admin" { - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false, "reason": "You don't have access to send mail"}`)) - return - } - - // ReferenceExecution and below are for execution continuations when user inputs arrive - type mailcheck struct { - Targets []string `json:"targets"` - Body string `json:"body"` - Subject string `json:"subject"` - Type string `json:"type"` - SenderCompany string `json:"sender_company"` - ReferenceExecution string `json:"reference_execution"` - WorkflowId string `json:"workflow_id"` - ExecutionType string `json:"execution_type"` - Start string `json:"start"` - } - - body, err := ioutil.ReadAll(request.Body) - if err != nil { - log.Printf("Body data error on mail: %s", err) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - var mailbody mailcheck - err = json.Unmarshal(body, &mailbody) - if err != nil { - log.Printf("Unmarshal error on mail: %s", err) - resp.WriteHeader(401) - resp.Write([]byte(`{"success": false}`)) - return - } - - ctx := context.Background() - confirmMessage := ` -You have a new alert from shuffler.io! - -%s - -Please contact us at shuffler.io or frikky@shuffler.io if there is an issue with this message.` - - parsedBody := fmt.Sprintf(confirmMessage, mailbody.Body) - - // FIXME - Make a continuation email here - might need more info from worker - // making the request, e.g. what the next start-node is and execution_id for - // how to make the links - if mailbody.Type == "User input" { - authkey := uuid.NewV4().String() - - log.Printf("Should handle differentiator for user input in email!") - log.Printf("%#v", mailbody) - - url := "https://shuffler.io" - //url := "http://localhost:5001" - continueUrl := fmt.Sprintf("%s/api/v1/workflows/%s/execute?authorization=%s&start=%s&reference_execution=%s&answer=true", url, mailbody.WorkflowId, authkey, mailbody.Start, mailbody.ReferenceExecution) - stopUrl := fmt.Sprintf("%s/api/v1/workflows/%s/execute?authorization=%s&start=%s&reference_execution=%s&answer=false", url, mailbody.WorkflowId, authkey, mailbody.Start, mailbody.ReferenceExecution) - - //item := &memcache.Item{ - // Key: authkey, - // Value: []byte(fmt.Sprintf(`{"role": "workflow_%s"}`, mailbody.WorkflowId)), - // Expiration: time.Minute * 1200, - //} - - //if err := memcache.Add(ctx, item); err == memcache.ErrNotStored { - // if err := memcache.Set(ctx, item); err != nil { - // log.Printf("Error setting new user item: %v", err) - // } - //} else if err != nil { - // log.Printf("error adding item: %v", err) - //} else { - // log.Printf("Set cache for %s", item.Key) - //} - - parsedBody = fmt.Sprintf(` -Action required! - -%s - -If this is TRUE click this: %s - -IF THIS IS FALSE, click this: %s - -Please contact us at shuffler.io or frikky@shuffler.io if there is an issue with this message. -`, mailbody.Body, continueUrl, stopUrl) - - } - - msg := &mail.Message{ - Sender: "Shuffle ", - To: mailbody.Targets, - Subject: fmt.Sprintf("Shuffle - %s - %s", mailbody.Type, mailbody.Subject), - Body: parsedBody, - } - - log.Println(msg.Body) - if err := mail.Send(ctx, msg); err != nil { - log.Printf("Couldn't send email: %v", err) - } - - resp.WriteHeader(200) - resp.Write([]byte(`{"success": true}`)) -} - func setBadMemcache(ctx context.Context, path string) { // Add to cache if it doesn't exist //item := &memcache.Item{ diff --git a/docker-compose.yml b/docker-compose.yml index 09f810e1..069fedad 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '3' services: frontend: - build: ./frontend + #build: ./frontend image: ghcr.io/frikky/shuffle-frontend:nightly container_name: shuffle-frontend hostname: shuffle-frontend @@ -16,7 +16,7 @@ services: depends_on: - backend backend: - build: ./backend + #build: ./backend image: ghcr.io/frikky/shuffle-backend:nightly container_name: shuffle-backend hostname: ${BACKEND_HOSTNAME} diff --git a/frontend/Dockerfile b/frontend/Dockerfile index ab5940b1..3d410405 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -21,7 +21,7 @@ COPY ./*.json /usr/src/app/ RUN yarn build # Production environment -FROM nginx:1.21 +FROM nginx:1.21.3 RUN mkdir -p /usr/share/nginx/html/build RUN mkdir -p /usr/share/nginx/html/css diff --git a/frontend/src/components/ParsedAction.jsx b/frontend/src/components/ParsedAction.jsx index 937f592e..ef635b6f 100644 --- a/frontend/src/components/ParsedAction.jsx +++ b/frontend/src/components/ParsedAction.jsx @@ -753,6 +753,10 @@ const ParsedAction = (props) => { var placeholder = "Static value" if (data.example !== undefined && data.example !== null && data.example.length > 0) { placeholder = data.example + + if (data.name === "url" && data.value.length === 0) { + data.value = data.example + } } if (data.name.startsWith("${") && data.name.endsWith("}")) { @@ -1270,6 +1274,13 @@ const ParsedAction = (props) => { } tmpitem = (tmpitem.charAt(0).toUpperCase()+tmpitem.substring(1)).replaceAll("_", " ") + + if (tmpitem === "Username basic") { + tmpitem = "Username" + } else if (tmpitem === "Password basic") { + tmpitem = "Password" + } + const description = data.description === undefined ? "" : data.description const tooltipDescription = diff --git a/frontend/src/views/AngularWorkflow.jsx b/frontend/src/views/AngularWorkflow.jsx index 274c21bf..78da9dee 100644 --- a/frontend/src/views/AngularWorkflow.jsx +++ b/frontend/src/views/AngularWorkflow.jsx @@ -9332,29 +9332,29 @@ const AngularWorkflow = (props) => {
    {selectedApp.documentation === undefined || selectedApp.documentation === null || selectedApp.documentation.length === 0 ? - - Description: {selectedApp.description} + + {selectedApp.description} - + There is currently no extended documentation available for this app. - + Want help with this app? Join the Discord! - + Want to help change this app? {selectedApp.reference_info === undefined || selectedApp.reference_info === null || selectedApp.reference_info.github_url === undefined || selectedApp.reference_info.github_url === null || selectedApp.reference_info.github_url.length === 0 ? - + Check it out on Github! : - + Check it out on Github! diff --git a/frontend/src/views/AppCreator.jsx b/frontend/src/views/AppCreator.jsx index bf0431fb..204bfb8a 100644 --- a/frontend/src/views/AppCreator.jsx +++ b/frontend/src/views/AppCreator.jsx @@ -201,8 +201,7 @@ const AppCreator = (props) => { const increaseAmount = 50 const actionNonBodyRequest = ["GET", "HEAD", "DELETE", "CONNECT"] const actionBodyRequest = ["POST", "PUT", "PATCH",] - //const authenticationOptions = ["No authentication", "API key", "Bearer auth", "Basic auth", "JWT", "Oauth2"] - const authenticationOptions = ["No authentication", "API key", "Bearer auth", "Basic auth", "Oauth2"] + const authenticationOptions = ["No authentication", "API key", "Bearer auth", "Basic auth", "Oauth2", "JWT"] const apikeySelection = ["Header", "Query",] const [name, setName] = useState(""); @@ -1004,8 +1003,15 @@ const AppCreator = (props) => { //if (Object.entries(securitySchemes) > 1 && var newauth = [] for (const [key, value] of Object.entries(securitySchemes)) { - //console.log(key, value) - if (value.scheme === "bearer") { + console.log(key, value) + if (key === "jwt") { + setAuthenticationOption("JWT") + setAuthenticationRequired(true) + + if (value.in !== undefined && value.in !== null && value.in.length > 0) { + setParameterName(value.in) + } + } else if (value.scheme === "bearer") { setAuthenticationOption("Bearer auth") setAuthenticationRequired(true) } else if (key === "Oauth2" || key === "Oauth2c") { @@ -1531,11 +1537,14 @@ const AppCreator = (props) => { "bearerFormat": "UUID", } } else if (authenticationOption === "JWT") { - data.components.securitySchemes["BearerAuth"] = { + data.components.securitySchemes["jwt"] = { "type": "http", "scheme": "bearer", "bearerFormat": "JWT", + "in": parameterName, } + + console.log("SECURITYSCHEMES: ", data.components) } else if (authenticationOption === "Basic auth") { data.components.securitySchemes["BasicAuth"] = { "type": "http", @@ -1853,6 +1862,37 @@ const AppCreator = (props) => { })}
    + const jwtAuth = authenticationOption === "JWT" ? +
    + JWT authentication + + Authentication path + + Must start with / and be a valid path} + onBlur={e => setParameterName(e.target.value)} + InputProps={{ + classes: { + notchedOutline: classes.notchedOutline, + }, + style:{ + color: "white", + }, + }} + /> +
    + : + null + const oauth2Auth = authenticationOption === "Oauth2" ?
    Oauth2 authentication @@ -3680,6 +3720,7 @@ const AppCreator = (props) => { {bearerAuth} {apiKey} {oauth2Auth} + {jwtAuth} {extraKeys} {/*authenticationOption === "No authentication" ? null : diff --git a/functions/onprem/orborus/Dockerfile b/functions/onprem/orborus/Dockerfile index 3ae68760..b7160a20 100644 --- a/functions/onprem/orborus/Dockerfile +++ b/functions/onprem/orborus/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.16.0-buster as builder +FROM golang:1.17.2-buster as builder RUN mkdir /app WORKDIR /app @@ -15,7 +15,7 @@ RUN go get github.com/docker/docker/api/types && \ RUN go build RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o orborus . -FROM alpine:3.12 +FROM alpine:3.14.2 RUN apk add --no-cache bash tzdata COPY --from=builder /app/ / diff --git a/functions/onprem/orborus/go.mod b/functions/onprem/orborus/go.mod index 30b558af..f48b58de 100644 --- a/functions/onprem/orborus/go.mod +++ b/functions/onprem/orborus/go.mod @@ -3,21 +3,44 @@ module orborus go 1.13 require ( + cloud.google.com/go/datastore v1.6.0 // indirect + cloud.google.com/go/storage v1.18.1 // indirect github.com/Masterminds/semver v1.5.0 // indirect - github.com/Microsoft/go-winio v0.4.16 // indirect - github.com/algolia/algoliasearch-client-go/v3 v3.18.1 // indirect + github.com/Microsoft/go-winio v0.5.0 // indirect + github.com/algolia/algoliasearch-client-go/v3 v3.21.0 // indirect github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 // indirect - github.com/containerd/containerd v1.4.3 // indirect + github.com/containerd/containerd v1.5.7 // indirect + github.com/creack/pty v1.1.16 // indirect github.com/docker/distribution v2.7.1+incompatible // indirect - github.com/docker/docker v20.10.1+incompatible + github.com/docker/docker v20.10.9+incompatible github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.4.0 // indirect - github.com/frikky/shuffle-shared v0.0.40 - github.com/gogo/protobuf v1.3.1 // indirect - github.com/mackerelio/go-osstat v0.1.0 + github.com/frikky/kin-openapi v0.40.0 // indirect + github.com/frikky/shuffle-shared v0.1.15 + github.com/go-openapi/swag v0.19.15 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/snappy v0.0.4 // indirect + github.com/google/go-querystring v1.1.0 // indirect + github.com/gorilla/mux v1.8.0 // indirect + github.com/kr/pretty v0.3.0 // indirect + github.com/mackerelio/go-osstat v0.2.1 + github.com/mailru/easyjson v0.7.7 // indirect + github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect + github.com/morikuni/aec v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.1 // indirect github.com/pkg/errors v0.9.1 // indirect + github.com/rogpeppe/go-internal v1.8.0 // indirect github.com/satori/go.uuid v1.2.0 - github.com/sirupsen/logrus v1.7.0 // indirect + go4.org v0.0.0-20201209231011-d4a079459e60 // indirect + golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect + golang.org/x/mod v0.5.1 // indirect + golang.org/x/net v0.0.0-20211014172544-2b766c08f1c0 // indirect + golang.org/x/sys v0.0.0-20211013075003-97ac67df715c // indirect + golang.org/x/text v0.3.7 // indirect + golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect + golang.org/x/tools v0.1.7 // indirect + google.golang.org/genproto v0.0.0-20211013025323-ce878158c4d4 // indirect + google.golang.org/grpc v1.41.0 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect ) diff --git a/functions/onprem/orborus/go.sum b/functions/onprem/orborus/go.sum index c5de5ac9..677b4993 100644 --- a/functions/onprem/orborus/go.sum +++ b/functions/onprem/orborus/go.sum @@ -1,3 +1,4 @@ +bazil.org/fuse v0.0.0-20160811212531-371fbbdaa898/go.mod h1:Xbm+BRKSBEpa4q4hTSxohYNQpsxXPbPry4JJWOB3LB8= cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU= @@ -15,8 +16,20 @@ cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOY cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go v0.66.0/go.mod h1:dgqGAjKCDxyhGTtC9dAREQGUJpkceNm1yt590Qno0Ko= cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= cloud.google.com/go v0.75.0 h1:XgtDnVJRCPEUG21gjFiRPz4zI1Mjg16R+NYQjfmU4XY= cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0 h1:3DXvAyifywvq64LfkKaMOmkWPS1CikIQdMe2lY9vxU8= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -27,6 +40,8 @@ cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7 cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/datastore v1.4.0 h1:CFDJm15RpYXeEblQ0TMDUrYtqmBmbAWTy536nA8JIc8= cloud.google.com/go/datastore v1.4.0/go.mod h1:d18825/a9bICdAIJy2EkHs9joU4RlIZ1t6l8WDdbdY0= +cloud.google.com/go/datastore v1.6.0 h1:wZaHIqu1tebvGRYhVgcfNX6jN2q638OGO23JyJckxuI= +cloud.google.com/go/datastore v1.6.0/go.mod h1:q3ZJj1GMQRdU0OCv5XXpCqfLqHHZnI5zcumkvuYDmHI= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -38,44 +53,269 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.12.0 h1:4y3gHptW1EHVtcPAVE0eBBlFuGqEejTTG3KdIE0lUX4= cloud.google.com/go/storage v1.12.0/go.mod h1:fFLk2dp2oAhDz8QFKwqrjdJvxSp/W2g7nillojlL5Ho= +cloud.google.com/go/storage v1.18.1 h1:hfXX1gE2mvuwoS/weBfgH1Vr+efX0uLdxX2ETJ9g1JQ= +cloud.google.com/go/storage v1.18.1/go.mod h1:FPalGc7eWSCKf26f2JwjrZhVibDE6pCDP19d1Pe2lB8= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= +github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= +github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= +github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= +github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk= github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU= +github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= +github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= +github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= +github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= +github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= +github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= +github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= +github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= +github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= +github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= github.com/algolia/algoliasearch-client-go v2.25.0+incompatible h1:FGQr9l++u4uQPDXrW8jM5kNJm3Iw5SxEJJtYXSFmPRY= github.com/algolia/algoliasearch-client-go/v3 v3.18.1 h1:FP2Xtqqs/sefR5Qluygp+jVV+juXzEdJaPrZTCDLhDQ= github.com/algolia/algoliasearch-client-go/v3 v3.18.1/go.mod h1:i7tLoP7TYDmHX3Q7vkIOL4syVse/k5VJ+k0i8WqFiJk= +github.com/algolia/algoliasearch-client-go/v3 v3.21.0 h1:rpzkfDdImYGGjKZq+NrovY5vQabD3Fk0DnBtZPk/2jo= +github.com/algolia/algoliasearch-client-go/v3 v3.21.0/go.mod h1:i7tLoP7TYDmHX3Q7vkIOL4syVse/k5VJ+k0i8WqFiJk= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= +github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= +github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 h1:/P9/RL0xgWE+ehnCUUN5h3RpG3dmoMCOONO1CCvq23Y= github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013/go.mod h1:pccXHIvs3TV/TUqSNyEvF99sxjX2r4FFRIyw6TZY9+w= +github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= +github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= +github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= +github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= +github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= +github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= +github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= +github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= +github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= +github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= +github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= +github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= +github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= +github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E= +github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= +github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= +github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI= +github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= +github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= +github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= +github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= +github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= +github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= +github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= +github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= +github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= +github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= +github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= +github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.4.3 h1:ijQT13JedHSHrQGWFcGEwzcNKrAGIiZ+jSD5QQG07SY= github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.11 h1:QCGOUN+i70jEEL/A6JVIbhy4f4fanzAzSR4kNG7SlcE= +github.com/containerd/containerd v1.4.11/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ= +github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU= +github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= +github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= +github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= +github.com/containerd/containerd v1.5.7 h1:rQyoYtj4KddB3bxG6SAqd4+08gePNyJjRqvOIfV3rkM= +github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= +github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= +github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= +github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= +github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= +github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= +github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= +github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= +github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= +github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= +github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= +github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU= +github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk= +github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= +github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= +github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= +github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0= +github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA= +github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow= +github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms= +github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= +github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= +github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= +github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= +github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= +github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= +github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= +github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= +github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= +github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= +github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw= +github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y= +github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= +github.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= +github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= +github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= +github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= +github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= +github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4= +github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= +github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= +github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= +github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.16 h1:vfetlOf3A+9YKggibynnX9mnFjuSVvkRj+IWpcTSLEQ= +github.com/creack/pty v1.1.16/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= +github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= +github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= +github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= +github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= +github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= +github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= +github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v20.10.1+incompatible h1:u0HIBLwOJdemyBdTCkoBX34u3lb5KyBo0rQE3a5Yg+E= github.com/docker/docker v20.10.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.9+incompatible h1:JlsVnETOjM2RLQa0Cc1XCIspUdXW3Zenq9P54uXBm6k= +github.com/docker/docker v20.10.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= +github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= +github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= +github.com/frikky/go-elasticsearch/v8 v8.13.1 h1:GB+Wr0Yx8efG7D1jc9fGGiqjjRRngWJTcMSua3QIDaM= +github.com/frikky/go-elasticsearch/v8 v8.13.1/go.mod h1:RPq0JXPQVVSFHTlPwj/go8BZ1hegRf+StaSpT2iGIoQ= github.com/frikky/kin-openapi v0.38.0 h1:V7ttwIJS8Vks4KL+mZVj1ZSqhIcQtgaG8akeqXEQgsE= github.com/frikky/kin-openapi v0.38.0/go.mod h1:Fr28TtCHL4K0kIqtqui8HWxN1LG5uAh3z/tDfFyiA1s= +github.com/frikky/kin-openapi v0.40.0 h1:D/b5MvxbQFn6J82Lfw7Gr1CzcNtjH4O6FSY4QuHHYGI= +github.com/frikky/kin-openapi v0.40.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4= github.com/frikky/shuffle-shared v0.0.12 h1:+0EIfThmK47Po+LogPYZR4XjbS4Ds19WNMFu2YUSjhw= github.com/frikky/shuffle-shared v0.0.12/go.mod h1:SEY432/xs4oBkOUnGwxiYKCZWZLRaheGInhAoW7N8ww= github.com/frikky/shuffle-shared v0.0.23 h1:Pnlc2M6fHnFRLFd5K1iLTVv4/t4P04Ri1GJ5CMxzq0U= @@ -84,22 +324,62 @@ github.com/frikky/shuffle-shared v0.0.28 h1:VQqL3+ePwKSUxCOiCC8DpOEgbb2GhXI8XzFB github.com/frikky/shuffle-shared v0.0.28/go.mod h1:H7SqOta/EAYnfYuWzwzYSh/oWfF0kgnuaJTQNKQBvoQ= github.com/frikky/shuffle-shared v0.0.40 h1:H0au2np5xSy9mZEUWN+a29IORk+YP95FipENBD7iJRw= github.com/frikky/shuffle-shared v0.0.40/go.mod h1:H7SqOta/EAYnfYuWzwzYSh/oWfF0kgnuaJTQNKQBvoQ= +github.com/frikky/shuffle-shared v0.1.15 h1:73XfrsY211Qjdo1RIU7NCl6HMguby0h4QJMUkQx9wQA= +github.com/frikky/shuffle-shared v0.1.15/go.mod h1:w7x1+j3xsAB5T5eeTbPSqs0egVadcoKzYW5GbNdretI= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= +github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= +github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= +github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= +github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= +github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= +github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.1 h1:DqDEcV5aeaTmdFBePNpYsp3FlcVH/2ISVVM9Qf8PSls= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -107,6 +387,8 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -123,6 +405,14 @@ github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0 github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -133,11 +423,27 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-github/v28 v28.1.1 h1:kORf5ekX5qwXO2mGzXXOjMe/g6ap8ahVe0sBEulhSxo= +github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM= +github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= +github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0 h1:wCKgOCHuUEVfsaQLpPSJb7VdYCdTVZQAuOdYm1yc/60= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -147,57 +453,311 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200905233945-acf8798be1f7/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gax-go/v2 v2.1.1 h1:dp3bWCh+PPO1zjRRiCSczJav13sBvG4UhNyVTa1KqdU= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= +github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= +github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= +github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/mackerelio/go-osstat v0.1.0 h1:e57QHeHob8kKJ5FhcXGdzx5O6Ktuc5RHMDIkeqhgkFA= github.com/mackerelio/go-osstat v0.1.0/go.mod h1:1K3NeYLhMHPvzUu+ePYXtoB58wkaRpxZsGClZBJyIFw= +github.com/mackerelio/go-osstat v0.2.1 h1:5AeAcBEutEErAOlDz6WCkEvm6AKYgHTUQrfwm5RbeQc= +github.com/mackerelio/go-osstat v0.2.1/go.mod h1:UzRL8dMCCTqG5WdRtsxbuljMpZt9PCAGXqxPst5QtaY= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= +github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= +github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= +github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= +github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= +github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= +github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= +github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= +github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= +github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= +github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= +github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= +github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= +github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= +github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= +github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= +github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= +github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= +github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= +github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= +github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= +github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= +github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= +github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= +github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= +github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= +go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -205,12 +765,31 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go4.org v0.0.0-20201209231011-d4a079459e60 h1:iqAGo78tVOJXELHQFRjR6TMwItrvXH4hrGJ32I/NFF8= +go4.org v0.0.0-20201209231011-d4a079459e60/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZMZXMSBGhxRdsvzbkg= +golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -235,6 +814,7 @@ golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPI golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -246,8 +826,14 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628= @@ -255,10 +841,16 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -274,10 +866,21 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211014172544-2b766c08f1c0 h1:xNP8gGXzUFztyWFRq+TV6zyPNxOr8lXtV6x0KMrhk0o= +golang.org/x/net v0.0.0-20211014172544-2b766c08f1c0/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -288,6 +891,15 @@ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210113160501-8b1d76fa0423 h1:/hEknzWkMPCjTo7StMHRrBRa8YBbXuBWfck8680k3RE= golang.org/x/oauth2 v0.0.0-20210113160501-8b1d76fa0423/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1 h1:B333XXssMuKQeBwiNODx4TupZy7bf4sxFZnN2ZOcvUE= +golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -297,27 +909,51 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190410235845-0ad05ae3009d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037 h1:YyJpGZS1sBuBCzLAR1VEpK193GlqGZbnPFnPV/5Rsb4= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -326,13 +962,49 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3 h1:kzM6+9dur93BcC2kVlYl34cHU+TYZLanmpSJHVMmL64= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210113181707-4bcb84eeeb78/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22 h1:RqytpXGR1iVNX7psjB3ff8y7sNFinVFvkx1c8SjBkio= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211013075003-97ac67df715c h1:taxlMj0D/1sOAuv/CbSD+MMDof2vbyPTqz5FNYKpXt8= +golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -341,9 +1013,19 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -351,11 +1033,14 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= @@ -382,6 +1067,7 @@ golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= +golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA= @@ -391,14 +1077,25 @@ golang.org/x/tools v0.0.0-20200915173823-2db8f0ff891c/go.mod h1:z6u4i615ZeAfBE4X golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963 h1:K+NlvTLy0oONtRtkl1jRD9xIhnItbG2PiE7YOdjPb+k= golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -420,6 +1117,19 @@ google.golang.org/api v0.32.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0 h1:l2Nfbl2GPXdWorv+dT2XfinX2jOOw4zv1VhLstx+6rE= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.58.0 h1:MDkAbYIB1JpSgCTOCYYoIec/coMlKK4oVbpnBLLcyT0= +google.golang.org/api v0.58.0/go.mod h1:cAbP2FsxoGVNwtgNAmmn3y5G1TWAiVYRmg4yku3lv+E= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -428,11 +1138,13 @@ google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCID google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= @@ -441,6 +1153,7 @@ google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= @@ -451,6 +1164,7 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= @@ -463,14 +1177,45 @@ google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200914193844-75d14daec038/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200921151605-7abf4a1a14d5/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210113195801-ae06605f4595 h1:x7nk+/4+SvuTDI4wnzQUlhvi+DTpyfncXBo3QWTFs7U= google.golang.org/genproto v0.0.0-20210113195801-ae06605f4595/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211013025323-ce878158c4d4 h1:NBxB1XxiWpGqkPUiJ9PoBXkHV5A9+GohMOA+EmWoPbU= +google.golang.org/genproto v0.0.0-20211013025323-ce878158c4d4/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= @@ -481,11 +1226,24 @@ google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0 h1:raiipEjMOIC/TO2AvyTxP25XFdLxNIBwzDh3FM3XztI= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.34.1 h1:ugq+9++ZQPFzM2pKUMCIK8gj9M0pFyuUWO9Q8kwEDQw= google.golang.org/grpc v1.34.1/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.41.0 h1:f+PlOh7QV4iIJkPrx5NQ7qaNGFQ3OTse67yaDHfju4E= +google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -497,16 +1255,51 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v1.4.0 h1:BjtEgfuw8Qyd+jPvQz8CfoxiO/UjFEidWinwEXZiWv0= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= +gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= +gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= +gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= +gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -514,6 +1307,37 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= +k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= +k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= +k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= +k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= +k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= +k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= +k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= +k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= +k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= +k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= +k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= +k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= +k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= +k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= +k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= +k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= +k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= +k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= +k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= +k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= +k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/functions/onprem/worker/Dockerfile b/functions/onprem/worker/Dockerfile index d49a1043..0dbefd26 100644 --- a/functions/onprem/worker/Dockerfile +++ b/functions/onprem/worker/Dockerfile @@ -1,4 +1,4 @@ -FROM golang:1.16.0-buster as builder +FROM golang:1.17.2-buster as builder WORKDIR /app #RUN go get github.com/docker/docker/api/types github.com/docker/docker/api/types/container github.com/docker/docker/client @@ -25,7 +25,7 @@ RUN go build RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o worker . ## ALPINE IMAGE -FROM alpine:3.12 +FROM alpine:3.14.2 ENV SHUFFLE_BASE_IMAGE_REGISTRY=docker.io ENV SHUFFLE_BASE_IMAGE_NAME=frikky/shuffle diff --git a/functions/onprem/worker/build.sh b/functions/onprem/worker/build.sh index a0f099a4..779339e7 100644 --- a/functions/onprem/worker/build.sh +++ b/functions/onprem/worker/build.sh @@ -1,5 +1,5 @@ NAME=shuffle-worker -VERSION=0.9.16 +VERSION=0.9.23 echo "Running docker build with $NAME:$VERSION" #CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o worker.bin . diff --git a/functions/onprem/worker/go.mod b/functions/onprem/worker/go.mod index 4ca87b17..32284534 100644 --- a/functions/onprem/worker/go.mod +++ b/functions/onprem/worker/go.mod @@ -3,26 +3,47 @@ module worker go 1.15 require ( + cloud.google.com/go/datastore v1.6.0 // indirect + cloud.google.com/go/storage v1.18.1 // indirect github.com/Masterminds/semver v1.5.0 // indirect - github.com/Microsoft/go-winio v0.4.16 // indirect - github.com/algolia/algoliasearch-client-go/v3 v3.18.1 // indirect + github.com/Microsoft/go-winio v0.5.0 // indirect + github.com/algolia/algoliasearch-client-go/v3 v3.21.0 // indirect github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 // indirect - github.com/containerd/containerd v1.4.4 // indirect + github.com/containerd/containerd v1.5.7 // indirect + github.com/creack/pty v1.1.16 // indirect github.com/docker/distribution v2.7.1+incompatible // indirect - github.com/docker/docker v20.10.5+incompatible + github.com/docker/docker v20.10.9+incompatible github.com/docker/go-connections v0.4.0 // indirect github.com/docker/go-units v0.4.0 // indirect - github.com/frikky/shuffle-shared v0.0.95 + github.com/frikky/kin-openapi v0.40.0 // indirect + github.com/frikky/shuffle-shared v0.1.15 github.com/fsouza/go-dockerclient v1.7.2 github.com/go-git/go-billy/v5 v5.3.1 // indirect github.com/go-git/go-git/v5 v5.4.2 // indirect + github.com/go-openapi/swag v0.19.15 // indirect github.com/gogo/protobuf v1.3.2 // indirect + github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect + github.com/golang/snappy v0.0.4 // indirect github.com/google/go-github/v28 v28.1.1 // indirect + github.com/google/go-querystring v1.1.0 // indirect github.com/gorilla/mux v1.8.0 + github.com/kr/pretty v0.3.0 // indirect + github.com/mailru/easyjson v0.7.7 // indirect + github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.0.1 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible github.com/pkg/errors v0.9.1 // indirect - google.golang.org/grpc v1.37.1 // indirect + github.com/rogpeppe/go-internal v1.8.0 // indirect + go4.org v0.0.0-20201209231011-d4a079459e60 // indirect + golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 // indirect + golang.org/x/mod v0.5.1 // indirect + golang.org/x/net v0.0.0-20211014172544-2b766c08f1c0 // indirect + golang.org/x/sys v0.0.0-20211013075003-97ac67df715c // indirect + golang.org/x/text v0.3.7 // indirect + golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac // indirect + golang.org/x/tools v0.1.7 // indirect + google.golang.org/genproto v0.0.0-20211013025323-ce878158c4d4 // indirect + google.golang.org/grpc v1.41.0 // indirect gopkg.in/src-d/go-git.v4 v4.13.1 // indirect ) diff --git a/functions/onprem/worker/go.sum b/functions/onprem/worker/go.sum index 8e527e61..19952995 100644 --- a/functions/onprem/worker/go.sum +++ b/functions/onprem/worker/go.sum @@ -16,8 +16,20 @@ cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOY cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY= cloud.google.com/go v0.66.0/go.mod h1:dgqGAjKCDxyhGTtC9dAREQGUJpkceNm1yt590Qno0Ko= cloud.google.com/go v0.72.0/go.mod h1:M+5Vjvlc2wnp6tjzE102Dw08nGShTscUx2nZMufOKPI= +cloud.google.com/go v0.74.0/go.mod h1:VV1xSbzvo+9QJOxLDaJfTjx5e+MePCpCWwvftOeQmWk= cloud.google.com/go v0.75.0 h1:XgtDnVJRCPEUG21gjFiRPz4zI1Mjg16R+NYQjfmU4XY= cloud.google.com/go v0.75.0/go.mod h1:VGuuCn7PG0dwsd5XPVm2Mm3wlh3EL55/79EKB6hlPTY= +cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECHg= +cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= +cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= +cloud.google.com/go v0.83.0/go.mod h1:Z7MJUsANfY0pYPdw0lbnivPx4/vhy/e2FEkSkF7vAVY= +cloud.google.com/go v0.84.0/go.mod h1:RazrYuxIK6Kb7YrzzhPoLmCVzl7Sup4NrbKPg8KHSUM= +cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWcY= +cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= +cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= +cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= +cloud.google.com/go v0.97.0 h1:3DXvAyifywvq64LfkKaMOmkWPS1CikIQdMe2lY9vxU8= +cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= @@ -28,6 +40,8 @@ cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7 cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/datastore v1.4.0 h1:CFDJm15RpYXeEblQ0TMDUrYtqmBmbAWTy536nA8JIc8= cloud.google.com/go/datastore v1.4.0/go.mod h1:d18825/a9bICdAIJy2EkHs9joU4RlIZ1t6l8WDdbdY0= +cloud.google.com/go/datastore v1.6.0 h1:wZaHIqu1tebvGRYhVgcfNX6jN2q638OGO23JyJckxuI= +cloud.google.com/go/datastore v1.6.0/go.mod h1:q3ZJj1GMQRdU0OCv5XXpCqfLqHHZnI5zcumkvuYDmHI= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -39,87 +53,288 @@ cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RX cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= cloud.google.com/go/storage v1.12.0 h1:4y3gHptW1EHVtcPAVE0eBBlFuGqEejTTG3KdIE0lUX4= cloud.google.com/go/storage v1.12.0/go.mod h1:fFLk2dp2oAhDz8QFKwqrjdJvxSp/W2g7nillojlL5Ho= +cloud.google.com/go/storage v1.18.1 h1:hfXX1gE2mvuwoS/weBfgH1Vr+efX0uLdxX2ETJ9g1JQ= +cloud.google.com/go/storage v1.18.1/go.mod h1:FPalGc7eWSCKf26f2JwjrZhVibDE6pCDP19d1Pe2lB8= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= +github.com/Azure/azure-sdk-for-go v16.2.1+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78 h1:w+iIsaOQNcT7OZ575w+acHgRric5iCyQh+xv+KJ4HB8= github.com/Azure/go-ansiterm v0.0.0-20170929234023-d6e3b3328b78/go.mod h1:LmzpDX56iTiv29bbRTIsUNlaFfuhWRQBWjQdVyAevI8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Azure/go-autorest v10.8.1+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24= +github.com/Azure/go-autorest/autorest v0.11.1/go.mod h1:JFgpikqFJ/MleTTxwepExTKnFUKKszPS8UavbQYUMuw= +github.com/Azure/go-autorest/autorest/adal v0.9.0/go.mod h1:/c022QCutn2P7uY+/oQWWNcK9YU+MH96NgK+jErpbcg= +github.com/Azure/go-autorest/autorest/adal v0.9.5/go.mod h1:B7KF7jKIeC9Mct5spmyCB/A8CG/sEz1vwIRGv/bbw7A= +github.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74= +github.com/Azure/go-autorest/autorest/mocks v0.4.0/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k= +github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Microsoft/go-winio v0.4.11/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcyvVC4A4RozmA= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= +github.com/Microsoft/go-winio v0.4.15-0.20190919025122-fc70bd9a86b5/go.mod h1:tTuCMEN+UleMWgg9dVx4Hu52b1bJo+59jBh3ajtinzw= github.com/Microsoft/go-winio v0.4.16-0.20201130162521-d1ffc52c7331/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= github.com/Microsoft/go-winio v0.4.16 h1:FtSW/jqD+l4ba5iPBj9CODVtgfYAD8w2wS923g/cFDk= github.com/Microsoft/go-winio v0.4.16/go.mod h1:XB6nPKklQyQ7GC9LdcBEcBl8PF76WugXOPRXwdLnMv0= +github.com/Microsoft/go-winio v0.4.17-0.20210211115548-6eac466e5fa3/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.4.17-0.20210324224401-5516f17a5958/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.4.17/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/go-winio v0.5.0 h1:Elr9Wn+sGKPlkaBvwu4mTrxtmOp3F3yV9qhaHbXGjwU= +github.com/Microsoft/go-winio v0.5.0/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= +github.com/Microsoft/hcsshim v0.8.6/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= +github.com/Microsoft/hcsshim v0.8.7-0.20190325164909-8abdbb8205e4/go.mod h1:Op3hHsoHPAvb6lceZHDtd9OkTew38wNoXnJs8iY7rUg= +github.com/Microsoft/hcsshim v0.8.7/go.mod h1:OHd7sQqRFrYd3RmSgbgji+ctCwkbq2wbEYNSzOYtcBQ= +github.com/Microsoft/hcsshim v0.8.9/go.mod h1:5692vkUqntj1idxauYlpoINNKeqCiG6Sg38RRsjT5y8= github.com/Microsoft/hcsshim v0.8.14/go.mod h1:NtVKoYxQuTLx6gEq0L96c9Ju4JbRJ4nY2ow3VK6a9Lg= +github.com/Microsoft/hcsshim v0.8.15/go.mod h1:x38A4YbHbdxJtc0sF6oIz+RG0npwSCAvn69iY6URG00= +github.com/Microsoft/hcsshim v0.8.16/go.mod h1:o5/SZqmR7x9JNKsW3pu+nqHm0MF8vbA+VxGOoXdC600= +github.com/Microsoft/hcsshim v0.8.21/go.mod h1:+w2gRZ5ReXQhFOrvSQeNfhrYB/dg3oDwTOcER2fw4I4= +github.com/Microsoft/hcsshim/test v0.0.0-20201218223536-d3e5debf77da/go.mod h1:5hlzMzRKMLyo42nCZ9oml8AdTlq/0cvIaBv6tK1RehU= +github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3/go.mod h1:mw7qgWloBUl75W/gVH3cQszUg1+gUITj7D6NY7ywVnY= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7 h1:YoJbenK9C67SkzkDfmQuVln04ygHj3vjZfd9FL+GmQQ= github.com/ProtonMail/go-crypto v0.0.0-20210428141323-04723f9f07d7/go.mod h1:z4/9nQmJSSwwds7ejkxaJwO37dru3geImFUdJlaLzQo= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/acomagu/bufpipe v1.0.3/go.mod h1:mxdxdup/WdsKVreO5GpW4+M/1CE2sMG4jeGJ2sYmHc4= github.com/alcortesm/tgz v0.0.0-20161220082320-9c5fe88206d7/go.mod h1:6zEj6s6u/ghQa61ZWa/C2Aw3RkjiTBOix7dkqa1VLIs= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/alexflint/go-filemutex v0.0.0-20171022225611-72bdc8eae2ae/go.mod h1:CgnQgUtFrFz9mxFNtED3jI5tLDjKlOM+oUF/sTk6ps0= github.com/algolia/algoliasearch-client-go v2.25.0+incompatible h1:FGQr9l++u4uQPDXrW8jM5kNJm3Iw5SxEJJtYXSFmPRY= github.com/algolia/algoliasearch-client-go/v3 v3.18.1 h1:FP2Xtqqs/sefR5Qluygp+jVV+juXzEdJaPrZTCDLhDQ= github.com/algolia/algoliasearch-client-go/v3 v3.18.1/go.mod h1:i7tLoP7TYDmHX3Q7vkIOL4syVse/k5VJ+k0i8WqFiJk= +github.com/algolia/algoliasearch-client-go/v3 v3.21.0 h1:rpzkfDdImYGGjKZq+NrovY5vQabD3Fk0DnBtZPk/2jo= +github.com/algolia/algoliasearch-client-go/v3 v3.21.0/go.mod h1:i7tLoP7TYDmHX3Q7vkIOL4syVse/k5VJ+k0i8WqFiJk= github.com/anmitsu/go-shlex v0.0.0-20161002113705-648efa622239/go.mod h1:2FmKhYUyUczH0OGQWaF5ceTx0UBShxjsH6f8oGKYe2c= +github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/aws/aws-sdk-go v1.15.11/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0= +github.com/beorn7/perks v0.0.0-20160804104726-4c0e84591b9a/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= +github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= +github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA= +github.com/blang/semver v3.1.0+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013 h1:/P9/RL0xgWE+ehnCUUN5h3RpG3dmoMCOONO1CCvq23Y= github.com/bradfitz/slice v0.0.0-20180809154707-2b758aa73013/go.mod h1:pccXHIvs3TV/TUqSNyEvF99sxjX2r4FFRIyw6TZY9+w= +github.com/bshuster-repo/logrus-logstash-hook v0.4.1/go.mod h1:zsTqEiSzDgAa/8GZR7E1qaXrhYNDKBYy5/dWPTIflbk= +github.com/buger/jsonparser v0.0.0-20180808090653-f4dd9f5a6b44/go.mod h1:bbYlZJ7hK1yFx9hf58LP0zeX7UjIGs20ufpu3evjr+s= +github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= +github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= +github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= +github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/checkpoint-restore/go-criu/v4 v4.1.0/go.mod h1:xUQBLp4RLc5zJtWY++yjOoMoB5lihDt7fai+75m+rGw= +github.com/checkpoint-restore/go-criu/v5 v5.0.0/go.mod h1:cfwC0EG7HMUenopBsUf9d89JlCLQIfgVcNsNN0t6T2M= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= github.com/cilium/ebpf v0.0.0-20200110133405-4032b1d8aae3/go.mod h1:MA5e5Lr8slmEg9bt0VpxxWqJlO4iwu3FBdHUzV7wQVg= +github.com/cilium/ebpf v0.0.0-20200702112145-1c8d4c9ef775/go.mod h1:7cR51M8ViRLIdUjrmSXlK9pkrsDlLHbO8jiB8X8JnOc= +github.com/cilium/ebpf v0.2.0/go.mod h1:To2CFviqOWL/M0gIMsvSMlqe7em/l1ALkX1PyjrX2Qs= +github.com/cilium/ebpf v0.4.0/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= +github.com/cilium/ebpf v0.6.2/go.mod h1:4tRaxcgiL706VnOzHOdBlY8IEAIdxINsQBcU4xJJXRs= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= +github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= +github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= +github.com/containerd/aufs v0.0.0-20200908144142-dab0cbea06f4/go.mod h1:nukgQABAEopAHvB6j7cnP5zJ+/3aVcE7hCYqvIwAHyE= +github.com/containerd/aufs v0.0.0-20201003224125-76a6863f2989/go.mod h1:AkGGQs9NM2vtYHaUen+NljV0/baGCAPELGm2q9ZXpWU= +github.com/containerd/aufs v0.0.0-20210316121734-20793ff83c97/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= +github.com/containerd/aufs v1.0.0/go.mod h1:kL5kd6KM5TzQjR79jljyi4olc1Vrx6XBlcyj3gNv2PU= +github.com/containerd/btrfs v0.0.0-20201111183144-404b9149801e/go.mod h1:jg2QkJcsabfHugurUvvPhS3E08Oxiuh5W/g1ybB4e0E= +github.com/containerd/btrfs v0.0.0-20210316141732-918d888fb676/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= +github.com/containerd/btrfs v1.0.0/go.mod h1:zMcX3qkXTAi9GI50+0HOeuV8LU2ryCE/V2vG/ZBiTss= +github.com/containerd/cgroups v0.0.0-20190717030353-c4b9ac5c7601/go.mod h1:X9rLEHIqSf/wfK8NsPqxJmeZgW4pcfzdXITDrUSJ6uI= +github.com/containerd/cgroups v0.0.0-20190919134610-bf292b21730f/go.mod h1:OApqhQ4XNSNC13gXIwDjhOQxjWa/NxkwZXJ1EvqT0ko= github.com/containerd/cgroups v0.0.0-20200531161412-0dbf7f05ba59/go.mod h1:pA0z1pT8KYB3TCXK/ocprsh7MAkoW8bZVzPdih9snmM= +github.com/containerd/cgroups v0.0.0-20200710171044-318312a37340/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= +github.com/containerd/cgroups v0.0.0-20200824123100-0b889c03f102/go.mod h1:s5q4SojHctfxANBDvMeIaIovkq29IP48TKAxnhYRxvo= +github.com/containerd/cgroups v0.0.0-20210114181951-8a68de567b68/go.mod h1:ZJeTFisyysqgcCdecO57Dj79RfL0LNeGiFUqLYQRYLE= +github.com/containerd/cgroups v1.0.1/go.mod h1:0SJrPIenamHDcZhEcJMNBB85rHcUsw4f25ZfBiPYRkU= github.com/containerd/console v0.0.0-20180822173158-c12b1e7919c1/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= +github.com/containerd/console v0.0.0-20181022165439-0650fd9eeb50/go.mod h1:Tj/on1eG8kiEhd0+fhSDzsPAFESxzBBvdyEgyryXffw= +github.com/containerd/console v0.0.0-20191206165004-02ecf6a7291e/go.mod h1:8Pf4gM6VEbTNRIT26AyyU7hxdQU3MvAvxVI0sc00XBE= +github.com/containerd/console v1.0.1/go.mod h1:XUsP6YE/mKtz6bxc+I8UiKKTP04qjQL4qcS3XoQ5xkw= +github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8acV1PGKCbzQ= +github.com/containerd/containerd v1.2.10/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.0-beta.2.0.20190828155532-0293cbd26c69/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.3.1-0.20191213020239-082f7e3aed57/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.3.2/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.0-beta.2.0.20200729163537-40b22ef07410/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.1/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.4.3/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/containerd v1.4.4 h1:rtRG4N6Ct7GNssATwgpvMGfnjnwfjnu/Zs9W3Ikzq+M= github.com/containerd/containerd v1.4.4/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.4.11 h1:QCGOUN+i70jEEL/A6JVIbhy4f4fanzAzSR4kNG7SlcE= +github.com/containerd/containerd v1.4.11/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= +github.com/containerd/containerd v1.5.0-beta.1/go.mod h1:5HfvG1V2FsKesEGQ17k5/T7V960Tmcumvqn8Mc+pCYQ= +github.com/containerd/containerd v1.5.0-beta.3/go.mod h1:/wr9AVtEM7x9c+n0+stptlo/uBBoBORwEx6ardVcmKU= +github.com/containerd/containerd v1.5.0-beta.4/go.mod h1:GmdgZd2zA2GYIBZ0w09ZvgqEq8EfBp/m3lcVZIvPHhI= +github.com/containerd/containerd v1.5.0-rc.0/go.mod h1:V/IXoMqNGgBlabz3tHD2TWDoTJseu1FGOKuoA4nNb2s= +github.com/containerd/containerd v1.5.1/go.mod h1:0DOxVqwDy2iZvrZp2JUx/E+hS0UNTVn7dJnIOwtYR4g= +github.com/containerd/containerd v1.5.7 h1:rQyoYtj4KddB3bxG6SAqd4+08gePNyJjRqvOIfV3rkM= +github.com/containerd/containerd v1.5.7/go.mod h1:gyvv6+ugqY25TiXxcZC3L5yOeYgEw0QMhscqVp1AR9c= github.com/containerd/continuity v0.0.0-20190426062206-aaeac12a7ffc/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20190815185530-f2a389ac0a02/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20191127005431-f65d91d395eb/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= +github.com/containerd/continuity v0.0.0-20200710164510-efbc4488d8fe/go.mod h1:cECdGN1O8G9bgKTlLhuPJimka6Xb/Gg7vYzCTNVxhvo= +github.com/containerd/continuity v0.0.0-20201208142359-180525291bb7/go.mod h1:kR3BEg7bDFaEddKm54WSmrol1fKWDU1nKYkgrcgZT7Y= github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e h1:6JKvHHt396/qabvMhnhUZvWaHZzfVfldxE60TK8YLhg= github.com/containerd/continuity v0.0.0-20210208174643-50096c924a4e/go.mod h1:EXlVlkqNba9rJe3j7w3Xa924itAMLgZH4UD/Q4PExuQ= +github.com/containerd/continuity v0.1.0/go.mod h1:ICJu0PwR54nI0yPEnJ6jcS+J7CZAUXrLh8lPo2knzsM= +github.com/containerd/fifo v0.0.0-20180307165137-3d5202aec260/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= github.com/containerd/fifo v0.0.0-20190226154929-a9fb20d87448/go.mod h1:ODA38xgv3Kuk8dQz2ZQXpnv/UZZUHUCL7pnLehbXgQI= +github.com/containerd/fifo v0.0.0-20200410184934-f15a3290365b/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= +github.com/containerd/fifo v0.0.0-20201026212402-0724c46b320c/go.mod h1:jPQ2IAeZRCYxpS/Cm1495vGFww6ecHmMk1YJH2Q5ln0= +github.com/containerd/fifo v0.0.0-20210316144830-115abcc95a1d/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= +github.com/containerd/fifo v1.0.0/go.mod h1:ocF/ME1SX5b1AOlWi9r677YJmCPSwwWnQ9O123vzpE4= +github.com/containerd/go-cni v1.0.1/go.mod h1:+vUpYxKvAF72G9i1WoDOiPGRtQpqsNW/ZHtSlv++smU= +github.com/containerd/go-cni v1.0.2/go.mod h1:nrNABBHzu0ZwCug9Ije8hL2xBCYh/pjfMb1aZGrrohk= github.com/containerd/go-runc v0.0.0-20180907222934-5a6d9f37cfa3/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/go-runc v0.0.0-20190911050354-e029b79d8cda/go.mod h1:IV7qH3hrUgRmyYrtgEeGWJfWbgcHL9CSRruz2Vqcph0= +github.com/containerd/go-runc v0.0.0-20200220073739-7016d3ce2328/go.mod h1:PpyHrqVs8FTi9vpyHwPwiNEGaACDxT/N/pLcvMSRA9g= +github.com/containerd/go-runc v0.0.0-20201020171139-16b287bc67d0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= +github.com/containerd/go-runc v1.0.0/go.mod h1:cNU0ZbCgCQVZK4lgG3P+9tn9/PaJNmoDXPpoJhDR+Ok= +github.com/containerd/imgcrypt v1.0.1/go.mod h1:mdd8cEPW7TPgNG4FpuP3sGBiQ7Yi/zak9TYCG3juvb0= +github.com/containerd/imgcrypt v1.0.4-0.20210301171431-0ae5c75f59ba/go.mod h1:6TNsg0ctmizkrOgXRNQjAPFWpMYRWuiB6dSF4Pfa5SA= +github.com/containerd/imgcrypt v1.1.1-0.20210312161619-7ed62a527887/go.mod h1:5AZJNI6sLHJljKuI9IHnw1pWqo/F0nGDOuR9zgTs7ow= +github.com/containerd/imgcrypt v1.1.1/go.mod h1:xpLnwiQmEUJPvQoAapeb2SNCxz7Xr6PJrXQb0Dpc4ms= +github.com/containerd/nri v0.0.0-20201007170849-eb1350a75164/go.mod h1:+2wGSDGFYfE5+So4M5syatU0N0f0LbWpuqyMi4/BE8c= +github.com/containerd/nri v0.0.0-20210316161719-dbaa18c31c14/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= +github.com/containerd/nri v0.1.0/go.mod h1:lmxnXF6oMkbqs39FiCt1s0R2HSMhcLel9vNL3m4AaeY= github.com/containerd/ttrpc v0.0.0-20190828154514-0e0f228740de/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/ttrpc v0.0.0-20190828172938-92c8520ef9f8/go.mod h1:PvCDdDGpgqzQIzDW1TphrGLssLDZp2GuS+X5DkEJB8o= +github.com/containerd/ttrpc v0.0.0-20191028202541-4f1b8fe65a5c/go.mod h1:LPm1u0xBw8r8NOKoOdNMeVHSawSsltak+Ihv+etqsE8= +github.com/containerd/ttrpc v1.0.1/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= +github.com/containerd/ttrpc v1.0.2/go.mod h1:UAxOpgT9ziI0gJrmKvgcZivgxOp8iFPSk8httJEt98Y= github.com/containerd/typeurl v0.0.0-20180627222232-a93fcdb778cd/go.mod h1:Cm3kwCdlkCfMSHURc+r6fwoGH6/F1hH3S4sg0rLFWPc= +github.com/containerd/typeurl v0.0.0-20190911142611-5eb25027c9fd/go.mod h1:GeKYzf2pQcqv7tJ0AoCuuhtnqhva5LNU3U+OyKxxJpk= +github.com/containerd/typeurl v1.0.1/go.mod h1:TB1hUtrpaiO88KEK56ijojHS1+NeF0izUACaJW2mdXg= +github.com/containerd/typeurl v1.0.2/go.mod h1:9trJWW2sRlGub4wZJRTW83VtbOLS6hwcDZXTn6oPz9s= +github.com/containerd/zfs v0.0.0-20200918131355-0a33824f23a2/go.mod h1:8IgZOBdv8fAgXddBT4dBXJPtxyRsejFIpXoklgxgEjw= +github.com/containerd/zfs v0.0.0-20210301145711-11e8f1707f62/go.mod h1:A9zfAbMlQwE+/is6hi0Xw8ktpL+6glmqZYtevJgaB8Y= +github.com/containerd/zfs v0.0.0-20210315114300-dde8f0fda960/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= +github.com/containerd/zfs v0.0.0-20210324211415-d5c4544f0433/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= +github.com/containerd/zfs v1.0.0/go.mod h1:m+m51S1DvAP6r3FcmYCp54bQ34pyOwTieQDNRIRHsFY= +github.com/containernetworking/cni v0.7.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/cni v0.8.0/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/cni v0.8.1/go.mod h1:LGwApLUm2FpoOfxTDEeq8T9ipbpZ61X79hmU3w8FmsY= +github.com/containernetworking/plugins v0.8.6/go.mod h1:qnw5mN19D8fIwkqW7oHHYDHVlzhJpcY6TQxn/fUyDDM= +github.com/containernetworking/plugins v0.9.1/go.mod h1:xP/idU2ldlzN6m4p5LmGiwRDjeJr6FLK6vuiUwoH7P8= +github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc= +github.com/containers/ocicrypt v1.1.0/go.mod h1:b8AOe0YR67uU8OqfVNcznfFpAzu3rdgUV4GP9qXPfu4= +github.com/containers/ocicrypt v1.1.1/go.mod h1:Dm55fwWm1YZAjYRaJ94z2mfZikIyIN4B0oB3dj3jFxY= +github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-iptables v0.4.5/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= +github.com/coreos/go-iptables v0.5.0/go.mod h1:/mVI274lEDI2ns62jHCDnCyBF9Iwsmekav8Dbxlm1MU= +github.com/coreos/go-oidc v2.1.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/coreos/go-systemd v0.0.0-20161114122254-48702e0da86b/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20180511133405-39ca1b05acc7/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk= +github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.11 h1:07n33Z8lZxZ2qwegKbObQohDhXDQxiMMz1NOUGYlesw= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.16 h1:vfetlOf3A+9YKggibynnX9mnFjuSVvkRj+IWpcTSLEQ= +github.com/creack/pty v1.1.16/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= +github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c/go.mod h1:Ct2BUK8SB0YC1SMSibvLzxjeJLnrYEVLULFNiHY9YfQ= +github.com/d2g/dhcp4client v1.0.0/go.mod h1:j0hNfjhrt2SxUOw55nL0ATM/z4Yt3t2Kd1mW34z5W5s= +github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5/go.mod h1:Eo87+Kg/IX2hfWJfwxMzLyuSZyxSoAug2nGa1G2QAi8= +github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4/go.mod h1:bMl4RjIciD2oAxI7DmWRx6gbeqrkoLqv3MV0vzNad+I= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba/go.mod h1:dV8lFg6daOBZbT6/BDGIz6Y3WFGn8juu6G+CQ6LHtl0= +github.com/dgrijalva/jwt-go v0.0.0-20170104182250-a601269ab70c/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no= +github.com/dnaeon/go-vcr v1.0.1/go.mod h1:aBB1+wY4s93YsC3HHjMBMrwTj2R9FHDzUr9KyGc8n1E= +github.com/docker/distribution v0.0.0-20190905152932-14b96e55d84c/go.mod h1:0+TTO4EOBfRPhZXAeF1Vu+W3hHZ8eLp8PgKVZlcvtFY= +github.com/docker/distribution v2.7.1-0.20190205005809-0d3efadf0154+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.7.1+incompatible h1:a5mlkVzth6W5A4fOsS3D2EO5BUmsJpcB+cRlLU7cSug= github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v1.13.1 h1:IkZjBSIc8hBjLpqeAbeE5mca5mNgeatLHBy3GO78BWo= github.com/docker/docker v20.10.3-0.20210216175712-646072ed6524+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v20.10.5+incompatible h1:o5WL5onN4awYGwrW7+oTn5x9AF2prw7V0Ox8ZEkoCdg= github.com/docker/docker v20.10.5+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v20.10.9+incompatible h1:JlsVnETOjM2RLQa0Cc1XCIspUdXW3Zenq9P54uXBm6k= +github.com/docker/docker v20.10.9+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ= github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= +github.com/docker/go-events v0.0.0-20170721190031-9461782956ad/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= +github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= +github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw= github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96/go.mod h1:Qh8CwZgvJUkLughtfhJv5dyTYa91l1fOUCrgjqmcifM= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= +github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/elastic/go-elasticsearch v0.0.0 h1:Pd5fqOuBxKxv83b0+xOAJDAkziWYwFinWnBO0y+TZaA= github.com/elastic/go-elasticsearch/v8 v8.0.0-20210531084204-f01628963386 h1:r0QXRPqcbciA/BJPhNE4kd5tEJR1AhyqoBXsV452XA4= github.com/elastic/go-elasticsearch/v8 v8.0.0-20210531084204-f01628963386/go.mod h1:xe9a/L2aeOgFKKgrO3ibQTnMdpAeL0GC+5/HpGScSa4= github.com/elastic/go-elasticsearch/v8 v8.0.0-20210608143047-aa1301e7ba9d h1:id0CyeIuvJ9hzYhLlKsXHQ10d/k4G+CexAu53Pl3hf4= github.com/elastic/go-elasticsearch/v8 v8.0.0-20210608143047-aa1301e7ba9d/go.mod h1:xe9a/L2aeOgFKKgrO3ibQTnMdpAeL0GC+5/HpGScSa4= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153/go.mod h1:/Zj4wYkgs4iZTTu3o/KG3Itv/qCCa8VVMlb3i9OVuzc= +github.com/emicklei/go-restful v0.0.0-20170410110728-ff4f55a20633/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= +github.com/emicklei/go-restful v2.9.5+incompatible/go.mod h1:otzb+WCGbkyDHkqmQmT5YD2WR4BBwUdeQoFo8l/7tVs= github.com/emirpasic/gods v1.12.0 h1:QAUIPSaCu4G+POclxeqb3F+WPpdKqFGlw36+yOzGlrg= github.com/emirpasic/gods v1.12.0/go.mod h1:YfzfFFoVP/catgzJb4IKIqXjX78Ha8FMSDh3ymbK86o= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po= +github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= +github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= +github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/evanphx/json-patch v4.9.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= github.com/flynn/go-shlex v0.0.0-20150515145356-3f9db97f8568/go.mod h1:xEzjJPgXI435gkrCt3MPfRiAkVrwSbHsst4LCFVfpJc= +github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k= github.com/frikky/go-elasticsearch/v8 v8.13.1 h1:GB+Wr0Yx8efG7D1jc9fGGiqjjRRngWJTcMSua3QIDaM= github.com/frikky/go-elasticsearch/v8 v8.13.1/go.mod h1:RPq0JXPQVVSFHTlPwj/go8BZ1hegRf+StaSpT2iGIoQ= github.com/frikky/kin-openapi v0.38.0 h1:V7ttwIJS8Vks4KL+mZVj1ZSqhIcQtgaG8akeqXEQgsE= github.com/frikky/kin-openapi v0.38.0/go.mod h1:Fr28TtCHL4K0kIqtqui8HWxN1LG5uAh3z/tDfFyiA1s= +github.com/frikky/kin-openapi v0.40.0 h1:D/b5MvxbQFn6J82Lfw7Gr1CzcNtjH4O6FSY4QuHHYGI= +github.com/frikky/kin-openapi v0.40.0/go.mod h1:ev9OZAw7Bv5p0w93j91++6a1ElPzGcCofst+kmrWsj4= github.com/frikky/shuffle-shared v0.0.20 h1:y6JlPnQDq//elICWvVfUfJyU9gH3fSpQmPy+agqZ5sA= github.com/frikky/shuffle-shared v0.0.20/go.mod h1:H7SqOta/EAYnfYuWzwzYSh/oWfF0kgnuaJTQNKQBvoQ= github.com/frikky/shuffle-shared v0.0.24 h1:AqaeKE+lUN9G2pfdFKlXD1ImAzcMjcpuGMSqjt15RIk= @@ -144,8 +359,17 @@ github.com/frikky/shuffle-shared v0.0.85 h1:0Xs1omjCzBKCxk6RZPrqyHx0UIuizMAEtAmP github.com/frikky/shuffle-shared v0.0.85/go.mod h1:pUK3pKKOFtu4mPJ68c396WqXJF9YTKWKtsAy871HdGY= github.com/frikky/shuffle-shared v0.0.88 h1:KmP41FLWUlOw5E954LYrArJy/QMiviUQw/5LxSKqzqo= github.com/frikky/shuffle-shared v0.0.88/go.mod h1:pUK3pKKOFtu4mPJ68c396WqXJF9YTKWKtsAy871HdGY= +github.com/frikky/shuffle-shared v0.0.95 h1:Uox2d1ZTjtG/tM54sJwQSzmxhTU387PDzzCYrwgTmZE= +github.com/frikky/shuffle-shared v0.0.95/go.mod h1:w7x1+j3xsAB5T5eeTbPSqs0egVadcoKzYW5GbNdretI= +github.com/frikky/shuffle-shared v0.1.15 h1:73XfrsY211Qjdo1RIU7NCl6HMguby0h4QJMUkQx9wQA= +github.com/frikky/shuffle-shared v0.1.15/go.mod h1:w7x1+j3xsAB5T5eeTbPSqs0egVadcoKzYW5GbNdretI= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= github.com/fsouza/go-dockerclient v1.7.2 h1:bBEAcqLTkpq205jooP5RVroUKiVEWgGecHyeZc4OFjo= github.com/fsouza/go-dockerclient v1.7.2/go.mod h1:+ugtMCVRwnPfY7d8/baCzZ3uwB0BrG5DB8OzbtxaRz8= +github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa/go.mod h1:KnogPXtdwXqoenmZCw6S+25EAm2MkxbG0deNDu4cbSA= +github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYisc3/PwhQ00EMzDiPmrwpPxAn5GI05/YaO1SY= +github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/gliderlabs/ssh v0.2.2/go.mod h1:U7qILu1NlMHj9FlMhZLlkCdDnU1DBEAqr0aevW3Awn0= @@ -164,19 +388,49 @@ github.com/go-git/go-git/v5 v5.4.2/go.mod h1:gQ1kArt6d+n+BGd+/B/I74HwRTLhth2+zti github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-ini/ini v1.25.4/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= +github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= +github.com/go-logr/logr v0.2.0/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU= +github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= +github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= +github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= +github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY= github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/godbus/dbus v0.0.0-20151105175453-c7fdd8b5cd55/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= +github.com/godbus/dbus v0.0.0-20180201030542-885f9cc04c9c/go.mod h1:/YcGZj5zSblfDWMMoOzV4fas9FZnQYTkDnsGvmh2Grw= +github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e/go.mod h1:bBOAhwG1umN6/6ZUMtDFBMQR8jRg9O75tm9K00oMsK4= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= +github.com/gogo/googleapis v1.2.0/go.mod h1:Njal3psf3qN6dwBtQfUmBZh2ybovJ0tlu3o/AC7HYjU= +github.com/gogo/googleapis v1.4.0/go.mod h1:5YRNX2z1oM5gXdAkurHa942MDgEJyk02w4OecKY87+c= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4= +github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/gogo/protobuf v1.3.0/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.1/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e h1:1r7pUrabqp18hOBcwBwiTsbnFeTZHV9eER/QT5JVZxY= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y= @@ -184,6 +438,8 @@ github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw= github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4= +github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3KVu8= +github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= @@ -199,6 +455,14 @@ github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QD github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.4.3 h1:JjCZWpVbqXDqFVmTfYWEVTMIYrL/NPdPSCHPJ0T/raM= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx4u74HPM= +github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw= +github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/snappy v0.0.3 h1:fHPg5GQYlCeLIPB9BZqMVR5nR9A+IM5zcgeTdjMYmLA= +github.com/golang/snappy v0.0.3/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ= github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= @@ -209,18 +473,29 @@ github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4 h1:L8R9j+yAqZuZjsqh/z+F1NCffTKKLShY6zXTItVIZ8M= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= github.com/google/go-github/v28 v28.1.1 h1:kORf5ekX5qwXO2mGzXXOjMe/g6ap8ahVe0sBEulhSxo= github.com/google/go-github/v28 v28.1.1/go.mod h1:bsqJWQX05omyWVmc00nEUql9mhQyv38lDZ8kPZcQVoM= github.com/google/go-querystring v1.0.0 h1:Xkwi/a1rcvNg1PPYe5vI8GbeBY/jrVuDX5ASuANWTrk= github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck= +github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= +github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.1.0 h1:wCKgOCHuUEVfsaQLpPSJb7VdYCdTVZQAuOdYm1yc/60= github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= +github.com/google/martian/v3 v3.2.1 h1:d8MncMlErDFTwQGBK1xhv026j9kqhvw1Qv9IbWT1VLQ= +github.com/google/martian/v3 v3.2.1/go.mod h1:oBOf6HBosgwRXnUGWUB05QECsc6uvmMiJ3+6W4l/CUk= github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc= github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= @@ -230,111 +505,333 @@ github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hf github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20200905233945-acf8798be1f7/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM= github.com/google/pprof v0.0.0-20201023163331-3e6fc7fc9c4c/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20201203190320-1bf35d6f28c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20201218002935-b9804c9f04c2/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210122040257-d980be63207e/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210601050228-01bbb1931b22/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210609004039-a478d1d731e9/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= +github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5 h1:sjZBwGj9Jlw33ImPtvFviGYvseOtDM7hkSKB7+Tv3SM= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= +github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= +github.com/googleapis/gax-go/v2 v2.1.1 h1:dp3bWCh+PPO1zjRRiCSczJav13sBvG4UhNyVTa1KqdU= +github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gnostic v0.4.1/go.mod h1:LRhVm6pbyptWbWbuZ38d1eyptfvIytN3ir6b65WBswg= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/gorilla/handlers v0.0.0-20150720190736-60c7bfde3e33/go.mod h1:Qkdc/uu4tH4g6mTK6auzZ766c4CA0Ng8+o/OAirnOIQ= +github.com/gorilla/mux v1.7.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= +github.com/gorilla/websocket v0.0.0-20170926233335-4201258b820c/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.0/go.mod h1:E7qHFY5m1UJ88s3WnNqhKjPHQ0heANvMoAMk2YaljkQ= +github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= +github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.9.5/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= +github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= +github.com/hashicorp/errwrap v0.0.0-20141028054710-7554cd9344ce/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v0.0.0-20161216184304-ed905158d874/go.mod h1:JMRHfdO9jKNzS/+BTlxCjKNQHg/jZAft8U7LloJvN7I= +github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/imdario/mergo v0.3.5/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.8/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= +github.com/imdario/mergo v0.3.10/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/imdario/mergo v0.3.11/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/j-keck/arping v0.0.0-20160618110441-2cf9dc699c56/go.mod h1:ymszkNOg6tORTn+6F6j+Jc8TOr5osrynvN6ivFWZ2GA= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 h1:BQSFePA1RWJOlocH6Fxy8MmwDt+yVQYULKfN0RoTN8A= github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99/go.mod h1:1lJo3i6rXxKeerYnT8Nvf0QmHCRC1n8sfWVwXF2Frvo= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.5.0/go.mod h1:Fw0T6WPc1dYxT4mKEZRfG5kJhaTDP9pj1c2EWnYs/m4= +github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jmespath/go-jmespath v0.0.0-20160803190731-bd40a432e4c7/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k= +github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= +github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/kevinburke/ssh_config v0.0.0-20190725054713-01f96b0aa0cd/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351 h1:DowS9hvgyYSX4TO5NpyC606/Z4SxnNYbT+WX27or6Ck= github.com/kevinburke/ssh_config v0.0.0-20201106050909-4977a11b4351/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF4nAY/ojJ6r6mM= +github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/klauspost/compress v1.11.3/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= +github.com/klauspost/compress v1.11.13/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= +github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/marstr/guid v1.1.0/go.mod h1:74gB1z2wpxxInTG6yaqA7KrtM0NZ+RbrcqDvYHefzho= github.com/matryer/is v1.2.0/go.mod h1:2fLPjFQM9rhQ15aVEtbuwhJinnOqrmgXPNdZsdwlWXA= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= +github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= +github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f/go.mod h1:OkQIRizQZAeMln+1tSwduZz7+Af5oFlKirV/MSYes2A= +github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= github.com/moby/sys/mount v0.2.0 h1:WhCW5B355jtxndN5ovugJlMFJawbUODuW8fSnEH6SSM= github.com/moby/sys/mount v0.2.0/go.mod h1:aAivFE2LB3W4bACsUXChRHQ0qKWsetY4Y9V7sxOougM= github.com/moby/sys/mountinfo v0.4.0 h1:1KInV3Huv18akCu58V7lzNlt+jFmqlu1EaErnEHE/VM= github.com/moby/sys/mountinfo v0.4.0/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +github.com/moby/sys/mountinfo v0.4.1/go.mod h1:rEr8tzG/lsIZHBtN/JjGG+LMYx9eXgW2JI+6q0qou+A= +github.com/moby/sys/symlink v0.1.0/go.mod h1:GGDODQmbFOjFsXvfLVn3+ZRxkch54RkSiGqsZeMYowQ= +github.com/moby/term v0.0.0-20200312100748-672ec06f55cd/go.mod h1:DdlQx2hp0Ss5/fLikoLlEeIYiATotOjgB//nb973jeo= github.com/moby/term v0.0.0-20201216013528-df9cb8a40635 h1:rzf0wL0CHVc8CEsgyygG0Mn9CNCCPZqOPaz8RiiHYQk= github.com/moby/term v0.0.0-20201216013528-df9cb8a40635/go.mod h1:FBS0z0QWA44HXygs7VXDUOGoN/1TV3RuWkLO04am3wc= +github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6 h1:dcztxKSvZ4Id8iPpHERQBbIJfabdt4wUm5qy3wOL2Zc= +github.com/moby/term v0.0.0-20210619224110-3f7ff695adc6/go.mod h1:E2VnQOmVuvZB6UYnnDB0qG5Nq/1tD9acaOpo6xmt0Kw= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= +github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/ncw/swift v1.0.47/go.mod h1:23YIA4yWVnGwv2dQlN4bB7egfYX6YLn0Yo/S6zZO/ZM= github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= +github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= +github.com/onsi/ginkgo v0.0.0-20151202141238-7f8ab55aaf3b/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.1/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.11.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= +github.com/onsi/gomega v0.0.0-20151007035656-2152b45fa28a/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= +github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.10.3/go.mod h1:V9xEwhxec5O8UDM77eCW8vLymOMltsqPVYWrpDsH8xc= +github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v0.0.0-20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= +github.com/opencontainers/go-digest v1.0.0-rc1.0.20180430190053-c9281466c8b2/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.0.0/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.0.1 h1:JMemWkRwHx4Zj+fVxWoMCFm/8sYGGrUVojFA6h/TRcI= github.com/opencontainers/image-spec v1.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/runc v0.0.0-20190115041553-12f6a991201f/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= github.com/opencontainers/runc v0.1.1 h1:GlxAyO6x8rfZYN9Tt0Kti5a/cP41iuiO2yYT0IJGY8Y= github.com/opencontainers/runc v0.1.1/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc8.0.20190926000215-3e425f80a8c9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc9/go.mod h1:qT5XzbpPznkRYVz/mWwUaVBUv2rmF59PVA73FjuZG0U= +github.com/opencontainers/runc v1.0.0-rc93/go.mod h1:3NOsor4w32B2tC0Zbl8Knk4Wg84SM2ImC1fxBuqJ/H0= +github.com/opencontainers/runc v1.0.2/go.mod h1:aTaHFFwQXuA71CiyxOdFFIorAoemI04suvGRQFzWTD0= +github.com/opencontainers/runtime-spec v0.1.2-0.20190507144316-5b71a03e2700/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.1/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.2-0.20190207185410-29686dbc5559/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.0.2/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.3-0.20200929063507-e6143ca7d51d/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-spec v1.0.3-0.20210326190908-1c3f411f0417/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-tools v0.0.0-20181011054405-1d69bd0f9c39/go.mod h1:r3f7wjNzSs2extwzU3Y+6pKfobzPh+kKFJ3ofN+3nfs= +github.com/opencontainers/selinux v1.6.0/go.mod h1:VVGKuOLlE7v4PJyT6h7mNWvq1rzqiriPsEqVhc+svHE= +github.com/opencontainers/selinux v1.8.0/go.mod h1:RScLhm78qiWa2gbVCcGkC7tCGdgk3ogry1nUQF8Evvo= +github.com/opencontainers/selinux v1.8.2/go.mod h1:MUIHuUEvKB1wtJjQdOyYRgOnLD2xAPP8dBsCoU0KuF8= github.com/patrickmn/go-cache v1.0.0 h1:3gD5McaYs9CxjyK5AXGcq8gdeCARtd/9gJDUvVeaZ0Y= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pelletier/go-buffruneio v0.2.0/go.mod h1:JkE26KsDizTr40EUHkXVtNPvgGtbSNq5BcowyYOWdKo= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pelletier/go-toml v1.8.1/go.mod h1:T2/BmBdy8dvIRq1a/8aqjN41wvWlN4lrapLU/GW4pbc= +github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1-0.20171018195549-f15c970de5b7/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/pquerna/cachecontrol v0.0.0-20171018203845-0dec1b30a021/go.mod h1:prYjPmNq4d1NPVmpShWobRqXY3q7Vp+80DqgxxUrUIA= +github.com/prometheus/client_golang v0.0.0-20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= +github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M= +github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/common v0.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= +github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo= github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.0-20190522114515-bc1a522cf7b1/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.5/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= +github.com/prometheus/procfs v0.0.8/go.mod h1:7Qr8sr6344vo1JqZ6HhLceV9o3AJ1Ff+GxbHq6oeK9A= +github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.2.0/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU= +github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA= +github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU= +github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg= +github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/rogpeppe/go-internal v1.8.0 h1:FCbCCtXNOY3UtUuHUYaghJg4y7Fd14rXifAYUAtL9R8= +github.com/rogpeppe/go-internal v1.8.0/go.mod h1:WmiCO8CzOY8rg0OYDC4/i/2WRWAB6poM+XZ2dLUbcbE= github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/rwcarlsen/goexif v0.0.0-20190401172101-9e8deecbddbd/go.mod h1:hPqNNc0+uJM6H+SuU8sEs5K5IQeKccPqeSjfgcKGgPk= +github.com/safchain/ethtool v0.0.0-20190326074333-42ed695e3de8/go.mod h1:Z0q5wiBQGYcxhMZ6gUqHn6pYNLypFAvaL3UvgZLR0U4= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo= github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo= github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/sirupsen/logrus v1.0.4-0.20170822132746-89742aefa4b2/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88= github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM= github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= +github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM= +github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.2-0.20171109065643-2da4a54c5cee/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/cobra v1.0.0/go.mod h1:/6GTrnGXV9HjY+aR4k0oJ5tcvakLuG6EuKReYlHNrgE= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1-0.20171106142849-4c012f6dcd95/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/yZzE= github.com/src-d/gcfg v1.4.0 h1:xXbNR5AlLSA315x2UO+fTSSAXCDf+Ar38/6oyGbDKQ4= github.com/src-d/gcfg v1.4.0/go.mod h1:p/UMsR43ujA89BJY9duynAwIpvqEujIH/jFlfL7jWoI= +github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980/go.mod h1:AO3tvPzVZ/ayst6UlUKUv6rcPQInYe3IknH3jYhAKu8= +github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/syndtr/gocapability v0.0.0-20170704070218-db04d3cc01c8/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/syndtr/gocapability v0.0.0-20180916011248-d98352740cb2/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= +github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= +github.com/ugorji/go v1.1.4/go.mod h1:uQMGLiO92mf5W77hV/PUCpI3pbzQx3CRekS0kk+RGrc= +github.com/urfave/cli v0.0.0-20171014202726-7bc6a0acffa5/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= +github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= github.com/urfave/cli v1.22.2/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0= +github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk= +github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= +github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= +github.com/vishvananda/netns v0.0.0-20180720170159-13995c7128cc/go.mod h1:ZjcWmFBXmLKZu9Nxj3WKYEafiSqer2rnvPr0en9UNpI= +github.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU= +github.com/vishvananda/netns v0.0.0-20200728191858-db3c7e526aae/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0= +github.com/willf/bitset v1.1.11-0.20200630133818-d5bec3311243/go.mod h1:RjeCKbqT1RxIR/KWY6phxZiaY1IyutSBfGjNPySAYV4= +github.com/willf/bitset v1.1.11/go.mod h1:83CECat5yLh5zVOf4P1ErAgKA5UDvKtgyUABdr3+MjI= github.com/xanzy/ssh-agent v0.2.1/go.mod h1:mLlQY/MoOhWBj+gOGMQkOeiEvkx+8pJSI+0Bx9h2kr4= github.com/xanzy/ssh-agent v0.3.0 h1:wUMzuKtKilRgBAD1sUb8gOwwRr2FGoBVumcjoOACClI= github.com/xanzy/ssh-agent v0.3.0/go.mod h1:3s9xbODqPuuhK9JV1R321M/FlMZSBvE5aY6eAcqrDh0= +github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= +github.com/xeipuuv/gojsonschema v0.0.0-20180618132009-1d523034197f/go.mod h1:5yf86TLmAcydyeJq5YvxkGPE2fm/u4myDekKRoLuqhs= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= +github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43/go.mod h1:aX5oPXxHm3bOH+xeAttToC8pqch2ScQN/JoXYupl6xs= +github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50/go.mod h1:NUSPSUX/bi6SeDMUh6brw0nXpxHnc96TguQh0+r/ssA= +github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f/go.mod h1:GlGEuHIJweS1mbCqG+7vt2nvWLzLLnRHbXz5JKd/Qbg= +go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= +go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ= +go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg= +go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1/go.mod h1:SNgMg+EgDFwmvSmLRTNKC5fegJjB7v23qTQ0XLGUNHk= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -342,17 +839,34 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5 h1:dntmOdLpSpHlVqbW5Eay97DelsZHe+55D+xC6i0dDS0= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= +go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M= +go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= +go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go4.org v0.0.0-20201209231011-d4a079459e60 h1:iqAGo78tVOJXELHQFRjR6TMwItrvXH4hrGJ32I/NFF8= +go4.org v0.0.0-20201209231011-d4a079459e60/go.mod h1:CIiUVy99QCPfoE13bO4EZaz5GZMZXMSBGhxRdsvzbkg= +golang.org/x/crypto v0.0.0-20171113213409-9f005a07e0d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/crypto v0.0.0-20181009213950-7c1a557ab941/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190219172222-a4c6cb3142f2/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200728195943-123391ffb6de/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b h1:7mWr3k41Qtv8XlltBkDkl8LoP3mpSgBW8BUoxtEdbXg= golang.org/x/crypto v0.0.0-20210421170649-83a5a9bb288b/go.mod h1:T9bdIzuCu7OtxOm1hfPfRQxPLYneinmdGuTeoZ9dtd4= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519 h1:7I4JAnoQBe7ZtJcBaYHi5UtiO8tQHbUSXxL+pnGRANg= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= @@ -377,6 +891,7 @@ golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPI golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5 h1:2M3HP5CCK1Si9FQhwnzYhXdG6DXeebvUHFpre8QvbyI= golang.org/x/lint v0.0.0-20201208152925-83fdc39ff7b5/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= @@ -388,19 +903,29 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1 h1:Kvvh58BN8Y9/lBi7hTekvtMpm07eUZ0ck5pRHpsMWrY= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181011144130-49bb7cea24b1/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190619014844-b5b0513f8c1b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191004110552-13f9640d40b9/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -417,13 +942,23 @@ golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81R golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA= +golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= +golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20201224014010-6772e930b67b h1:iFwSg7t5GZmB/Q5TjiEAsdoLDrdJRC1RiF2WhuV29Qw= golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210119194325-5f4716e94777/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210326060303-6b1517762897 h1:KrsHThm5nFk34YtATK1LsThyGhGbGe1olrte/HInHvs= golang.org/x/net v0.0.0-20210326060303-6b1517762897/go.mod h1:uSPa2vr4CLtc/ILN5odXGNXS6mhrKVzTaCXzk9m6W3k= +golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= +golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211014172544-2b766c08f1c0 h1:xNP8gGXzUFztyWFRq+TV6zyPNxOr8lXtV6x0KMrhk0o= +golang.org/x/net v0.0.0-20211014172544-2b766c08f1c0/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -434,6 +969,15 @@ golang.org/x/oauth2 v0.0.0-20201109201403-9fd604954f58/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20201208152858-08078c50e5b5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210113160501-8b1d76fa0423 h1:/hEknzWkMPCjTo7StMHRrBRa8YBbXuBWfck8680k3RE= golang.org/x/oauth2 v0.0.0-20210113160501-8b1d76fa0423/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210218202405-ba52d332ba99/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210220000619-9bb904979d93/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210313182246-cd4f82c27b84/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1 h1:B333XXssMuKQeBwiNODx4TupZy7bf4sxFZnN2ZOcvUE= +golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -444,8 +988,13 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190221075227-b4e8571b14e0/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -453,20 +1002,36 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190514135907-3a4b5fb9f71f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190602015325-4c4f7f33c9ed/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190812073006-9eafafc0a87e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190826190057-c7b8b68b1456/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191022100944-742c48ecaeb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191210023423-ac6580df4449/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200120151820-655fe14d7479/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200124204421-9fbb57f87de9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -475,22 +1040,52 @@ golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200622214017-ed371f2e16b4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200728102440-3e129f6d46b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200817155316-9781c653f443/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200828194041-157a740278f4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200831180312-196b9ba8737a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200909081042-eff7692f9009/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200916030750-2334cc1a136f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200922070232-aee5d888a860/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201112073958-5cba982894dd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201117170446-d9b008d0a637/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3 h1:kzM6+9dur93BcC2kVlYl34cHU+TYZLanmpSJHVMmL64= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201202213521-69691e467435/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210216224549-f992740a1bac h1:9glrpwtNjBYgRpb67AZJKHfzj1stG/8BL5H7In2oTC4= golang.org/x/sys v0.0.0-20210216224549-f992740a1bac/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210320140829-1e4c9ba3b0c4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210324051608-47abb6519492/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79 h1:RX8C8PRZc2hTIod4ds8ij+/4RQX3AqhYj3uOHmyaz4E= golang.org/x/sys v0.0.0-20210502180810-71e4cd670f79/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211013075003-97ac67df715c h1:taxlMj0D/1sOAuv/CbSD+MMDof2vbyPTqz5FNYKpXt8= +golang.org/x/sys v0.0.0-20211013075003-97ac67df715c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201113234701-d7a72108b828/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -500,9 +1095,19 @@ golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.4 h1:0YWbFKbhXG/wIiuHDSKpS0Iy7FSA+u45VtBMfQcFTTc= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20191024005414-555d28b269f0 h1:/5xXl8Y5W96D+TtHSlonuFqGHIWVuyCkGJLwGh9JJFs= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20200630173020-3af7569d3a1e/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac h1:7zkz7BUtwNFFqcowJ+RIgu2MaV/MapERkDIy+mwPyjs= +golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -510,10 +1115,12 @@ golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3 golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= @@ -553,15 +1160,25 @@ golang.org/x/tools v0.0.0-20200915173823-2db8f0ff891c/go.mod h1:z6u4i615ZeAfBE4X golang.org/x/tools v0.0.0-20200918232735-d647fc253266/go.mod h1:z6u4i615ZeAfBE4XtMziQW1fSVJXACjjbWkB/mvPzlU= golang.org/x/tools v0.0.0-20201110124207-079ba7bd75cd/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20201201161351-ac6f37ff4c2a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20201208233053-a543418bbed2/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963 h1:K+NlvTLy0oONtRtkl1jRD9xIhnItbG2PiE7YOdjPb+k= golang.org/x/tools v0.0.0-20210114065538-d78b04bdf963/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= +golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0= +golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/api v0.0.0-20160322025152-9bf6e6e569ff/go.mod h1:4mhQ8q/RsB7i+udVvVy5NUi08OU8ZlA0gRVgrF7VFY0= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -583,6 +1200,19 @@ google.golang.org/api v0.32.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ google.golang.org/api v0.35.0/go.mod h1:/XrVsuzM0rZmrsbjJutiuftIzeuTQcEeaYcSk/mQ1dg= google.golang.org/api v0.36.0 h1:l2Nfbl2GPXdWorv+dT2XfinX2jOOw4zv1VhLstx+6rE= google.golang.org/api v0.36.0/go.mod h1:+z5ficQTmoYpPn8LCUNVpK5I7hwkpjbcgqA7I34qYtE= +google.golang.org/api v0.40.0/go.mod h1:fYKFpnQN0DsDSKRVRcQSDQNtqWPfM9i+zNPxepjRCQ8= +google.golang.org/api v0.41.0/go.mod h1:RkxM5lITDfTzmyKFPt+wGrCJbVfniCr2ool8kTBzRTU= +google.golang.org/api v0.43.0/go.mod h1:nQsDGjRXMo4lvh5hP0TKqF244gqhGcr/YSIykhUk/94= +google.golang.org/api v0.47.0/go.mod h1:Wbvgpq1HddcWVtzsVLyfLp8lDg6AA241LmgIL59tHXo= +google.golang.org/api v0.48.0/go.mod h1:71Pr1vy+TAZRPkPs/xlCf5SsU8WjuAWv1Pfjbtukyy4= +google.golang.org/api v0.50.0/go.mod h1:4bNT5pAuq5ji4SRZm+5QIkjny9JAyVD/3gaSihNefaw= +google.golang.org/api v0.51.0/go.mod h1:t4HdrdoNgyN5cbEfm7Lum0lcLDLiise1F8qDKX00sOU= +google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6z3k= +google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= +google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= +google.golang.org/api v0.58.0 h1:MDkAbYIB1JpSgCTOCYYoIec/coMlKK4oVbpnBLLcyT0= +google.golang.org/api v0.58.0/go.mod h1:cAbP2FsxoGVNwtgNAmmn3y5G1TWAiVYRmg4yku3lv+E= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -591,11 +1221,13 @@ google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCID google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= +google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8/go.mod h1:0H1ncTHf11KCFhTc/+EFRbzSCOZx+VUbRMk55Yv5MYk= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE= +google.golang.org/genproto v0.0.0-20190522204451-c2c4e71fbf69/go.mod h1:z3L6/3dTEVtUr6QSP8miRzeRqwQOioJ9I66odjN4I7s= google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8= @@ -604,6 +1236,7 @@ google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvx google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= +google.golang.org/genproto v0.0.0-20200117163144-32f20d992d24/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc= google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA= google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= @@ -614,6 +1247,7 @@ google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfG google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= +google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c= google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U= google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA= @@ -625,15 +1259,45 @@ google.golang.org/genproto v0.0.0-20200904004341-0bd0a958aa1d/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20200914193844-75d14daec038/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20200921151605-7abf4a1a14d5/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201109203340-2640f1f9cdfb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201110150050-8816d57aaa9a/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20201201144952-b05cb90ed32e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201210142538-e3217bee35cc/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20201214200347-8c77b98c765d/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210108203827-ffc7fda8c3d7/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210113195801-ae06605f4595 h1:x7nk+/4+SvuTDI4wnzQUlhvi+DTpyfncXBo3QWTFs7U= google.golang.org/genproto v0.0.0-20210113195801-ae06605f4595/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= +google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= +google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210604141403-392c879c8b08/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210608205507-b6d2f5bf0d7d/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20210624195500-8bfb893ecb84/go.mod h1:SzzZ/N+nwJDaO1kznhnlzqS8ocJICar6hYhVyhi++24= +google.golang.org/genproto v0.0.0-20210713002101-d411969a0d9a/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210716133855-ce7ef5c701ea/go.mod h1:AxrInvYm1dci+enl5hChSFPOmmUF1+uAa/UsgNRWd7k= +google.golang.org/genproto v0.0.0-20210728212813-7823e685a01f/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210805201207-89edb61ffb67/go.mod h1:ob2IJxKrgPT52GcgX759i1sleT07tiKowYBGbczaW48= +google.golang.org/genproto v0.0.0-20210813162853-db860fec028c/go.mod h1:cFeNkxwySK631ADgubI+/XFU/xp8FD5KIVV4rj8UC5w= +google.golang.org/genproto v0.0.0-20210821163610-241b8fcbd6c8/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= +google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211013025323-ce878158c4d4 h1:NBxB1XxiWpGqkPUiJ9PoBXkHV5A9+GohMOA+EmWoPbU= +google.golang.org/genproto v0.0.0-20211013025323-ce878158c4d4/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/grpc v0.0.0-20160317175043-d3ddb4469d5a/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= +google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.23.1/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.24.0/go.mod h1:XDChyiUovWa60DnaeDeZmSW86xtLtjtZbwvSiRnRtcA= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= @@ -644,12 +1308,24 @@ google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.31.1/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= google.golang.org/grpc v1.32.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= google.golang.org/grpc v1.34.1 h1:ugq+9++ZQPFzM2pKUMCIK8gj9M0pFyuUWO9Q8kwEDQw= google.golang.org/grpc v1.34.1/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8= +google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.36.1/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU= +google.golang.org/grpc v1.37.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= google.golang.org/grpc v1.37.1 h1:ARnQJNWxGyYJpdf/JXscNlQr/uv607ZPU9Z7ogHi+iI= google.golang.org/grpc v1.37.1/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM= +google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= +google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.41.0 h1:f+PlOh7QV4iIJkPrx5NQ7qaNGFQ3OTse67yaDHfju4E= +google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= @@ -661,29 +1337,54 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20141024133853-64131543e789/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/cheggaaa/pb.v1 v1.0.25/go.mod h1:V/YB90LKu/1FcN3WVnfiiE5oMCibMjukxqG/qStrOgw= gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= +gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= +gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= +gopkg.in/square/go-jose.v2 v2.2.2/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.3.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= gopkg.in/src-d/go-billy.v4 v4.3.2/go.mod h1:nDjArDMp+XMs1aFAESLRjfGSgfvoYN0hDfzEk0GjC98= gopkg.in/src-d/go-git-fixtures.v3 v3.5.0/go.mod h1:dLBcvytrw/TYZsNTWCnkNF2DSIlzWYqTe3rJR56Ac7g= gopkg.in/src-d/go-git.v4 v4.13.1 h1:SRtFyV8Kxc0UP7aCHcijOMQGPxHSmMOPrzulQWolkYE= gopkg.in/src-d/go-git.v4 v4.13.1/go.mod h1:nx5NYcxdKxq5fpltdHnPa2Exj4Sx0EclMWZQbYDu2z8= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/warnings.v0 v0.1.2 h1:wFXVbFY8DY5/xOe1ECiWdKCzZlxgshcYVNkBHstARME= gopkg.in/warnings.v0 v0.1.2/go.mod h1:jksf8JmL6Qr/oQM2OXTHunEvvTAsrWBLb6OOjuVWRNI= +gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo= gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= +gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= @@ -692,6 +1393,37 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/api v0.20.1/go.mod h1:KqwcCVogGxQY3nBlRpwt+wpAMF/KjaCc7RpywacvqUo= +k8s.io/api v0.20.4/go.mod h1:++lNL1AJMkDymriNniQsWRkMDzRaX2Y/POTUi8yvqYQ= +k8s.io/api v0.20.6/go.mod h1:X9e8Qag6JV/bL5G6bU8sdVRltWKmdHsFUGS3eVndqE8= +k8s.io/apimachinery v0.20.1/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= +k8s.io/apimachinery v0.20.4/go.mod h1:WlLqWAHZGg07AeltaI0MV5uk1Omp8xaN0JGLY6gkRpU= +k8s.io/apimachinery v0.20.6/go.mod h1:ejZXtW1Ra6V1O5H8xPBGz+T3+4gfkTCeExAHKU57MAc= +k8s.io/apiserver v0.20.1/go.mod h1:ro5QHeQkgMS7ZGpvf4tSMx6bBOgPfE+f52KwvXfScaU= +k8s.io/apiserver v0.20.4/go.mod h1:Mc80thBKOyy7tbvFtB4kJv1kbdD0eIH8k8vianJcbFM= +k8s.io/apiserver v0.20.6/go.mod h1:QIJXNt6i6JB+0YQRNcS0hdRHJlMhflFmsBDeSgT1r8Q= +k8s.io/client-go v0.20.1/go.mod h1:/zcHdt1TeWSd5HoUe6elJmHSQ6uLLgp4bIJHVEuy+/Y= +k8s.io/client-go v0.20.4/go.mod h1:LiMv25ND1gLUdBeYxBIwKpkSC5IsozMMmOOeSJboP+k= +k8s.io/client-go v0.20.6/go.mod h1:nNQMnOvEUEsOzRRFIIkdmYOjAZrC8bgq0ExboWSU1I0= +k8s.io/component-base v0.20.1/go.mod h1:guxkoJnNoh8LNrbtiQOlyp2Y2XFCZQmrcg2n/DeYNLk= +k8s.io/component-base v0.20.4/go.mod h1:t4p9EdiagbVCJKrQ1RsA5/V4rFQNDfRlevJajlGwgjI= +k8s.io/component-base v0.20.6/go.mod h1:6f1MPBAeI+mvuts3sIdtpjljHWBQ2cIy38oBIWMYnrM= +k8s.io/cri-api v0.17.3/go.mod h1:X1sbHmuXhwaHs9xxYffLqJogVsnI+f6cPRcgPel7ywM= +k8s.io/cri-api v0.20.1/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= +k8s.io/cri-api v0.20.4/go.mod h1:2JRbKt+BFLTjtrILYVqQK5jqhI+XNdF6UiGMgczeBCI= +k8s.io/cri-api v0.20.6/go.mod h1:ew44AjNXwyn1s0U4xCKGodU7J1HzBeZ1MpGrpa5r8Yc= +k8s.io/gengo v0.0.0-20200413195148-3a45101e95ac/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= +k8s.io/klog/v2 v2.0.0/go.mod h1:PBfzABfn139FHAV07az/IF9Wp1bkk3vpT2XSJ76fSDE= +k8s.io/klog/v2 v2.4.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y= +k8s.io/kube-openapi v0.0.0-20201113171705-d219536bb9fd/go.mod h1:WOJ3KddDSol4tAGcJo0Tvi+dK12EcqSLqcWsryKMpfM= +k8s.io/kubernetes v1.13.0/go.mod h1:ocZa8+6APFNC2tX1DZASIbocyYT5jHzqFVsY5aoB7Jk= +k8s.io/utils v0.0.0-20201110183641-67b214c5f920/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.14/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.15/go.mod h1:LEScyzhFmoF5pso/YSeBstl57mOzx9xlU9n85RGrDQg= +sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/structured-merge-diff/v4 v4.0.3/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= +sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= +sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= diff --git a/functions/onprem/worker/worker.go b/functions/onprem/worker/worker.go index a2c62b25..0db54ce0 100644 --- a/functions/onprem/worker/worker.go +++ b/functions/onprem/worker/worker.go @@ -964,8 +964,8 @@ func handleExecutionResult(workflowExecution shuffle.WorkflowExecution) { // 3. Add remote repo location images := []string{ image, - fmt.Sprintf("%s:%s_%s", baseimagename, strings.Replace(action.AppName, " ", "-", -1), action.AppVersion), fmt.Sprintf("%s/%s:%s_%s", registryName, baseimagename, parsedAppname, action.AppVersion), + fmt.Sprintf("%s:%s_%s", baseimagename, strings.Replace(action.AppName, " ", "-", -1), action.AppVersion), } // If cleanup is set, it should run for efficiency From 4a0e85d0958906853028fffcea348d2717ac8725 Mon Sep 17 00:00:00 2001 From: frikky Date: Thu, 14 Oct 2021 23:17:17 +0200 Subject: [PATCH 116/117] Frontend security fixes and login page update --- frontend/package.json | 12 ++++++------ frontend/src/views/LoginPage.jsx | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/frontend/package.json b/frontend/package.json index 43072ce4..14744ca8 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,7 +1,7 @@ { "name": "shuffler", "homepage": "https://shuffler.io", - "version": "0.9.22", + "version": "0.9.24", "private": true, "dependencies": { "@material-ui/core": "^4.5.2", @@ -19,23 +19,23 @@ "cytoscape-clipboard": "^2.2.1", "cytoscape-cxtmenu": "^3.1.1", "cytoscape-edgehandles": "^3.6.0", - "cytoscape-grid-guide": "~2.1.2", + "cytoscape-grid-guide": "~2.3.3", "cytoscape-node-html-label": "^1.1.5", "cytoscape-panzoom": "^2.5.2", "cytoscape-undo-redo": "^1.3.2", - "d3": "~4.10.0", + "d3": "^7.1.1", "dotenv": "^6.1.0", "downshift": "^3.3.5", "github-markdown-css": "^3.0.1", "import": "0.0.6", "interweave": "^11.2.0", - "material-icons": "^0.3.1", + "material-icons": "^0.7.7", "material-icons-react": "^1.0.4", "material-ui-chip-input": "^2.0.0-beta.2", "material-ui-nested-menu-item": "^1.0.2", "md5-file": "^4.0.0", "mdbreact": "^4.21.1", - "moment": "~2.20.1", + "moment": "^2.29.1", "react": "^16.14.0", "react-alert": "^5.5.0", "react-alert-template-basic": "^1.0.0", @@ -66,7 +66,7 @@ "websocket": "^1.0.30", "yaml": "^1.7.2", "yamljs": "^0.3.0", - "zone.js": "~0.8.26" + "zone.js": "~0.11.4" }, "scripts": { "start": "react-scripts start", diff --git a/frontend/src/views/LoginPage.jsx b/frontend/src/views/LoginPage.jsx index 35ee4e90..b71313cc 100644 --- a/frontend/src/views/LoginPage.jsx +++ b/frontend/src/views/LoginPage.jsx @@ -240,8 +240,8 @@ const LoginDialog = props => { - 2. Ensure vm.max_map_count is set:

    - sudo sysctl -w vm.max_map_count=262144 + 2. Restart docker-compose:

    + sudo docker-compose restart
    From 3911a28426568ff15be7fc5cf0d8b63e81e8584b Mon Sep 17 00:00:00 2001 From: frikky Date: Fri, 15 Oct 2021 00:52:40 +0200 Subject: [PATCH 117/117] #499: Added and reformatted tests --- .../database/opensearch/docker-compose.yml | 34 ++ backend/go-app/docker.go | 6 +- backend/go-app/go.mod | 4 +- backend/go-app/main.go | 32 +- backend/go-app/main_test.go | 340 ++++++++++++++++++ backend/go-app/run.sh | 1 + backend/go-app/walkoff.go | 31 +- 7 files changed, 407 insertions(+), 41 deletions(-) create mode 100644 backend/database/opensearch/docker-compose.yml create mode 100644 backend/go-app/main_test.go create mode 100755 backend/go-app/run.sh diff --git a/backend/database/opensearch/docker-compose.yml b/backend/database/opensearch/docker-compose.yml new file mode 100644 index 00000000..9dfae6a1 --- /dev/null +++ b/backend/database/opensearch/docker-compose.yml @@ -0,0 +1,34 @@ +version: '3' +services: + opensearch-node1: + image: opensearchproject/opensearch:latest + hostname: shuffle-database + container_name: shuffle-opensearch + environment: + - cluster.name=shuffle-cluster + - node.name=shuffle-opensearch + - discovery.seed_hosts=shuffle-opensearch + - cluster.initial_master_nodes=shuffle-opensearch + - bootstrap.memory_lock=true # along with the memlock settings below, disables swapping + - "OPENSEARCH_JAVA_OPTS=-Xms512m -Xmx512m" # minimum and maximum Java heap size, recommend setting both to 50% of system RAM + - cluster.routing.allocation.disk.threshold_enabled=false + - opendistro_security.disabled=true + ulimits: + memlock: + soft: -1 + hard: -1 + nofile: + soft: 65536 # maximum number of open files for the OpenSearch user, set to at least 65536 on modern systems + hard: 65536 + volumes: + - ~/git/shuffle/shuffle-database:/usr/share/opensearch/data + ports: + - 9200:9200 + networks: + - opensearch-net + +volumes: + opensearch-data1: + +networks: + opensearch-net: diff --git a/backend/go-app/docker.go b/backend/go-app/docker.go index 2e9a4314..62c5878b 100644 --- a/backend/go-app/docker.go +++ b/backend/go-app/docker.go @@ -2,7 +2,7 @@ package main // Docker import ( - "github.com/frikky/shuffle-shared" + "github.com/shuffle/shuffle-shared" "archive/tar" //"bufio" @@ -778,7 +778,7 @@ func getDockerImage(resp http.ResponseWriter, request *http.Request) { newClient, err := newdockerclient.NewClientFromEnv() if err != nil { - log.Printf("[WARNING] Failed setting up docker env: %s", newClient) + log.Printf("[ERROR] Failed setting up docker env: %#v", newClient) resp.WriteHeader(401) resp.Write([]byte(fmt.Sprintf(`{"success": false, "message": "Couldn't make docker client"}`))) return @@ -792,7 +792,7 @@ func getDockerImage(resp http.ResponseWriter, request *http.Request) { } if err := newClient.ExportImage(opts); err != nil { - log.Printf("[WARNING] FAILED to save image to file: %s", err) + log.Printf("[ERROR] FAILED to save image to file: %s", err) resp.WriteHeader(401) resp.Write([]byte(fmt.Sprintf(`{"success": false, "message": "Couldn't export image"}`))) return diff --git a/backend/go-app/go.mod b/backend/go-app/go.mod index 7dc5e24a..16f22e0f 100644 --- a/backend/go-app/go.mod +++ b/backend/go-app/go.mod @@ -23,7 +23,7 @@ require ( github.com/docker/go-units v0.4.0 // indirect github.com/elastic/go-elasticsearch/v7 v7.13.1 // indirect github.com/frikky/kin-openapi v0.39.0 - github.com/frikky/shuffle-shared v0.1.15 // indirect + github.com/frikky/shuffle-shared v0.1.15 github.com/fsouza/go-dockerclient v1.7.2 github.com/ghodss/yaml v1.0.0 github.com/go-git/go-billy/v5 v5.0.0 @@ -36,7 +36,7 @@ require ( github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect github.com/patrickmn/go-cache v2.1.0+incompatible github.com/satori/go.uuid v1.2.0 - github.com/shuffle/shuffle-shared v0.1.14 + github.com/shuffle/shuffle-shared v0.1.15 go4.org v0.0.0-20201209231011-d4a079459e60 // indirect golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 golang.org/x/oauth2 v0.0.0-20210113160501-8b1d76fa0423 diff --git a/backend/go-app/main.go b/backend/go-app/main.go index e330ca0f..2b644674 100644 --- a/backend/go-app/main.go +++ b/backend/go-app/main.go @@ -1,7 +1,7 @@ package main import ( - "github.com/frikky/shuffle-shared" + "github.com/shuffle/shuffle-shared" "bufio" "bytes" @@ -580,23 +580,6 @@ func redirect(w http.ResponseWriter, req *http.Request) { http.StatusTemporaryRedirect) } -func parseLoginParameters(resp http.ResponseWriter, request *http.Request) (loginStruct, error) { - - body, err := ioutil.ReadAll(request.Body) - if err != nil { - return loginStruct{}, err - } - - var t loginStruct - - err = json.Unmarshal(body, &t) - if err != nil { - return loginStruct{}, err - } - - return t, nil -} - // No more emails :) func checkUsername(Username string) error { // Stupid first check of email loool @@ -731,6 +714,7 @@ func handleRegister(resp http.ResponseWriter, request *http.Request) { // Only admin can CREATE users, but if there are no users, anyone can make (first) ctx := context.Background() users, countErr := shuffle.GetAllUsers(ctx) + count := len(users) user, err := shuffle.HandleApiAuthentication(resp, request) if err != nil { @@ -753,7 +737,7 @@ func handleRegister(resp http.ResponseWriter, request *http.Request) { } // Gets a struct of Username, password - data, err := parseLoginParameters(resp, request) + data, err := shuffle.ParseLoginParameters(resp, request) if err != nil { log.Printf("Invalid params: %s", err) resp.WriteHeader(401) @@ -813,7 +797,7 @@ func handleRegister(resp http.ResponseWriter, request *http.Request) { err = shuffle.SetEnvironment(ctx, &item) if err != nil { - log.Printf("[WARNING] Failed setting up new environment for new org: %s") + log.Printf("[WARNING] Failed setting up new environment for new org: %s", err) } currentOrg = shuffle.OrgMini{ @@ -1194,7 +1178,7 @@ func handleLogin(resp http.ResponseWriter, request *http.Request) { } // Gets a struct of Username, password - data, err := parseLoginParameters(resp, request) + data, err := shuffle.ParseLoginParameters(resp, request) if err != nil { resp.WriteHeader(401) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err))) @@ -1971,6 +1955,10 @@ func handleWebhookCallback(resp http.ResponseWriter, request *http.Request) { // 1. Get callback data // 2. Load the configuration // 3. Execute the workflow + cors := shuffle.HandleCors(resp, request) + if cors { + return + } path := strings.Split(request.URL.String(), "/") if len(path) < 4 { @@ -5173,7 +5161,7 @@ func handleCloudSetup(resp http.ResponseWriter, request *http.Request) { // 2. If cloud env found, enable it (un-archive) // 3. If it doesn't create it environments, err := shuffle.GetEnvironments(ctx, org.Id) - log.Printf("GETTING ENVS: %#s", environments) + log.Printf("GETTING ENVS: %#v", environments) if err == nil { // Don't disable, this will be deleted entirely diff --git a/backend/go-app/main_test.go b/backend/go-app/main_test.go new file mode 100644 index 00000000..e9d358b6 --- /dev/null +++ b/backend/go-app/main_test.go @@ -0,0 +1,340 @@ +package main + +import ( + "github.com/shuffle/shuffle-shared" + + "bytes" + "context" + "log" + "net/http" + "net/http/httptest" + "reflect" + "runtime" + "testing" + "time" + + "cloud.google.com/go/datastore" + "cloud.google.com/go/storage" + "google.golang.org/api/option" + + "google.golang.org/grpc" +) + +type endpoint struct { + handler http.HandlerFunc + path string + method string +} + +func init() { + ctx := context.Background() + dbclient, err := datastore.NewClient(ctx, gceProject, option.WithGRPCDialOption(grpc.WithNoProxy())) + if err != nil { + log.Fatalf("[DEBUG] Database client error during init: %s", err) + } + + _, err = shuffle.RunInit(*dbclient, storage.Client{}, gceProject, "onprem", true, "elasticsearch") + log.Printf("INIT") +} + +// TestTestAuthenticationRequired tests that the handlers in the `handlers` +// variable returns 401 Unauthorized when called without credentials. +func TestAuthenticationRequired(t *testing.T) { + handlers := []endpoint{ + {handler: shuffle.HandleNewOutlookRegister, path: "/functions/outlook/register", method: "GET"}, + {handler: shuffle.HandleGetOutlookFolders, path: "/functions/outlook/getFolders", method: "GET"}, + {handler: shuffle.HandleApiGeneration, path: "/api/v1/users/generateapikey", method: "GET"}, + {handler: handleLogin, path: "/api/v1/users/login", method: "POST"}, // prob not this one + // handleRegister generates nil pointer exception. Not necessary for this anyway. + //{handler: handleRegister, path: "/api/v1/users/register", method: "POST"}, + {handler: shuffle.HandleGetUsers, path: "/api/v1/users/getusers", method: "GET"}, + {handler: handleInfo, path: "/api/v1/users/getinfo", method: "GET"}, + {handler: shuffle.HandleSettings, path: "/api/v1/users/getsettings", method: "GET"}, + {handler: shuffle.HandleUpdateUser, path: "/api/v1/users/updateuser", method: "PUT"}, + {handler: shuffle.DeleteUser, path: "/api/v1/users/123", method: "DELETE"}, + {handler: shuffle.HandlePasswordChange, path: "/api/v1/users/passwordchange", method: "POST"}, + {handler: shuffle.HandleGetUsers, path: "/api/v1/users", method: "GET"}, + {handler: shuffle.HandleGetEnvironments, path: "/api/v1/getenvironments", method: "GET"}, + {handler: shuffle.HandleSetEnvironments, path: "/api/v1/setenvironments", method: "PUT"}, + + // handleWorkflowQueue generates nil pointer exception + //{handler: handleWorkflowQueue, path: "/api/v1/streams", method: "POST"}, + // handleGetStreamResults generates nil pointer exception + //{handler: handleGetStreamResults, path: "/api/v1/streams/results", method: "POST"}, + + {handler: handleAppHotloadRequest, path: "/api/v1/apps/run_hotload", method: "GET"}, + {handler: LoadSpecificApps, path: "/api/v1/apps/get_existing", method: "POST"}, + {handler: shuffle.UpdateWorkflowAppConfig, path: "/api/v1/apps/123", method: "PATCH"}, + {handler: validateAppInput, path: "/api/v1/apps/validate", method: "POST"}, + {handler: shuffle.DeleteWorkflowApp, path: "/api/v1/apps/123", method: "DELETE"}, + {handler: shuffle.GetWorkflowAppConfig, path: "/api/v1/apps/123/config", method: "GET"}, + {handler: getWorkflowApps, path: "/api/v1/apps", method: "GET"}, + {handler: setNewWorkflowApp, path: "/api/v1/apps", method: "PUT"}, + //{handler: shuffle.GetSpecificApps, path: "/api/v1/apps/search", method: "POST"}, + + {handler: shuffle.GetAppAuthentication, path: "/api/v1/apps/authentication", method: "GET"}, + {handler: shuffle.AddAppAuthentication, path: "/api/v1/apps/authentication", method: "PUT"}, + {handler: shuffle.DeleteAppAuthentication, path: "/api/v1/apps/authentication/123", method: "DELETE"}, + + {handler: validateAppInput, path: "/api/v1/workflows/apps/validate", method: "POST"}, + {handler: getWorkflowApps, path: "/api/v1/workflows/apps", method: "GET"}, + {handler: setNewWorkflowApp, path: "/api/v1/workflows/apps", method: "PUT"}, + + {handler: shuffle.GetWorkflows, path: "/api/v1/workflows", method: "GET"}, + {handler: shuffle.SetNewWorkflow, path: "/api/v1/workflows", method: "POST"}, + {handler: handleGetWorkflowqueue, path: "/api/v1/workflows/queue", method: "GET"}, + {handler: handleGetWorkflowqueueConfirm, path: "/api/v1/workflows/queue/confirm", method: "POST"}, + {handler: shuffle.HandleGetSchedules, path: "/api/v1/workflows/schedules", method: "GET"}, + {handler: loadSpecificWorkflows, path: "/api/v1/workflows/download_remote", method: "POST"}, + {handler: executeWorkflow, path: "/api/v1/workflows/123/execute", method: "GET"}, + {handler: scheduleWorkflow, path: "/api/v1/workflows/123/schedule", method: "POST"}, + {handler: stopSchedule, path: "/api/v1/workflows/123/schedule/abc", method: "DELETE"}, + // createOutlookSub generates nil pointer exception + {handler: shuffle.HandleCreateOutlookSub, path: "/api/v1/workflows/123/outlook", method: "POST"}, + // handleDeleteOutlookSub generates nil pointer exception + {handler: shuffle.HandleDeleteOutlookSub, path: "/api/v1/workflows/123/outlook/abc", method: "DELETE"}, + {handler: shuffle.GetWorkflowExecutions, path: "/api/v1/workflows/123/executions", method: "GET"}, + {handler: shuffle.AbortExecution, path: "/api/v1/workflows/123/executions/abc/abort", method: "GET"}, + {handler: shuffle.GetSpecificWorkflow, path: "/api/v1/workflows/123", method: "GET"}, + {handler: shuffle.SaveWorkflow, path: "/api/v1/workflows/123", method: "PUT"}, + {handler: deleteWorkflow, path: "/api/v1/workflows/123", method: "DELETE"}, + + {handler: shuffle.HandleNewHook, path: "/api/v1/hooks/new", method: "POST"}, + {handler: handleWebhookCallback, path: "/api/v1/hooks/123", method: "POST"}, + {handler: shuffle.HandleDeleteHook, path: "/api/v1/hooks/123/delete", method: "DELETE"}, + + {handler: shuffle.HandleGetSpecificTrigger, path: "/api/v1/triggers/123", method: "GET"}, + //{handler: shuffle.HandleGetSpecificStats, path: "/api/v1/stats/123", method: "GET"}, + + {handler: verifySwagger, path: "/api/v1/verify_swagger", method: "POST"}, + {handler: verifySwagger, path: "/api/v1/verify_openapi", method: "POST"}, + {handler: echoOpenapiData, path: "/api/v1/get_openapi_uri", method: "POST"}, + {handler: echoOpenapiData, path: "/api/v1/validate_openapi", method: "POST"}, + {handler: shuffle.ValidateSwagger, path: "/api/v1/validate_openapi", method: "POST"}, + {handler: getOpenapi, path: "/api/v1/get_openapi", method: "GET"}, + + //{handler: shuffle.CleanupExecutions, path: "/api/v1/execution_cleanup", method: "GET"}, + + {handler: handleCloudSetup, path: "/api/v1/cloud/setup", method: "POST"}, + {handler: shuffle.HandleGetOrgs, path: "/api/v1/orgs", method: "POST"}, + {handler: shuffle.HandleGetFileContent, path: "/api/v1/files/{fileId}/content", method: "POST"}, + } + + var err error + ctx := context.Background() + + // Most handlers requires database access in order to not crash or cause + // nil pointer issues. + // To start a local database instance, run: + // docker-compose up database + // To let the tests know about the database, run: + // DATASTORE_EMULATOR_HOST=0.0.0.0:8000 go test + dbclient, err = datastore.NewClient(ctx, gceProject, option.WithGRPCDialOption(grpc.WithNoProxy())) + if err != nil { + t.Fatal(err) + } + + dummyBody := bytes.NewBufferString("dummy") + + for _, e := range handlers { + log.Printf("Endpoint: %#v", e.path) + req, err := http.NewRequest(e.method, e.path, dummyBody) + if err != nil { + t.Fatal(err) + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(e.handler) + + timeoutHandler := http.TimeoutHandler(handler, 2*time.Second, `Request Timeout.`) + timeoutHandler.ServeHTTP(rr, req) + + funcName := getFunctionNameFromFunction(e.handler) + if status := rr.Code; status != http.StatusUnauthorized { + t.Errorf("%s handler returned wrong status code: got %v want %v", + funcName, status, http.StatusUnauthorized) + } + } +} + +func TestAuthenticationNotRequired(t *testing.T) { + // All of these return 200 OK when user not logged in + handlers := []endpoint{ + {handler: checkAdminLogin, path: "/api/v1/users/checkusers", method: "GET"}, + {handler: shuffle.HandleLogout, path: "/api/v1/users/logout", method: "POST"}, + {handler: shuffle.GetDocList, path: "/api/v1/docs", method: "GET"}, + {handler: shuffle.GetDocs, path: "/api/v1/docs/123", method: "GET"}, + {handler: healthCheckHandler, path: "/api/v1/_ah/health"}, + } + + for _, e := range handlers { + log.Printf("Endpoint: %#v", e.path) + req, err := http.NewRequest(e.method, e.path, nil) + if err != nil { + t.Fatal(err) + } + + rr := httptest.NewRecorder() + handler := http.HandlerFunc(e.handler) + + timeoutHandler := http.TimeoutHandler(handler, 2*time.Second, `Request Timeout.`) + timeoutHandler.ServeHTTP(rr, req) + + funcName := getFunctionNameFromFunction(e.handler) + if status := rr.Code; status != http.StatusOK { + t.Errorf("%s handler returned wrong status code: got %v want %v", + funcName, status, http.StatusOK) + } + } +} + +// TestCors tests that all endpoints returns the same CORS headers when hit +// with an OPTIONS type request. +// It feels very fragile to test headers like this, especially for the +// "Access-Control-Allow-Origin", but this test should be helpful while +// refactoring the CORS logic into a middleware, and that's the reason this +// test exists right now. It might change after the refactor because our +// requirements might change after the refactor. +func TestCors(t *testing.T) { + handlers := []endpoint{ + {handler: shuffle.HandleNewOutlookRegister, path: "/functions/outlook/register", method: "GET"}, + {handler: shuffle.HandleGetOutlookFolders, path: "/functions/outlook/getFolders", method: "GET"}, + {handler: shuffle.HandleApiGeneration, path: "/api/v1/users/generateapikey", method: "GET"}, + {handler: handleLogin, path: "/api/v1/users/login", method: "POST"}, // prob not this one + // handleRegister generates nil pointer exception + {handler: handleRegister, path: "/api/v1/users/register", method: "POST"}, + {handler: shuffle.HandleGetUsers, path: "/api/v1/users/getusers", method: "GET"}, + {handler: handleInfo, path: "/api/v1/users/getinfo", method: "GET"}, + {handler: shuffle.HandleSettings, path: "/api/v1/users/getsettings", method: "GET"}, + {handler: shuffle.HandleUpdateUser, path: "/api/v1/users/updateuser", method: "PUT"}, + {handler: shuffle.DeleteUser, path: "/api/v1/users/123", method: "DELETE"}, + // handlePasswordChange generates nil pointer exception + {handler: shuffle.HandlePasswordChange, path: "/api/v1/users/passwordchange", method: "POST"}, + {handler: shuffle.HandleGetUsers, path: "/api/v1/users", method: "GET"}, + {handler: shuffle.HandleGetEnvironments, path: "/api/v1/getenvironments", method: "GET"}, + {handler: shuffle.HandleSetEnvironments, path: "/api/v1/setenvironments", method: "PUT"}, + + // handleWorkflowQueue generates nil pointer exception + {handler: handleWorkflowQueue, path: "/api/v1/streams", method: "POST"}, + // handleGetStreamResults generates nil pointer exception + {handler: handleGetStreamResults, path: "/api/v1/streams/results", method: "POST"}, + + {handler: handleAppHotloadRequest, path: "/api/v1/apps/run_hotload", method: "GET"}, + {handler: LoadSpecificApps, path: "/api/v1/apps/get_existing", method: "POST"}, + {handler: shuffle.UpdateWorkflowAppConfig, path: "/api/v1/apps/123", method: "PATCH"}, + {handler: validateAppInput, path: "/api/v1/apps/validate", method: "POST"}, + {handler: shuffle.DeleteWorkflowApp, path: "/api/v1/apps/123", method: "DELETE"}, + {handler: shuffle.GetWorkflowAppConfig, path: "/api/v1/apps/123/config", method: "GET"}, + {handler: getWorkflowApps, path: "/api/v1/apps", method: "GET"}, + {handler: setNewWorkflowApp, path: "/api/v1/apps", method: "PUT"}, + //{handler: shuffle.GetSpecificApps, path: "/api/v1/apps/search", method: "POST"}, + + {handler: shuffle.GetAppAuthentication, path: "/api/v1/apps/authentication", method: "GET"}, + {handler: shuffle.AddAppAuthentication, path: "/api/v1/apps/authentication", method: "PUT"}, + {handler: shuffle.DeleteAppAuthentication, path: "/api/v1/apps/authentication/123", method: "DELETE"}, + + {handler: validateAppInput, path: "/api/v1/workflows/apps/validate", method: "POST"}, + {handler: getWorkflowApps, path: "/api/v1/workflows/apps", method: "GET"}, + {handler: setNewWorkflowApp, path: "/api/v1/workflows/apps", method: "PUT"}, + + {handler: shuffle.GetWorkflows, path: "/api/v1/workflows", method: "GET"}, + {handler: shuffle.SetNewWorkflow, path: "/api/v1/workflows", method: "POST"}, + {handler: handleGetWorkflowqueue, path: "/api/v1/workflows/queue", method: "GET"}, + {handler: handleGetWorkflowqueueConfirm, path: "/api/v1/workflows/queue/confirm", method: "POST"}, + {handler: shuffle.HandleGetSchedules, path: "/api/v1/workflows/schedules", method: "GET"}, + {handler: loadSpecificWorkflows, path: "/api/v1/workflows/download_remote", method: "POST"}, + {handler: executeWorkflow, path: "/api/v1/workflows/123/execute", method: "GET"}, + {handler: scheduleWorkflow, path: "/api/v1/workflows/123/schedule", method: "POST"}, + {handler: stopSchedule, path: "/api/v1/workflows/123/schedule/abc", method: "DELETE"}, + // createOutlookSub generates nil pointer exception + {handler: shuffle.HandleCreateOutlookSub, path: "/api/v1/workflows/123/outlook", method: "POST"}, + // handleDeleteOutlookSub generates nil pointer exception + {handler: shuffle.HandleDeleteOutlookSub, path: "/api/v1/workflows/123/outlook/abc", method: "DELETE"}, + {handler: shuffle.GetWorkflowExecutions, path: "/api/v1/workflows/123/executions", method: "GET"}, + {handler: shuffle.AbortExecution, path: "/api/v1/workflows/123/executions/abc/abort", method: "GET"}, + {handler: shuffle.GetSpecificWorkflow, path: "/api/v1/workflows/123", method: "GET"}, + {handler: shuffle.SaveWorkflow, path: "/api/v1/workflows/123", method: "PUT"}, + {handler: deleteWorkflow, path: "/api/v1/workflows/123", method: "DELETE"}, + + {handler: shuffle.HandleNewHook, path: "/api/v1/hooks/new", method: "POST"}, + {handler: handleWebhookCallback, path: "/api/v1/hooks/123", method: "POST"}, + {handler: shuffle.HandleDeleteHook, path: "/api/v1/hooks/123/delete", method: "DELETE"}, + + {handler: shuffle.HandleGetSpecificTrigger, path: "/api/v1/triggers/123", method: "GET"}, + //{handler: shuffle.HandleGetSpecificStats, path: "/api/v1/stats/123", method: "GET"}, + + {handler: verifySwagger, path: "/api/v1/verify_swagger", method: "POST"}, + {handler: verifySwagger, path: "/api/v1/verify_openapi", method: "POST"}, + {handler: echoOpenapiData, path: "/api/v1/get_openapi_uri", method: "POST"}, + {handler: echoOpenapiData, path: "/api/v1/validate_openapi", method: "POST"}, + {handler: shuffle.ValidateSwagger, path: "/api/v1/validate_openapi", method: "POST"}, + {handler: getOpenapi, path: "/api/v1/get_openapi", method: "GET"}, + + //{handler: shuffle.CleanupExecutions, path: "/api/v1/execution_cleanup", method: "GET"}, + + {handler: handleCloudSetup, path: "/api/v1/cloud/setup", method: "POST"}, + {handler: shuffle.HandleGetOrgs, path: "/api/v1/orgs", method: "POST"}, + {handler: shuffle.HandleGetFileContent, path: "/api/v1/files/{fileId}/content", method: "POST"}, + } + + //r := initHandlers(context.TODO()) + initHandlers() + +outerLoop: + for _, e := range handlers { + log.Printf("Endpoint: %#v", e.path) + req, err := http.NewRequest("OPTIONS", e.path, nil) + req.Header.Add("Origin", "http://localhost:3000") + req.Header.Add("Access-Control-Request-Method", "POST") + req.Header.Add("Access-Control-Request-Headers", "Content-Type, Accept, X-Requested-With, remember-me") + + // OPTIONS /resource/foo + // Access-Control-Request-Method: DELETE + // Access-Control-Request-Headers: origin, x-requested-with + // Origin: https://foo.bar.org + + if err != nil { + t.Errorf("Failure in OPTIONS setup: %s", err) + continue + } + + rr := httptest.NewRecorder() + + //timeoutHandler := http.TimeoutHandler(r, 2*time.Second, `Request Timeout`) + //timeoutHandler.ServeHTTP(rr, req) + + funcName := getFunctionNameFromFunction(e.handler) + if status := rr.Code; status != http.StatusOK { + t.Errorf("%s handler returned wrong status code: got %v want %v", + funcName, status, http.StatusOK) + continue + } + + want := map[string]string{ + "Vary": "Origin", + "Access-Control-Allow-Headers": "Content-Type, Accept, X-Requested-With, Remember-Me", + "Access-Control-Allow-Methods": "POST", + "Access-Control-Allow-Credentials": "true", + "Access-Control-Allow-Origin": "http://localhost:3000", + } + + // Remember to use canonical header name if accessing the headers array + // directly: + // v := r.Header[textproto.CanonicalMIMEHeaderKey("foo")] + // When using Header().Get(h), h will automatically be converted to canonical format. + + for key, value := range want { + got := rr.Header().Get(key) + if got != value { + t.Errorf("%s handler returned wrong value for '%s' header: got '%v' want '%v'", + funcName, key, got, value) + continue outerLoop + } + } + + } +} + +func getFunctionNameFromFunction(f interface{}) string { + return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name() +} diff --git a/backend/go-app/run.sh b/backend/go-app/run.sh new file mode 100755 index 00000000..2ebf72c9 --- /dev/null +++ b/backend/go-app/run.sh @@ -0,0 +1 @@ +go run main.go walkoff.go docker.go diff --git a/backend/go-app/walkoff.go b/backend/go-app/walkoff.go index a4eb0bca..7078110e 100644 --- a/backend/go-app/walkoff.go +++ b/backend/go-app/walkoff.go @@ -1,7 +1,7 @@ package main import ( - "github.com/frikky/shuffle-shared" + "github.com/shuffle/shuffle-shared" "bytes" "context" @@ -709,6 +709,11 @@ func handleGetStreamResults(resp http.ResponseWriter, request *http.Request) { return } + if request.Body == nil { + resp.WriteHeader(http.StatusBadRequest) + return + } + body, err := ioutil.ReadAll(request.Body) if err != nil { log.Println("Failed reading body for stream result queue") @@ -762,9 +767,14 @@ func handleWorkflowQueue(resp http.ResponseWriter, request *http.Request) { return } + if request.Body == nil { + resp.WriteHeader(http.StatusBadRequest) + return + } + body, err := ioutil.ReadAll(request.Body) if err != nil { - log.Println("(3) Failed reading body for workflowqueue") + log.Println("[WARNING] (3) Failed reading body for workflowqueue") resp.WriteHeader(401) resp.Write([]byte(fmt.Sprintf(`{"success": false, "reason": "%s"}`, err))) return @@ -2636,25 +2646,18 @@ func getWorkflowApps(resp http.ResponseWriter, request *http.Request) { return } - // FIXME - set this to be per user IF logged in, - // as there might exist private and public - //memcacheName := "all_apps" - ctx := context.Background() - // Just need to be logged in - // FIXME - need to be logged in? user, userErr := shuffle.HandleApiAuthentication(resp, request) if userErr != nil { - log.Printf("Continuing with apps even without auth") - //log.Printf("Api authentication failed in get all apps: %s", userErr) - //resp.WriteHeader(401) - //resp.Write([]byte(`{"success": false}`)) - //return + log.Printf("[WARNING] Api authentication failed in get all apps - this does NOT require auth in cloud.: %s", userErr) + resp.WriteHeader(401) + resp.Write([]byte(`{"success": false}`)) + return } workflowapps, err := shuffle.GetAllWorkflowApps(ctx, 1000) if err != nil { - log.Printf("Failed getting apps (getworkflowapps): %s", err) + log.Printf("{WARNING] Failed getting apps (getworkflowapps): %s", err) resp.WriteHeader(401) resp.Write([]byte(`{"success": false}`)) return